Skip to content

fix(gps): correct UBX NAV-SVINFO and NAV-SAT satellite decoding#524

Open
mrosseel wants to merge 3 commits into
brickbots:mainfrom
mrosseel:fix/gps-svinfo-field-offsets
Open

fix(gps): correct UBX NAV-SVINFO and NAV-SAT satellite decoding#524
mrosseel wants to merge 3 commits into
brickbots:mainfrom
mrosseel:fix/gps-svinfo-field-offsets

Conversation

@mrosseel

@mrosseel mrosseel commented Jul 5, 2026

Copy link
Copy Markdown
Collaborator

Problem

The satellite counts shown by PiFinder (e.g. GPS 20/0 on the status screen) were wrong on all GPS receiver generations, in two different ways:

  • protVer < 15 receivers (NEO-6/7, early M8 — NAV-SOL + NAV-SVINFO from gpsd): _parse_nav_svinfo read every per-satellite field one byte off. The UBX repeated block is chn, svid, flags, quality, cno, elev, azim, but parsing started at chn. So satellite IDs were channel numbers, the per-sat used flag was svid & 1 (random), and "C/N0" was really the 0–7 quality indicator. Since quality is ≥1 even for idle SBAS/QZSS channels still searching, the seen count was inflated to near the full channel count (the mysterious "20 seen" with nothing tracked), and uSat was garbage — which is presumably why it was treated as stale and ignored in favour of NAV-SOL.
  • protVer ≥ 15 receivers (later M8, M9, M10 — NAV-PVT + NAV-SAT; M10 has no NAV-SOL/SVINFO at all): the used count stayed 0 forever despite a valid fix, because NAV-PVT's numSV was parsed but never surfaced, and NAV-SOL never arrives on these units.

Fix

  • _parse_nav_svinfo: shift field offsets by one per the u-blox spec; decode elev/azim as signed; count only satellites with an actual signal as seen. With correct offsets the svUsed flag is valid, so SVINFO now reports a trustworthy uSat.
  • gps_ubx.py: report uSat from SVINFO instead of the stale-info workaround; update the used count from NAV-PVT numSV; actually set got_sat_update when NAV-SAT arrives so the SVINFO fallback defers to it as intended.
  • _parse_nav_sat: take used from the dedicated svUsed bit (bit 3) instead of the quality heuristic (which also counts tracked-but-unused sats); don't count zero-signal entries as seen; decode elev/azim as signed.

Verification

  • New unit tests (tests/test_gps_ubx_parser.py) with spec-built payloads for both message types, including the idle-channel inflation case.
  • Live-verified against a real MAX-M8 (PROTVER 14) via gpsd: the patched parser's SVINFO output (nSat 11 / uSat 6, PRNs, C/N0) exactly matches NAV-SOL numSV and gpsd's own SKY decode; the old code produced the inflated counts on the same stream.
  • Full unit suite, ruff, and mypy pass.

The NAV-PVT/NAV-SAT path is verified against the spec and unit tests only — a sanity check on a unit with a newer (M9/M10) module would be welcome.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NrdGJ1s3jmA9qzV8HAsdZs

mrosseel and others added 3 commits July 5, 2026 23:29
The repeated block in UBX-NAV-SVINFO is chn, svid, flags, quality,
cno, elev, azim — but the parser started reading at chn, so every
field was shifted one byte: satellite IDs were channel numbers, the
used flag was svid bit 0, and the C/N0 was the 0-7 quality indicator.

Because "cno" was really the quality indicator (>=1 even for idle
SBAS/QZSS channels still searching), the sats-seen count was inflated
to near the full channel count, and the per-satellite used flags were
random — which is why uSat was previously treated as stale and
ignored.

With the offsets fixed:
- nSat only counts satellites with an actual signal (cno > 0)
- uSat from SVINFO is valid, so report it as the used count instead
  of relying solely on NAV-SOL
- elevation/azimuth are decoded as signed values per the spec

Also update the used count from NAV-PVT numSV, which was parsed but
never surfaced — on protVer >= 15 receivers gpsd enables NAV-PVT
instead of NAV-SOL, so the used count stayed 0 forever despite a
valid fix. And actually set got_sat_update when NAV-SAT arrives so
the SVINFO fallback defers to it as intended.

Verified against a live MAX-M8 (PROTVER 14) stream: SVINFO uSat now
matches NAV-SOL numSV, and IDs/C-N0 match gpsd's own SKY decode.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NrdGJ1s3jmA9qzV8HAsdZs
Newer u-blox receivers (later M8 firmware, M9, M10) get NAV-PVT +
NAV-SAT from gpsd instead of NAV-SOL + NAV-SVINFO, so this path is
what recent PiFinder GPS units actually exercise.

- take "used" from the dedicated svUsed flag (bit 3) instead of
  inferring it from the quality indicator
- only count satellites with an actual signal: NAV-SAT lists every
  known satellite, so nSat was inflated by idle entries
- decode elevation/azimuth as signed values

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NrdGJ1s3jmA9qzV8HAsdZs
During cold start the receiver reports an estimated C/N0 for
almanac-predicted satellites it is still trying to confirm, so a
cno > 0 filter makes the seen count start around 20+ and sink to the
real value as candidates fail to confirm. Gate on the quality
indicator instead: only code-locked signals (quality >= 4) count as
seen, in both NAV-SVINFO and NAV-SAT.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NrdGJ1s3jmA9qzV8HAsdZs
@mrosseel mrosseel force-pushed the fix/gps-svinfo-field-offsets branch 4 times, most recently from be023f6 to 3071268 Compare July 6, 2026 15:20
@brickbots

Copy link
Copy Markdown
Owner

@mrosseel is this still waiting on some additional commits, or is it ready to merge?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants