Replaying any minute of a day of ADS-B from a plain log file
Stargazer keeps every decoded aircraft position as one line in a daily JSONL file and replays by bisecting the file on byte offsets. The row format, why a database was not needed, and how public data fills gaps.
- Published
- 3 min
- To read
- Stargazer
- Project
Stargazer has saved 198,782 position fixes across the two antenna profiles on its project page, and it can play back any moment of any day. There is no database behind that. The storage is one text file for each day, and the replay is a binary search.
One line for each position
Each day gets a file named for its date, YYYY-MM-DD.jsonl, in a positions folder. Every decoded position is appended as one line holding eight values:
[ms, hex, lat, lon, alt, gs, track, local]
- ms
- is the time in milliseconds.
- hex
- is the aircraft's six character address.
- lat
- and lon are the position.
- alt
- is altitude.
- gs
- is ground speed, and track is the heading.
- local
- says whether this receiver heard the position itself or whether it was filled in from public data.
Every line is a small array and not an object with named keys, which keeps the file compact. A day holds a position every few seconds from every aircraft in range, so it is a lot of lines, and repeating eight key names on each of them would add up.
Why a plain file is enough
Replay needs one operation: given a time, start reading from there. A log written in time order makes that cheap. Because lines are appended as they arrive, the first number on each line only ever increases down the file.
Stargazer's replay reads by byte offset bisection. It looks at the byte in the middle of the file, finds the start of the next line, and reads that line's time. If the time is too early it moves into the upper half of the file, and if too late into the lower half. Each step halves the range, so even a file of hundreds of megabytes takes only a few dozen steps to arrive at the byte offset of the first line at or after the requested time. It then reads forward from there and plays the lines in order.
Scrubbing a slider to any moment of the day takes the same few reads, however long the day was. The file never has to be loaded whole.
There are three side benefits to keeping the log as text:
- It is easy to inspect
- Any line can be read in an editor, and
grepfinds an aircraft by its address. - It cannot be corrupted in the middle
- Appends only ever add to the end, and a crash can at worst leave one partial last line.
- It does not need migrating
- The format is the whole of the schema.
Own data and filled data
The local value matters for how a position is shown. An aircraft Stargazer's own receiver hears is drawn in green. Where the receiver misses a position that the public network has, the gap is filled from public data and the aircraft is drawn in orange and pulses. The distinction is carried through to replay, so a recording of a day shows what the antenna heard and what it only learned about.
This is the same information the hit rate is built on. Each time an aircraft from the public network is beyond 50 km and above the radio horizon, either the receiver heard it too or it did not, and the log holds both.
The rest of what is kept
Alongside the daily files are three small ones in the data folder: statistics with the range and reach per degree and the totals of hits and misses against adsb.fi, a file with the callsign, registration and type of every aircraft seen, and a configuration file with the feed address and the receiver position. Aircraft cards show a photo from Planespotters with the photographer credited.
Stargazer needs Node 22 and no other dependencies. The Stargazer page has the measured antenna pattern and a radio horizon calculator, and the quadrant comparison shows what the data says about two antennas.
Written by Caisson AI. Questions or corrections go to hello@caissonai.co.