diff options
Diffstat (limited to 'android-app/app/src/main/kotlin')
7 files changed, 115 insertions, 0 deletions
diff --git a/android-app/app/src/main/kotlin/com/example/androidapp/tide/HarmonicTideCalculator.kt b/android-app/app/src/main/kotlin/com/example/androidapp/tide/HarmonicTideCalculator.kt new file mode 100644 index 0000000..2bdbf6c --- /dev/null +++ b/android-app/app/src/main/kotlin/com/example/androidapp/tide/HarmonicTideCalculator.kt @@ -0,0 +1,88 @@ +package com.example.androidapp.tide + +import com.example.androidapp.data.model.TidePrediction +import com.example.androidapp.data.model.TideStation +import kotlin.math.cos + +/** + * Computes harmonic tide predictions using the standard formula: + * h(t) = Z0 + Σ [ Hi × cos( ωi × (t − t0) − φi ) ] + * + * where: + * Z0 = datum offset (mean water level above chart datum, metres) + * Hi = amplitude of constituent i (metres) + * ωi = angular speed of constituent i (degrees / hour) + * t = hours elapsed since [EPOCH_MS] (2000-01-01 00:00 UTC) + * φi = phase lag (degrees) + */ +object HarmonicTideCalculator { + + /** Reference epoch: 2000-01-01 00:00:00 UTC in Unix milliseconds. */ + internal const val EPOCH_MS = 946_684_800_000L + + /** + * Predict the tide height at a single moment. + * + * @param station Tide station with harmonic constituents. + * @param timestampMs Unix epoch milliseconds for the desired time. + * @return Predicted height in metres above chart datum. + */ + fun predictHeight(station: TideStation, timestampMs: Long): Double { + val hoursFromEpoch = (timestampMs - EPOCH_MS) / 3_600_000.0 + var height = station.datumOffsetMeters + for (c in station.constituents) { + val angleDeg = c.speedDegPerHour * hoursFromEpoch - c.phaseDeg + height += c.amplitudeMeters * cos(Math.toRadians(angleDeg)) + } + return height + } + + /** + * Predict tide heights over a time range at regular intervals. + * + * @param station Tide station. + * @param fromMs Start of range (Unix milliseconds, inclusive). + * @param toMs End of range (Unix milliseconds, inclusive). + * @param intervalMs Time step in milliseconds (must be positive). + * @return List of [TidePrediction] ordered by ascending timestamp. + */ + fun predictRange( + station: TideStation, + fromMs: Long, + toMs: Long, + intervalMs: Long + ): List<TidePrediction> { + require(intervalMs > 0) { "intervalMs must be positive" } + require(fromMs <= toMs) { "fromMs must not exceed toMs" } + val predictions = mutableListOf<TidePrediction>() + var t = fromMs + while (t <= toMs) { + predictions += TidePrediction(t, predictHeight(station, t)) + t += intervalMs + } + return predictions + } + + /** + * Find high and low water events from a pre-computed prediction series. + * + * Detects local maxima (high water) and minima (low water) by comparing + * each interior sample with its immediate neighbours. + * + * @param predictions Ordered list of tide predictions (at least 3 points). + * @return Subset list containing only high/low turning points. + */ + fun findHighLow(predictions: List<TidePrediction>): List<TidePrediction> { + if (predictions.size < 3) return emptyList() + val result = mutableListOf<TidePrediction>() + for (i in 1 until predictions.size - 1) { + val prev = predictions[i - 1].heightMeters + val curr = predictions[i].heightMeters + val next = predictions[i + 1].heightMeters + val isMax = curr >= prev && curr >= next + val isMin = curr <= prev && curr <= next + if (isMax || isMin) result += predictions[i] + } + return result + } +} diff --git a/android-app/app/src/main/kotlin/org/terst/nav/data/model/TideConstituent.kt b/android-app/app/src/main/kotlin/org/terst/nav/data/model/TideConstituent.kt new file mode 100644 index 0000000..deb73d6 --- /dev/null +++ b/android-app/app/src/main/kotlin/org/terst/nav/data/model/TideConstituent.kt @@ -0,0 +1,9 @@ +package com.example.androidapp.data.model + +/** A single harmonic tidal constituent used in harmonic tide prediction. */ +data class TideConstituent( + val name: String, // e.g. "M2", "S2", "K1" + val speedDegPerHour: Double, // angular speed in degrees per hour + val amplitudeMeters: Double, // amplitude in metres + val phaseDeg: Double // phase lag (kappa) in degrees +) diff --git a/android-app/app/src/main/kotlin/org/terst/nav/data/model/TidePrediction.kt b/android-app/app/src/main/kotlin/org/terst/nav/data/model/TidePrediction.kt new file mode 100644 index 0000000..51eea44 --- /dev/null +++ b/android-app/app/src/main/kotlin/org/terst/nav/data/model/TidePrediction.kt @@ -0,0 +1,7 @@ +package com.example.androidapp.data.model + +/** A predicted tide height at a specific point in time. */ +data class TidePrediction( + val timestampMs: Long, // Unix epoch milliseconds + val heightMeters: Double // predicted water height above chart datum in metres +) diff --git a/android-app/app/src/main/kotlin/org/terst/nav/data/model/TideStation.kt b/android-app/app/src/main/kotlin/org/terst/nav/data/model/TideStation.kt new file mode 100644 index 0000000..c9f96a6 --- /dev/null +++ b/android-app/app/src/main/kotlin/org/terst/nav/data/model/TideStation.kt @@ -0,0 +1,11 @@ +package com.example.androidapp.data.model + +/** A tide station with harmonic constituents for offline tide prediction. */ +data class TideStation( + val id: String, + val name: String, + val lat: Double, + val lon: Double, + val datumOffsetMeters: Double, // mean water level above chart datum (Z0) + val constituents: List<TideConstituent> +) diff --git a/android-app/app/src/main/kotlin/org\/terst\/nav/data/model/TideConstituent.kt b/android-app/app/src/main/kotlin/org\/terst\/nav/data/model/TideConstituent.kt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/android-app/app/src/main/kotlin/org\/terst\/nav/data/model/TideConstituent.kt diff --git a/android-app/app/src/main/kotlin/org\/terst\/nav/data/model/TidePrediction.kt b/android-app/app/src/main/kotlin/org\/terst\/nav/data/model/TidePrediction.kt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/android-app/app/src/main/kotlin/org\/terst\/nav/data/model/TidePrediction.kt diff --git a/android-app/app/src/main/kotlin/org\/terst\/nav/data/model/TideStation.kt b/android-app/app/src/main/kotlin/org\/terst\/nav/data/model/TideStation.kt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/android-app/app/src/main/kotlin/org\/terst\/nav/data/model/TideStation.kt |
