MeasurementScheduler#

class MeasurementScheduler(target: Spacecraft | Body = None, start: str | float | EpochArray = None, end: str | float | EpochArray = None, cadence: ArrayWUnits = None, elevation_mask: ArrayWUnits | float = 10.0, frame: Frame = J2000 (0 - SOLAR SYSTEM BARYCENTER), noisy: bool = False, output_dir: str = None, fmt: str = 'json', pass_gap: ArrayWUnits = None)#

Bases: object

Builds measurement models and their synthetic observations from tracking requests.

A request is an observer (a DSN station name such as "DSS-14", a GroundStation, a Camera or a Spacecraft), a measurement type and a tracking window: start, end and cadence. Every request carries its own window, so one scheduler holds, side by side, range from Goldstone every ten minutes over three days, Doppler at one minute over a single pass and OpNav images every hour over a week, and hands them all to a filter as one MeasurementSpec. Whatever is given to the constructor (target, window, elevation mask, …) is only the default for requests that do not set their own.

A window can recur, with a period of its own or on the UTC calendar:

  • pass_cadence / pass_duration (and an optional pass_offset), all ArrayWUnits times, keep slots of pass_duration starting every pass_cadence from start + pass_offset, at any period: an OpNav batch of 30 minutes every 6 hours is pass_cadence=ArrayWUnits(6, hr), pass_duration=ArrayWUnits(30, min), one image per orbit pass_cadence=orbit_period, pass_duration=cadence;

  • weekdays keeps only some days of the week;

  • hours keeps one or several UTC time-of-day ranges within each day, so two passes a day are hours="02:00-04:00, 14:00-16:00";

  • passes_per_day keeps the first n tracking passes that start on each UTC day.

They combine, so “one pass every Monday from today to next month” is schedule("DSS-14", "RangeIdeal", start=date.today(), end=..., cadence=ArrayWUnits(10, min), weekdays="Mon", passes_per_day=1).

For every request the scheduler

  1. builds the epoch grid [start, end) at the request’s cadence (only inside the slots and days / hours kept by the recurrence),

  2. keeps the epochs at which the observer can see the target (elevation above the mask for a ground station, target inside the field of view for a camera),

  3. builds the measurement model named by the measurement type,

  4. writes the synthetic observations to disk and reads them back,

  5. returns a MeasurementSpec ready for a filter.

The measurement type is the name of the model class that is built: "RangeIdeal", "RangeRateIdeal", "DopplerIdeal", "DiffOneWayRangeIdeal" (radiometric, from a ground station), "CentroidingIdeal" (from a camera) or "AngularIdeal" (from a spacecraft or station). Case and separators do not matter ("doppler_ideal" works) and the class itself can be passed. The short spellings "range", "range_rate", "doppler", "dor", "opnav", "angular", … (MEASUREMENT_TYPE_ALIASES) always mean the ideal model. The real TRK-2-34 models ("DopplerReal", "SequentialRangingReal") raise MeasurementScheduler.RealModelNotSupportedError: their observable is computed from per-epoch tracking data (ramp tables, count times, turnaround ratios, station delays) that cannot be synthesised yet.

Parameters:
targetSpacecraft or Body, optional

Default body being tracked or imaged. Its SPICE name must resolve over the requested windows (an SPK for it must be loaded). Requests can name their own target (the imaged body for OpNav). Defaults to None.

startstr, float or EpochArray, optional

Default first epoch of a request. Calendar strings are UTC and parsed with cal2et(); floats are TDB seconds past J2000; a datetime.date / datetime.datetime is UTC; an EpochArray is taken at its first epoch. Defaults to None.

endstr, float or EpochArray, optional

Default end of a request, same conventions as start (an EpochArray is taken at its last epoch). Grids are half open: end itself is not included. Defaults to None.

cadenceArrayWUnits, optional

Default spacing between consecutive measurements, a single ArrayWUnits time such as ArrayWUnits(10, min). Defaults to None.

elevation_maskArrayWUnits or float, optional

Default minimum elevation angle for ground-station observers, an ArrayWUnits angle such as ArrayWUnits(10, deg). A bare number is degrees, and one that could be radians is refused (see resolve_elevation_mask()). Defaults to 10.0 deg.

frameFrame, optional

Frame in which the measurements are computed. Defaults to J2000.

noisybool, optional

Whether the generated observations carry Gaussian noise drawn from each model’s sigma. Defaults to False.

output_dirstr, optional

Folder that receives the measurement files. None lets each model pick data/measurements/radiometric or data/measurements/optical from its file name. Defaults to None.

fmt{“json”, “pickle”}, optional

On-disk format of the measurement files. Defaults to "json".

pass_gapArrayWUnits, optional

Default gap between consecutive visible epochs that starts a new tracking pass, an ArrayWUnits time. None uses one and a half cadences of each request. Defaults to None.

Attributes:
cadence

Default spacing between consecutive measurements, or None.

edge_tolerance

Slack when deciding whether a grid point reaches the end of a request, a UTC midnight or an hour-of-day bound.

elevation_mask

Default minimum elevation angle for ground-station observers, in degrees.

end

Default end of a request (excluded from the grid), or None.

epochs

Default epoch grid (before any visibility check), or None.

