orbitforge od bls
Synopsis
orbitforge od bls --obs <PATH> --stations <PATH> --initial <PATH> --epoch <RFC3339> [OPTIONS]Description
Batch least squares estimates one state at one epoch using all observations in the arc at once. It iterates: propagate from the current estimate, compute the residual against every measurement, solve for the correction that minimizes the weighted sum of squares, apply it, repeat.
This is the classic orbit determination method, and it is the right choice when you have a complete arc and want the best single answer with a covariance.
Options
| Parameter | Type | Unit | Default | Required | Description |
|---|---|---|---|---|---|
--obs | path | n/a | — | Yes | Tracking CSV, as produced by `od simulate-tracking --csv`. |
--stations | path | n/a | — | Yes | Tracking-stations JSON. Must describe every station appearing in the observation file. |
--initial | path | n/a | — | Yes | Keplerian-elements JSON, the initial guess at the solution epoch. |
--epoch | string | RFC 3339 UTC | — | Yes | Solution epoch. Observations must not precede it. |
--force | enum | n/a | j2 | No | Reference force model: `two-body`, `j2`, or `j4`. |
--edit-sigma | float | sigma | 4 | No | Residual editing threshold. Observations beyond this many sigma are excluded. Zero disables editing. |
--max-iterations | integer | count | 10 | No | Iteration cap. Reaching it without convergence is a failure, not an answer. |
--json | path | n/a | — | No | Write the full solution: state, covariance, and per-observation residuals. |
Worked example
Fitting the 220 observations generated by
od simulate-tracking:
orbitforge od bls \
--obs obs.csv \
--stations stations.json \
--initial orbit.json \
--epoch 2026-01-01T00:00:00ZBLS converged in 3 iterations: RMS 1.030, 220 used / 0 edited.
Position sigma [0.0006, 0.0008, 0.0009] km; velocity sigma [6.12e-7, 9.65e-7, 9.25e-7] km/s.Reading the output
| Figure | Value | Meaning |
|---|---|---|
| Iterations | 3 | How many corrections were needed. Fewer is better; hitting the cap means it did not converge |
| RMS | 1.030 | Weighted root-mean-square residual, dimensionless |
| Used / edited | 220 / 0 | Observations accepted, and rejected by residual editing |
| Position sigma | sub-meter, per axis | Formal uncertainty in the estimated position at the epoch |
| Velocity sigma | sub-mm/s, per axis | Formal uncertainty in velocity |
An RMS near 1.0 is the target, and 1.030 is close to ideal. The residuals are weighted by each observation’s sigma, so an RMS of 1 means the fit disagrees with the data by about as much as the stated measurement noise, which is exactly right.
Much above 1 means the model cannot explain the data: wrong force model, a station in the wrong place, or an epoch mismatch. Much below 1 means the stated sigmas are pessimistic, and the covariance is then too conservative to be useful.
Zero edited observations is consistent with synthetic data at the same noise level the solver assumes. Against real tracking, some editing is normal.
Residual editing
--edit-sigma excludes observations whose residuals exceed the threshold, which
protects the solution from outliers: an epoch tagged wrongly or a transcription error
can otherwise drag the entire fit.
It is also a way to hide a real problem. If editing removes a substantial
fraction of the arc, the correct response is to find out why those observations
disagree, not to raise the threshold until they vanish. Setting --edit-sigma 0
disables editing and shows the unfiltered fit, which is worth doing once when
diagnosing.
Covariance is formal, not truth
The reported sigmas are the formal covariance from the fit: what the estimator believes given its own model and the stated measurement noise. They are optimistic whenever the force model is incomplete, because unmodeled acceleration appears as signal the estimator confidently absorbs.
A sub-meter formal sigma from a J2-only fit does not mean the orbit is known to sub-meter accuracy. It means the fit is internally consistent to sub-meter level.
Batch or filter
| Use batch least squares when | Use od ekf when |
|---|---|
| The whole arc is available | Observations arrive over time |
| You want one best epoch state | You want a current state that updates |
| You want a covariance for that epoch | You need to track a maneuvering or evolving object |
| Post-processing | Near real time |
Batch uses every observation to constrain one epoch, so it smooths over the whole arc. The filter propagates forward and never revisits an earlier measurement. That difference shows up directly in the reported uncertainty: compare these sigmas against the EKF’s on the same data.
See also
od simulate-trackingto generate input.od ekffor the sequential alternative.
main (pre-release)