HackRF Stream: Update 6

I’m referring to the project as HackRF Stream rather than HackRF Racket Bindings because one could have any programming language tie into the stream interface.

It proved that having the data for all RX streams come through one pipe, and having the notifications go to the control stream, created too many complications. So I re-did the startrx and stoprx code to shunt each RX stream to a separate fifo pipe. Before each chunk of RX data sent to the pipe, I send a newline terminated string to the pipe, which indicates the amount of bytes following.

I needed a hashmap (a.k.a., associate array) to keep track of the streams internally. So, I added glib as a dependency, which has the well documented g_hash_table functions.

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

HackRF Racket Bindings: Update 5

The current approach is working out well so far. There is a “hackrf-kernel” C program which receives commands over a control input stream (pipe file) and sends responses back to a control output stream. Data from RX is sent to the data output stream, after calling the startrx command. (The starttx command is not yet implemented.) Since hackrf supports multiple devices streaming simultaneously, there was a question of how to handle the multiple data streams. Rather than having separate pipes for each device, I opted instead to stick with one output pipe, and just have hackrf-kernel send a notification to control output, explaining how many bytes where coming down the pipe, and which device it is for. I used semaphores to ensure that the order of the incoming RX data matches the order of the control notifications. So the external program must simply pull the correct number of bytes from the stream.

A benefit of this approach is that the stream interface is language agnostic, so that I could use it with a Racket program, and somebody else could use it with a C program, or Python program, or anything else that supports reading and writing from file streams. Since I have not written any of the Racket code, for testing I have just been connecting to the pipes with the cat command and redirection arrows. I successfully downloaded about 500MB of RX data this way in a few seconds before sending the stoprx command.

So, I can do RX now, but the hackrf-kernel is not quite useful yet: I need to implement the commands for set the frequency, sample rate, etc.

HackRF Racket Bindings: Update 4

I had too much trouble, unfortunately, trying to bind the libhackrf functions directly to Racket functions with the FFI interface. The two hold ups were (1) I discovered I wasn’t actually supposed to be doing I/O inside a callout function using async-apply, and was having trouble trying to set up some kind of thunk queuing system instead; (2) for some reason the hackrf_stop_rx function would always hang when called from within Racket. On the second problem, I didn’t really have any way to debug the issue, but it looked liked pthread_join was not finishing from inside hackrf_stop_rx, perhaps something to do with the async-apply mechanics.

Feeling like I didn’t have way to move forward, because I don’t know how to debug C functions called from Rcket, I instead decided to back up and change my approach. Instead, I’m just going to use a separate small C program “kernel” to call the libhackrf functions, and communicate with that over named pipes. C programming is a lot of work, but I’ve gotten as far as writing the code in the kernel that creates the pipes and pulls a line from the input control pipe. There are four pipes: control input, control output, data input, and data output.

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

Hackrf Racket Bindings: Update 3

tl;dr: I can set radio parameters and also transfer RX data to a file.

I created many of the bindings for setting radio parameters, such as setting the center frequency and the sample rate. Those bindings were easy.

Figuring out the binding for the hackrf_start_rx function, and figuring out how to make use of it, was difficult and took a lot of time. This is because internally hackrf_start_rx utilizes pthreads, which apparently does not play well with the Racket environment unless you pass in special parameters to _fun ctype. Once I figured that out, I had a lot of difficult figuring out how pull data from the hackrf_transfer buffer, and I kept trying to write the data to a file but couldn’t seem to pull it off.

Anyway, I figured that out for the most part, and was able to dump RX data to a file at a speed that seemed to match up with my set sample rate, which was 8 mhz. However, I’m not quite sure about the efficiency of that operation as I was calling write-byte for each byte in the buffer. I wanted to use write-bytes instead to transfer the whole buffer in one call, but I was having trouble getting that to work.

 racket@hackrf.rkt> (hackrf-init)
 racket@hackrf.rkt> (define h (hackrf-open))
 racket@hackrf.rkt> (sensible-defaults h)
 racket@hackrf.rkt> (define out (open-output-file "out.bin"))
 racket@hackrf.rkt> (define cb (cb-rx-to-port out))
 racket@hackrf.rkt> (hackrf-start-rx h cb)
christopher@nightshade:~/Repos/hackrf-rkt$ hexdump out.bin | head
 0000000 fa00 0201 f6ff 0303 f802 0100 fc00 ff04
 0000010 fff7 fa03 01f8 fdfe fefe fbfd 0000 f6fe
 0000020 0305 f9fe fb08 02fb f803 05fd fbff ffff
 0000030 fbf9 0100 faf9 ff03 fcfd f702 0200 f902
 0000040 0102 fc03 fefe fcfc 0203 fdf4 fe06 fff8
 0000050 f9fe 0202 f7fe 0104 f401 0000 feff fb04
 0000060 05fa f702 03fa fffc fe00 fdfc fe04 f6fb
 0000070 0105 fafd f906 02ff f500 0600 fdfc 0000
 0000080 fdfc fe01 fef6 fd04 fffc f500 0004 f7fb
 0000090 0103 fb01 fe01 fdfc 0003 02f5 fd05 01fb

