Three things wrong with the window, all reported from the chair in front of
it, and all three the same kind of wrong: the map had been handed the aircraft
map's furniture and nobody had checked which bits of it were about aircraft.
The empty screen said "listening on 1090 MHz" and went on to explain that an
aeroplane is placed once an even and an odd position frame have both arrived.
That sentence was true of the window it was written for and false of every
word in this one. What has to arrive before a mark can be placed is a fact
about the signal rather than about the window, so it is now a thing the window
is told: an aeroplane still says what an aeroplane needs, a station says that
most of them mention where they are every few minutes, and left unsaid the
words follow whatever channel the window was given rather than naming 1090
from memory.
There was no red flag. The flag is drawn where the receiver was actually told
it is, and this section has its own --at that had never been filled in -- but
the aircraft section's had, by the same person, about the same aerial, on the
same roof. One aerial does not move because the receiver was pointed at a
different band, so a position set on either side now serves both, this
section's own winning where it has one because two receivers in two places is
exactly what a separate setting is for. It says out loud which it used, an
inherited position being a convenience right up until somebody has moved and
changed only one of them. The flag, the range rings and every distance and
bearing all come from the same answer, so all four arrive together.
The boxes drew a station's position and never wrote it down. A mark on a map
shows where something is; the figures are what gets read out over the air,
copied into a log or typed into something else. They are also the only place
the doubt shows: a station that blanks its minutes is somewhere inside a
two-degree square, the diamond is as definite there as it is anywhere, and
only "49.0000N 72.0000W +/-340 km" admits it.
Twelve new tests against six deliberately broken builds. One survived, and
it is the one that matters: with the drawing code changed back to print
aeroplanes at 1090 MHz, every test still passed, because they all read the
sentence off the object and none of them read the pixels. That is the
reported fault exactly -- a window that holds one sentence and paints another
-- and the suite could not see it. It reads the picture now, cropped below
the header, whose ticking clock has made a test pass for the wrong reason
twice already this session. Full suite 2664 passed. Built as
2026-09-21_02.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016PsWPTweCT6pwxKngvVxcg
The same window the aircraft use, given marks that are not aeroplanes. It
already knew how to fetch a map, place an information box where it covers
nothing, glide it when its owner moves, draw range rings, put a flag where
the aerial stands and a scale along the bottom -- and not one of those has
anything to do with aviation. What it did not know is that a mark might not
fade, might not point anywhere, and might be coloured by what it is rather
than by how high it is. Those are three hooks rather than a second window,
and the aircraft map is untouched: every one of them defaults to exactly what
an aeroplane does.
Nothing fades, which is the difference asked for and the right one. An
aeroplane that stops transmitting has flown out of range, and drawing it an
hour later where it was would be drawing something that is certainly not
there. A fixed amateur station that stops transmitting is still exactly
where it was -- it beacons every half hour, and the gaps are silence rather
than absence. So the picture accumulates and an evening of listening fills a
map. Marks are ordered most-recently-heard first, because that is the order
the boxes are laid out in and an accumulating map has more marks on it than
it has room for boxes.
Marks are drawn by what they are: something moving as a body with a stalk
pointing where it is going, and anything fixed as a diamond, which is the one
shape on the picture with no front -- a house that beacons twice an hour is a
place, and a triangle would have it pointing north for no reason. Colours
come off the altitude ramp, not because a station has an altitude but because
that ramp is the one set of colours all five themes define: warm to cold on
the default map, dim to bright on the phosphor ones, so a digipeater stays
distinguishable from a car everywhere without a colour being named here.
The box says what the station is, how far off and in which bearing, what it
is doing if it is moving, its altitude, its weather, its status, the
digipeaters it came through, how many packets and how many of those arrived
directly, and how strongly. The heading prints the callsign once: an
aeroplane has two names and the heading was built for that.
Reachable both ways, as everything here is -- --window on the command line, w
in the menu -- with the same eight map settings the aircraft side has.
Forty new tests against nine deliberately broken builds. Two of them survived
the first attempt, and both for the same reason: they asked whether the
rendered frames differed rather than whether the symbol did, and the strip
along the top carries a running clock, so two frames taken a millisecond apart
differ by a few hundred pixels whatever is on the map. They now crop to the
mark. That is the second time this session that a ticking header has made a
test pass for the wrong reason. Full suite 2652 passed. Built as
2026-09-21_01.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016PsWPTweCT6pwxKngvVxcg
Two things that move on their own, on the window and in the animated
pictures alike, and both turned off with a flag.
An aircraft swells from bright to dim and back. At the top of the swell it
burns: drawn in a set of peak colours and wearing a halo, which is what a
phosphor does when the beam sits in one place a little too long. The halo
is the aeroplane's own shape spread outward a pixel at a time rather than a
circle drawn round it -- the first attempt did draw a circle and it looked
exactly like what it was, a ring of dots -- so the glow has the shape of the
thing casting it. It goes only where the picture was still empty, over the
ground and the grid and the background and never over another aircraft,
since a halo is what light does to the dark around a thing.
And a ring leaves each aircraft and travels outward, growing and dimming as
it goes. What a radar repeater does, and what the eye reads as this thing
is transmitting -- which is exactly what an aeroplane on this picture is
doing, twice a second, and is how it got on the picture at all. One ring at
a time per aircraft: a new one leaves as the last reaches the end of its
reach, so the sky has one ring per aeroplane rather than a stack of them to
read through.
Five settings, in a group of their own because the others were already at
eight: whether to pulse, how long a swell takes, whether to echo, how long
between rings, and how far a ring gets. Both times are seconds of watching
rather than of flying, so the rhythm looks the same whatever speed an
evening is being run through.
Two decisions worth naming. Each aircraft is offset by its own address, so
a sky full of them swells and rings separately rather than beating as one,
which would read as a display flashing rather than as a lot of separate
things transmitting; the offset comes from the address, so an aeroplane
keeps its rhythm from one frame to the next and from one drawing of the same
log to the next. And only an aircraft still being heard pulses: one that
has gone quiet is already fading, and a thing that is fading and beating at
once says two contradictory things about itself.
The window can blend and its swell is continuous. The animation cannot, a
GIF being indexed colour, so there the swell is the steps a palette allows
-- which family of colours the aeroplane is drawn from, and how far its halo
reaches, which is six between them and reads as a swell at a couple of
seconds a cycle. The peak has sixteen colours of its own, sixteen being
exactly what was left of the palette once 255 is set aside as the
transparent index.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016PsWPTweCT6pwxKngvVxcg
An audit rather than a feature, prompted by wanting this fit to hand to
somebody else.
Five options had no command-line flag written down in the table the manual
is generated from -- location, hold, schedules, tile_url and speed_unit --
so five flags that exist were missing from the manual. Four of them did
exist under other names and are now recorded; hold had no flag at all and
has one.
Four switches could be turned off from the command line and not back on:
--no-lookup, --no-basemap, --no-airports and --no-labels had no positive
halves, so an option turned off in the saved settings could not be turned on
again for one run. All four now have both.
And adsb, which opens the window and draws a map when it stops, could not be
given any of the settings that decide what those look like: no --at, no
--radius, no --tiles, no --map-brightness, no --width, --fps, --trail,
--fade, --stale, --airports or --labels. It takes all of them now.
The manual had no list of the aircraft options at all -- the ADS-B sections
were hand-written prose -- so five of them appeared nowhere in it. It now
generates an AIRCRAFT OPTIONS section from the same table the menu and the
flags come from, and the readme carries a table of all thirty-four with
their flags and defaults. Three tests hold the three of them together: one
that every option records its flag, one that every flag the table claims
actually exists on a command, and one that the readme names them all.
The installing instructions now list every dependency rather than only the
optional ones: the four Python packages with their names in Debian, Fedora
and Arch, and librtlsdr, which is a C library and therefore the one thing
pip cannot bring and a virtual environment cannot supply. What reaches a
network is written down too -- which host, when, and which file it is cached
in -- since somebody installing this on a metered or air-gapped machine has
to be able to see that nothing is fetched behind their back. build-repo.sh
needs dpkg-dev and apt-utils, which a minimal system does not have, and now
says so.
Verified rather than asserted: a clean virtual environment, pip install from
this tree, and a real log read back through the installed command. It pulls
seven wheels rather than the four the page claimed, the other three being
what Rich brings with it.
Also: matplotlib is gone from the readme's dependency table, nothing having
imported it; ffmpeg and Qt are in it, both having been missing; ffmpeg is a
Suggests on the package; and resume.sh is ignored.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016PsWPTweCT6pwxKngvVxcg
Three changes to what is on the picture, and both drawings got all three.
The line from an information box to its aircraft was drawn in the
aircraft's own colour, which made it the same colour as that aircraft's
trail: a straight solid line running out of an aeroplane, in the colour of
the path behind the aeroplane, reads as more path, and on a busy picture
that is a heading nobody flew. It has its own colour now, and is dashed.
Qt measures a dash pattern in multiples of the pen's width, so each pass of
the glow divides the pattern by its own width; without that the halo's
dashes are three times the core's and the line comes out as beads.
The animation had no such line at all, which only came out when the two
were held up against each other: a label pushed into one of the outward
rings by a crowd had nothing tying it to the aeroplane it was about. It has
one now, walked along the line's own length rather than along whichever axis
is longer, so that a nearly horizontal leader and a nearly vertical one get
dashes of the same length and neither runs past the aircraft it points at.
A red flag stands where the receiver was told it is standing, from the
coordinates in the settings. The foot of the pole is the position and the
pennant flies up and to the right, so nothing the flag is made of covers the
place it points at. It is pure red in every theme: that is the one mark on
the picture whose meaning must not change with the colours, and pure red is
both the brightest red there is and the one furthest from every altitude
colour -- a softer one sat close enough to a low aeroplane on the default
map, and to a mid-altitude one on the red theme, to be taken for one. It is
drawn only where a position was actually given, since a middle worked out
from whatever flew past is not a place anybody is standing.
And the range rings: faint discs at a quarter, a half and three quarters of
the radius, concentric on the receiver and labelled with the distance. They
are translucent and they stack, so the ground inside the innermost is lifted
three times and the outer once, which gives a sense of how far away a thing
is without measuring anything. An indexed picture cannot blend, so
translucent there means moving the ground under the disc a step or two up
its own ramp of shades, which keeps the coastline and the roads visible
through it. Separate settings for the two, because a picture is studied and
a window is glanced at.
Two bugs found by the tests rather than by looking. The rings were built
across the whole canvas instead of the part of it the map is drawn on, so
they came out stretched by the height of the title bar -- four per cent,
which is invisible and wrong. And a radius of zero killed the window: the
projection collapses, every pixel maps to one point, and the graticule asks
for lines across a span of nothing until Qt aborts. The program never
passes a zero, but a caller could, and a crash is not an answer.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016PsWPTweCT6pwxKngvVxcg
The aerodromes were never on the window at all: only the animation drew
them, and the window's map had whatever airport glyphs the tiles happened to
carry. They are drawn there now, in the same colour and the same square.
The first attempt at that had a bug worth naming, because it would have
looked like the feature simply not working. They were fetched inside the
same pass of the fetching loop as a piece of map, so they queued behind a
hundred and twenty tiles coming off a network -- and once the map was in
hand there were no more passes, so they were never fetched again. They are
their own question now, asked on the same thread but not behind the tiles,
and they arrive whether the tiles do or not. An area that has been asked
about and has none in it is remembered as none, rather than asked about
again five times a second for the rest of the night. And the thread now
starts if either the map or the aerodromes are wanted, so --no-basemap no
longer quietly takes the airports with it.
Then the themes, which change the window and the animated pictures together
because both read their colours out of the same palette. night is what this
program has always drawn and is untouched. digital, phosphor, amber and red
are the screens the phrase "air defence display" actually calls to mind: a
black tube, one phosphor, and thin bright vector lines with a halo round
them.
Three things follow from having one colour to spend, and they are
constraints rather than decoration. Height becomes brightness, since hue is
no longer free -- low is dim and high burns, which is the trade those
displays made. The map underneath drops to about a quarter of the
brightness asked for, because a tinted photograph of a county behind the
vectors is the one thing that stops a vector display looking like one. And
a country is named in two letters rather than drawn as a flag, a flag being
half a dozen colours.
The glow is done twice, differently, because the two are different kinds of
picture. The window lays each line down two or three times, wider and
fainter each pass, with the core last: trails, symbols, leader lines, box
borders and the aerodrome squares. The animation cannot blend at all, a GIF
being indexed colour, so it dilates what it has drawn and fills the halo
with the dimmed copy of the colour underneath -- and the aircraft colours
already had dimmed copies, since those are the trail shades, so an aeroplane
glows into the colour its own trail is drawn in, which is the colour a
phosphor would have spread into. The fixed colours get two rings each in
the palette for the purpose. The halo goes over the map, the grid and the
background and over nothing else that was drawn, since a halo is what light
does to the dark around a line; where two rings meet the nearer wins. It
costs about 55 ms a frame at 1400 by 1258 and the default theme skips the
pass entirely.
The palette is written over in place rather than replaced, because both
drawings and every one of their helpers hold a reference to that array and a
new one would leave half the program painting in the colours of the theme
before. There is a test that every theme keeps the aerodrome colour more
than forty units of CIELAB from every altitude colour, stated as the
distance rather than as the colour, so that a new theme cannot quietly walk
an aircraft back into the airports.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016PsWPTweCT6pwxKngvVxcg
Four things about the animated pictures, and the window kept in step with
them.
The aerodromes were amber, and so is an aeroplane at twelve thousand feet.
Sixteen units of CIELAB apart is not two colours, it is one: an aircraft low
over a field was drawn in the field's own colour and neither could be picked
out from the other. They are magenta now, fifty-six units from the nearest
altitude colour, which is what the ramp leaves free once red, amber, green,
cyan and violet have gone on height -- and what an aeronautical chart marks
an aerodrome in anyway. The test states that as the distance rather than as
the colour, so that changing the ramp cannot quietly walk an aircraft back
into the airports.
The height beside an aircraft was the flight level, which is shorter and is
what an aviator reads, but "376" is only a height to somebody who already
knows it is one. It is feet with the unit on it now, rounded to the
twenty-five feet Mode S reports altitude in: a real reading is a multiple of
that and comes through untouched, while a moment interpolated between two
reports stops claiming to know the height to the foot.
The flag of the country of registration now comes off the address block
where no register answered. Taking it from the register's answer alone left
the flag off exactly the aircraft that had nothing else beside them either.
Mexico was missing from the address table while we were in there, which a
receiver in the American southwest notices; two registers independently give
XA- registrations for that block.
And what sort of aircraft it is, which is two facts and not one. What it is
comes off the air: every identification message carries three bits under its
type code saying whether it is light, large, heavy, a rotorcraft, a glider,
a drone or a van on the apron, and that is the only word about what an
aircraft *is* that needs no register. They were being decoded and thrown
away. They are inside the identification frame the log already writes down
in full, so every log this program has ever written has them, including the
ones written before anything here knew to look.
Whether it is military comes off no air at all -- a tanker calls itself
heavy exactly as an airliner does -- and is read from the address block
instead. On one evening here AE07D3 broadcast "heavy" and sat in the United
States military block; the register, asked separately, came back with a
C-17A Globemaster III, tail 90-0534, United States Air Force.
Labels in an animation now behave as the boxes in the window do. One keeps
its place for as long as that place still works and is moved only when
something takes it, which on a real log is about half as many moves as
deciding afresh every frame; the moves that are left are eased over half a
second of playback; and what the next label is laid out against is where a
moving one is going rather than where it has reached. A still has no frame
before it and places its labels exactly as it always did.
The fading was only half done. A label's name faded, because it is drawn in
the aircraft's own colour, but its rows and its flag were fixed colours and
stayed at full brightness -- so the brightest thing on that part of the
picture was the one aeroplane nothing had been heard from. An indexed
picture cannot blend, so the row grey and all twelve flag colours now have
dimmed copies at each of the four fade steps, and the whole box goes
together. A crowded frame, which drops back to the short label, drops the
class and the flag with it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016PsWPTweCT6pwxKngvVxcg
A callsign is a flight number rather than a leg, and the free registers hold
one route per number, so an aircraft over Arizona kept being handed a hop
between two airports in Texas. Nothing on the air settles it: ADS-B carries
no origin or destination. A commercial schedule service does know, because
it holds the day's actual movements.
Four are wired up and all four are optional: FlightAware AeroAPI,
Flightradar24, OAG and Cirium. Each is asked before the free databases and
each answers for the moment the aircraft was overhead rather than for the
flight number in general, so the leg chosen is the one that was in the air.
With no keys set nothing changes at all: a source with no key is skipped
rather than asked and refused, and the free databases answer as before.
Keys come from the environment and are never written to the settings file,
because a settings file is meant to be copied between machines and pasted
into a message asking for help, and an API key is not. There is a test that
holds that line.
None of the four has been run against its live service, since each wants a
paid account. They were written from the published response shapes and are
tested against those shapes, so each reader finds what it recognises and
returns nothing otherwise: a service that has changed since costs a route
rather than a scan. Cirium's plain departureTime is local and carries no
offset, so the UTC field is preferred where it is there -- reading the local
one as UTC is up to half a day out, which is exactly far enough to pick the
wrong leg of the same number. Reading now happens inside the same guard as
asking, as an answer shaped differently from the documented one is the
failure most likely to actually happen.
And the map. The zoom is now chosen from how wide the picture is rather
than from the area alone, with half again over the width fetched and
averaged down, since a downscaled tile is sharp and an upscaled one is not.
The window fetches a little more world than it shows so panning does not
leave the ground blank, and now fetches that bigger piece at the bigger
piece's own size: rendering it into the window's own pixels and stretching
it back was a fifth of an upscale over the whole map, which is what a sharp
map looks like when it looks blurred. The comment in the fetcher said the
opposite of what the code did, which is how it stayed hidden.
At 1920 by 1080 over a hundred miles the tiles now hold about 1.6 times the
pixels the window wants. At 3840 by 2160 the tile budget is reached, the
zoom stops climbing and the map is enlarged after all; a smaller radius buys
the detail back, and somebody else's tile server is not a thing to fetch a
thousand tiles from for one picture. The README says so rather than
implying otherwise.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016PsWPTweCT6pwxKngvVxcg
Three things asked for, and a fourth found while doing them.
The map looked like a photograph of a map, and did so twice over. The
window fetched at its own pixel size but for a box a third larger in each
direction -- the margin added to stop the ground blinking -- and then cut
the middle out, so every pixel was enlarged by two thirds. It now asks
for enough pixels to cover the bigger box at the window's own detail.
Underneath that, both the window and the animation took the nearest source
pixel: the tile mosaic is commonly half again the size of the picture, so
most of every tile was thrown away and what survived was the aliasing.
Both now average the source pixels that fall in each output cell, done as
the difference of a running total rather than a loop.
An aircraft that goes quiet now fades instead of vanishing. Taking it off
between one frame and the next says it stopped existing; fading says it
stopped talking, which is what happened. It fades where it was last
actually seen and never along a reckoned track, because the reason for
giving up on it is that where it would be by now is a guess. --fade sets
how long, and it is in the menu. The window has alpha and fades smoothly;
an indexed picture cannot blend, so the animation gained a fourth ramp at
a seventh of full and fades in four steps, which at a second apart reads
as a fade. A trail fades with the aircraft it belongs to, and the box
goes before the symbol does.
The aircraft's country of registration carries a flag now as well as the
two ends of its route, from the register where one answered and from the
address block otherwise.
And the fourth: the window's header counted an aircraft that had gone
quiet as overhead, which was saying more than had been heard.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016PsWPTweCT6pwxKngvVxcg
The terminal board says what is overhead. This says where: a real map
with the aircraft moving on it as the frames arrive, and beside each one a
box carrying everything known about the flight -- type and registration,
who operates it, where it came from and where it is going, height with a
rate of climb, speed and heading, how far away and on what bearing, its
position, how many frames it has sent and how long since the last one.
Qt is asked for and not required. Four bindings are tried, the module
imports on a machine with none of them, and asking for the window without
one gets the instructions rather than a traceback -- before the receiver
is opened, since nothing is gained by taking the dongle for a window that
cannot be drawn.
In the menu, "listen now" is now "passive capture" with a realtime
display beside it. Closing the window leaves exactly the files pressing
control-C leaves, because listen and watch share one read loop and one
finishing step; the receiver runs on its own thread, so a slow repaint
cannot cost a frame and a slow tile fetch cannot stall the picture.
The animation's labels grew to match: flight level and speed, type and
registration, and both ends of the route, each with a small flag of the
country its airport is in. The flags are a table rather than a network --
twelve pixels by eight, where a flag is the arrangement that makes one
recognisable rather than a rendering of the real thing -- and a country
not in the table is named by its two letters, since a flag that is nearly
another country's is worse than none. Where a route arrives as bare
codes the country comes from the ICAO prefix.
Four things found on the way. The window ignored --seconds, so "listen
for ten minutes" meant something different with a window open; it closes
itself now. The register was being asked twice per aircraft, once for
labels and once for airport positions. Cached routes had no country in
them, so the first real redraw drew no flags at all -- routes are
versioned now. And past fourteen aircraft on one frame the labels go
back to the callsign, the height and the speed, because five lines beside
each of three hundred aircraft is a page of overlapping text with a map
somewhere behind it.
Long names are folded rather than allowed to stretch a box, breaking at
the arrow of a route so the two ends stay whole; and the animation's
label placement gained the same ring search the window uses, having only
ever tried four spots.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016PsWPTweCT6pwxKngvVxcg