Measurement Framework#

Last revised by G. Fereoli on 2026 OCT 11

A measurement model predicts what an instrument sees from a reference trajectory. The instrument and the model are separate objects: a GroundStation, Camera or spacecraft observes, and the model carries the physics. Every model gives the filter the same three things: the observations \(O\), the computed values \(C\), and the partials \(\tilde{H} = \partial h/\partial X\).

Models#

All models derive from Measurement.

Model

Instrument

Observable

RangeIdeal, RangeRateIdeal

Ground station

Geometric range and range rate

DopplerIdeal

Ground station

Doppler shift, two- or three-way (ground_station_2)

DiffOneWayRangeIdeal

Two ground stations

Difference of the one-way ranges

AngularIdeal

Spacecraft or body

Right ascension and declination of a target

SequentialRangingReal, DopplerReal

Ground station (SPK)

DSN tracking data in range units and Hz

CentroidingIdeal

Camera

Image centroid of the target (two components)

CustomMeasurement

Any

Your own \(h(\mathbf r, \mathbf v, p, t)\), with CustomParameter objects

The ideal models are instantaneous geometry and are meant for simulation. The real models reproduce the DSN observables (Moyer, 2000): light time solved leg by leg, ramped frequencies (RampTableManager), relativistic delay (rlt_bodies), solar corona, electronic delays and media corrections.

Simulate, read, compute#

gs = scb.GroundStation("DSS-14")
rng = scb.RangeIdeal(name="GS1 Range", instrument=gs, sigma=scb.ArrayWUnits(1e-3, km))

# simulate on the true trajectory, then read back as observations
rng.write_observed_measurements(target=truth, epoch_array=epochs, noisy=True,
                                file_name="meas_range", folder_path_override="data")
obs = rng.observed_measurements(file_name="data/meas_range.json",
                                meas_name="meas_ideal", units=km)

# computed values, partials and residuals on the reference
_, _, _, comp = rng.computed_measurements(target=ref, epoch_array=epochs, frame=J2000)
H = np.array(rng.compute_partials(ref, epochs, J2000))
res = rng.residuals(obs.meas_obs, comp)

observed_measurements returns an ObsData: (meas_time_et, meas_sec, meas_obs, meas_outliers, aux_dict). The filter never calls these methods directly. It receives a MeasurementSpec and rebuilds the MeasurementDataSet objects on every iteration:

meas = scb.MeasurementSpec.many(
    scb.MeasurementSpec(model=rng, observed_meas=obs, dataset_name="GS1 Range"),
    scb.MeasurementSpec(model=dop, observed_meas=obs_dop, dataset_name="GS1 Doppler"),
)

For a covariance analysis, give epochs= instead of observed_meas.

Tracking schedules#

MeasurementScheduler builds the models and their simulated observations from tracking requests, keeping only the epochs where the target is above the elevation mask:

scheduler = scb.MeasurementScheduler(target=truth, elevation_mask=15.0, noisy=True)
range_dss14 = scheduler.schedule(
    "DSS-14", "RangeIdeal", sigma=scb.ArrayWUnits(1e-3, km),
    start="2029 APR 01 00:00:00", end="2029 APR 03 00:00:00",
    cadence=scb.ArrayWUnits(10, minute), hours="02:00-06:00")
meas = scb.MeasurementSpec.many(range_dss14, doppler_dss63, opnav)

Recurring windows are set with weekdays, hours, passes_per_day, pass_cadence and pass_duration; schedule_many takes several requests at once. Real models cannot be simulated and raise RealModelNotSupportedError.

Real tracking data#

MeasurementSpec.build reads real tracking files directly. Each stream is a (file, type, station) tuple:

cfg = scb.MeasurementConfig(
    media_folder="data/media", tropo_seasonal_file_path=tropo_file,
    sigma={"SeqRange": scb.ArrayWUnits(10.0, km / km),
           "Doppler": scb.ArrayWUnits(1e-2, 1 / sec)})
meas = scb.MeasurementSpec.build([(trk_file, "SeqRange", dss25),
                                  (trk_file, "Doppler", dss25)], config=cfg)
  • MeasurementRegistry maps the type names ("SeqRange", "Doppler", "IdealRange", "IdealRangeRate", "Centroid") to a model class, a data block and units. register adds your own.

  • MeasurementConfig holds what streams share: sigmas and biases per type or dataset, media folders, sc_transponder_delay_RU and extra model keywords. A MeasurementStream overrides any of them.

  • MediaCorrections (tropospheric and ionospheric delays) is attached for each pass date. A correction file that is missing or of the wrong type raises instead of being skipped.

  • The light time is barycentric by default; light_time_frame="geocentric" is available for spacecraft near the Earth. light_time_settings sets its tolerance and iterations.

Biases and parameters#

  • meas_bias sets a fixed bias. One value applies to every pass; several give one per pass, grouped by set_pass_epochs.

  • To estimate a bias, add it to the state instead (range_bias, range_bias_RU, rangerate_bias_Hz). A state component overrides meas_bias.

  • Station, Earth orientation, tide, IAU pole and zenith-delay parameters are covered in Stations and Body Orientation.

Partials are analytical for every model. partials_method="fd" switches a model to finite differences, with steps from eps_dict.

See also#

  • Estimation Framework: how the filters use \(O - C\) and \(\tilde{H}\).

  • The Measurement Simulation, Ideal MSR and Real MSR tutorials in Examples.