DVB-S2 Downlink + Spectrum Monitor ACI (haifuraiya_aci)
Transmit IP network traffic over a DVB-S2 RF link while simultaneously capturing receive-side spectrum snapshots and embedding them in the downlink.
Description
haifuraiya_aci combines the DVB-S2 downlink transmitter with an
FPGA-based spectrum monitor. IP packets arriving on a Linux TUN virtual
network interface are encapsulated using Generic Stream Encapsulation
(GSE, ETSI TS 102 606), packed into DVB-S2 Baseband (BB) frames, and
streamed to the on-board FPGA DVB-S2 encoder via the OpenCPI Application
Control Interface (ACI). In parallel, the application periodically
captures a 4096-point FFT spectrum snapshot from the receive path,
formats it as JSON, and sends it as a UDP datagram routed through the
same TUN interface — so the spectrum data is itself encapsulated and
transmitted over the DVB-S2 carrier.
The application uses a three-thread architecture:
TUN reader thread — reads IP packets from the TUN device (including the spectrum UDP packets produced by the FFT thread), fragments them into GSE packets sized to fit the BB frame data field, and pushes complete BB frames onto a bounded queue.
FFT UDP thread — triggers the FPGA triggered-sampler to capture a single spectrum snapshot once per second, computes log-power (dB) from the complex FFT output, serialises the result as a JSON object, and sends it as a UDP datagram to a configurable destination address/port via tun0.
Main thread — blocks on
getOutputBufferuntil the FPGA encoder has a free DMA buffer, then dequeues and sends the next BB frame. If the queue is empty (no IP traffic), a pre-built GSE null-packet padding frame is sent instead to keep the encoder fed.
The FPGA application (haifuraiya_aci.xml) connects:
The external
bbframe_inputport to thedvbs2_encoderworker input (TX path).The
dvbs2_encoderoutput to the Digital Radio Controller (DRC) TX port.The DRC RX port through a
triggered_sampler_xs→windower_xs_4k_proxy(Blackman-Harris) →xilinx_fftpipeline to the externalfft_outport (RX/spectrum path).
Default DVB-S2 parameters (compile-time constants in haifuraiya_aci.cc):
Parameter |
Value |
|---|---|
Frame type |
Long frame (FECFRAME = 64 800 bits) |
Constellation |
QPSK |
Code rate |
9/10 |
Pilots |
Disabled |
MATYPE1 |
|
BB frame size |
7153 bytes (derived from DVB-S2 ETSI EN 302 307 tables) |
DRC TX configuration (haifuraiya_aci.xml):
Parameter |
Value |
|---|---|
Center frequency |
400.0 MHz |
Bandwidth |
20 MHz |
Sample rate |
4.0 MSPS |
Gain |
−50 dB (manual) |
DRC RX configuration (haifuraiya_aci.xml):
Parameter |
Value |
|---|---|
Center frequency |
98.5 MHz |
Bandwidth |
20 MHz |
Sample rate |
20 MSPS |
Gain |
60 dB (auto) |
FFT parameters:
Parameter |
Value |
|---|---|
FFT size |
4096 points |
Window function |
Blackman-Harris |
Capture mode |
Single-shot (triggered once per second) |
Output format |
JSON object |
Hardware Portability
This application is designed to run on the following platforms:
Hardware |
RCC Platform |
HDL Platform |
|---|---|---|
ADALM-PLUTOSDR |
|
|
LibreSDR |
|
|
Xilinx ZC706 |
|
|
Xilinx ZCU102 |
|
|
Prebuilt SD Card Images
Prebuilt SD card images are available for download from GitLab CI. These
contain the OpenCPI runtime, bitstream, and haifuraiya_aci configured
to autostart on boot — no build environment required.
Platform |
Download Link |
|---|---|
LibreSDR |
|
Xilinx ZC706 |
|
Xilinx ZCU102 |
These links always point to artifacts from the latest successful CI
pipeline on the main branch.
SD Card Setup
Download the artifact archive for your platform and extract it.
Format an SD card with a FAT32 partition (or use an existing one).
Copy the extracted files to the FAT32 boot partition.
Insert the SD card into the target board and power on.
haifuraiya_aci will autostart on boot and begin transmitting on the
DVB-S2 downlink. This serves as the TX portion of the end-to-end link
described in the Receiving the Signal section below.
Execution
Prerequisites
Build the project for the target RCC and HDL platforms.
Deploy the bitstream and application artifacts to the target hardware.
Ensure the OpenCPI runtime is available on the target.
The application opens
/dev/net/tunand requires either root privileges orCAP_NET_ADMINon the target system.
Command(s)
Run the application on the target hardware:
./haifuraiya_aci [fft_dest_ip [fft_dest_port]]
Argument |
Default |
Description |
|---|---|---|
|
|
IP address (reachable via tun0) to receive spectrum JSON datagrams |
|
|
UDP port for spectrum data |
The application prints the name of the TUN interface it allocates (e.g.
tun0) and the commands needed to bring it up:
sudo ip link set tun0 up
sudo ip addr add 10.0.0.1/24 dev tun0
After running those commands, any IP traffic routed to the 10.0.0.1/24
subnet will be encapsulated and transmitted over the DVB-S2 RF link.
Spectrum data will be sent as UDP to the configured destination, routed
through tun0 and embedded in the downlink.
Verification
The application prints
Running: DVB-S2 TX active, spectrum → tun0 → downlinkonce the encoder and FFT threads are started.The FFT thread prints
FFT thread: sending spectrum UDP to <ip>:<port> (via tun0 → DVB-S2 downlink)confirming the spectrum path is active.Sending a ping from the host to
10.0.0.2will produce GSE encapsulation debug output on stdout showing the fragment structure and CRC-32 of each GSE packet.A spectrum analyzer or DVB-S2 receiver tuned to 400 MHz should detect the QPSK signal.
A UDP listener on the receiver side at the configured port should receive JSON objects containing the log-power spectrum array.
Troubleshooting
Cannot open /dev/net/tun — the process lacks
CAP_NET_ADMIN; run as root or grant the capability.Cannot ioctl TUNSETIFF — the
tunkernel module is not loaded; runmodprobe tun.app failed: … — an OpenCPI runtime error occurred (bitstream not loaded, DRC initialization failure, etc.). Check that the correct bitstream is programmed and that the DRC worker is available for the target platform.
BB queue full, dropping oldest frame — the FPGA encoder cannot consume frames as fast as IP traffic arrives. Reduce traffic rate or verify that the encoder bitstream is running at the expected sample rate.
FFT thread: failed to create UDP socket — the system could not create a UDP socket; check that networking is available on the target.
FFT thread: invalid destination address — the
fft_dest_ipargument is not a valid IPv4 address.
Worker Properties
The dvbs2_encoder worker (instance enc) exposes a regs array
property used at startup to configure the QPSK constellation mapper:
Register offset |
Value written |
Description |
|---|---|---|
|
|
QPSK symbol (+1, +1) |
|
|
QPSK symbol (+1, −1) |
|
|
QPSK symbol (−1, +1) |
|
|
QPSK symbol (−1, −1) |
Each 32-bit register packs the I sample in the upper 16 bits and the Q sample in the lower 16 bits. The magnitude used is 32 000 (out of a signed 16-bit range of ±32 767).
Receiving the Signal
To receive the DVB-S2 downlink produced by haifuraiya_aci you need a
MiniTiouner (or compatible DVB-S2 USB tuner) running the
LongMynd
software, together with the gse-rx application to decode the GSE stream
back into IP packets.
The receive-side data flow is:
RF → MiniTiouner → LongMynd (UDP BB frames) → gse-rx → TUN interface → host network
LongMynd Setup
LongMynd is an open-source DVB-S/S2 demodulator for the MiniTiouner hardware. It outputs BB frames as UDP datagrams and also provides a status stream and an optional web interface.
The easiest way to run LongMynd is to edit the example-run.sh script
included in the LongMynd source tree. Set the frequency and symbol rate
to match the haifuraiya_aci transmitter:
# In example-run.sh — key parameters to set:
FREQ=400000 # frequency in kHz (400 MHz)
SR=10000 # symbol rate in ks/s
The script configures the following defaults which work with gse-rx:
Parameter |
Default |
Description |
|---|---|---|
|
|
UDP destination for BB frame data (consumed by |
|
|
UDP destination for receiver status messages |
|
|
HTTP port for the LongMynd web interface |
|
|
LNB polarisation (sets LNB voltage) |
|
|
Milliseconds without data before receiver reset |
Then run the script:
cd /path/to/longmynd
./example-run.sh
LongMynd will lock onto the QPSK signal transmitted by haifuraiya_aci
and forward raw BB frame data as UDP packets to 127.0.0.1:9002. The
web interface at http://localhost:9004 can be used to monitor lock
status, signal strength, and MER.
gse-rx Setup
gse-rx is a standalone Linux application that receives the BB frame
UDP stream from LongMynd, synchronises and parses the BB frames, decodes
GSE packets, reassembles fragmented IP packets, and injects the recovered
IP traffic into a Linux TUN interface.
Build gse-rx (requires CMake 3.14+ and a C++17 compiler):
cd /home/aaron/git/gse-rx
cmake -B build
cmake --build build
Run gse-rx (requires root or CAP_NET_ADMIN):
sudo ./build/gse-rx
The program creates a TUN interface and prints the commands to bring it up:
sudo ip link set tun0 up
sudo ip addr add 10.0.0.2/24 dev tun0
Once the TUN interface is up, recovered IP packets from the DVB-S2
downlink are injected into the host network stack. This includes the
spectrum JSON UDP datagrams sent by the haifuraiya_aci FFT thread —
they will appear on UDP port 5005 (or whichever port was configured on
the transmit side) and can be consumed by a spectrum viewer application.
gse-rx listens on 127.0.0.1:9002 by default. These values are
compile-time constants in src/main.cc (LONGMYND_ADDR and
LONGMYND_PORT).
fft-display Setup
fft-display is a standalone web-based spectrum display that sits
downstream of gse-rx. It listens for the log-power FFT JSON UDP
datagrams recovered from the DVB-S2 downlink and serves a live spectrum
visualisation in a web browser. It has no OpenCPI dependency and can run
on any Linux host.
The application has two components:
UDP receiver (main thread) — binds to a UDP port and receives the
{"s": [dB, …]}JSON datagrams thatgse-rxdelivers to the host network stack via the TUN interface.HTTP/WebSocket server (server thread, using uWebSockets) — serves static HTML/CSS/JS from a
public/directory and forwards each received spectrum JSON to all connected WebSocket clients in real time.
On initial WebSocket connection the server sends a configuration message
with the center frequency and span:
{"center": 98500000, "span": 20000000}.
Clone and build fft-display (requires CMake 3.14+, a C++20 compiler,
and zlib):
git clone --recurse-submodules git@gitlab.com:aolivarez/fft-display.git
cd fft-display
cmake -B build
cmake --build build
Run the display application on the receive host:
./build/fft-display [udp_port [http_port]]
Argument |
Default |
Description |
|---|---|---|
|
|
UDP port to listen on for spectrum JSON datagrams |
|
|
HTTP port for the web-based spectrum viewer |
Then open http://localhost:3000 (or the appropriate host address) in a
browser to view the live spectrum display.
End-to-End Summary
A complete link requires two machines (or two terminals on the same host with an RF loopback):
TX side:
haifuraiya_aci (FPGA) → tun0 → DVB-S2 RF
RX side:
RF → MiniTiouner → LongMynd (UDP) → gse-rx → tun0 → fft-display → browser
Side |
Steps |
|---|---|
TX |
|
RX |
|