Tidal prediction by harmonic synthesis
Weather is chaotic, so a weather-derived marine feed decays with its horizon and returns a different answer tomorrow than it returned today. Tides are not weather. They are driven by the relative motion of Earth, Moon and Sun — orbital mechanics, known to extraordinary precision and running on a clock that does not care what century you ask about.
Fit a station once against its observation record and the station is solved. The same ten numbers that give you Tuesday at 0412 give you the afternoon of 3 March 2184, at the same confidence, on a phone in airplane mode.
Move the scale. The curve does not degrade, because there is nothing in it to decay.
Every speed above is a physical constant in degrees per mean solar hour, fixed by orbital period. Only amplitude H and phase lag g belong to the station. Each preview wave is drawn to its own height, so every constituent's period stays legible however small it is. The previews compare shape, never size — amplitude is the H column.
Harmonic synthesis gives the astronomical tide. Observed water level also carries a meteorological residual — storm surge, barometric pressure, wind setup — and that part is weather, with all of weather's honesty problems.
We do not fold the two into one confident number. The astronomical tide is returned at full weight because it is knowable. The residual is returned as a bounded band, drawn thin, because it is not.
Astronomical tideDeterministic. Evaluated from the constituent set, identical every time you ask.
Observed levelWhat the gauge actually read. Astronomical tide plus whatever the weather added.
Meteorological residualBounded, not predicted. Line weight carries confidence, so the uncertain part never looks certain.
// The fit. Speeds are physical constants; H and g are the station's.
const CALDER_SOUND = [
// σ °/h H (m) g (°)
[28.9841042, 3.952, 184.2], // M2
[30.0000000, 1.318, 231.7], // S2
[28.4397295, 0.762, 165.8], // N2
[30.0821373, 0.359, 229.4], // K2
[57.9682084, 0.211, 88.5], // M4
[58.9841042, 0.147, 142.0], // MS4
[15.0410686, 0.138, 118.6], // K1
[13.9430356, 0.104, 342.9], // O1
[14.9589314, 0.046, 115.2], // P1
[13.3986609, 0.029, 331.4], // Q1
];
const DEG = Math.PI / 180;
// Metres above chart datum, at any instant, for ever.
export const level = (hours) =>
6.10 + CALDER_SOUND.reduce(
(h, [σ, H, g]) => h + H * Math.cos((σ * hours - g) * DEG),
0
);
Notes
Resolve a station once and you hold its constituent set. Every prediction after that is arithmetic on the client — no round trip, no rate limit, no signal.
The lunar node precesses on an 18.61-year cycle, so amplitudes and phases drift. Long-horizon predictions correct for it. This is where naïve implementations quietly lose centimetres.
Heights are returned against a declared datum, stated on every response. A number without its datum is not a measurement, and confusing chart datum with sea level is how vessels find the bottom.
Accuracy follows the length and quality of the observations behind the fit. Stations report their record length so you can decide whether to trust the eighth decimal.