Plan Serialisation

COASTSim can save an observation plan produced by a DITL run to a portable JSON file and reload it later. The serialisation layer is built on Pydantic v2 and lives in conops.targets.plan_schema. Convenience methods are also available directly on Plan so you rarely need to import the schema classes explicitly.

Overview

Two Pydantic models handle the conversion:

  • PlanEntrySchema — represents a single observation entry (a PlanEntry or Pointing).

  • PlanSchema — top-level container that bundles metadata (version, timestamps, entry count) with the list of entries.

Both models support model_validate(..., from_attributes=True), so they accept plain Python objects produced by the scheduler without any intermediate conversion step.

Quick Start

Save a plan after a DITL run

The simplest approach is to call save() directly on the plan:

# `ditl` is a QueueDITL (or similar) instance that has already been run
saved_path = ditl.plan.save("plan_20251201.json")
print(f"Saved to {saved_path}")

You can also go via PlanSchema if you need access to the metadata fields before writing:

from conops.targets import PlanSchema

schema = PlanSchema.from_plan(ditl.plan)
print(f"Saving {schema.num_entries} entries (version {schema.version})")
schema.save("plan_20251201.json")

Load it back

load() is a class method on Plan that returns a PlanSchema (preserving all metadata):

schema = Plan.load("plan_20251201.json")
print(schema.version)          # schema format version (integer)
print(schema.num_entries)      # number of plan entries
print(schema.entries[0].name)  # first target name

Or equivalently via PlanSchema directly:

from conops.targets import PlanSchema

schema = PlanSchema.load("plan_20251201.json")

Round-trip via model_validate

schema = PlanSchema.model_validate(ditl.plan, from_attributes=True)

JSON File Format

The JSON file contains a metadata envelope followed by the entry list.

{
  "version": 3,
  "coast_sim_version": "0.1.3",
  "created_at": "2025-12-01T00:00:00+00:00",
  "start": "2025-12-01T00:00:00+00:00",
  "end": "2025-12-01T23:59:00+00:00",
  "num_entries": 42,
  "entries": [
    {
      "name": "TEST_001",
      "ra": 83.82,
      "dec": -5.39,
      "roll": 0.0,
      "begin": "2025-12-01T00:00:00+00:00",
      "end": "2025-12-01T00:16:40+00:00",
      "merit": 95.0,
      "slewtime": 120,
      "insaa": 0,
      "obsid": 1001,
      "obstype": "AT",
      "slewdist": 10.3,
      "ss_min": 300.0,
      "ss_max": 1000000.0,
      "exptime": 880,
      "exporig": 1000,
      "isat": false,
      "done": true,
      "exposure": 880
    },
    {
      "name": "SGS_PASS",
      "ra": 120.0,
      "dec": 45.0,
      "roll": 0.0,
      "begin": "2025-12-01T00:18:00+00:00",
      "end": "2025-12-01T00:28:00+00:00",
      "merit": 101.0,
      "slewtime": 120,
      "insaa": 0,
      "obsid": 65535,
      "obstype": "GSP",
      "slewdist": 5.2,
      "ss_min": 45.0,
      "ss_max": 180.0,
      "exptime": 480,
      "exporig": 600,
      "isat": false,
      "done": true,
      "exposure": 480,
      "station": "SGS",
      "contact_begin": "2025-12-01T00:20:00+00:00",
      "contact_end": "2025-12-01T00:28:00+00:00"
    }
  ]
}

Metadata Fields

Field

Type

Description

version

int

Integer plan-file revision counter. Starts at 0 and is incremented automatically each time a new plan is saved to the same directory for the same time window (see Auto-versioning below).

coast_sim_version

string

COASTSim package version that produced the file (e.g. "0.1.3").

created_at

string

ISO-8601 UTC timestamp of when the PlanSchema instance was created/validated (not updated by save()).

start

string

ISO-8601 UTC timestamp of the first entry’s begin time ("1970-01-01T00:00:00+00:00" if the plan is empty).

end

string

ISO-8601 UTC timestamp of the last entry’s end time ("1970-01-01T00:00:00+00:00" if the plan is empty).

num_entries

int

Total number of entries in the file.

Entry Fields

Field

Type

Description

name

string

Human-readable target name (e.g. "Crab Nebula").

ra

float

Right ascension in degrees (J2000).

dec

float

Declination in degrees (J2000).

roll

float

Spacecraft roll angle in degrees (-1 = unset).

begin

string

Start of the observation window (ISO-8601 UTC).

end

string

End of the observation window (ISO-8601 UTC).

merit

float

Scheduler merit/priority figure of merit.

slewtime

int

Slew duration in seconds.

insaa

int

Time spent in the South Atlantic Anomaly during the window (seconds).

obsid

int

Numeric observation identifier.

obstype

string

Observation type. Valid values: "AT" (Astronomical Target), "PPT" (Preprogrammed Target), "TOO" (Target of Opportunity), "SAFE" (Safe mode pointing), "CHARGE" (Emergency charging), "GSP" (Ground Station Pass).

slewdist

float

Angular slew distance in degrees.

ss_min

float

Minimum Sun-spacecraft separation angle encountered (degrees).

ss_max

float

Maximum Sun-spacecraft separation angle encountered (degrees).

exptime

int

Exposure time in seconds (may be shorter than original if interrupted).

exporig

int

Originally requested exposure time in seconds.

isat

bool

true if the detector was saturated during the exposure.

done

bool

true if the observation completed successfully.

exposure

int

Net science exposure time: end begin slewtime insaa (seconds).

station

string | null

Ground station code for GSP (Ground Station Pass) entries. Only present for obstype="GSP". Identifies which station is used for the commanded pass.

contact_begin

string | null

ISO-8601 UTC timestamp of when the ground station contact window begins. Only present for obstype="GSP". This is the actual pass start time; begin is the reservation time (which may include slew preparation).

contact_end

string | null

ISO-8601 UTC timestamp of when the ground station contact window ends. Only present for obstype="GSP". Typically matches end.

Ground Station Pass (GSP) Entries

Ground station pass entries (obstype="GSP") represent commanded communication windows with ground stations. Unlike science observations, these entries capture the reservation and execution of data downlink passes.

Key characteristics of GSP entries:

  • Automatic creation: Created by QueueDITL when the spacecraft enters a ground station visibility window.

  • Pass reservation: The begin time marks when the spacecraft reserves the window (potentially including slew preparation), while contact_begin marks the actual start of the ground station contact.

  • Metadata fields: The station, contact_begin, and contact_end fields are only present for GSP entries (null or omitted for other observation types).

  • Deconfliction: When multiple ground stations are visible simultaneously, COASTSim automatically selects the pass with the highest expected data volume (downlink rate × duration). Dropped overlapping opportunities are logged but not exported to the plan.

  • Visualization: GSP entries are excluded from the science observation bands in plot_ditl_timeline() and plot_ditl_timeline(). Ground station passes are still shown in the timeline’s dedicated “Ground Contact” row.

  • Safe mode: No GSP entries are created when the spacecraft is in SAFE mode.

Example GSP entry:

{
  "name": "TRO_PASS",
  "obstype": "GSP",
  "station": "TRO",
  "begin": "2025-12-01T12:00:00+00:00",
  "contact_begin": "2025-12-01T12:02:00+00:00",
  "contact_end": "2025-12-01T12:12:00+00:00",
  "end": "2025-12-01T12:12:00+00:00",
  "slewtime": 120,
  "exptime": 600,
  "obsid": 65535
}

In this example, the spacecraft begins reserving the pass window at 12:00:00 (begin), uses 2 minutes for slew preparation (slewtime), and the actual ground station contact runs from 12:02:00 to 12:12:00 (contact_begin to contact_end).

Auto-versioning

When path is a directory (or ends with /), save() scans the directory for existing files matching plan_<start>_<end>_v<N>.json and sets version to max(N) + 1 (or 0 if no matching files exist). Saving to an explicit file path leaves version unchanged.

Backward Compatibility

save() creates any missing parent directories automatically, so you can pass a nested path without creating it first.

load() accepts files written by older versions of COASTSim that predate PlanSchema. Fields not present in the file (e.g. created_at, num_entries) are filled with schema defaults; num_entries is always recomputed from the actual entry list after loading. Legacy files that store start, end, begin, or end as numeric Unix timestamps (float/int) are accepted and converted to datetime objects internally — only the on-disk format changed to ISO-8601. Legacy files that stored version as a semantic-version string (e.g. "0.1.3") are coerced to 0. Legacy files must already use the field names documented above (including exporig); there is currently no automatic renaming or aliasing of deprecated keys.

The from_attributes=True model configuration means the schema can also validate against any object that exposes the expected attributes, not just plain dicts.

API Reference