-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Signal Preprocessing
GNU Radio can be used to preprocess and display the signal sent to rtl_433
. This can be helpful when processing IQ samples from a HackRF.
The HackRF has a minimum sample rate (bandwidth) of 2 Msps and has a significant DC offset spike, which seems to cause rtl_433
to fail to detect weak signals.
In this case, it is beneficial to preprocess the signal before sending it into rtl_433
.
GNU Radio can be used for this. The trick is to create a named pipe and set GNU Radio to output to that pipe.
Then, use the -r
option in rtl_433
to read from that named pipe.
Because of the behavior of a named pipe, rtl_433
won't read off the end of the pipe and exit when finished like it would when reading from a file.
Instead, it will block as it waits for more samples from GNU radio. Moreover, no data is saved to disk with this method.
The details for how to achieve this are as follows:
-
Create a named pipe as follows. Note that the name is important. The
rtl_433
program will use this name to detect the sample rate and bandwidth. See "File names" for details on how this works. In this example, we use a sample rate of 500 ksps and a center frequency of 433.95 Mhz, with 16-bit signed interleaved IQ samples (.cs16
).mkfifo 001_433.95Mhz_500.0ksps.cs16
-
Start
rtl_433
with the required options. It seems that-Y autolevel
is necessary for this method as the noise floor can change significantly depending on how the gain is configured in the GNU Radio. In this example, we also add-M time:iso
to get an absolute timestamp (instead of a relative count of the number of seconds since the program started).rtl_433 -r 001_433.95Mhz_500.0ksps.cs16 -Y autolevel -M time:iso
-
Add a
Complex To IShort
block and set the scale factor to32767
(as the docs suggest). This will interleave the IQ samples as 16-bit signed integers, so that we match the file format we specified above. Now, add aFile Sink
. Set the file name to match the name of the pipe created withmkfifo
above and set the input type to "short". -
Last, start the flowgraph and
rtl_433
should begin displaying output (showing that the noise level is being adjusted). If a signal is received, the output data will be displayed in the terminal window runningrtl_433
.
Note: This method has only been tested on Linux.
Setting all of this up every time you want to use GNU Radio can be a pain, especially if you adjust the center frequency or bandwidth often - both of which require changing the file name.
The example GNU Radio companion flowgraph below automatically creates the correctly-named FIFO (named pipe) in the /tmp/
directory and launches rtl_433
with the parameters needed to pull from that pipe. On exit, it cleans up the named pipe.
This means you can change the center frequency or bandwidth and just click the "run" button in GNU Radio companion to start the flowgraph.
The flowgraph is available below in the Flowgraph Source
section.
A screenshot of this flowgraph running from the terminal is shown below.
The following is the source code for the rtl_433_connect.grc
example GNU Radio Companion flowgraph.
Copy and paste this into a file named rtl_433_connect.grc
and open in GNU Radio Companion to use it.
options:
parameters:
author: surface
catch_exceptions: 'True'
category: '[GRC Hier Blocks]'
cmake_opt: ''
comment: ''
copyright: ''
description: ''
gen_cmake: 'On'
gen_linking: dynamic
generate_options: qt_gui
hier_block_src_path: '.:'
id: rtl_433_connect
max_nouts: '0'
output_language: python
placement: (0,0)
qt_qss_theme: ''
realtime_scheduling: ''
run: 'True'
run_command: '{python} -u {filename}'
run_options: prompt
sizing_mode: fixed
thread_safe_setters: ''
title: RTL_433 Connect
window_size: (1000,1000)
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [8, 8]
rotation: 0
state: enabled
blocks:
- name: baseband_freq
id: variable_qtgui_range
parameters:
comment: 'Frequency the HackRF
is tuned to'
gui_hint: ''
label: ''
min_len: '200'
orient: QtCore.Qt.Horizontal
rangeType: float
start: 10e6
step: 5e6
stop: 3000e6
value: 433e6
widget: counter_slider
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [280, 8.0]
rotation: 0
state: true
- name: file_sink_output
id: variable
parameters:
comment: 'The file is named such that
rtl_433 can parse out the center
frequency and sample rate.'
value: '"/tmp/initial-grc-output"'
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [864, 16.0]
rotation: 0
state: enabled
- name: filter_bandwidth
id: variable
parameters:
comment: ''
value: 75e3
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [24, 168.0]
rotation: 0
state: enabled
- name: filter_decimation
id: variable
parameters:
comment: ''
value: '30'
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [592, 16.0]
rotation: 0
state: enabled
- name: filter_samp_rate
id: variable
parameters:
comment: Filter output sample rate
value: samp_rate / filter_decimation
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [592, 88.0]
rotation: 0
state: enabled
- name: filter_transition_width
id: variable
parameters:
comment: ''
value: 5e3
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [144, 168.0]
rotation: 0
state: enabled
- name: output_file_name
id: variable
parameters:
comment: 'The file is named such that
rtl_433 can parse out the center
frequency and sample rate.'
value: '"/tmp/001_" + str(tune_freq / 1e6) + "Mhz_" + "{:.3f}".format(filter_samp_rate
/ 1e3) + "ksps.cs16"'
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [600, 184.0]
rotation: 0
state: enabled
- name: samp_rate
id: variable
parameters:
comment: ''
value: 5e6
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [176, 8.0]
rotation: 0
state: enabled
- name: tune_freq
id: variable
parameters:
comment: "Frequency we are \ntuned to in software"
value: 433.95e6
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [448, 16.0]
rotation: 0
state: enabled
- name: blocks_complex_to_interleaved_short_1
id: blocks_complex_to_interleaved_short
parameters:
affinity: ''
alias: ''
comment: ''
maxoutbuf: '0'
minoutbuf: '0'
scale_factor: '32767'
vector_output: 'False'
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [608, 448.0]
rotation: 0
state: true
- name: blocks_file_sink_0
id: blocks_file_sink
parameters:
affinity: ''
alias: ''
append: 'True'
comment: ''
file: file_sink_output
type: short
unbuffered: 'False'
vlen: '1'
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [800, 440.0]
rotation: 0
state: enabled
- name: freq_xlating_fir_filter_xxx_0
id: freq_xlating_fir_filter_xxx
parameters:
affinity: ''
alias: ''
center_freq: tune_freq - baseband_freq
comment: ''
decim: filter_decimation
maxoutbuf: '0'
minoutbuf: '0'
samp_rate: samp_rate
taps: firdes.low_pass(1, samp_rate, filter_bandwidth, filter_transition_width)
type: ccc
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [352, 416.0]
rotation: 0
state: true
- name: import_0
id: import
parameters:
alias: ''
comment: ''
imports: import os
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [768, 16.0]
rotation: 0
state: true
- name: osmosdr_source_0
id: osmosdr_source
parameters:
affinity: ''
alias: ''
ant0: ''
ant1: ''
ant10: ''
ant11: ''
ant12: ''
ant13: ''
ant14: ''
ant15: ''
ant16: ''
ant17: ''
ant18: ''
ant19: ''
ant2: ''
ant20: ''
ant21: ''
ant22: ''
ant23: ''
ant24: ''
ant25: ''
ant26: ''
ant27: ''
ant28: ''
ant29: ''
ant3: ''
ant30: ''
ant31: ''
ant4: ''
ant5: ''
ant6: ''
ant7: ''
ant8: ''
ant9: ''
args: '""'
bb_gain0: '35'
bb_gain1: '20'
bb_gain10: '20'
bb_gain11: '20'
bb_gain12: '20'
bb_gain13: '20'
bb_gain14: '20'
bb_gain15: '20'
bb_gain16: '20'
bb_gain17: '20'
bb_gain18: '20'
bb_gain19: '20'
bb_gain2: '20'
bb_gain20: '20'
bb_gain21: '20'
bb_gain22: '20'
bb_gain23: '20'
bb_gain24: '20'
bb_gain25: '20'
bb_gain26: '20'
bb_gain27: '20'
bb_gain28: '20'
bb_gain29: '20'
bb_gain3: '20'
bb_gain30: '20'
bb_gain31: '20'
bb_gain4: '20'
bb_gain5: '20'
bb_gain6: '20'
bb_gain7: '20'
bb_gain8: '20'
bb_gain9: '20'
bw0: 3e6
bw1: '0'
bw10: '0'
bw11: '0'
bw12: '0'
bw13: '0'
bw14: '0'
bw15: '0'
bw16: '0'
bw17: '0'
bw18: '0'
bw19: '0'
bw2: '0'
bw20: '0'
bw21: '0'
bw22: '0'
bw23: '0'
bw24: '0'
bw25: '0'
bw26: '0'
bw27: '0'
bw28: '0'
bw29: '0'
bw3: '0'
bw30: '0'
bw31: '0'
bw4: '0'
bw5: '0'
bw6: '0'
bw7: '0'
bw8: '0'
bw9: '0'
clock_source0: ''
clock_source1: ''
clock_source2: ''
clock_source3: ''
clock_source4: ''
clock_source5: ''
clock_source6: ''
clock_source7: ''
comment: ''
corr0: '0'
corr1: '0'
corr10: '0'
corr11: '0'
corr12: '0'
corr13: '0'
corr14: '0'
corr15: '0'
corr16: '0'
corr17: '0'
corr18: '0'
corr19: '0'
corr2: '0'
corr20: '0'
corr21: '0'
corr22: '0'
corr23: '0'
corr24: '0'
corr25: '0'
corr26: '0'
corr27: '0'
corr28: '0'
corr29: '0'
corr3: '0'
corr30: '0'
corr31: '0'
corr4: '0'
corr5: '0'
corr6: '0'
corr7: '0'
corr8: '0'
corr9: '0'
dc_offset_mode0: '0'
dc_offset_mode1: '0'
dc_offset_mode10: '0'
dc_offset_mode11: '0'
dc_offset_mode12: '0'
dc_offset_mode13: '0'
dc_offset_mode14: '0'
dc_offset_mode15: '0'
dc_offset_mode16: '0'
dc_offset_mode17: '0'
dc_offset_mode18: '0'
dc_offset_mode19: '0'
dc_offset_mode2: '0'
dc_offset_mode20: '0'
dc_offset_mode21: '0'
dc_offset_mode22: '0'
dc_offset_mode23: '0'
dc_offset_mode24: '0'
dc_offset_mode25: '0'
dc_offset_mode26: '0'
dc_offset_mode27: '0'
dc_offset_mode28: '0'
dc_offset_mode29: '0'
dc_offset_mode3: '0'
dc_offset_mode30: '0'
dc_offset_mode31: '0'
dc_offset_mode4: '0'
dc_offset_mode5: '0'
dc_offset_mode6: '0'
dc_offset_mode7: '0'
dc_offset_mode8: '0'
dc_offset_mode9: '0'
freq0: baseband_freq
freq1: 100e6
freq10: 100e6
freq11: 100e6
freq12: 100e6
freq13: 100e6
freq14: 100e6
freq15: 100e6
freq16: 100e6
freq17: 100e6
freq18: 100e6
freq19: 100e6
freq2: 100e6
freq20: 100e6
freq21: 100e6
freq22: 100e6
freq23: 100e6
freq24: 100e6
freq25: 100e6
freq26: 100e6
freq27: 100e6
freq28: 100e6
freq29: 100e6
freq3: 100e6
freq30: 100e6
freq31: 100e6
freq4: 100e6
freq5: 100e6
freq6: 100e6
freq7: 100e6
freq8: 100e6
freq9: 100e6
gain0: '15'
gain1: '10'
gain10: '10'
gain11: '10'
gain12: '10'
gain13: '10'
gain14: '10'
gain15: '10'
gain16: '10'
gain17: '10'
gain18: '10'
gain19: '10'
gain2: '10'
gain20: '10'
gain21: '10'
gain22: '10'
gain23: '10'
gain24: '10'
gain25: '10'
gain26: '10'
gain27: '10'
gain28: '10'
gain29: '10'
gain3: '10'
gain30: '10'
gain31: '10'
gain4: '10'
gain5: '10'
gain6: '10'
gain7: '10'
gain8: '10'
gain9: '10'
gain_mode0: 'False'
gain_mode1: 'False'
gain_mode10: 'False'
gain_mode11: 'False'
gain_mode12: 'False'
gain_mode13: 'False'
gain_mode14: 'False'
gain_mode15: 'False'
gain_mode16: 'False'
gain_mode17: 'False'
gain_mode18: 'False'
gain_mode19: 'False'
gain_mode2: 'False'
gain_mode20: 'False'
gain_mode21: 'False'
gain_mode22: 'False'
gain_mode23: 'False'
gain_mode24: 'False'
gain_mode25: 'False'
gain_mode26: 'False'
gain_mode27: 'False'
gain_mode28: 'False'
gain_mode29: 'False'
gain_mode3: 'False'
gain_mode30: 'False'
gain_mode31: 'False'
gain_mode4: 'False'
gain_mode5: 'False'
gain_mode6: 'False'
gain_mode7: 'False'
gain_mode8: 'False'
gain_mode9: 'False'
if_gain0: '40'
if_gain1: '20'
if_gain10: '20'
if_gain11: '20'
if_gain12: '20'
if_gain13: '20'
if_gain14: '20'
if_gain15: '20'
if_gain16: '20'
if_gain17: '20'
if_gain18: '20'
if_gain19: '20'
if_gain2: '20'
if_gain20: '20'
if_gain21: '20'
if_gain22: '20'
if_gain23: '20'
if_gain24: '20'
if_gain25: '20'
if_gain26: '20'
if_gain27: '20'
if_gain28: '20'
if_gain29: '20'
if_gain3: '20'
if_gain30: '20'
if_gain31: '20'
if_gain4: '20'
if_gain5: '20'
if_gain6: '20'
if_gain7: '20'
if_gain8: '20'
if_gain9: '20'
iq_balance_mode0: '0'
iq_balance_mode1: '0'
iq_balance_mode10: '0'
iq_balance_mode11: '0'
iq_balance_mode12: '0'
iq_balance_mode13: '0'
iq_balance_mode14: '0'
iq_balance_mode15: '0'
iq_balance_mode16: '0'
iq_balance_mode17: '0'
iq_balance_mode18: '0'
iq_balance_mode19: '0'
iq_balance_mode2: '0'
iq_balance_mode20: '0'
iq_balance_mode21: '0'
iq_balance_mode22: '0'
iq_balance_mode23: '0'
iq_balance_mode24: '0'
iq_balance_mode25: '0'
iq_balance_mode26: '0'
iq_balance_mode27: '0'
iq_balance_mode28: '0'
iq_balance_mode29: '0'
iq_balance_mode3: '0'
iq_balance_mode30: '0'
iq_balance_mode31: '0'
iq_balance_mode4: '0'
iq_balance_mode5: '0'
iq_balance_mode6: '0'
iq_balance_mode7: '0'
iq_balance_mode8: '0'
iq_balance_mode9: '0'
maxoutbuf: '0'
minoutbuf: '0'
nchan: '1'
num_mboards: '1'
sample_rate: samp_rate
sync: sync
time_source0: ''
time_source1: ''
time_source2: ''
time_source3: ''
time_source4: ''
time_source5: ''
time_source6: ''
time_source7: ''
type: fc32
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [48, 268.0]
rotation: 0
state: true
- name: qtgui_waterfall_sink_x_0
id: qtgui_waterfall_sink_x
parameters:
affinity: ''
alias: ''
alpha1: '1.0'
alpha10: '1.0'
alpha2: '1.0'
alpha3: '1.0'
alpha4: '1.0'
alpha5: '1.0'
alpha6: '1.0'
alpha7: '1.0'
alpha8: '1.0'
alpha9: '1.0'
axislabels: 'True'
bw: filter_samp_rate
color1: '0'
color10: '0'
color2: '0'
color3: '0'
color4: '0'
color5: '0'
color6: '0'
color7: '0'
color8: '0'
color9: '0'
comment: ''
fc: tune_freq
fftsize: '8192'
freqhalf: 'True'
grid: 'False'
gui_hint: ''
int_max: '10'
int_min: '-140'
label1: ''
label10: ''
label2: ''
label3: ''
label4: ''
label5: ''
label6: ''
label7: ''
label8: ''
label9: ''
legend: 'True'
maxoutbuf: '0'
minoutbuf: '0'
name: '""'
nconnections: '1'
showports: 'False'
type: complex
update_time: '0.025'
wintype: window.WIN_BLACKMAN_hARRIS
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [616, 360.0]
rotation: 0
state: enabled
- name: qtgui_waterfall_sink_x_0_0
id: qtgui_waterfall_sink_x
parameters:
affinity: ''
alias: ''
alpha1: '1.0'
alpha10: '1.0'
alpha2: '1.0'
alpha3: '1.0'
alpha4: '1.0'
alpha5: '1.0'
alpha6: '1.0'
alpha7: '1.0'
alpha8: '1.0'
alpha9: '1.0'
axislabels: 'True'
bw: samp_rate
color1: '0'
color10: '0'
color2: '0'
color3: '0'
color4: '0'
color5: '0'
color6: '0'
color7: '0'
color8: '0'
color9: '0'
comment: ''
fc: baseband_freq
fftsize: '8192'
freqhalf: 'True'
grid: 'False'
gui_hint: ''
int_max: '10'
int_min: '-140'
label1: ''
label10: ''
label2: ''
label3: ''
label4: ''
label5: ''
label6: ''
label7: ''
label8: ''
label9: ''
legend: 'True'
maxoutbuf: '0'
minoutbuf: '0'
name: '""'
nconnections: '1'
showports: 'False'
type: complex
update_time: '0.025'
wintype: window.WIN_BLACKMAN_hARRIS
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [336, 296.0]
rotation: 0
state: disabled
- name: snippet_0
id: snippet
parameters:
alias: ''
code: '# Remove the file GNU Radio creates initially.
os.system("rm " + self.file_sink_output)
# Create the named pipe that we will output to.
os.system("mkfifo " + self.output_file_name)
# Start the RTL_433 program to read from our FIFO in the background
os.system("rtl_433 -r " + self.output_file_name + " -Y autolevel -M time:iso
-M level -M protocol &")
# Set the file sink output to use our named pipe.
self.set_file_sink_output(self.output_file_name)'
comment: 'Named pipe setup code
and execution of rtl_433'
priority: '0'
section: main_after_init
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [848, 200.0]
rotation: 0
state: enabled
- name: snippet_0_0
id: snippet
parameters:
alias: ''
code: '# Remove the named pipe
os.system("rm " + self.output_file_name)'
comment: Named pipe cleanup code
priority: '0'
section: main_after_stop
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [848, 320.0]
rotation: 0
state: enabled
connections:
- [blocks_complex_to_interleaved_short_1, '0', blocks_file_sink_0, '0']
- [freq_xlating_fir_filter_xxx_0, '0', blocks_complex_to_interleaved_short_1, '0']
- [freq_xlating_fir_filter_xxx_0, '0', qtgui_waterfall_sink_x_0, '0']
- [osmosdr_source_0, '0', freq_xlating_fir_filter_xxx_0, '0']
- [osmosdr_source_0, '0', qtgui_waterfall_sink_x_0_0, '0']
metadata:
file_format: 1
grc_version: 3.10.5.0