feat: GNU Radio front-end to filter/decimate before rtl_433 #44

Open
opened 2026-08-22 21:04:41 +00:00 by time · 1 comment
Contributor

When using an RTL-SDR with an R820T tuner via SoapySDR, the hardware IF filter's minimum bandwidth is 350 kHz — it clamps any lower request to that. For the PIC[k]LE decoder (OOK at 250 s/s with a ~2 kHz symbol rate), 350 kHz of noise bandwidth results in unnecessarily high noise floors that impact autolevel detection.

SoapyRTLSDR accepts and silently clamps the bandwidth value, and its getBandwidth() just returns the requested value (not the actual), so rtl_433 can't detect the mismatch. We've patched rtl_433 locally to use the existing soapysdr_set_bandwidth() helper which adds a readback log, but that still doesn't solve the underlying problem.

The R820T's IF filter table (from librtlsdr tuner_r82xx.c) has discrete entries:

1700k, 1600k, 1550k, 1450k, 1200k, 900k, 700k, 550k, 450k, 350k

There's no 32 kHz option. The only way to achieve a narrow noise floor is digital filtering.

Plan: Pipe raw IQ samples from GNU Radio into rtl_433 via rtl_tcp, using GNU Radio as a programmable digital IF filter + decimator. The GNU Radio chain would:

  1. Read from RTL-SDR (same SoapySDR or native rtlsdr)
  2. Apply a real-valued FIR low-pass filter at ~5-10 kHz cutoff
  3. Decimate from 1 MSps to ~250 ksps (or lower)
  4. Output over rtl_tcp to rtl_433

Benefits:

  • Noise power reduced by ~20-25 dB vs 350 kHz (from 350k to ~10k equivalent bandwidth)
  • rtl_433 autolevel gets a much cleaner baseline
  • No modifications needed to rtl_433 core
  • Flexible — cutoff and decimation can be tuned per target
  • Could also be used to try different center frequencies without re-tuning hardware

A basic GNU Radio flowgraph would be:

RTL-SDR Source → Band/Decim Filter (1→N, cutoff ~10 kHz) → rtl_tcp Server

rtl_433 would then connect with -d rtl_tcp://127.0.0.1:1234 and the sample rate in rtl_433 would match the post-decimation rate.

Investigate: Whether the tx_emulator/ directory already has or could have a GNU Radio flowgraph for this. The existing sniff_915.grc is for receiving but may not have filtering/decimation. Also worth checking whether run_headless.sh can be adapted.

When using an RTL-SDR with an R820T tuner via SoapySDR, the hardware IF filter's minimum bandwidth is 350 kHz — it clamps any lower request to that. For the PIC[k]LE decoder (OOK at 250 s/s with a ~2 kHz symbol rate), 350 kHz of noise bandwidth results in unnecessarily high noise floors that impact autolevel detection. SoapyRTLSDR accepts and silently clamps the bandwidth value, and its getBandwidth() just returns the requested value (not the actual), so rtl_433 can't detect the mismatch. We've patched rtl_433 locally to use the existing soapysdr_set_bandwidth() helper which adds a readback log, but that still doesn't solve the underlying problem. The R820T's IF filter table (from librtlsdr tuner_r82xx.c) has discrete entries: ``` 1700k, 1600k, 1550k, 1450k, 1200k, 900k, 700k, 550k, 450k, 350k ``` There's no 32 kHz option. The only way to achieve a narrow noise floor is digital filtering. **Plan**: Pipe raw IQ samples from GNU Radio into rtl_433 via rtl_tcp, using GNU Radio as a programmable digital IF filter + decimator. The GNU Radio chain would: 1. Read from RTL-SDR (same SoapySDR or native rtlsdr) 2. Apply a real-valued FIR low-pass filter at ~5-10 kHz cutoff 3. Decimate from 1 MSps to ~250 ksps (or lower) 4. Output over rtl_tcp to rtl_433 Benefits: - Noise power reduced by ~20-25 dB vs 350 kHz (from 350k to ~10k equivalent bandwidth) - rtl_433 autolevel gets a much cleaner baseline - No modifications needed to rtl_433 core - Flexible — cutoff and decimation can be tuned per target - Could also be used to try different center frequencies without re-tuning hardware A basic GNU Radio flowgraph would be: ``` RTL-SDR Source → Band/Decim Filter (1→N, cutoff ~10 kHz) → rtl_tcp Server ``` rtl_433 would then connect with `-d rtl_tcp://127.0.0.1:1234` and the sample rate in rtl_433 would match the post-decimation rate. **Investigate**: Whether the tx_emulator/ directory already has or could have a GNU Radio flowgraph for this. The existing `sniff_915.grc` is for receiving but may not have filtering/decimation. Also worth checking whether `run_headless.sh` can be adapted.
Owner

The tx_emulator doesn't have this. It does sound like a good experiment, though. On a feature branch, let's create a flowgraph for a tighter IF filter and pipe it into rtl_433. We'll want a new flex decoder script that hooks to the tcp socket, also.

The tx_emulator doesn't have this. It does sound like a good experiment, though. On a feature branch, let's create a flowgraph for a tighter IF filter and pipe it into rtl_433. We'll want a new flex decoder script that hooks to the tcp socket, also.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
daru/PICsense#44
No description provided.