Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,17 @@ Work is organized in titled sprints. Ben approves each title in chat, and the ag

The old weeks 3–4 ("All 15 classes, noise, split; second-reviewer check") are split into Sprints 3 and 4. Later phases are renumbered one per sprint.

### Web v1 (parallel track)

Interactive globe in `web/`. It is independent of the Python benchmark: `make test` and `make reproduce` must not require Node. **v2** (onboard diagnosis story) starts only after committed files exist in `results/`. Sprint titles below are *(title pending Ben's approval)*.

| Sprint | Work | Deliverable |
|---|---|---|
| 12: Isolated Vite globe shell *(title pending Ben's approval)* | `web/` Vite + React + TypeScript + Vitest; CI job; no Python coupling | `cd web && npm install && npm run dev` and `npm test` |
| 13: TLE cache and SGP4 *(title pending Ben's approval)* | Fetch CelesTrak GP TLEs, cache with fetch time, propagate with `satellite.js` | Unit-tested TLE parse + ECI/ECF position |
| 14: Nairobi contact windows *(title pending Ben's approval)* | Look angles, elevation mask, next-pass countdown | Rise / max elevation / set for Nairobi, tested |
| 15: Live globe, picker and credits *(title pending Ben's approval)* | CesiumJS globe, moving sat, highlighted next pass, picker, credits; Heavens-Above/N2YO check | v1 acceptance: moving satellite, pass times within ~1 min of an independent source |

## 9. Hardware and costs (approximate; check local prices)
- Laptop he already owns (development and the main grid): KES 0.
- Raspberry Pi 5 (8 GB, so the cap is enforced in software) + NVMe/SD + PSU: about USD 120–150 **[verify the Nairobi price]**.
Expand Down
32 changes: 32 additions & 0 deletions docs/tasks/sprint12-web-scaffold.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Sprint 12: Isolated Vite globe shell

- **Sprint:** 12: Isolated Vite globe shell *(title pending Ben's approval)*
- **Owner:** Ben's agent
- **Branch:** `feat/web-vite-scaffold`
- **PR title:** `feat(web): add isolated Vite React TypeScript app`

## Goal
A `web/` app with its own `package.json` that installs and runs without touching the Python benchmark. `make test` and `make reproduce` still work with no Node.

## Context
The visualization brief (`web-agent-brief-visualisation`) is v1 first: a Cesium globe later; this sprint only proves the Node island. Stack: Vite + React + TypeScript + Vitest. Pin exact npm versions. Inspired by [Skyhitch](https://skyhitch.app/) / [God's Eye View](https://github.com/bilawalsidhu/gods-eye-view) (MIT), but this PR does not copy their code.

Cesium stays out of this sprint so CI stays light and the globe work is one PR (`feat/web-orbit-contact-windows` in sprint 15).

## Steps
1. Scaffold `web/` (Vite React-TS). Pin versions. Add Vitest with one smoke test.
2. Root `.gitignore`: `web/node_modules/`, `web/dist/`.
3. `web/README.md`: `npm install`, `npm run dev`, `npm test`. State that this app never computes benchmark numbers.
4. Placeholder page: what OrbitLog is, that v1 will show real orbits, that diagnosis UI is v2 and **pending** until `results/` exists. No fake figures.
5. Optional `make test-web` that runs `npm test` in `web/`. Do **not** fold it into `make test`.
6. GitHub Actions: a `web` job on PRs that touch `web/**`, using Node 22 (matches the local toolchain used to pin packages).

## Acceptance criteria
- [ ] `cd web && npm install && npm run dev` serves the placeholder.
- [ ] `cd web && npm test` passes.
- [ ] `make test` still runs pytest only (no Node).
- [ ] `make test` passes locally and in CI.

## Out of scope
- Cesium, TLEs, SGP4, pass prediction (sprints 13–15).
- Anything under `src/orbitlog/` or `results/`.
35 changes: 35 additions & 0 deletions docs/tasks/sprint13-web-tle-sgp4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Sprint 13: TLE cache and SGP4

- **Sprint:** 13: TLE cache and SGP4 *(title pending Ben's approval)*
- **Owner:** Ben's agent
- **Branch:** `feat/web-tle-sgp4`
- **PR title:** `feat(web): fetch CelesTrak TLEs and propagate with SGP4`

## Goal
The web app can load a Two-Line Element set (TLE) from CelesTrak, cache it with the fetch time, and turn it into a position at a given UTC instant using SGP4 (`satellite.js`). No globe yet.

## Context
Operators plan **contact windows** by propagating published TLEs with **SGP4**. CelesTrak GP queries are `https://celestrak.org/NORAD/elements/gp.php?{QUERY}=VALUE[&FORMAT=VALUE]` ([GP data formats](https://celestrak.org/NORAD/documentation/gp-data-formats.php)). `{QUERY}` must be uppercase. **Always pass `FORMAT=TLE`**: as of 2026-05-09 the default format is CSV, not TLE.

v1 catalog (fixed CATNRs, not a live group dump):
- ISS (ZARYA), CATNR **25544**
- LANDSAT 9 (Earth observation), CATNR **49260** ([SATCAT](https://en.wikipedia.org/wiki/Landsat_9))
- One CubeSat whose TLE still exists on CelesTrak `GROUP=cubesat` at implementation time (record the CATNR and a live fetch in the PR)

Comment in plain words: a TLE is a snapshot of an orbit; SGP4 is the standard propagator; it is good for days, not months.

## Steps
1. Depend on pinned `satellite.js`.
2. Fetch `gp.php?CATNR=<id>&FORMAT=TLE`, parse name + two lines, store in `localStorage` (or equivalent) with `fetchedAt` ISO UTC. Show fetch time in a small status line.
3. `propagateEci(tle, date)` using `satellite.twoline2satrec` + `satellite.propagate`.
4. Vitest: parse a **fixture** TLE (committed, not live-fetched in CI); reject truncated lines; cache round-trip including `fetchedAt`. Live CelesTrak is manual / documented in the PR, not required for CI.

## Acceptance criteria
- [ ] Unit tests pass with no network in CI.
- [ ] A live fetch of ISS 25544 is pasted into the PR (raw TLE + timestamp).
- [ ] UI (even the placeholder) shows TLE fetch time or “not fetched”.
- [ ] `make test` (Python) still passes without Node.

## Out of scope
- Look angles, Nairobi passes, Cesium (sprints 14–15).
- Diagnosis / `results/` (v2).
39 changes: 39 additions & 0 deletions docs/tasks/sprint14-web-nairobi-passes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Sprint 14: Nairobi contact windows

- **Sprint:** 14: Nairobi contact windows *(title pending Ben's approval)*
- **Owner:** Ben's agent
- **Branch:** `feat/web-nairobi-passes`
- **PR title:** `feat(web): predict Nairobi contact windows from look angles`

## Goal
Given a TLE and “now”, compute the next pass over Nairobi: rise time, max elevation, set time, and a live countdown. Pure functions, fully unit-tested. The globe still comes in sprint 15.

## Context
A **contact window** is the stretch of time the satellite is above a minimum elevation at the ground station. **Look angles** are azimuth (compass direction) and elevation (angle above the horizon) from that station to the satellite.

**Nairobi station** (city, not a specific dish), WGS-84:
- latitude **−1.286389°**, longitude **36.817223°** (1°17′11″S, 36°49′02″E, [Wikipedia: Nairobi](https://en.wikipedia.org/wiki/Nairobi))
- altitude **1795 m** (same article, elevation of the city)

**Elevation mask:** default **10°** (a typical “usable pass” cut, and the usual Heavens-Above default). Also compute **0°** internally so the Heavens-Above / N2YO comparison in sprint 15 can match their horizon setting. Document the mask in the UI.

`satellite.gstime` + `eciToEcf` + `ecfToLookAngles` in `satellite.js` give azimuth/elevation. A pass is a contiguous run of samples with elevation ≥ mask.

OrbitLog’s story: windows are about 5–10 minutes, a few times a day. Do not invent byte counts here.

## Steps
1. `lookAngles(tle, station, date)` → az, el, range.
2. `nextPass(tle, station, from, maskDeg)` → `{ rise, maxEl, maxElAt, set }` or none in the search window (search ≥ 24 h, 1 s or better near crossings).
3. `countdown(to, now)` → remaining seconds (clamp at 0).
4. Vitest with the sprint 13 ISS fixture: a known-in-test pass (freeze `now`); countdown arithmetic; mask 10° vs 0° changes rise/set.
5. Placeholder UI: next ISS pass over Nairobi (rise, max el, set, countdown) once a TLE is cached.

## Acceptance criteria
- [ ] Unit tests pass with no network.
- [ ] UI shows rise, max elevation, set, countdown, elevation mask, and TLE epoch/fetch time.
- [ ] `make test` (Python) still passes without Node.

## Out of scope
- Independent Heavens-Above / N2YO check (sprint 15 PR, needs the live globe and a dated screenshot).
- Cesium rendering, satellite picker.
- v2 diagnosis panels (show **pending** if anything is sketched).
35 changes: 35 additions & 0 deletions docs/tasks/sprint15-web-cesium-globe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Sprint 15: Live globe, picker and credits

- **Sprint:** 15: Live globe, picker and credits *(title pending Ben's approval)*
- **Owner:** Ben's agent
- **Branch:** `feat/web-orbit-contact-windows`
- **PR title:** `feat(web): show a live satellite and Nairobi contact windows`

## Goal
v1 of the visualization: a CesiumJS globe with a real satellite moving on its SGP4 track, Nairobi marked, the next contact highlighted, a few satellites to pick, and credits. Pass times agree with an independent source to about a minute.

## Context
Closes the v1 acceptance list in the visualization brief. Use CesiumJS (or Resium) plus the sprint 13–14 libraries. Official Vite asset wiring: copy Workers/ThirdParty/Assets/Widgets and set `CESIUM_BASE_URL` ([Cesium + Vite](https://cesium.com/blog/2024/02/13/configuring-vite-or-webpack-for-cesiumjs/)). Prefer OSM / Ion-optional imagery so the app runs without a paid token; if `VITE_CESIUM_ION_TOKEN` is set, use it.

Satellites: ISS 25544, Landsat 9 49260, plus the CubeSat CATNR chosen in sprint 13.

Honesty: no diagnosis numbers. Any v2 panel is **pending**. Faults are not shown.

## Steps
1. Pin `cesium` (and Resium only if it earns its keep). Wire static assets.
2. Viewer: Earth, Nairobi point, satellite entity updated from SGP4, orbit polyline, next-pass arc highlighted.
3. HUD: countdown, rise / max el / set, TLE fetch time, elevation mask, satellite picker.
4. In-app credits: CelesTrak, Cesium, imagery provider, satellite.js; mention Skyhitch / God's Eye View as inspiration (MIT) without copying code.
5. Vitest stays on orbital math (already in 13–14). Do not try to unit-test WebGL.
6. **Verification (required in the PR):** ISS over Nairobi vs [Heavens-Above](https://heavens-above.com/) or [N2YO](https://www.n2yo.com/). Use the same mask those sites use (state it). Rise/set within **~1 minute**. Paste times, URLs, and a screenshot.

## Acceptance criteria
- [ ] `cd web && npm install && npm run dev` shows a real satellite moving.
- [ ] Next Nairobi pass (rise, max elevation, set) shown; independent check within ~1 min, recorded in the PR.
- [ ] Unit tests pass; load feels under ~5 s on a normal laptop (note machine in the PR).
- [ ] Credits visible in the app.
- [ ] `make test` (Python) still passes without Node.

## Out of scope
- v2: fault injection playback, byte comparison, downlink queue (needs committed `results/`).
- Any fabricated accuracy or byte figures.
Loading