Chrony with GPS fallback; robust antenna supply; all GNSS constellations

Time: chrony replaces timesyncd, with NICT/JP-pool NTP primary and gpsd
SHM as an offline-only refclock; makestep any time and fake-hwclock
cover the battery-less RTC.

Antenna: the modem cleared the supply again after the service set it,
so the service is now idempotent, configurable (ANTENNA=active|passive,
passive keeps it off) and re-checked by a 30 s timer.

gpsd: a gpsdctl udev hook spawned a stray gpsd without -n at boot that
blocked the real service; gpsd.service now binds to the NMEA device.

GNSS: CGNSSMODE=15 (GLONASS, BeiDou, Galileo, QZSS) and all supported
NMEA sentences; both persist across a modem reset. Adds sim7600-at.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mikkeli
2026-10-09 19:18:16 +09:00
parent fe284ce540
commit 28e056dc4d
2 changed files with 70 additions and 20 deletions
+40 -13
View File
@@ -135,8 +135,8 @@ brief NM connectivity-check penalty (`+20000`) after WiFi returns.
- ModemManager location is **not** used. gpsd owns the NMEA port; ModemManager only labels it `(gps)` and leaves it unopened. - ModemManager location is **not** used. gpsd owns the NMEA port; ModemManager only labels it `(gps)` and leaves it unopened.
- Stable device names, `/etc/udev/rules.d/99-sim7600-gps.rules`: - Stable device names, `/etc/udev/rules.d/99-sim7600-gps.rules`:
``` ```
# if01: NMEA. Re-attach gpsd on every appearance (USBAUTO is off, so gpsd would otherwise lose it after a modem reset). # if01: NMEA. gpsd.service follows this device (drop-in BindsTo), so it restarts with a fresh handle after a modem reset.
SUBSYSTEM=="tty", ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9001", ENV{ID_USB_INTERFACE_NUM}=="01", SYMLINK+="sim7600-gps", RUN+="/usr/sbin/gpsdctl add /dev/sim7600-gps" SUBSYSTEM=="tty", ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9001", ENV{ID_USB_INTERFACE_NUM}=="01", SYMLINK+="sim7600-gps", TAG+="systemd", ENV{SYSTEMD_WANTS}+="gpsd.service"
# if03: spare AT port, reserved for us (ModemManager keeps if02). Powers the active GNSS antenna on every modem start. # if03: spare AT port, reserved for us (ModemManager keeps if02). Powers the active GNSS antenna on every modem start.
SUBSYSTEM=="tty", ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9001", ENV{ID_USB_INTERFACE_NUM}=="03", SYMLINK+="sim7600-at", ENV{ID_MM_PORT_IGNORE}="1", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sim7600-gnss-antenna.service" SUBSYSTEM=="tty", ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9001", ENV{ID_USB_INTERFACE_NUM}=="03", SYMLINK+="sim7600-at", ENV{ID_MM_PORT_IGNORE}="1", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sim7600-gnss-antenna.service"
``` ```
@@ -147,6 +147,10 @@ brief NM connectivity-check penalty (`+20000`) after WiFi returns.
USBAUTO="false" # hotplug would let gpsd probe the modem's AT ports and conflict with ModemManager USBAUTO="false" # hotplug would let gpsd probe the modem's AT ports and conflict with ModemManager
``` ```
- `systemctl enable --now gpsd.socket gpsd` - `systemctl enable --now gpsd.socket gpsd`
- `/etc/systemd/system/gpsd.service.d/10-sim7600.conf`: `BindsTo=` / `After=dev-sim7600\x2dgps.device`. gpsd stops when the NMEA port
vanishes (modem reset, unplug) and the udev rule starts it again when the port returns. An earlier `RUN+="gpsdctl add …"` hook was
dropped: at boot it fired before `gpsd.service`, so `gpsdctl` spawned its own gpsd **without `-n`**, which then held port 2947 and kept
the real service from ever starting.
Check: `cgps` or `gpspipe -w -n 10`. On 2026-10-07, tested indoors, gpsd saw `nSat: 10, uSat: 0`, `mode: 1` (no fix). Check: `cgps` or `gpspipe -w -n 10`. On 2026-10-07, tested indoors, gpsd saw `nSat: 10, uSat: 0`, `mode: 1` (no fix).
Next: test with the GNSS antenna (IPEX, labeled GNSS on the HAT) with a clear view of the sky. A cold start can take a few minutes. Next: test with the GNSS antenna (IPEX, labeled GNSS on the HAT) with a clear view of the sky. A cold start can take a few minutes.
@@ -169,19 +173,42 @@ AT+CVAUXS=1 # switch the supply on → fix back within 1 s
| Satellites used | 6.3 avg (max 7) | 6.5 avg (max 7) | | Satellites used | 6.3 avg (max 7) | 6.5 avg (max 7) |
| Signal of used satellites | 31.3 dB-Hz | **36.8 dB-Hz** (peaks 43–47) | | Signal of used satellites | 31.3 dB-Hz | **36.8 dB-Hz** (peaks 43–47) |
Satellites used barely change; that count looks limited by which constellations are enabled (GPS and GLONASS in use; BeiDou visible in Satellites used barely change; that count looks limited by which constellations are enabled (see below). The gain is ~5.5 dB of margin,
NMEA; QZSS/Galileo not checked yet). The gain is ~5.5 dB of margin, which is what matters under trees and between buildings. which is what matters under trees and between buildings.
**Both settings are volatile.** They reset to off/1.7 V on every modem restart (`AT+CRESET` tested), and `AT&W` does not save them. So: **Both settings are volatile.** They reset to off/1.7 V on every modem restart (`AT+CRESET` tested), and `AT&W` does not save them. So:
- `/usr/local/sbin/sim7600-gnss-antenna` sends them over `/dev/sim7600-at` (retrying while the modem boots) and verifies them; - `/etc/default/sim7600-gnss-antenna`: `ANTENNA=active|passive`, `CVAUXV_MV=3050`.
- `sim7600-gnss-antenna.service` (oneshot, `BindsTo=dev-sim7600\x2dat.device`) runs it, started by the udev rule above whenever - `/usr/local/sbin/sim7600-gnss-antenna` reads the modem's state over `/dev/sim7600-at` and writes only if it differs from the config
interface 03 appears: boot, hot-plug, modem reset. (`passive` keeps the supply **off**). It logs only actual changes.
- `sim7600-gnss-antenna.service` (oneshot, `LogLevelMax=notice`) runs it when interface 03 appears (udev), **and**
`sim7600-gnss-antenna.timer` re-runs it every 30 s.
Verified with `mmcli -m any --reset` while everything was running: the antenna supply was re-applied as soon as the ports returned, The timer is needed: one modem reset showed the modem clearing the supply again **after** the service had set and verified it
LTE reconnected ~50 s after the reset, and gpsd re-attached via `gpsdctl` with a 3D fix. (most likely ModemManager bringing the radio up). With the timer, any reset is corrected within ~30 s (tested: supply switched off by
hand at 19:13:59, restored 19:14:34). Full recovery after `mmcli -m any --reset` (tested twice): LTE back in ~30–50 s, antenna supply
back on, gpsd restarted with the port.
With a **passive** antenna the supply does no harm, but it can be turned off by disabling the service. ⚠ **Passive antenna: set `ANTENNA=passive` before connecting it**, then `sudo systemctl start sim7600-gnss-antenna`. Many passive GNSS
antennas are a DC short to ground (inductive or shorted-patch feeds), and `CVAUX` is a general-purpose modem output, not a dedicated
antenna supply with documented short-circuit protection. With nothing connected the supply is harmless.
#### Constellations (2026-10-09)
`AT+CGNSSMODE=<0-15>,<dpo>` sets the satellite systems on top of GPS. The default `3` is GPS+GLONASS+BeiDou (per SIMCom's GNSS
application note), which fits bit 0 = GLONASS, bit 1 = BeiDou. The note lists Galileo and QZSS next, so bits 2–3 are most likely those,
but it doesn't define the bits explicitly.
```
AT+CGPS=0
AT+CGNSSMODE=15,1 # all four: GLONASS, BeiDou, Galileo, QZSS (Japan's regional system, "Michibiki")
AT+CGPSNMEA=262143 # all NMEA sentence types; the modem keeps only those it supports: reads back 198143
AT+CGPS=1
```
Both settings **persist across a modem reset** (checked), unlike the antenna supply. **Not yet verified on sky:** whether Galileo/QZSS
satellites actually appear (look for Galileo/QZSS PRNs in `gpspipe -w` SKY, or `GA`/`GQ` NMEA talkers). Indoors with no antenna nothing is
tracked. No AT command for SBAS/MSAS (Japan's augmentation service) appears in the application note.
## Rebuilding qmi_wwan after a kernel update ## Rebuilding qmi_wwan after a kernel update
@@ -198,7 +225,7 @@ sudo modprobe qmi_wwan && sudo systemctl restart ModemManager
## Useful one-offs ## Useful one-offs
- Talk to the modem directly: use **`/dev/sim7600-at`**. ModemManager ignores it, so there's no need to stop anything - Talk to the modem directly: use **`/dev/sim7600-at`**. ModemManager ignores it, so there's no need to stop anything.
(e.g. `picocom -b 115200 /dev/sim7600-at`, or `minicom -D /dev/sim7600-at`). Don't send commands that change the data One-shot: `sudo sim7600-at 'AT+CGPS?' 'AT+CVAUXS?'` (`/usr/local/sbin/sim7600-at`; filters NMEA noise). Interactive:
connection behind ModemManager's back. `picocom -b 115200 /dev/sim7600-at`. Don't send commands that change the data connection behind ModemManager's back.
- Verbose ModemManager log incl. AT traffic: `sudo mmcli -G debug` then `journalctl -u ModemManager -f` (resets on restart). - Verbose ModemManager log incl. AT traffic: `sudo mmcli -G debug` then `journalctl -u ModemManager -f` (resets on restart).
+30 -7
View File
@@ -8,10 +8,11 @@ date: 2026-10-08
| Item | State | | Item | State |
|------|-------| |------|-------|
| Network time | `systemd-timesyncd`, primary `ntp.nict.jp` (stratum 1), synced over whichever uplink holds the default route (Ethernet / WiFi / LTE) | | Network time | **chrony** (since 2026-10-09; was `systemd-timesyncd`), primary `ntp.nict.jp` (stratum 1) + JP pool + Ubuntu pools, over whichever uplink holds the default route |
| Timezone | `Asia/Tokyo` (was `Asia/Yakutsk`: same +09:00 and no DST either, but the wrong place) | | Timezone | `Asia/Tokyo` (was `Asia/Yakutsk`: same +09:00 and no DST either, but the wrong place) |
| Clock across power-off | **Not kept.** The RTC has no backup battery (see below) | | Clock across power-off | **Not kept.** The RTC has no backup battery (see below) |
| GPS time | Not yet. Waiting on a GNSS fix (see [SIM7600 report](SIM7600-SETUP.md)) | | GPS time | chrony refclock `GPS` (gpsd SHM 0), fallback only; used when offline with a fix |
| Boot clock without network | `fake-hwclock` restores the last saved time (replaces timesyncd's saved timestamp) |
## RTC loses time when powered off ## RTC loses time when powered off
@@ -41,11 +42,33 @@ FallbackNTP=ntp.ubuntu.com 0.jp.pool.ntp.org 1.jp.pool.ntp.org 2.jp.pool.ntp.org
`sudo systemctl restart systemd-timesyncd`, then check with `timedatectl timesync-status` (server, stratum, offset). `sudo systemctl restart systemd-timesyncd`, then check with `timedatectl timesync-status` (server, stratum, offset).
## Later: GPS as a time source ## Chrony with GPS fallback (2026-10-09)
timesyncd only speaks NTP. Once GNSS has a fix, replace it with `chrony`, configured with: Replaced `systemd-timesyncd` (removed by the `chrony` package) so GPS can serve as an offline time source.
- the same NTP servers as above, which remain the default whenever any uplink exists, and ```
- a gpsd refclock (`refclock SHM 0`), which takes over when offline. sudo apt install chrony fake-hwclock
```
NMEA over the USB serial port carries no PPS signal, so GPS time alone is only good to tens of milliseconds. That is plenty for log timestamps. `/etc/chrony/conf.d/10-yukart.conf`:
```
server ntp.nict.jp iburst prefer
pool 2.jp.pool.ntp.org iburst maxsources 2
refclock SHM 0 refid GPS precision 1e-1 offset 0.0 delay 0.5 poll 3
```
- **NTP stays primary.** The GPS refclock's generous `precision`/`delay` give it a large root distance, so any reachable NTP server wins.
It steers the clock only offline with a fix. NMEA over USB has no PPS, so GPS time is good to ~tens of ms, plenty for log timestamps.
- **`offset 0.0` is a placeholder.** Tune it once NTP and a GNSS fix are both available: `chronyc sourcestats` shows the GPS offset
against NTP.
- **`makestep 1 -1`** in `/etc/chrony/chrony.conf` (was `1 3`): step the clock whenever it's > 1 s off, not only in the first three
updates, because with no RTC battery a stale clock may only meet its first source (a late GPS fix) well into an offline drive.
chrony's config format has no inline comments; the explanation sits on its own line above.
- **`fake-hwclock`** saves the time hourly and at shutdown and restores it at boot, so an offline boot starts near the last shutdown
instead of the RTC's reset date. This replaces what timesyncd's `/var/lib/systemd/timesync/clock` did.
- gpsd must run with **`-n`** to feed SHM without a client. See the SIM7600 report: a stray gpsd started by a `gpsdctl` udev hook ran
without it, now fixed.
Check: `chronyc -n sources` (`^*` = selected NTP server, `#` = GPS refclock), `chronyc tracking`.
At setup: synced to `ntp-a3.nict.go.jp` (stratum 1); `GPS` listed with no samples (antenna detached indoors).