Nachbau von Frequenzregler/-anzeige und Spektrumdarstellung des
USB-Sprachempfängers in Kivy — siehe
homepage/src/projekte/ssb-usb-frontpanel/index.md für den Hintergrund
(native GRC-Widgets vs. Alternativen).
Trennt Signalverarbeitung und Darstellung bewusst:
spectrum_source.grc / .py — reiner GNU-Radio-Flowgraph (kein GUI), die
echte Empfänger-Kette aus ssb-usb-empfaenger/ssb_usb_144300_file_test.grc
(IQ-Testdatei → Freq-Xlating-USB-Filter → Realteil → AGC). Liefert drei
unabhängig abfragbare Signale über je einen Probe Signal Vector-Block:
blocks_probe_signal_vx_0 — Original-IQ-Spektrum (vor der Demodulation)blocks_probe_signal_vx_1 — demoduliertes NF-Spektrumblocks_probe_signal_vx_2 — demoduliertes NF-Zeitsignal (1024 Samples)Wird von der Kivy-App importiert, nicht eigenständig ausgeführt.
spectrum_widget.py — SpectrumWidget: eigenständiges, matplotlib-basiertes
Kivy-Widget (kivy_garden.matplotlib) mit genau einer Spektrum-Achse.
Kennt GNU Radio nicht, nur die Kivy-Properties label, values,
samp_rate, freq_divisor und line_color — main.py instanziiert es
zweimal unabhängig (Original- und demoduliertes Spektrum), jeweils mit
eigenem Label, eigener Abtastrate und eigener Farbe.
time_widget.py — TimeWidget: dasselbe Prinzip für das Zeitsignal, mit
den Properties label, values und samp_rate.
frequency_display.py — FrequencyDisplay: wiederverwendbares
Digitalanzeige-Widget, formatiert frequency_hz als „xxx.xxxx MHz".
main.py — App: startet den Flowgraph, instanziiert SpectrumWidget (×2)
und TimeWidget (×1) mit je eigenem Label/eigener Datenquelle, pollt die
drei Probes reihum per Clock.schedule_interval (siehe „Bekannte
Einschränkung" unten). Zwei Buttons (- 1 kHz / + 1 kHz) verstimmen die
Empfangsfrequenz live über tb.set_carrier_freq().
pip install kivy kivy_garden.matplotlib
python3 main.py
pip install kivy kivy_garden.matplotlib
python3 main.py
Voraussetzung: ../ssb-usb-sender/ssb_usb_144300_testsignal.iq muss
existieren (mit ssb-usb-sender/ssb_usb_sender_testsignal.grc erzeugt).
Jeder Probe Signal Vector-Block liefert bei .level() immer einen
kompletten Vektor auf einmal, nicht sequenziell:
_0, _1): fft_size (512) Float-Werte in dB, dasLog Power FFT-Frames. shift=True sorgt dafür, dass_0: -iq_samp_rate/2,_1: -audio_rate/2) und Index fft_size-1 der höchsten — die(i - N/2) * samp_rate / N._2): time_len (1024) Float-Werte in linearer Amplitude,time_len aufeinanderfolgende NF-Samples nach der Demodulation (perStream to Vector gruppiert). Die Sample-Achse n = 0 … time_len-1n / audio_rate Sekunden.carrier_freq ist eine normale GRC-Variable, die als center_freq des
Freq Xlating FIR Filter verwendet wird. GRC generiert dafür automatisch
set_carrier_freq(), das intern freq_xlating_fir_filter_xxx_0.set_center_freq()
aufruft — ein Kivy-Button kann diese Methode direkt aufrufen, um den
Empfänger live umzustimmen (siehe main.py:_step_frequency).
kivy_garden.matplotlib hat einen Race-Condition-Bug: Zeichnen mehrere
FigureCanvasKivyAgg-Widgets ihre Textur im selben Kivy-Frame neu, kann
die GPU-Textur eines Widgets die eines anderen überschreiben — nur das
zuletzt gezeichnete Widget bleibt sichtbar, die anderen bleiben schwarz.
Reale, wiederholt reproduzierte Beobachtung (GNU Radio 3.10.12.0, Kivy 2.3.1,
matplotlib 3.10.1, macOS/AMD-GPU über OpenGL 2.1): Werden spectrum_orig,
spectrum_demod und time_widget synchron in einem _update()-Aufruf
aktualisiert, rendert zuverlässig nur das zuletzt gesetzte Widget.
Workaround in main.py: _update() aktualisiert reihum immer nur genau
eines der drei Widgets pro Tick (Round-Robin über eine Liste aus
(Widget, Probe)-Paaren), nie alle drei gleichzeitig. Damit bleibt jedes
Widget bei jedem dritten Tick (bei UPDATE_HZ=20 also ~6,7-mal pro Sekunde)
aktuell — für eine Live-Anzeige ausreichend, und über mehrere Minuten Laufzeit
stabil getestet, ohne dass die Race Condition erneut auftritt.
GNU Radio 3.10.12.0, Kivy 2.3.1, matplotlib 3.10.1,
per Widget.export_to_png() geprüft — screenshot.png zeigt die
laufende App bei 500 kHz mit allen drei unabhängigen Widgets: Original-Spektrum
mit deutlichem Träger, demoduliertes Spektrum und sauberes Zeitsignal.
Weitere QT-Elemente aus dem Empfänger, die sich nach demselben Muster
nachbauen ließen: Lautstärke-/Squelch-Regler (Kivy Slider), LED-Indikator
für Squelch-Status, S-Meter (analog.probe_avg_mag_sqrd_f statt
probe_signal_vf, aggregierter Einzelwert statt eines Vektors).