Make the tide clock configurable for any location by generating a single location_config.h from scripts.
For simplicity, we'll support these common coastal timezone rules:
| Rule | Winter Offset | Summer Offset | DST Transition |
|---|---|---|---|
UTC |
+0 | +0 | None |
UK |
+0 (GMT) | +1 (BST) | Last Sun Mar/Oct |
EU_CENTRAL |
+1 (CET) | +2 (CEST) | Last Sun Mar/Oct |
EU_EASTERN |
+2 (EET) | +3 (EEST) | Last Sun Mar/Oct |
US_EASTERN |
-5 (EST) | -4 (EDT) | 2nd Sun Mar / 1st Sun Nov |
US_CENTRAL |
-6 (CST) | -5 (CDT) | 2nd Sun Mar / 1st Sun Nov |
US_MOUNTAIN |
-7 (MST) | -6 (MDT) | 2nd Sun Mar / 1st Sun Nov |
US_PACIFIC |
-8 (PST) | -7 (PDT) | 2nd Sun Mar / 1st Sun Nov |
US_ALASKA |
-9 (AKST) | -8 (AKDT) | 2nd Sun Mar / 1st Sun Nov |
US_HAWAII |
-10 (HST) | -10 (HST) | None |
AU_EASTERN |
+10 (AEST) | +11 (AEDT) | 1st Sun Oct / 1st Sun Apr |
AU_WESTERN |
+8 (AWST) | +8 (AWST) | None |
NZ |
+12 (NZST) | +13 (NZDT) | Last Sun Sep / 1st Sun Apr |
Users in unsupported timezones can use UTC and accept times in UTC, or request support.
- NOAA CO-OPS provides harmonic constituents directly via API
- No fitting required - just fetch and convert
- ~3000 tide stations available
- Script:
scripts/fetch_noaa.py
- User supplies high/low water CSV data (or minute-by-minute)
- Run existing fitting scripts to generate constituents
- Script:
scripts/harmonic_fitting/fit_nodal_fast.py(existing)
Single header file included by firmware, containing:
// ===========================================
// AUTO-GENERATED - DO NOT EDIT
// Generated by: scripts/generate_location.py
// Location: Margate, UK
// Date: 2026-01-07
// ===========================================
#ifndef LOCATION_CONFIG_H
#define LOCATION_CONFIG_H
// --- Location ---
#define LOCATION_NAME "Margate"
#define LOCATION_LAT 51.3813f
#define LOCATION_LON 1.3862f
// --- Timezone ---
#define TZ_RULE TZ_RULE_UK
#define TZ_OFFSET_WINTER 0 // hours from UTC
#define TZ_OFFSET_SUMMER 1 // hours from UTC
// --- Tidal Model ---
#define TIDE_MODEL_EPOCH 1546301340L // Unix timestamp
#define TIDE_MEAN_SEA_LEVEL 2.64f // meters
// 31 harmonic constituents: {amplitude, speed (rad/hr), phase (rad)}
#define TIDE_NUM_CONSTITUENTS 31
static const float TIDE_CONSTITUENTS[31][3] = {
{1.6236f, 0.5058681f, 3.6581f}, // M2
{0.4758f, 0.5235988f, 0.5344f}, // S2
// ... 29 more
};
// Empirical time corrections (nodal + seasonal)
#define NODAL_AMP -4.922f
#define NODAL_PHASE 2015.522f
#define NODAL_OFFSET 0.424f
#define NODAL_PERIOD 18.61f
#define ANNUAL_AMP 1.674f
#define ANNUAL_PHASE -0.263f
#define SEMIANN_AMP 0.699f
#define SEMIANN_PHASE -0.129f
#endif // LOCATION_CONFIG_HUsage:
# For US locations (NOAA):
python generate_location.py --noaa 8518750 --name "New York" --tz US_EASTERN
# For other locations (from fitted JSON):
python generate_location.py --json margate_nodal_fast.json --name "Margate" \
--lat 51.3813 --lon 1.3862 --tz UK
Output:
firmware/revA/include/location_config.h
firmware/revB/include/location_config.h
simulator/include/location_config.h
Usage:
python fetch_noaa.py 8518750 # Station ID
Output:
- Fetches harmonic constituents from NOAA CO-OPS API
- Outputs JSON in same format as fit_nodal_fast.py
- Can be piped to generate_location.py
scripts/harmonic_fitting/fit_nodal_fast.py- fits constituents from datascripts/harmonic_fitting/fit_time_correction.py- fits empirical corrections
Replace hardcoded arrays with:
#include "location_config.h"
static TideConstituent constituents[TIDE_NUM_CONSTITUENTS];
void tide_init(void) {
for (int i = 0; i < TIDE_NUM_CONSTITUENTS; i++) {
constituents[i].amplitude = TIDE_CONSTITUENTS[i][0];
constituents[i].omega = TIDE_CONSTITUENTS[i][1];
constituents[i].phase_rad = TIDE_CONSTITUENTS[i][2];
}
}Replace:
#define MARGATE_LAT 51.3813f
#define MARGATE_LON 1.3862fWith:
#include "location_config.h"
// Use LOCATION_LAT, LOCATION_LONAdd timezone rule enum and dispatch:
#include "location_config.h"
typedef enum {
TZ_RULE_UTC,
TZ_RULE_UK,
TZ_RULE_EU_CENTRAL,
TZ_RULE_US_EASTERN,
// ...
} TzRule;
int get_dst_offset(time64_t utc) {
switch (TZ_RULE) {
case TZ_RULE_UK: return is_bst(utc) ? 3600 : 0;
case TZ_RULE_US_EASTERN: return is_us_dst(utc) ? 3600 : 0;
// ...
}
}Replace:
display_draw_text(..., "Margate", ...);With:
#include "location_config.h"
display_draw_text(..., LOCATION_NAME, ...);- Create
location_config.hfor Margate (current values, no behavior change) - Refactor firmware to use
location_config.h - Test - verify simulator and firmware still work
- Create
generate_location.py- generates config from JSON - Create
fetch_noaa.py- fetches NOAA data - Add timezone rules to
timezone.c - Update README with customization instructions
All changes apply to all three targets: firmware/revA/, firmware/revB/, and simulator/.
| File | Change |
|---|---|
*/include/location_config.h |
NEW - generated (identical in all 3) |
*/src/tide.c |
Use config, add init function |
*/src/astro.c |
Use config for lat/lon |
*/src/timezone.c |
Add multi-timezone support |
*/src/render.c |
Use LOCATION_NAME |
scripts/generate_location.py |
NEW - writes to all 3 locations |
scripts/fetch_noaa.py |
NEW |
README.md |
Add customization section |
Note: The generate_location.py script will write the same location_config.h to all three locations automatically.