8sync Examples Part 1

I want to play around with the 8sync framework, but the project is unfortunately lacking an API reference. So, I am writing example code to help me learn each of the modules, and thought I might as well share. Here is some example code for the (8sync agenda) module.

(define-module (8sync agenda examples))

(use-modules (8sync agenda)
             (srfi srfi-26)
             (srfi srfi-31))

(export make-stuff)

(define (make-something thing amt)
  ((rec (l n)
        (when (positive? n)
          (display "Making ") (display thing) (display "\n")
          (8yield)
          (l (- n 1)))) amt))

(define (make-stuff)
  (define my-agenda
    (make-agenda
     #:queue (make-q* (cut make-something "widget" 10)
                      (cut make-something "thingamabob" 5)
                      (cut make-something "sprocket" 5))))
  (run-agenda my-agenda))

This illustrates how an Agenda can be used to interweave the execution of computations, by yielding to other computations during your loop:

scheme@(guile-user)> ,use (8sync agenda examples)
scheme@(guile-user)> (make-stuff)
Making widget
Making thingamabob
Making sprocket
Making widget
Making thingamabob
Making sprocket
Making widget
Making thingamabob
Making sprocket
Making widget
Making thingamabob
Making sprocket
Making widget
Making thingamabob
Making sprocket
Making widget
Making widget
Making widget
Making widget
Making widget
$2 = done

HackRF Shell: (Learning) Delays

I wanted to move on quickly from completing the FFT interface, on to FM demodulation. However, I realized that if the rest of the project was going to practical and modular, I would need some kind of block style I/O buffer management system, like GnuRadio has. And as I started to pursue that, I realized that I would need to properly modularize my code.

Up until now, all the functions were either being dumped into the default module by the C code, or loaded from one Scheme file in the source directory. I took all the external C code and organized it in sensible module divisions, and then took all the C-based Scheme primitives and put them in two modules ((sdr primitives) and (sdr hackrf primitives)). Those two modules are created and loaded when HackRF Shell is launched, while the other modules can be compiled independently.

That all required taking the time to actually learn how the module system works, as well as understanding how to create and fill-out modules through the C interfaces. So, not much amazing progress to the outside observer, but I learned a lot. The FFT demo function is still working:

One neat thing about programming in Guile is that you can jump into various modules at any time, to play around in them or see what variables are defined in them (private and public):

christopher@nightshade:~/Repos/hackrf-shell$ ./hackrf-shell 
HackRF Shell, copyright 2019 Christopher Howard

Initializing primitive functions.
Entering the Scheme shell.

GNU Guile 2.2.3
Copyright (C) 1995-2017 Free Software Foundation, Inc.

Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'.
This program is free software, and you are welcome to redistribute it
under certain conditions; type `,show c' for details.

Enter `,help' for help.
scheme@(guile-user)> (set! %load-path (cons "/home/christopher.qlfiles.net/Repos/hackrf-shell" %load-path))
scheme@(guile-user)> ,m (sdr hackrf util)
scheme@(sdr hackrf util)> ,b
hackrf-sensible-defaults #<variable 56314e6aaac0 value: #<procedure hackrf-sensible-defaults (d)>>
filename                #<variable 56314e6aa940 value: #<undefined>>
timed-read              #<variable 56314e6aa8a0 value: #<procedure timed-read (d file-path start-h start-m end-h end-m)>>
hackrf-cb-rx-to-file    #<variable 56314e6aa990 value: #<procedure hackrf-cb-rx-to-file (filename)>>
out                     #<variable 56314e6aa9e0 value: #<undefined>>
when-hour-min           #<variable 56314e6bd660 value: #<procedure when-hour-min (h m l)>>
hackrf-cb-rx-to-stream  #<variable 56314e6aaa30 value: #<procedure hackrf-cb-rx-to-stream (out)>>
cb-fft-to-gnuplot       #<variable 56314e6aa8f0 value: #<procedure cb-fft-to-gnuplot (center-freq samp-rate averaging-alpha)>>
scheme@(sdr hackrf util)> hackrf-
hackrf-cb-rx-to-file                  hackrf-open                           hackrf-set-sample-rate
hackrf-cb-rx-to-stream                hackrf-sensible-defaults              hackrf-set-vga-gain
hackrf-close                          hackrf-set-baseband-filter-bandwidth  hackrf-start-rx
hackrf-disable-amp                    hackrf-set-freq                       hackrf-stop-rx
hackrf-enable-amp                     hackrf-set-lna-gain                   
scheme@(sdr hackrf util)> ,m (sdr util)
scheme@(sdr util)> ,b
bv-complex-to-mag       #<variable 56314e6b4700 value: #<procedure bv-complex-to-mag (complex-float-bv n_complex_nums dest-bv)>>
freq-shifter            #<variable 56314e6b4670 value: #<procedure freq-shifter (freq samprate)>>

The next step in my project will be to work out my block-style I/O buffer management system, which will require me to re-learn GOOPS, Guile’s object oriented programming extension, so that will likely still take a while.

git clone git://git.librehacker.com/pub/git/hackrf-rkt.git

HackRF Shell: FFT Averaging and Display Progress

I’m not quite sure I have all the bugs worked out yet, but I implemented simple averaging, as well and the frequency display. The latter required implementing fft-shift to move center frequency to the center. The above plot shows FM stations at 101.1, 102.5, 103.9, and 104.7 Mhz, matching local stations I know about.

These simple things took quite a while, mainly because of the busyness of other life responsibilities; but also because the process of implementation led to fixing some bugs, which led to cleaning up some code, which led to learning new things about Guile scheme and C.

In Guile scheme, I learned how to implement a procedure to auto-close unreachable ports:

(define ports-guardian #f)

(define (close-guarded-ports)
  (let f ()
    (let ((p (ports-guardian)))
      (when p (begin (close-port p) (f))))))

(define (initialize-ports-guardian)
  (when (not ports-guardian)
    (set! ports-guardian (make-guardian))
    (add-hook! after-gc-hook (lambda () (close-guarded-ports)))))

(initialize-ports-guardian)

(define (register-autoclose p) (ports-guardian p) p)

register-autoclose can simply be wrapped around the port, e.g.:

(define (hackrf-cb-rx-to-file filename)
  (hackrf-cb-rx-to-stream
   (register-autoclose (open-output-file filename))))

This uses Guile scheme “guardian” functionality, which is an object that can return registered objects if the have become unreachable everywhere else.

git clone git://git.librehacker.com/pub/git/hackrf-rkt.git

HackRF Shell: FFT Plot Progress

I threw together a scheme procedure which dumps FFT data to Gnuplot.

You see, however, that I have more work to do as properly labeling the graph, as well as implementing averaging for a more practical spectrum display. The 512 points on the x-axis correspond to the 512 complex numbers in the FFT output buffer, but center frequency in this case is actually 103Mhz, at 8 msps.

scheme@(guile-user)> (define d (hackrf-open))
scheme@(guile-user)> (hackrf-sensible-defaults d)
scheme@(guile-user)> (hackrf-set-freq d 103000000)
scheme@(guile-user)> (hackrf-set-baseband-filter-bandwidth d 2000000)
scheme@(guile-user)> (hackrf-enable-amp d)
scheme@(guile-user)> (define c (cb-fft-to-gnuplot))
scheme@(guile-user)> (hackrf-start-rx d c)
scheme@(guile-user)> (hackrf-stop-rx d)

It was mostly easy to write the scheme -> Gnuplot interface as that is just sending text commands to Gnuplot standard input. However, it took me a while to fix an elusive bug in my C FFT function.

HackRF Shell: RX Restart Bug

I spent quite a while troubleshooting a bug in which RX would mysteriously not restart, if you did a hackrf-stop-rx followed by another hackrf-start-rx. The problem actually was not in my code, but due to some old libhackrf bugs that had not been patched in the old Debian 9 version of libhackrf which I am using.

This is a serious enough annoyance that you won’t want to be using HackRF Shell with the unpatched version. So, I added instructions to my git repo (debian9-libhackrf-patch directory, see commit 346c50e) on how to get a patched version of the Debian 9 packages. I think that the Debian 10 library version is actually not new enough to avoid all the bugs, either, so the info I have provided might be of value to Debian 10 users as well.

I would like to switch my home system from Debian to Gnu Guix, and use Guix package management for development, but I’m not sure how soon that will happen.

git clone git://git.librehacker.com/pub/git/hackrf-rkt.git

HackRF Shell: Progress Update

I added in the baseband-filter-bandwidth control procedure, which is something I forgot to do earlier. This was critical for picking up the weaker stations, such as KJNP 100.3Mhz, which is around 20 or 30 miles away, I think. I coded some simple helper functions (in Scheme) to start and stop receiving data according to time parameters, which I will use to record my favorite radio program each morning. This example records data for one minute from 8:43pm to 8:44pm (code checkout a992f67).

scheme@(guile-user)> (define d (hackrf-open))
scheme@(guile-user)> (load "hackrf-shell-lib.scm")
scheme@(guile-user)> (hackrf-sensible-defaults d)
scheme@(guile-user)> (hackrf-set-baseband-filter-bandwidth d 2000000)
scheme@(guile-user)> (hackrf-enable-amp d)
scheme@(guile-user)> (timed-read d "out.bin" 20 43 20 44)

This still just dumps the floating point signal data to a file, rather that doing any demodulation, so the file size is very large, and I must feed it into GnuRadio. Yet, it is progress.

I need to go over the RX start/stop code again as I get an error if I try to start RX again after stopping it. I coded that part of the device management rather quickly so I am not surprised.

I started playing around with merging in FFT functionality. I added an fft-512 procedure which does FFT on a 512 byte buffer using libfftwf. I think it works, but I haven’t added any procedures yet to do anything useful with fft-512 so I don’t really know yet. I was going to code something which feeds data to GnuPlot for a spectrum analysis display, in the usual fashion like all the SDR software does:

I have been learning a lot lately about Fourier transform and DFT (Discrete Fourier Transform) and I think I have a mostly clear understanding of the basic math and concepts involved now. For fun, I did a DFT operation manually in Emacs Calc on a length 8 data sample, and the results came out making sense. This article is a nice introduction to the Fourier transform, though you need to have a good understanding of complex numbers to fully grasp the DFT equation:

An Interactive Guide to the Fourier Transform

HackRF Shell: Conversion to Floating Point

As expected, the bottle neck has disappeared at the floating point conversion. At least, I can say that I didn’t have trouble pulling 8 million samples per second (msps), which with 32 bit floating point (and 2 floating point numbers per sample) is 64 MB/sec. (I am short on time this evening, so I haven’t had a chance yet to try 20 msps.) As before, I fed the data into a Gnu Radio FM demodulator and got clean FM radio station audio out of it.

GCC 6.3 with -O3 appears to do some SSE optimization on the byte buffer to float buffer conversion:

christopher@nightshade:~/Repos/hackrf-shell$ objdump hackrf-shell -x -D | less
<snip>
    172c:       66 0f 6f c1             movdqa %xmm1,%xmm0
    1730:       66 0f 68 cc             punpckhbw %xmm4,%xmm1
    1734:       66 0f 60 c4             punpcklbw %xmm4,%xmm0
    1738:       66 0f 6f f1             movdqa %xmm1,%xmm6
    173c:       66 0f 65 e8             pcmpgtw %xmm0,%xmm5
    1740:       66 0f 6f f8             movdqa %xmm0,%xmm7
    1744:       66 0f 61 fd             punpcklwd %xmm5,%xmm7
    1748:       66 0f 69 c5             punpckhwd %xmm5,%xmm0
    174c:       66 0f 6f eb             movdqa %xmm3,%xmm5
    1750:       0f 5b ff                cvtdq2ps %xmm7,%xmm7
    1753:       66 0f 65 e9             pcmpgtw %xmm1,%xmm5
    1757:       0f 17 3c 24             movhps %xmm7,(%rsp)
<snip>

The next thing, perhaps, should be to create a little demo program where it captures the data at certain times of day. Or I could work on the next stages of an FM receiver, i.e., frequency multiplication, a low pass filter, and the FM demodulator.