open-standards/map/map-rs/crates/map-orbital/src/bridge.rs
Transaction Science ba1212361f Add aac, map, sandbox standards; catalog the standards family
Commit the previously-untracked standard directories and catalog them in the
root README alongside proof:

- aac/    — communication cargo class: portable boards + a ten-level
            offline-first cascade (Apache-2.0 code / CC-BY-4.0 spec).
- map/    — geospatial cargo class: range-requestable signed tilesets +
            an F0–F4 fidelity cascade (Apache-2.0 code / CC-BY-4.0 spec).
- sandbox/— isolated, metered, attested execution across the heterogeneous
            compute fabric (Apache-2.0 code / CC-BY-4.0 spec).
- jouleclaw-otel-authz, jouleclaw-trust — new jouleclaw reference crates.

Root README now lists aac, map, sandbox, and proof in the catalog, the tree,
and the contribution guide.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-07 17:04:36 -04:00

77 lines
2.8 KiB
Rust

//! Bridge from orbital propagation to the standard's address space.
//!
//! A live satellite occupies the `F3` (feature / live-entity) tier. Its
//! ground-relative address is its **sub-point**: the geodetic position of the
//! propagated state, projected to an S2 cell at the requested level, with the
//! altitude band for its altitude and the time bucket for the instant. This puts
//! a satellite on the same 4D grid (SPEC §2) as every other tile, so an entity
//! feed and a basemap share one index.
use chrono::{DateTime, Utc};
use map_index::volume::TimeTier;
use map_schema::{CellKey, Lod};
use crate::{satellite_position, GeodeticPosition, OrbitalResult, Sgp4State};
/// The [`CellKey`] for a geodetic position at an instant: S2 cell at `level`,
/// the altitude band for `alt_km`, and the time bucket at `tier`.
pub fn geodetic_cell_key(
geo: &GeodeticPosition,
level: u8,
time_secs: i64,
tier: TimeTier,
lod: Lod,
) -> CellKey {
map_index::cell_key(
geo.lat_deg,
geo.lon_deg,
level,
map_index::alt_band(geo.alt_km * 1000.0),
map_index::time_bucket(time_secs, tier),
lod,
)
}
/// Propagate `state` to `dt` and return the satellite sub-point's [`CellKey`].
pub fn subpoint_cell_key(
state: &Sgp4State,
dt: &DateTime<Utc>,
level: u8,
tier: TimeTier,
lod: Lod,
) -> OrbitalResult<CellKey> {
let geo = satellite_position(state, dt)?;
Ok(geodetic_cell_key(&geo, level, dt.timestamp(), tier, lod))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tle::parse_tle;
const ISS_NAME: &str = "ISS (ZARYA)";
const ISS_L1: &str = "1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9009";
const ISS_L2: &str = "2 25544 51.6416 247.4627 0006703 30.0000 10.0000 15.49560000000007";
#[test]
fn iss_subpoint_lands_in_leo_band() {
let tle = parse_tle(ISS_NAME, ISS_L1, ISS_L2).unwrap();
let state = Sgp4State::initialize(&tle).unwrap();
// Realtime tier (1 s buckets) is right for a live satellite.
let key = subpoint_cell_key(&state, &tle.epoch, 10, TimeTier::Realtime, Lod(0)).unwrap();
// The ISS orbits at ~408 km → the LEO altitude band (schema index 4).
assert_eq!(key.alt, map_schema::AltBand(4), "ISS should be in the LEO band");
// Horizontal address is a valid S2 cell at the requested level.
assert_eq!(key.s2.level(), Some(10));
// Realtime bucket is 1 s wide.
assert_eq!(key.time.width_ms, 1_000);
}
#[test]
fn geodetic_cell_key_maps_surface_to_band_zero() {
let geo = GeodeticPosition { lat_deg: 37.77, lon_deg: -122.42, alt_km: 0.0 };
let key = geodetic_cell_key(&geo, 14, 0, TimeTier::Recent, Lod(0));
assert_eq!(key.alt, map_schema::AltBand(0));
assert_eq!(key.s2.level(), Some(14));
}
}