TelemetryCore
DeepSpaceTelemetry.TelemetryCore — Module
TelemetryCoreShared infrastructure of the simulation: configuration loading, schema-aware validation, storage governance (artifact estimation, budgets, retention settings), the accelerated mission clock and its persisted anchor, run provenance (event logs, snapshots, safesave rotation), and batch/segment I/O. Contains no routing logic — the queuing doctrine lives in Emitter and Receiver.
DeepSpaceTelemetry.TelemetryCore.L_ARM — Constant
L_ARMLength of the LISA constellation arms [m]: 2.5e9 m (2.5 million km).
DeepSpaceTelemetry.TelemetryCore.C_LIGHT — Constant
C_LIGHTSpeed of light in vacuum (m/s).
DeepSpaceTelemetry.TelemetryCore.F_STAR — Constant
F_STARCharacteristic transfer frequency of the LISA arm (Hz).
DeepSpaceTelemetry.TelemetryCore.DATA_ROOT — Constant
DATA_ROOTBase directory for run storage (a Ref; default <PROJECT_ROOT>/data). Every run-directory path resolves through run_directory; tests and embedding applications may redirect it (e.g. to a temporary directory).
DeepSpaceTelemetry.TelemetryCore.PROJECT_ROOT — Constant
PROJECT_ROOTAbsolute path of the package root, anchored at this source file through @__DIR__ so that it is stable across precompilation and relocation of the checkout. Relative configuration paths (physics.external_data_path, contacts.schedule_csv, the config file itself) resolve against it.
DeepSpaceTelemetry.TelemetryCore.run_directory — Function
run_directory(run_id::String) -> StringCanonical run-directory path <DATA_ROOT>/runs/<run_id> — the single source of the run layout for components, scripts, and post-processing tools.
DeepSpaceTelemetry.TelemetryCore.runs_root — Function
runs_root() -> StringDirectory holding every run directory (<DATA_ROOT>/runs).
DeepSpaceTelemetry.TelemetryCore.latest_run_id — Function
latest_run_id() -> Union{Nothing, String}ID of the most recently modified run directory under runs_root that carries a config_snapshot.toml, or nothing when no run exists. Used by the post-processing scripts when no run ID is given on the command line.
DeepSpaceTelemetry.TelemetryCore.batch_name — Function
batch_name(id::Integer, live::Bool) -> StringDirectory name of batch id: LIVE_batch_<id> for a batch finalized while the link was transmittable, ARCH_batch_<id> otherwise.
Examples
julia> TelemetryCore.batch_name(42, true)
"LIVE_batch_42"
julia> TelemetryCore.batch_name(42, false)
"ARCH_batch_42"DeepSpaceTelemetry.TelemetryCore.batch_id — Function
batch_id(name::AbstractString) -> IntNumeric ID parsed from a batch directory name (the trailing _<id> field); 0 for a name that does not carry one, so directory sweeps tolerate stray entries instead of throwing.
Examples
julia> TelemetryCore.batch_id("LIVE_batch_42")
42
julia> TelemetryCore.batch_id("stray_entry")
0DeepSpaceTelemetry.TelemetryCore.is_live_batch — Function
is_live_batch(name::AbstractString) -> Booltrue for a LIVE_batch_<id> directory name (finalized while the link was transmittable).
Examples
julia> TelemetryCore.is_live_batch("LIVE_batch_7"), TelemetryCore.is_live_batch("ARCH_batch_7")
(true, false)DeepSpaceTelemetry.TelemetryCore.is_archive_batch — Function
is_archive_batch(name::AbstractString) -> Booltrue for an ARCH_batch_<id> directory name (blind-spot or blackout generation, delivered by the LIFO backfill).
DeepSpaceTelemetry.TelemetryCore.is_batch_name — Function
is_batch_name(name::AbstractString) -> Booltrue for either batch class; false for any other directory entry.
DeepSpaceTelemetry.TelemetryCore.load_config — Function
load_config(path::String="")Loads the mission configuration from config.toml. Uses the provided path (relative paths resolve against the current directory first, then the project root) or defaults to config.toml at the project root.
DeepSpaceTelemetry.TelemetryCore.load_run_config — Function
load_run_config(run_dir::String)Loads the configuration that produced a given run: the run's own config_snapshot.toml when present, falling back to the project-level config.toml for legacy runs. Post-processing must always use this instead of load_config, otherwise editing config.toml silently re-parametrizes the analysis of old runs (disruption windows, session times, physics rates).
DeepSpaceTelemetry.TelemetryCore.checked_number — Function
checked_number(v, name::String) -> Float64Coerces a config value to Float64, aborting with a clean [CONFIG] error when the TOML value is not numeric (e.g. a quoted "3600"), instead of surfacing a raw MethodError from deep inside the validator or a builder.
DeepSpaceTelemetry.TelemetryCore.checked_integer — Function
checked_integer(v, name::String) -> IntCoerces a config value to Int with a clean [CONFIG] error on non-integer TOML values (strings, floats, booleans).
DeepSpaceTelemetry.TelemetryCore.checked_string — Function
checked_string(v, name::String) -> StringCoerces a config value to String with a clean [CONFIG] error when the TOML value is not a string.
DeepSpaceTelemetry.TelemetryCore.checked_flag — Function
checked_flag(v, name::String) -> BoolCoerces a config value to Bool with a clean [CONFIG] rejection when the TOML value is not a boolean (e.g. a quoted "true").
DeepSpaceTelemetry.TelemetryCore.dashboard_settings — Function
dashboard_settings(cfg::AbstractDict) -> NamedTupleValidated [dashboard] flags, each a boolean: open_live_viewer, open_receiver_log, and open_emitter_log (the terminals the dashboard launcher opens; default true) and receiver_status_panel (the receiver's in-console status panel; default false). A non-boolean value is rejected with a [CONFIG] error.
Examples
julia> TelemetryCore.dashboard_settings(Dict{String,Any}()).receiver_status_panel
falseDeepSpaceTelemetry.TelemetryCore.config_error — Function
config_error(msg::AbstractString)Rejects a configuration with an ArgumentError carrying the [CONFIG] message — the single throw point for every validator and coercion rejection, so callers can rely on the exception type.
DeepSpaceTelemetry.TelemetryCore.required_value — Function
required_value(section::AbstractDict, sec_name::String, key::String)Fetches a required configuration key, rejecting with a precise [CONFIG] message (instead of a raw KeyError) when it is absent.
DeepSpaceTelemetry.TelemetryCore.normalize_target_rows — Function
normalize_target_rows(raw) -> Union{Symbol, Vector{Int}}Parses the post_processing.target_event_rows configuration entry into a canonical form. Accepts the bare string "all", or an array mixing integer row indices (-1 meaning the final row), "start:stop" range strings, and the string "all". Returns the symbol :all when every row is requested, otherwise a sorted vector of unique row indices. Unrecognized entries are skipped with a warning.
Examples
julia> TelemetryCore.normalize_target_rows("all")
:all
julia> TelemetryCore.normalize_target_rows([3, "1:2", 3, -1])
4-element Vector{Int64}:
-1
1
2
3DeepSpaceTelemetry.TelemetryCore.reject_removed_key — Function
reject_removed_key(section::AbstractDict, sec_name::String, key::String)Raises a [CONFIG] ArgumentError naming the replacement when section still carries key, a configuration key retired at 1.0.0 (REMOVED_CONFIG_KEYS); returns nothing otherwise. Called by the accessors that once read the key through a fallback and by validate_config.
DeepSpaceTelemetry.TelemetryCore.reject_removed_section — Function
reject_removed_section(cfg::AbstractDict)Raises a [CONFIG] ArgumentError when the configuration carries the [disaster] section, renamed [disruption] before 1.0.0; returns nothing otherwise.
DeepSpaceTelemetry.TelemetryCore.mission_wall_seconds — Function
mission_wall_seconds(cfg::AbstractDict) -> Float64Validated wall-clock mission span simulation.mission_wall_seconds (> 0); the retired alias simulation.test_duration_sec is rejected with the replacement named.
Examples
julia> TelemetryCore.mission_wall_seconds(Dict{String,Any}("simulation" => Dict{String,Any}("mission_wall_seconds" => 168.0)))
168.0DeepSpaceTelemetry.TelemetryCore.normalize_profile! — Function
normalize_profile!(df::DataFrame) -> DataFrameBrings a mission_profile.csv frame to the current column schema: the pre-0.10 Ground_Archive column (the live + archive total) is renamed Ground_Total. Every reader of the profile passes through here.
DeepSpaceTelemetry.TelemetryCore.telemetry_settings — Function
telemetry_settings(cfg::AbstractDict) -> NamedTupleValidated [telemetry] parameters: session_start::Time, session_duration::Second, bandwidth_profile::String, sigmoid_steepness, gaussian_sigma, max_batches_per_hour, max_inflight_batches::Int, min_link_factor, range_million_km ≥ 0 (spacecraft–Earth range; 0 disables the light-time delay), and the derived round_trip_light_time_sec = 2 · range / c. The link capacity is given either as max_batches_per_hour or as the physical pair downlink_kbps and onboard_data_rate_kbps (both > 0), from which the capacity in batches per hour follows through the batch content span ([physics]); the two forms are mutually exclusive. The result carries nominal_batch_transfer_sec (the transfer time of one batch at full capacity), catch_up_ratio (downlink over production rate; NaN in the batches-per-hour form), and onboard_data_rate_kbps (NaN likewise). Bounds are enforced with [CONFIG] errors; absent keys take the documented defaults (post-processing of legacy snapshots), while the live-config required-key policy is applied by validate_config.
Examples
julia> cfg = Dict{String,Any}(
"telemetry" => Dict{String,Any}("downlink_kbps" => 230.0, "onboard_data_rate_kbps" => 75.0),
"physics" => Dict{String,Any}(
"data_source" => "synthetic",
"sample_rate" => 4.0,
"segment_duration_sec" => 60.0,
"batch_size" => 10,
),
);
julia> tel = TelemetryCore.telemetry_settings(cfg);
julia> round(tel.nominal_batch_transfer_sec; digits = 1), round(tel.max_batches_per_hour; digits = 1), round(tel.catch_up_ratio; digits = 2)
(195.7, 18.4, 3.07)
julia> tel = TelemetryCore.telemetry_settings(Dict{String,Any}("telemetry" => Dict{String,Any}("max_batches_per_hour" => 20.0)));
julia> tel.nominal_batch_transfer_sec, isnan(tel.catch_up_ratio)
(180.0, true)DeepSpaceTelemetry.TelemetryCore.physics_settings — Function
physics_settings(cfg::AbstractDict) -> NamedTupleValidated [physics] parameters: data_source ("synthetic" or "external"), external_data_path (as configured; consumers resolve it against the package root), sample_rate > 0, segment_duration_sec > 0, and batch_size ≥ 1, with at least two samples per segment (the FFT synthesis block). The four core keys are required. The optional confusion_observation_years (one of 0.5, 1.0, 2.0, 4.0; default 1.0) selects the galactic-confusion fit of the noise model and noise_f_min_hz > 0 (default 1e-5) the lower edge of the synthesized band. The retired signal_injection_probability is rejected (event instants are [[events.markers]]). The existence of the external file is checked by validate_config only, so post-processing of a finished run does not depend on the input file still being present.
DeepSpaceTelemetry.TelemetryCore.supervision_settings — Function
supervision_settings(cfg::AbstractDict) -> NamedTupleValidated [supervision] parameters: on_component_failure ("abort", "continue", or "restart", lower-cased; default "abort"), max_restarts ≥ 0 (default 3), watchdog_sec > 0 (default 30).
DeepSpaceTelemetry.TelemetryCore.visibility_model — Function
visibility_model(cfg::AbstractDict) -> VisibilityModelThe VisibilityModel described by [telemetry] and [contacts], built from telemetry_settings and contacts_settings. Low-latency periods enter only when contacts.low_latency_enabled is set.
DeepSpaceTelemetry.TelemetryCore.capacity_balance — Function
capacity_balance(cfg::AbstractDict) -> NamedTupleCapacity of one nominal pass against the daily production, from telemetry_settings, physics_settings, and the pass profile: rate_form (the capacity is given as the physical rate pair), profile_mean (profile_mean of the pass profile), pass_hours (telemetry.session_duration_hours), capacity_per_pass (batches, max_batches_per_hour × profile_mean × pass_hours), and produced_per_day (batches, 86 400 / (batch_size × segment_duration_sec)). Seasonal extensions, exceptions, and low-latency periods are not included: the figure is the balance of the nominal daily pass.
DeepSpaceTelemetry.TelemetryCore.PROFILE_MEAN_SUBINTERVALS — Constant
PROFILE_MEAN_SUBINTERVALSNumber of composite-Simpson subintervals of profile_mean: 1024 gives 10⁻¹⁰ accuracy on the shipped profiles at negligible cost (one evaluation per validation and banner).
DeepSpaceTelemetry.TelemetryCore.hours_period — Function
hours_period(hours::Float64) -> MillisecondThe Millisecond period of a duration given in hours, rounded to the millisecond (contact-window and low-latency-period lengths).
DeepSpaceTelemetry.TelemetryCore.ContactWindow — Type
ContactWindowOne ground-contact interval on the mission timeline: start and stop (DateTime, closed interval), the link capacity relative to peak (1.0 for a nominal pass; the station-availability fraction of a low-latency period), the low_latency flag, and a free-text label (empty for nominal passes). ContactWindow(start, stop) builds a nominal pass.
DeepSpaceTelemetry.TelemetryCore.ContactsSettings — Type
ContactsSettingsValidated [contacts] section as returned by contacts_settings.
DeepSpaceTelemetry.TelemetryCore.contacts_settings — Function
contacts_settings(cfg::AbstractDict) -> ContactsSettingsValidated [contacts] section — the ground-contact schedule layered on the daily window of [telemetry]; every key is optional and an absent section reproduces the plain daily window.
seasonal_extension_hours ≥ 0(default 0): peak extension of the daily window, cosine-modulated overseason_period_days > 0(default 365.25) and peaking atseason_peak_day_of_year ∈ [1, 366](default 172);telemetry.session_duration_hoursplus the extension may not exceed 24 h.[[contacts.passes]](startdatetime,duration_hours > 0) orschedule_csv(columnsStart,DurationHours; relative paths resolve against the current directory, then the project root) — an explicit, non-overlapping pass list that replaces the daily generator. The two forms are mutually exclusive.[[contacts.exceptions]](date, optionalstart,duration_hours ∈ [0, 24],0= missed pass): the window of that date verbatim, in place of the generated one; incompatible with an explicit schedule.low_latency_enabled(defaulttrue),low_latency_capacity_fraction ∈ (0, 1](default 1), and[[contacts.low_latency_periods]](start,duration_hours > 0, optionalcapacity_fraction,label): extra contact windows at constant capacity outside the nominal passes. Periods triggered by[[events.markers]](event_marker_settings) are appended, labelled by the marker.
Malformed entries raise a [CONFIG] error: a silently dropped pass or period invalidates the scenario.
DeepSpaceTelemetry.TelemetryCore.EventMarker — Type
EventMarkerOne [[events.markers]] entry: the mission instant time of an event of interest (a transient, a glitch), its label, and an optional triggered low-latency period — low_latency_after_hours after the marker, lasting low_latency_duration_hours (0 = none) at low_latency_capacity_fraction of peak capacity (NaN = the [contacts] default). EventMarker(time, label) builds a marker without a triggered period.
DeepSpaceTelemetry.TelemetryCore.event_marker_settings — Function
event_marker_settings(cfg::AbstractDict) -> Vector{EventMarker}Validated [[events.markers]] entries sorted by time: time (datetime, required), label (default marker <i>), low_latency_after_hours ≥ 0, low_latency_duration_hours ≥ 0, and low_latency_capacity_fraction ∈ (0, 1] when given. Labels must be unique. A malformed marker raises a [CONFIG] error.
DeepSpaceTelemetry.TelemetryCore.ground_settings — Function
ground_settings(cfg::AbstractDict) -> NamedTupleValidated [ground] section: processing_latency_hours ≥ 0 (default 1, the low-latency alert pipeline budget added to every alert latency).
DeepSpaceTelemetry.TelemetryCore.publication_settings — Function
publication_settings(cfg::AbstractDict) -> NamedTupleValidated [post_processing.publication]: enabled (default false), format ("pdf" | "svg", default "pdf"), column_width_mm ∈ [40, 400] (default 178, the double-column width the figures are designed at; 86–90 for a single column), and export_dir (default "" = <run_dir>/publication; relative paths resolve against the current directory). Unrecognized keys warn.
DeepSpaceTelemetry.TelemetryCore.batch_markers — Function
batch_markers(markers::Vector{EventMarker}, epoch::DateTime, stop::DateTime) -> Vector{EventMarker}The markers whose instant lies in the content span [epoch, stop) of a batch.
DeepSpaceTelemetry.TelemetryCore.save_markers — Function
save_markers(run_dir::String, markers::Vector{EventMarker})Writes <run_dir>/markers.csv (SimTime, Label) — the run's event instants for consumers and post-processing; nothing is written when there are no markers.
DeepSpaceTelemetry.TelemetryCore.load_markers — Function
load_markers(run_dir::String) -> Vector{EventMarker}The markers recorded in <run_dir>/markers.csv (empty when absent), as plain EventMarkers without triggered periods.
DeepSpaceTelemetry.TelemetryCore.parsed_datetime — Function
parsed_datetime(v, name::String) -> DateTimeCoerces a config value to DateTime: TOML local datetimes and dates arrive already parsed, strings are read as ISO-8601; anything else is rejected with a [CONFIG] error naming name.
DeepSpaceTelemetry.TelemetryCore.parsed_date — Function
parsed_date(v, name::String) -> DateCoerces a config value to Date (TOML local date, datetime, or ISO-8601 string) with a [CONFIG] error naming name otherwise.
DeepSpaceTelemetry.TelemetryCore.parsed_time — Function
parsed_time(v, name::String) -> TimeCoerces a config value to Time (TOML local time or HH:MM:SS string) with a [CONFIG] error naming name otherwise.
DeepSpaceTelemetry.TelemetryCore.loss_channel_settings — Function
loss_channel_settings(cfg::AbstractDict) -> NamedTupleValidated [packet_loss] parameters: enabled, model ("bernoulli" or "gilbert_elliott", lower-cased), the five probabilities p_loss, p_good_to_bad, p_bad_to_good, p_loss_good, p_loss_bad (each in [0, 1]), on_loss ("retransmit" or "drop"), and max_retries ≥ 0. Types, enumerations, and bounds are enforced regardless of enabled: a malformed-but-disabled section fails fast instead of surfacing only once the channel is enabled.
DeepSpaceTelemetry.TelemetryCore.DisruptionEventSettings — Type
DisruptionEventSettingsOne validated [[disruption.events]] entry as returned by disruption_event_settings.
DeepSpaceTelemetry.TelemetryCore.disruption_event_settings — Function
disruption_event_settings(cfg::AbstractDict) -> Vector{DisruptionEventSettings}Validated [[disruption.events]] entries in file order (the pre-1.0 [[disaster.events]] section name is rejected): type, label, start_day ≥ 0, duration_hours > 0, recovery_hours ≥ 0, severity ∈ [0, 1], loss_multiplier, and affects — "link" (the default: a capacity and loss disruption) or "generation" (a scheduled gap in data production of duration_hours; severity, recovery, and loss keys are ignored). type = "antenna_repointing" defaults to "generation". A malformed event raises an error rather than being skipped: a silently missing disruption invalidates the scenario.
DeepSpaceTelemetry.TelemetryCore.onboard_capacity — Function
onboard_capacity(cfg::AbstractDict) -> NamedTupleThe on-board recorder ceiling from storage.onboard_capacity_days > 0 (default 14, the Definition Study Report's autonomy without ground contact): days, batches (the ceiling the emitter enforces — production over that span in whole batches, at least one), and gigabit (the physical volume when the link is given as a rate pair, else NaN).
DeepSpaceTelemetry.TelemetryCore.open_recorder_gap — Function
open_recorder_gap(run_dir::String) -> BoolWhether the last recorder-overflow gap in events_tx.csv (rows with Batch = RECORDER) is still open — a gap_start without its gap_end — so a re-attaching emitter closes it when the buffer has room again.
DeepSpaceTelemetry.TelemetryCore.validate_config — Function
validate_config(cfg::AbstractDict)Validates every tunable against its safe interval (documented inline in config.toml) before any directory is created or any computation starts. Code-breaking values are rejected with an ArgumentError (early termination); suspicious but runnable values emit a @warn. Returns cfg for chaining.
Hard errors (would break the pipeline):
- non-positive
speed_up,mission_wall_seconds,sample_rate,segment_duration_sec,max_batches_per_hour,max_storage_gb batch_size < 1,initial_downtime_days < 0session_duration_hoursoutside(0, 24](the daily session scheduler wrapsTimearithmetic at 24 h)- fewer than 2 samples per segment (
sample_rate * segment_duration_sec < 2breaks the FFT synthesis block) - unknown
data_source;data_source = "external"with a missing file - packet-loss probabilities outside
[0, 1], unknown lossmodeloron_losspolicy, negativemax_retries - disruption events with negative
start_day, non-positiveduration_hours, negativerecovery_hours, orseverityoutside[0, 1] - type-mismatched values anywhere (a quoted
"3600"where a number is expected, a float where an integer is expected) — reported as a precise[CONFIG]message instead of a raw conversion stacktrace - configuration keys retired at 1.0.0 (
simulation.test_duration_sec,simulation.max_storage_gb,physics.signal_injection_probability,post_processing.generate_batch_matrix) and the[disaster]section name — rejected with the replacement named
Warnings (runnable but likely unintended):
- emitter wall-clock period
segment_duration_sec / speed_upbelowEMITTER_PERIOD_WARN_MS(the generation loop cannot keep pace; sim-time desync) - receiver nominal download slot
3600 / (max_batches_per_hour · speed_up)belowRECEIVER_SLOT_WARN_MS(theRECEIVER_SLEEP_FLOOR_SECsleep floor distorts the download rate) - unknown
bandwidth_profile(falls back to"sine") - the physical rate pair combined with a shaped
bandwidth_profile(the profile mean scales a link rate that the pass sustains; the capacity of one nominal pass against the daily production is stated) - non-integer
sample_rate * segment_duration_sec(rounded) - Gilbert–Elliott
p_bad_to_good = 0(the channel never recovers) - disruption events starting at or after mission end (never fire), events whose blackout + recovery tail is truncated by mission end, blackouts spanning the entire remaining mission, and events overlapping in time (capacity composes as the minimum, loss multiplier as the maximum)
- unrecognized sections or keys anywhere in the config (typo guard — an unknown key would otherwise silently fall back to its default)
- loss saturation — the worst-channel-state per-attempt loss composed with the largest disruption
loss_multiplierreaches ≥ 1 (every transfer fails while that regime is active)
DeepSpaceTelemetry.TelemetryCore.check_storage_limits — Function
check_storage_limits(cfg::AbstractDict)Pre-run storage safety gate. Prints the per-class artifact estimate (estimate_artifacts), then enforces the [storage] budgets with mitigation awareness:
- retention disabled: abort when the projected total exceeds the budget (the error names
[retention]as the mitigation); warn within 10 % of it. - retention enabled: abort only when even the steady-state footprint (non-prunable classes + payload capped at the watermark) exceeds the budget; otherwise warn if the unbounded projection exceeds the budget and proceed. Additionally warns when the payload generated within one
grace_hourswindow alone exceeds the watermark (the custodian could never satisfy both constraints simultaneously).
The same logic gates storage.max_file_count, and the post-processing replay RAM estimate is gated against storage.max_ram_gb (a mitigation-free hard budget). Every abort is a StorageBudgetError. Returns nothing; called before any run directory is created.
DeepSpaceTelemetry.TelemetryCore.estimate_artifacts — Function
estimate_artifacts(cfg::AbstractDict) -> NamedTupleClosed-form per-class artifact estimate for a run, computed entirely from the configuration (upper bounds where exact counts depend on stochastic outcomes). Classes: payload segment CSVs, batch metadata, event logs, metrics, the 2D mask timeline, point-wise expansions, plots (session + summary figures, PNG and vector-PDF twins; the optional GIF is a manual post-processing product and is excluded), and text logs. replay_ram_bytes estimates the post-processing replay RAM (gated against storage.max_ram_gb). Calibration constants default to measured values and are overridable key-by-key in [storage].
Returns counts (n_segments, n_batches, n_points, mission_days, metrics_rows), per-class byte fields, total_bytes, file_count, and the retention-prunable subset (prunable_bytes, prunable_files): the payload scaled by the expected delivered fraction under the configured loss model — terminally lost batches land in lost/, and metadata, event logs, metrics, masks, and lost/ are never prunable by construction.
DeepSpaceTelemetry.TelemetryCore.storage_budget — Function
storage_budget(cfg::AbstractDict) -> (max_gb, max_files, max_ram_gb)Resolves the run-directory disk budget [GB] and inode budget from [storage]. The retired simulation.max_storage_gb is rejected with the replacement named; without the key the default of 5.0 GB applies. max_ram_gb (default 8.0) budgets the post-processing replay RAM.
DeepSpaceTelemetry.TelemetryCore.StorageBudgetError — Type
StorageBudgetError(msg::String)Raised by check_storage_limits when the projected run footprint — disk volume, file count, or post-processing replay RAM — exceeds the [storage] budget and no configured mitigation bounds it. msg carries the [STORAGE] diagnosis: the estimate, the budget, and the remedies.
DeepSpaceTelemetry.TelemetryCore.RetentionPolicy — Type
RetentionPolicyImmutable operating parameters of the retention custodian: enabled, grace (the mission-time availability guarantee for delivered payloads, as a Millisecond period), watermark_bytes (prunable-payload size that triggers pruning), and log_rotate_bytes (size-capped log rotation, active regardless of enabled). Constructed by retention_settings.
DeepSpaceTelemetry.TelemetryCore.retention_settings — Function
retention_settings(cfg::AbstractDict) -> RetentionPolicyParses the [retention] section into a RetentionPolicy: retention.grace_hours converts to a Millisecond period at parse time, and high_watermark_gb defaults to 75 % of the storage budget.
DeepSpaceTelemetry.TelemetryCore.RECEIVER_POLL_INTERVAL_SEC — Constant
RECEIVER_POLL_INTERVAL_SECReceiver idle-poll / metrics-tick interval [wall-clock s]. Shared between the receiver loop and the artifact estimator so the metrics-row upper bound and the realized sampling cadence cannot drift apart.
DeepSpaceTelemetry.TelemetryCore.RECEIVER_SLEEP_FLOOR_SEC — Constant
RECEIVER_SLEEP_FLOOR_SECMinimum receiver download-slot sleep [s] — an OS scheduler property, not a tunable. Shared between the receiver loop and the validator warning about download-rate distortion so the two can never drift apart.
DeepSpaceTelemetry.TelemetryCore.EMITTER_PERIOD_WARN_MS — Constant
EMITTER_PERIOD_WARN_MSEmitter wall-clock segment period segment_duration_sec / speed_up [ms] below which validate_config warns: at shorter periods the generation loop cannot keep pace with the accelerated clock and the batch timestamps desynchronize from mission time.
DeepSpaceTelemetry.TelemetryCore.RECEIVER_SLOT_WARN_MS — Constant
RECEIVER_SLOT_WARN_MSReceiver wall-clock download slot nominal_batch_transfer_sec / speed_up [ms] below which validate_config warns: the RECEIVER_SLEEP_FLOOR_SEC sleep floor then distorts the effective downlink rate.
DeepSpaceTelemetry.TelemetryCore.HEARTBEAT_INTERVAL_MS — Constant
HEARTBEAT_INTERVAL_MSWall-clock interval between two touches of a component's liveness file [ms]; the supervisor's watchdog reads the file's modification time.
DeepSpaceTelemetry.TelemetryCore.MS_PER_HOUR — Constant
MS_PER_HOURMilliseconds per hour, converting Dates.Millisecond periods of the mission clock to mission hours.
DeepSpaceTelemetry.TelemetryCore.MS_PER_DAY — Constant
MS_PER_DAYMilliseconds per day, converting Dates.Millisecond periods of the mission clock to mission days.
DeepSpaceTelemetry.TelemetryCore.METRICS_BANDWIDTH_HYSTERESIS_PCT — Constant
METRICS_BANDWIDTH_HYSTERESIS_PCTBandwidth change [percentage points] that admits a new mission_profile.csv row. Shared between the receiver's metrics write gate and the artifact estimator so the metrics-row bound and the realized sampling cadence cannot drift apart.
DeepSpaceTelemetry.TelemetryCore.STORAGE_CALIBRATION_DEFAULTS — Constant
STORAGE_CALIBRATION_DEFAULTSMeasured calibration constants of the artifact estimator, overridable key-by-key in [storage]. The bytes_* disk constants are per-artifact CSV or figure sizes; bytes_replay_cell is the post-processing replay RAM per (metrics row × batch) membership. The schema entry for [storage] and the validator both iterate this table, so a new calibration key is declared exactly once.
DeepSpaceTelemetry.TelemetryCore.DEFAULT_WATERMARK_FRACTION — Constant
DEFAULT_WATERMARK_FRACTIONFraction of the storage budget at which the retention custodian begins pruning when retention.high_watermark_gb is not configured explicitly.
DeepSpaceTelemetry.TelemetryCore.STORAGE_WARN_FRACTION — Constant
STORAGE_WARN_FRACTIONFraction of a [storage] budget above which the pre-run gate warns without aborting.
DeepSpaceTelemetry.TelemetryCore.MASK_ROW_OVERHEAD_BYTES — Constant
MASK_ROW_OVERHEAD_BYTESEstimator calibration: fixed per-row overhead of the mask-timeline CSV (timestamp column + separators) beyond its per-batch cells.
DeepSpaceTelemetry.TelemetryCore.LOG_FIXED_OVERHEAD_BYTES — Constant
LOG_FIXED_OVERHEAD_BYTESEstimator calibration: mission-level fixed size of the emitter and receiver text logs (banners, startup and post-processing records) independent of the batch count.
DeepSpaceTelemetry.TelemetryCore.RUN_FILE_COUNT_SLACK — Constant
RUN_FILE_COUNT_SLACKEstimator calibration: fixed file-count slack for rotation backups and sentinel files beyond the per-class counts.
DeepSpaceTelemetry.TelemetryCore.generate_run_id — Function
generate_run_id()Generates a unique ID for the current simulation run, RUN_pid=<pid>_t=<yyyymmdd_HHMMSS> (key=value fields in alphabetical order, the layout of DrWatson's savename).
DeepSpaceTelemetry.TelemetryCore.platform_provenance — Function
platform_provenance() -> Dict{String,Any}Hardware and runtime fingerprint stamped into every run's config_snapshot.toml under [provenance.platform]: hostname, OS kernel and architecture, CPU model and logical core count, total memory, Julia version, thread/BLAS-thread counts, the package version, and the git commit of the checkout (git_commit, empty outside a repository). Together with the configuration snapshot and the recorded input identity, every result is attributable to config + commit + platform. (No GPU fields: the pipeline is I/O- and event-loop-bound and uses no GPU backend.)
DeepSpaceTelemetry.TelemetryCore.git_commit — Function
git_commit() -> StringThe HEAD commit of the package checkout (git rev-parse HEAD in the project root), or "" when git or the repository is unavailable.
DeepSpaceTelemetry.TelemetryCore.setup_run_dir — Function
setup_run_dir(run_id::String; cfg=nothing)Creates and returns the base directory for a simulation run along with its required subdirectories; a non-empty directory under the same run ID is rejected with an ArgumentError. When the parsed configuration cfg is provided, a config_snapshot.toml is written into the run directory (with safesave-style backup rotation) so every run's exact parameters remain reproducible after config.toml changes; the snapshot additionally carries the platform_provenance fingerprint under [provenance.platform].
DeepSpaceTelemetry.TelemetryCore.backup_existing — Function
backup_existing(path::String) -> Union{String, Nothing}If path exists, renames it to <name>#<k><ext> using the smallest unused k, mirroring DrWatson's safesave backup rotation so no result file is ever silently overwritten. Returns the backup path, or nothing if path did not exist.
DeepSpaceTelemetry.TelemetryCore.backup_existing_dir — Function
backup_existing_dir(path::String) -> Union{String, Nothing}Directory counterpart of backup_existing: renames an existing directory to <name>#<k> (smallest unused k) so a same-named arrival never silently overwrites recorded data. Returns the backup path, or nothing when path did not exist.
DeepSpaceTelemetry.TelemetryCore.safe_csv_write — Function
safe_csv_write(path::String, table) -> StringWrites table to path as CSV, first rotating any pre-existing file to a #k-suffixed backup via backup_existing. Returns path.
DeepSpaceTelemetry.TelemetryCore.SimulationClock — Type
SimulationClock(start_real_time::DateTime, start_sim_time::DateTime, speed_up::Float64)The accelerated mission clock: mission time advances speed_up times faster than wall-clock time from the anchor pair start_real_time (the wall instant of the anchor) and start_sim_time (the mission instant at that anchor). get_current_sim_time maps the wall clock to mission SimTime; due_wall_time is its inverse.
DeepSpaceTelemetry.TelemetryCore.get_current_sim_time — Function
get_current_sim_time(clock::SimulationClock)Returns the current accelerated simulation time.
DeepSpaceTelemetry.TelemetryCore.due_wall_time — Function
due_wall_time(clock::SimulationClock, sim_time::DateTime) -> DateTimeWall-clock instant at which the mission clock reaches sim_time — the inverse of get_current_sim_time. Each due time is computed from the clock anchor and the absolute mission instant, so pacing loops that sleep until a due time accumulate no rounding across iterations.
DeepSpaceTelemetry.TelemetryCore.EMITTER_MAX_SLEEP_SEC — Constant
EMITTER_MAX_SLEEP_SECUpper bound on a single emitter pacing sleep [wall-clock s]. The generation loop wakes at least this often to refresh its heartbeat and to honor the stop flag, the HALT sentinel, and the deadline even when one segment period is long (real-time rehearsals at low speed_up).
DeepSpaceTelemetry.TelemetryCore.EMITTER_LAG_WARN_SEC — Constant
EMITTER_LAG_WARN_SECWall-clock duration [s] for which the emitter's content lag must persist above one segment period before the loop warns that the host cannot keep pace with the accelerated clock. Startup compilation and transient stalls are recovered by burst catch-up within this window and never warn.
DeepSpaceTelemetry.TelemetryCore.thread_advisory — Function
thread_advisory() -> Union{Nothing, String}Returns an advisory message when the process runs on a single Julia thread, nothing otherwise. The emitter, the receiver, and the supervisor are cooperative tasks: on one thread any non-yielding stretch in one of them (compilation warm-up, garbage collection, figure rendering) pauses the others until it yields. Three threads let each task own one; more bring no benefit because nothing else in the pipeline is parallel.
DeepSpaceTelemetry.TelemetryCore.save_clock_anchor — Function
save_clock_anchor(run_dir::String, clock::SimulationClock, deadline::DateTime)Persists the mission clock anchor (wall epoch, mission epoch, speed-up) and the absolute wall-clock deadline into <run_dir>/clock_anchor.toml (an existing file is rotated to clock_anchor#k.toml first, never overwritten). Written once at mission start; a re-attaching or restarted component reconstructs the identical clock from it (load_clock_anchor), so mission time survives component outages — the outage simply elapses as mission time.
DeepSpaceTelemetry.TelemetryCore.load_clock_anchor — Function
load_clock_anchor(run_dir::String) -> (clock::SimulationClock, deadline::DateTime)Reconstructs the mission clock and the absolute deadline persisted by save_clock_anchor. Throws an ArgumentError when the anchor file is absent (runs started by an older pipeline cannot be re-attached).
DeepSpaceTelemetry.TelemetryCore.max_logged_batch_id — Function
max_logged_batch_id(run_dir::String) -> IntHighest batch ID recorded in events_tx.csv (0 when the log is absent or empty) — the authoritative resume point for a re-attaching emitter's batch counter, immune to batches already delivered out of onboard/.
DeepSpaceTelemetry.TelemetryCore.DataSegment — Type
DataSegment(id::Int, timestamp::DateTime, data::Vector{Float32})One contiguous segment of the observed time series: the Float32 samples data of one segment_duration_sec span whose first sample lies at the mission instant timestamp; id is the instrument's running segment counter.
DeepSpaceTelemetry.TelemetryCore.DataBatch — Type
DataBatch(id::Int, segments::Vector{DataSegment}, created_at::DateTime)A collection of DataSegments prepared for bulk transmission over the DSN: id is the batch counter, segments the payload in content order, and created_at the mission instant of finalization (when the batch became transmittable; the content epoch is segments[1].timestamp).
DeepSpaceTelemetry.TelemetryCore.MissionMetrics — Type
MissionMetricsA snapshot of the mission state including queue sizes, effective and nominal bandwidth, packet-loss counters, and the disruption flag. bandwidth_pct is the effective link capacity (visibility × disruption factor); nominal_bandwidth_pct is the visibility profile alone.
DeepSpaceTelemetry.TelemetryCore.save_metrics — Function
save_metrics(run_dir::String, m::MissionMetrics)Appends a new metrics snapshot to mission_profile.csv. The packet-loss and disruption columns are appended after the legacy columns so pre-loss readers of old profiles keep working.
DeepSpaceTelemetry.TelemetryCore.log_tx_event — Function
log_tx_event(run_dir::String, sim_t::DateTime, batch::String, event::String)Appends one emitter-side batch milestone to events_tx.csv. Events: "gen" (batch finalized onboard), "tx" (batch placed on the downlink), and "marker" (sim_t = an event-marker instant, batch = the batch holding it; state-preserving). Together with log_rx_event this forms the exact per-batch state history used by the mask/animation reconstruction — no heuristic replay. Only the emitter task writes this file (single-writer; no lock needed).
DeepSpaceTelemetry.TelemetryCore.log_rx_event — Function
log_rx_event(run_dir::String, sim_t::DateTime, batch::String, event::String, attempt::Int)Appends one receiver-side batch milestone to events_rx.csv. Events: "ingested" (batch reached the ground archive), "retry" (transfer attempt lost, batch remains on the link), "lost" (retry budget exhausted, batch moved to lost/), and "pruned" (retention custodian deleted the delivered payload CSVs after the grace window; state-preserving for the mask replay). attempt counts failed transfer attempts so far (0 for "pruned"). Only the receiver task writes this file (single-writer; no lock needed).
DeepSpaceTelemetry.TelemetryCore.save_batch — Function
save_batch(path::String, batch::DataBatch; markers = String[])Serializes a DataBatch and its metadata to the specified physical directory. metadata.json carries batch_id, segment_count, created_at (mission time at which the batch was finalized and became transmittable), content_epoch (mission timestamp of the first sample of the payload — the physical epoch the segment data belong to), and, when given, markers — the labels of the event markers whose instant lies in the payload.
DeepSpaceTelemetry.TelemetryCore.read_batch_metadata — Function
read_batch_metadata(batch_dir::String) -> Dict{String,Any}Parses <batch_dir>/metadata.json. Returns an empty dictionary when the file is absent or unparsable (a foreign or truncated directory), so directory sweeps degrade to "unknown" instead of faulting.
DeepSpaceTelemetry.TelemetryCore.batch_content_epochs — Function
batch_content_epochs(run_dir::String) -> Dict{String,DateTime}Batch name → content epoch (first-sample mission timestamp) for every batch directory under onboard/, link/, ground/, and lost/ whose metadata.json records a content_epoch (batches written before that key existed are omitted).
DeepSpaceTelemetry.TelemetryCore.save_segment — Function
save_segment(path::String, seg::DataSegment)Saves a 1D DataSegment array to a raw CSV format for downstream pipeline usage.
DeepSpaceTelemetry.TelemetryCore.load_segment — Function
load_segment(path::String; timestamp::DateTime = DateTime(0))Loads a 1D CSV time series back into a DataSegment. Segment CSVs persist only amplitudes, so the mission timestamp cannot be recovered from the file: callers that know the epoch (e.g. from metadata.json's created_at) pass it via timestamp; otherwise the DateTime(0) sentinel marks it unknown — never a fabricated wall-clock time.
DeepSpaceTelemetry.TelemetryCore.VisibilityModel — Type
VisibilityModelGround-contact model of the downlink. The nominal daily window opens at session_start for session_duration with the capacity profile (sigmoid_steepness, gaussian_sigma); seasonal_extension widens it symmetrically about its centre, cosine-modulated with period season_period_days and peaking at season_peak_day_of_year; exceptions (date => (start, duration), zero duration = missed pass) replace the window of a date verbatim; a non-empty schedule of explicit passes replaces the daily generator altogether; low_latency periods are additional windows at constant capacity outside the nominal passes. The three- to five-argument constructors build the plain daily model.
Examples
julia> model = TelemetryCore.VisibilityModel(Time(8), Second(8 * 3600), "sine");
julia> TelemetryCore.is_visible(model, DateTime(2035, 1, 1, 10)), TelemetryCore.is_visible(model, DateTime(2035, 1, 1, 3))
(true, false)
julia> round(TelemetryCore.profile_mean(model); digits = 3)
0.5DeepSpaceTelemetry.TelemetryCore.seasonal_window_duration — Function
seasonal_window_duration(model::VisibilityModel, date::Date) -> SecondDuration of the generated window on date: session_duration plus the seasonal extension modulated as ½ [1 + cos(2π (d − d_peak) / P)] with the day of year d, so the full extension applies at season_peak_day_of_year and none half a period away.
DeepSpaceTelemetry.TelemetryCore.nominal_window — Function
nominal_window(model::VisibilityModel, date::Date) -> Union{Nothing,ContactWindow}The nominal pass anchored on date under the daily generator: the exception of that date verbatim when one exists (nothing for a missed pass), otherwise the configured window extended symmetrically by the seasonal term. nothing in explicit-schedule mode.
DeepSpaceTelemetry.TelemetryCore.contact_windows — Function
contact_windows(model::VisibilityModel, t_lo::DateTime, t_hi::DateTime) -> Vector{ContactWindow}Every contact window — nominal passes and low-latency periods — that overlaps [t_lo, t_hi], sorted by start.
DeepSpaceTelemetry.TelemetryCore.active_window — Function
active_window(model::VisibilityModel, t::DateTime) -> Union{Nothing,ContactWindow}The contact window containing t — a nominal pass first, else a low-latency period — or nothing when the spacecraft is out of contact. Under the daily generator only the windows anchored on the date of t and on the previous date (a pass crossing midnight) can contain t.
DeepSpaceTelemetry.TelemetryCore.is_visible — Function
is_visible(model::VisibilityModel, t::DateTime) -> BoolWhether the spacecraft is in ground contact at t — inside a nominal pass or a low-latency period (active_window).
DeepSpaceTelemetry.TelemetryCore.profile_factor — Function
profile_factor(model::VisibilityModel, progress::Float64) -> Float64The capacity profile of a nominal pass at the normalized position progress ∈ [0, 1] within the window: sine, sigmoid, gaussian, or flat (unknown names fall back to sine).
DeepSpaceTelemetry.TelemetryCore.profile_mean — Function
profile_mean(model::VisibilityModel) -> Float64Mean of the capacity profile over one nominal pass, ∫₀¹ profile_factor(model, p) dp, by composite Simpson quadrature on PROFILE_MEAN_SUBINTERVALS: 1 for flat, 1/2 for sine, ln(cosh k)/k for sigmoid with steepness k, and σ √(2π) erf(1/(2√2 σ)) for gaussian. Multiplied by the full-capacity rate and the pass length it gives the capacity of one pass in batches (capacity_balance).
DeepSpaceTelemetry.TelemetryCore.get_bandwidth_factor — Function
get_bandwidth_factor(model::VisibilityModel, t::DateTime) -> Float64Effective link capacity in [0, 1] at t: the pass profile evaluated at the position of t within the active nominal window, the constant capacity fraction inside a low-latency period, and 0 out of contact.
Examples
julia> model = TelemetryCore.VisibilityModel(Time(8), Second(8 * 3600), "sine");
julia> TelemetryCore.get_bandwidth_factor(model, DateTime(2035, 1, 1, 12))
1.0
julia> TelemetryCore.get_bandwidth_factor(model, DateTime(2035, 1, 1, 8))
0.0
julia> TelemetryCore.get_bandwidth_factor(model, DateTime(2035, 1, 1, 20))
0.0