Eigene Sende- und Empfangssoftware für den ADALM-PLUTO (Pluto+, Tezuka-Firmware), HackRF One und RTL-SDR, gebaut mit GNU Radio — FM/SSB-Sprechfunk, mehrere Digitalsprache-Modi (M17, FreeDV 2020/2020B, RADE), Digimodes (Waterfall Writer, PSK31, RTTY, POCSAG, FT8, JS8, JTTY, Meshtastic/LoRa, MeshCore/LoRa, APRS, ADS-L- und ADS-B-Empfang) und ein wiederholender Datei-Broadcast. Entstanden, weil vorhandene TX-Software (SDRangel) den AD9361-Sendezweig nach "Stop" aktiv weitersenden ließ — dieses Projekt legt deshalb besonderen Wert auf eine eigene, von GNU Radio unabhängige Sicherheitsschicht (siehe Sicherheit).
Vier Programme, ein Repo:
pluto-tx— Sende-App (GUI)pluto-advanced-rx— Empfänger-App mit interaktivem, SDR++-artigem Wasserfall (GUI)pluto-cli— headless Kommandozeilen-Zugriff auf die wichtigsten Funktionen beider Apps, für Skripte/Automatisierung (volle Referenz)web-trx— Browser-Oberfläche für Senden und Empfangen übers Netz oder VPN, mit Wasserfall, Audio und Digimodes (Anleitung). Start und Stopp auch aus beiden Apps heraus.
Sendebetrieb nur mit gültiger Amateurfunklizenz. Verantwortung für Frequenzwahl, Bandplan und Sendeleistung liegt beim Betreiber.
TX-Modi (pluto-tx) |
FM (optional mit CTCSS/DCS), SSB (USB/LSB), M17, FreeDV 2020/2020B, RADE V1, File Broadcast, Baseband |
RX-Modi (pluto-advanced-rx) |
FM, SSB (USB/LSB), RADE V1, M17, Baseband |
| Digimodes (beide Apps, eigener Reiter) | Waterfall Writer (Text im Wasserfall), PSK31-Chat, RTTY, POCSAG (Funkruf), FT8, JS8 (JS8Call-kompatibel, Normal/Fast/Turbo/Slow), JTTY (WSJT-X, Tastaturmodus), Meshtastic (LoRa; nur Pluto/HackRF/RTL-SDR), MeshCore (LoRa, nur RF-Geräte), APRS (Bake und Nachricht von Hand), nur RX: ADS-L (Luftfahrt, 868 MHz), ADS-B (Luftfahrt, 1090 MHz) |
| Hardware | PlutoSDR/Pluto+ (TX+RX), HackRF One (TX+RX), RTL-SDR (RX), Soundkarte/externes Funkgerät (TX+RX), AIOC-Adapter/analoges Funkgerät (TX), ESP32-S3-Board (TX, nur 13 cm, nur FM und M17, experimentell) |
| Automatisierung | pluto-cli — dieselbe Codebasis headless, --json-Ausgabe |
| Fernbetrieb | web-trx — Browser-Oberfläche (FastAPI + Svelte) auf derselben Codebasis, ein Betreiber, Login, HTTPS |
| Sicherheit | NOTAUS, von GNU Radio unabhängige Abschalt-Logik, Live-Hardware-Readout |
Zwei Wege, je nach Zweck:
- Paket (für den normalen Betrieb): das
.debaus den GitHub-Releases enthält alle Features, siehepackaging/README.md. - Aus dem Checkout (für Entwicklung und den neuesten Stand): ein Befehl für alles:
./install.sh # Menü: zeigt Stand und Bauzeit je Feature
./install.sh --all # alles
./install.sh m17 rade # gezielt; die Grundinstallation kommt automatisch dazu
./install.sh --list # was es gibt und was installiert ist; ändert nichts
./install.sh --dry-run ... # zeigt, was passieren würde; ändert nichts
./install.sh --force ... # auch, was schon aktuell ist
./install.sh --yes ... # keine Rückfragen; ohne Features: nur die Grundinstallation
Nur Debian/Ubuntu, nicht mit sudo starten (das Skript ruft es selbst auf). Derselbe Befehl aktualisiert: Es wird nur neu gebaut, was sich geändert hat. Ein fehlgeschlagenes Feature hält die anderen nicht auf; am Ende stehen ein Ergebnis je Feature, die Logdatei unter ~/.cache/pluto-tx/install/ und der Bericht des Doctors (python3 -m pluto_tx.doctor). Ohne ein Feature ist sein Eintrag in den Apps ausgegraut, der Rest läuft normal.
| Feature | Inhalt | Woher |
|---|---|---|
base |
Apps und Starter, FM, SSB, FreeDV, Waterfall Writer, PSK31, RTTY, POCSAG, APRS; Pluto, HackRF, RTL-SDR, X6200, AIOC | apt |
m17 |
M17 | gr-m17, Build nach ~/.local |
lora |
Meshtastic und MeshCore | gr-lora_sdr (GPL-3.0), Build nach ~/.local |
rade |
RADE V1 | rade_c, Build in rade_c/ |
ft8 |
FT8 | ft8_lib (MIT), Build in ft8_lib/ |
wsjtx |
besserer FT8-Decoder jt9, findet rund 1,5-mal so viele Signale wie ft8_lib |
apt |
jtty |
JTTY | Release des WSJT-X-Projekts mit Prüfsumme, in wsjt-codec/ |
js8 |
JS8: JSC-Wortlisten für Freitext in Fast, Turbo und Slow, Decoder js8ref |
JS8Call-improved, in js8call/ |
js8call |
die Anwendung JS8Call zum Gegenprüfen; nicht in --all |
apt |
espdr |
ESP32-S3 als Sender: esptool 5, udev-Regel | pipx, dieses Repository |
web-trx |
Browser-Oberfläche; braucht Node.js ≥ 18, das nicht mitinstalliert wird. Ohne Node.js wird es übersprungen. Betrieb: web-trx/README.md |
venv, Frontend-Build |
Alle Fremdversionen stehen an einer Stelle: packaging/components.lock.
Grundinstallation: Sie nimmt den Benutzer in die Gruppen plugdev (HackRF, RTL-SDR) und dialout (X6200, AIOC, ESP32-S3) auf; das wirkt erst nach Neu-Login. Sie sperrt den Kernel-DVB-T-Treiber dvb_usb_rtl28xxu, der RTL-SDR-Sticks sonst als TV-Tuner beansprucht; ein bereits gestecktes Gerät braucht ein Aus- und Einstecken oder sudo rmmod dvb_usb_rtl28xxu. Der Pluto muss per Netzwerk (ip:...) oder USB (usb:...) erreichbar sein.
Früher hießen die Features eigene Skripte: ./install-m17.sh ist jetzt ./install.sh m17, ebenso lora, rade, ft8, jtty, js8, espdr, web-trx; ./install-js8.sh --with-js8call ist ./install.sh js8 js8call; wsjtx kam früher mit ./install-ft8.sh. Optionen stehen hinter --, etwa ./install.sh web-trx -- --reuse-dist DIR oder ./install.sh espdr -- --no-udev. Vorhandene Starter werden beim ersten Lauf als *.bak gesichert.
FreeDV braucht kein eigenes Feature: libcodec2 mit LPCNet/2020/2020B-Support kommt mit gnuradio.
Der
"AIOC"-Gerätetyp in pluto-tx sendet FM/RADE/Waterfall-Writer/PSK31/RTTY/
POCSAG als reines Audiosignal an ein per AIOC
angeschlossenes analoges Funkgerät (das Funkgerät moduliert selbst,
z. B. ein Quansheng UV-K5) -- PTT läuft über den seriellen DTR/RTS des
AIOC, nicht über SDR-Hardware. Verbindungsfeld-Format:
<serieller Port>|<ALSA-Gerät>, z. B.
/dev/ttyACM0|plughw:CARD=AllInOneCable,DEV=0 (Scan versucht, beide
Seiten automatisch zu finden und zu koppeln). Reine Digitalsprache-Modi
(M17/FreeDV/RADE-über-SDR) und LoRa (Meshtastic/MeshCore) brauchen echte
RF-Hardware und sind mit diesem Gerätetyp nicht wählbar. Das
eigenständige Skript pluto_tx/tx-stdin.py (Audio von stdin senden,
z. B. für eine TTS-Pipe ohne die GUI) nutzt dieselbe PTT-Logik
(pluto_tx/aioc_ptt.py) und funktioniert unabhängig von der App weiter.
Der Gerätetyp "ESP32-S3 (13 cm)" sendet mit einem ESP32-S3-Entwicklungsboard (mit zwei USB-Ports) im 13-cm-Band (2320–2450 MHz). Das Board hat
keinen I/Q-Ausgang: Es steuert den Testton seines WLAN-Funkteils in Frequenz (und ein wenig in der Amplitude), 40 000-mal pro Sekunde. Das genügt
für Signale mit konstanter Hüllkurve: pluto-tx rechnet das Basisband des Flowgraphs in die Momentanfrequenz um, und nur diese geht zum Chip. Deshalb
sind nur FM und M17 wählbar; alles mit Amplitudenmodulation (SSB, PSK31, FT8, FreeDV, RADE, ...) und LoRa ist bei diesem Gerät gesperrt
(GUI: ausgegraut mit Hinweis, pluto-cli: Fehlermeldung, Flowgraph: Rückfall auf FM).
Einrichten: ./install.sh espdr (esptool 5 über pipx, udev-Regel für das Board).
Der Code für das Board (espdr/, einschließlich der Firmware-Images) gehört zu diesem Repository: Er ist eine Kopie aus
esp32-sdr-trx (Zero-Clause-BSD-Lizenz, Stand in espdr/UPSTREAM), die tools/sync-espdr.sh
aktualisiert. espdr/ hier nicht ändern, sondern in esp32-sdr-trx. Es wird nichts nachgeladen; die Firmware kommt bei Bedarf ins RAM
des Boards (der Flash bleibt unberührt). Verbindungsfeld: leer (Board an den USB-Ports finden) oder NATIVER_PORT[,UART_PORT], z. B. /dev/ttyACM1,/dev/ttyACM0.
Vor der ersten Sendung einmal den Lizenzhinweis bestätigen (das sendet nichts):
python3 -m espdr.cli_tx -f 2345 --test-tone 1000 --dry-run --accept-licence. Dann z. B. pluto-cli tx m17 --device espdr --freq 2345000000 --src-callsign <Rufzeichen>.
Was du wissen musst (gemessen mit einem Board und einem PlutoSDR als Empfänger, Einzelheiten in
espdr/ → docs/research/PLAN-M17.md in esp32-sdr-trx, Abschnitt 10):
- Die M17-Kette von
pluto-txdekodiert das Signal (Rufzeichen und Sprache), auch noch am Ende einer 120-s-Sendung. - Mit dem Gerät selbst (
pluto-cli, ein Board, ein PlutoSDR als Empfänger, 2345 MHz, −6 dB) getestet: M17 von einer WAV-Datei wird dekodiert (Rufzeichen, 4,5 s Sprache); FM sendet und endet sauber; fünf Sendungen hintereinander ohne hängenden Träger (Träger innerhalb der Messauflösung von 0,22 s beim Tasten und beim Loslassen). Nicht getestet: die GUI im echten Betrieb, Mikrofonbetrieb über längere Zeit (Taktdrift Soundkarte gegen Chip), mehr als ein Board, 600-s-Sendungen. - Außerhalb des Kanals ist der Sender nicht sauber: die Nachbarfenster (9 kHz) liegen 37 dB (±12,5 kHz) und 46 dB (±25 kHz) unter der Kanalleistung, weiter draußen etwa −55 dBc bis ±200 kHz. Oberwellen sind nicht gemessen, die Leistung ist nicht kalibriert (Standardobergrenze −12 dB gegen den stärksten Wert).
- Die Trägerfrequenz weicht ab (der Quarz des Boards, einige kHz, je nach Temperatur) und driftet (1,5 kHz in 106 s, gemessen mit aktivem Thermalmodell des Boards und ohne ppm-Korrektur); M17-Empfänger folgen dem. Bei etwa 10 % der Frequenzen liegt das Signal wegen der Einstellmöglichkeiten des Chips ein paar kHz neben dem Wunsch (die Warnung nennt die tatsächliche Frequenz).
- Die Latenz von PTT bis Träger beträgt mindestens 100 ms, das Ende des Trägers kommt etwa 125 ms nach dem Loslassen; die Frequenz lässt sich während der Sendung nicht ändern.
- Eine Sendung braucht eine Amateurfunklizenz, und du bist für das verantwortlich, was du sendest. Nur 2320–2450 MHz.
pluto-tx
pluto-advanced-rx
Start verbindet automatisch mit config.DEFAULT_URI (ip:plutoplus.local).
Läuft der Verbindungsaufbau nicht sofort durch (z.B. errno 113/"No
route to host" bei mDNS-Problemen), im Device-Feld stattdessen die
direkte IP eintragen, z.B. ip:192.168.2.1 (USB-Ethernet-Gadget-IP des
Pluto), und "Connect" klicken.
Erste FM-Verbindung senden: pluto-tx starten, Frequenz eintragen
(z.B. 432.1500 MHz im 70cm-Band), TX Power niedrig lassen (Standard-
Ceiling ist bereits konservativ), Mikrofon oder eine Testdatei
(da2jh-test.wav ist voreingestellt) als Quelle wählen, PTT-Knopf
drücken und halten. Ein zweites Gerät (RTL-SDR mit pluto-advanced-rx,
oder jeder andere Empfänger) sollte das Signal bei der eingestellten
Frequenz zeigen.
M17 direkt gestartet (nicht über den pluto-tx-Starter) bleibt
ausgegraut — LD_LIBRARY_PATH wird nur vom generierten Starter
gesetzt:
export LD_LIBRARY_PATH="$HOME/.local/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH"
Web-TRX starten und stoppen: web-trx start|stop|status|open|log,
oder in pluto-tx bzw. pluto-advanced-rx in der Zeile „Web-TRX“ auf
Start, Stop oder Open klicken. Der Server läuft weiter, wenn
die App geschlossen wird. Web-TRX und eine App dürfen nie gleichzeitig
dasselbe SDR öffnen: Hat die App beim Start von Web-TRX ein Gerät offen,
fragt sie, ob sie es vorher trennen soll; hat Web-TRX einen Gerätetyp
offen, warnt die App vor „Connect“ damit und verbindet sich beim Start
nicht automatisch. PLUTO_WEBTRX_CONTROL=off blendet die Zeile aus
(nur Hinweistext).
FM/SSB-Sprechfunk. Klassischer Sprechfunk über Pluto oder HackRF —
Mikrofon oder Audiodatei als Quelle, Noise-Gate/Kompressor/Limiter
optional zuschaltbar (siehe Sicherheit für den
PTT-Sicherheitsmechanismus dahinter). Im FM-Modus kann ein CTCSS-Ton
(50 Standardtöne, 67–254,1 Hz) oder ein DCS-Code (83 Standardcodes, Polarität
N/I) unhörbar mitgesendet werden — z. B. um ein Relais zu öffnen; Pegel in % des
Hubs einstellbar (Standard 12 %, die Stimme wird um denselben Anteil leiser, der
Gesamthub bleibt gleich). Der Ton läuft ab PTT-Druck; dem Empfänger ~0,3 s
Vorlauf geben, bevor gesprochen wird. Ein Squelch-Tail (Reverse-Burst bzw.
DCS-Abschaltcode) wird nicht gesendet. SSB gibt es als USB und LSB
(HF-Konvention: unter 10 MHz LSB); die Digimodes (PSK31, RTTY, …) bleiben USB.
FM-Hub wählbar ±2,5 kHz (schmal, 12,5-kHz-Raster) oder ±5 kHz (breit — was
viele 2m-Relais und Funkgeräte erwarten; schmal klingt dort 6 dB leiser). Die
FM-Sprache wird wie bei jedem Funkgerät mit 750 µs Pre-Emphasis gesendet
(abschaltbar), damit sie nach der De-Emphasis des Empfängers nicht dumpf ankommt;
pluto-advanced-rx macht im FM-Modus die passende De-Emphasis (ebenfalls
abschaltbar). Tipp: Ein Headset/Nahbesprechungsmikro klingt deutlich besser als
das eingebaute Laptop-Mikrofon (Raumhall).
CLI: pluto-cli tx fm --ctcss 88.5 bzw. --dcs 023 / --dcs 754I, --deviation 5000,
--no-preemphasis,
pluto-cli tx lsb, pluto-cli rx lsb.
Digitalsprache mit M17. pluto-tx sendet, jeder M17-fähige
Empfänger (auch pluto-advanced-rx selbst, oder SDR++ mit dessen
eigenem M17-Demodulator) kann mitlesen/mithören. Rufzeichen (Quelle/
Ziel) direkt in der GUI einstellbar.
PSK31-Chat. Klassisches Keyboard-to-Keyboard-Digimode, eigener Reiter "Digimodes" in beiden Apps. Läuft unabhängig vom gerade gewählten primären Empfangsmodus (man kann z.B. FM hören und gleichzeitig einen PSK31-Chat auf derselben Bandbreite mitverfolgen) — aber nur einer der Digimodes (PSK31, RTTY, POCSAG, APRS, FT8, JS8, JTTY, Meshtastic, MeshCore, ADS-L oder ADS-B) kann gleichzeitig aktiv dekodieren, per eigenem Digimode-Kombo im Digimodes-Reiter umschaltbar.
Wiederholte Aussendung. Im Digimodes-Reiter der TX-App (Waterfall Writer,
PSK31, RTTY, POCSAG, Meshtastic) gibt es „Repeat“: Anzahl der Aussendungen (1–999)
und Intervall in Sekunden (Pause zwischen Ende der einen und Beginn der nächsten
Aussendung, der Sender ist dazwischen aus). PTT startet die Serie, ein erneuter
Klick auf PTT beendet sie sofort, auch während der Pausen; E-STOP, Trennen und
Moduswechsel ebenfalls. Vor jeder Wiederholung laufen die Prüfungen erneut
(z. B. Meshtastic-Duty-Cycle, leerer Text): schlägt eine fehl, endet die Serie.
CLI: --repeat-count N --repeat-interval SEKUNDEN bei tx digitext|psk31|rtty|pocsag|meshtastic.
RTTY. Klassisches 2-Ton-FSK-Fernschreiben (Baudot/ITA2), ebenfalls im Digimodes-Reiter. Baudrate (45.45/50/75/100) und Shift (170/425/850Hz) frei einstellbar, inkl. Normal/Reverse-Umschalter für Gegenstationen mit vertauschter Ton-Zuordnung.
POCSAG (Funkruf). Standardkonformer POCSAG-Digimode nach ITU-R M.584
(2-FSK, ±4,5 kHz Hub, 512/1200/2400 Bit/s, Logik 1 = niedrigere Frequenz), mit
Pluto/HackRF/RTL-SDR und über die Soundkarte. Soundkarte: TX gibt das geformte
NRZ-Signal (±1, Pegel 0,7) am Ausgang aus, um es in den Daten-/Mikrofoneingang eines
FM-Funkgeräts zu speisen (das Gerät moduliert, Eingangspegel auf ±4,5 kHz Hub
einstellen; DC-gekoppelter Datenanschluss ist ideal, ein wechselstromgekoppelter
Mikrofoneingang verformt lange gleiche Bitfolgen); RX liest das demodulierte
Diskriminator-/Datensignal eines Funkgeräts vom Audio-Eingang (Pegel beliebig, AGC).
RX: Kanalmitte einstellen, alle drei Baudraten
werden parallel dekodiert (Autobaud, beide Polaritäten, BCH-Fehlerkorrektur bis
2 Bit); Tabelle mit Zeit, Baud, RIC, Funktion, Text und Fehleranzeige,
Darstellung „Auto/Alpha/Numerisch“ und optional deutscher Zeichensatz
(DIN 66003). TX: Aussendung eines Rufs an eine RIC (0–2097151; Vorgabe ist
eine Test-RIC) als Alpha-, Numerisch- oder Nur-Ton-Nachricht mit Funktionsbits
0–3, Direktmodulation des Trägers mit Gauß-geformtem NRZ. Nur in Amateurfunk-
bändern senden (nicht auf kommerziellen Funkrufkanälen oder dem DAPNET-Kanal).
Die Inhalte fremder, nichtöffentlicher Funkrufe sind nicht für den Empfänger
bestimmt: die App zeigt sie an, speichert sie aber nicht.
CLI: pluto-cli tx pocsag --ric 1234567 --text "DA2JH Test" --baud 1200,
pluto-cli rx fm --digimode pocsag.
FT8. Im Digimodes-Reiter beider Apps, mit manueller QSO-Führung: man wählt
pro 15-s-Slot die Nachricht selbst (CQ, Antwort mit Locator, Rapport, R+Rapport,
RRR, RR73, 73 oder Freitext bis 13 Zeichen), ein automatischer QSO-Ablauf wie in
WSJT-X ist bewusst nicht eingebaut. TX: Eigenes Rufzeichen und Locator werden
gespeichert, DX-Rufzeichen und Rapport einstellbar, Audio-Offset 200–2900 Hz
(Signal 50 Hz breit), Slot „Next“, „1st (:00/:30)“ oder „2nd (:15/:45)“. PTT
schaltet die Aussendung für den nächsten passenden UTC-Slot scharf (Countdown
auf dem PTT-Knopf, ein erneuter Klick bricht ab). Gesendet wird 0,5 s nach
Slotbeginn, Repeat wiederholt alle 30 s im selben Slot. Das GFSK-Signal wird
direkt als komplexes Basisband erzeugt (USB, kein Hilbert-Umweg), Soundkarte
und AIOC bekommen dasselbe Signal als Audio. RX: USB-Demodulation auf der
eingestellten Dial-Frequenz (z. B. 14,074 / 144,174 MHz), Dekodierung jedes
Slots am Slotende mit WSJT-X' jt9 (oder ft8_lib als Rückfall), Tabelle mit
UTC, SNR, DT, Frequenz und Nachricht; „Only CQ“-Filter, Hervorhebung des
eigenen Rufzeichens, Doppelklick kopiert das Rufzeichen des Absenders. Mit der
Soundkarte wird das Audio eines USB-Empfängers dekodiert. Die Systemuhr muss
per NTP synchron sein (±1 s), beide Apps warnen sonst. RX im Realbetrieb erfolgreich
getestet: echter FT8-Empfang auf 20 m (2026-09-28). TX mit echten Gegenstationen steht noch
aus. Außerdem verifiziert:
TX-Aussendungen über Luft auf kurze Distanz (Pluto+ mit −40 dB → RTL-SDR) von jt9
und ft8_lib dekodiert, Tonabstand exakt 6,25 Hz, Zeitlage DT +0,1 s (die App
sendet Stille, bis die Uhr den Slotstart erreicht, unabhängig davon, wie lange das
Umschalten des Flowgraphen dauert); RX gegen echte 20-m-Aufnahmen (KiwiSDR) und
die Referenzaufnahmen von ft8_lib.
Driftkompensation (Pluto): Der Referenzoszillator des Pluto+ erwärmt sich beim Senden;
gemessen wurden −3 ppb/s in der ersten Aussendung nach dem Start, danach ≈ −1 ppb/s (auf
23 cm −1,2 bis −3,8 Hz/s innerhalb einer Aussendung, also mehrere Tonabstände: nicht
dekodierbar). Die App verzerrt das Signal deshalb mit einem gegenläufigen Chirp vor, nach
einem gemessenen Modell (Dauerdrift beim Senden + Aufwärmen nach dem Start + Abkühlen in
Pausen, config.FT8_DRIFT_MODEL). Auf 23 cm blieb damit ≤ 0,5 Hz/s Restdrift, 3 von 3
Aussendungen wurden dekodiert, ohne Kompensation 0 von 3. Das Modell stammt von einem
Gerät; abschaltbar per „Drift comp.“ bzw. --no-drift-comp.
CLI: pluto-cli tx ft8 --call DA2JH --locator JO31 (CQ) bzw. --kind report --dx DL1ABC --report -12, --message "...", --slot even|odd, und
pluto-cli rx ssb --freq 14074000 --digimode ft8.
JTTY (WSJT-X). Im Digimodes-Reiter beider Apps, in Web-TRX und in pluto-cli. Der Tastaturmodus aus WSJT-X 3.2 sendet wie RTTY jederzeit, ohne Zeitschlitze, aber mit Fehlerkorrektur. 4-GFSK, 31,25 Baud, 127 Hz breit, USB.
- Senden: ganze Nachrichten bis 80 Zeichen von Hand. Rufzeichen und Rapporte werden kompakt gepackt:
CQ DA2JH CQbraucht einen Rahmen (1,9 s), Freitext 5 Zeichen je Rahmen. Vor dem Senden steht da, was hinausgeht; Zeichen, die JTTY nicht kennt (Umlaute), werden#. - Empfang: Der Text wächst, während die Rahmen eintreffen. Der Decoder sucht um die eingestellte Audiofrequenz (Standard 1500 Hz ± 50 Hz) und in zwei festen Kanälen bei 1200–1800 Hz, nicht im ganzen Band.
- Geräte: Pluto, HackRF, RTL-SDR (nur Empfang), Soundkarte und AIOC; am Funkgerät dann USB einstellen.
- Frequenzen: Die Vorschlagsliste kommt aus WSJT-X (zum Beispiel 14,090 MHz) und gilt dort als vorläufig.
- Nicht enthalten: Senden beim Tippen, F1–F8-Vorlagen, Contest-Profile, N1MM, automatische Antworten.
- Drift: Der Pluto driftet beim Senden, auf 23 cm am stärksten (kalt rund 300 Hz zu hoch und −4 Hz/s). Der Sender gleicht die Drift während der Sendung aus, abschaltbar. Ab 1 GHz sucht der Empfänger mindestens ±500 Hz. Der Ausgleich hat sich über Funk nur mit warmem Pluto gezeigt, der Kaltstart ist nicht gemessen. Auf 23 cm kann schon ein Quarzfehler von 0,65 ppm aus dem Suchfenster führen: dann Audio Hz auf das Signal im Wasserfall stellen oder die ppm-Korrektur setzen.
- Über Funk geprüft: Pluto+ und HackRF als Sender, RTL-SDR und HackRF als Empfänger, auf 2 m, 70 cm und 23 cm. Nicht geprüft: AIOC, Soundkarte am Funkgerät, Web-TRX und Desktop-Apps mit echter Hardware, Betrieb mit anderen Stationen. Offen: Ein CLI-Empfänger hat nach 26 Minuten Laufzeit eine Serie nicht dekodiert und endete mit einem Absturz, die Ursache ist unbekannt.
- Status: experimentell.
wsjt-codecstammt aus der Betav3.3.0-beta5, das Protokoll hat keinen Versionsmarker und kann sich noch ändern. Einzelne Fehldekodierungen gibt es auch im Original. Details und Prüfstand:docs/jtty/SPEC.md,docs/jtty/TESTS.md.
JS8 (JS8Call). Im Digimodes-Reiter beider Apps, in Web-TRX und in
pluto-cli, in allen vier Geschwindigkeiten:
| Speed | Periode | Tonabstand |
|---|---|---|
| Normal | 15 s | 6,25 Hz |
| Fast | 10 s | 10 Hz |
| Turbo | 6 s | 20 Hz |
| Slow | 30 s | 3,125 Hz |
- Protokoll: Nachrichten, Rahmen und Wellenform sind nach dem gepinnten
JS8Call-Quelltext nachgebaut und bitgenau gegen 176 Referenzfälle aus
JS8Call geprüft. Die Referenzen stehen in
docs/js8/SPEC.md. - TX:
- Nachrichtenarten: CQ, Heartbeat, @ALLCALL, gerichtete Nachricht, SNR?, SNR-Rapport, ACK, Freitext.
- Eine Nachricht geht in bis zu 20 aufeinanderfolgenden Perioden hinaus. Jeder Rahmen wird einzeln getastet, dazwischen ist der Sender dunkel.
- Der erste Rahmen startet nie verspätet in eine laufende Periode. Ein Abbruch oder NOTAUS verwirft den Rest der Nachricht.
- Die ganze Signalbreite muss in einem Amateurband liegen. Die Pluto-Driftkompensation wird wie bei FT8 genutzt.
- Automatische Antworten, Heartbeat, Relay und Inbox gibt es nur in
Web-TRX (Reiter „Automatik“). Nur dort laufen Empfang und Senden in einem
Prozess. Alles startet aus, siehe
web-trx/docs/JS8.md. TX-App, RX-App undpluto-clibleiben manuell.
- RX:
- Eine Geschwindigkeit oder alle gleichzeitig.
- Mehrrahmen-Nachrichten werden je Frequenz wie in JS8Call zusammengesetzt.
- Dekodiert wird mit JS8Calls eigenem Decoder (
js8ref) oder dem eigenen numpy-Decoder.
- Getestet (J7, 2026-09-30): Pluto+ auf 2 m mit −40 dB → RTL-SDR über
Luft.
- Alle Geschwindigkeiten über CLI, TX-App und Web-TRX wurden von beiden Decodern gelesen, DT ±0,02 s.
- Die Abbruchwege wurden mit
iio_attrgeprüft. - Einen Test mit echten KW-Gegenstationen gibt es noch nicht (keine
KW-Antenne). Details in
docs/js8/TESTS.md.
- CLI:
pluto-cli tx js8 --mycall DA2JH --kind cq --submode normal, bzw.--kind directed --to DL1ABC --text "...";pluto-cli rx ssb --freq 7078000 --digimode js8 --js8-submode all.
Meshtastic (LoRa). Sendet und empfängt echte Meshtastic-Pakete
(alle neun Modem-Presets LongFast … ShortTurbo, je EU433/EU868; Träger und
Standard-Kanalname wie in der Firmware berechnet) im Digimodes-Reiter beider
Apps. Nur LongFast (SF11/250 kHz) ist bit-genau gegen einen echten Heltec V3
verifiziert (868 MHz, beide Richtungen); die übrigen Presets stammen aus der
Firmware-Tabelle, ihre Sync-Symbole sind extrapoliert (im Programm markiert).
pluto-tx sendet eine Textnachricht als Broadcast auf dem Standardkanal
(Kanalname/PSK, Hop-Limit und Node-ID einstellbar, PTT sendet genau ein
Paket und löst danach selbst aus); pluto-advanced-rx zeigt jedes
empfangene Paket in einer Tabelle (Absender, Ziel, Typ, Hops, Text —
Pakete anderer Kanäle als "other channel"). Nur ein Digimode gleichzeitig
(wie bei PSK31/RTTY), und nur mit RF-Gerät (Pluto/HackRF/RTL-SDR),
nicht über Soundkarte.
MeshCore (LoRa). Eigener Digimode neben Meshtastic (gleiche LoRa-Basis,
anderes Paketformat). Preset „MeshCore EU/UK Narrow“: 869,618 MHz, SF8, 62,5 kHz,
CR 4/8, 32 Präambel-Symbole, Sync 0x12 — bit-genau gegen einen echten Heltec V3
verifiziert (RX dekodiert alle acht aufgezeichneten Bursts byte-gleich zum
ESP32-Referenz, unsere TX-Chirp-Symbole stimmen mit dem echten Burst überein).
pluto-advanced-rx zeigt Adverts (Name, Rolle, Position, Ed25519-Signatur
geprüft), entschlüsselt Gruppentexte des öffentlichen Kanals (und weiterer
Kanäle mit eigenem 32-Hex-Schlüssel; AES-128-ECB + 2-Byte-HMAC, MAC geprüft) und
führt eine Knotenliste; andere Pakettypen erscheinen mit Typ, Route, Pfad und
Länge als „unverifiziert“ (ausblendbar). Direktnachrichten an diesen Knoten
(dieselbe Schlüsseldatei wie in der TX-App; Häkchen „Decrypt direct messages to this
node“) werden entschlüsselt, sobald der Advert des Absenders gehört wurde (auch
nachträglich); Direktnachrichten an andere Knoten bleiben unlesbar. Nichts davon wird gespeichert.
pluto-tx sendet einen signierten Advert (Flood/Direct, Name, Rolle, optional
Position), einen Gruppentext (Public oder eigener Kanal) oder eine
Direktnachricht an den öffentlichen Schlüssel eines Knotens (64 Hex, aus der
Knotenliste der RX-App kopierbar; gemeinsames Geheimnis per X25519 aus den
Ed25519-Schlüsseln, AES-128-ECB + 2-Byte-HMAC wie in der MeshCore-Firmware); die App ist ein
eigener neuer MeshCore-Knoten (Ed25519-Schlüssel wird beim ersten Mal in
~/.config/pluto-tx/meshcore_identity.json angelegt, Rechte 0600, nie den Schlüssel
eines anderen Geräts benutzen). 10 % Duty-Cycle wird durchgesetzt. In
Amateurfunkbändern nur Adverts (MeshCore-Text ist immer verschlüsselt, und der
Advert braucht dort einen Namen = Rufzeichen). Ein Flood-Advert wird von
Repeatern weiterverteilt — Sendetests zunächst gegen einen isolierten Empfänger.
ACK/PATH/TRACE und Weiterleiten sind nicht implementiert (eine Direktnachricht wird
genau einmal gesendet, ohne Wiederholung bei fehlendem ACK).
CLI: pluto-cli tx meshcore --kind advert --name DA2JH, pluto-cli rx fm --digimode meshcore.
Rechtlicher Hinweis, im Programm bei jedem Preset sichtbar:
433 MHz (Band 433,0–434,0 MHz) liegt im deutschen 70-cm-Amateurfunkband — dort greift die
Eigenbau-Ausnahme, es gilt aber Ham Mode (Rufzeichen Pflicht, keine
Verschlüsselung; die App erzwingt beides). 869,525 MHz ist reines
ISM/SRD-Band ohne Amateurfunk-Sonderrecht: der Betrieb unzertifizierter
SDR-Hardware ist dort ein eigenes, bewusst zu tragendes Risiko; die App
erzwingt wenigstens die 10 % Duty-Cycle pro Stunde (Sendungen werden
sonst abgelehnt). Zum Testen --hop-limit 0 und geringe Leistung
verwenden, kein Rufzeichen in Text auf dem öffentlichen 868-MHz-Kanal.
ADS-L (Luftfahrt, nur Empfang). pluto-advanced-rx und pluto-cli rx --digimode adsl
empfangen die Positionsmeldungen von Luftfahrzeugen nach EASA ADS-L 4 SRD860 auf 868,2 und
868,4 MHz gleichzeitig (M-Band: GFSK, 100 kchip/s, Manchester, kein LoRa). Der Empfänger
stimmt selbst auf 868,85 MHz ab und stellt beim Verlassen des Modus die vorige Frequenz wieder her.
In der App: Reiter „Digimodes“, Eintrag „ADS-L (Aircraft, 868 MHz)“. Auf der Kommandozeile:
pluto-cli rx fm --device rtlsdr --digimode adsl --jsonDie RX-Bandbreite muss mindestens 1,8 MS/s betragen, darunter verweigert die App den Start. Die Standardwerte passen, mehr Bandbreite bringt keine bessere Empfangsquote:
| Gerät | Standard | Kleinste passende Stufe |
|---|---|---|
| RTL-SDR | 2,4 MS/s | 1,8 MS/s |
| Pluto+ | 2,5 MS/s | 2,5 MS/s |
| HackRF | 4 MS/s | 2 MS/s |
Der Wasserfall zeigt in diesem Modus 868,3 MHz in der Mitte mit den beiden Kanälen links und rechts davon; das ändert nur die Anzeige, der Zoom der anderen Modi kommt danach zurück. Ein Paket dauert nur etwa 5 ms und erscheint deshalb nicht zuverlässig im Wasserfall. Verlässlich sind Tabelle und Zähler.
Die Tabelle zeigt pro Luftfahrzeug Adresse, Kategorie, Position, Höhe (über dem WGS-84-Ellipsoid, nicht über Grund oder Meer), Tempo, Steigen, Kurs und die gemessene Frequenzablage; darunter steht das Paket-Log. Angezeigt wird nur, was die CRC besteht, gespeichert wird nichts. O-Band (LDR/HDR), FEC-Rahmen, Status- und Uplink-Inhalte sowie FLARM werden nicht dekodiert. Solange ADS-L aktiv ist, ist der File-Broadcast-Empfang abgeschaltet.
Empfangstipps: Das Signal braucht rund 10 dB über dem Rauschen im 200-kHz-Kanal. Beim RTL-SDR stellt
der Modus eine unveränderte Verstärkung selbst auf 40 dB und beim Verlassen zurück; die allgemeine Vorgabe
von 20 dB ist hierfür zu wenig. Am Pluto über das Netzwerk für Dauerbetrieb den RX-Puffer 262144 wählen. In pluto-cli bleibt der Ton bei ADS-L stumm, in der App
ist er wie immer aus, bis du den Empfang einschaltest. Ob gerade jemand in der Nähe sendet, zeigt das
OGN-Netz (Protokollkennung OGADSL); im Test war ADS-L deutlich seltener
als FLARM.
Nur zur Beobachtung, nicht zur Navigation oder Kollisionsvermeidung: Ein Empfänger hört nur einen Teil des Verkehrs. Das Projekt enthält bewusst keinen ADS-L-Sender.
Auch in Web-TRX verfügbar, dort zusätzlich mit Live-Karte.
Prüfstand: über Funk mit Testpaketen vom Pluto+ (−40 dB, Position bei 0°N 0°E, kurze Distanz) kamen auf
Pluto+ und RTL-SDR (40 dB) je 8 von 8 Paketen korrekt an, auch über Web-TRX bis zur Karte im Browser. Mit
nur 20 dB am RTL-SDR gingen Pakete verloren. Echter Flugverkehr ist noch nicht empfangen worden, und der
HackRF ist nur synthetisch getestet, nicht am echten Gerät. Details: docs/adsl/SPEC.md.
ADS-B (Luftfahrt, nur Empfang). pluto-advanced-rx, pluto-cli rx --digimode adsb und Web-TRX
empfangen die Positionsmeldungen von Flugzeugen auf 1090 MHz (DF17/DF18). Der Modus stimmt selbst ab. Die Tabelle zeigt
ICAO-Adresse, Rufzeichen, Position, Höhe in Fuß (meist barometrisch), Knoten, ft/min, Kurs und Notfall samt Squawk. In Web-TRX kommt die Live-Karte dazu. Nicht zur Navigation oder Kollisionsvermeidung. Das Projekt sendet auf
1090 MHz grundsätzlich nicht, auch nicht zum Testen.
pluto-cli rx fm --device rtlsdr --digimode adsb --adsb-locator JO43 --jsonDie RX-Bandbreite muss mindestens 2,4 MS/s betragen; das sind die Standardwerte von RTL-SDR (2,4) und Pluto+ (2,5), HackRF läuft mit 4 MS/s. Mehr Bandbreite verbessert hier den Empfang messbar, kostet aber CPU. Eine Position braucht zwei passende Nachrichten, ein Flugzeug kann also zunächst ohne Position erscheinen. Die Antenne sollte für 1090 MHz taugen und möglichst frei stehen.
Prüfstand: am Pluto+ über Funk an echtem Verkehr geprüft, auch über Web-TRX bis zur Karte; die Positionen passen zum
OGN-Netz, mit der Behelfsantenne kamen aber nur wenige, nahe Flugzeuge an. Das RTL-SDR hat bei 40 dB ein echtes Flugzeug mit
13 Nachrichten empfangen; es stand nicht im OGN-Feed, ein Abgleich fehlt dort also. Der HackRF ist mit ADS-B noch nicht am
echten Signal getestet. Details und Messwerte: docs/adsb/SPEC.md.
APRS (144,800 MHz, Empfang und Senden). pluto-advanced-rx zeigt Stationen, Nachrichten und Pakete, pluto-tx sendet
Bake oder Nachricht, pluto-cli rx --digimode aprs und pluto-cli tx aprs tun dasselbe auf der Kommandozeile,
Web-TRX zeigt zusätzlich eine Karte. Beim Wechsel in den Modus stellt der Empfänger 144,800 MHz ein, die
Frequenz bleibt aber änderbar. Es läuft nur über Funk: kein Igate, kein APRS-IS.
Gesendet wird nur auf Klick oder Befehl, ein Paket je Auslösung. Kommt die nächste Sendung früher als 30 s nach der letzten, warnt die App, sendet aber trotzdem. Kein Bake-Timer und kein Digipeater.
Vor dem Tasten abgewiesen werden ein fehlendes oder ungültiges Rufzeichen, Frequenzen außerhalb der Amateurbänder und zu
lange Texte. Eine Sendung dauert auf dem Pluto rund 1 s bei 0,6 s Paket. Die eigene Position ist die Mitte des Locators.
pluto-cli tx aprs verlangt --freq ausdrücklich. Web-TRX hat zusätzlich den Schalter „Automatisch bestätigen“: Er ist
aus, wird nicht gespeichert, geht mit dem letzten Browser oder dem NOTAUS aus und antwortet nur auf der Empfangsfrequenz.
Prüfstand: Über Funk geprüft auf 432,150 MHz bei −40 dB, Pluto+ zu RTL-SDR, mit der CLI 5 von 5 Paketen bei allen Feldern
wie gesendet. Echter Verkehr auf 144,800 MHz kam mit CLI und Web-TRX bis zur Karte an. Auf 144,800 MHz wurde noch nie
gesendet, der HackRF, die Soundkarte und der AIOC sind mit APRS noch nicht über Funk getestet, ebenso wenig die
Sendeknöpfe der Desktop-Apps. Details und Messwerte: docs/aprs/SPEC.md.
Waterfall Writer. Sendet eingegebenen Text so, dass er beim Empfänger direkt im Wasserfall/Spektrum als lesbares Bild erscheint — dieselbe Grundidee wie klassisches Hellschreiber. Zwei Layouts (horizontal/vertikal), Zoom-Faktor und Frequenzoffset einstellbar.
File Broadcast. Eine oder mehrere Dateien werden wiederholend
gesendet (Round-Robin, mit Gap-Filling) — praktisch für z.B.
Notfunk-Bulletins oder Community-Downloads, bei denen Empfänger
jederzeit einsteigen können, ohne den Sendebeginn abzuwarten.
pluto-advanced-rx empfängt File Broadcast ebenfalls immer parallel im
Hintergrund und speichert vollständig empfangene Dateien automatisch.
RTL-SDR über das Netzwerk. Ein RTL-SDR an einem anderen Rechner (z.B.
Raspberry Pi am Dachboden) läuft dort über den Standard-Server
rtl_tcp -a 0.0.0.0 -p 1234 (Paket rtl-sdr). In pluto-advanced-rx den
Gerätetyp „RTL-SDR“ wählen und im Verbindungsfeld neben dem Scan-Button
192.168.178.34:1234 (oder nur die IP, Port-Default 1234) eintragen —
Seriennummern für lokale USB-Sticks funktionieren wie bisher. Gleich per
CLI: pluto-cli rx fm --device rtlsdr --uri 192.168.178.34:1234. Hinweise:
rtl_tcp bedient nur einen Client gleichzeitig und hat keine
Authentifizierung (nur im vertrauenswürdigen LAN betreiben); bei 2,4 MS/s
fließen ~4,8 MB/s, über WLAN eine niedrigere RX-Bandbreite wählen (gemessen: bei 2,4 MS/s
verliert die WLAN-Strecke Daten, bis ~1 MS/s ist sie verlustfrei).
rtl_tcp liefert den Strom in 256-kB-Blöcken (bei niedrigen Raten Pausen bis
~0,5 s); ein selbstanpassender Puffer glättet das und hält den Ton
ruckelfrei, kostet aber entsprechend Latenz (Abstimmen wirkt ~0,5–1 s
verzögert; Puffer/Unterläufe stehen im HW-Status). Bricht die
Verbindung ab, verbindet sich die App selbst neu und sendet die Einstellungen
erneut (Statuszeile zeigt „lost“/„restored“).
Frequenzkorrektur (ppm) und Direct Sampling. Beide Apps haben für alle
RF-Geräte (Pluto, HackRF, RTL-SDR) ein Feld „Freq. correction (ppm)“, Default 0.
Der Wert ist die Abweichung des Geräts (wie bei rtl_test/Kalibrate/gqrx):
positiv = das Gerät läuft zu hoch, die App stimmt die Hardware entsprechend
tiefer ab. Da es ein Verhältnis ist, skaliert eine einmal bekannte Abweichung
mit der Frequenz (Beispiel: zeigt ein Empfänger deinen 432,150-MHz-Träger bei
432,125 MHz, ist das Gerät ~58 ppm zu tief → −58 eintragen). In
pluto-advanced-rx steht die Zeile direkt unter der Geräteauswahl; für den
RTL-SDR gibt es dort zusätzlich Direct sampling (Tuner wird umgangen, HF
0,1–28,8 MHz (über 14,4 MHz aliased: das Band mischt sich mit dem Spiegelbild von 28,8 MHz − f, Bandpass empfehlenswert), Dropdown Q-/I-Zweig — je nach Verdrahtung des HF-Eingangs, beim
RTL-SDR Blog V3 Q); beim Einschalten wechselt der Frequenzbereich auf HF, beim
Ausschalten kommt die vorherige Frequenz zurück. Der Tuner-Gain wirkt im
Direct-Modus nicht. AM (typisch für HF) kann die RX-App noch nicht; LSB gibt es als eigenen Modus „SSB (LSB)“.
Baseband-Modus + fldigi. Roher, unverarbeiteter Audio-Durchgriff
(kein Noise-Gate/Kompressor/Limiter, breiter als normales 3kHz-SSB-
Audio) — macht beide Apps zu einem Input/Output für externe
Digimode-Software wie fldigi, verbunden über die eingebauten,
persistenten PipeWire-Loopback-Knoten. Details und ein vollständiges
Rezept in pluto_cli/README.md.
Automatisierung via pluto_cli.
pluto-cli tx fm --freq 432150000 --yes
pluto-cli rx m17 --freq 432150000 --json
pluto-cli devices scan --tx --device pluto
Eigener, headless Prozess — importiert pluto_tx/pluto_advanced_rx
statt sie zu kopieren, kein Qt-Event-Loop, läuft auch über SSH/Cron.
Vollständige Referenz (alle Modi/Flags, JSON-Schema, Sicherheitsmodell,
weitere Rezepte) in pluto_cli/README.md.
| Backend | pluto-tx (TX) |
pluto-advanced-rx (RX) |
|---|---|---|
| PlutoSDR / Pluto+ | alle Modi, volle Sicherheitsschicht (Dämpfung + LO-Powerdown, siehe unten) | alle Modi |
| HackRF One | alle Modi | alle Modi |
RTL-SDR (USB oder per rtl_tcp im Netzwerk) |
— (kein TX-fähiges Gerät) | alle Modi |
| Soundkarte / externes Funkgerät | FM, RADE, Waterfall Writer, PSK31, RTTY, POCSAG, APRS, FT8, JS8, JTTY | alle Modi außer LoRa, ADS-L und ADS-B (Audio Input, z.B. für RADE über ein SSB-Funkgerät) |
Xiegu X6200 mit OpenX6X00 (USB-Audio und Hamlib-CAT, siehe docs/x6200/SPEC.md) |
SSB, LSB, FreeDV 2020, RADE, Waterfall Writer, PSK31, RTTY, FT8, JS8, JTTY | SSB, LSB, RADE, die Digimodes über Audio; Abstimmung über CAT |
| AIOC-Adapter / analoges Funkgerät (z. B. Quansheng UV-K5) | FM, RADE, Waterfall Writer, PSK31, RTTY, POCSAG, APRS, FT8, JS8, JTTY (Audio + echtes serielles PTT) | — (kein RX-Backend) |
| ESP32-S3-Board (nur 13 cm, experimentell) | nur FM und M17 (konstante Hüllkurve; Frequenz 2320–2450 MHz) | — (kein RX-Backend) |
Geräteauswahl per editierbarem Dropdown ("Device") plus Scan- und
Connect/Disconnect-Buttons. Nur pluto-tx hat zusätzlich ein
"Device Type"-Dropdown (PlutoSDR/HackRF/Soundcard/AIOC/ESP32-S3), das Verbindungsfeld,
Frequenzbereich und Pegel-Regler passend umschaltet.
Der AD9361-Treiber (libiio) trennt Buffer-Streaming von Dämpfung
(hardwaregain) und LO-Zustand (powerdown) — keine getestete
SDR-Software setzt diese beim Stoppen automatisch zurück (der konkrete
Auslöser für dieses Projekt: SDRangel ließ nach "Stop" einen Träger
aktiv weiterlaufen). pluto-tx verwaltet TX-Dämpfung und LO-Powerdown
deshalb komplett unabhängig von GNU Radio über rohes python3-libiio.
Eine einzige Funktion (force_safe_state(): Dämpfung auf Minimum + LO
aus) ist das, worauf jeder denkbare Shutdown-Pfad läuft — normales
Programmende, Fenster schließen, SIGINT/SIGTERM, unbehandelte
Exceptions. Die GUI zeigt alle 500ms den tatsächlichen Hardware-Zustand
an (nicht nur den zuletzt gesendeten Sollwert) und hat einen
NOTAUS-Button.
PTT ist in jedem Modus und auf jedem Gerät hart mit dem tatsächlichen HF-Abschalten verknüpft, nicht nur mit einer Pegelabsenkung: bei Pluto zusätzlich LO-Powerdown (reine Dämpfung unterdrückt LO-Leckage nicht ausreichend), bei HackRF ein Neuaufbau der Geräteverbindung (Nullen der Gain-Register allein stoppt die Sendung nachweislich nicht).
install.sh # Einstieg der Installation aus dem Checkout
scripts/install/ # ein Skript je Feature, lib.sh, features.sh, packages-base.txt
espdr/ # Kopie aus esp32-sdr-trx (0BSD): Host-Teil des ESP32-S3-Senders + Firmware-Images; `tools/sync-espdr.sh`, nicht hier ändern
pluto_tx/ # Sende-App
├── config.py # geräteunabhängige Konstanten
├── devices/ # TX-Geräte-Abstraktionsschicht (Pluto/HackRF/Soundcard/AIOC/ESP32-S3)
├── safety.py # PlutoSafety: rohes python3-libiio, unabhängig von GNU Radio
├── flowgraph.py # PlutoTxFlowgraph: die gesamte Signalkette
├── dynamics.py # Kompressor/Limiter
├── lora.py / lora_airtime.py # LoRa-PHY (gr-lora_sdr), Airtime-Formel + Duty-Cycle-Begrenzer
├── meshtastic_codec.py # Meshtastic-Paketformat (Header, AES-CTR, Protobuf)
├── meshcore_codec.py / meshcore_identity.py # MeshCore: Paketformat, Adverts (Ed25519), Gruppentext (AES-ECB+HMAC), Knotenidentität
├── pocsag.py / pocsag_codec.py # POCSAG: NRZ-Audio, Codewörter/BCH/Batches/Decoder (auch von der RX-App genutzt)
├── aprs_codec.py / aprs.py # APRS: AX.25 und Infofeld, AFSK-Audio
├── jtty_codec.py # JTTY: wsjt-codec aus WSJT-X als Unterprozess (Senden und Empfang)
├── armed_source.py # Audioquelle für APRS und POCSAG: startet ohne Trägervorlauf
├── adsl_codec.py # ADS-L: XXTEA, Traffic-Payload (nur Empfang, von der RX-App genutzt)
├── adsb_codec.py / mode_s_crc.py # ADS-B: Nachrichten, CPR-Positionen (nur Empfang); CRC-24 für ADS-B und ADS-L
├── ft8.py / ft8_ctypes.py # FT8: GFSK-Synthese, Nachrichten, Slot-Planung; ctypes-Anbindung an ft8_lib
├── js8.py / js8_phy.py / js8_message.py / js8_source.py / js8_tables.py # JS8: Wellenform + Slotplan, Rahmen (LDPC/CRC/Costas), Nachrichten (Varicode/JSC), Sequenzquelle, aus JS8Call extrahierte Tabellen
├── gui.py / app.py
├── webtrx_control.py / webtrx_widget.py # Web-TRX starten/stoppen/abfragen (Qt-frei) + die Zeile in beiden Apps
└── da2jh-test.wav # Standard-Testaufnahme
pluto_advanced_rx/ # Empfänger-App mit interaktivem Wasserfall
├── devices/ # RX-Geräte-Abstraktionsschicht (Pluto/HackRF/RTL-SDR/Audio)
├── waterfall_widget.py # eigenes pyqtgraph-Wasserfall-Widget
├── jtty_rx.py / jtty_state.py # JTTY: Audio an wsjt-codec, wachsende Nachrichten
├── aprs_demod.py / aprs_deframer.py / aprs_state.py # APRS: AFSK-Demodulator (numpy), GNU-Radio-Hülle, Stationen und Nachrichten
├── adsl_demod.py / adsl_deframer.py / adsl_state.py # ADS-L: Burst-Demodulator (numpy), GNU-Radio-Hülle je Kanal, Luftfahrzeugliste
├── adsb_demod.py / adsb_deframer.py / adsb_state.py # ADS-B: Pulsdemodulator (numpy), GNU-Radio-Hülle, Flugzeugliste mit Positionslogik
├── ft8_decoder.py / ft8_rx.py # FT8: Slot-Decoder (jt9 oder ft8_lib), UTC-Slot-Empfänger
├── js8_decoder.py / js8_rx.py / js8_assembly.py # JS8: Slot-Decoder (js8ref oder eigener), Empfänger je Speed, Mehrrahmen-Zusammensetzung
└── ...
tools/js8ref/ # JS8Calls Decoder als Kommandozeilenprogramm (Referenz, von ./install.sh js8 gebaut)
tests/ # Unit-/Loopback-Tests: python3 -m unittest discover tests
pluto_cli/ # headless CLI, importiert die beiden Apps oben, siehe pluto_cli/README.md
web-trx/ # Browser-Oberfläche: backend/ (FastAPI), frontend/ (Svelte), scripts/, docs/ -- siehe web-trx/README.md
# eigene Tests: cd web-trx/backend && .venv/bin/python -m pytest (nie pytest aus der Repo-Wurzel)
.github/workflows/ # CI für web-trx (Backend-Tests, Frontend-Build) und die Web-TRX-Steuerung der Apps
docs/screenshots/ # Screenshots für dieses README
backlog/ # geparkte/archivierte Arbeit (Entwicklungshistorie, nicht aktiv genutzte Module)
pluto_advanced_rx ist unabhängig von pluto_tx (RX kann nicht senden,
braucht keine TX-Sicherheitsschicht), teilt sich aber Bandplan-/
URI-Konstanten mit ihm.
Der Wasserfall in pluto-advanced-rx ist ein eigenes
pyqtgraph-Widget statt GNU Radios
qtgui.waterfall_sink_c: Live-Spektrum gekoppelt mit dem Wasserfall,
Klick-zum-Tunen, Tuning-Marker, Demod-Bandbreiten-Anzeige, Zoom/Pan,
einstellbare Floor/Ceiling-Slider, die sich nach dem Verbinden automatisch
am Rauschteppich einpegeln (Rauschen −15 dB … +45 dB, „Auto“-Knopf zum Neu-Einpegeln;
Pluto, HackRF und RTL-SDR liegen je nach Verstärkung 20 dB und mehr auseinander),
Averaging standardmäßig 3. RX-Bandbreiten-Presets bis 10 MHz
(1/2,5/5/8/10 MHz).
- ADS-L ist noch nicht an echtem Flugverkehr bestätigt, sondern an synthetischen Signalen und an eigenen Testpaketen über Funk. Empfangen wird nur das M-Band. Am Pluto über das Netzwerk ist für Dauerbetrieb ein großer RX-Puffer (262144) sinnvoll, mit dem Standardpuffer traten vereinzelt Überläufe auf.
- Meshtastic ist ein Basis-Digimode, kein vollwertiger Mesh-Knoten: gesendet wird eine Textnachricht als Broadcast (kein NodeInfo/Position, keine Empfangsbestätigungen, kein Weiterleiten fremder Pakete), nur der ein Kanal gleichzeitig. Bit-genau verifiziert ist nur LongFast auf 868 MHz gegen einen echten Heltec V3; andere Presets und 433 MHz (Ham Mode) wurden bisher nur im Selbst-Loopback getestet. Bei benutzerdefiniertem Kanalnamen wählt die Firmware einen anderen Frequenz-Slot — dann Frequenz von Hand einstellen. Ein Presetwechsel mit anderer SF/BW baut die TX-Kette neu auf.
- Datei-Wechsel ("Choose File") baut den Flowgraph komplett neu auf (kein Live-Swap in dieser GNU-Radio-Version) — kurze, aber sichere Unterbrechung. Audiodatei loopt unabhängig von PTT weiter, Position wird bei PTT nicht zurückgesetzt.
plutoplus.localkann per mDNS zeitweise auf eine nicht erreichbare Adresse auflösen, während der Pluto über eine direkte IP (z.B.192.168.2.1bei USB-Ethernet-Gadget) einwandfrei erreichbar bleibt. Beierrno 113/"No route to host": direkte IP statt Hostname verwenden, oderiio_info -u ip:plutoplus.local/den GUI-Scan-Button zur Kontrolle nutzen.- Der AD9361 teilt sich einen Takt zwischen RX und TX: läuft
pluto-advanced-rxgleichzeitig mitpluto-txund ändert ihre RX-Bandbreite, verschiebt das messbar den tatsächlich gesendeten Takt. RX allein ist nicht betroffen. Workaround bis zu einer echten Lösung: RX-Bandbreite nicht während aktiver Sendung ändern. - 5/10 MHz RX-Bandbreite nur eingeschränkt nutzbar: die
ip:plutoplus.local-Netzwerkverbindung (IIOD-Protokoll) schafft nur ~4,7-4,9 MSa/s, darüber gibt es Overruns/abgehacktes Audio. - FreeDV-Rufzeichen/Stationskennung ist dauerhaft deaktiviert — ein
bestätigter Bug in der gepackten
libcodec2(1.2.0-4) führt beim Verknüpfen des Textkanals zu einem reproduzierbaren Absturz. Stationskennung bis auf Weiteres per Sprache vor/nach der Übertragung. - Sichtbares Seitenband-/Spiegelsignal bei einigen Digitalmodi (FreeDV, Waterfall Writer) — die digitale Basisbandkette misst offline sauber (>60dB Unterdrückung), die reale Analogkette des AD9361 (IQ-Imbalance) zeigt auf echter Hardware deutlich weniger. Kein Software-Bug, nicht per Software behebbar; Waterfall Writer weicht dem durch einen einstellbaren Frequenzoffset aus.
Mehr Details und die volle Bug-Historie: siehe
backlog/DEVELOPMENT_HISTORY.md.
Issues und Pull Requests willkommen. Die ausführliche
Entwicklungsgeschichte — jeder gefundene Bug, jede Verifikation, interne
Architektur-Details — steht in
backlog/DEVELOPMENT_HISTORY.md;
offene Punkte stehen am Ende dieser Datei.
GPLv3 — Copyright (C) 2026 Jochen Hammes (DA2JH).
Das Verzeichnis espdr/ (Host-Teil des ESP32-S3-Senders und seine Firmware-Images) ist eine Kopie aus
esp32-sdr-trx unter der Zero-Clause-BSD-Lizenz (espdr/LICENSE, espdr/NOTICE); der Rest dieses
Repositories steht unter GPLv3. Die Kopie erzeugt tools/sync-espdr.sh, der Stand steht in espdr/UPSTREAM.
Die optionalen Abhängigkeiten gr-m17 (GPLv2), rade_c/RADE
(BSD-2-Clause) und ft8_lib (MIT) werden von ./install.sh m17/rade/
ft8 separat aus ihren jeweiligen Quellen gebaut, nicht in diesem Repo
vendort. WSJT-X (jt9, GPLv3) wird als eigenständiges Programm aufgerufen.
pluto_tx/js8_tables.py ist aus dem JS8Call-Quelltext (GPLv3) abgeleitet;
./install.sh js8 klont JS8Call separat und baut tools/js8ref gegen die
unveränderten Quellen.
- gr-m17 / M17-Project — offener Digitalsprache-Standard
- freedv/rade_c — RADE (Radio Autoencoder)
- FreeDV / codec2 — HF-Digitalsprache
- kgoba/ft8_lib und WSJT-X — FT8
- JS8Call (KN4CRD) und JS8Call-improved — JS8
- GNU Radio — das Fundament, auf dem alles hier aufbaut


