Skip to content

Using MultiFM

Phil Vachon edited this page Apr 7, 2019 · 16 revisions

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.

Supported SDR Receivers

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.

Starting MultiFM

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).

Theory of Operation

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.

Output Format

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.

Annotated Example Configuration

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
    }
  ]
}

device Stanza

The mandatory parameter that always must be present in the device stanza is type. Type can be one of the following:

  • rtlsdr for a Realtek RTL2832U
  • airspy for an Airspy or Airspy Mini
  • usrp for a USRP of any sort

Configuration options for Airspy

These are the main configuration options for an Airspy receiver.

serialNo - Device Serial Number

If you want to specify a specific device, if multiple Airspy devices are present, specify its serialNo.

lnaGain - Low-Noise Amplifier (pre-IF) Gain (float)

Set the gain for the Low-Noise Amplifier of the Airspy. In decibels.

vgaGain - Variable Gain Amplifier (at-IF) Gain (float)

Set the VGA gain for the Airspy. In decibels.

mixerGain - Mixer Gain (float)

Set the mixer gain for the Airspy. In decibels.

enableBiasTee - Enable the Bias Tee (boolean)

Set to true to enable the Bias Tee, false otherwise.

Configuration options for an RTL-SDR

These are the main configuration options for an RTL-SDR receiver.

deviceIndex - the Device ID

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.

dBGainLNA - LNA gain, in decibels

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).

dBGainIF - Intermediate Frequency signal gain

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.

Additional RTL-SDR Parameters

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.

Configuration options for USRP

These are the main configuration options for USRP radios used with MultiFM.

deviceId - UHD Device Identifier String

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.

channelId - Device Channel ID

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.

antenna - Device Antenna ID

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".

gain - Stanza Containing array of Gains

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.

Example Gain Specification:
"gain" : [
  { "name" : "PGA", "dBValue" : 14.4 }
]

sampleRateHz - The Sample Rate, in Hertz

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

centerFreqHz - The RF Tuner Center Frequency in Hertz

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.

nrSampBufs - The Number of Sample Buffers

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.

decimationFactor - Factor to Decimate Broadband spectrum for Narrowband Channels

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.

The channels Stanza

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.

chanCenterFreq - Center frequency of the channel (integer)

The center frequency, in hertz, of this channel.

outFifo - Output demodulated samples target file (string)

The output FIFO/file to write demodulated samples to.

dBGain - Filter gain, in decibels (float)

Gain applied to the filtering stage for the channel. In decibels. Set to 0.0 dB by default.

Sample Configurations

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 with decoder).

An Example Use Case with Decoder

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:

  1. Create the output fifos (in this case /home/pi/ch[0-7].out). This is done using the mkfifo(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 (like tempfs) 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.out
    

    will get you where you want to be.

  2. Start MultiFM.

    multifm etc/multifm.json etc/flex_25khz_lpf.json
    

    MultiFM will now wait for processes to connect to the FIFOs.

  3. 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.out
    

    For 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.

Creating a Filter Kernel

A filter kernel is simply a JSON file specifying the taps and parameters for a filter. All filters used in MultiFM are FIRs.

lpfTaps - Array of Filter Taps

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.

Sample Kernels

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.

Clone this wiki locally