Repository navigation
Releases: juricabi/groundtrack
Release list
v2.6.0 Groundtrack
Telemetry viewer is now Groundtrack
"Telemetry viewer" was the name of the original app on Google Play, so searching for this one found that one. The launcher, the APK, the GPX files it writes and the map requests all say Groundtrack now.
It is a new app, not an update. Its id is juricabi.com.groundtrack, so it installs beside Telemetry viewer rather than over it, and it starts with default settings. Telemetry viewer stays on the phone until you remove it.
Bringing your flights across. Flights are now kept in GroundtrackLogs and video recordings in Movies/Groundtrack. To see the flights you already have, rename the TelemetryLogs folder to GroundtrackLogs before the first launch — or copy it, if you want Telemetry viewer to keep its list too. Recordings in Movies/Telemetry can be moved the same way.
What went with the old name
- A privacy policy that is this app's own. The old one was the original app's: another author's name and address, and Firebase, Crashlytics and Fabric, none of which this build has. It now says what the app stores, what it fetches and from whom: no accounts, no advertising, no analytics, no crash reporting.
- No more request to subscribe to the original author's YouTube channel three days after install. Under another name it spoke for someone else.
- The repository's Sponsor button, which paid someone else, is gone.
Nothing else changes: everything flies, records and replays as it did in 2.5.3.
2.5.3 — The picture records, and takes the whole screen
Recording the picture
The live picture now records. Tap the red dot on the picture; the label over it counts the time and the size, and a second tap saves the file to Movies/Telemetry, named with the same date stamp as the flight logs. It is kept out of TelemetryLogs on purpose: that folder is often synced off the phone, and video runs to gigabytes.
- Network streams are kept exactly as they arrive. RTSP (the Orqa goggles, IP cameras) and raw RTP pushed at a port are written as the H.264 or H.265 the sender made — no re-encoding, no quality lost, next to no work for the phone.
- USB cameras and MJPEG go through the phone's hardware encoder, at the camera's size, with frame times smoothed to the camera's own rate so the recording plays back evenly.
- A file a crash cannot spoil. Recordings are MPEG transport streams (
.ts), readable up to their last byte: a flat battery, a crash or an update mid-flight keeps everything up to the last second. VLC, mpv and ffmpeg play them directly. - One recording, one file. Turning the phone, a stream dropping out and coming back, leaving the screen and returning — the recording carries on in the same file, each gap kept at its real length.
- Never turned. The quarter-turn button is a way of watching; the recording keeps the picture as the camera sent it.
- Picture only — the stream's sound is not recorded. A recording ends by itself, and says so, when storage runs low; one that caught nothing leaves no file behind.
The picture takes the whole screen
A new expand button on the picture sets the map, the readouts and the system bars aside, so the picture fills the screen. Back, or the button again, returns the split exactly as it was. It survives turning the phone, and the 3D world keeps its ground while it is hidden.
Fixes
- A stream that goes quiet no longer leaves its last frame standing at its old size, spread over the map when the pane changes shape. The frame is cleared, the waiting card stands on its own, and the picture returns by itself with the stream.
More from the link
Every value below is read the way the firmwares' current sources send it — and where a link does not send one, it is not guessed.
- GPS precision — a new tile, off until you turn it on in Sensor display. It shows what the link sends: the receiver's accuracy in metres (Betaflight over Crossfire or Tracer, MAVLink from any firmware, PX4 High Latency), or HDOP (ArduPilot passthrough and MAVLink, iNav LTM), or S.Port's single digit as a bound. Metres and HDOP are different things and are never converted; the README's How GPS precision works says which link sends which.
- Temperature and motor RPM — two new tiles, off by default: iNav's ESC telemetry (hottest ESC, motors' mean), ArduPilot's temperature through ExpressLRS, Betaflight's barometer.
- Exact battery voltage from ExpressLRS 4.1 receivers, in millivolts rather than tenths.
- Betaflight's own GPS fix over Crossfire and Tracer, instead of a guess from the satellite count. (Betaflight does not send it over ExpressLRS; there the count still decides.)
Tested on a Galaxy S24 Ultra with the Orqa goggles over RTSP, a DJI Osmo Action 5 Pro over USB, test streams over raw RTP and MJPEG, and the flight simulator for the new readings. The unit-test suite grew from 192 to 259 tests.
2.5.2 — The cable that returns is the retry
USB auto-reconnect
A USB serial link that drops without being asked now comes back by itself. There is nothing to poll while the port is gone, so the app does not try — the replug is the retry: plug the cable back in (and pick USB Serial on the radio if it asks) within a minute of the drop, and the link resumes into the same flight and the same log. The button reads Reconnecting… for as long as the intent stands.
- One minute, every transport. Bluetooth, BLE, network and USB now share the same reconnect window. Bluetooth had 21 seconds, which gave a radio that browned out and rebooted no time to come back.
- Every ending is announced. A retry that runs out of time says Reconnect timed out; a deliberate act — pressing Connect, opening a replay, ending the flight — says Reconnect called off. Silence now genuinely means "still trying".
- The charging hint. If USB drops and reconnects three times in quick succession, that rhythm is usually the radio charging from the phone faster than the phone can feed it, resetting the bus. The app says so once, in a bar that stays until dismissed, and names the cure: a USB-A OTG adapter or a powered hub. Tip: ticking Always allow on the USB permission dialog and pinning the radio's USB mode to Serial (VCP) makes recovery fully hands-free.
- The reconnect switch in settings now says what it governs: a lost radio link — Bluetooth, BLE or USB serial alike.
Under the hood
This release also carries the largest internal restructuring the app has had. The main screen shed 40% of its code — the telemetry panel, the video pane, the connect chooser, the log dialogs, the replay resume logic, the two-view overlays, the permission sequencing and the traffic voice each moved into a module of their own, and the connection paths for Bluetooth, USB and network now share one chassis. Behavior is unchanged on purpose: the unit-test suite grew from 120 to 192 tests, two deep code reviews ran over the moves, and the full connect/replay/video/rotation matrix was re-verified on a phone.
Small things that ride along:
- Exporting GPX/KML no longer rewinds an open replay to the start.
- Long-pressing the rate tile now opens the rate-system chooser even before any link has connected.
- A manually chosen rate system (for nameless links) is remembered in app settings now; an override saved by an older version is forgotten once — pick it again and it sticks.
2.5.1 — The stream holds, and the logs line up
Log manager
- The recordings have a manager, not a list. Flights grouped under the
day they were flown, opened with a tap — or marked several at a time: a
long press starts a drag that paints a run of rows. Delete or rename from
there, and the list refreshes in place without reopening. - Back up and restore, as one zip. Export the marked flights — each
recording and the sidecar beside it — to a place you pick: a folder, a USB
stick, Drive, with no storage permission asked. Import that zip back on
another phone; a name that clashes with a flight already there is taken in
beside it as name (1), never over it, and an identical file is left alone.
Attitude, put right
- The model pitches the way the drone flew. The 3D model drew a
forward-flying quad nose-up where the horizon and the flight itself said
nose-down. Measured from a recording and corrected in one place, so every
protocol's model is right at once — the horizon was already true. ArduPilot's
passthrough, which packs pitch the opposite way round, is put right against
its own source. Roll and yaw were already right.
Link rate
- The rate reads under the right table. ExpressLRS, Crossfire and Tracer
number their RF modes differently, and the module's name — the one thing
that tells them apart — never arrives over a Bluetooth telemetry mirror, so
a Crossfire's rate showed under the ExpressLRS numbers. Long-press the rate
tile to set the system by hand; a real detected name (WebSocket, USB) still
wins over the choice.
Video
- Faster, cooler video. The live picture now rides a SurfaceView — a hardware layer the whole screen shares — so it runs at higher FPS and the phone stays cooler, especially next to the 2D map and 3D terrain.
- Turn the picture, on any source. A rotate button turns the feed for a camera or receiver mounted sideways — UDP, MJPEG, USB (UVC) and RTSP alike. The button wears the current angle and brightens only while held.
- RTSP over UDP or TCP — your choice. A switch beside the stream address (in Settings and the long-press panel). TCP, the default, is steady on any link and never tears a frame. UDP is lower-latency and is the one for FPV goggles on their own WiFi: some goggle servers (Orqa's among them) drop a TCP stream moments after every keep-alive, the freeze-every-half-minute this tracked down — over UDP the keep-alive never touches the picture, and the feed plays as smoothly as the goggle maker's own app.
- RTSP recovery, steadied. Starts fast (100 ms), rides out a stutter, and when a stream truly dies it recovers in about two and a half seconds with a fresh connection instead of freezing — reconnects no longer get slower the longer you fly, and a dead stream no longer hangs the screen. A rotate reconnects cleanly behind a black cover, and a crash when the stream was lost mid-turn is fixed.
- Long-press the video button for a quick source/address panel — switch between the bench camera, goggles and ground station without opening Settings.
- Hear the USB receiver. An analog VRX over USB (UVC) that carries audio now plays its sound through the phone; a speaker button appears only when there's audio, and the microphone permission is asked the first time you turn it on.
Connections
- Keep mobile data on, and everything still connects. Telemetry and video now go out by whichever network actually reaches the module — the goggles' or module's WiFi, this phone's own hotspot, even a USB-ethernet adapter — while the maps carry on loading over mobile. So you can set this app to mobile data preferred in Android's settings and have both at once: a link with no internet, and tiles that keep arriving. Nothing changes for a normal setup.
- Find works when it should. The subnet search now looks on the network you picked instead of whichever one the phone preferred, so a module that was there all along is found — including one that joined this phone's hotspot.
- The network dialog keeps up. Join the module's WiFi or plug an adapter in while it is open and it says so within a couple of seconds, instead of describing whatever was there when it opened.
Screen
- Fullscreen reaches the edges now. On a phone with a camera cutout it fills the whole screen, up to the top edge, instead of leaving a black band where the status bar was. And pulling down the notifications no longer drops you out of it — the bars slide over and away, and you stay fullscreen.
The map before a fix
- Nothing is drawn from a position the receiver has not fixed yet. While it is still finding satellites a receiver reports where it last was — which can be a continent away. That position used to draw a heading line and a line home to a model that was not on the map, aim "Find my quad" at the wrong side of the world, and swing both views there the moment you opened one. Now the map and the 3D view both wait, and open on you instead; the model and its lines appear together when the fix is real.
- LTM links read the fix correctly. An LTM receiver announcing "I have a GPS but no fix yet" was being taken at the opposite of its word, so everything above happened there even after it was fixed elsewhere. A genuine 2D fix was likewise being ignored.
3D terrain
- The flight plan stays on the ground. Imported plans now ride the terrain as it sharpens at close zoom, instead of sinking beneath a finer mesh.
2.5.0 — A picture beside the map
Two features, both flown until they behaved: live video beside the map, and the drone's GPS as the phone's own position.
-
A live picture beside the map. Pick a source under Settings → Video and a video button joins the top bar: the picture takes one half of the screen, the flight — map or 3D, with every overlay — keeps the other. The seam between them drags, remembered separately for portrait and landscape, and the picture never crowds past half. On the picture: a speaker where the stream could carry sound, fill to crop away the letterbox, and a quarter-turn per tap for a sideways camera — all remembered.
-
Four kinds of source, each field-tested. USB (UVC) over OTG — analog receiver dongles, DJI / Walksnail / Orqa goggles in webcam mode, and UVC 1.5 action cameras like the DJI Osmo Action series that stock Android libraries refuse. RTSP for IP cameras and relays. MJPEG for the cheap end — ESP32-CAM, mjpg-streamer, the IP Webcam app. And a raw pushed stream (
udp://5600): RTP straight at the phone the way OpenIPC / wfb-ng ground stations send it, H.264 or H.265, codec read off the stream itself. -
Latency treated as the point, not a nicety. RTSP holds a 150 ms cushion instead of ExoPlayer's seconds, and a picture that slips behind the camera's clock jumps back to the live edge. MJPEG decodes only the newest frame and drops the rest, so a slow phone stays live instead of minutes behind. The raw
udp://path has no buffer at all — every frame renders the moment it decodes. -
A stream that cannot connect keeps its pane. The card says what is wrong — with the whole address, so a typo has nowhere to hide — and the stream is retried every two seconds until the server appears. RTSP rides TCP from the first frame, which ended the smeared opening second every fresh address used to pay.
-
Refusals come with words. A 4:4:4 or RGB test stream — what ffmpeg encodes if not told
-pix_fmt yuv420p— is named and refused before the decoder can die on it. MPEG-TS on the UDP port says what it is and what to change. One honest limit: H.265 over RTSP does not play — the player library's H.265 RTP reader is unfinished upstream — so send H.265 as audp://push, where it works. -
The drone's GPS as the phone's position. Settings → Mock location republishes the decoded drone GPS as the phone's own fix while telemetry is connected — position, altitude, speed — so a tracker app on the phone broadcasts the drone instead of the pilot. Opt-in, one-time setup under Developer options, and the phone's own GPS is back the moment the link ends or the switch goes off.
-
The camera permission asked where it matters. Android hands a USB camera only to a holder of the camera permission, and refuses silently otherwise — the ask now happens at the tap of the video button, the pane waits with a card saying so, and a permanent denial offers the app settings instead of a dead end.
-
Smaller, from the release audits: the debug build no longer ships a USB library's exported test screens; CI refuses to publish a tag it cannot sign; every video path starts on a fresh surface, so switching MJPEG to RTSP mid-session cannot poison the hardware decoder; and a portrait-only launch crash found in the field is fixed and fenced.
2.4.1 — The flight ends when you say so
Fixes and finishing on top of 2.4.0, most of it found by flying it.
-
The map no longer turns to rainbow. Zooming into new ground came up in coloured bands whenever a model was on screen. The moving-lines layer was leaving the map's graphics state altered; it puts it back now.
-
A flight ends when you say so. Disconnect clears the flight from both views. A link that drops keeps everything — that is the one where the model may be lying in a field.
-
The row says what the link speaks. FrSky, CRSF, GHST, LTM, MAV v1, MAV v2, MAV HL, and CRSF+AP when an ArduPilot is talking over the link. Replays say it too. Off by default, in Sensor display settings.
-
Traffic knows where to measure from. From the model while it is drawn, from you when it is not — and the warning says which. Standing at the field between packs is when an aircraft overhead matters most.
-
Longitude crosses the 180th meridian. A flight at Fiji used to anchor its world nineteen thousand kilometres away. Held up by tests, since nobody here can fly there.
-
Smaller, from four audits: no ghost flight behind the map, a replay played out in 2D draws when you switch to 3D, far replays open over their own ground, and lines clear instead of hanging from a model that has gone.
-
The 3D ground stops hanging a wall while it loads. The dark shelf that stood over the terrain until the last of the loading grid was a tile skirt — a coarse tile's curtain, drawn down edges where the finer ground below had already risen to meet it, hiding nothing. Skirts now hang only where a crack can actually open.
-
The cameras behave the same in every view, live or replayed. A flight is framed the moment it appears — in 2D and 3D alike, and whether you are chasing, following, or looking freely. A replay opens in the mode you last chose instead of forcing tracking, and the mode is remembered between runs. Ending a flight leaves the camera where it stands — the views are yours to place — except a flight far from where you are, whose 3D world rebuilds at you, since a world cannot be kept where you are not standing. With no mode keeping up, zooming in reaches the ground and panning keeps pace, and the arrows hold their size instead of shrinking behind a chased model.
-
Reconnect lets go when you do. A dropped link's retry is called off the moment you ask to connect or open a replay, so nothing dials back in behind your choice.
-
Paused replays draw at once. Left at its start, a replay shows its model and track on the 3D ground immediately, and keeps them across a 2D↔3D switch. Chase and follow re-centre on the model when turned on — even paused or after panning away — and a remembered chase mode no longer crashes on launch.
-
A hole in the elevation data no longer stands as a cliff. Where the height source is dead, a tile's edge into the void is filled from the whole coarser ground, not averaged into a wall.
-
A dropped link keeps its flight — and its log. The model stayed on screen after a drop, for walking to where it came down; now a reconnect no longer wipes it the moment the link returns. And it writes on into the same recording rather than starting a second beside it, so one flight is one log — not a new file each time the link blinks.
-
The flight survives a turn of the phone. Rotating the screen builds it again from nothing, and used to lose the map's trail, a paused replay's place, and — if the link then dropped — the way back to it. Now the trail, the recording, the reconnect, and a replay's position all carry across, and a replay that was playing goes on playing.
2.4.0 — The map that loads everywhere, and the ground that holds
Folds in and replaces 2.3.1 — everything here shipped since 2.3.0.
- The 2D map is fixed. It could go grey everywhere except under the drone. The cause was the location arrows' special native layer, which stopped raster tiles on some devices — the arrows are ordinary map symbols now, same look, and the ground loads wherever you look.
- The world follows the flight. A flight further than fifty kilometres away re-anchors the 3D world to itself instead of playing out over a void. Beyond the world's edge the locate button says so, and the arrows wait until they are back inside.
- The walls come down. Level boundaries morph exactly onto the coarser neighbour's line, finished at the distance where that neighbour can first stand, and every picture change dissolves.
- Loading serves the eye. The nearest missing ground builds first and shows the moment it is finished: the shape of the mountains in the first second, photographs one tile at a time from underfoot outward.
- The world survives everything but distance. Switching views parks the 3D world instead of destroying it — back in a couple of seconds instead of twenty — and connecting, opening a replay or closing one keeps the standing ground whenever the flight, or you, are within fifty kilometres of it. Only a subject further than that rebuilds the world where it is needed.
- Memory-stable by audit. Mesh arrays die after their one GPU upload, pictures decode at the size asked, every queue and cache is bounded — the heap that lived at ninety percent now idles near a third.
- Smaller and honest: both arrows stand on the drawn ground and ride it down as detail arrives; chase and follow say when there is no flight to hold instead of pointing at nothing; Home from the settings no longer strands the app; the satellite map stops at zoom 18, where the imagery's real pictures end.
2.3.0 — The ground, rebuilt
The ground, rebuilt as a quadtree
The 3D world is no longer a fixed window of tiles chasing the model — it is the design Google Earth and Cesium use: a quadtree of web-mercator tiles, split wherever their geometric error would show on screen, so the camera decides where the detail is. Coarse roots cover the whole region for pennies; leaves are metre-sharp under the model; a fast traverse can no longer fly off the edge of the loaded world.
| Velebit, replayed | Traffic, live |
|---|---|
![]() |
![]() |
- Cold start: ground in about a second, first pictures right behind it, full sharpness a breath later through a stepped ladder. Warm view switches in milliseconds.
- Pictures live under a real budget. The cache answers at the size it was asked — it used to hand back the sharpest picture it held, which quietly put the card 40% over budget and starved the tiles you were actually looking at.
- Detail chases the view: tiles behind the camera split reluctantly and stay covered by the roots, so a turn shows soft ground that sharpens instead of a hole.
- 120 Hz steady on the test phone, worst frames in the tens of milliseconds, memory bounded and measured — no lock-ups under fast panning, scrubbing, or view switches.
Data that heals itself
- The elevation dataset has holes — whole columns near the Velebit flight start answer a 270-byte void at two zoom levels while the level below is whole. The ground now builds from the level below the damage instead of failing every thirty seconds forever; each step down is named in the diagnostics.
- A failed fetch is not a hole in the data: no signal keeps the old road — retry and end sharp — instead of settling for coarse ground.
- S3 throttling a burst of elevation requests is no longer remembered as "these tiles do not exist" for the rest of the run.
- Watermark "no imagery" tiles are told apart from snowfields; incomplete fallback mosaics are never cached under the sharper name they failed to be.
One flight, two arrows, three lines
The lines and their settings were rebuilt around what each one means, the same in both views:
- Home line (blue, like the arrow it stands on): drone to where this phone is now, live and during playback — for walking to a downed model, so it draws on top. Hidden with the arrow.
- Show recorded operator is one switch for the whole idea: the orange arrow where the operator stood as recorded, its accuracy ring, and the operator line from the drone to it — playback only, together on or off, with the colour settings greyed out under it.
- Every line is hidden with its arrow — a line pointing at nothing points at nothing — and settings apply the moment you come back from them, over a paused replay too.
- "Record operator position" writes your position into the flight log so the operator can be drawn at replay; turning it off silences the rows rather than repeating the last place forever.
Traffic in 3D
Flightradar24 aircraft were an orange cross on a post. They are now small planes at their true altitude, pointed down their track, on the same ground post — height over ground is the thing a flat map cannot say. Tap one and it answers with the same card the 2D marker shows: type, registration, altitude, speed, route. Barometric altitude near high ground is floored just above the terrain, so no aircraft is drawn inside a hill.
The phone knows where it stands
- The one location listener lives in the service that outlives the screen: a connected flight records your position and heading with the phone in your pocket, which the screen-owned listener never could.
- The last fix survives the screen going away. Coming back from the settings or another app, the arrow stands exactly where it stood — ring and all — instead of wandering while the satellites are asked again.
- Nothing listens in the background without a link: no flight, no location icon.
Also in here
Playback starts by itself only when its switch says so, and holds on the ground until the terrain is dressed. Scrubbing is coalesced to what the screen can show. Field diagnostics write one named file per app start — debug builds only, coordinates stay off release phones — and every failure the terrain can have now logs its tile and its reason, which is how most of the above was found.
Re-released in place with three late fixes: the disk caches got ceilings — two gigabytes all told (1000MB imagery squares, 625MB mosaics, 375MB heights), oldest out first, where the squares previously grew without bound; tiles bordering the elevation dataset's damaged stripe stopped being rebuilt forever for data that was never coming; and the setting is Model color now, because it colours the quad and the helicopter too.
2.2.1 — Telemetry on a whisper, and three models rebuilt
Low-bandwidth telemetry
- ArduPilot passthrough over CRSF / ExpressLRS — flight mode and the real armed bit, GPS fix and satellites, battery voltage / current / consumed mAh, distance and altitude to the actual home, velocity, attitude, throttle and status texts, over a plain ELRS link with no FrSky hardware. Both frame types (
0x80, legacy0x7F), single- and multi-packet forms. Native CRSF frames stay authoritative for what they already carry; passthrough fills their silences and wins for what only it knows. Autopilot side:RC_OPTIONS += 256, receiver serial on protocol 23. - MAVLink High Latency (HIGH_LATENCY2) — the one 42-byte message per five seconds a satellite- or LoRa-class link carries: position, MSL altitude, heading, speeds, throttle, battery, mode and armed state, in MAVLink 1 or 2 framing. A checksum-table bug had rejected every such message as corrupt since forever; fixed. Autopilot side:
SERIALn_PROTOCOL = 43. - A MAVLink High Latency (UDP) network preset completes the loop: an autopilot boots with that stream off, so the preset sends
MAV_CMD_CONTROL_HIGH_LATENCYto the typed modem address until telemetry answers, and a courtesy disable on disconnect. The protocol is pinned rather than detected (detection would stare at nothing for ten seconds), and sensor timeouts stretch to fifteen seconds on this link only — one dropped frame no longer greys the panel. The command frame is verified byte for byte against an independent X.25 implementation. - The copter mode table now knows LOITER — every LOITER flight over MAVLink used to show no mode at all.
tools/simflight.pyspeaks both:--passthroughweaves ArduPilot frames into its CRSF stream,--protocol mavlink-hlsends HIGH_LATENCY2, and--wait-enablemakes it behave like a real high-latency port — silent until the app's enable command arrives, checksum verified, streaming to whoever asked. The simulator's own byte streams are replayed through the real parsers in the unit tests, and the full enable handshake was rehearsed on loopback with the app's exact bytes.
Three models, rebuilt
Model type now offers Helicopter beside Quad and Plane — same colour, same heading, both views, with its own nose-up map marker. All three 3D models were rebuilt in one design language and approved shape by shape from renders before ever being built into the app:
| Quad | Plane | Helicopter |
|---|---|---|
![]() |
![]() |
![]() |
- Quad: a faceted military-style hull over plate arms with rounded tips flush at the motors, stacked motor bells, the sensor pod set into the chin, aerials on the tail deck.
- Plane: tapered swept wings and tailplane, a swept fin, a fuselage that narrows to a wedge of a nose.
- Helicopter: lofted cabin, tapering boom with tailplane, swept fin carrying a hubbed tail-rotor disc, skids on struts that meet the belly.
Since 2.2.0, also in here
Everything shipped into 2.2.0's replaced asset along the way: replay seek that places instead of inventing lines, one-world replay opening with playback held until the terrain is dressed, terrain tile caches that retry a bad network moment instead of remembering it forever, flight plans that settle onto ground as it arrives, the playback speed control (3× slower to 10× faster, never squeezed under ten seconds), and 3D memory roughly halved — terrain textures at half the bytes and no heap-side copies at all.
2.2.0 — Everything moves together
Android Telemetry Viewer 2.2.0
The complete current release, replacing all earlier builds from this fork.
Most important changes
- Buttery 2D tracking: the aircraft, flight-line head, home line, heading line and tracking camera are committed as one MapLibre render scene. They stay attached through turns, rotation, zoom and fast replay, with flight paths and plans kept deterministically below the moving model and navigation markers.
- Background compass: enabled by default, with phone direction recorded by the foreground service while the screen is off. Missing direction is shown honestly as a dot in 2D or a puck in 3D.
- Real 3D terrain: satellite imagery over elevation data, true aircraft attitude, flight shadow and altitude curtain, flight plans and nearby traffic.
- Better replay: smooth high-speed playback, original phone position and clock, safer seeking, and an altitude profile showing terrain clearance.
- Correct altitude handling: one per-flight MSL/above-launch decision shared by the 2D map, 3D terrain and altitude profile.
- More ways to connect: Bluetooth, BLE, USB serial, TCP, UDP and TBS Crossfire Wi-Fi WebSocket.
- More telemetry: FrSky S.PORT, CRSF/Crossfire/Tracer/ExpressLRS, Ghost, LTM and MAVLink 1/2.
- Reliability audit: safer reconnect/cleanup, replay and terrain cancellation, logging failures, FlightRadar polling, and protocol length/unit/signedness fixes. All 41 regression tests pass.
- Flight plans, Find My Quad, per-cell battery voltage, RF-rate identification, map caching and GPX/KML export remain included.
Install
Download telemetry-2.2.0.apk. It supports Android 6+ on ARM64 and ARMv7. This is a sideloaded APK and intentionally remains on targetSdk 28.
Ghost telemetry mirror requires EdgeTX with the upstream mirror fix and a 115200-baud serial connection.