frame

Frame in which the measurements are computed.

last

Most recently completed request, or None.

noisy

Whether generated observations carry noise.

output_dir

Folder receiving the measurement files (None = model default).

pass_gap

Default gap between visible epochs that starts a new pass (None = 1.5 cadences).

scheduled

Completed requests keyed by dataset name.

span

Earliest start and latest end over the completed requests, or None.

start

Default first epoch of a request, or None.

target

Default body being tracked or imaged (None = set per request).

window

Default window, or None when start, end or cadence is left to the requests.

Methods

build_epochs(start, end, cadence[, ...])

Build the half-open epoch grid [start, end) at a fixed cadence.

build_model(measurement_type, observer[, ...])

Instantiate the measurement model for a type and observer.

elevation(observer[, target, start, end, ...])

Elevation of the target above a ground station's horizon over a window's grid.

generate(target, observer, measurement_type, ...)

One-call form: build a scheduler and run a single request.

passes(observer[, elevation_mask, target, ...])

Return the visible epochs grouped into the tracking passes that would be scheduled.

resolve_measurement_type(measurement_type)

Map a measurement type to the class name of the model it builds.

resolve_observer(observer)

Turn a station name into a GroundStation; pass objects through.

resolve_window([start, end, cadence, ...])

Complete a request's window with the scheduler defaults and build its grid.

schedule(observer, measurement_type[, ...])

Build one measurement model and generate its observations.

schedule_many(requests[, skip_invisible])

Run a tracking plan of several requests and return one container spec.

split_passes(epochs, gap)

Split an epoch array into passes at gaps larger than gap.

visibility(observer[, elevation_mask, ...])

Boolean visibility mask of the target from an observer over a window's grid.

visibility_on(observer, target, epochs[, ...])

Boolean visibility mask of a target from an observer at given epochs.

visible_epochs(observer[, elevation_mask, ...])

Return the epochs of a window's grid at which the observer sees the target.

Raises:
MeasurementScheduler.InvalidScheduleError

If a default end is not after the default start, the default cadence is not positive, or the default elevation mask is not an angle between 0 and 90 deg.

See also

scarabaeus.MeasurementSpec

What schedule() returns.

scarabaeus.GroundStation.station_visibility

Elevation-based visibility.

scarabaeus.Camera.target_in_fov

Field-of-view visibility.

Examples

import scarabaeus as scb

km, sec, minute, hr, Hz = scb.Units.get_units(["km", "sec", "min", "hr", "Hz"])

# Defaults shared by the requests below; every request may override them
scheduler = scb.MeasurementScheduler(
    target=Orbiter, elevation_mask=15.0, noisy=True
)

# Range from Goldstone every 10 min over two days, Doppler from Madrid
# every minute over one pass, OpNav of the Moon every hour over a week:
# three windows, three cadences, one scheduler
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),
)
doppler_dss63 = scheduler.schedule(
    "DSS-63", "DopplerIdeal", sigma=scb.ArrayWUnits(5e-3, Hz),
    start="2029 APR 01 20:00:00", end="2029 APR 02 04:00:00",
    cadence=scb.ArrayWUnits(1, minute),
    antenna_name="HGA", doppler_transmit_frequency=scb.ArrayWUnits(7.2e9, Hz),
)
opnav = scheduler.schedule(
    camera, "CentroidingIdeal", target=scb.Body("MOON"),
    sigma=scb.ArrayWUnits([0.5, 0.5], None),
    start="2029 APR 01 00:00:00", end="2029 APR 08 00:00:00",
    cadence=scb.ArrayWUnits(1, hr),
)

# Recurring windows: one Goldstone pass every Monday for a month,
# Canberra twice a day, five OpNav images a minute apart every 6 h
weekly = scheduler.schedule(
    "DSS-14", "RangeIdeal", sigma=scb.ArrayWUnits(1e-3, km),
    start="2029 APR 01", end="2029 MAY 01", cadence=scb.ArrayWUnits(10, minute),
    weekdays="Mon", passes_per_day=1,
)
twice_daily = scheduler.schedule(
    "DSS-43", "RangeRateIdeal", sigma=scb.ArrayWUnits(1e-7, km / sec),
    start="2029 APR 01", end="2029 MAY 01", cadence=scb.ArrayWUnits(1, minute),
    hours="02:00-06:00, 14:00-18:00",
)
batches = scheduler.schedule(
    camera, "CentroidingIdeal", target=scb.Body("MOON"),
    sigma=scb.ArrayWUnits([0.5, 0.5], None),
    start="2029 APR 01", end="2029 APR 08", cadence=scb.ArrayWUnits(1, minute),
    pass_cadence=scb.ArrayWUnits(6, hr), pass_duration=scb.ArrayWUnits(5, minute),
)

# Or the whole plan at once
measurements = scheduler.schedule_many([
    ("DSS-14", "RangeIdeal", {"sigma": ..., "cadence": scb.ArrayWUnits(10, minute)}),
    ("DSS-63", "DopplerIdeal",
     {"sigma": ..., "cadence": scb.ArrayWUnits(1, minute), "antenna_name": "HGA", ...}),
    (camera, "CentroidingIdeal",
     {"target": scb.Body("MOON"), "sigma": ..., "cadence": scb.ArrayWUnits(1, hr)}),
], start="2029 APR 01 00:00:00", end="2029 APR 03 00:00:00")

