-
Notifications
You must be signed in to change notification settings - Fork 15
Using MultiFM
MultiFM is a fairly complex tool for building channelizing radio front-ends. Given a wideband chunk of the spectrum, MultiFM can extract multiple narrowband subchannels from that spectrum. MultiFM has been designed to work efficiently on medium-sized systems (such as a Raspberry Pi) running Linux.
Some basic optimization has been done for ARM, using NEON primitives to perform some compute-intensive actions (i.e. around the filtering operation and data ingest). There are plenty of opportunities to improve on this, though. All operations in MultiFM (except for the atan2(3) operation performed in FM demodulation) are done using fixed point arithmetic.
Any configuration for MultiFM requires a valid set of channels to slice the spectrum into, plus a low pass filter, designed to prevent aliasing during decimation. See this page for details on designing and creating the configuration for such a filter.
MultiFM supports the following SDRs:
- RTL-SDR
- Airspy (Mini and original)
- USRP (tested with a B210, should work with others)
Each have slightly different parameters, see the documentation below.
MultiFM is started by specifying one or more configuration JSON files on its command line. If, for example, you wanted to use the MultiFM configuration from the examples in etc:
multifm etc/multifm.json etc/flex_25khz_lpf.json
In this case, the configuration is split into two files (multifm.json with the receiver configuration and channel configuration, flex_25khz_lpf.json with the low-pass filter to be used in decimation).
MultiFM takes a filter kernel, applies it to a wideband stream of samples, decimating the results. This section needs to be fleshed out a lot more.
MultiFM outputs PCM samples on multiple output files (specified in the configuration; see below). These samples are 16-bit signed integers, in the endianess of the machine you are running on. The output sample rate is equal to the input sample rate divided by the decimation factor.
As a worked example:
- Input sample rate is 1MHz (1 000 000 Hz)
- Decimation Factor is 40
- Output sample rate would be 25 kHz (25 000 Hz).
The decimation factor must always be an integer value.
The following is the sample configuration we're going to work from.
{
"device" : {
"type" : "rtlsdr",
"deviceIndex" : 0,
"dBGainLNA" : 10.0
},
"sampleRateHz" : 1000000,
"centerFreqHz" : 929500000,
"nrSampBufs" : 16,
"decimationFactor" : 40,
"channels" : [
{
"outFifo" : "/home/pi/ch7.out",
"chanCenterFreq" : 929838000
},
{
"outFifo" : "/home/pi/ch6.out",
"chanCenterFreq" : 929538000
},
{
"outFifo" : "/home/pi/ch5.out",
"chanCenterFreq" : 929388000
},
{
"outFifo" : "/home/pi/ch4.out",
"chanCenterFreq" : 929938000
},
{
"outFifo" : "/home/pi/ch3.out",
"chanCenterFreq" : 929362000
},
{
"outFifo" : "/home/pi/ch2.out",
"chanCenterFreq" : 929662500
},
{
"outFifo" : "/home/pi/ch1.out",
"chanCenterFreq" : 929638000
},
{
"outFifo" : "/home/pi/ch0.out",
"chanCenterFreq" : 929612000
}
]
}The mandatory parameter that always must be present in the device stanza is type. Type can be one of the following:
-
rtlsdrfor a Realtek RTL2832U -
airspyfor an Airspy or Airspy Mini -
usrpfor a USRP of any sort
These are the main configuration options for an Airspy receiver.
If you want to specify a specific device, if multiple Airspy devices are present, specify its serialNo.
Set the gain for the Low-Noise Amplifier of the Airspy. In decibels.
Set the VGA gain for the Airspy. In decibels.
Set the mixer gain for the Airspy. In decibels.
Set to true to enable the Bias Tee, false otherwise.
These are the main configuration options for an RTL-SDR receiver.
This is used by librtlsdr to identify which RTL-SDR device you wish to use. If you have only one RTL-SDR device installed, the index will most likely be 0.
This gain factor is set for the RTL-SDR device's LNA on startup. A list of valid gain factors is printed out at startup. In the event you pick a gain that isn't exactly valid, the next closest gain will be chosen (and the real value is printed to the console).
Note: This only applies to the Elonics E4000 receiver
The amplifier chain before digitization in the Elonics E4000 provides for some amplification of the IF signal. This sets this gain factor. If you do not have an E4000 receiver, this value is ignored. This value is specified in Decibels.
Many of these parameters should only be used by trained professionals (or those who play them on TV).
-
ppmCorrection(integer): The PPM correction coefficient to be applied to the oscillator in the RTL-SDR. -
iqDumpFile(integer): A file to dump the raw, unprocessed samples to, before processing them. Use this for debugging only. -
sdrTestMode(boolean): Specify whether or not the SDR should be put in counter test mode. Not useful unless you know exactly what this does. -
enableDCBlocker(boolean): Enables a DC block filter on each output stream. You typically don't want this. The filter is a simple differentiator + leaky integrator, with noise steering. -
dcBlockerPole(float): The value for the leaky integrator's sole pole.
Don't use these options unless you know exactly what you're doing, they can really adversely affect the functionality of the program.
These are the main configuration options for USRP radios used with MultiFM.
This is the string used to identify (and possibly connect) to a USRP to be used by MultiFM. This string's format is based on the expected identifier format for libuhd - see the official libuhd documentation for more details on how to identify your device.
If the device has multiple channels, this is a numeric value to specify which channel to use. For example, to use channel RF A of a USRP B210, you would specify the value 0.
If your device has more than one antenna port, this option is a string that identifies the specific antenna. For example, to use the RF A TX/RX port on a USRP B210 you would specify this value as "TX/RX".
This stanza contains a list of JSON objects defining the gain for the channel in use. These gains are specified as follows:
-
name- the name of the gain, this maps directly to what UHD expects for a gain name. -
dBValue- the value of the gain, in decibels
The specific names and values of gains for a USRP can be found on the specific device documentation page on the Ettus website.
"gain" : [
{ "name" : "PGA", "dBValue" : 14.4 }
]The sample rate at which the spectrum will be sampled. This represents the bandwidth of the 'broadband' sample of the spectrum. This factor impacts the filter kernel's structure, so tune it carefully (and make sure you update your filter kernel).
This value must be supported by the device. If your device type is airspy, valid sample rates are:
- For the Airspy
- 2500000
- 10000000
- For the Airspy Mini
- 3000000
- 6000000
This is the center frequency the RF tuner is set to. If you selected a 1MHz sample rate, and opted to tune to 929500000 Hz, you will be capturing the RF spectrum from 929.0 MHz to 930.0 MHz.
MultiFM strives to ensure resource utilization is constant, during operation. The sample buffer pool is allocated at startup to receive samples from the RTL-SDR. All samples pass through a buffer allocated from this pool. nrSampBufs allows you to tune the number of sample buffers. This is useful if a system is fast enough to process the input sample stream, but needs a deeper queue to trade-off on latency. For most users, a value of 16 is more than enough.
The decimationFactor is an integer value that determines the sample rate of the narrowband channels extracted from the broadband spectrum. The output channel sample rate is simply:
sampleRateHz
F_out = ----------------
decimationFactor
So for the above example, the 1MHz sample will be sliced into output channels that are 1MHz/40 = 25kHz.
This scale factor needs to be selected based on the bandwidth of the output channels, per the Nyquist criterion. For example, if you were decoding FLEX channels, the FCC allocates these channels in 25kHz chunks. This is the "easiest" number to work with then.
A channels stanza must be present in a MultiFM configuration. Each channel needs to have its center frequency (chanCenterFreq in Hertz) and a destination file (outFifo, but the name is misleading: it can be any file) to write to. A channel also can have a per-channel gain, applied during the filtering stage, specified in decibels with the dBGain parameter.
The center frequency, in hertz, of this channel.
The output FIFO/file to write demodulated samples to.
Gain applied to the filtering stage for the channel. In decibels. Set to 0.0 dB by default.
Sample configuration files for various uses (and SDRs) can be found in the etc/ directory of the source distribution. These configurations attempt to be benign (i.e. not damage your receiver), but before blindly using a configuration please ensure that it does match the hardware setup you're trying to use.
-
multifm.json- simple configuration for receiving multiple channels with an RTL-SDR. -
multifm_1ch.json- simple configuration for receiving a single channel with an RTL-SDR. -
multifm_airspy.json- simple configuration for receiving a single channel with an Airspy Mini. -
multifm_usrp.json- simple configuration for receiving a single channel with a USRP B210. -
flex_25khz_lpf.json- 25kHz wide filter (w/ Hamming window), for 1MHz sample rate (RTL-SDR and others) -
flex_25khz_lpf_3mhz.json- 25kHz wide filter (w/ Hamming window), for 3MHz sample rate (Airspy Mini) -
resampler_filter.json- filter for resampling a 25kHz channel to a 16kHz channel (for use withdecoder).
Using the above configuration (which comes from etc/multifm.json) and the provided sample low-pass filter (etc/flex_25khz_lpf.json), we can channelize the RF spectrum, with one output per FLEX channel.
To do this:
-
Create the output fifos (in this case
/home/pi/ch[0-7].out). This is done using themkfifo(1)command. This typically only needs to be done the first time you're running the application, but FIFOs can be put in ephemeral filesystems (liketempfs) too. So something as simple as:mkfifo /home/pi/ch0.out /home/pi/ch1.out /home/pi/ch2.out /home/pi/ch3.out /home/pi/ch4.out /home/pi/ch5.out /home/pi/ch6.out /home/pi/ch7.outwill get you where you want to be.
-
Start MultiFM.
multifm etc/multifm.json etc/flex_25khz_lpf.jsonMultiFM will now wait for processes to connect to the FIFOs.
-
Start Decoder. For each channel you wish to decode (in this case, all channels specified in
etc/multifm.json), you'll need to specify a command line like:decoder -D 25 -I 16 -F etc/resampler_filter.json -S 25000 -f 929612500 /home/pi/ch0.outFor more information on the command line arguments for decoder, see how to use Decoder.
In this example, the Decoder instances will just dump their messages to stdout. Error messages and warnings are dumped to stderr. Doing anything further with the messages is an exercise for the reader.
A filter kernel is simply a JSON file specifying the taps and parameters for a filter. All filters used in MultiFM are FIRs.
This is an array of real-valued taps for the low-pass filter. These taps could be generated using GNUradio, MATLAB or other tools that can output to JSON (or text that can be reformatted). See this page for more information on designing these low-pass filters.
The sample FLEX filter kernel was designed using the DSP Toolbox in MATLAB. TODO: provide a tutorial on how to use the DSP Toolbox filter designer.
TSL-SDR is an open source project to provide easy-to-use tools for building production software defined radio workflows.