Pirip
Minimal Hardware IP over VHF/UHF Radio using RpiTx and RTLSDR
Install / Use
/learn @drowe67/PiripREADME
Pi Radio IP
Minimal hardware IP over VHF/UHF Radio using RpiTx and RTLSDRs [1].

Project Plan and Status
Status Oct 2021 - M9 in progress - have demonstrated 15km over the air link at 1000 bits/s.
| Milestone | Description | Comment | | --- | --- | --- | | M1 | ~~Proof of Concept Physical Layer~~ | | M2 | ~~Git repo for project, integrated tx and rx applications~~ | | M3 | ~~Simple GUI Dashboard that can be used to tune and debug link~~ | | M4 | ~~first OTA tests using uncoded modem~~ | repeat after recent tuning - demo an OTA link with margin | | M5 | ~~Pi running Tx and Rx~~ | Half duplex, loopback demo would be neat | | M6 | ~~Add LDPC FEC to waveform~~ | Needs to be tested/tuned OTA | | M7 | ~~Bidirectional half duplex Tx/Rx on single Pi~~ | frame repeater (ping) application developed and tested on the bench | | M8 | ~~Automated test system~~ | service scripts and 24 hour bench test completed | | M9 | OTA physical layer tests | 15 km link at 1 kbit/s | | M10 | TAP/TUN integration and demo IP link | What protocol? | | M11 | Document how to build simple wire antennas | |
Building
This procedure builds everything locally, so won't interfere with any installed versions of the same software.
RpiTx Transmitter
ssh into your Pi, then:
$ git clone https://github.com/drowe67/pirip.git
$ cd pirip
$ ./build_codec2.sh
$ ./build_rpitx.sh
$ cd tx && make
At the end of /boot/config.txt you need:
gpu_freq=250
force_turbo=1
Then reboot your Pi.
RTLSDR FSK Receiver
On your Pi (or laptop/PC):
$ sudo apt update
$ sudo apt install libusb-1.0-0-dev git cmake
$ ./build_codec2.sh
$ ./build_csdr.sh
$ ./build_rtlsdr.sh
Frame Repeater Automated Testing
A Frame Repeater has been developed to test the physical layer over the air. Terminal 1 sends a burst of frames to Terminal 2, which echoes the same frames back to Terminal 1. Terminal 1 logs metadata for each frame (Signal and Noise Power, SNR, time of arrival). The system is automated, so that it can run for hours unattended. By analysing the log files the Packet Error Rate (PER) and SNR of both legs of the link can be analysed. Knowing the gain of the RTLSDR receiver, we can use signal power S, and noise power N to estimate the link budget and local noise density (EMI) at the receiver.
Terminal 1 is a laptop with a HackRF Tx and RTLSDR Rx. Terminal 2 is a Pi running rpitx and a RTLSDR.
Service scripts have been written to wrap up the complex command lines.
scripts/ping is the service that sends the Tx bursts, and logs data on the received bursts; scripts/frame_repeater is the frame repeater service that runs on the Pi. Both service scripts include debug/test modes and command line help. The start_loopback command is a good way to test the local Tx/Rx is working OK. A nearby SSB radio tuned to the same frequency is useful to monitor transmissions.

Service command lines
-
Loopback test is a good start, this checks each terminal is working stand alone. These tests send a burst from the Terminals Tx to it's Rx:
laptop$ sudo ./ping start_loopbackpi$ sudo ./frame_repeater start_loopbackThe verbose options are useful for short tests to make sure the software is starting and running OK.
-
To use the frame repeater start the
frame_repeaterservice on the Pi (Terminal 2):pi$ sudo ./frame_repeater startThen start the Terminal 1
pingservice:laptop$ sudo ./ping start 6... will send 6 packets, 10 seconds apart (a 1 minute total run time). Look at
/var/log/pingfor results.
Useful Command Lines
This section contains command lines that were used during development to build up the system. Some are pretty complex and not easily remembered, so I have logged them here.
- Transmit two tone test signal for Pi:
pi@raspberrypi:~/pirip/tx $ sudo ./rpitx_fsk -t /dev/null - Transmit test frames from Pi for 60 seconds:
pi@raspberrypi:~/pirip/tx $ ../codec2/build_linux/src/fsk_get_test_bits - 600000 | sudo ./rpitx_fsk - - Receive test frames on x86 laptop for 5 seconds (vanilla rtl_sdr):
~/pirip$ Fs=240000; librtlsdr/build_rtlsdr/src/rtl_sdr -g 49 -s $Fs -f 144490000 - | codec2/build_linux/src/fsk_demod --fsk_lower 500 --fsk_upper 25000 -d -p 24 2 240000 10000 - - | codec2/build_linux/src/fsk_put_test_bits - - Receive test frames on x86 laptop for 5 seconds (vanilla rtl_sdr at Fs=1.8MHz):
Fs=1800000; ./src/rtl_sdr -g 49 -s $Fs -f 144500000 - | csdr convert_u8_f | csdr fir_decimate_cc 45 | csdr convert_f_s16 | ../../codec2/build_linux/src/fsk_demod --fsk_lower 500 -c 2 40000 1000 - - | ../../codec2/build_linux/src/fsk_put_test_bits -
-
Receive test frames on x86 laptop for 5 seconds (integrated rtl_fsk):
~/pirip$ Fs=240000; tsecs=5; ./librtlsdr/build_rtlsdr/src/rtl_fsk -g 49 -f 144490000 - -n $(($Fs*$tsecs)) | codec2/build_linux/src/fsk_put_test_bits -Note this is tuned about 10kHz low, to put the two tones above the rtl_sdr DC line.
-
Demod GUI Dashboard. Open a new console and start
dash.py:~/pirip$ netcat -luk 8001 | ./script/dash.pyIn another console start the FSK demod:
~/pirip$ Fs=240000; tsecs=20; ./librtlsdr/build_rtlsdr/src/rtl_fsk -g 1 -f 144490000 - -n $(($Fs*$tsecs)) -u localhost | codec2/build_linux/src/fsk_put_test_bits -
-
Automated loopback tests. Connect your Pi to your RTLSDR via a 60dB attenuator
Using vanilla
rtl_sdr:./test/loopback_rtl_sdr.shUsing integrated
rtl_fsk:./test/loopback_rtl_fsk.shYou can monitor
loopback_rtl_fsk.shusingdash.pyas above. -
Using a HackRF as a transmitter, useful for bench testing the link. The relatively low levels out of the HackRF make MDS testing easier compared to attenuating the somewhat stronger signal from the Pi. This example generates 1000 bit/s FSK with a 2000Hz shift:
cd codec2/build_linux/src ./fsk_get_test_bits - 60000 | ./fsk_mod -c -a 30000 2 40000 1000 1000 2000 - - | ../misc/tlininterp - t.iq8 100 -d -fThe output samples are at a sample rate of 4MHz, and a frequency offset of +1 MHz. They can be played out of the HackRF with:
hackrf_transfer -t t.iq8 -s 4E6 -f 143.5E6The signal will be centred on 144.5 MHz (143.5 + 1 MHz offset).
You can receive it with:
./rtl_fsk -w 500E3 -e ff8 -r 1000 -f 144490000 - -u localhost | ~/pirip/codec2/build_linux/src/fsk_put_test_bits - -
Noise Figure Testing
Connect a signal generator to the input of the RTLSDR. Set the frequency to 144.5MHz, and amplitude to -100dBm.
The following command pipes the RTL output to an Octave script to measure noise figure. You need the CSDR tools and Octave installed:
$ cd ~/pirip/rtl-sdr-blog/build_rtlsdr/src $ ./rtl_sdr -g 50 -s 2400000 -f 144.498E6 - | csdr convert_u8_f | csdr fir_decimate_cc 50 | csdr convert_f_s16 | octave --no-gui -qf ~/pirip/codec2/octave/nf_from_stdio.m 48000 complexA few Octave plot windows will pop up. Adjust your signal generator frequency so the sine wave is between 2000 and 4000, the script will print the Noise Figure (NF). Around 6.5 dB was obtained using RTL-SDR.COM V3s using"-g 50"
See also codec2/octave/nf_from_stdio.m and Measuring SDR Noise Figure in Real Time.
-
Running Rx on Pi: This example 10 kbit/s, dashboard running on laptop 192.168.1.100
./rtl_fsk -s 2400000 -a 80000 -w 500E3 -e ff8 -r 10000 -f 144490000 - -u 192.168.1.100 | ~/pirip/codec2/build_linux/src/fsk_put_test_bits - -
FSK with LDPC and framer at 1000 bit/s. On the Pi Tx, we use an external source of test frames:
$ cd ~/pirip/tx $ ../codec2/build_linux/src/fsk_get_test_bits - 2560 256 | sudo ./rpitx_fsk - --code H_256_512_4 -r 1000 -s 1000Laptop Rx:
$ cd ~/pirip/librtlsdr/build_rtlsdr $ ./src/rtl_fsk -g 49 -f 144490000 - -r 1000 --code H_256_512_4 -v -u localhost > /dev/nullIn this example we aren't counting errors in the received frames, but you can get some indication from the number of "iters" - if it's just 1 the FEC decoded isn't working very hard.
-
FSK with LDPC and framer at 10000 bit/s, internal test frames. On the Pi Tx:
$ sudo ./rpitx_fsk /dev/zero --code H_256_512_4 -r 10000 -s 10000 --testframes 10Laptop Rx:
$ cd ~/pirip/librtlsdr/build_rtlsdr $ ./src/rtl_fsk -g 1 -f 144490000 - -a 100000 -r 10000 --code H_256_512_4 -v -u localhost --testframes > /dev/nullWhen the Pi transmits a burst, you'll see something like:
721 nbits: 28 state: 1 uw_loc: 58 uw_err: 5 bad_uw: 0 snrdB: 7.4 eraw: 36 ecdd: 0 iter: 9 pcc: 256 rxst: -BS- 722 nbits: 34 state: 1 uw_loc: 58 uw_err: 2 bad_uw: 0 snrdB: 6.4 eraw: 44 ecdd: 0 iter: 9 pcc: 256 rxst: -BS- 723 nbits: 40 state: 1 uw_loc: 58 uw_err: 4 bad_uw: 0 snrdB: 7.1 eraw: 46 ecdd: 0 iter: 10 pcc: 256 rxst: -BS- 724 nbits: 46 state: 1 uw_loc: 58 uw_err: 3 bad_uw: 0 snrdB: 7.3 eraw: 44 ecdd: 0 iter: 7 pcc: 256 rxst: -BS- 725 nbits: 2 state: 1 uw_loc: 58 uw_err: 2 bad_uw: 0 snrdB: 6.8 eraw: 30 ecdd: 0 iter: 5 pcc: 256 rxst: -BS- 726 nbits: 8 state: 1 uw_loc: 58 uw_err: 1 bad_uw: 0 snrdB: 6.8 eraw: 55 ecdd: 0 iter: 14 pcc: 256 rxst: -BS- 727 nbits: 14 state: 1 uw_loc: 58 uw_err: 3 bad_uw: 0 snrdB: 7.6 eraw: 64 ecdd: 0 iter: 15 pcc: 250 rxst: EBS- 728 nbits: 20 state: 1 uw_loc: 58 uw_err: 1 bad_uw: 0 snrdB: 7.5 eraw: 42 ecdd: 0 iter: 8 pcc: 256 rxst: -BS- 729 nbits: 26 state: 1 uw_loc: 58 uw_err: 4 bad_uw: 0 snrdB
Related Skills
node-connect
349.7kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
109.7kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
349.7kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
349.7kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