# Everything a filter needs
measurements = scb.MeasurementSpec.many(range_dss14, doppler_dss63, opnav)
range_dss14.model                                  # the RangeIdeal object
scheduler.scheduled["DSS-14 RangeIdeal"].epochs    # the visible epochs
scheduler.scheduled["DSS-14 RangeIdeal"].window    # its start / end / cadence / grid
class ScheduledMeasurement(spec: MeasurementSpec, model: Measurement, observed_meas: tuple, measurement_type: str, observer: object, target: object, window: Window, epochs: EpochArray, visibility_mask: ndarray, passes: list, file_path: str)#

Bases: object

One completed request: the model, its observations and how they were made.

Attributes:
specMeasurementSpec

Filter-ready specification (model + observed_meas).

modelMeasurement

The measurement model built for this request.

observed_meastuple

Output of observed_measurements().

measurement_typestr

Class name of the model built ("RangeIdeal", "CentroidingIdeal", …).

observerGroundStation, Camera or Spacecraft

The instrument or body the measurements are taken from.

targetSpacecraft or Body

The tracked/imaged body.

windowMeasurementScheduler.Window

Start, end, cadence, recurrence and full epoch grid of this request.

epochsEpochArray

Epochs kept after the visibility check (those in the file).

visibility_masknumpy.ndarray

Boolean mask over window.epochs: True where the target is visible and the epoch lies inside a kept pass (passes_per_day may drop visible passes).

passeslist of EpochArray

epochs grouped into tracking passes.

file_pathstr

Measurement file on disk.

property cadence: ArrayWUnits#

Time step of the epoch grid.

property dataset_name: str#

Dataset name of the request.

property end: EpochArray#

Last epoch of the window.

epochs: EpochArray#
file_path: str#
property grid: EpochArray#

Full epoch grid of the request (before the visibility check).

measurement_type: str#
model: Measurement#
observed_meas: tuple#
observer: object#
passes: list#
spec: MeasurementSpec#
property start: EpochArray#

First epoch of the window.

target: object#
visibility_mask: ndarray#
window: Window#
class Window(start_et: float, end_et: float, cadence_sec: float, pass_gap_sec: float, weekdays: tuple = None, hours: tuple = None, passes_per_day: int = None, pass_cadence_sec: float = None, pass_duration_sec: float = None, pass_offset_sec: float = 0.0, edge_tolerance_sec: float = None)#

Bases: object

Time span and cadence of one request, with its epoch grid.

Attributes:
start_etfloat

First epoch, TDB seconds past J2000.

end_etfloat

End of the span (excluded from the grid), TDB seconds past J2000.

cadence_secfloat

Spacing between consecutive epochs [s].

pass_gap_secfloat

Gap between visible epochs that starts a new pass [s].

weekdaystuple of int or None

Days of the week kept in the grid, 0 = Monday … 6 = Sunday (UTC calendar). None keeps every day.

hourstuple of (float, float) or None

UTC time-of-day ranges (from, to) in hours kept within each day (their union); to < from wraps around midnight. None keeps the whole day.

passes_per_dayint or None

Only the first passes_per_day tracking passes starting on each UTC day are scheduled. None keeps them all.

pass_cadence_secfloat or None

Time [s] between the starts of two tracking slots. None means one continuous slot [start, end).

pass_duration_secfloat or None

Length [s] of each slot, at most pass_cadence_sec.

pass_offset_secfloat

Delay [s] of the first slot after start_et.

edge_tolerance_secfloat or None

Slack [s] at the end of the span, at UTC midnights and at the hours bounds. None takes MeasurementScheduler.EDGE_TOLERANCE.

epochsEpochArray

The half-open grid [start, end) at cadence_sec, TDB, restricted to the slots, weekdays and hours.

Methods

day_index(et_values)

Return the index of the UTC day of each epoch.

select(mask)

Turn a visibility mask over epochs into the scheduled passes.

Raises:
MeasurementScheduler.InvalidScheduleError

If the recurrence leaves no epoch in [start, end).

day_index(et_values) → ndarray#

Return the index of the UTC day of each epoch.

Parameters:
et_valuesfloat or array_like of float

Epochs, in TDB seconds past J2000.

Returns:
numpy.ndarray of int

Index of the UTC day of each epoch, with 0 the day of start. An epoch within edge_tolerance_sec of a midnight belongs to the new day.

select(mask: ndarray) → tuple#

Turn a visibility mask over epochs into the scheduled passes.

Visible epochs are split into passes at gaps larger than pass_gap_sec and, when passes_per_day is set, only the first passes starting on each UTC day are kept.

Parameters:
masknumpy.ndarray of bool

Visibility of the target at each epoch of epochs.

Returns:
tuple

(mask, passes): the mask of the epochs actually scheduled and the list of passes (EpochArray each), in time order.

property cadence: ArrayWUnits#

Spacing between consecutive epochs.

cadence_sec: float#
edge_tolerance_sec: float = None#
property end: EpochArray#

End of the window (excluded from the grid).

end_et: float#
epochs: EpochArray#
hours: tuple = None#
pass_cadence_sec: float = None#
pass_duration_sec: float = None#
property pass_gap: ArrayWUnits#

Gap between visible epochs that starts a new pass.

pass_gap_sec: float#
pass_offset_sec: float = 0.0#
passes_per_day: int = None#
property recurrence: str#

Human-readable recurrence, empty if none.

E.g. "30min every 6h, on Mon, 02:00-04:00 & 14:00-16:00 h UTC, 1 pass(es)/day".

property size: int#

Number of epochs in the grid.

property start: EpochArray#

First epoch of the window.

start_et: float#
weekdays: tuple = None#
classmethod build_model(measurement_type: str | type, observer, name: str = None, sigma: ArrayWUnits = None, **model_kwargs) → Measurement#

Instantiate the measurement model for a type and observer.

Parameters:
measurement_typestr or type

Model class name, class or alias accepted by resolve_measurement_type().

observerstr, GroundStation, Camera or Spacecraft

Station name or observer object.

namestr, optional

Model name. Defaults to "<observer> <class name>", e.g. "DSS-14 RangeIdeal".

sigmaArrayWUnits, optional

Measurement standard deviation passed to the model.

**model_kwargs

Extra constructor arguments of the model class (meas_bias, state_definition, sequence_definition, antenna_name, doppler_transmit_frequency, ground_station_2, observed_target, …).

Returns:
Measurement

The model instance.

Raises:
MeasurementScheduler.RealModelNotSupportedError

If the type names a Real model.

MeasurementScheduler.UnknownMeasurementTypeError

If the type is not registered.

MeasurementScheduler.ObserverTypeError

If the observer does not fit the measurement type (a station for centroiding, a camera for range, …).

MeasurementScheduler.UnsupportedModelArgumentError

If a keyword in model_kwargs is not accepted by the class.

classmethod generate(target: Spacecraft | Body, observer, measurement_type: str | type, start: str | float | EpochArray, end: str | float | EpochArray, cadence: ArrayWUnits, elevation_mask: ArrayWUnits | float = 10.0, sigma: ArrayWUnits = None, frame: Frame = J2000 (0 - SOLAR SYSTEM BARYCENTER), noisy: bool = False, output_dir: str = None, fmt: str = 'json', **schedule_kwargs) → MeasurementSpec#

One-call form: build a scheduler and run a single request.

Parameters:
targetSpacecraft or Body

Body being tracked or imaged.

observerstr, GroundStation, Camera or Spacecraft

Station name or observer object.

measurement_typestr or type

Model class name ("RangeIdeal", …), class or alias (see schedule()).

start, endstr, float or EpochArray

Window bounds (see the class docstring).

cadenceArrayWUnits

Spacing between measurements, e.g. ArrayWUnits(10, min).

elevation_maskArrayWUnits or float, optional

Minimum elevation, an ArrayWUnits angle or a bare number of degrees. Defaults to 10.0 deg.

sigmaArrayWUnits, optional

Measurement standard deviation.

frame, noisy, output_dir, fmt

Forwarded to the constructor.

**schedule_kwargs

Forwarded to schedule() (name, dataset_name, file_name, units, hours, pass_cadence/pass_duration, model keywords, …).

Returns:
MeasurementSpec
classmethod resolve_measurement_type(measurement_type: str | type) → str#

Map a measurement type to the class name of the model it builds.

The explicit form is the model class name ("RangeIdeal", "DopplerIdeal", …) or the class itself. The short aliases of MEASUREMENT_TYPE_ALIASES ("range", "doppler", "opnav", …) always mean the Ideal model. Case, spaces, - and _ are ignored.

Parameters:
measurement_typestr or type

E.g. "DopplerIdeal", DopplerIdeal, "range", "Range-Rate", "opnav", "RA_DEC".

Returns:
str

One of MODEL_CLASSES’ keys.

Raises:
MeasurementScheduler.RealModelNotSupportedError

If the type names a Real model ("DopplerReal", "SequentialRangingReal").

MeasurementScheduler.UnknownMeasurementTypeError

If the spelling is not registered.

static build_epochs(start: EpochArray | float, end: EpochArray | float, cadence: ArrayWUnits | float, pass_cadence: ArrayWUnits | float = None, pass_duration: ArrayWUnits | float = None, pass_offset: ArrayWUnits | float = 0.0, edge_tolerance: ArrayWUnits | float = None) → EpochArray#

Build the half-open epoch grid [start, end) at a fixed cadence.

With pass_cadence the grid is cut into slots [s_k, s_k + pass_duration), s_k = start + pass_offset + k * pass_cadence, each sampled from its own start at the cadence (so pass_cadence need not be a multiple of the cadence).

Parameters:
startEpochArray or float

First epoch: an EpochArray (its first epoch) or TDB seconds past J2000. Calendar strings and dates work as for the scheduler’s start.

endEpochArray or float

End of the grid (excluded), same conventions as start; an EpochArray is taken at its last epoch.

cadenceArrayWUnits or float

Spacing between epochs, an ArrayWUnits time such as ArrayWUnits(1, min). A bare number is seconds.

pass_cadenceArrayWUnits or float, optional

