GNSS-Rohdaten auf Android: jeden Satelliten nutzen
Wie walker Androids GNSS-Rohdaten aus fünf Satellitensystemen und zwei Frequenzbändern auf dem Handy auswertet, und was das auf einer echten Radtour brachte.
· Malik · 21 Min. Lesezeit
Im Freien hat mein Handy 26 bis 36 Satelliten gleichzeitig gemessen, aus fünf Systemen und auf zwei Frequenzbändern. Die erste Bereinigung mit Satelliten, die ich geschrieben habe, nutzte davon etwa 12: GPS auf L1 und BeiDou auf B1I. Mehr als die Hälfte dessen, was das Handy maß, kam nie beim Solver an.
walker, die App für Spaziergänge, Wanderungen und Radtouren, die ich für Android entwickle, bereinigt jeden Track auf dem Handy. Eine Zeit lang hat sie dafür die fertigen GPS-Punkte des Chips verwendet. Inzwischen liest sie auch die Satelliten selbst: jedes System und jedes Band, das mein Handy misst und für das es gesendete Bahndaten gibt. Dieser Beitrag erzählt, was dafür nötig war und was es gebracht hat. Der Faktorgraph, der all das gewichtet, steht in GPS-Tracks glätten mit einem Faktorgraphen; die Reihe beginnt mit Eine ganze Android-App in Rust statt Kotlin.
Unter dem GPS-Punkt
Ein GPS-Punkt des Handys ist das Ergebnis eines Filters im GNSS-Chip, und meine zwei größten Probleme kamen aus diesem Filter, nicht vom Himmel. Das erste ist der Static Hold: Manche Chips halten den GPS-Punkt fest, während du weitergehst. Die Rohmessungen ändern sich währenddessen weiter, Satellit für Satellit, und eine Lösung aus ihnen friert nicht ein. Das zweite: Ein GPS-Punkt trägt nur eine Zahl für seine Qualität, eine einzige Genauigkeit. Die Rohmessungen dagegen liefern für jeden Satelliten die Signalstärke (C/N0), das Band und einen Mehrwegeindikator, ein Flag für Anzeichen, dass das Signal auch über Reflexionen angekommen ist.
Was Android dir gibt
Rohmessungen gibt es in android.location seit Android 7 (API-Level 24): Ein
GnssMeasurementsEvent enthält eine GnssClock und ein GnssMeasurement pro Signal. Seit
Android 10 sind sie Pflicht. Was du nicht bekommst, ist eine Pseudoentfernung, also die
Entfernung, die aus der Laufzeit des Signals folgt. Die baust du aus zwei Zeiten selbst, so
wie es Googles gps-measurement-tools und das White Paper der Europäischen GNSS-Agentur (GSA)
tun.
Die erste Zeit ist der Moment, in dem der Empfänger gemessen hat, in GPS-Zeit, die ab dem
6. Januar 1980 zählt. Android liefert sie in Teilen. TimeNanos ist die Uhr des Empfängers.
FullBiasNanos und BiasNanos sind die Schätzung des Chips, wie weit diese Uhr von der
GPS-Zeit abweicht, in ganzen Nanosekunden und dem verbleibenden Bruchteil; sie werden
abgezogen. TimeOffsetNanos ist der Versatz dieser einen Messung gegenüber der Uhr der
Epoche. Android legt fest, dass er zu TimeNanos addiert wird, er gehört also zur
Empfangszeit und nicht zur Zeit des Satelliten.
Die zweite Zeit, , ist der Moment, in dem der Satellit das Signal gesendet hat, nach
der Uhr des Satelliten: ReceivedSvTimeNanos. Sie zählt ab dem Beginn der Woche des Systems,
bei GLONASS ab dem Beginn des Tages, und in der Zeit des jeweiligen Systems. Galileo und QZSS
laufen auf GPS-Zeit. Die BeiDou-Zeit liegt 14 s dahinter: Sie begann 2006, als seit dem Start
der GPS-Zeit 14 Schaltsekunden vergangen waren, und hat seitdem keine weitere übernommen.
GLONASS zählt den Moskauer Tag, der UTC mitsamt den Schaltsekunden folgt. Die Empfangszeit
wird deshalb zuerst in die Systemzeit des Satelliten umgerechnet, .
Beide Zeiten zählen innerhalb einer Spanne, die sich wiederholt, einer Woche oder einem Tag. Ihre Differenz muss deshalb auch innerhalb dieser Spanne gebildet werden. Laufzeit und Pseudoentfernung sind dann
wobei die Spanne ist, in der der Satellit zählt (eine Woche oder bei GLONASS ein Tag), die Lichtgeschwindigkeit und die Pseudoentfernung in Metern.
Die Fallen stecken alle an den Rändern.
- Ein Signal, das kurz vor dem Ende der Woche oder des Tages gesendet und kurz danach empfangen wird, käme auf eine Laufzeit von fast einem ganzen . Eine Laufzeit über der Hälfte von wird deshalb zurückgefaltet. Einer von walkers Tests sendet ein GLONASS-Signal um 23:59:59,96 Uhr Moskauer Zeit und empfängt es um 00:00:00,03 Uhr; heraus kommen 70 ms Laufzeit.
- Galileos E1-Signal trägt einen Code, der sich alle 100 ms wiederholt, den Sekundärcode von E1C. Ist der Chip auf diesen Code eingerastet, kennt er die Sendezeit auf 100 ms genau, bevor er die Zeit in der Woche decodiert hat. Der ganze Flug eines Signals passt da hinein, also nutzt walker das, mit einem von 100 ms.
- Eine Pseudoentfernung entsteht nur, wenn die Statusbits der Messung sagen, dass die volle Zeit in der Woche bekannt ist (bei GLONASS die Zeit am Tag) oder bei Galileo die 100 ms von oben. Diese Bits sind Androids Flags dafür, wie weit sich der Chip mit dem Signal synchronisiert hat. Sonst kennt der Chip nur die Codephase: die Stelle innerhalb einer Wiederholung des Codes.
- GPS-Zeit in Nanosekunden braucht 61 Bit, ein
f64hat 53. walker zieht zuerst die ganzen Zahlen voneinander ab und addiert die Bruchteile danach.
Die Rate musst du nicht selbst bauen: PseudorangeRateMetersPerSecond ist der Doppler in
Metern pro Sekunde. Während der Aufzeichnung fordert walker außerdem volles Tracking an.
Ohne das arbeitet der Chip im Duty-Cycling, schaltet seinen Empfänger also zwischen zwei
GPS-Punkten ab, um Strom zu sparen, und die Messungen verlieren ihre Kontinuität.
Was mein Handy tatsächlich misst
Mein Handy ist ein OnePlus 7 Pro. Der erste Test, drei Sekunden im Stehen bei schwachem Signal, sah nur GPS L1 und BeiDou B1I, 8–9 Satelliten. Laut seiner Liste der Fähigkeiten liefert das Handy weder Messungen noch Navigationsnachrichten. Messungen kommen trotzdem an, der Liste ist also nicht zu trauen.
Im Freien sah es ganz anders aus. Drei GnssLogger-Logs und eine Radtour an einem Tag zeigten 26–36 Satelliten pro Epoche (einem Messzeitpunkt):
| System | Satelliten pro Epoche | Bänder | Median des Doppler-σ |
|---|---|---|---|
| GPS | 11–15 | L1, L5 | 0,05–0,67 m/s |
| Galileo | 10–13 | E1, E5a | 0,59–0,86 m/s |
| GLONASS | 4–6 | G1 | 0,26–1,01 m/s |
| BeiDou | 1–2 | B1I | 0,67–0,81 m/s |
| QZSS | 0–0,4 | L1 | 0,85–4,36 m/s |
Bei 84–85 % dieser Messungen war die Zeit in der Woche decodiert, bei 0 % gab es eine gültige Trägerphase. Alles Weitere beruht also nur auf Code und Doppler. Von diesen 26–36 kam die erste Bereinigung mit GPS L1 und BeiDou B1I auf etwa 12 Satelliten pro Epoche. Das ist die Hälfte, die nie beim Solver ankam.
Bahndaten, ohne zu verraten, wo du warst
Eine Pseudoentfernung nützt nichts ohne die Position des Satelliten, und Android gibt die Ephemeriden des Chips, seine Kopie der Bahndaten, nicht heraus. walker hat zwei Quellen.
Die erste sind die Navigationsnachrichten, die Daten, die jeder Satellit über seine Bahn sendet. walker decodiert GPS LNAV, die ältere Nachricht auf L1, und BeiDous D1 und D2. Android sagt nicht, ob die GPS-Daten noch invertiert oder die BeiDou-Wörter noch verschachtelt sind. Jeder Unterrahmen wird deshalb auf jede Art gelesen und dort behalten, wo die Parität jedes Wortes stimmt. In der Praxis hat mein Handy in vier Aufzeichnungen keine einzige geliefert.
Die zweite ist die Tagesdatei der gesendeten Bahndaten vom BKG, eine zusammengeführte Navigationsdatei in RINEX 3, dem empfängerunabhängigen Austauschformat, mit GPS, GLONASS, Galileo, BeiDou und QZSS, alle 15 Minuten aktualisiert. walker lädt sie nur herunter, wenn du es in Settings unter Download satellite orbits (Satellitenbahnen herunterladen) erlaubst: Always (immer), Ask (fragen, die Voreinstellung) oder Never (nie). Die App speichert den Tag zwischen, und eine Tour behält die paar hundert Byte Bahndaten je Satellit, die sie braucht. Einen weiteren Download braucht sie danach nie.
Der Download hat walkers Regel, das Netz nur auf deinen Wunsch zu nutzen, erweitert. Deshalb lohnt es sich, genau zu sein. Die Datei ist an einem Tag für alle gleich. Der Server erfährt deine IP-Adresse und nach welchem Tag du gefragt hast. Eine Position oder eine Route erfährt er nie.
Für jede Messung wählt walker den zeitlich nächsten gesunden Datensatz des Satelliten, innerhalb der Zeit, für die die Datensätze seines Systems gelten.
Von Bahnelementen zur Entfernung
GPS, Galileo, QZSS und BeiDou senden Keplersche Bahnelemente: eine Ellipse und wie sie sich verschiebt. walker macht daraus eine Position, wie es IS-GPS-200 beschreibt, mit den Konstanten des jeweiligen Systems. Im Kern steht die Kepler-Gleichung. Die mittlere Anomalie ist ein Uhrzeiger, der sich gleichmäßig dreht, und der tatsächliche Winkel des Satelliten muss daraus berechnet werden:
Dabei ist die Exzentrizität der Bahn und die exzentrische Anomalie. Eine geschlossene Lösung gibt es nicht. walker löst die Gleichung mit dem Newton-Verfahren ab , und jeder Schritt korrigiert die Schätzung um das, was ihr fehlt:
Dabei zählt die Schritte. Die Bahnen sind fast kreisrund, also reichen wenige Schritte. Die gesendeten Korrekturen, die Drift des Knotens (wo die Bahn den Äquator kreuzt) und die Drehung in erdfeste Koordinaten folgen so, wie das ICD sie beschreibt.
Die geostationäre Ausnahme
Die geostationären Satelliten von BeiDou sind die Ausnahme. Eine wirklich äquatoriale Bahn hat keinen sauber definierten Knoten. Ihre Elemente sind deshalb in einem gekippten System angegeben, und die Position muss zurückgedreht werden. walkers Test setzt einen solchen Satelliten auf seinen geostationären Radius und prüft, dass er eine Stunde lang auf einen Meter genau über demselben Ort bleibt.
Die Uhr des Satelliten
Die Uhr des Satelliten weicht nach einem gesendeten Polynom von der Zeit ihres Systems ab. Dazu kommen ein Term für die Relativität und einer für die Verzögerung des Signals im Satelliten selbst, die je nach Band verschieden ist. Bei GPS L5 weicht walker vom ICD ab. IS-GPS-705 korrigiert L5 mit einer Inter-Signal-Korrektur aus CNAV, der neueren Nachricht auf L5. Die Bahndatendatei, die walker liest, hat für diese Verzögerung aber nur den älteren LNAV-Wert, skaliert nach der Frequenz. Der Unterschied, einige Dezimeter je Satellit, landet im L5-Bias des Empfängers weiter unten.
Die Erde dreht sich, während das Signal fliegt
Die Bahn liefert den Satelliten im erdfesten System des Moments, in dem er gesendet hat. In den rund 70 ms des Flugs dreht sich die Erde unter ihm weiter. Das ist der Sagnac-Effekt. walker dreht den Satelliten in das System des Empfangsmoments. Das verschiebt einen GPS-Satelliten um bis zu etwa 150 m und eine Entfernung um bis zu einige Dutzend Meter.
Das Messmodell
Der Graph braucht jede Entfernung als Funktion der Größen, nach denen er löst. Ist der Uhrenfehler des Satelliten herausgerechnet, gewichtet walkers Graph für jedes Signal, in groben Zügen,
ist der Uhrenfehler des Satelliten, die Position des Satelliten und die des Empfängers, der Uhrenfehler des Empfängers in Metern, die Verzögerung in der Troposphäre, der Bias des Empfängers für dieses Signal gegenüber GPS L1 (mehr dazu unten) und das Rauschen. Für die Troposphäre reicht ein einfaches Modell auf Meereshöhe nach dem Höhenwinkel: etwa 2,4 m senkrecht nach oben und etwa 9 m bei 15°. Die Ionosphäre fehlt mit Absicht; sie bekommt weiter unten einen eigenen Abschnitt. Auch die Doppler-Raten bekommen ein Modell, aus den Geschwindigkeiten von Satellit und Empfänger entlang der Linie zwischen ihnen.
Der Uhrenfehler des Empfängers ist der Grund, warum eine Pseudoentfernung „pseudo“ ist: Er verlängert jede Entfernung einer Epoche um denselben Betrag und wird deshalb zusammen mit der Position gelöst (Abbildung 3).
Wo es gehakt hat
GLONASS ist nicht Keplersch
GLONASS sendet alle 30 Minuten eine Position, eine Geschwindigkeit und die Anziehung von Sonne und Mond (die lunisolare Beschleunigung), gültig für etwa 15 Minuten davor und danach. Integrieren musst du selbst: die Schwerkraft, die Abplattung der Erde (), diese Anziehung und die Zentrifugal- und Coriolis-Terme eines sich drehenden erdfesten Systems, schrittweise nach dem Runge-Kutta-Verfahren vierter Ordnung, wie es das ICD vorgibt.
Wer diese Gleichungen in der Ausgabe des ICD von 2008 nachschlägt, findet zwei Terme, die wie Druckfehler aussehen. In der erdfesten Form hat der Coriolis-Term in der - und der -Zeile dasselbe Vorzeichen, obwohl er in jedem drehenden System zwischen ihnen das Vorzeichen wechselt. Und der Abplattungsterm in der -Zeile weicht von der inertialen Form derselben Gleichungen ein paar Seiten vorher ab. walker folgt der Physik. Der Test nimmt einen echten Zustand und führt ihn 15 Minuten weiter bis zur nächsten Sendung. Er landet auf 5 m genau. Mit dem Coriolis-Vorzeichen wie gedruckt verfehlt er sie um 31 km, ohne um 14 m. Echte Datensätze, gegeneinander geprüft, haben die Referenzwerte ersetzt, die ich aus RTKLIB übernehmen wollte.
Die Uhr von GLONASS ist einfacher als die der anderen, mit eingebauter Relativität. Die Bahndatendatei gibt ihre Bezugszeiten in UTC an. walker rechnet sie mit den Schaltsekunden in GPS-Zeit um, und so laufen die GLONASS-Bahnen auf derselben Uhr wie alle anderen.
Galileo sendet jeden Satelliten doppelt
Galileos offener Dienst hat zwei Navigationsnachrichten, I/NAV und F/NAV. Sie teilen sich die Bahn, aber nicht die Gruppenlaufzeit, weil die Uhr jeder Nachricht für ein anderes Frequenzpaar gilt. E1 nimmt den I/NAV-Datensatz, E5a den F/NAV-Datensatz; welcher welcher ist, sagen die Datenquellen-Bits in RINEX.
Ein Bias pro Signal
Die Biases des Empfängers stellten sich als Eigenschaft des Signals heraus, nicht des Systems. Gegenüber GPS L1 verzögerte der Empfänger Galileo E1 um −219 m, GPS L5 um −2.350 m, Galileo E5a um −2.348 m, GLONASS um +1.141 m und BeiDou um −459 m, in jedem Log auf wenige Meter gleich. Galileos E5a liegt bei GPS L5, nicht bei Galileos E1. Ein Versatz pro System und ein gemeinsames L5 − L1 ließen Residuen von 45 m übrig. Der Graph hat deshalb einen Bias pro Signal: von oben ist eine von fünf Zahlen, für B1I, E1, E5a, L5 und G1.
Die GLONASS-Kanäle driften auseinander
GLONASS trennt seine Satelliten nach Frequenz. Jeder sendet G1 auf einem Kanal von −7 bis +6, und der Kanal legt die Trägerfrequenz fest:
Dabei ist die Trägerfrequenz von Kanal . Die Verzögerung eines Empfängers hängt von der Frequenz ab, und die Residuen reichten von etwa −4 m bei bis +3–5 m bei , bis der Bias neben einem Versatz auch eine Steigung in bekam. Die Steigung lag in allen drei Logs bei +0,7 bis +0,8 m pro . Das gilt für diesen Chip; ein anderer ist in vielleicht nicht linear.
Eine Höhenwinkelmaske
Tief stehende Satelliten haben die stärksten Mehrwegefehler, und bei 25 oder mehr Satelliten bleiben ohne sie genug übrig. Signale von knapp über dem Horizont fallen deshalb weg.
Die Entfernungen gewichten
Die erste Radtour mit Satelliten kam 13 % länger heraus als ohne sie. Das Gewicht jeder Entfernung berücksichtigt inzwischen ihre Signalstärke und Fehler, die länger anhalten. Die Entfernungen einer Epoche zählen zusammen wie einige wenige unabhängige, weil die Umgebung sie alle zugleich verbiegt. Die ganze Geschichte steht im Beitrag über den Faktorgraphen.
Die Bänder haben mich überrascht. L5 und E5a passen in jeder Epoche doppelt so gut wie L1 und E1: Median-Residuen von 1,7–2,6 m gegenüber 3,2–5,4 m. Trotzdem gab ihnen die Abstimmung eine höhere Untergrenze für ihr σ als L1, keine niedrigere, weil ihre Fehler länger anhalten. GLONASS bekommt ebenfalls eine höhere, für den Kanal-Bias, den das Modell übrig lässt.
Die Ionosphäre, die ich weggelassen habe
Die Ionosphäre verzögert jedes Signal um einen Betrag, der von seiner Frequenz abhängt. Genau das erlaubt es, sie mit zwei Bändern zu messen:
in Metern. TEC ist der Gesamtelektronengehalt entlang des Wegs in Elektronen pro Quadratmeter und die Trägerfrequenz in Hertz. Das gesendete Klobuchar-Modell kommt aus den GPS-Navigationsnachrichten, die mein Handy nie liefert. Die Datei des BKG hatte keine Kopfzeilen zur Ionosphäre. Auf diesem Handy gab es überhaupt kein Modell.
Satelliten mit zwei Frequenzen können sie messen. Die klassische Antwort ist die ionosphärenfreie Linearkombination der Pseudoentfernungen auf L1 und auf L5, wobei eine Pseudoentfernung auf einem Band ist, das von oben. Weil die Verzögerung mit dem Quadrat der Frequenz fällt, hebt eine gewichtete Differenz der beiden sie auf:
mit MHz und MHz. Die -Verzögerungen heben sich genau auf. Das Rauschen nicht: Bei gleichem, unabhängigem Rauschen σ auf beiden Entfernungen trägt die Kombination
Beim Code eines Handys, dessen Rauschen und Mehrwegefehler bei einigen Metern liegen, ist das ein schlechter Tausch für eine Verzögerung von ein paar Metern. walker berechnet sie nie.
Ich habe es deshalb sanfter versucht: eine einzige glatte Ionosphäre, eine dünne Schale mit Gradienten, angepasst an alle L1-L5-Paare eines ganzen Logs auf einmal, zusammen mit dem L5 − L1-Bias des Empfängers, damit sich das Coderauschen herausmitteln kann. Über drei Logs ergab das senkrechte Verzögerungen von −0,5, 1,6 und 2,7 m, bei einem σ der Paare von 5–6,5 m. Angepasst an jeweils fünf Minuten schwankte sie zwischen −2,3 und 9,3 m: Das Rauschen der Paare ist viel größer als die Verzögerung. Sie herauszurechnen verschob die Lösung je Epoche um höchstens 0,7 m, in die eine wie die andere Richtung, und änderte die bereinigte Distanz der Radtour um nur 2 m. Sie bleibt im Diagnosewerkzeug und kommt nicht in die App. Die Biases pro Signal tragen die Entfernungen auf zwei Bändern.
Die Daten speichern
walker speichert die Rohmessungen und keine daraus abgeleitete Geschwindigkeit. So macht ein besserer Solver später auch alte Touren besser. Das kostet Platz. Die 1 h 16 min aufgezeichneter GPS-Punkte der Radtour kamen gepackt auf 8,5 MB, bei 64 Byte pro Messung: vier- bis fünfmal so viel, wie ich geschätzt hatte.
| Verfahren | Byte |
|---|---|
| walkers eigenes Format | 8.541.131 |
LZ4, schnell (lz4_flex) |
3.201.331 |
| LZ4 HC Stufe 9 | 2.397.753 |
| Deflate Stufe 6 | 2.300.428 |
Deflate ist am kleinsten, aber lz4_flex war über die Datenbank ohnehin im Build und brachte
kein neues Crate mit. Die Roh-GNSS-Daten jeder Aufzeichnung liegen jetzt als LZ4-Datei neben
der Datenbank, zuerst unter anderem Namen geschrieben und dann umbenannt, und mit SHA-256
geprüft. Die Datei der Radtour kam auf 3,2 MB, geschrieben in 38 ms und gelesen in 26 ms auf
meinem Desktop-Rechner. Nach Spalten gepackt ließe sie sich noch viel stärker komprimieren;
die Dateien tragen eine Version, das kann also später kommen.
Backups schreiben jede Datei in versiegelten Blöcken, sodass ein Jahr Touren nie auf einmal im Speicher liegt. Wie sie funktionieren, steht in Eine App ohne Server.
Was es gebracht hat
Pro Epoche, auf drei Logs, alle Signale gegenüber dem alten Satz aus GPS und BeiDou:
| Log | Signale pro Epoche | Median-Abstand zum GPS-Punkt des Chips |
|---|---|---|
| 9 min | 9,6 → 27,6 | 14,6 → 7,3 m |
| 4 min | 8,4 → 25,1 | 17,7 → 12,0 m |
| 28 min | 11,2 → 30,7 | 7,8 → 4,9 m |
Der Abstand zum GPS-Punkt des Chips halbiert sich im ersten Log und sinkt in den beiden anderen um etwa ein Drittel. Das ist Übereinstimmung mit dem Chip, keine Genauigkeit: Für diese Logs habe ich keinen wahren Weg.
Für die bereinigte Radtour gelten die zwei Maße aus dem Beitrag über den Faktorgraphen: wie weit die GPS-Punkte von den kartierten Wegen liegen (Median und 90. Perzentil) und wie gut zurückgehaltene Satelliten dazu passen:
| Radtour, bereinigt | Abstand zu den Wegen | σ der zurückgehaltenen Entfernungen |
|---|---|---|
| ohne Satelliten | 2,37 / 5,98 m | 7,61 m |
| nur GPS L1 und BeiDou | 2,29 / 5,28 m | 7,57 m |
| alle Signale | 2,24 / 5,34 m | 7,51 m |
Alle Signale schlagen den alten Satz beim Median und bei den zurückgehaltenen Satelliten, nicht aber beim 90. Perzentil. Ehrlich gesagt ist das wenig: Der Gewinn liegt vor allem bei den schlechtesten GPS-Punkten, und ein guter GPS-Punkt des Chips lag schon vorher nah am Weg. Außerdem kostet es Zeit: Eine Stunde mit 29 Signalen pro Epoche war in 3,5 s bereinigt, gegenüber 2,4 s mit 10, in einem Debug-Build auf meinem Desktop-Rechner.
Als Nächstes hätte ich gern eine echte Referenz: Punkte, zu denen ich gehe und die ich auf der Karte markiere, und eine Autofahrt, die auf die Mitte ihrer Fahrspur abgeglichen wird. Damit ließen sich die Gewichte an der Wirklichkeit abstimmen statt an einer Karte. Bis dahin sind die Zahlen oben die, für die ich einstehe.