Skip to content

Live waterfall view prototype - #297

Open
dannyjacobs wants to merge 6 commits into
mainfrom
DCJ-liveview
Open

Live waterfall view prototype#297
dannyjacobs wants to merge 6 commits into
mainfrom
DCJ-liveview

Conversation

@dannyjacobs

@dannyjacobs dannyjacobs commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

This adds a prototype waterfall style live view showing the "current" data coming off the SDR. The project was also a test of augmented development using a LLM (Claude Opus 4.2). That is described in more detail below.

Design factors considered include:

  • use of a small screen and single board computer
  • deference to student developers by minimizing changes to existing code and selecting comprehensible architecture patterns
  • considering careful the experience and perception of novice users

This feature is now ready for Beta testing on a RPi. It has been extensively alpha tested on a Macbook Pro and resources monitored to make sure it will probably be ok on a Pi. See the TODO section below for more testing instructions.

I believe this branch to be based on the current released version on the Main branch, but it may be behind one or two fixes.

Live spectrum display and front-end monitor

Adds a live waterfall display to the DAQ GUI. The goal is "see what the
radio sees" — is the LNA on, is there in-band or out-of-band RFI — without
waiting for a sweep to finish. The viewer is fed either by an in-progress
science sweep or by a new standalone diagnostic monitor gnuradio graph.

The display taps the GNU Radio stream before the science integration, so it
updates several times faster than integrated spectra are written to disk, and
its averaging depth is independent of the science scan settings.

Files changed

File Change
src/chart/blocks.py New display branch on the flowgraph
daq/freq_and_time_scan.py Optional on_tb_ready callback (additive only)
daq/live_view.py New — waterfall panel + producer workers
daq/chart-observe.py Tabbed layout, control strip, live display, monitor

Features added

Flowgraph display branch (blocks.py). Tees off
complex_to_mag_squared — upstream of the science integrate_ff — into a
second integrator and a probe_signal_vf. Exposes get_display_spectrum()
and set_display_int_length(). The science branch and file sink are
untouched; display averaging is set independently of int_length/nint.

runObservation hook (freq_and_time_scan.py). Gains an optional
on_tb_ready callback that hands out the live TopBlock. Defaults to None,
so the CLI path and existing GUI behaviour are unchanged.

live_view.py (new module).

  • WaterfallPanel — matplotlib waterfall embedded via FigureCanvasTkAgg,
    rolling numpy buffer, blit rendering, calibrated frequency axis that retunes
    with the data.
  • LiveViewWorker — runs a lightweight TopBlock at a fixed frequency for
    standalone monitoring (supports bias-T/LNA power).
  • ScienceWorker — wraps runObservation plus a display tap.
  • Standalone test app for exercising the panel outside the main GUI.

GUI integration (chart-observe.py).

  • Body split into Configuration and Display tabs.
  • Persistent control strip below the tabs (above the terminal), visible from
    either tab.
  • Start/Stop collapsed into a single Start Sweep / Stop Sweep toggle.
  • Monitor switch with its own tuning entry (defaults to 1420.4 MHz) and
    LNA power checkbox, kept in sync with the Configuration tab's Bias-T switch
    and firing the same warning.
  • Sweep and monitor are mutually exclusive on the SDR. Start Sweep always takes
    priority and stops the monitor; the monitor is locked out while a sweep runs.
  • Three-state indicator — green Recording, blue Monitoring, grey Idle
    shown both in the top bar (visible from any tab) and on the Display tab, so
    monitoring is never mistaken for recording.
  • Display tab shows a read-only parameter summary alongside the waterfall.
  • Auto-switches to the Display tab when a sweep starts.

Also folds in the team's proposed milestone version (pointing-window
persistence, altitudePlot, specializedErrors, label and typo cleanups). That
merge was clean — no overlap with the changes here.

Design notes

  • Producers and the display are decoupled by a bounded queue.Queue carrying
    (centre_freq, spectrum) tuples, with drop-oldest on overflow. The display
    is source-agnostic: sweep and monitor feed the same waterfall through the
    same path.
  • The queue drain runs at ~30 Hz, deliberately oversampling the ~15 Hz
    producer. A consumer running near the producer rate produces beat-frequency
    stalls where a tick finds no new frame; oversampling makes any missed frame
    imperceptible.
  • The waterfall Figure is built with the object-oriented API, not
    plt.subplots(). A pyplot-registered figure would be popped up in its own
    window by plotObservation's plt.show() and have its canvas reassigned,
    breaking blit on subsequent runs.

Tests still required

Developed and tested on macOS against a real RTL-SDR. Not yet exercised on a
Raspberry Pi.

  • Full Pi validation. Performance, blit cost, layout, window geometry.
    Blit cost scaling with figure size was observed on Retina and set aside
    as likely Mac-specific — needs confirming on Pi hardware.
  • Monitor ↔ sweep swap on hardware. Device release timing when Start
    Sweep takes the SDR from a running monitor. The equivalent science-only
    restart path is confirmed working.
  • LNA / Bias-T sync on hardware. Bidirectional sync and warning from
    both the Configuration switch and the monitor checkbox.
  • Monitor cadence at fixed frequency. Faster-than-science update rate is
    confirmed for the sweep tap, not yet for the standalone monitor.
  • Team's merged pointing/altitude features in this tree.

Major TODO

  • Test on Rpi 4
  • Test on Rpi 5
    Things to watch for:
  • I don't think any additional installations are necessary, but I have added a conda/mamba venv yaml for convenient install just in case. use mamba env create -f chart.yml, then mamba activate chart. Come to think of it, you may need to sudo apt install mamba
  • test the live view option. No settings on the frequency sweep page are necessary. It should flow down nice and smooth, no flickering or lagging
  • test the science scan. On "start" it should click over to the waterfall view, you should see the frequency axis changing as the tunings change, the "live" plot (name collision to be addressed somehow) should still pop up when the scan is complete and the data should arrive where you expect it to.

Minor TODO

Display features

  • Floating / auto-scaling colorbar (currently fixed DB_LO/DB_HI)
  • XY line-plot mode (live power-vs-frequency trace)

Status readout

  • Drop the frame count from the Display status line
  • Port the fps / queue-depth readout from live_view.py into the
    integrated status line — more diagnostic than frame count

Layout (intended for tuning on the Pi)

  • Display tab split ratio: SUMMARY_WEIGHT / WATERFALL_WEIGHT
  • Summary arrangement: SETTINGS_PLACEMENT is "stacked"; "side"
    mirrors the Configuration tab's geography if the team prefers it
  • Window geometry still 786x510

Housekeeping

  • LiveViewWorker builds a TopBlock with a throwaway temp file_sink
    because TopBlock requires one — make the sink optional
  • Monitor uses _LIVE_NINT = 700_000 (~5 h) to approximate an unbounded
    run; a genuine run-forever mode would be cleaner
  • blocks_vector_to_stream_0 in blocks.py is instantiated but never
    connected (pre-existing dead code, left alone deliberately)
  • Decide whether the incremental test scripts (test_display_probe.py,
    test_queue_display.py, test_science_view.py) become regression tests
    or get archived
  • matplotlib.use("TkAgg") ordering is a latent fragility if the import
    structure between chart-observe.py and live_view.py changes
  • Monitor toggle uses CTkSwitch; a knife-switch style widget was the
    original intent

Use of an LLM

This project was used an opportunity to test the latest Large Language Models. Claude Opus 4.5 was used for search, note taking, code generation and documentation. The problem itself is quite amenable as it is

  1. well defined: many similar programs exist with established interface patterns.
  2. self contained: Large changes to existing code base are not required. (though this was a subject of discussion and informed the decision-making as development went along
  3. fairly simple: The number of lines of code needed are not so large as to be unreadable by a home, nor extensive use of many complexity
  4. Exceedingly unlikely otherwise: This was feature I had long desired, but having written such things before I knew it would be a good solid several weeks of work. Blitting to a double-buffered imshow without causing seizures or setting the Pi on fire is no small task. Spending the time for a feature that may not be loved by users or turn out to be impossible on the hardware would be exceedingly unwise in this economy.
  5. Mining the bubble: The LLM economy has all the signs of a robust bubble. At the moment, the product is free* but that will not last much longer. Lets make hay while the sun shines.
  6. Experienced Developer: I have written similar programs before in both python and MATLAB. I also have 26 years of experience working with developers, engineers, and students. Working with the LLM at the current state of the art is much like that kind of interaction. There is an art to phrasing requests to get a reasonable set of answers without overfitting on a single solution or following rabbits down holes resulting an enormous engineering bill.

*In the middle of this project I ran out of "free" Claude credits (the meter is not easy to find). To everyone's amusement I was obliged to purchase a year's subscription to "Pro" level access to complete the project at the cost of ~$200US.

The pattern I followed was to iteratively present a problem, have the model present options, discuss the merits of the solution, and then have the model generate the necessary code. Design factors such as use of a small screen and single board computer, deference to student developers, and the user experience of novice ... users were carried throughout.

All in all I spent about 4 days on the project. This was much faster than it would have been un-aided. However, I would caution against extrapolating this result. For all the reasons I have given above, this project is almost a best-case scenario.

Claude was particularly good for keeping track of todo items. As the discussion evolved some features or minor changes were tabled to be returned to after testing on the Pi. It has done a good job of going back through the long conversation and summarizing these items. I can generate more details on any one of them by requesting them from the model. The above PR was generated by Claude which I then lightly edited, mainly to add instructions for testing on the Pi. I wrote this section the old fashioned way.

I have more to say on the LLM experience but for now will leave a final thought. I have written more in doing this project than I have quite some time.

D. Jacobs
Tempe AZ, 21 July 2026

@NHeier

NHeier commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

It seems this version doesn't include the chart-observe changes, so I only tested the live_view.py file.

With a signal generator I was able to see a spectrum drawn on the graph. The stats are: ~17fps in, ~8.8 fps out, 30 queued, and dropped was matching the amount drawn.

@dannyjacobs

dannyjacobs commented Jul 24, 2026 via email

Copy link
Copy Markdown
Contributor Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants