Analyze RINEX observation files to compute signal quality scores, detect threats (jamming, spoofing, interference), generate per-constellation statistics, and produce skyplot data. Used in production at Romanian national geodetic network (ROMPOS), precision agriculture, and GNSS security research.
Features
|
🛡️ Threat Detection Jamming · Spoofing · Interference Three independent detectors Visibility-based spoofing (new in 0.3.8) |
📊 Quality Scoring Composite 0–100 score 5 weighted components A–F letter grade |
🌍 6 Constellations GPS · GLONASS · Galileo BeiDou · QZSS · NavIC Per-constellation stats |
|
⚡ Rust Performance 0.14–2.6 s per 24 h file Zero Python dependencies ThreadPool-parallel batches |
📡 RINEX Support v2.x / v3.x / v4.x Broadcast navigation Visibility prediction |
🔬 Full API JSON export · Timeseries Skyplot data · Anomaly list Desktop GUI script included |
Installation
pip install geoveil-cn0
No Rust toolchain required — pre-built wheels for Linux (x86_64 + ARM/piwheels), Windows, and macOS. Python 3.9–3.12.
Quick Start
from geoveil_cn0 import AnalysisConfig, CN0Analyzer
config = AnalysisConfig(
min_elevation=10.0, # mask angle, degrees
time_bin_seconds=60, # 1-minute bins
anomaly_sensitivity=0.5, # 0.0 = permissive, 1.0 = strict
interference_threshold_db=6.0,
)
analyzer = CN0Analyzer(config)
result = analyzer.analyze_file("COST00ROU_R_20260980000_01D_30S_MO.rnx")
q = result.quality_score
print(f"Quality score : {q.overall:.1f} / 100 ({q.rating})")
print(f"Jamming : {'DETECTED' if result.jamming_detected else 'clean'}")
print(f"Spoofing : {'DETECTED' if result.spoofing_detected else 'clean'}")
print(f"Interference : {'DETECTED' if result.interference_detected else 'clean'}")
print(f"Satellites : {result.total_satellites} tracked")
print(f"Constellations: {', '.join(result.get_systems())}")
geoveil-cn0 reads plain RINEX. Decompress Hatanaka (.crx, .??d) and gzip files first, for example with the hatanaka package: hatanaka.decompress_on_disk("file.crx.gz").
Spoofing: visibility-based detection (new in 0.3.8)
# Requires navigation file for ephemeris comparison
result = analyzer.analyze_with_nav(
"COST00ROU_R_20260408_0100_30S_MO.rnx",
"BRDC00IGS_R_20260408_01D_MN.rnx",
)
if result.has_visibility_prediction:
print(f"Confirmation rate: {result.visibility_confirmation_rate:.0f}%")
print(f"Unexpected sats : {result.visibility_mean_unexpected:.1f}")
print(f"Missing sats : {result.visibility_mean_missing:.1f}")
Architecture
flowchart LR
A["RINEX obs\n.rnx / .??o"] --> C
B["BRDC nav\n.nav/.rnx"] --> C
C["CN0Analyzer\nRust core"] --> D["Quality Score\n0–100"]
C --> E["Threat Flags\nJam/Spoof/Interf"]
C --> F["Visibility\nPrediction"]
C --> G["Timeseries\nCN0 per bin"]
C --> H["Skyplot\nAz/El tracks"]
D & E & F & G & H --> I["AnalysisResult\nJSON / Python API"]
Performance
Public IGS files from 31 December 2025, plain RINEX, one core of an Intel Xeon E5-2620 v3 (2014), median of three runs, min_elevation=10, 60 s bins:
| File | Size | Epochs × sats | analyze_file |
analyze_with_nav |
Peak memory |
|---|---|---|---|---|---|
| RINEX 2.11 · 24 h · 30 s · ZIMM (GPS) | 5.4 MB | 2,880 × 32 | 0.14 s | 4.31 s | 104 MB |
| RINEX 3.02 · 24 h · 30 s · BOR1 (6 GNSS) | 19.4 MB | 2,880 × 106 | 2.57 s | 7.97 s | 446 MB |
| RINEX 3.05 · 15 min · 1 Hz · BUCU | 9.6 MB | 900 × 44 | 0.99 s | 2.62 s | 201 MB |
| RINEX 3.05 · 1 h · 1 Hz · BUCU | 39.9 MB | 3,600 × 52 | 3.78 s | 9.98 s | 739 MB |
| RINEX 3.05 · 24 h · 1 Hz · BUCU | 948.1 MB | 86,400 × 127 | 129.1 s | 263.8 s | 14.2 GB |
The analysis is single-threaded; CN0Analyzer is stateless, so a ThreadPoolExecutor over files scales with the cores available. analyze_with_nav adds visibility prediction from broadcast ephemeris.
Quality Score Components
The composite quality score (0–100, result.quality_score) is computed from five weighted components:
| Component | Weight | Description |
|---|---|---|
| CN0 Quality | 30% | Mean signal strength: 0 at 30 dB-Hz, 100 at 45 dB-Hz |
| Availability | 25% | Satellites observed / satellites expected |
| Continuity | 20% | Absence of tracking gaps |
| Stability | 15% | Low CN0 standard deviation: 100 at 2 dB-Hz, 0 at 10 dB-Hz |
| Diversity | 10% | Constellations observed (4 = 100%) |
Rating: A ≥ 90 · B ≥ 80 · C ≥ 70 · D ≥ 60 · F < 60. result.score and result.quality_grade report the same score.
Threat Detection
| Threat | Algorithm | Default Threshold |
|---|---|---|
| Jamming | Rapid CN0 drop rate | >6 dB in <3 s |
| Spoofing | Unexpected satellite ratio (BRDC ephemeris comparison) | >40% ratio + >8 count + corroboration |
| Interference | Sustained CN0 degradation | >6 dB from baseline |
Spoofing detection requires a navigation file (
analyze_with_nav). The 0.3.8 algorithm compares observed satellites against ephemeris predictions — a high ratio of unexplained observations indicates signal replay attacks.
API Reference
AnalysisConfig
| Parameter | Type | Default | Description |
|---|---|---|---|
min_elevation |
float |
5.0 |
Mask angle in degrees |
time_bin_seconds |
int |
60 |
Seconds per analysis bin |
systems |
list[str] |
all | Constellations to analyze, e.g. ["G", "R", "E", "C"] |
detect_anomalies |
bool |
True |
Run the anomaly detectors |
anomaly_sensitivity |
float |
0.5 |
Detection sensitivity 0–1 |
interference_threshold_db |
float |
6.0 |
Interference trigger (dB) |
spoofing_unexpected_threshold |
float |
0.40 |
Fraction of unexpected satellites |
spoofing_min_unexpected_count |
float |
8 |
Minimum count to flag |
CN0Analyzer
| Method | Description |
|---|---|
analyze_file(obs_path) |
Analyze an observation file |
analyze_with_nav(obs_path, nav_path) |
Also predict visibility from broadcast ephemeris (spoofing check, skyplot) |
AnalysisResult — key members
| Member | Type | Description |
|---|---|---|
quality_score |
QualityScore |
overall, rating, cn0_quality, availability, continuity, stability, diversity |
quality_grade |
str |
Letter A–F |
jamming_detected / spoofing_detected / interference_detected |
bool |
Threat flags |
mean_cn0, min_cn0, max_cn0 |
float |
CN0 statistics (dB-Hz) |
total_epochs, total_satellites |
int |
File size in epochs and satellites |
visibility_confirmation_rate |
float |
Percent of predicted satellites observed (with nav) |
get_systems() |
list |
Constellations present |
get_constellation_summary(name) |
dict |
Per-constellation CN0 and satellite counts |
get_timeseries_data() |
dict |
hours, mean_cn0, satellite_count per epoch |
get_skyplot_data() |
list |
Per-satellite azimuth, elevation and CN0 tracks (with nav) |
get_anomalies() |
list |
Detected anomaly events |
to_json() |
str |
Full result as JSON |
Supported Formats
| Input | Extensions | Notes |
|---|---|---|
| RINEX 2.x observation | .??o, .obs |
|
| RINEX 3.x / 4.x observation | .rnx, .obs |
Mixed observation files |
| RINEX navigation | .rnx, .??n, .??p |
For analyze_with_nav |
Compressed files (Hatanaka .crx/.??d, gzip, ZIP) must be decompressed first.
Website
miluta7.github.io/geoveil-cn0: live CN0 skyplot, per-constellation signal curves and benchmarks from real geoveil-cn0 output.
Live Demo
batch.geoveil-rinex.eu — the GeoVeil batch dashboard runs this library in production: CN0 quality scoring, threat detection, skyplots and heatmaps for every processed RINEX file, plus advanced multipath sessions (per-code MP RMS, cycle slips, SNR-residual wavelet spectra, Fresnel zones) and long-term trend monitoring on daily 30 s station data.
Batch Processing
For large-scale processing this library is wrapped by the GeoVeil batch system (FastAPI + Celery + MongoDB + MinIO + React dashboard): parallel workers, automatic BRDC ephemeris download, per-session analysis settings, WebSocket progress, and result persistence. See the live demo above. For local scripting, CN0Analyzer is stateless — instantiate one per thread and process files with a ThreadPoolExecutor.
License
PolyForm Noncommercial 1.0.0 with an attribution term — see LICENSE.
Free for research, education, personal and other non-commercial use, provided you credit the author and cite the library (see Citation below). Commercial use requires a separate license — contact miluta.flueras@cartografie.ro.
Citation
@software{geoveil_cn0_2026,
title = {geoveil-cn0: High-performance GNSS signal quality analysis},
author = {Dulea-Flueras, Miluta},
year = {2026},
version = {0.4.1},
url = {https://github.com/miluta7/geoveil-cn0},
}
Metadata
Release files for geoveil-cn0 0.4.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| geoveil_cn0-0.4.1.tar.gz | 678.9 kB | Details |
Built distributions (wheels)
Total release size: 6.3 MB
Release files / geoveil_cn0-0.4.1.tar.gz
| Download URL | geoveil_cn0-0.4.1.tar.gz |
|---|---|
| Size | 678.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3a69766410ce914b71ea0e0fb7555cb8215844dd1c491c7471dd0e63212bd7e6
|
|
BLAKE2b-256 checksum How to use checksums |
e46489cf73f5fa61b3f861a7d7029b84908378a48009b4ff565674f31510e5cd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|
Release files / geoveil_cn0-0.4.1-cp312-cp312-win_amd64.whl
| Download URL | geoveil_cn0-0.4.1-cp312-cp312-win_amd64.whl |
|---|---|
| Size | 447.2 kB |
| Tags | CPython 3.12 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
1ed316c7ff6e6645339997176a7906f1ef0ec68a247a9de497ec9fb0085948a5
|
|
BLAKE2b-256 checksum How to use checksums |
085dadc6cbe909160b58d790dac5b73780592fbcf0bb191937420a740b8a2e75
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|
Release files / geoveil_cn0-0.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | geoveil_cn0-0.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 496.3 kB |
| Tags | CPython 3.12 Linux glibc 2.17+ x86-64 |
|
SHA-256 checksum How to use checksums |
7b55757961c5eb654e21ff3707a485ebd1d4492a93494d7b91c755988813d421
|
|
BLAKE2b-256 checksum How to use checksums |
d65661f319a8c43feb330820dfab35f9f048be061a711d14a98cc8b4ea8368a5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|
Release files / geoveil_cn0-0.4.1-cp312-cp312-macosx_11_0_arm64.whl
| Download URL | geoveil_cn0-0.4.1-cp312-cp312-macosx_11_0_arm64.whl |
|---|---|
| Size | 452.1 kB |
| Tags | CPython 3.12 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
409e02c93de52091f20017ed91b0f38514b5ff01977ae474c850549cd40df5a2
|
|
BLAKE2b-256 checksum How to use checksums |
92ee4f48b35fe7dbc7b20a7520e4bac5edd088f59ba1ceda1012e934e1c186a6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|
Release files / geoveil_cn0-0.4.1-cp311-cp311-win_amd64.whl
| Download URL | geoveil_cn0-0.4.1-cp311-cp311-win_amd64.whl |
|---|---|
| Size | 445.8 kB |
| Tags | CPython 3.11 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
b33b7ca609db9b7ca39722b4117660beb81f4ce69953137fbe6c025d52045248
|
|
BLAKE2b-256 checksum How to use checksums |
112e78e75e690aa74190f3ea2e18e6943f03a1c6612a6949cc9d0a102620be40
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|
Release files / geoveil_cn0-0.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | geoveil_cn0-0.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 495.6 kB |
| Tags | CPython 3.11 Linux glibc 2.17+ x86-64 |
|
SHA-256 checksum How to use checksums |
948ccf3284e27a1de4999c4cd5799daa4c31fb5ebe8da93792f7e9f189464591
|
|
BLAKE2b-256 checksum How to use checksums |
99b59a64bcaafa0fae3b92a56eff6c44bd7a940cbea1c68a33365a58a37d8fad
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|
Release files / geoveil_cn0-0.4.1-cp311-cp311-macosx_11_0_arm64.whl
| Download URL | geoveil_cn0-0.4.1-cp311-cp311-macosx_11_0_arm64.whl |
|---|---|
| Size | 452.3 kB |
| Tags | CPython 3.11 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
57d5a79bfc05d307a91ed482f506efe837a5109a485ffec954b2b4c3af283fe5
|
|
BLAKE2b-256 checksum How to use checksums |
df44974a54b67c10aa2ab567e8e16ebed2379ce2bb52185940ac2c370eaa302c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|
Release files / geoveil_cn0-0.4.1-cp310-cp310-win_amd64.whl
| Download URL | geoveil_cn0-0.4.1-cp310-cp310-win_amd64.whl |
|---|---|
| Size | 446.0 kB |
| Tags | CPython 3.10 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
6a5b2c67218acfd5df46d490b373c5e4ce93325f4f93ef2eb9f6a32505f480d8
|
|
BLAKE2b-256 checksum How to use checksums |
f76aceac297048926f3d9124aaae54df822245d5c820fe4b37f0c8de10d3ac83
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|
Release files / geoveil_cn0-0.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | geoveil_cn0-0.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 495.4 kB |
| Tags | CPython 3.10 Linux glibc 2.17+ x86-64 |
|
SHA-256 checksum How to use checksums |
0c1626bfbb6f9a6080be86183115b761951828bd9dabf4fea93d7170e6e71b11
|
|
BLAKE2b-256 checksum How to use checksums |
1a1b42c9e198876f87020e8a2cb182ca7e24de41ac87bf06d32a5bf5fad7d386
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|
Release files / geoveil_cn0-0.4.1-cp310-cp310-macosx_11_0_arm64.whl
| Download URL | geoveil_cn0-0.4.1-cp310-cp310-macosx_11_0_arm64.whl |
|---|---|
| Size | 452.2 kB |
| Tags | CPython 3.10 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
7bd07774b89ab09b3766d84a76715984cb55f05ecb7c653476c012b97aaf2d96
|
|
BLAKE2b-256 checksum How to use checksums |
1892c605d84a78c180c40a02e1818b443165673976de09db35421914212d69b4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|
Release files / geoveil_cn0-0.4.1-cp39-cp39-win_amd64.whl
| Download URL | geoveil_cn0-0.4.1-cp39-cp39-win_amd64.whl |
|---|---|
| Size | 446.7 kB |
| Tags | CPython 3.9 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
75e1e6056d1602b4ce35bbdd28af496a409042017ba890c48f4f4769739c3e12
|
|
BLAKE2b-256 checksum How to use checksums |
7422cb38e9c7235e5f63436a0ffcc78c1f3e7370dbc694a1d38fb32e27ac79ab
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|
Release files / geoveil_cn0-0.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | geoveil_cn0-0.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 495.9 kB |
| Tags | CPython 3.9 Linux glibc 2.17+ x86-64 |
|
SHA-256 checksum How to use checksums |
2bbf3619a8826d53dc09f568286bcdc4a08ba6963eba4a829c6f81e70aa59375
|
|
BLAKE2b-256 checksum How to use checksums |
068d8d973b1c7d9d1414c63dde05803231253ee107d8c8f418cad9f4275352eb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|
Release files / geoveil_cn0-0.4.1-cp39-cp39-macosx_11_0_arm64.whl
| Download URL | geoveil_cn0-0.4.1-cp39-cp39-macosx_11_0_arm64.whl |
|---|---|
| Size | 452.6 kB |
| Tags | CPython 3.9 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
e04cbfa79d94d7c853fa1dfdb19db9912294179349f9771536e467c1302ae8b0
|
|
BLAKE2b-256 checksum How to use checksums |
8fdf78d34629aef7128dd020a78c3dffc80aeab31d0fe9b9a88f505701280d70
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|