HackRF Racket Bindings: Update 2

It took me a while to produce one more function, which is hackrf-open. This delay was mainly because I had a shaky understanding of some of the Racket FFI concepts and functions, so I had to go back and relearn several things, especially relating to handling C pointers in Racket. Another thing that is interesting: In C you are expected to manually allocate and deallocate memory, and signal errors via an integer return value, whereas in Scheme we are expecting the memory management to happen automatically, with errors being signaled by exceptions, so there is some work to be done bridging between to two ideas.

Anyway, hackrf-open was a good step as it gives you access to the hackrf_device pointer, which has to be passed to most of the other hackrf control functions.

racket@hackrf.rkt> (define dev (hackrf-open))
racket@hackrf.rkt> dev
#<cpointer:hackrf_device>

If you run (exit) or assign something else to dev, the program will run hackrf_close on #<cpointer:hackrf_device>.

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

Hackrf Racket Bindings: Update

I code bindings for the info functions, the functions used to pull basic information about the HackRF devices that are plugged in to USB.

racket@hackrf.rkt> (hackrf-init)
0
racket@hackrf.rkt> (define dl (get-hackrf-device-list))
racket@hackrf.rkt> (hackrf-device-count dl)
1
racket@hackrf.rkt> (hackrf-serial-numbers dl)
'("000000000000000087c867dc29903e5f")
racket@hackrf.rkt> 
racket@hackrf.rkt> (hackrf-usb-board-ids dl)
'(USB_BOARD_ID_HACKRF_ONE)
racket@hackrf.rkt> (hackrf-usb-device-index dl)
'(0)
racket@hackrf.rkt> (hackrf-usb-devices dl)
'(#<cpointer>)

There are a few earlier boards that are also supported by this interface besides just the HackRF One.

I’m coding for the version of libhackrf in Debian Stretch, which I’ve discovered is a few years older than the master branch. Maybe after finishing these binds I’ll create a new Git branch for the newer library, and update the bindings for it.

Here is the repo:

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

Four IPv6 Addresses

When working with IPv6 addresses for your host, you should be familiar with four different types:

  • DHCPv6
  • Autoconfigured Addresses (SLAAC)
    • Stable
      • EUI64
      • stable-privacy
    • Privacy temporary addresses

Quick review: an IPv6 address will have a 64 bit prefix portion, specifying the network (e.g., 2001:470:b:449) and a 64 bit suffix portion, specifying the host (e.g., 1e6f:65ff:feac:7d41).

(I skipped over static addresses as that is boring.)

DHCPv6 is listed first simply because it is the least interesting of those listed. This is the traditional way to receive an IP address, where a DHCP server on your network just picks one for you. I don’t like this approach — why depend on a DHCP server when we have IPv6 autoconfiguration? The only advantage I see is that DHCP servers I work with usually pick a shorter, easier to remember suffix, if you happen to want to memorize it. E.g., I received 2001:470:b:449::2d3, where ::2d3 is the suffix.

Stable SLAAC addresses are auto-configured, but according to a deterministic algorithm, such that you can rely on your IP address being the same every time. This is important if running a server, or for some firewall rules. The EUI64 algorithm generates this IP address by joining your network prefix to a modified form of the MAC address for your network interface. Since your MAC address is usually hardcoded into the hardware, you can rely on that not changing (often) and also figure it out in advance if you need to do so. E.g, my MAC address is 1c:6f:65:ac:7d:41, on network 2001:470:b:449, so my EUI64 address is 2001:470:b:449:1e6f:65ff:feac:7d41. ♬ here’s my number, so ping me maybe

Stable EUI64 addresses are a problem if you need privacy, because your IP suffix will remain the same on every ethernet or Wi-Fi network you visit, making it rather easy to track you. If you set your host’s network connection to use privacy temporary addresses (this might be the default) your system will generate a new IP address for you periodically, with a random suffix, and use one of these when you make a new connection.

There is also available a compromise between EUI64 and privacy temporary addresses, called stable-privacy. This generates an IP address for you deterministically based on some information on your system and some information in your network environment. Consequently, you get a different IP address for each network you visit, but it is the same address always paired to each network. Part of the formula involves a secret key on your host, so in principle the process can’t be fully reversed. So, basically, you get to be a different person on each network, but they recognize you when you come back.

In Gnome desktop (my version at least) you don’t get very much control of these options from the GUI widget. In my experience it is better to learn how to use the nmcli(1) command-line tool to control Network Manager directly.