Skip to content

Latest commit

 

History

1,387 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

libgnss++

Modern C++20 GNSS toolkit for non-GUI positioning.

Native SPP, RTK, PPP, CLAS/MADOCA, RTCM, UBX, and direct QZSS L6 handling without an external RTKLIB runtime.

UrbanNav Odaiba social card

What You Get

  • Solvers: gnss spp, gnss solve, gnss ppp
  • Inputs/tools: RINEX, RTCM, UBX, SBF, NMEA, BINEX, QZSS L6
  • Products: SP3, CLK, IONEX, DCB
  • Extras: benchmarks, web dashboard, Python bindings, Docker, ROS 2 playback

Choose a use case: urban RTK + IMU continuity, RTKLIB migration, ROS2 receiver/bag replay, or QZSS L6 / CLAS / MADOCA.

Feature overview

Try it

The fastest first run uses the published runtime image; no local build is needed:

mkdir -p output
docker run --rm \
  -v "$PWD/output:/workspace/output" \
  ghcr.io/rsasaki0109/gnssplusplus-library:v0.2.0 \
  demo --output-dir /workspace/output/self-contained-demo

The command runs the tracked, project-authored synthetic PPP fixture entirely offline after the image is available. It should report 8 processed and 8 valid PPP solutions and write demo_solution.pos, demo_solution.kml, and demo_summary.json under output/self-contained-demo/. This validates CLI/build/artifact plumbing, not field accuracy, real-world satellite geometry, or RTK fix performance. See the full demo guide for provenance and native-build instructions.

Prefer a native source checkout? Build the PPP executable and run the same tracked demo:

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --target gnss_ppp --parallel 2
python3 apps/gnss.py demo

After the native demo, run python3 apps/gnss.py next. It validates the local demo result and gives one copy-paste next command for SPP, RTK, PPP, ROS2, QZSS, or C++ integration. The check is local; it does not transmit or store usage data. After a standard SPP, RTK, or PPP output is created, the same command validates that it contains solution epochs and advances to a KML inspection step. Installed builds expose the command as gnss next.

Use the C++20 library

The install exports a standard CMake package and the libgnsspp::gnss_lib target for downstream C++20 applications:

cmake_minimum_required(VERSION 3.14)
project(my_gnss_app LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(libgnsspp CONFIG REQUIRED)
add_executable(my_gnss_app main.cpp)
target_link_libraries(my_gnss_app PRIVATE libgnsspp::gnss_lib)

Build the complete exported library set before installing it: cmake --build build --parallel 2, followed by cmake --install build --prefix <prefix>. Configure the consumer with -DCMAKE_PREFIX_PATH=<prefix>. Start with the simple SPP example, the RTK positioning example, the public API header, and the interface notes.

See the v0.2.0 release highlights and maintainer release runbook for distribution details.

Results And Validation Status

Area Public comparison Evidence / status
RTK PPC Tokyo/Nagoya vs RTKLIB demo5 b34k +56.8 pp fix rate, +45.4 pp official score, -11.44 m P95 H delta (six-run average; demo5 publishes mostly FLOAT/SINGLE on more epochs, so Positioning is -9.5 pp)
GNSS/IMU FGO PPC Tokyo vs tightly-coupled-gnss-imu-fgo Higher fix rate on all 3 runs (avg +10.7 pp); <50 cm fraction higher on 2 of 3 runs (avg +7.9 pp, run1 -1.8 pp); fixed-only RMS wins 2 of 3 runs
CLAS PPP Six PPC Tokyo/Nagoya runs vs MRTKLIB CLAS 25.121% aggregate FIX, 0.359 m FIX RMS2D, and zero FIX epochs above 3 m across 58,259 scored epochs; every run passes the MRTKLIB v0.4.2 FIX-rate and FIX-RMS2D hard gates
Urban RTK UrbanNav Tokyo Odaiba vs RTKLIB demo5 b34k 922 vs 205 fixes, Hp95 5.87 vs 25.62 m, Vp95 16.42 vs 43.37 m; lower Hmed on common epochs (0.294 vs 0.548 m); both solvers use the surveyed base position
SPP PPC SPP adaptive robust + policy gate (Tokyo run1, Nagoya run1-3) No P95 H regression with <=1 pp positioning drop on the 4 checked runs
Smartphone GNSS/IMU GSDC 2023-2024 official submission Private 0.984 m / Public 0.915 m; 40 drives, 71,936 native output rows

Reproduce: python3 apps/gnss.py reproduce list shows one command per row; see docs/reproduce.md for datasets, tool pins, and the latest local results.

Smartphone GNSS/IMU

Official Google Smartphone Decimeter Challenge 2023-2024 submission 56625084 (2026-09-28) scored Private 0.984 m / Public 0.915 m. The submitted recipe combines Pixel5 heading initialization with Android clock handling and observation-model updates for supported phones. All 40 drives and 71,936 output rows were checked against native solver results.

The recipe uses raw GNSS/IMU, broadcast navigation, base observations, and training-derived height constraints where configured; evaluation ground truth is not used during inference. These official scores are separate from the development-route measurements below. The 0.928 m Private target remains unmet; work is paused at the sub-meter milestone. See the official score record and experiment and validation log.

Base-surveyed correction on the GSDC dev routes (Pixel5, (P50+P95)/2 m):

route H U A LAX-T
base-surveyed 0.576 0.740 0.303 0.716

GSDC base-surveyed dev routes on OpenStreetMap

Raw GNSS + IMU + broadcast nav only. Dev routes, not a Kaggle leaderboard score. Reproduce with gnss reproduce gsdc-dev-routes (about 23 min); details in the record.

PPC 2024 goal matrix vs Kaiyodai and gici-open

The audited KF/FGO selected profile clears the distance-weighted PPC public target at 78.8455% (published target: 78.7%). It also exceeds the Tokyo 1 public FIX rate (80.861% vs 80.8%). A separate FIX-target profile clears Nagoya 1 by the narrow measured margin 85.100974% vs 85.1%, with 0.913% Wrong FIX/FIX and 1.460 m P95 horizontal error. The public FIX targets come from the Kaiyodai RTK paper, and the PPC score target from the Turing tight-coupling slides.

Run libgnss++ FIX gici-open FIX Wrong FIX/FIX libgnss++ correct FIX/ref gici-open correct FIX/ref 50 cm/ref libgnss++ official gici-open official P95 H
Tokyo 1 80.861% 46.472% 2.855% 71.467% 43.528% 80.286% 79.458% 80.263% 2.082 m
Tokyo 2 82.340% 76.938% 0.463% 79.827% 74.462% 88.395% 88.696% 90.652% 1.604 m
Tokyo 3 78.461% 73.347% 1.366% 75.962% 71.923% 86.295% 85.969% 83.787% 1.671 m
Nagoya 1 83.659% 67.812% 0.547% 78.460% 60.005% 85.845% 65.201% 70.851% 1.332 m
Nagoya 2 55.553% 39.988% 0.541% 48.587% 35.330% 60.787% 55.529% 39.847% 18.144 m
Nagoya 3 44.761% 21.399% 3.007% 43.415% 18.285% 61.354% 72.336% 33.495% 1.908 m
Macro mean 70.939% 54.326% 1.463% 66.287% 50.589% 77.160% 74.532% 66.483% 4.457 m

PPC libgnss++ and gici-open comparison

PPC public targets

The audited runtime profile uses candidate telemetry only; reference truth is used after output generation for scoring. It reaches an official score of 78.845491% while reducing aggregate wrong FIX from 869 to 574, errors above 5 m from 96 to 42, and errors above 10 m from 59 to 5.

PPC selected XY trajectories by FIX status

gici-open was reproduced from commit e7666110a88d22e08aad24345a253564af9b8024 on its forppc2024 branch and evaluated with the same six references and metric code. The libgnss++ FIX macro is +16.613 pp higher and Wrong FIX/FIX is 1.025 pp lower. These are in-sample benchmark results, not a held-out generalization claim.

See the goal audit, FIX integrity audit, kinematic integrity LOO report, and external residual-integrity holdout. The Nagoya 3 root-cause analysis documents the catastrophic float-KF wrong basin. See the reproduction commands for the gate design, external replay, event ledger, machine-readable metrics, and licensing details. gnss reproduce ppc-goal rescores this section (about 1 min) from 26 SHA-256-pinned tier inputs that are not published; see reproduce. This is a score-only reproduction: the upstream solver tiers behind those inputs were produced with unrecorded commands and earlier solver revisions, so they cannot be regenerated from scratch.

GNSS/IMU Tightly-Coupled FGO vs tightly-coupled-gnss-imu-fgo

GTSAM fixed-lag factor-graph backend (tightly-coupled IMU, multi-frequency DD RTK, partial LAMBDA, fix-and-hold, CMC screening, CP-hold recovery, DDPR-anchored resets, FDE, varerr, surplus-satellite validation) on public PPC Tokyo replays, versus inuex35/tightly-coupled-gnss-imu-fgo on the same rover/base/IMU data:

Run libgnss++ <50cm Reference <50cm libgnss++ fix Reference fix libgnss++ fixed RMS Reference fixed RMS
Tokyo run1 54.9% 56.7% 53.8% 49.5% 1.180 m 0.815 m
Tokyo run2 85.7% 69.9% 78.6% 60.8% 0.109 m 0.277 m
Tokyo run3 77.5% 67.9% 69.3% 59.4% 0.125 m 0.211 m

Tokyo run1 GNSS/IMU FGO Tokyo run2 GNSS/IMU FGO Tokyo run3 GNSS/IMU FGO

The figures are rendered by scripts/plot_fgo_parity_runs.py from the per-run gnss_fgo_parity --dump-csv outputs.

With the geometry-free reset, libgnss++ exceeds the reference on raw FIX rate (all runs) and on <50 cm / fixed RMS (two of three), and improves the PPC official score:

Run Correct FIX, baseline -> GF reset Wrong FIX, baseline -> GF reset Official score, baseline -> GF reset GF guard demotions
Tokyo run1 25.656% -> 33.322% 23.765% -> 21.221% 30.823% -> 39.375% 14
Tokyo run2 60.304% -> 74.599% 12.573% -> 1.386% 65.264% -> 81.615% 0
Tokyo run3 59.712% -> 65.099% 7.726% -> 1.818% 65.446% -> 71.207% 17
Aggregate 49.440% -> 57.409% 13.649% -> 7.650% 54.837% -> 63.692% 31

Wrong FIX/FIX falls 21.634% -> 11.759%; matched distance is unchanged (99.682%); fixed-only RMS/P95 and vertical P95 improve on all runs. Reproduce both tables with gnss reproduce fgo-tokyo ("baseline" is the preset below without --gf-slip-reset; about 35 min for all six replays on a Windows MSVC build).

Every new behavior is opt-in; non-GTSAM builds are unchanged. Reproduce with gnss_fgo_parity (GTSAM build) and this preset:

--imu <run>/imu.csv --fixed-lag 5 --multi-freq --partial-ar --hold \
--elev-mask 25 --snr-mask 30 --cmc --cmc-level 0.75 --cp-hold \
--exc-recovery --ddpr-anchor --fde --varerr --fix-demote \
--fix-demote-res 25 --fix-demote-posthold 5 \
--fix-demote-surplus-crosscheck --fix-demote-surplus-anchor-reprieve \
--fix-demote-spp-model-reprieve --surplus-validation \
--surplus-validation-min-n 3 \
--surplus-validation-aperture-lt1 0.15 --surplus-validation-aperture-1to2 0.3 \
--surplus-validation-aperture-gt2 0.45 --anchor-gated-unfix-reset \
--imu-ratio-relaxed 1.5 --gf-slip-reset

A default-off Doppler-only DR witness failed the run1 zero-wrong gate (112 correct / 112 wrong), so it stays monitor-only. See the external Doppler-DR witness audit.

--dump-csv writes the ambiguity-candidate funnel (amb_*), the last LAMBDA candidate (lambda_candidate_*), and a satellite trace (<path>.ar_candidates.csv); its disposition codes (0-10) label each row's funnel exit. The same dump adds default-off, monitor-only diagnostics -- receiver-clock-free temporal carrier, causal DD-PR Doppler/IMU and pair-bias, geometry-free slip (--gf-slip-reset restarts both bands and guards low-redundancy FIX), integer consensus, counterfactual partial-AR (--ratio-impact-monitor), disjoint-partition AR, fresh-SPP (spp_seed_fresh), and FFRT covariance -- each documented under docs/.

Surplus-satellite rescue integrity

Ratio-rejected candidates can be rescued by an independent surplus-satellite test (excluded DD carrier rows re-differenced against an alternate reference, PDOP-scaled nearest-integer aperture, GQEBR->GQ fallback; requires >=10 sats, DD-code RMS <=5 m, carrier RMS <=0.05 m). The fixed-lag-5 preset replayed over all three Tokyo runs ("Correct"/"wrong" = FIXED 3D error < / >= 0.5 m). This table is a historical record from an earlier preset and solver revision and is not regenerated by gnss reproduce:

Run Correct FIX Wrong FIX Fixed horizontal RMS All epochs 3D <50 cm
Tokyo run1 4759 -> 4955 1088 -> 978 0.6866 -> 0.6616 m 5722 -> 5932
Tokyo run2 6336 -> 6339 314 -> 314 0.21668 -> 0.21663 m 7130 -> 7130
Tokyo run3 9961 -> 9963 1002 -> 1002 0.25744 -> 0.25742 m 10407 -> 10407

Three opt-in, fail-closed guards add only correct fixes: --fix-demote-surplus-anchor-reprieve (surplus + >=12 sats + DD-code anchor within 8 m), --anchor-gated-unfix-reset (reacquisition only after a current-epoch anchor agrees with the IMU prediction and differs from the antenna by >=1 m), and --fix-demote-spp-model-reprieve (fresh candidate, <=2 cm IMU separation, <=8 m SPP separation). Full replays added correct fixes with zero wrong; correct-FIX distance reached ~58.28% (+1.2 pp). --surplus-validation-veto is false-alarm dominated - leave it off; --problem-cache speeds repeated validation. The tables above use the fixed-lag-5 preset shown earlier (--fix-demote-res 25). Separately, a counterfactual audit of a different fixed-lag-1 --fixed-lag-qr configuration used --fix-demote-res 40 (tokyo fix 67.23/77.71/75.14%, fixed RMS 0.797/0.742/1.054 m); that setting is not part of the preset or tables here.

Moving CLAS PPP vs MRTKLIB

The current moving-data gate replays all six public PPC Tokyo/Nagoya runs at 5 Hz from QZSS L6 corrections with kinematic dynamics enabled. Scoring matches solutions to the PPC reference, discards the first 60 matched epochs per run, and defines TTFF as the first run of at least 30 consecutive FIX epochs.

MRTKLIB columns are the published v0.4.2 results from the CLAS benchmark article. The native results below use the bounded held-DD continuation policy: the ordinary direct state-DD floor remains six rows, while a valid uninterrupted hold may publish at four rows for at most five consecutive reduced-row epochs. Each run has 100% interval coverage and at least 99.95% epoch coverage.

Run libgnss++ FIX MRTKLIB FIX FIX RMS2D* MRTKLIB RMS2D† All RMS2D* FLOAT RMS2D* SINGLE RMS2D* max FIX* >3 m FIX*
Tokyo 1 10.982% 4.900% 0.351 m 0.747 m 41.861 m 16.824 m 80.507 m 1.961 m 0
Tokyo 2 21.859% 21.700% 0.329 m 0.514 m 25.880 m 18.346 m 45.128 m 1.333 m 0
Tokyo 3 38.378% 7.400% 0.191 m 0.801 m 35.276 m 19.617 m 88.519 m 2.986 m 0
Nagoya 1 36.923% 17.000% 0.451 m 1.105 m 57.163 m 7.966 m 119.111 m 1.043 m 0
Nagoya 2 23.969% 23.400% 0.554 m 1.119 m 25.829 m 16.233 m 40.405 m 0.767 m 0
Nagoya 3 9.010% 6.300% 0.304 m 0.318 m 13.724 m 14.392 m 14.380 m 0.587 m 0
Six-run aggregate 25.121% — 0.359 m — 36.522 m 16.885 m 70.361 m 2.986 m 0

* libgnss++ precision uses the raw PPC reference point (already antenna-positioned; no lever-arm transform is applied — an earlier revision of this table double-applied a vehicle→antenna lever arm on top of an already-antenna-positioned reference, inflating FIX RMS2D by ~0.3–0.9 m and incidentally masking the Nagoya 2 tail below the 3 m line). † The published MRTKLIB precision uses the same raw PPC reference, so the FIX RMS2D and p68 columns are directly comparable, not merely contextual — libgnss++ FIX RMS2D is now lower than MRTKLIB's on all six runs (bolded above).

PPC six-run moving CLAS metric comparison

Across 58,259 scored epochs, native CLAS produced 14,635 FIX epochs: 176 more than the fixed baseline, with no lost baseline FIX epochs. Every run meets or exceeds the published MRTKLIB v0.4.2 FIX rate, beats its FIX RMS2D, and has zero FIX epochs above 3 m. The six-run all-solution RMS2D is 36.522 m; FLOAT and SINGLE RMS2D are 16.885 m and 70.361 m respectively. p68 is a soft miss on Tokyo 2, Tokyo 3, Nagoya 1, and Nagoya 2; TTFF is a soft miss only on Nagoya 3.

The production defaults are held floor 4 and maximum reduced-publication streak 5. Set GNSS_PPP_CLAS_AR_HELD_MIN_DD_ROWS=6 and GNSS_PPP_CLAS_AR_HELD_MAX_PUBLICATION_STREAK=1 to reproduce the previous one-epoch/six-row behavior explicitly.

Complete trajectories Horizontal error and FIX epochs
PPC six-run CLAS trajectories PPC six-run CLAS errors

See the complete table, machine-readable metrics, and PPC CLAS validation note for definitions and reproduction details.

Quick Start

Choose the entrypoint that matches your job:

For repository orientation, see application structure, script layout, and standalone tools.

Build:

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
python3 apps/gnss.py doctor
python3 apps/gnss.py demo
python3 apps/gnss.py ros2-doctor --device /dev/ttyUSB0
python3 apps/gnss.py ros2-bag-doctor --bag /path/to/rosbag --summary-json output/ros2_bag_doctor_summary.json
python3 apps/gnss.py field-report --out output/field_report.md
python3 apps/gnss.py robotics-smoke --profile realtime

ros2-bag-doctor reads sqlite ROS2 bags for message-level rates/gaps. For MCAP bags it uses the optional Python mcap package when available, and otherwise falls back to MCAP metadata.yaml for topic presence, counts, and duration. gnss web auto-discovers output/field_report*.json and shows the report links, Markdown preview, setup/ROS2/bag/smoke status, and next debug commands.

Run a solution:

python3 apps/gnss.py spp \
  --obs data/rover_static.obs \
  --nav data/navigation_static.nav \
  --out output/spp_solution.pos

RTK example:

python3 apps/gnss.py solve \
  --rover data/rover_kinematic.obs \
  --base data/base_kinematic.obs \
  --nav data/navigation_kinematic.nav \
  --mode kinematic \
  --out output/rtk_solution.pos

Run the web UI:

python3 apps/gnss.py web --port 8085

Then open http://127.0.0.1:8085.

List commands:

python3 apps/gnss.py commands
python3 apps/gnss.py commands --json
python3 apps/gnss.py commands --query ppp --limit 10

Docker

docker build -t libgnsspp:latest .
docker run --rm -it -p 8085:8085 -v "$PWD:/workspace" \
  libgnsspp:latest web --host 0.0.0.0 --port 8085 --root /workspace

Benchmarks

Docs

Install

cmake --install build --prefix /opt/libgnsspp
/opt/libgnsspp/bin/gnss --help

Tests

ctest --test-dir build --output-on-failure

License

MIT License. See LICENSE. Permissive third-party attributions and the separate GPL-only competitor-benchmark boundary are documented in THIRD_PARTY_NOTICES.md.

About

Modern C++20 GNSS toolkit — RTK/PPP/CLAS/IMU fusion.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

224 stars

Watchers

7 watching

Forks

Releases

Packages

Contributors

Languages