ADES Module: Parse, Build, and Exchange¶
The adam_core.observations.ades module handles MPC ADES exchange in both
directions:
parse ADES text into structured
ObsContext+ADESObservationsserialize
ADESObservations+ contexts back to ADES text
Use this when ADES is your system boundary for ingest, QA, submission, or handoff to orbit-determination pipelines.
Current Format Support¶
adam_core currently supports ADES PSV (pipe-separated values) only:
supported: ADES PSV parse/serialize via
ADES_string_to_tablesandADES_to_stringnot currently implemented: ADES XML parse/serialize
Parse ADES Text into Tables¶
from adam_core.observations.ades import ADESObservations, ADES_string_to_tables, ObsContext
ades_text: str = """# version=2022
# observatory
! mpcCode 695
# submitter
! name J. Doe
# observers
! name Survey Team
# measurers
! name Reduction Team
# telescope
! design Reflector
! aperture 1.0
! detector CCD
permID|obsTime|ra|dec|stn|mode|astCat
1234|2024-01-01T00:00:00.000Z|180.0|0.0|695|CCD|Gaia2
"""
contexts: dict[str, ObsContext]
observations: ADESObservations
contexts, observations = ADES_string_to_tables(ades_text)
print(contexts["695"].observatory.mpcCode, len(observations))
Build ADES Tables and Contexts in Python¶
import numpy as np
from adam_core.observations.ades import (
ADESObservations,
ObsContext,
ObservatoryObsContext,
SoftwareObsContext,
SubmitterObsContext,
TelescopeObsContext,
)
from adam_core.time import Timestamp
observations: ADESObservations = ADESObservations.from_kwargs(
permID=["3000", "3000"],
trkSub=["a1234b", "a1234b"],
obsSubID=["obs01", "obs02"],
obsTime=Timestamp.from_mjd(np.array([60434.0, 60434.1]), scale="utc"),
ra=[240.0, 240.05],
dec=[-15.0, -15.05],
rmsRACosDec=[0.9659, 0.9657],
rmsDec=[1.0, 1.0],
mag=[20.0, 20.3],
band=["r", "g"],
stn=["W84", "W84"],
mode=["CCD", "CCD"],
astCat=["Gaia2", "Gaia2"],
)
context_w84: ObsContext = ObsContext(
observatory=ObservatoryObsContext(mpcCode="W84", name="Cerro Tololo - Blanco + DECam"),
submitter=SubmitterObsContext(name="J. Doe", institution="B612 Asteroid Institute"),
observers=["Survey Team"],
measurers=["Reduction Team"],
telescope=TelescopeObsContext(
name="Blanco 4m",
design="Reflector",
aperture=4.0,
detector="CCD",
),
software=SoftwareObsContext(objectDetection="ADAM::THOR"),
comments=["TEST DATA"],
)
contexts: dict[str, ObsContext] = {"W84": context_w84}
Serialize ADES for Submission or Handoff¶
from adam_core.observations.ades import ADES_to_string
ades_text: str = ADES_to_string(
observations,
contexts,
seconds_precision=3,
columns_precision={
"ra": 9,
"dec": 9,
"rmsRACosDec": 5,
"rmsDec": 5,
"mag": 4,
"rmsMag": 4,
},
)
print(ades_text.splitlines()[0]) # "# version=2022"
Round-Trip Validation Pattern¶
import numpy as np
from adam_core.observations.ades import ADES_string_to_tables
parsed_contexts, parsed_observations = ADES_string_to_tables(ades_text)
# Sort before comparing because ADES is grouped by observatory block.
original = observations.sort_by(["stn", "obsTime.days", "obsTime.nanos"])
parsed = parsed_observations.sort_by(["stn", "obsTime.days", "obsTime.nanos"])
np.testing.assert_allclose(
original.obsTime.mjd().to_numpy(zero_copy_only=False),
parsed.obsTime.mjd().to_numpy(zero_copy_only=False),
)
Important ADES Behaviors¶
At least one of
permID,provID, ortrkSubmust be present for serialization.Unknown ADES columns are ignored on parse (with a warning).
Empty/whitespace optional fields are normalized to null values.
rmsRAin ADES maps tormsRACosDecinADESObservations.
When To Use¶
ingesting observations from ADES exchange files
generating ADES for MPC-style submission pipelines
reproducible round-trip QA between internal tables and ADES text