Appearance
Troubleshooting
muxen-sextant has no control socket, no status command and no configuration file. Everything it knows it either says in the journal at startup or publishes on MQTT, so those two are the whole toolbox:
sh
systemctl status muxen-sextant
journalctl -u muxen-sextant -n 100 # the startup log and any errors
journalctl -u muxen-sextant -f # follow it
mosquitto_sub -h 127.0.0.1 -t 'sextant/#' -v # what is being published
mosquitto_sub -h 127.0.0.1 -t 'nmea/navigation' -v # what is coming inFor anything that needs more detail, stop the service and run the binary by hand with --verbose-all — see Running it by hand in Getting started.
Before anything else, check the clock. More wrong answers from this daemon come from a bad system clock than from every other cause combined, and none of them look like a fault:
sh
timedatectlNo sextant/… topic at all
Nothing is published for any report.
| Likely cause | What to check | Fix |
|---|---|---|
| No position has ever arrived | mosquitto_sub -t nmea/navigation -C 1 | the daemon publishes nothing until the first fix; this is the normal state before GPS lock |
| The broker is not reachable | no Subscribed to nmea/navigation and nmea/datetime line in the journal | the daemon retries every 5 s and does not exit; check the broker |
nmea/datetime is missing | mosquitto_sub -t nmea/datetime -C 1 | a position alone is not enough — the daemon waits for one datetime message before computing anything |
| Reports were disabled | Config: … features: (none) | somebody overrode ExecStart; the shipped unit passes --report-all |
| The daemon is not running | systemctl status muxen-sextant | see the next entry |
The Subscribed to … line is printed from the MQTT connect callback, so its absence is proof that the broker connection never came up. There is no separate "connected" message.
The service restarts every 10 seconds
Restart=always with RestartSec=10, bounded by StartLimitBurst=5 inside StartLimitIntervalSec=300 — so five failed starts in five minutes and systemd gives up and leaves the unit failed.
The daemon has exactly one way to exit non-zero, and it is command-line parsing:
Config: Option parsing failed: Unknown option --tide-radiusThat is exit status 1. Everything else — a missing coefficient file, a missing tide database, a broker that is not there — is logged and survived. If the unit is looping, the fault is in a drop-in that added a bad flag.
Everything is published, but the times are wrong
Almost always the clock, and the size of the error tells you which clock:
| Error | Cause |
|---|---|
| A whole number of hours | The Brain is running on local time, or the wrong timezone. All published times are UTC. |
| Days, or an absurd date | The Brain has no RTC battery and booted at the epoch. Sunrise is computed for that date. |
| Tens of minutes | The position is stale — see the next entry. |
| A minute or two at high latitude | Expected. See Accuracy in Sun and moon. |
The time in nmea/datetime is not what the daemon computes with; it reads the Brain's system clock. So the clock on the screens can be right while every sextant report is wrong.
The position is stale after a GPS outage
The tide stations are hundreds of miles away, or sunrise is right for where the boat was yesterday.
When the fix is lost, nmea/navigation stops carrying a position object and the daemon keeps its last known one. Because nmea/datetime keeps arriving and also drives the scheduler, recomputes continue at that stale position, with a fresh rxDate each time. From the outside it looks perfectly healthy.
There is no staleness flag in the payload. To confirm:
sh
mosquitto_sub -h 127.0.0.1 -t nmea/navigation -C 1 | python3 -m json.toolIf there is no data.position object, the GPS has no fix, and every sextant/… payload describes the last place it did.
The numbers on the screen never change
All five topics are retained, so the broker hands the last payload to every new subscriber forever. A stopped daemon looks exactly like a running one to a screen that does not check metadata.rxDate.
sh
mosquitto_sub -h 127.0.0.1 -t 'sextant/#' -v -W 3If each payload arrives once and never repeats, compare its metadata.rxDate against the current time. More than --recompute-interval-min old and nothing is being recomputed.
Note that a quarter of an hour of silence is normal. Sunrise does not move, so the daemon does not republish it. See When it recomputes in muxen-sextant — Overview.
A decommissioned topic is still on the broker
Disabling a report stops the daemon publishing it but does not remove the retained message already on the broker. Clear it explicitly:
sh
mosquitto_pub -h 127.0.0.1 -t sextant/skymap -r -nsextant/magnetic is missing, everything else works
Three causes, each with its own line on stderr:
| Log line | Cause | Fix |
|---|---|---|
Magnetic: WMM coefficient file not found: <path> | muxen-sextant-database not installed, or --wmm-cof-path wrong | install the package, or fix the path |
Magnetic: Failed to read WMM model from <path> | the file exists but is not a parsable coefficient file | reinstall the data package |
Magnetic: WMM model expired (epoch 2025.0-2030.0, requested …) | the date is outside the model's validity window | install a newer coefficient file — see Magnetic declination |
The retained payload from before the failure stays on the broker, so the declination on the screen keeps showing its last value. That is the dangerous case: nothing looks broken.
A Magnetic: WARNING - WMM model past nominal end date line means the model is in its final grace year. It is still computing; plan the coefficient update now.
sextant/tides is missing
| Log line | Cause |
|---|---|
Tides: Database not found: <path> | muxen-sextant-database is not installed or --tides-db-path is wrong. The report is never created; everything else runs. |
Tides: No tide stations found within 50 NM | genuinely offshore, or the radius is too small |
Tides: Failed to compute tides | the database file exists but could not be opened or queried |
The last two lines appear only under --verbose-tides. Without it, a tides report that finds nothing is completely silent — which is why the verbose run is the first thing to reach for here.
The startup line proves which database is in use:
Tides: Report started (db=/var/lib/muxen-sextant/tides.db, radius=50 NM, recompute=30 min)The tide heights disagree with the tide table
They will, and by a lot. The heights are not referenced to a chart datum — they oscillate about local mean water and are negative about half the time. The timing, the range and the coefficient are the parts worth comparing. See the opening of Tides.
If the timing is out by more than about an hour, that is worth reporting: the test suite holds it inside 60 minutes for ten validated ports.
nextHighTide or nextLowTide is absent
The extremum search applies a prominence filter, and one of two things happened:
- A micro-tidal port. The whole curve fails the prominence floor — the greater of 10 % of the curve range and 0.05 m — so no extremum is accepted. Common in the Mediterranean.
- The extremum is outside the window. The search covers only the 36-hour curve, from −12 h to +24 h.
curve is still published and still correct in both cases, and tidalCoefficient is deliberately withheld whenever either extremum is missing.
The tides payload is enormous
Every station within --tide-radius-nm carries a 216-point curve, and on an indented coast that is a lot of stations — 83 within 50 NM of Bergen, for instance. Lower the radius:
ini
ExecStart=/usr/bin/muxen-sextant --report-all --tide-radius-nm 15There is no cap on the station count in the daemon; the radius is the only control.
The tide page updates half as often as everything else
By design. The tides report has its own floor, --tide-recompute-min, default 30 minutes, while the central scheduler runs at --recompute-interval-min, default 15. A recompute that arrives inside the floor is skipped for tides only.
The sky map shows stars in daylight
There is no daylight filter. The sky map reports what is geometrically above the horizon at any hour. Blank the planisphere in the client using the sun's altitude from sextant/sun, which comes from the same recompute.
The sky map shows planets nobody can see
Planets are filtered on altitude only — --skymap-max-mag does not apply to them — so Uranus and Neptune appear whenever they are above the horizon. Filter on magnitude in the client.
The sun's position on the dial lags
altitude and azimuth are a snapshot from the last recompute, so at the default they are up to 15 minutes old — about 3.75° of sky rotation. Lower --recompute-interval-min. It also speeds up the moon, magnetic and sky map reports, which share the trigger.
A report is missing from features:
Config: muxen-sextant 2.2.0 features: MQTT Astro MagneticThe features: line lists exactly what was enabled. Tides and Skymap missing here means the flags were not passed, not that the data is unavailable. The shipped unit passes --report-all; check for a systemd drop-in that replaced ExecStart without re-adding it.
Nothing appears in the browser
The daemon publishes to the broker on TCP 1883 and opens no socket. The browser path is /ws/sextant → 127.0.0.1:1884, which is the broker's WebSocket listener, configured by the muxen-boat package. Confirm the data exists first:
sh
mosquitto_sub -h 127.0.0.1 -t 'sextant/#' -vIf it is on the broker, the problem is in nginx or the broker's WebSocket listener, not in this daemon.
FAQ
Why does the sunrise time not update every second? Because it does not change. The daemon recomputes every 15 minutes, or sooner if the boat has moved a long way. Only the live sun and moon positions are affected, and they move slowly.
The tide height on the screen is negative. Is it broken? No. These heights are measured about the local mean water level, not from the seabed, so half the time they are below it and read negative. Use the official tide table for the port for anything to do with depth.
Why does the tide page say a different port from the one we are in? It lists every port within 50 nautical miles, nearest first, and the screen chooses one. The nearest station is not always the one whose tide matters.
Why is there no tide information offshore? Because there is no port within the search radius. Nothing is published rather than an empty list, so the page keeps showing the last coast.
Why is the compass variation not changing as we sail? It changes very slowly with position — much less than a degree over a long day's sail in most of the world — and it is recomputed on the same 15-minute schedule as everything else.
Do we need an internet connection? No. Everything is computed on board from two files that ship with the software.
What happens in 2030? The magnetic model runs out. Through 2030 the declination is still computed but the daemon logs a warning; from 2 January 2031 it stops publishing altogether and the last value stays frozen on the screens. The fix is a coefficient-file update, which arrives as a package update.
Why does the moon phase say 0.72 when the moon looks half full?phase is the position in the cycle, not the lit fraction. 0.72 is nearly three-quarters of the way from one new moon to the next, so the moon is waning and close to last quarter — which is half lit. The lit fraction is the separate illumination field, and the name of the phase is in phaseName.
Can we see the stars for a sight without the screen? The sky map is not a substitute for an almanac, but it does give altitude and compass bearing for every star above the horizon, which is enough to point a sextant at the right one.
Does it interfere with anything on the boat? No. It reads two MQTT topics and writes five. It never transmits on the CAN bus and never sends a command to a device.
Tips
Check the clock at commissioning, and check it again. Everything in this manual assumes the Brain's system clock is correct UTC. It is the single input that produces plausible, confident, wrong answers when it is off.
Run it once by hand with --verbose-all during commissioning. One run tells you the recompute reason, the station count, the star count and the declination in five lines, which is faster than reading the payloads.
Compare the first tide payload against the local tide table for timing and range — not for height. If timing is inside an hour and the range is within half a metre, the installation is behaving as validated.
Set --tide-radius-nm to suit the cruising area, not to the default. 50 NM is generous on an indented coast and produces very large payloads; 15 NM is usually enough for a tide page.
Watch metadata.rxDate, not the values. Every payload carries it, and it is the only way to distinguish live data from a retained message left by a stopped daemon.
Clear retained topics when you disable a report, otherwise its last payload sits on the broker for the next screen that subscribes.
Do not treat a missing field as an error. Every time field in the sun, moon and tide reports is optional and is legitimately absent in polar latitudes, in micro-tidal ports, and on days when the moon does not rise. A client that assumes presence will break on a passage north.
Plan the WMM coefficient update before 2030. It is a data file, not a code change, but nothing on the boat will tell you it is due.
Leave --no-mqtt out of the unit. It is a diagnostic flag for a hand run; in the unit it silently disables every output while the service continues to look healthy.
