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
142 lines
5.4 KiB
Bash
Executable file
142 lines
5.4 KiB
Bash
Executable file
#!/bin/sh
|
|
# Build a .deb that pulls in every dependency through apt.
|
|
#
|
|
# Deliberately plain dpkg-deb rather than debhelper: everything this package
|
|
# needs is already in Debian, the payload is pure Python with no compiled
|
|
# parts, and this way the build needs nothing installed beyond dpkg itself.
|
|
set -eu
|
|
|
|
here=$(cd "$(dirname "$0")/.." && pwd)
|
|
version=$(cd "$here" && python3 -c 'import bandsaunter; print(bandsaunter.debian_version())')
|
|
revision=${DEB_REVISION:-1}
|
|
arch=all
|
|
stage=$(mktemp -d)
|
|
trap 'rm -rf "$stage"' EXIT
|
|
|
|
pkgdir="$stage/bandsaunter_${version}-${revision}_${arch}"
|
|
site="$pkgdir/usr/lib/python3/dist-packages"
|
|
mkdir -p "$site/bandsaunter" "$pkgdir/usr/bin" "$pkgdir/DEBIAN" \
|
|
"$pkgdir/usr/share/doc/bandsaunter"
|
|
|
|
cp "$here"/bandsaunter/*.py "$site/bandsaunter/"
|
|
cp "$here/README.md" "$here/INSTALL.md" "$pkgdir/usr/share/doc/bandsaunter/"
|
|
gzip -9n "$pkgdir/usr/share/doc/bandsaunter/README.md"
|
|
gzip -9n "$pkgdir/usr/share/doc/bandsaunter/INSTALL.md"
|
|
|
|
# Debian Policy requires the licence verbatim, at this exact path, in every
|
|
# package. The header above it is the machine-readable format apt tools read.
|
|
{
|
|
echo "Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/"
|
|
echo "Upstream-Name: bandsaunter"
|
|
echo
|
|
echo "Files: *"
|
|
echo "Copyright: 2026 The Dust Council"
|
|
echo "License: GPL-3.0-or-later"
|
|
echo
|
|
echo "License: GPL-3.0-or-later"
|
|
sed 's/^$/./; s/^/ /' "$here/LICENSE"
|
|
} > "$pkgdir/usr/share/doc/bandsaunter/copyright"
|
|
|
|
# Regenerated from the settings table rather than copied, so the manual
|
|
# cannot describe a version of the settings that no longer exists.
|
|
mkdir -p "$pkgdir/usr/share/man/man1"
|
|
python3 "$here/packaging/make-man.py" "$pkgdir/usr/share/man/man1/bandsaunter.1" \
|
|
>/dev/null
|
|
python3 "$here/packaging/make-browse-man.py" \
|
|
"$pkgdir/usr/share/man/man1/saunterbrowse.1" >/dev/null
|
|
gzip -9n "$pkgdir/usr/share/man/man1/bandsaunter.1"
|
|
gzip -9n "$pkgdir/usr/share/man/man1/saunterbrowse.1"
|
|
|
|
cat > "$pkgdir/usr/bin/bandsaunter" <<'EOF'
|
|
#!/usr/bin/python3
|
|
import sys
|
|
from bandsaunter.cli import main
|
|
sys.exit(main())
|
|
EOF
|
|
chmod 755 "$pkgdir/usr/bin/bandsaunter"
|
|
|
|
cat > "$pkgdir/usr/bin/saunterbrowse" <<'EOF'
|
|
#!/usr/bin/python3
|
|
import sys
|
|
from bandsaunter.browse import main
|
|
sys.exit(main())
|
|
EOF
|
|
chmod 755 "$pkgdir/usr/bin/saunterbrowse"
|
|
|
|
# Every one of these is in Debian, so apt resolves the lot. No speech
|
|
# recogniser is packaged for Debian, so that part ships as its own package
|
|
# (built by build-repo.sh) and is recommended rather than depended on: apt
|
|
# pulls it in from the repository, and a bare "dpkg -i" of this file still
|
|
# installs, without transcription.
|
|
cat > "$pkgdir/DEBIAN/control" <<EOF
|
|
Package: bandsaunter
|
|
Version: ${version}-${revision}
|
|
Section: hamradio
|
|
Priority: optional
|
|
Architecture: ${arch}
|
|
Depends: python3 (>= 3.10), python3-numpy, python3-scipy, python3-rich,
|
|
python3-yaml, librtlsdr0
|
|
Recommends: bandsaunter-transcribe, espeak-ng
|
|
Suggests: rtl-sdr, python3-pyqt6
|
|
Maintainer: bandsaunter
|
|
Installed-Size: $(du -ks "$pkgdir" | cut -f1)
|
|
Description: signal scanner and recorder for RTL-SDR receivers
|
|
Sweeps any set of frequency ranges, or presets from a built-in US band plan,
|
|
records what it finds and works out what kind of signal it was.
|
|
.
|
|
Captures are kept only if they carry voice, decodable CW or an identified
|
|
digital keying scheme, so static and interference are discarded rather than
|
|
filling the disk. CW is decoded to text, and speech can be transcribed when
|
|
a recogniser is installed.
|
|
EOF
|
|
|
|
cat > "$pkgdir/DEBIAN/postinst" <<'EOF'
|
|
#!/bin/sh
|
|
set -e
|
|
if [ "$1" = configure ]; then
|
|
# The DVB-T driver claims the dongle on sight and has to be kept off it.
|
|
if [ ! -e /etc/modprobe.d/blacklist-rtlsdr.conf ]; then
|
|
echo 'blacklist dvb_usb_rtl28xxu' > /etc/modprobe.d/blacklist-rtlsdr.conf
|
|
echo 'bandsaunter: blacklisted dvb_usb_rtl28xxu; unplug and replug the'
|
|
echo ' receiver, or run: sudo rmmod dvb_usb_rtl28xxu'
|
|
fi
|
|
|
|
# Said once, at install time, only when there is genuinely nothing to
|
|
# transcribe with -- otherwise the recogniser package has already
|
|
# arrived and there is nothing to explain.
|
|
if ! python3 -c 'import importlib.util as u, sys
|
|
sys.path.append("/usr/lib/bandsaunter/vendor")
|
|
try:
|
|
found = any(u.find_spec(m) for m in
|
|
("faster_whisper", "whisper", "vosk", "pocketsphinx"))
|
|
except Exception:
|
|
found = True # something is there, however unhappy; say nothing
|
|
sys.exit(0 if found else 1)' \
|
|
2>/dev/null; then
|
|
echo 'bandsaunter: speech transcription is unavailable -- no recogniser'
|
|
echo ' is installed. Scanning, recording and CW decoding all'
|
|
echo ' work without one. To add transcription, either:'
|
|
echo ' apt install bandsaunter-transcribe'
|
|
echo ' (from the repository this package came from), or'
|
|
echo ' pip install --break-system-packages faster-whisper'
|
|
echo ' Then: bandsaunter transcribe --engines'
|
|
fi
|
|
fi
|
|
exit 0
|
|
EOF
|
|
chmod 755 "$pkgdir/DEBIAN/postinst"
|
|
|
|
cat > "$pkgdir/DEBIAN/prerm" <<'EOF'
|
|
#!/bin/sh
|
|
set -e
|
|
if [ "$1" = remove ] || [ "$1" = purge ]; then
|
|
rm -f /etc/modprobe.d/blacklist-rtlsdr.conf
|
|
fi
|
|
exit 0
|
|
EOF
|
|
chmod 755 "$pkgdir/DEBIAN/prerm"
|
|
|
|
out=${1:-$here/dist}
|
|
mkdir -p "$out"
|
|
fakeroot dpkg-deb --build "$pkgdir" "$out" >/dev/null
|
|
echo "$out/bandsaunter_${version}-${revision}_${arch}.deb"
|