101 lines
4.0 KiB
Markdown
101 lines
4.0 KiB
Markdown
# @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.
|