Files
suaveplan/openspec/specs/tides/spec.md
2026-05-27 23:29:53 +01:00

101 lines
4.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# @suaveplan/tides — Tidal Prediction Capability
Universal-tier tidal prediction surface exposing the unified `TidalProvider`
contract from `@suaveplan/types`, three concrete provider factories
(harmonic / equilibrium-physics / ADMIRALTY API), harmonic-constituent math
with nodal corrections, equilibrium-tide physics, and tide-station search.
## Requirements
### Requirement: Harmonic prediction engine
The package SHALL expose `predictHeight(constituents, time, meanSeaLevel?)`
and `findExtremes(constituents, from, to, meanSeaLevel?)` computing tide
height per the harmonic formula `h(t) = H₀ + Σ f(t)·Hₙ·cos(ωₙ·t + V₀ₙ(t) − Gₙ)`
using the J2000 epoch (`2000-01-01T12:00:00Z`) as the time base.
#### Scenario: Empty constituent list rejected
- **WHEN** `predictHeight([], time)` is called with zero constituents
- **THEN** it MUST throw `TideError` with code `NO_CONSTITUENTS`.
#### Scenario: Nodal corrections applied
- **WHEN** `predictHeight` runs over named constituents (M2, S2, K1, …)
- **THEN** the per-constituent node factor `f` and astronomical argument
`V₀` MUST be derived from the time argument via `nodeFactor(name, time)` /
`astronomicalArgument(name, time)` rather than treated as static.
### Requirement: Harmonic-constituent registry
The package SHALL ship `CONSTITUENT_SPEEDS` (degrees/hour angular speeds)
and `DOODSON_NUMBERS` tables for the canonical major constituents
(M2, S2, N2, K2, K1, O1, P1, Q1, M4, …) re-exported from
`./harmonic/harmonic-constituents`.
### Requirement: Equilibrium-tide physics
The package SHALL expose `equilibriumTideHeight(planet, coord, time)` and
`findEquilibriumExtremes(planet, coord, date, signal?)` modelling the
solid-body equilibrium tide on a `PlanetConfig` ellipsoid (consumes
`@suaveplan/geo-planet`).
### Requirement: ADMIRALTY UK Hydrographic Office client
The package SHALL expose `AdmiraltyProvider` and `createAdmiraltyProvider`
binding the ADMIRALTY UK Tidal API
(`https://admiraltyapi.azure-api.net/uktidalapi/api/V1`) with:
- `Ocp-Apim-Subscription-Key` header taken from constructor option `apiKey`.
- Injectable `fetchFn` (defaults to `globalThis.fetch`).
- Configurable `cacheTtlMs` (default `3_600_000`).
#### Scenario: ADMIRALTY API key absent
- **WHEN** `createAdmiraltyProvider({ apiKey: "" })` is called
- **THEN** the resulting provider MUST throw `TideError` (`MISSING_API_KEY`)
on first network call.
### Requirement: Provider factory surface
The package SHALL expose three factories returning the unified
`TidalProvider` shape from `@suaveplan/types`:
- `createTiconProvider(constituents, meanSeaLevel?)` — offline harmonic.
- `createPhysicsBasedProvider(planet)` — equilibrium-tide approximation.
- `createAdmiraltyProvider(options)` — ADMIRALTY UK API.
All three MUST satisfy `predictHeight(planet, coord, time)` and
`getExtremes(planet, coord, date, signal?)`.
### Requirement: Tide-station search
The package SHALL expose `findNearestStation(stations, latitude, longitude)`
returning `NearestStationResult` with great-circle `distanceKm` computed
via `@suaveplan/geo` haversine math.
### Requirement: Typed error surface
The package SHALL expose a frozen `TideError` class with stable codes
covering `NO_CONSTITUENTS`, `MISSING_API_KEY`, `STATION_NOT_FOUND`, and
network/parse failure paths surfaced by the ADMIRALTY provider.
### Requirement: AbortSignal threading
Every async provider call (`predictHeight`, `getExtremes`,
`getNearestStation`, `getStations`) SHALL accept and honour an optional
`AbortSignal`, aborting in-flight ADMIRALTY HTTP requests via the underlying
`fetchFn`.
### Requirement: Universal-tier purity
The package MUST NOT import browser-only or Node-only APIs at module top
level — `fetch` is consumed only through the injectable `fetchFn` so the
package runs unchanged in browser, Bun, and Node environments.
### Requirement: Test + coverage gates
`bunx turbo lint typecheck test --filter=@suaveplan/tides` MUST be green
and `bunx turbo test:coverage --filter=@suaveplan/tides` MUST hit 100%
on touched modules before any release.