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

4.0 KiB
Raw Blame History

@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?).

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.