Recording a Band Waterfall with an SDR Receiver

3 min read

Archive post.

Translated from Ukrainian.

It would be nice to record the waterfall history over a day or a week so I can analyze it later. There’s a great little program for this, rtl_power by Kyle Keen. But there’s a catch: rtl_power needs its own SDR receiver. This is where OpenWebRX comes to the rescue. We just open another TCP connection with NetCat and pull the raw data stream for processing.

We’ll process the data with csdr again, which has special options for FFT processing.

csdr fft_cc <fft_size> <out_of_every_n_samples> 
csdr logaveragepower_cf <add_db> <fft_size> <avgnumber>
csdr fft_exchange_sides_ff <fft_size>

At first I used fft_size = 4096, which produced 4096px X 44000px images that were practically impossible to open in a browser. Here’s a 30-minute waterfall with a value of 4096, and the image weighs 3.2Mb. So for now I’ve settled on 1024. The number has to be a power of two. The out_of_every_n_samples parameter sets how many samples are skipped before the next output. I don’t fully understand how to calculate it properly, but with fft_cc 1024 109226 the image resolution comes out to 1.4khz and 2 seconds per pixel. So 5 vertical pixels equal 10 seconds, which I think is enough for an overview, and the file doesn’t get too long.

In the end I came up with this command, which outputs decibel values every 2 seconds, each one corresponding to one pixel.

nc -v 127.0.0.1 4951 | csdr convert_u8_f | csdr fft_cc 1024 109226 | csdr logaveragepower_cf -80 1024 40 | csdr fft_exchange_sides_ff 1024 | csdr dump_f

I could have written a program that builds the waterfall and so on from this data, but I decided to convert the data on the fly into the rtl_power format so I could use existing renderers:

I won’t go into detail on how to install the Go toolchain. There’s plenty of that on the internet.

Here’s a Go program that takes values from csdr through a pipe and converts them to the rtl_power format. Let’s call the file csdr2heatmap.go

package main

import (
    "bufio"
    "fmt"
    "os"
    "time"
)

func main() {
    startFreq := 144000000
    endFreq := 146000000
    samples := 1024

    buf := make([]string, 0, samples)
    scanner := bufio.NewScanner(os.Stdin)
    scanner.Split(bufio.ScanWords)

    count := 0
    for scanner.Scan() {
        count++
        value := scanner.Text()
        buf = append(buf, value)
        if count%samples == 0 {
            fmt.Print(time.Now().Format("2006-01-02, 15:04:05, "))
            fmt.Printf("%d, %d, %d, %d, ", startFreq, endFreq, samples, samples)
            for i := range buf {
                fmt.Print(buf[i])
                if i < samples-1 {
                    fmt.Print(", ")
                }
            }
            fmt.Println("")
            buf = buf[:0]
        }
    }

}

Compile it: go build csdr2heatmap.go. Then start recording the data to a csv file:

nc -v 127.0.0.1 4951 | csdr convert_u8_f | csdr fft_cc 1024 109226 | csdr logaveragepower_cf -80 1024 40 | csdr fft_exchange_sides_ff 1024 | csdr dump_f | ./csdr2heatmap  >> /tmp/data.csv

We build the PNG file with rtl-gopow. Download the file for your operating system. On Linux I put the program in my home directory at ~/bin/gopow.

I use these parameters:

/home/ruslan/bin/gopow -i /tmp/data.csv --min-power "-67.0"  -f "png" -o /var/www/waterfalls/today.png --verbose

And to redraw the image periodically we use crontab:

*/10 * * * *  /home/ruslan/bin/gopow -i /tmp/data.csv --min-power "-65.0"  -f "png" -o /var/www/waterfalls/today.png

You can see the result here:

Keep in mind that with these parameters the /tmp/data.csv file grows to 400 MB. It needs to be cleared at midnight, for example by adding this line to crontab:

0 0 * * *    echo '' > /tmp/data.csv

You can also set up data rotation and keep history for previous days with logrotate.

RadioSDRScripts and automation