Give the aerodromes a colour of their own, and say what each aircraft is
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
This commit is contained in:
parent
8eb3bdbb86
commit
f9b21f7e35
13 changed files with 1616 additions and 67 deletions
|
|
@ -786,3 +786,104 @@ def test_an_airport_nobody_can_place_costs_only_its_own_leg(register,
|
|||
# The middle stop cannot be placed, so neither leg touching it can be
|
||||
# tested; nothing is claimed.
|
||||
assert book.leg_for(entry, 40.8, -78.0) is None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# What sort of aircraft it is
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_a_military_address_is_read_off_the_block():
|
||||
from bandsaunter.flights import aircraft_class, is_military
|
||||
|
||||
assert is_military("AE07D3") # a United States military block
|
||||
assert not is_military("AC4C44") # the civil part of the same range
|
||||
assert not is_military("") and not is_military("nonsense")
|
||||
assert aircraft_class("AE07D3", "heavy") == "military heavy"
|
||||
assert aircraft_class("AC4C44", "large") == "large"
|
||||
assert aircraft_class("AE07D3") == "military"
|
||||
assert aircraft_class("AC4C44") == ""
|
||||
|
||||
|
||||
def test_military_and_the_country_it_is_described_with_never_disagree():
|
||||
"""Both read the same table, so an address cannot be military and be
|
||||
described as an ordinary country at the same time."""
|
||||
from bandsaunter.flights import (MILITARY_BLOCKS, describe_address,
|
||||
is_military)
|
||||
|
||||
for low, high, name in MILITARY_BLOCKS:
|
||||
for address in (low, (low + high) // 2, high):
|
||||
assert is_military(f"{address:06X}")
|
||||
assert describe_address(f"{address:06X}") == name
|
||||
assert name.endswith(" military")
|
||||
|
||||
|
||||
def test_the_report_says_what_sort_of_aircraft_it_is(tmp_path):
|
||||
"""Off the air, not out of a register: the aeroplane says "heavy" and
|
||||
the address says whose it is."""
|
||||
from bandsaunter.flightlog import report
|
||||
|
||||
log = _log(tmp_path)
|
||||
for i in range(3):
|
||||
log.append(_Frame(icao="AE07D3", callsign="PRIME04"),
|
||||
_Craft(32.7086, -110.4061 + i * 0.01, "PRIME04"),
|
||||
when=1_000_000.0 + i)
|
||||
log.close()
|
||||
tracks = read_logs(log.path)
|
||||
tracks[0].category = "heavy"
|
||||
text = "\n".join(report(tracks, None))
|
||||
assert "class: military heavy" in text
|
||||
|
||||
|
||||
def test_the_report_leaves_the_class_out_when_nothing_said_one(tmp_path):
|
||||
from bandsaunter.flightlog import report
|
||||
|
||||
log = _log(tmp_path)
|
||||
for i in range(3):
|
||||
log.append(_Frame(icao="AC4C44", callsign="SWA2444"),
|
||||
_Craft(32.7086, -110.4061 + i * 0.01, "SWA2444"),
|
||||
when=1_000_000.0 + i)
|
||||
log.close()
|
||||
assert "class:" not in "\n".join(report(read_logs(log.path), None))
|
||||
|
||||
|
||||
def test_the_category_is_read_back_out_of_a_logged_frame(tmp_path):
|
||||
"""The three bits are inside the identification message, which is
|
||||
written down in full, so every log this program has ever written has
|
||||
them -- including the ones written before anything here knew to look."""
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from bandsaunter.flightlog import read_logs as read
|
||||
|
||||
path = Path(tmp_path) / "old.jsonl"
|
||||
with path.open("w") as out:
|
||||
out.write(json.dumps({"log": "bandsaunter-adsb", "version": 1}) + "\n")
|
||||
# A real identification frame: type code 4, category 3, "large".
|
||||
out.write(json.dumps(
|
||||
{"t": 1.0, "icao": "A80A97", "df": 17, "tc": 4,
|
||||
"hex": "8DA80A97234CB5F5CF4C604EB016",
|
||||
"callsign": "SKW5341"}) + "\n")
|
||||
out.write(json.dumps(
|
||||
{"t": 2.0, "icao": "A80A97", "df": 17, "tc": 11,
|
||||
"hex": "8DA338A6591521A2C718D47E24C0",
|
||||
"lat": 32.2, "lon": -111.0, "alt_ft": 3050}) + "\n")
|
||||
track = read([path])[0]
|
||||
assert track.category == "large"
|
||||
assert track.kind == "large"
|
||||
|
||||
|
||||
def test_a_log_with_nothing_to_read_the_category_from_says_nothing(tmp_path):
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from bandsaunter.flightlog import read_logs as read
|
||||
|
||||
path = Path(tmp_path) / "thin.jsonl"
|
||||
with path.open("w") as out:
|
||||
for line in ({"t": 1.0, "icao": "A80A97", "tc": 4}, # no hex
|
||||
{"t": 2.0, "icao": "A80A97", "tc": 11,
|
||||
"hex": "8DA338A6591521A2C718D47E24C0", # not an ident
|
||||
"lat": 32.2, "lon": -111.0},
|
||||
{"t": 3.0, "icao": "A80A97", "tc": 4, "hex": "8D"}):
|
||||
out.write(json.dumps(line) + "\n")
|
||||
assert read([path])[0].category == ""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue