Hacking, HAM Radio (EA1IYR), DSP, physics and more
As the latest SigDigger 0.2 release included lots of new features that are difficult to summarize in GitHub’s release notes, I will write a series of posts about typical use cases for this features. It will not have a regular schedule (I am still writing down my master thesis, plus other duties far from my computer).
This first post will be dedicated to suscli, a command-line front-end for Suscan that does not depend on a graphical environment.
Suscli was born out of necessity during the C Band experiments in 4 GHz I perfomed last saturday, with the aim of receiving the synchrotron radiation of Cygnus A. The project has some increased difficulty as the synchrotron emission of Cygnus A is incoherent and therefore I can only rely on the amplitude of the signal. I needed an application that, departing from an already defined source profile, opened a channel and measured the root mean squared value (RMS) of the noise, which should proportional to the band-limited noise + additional contributions.
The result was suscli. suscli is a command line application with multiple subcommands that accept parameters in the form of key=value. You generally invoke suscli like this:
% suscli subcommand param1=value1 param2=value2 ...
The number of subcommands is not fixed and it is expected to grow in time. This post is simply a comprehensive description of each subcommand.
One of the first things you may want to do is to list the available source profiles in your configuration directory. This is something you can do with suscli profiles
:
% suscli profiles
(potential debug messages from SoapySDR drivers)
[ 1] "Default source"
[ 2] "C-Band experiments"
[ 3] "HF with AirSpy"
...
Profiles are enumerated by order of creation and have a name. Suscli encloses the name with double quotes just in case a profile with an empty name exists. Other subcommands accept profiles as a parameter that can be either specified by an index number (as displayed by suscli profiles
) or their name. This subcommand tells you which is which.
NB: there is a profile that is not listed as it is not in the sources.xml
file in Suscan’s configuration directory. This is the UI profile (the one loaded by SigDigger at startup) and has always index 0. It is usually the default profile when no profile index is provided.
Other interesting subcommand from the perspective of profile management is suscli profinfo
, which prints a brief summary of a source profile. It accepts an optional parameter (profile
) which expects a profile index or a profile name (both work).
Subcommands that interact with devices need to probe them at startup. Depending on the underlying SoapySDR driver, they may produce quite verbose debug messages that can be mixed with the true command’s output. Fortunately, most of these messages are printed to the standard error output, and can be suppressed with 2> /dev/null
.
If no profile is specified, it defaults to 0 (the UI profile):
% suscli profinfo 2> /dev/null
linux; GNU C++ version 6.2.0 20161103; Boost_106200; UHD_003.009.005-0-unknown
Profile: "UI profile"
----------------------------
Frequency: 179100000 Hz
LNB: 0 Hz
Sample rate: 250000 sps
Decimation: 1
Type: file
Format: Automatic
Path: /home/waldo/Documents/Radio/Examples/gmsk-250ksps.raw
Loop: yes
% suscli profinfo profile=4 2> /dev/null
linux; GNU C++ version 6.2.0 20161103; Boost_106200; UHD_003.009.005-0-unknown
Profile: "lime (Remote [TCP])"
----------------------------
Frequency: 433000000 Hz
LNB: 0 Hz
Sample rate: 100000 sps
Decimation: 1
Type: real-time
Device: lime (Remote [TCP])
Channel: 0
Bandwidth: 100000 Hz
Antenna: (none)
I/Q Balance: no
Gains:
And, if we want to know how many devices were deteced by Suscan and whether they are available or not for reading, we can use suscli devices
:
% suscli devices 2> /dev/null
linux; GNU C++ version 6.2.0 20161103; Boost_106200; UHD_003.009.005-0-unknown
ndx Device name Driver Interface Availability
------------------------------------------------------------------------------
[ 0] Dummy device null local available
[ 1] Audio input (hw:HDA Intel PCH,0) audio local unavailable
[ 2] Audio input (default) audio local available
[ 3] lime (Remote [TCP]) lime local available
[ 4] Audio input (hw:HDA Intel PCH,0) audio local available
[ 5] airspy (AIRSPY [26a464dc:28621d93]) airspy local unavailable
Devices listed as unavailable
are devices that were found in existing profiles, but may not be currently plugged to the computer.
Column Interface
tells whether the device refers to some hardware device plugged to the host computer, or whether it is accessed remotely by a network interface. By default, only local devices are listed, but this behavior can be adjusted by setting environment variables. More on this below.
Sometimes, suscli
is installed in a headless computer to run as a device server. In these cases, we need to create profiles by hand for each device we want to expose. Most of this work can be done automatically by suscli makeprof
, which creates default profiles from detected devices.
suscli makeprof
accepts a variety of parameters:
Parameter | Type | Description | Default |
---|---|---|---|
prefix |
String | String prefix to prepend to profile names | (Empty string) |
device |
Integer | Device index (as in suscli devices ) |
-1 |
ask |
Boolean | Ask before creating multiple profiles | True |
freq |
Double | Profile frequency (in Hz) | 433000000 |
unavailable |
Boolean | Create profiles for unavailable devices too | False |
If no device index is provided, a profile will be created for all available devices (or, if unavailable
is passed, for all devices regardless of their availability). It is in general a good idea to run suscli devices
to be sure that all devices are plugged and properly detected before running suscli makeprof
.
The created profiles (all in ~/.suscan/config/sources.xml
) can be manually adjusted according to the user requirements with a simple text editor.
The first truly useful subcommand is suscli radio
, which allows you to listen to analog radio from the command line, exactly as you would do from SigDigger’s audio preview. The following parameters control the behavior of suscli radio
:
Parameter | Type | Description | Default |
---|---|---|---|
profile |
Integer / String | Source profile index or name | 0 |
demod |
String | Audio demodulator (fm , am , lsb , usb ) |
fm |
volume |
Float | Volume (in dB, 0 is 100%) | 0 |
frequency |
Double | Channel frequency (in Hz) | Profile’s center frequency |
samp_rate |
Integer | Audio sample rate (in Hz) | 44100 |
cutoff |
Float | Audio low pass filter cut-off frequency (Hz) | Half of audio sample rate |
squelch |
Boolean | Enable squelch | False |
squelch_level |
Float | Squelch level (RMS, linear) | 0.5 |
buffering_ms |
Integer | Buffering audio (in milliseconds) | 100 |
Upon successful execution, suscli
radio starts demodulating audio to the default audio device and prints a brief summay of the current parameters:
% suscli radio frequency=97900000 volume=-10 2> /dev/null
linux; GNU C++ version 6.2.0 20161103; Boost_106200; UHD_003.009.005-0-unknown
Demodulator summary:
Profile: UI profile
Device: Dummy device
Frequency: 97.900000 MHz
Demodulator: FM
Cutoff: 22.050 kHz
Squelch: No
Squelch level: 0.5
Sample rate: 44100 sp/s
Volume: -10 dB
Additionally, the user can switch frequencies and demodulators interactively with the keyboard:
The original purpose for suscli
was to perform power measurements (in arbitrary power units) and store them to a file or deliver them through the network. This is implemented by suscli rms
.
As I progressed in my experiments, I discovered I needed to augment the power-measuring features of suscli
to perform real-time power measurements without having to look continuously to a screen that could be meters away. That’s why it also features an audio output that beeps in different ways according to the measured signal power.
Regardless of the output type, suscli rms
accepts the following parameters:
Parameter | Type | Description | Default |
---|---|---|---|
profile |
Integer / String | Source profile index or name | 0 |
rms_interval |
Float | RMS measurement interval (in milliseconds) | 50 |
disp_interval |
Float | Measure display interval (in milliseconds) | 500 |
audio |
Boolean | Enable beeper (audio output) | False |
tcp |
Boolean | Forward TCP measures via TCP | False |
matlab |
Boolean | Store measures to a Matlab’s .mat file | False |
suscli rms
actually calculatesIn any case, suscli rms
will run an analyzer on the specified source profile and open a channel centered in a frequency ($f_0$) that depends on the sample rate setting of the source ($f_s$) and the tuner (device) frequency $f_t$ as:
This is, the channel is opened on a center frequency that is 1/12 the sample rate below the device frequency (the one you configure in SigDigger’s frequency LCD display).
The channel bandwidth is chosen so that it does not overlap the DC component of the radio. As the maximum bandwidth of such channel (assuming that is $f_s/12$ Hz away from the DC) is $f_s/6$, I chose a slightly smaller bandwidth, $f_s/6.3$.
The resulting channel has much smaller sample rate, let’s say $f_s’\ll f_s$. This channel is then further filtered to match the exact bandwidth demanded by suscli rms
. Then, it reads the source in batches of $N$ samples, where $N$ is derived from the RMS interval (in milliseconds) by:
And, for each sample batch, computes an approximation of the channel power by:
\[P=\frac{1}{N}\sum_{i=1}^{N}x[n]x^*[n]\]With $x^*[n]$ the complex conjugate of $x[n]$. Note that $x[n]x^*[n]=\lVert x[n]\rVert^2$. Since power is measured after every $N$ samples, a channel with a sample rate of $f’_s$ will produce power measurements at a rate of $f’_s/N$.
In the simplest case, we just want to print RMS measures in the screen. We just run:
% suscli rms profile=0
linux; GNU C++ version 6.2.0 20161103; Boost_106200; UHD_003.009.005-0-unknown
(lots of debug messages from device libraries)
Tone generator parameter summary:
Profile: UI profile
RMS update interval: 50 ms
Display interval: 500 ms
Audio: OFF
Inspector opened!
Inspector ID: 0x00000000
Request ID: 0x000c1009
Handle: 0x00000000
EquivFS: 125000 sps
Ft: 179100000 Hz
BW: 79376.2 Hz
LO: -41666.7 Hz
[1626619312.732329] RMS = -92.458 dB
If we want to store data to a file, we must specify mat5
in the parameter list. This parameter tells suscli rms
to store all measurements (at the rate they are generated!) to a binary Matlab’s Mat5 file:
% suscli rms mat5
This Mat5 file can be loaded from Matlab or Octave and contains a series of arrays with the contents of the measurements:
% octave
GNU Octave, version 5.2.0
Copyright (C) 2020 John W. Eaton and others.
This is free software; see the source code for copying conditions.
There is ABSOLUTELY NO WARRANTY; not even for MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. For details, type 'warranty'.
Octave was configured for "x86_64-pc-linux-gnu".
Additional information about Octave is available at https://www.octave.org.
Please contribute if you find this software useful.
For more information, visit https://www.octave.org/get-involved.html
Read https://www.octave.org/bugs.html to learn how to submit bug reports.
For information about changes from previous versions, type 'news'.
octave:1> load('capture_20210718_145111.mat')
octave:2> whos
Variables in the current scope:
Attr Name Size Bytes Class
==== ==== ==== ===== =====
X 4x98 1568 single
XT0 1x1 4 single
Total is 393 elements using 1572 bytes
By default, suscli rms
names capture files as capture_DATE_UTCTIME.mat
, easing data classifiction. A scalar and a float matrix are generated. XT0
contains the Unix timestamp (in seconds) of the first measurement. X
is a $4\times M$ matrix that contains data of the $M$ power measurements:
XT0
.XT0
.suscli rms mat5
also accepts an optional string parameter (mat5-path
) that can be used to specify different names and locations for the output file.suscli rms
can also forward measures via IP to certain consumer. This is done by specifying tcp
in the parameter list. Additionally, you have to specify:
Parameter | Type | Description | Default |
---|---|---|---|
tcp-host |
String | Destination host | localhost |
tcp-port |
Integer | Destination port | 9999 |
tcp-desc |
String | Optional description string | suscli@client IP (PID) |
Data is delivered in plain text to ease its exploitation by scripting languages. The TCP stream consists of message lines delimited by a regular line break (\n
). Currently, only 3 messge types exist:
RATE,XXXXX
: Notifies the consumer that measures will be received at a rate of XXXXX times per second.DESC,YYYYY
: Notifies the consumer the description string (tcp-desc) of the suscli
instance performing the measurements.INT_T0,DEC_T0,POWER_LIN,POWER_DB
: Notifies the consumer about a power measure taken at Unix time INT_T0
+ DEC_T0
(where INT_T0
is the integer part of the timestamp in seconds) with the value of POWER_LIN
(which equals to POWER_DB
dB).The most typical consumer is RMSViewer, a subtool of SigDigger that can be spawned with:
% SigDigger -t RMSViewer
RMSViewer is a basic graphical tool that opens a TCP server and expects connections by suscli rms
. Once a connection is opened and data is received, measures are plotted in an interactive window in real time:
The beeper feature translates power measurements to different combinations of tones played in the default soundcard. This feature can be enbled by passing audio
to the parameter list.
In any case, we need to known the dB range we want to map. We can do that experimentally by running suscli rms
, reading the different measures and setting db_min
and db_max
accordingly. Less critically, we may want to adjust other parameters:
Parameter | Type | Description | Default |
---|---|---|---|
mode |
String | Beeper mode (tone , 2tones or beeper ) |
tone |
volume |
Float | Tone volume, as a percentage | 12.5 |
db_min |
Float | Lower bound of the power range, in dB | -70 |
db_max |
Float | Upper bound of the power range, in dB | -10 |
freq_min |
Float | Lower bound of the audio freq. range, in Hz | 220 |
freq_max |
Float | Upper bound of the audio freq. range, in Hz | 1760 |
beep_short |
Float | Shortest tone duration in milliseconds | 1000 |
beep_long |
Float | Tone spacing in milliseconds | 1000 |
scale |
Float | Scale dB measures by a given factor | 1 |
Currently, three tone configurations are accepted:
tone
(default): maps a power range (in dB) to a continuously exponentially-varying tone frequency2tones
: same as tone
, but switches it periodically with the tone corresponding to the minimum power.beeper
: plays a series of fixed-frequency tone pulses, more and more rapidly as measured power increases (tone frequency given by freq_max
).A typical beeper invocation would look like this:
% suscli rms audio db_min=-90 db_max=-80 mode=beeper profile=2
Finally, the subcommand suscli devserv
will spawn the device server, which is used by the remote analyzer feature. The device server publishes profiles in the network via multicast that can be later detected and used by SigDigger. When SigDigger uses one of these profiles, offloads all analysis features to the device server, enabling headless operation of SDR devices at a (generally) low data rate.
This feature is stil in experimental phase, many aspects of it are not complete (like proper protocol security) and can change in any later version. For that reason, automatic device discovery is disabled by default, although can be enabled from the command line by setting an environment variable. However, this feature deserves a post on its own, which I plan to publish soon.
Stay tuned!