OnAir

Contributing to OnAir and a map of the code

Bug reports, test results from your radio and pull requests are all welcome.

Reporting a problem#

Open an issue and say what you did, what you expected, what happened, your operating system, your radio and the standard of the channel. The log (onair.log, see Settings and files) helps, and so does a screenshot of the status bar and the constellations. Save bug report writes one zip with everything. An IQ recording of the problem (Record IQ, or onair-cli --record auto --secs 10) is the most useful attachment.

Pull requests#

  • Keep a change focused and explain why in the description.
  • The build must pass on every system in the CI (Linux x86-64 and arm64, macOS Apple silicon and Intel, Windows). Keep system-specific code behind #ifdef __APPLE__ / _WIN32 and in the platform files.
  • Add or extend a test when you change the receiver, the error correction or the player logic. The generators in the library make most tests possible without a radio.
  • Do not change the decoded output of the receiver unless that is the point of the change: the tests compare it.
  • Match the style around you: short comments that say why, no large reformatting.
  • Say how you tested it, and on which system and radio.

By sending a contribution you agree it is released under the GPL-3.0-or-later. See Build from source.

How OnAir is put together#

OnAir is a C++20 program in two layers: a receiver library (core/) that knows nothing about windows, and a graphical app (app/) that draws what the library reports. The command-line tools use the same library.

The path of a signal#

radio -> ring buffer -> Engine -> standard receiver -> transport stream -> Player -> screen and speakers
(source)   (ring.h)    (analysis    T2 / DVB-T / ATSC / DAB     (ts.cpp)    (player.cpp)
                        thread)
  1. Sources (core/src/source*.cpp) deliver complex samples from a radio or a file: the HackRF natively, other radios through their own library or SoapySDR, recordings and a built-in synthetic signal.
  2. Engine (engine.cpp) owns the source, runs the analysis thread, feeds the spectrum analyser and the receiver for the chosen standard, watches the radio (unplug and reconnect) and publishes snapshots for the interface.
  3. Receivers: one folder per mode under core/src/ (for example adsb/, dab/, fm/); the main TV receivers are t2rx.cpp, dvbt_rx.cpp, atsc_rx.cpp, the atsc3_*.cpp files and isdbt_rx.cpp.
  4. Transport stream (ts.cpp, bbunpack.cpp) turns baseband frames into packets and parses services, guide and subtitles.
  5. Player (player.cpp) decodes video and audio with FFmpeg, keeps the audio clock and repairs short gaps.
  6. Outputs (tsout.cpp, nettuner.cpp): recording, UDP / RTP and the built-in network tuner.

Threads: the analysis thread runs the receiver (it must never wait for the interface), error correction runs on helper threads or the GPU, the player has its own reader thread and audio callback, and the interface runs on the main thread.

Folders#

FolderWhat is in it
core/include/dect2/public headers of the receiver library
core/src/the implementation, one folder or file per topic
app/the window: main.cpp (loop and layout), app.h (state) and one folder per mode's screens (<mode>_ui.cpp)
tools/dect2cli, dect2scan and packaging scripts
tests/unit tests, benchmarks and a fuzzer; run with ctest
packaging/icons and installer resources
third_party/Dear ImGui, miniaudio, codec2

Adding a mode#

A mode is one row in kModes (app/toolbar.cpp: name, group, blurb, tip, short label and search keys) plus its receiver in core/src/<mode>/ with a xxxTuning() function describing its usual frequency and sample rate (core/include/dect2/mode_tuning.h), and its screens in app/<mode>/.

Platform notes#

  • macOS: Accelerate (vDSP), Metal for LDPC, CoreAudio, Apple's frame interpolation for picture repair.
  • Windows: Direct3D 11 for LDPC and picture repair, D3D11VA for video, miniaudio (WASAPI) for sound.
  • Linux: vector kernels exist twice (AVX2 and baseline) and the loader picks one; no GPU decoder yet.

Tests#

ctest --test-dir build runs the unit tests. Most use synthetic signals produced by the generators in the library, so they need no radio. See ARCHITECTURE.md for more, and REAL_WORLD_CHECKLIST.md for what to check with a real radio.