Time between the starts of two slots (bare number: seconds). None is one slot over the whole span.

pass_durationArrayWUnits or float, optional

Length of each slot (bare number: seconds); required with pass_cadence.

pass_offsetArrayWUnits or float, optional

Delay of the first slot after start (bare number: seconds). Defaults to 0.

edge_toleranceArrayWUnits or float, optional

Slack at end and at the slot ends (bare number: seconds): a grid point this close to them counts as reaching them. Defaults to EDGE_TOLERANCE.

Returns:
EpochArray

The epochs, in TDB (possibly empty when pass_offset passes end).

Raises:
MeasurementScheduler.InvalidScheduleError

If end is not after start, a bound is not an epoch, or a span is not a single positive time (pass_offset and edge_tolerance may be zero; see _to_seconds()), including a pass_cadence given without pass_duration.

static resolve_observer(observer: str | GroundStation | Camera | Spacecraft)#

Turn a station name into a GroundStation; pass objects through.

Parameters:
observerstr, GroundStation, Camera or Spacecraft

A DSN station name such as "DSS-14" or an already-built observer object.

Returns:
GroundStation, Camera or Spacecraft
static split_passes(epochs: EpochArray, gap: ArrayWUnits | float) → list#

Split an epoch array into passes at gaps larger than gap.

Parameters:
epochsEpochArray

Epochs in time order.

gapArrayWUnits or float

Time between consecutive epochs beyond which a new pass starts, an ArrayWUnits time such as ArrayWUnits(15, min). A bare number is seconds.

Returns:
list of EpochArray

One entry per pass, in TDB.

Raises:
MeasurementScheduler.InvalidScheduleError

If gap is not a single positive time span.

static visibility_on(observer, target: Spacecraft | Body, epochs: EpochArray, elevation_mask: ArrayWUnits | float = 10.0) → ndarray#

Boolean visibility mask of a target from an observer at given epochs.

Parameters:
observerstr, GroundStation, Camera or Spacecraft

Ground stations use their elevation test, cameras their FOV projection. Any other observer sees the target at every epoch.

targetSpacecraft or Body

Tracked or imaged body.

epochsEpochArray

Epochs to test.

elevation_maskArrayWUnits or float, optional

Minimum elevation for ground stations, an ArrayWUnits angle or a bare number of degrees. Defaults to 10.0 deg.

Returns:
numpy.ndarray of bool

One flag per epoch.

elevation(observer, target: Spacecraft | Body = None, start: str | float | EpochArray = None, end: str | float | EpochArray = None, cadence: ArrayWUnits = None, weekdays=None, hours=None, pass_cadence: ArrayWUnits = None, pass_duration: ArrayWUnits = None, pass_offset: ArrayWUnits = None) → ndarray#

Elevation of the target above a ground station’s horizon over a window’s grid.

Parameters:
observerstr or GroundStation

Station name or ground station.

targetSpacecraft or Body, optional

Overrides the scheduler target.

start, end, cadence, weekdays, hours, pass_cadence, pass_duration, pass_offsetoptional

Window of the request; None takes the scheduler defaults (see resolve_window()).

Returns:
numpy.ndarray

Elevation in degrees, one value per epoch of the window’s grid.

Raises:
MeasurementScheduler.ObserverTypeError

If the observer is not a ground station.

passes(observer, elevation_mask: ArrayWUnits | float = None, target: Spacecraft | Body = None, start: str | float | EpochArray = None, end: str | float | EpochArray = None, cadence: ArrayWUnits = None, pass_gap: ArrayWUnits = None, weekdays=None, hours=None, passes_per_day: int = None, pass_cadence: ArrayWUnits = None, pass_duration: ArrayWUnits = None, pass_offset: ArrayWUnits = None) → list#

Return the visible epochs grouped into the tracking passes that would be scheduled.

A new pass starts whenever two consecutive visible epochs are more than the window’s pass gap apart. With pass_cadence and pass_duration each slot is its own pass, unless the pass gap bridges consecutive slots.

Parameters:
observerstr, GroundStation, Camera or Spacecraft

Ground stations use their elevation test, cameras their FOV projection. Any other observer sees the target at every epoch.

elevation_maskArrayWUnits or float, optional

Minimum elevation, an ArrayWUnits angle or a bare number of degrees; None uses the scheduler default.

targetSpacecraft or Body, optional

Overrides the scheduler target (e.g. the imaged body for OpNav).

start, end, cadence, pass_gap, weekdays, hours, passes_per_day, pass_cadence, pass_duration, pass_offsetoptional

Window of the request; None takes the scheduler defaults (see resolve_window()). passes_per_day keeps the first passes of each UTC day.

Returns:
list of EpochArray

One entry per pass, in time order.

resolve_window(start: str | float | EpochArray = None, end: str | float | EpochArray = None, cadence: ArrayWUnits = None, pass_gap: ArrayWUnits = None, weekdays=None, hours=None, passes_per_day: int = None, pass_cadence: ArrayWUnits = None, pass_duration: ArrayWUnits = None, pass_offset: ArrayWUnits = None) → Window#

Complete a request’s window with the scheduler defaults and build its grid.

Every time span (cadence, pass_gap, pass_cadence, pass_duration, pass_offset) is a single ArrayWUnits time, such as ArrayWUnits(10, min).

Parameters:
start, endstr, float or EpochArray, optional

Bounds of the request (class docstring conventions). None takes the scheduler default.

cadenceArrayWUnits, optional

Spacing between epochs. None takes the scheduler default.

pass_gapArrayWUnits, optional

Gap that separates two passes. None takes the scheduler default, itself None meaning one and a half cadences.

weekdaysstr, int or iterable, optional

Days of the week to keep: names ("Mon", "Mon,Thu", ["Mon", "Fri"]), "weekdays" / "weekends" or 0 (Monday) .. 6 (Sunday). None keeps every day.

hoursstr, tuple or list, optional

UTC time-of-day ranges kept within each day: one range "08:00-12:00" or (8, 12), several "02:00-04:00, 14:00-16:00" or [(2, 4), (14, 16)]; "22:00-04:00" wraps midnight. None keeps the whole day.

passes_per_dayint, optional

Keep only the first passes_per_day tracking passes starting on each UTC day. None keeps them all.

pass_cadenceArrayWUnits, optional

Time between the starts of two tracking slots, counted in elapsed time from start (not tied to the UTC day: use hours for that). Needs pass_duration. None tracks over the whole window.

pass_durationArrayWUnits, optional

Length of each slot, at most pass_cadence. Each slot holds the epochs slot start + k * cadence before its end, so a pass_duration of one cadence (or less) is one epoch per slot.

pass_offsetArrayWUnits, optional

Delay of the first slot after start. Defaults to 0.

Returns:
MeasurementScheduler.Window
Raises:
MeasurementScheduler.InvalidScheduleError

If a bound or the cadence is set neither here nor on the scheduler, end is not after start, a recurrence argument is malformed (pass_cadence without pass_duration or the reverse, a time span that is not an ArrayWUnits time, pass_duration longer than pass_cadence, …) or the recurrence leaves no epoch.

schedule(observer, measurement_type: str | type, sigma: ArrayWUnits = None, start: str | float | EpochArray = None, end: str | float | EpochArray = None, cadence: ArrayWUnits = None, elevation_mask: ArrayWUnits | float = None, target: Spacecraft | Body = None, name: str = None, dataset_name: str = None, file_name: str = None, units: Units = None, noisy: bool = None, per_pass_bias: bool = False, pass_gap: ArrayWUnits = None, weekdays=None, hours=None, passes_per_day: int = None, pass_cadence: ArrayWUnits = None, pass_duration: ArrayWUnits = None, pass_offset: ArrayWUnits = None, **model_kwargs) → MeasurementSpec#

Build one measurement model and generate its observations.

Parameters:
observerstr, GroundStation, Camera or Spacecraft

Station name ("DSS-14"), ground station, camera (for "CentroidingIdeal") or observing body (for "AngularIdeal").

measurement_typestr or type

Class name of the model to build: "RangeIdeal", "RangeRateIdeal", "DopplerIdeal", "DiffOneWayRangeIdeal", "CentroidingIdeal" or "AngularIdeal" (or the class itself). The short aliases of MEASUREMENT_TYPE_ALIASES ("range", "doppler", "opnav", …) always mean the Ideal model. Real models ("DopplerReal", "SequentialRangingReal") are refused.

sigmaArrayWUnits, optional

Measurement standard deviation handed to the model.

start, endstr, float or EpochArray, optional

Window of this request (class docstring conventions). None takes the scheduler default.

cadenceArrayWUnits, optional

Spacing between this request’s measurements, e.g. ArrayWUnits(1, min). None takes the scheduler default.

elevation_maskArrayWUnits or float, optional

Minimum elevation for this request only, an ArrayWUnits angle or a bare number of degrees.

targetSpacecraft or Body, optional

Overrides the scheduler target (the imaged body for OpNav).

namestr, optional

Model name. Defaults to "<observer> <class name>", e.g. "DSS-14 RangeIdeal".

dataset_namestr, optional

Dataset label on the returned spec and key in scheduled. Defaults to the model name.

file_namestr, optional

Measurement file name without extension. Defaults to a slug of the dataset name; optical types get an opnav_ prefix so the model files them under data/measurements/optical.

unitsUnits, optional

Units the file is read back with. Defaults to OBSERVATION_UNITS for the type.

noisybool, optional

Overrides the scheduler’s noisy flag for this request.

per_pass_biasbool, optional

When True, the visible passes are registered on the model with set_pass_epochs() so a per-pass bias can be estimated. Defaults to False.

pass_gapArrayWUnits, optional

Gap between visible epochs that starts a new pass for this request. None takes the scheduler default (1.5 cadences).

weekdaysstr, int or iterable, optional

Days of the week to track on ("Mon", "Mon,Thu", "weekdays", 0-6). Defaults to every day.

hoursstr, tuple or list, optional

UTC time-of-day ranges to track in: "08:00-12:00", (8, 12), or several such as "02:00-04:00, 14:00-16:00" or [(2, 4), (14, 16)]. Defaults to the whole day.

passes_per_dayint, optional

Keep only the first passes_per_day passes starting on each UTC day (“one pass a day”). Defaults to all of them.

pass_cadenceArrayWUnits, optional

Time between the starts of two tracking slots, in elapsed time from start (ArrayWUnits(6, hr), ArrayWUnits(90, min)), for schedules not tied to the UTC day such as OpNav images. Needs pass_duration. Defaults to tracking over the whole window.

pass_durationArrayWUnits, optional

Length of each slot, at most pass_cadence; a slot holds the epochs slot start + k * cadence before its end (one epoch when pass_duration is one cadence or less).

pass_offsetArrayWUnits, optional

Delay of the first slot after start. Defaults to 0.

**model_kwargs

Extra constructor arguments of the model class (meas_bias, antenna_name, doppler_transmit_frequency, ground_station_2, observed_target, …).

Returns:
MeasurementSpec

Leaf spec with the model and the observations read from disk. The full record (window, epochs, mask, passes, file path) is stored in scheduled under dataset_name.

Raises:
MeasurementScheduler.DuplicateDatasetError

If dataset_name is given and already in scheduled (a default name is suffixed (2), (3), … instead).

MeasurementScheduler.RealModelNotSupportedError

If measurement_type names a Real model.

MeasurementScheduler.UnknownMeasurementTypeError

If measurement_type is not registered.

MeasurementScheduler.InvalidScheduleError

If the request has no start, end or cadence (neither given here nor as a scheduler default), its recurrence is malformed or leaves no epoch, or elevation_mask is not an angle between 0 and 90 deg.

MeasurementScheduler.MissingTargetError

If the request has no target.

MeasurementScheduler.MissingSigmaError

If noisy observations are requested without a sigma.

MeasurementScheduler.NoVisibleEpochsError

If the observer never sees the target over the window.

MeasurementScheduler.MeasurementFileNotWrittenError

If the model wrote no file.

schedule_many(requests, skip_invisible: bool = True, **common) → MeasurementSpec#

Run a tracking plan of several requests and return one container spec.

Requests of different kinds (stations and cameras, radiometric and optical types) with different windows and cadences go in the same plan.

Parameters:
requestsiterable of dict or tuple

Each entry is the keyword dictionary of one schedule() call, or a (observer, measurement_type) pair optionally followed by a keyword dictionary, e.g. ("DSS-14", "RangeIdeal", {"sigma": s, "cadence": ArrayWUnits(10, min)}).

skip_invisiblebool, optional

When True a request whose observer never sees the target is skipped with a RuntimeWarning instead of stopping the plan. Defaults to True.

**common

schedule() keywords applied to every request of the plan that does not set them itself (a shared start/end, an elevation_mask, …).

Returns:
MeasurementSpec

Container built with many() over the completed requests, in plan order.

visibility(observer, elevation_mask: ArrayWUnits | float = None, target: Spacecraft | Body = None, start: str | float | EpochArray = None, end: str | float | EpochArray = None, cadence: ArrayWUnits = None, weekdays=None, hours=None, pass_cadence: ArrayWUnits = None, pass_duration: ArrayWUnits = None, pass_offset: ArrayWUnits = None) → ndarray#

Boolean visibility mask of the target from an observer over a window’s grid.

Parameters:
observerstr, GroundStation, Camera or Spacecraft

Ground stations use their elevation test, cameras their FOV projection. Any other observer sees the target at every epoch.

elevation_maskArrayWUnits or float, optional

Minimum elevation, an ArrayWUnits angle or a bare number of degrees; None uses the scheduler default.

targetSpacecraft or Body, optional

Overrides the scheduler target (e.g. the imaged body for OpNav).

start, end, cadence, weekdays, hours, pass_cadence, pass_duration, pass_offsetoptional

Window of the request; None takes the scheduler defaults (see resolve_window()).

Returns:
numpy.ndarray of bool

One flag per epoch of the window’s grid.

visible_epochs(observer, elevation_mask: ArrayWUnits | float = None, target: Spacecraft | Body = None, start: str | float | EpochArray = None, end: str | float | EpochArray = None, cadence: ArrayWUnits = None, weekdays=None, hours=None, pass_cadence: ArrayWUnits = None, pass_duration: ArrayWUnits = None, pass_offset: ArrayWUnits = None) → EpochArray#

Return the epochs of a window’s grid at which the observer sees the target.

Parameters:
observerstr, GroundStation, Camera or Spacecraft

Ground stations use their elevation test, cameras their FOV projection. Any other observer sees the target at every epoch.

elevation_maskArrayWUnits or float, optional

Minimum elevation, an ArrayWUnits angle or a bare number of degrees; None uses the scheduler default.

targetSpacecraft or Body, optional

Overrides the scheduler target (e.g. the imaged body for OpNav).

start, end, cadence, weekdays, hours, pass_cadence, pass_duration, pass_offsetoptional

Window of the request; None takes the scheduler defaults (see resolve_window()).

Returns:
EpochArray

The visible epochs.

EDGE_TOLERANCE = 0.01 sec#

Default slack when deciding whether a scheduled grid point reaches the end of the schedule, a UTC midnight or an hour-of-day bound; the starting edge_tolerance of a scheduler.

A grid at a fixed TDB cadence drifts against the UTC calendar by the TDB-UTC periodic terms (< 2 ms), on top of the ET float rounding.

MEASUREMENT_TYPE_ALIASES = {'angles': 'AngularIdeal', 'angular': 'AngularIdeal', 'centroid': 'CentroidingIdeal', 'centroiding': 'CentroidingIdeal', 'deltador': 'DiffOneWayRangeIdeal', 'diffonewayrange': 'DiffOneWayRangeIdeal', 'doppler': 'DopplerIdeal', 'dor': 'DiffOneWayRangeIdeal', 'opnav': 'CentroidingIdeal', 'optical': 'CentroidingIdeal', 'radec': 'AngularIdeal', 'range': 'RangeIdeal', 'rangerate': 'RangeRateIdeal', 'rng': 'RangeIdeal', 'rr': 'RangeRateIdeal'}#
MODEL_CLASSES = {'AngularIdeal': <class 'scarabaeus.measurements.AngularIdeal.AngularIdeal'>, 'CentroidingIdeal': <class 'scarabaeus.measurements.CentroidingIdeal.CentroidingIdeal'>, 'DiffOneWayRangeIdeal': <class 'scarabaeus.measurements.DiffOneWayRangeIdeal.DiffOneWayRangeIdeal'>, 'DopplerIdeal': <class 'scarabaeus.measurements.DopplerIdeal.DopplerIdeal'>, 'RangeIdeal': <class 'scarabaeus.measurements.RangeIdeal.RangeIdeal'>, 'RangeRateIdeal': <class 'scarabaeus.measurements.RangeRateIdeal.RangeRateIdeal'>}#
OBSERVATION_UNITS = {'AngularIdeal': rad, 'CentroidingIdeal': None, 'DiffOneWayRangeIdeal': sec, 'DopplerIdeal': Hz, 'RangeIdeal': km, 'RangeRateIdeal': km/sec}#
OPTICAL_TYPES = frozenset({'AngularIdeal', 'CentroidingIdeal'})#
RADIOMETRIC_TYPES = frozenset({'DiffOneWayRangeIdeal', 'DopplerIdeal', 'RangeIdeal', 'RangeRateIdeal'})#
REAL_MODEL_ALIASES = {'seqrange': 'SequentialRangingReal', 'seqranging': 'SequentialRangingReal', 'sequentialranging': 'SequentialRangingReal'}#
REAL_MODEL_SUBSTITUTES = {'DopplerReal': 'DopplerIdeal', 'SequentialRangingReal': 'RangeIdeal'}#
WEEKDAY_NAMES = ('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun')#

Weekday spellings accepted by weekdays (case-insensitive prefixes work too).

property cadence: ArrayWUnits | None#

Default spacing between consecutive measurements, or None.

property edge_tolerance: ArrayWUnits#

Slack when deciding whether a grid point reaches the end of a request, a UTC midnight or an hour-of-day bound. Starts as EDGE_TOLERANCE.

Set a single, non-negative ArrayWUnits time. The default window, if any, is rebuilt with it; requests already scheduled keep the tolerance they were built with.

Raises:
InvalidScheduleError

If the value is not a single non-negative time span (see _to_seconds()).

property elevation_mask: ArrayWUnits#

Default minimum elevation angle for ground-station observers, in degrees.

property end: EpochArray | None#

Default end of a request (excluded from the grid), or None.

property epochs: EpochArray | None#

Default epoch grid (before any visibility check), or None.

property frame: Frame#

Frame in which the measurements are computed.

property last: ScheduledMeasurement | None#

Most recently completed request, or None.

property noisy: bool#

Whether generated observations carry noise.

property output_dir: str | None#

Folder receiving the measurement files (None = model default).

property pass_gap: ArrayWUnits | None#

Default gap between visible epochs that starts a new pass (None = 1.5 cadences).

property scheduled: dict#

Completed requests keyed by dataset name.

property span: tuple | None#

Earliest start and latest end over the completed requests, or None.

property start: EpochArray | None#

Default first epoch of a request, or None.

property target: Spacecraft | Body | None#

Default body being tracked or imaged (None = set per request).

property window: Window | None#

Default window, or None when start, end or cadence is left to the requests.

exception DuplicateDatasetError#

Bases: ValueError

Raised when an explicit dataset_name is already scheduled.

exception InvalidScheduleError#

Bases: ValueError

Raised when the start, end or cadence of a request do not define a usable grid.

exception MeasurementFileNotWrittenError#

Bases: RuntimeError

Raised when the measurement model produced no file to read back.

exception MissingTargetError#

Bases: ValueError

Raised when neither the request nor the scheduler names a target.

exception NoVisibleEpochsError#

Bases: RuntimeError

Raised when the observer never sees the target over the request’s window.

exception ObserverTypeError#

Bases: TypeError

Raised when the observer cannot be used for the requested measurement type.

exception RealModelNotSupportedError#

Bases: NotImplementedError

Raised when a real (TRK-2-34) model is requested: its tracking data cannot be synthesised yet.

exception UnknownMeasurementTypeError#

Bases: ValueError

Raised when the measurement type string is not one the scheduler knows.

exception UnsupportedModelArgumentError#

Bases: TypeError

Raised when a keyword argument is not accepted by the measurement model class.