1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
//! Tides: height between high and low water, secondary port corrections, tidal
//! stream as a [`CurrentModel`].
//!
//! From tide-table high and low waters at a standard port to the height or time
//! needed at a nearby place, in three steps:
//!
//! 1. [`SecondaryPort`] — Admiralty time and height differences applied to the
//! standard port's high and low waters.
//! 2. [`TidalCycle`] — one rise or fall between turning points: height at any
//! time by the rule of twelfths, and time of any height by its inverse.
//! 3. [`TidalStream`] — tidal diamond: set and rate hour by hour relative to
//! HW, springs and neaps, interpolated for the day's range and the requested
//! time, as a [`CurrentModel`].
//!
//! # Rule of twelfths
//!
//! Over a six-hour rise the tide gains 1, 2, 3, 3, 2, 1 twelfths of the range
//! per hour: a piecewise-linear approximation of a cosine, accurate to a few
//! centimetres for regular semi-diurnal tides. [`TidalCycle::height_at`]
//! applies it scaled to the actual cycle duration. Irregular tides (Solent
//! double high waters, diurnal tides) are out of scope; their tables give
//! dedicated curves.
//!
//! Heights are above chart datum, as tabulated; no datum conversion.
//!
//! ```rust
//! use kinavis::tides::{SecondaryPort, TidalCycle, TideEvent};
//! use kinavis::{Civil, Distance, Instant, Utc};
//!
//! let at = |hour, minute| Instant::<Utc>::from_civil(Civil { hour, minute, ..Civil::date(2026, 9, 15) });
//!
//! // The standard port: low water 0412 0.8 m, high water 1030 4.6 m.
//! let low = TideEvent::new(at(4, 12)?, Distance::from_metres(0.8)?);
//! let high = TideEvent::new(at(10, 30)?, Distance::from_metres(4.6)?);
//!
//! // The secondary port, from the tables: HW +0020, LW +0035, heights −0.4 and −0.1.
//! let port = SecondaryPort::new()
//! .high_water(20, Distance::from_metres(-0.4)?)
//! .low_water(35, Distance::from_metres(-0.1)?);
//! let rise = TidalCycle::new(port.adjust_low_water(low), port.adjust_high_water(high))?;
//!
//! // The height at 0800, and when there is first 3 m of water.
//! assert_eq!(format!("{:.2}", rise.height_at(at(8, 0)?)?.metres()), "2.62");
//! let enough = rise.time_of_height(Distance::from_metres(3.0)?)?;
//! assert_eq!(format!("{}", enough), "2026-09-15T08:26:31.714 UTC");
//! # Ok::<(), kinavis::NavigationError>(())
//! ```
pub use ;
pub use ;
// Imported for the module docs link.
use crateCurrentModel;