# Changelog
All notable changes to this project are documented here.
The format is based on [Keep a Changelog](https://keepachangelog.com/),
and this project aims to adhere to [Semantic Versioning](https://semver.org/).
**Versioning policy:** the project remains in the `0.x.y` range while its public API is still
evolving. Per Cargo's SemVer rules, during `0.x` the *minor* version carries breaking changes
(`0.2.0` → `0.3.0`) and the *patch* version carries compatible ones (`0.2.0` → `0.2.1`).
A `1.0.0` release is reserved for a deliberately committed-stable API.
## [Unreleased]
## [0.4.0] - 2026-06-22
First [crates.io](https://crates.io/crates/ephemerust) release: teaching-grade astronomy,
orbital-mechanics, and satellite-tracking library + `ephemerust` CLI (`cargo add ephemerust`,
`cargo install ephemerust`).
### Added
- **`PropagationModel` and `--afspc`** — library functions `propagate_with_model`,
`subpoint_with_model`, `look_angles_with_model`, `predict_passes_with_model`, and
`ground_track_with_model` accept [`PropagationModel::AfspcCompatibility`] for WGS72 +
legacy AFSPC sidereal/epoch (Vallado reference). The `track` command adds `--afspc`; JSON
output includes `propagation_model`.
- **Satellite-tracking plan cleanup** — milestones M0–M9 marked complete; stale “Await sign-off”
gates removed; deferred work consolidated under **Future work** in
[`docs/satellite-tracking-plan.md`](docs/satellite-tracking-plan.md).
- **Documentation** — corrected WGS84 default propagation vs optional AFSPC path in the plan,
[`docs/accuracy-and-limits.md`](docs/accuracy-and-limits.md), and module rustdoc (replacing
the outdated “WGS72 propagator vs WGS84 geodetic” wording).
- **M2 / M3 regression tests** — `propagation_diverges_*` (official `sgp4` decay/divergence
TLEs) and `subpoint_*` boundary tests (equator, polar latitude, ±180° longitude wrap).
- **Library polish (Milestone 9)** — Cargo feature `network` gates the `track --tle-url` CLI
flag (stub handler unchanged); default integration tests assert clap rejects the flag when
disabled. `examples/track_subpoint.rs` demonstrates `Tle` + `subpoint` from the library.
Crate rustdoc adds MSRV/feature notes, `#![deny(rustdoc::broken_intra_doc_links)]`, and the
satellite-tracking plan records milestones **8–9** signed off with a crates.io publication
deferral for `0.x`.
- **SGP4 teaching layer (Milestone 8)** — new `docs/sgp4.md` (near-Earth SGP4 narrative,
references, Ephemerust layering) and [`sgp4_teaching`](src/sgp4_teaching.rs): WGS-72
mean-motion → **a**, linear mean-anomaly advance, two-body state vs `sgp4` oracle checks.
Not used by production propagation. See [docs/sgp4.md](docs/sgp4.md).
- **Track CLI (Milestone 7)** — `track` supports `--mode` (`all`, `tle`, `state`, `subpoint`,
`look`, `passes`, `ground`), `--format human|json` for a unified JSON document, exclusive
TLE inputs (`--tle-file`, `--tle`, optional `--tle-url` with `--features network`), and validation for pass/ground
modes. Library: `Serialize` on `TemeState`, `LookAngles`, and `Pass` for machine-readable
output. See [docs/satellite-tracking-plan.md](docs/satellite-tracking-plan.md).
- **Ground track (Milestone 6)** — `ground_track` samples `[window_start, window_end)` every
`step` into `GroundTrackSample` (UTC + `Subpoint`); `ground_track_to_csv` and
`ground_track_to_json` for plotting pipelines. `track` adds `--ground-track-hours`,
`--ground-track-step-sec`, and `--ground-track-json`. See
[docs/satellite-tracking-plan.md](docs/satellite-tracking-plan.md).
- **Pass prediction (Milestone 5)** — `predict_passes` finds passes over `[window_start,
window_end)` with a coarse elevation ladder and bisection-refined AOS/LOS; culmination via
ternary search; [`Pass`] now includes azimuths at AOS/LOS. GEO-like element sets use a
simplified all-or-nothing visibility. `track` accepts `--predict-passes-hours` and
`--pass-min-elevation-deg`. See [docs/satellite-tracking-plan.md](docs/satellite-tracking-plan.md).
- **Look angles (Milestone 4)** — `look_angles(tle, time, observer)` produces azimuth
(clockwise from true north), elevation, slant range, and range rate via an **ENU** (east–
north–up) rotation from the observer–satellite ECEF vector; satellite velocity uses the same
TEME→ECEF rotation as position plus **ω** × **r**, and the observer station includes **ω** ×
**r_obs**. `track` prints look angles at epoch with Everett, WA as the default observer
(`--latitude` / `--longitude` override). See [docs/satellite-tracking-plan.md](docs/satellite-tracking-plan.md).
- **Frame conversions & subpoint (Milestone 3)** — `teme_to_ecef` rotates TEME position into
`Ecef` using the same GMST Z-rotation as ECI→ECEF; WGS84 `ecef_to_geodetic_wgs84` / `Geodetic`
and `ecef_to_geodetic` → `Subpoint`; `subpoint` chains propagate → TEME→ECEF→geodetic.
Bowring latitude with equator/pole/longitude tests; `track` prints the sub-satellite point at
epoch. See [docs/satellite-tracking-plan.md](docs/satellite-tracking-plan.md).
- **Documentation alignment** — added thorough Rustdoc to every public item across `lib.rs`,
`time.rs`, `coordinates.rs`, `celestial.rs`, `orbital.rs`, `planets.rs`, and `satellite.rs`,
each with a runnable example and the physical reasoning behind it (why SGP4 output is in the
TEME frame, why sidereal time bridges Earth-fixed and inertial frames, why the horizon
corrections differ for point sources versus the Sun/Moon, and so on). Added a crate-level
overview positioning Ephemerust as a teaching-grade wrapper between the `sgp4` engine and
full mission toolkits, enabled `#![warn(missing_docs)]` to keep the public surface
documented, and made `cargo clippy --all-targets` and `cargo doc` clean. Doctests grew from
6 to 20. Renamed `Planet::from_str` to `Planet::from_name` to avoid confusion with the
`std::str::FromStr` trait.
- **Educational error handling** — introduced a structured `satellite::TleError` enum whose
every variant explains *what was expected*, *what was found*, and *the underlying rule* of
the fixed-column TLE format (wrong line count, non-ASCII, short line, wrong line number,
checksum-not-a-digit, checksum mismatch, catalog mismatch, unparseable field with its named
column range, and epoch problems). `TleError::hint()` (exposed via `AstroError::hint()`)
returns a corrective next step. `TleError` folds into `AstroError` via
`#[error(transparent)] Tle(#[from] TleError)`, so callers can match precise variants. The
CLI now renders errors as `Error:`/`Hint:` lines on stderr with a non-zero exit code instead
of the default `Debug` rendering. Added a `tests/cli_track.rs` integration test covering the
malformed-TLE path end to end.
- **crates.io preparation** — completed the package manifest with `documentation`, `readme`,
and an explicit `rust-version` (MSRV **1.88**, set by the `sgp4` dependency), refined the
`description`, and documented the project's `0.x`-until-stable versioning policy.
- **Propagation wrapper (Milestone 2)** — `satellite::propagate(tle, time)` wraps the `sgp4`
engine, converting a UTC datetime to the engine's minutes-since-epoch representation and
returning the satellite's `TemeState` (TEME-frame position in km and velocity in km/s).
Parse failures, invalid epoch constants, unrepresentable times (nanosecond overflow), and
propagation divergence are all mapped to actionable `SatelliteError`s. The `track` command
now also reports the TEME state at the element-set epoch. Validated against the published
Vallado verification vector for satellite 00005 (within the documented WGS84/IAU-vs-AFSPC
model tolerance), with a cross-check that the wrapper's UTC→minutes path matches direct
engine calls across several offsets. See [docs/satellite-tracking-plan.md](docs/satellite-tracking-plan.md).
- **TLE ingestion & parsing (Milestone 1)** — `Tle` now parses and validates standard 2- and
3-line element sets, exposing the catalog number, classification, international designator,
epoch (UTC), and the full orbital element set as typed fields. Validation covers ASCII/line
length, line numbers, matching catalog numbers, and modulo-10 checksums, each with
actionable error messages. TLEs can be read from a file (`Tle::from_file`) or an inline
string (`Tle::parse`). The `track` command now loads a TLE (`--tle-file` / `--tle`) and
prints a parsed summary. A validation test cross-checks every parsed field against the
`sgp4` crate's own parser. See [docs/satellite-tracking-plan.md](docs/satellite-tracking-plan.md).
- **Satellite-tracking foundation (Milestone 0)** — added the `sgp4` crate dependency and a
new `satellite` module documenting the frame/unit conventions (TEME → ECEF → WGS84
geodetic) and defining the public type stubs `Tle`, `TemeState`, `Subpoint`, `LookAngles`,
and `Pass`. Added a `SatelliteError` variant to `AstroError` and a `track` CLI subcommand
stub. A smoke test propagates a canonical ISS element set through the `sgp4` engine and
asserts a physically plausible low-Earth-orbit radius. See
[docs/satellite-tracking-plan.md](docs/satellite-tracking-plan.md).
- **`orbital` command** now computes results instead of echoing inputs: orbital period,
true anomaly, and the orbital-elements → state-vector conversion. New optional flags
`--raan`, `--arg-periapsis`, `--mean-anomaly`, and `--mu` (defaulting to Earth's μ).
- **Planet positions for all eight planets** via truncated VSOP87D, including Earth's
series (required for the geocentric conversion). Geocentric RA/Dec agrees with JPL
Horizons to within a few arcminutes at J2000.0.
- **Planet rise/set times** — the `rise-set` command now supports planets in addition to the
Sun and Moon, using a shared rise/set routine with a point-source horizon correction.
### Fixed
- **VSOP87 time argument** now uses Julian *millennia* (`τ = (JD − 2451545)/365250`) instead
of Julian centuries.
- **VSOP87 series scaling** — removed an erroneous `/10⁸` factor; VSOP87D spherical
coefficients are stored directly in radians (L, B) and AU (R). Together with the time-unit
fix, this makes planet positions correct (previously they collapsed toward zero).
### Planned next steps
See [docs/roadmap.md](docs/roadmap.md):
- Extend the VSOP87 series with more terms toward arcsecond accuracy.
- Begin Part 2: the space-weather REST service.
## [0.2.0] - 2024-12
Second development phase. Added Earth-centered coordinate transforms and the foundation
of a planetary ephemeris.
### Added
- **ECEF ↔ ECI coordinate transformations** using a GMST-based Z-axis rotation matrix,
in both directions, with input validation (NaN/infinity), GMST normalization, and
round-trip accuracy within ~1 mm at Earth scale.
- Extended the `convert` command with `ecef`/`eci` source/target options and an
optional `--gmst` flag (auto-calculated from the current time when omitted).
- **VSOP87 planetary position foundation** (`planets.rs`):
- `Planet` enum (Mercury–Neptune), `Vsop87Term`/`Vsop87Series`/`PlanetVsop87Data`
structures stored as compile-time constants.
- VSOP87 series evaluation (`A·cos(B + C·t)` summed across L0–L5, B0–B4, R0–R4).
- Full coordinate pipeline: heliocentric ecliptic (L, B, R) → equatorial (rotation by
obliquity) → geocentric (subtract Earth) → RA/Dec.
- `position` command extended to accept planet names (case-insensitive).
- Convenience re-exports in `lib.rs` for common types and functions.
- Educational/technical documentation for the above (now in [docs/](docs/)).
### Changed
- Enhanced error handling with contextual, actionable messages.
- Multi-level logging (Debug/Info/Warn/Error) across coordinate and planet calculations.
- Performance optimizations (pre-computed time powers for VSOP87 series).
### Test coverage
- 71 unit tests + 5 doctests, all passing.
- 15 tests for ECEF/ECI transformations; 26 tests for VSOP87 / planet positions,
including performance benchmarks (< 1 ms per planet calculation).
### Known limitations
- Earth's VSOP87 data is a placeholder; only Mercury has truncated coefficients, so
geocentric planet positions are not yet meaningful.
- Planet rise/set times are not implemented (the command returns an error).
- No precession/nutation (J2000.0 assumed), no light-time correction, no atmospheric
refraction.
- The CLI `orbital` command is a stub that prints its inputs; the underlying library
functions are implemented and tested.
## [0.1.0] - Initial phase
First development phase: a command-line astronomy toolset covering the core time,
coordinate, and orbital calculations.
### Added
- **Time systems** (`time.rs`): Julian Date, Greenwich Mean Sidereal Time (GMST),
Local Sidereal Time (LST).
- **Solar position**: Sun RA/Dec via mean anomaly + equation of center.
- **Lunar position**: Moon RA/Dec via perturbation theory (periodic terms).
- **Rise/set times** for the Sun and Moon at any location.
- **Coordinate conversions**: RA/Dec ↔ Alt/Az.
- **Orbital mechanics** (`orbital.rs`): Kepler's equation solver, orbital period
(Kepler's Third Law), orbital elements → state vectors.
- CLI scaffolding (clap subcommands), logging, and error-handling infrastructure.
### Development notes
This phase was originally tracked as Phases 1–4 (basic structure, core astronomy,
coordinate conversions, lunar & orbital mechanics), each with comprehensive unit tests.
The detailed per-step implementation log that previously lived in the README has been
condensed into the entries above.