Skip to content
jochenhammesPublic

About

Transmit Software for FM and SSB/USB audio transmission via pluto sdr

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

pluto-tx

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.

Auf einen Blick

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

pluto-tx: FM-Sendung läuft, TX-Basisband-Wasserfall, Sicherheits-Readout pluto-advanced-rx: interaktiver Wasserfall mit echtem Empfang

Web-TRX im Browser: Empfang mit RTL-SDR, Wasserfall mit Empfangsbandbreite und Frequenzskala, Sendeteil

Installation

Zwei Wege, je nach Zweck:

  • Paket (für den normalen Betrieb): das .deb aus den GitHub-Releases enthält alle Features, siehe packaging/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.

AIOC-Adapter (Quansheng UV-K5 & Co.)

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.

ESP32-S3 als 13-cm-Sender (FM und M17, experimentell)

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-tx dekodiert 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.

Erste Schritte

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).

Beispielhafte Anwendungsfälle

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 CQ braucht 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-codec stammt aus der Beta v3.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 und pluto-cli bleiben 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_attr geprü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 --json

Die 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 --json

Die 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 — Serien­nummern 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.

Unterstützte Hardware

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.

Sicherheit

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).

Struktur

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).

Bekannte Einschränkungen

  • 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.local kann per mDNS zeitweise auf eine nicht erreichbare Adresse auflösen, während der Pluto über eine direkte IP (z.B. 192.168.2.1 bei USB-Ethernet-Gadget) einwandfrei erreichbar bleibt. Bei errno 113/"No route to host": direkte IP statt Hostname verwenden, oder iio_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-rx gleichzeitig mit pluto-tx und ä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.

Mitmachen / Weiterentwicklung

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.

Lizenz

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.

Danksagungen

About

Transmit Software for FM and SSB/USB audio transmission via pluto sdr

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages