A long position track approximated by a sequence of contiguous, independently
fitted Sidereon.GNSS.ReducedOrbit segments.
A single Sidereon.GNSS.ReducedOrbit distills a whole track into one set of mean
elements. A Piecewise model instead splits the span [t0, t1] into
contiguous segments of :segment_s seconds and fits each segment. Every query
lands inside one fit window, so residuals are controlled by the samples in that
segment. The orbit math, frames, and time scales are unchanged.
This trades stored segment count for residual control. It is not orbit
determination and not a substitute for source ephemeris data. Measure the
residual with drift/3 against caller-provided truth samples.
Models
Each segment is one of the two Sidereon.GNSS.ReducedOrbit models, selected with the
same :model option:
:circular_secular(the default) - a circular orbit;:eccentric_secular- recovers the radiala·esignal.
See Sidereon.GNSS.ReducedOrbit for the per-model details.
Size / residual tradeoff
Storage grows roughly linearly with segment count. Shorter segments cost more memory but keep queries closer to a fit window center.
Segment selection
Segments tile [t0, t1] with no gaps. A query epoch is resolved by finding the
segment whose half-open interval [seg_t0, seg_t1) contains it; the final
segment is treated as inclusive at the very end so the exact end-of-span epoch
resolves to the last segment. An epoch exactly on an interior boundary resolves
to the later segment (where it is the in-window start), which is
deterministic. Selection is O(segments); the segment count is modest and the
ordered list is binary-searchable if it ever grows large. An epoch before t0
or after t1 returns {:error, :out_of_range}.
Summary
Functions
Evaluate the piecewise model error against truth samples.
Fit a piecewise model over a span, one contiguous Sidereon.GNSS.ReducedOrbit segment
per :segment_s seconds.
Position of the piecewise model at epoch, ECEF (ITRF) meters by default.
Position and velocity of the piecewise model at epoch.
Select the segment whose coverage interval contains epoch.
Types
@type epoch() :: Sidereon.GNSS.ReducedOrbit.epoch()
@type segment() :: %{ t0: NaiveDateTime.t(), t1: NaiveDateTime.t(), model: Sidereon.GNSS.ReducedOrbit.t() }
@type t() :: %Sidereon.GNSS.ReducedOrbit.Piecewise{ frame: String.t(), model: String.t(), segment_s: number(), segments: [segment()], time_scale: String.t(), version: pos_integer(), window: {NaiveDateTime.t(), NaiveDateTime.t()} }
Functions
@spec drift(t(), [{epoch(), {number(), number(), number()}}], keyword()) :: {:ok, map()} | {:error, term()}
Evaluate the piecewise model error against truth samples.
Returns
{:ok, %{per_epoch: [%{epoch:, error_m:}], max_m:, rms_m:, threshold_horizon:,
requested:, used:}}matching the single-segment Sidereon.GNSS.ReducedOrbit.drift/3 report. Epochs outside
the model's span are skipped (counted in requested, not used).
threshold_horizon is the first epoch the ECEF error exceeds :threshold_m
(or nil).
@spec fit( [{epoch(), {number(), number(), number()}}], keyword() ) :: {:ok, t()} | {:error, term()}
Fit a piecewise model over a span, one contiguous Sidereon.GNSS.ReducedOrbit segment
per :segment_s seconds.
Accepts a list of {epoch, {x_m, y_m, z_m}} ECEF samples.
Options
:window-{t0, t1}epochs bounding the full span (t1strictly aftert0, else:invalid_window):segment_s- positive segment length in seconds, e.g.7200(non-positive →:invalid_segment):model-:circular_secular(default) or:eccentric_secular:time_scale- the scale the sample epochs are in
Segments are contiguous (seg_t1 of one is seg_t0 of the next); the final
segment may be shorter. A :segment_s at least the full span yields a single
segment equal to the whole window (piecewise with one segment ≡ single).
Returns {:ok, %Sidereon.GNSS.ReducedOrbit.Piecewise{}} or a tagged error. The error
set is exactly the single model's fit errors ({:too_few_samples, got, req},
:invalid_window, {:unsupported_model, m}, {:unsupported_source_frame, f},
{:unsupported_time_scale, s}, :transform_unavailable, …) plus
:invalid_segment. A too-few-samples failure on a non-terminal segment is
surfaced (a genuinely under-covered interior span is an error, not a silent
hole); only the terminal short segment may be dropped. If nothing fits at all,
{:error, {:too_few_samples, 0, 4}}.
@spec position(t(), epoch(), keyword()) :: {:ok, Sidereon.GNSS.ReducedOrbit.vec3()} | {:error, term()}
Position of the piecewise model at epoch, ECEF (ITRF) meters by default.
Selects the segment covering epoch and delegates to
Sidereon.GNSS.ReducedOrbit.position/3. Pass frame: :gcrs for the inertial position.
An epoch outside the full span returns {:error, :out_of_range}.
Position and velocity of the piecewise model at epoch.
Selects the covering segment and delegates to
Sidereon.GNSS.ReducedOrbit.position_velocity/3. An epoch outside the full span
returns {:error, :out_of_range}.
Select the segment whose coverage interval contains epoch.
Returns {:ok, segment} or {:error, :out_of_range}. Interior boundaries
resolve to the later segment; the exact end-of-span epoch resolves to the last
segment.