moontide

    Local-first tidal prediction with multi-station NOAA validation, written in MoonBit.

    tide
    ocean
    astronomy
    harmonic-analysis
    coastal
    Download zip
    Author
    Version
    0.3.0
    License
    Apache-2.0
    Last updated
    16 days ago
    Downloads
    36

    #MoonTide

    MoonTide 是一个使用 MoonBit 编写的本地优先潮汐预测工具。它把站点数据接入、调和常数换算、潮高预测、权威预测对照、边界报告和 CLI 输出放在同一个可测试工程中,既能直接生成潮汐信息,也能作为其他 MoonBit 项目复用潮汐计算的参考实现。

    #项目目标

    MoonTide 解决三个具体问题:

    1. 让真实潮汐站的元数据、调和常数和基准面数据可以直接进入计算流程。
    2. 让预测结果能够与权威机构发布的预测做逐时对照,而不是只展示内置演示数据。
    3. 让误差、分潮覆盖率和适用边界显式暴露,避免把科研或安全场景误用在普通活动建议之上。

    #为什么对 MoonBit 生态有用

    • 真实数据接入:已经支持 NOAA CO-OPS 的站点元数据、调和常数、基准面和预测 JSON。
    • 可复现验证:离线保存真实站点 fixture,并用 validate-reference 命令与 NOAA 预测逐时比较。
    • 数值计算示例:覆盖儒略日、天文参数、Doodson 数、结点改正、调和分潮求和和误差统计。
    • 工程化交付:包含库 API、CLI、JSON、Markdown、CSV、iCalendar、ASCII 曲线和 CI。
    • 边界清晰:未支持的 NOAA 分潮会被显式列出,不会悄悄把未知常数当作已支持分潮。

    #快速开始

    要求 moonc >= 0.10.14。当前验证使用:

    moon 0.1.20261012 moonc v0.10.15+943464bfa

    克隆、检查和测试:

    git clone https://github.com/WYT-ai-nb/moontide.git cd moontide moon version --all moon check --target wasm-gc moon test --target wasm-gc moon build --target wasm-gc

    运行最小示例:

    moon run cmd/moontide --target wasm-gc -- predict \ --station demo \ --date 2026-10-12 \ --tz 8 \ --hours 24

    #CLI

    moontide list moontide validate --station demo moontide predict --station demo --date 2026-10-12 --tz 8 --hours 24 moontide stats --station demo --date 2026-10-12 --hours 48 moontide events --station demo --date 2026-10-12 --format csv moontide curve --station demo --date 2026-10-12 moontide plan --station demo --date 2026-10-12 moontide export --station demo --date 2026-10-12 --format ics moontide validate-reference \ --metadata validation/noaa-8518750/metadata.json \ --harcon validation/noaa-8518750/harcon.json \ --datums validation/noaa-8518750/datums.json \ --predictions validation/noaa-8518750/predictions-20261012-20261015.json \ --tolerance 0.12

    常用参数:

    • --station <id>:内置示例站点 demo、qingdao、xiamen
    • --date <YYYY-MM-DD>:预测起始日期
    • --tz <offset>:UTC 偏移小时数,默认 8
    • --hours <n>:预测时长,默认 24
    • --format <markdown|json|csv|ics>:输出格式
    • --tolerance <m>:NOAA 对照的平均绝对误差阈值,默认 0.3 m

    #真实站点兼容与权威对照

    验证案例使用两个独立的 NOAA CO-OPS 站点,避免只针对单一站点调参:

    • 8518750:The Battery, New York
    • 8443970:Boston, Massachusetts

    New York 数据:

    • 站点中心:40.700554, -74.01417
    • 官方调和常数:37 个
    • MoonTide 当前使用:11 个
    • 当前未纳入:26 个
    • 基准面:NOAA 预测使用 MLLW
    • 调和常数:NOAA 常数使用 MSL
    • 基准换算:MSL - MLLW = 1.785 - 1.002 = 0.783 m
    • 对照区间:2026-10-12 至 2026-10-15,逐小时,共 96 个样本

    Boston 数据:

    • 站点中心:42.35389, -71.05028
    • 官方调和常数:37 个
    • MoonTide 当前使用:11 个
    • 当前未纳入:26 个
    • 基准换算:MSL - MLLW = 2.660 - 1.074 = 1.586 m
    • 对照区间与 New York 相同,共 96 个逐时样本

    运行 New York 对照:

    moon run cmd/moontide --target wasm-gc -- validate-reference \ --metadata validation/noaa-8518750/metadata.json \ --harcon validation/noaa-8518750/harcon.json \ --datums validation/noaa-8518750/datums.json \ --predictions validation/noaa-8518750/predictions-20261012-20261015.json \ --tolerance 0.12

    New York 当前输出:

    # MoonTide NOAA Validation - Station: The Battery (8518750) - Source: NOAA CO-OPS station 8518750; harmonic constants + published datums - Datum offset (MSL - MLLW): 0.78 m - Harmonic constituents: 37 total, 11 used, 26 ignored - Ignored by the current catalog: MK3, S4, MN4, NU2, S6, MU2, 2N2, OO1, LAM2, S1, M1, J1, MM, SSA, SA, MSF, MF, RHO, T2, R2, 2Q1, 2SM2, M3, L2, 2MK3, M8 - Samples: 96 - Bias: -0.08 m - Mean absolute error: 0.09 m - Root mean square error: 0.10 m - Maximum absolute error: 0.20 m - Mean-error tolerance: 0.12 m - Result: **PASS**

    更精确的内部统计值为:

    • Bias:约 -0.0845 m
    • MAE:约 0.0883 m
    • RMSE:约 0.1036 m
    • 最大绝对误差:约 0.1959 m

    Boston 对照命令:

    moon run cmd/moontide --target wasm-gc -- validate-reference \ --metadata validation/noaa-8443970/metadata.json \ --harcon validation/noaa-8443970/harcon.json \ --datums validation/noaa-8443970/datums.json \ --predictions validation/noaa-8443970/predictions-20261012-20261015.json \ --tolerance 0.12

    Boston 当前输出:

    # MoonTide NOAA Validation - Station: Boston (8443970) - Source: NOAA CO-OPS station 8443970; harmonic constants + published datums - Datum offset (MSL - MLLW): 1.59 m - Harmonic constituents: 37 total, 11 used, 26 ignored - Samples: 96 - Bias: -0.04 m - Mean absolute error: 0.07 m - Root mean square error: 0.09 m - Maximum absolute error: 0.21 m - Mean-error tolerance: 0.12 m - Result: **PASS**

    两个站点的完整 96 点对照均在 0.12 m 平均误差阈值内通过。当平均绝对误差超过 --tolerance 时,命令会以非零状态退出,CI 会失败。NOAA 数据来源和下载地址记录在各自的 SOURCE.md 中:

    • validation/noaa-8518750/SOURCE.md
    • validation/noaa-8443970/SOURCE.md

    #核心处理机制

    1. 站点元数据解析:从 NOAA metadata JSON 读取站点 ID、名称、纬度和经度。
    2. 调和常数映射:读取 NOAA HarmonicConstituents,按名称映射到 MoonTide 内置分潮目录;当前目录包含 M2、S2、N2、K2、K1、O1、P1、Q1、M4、MS4、M6。
    3. 分潮覆盖审计:未映射的 NOAA 分潮由 noaa_unsupported_constituents 返回,并在 CLI 输出中列出,防止静默丢失。
    4. 基准面归一化:NOAA 调和常数以 MSL 为基准,预测 API 使用 MLLW;MoonTide 使用已发布的 MSL 和 MLLW 数值计算偏移量,并把它作为站点的垂直基准。
    5. 调和预测:使用 J2000 起算的天文参数,计算平衡潮角、结点因子、结点角和各分潮贡献,再叠加站点基准得到潮高。
    6. 权威预测对照:逐时读取 NOAA 预测 JSON,与 MoonTide 结果计算 Bias、MAE、RMSE 和最大绝对误差。
    7. 验收闸门:validate-reference 在平均误差不达标时失败退出,CI 自动验证真实站点案例。

    #精度与适用边界

    MoonTide 当前是调和潮汐预测器,不是完整海洋动力模型,也不包含:

    • 风暴增水、海啸、河流径流、波浪爬高和冰情
    • 长期海平面变化和局部港口工程改造影响
    • 导航、海上作业、救援和其他安全关键决策

    精度取决于站点调和常数质量、分潮覆盖范围、数据和预测是否使用相同基准面、常数适用的 epoch/年份,以及非线性分潮和季节项是否完整。内置 demo、qingdao 和 xiamen 站点只用于演示 CLI,不代表官方常数;真实使用应像 NOAA 案例一样提供可追溯数据并运行对照验证。

    #测试与 CI

    当前包含 29 项自动化测试,覆盖:

    • 天文参数、分潮速度和结点改正
    • 儒略日和日期时间转换
    • 极值查找、潮汐统计和时间窗规划
    • CSV、ICS、Markdown 和 JSON 输出
    • NOAA 站点、调和常数、基准面和预测 JSON 解析
    • NOAA 分潮覆盖审计
    • MoonTide 对 New York 和 Boston 各前 24 个逐时点的误差断言

    CI 位于 .github/workflows/ci.yml,覆盖检查、构建、测试、最小示例,以及两个站点的完整 96 点 NOAA 对照。

    #代码规模与结构

    当前非测试 MoonBit 源码约 2794 行,其中非空且非注释代码约 2358 行。测试源码约 480 行。

    moontide/ astro.mbt 天文基本参数 time.mbt 儒略日、日期转换和本地时间 constituent.mbt 分潮定义和速度 harmonic.mbt 平衡潮角、结点改正和潮高计算 station.mbt 站点模型与示例站点 noaa.mbt NOAA 数据接入、误差统计和验证报告 extreme.mbt 高低潮查找 analysis.mbt 潮汐状态、速率、统计和时间窗 planner.mbt 活动评分和推荐计划 exporter.mbt CSV、ICS 和 Markdown 导出 validation.mbt 站点与参数校验 station_search.mbt 距离计算、站点检索和质量评分 solar.mbt 日出日落近似 activity.mbt 活动推荐规则 render.mbt Markdown、JSON 和 ASCII 曲线 moontide.mbt 高层预测接口 cmd/moontide/ CLI 入口 validation/ New York 与 Boston 的 NOAA 离线验证数据

    #许可证

    Apache-2.0。NOAA 数据来源和日期记录在两个站点的 SOURCE.md 中。

    Activity

    pub struct Activity {
    title : String
    detail : String
    windows : Array[String]
    } derive(
    Debug
    )

    A recommended activity with an explanation and one or more time windows.

    ActivityPlan

    pub struct ActivityPlan {
    activity : String
    windows : Array[ScoredWindow]
    note : String
    } derive(
    Debug
    )

    A ranked set of windows for one activity.

    Astro

    pub struct Astro {
    s : Double
    h : Double
    p : Double
    n : Double
    pp : Double
    } derive(
    Debug
    )

    Fundamental astronomical arguments, all in degrees.

    Constituent

    pub struct Constituent {
    name : String
    d1 : Int
    d2 : Int
    d3 : Int
    d4 : Int
    d5 : Int
    d6 : Int
    speed : Double
    nodal_kind : NodalKind
    } derive(
    Debug
    )

    A tidal harmonic constituent expressed with Doodson numbers.

    ExtremeKind

    pub(all) enum ExtremeKind {
    High
    Low
    } derive(Eq,
    Debug
    )

    A tidal extreme: a local maximum or minimum of the tide curve.

    Harmonic

    pub struct Harmonic {
    constituent : Constituent
    amplitude : Double
    phase_lag : Double
    } derive(
    Debug
    )

    A harmonic component: a constituent plus station-specific amplitude and Greenwich phase lag.

    Moment

    pub struct Moment {
    year : Int
    month : Int
    day : Int
    local_hour : Double
    } derive(Eq,
    Debug
    )

    A calendar moment expressed in a local clock (the UTC offset is supplied separately).

    NodalKind

    pub enum NodalKind {
    Solar
    SemiDiurnal
    LunarDiurnal
    LuniSolarDiurnal
    } derive(Eq,
    Debug
    )

    Nodal-correction family for a constituent.

    Prediction

    pub struct Prediction {
    station : Station
    start_jd : Double
    duration_hours : Double
    tz : Double
    extremes : Array[TideExtreme]
    max_height : Double
    min_height : Double
    tidal_range : Double
    spring_neap : String
    sunrise_hour : Double
    sunset_hour : Double
    activities : Array[Activity]
    } derive(
    Debug
    )

    The result of a tide prediction for a station over a time window.

    ReferencePrediction

    pub struct ReferencePrediction {
    time_jd : Double
    height : Double
    } derive(
    Debug
    )

    One timestamped reference height used for validation.

    ScoredWindow

    pub struct ScoredWindow {
    window : TideWindow
    score : Double
    reason : String
    } derive(
    Debug
    )

    A scored activity window.

    Station

    pub struct Station {
    id : String
    name : String
    lat : Double
    lon : Double
    datum : Double
    components : Array[Harmonic]
    source : String
    } derive(
    Debug
    )

    A tide station: location, datum, harmonic components, and data provenance.

    TideExtreme

    pub struct TideExtreme {
    time_jd : Double
    height : Double
    kind : ExtremeKind
    } derive(
    Debug
    )

    TideSample

    pub struct TideSample {
    jd : Double
    height : Double
    rate : Double
    state : TideState
    } derive(
    Debug
    )

    One sampled point on a tide curve.

    TideState

    pub enum TideState {
    Rising
    Falling
    HighSlack
    LowSlack
    } derive(Eq,
    Debug
    )

    The instantaneous direction of the tide.

    TideStats

    pub struct TideStats {
    min_height : Double
    max_height : Double
    mean_height : Double
    range : Double
    max_flood_rate : Double
    max_ebb_rate : Double
    rising_fraction : Double
    } derive(
    Debug
    )

    Aggregate information about a sampled tide series.

    TideWindow

    pub struct TideWindow {
    start_jd : Double
    end_jd : Double
    label : String
    } derive(
    Debug
    )

    A closed interval on the tide timeline.

    ValidationIssue

    pub struct ValidationIssue {
    level : ValidationLevel
    field : String
    message : String
    } derive(
    Debug
    )

    One validation issue.

    ValidationLevel

    pub enum ValidationLevel {
    Error
    Warning
    Info
    } derive(Eq,
    Debug
    )

    Severity of a station or option validation issue.

    ValidationStats

    pub struct ValidationStats {
    sample_count : Int
    bias : Double
    mean_absolute_error : Double
    root_mean_square_error : Double
    max_absolute_error : Double
    tolerance : Double
    passed : Bool
    } derive(
    Debug
    )

    Error statistics for a MoonTide prediction compared with an external source.

    amplitude_sum

    fn amplitude_sum(station : Station, names : Array[String]) -> Double

    Compute the sum of amplitudes for a list of constituent names.

    astro_at

    fn astro_at(jd : Double) -> Astro

    Fundamental arguments for a Julian day, following the standard Schureman series.

    beach_plan

    fn beach_plan(extremes : Array[TideExtreme], sunrise : Double, sunset : Double, tz : Double, range : Double) -> ActivityPlan

    Build a beachcombing plan from high/low water extremes.

    birding_plan

    fn birding_plan(extremes : Array[TideExtreme], sunrise : Double, sunset : Double, tz : Double) -> ActivityPlan

    Build a bird-watching plan from daytime low-water windows.

    builtin_stations

    fn builtin_stations() -> Array[Station]

    Stations bundled with MoonTide. Their constants are illustrative demo values.

    catalog_size

    fn catalog_size() -> Int

    Count how many constituents are available in the standard catalog.

    compare_with_reference

    fn compare_with_reference(station : Station, references : Array[ReferencePrediction], tolerance : Double) -> ValidationStats?

    Compare modeled tide heights with externally supplied reference predictions.

    component_names

    fn component_names(station : Station) -> Array[String]

    Return unique constituent names in stable order.

    compute_stats

    fn compute_stats(samples : Array[TideSample]) -> TideStats

    Compute descriptive statistics from a sampled series.

    coordinate_label

    fn coordinate_label(lat : Double, lon : Double) -> String

    Produce a coordinate label for reports.

    day_of_year

    fn day_of_year(year : Int, month : Int, day : Int) -> Int

    Day of year, 1..365/366.

    distance_km

    fn distance_km(lat1 : Double, lon1 : Double, lat2 : Double, lon2 : Double) -> Double

    Great-circle distance in kilometers between two coordinates.

    dominant_state

    fn dominant_state(samples : Array[TideSample]) -> TideState

    Return the dominant state in a sampled series.

    equilibrium_argument

    fn equilibrium_argument(c : Constituent, a : Astro, t_hours : Double) -> Double

    Equilibrium argument (degrees) for a constituent at t_hours since J2000.0.

    extremes_to_csv

    fn extremes_to_csv(extremes : Array[TideExtreme], tz : Double) -> String

    Export high/low water events as CSV.

    extremes_to_ics

    fn extremes_to_ics(station : Station, extremes : Array[TideExtreme], tz : Double) -> String

    Export high/low water events as an iCalendar feed.

    fastest_constituent

    fn fastest_constituent() -> Constituent

    Return the speed of the fastest supported constituent.

    find_constituent

    fn find_constituent(name : String) -> Constituent?

    Find a constituent by name (case-insensitive).

    find_extremes

    fn find_extremes(station : Station, start_jd : Double, duration_hours : Double, step_hours : Double) -> Array[TideExtreme]

    Find high and low water by sampling the curve and locating local extrema.

    find_next_extreme

    fn find_next_extreme(station : Station, jd : Double, horizon_hours : Double, step_hours : Double) -> TideExtreme?

    Find the next high or low water after jd.

    find_station

    fn find_station(id : String) -> Station?

    Find a built-in station by id (case-insensitive).

    fishing_activity_plan

    fn fishing_activity_plan(extremes : Array[TideExtreme], sunrise : Double, sunset : Double, tz : Double, station : Station) -> ActivityPlan

    Build a fishing plan from low-water to high-water transitions.

    format_local_datetime

    fn format_local_datetime(jd : Double, tz_offset : Double) -> String

    Format a Julian day as "YYYY-MM-DD HH:MM" in the given UTC offset.

    format_local_time

    fn format_local_time(jd : Double, tz_offset : Double) -> String

    Format a Julian day as "HH:MM" in the given UTC offset.

    format_meters

    fn format_meters(x : Double) -> String

    harmonic_count

    fn harmonic_count(station : Station) -> Int

    Count the number of harmonics in a station.

    has_constituent

    fn has_constituent(station : Station, name : String) -> Bool

    Return true when a station contains a named constituent.

    hemisphere_label

    fn hemisphere_label(station : Station) -> String

    Return a hemisphere label for display.

    high_water_windows

    fn high_water_windows(extremes : Array[TideExtreme], before_hours : Double, after_hours : Double) -> Array[TideWindow]

    Produce windows around every high water in a series.

    is_eastern

    fn is_eastern(station : Station) -> Bool

    Return true when the station is in the eastern hemisphere.

    is_neap

    fn is_neap(tidal_range : Double, mean_range : Double) -> Bool

    Return true when a range is below the neap threshold.

    is_northern

    fn is_northern(station : Station) -> Bool

    Return true when the station is in the northern hemisphere.

    is_spring

    fn is_spring(tidal_range : Double, mean_range : Double) -> Bool

    Return true when a range is above the spring threshold.

    is_valid

    fn is_valid(issues : Array[ValidationIssue]) -> Bool

    Return true when no error-level issues exist.

    jd_to_calendar

    fn jd_to_calendar(jd : Double) -> (Int, Int, Int, Double)

    Inverse of julian_day_utc: returns (year, month, day, hour) in UTC.

    julian_day_utc

    fn julian_day_utc(year : Int, month : Int, day : Int, utc_hour : Double) -> Double

    Julian day from a Gregorian date and a UTC hour (Meeus, "Astronomical Algorithms").

    local_hour_of

    fn local_hour_of(jd : Double, tz_offset : Double) -> Double

    Local hour-of-day [0, 24) for a Julian day and UTC offset.

    low_water_windows

    fn low_water_windows(extremes : Array[TideExtreme], before_hours : Double, after_hours : Double) -> Array[TideWindow]

    Produce windows around every low water in a series.

    mean_tidal_range

    fn mean_tidal_range(station : Station) -> Double

    Rough mean tidal range from the dominant constituents.

    merge_windows

    fn merge_windows(windows : Array[TideWindow]) -> Array[TideWindow]

    Merge overlapping or touching windows.

    moment_to_jd

    fn moment_to_jd(moment : Moment, tz_offset : Double) -> Double

    Convert a local clock moment to a UTC Julian day.

    nearest_station

    fn nearest_station(lat : Double, lon : Double) -> Station?

    Find the nearest built-in station to a coordinate.

    nearest_station_summary

    fn nearest_station_summary(lat : Double, lon : Double) -> String

    Find the closest station and return its distance as text.

    noaa_unsupported_constituents

    fn noaa_unsupported_constituents(harcon : Json) -> Array[String]

    Return NOAA constituent names that MoonTide's harmonic catalog cannot use.

    nodal_correction

    fn nodal_correction(c : Constituent, n_degrees : Double) -> (Double, Double)

    Nodal factor and nodal angle (degrees) for a constituent.

    normalize_degrees

    fn normalize_degrees(x : Double) -> Double

    Reduce an angle to [0, 360).

    overlap_hours

    fn overlap_hours(a : TideWindow, b : TideWindow) -> Double

    Number of overlapping hours between two windows.

    parse_local_datetime

    fn parse_local_datetime(s : String) -> Moment?

    Parse "YYYY-MM-DD" or "YYYY-MM-DD HH:MM" (the "T" separator is also accepted).

    parse_station_file

    fn parse_station_file(content : String) -> Station?

    Parse a station file containing a single JSON object.

    plan_to_markdown

    fn plan_to_markdown(plan : ActivityPlan, tz : Double) -> String

    Render one activity plan as Markdown.

    plans_for

    fn plans_for(prediction : Prediction) -> Array[ActivityPlan]

    Build all activity plans for a prediction.

    plans_to_markdown

    fn plans_to_markdown(plans : Array[ActivityPlan], tz : Double) -> String

    Render all activity plans as Markdown.

    predict

    fn predict(station : Station, start : Moment, tz : Double, duration_hours : Double) -> Prediction

    Predict tides for a station starting at a local clock moment.

    predict_on

    fn predict_on(station : Station, date : String, tz : Double, duration_hours : Double) -> Prediction?

    Convenience wrapper: parse a local date string and predict.

    predictions_from_noaa

    fn predictions_from_noaa(root : Json) -> Array[ReferencePrediction]?

    Parse NOAA CO-OPS prediction JSON (time_zone=gmt).

    rank_windows

    fn rank_windows(windows : Array[ScoredWindow]) -> Array[ScoredWindow]

    Rank windows by descending score.

    recommend

    fn recommend(extremes : Array[TideExtreme], sunrise : Double, sunset : Double, tz : Double) -> Array[Activity]

    Build activity suggestions from tidal extremes and the daylight window.

    render_curve

    fn render_curve(station : Station, start_jd : Double, duration_hours : Double, step_hours : Double, tz : Double) -> String

    Render an ASCII tide curve over a window.

    render_json

    fn render_json(p : Prediction) -> String

    Render a prediction as compact JSON-compatible text.

    render_report

    fn render_report(p : Prediction) -> String

    Render a full Markdown prediction report.

    sample_series

    fn sample_series(station : Station, start_jd : Double, duration_hours : Double, step_hours : Double) -> Array[TideSample]

    Sample a station over a time window.

    samples_to_csv

    fn samples_to_csv(samples : Array[TideSample], tz : Double) -> String

    Export a sampled tide curve as CSV.

    score_fishing_window

    fn score_fishing_window(window : TideWindow, sunrise : Double, sunset : Double, tz : Double, flood_rate : Double) -> Double

    Score a window for fishing during the start of a flood tide.

    score_low_tide_window

    fn score_low_tide_window(window : TideWindow, sunrise : Double, sunset : Double, tz : Double, height_span : Double) -> Double

    Score a window for a low-tide activity such as beachcombing.

    slowest_constituent

    fn slowest_constituent() -> Constituent

    Return the slowest supported constituent.

    solar_declination

    fn solar_declination(day_of_year : Int) -> Double

    Approximate solar declination in degrees for a day of year (1..365/366).

    standard_constituents

    fn standard_constituents() -> Array[Constituent]

    The standard set of constituents supported by MoonTide.

    state_label

    fn state_label(state : TideState) -> String

    Convert the dominant state to a human-readable label.

    station_from_json

    fn station_from_json(root : Json) -> Station?

    Parse a station from MoonTide's JSON format.

    station_from_noaa

    fn station_from_noaa(metadata : Json, harcon : Json, datums? : Json) -> Station?

    Build a station from NOAA CO-OPS metadata, harmonic constants, and datums.

    NOAA harmonic constants are referenced to MSL while the common prediction API used in validation is referenced to MLLW. When datum data is supplied, MoonTide records MSL - MLLW as the station datum so predictions use the same vertical reference.

    station_id

    fn station_id(station : Station) -> String

    station_lat

    fn station_lat(station : Station) -> Double

    station_lon

    fn station_lon(station : Station) -> Double

    station_name

    fn station_name(station : Station) -> String

    station_quality_score

    fn station_quality_score(station : Station) -> Double

    Return a simple coverage score for the station constants.

    station_source

    fn station_source(station : Station) -> String

    station_summary

    fn station_summary(station : Station) -> String

    Produce a one-line station summary.

    stations_in_bounds

    fn stations_in_bounds(min_lat : Double, max_lat : Double, min_lon : Double, max_lon : Double) -> Array[Station]

    Return stations within a coordinate bounding box.

    stations_to_markdown

    fn stations_to_markdown(stations : Array[Station]) -> String

    Render a compact station inventory as Markdown.

    stats_to_markdown

    fn stats_to_markdown(stats : TideStats, coefficient : Double) -> String

    Render descriptive statistics as Markdown.

    sunrise_sunset

    fn sunrise_sunset(lat_degrees : Double, day_of_year : Int) -> (Double, Double)?

    Simplified NOAA sunrise/sunset in local solar hours, or None for polar day/night.

    tide_coefficient

    fn tide_coefficient(tidal_range : Double, mean_range : Double) -> Double

    Compute the tide coefficient using the range relative to the mean range.

    tide_height

    fn tide_height(station : Station, jd : Double) -> Double

    Tide height in meters for a station at a Julian day.

    tide_rate

    fn tide_rate(station : Station, jd : Double, delta_hours : Double) -> Double

    Approximate rate of change in meters per hour.

    tide_state

    fn tide_state(station : Station, jd : Double, tolerance : Double) -> TideState

    Classify a tide point by its local rate and height.

    to_degrees

    fn to_degrees(radians : Double) -> Double

    to_radians

    fn to_radians(degrees : Double) -> Double

    validate_coordinates

    fn validate_coordinates(lat : Double, lon : Double) -> Array[ValidationIssue]

    Validate station coordinates.

    validate_datum

    fn validate_datum(datum : Double) -> Array[ValidationIssue]

    Validate station datum.

    validate_harmonic

    fn validate_harmonic(harmonic : Harmonic) -> Array[ValidationIssue]

    Validate one harmonic component.

    validate_hours

    fn validate_hours(hours : Double) -> Array[ValidationIssue]

    Validate a prediction window in hours.

    validate_station

    fn validate_station(station : Station) -> Array[ValidationIssue]

    Validate a station and all of its harmonic components.

    validate_timezone

    fn validate_timezone(tz : Double) -> Array[ValidationIssue]

    Validate a UTC offset.

    validation_stats_to_markdown

    fn validation_stats_to_markdown(stats : ValidationStats) -> String

    Render NOAA validation statistics as Markdown.

    validation_to_markdown

    fn validation_to_markdown(issues : Array[ValidationIssue]) -> String

    Render a validation report as Markdown.

    window_around

    fn window_around(extreme : TideExtreme, before_hours : Double, after_hours : Double, label : String) -> TideWindow

    Build a time window around an extreme.

    window_contains

    fn window_contains(window : TideWindow, jd : Double) -> Bool

    Test whether a window contains a Julian day.