Horus Binary Telemetry and FreeDV

Since our last post on Testing HAB Telemetry Protocols, Mark and I have been working steadily on making the Horus Binary protocol easier to use for High Altitude Balloon (HAB) telemetry.

The objectives for this work are:

  1. Test our 4FSK Binary open source balloon telemetry over the air on a real HAB mission.
  2. Develop a GUI version of the decoder to make it usable by a wider audience. Now anyone with a SSB radio (or SDR) and Windows machine can crowd source telemetry using the new protocol.
  3. Move the art of open source, high performance telemetry forward. We now have a C API suitable for integrating telemetry protocols into other projects, and a cross platform GUI for end users. In our use case, we are outperforming Lora by a factor of 10.
  4. It’s a cool AREG club project. Now we’ve moved it off the Linux command line, club members who prefer Windows and SSB radios can get involved, along with people who like Linux and SDRs.
  5. Show how we develop open source protocols, by blogging on our work, sharing our design and test techniques and most importantly the source code.

RS41 Transmitter

Mark has been collecting RS41 Radiosondes. Conveniently, our local Bureau of Meterology launches two every day. They can be tracked, and many of them eventually fall from the sky into the hands of eager South Australian Radio Amateurs. Batteries are even included in the deal.

Mark has built a nice system to help track and recover them. Turns out they are rather hackable so he reprogrammed one to be a Horus Binary transmitter using this source code:

FreeDV GUI changes

FreeDV GUI is a cross platform (Linux/OSX/Windows) GUI program for digital voice. It’s designed to demodulate modem signals from sound cards, and I’m its maintainer so we decided to press it into service as a HAB telemetry decoder.

I spent a few hours each day for a week refactoring the code to handle 48 kHz sample rate we use for the Horus API. This code needed some maintenance so it was a useful step for my digital voice work as well. I post experimental Windows versions here. Or you can just build it from the source code.

Here is a weak (-7db SNR) Horus Binary mode test signal. You may be able to hear some tinkling sounds buried in the noise – that’s the 4FSK:

You can download this weak test signal to test FreeDV GUI using the Tool-Start/Stop Play File from Radio menu option.

Now FreeDV GUI is not for everyone. The good news is you can use the Horus API to roll your own GUI front end if that’s the itch you’d like to scratch. I have limited time, and my main focus is Digital Voice over HF radio. So I would really appreciate other people from the HAB community stepping up to help with further HAB features. If you can’t code, please consider supporting this work via Pateron or PayPal.

Bench Testing MDS

On Sat 23 June Mark and I performed some Minimum Detectable signal (MDS) tests on the telemetry stack. We used a RS41 as the Tx (13 dBm output power), many dBs of attenuators, and careful experimental design to attenuate the signal down to very low levels.

This post also describes how we do MDS tests, and includes some video.

For these tests we used my FT817 SSB radio and a RTL-SDR, both with a LNA. There was up to 160dB of fixed and switched attenuators. The Tx was placed in a metal box, and we added 100dB of attenuation directly at the output. We then routed the signal to the Rx in another room using good quality coax (so it wouldn’t leak RF). A switched attenuator was placed in front of the Rx so we could adjust the attenuation in 1dB steps.

It’s important to make sure the Rx signal is coming from the desired path. You can test this using a spectrum analyser or in our case the calibrated SNR meter in FreeDV GUI. Insert more attenuation and note how far the signal level drops. If you insert 10dB more attenuation and it drops just 1dB, well you have some RF feeding through another path!

Our calculated MDS (50% packet loss) was:

  MDS = EbNo + 10log10(Rb) + NF - 174
      = 4.5  + 10log10(200) + 1 - 174
      = -145.45 dBm

Note the baud (symbol) rate is 100 symbols/s, but 4FSK sends 2 bits/symbol, so our bit rate is 200 bits/s. Our experiments on HAB protocols gave us the Eb/No figure. The Noise Figure (NF) of our preamp is an estimate from previous work and the pre-amp data sheet.

We obtained consistent results down to about -143dBm, but from there on it was difficult to get repeatable results. The signal is so weak, and the RF finds other paths. But hey – that lets us know we are close and we can conclude there are no obvious problems in the integrated system.

The comparable Lora mode 10 with a similar (1 second) packet duration has a MDS of -132dBm. Our mode requires at least 10dB less power, which gives it four times the range using the same transmit power (every 6dB doubles the line of sight range). Or we can transmit the same distance on 10% of the power of Lora. Our solution is based on good engineering, open source software, and commodity hardware rather than a chipset. The chipset form-factor has some advantages, and we hope to develop a similar form factor solution in the future. You can help if you like.

Test Flight

On Sunday 8 July the 4FSK Binary Horus system had a work out on a real Horus HAB flight. A lot of things can go wrong on a real flight but it worked well. Many people set up receive stations for the binary protocol, and crowd sourced the uploading of packets.

Callsign Packets Rx-ed % of Flight
VK5AKH 1511 82.4%
VK5APR 1541 84.0%
VK5FJGM 1234 67.3%
VK5FLJG 1577 86.0%
VK5FTAZ 832 45.4%
VK5IX 1491 81.3%
VK5KJP 1471 80.2%
VK5RR 288 15.7%
VK5ST 1663 90.7%
VK5TRM 1719 93.7%
VK5QI-9 (Mobile Station) 1560 85.1%
VK5WTF (Mobile Station) 1039 56.7%

Here is a plot of the telemetry collated by HabHub. Note on the far right hand side that the telemetry is still being received on the ground, as the recovery team tracked through the bush to collect the various payloads – fortunately Mark’s car was nearby so the signal was strong enough to be picked up even after landing.

Next Steps

The current system requires both FreeDV GUI and a Python script to to upload packets to HabHub. An obvious next step is to put some C++ code into FreeDV GUI to create a single application. Would anyone like to help us with this task?

I’d like to see more Open source telemetry, and encourage people to build their own protocols using open source components. We have demonstrated that we can outperform closed source systems like Lora. A great next step would be our own radio hardware, such as a simple FSK transmitter that can be easily integrated with a microcontroller.

Reading Further

FreeDV GUI for Windows with Horus Binary support.
Horus binary set up instructions with FreeDV.
AREG post on the flight.
RS41 source code to make it a Horus Binary telemetry transmitter.
Wenet – 115 kbit/s system for HD images that uses the same FSK modem.
All Your Modem are Belong To Us – the start of our open source modem development for HAB.
Binary Telemetry Protocol – first flight of a similar binary protocol in 2016, using 2FSK.
Testing HAB Telemetry Protocols – bench testing the 4FSK binary protocol against various RTTY demodulators.
High Speed Balloon Data Link – start of the Wenet work, and a nice example of MDS testing @ home.

Codec 2 at 450 bit/s

Thomas Kurin is a Masters student at the Institute of Electronics Engineering, part of the Friedrich-Alexander University Erlangen-Nuremberg. Thomas and his supervisor Stefan Erhardt recently contacted me with some exciting news – a version of Codec 2 running at 450 bit/s, including a 16 kHz mode!

I’m very happy that Codec 2 can be used as a starting point for academic research, and thrilled about the progress Thomas has made. It’s also great for me to have a contribution to Codec 2 on such a deep technical level. Thomas has done a lot of work in vector quantisation, an area that I struggle in, and has developed an innovative 16 kHz mode.

Any speech codec running as low at 450 bit/s is pretty special, and this work is also a great starting point for further innovation and quality improvements.

Thomas has sent me some samples, and is working with me to merge his new mode into codec2-dev, and has kindly offered to tell us his story below. Well done Thomas :-)

Here are Thomas (left) and Stefan (right) working together on their innovative 450 bit/s Codec 2 mode at the University of Erlangen:


Now this is bleeding edge, very low bit rate speech coding. Don’t expect Hi-Fi. The use case is channels where no other speech signal can get through. Ham radio operators will know what I mean.

If integrated with FreeDV, a 450 bit/s codec translates to a 2dB improvement in SNR over FreeDV 700D. This would support digital speech over HF radio at lower than -4dB SNR. Weak signal or FreeDV EME anyone?

Listen to the samples, and please add your comments. How does it compare to Codec 2 at 1300 and 700 bit/s? Could you communicate using this mode?

Sample 1300 700C 450 8kHz 450 16kHz
hts1a Listen Listen Listen Listen
hts2a Listen Listen Listen Listen
forig Listen Listen Listen Listen
ve9qrp_10s Listen Listen Listen Listen
mmt1 Listen Listen Listen Listen
vk5qi Listen Listen Listen Listen
cq_ref Listen Listen Listen Listen

The samples I use are deliberately chosen to give codecs a hard time. mmt1 is the worst, it has high level truck background noise added to it.

Thomas’ Story

As a masters student at FAU Erlangen-Nürnberg I have been working on and with Codec 2 and especially with 700C for the last few months. I experimented with the source code and the Vector Quantisation. This lead to the 450 bit/s Codec which shall be shown in the following post.

The 450 Codec builds on the 700C Codec. It consists of 3 major changes:

  1. The training data used for Vector Quantisation (VQ) was changed to include multiple languages, as for other languages the VQ sometimes performed poorly because certain vectors were missing. The used dataset consisted of approximately 30 minutes English, 30 minutes German, 15 minutes Russian, 15 minutes Chinese and 15 minutes Spanish. This Codebook allowed a switch from the two stage VQ with 9 * 2 = 18 bit per frame to a one stage VQ with just 9 bit per frame for VQ. The audio quality is of course somewhat changed, but still understandable.
  2. The energy quantisation was changed from 4 bit to 3 bit as no change in quality for the one stage VQ was detectable. This leads to a reduction of 9 + 1 = 10 bit, from a 28 bit frame to a 18 bit frame. This means a 450 bit/s Codec.
  3. The biggest change was the inclusion of a 16k mode. This means the 8kHz sampled and encoded signal can be converted to a 16kHz sampled signal at the decoder. To achieve this, the codebook was trained with data sampled at 16kHz. Then only the vectors for the 0-4 kHz frequencies (= 8kHz sample rate) are used for encoding. The decoder uses the indices to look up the 16kHz sampled and trained vectors. This works because a vector that is similar between 0-4 kHz mostly also looks similar in 4-8 kHz. This results in a pseudo-wideband format without any additional bits. For some speakers this improves quality, but for some speakers it just creates noise. But that’s the beauty of the system, as the same data can both be decoded to a 8kHz sampled signal, and to a 16kHz sampled signal. Therefore the transmitted signal stays the same and the receiver can choose dependent on quality which mode he wants to listen on.The 16k mode still needs some refinement but its fascinating that it works at all.

Over the next week or so I will patch the changes with David against codec2-dev. As it is a new codec, it shouldn’t change any of the other codecs when patching, as most of the patching will be to include new files.

Reading Further

Codec 2
Codec 2 700C

Bench Testing HF Radios with a HackRF

This post describes how we implemented a HF channel simulator to bench test a digital HF radio using modern SDRs.

Yesterday Mark and I bench tested a HF radio with calibrated SNR over simulated AWGN and HF channels. We recorded the radios transmit signal with an AirSpy HF and GQRX, added calibrated noise and “CCIR Poor” fading, and replayed the signal using a HackRF.

For the FreeDV 700C and 700D work I have developed a utility called cohpsk_ch, that takes a real modem signal, adds channel impairments like noise and fading, and outputs another real signal. It has a built in Hilbert Transformer so it can do complex math cleverness like small frequency shifts and ITUT/CCIR HF fading channel models.

Set Up

The basic idea is to upconvert a 8 kHz real sample file to HF in real time. I have some utilities to help with this in codec2-dev:

$ svn co https://svn.code.sf.net/p/freetel/code/codec2-dev codec2-dev
$ cd codec2-dev/octave
$ octave --no-gui
octave:1> cohpsk_ch_fading("../raw/fast_fading_samples.float", 8000, 1.0, 8000*60)
octave:2> cohpsk_ch_fading("../raw/slow_fading_samples.float", 8000, 0.1, 8000*60)
$ exit
$ cd ..
$ cd codec2-dev && mkdir build_linux && cd build_linux
$ cmake -DCMAKE_BUILD_TYPE=Debug ..
$ make
$ cd unittest 

You also need GNU Octave to generate the HF fading files for cohpsk_ch, and you need to install the very useful CSDR tools.

Connect the HackRF to your SSB receiver, we put a 30dB attenuator in line. Tune the radio to 7.177 MHz LSB. First generate a carrier with your HackRF, offset so we get a 500Hz tone in the SSB radio in LSB mode:

$ hackrf_transfer -f 7176500 -s 8000000 -c 127

Now lets try some DSB audio:

$ cat ../../wav/ve9qrp.wav | csdr mono2stereo_s16 | ./tsrc - - 10 -c |
./tlininterp - - 100 -df | hackrf_transfer -f 5177000 -s 8000000  -t - 2>/dev/null

Don’t change the frequency, but try switching the mode between USB and LSB. Should sound about the same, with a slight frequency offset due to the HackRF. Note that HackRF is tuned to Fs/4 = 2MHz beneath 7.177MHz. “tlininterp” has a simple Fs/4 mixer that we use to shift the signal away from the HackRF DC spike. We up-sample from 8 kHz to 8 MHz in two steps to save MIPs.

The “csdr mono2stereo_s16” just repeats the real output samples, so we get a DSB signal at HF. A bit lazy I know, a better approach would be to modify cohpsk_ch to have a complex output option. Let me know if you want to modify cohpsk_ch – I can tell you how.

Checking Calibration

Now I’m pretty confident that cohpsk_ch works well at baseband on digital signals as I have used it extensively in my HF DV work. However I wanted to make sure the off air signal had the correct SNR.

To check the calibration, we generated a 1000 Hz sine wave Signal + Noise signal:

$ ./mksine - 1000 30  | ./../src/cohpsk_ch - - -30 --Fs 8000 --ssbfilt 0 | csdr mono2stereo_s16 | ./tsrc - - 10 -c | ./tlininterp - - 100 -df | hackrf_transfer -f 12177000 -s 8000000  -t - 2>/dev/null 

Then just a noise signal:

cat /dev/zero | ./../src/cohpsk_ch - - -30 --Fs 8000 --ssbfilt 0 | csdr mono2stereo_s16 | ./tsrc - - 10 -c | ./tlininterp - - 100 -df | hackrf_transfer -f 5177000 -s 8000000  -t - 2>/dev/null

With moderate SNRs (say 10dB), Signal + Noise power is roughly Signal power. So I measured the off air power of the above signals using my FT817 connected to a USB sound card, and an Octave script:

$ rec -t raw -r 8000 -s -2 -c 1 - -q | octave --no-gui -qf power_from_stdio.m

I used alsamixer and the plots from the script to make sure I wasn’t overloading the ADC. You need to turn your receiver AGC OFF, and adjust RF/AF gain to get the levels right.

However from the FT817 I was getting results a few dB off due to the crystal filter bandwidth and non-rectangular shape factor. Mark hooked up his AirSpy HF and GQRX, and we piped the received audio over the LAN to the script:

nc -ul 7355 | octave --no-gui -qf power_from_stdio.m

GQRX had a nice flat response from a few 100 Hz to 3kHz, the same bandwidth cohpsk_ch uses for SNR measurement. OK, so now we had sensible numbers, within 0.2dB of the SNR reported by cohpsk_ch. We moved the levels up and down 3dB, made sure everything was repeatable and linear. We went down to 0dB, where signal and noise power is the same, and Signal+Noise power should be 3dB more than Noise alone. Check.


Then we could play the HF tx signal at a variety of SNRS, by tweaking third (No) argument. In this case we set No to -100dB, so no noise:

cat tx_file_from_radio.wav | ./../src/cohpsk_ch - - -100 --Fs 8000 --ssbfilt 0 | csdr mono2stereo_s16 | ./tsrc - - 10 -c | ./tlininterp - - 100 -df | hackrf_transfer -f 5177000 -s 8000000  -t - 2>/dev/null

At the end of the cohpsk_ch run, it will print the SNR is has measured. So you read that and tweak No as required to get the SNR you need. In our case around -30 was 8dB SNR. You can also add fast (–fast) or slow (–slow) fading, here is a fast fading run at about 2dB SNR:

cat tx_file_from_radio.wav | ./../src/cohpsk_ch - - -24 --Fs 8000 --ssbfilt 0 --fast | csdr mono2stereo_s16 | ./tsrc - - 10 -c | ./tlininterp - - 100 -df | hackrf_transfer -f 5177000 -s 8000000  -t - 2>/dev/null

The “–ssbfilt 0” option switches off the 300-2600 Hz filter inside cohpsk_ch, that is used to simulate a SSB radio crystal filter. For out tests, the modem waveform was too wide for that filter.


I guess we could also have used the HackRF to sample the signal. The nice thing about SDRs is the frequency response is ‘flat”, no crystal filters messing things up.

The only thing we weren’t sure about was the sample rate and frequency offset accuracy of the HackRF, for example if the sample clock was a bit off that might upset modems.

The radio we tested delivered performance pretty much on it’s data sheet at the SNRs tested, giving us extra confidence in the bench testing system described here.

Reading Further

Measuring SDR Noise Figure in Real Time
High Speed Balloon Data Link, here we bench test a UHF FSK data radios
README_ofdm.txt, Lots of examples of using cohpsk_ch to test the latest and greatest OFDM modem.
PathSim is a very nice Windows GUI HF path simulator, that runs well on Linux using Wine.

FreeDV 700D Released

Here is a sample of Mark, VK5QI, sending a FreeDV 700D signals from Adelaide, South Australia, to a Kiwi SDR at the Bay of Islands, New Zealand. It was a rather poor channel with a path length of 3200km (2000 miles). First SSB, then FreeDV 700D, then SSB again:

Here is FreeDV 700D on the waterfall of Mark’s IC7610. That little narrow signal at 7.176 MHz is 700D, note the “overweight” SSB signals to the right! This is a very bandwidth efficient mode.

Last weekend FreeDV GUI 1.3 was released, which includes the new 700D mode. I’ve been working hard for the last few months to get 700D out of the lab and onto the air. Overall, I estimate about 1000 hours were required to develop FreeDV 700D over the last 12 months.

For the last few weeks teams of beta testers dotted around the world have been running FreeDV 1.3 in the wild. FreeDV 700D is fussy about lost samples so I had to do some work with care and feeding of the sound drivers, espcially on the Windows build. Special thanks to Steve K5OKC, Richard KF5OIM, Mark VK5QI, Bill VK5DSP; Hans PA0HWB and the Dutch team; Eric GW8LJJ, Kieth GW8TRO and the UK team; Mel K0PFX, Walt K5WH and the US team, Peter VK5APR, Peter VK2TPM, Bruce K6BP, Gerhard OE3GBB, John VK5DM/VK3IC, Peter VK3RV and the Sunbury team, and my local AREG club. I apologise if I have missed anyone, all input is greatly appreciated.

Anyone who writes software should be sentenced to use it. So I’ve poked a few antennas up into the air and, conditions permitting have made 700D contacts, getting annoyed with things that don’t work, then tweaking and improving. Much to my surprise it really does handle some nasty fading, and it really does work better than SSB in many cases. Engineers aren’t used to things working, so this is a bit of an adjustment for me personally.


Here’a demo video of FreeDV 1.3 decoding a low SNR Transatlantic contact between Gerhard OE3GBB and Walt, K5WH:

You can see the fast fading on the signal. The speech quality is not great, but you get used to it after a little while and it supports conversations just fine. Remember at this stage we are targeting low SNR communications, as that has been the major challenge to date.

Here’s a screen shot of the FreeDV QSO Finder (thanks John K7VE) chat log, when the team tried SSB shortly afterwards:

FreeDV 700D also has some robustness to urban HF Noise. I’m not sure why, this still needs to be explored. Here is the off-air signal I received from Peter, VK2TPM. It’s full of nasty buzzing switching power supply noises, and is way down in the noise, but I obtained an 80% decode:

It’s hard to hear the modem signal in there!

FreeDV 700D Tips

Lots of information of FreeDV, and the latest software, at freedv.org. Here are some tips on using 700D:

  1. The 700 bit/s codec is’s sensitive to your microphone and the FreeDV microphone equaliser settings (Tools-Filter). Suggest you set up a local loopback to hear your own voice and tune the quality using the Tools-Filter Mic equaliser. You can play pre-recorded wave files of your own voice using Tools-Play File to Mic in or with the “voice keyer” feature.
  2. The current 700D modem is sensitive to tuning, you need to be within +/- 20Hz for it to acquire. This is not a practical problem with modern radios that are accurate to +/- 1Hz. One you have acquired sync it can track drift of 0.2Hz/s. I’ll get around to improving the sync range one day.
  3. Notes on the new features in FreeDV 1.3 User Guide.
  4. Look for people to talk to on the FreeDV QSO Finder (thanks John K7VE)
  5. Adjust the transmit drive to your radio so it’s just moving the ALC. Don’t hammer your PA! Less is more with DV. Aim for about 20W average power output on a 100W PEP radio.
  6. If you get stuck reach out for help on the Digital Voice mailing list (digitalvoice at googlegroups.com)


The last time a new HF voice mode was introduced was the 1950’s and it was called Single Side Band (SSB). It’s lasted so long because it works well.

So a new voice mode that competes with SSB is something rare and special. We don’t want the next HF Voice mode to be locked down by codec vendors. We want it to be open source.

I feel 700D is a turning point for FreeDV and open source digital voice. After 10 years of working on Codec 2 and FreeDV, we are now competitive with SSB on HF multipath channels at low SNRs. The 700 bits/ codec isn’t great. It’s fussy about microphones, EQ settings, and background noise. But it’s a start, and we can improve from here.

It takes some getting used to, but our growing experience has shown 700D is quite usable for conversations. Bear in mind SSB isn’t pretty at low SNRs either (see sample at the top), indeed untrained listeners struggle with SSB even at high SNRs.

Quite remarkably, the 700 bit/s codec outperforms locked down, proprietary, expensive, no you can’t look at my source or modify me, codecs like MELP and TWELP at around the same bit rate.

The FreeDV 700D waveform (the combined speech codec, FEC, modem, protocol) is competitive at low SNRs (-2dB AWGN, +2dB CCIR Poor channel), with several closed source commercial HF DV systems that we have explored.

FreeDV 700D requires about 1000 Hz of RF bandwidth, half of SSB.

Most importantly FreeDV and Codec 2 are open source. It’s freely available to not just Radio Amateurs, but emergency services, the military, humanitarian organisations, and commercial companies.

Now that we have some traction with low SNR HF fading channels, the next step is to improve the speech quality. We can further improve HF performance with experience, and I’d like to look at VHF/UHF again, and push down to 300 bit/s. The Lower SNR limit of Digital Voice is around -8dB SNR.

This is experimental radio. DV over HF is a very tough problem. Unlike other almost all other voice services (mobile phones, VHF/UHF radio), HF is still dominatted by analog SSB modulation. I’m doing much of the development by myself, so I’m taking one careful, 1000 man-hour, step at a time. Unlike other digital voice modes (I’m looking at you DStar/C4FM/DMR/P25) – we get to set the standard (especially the codec), rather than following it and being told “this is how it is”.

Get Involved

Here is how you can get involved:

  1. Support this work via Patreon or PayPal
  2. Refactor and maintain the FreeDV GUI source code. This will help free me to push forward the DSP code where my skills are unique. See bottom of FreeDV GUI README.
  3. Experienced or not, if you want to play DSP, I have some work for you too. You will learn a lot. Like Steve Did.
  4. Find corner cases where 700D breaks. Then help me fix it.
  5. Work with me to port 700D to the SM1000.
  6. Make freedv.org look great and maintain it.
  7. Help me use Deep Learning to make Codec 2 even better.
  8. Start a FreeDV Net.
  9. Set up a FreeDV beacon.
  10. Help me get some UHF/VHF FreeDV modes on the air. Some coding and messing with radios required.
  11. Help others get set up on FreeDV, 700D voice quality depends on the right microphone and equaliser settings, and noobs tend to over drive their PA.
  12. Create and Post Demo/instructional Videos.

Like the good people above, you have the opportunity to participate in the evolution of HF radio. This has happened once in the last 60 years. Lets get started.

If you are interested in development, please subscribe to the Codec 2 Mailing List.

Reading Further

Peter VK2TPM, blogs on 700D.
AREG Blog Post on FreeDV 700D
Steve Ports an OFDM modem from Octave to C. This is the sort of support I really need – thanks Steve for stepping up and helping!
Windows Installers for development versions of FreeDV.
Codec 2 700C
AMBE+2 and MELPe 600 Compared to Codec 2
Lower SNR limit of Digital Voice
700D OFDM modem README and specs
FreeDV User Guide, including new 700D features.
Bill, VK5DSP designed the LDPC code used in 700D and has helped with its care and feeding. He also encouraged me to carefully minimise the synchronisation (pilot symbol) overhead for the OFDM modem used in 700D.

FreeDV 700D Part 4 – Acquisition

Since 2012 I have built a series of modems (FDMDV, COHPSK, OFDM) for HF Digital voice. I always get stuck on “acquisition” – demodulator algorithms that acquire and lock onto the received signal. The demod needs to rapidly estimate the frequency offset and “coarse” timing – the position where the modem frame starts in the sequence of received samples.

For my application (Digital Voice over HF), it’s complicated by the low SNR and fading HF channels, and the requirement for fast sync (a few hundred ms). For Digital Voice (DV) we need something fast enough to emulate Push To Talk (PTT) operation. In comparison HF data modems have it easy – they can take many lazy seconds to synchronise.

The latest OFDM modem has been no exception. I’ve spent several weeks messing about with acquisition algorithms to get half decent performance. Still some tuning to do but for my own sanity I think I’ll stop development here for now, write up the results, and push FreeDV 700D out for general consumption.

Acquisition and Sync Requirements

  1. Sync up quickly (a few 100ms) with high SNR signals.
  2. Sync up eventually (a few is seconds OK) for low SNR signals over poor channels. Sync eventually is better than none on channels where even SSB is struggling.
  3. Detect false sync and get out of it quickly. Don’t stay stuck in a false sync state forever.
  4. Hang onto sync through fades of a few seconds.
  5. Assume the operator can tune to within +/- 20Hz of a given frequency.
  6. Assume the radio drifts no more than +/- 0.2Hz/s (12 Hz a minute).
  7. Assume the sample clock offset (difference in ADC/DAC sample rates) is no more than 500ppm.

Actually the last three aren’t really requirements, it’s just what fell out of the OFDM modem design when I optimised it for low SNR performance on HF channels! The frequency stability of modern radios is really good; sound card sample clock offset less so but perhaps we can measure that and tell the operator if there is a problem.

Testing Acquisition

The OFDM modem sends pilot (known) symbols every frame. The demodulator correlates (compares) the incoming signal with the pilot symbol sequence. When it finds a close match it has a coarse timing candidate. It can then try to estimate the frequency offset. So we get a coarse timing estimate, a metric (called mx1) that says how close the match is, and a frequency offset estimate.

Estimating frequency offsets is particularly tricky, I’ve experienced “much wailing and gnashing of teeth” with these nasty little algorithms in past (stop laughing Matt). The coarse timing estimator is more reliable. The problem is that if you get an incorrect coarse timing or frequency estimate the modem can lock up incorrectly and may take several seconds, or operator intervention, before it realises its mistake and tries again.

I ended up writing a lot of GNU Octave functions to help develop and test the acquisition algorithms in ofdm_dev.

For example the function below runs 100 tests, measures the timing and frequency error, and plots some histograms. The core demodulator can cope with about +/ 1.5Hz of residual frequency offset and a few samples of timing error. So we can generate probability estimates from the test results. For example if we do 100 tests of the frequency offset estimator and 50 are within 1.5Hz of being correct, then we can say we have a 50% (0.5) probability of getting the correct frequency estimate.

octave:1> ofdm_dev
octave:2> acquisition_histograms(fin_en=0, foff_hz=-15, EbNoAWGN=-1, EbNoHF=3)
AWGN P(time offset acq) = 0.96
AWGN P(freq offset acq) = 0.60
HF P(time offset acq) = 0.87
HF P(freq offset acq) = 0.59

Here are the histograms of the timing and frequency estimation errors. These were generated using simulations of noisy HF channels (about 2dB SNR):

The x axis of timing is in samples, x axis of freq in Hz. They are both a bit biased towards positive errors. Not sure why. This particular test was with a frequency offset of -15Hz.

Turns out that as the SNR improves, the estimators do a better job. The next function runs a bunch of tests at different SNRs and frequency offsets, and plots the acquisition probabilities:

octave:3> acquisition_curves

The timing estimator also gives us a metric (called mx1) that indicates how strong the match was between the incoming signal and the expected pilot sequence. Here is a busy little plot of mx1 against frequency offset for various Eb/No (effectively SNR):

So as Eb/No increases, the mx1 metric tends to gets bigger. It also falls off as the frequency offset increases. This means sync is tougher at low Eb/No and larger frequency offsets. The -10dB value was thrown in to see what happens with pure noise and no signal at the input. We’d prefer not to sync up to that. Using this plot I set the threshold for a valid signal at 0.25.

Once we have a candidate time and freq estimate, we can test sync by measuring the number of bit errors a set of 10 Unique Word (UW) bits spread over the modem frame. Unlike the payload data in the modem frame, these bits are fixed, and known to the transmitter and receiver. In my initial approach I placed the UW bits right at the start of the modem frame. However I discovered a problem – with certain frequency offsets (e.g. multiples of the modem frame rate like +/- 6Hz) – it was possible to get a false sync with no UW errors. So I messed about with the placement of the UW bits until I had a UW that would not give any false syncs at any incorrect frequency offset. To test the UW I wrote another script:

octave:4> debug_false_sync

Which outputs a plot of UW errors against the residual frequency offset:

Note how at any residual frequency offset other than -1.5 to +1.5 Hz there are at least two bit errors. This allows us to reliably detect a false sync due to an incorrect frequency offset estimate.

State Machine

The estimators are wrapped up in a state machine to control the entire sync process:

  1. SEARCHING: look at a buffer of incoming samples and estimate timing, freq, and the mx1 metric.
  2. If mx1 is big enough, lets jump to TRIAL.
  3. TRIAL: measure the number of Unique Word bit errors for a few frames. If they are bad this is probably a false sync so jump back to SEARCHING.
  4. If we get a low number of Unique Word errors for a few frames it’s high fives all round and we jump to SYNCED.
  5. SYNCED: We put up with up two seconds of high Unique Word errors, as this is life on a HF channel. More than two seconds, and we figure the signal is gone for good so we jump back to SEARCHING.

Reading Further

HF Modem Frequency Offset Estimation, an earlier look at freq offset estimation for HF modems
COHPSK and OFDM waveform design spreadsheet
Modems for HF Digital Voice Part 1
Modems for HF Digital Voice Part 2
README_ofdm.txt, including specifications of the OFDM modem.

FreeDV 700D and SSB Comparison

Mark, VK5QI has just performed a SSB versus FreeDV 700D comparison between his home in Adelaide and the Manly Warringah Radio Society WebSDR SDR in Sydney, about 1200km away. The band was 40m, and the channel very poor, with some slow fading. Mark used SVN revision 3581, built himself on Ubuntu, with an interleaver setting (Tools-Options menu) of 1 frame. Transmit power for SSB and FreeDV 700D was about the same.

I’m still finishing off FreeDV 700D integration and tuning the mode – but this is a very encouraging start. Thanks Mark!

FreeDV 1600 Sample Clock Offset Bug

So I’m busy integrating FreeDV 700D into the FreeDV GUI program. The 700D modem works on larger frames (160ms) than the previous modes (e.g. 20ms for FreeDV 1600) so I need to adjust FIFO sizes.

As a reference I tried FreeDV 1600 between two laptops (one tx, one rx) and noticed it was occasionally losing frame sync, generating bit errors, and producing the occasional bloop in the audio. After a little head scratching I discovered a bug in the FreeDV 1600 FDMDV modem! Boy, is my face red.

The FMDMV modem was struggling with sample clock differences between the mod and demod. I think the bug was introduced when I did some (too) clever refactoring to reduce FDMDV memory consumption while developing the SM1000 back in 2014!

Fortunately I have a trail of unit test programs, leading back from FreeDV GUI, to the FreeDV API (freedv_tx and freedv_rx), then individual unit tests for each modem (fdmdv_mod/fdmdv_demod), and finally Octave simulation code (fdmdv.m, fdmdv_demod.m and friends) for the modem.

Octave (or an equivalent vector based scripting language like Python/numpy) is much easier to work with than C for complex DSP problems. So after a little work I reproduced the problem using the Octave version of the FDMDV modem – bit errors happening every time there was a timing jump.

The modulator sends parallel streams of symbols at about 50 baud. These symbols are output at a sample rate of 8000 Hz. Part of the demodulators job is to estimate the best place to sample each received modem symbol, this is called timing estimation. When the tx and rx are separate, the two sample clocks are slightly different – your 8000 Hz clock will be a few Hz different to mine. This means the timing estimate is a moving target, and occasionally we need to compenstate by talking a few more or few less samples from the 8000 Hz sample stream.

In the plot below the Octave demodulator was fed with a signal that is transmitted at 8010 Hz instead of the nominal 8000 Hz. So the tx is sampling faster than the rx. The y axis is the timing estimate in samples, x axis time in seconds. For FreeDV 1600 there are 160 samples per symbol (50 baud at 8 kHz). The timing estimate at the rx drifts forwards until we hit a threshold, set at +/- 40 samples (quarter of a symbol). To avoid the timing estimate drifting too far, we take a one-off larger block of samples from the input, the timing takes a step backwards, then starts drifting up again.

Back to the bug. After some head scratching, messing with buffer shifts, and rolling back phases I eventually fixed the problem in the Octave code. Next step is to port the code to C. I used my test framework that automatically compares a bunch of vectors (states) in the Octave code to the equivalent C code:

octave:8> system("../build_linux/unittest/tfdmdv")
sizeof FDMDV states: 40032 bytes
ans = 0
octave:9> tfdmdv
tx_bits..................: OK
tx_symbols...............: OK
tx_fdm...................: OK
pilot_lut................: OK
pilot_coeff..............: OK
pilot lpf1...............: OK
pilot lpf2...............: OK
S1.......................: OK
S2.......................: OK
foff_coarse..............: OK
foff_fine................: OK
foff.....................: OK
rxdec filter.............: OK
rx filt..................: OK
env......................: OK
rx_timing................: OK
rx_symbols...............: OK
rx bits..................: OK
sync bit.................: OK
sync.....................: OK
nin......................: OK
sig_est..................: OK
noise_est................: OK

passes: 46 fails: 0

Great! This system really lets me move fast once the Octave code is written and tested. Next step is to test the C version of the FDMDV modem using the command line arguments. Note how I used sox to insert a sample rate offset by changing the same rate of the raw sample stream:

build_linux/src$ ./fdmdv_get_test_bits - 30000 | ./fdmdv_mod - - | sox -t raw -r 8000 -s -2 - -t raw -r 7990 - | ./fdmdv_demod - - 14 demod_dump.txt | ./fdmdv_put_test_bits -
bits 29568  errors 0  BER 0.0000

Zero errors, despite 10Hz sample clock offset. Yayyyyy. The C demodulator outputs a bunch of vectors that can be plotted with an Octave helper program:

octave:6> fdmdv_demod_c("../build_linux/src/demod_dump.txt",28000)

The FDMDV modem is integrated with Codec 2 in the FreeDV API. This can be tested using the freedv_tx/freedv_rx programs. For convenience, I generated some 60 second test files at different sample rates. Here is how I test using the freedv_rx program:

./freedv_rx 1600 ~/Desktop/ve9qrp_1600_8010.raw - | aplay -f S16

The ouput audio sounds good, no bloops, and by examining the freedv_rx_log.txt file I can see the demodulator didn’t loose sync. Cool.

Here is a table of the samples I used for testing:

No clock offset Simulates Tx sample rate 10Hz slower than Rx Simulates Tx sampling 10Hz faster than Rx

Finally, the FreeDV API is linked with the FreeDV GUI program. Here is a video of me testing different sample clock offsets using the raw files in the table above. Note there is no audio in this video as my screen recorder fights with FreeDV for use of sound cards. However the decoded FreeDV audio should be uninterrupted, there should be no re-syncs, and zero bit errors:

The fix has been checked into codec2-dev SVN rev 3556, and will make it’s way into FreeDV GUI 1.3, to be released in late May 2018.

Reading Further

FDMDV modem
Steve Ports an OFDM modem from Octave to C, some more on the Octave/C automated test framework and porting complex DSP algorithms.
Testing a FDMDV Modem. Early blog post on FDMDV modem with some more disucssion on sample clock offsets
Timing Estimation for PSK modems, talks a little about how we generate a timing estimate

FreeDV 700D Part 3

After a 1 year hiatus, I am back into FreeDV 700D development, working to get the OFDM modem, LDPC FEC, and interleaver algorithms developed last year into real time operation. The aim is to get improved performance on HF channels over FreeDV 700C.

I’ve been doing lots of refactoring, algorithm development, fixing bugs, tuning, and building up layers of C code so we can get 700D on the air.

Steve ported the OFDM modem to C – thanks Steve!

I’m building up the software in the form of command line utilities, some notes, examples and specifications in Codec 2 README_ofdm.txt.

Last week I stayed at the shack of Chris, VK5CP, in a quiet rural location at Younghusband on the river Murray. As well as testing my Solar Boat, Mark (VK5QI) helped me test FreeDV 700D. This was the first time the C code software has been tested over a real HF radio channel.

We transmitted signals from YoungHusband, and received them at a remote SDR in Sydney (about 1300km away), downloading wave files of the received signal for off-line analysis.

After some tweaking, it worked! The frequency offset was a bit off, so I used the cohpsk_ch utility to shift it within the +/- 25Hz acquisition range of the FreeDV 700D demodulator. I also found some level sensitivity issues with the LDPC decoder. After implementing a form of AGC, the number of bit errors dropped by a factor of 10.

The channel had nasty fading of around 1Hz, here is a video of the “sample #32” spectrum bouncing around. This rapid fading is a huge challenge for modems. Note also the spurious birdie off to the left, and the effect of receiver AGC – the noise level rises during fades.

Here is a spectrogram of the same sample 33. The x axis is time in seconds. It’s like a “waterfall” SDR plot on it’s side. Note the heavy “barber pole” fading, which corresponds to the fades sweeping across the spectrum in the video above.

Here is the smoothed SNR estimate. The SNR is moving target for real world HF channels, the SNR moves between 2 and 6dB.

FreeDV 700D was designed to work down to 2dB on HF fading channels so pat on the back for me! Hundreds of hours of careful development and testing meant this thing actually worked when it went on air….

Sample 32 is a longer file that contains test frames instead of coded voice. The QPSK scatter diagram is a messy cross, typical of fading channels, as the amplitude of the signal moves in and out:

The LDPC FEC does a good job. Here are plots of the uncoded (raw) bit errors, and the bit errors after LDPC decoding, with the SNR estimates below:

Here are some wave and raw (headerless) audio files. The off air audio is error free, albeit at the low quality of Codec 2 at 700 bits/s. The goal of this work is to get intelligible speech through HF channels at low SNRs. We’ll look at improving the speech quality as a future step.

Still, error free digital voice on a heavily faded HF channel at 2dB SNR is pretty cool.

See below for how to use the last two raw file samples.

sample 33 off air modem signal Sample 33 decoded voice Sample 32 off air test frames raw file Sample 33 off air voice raw file

SNR estimation

After I sampled the files I had a problem – I needed to know the SNR. You see in my development I use simulated channels where I know exactly what the SNR is. I need to compare the performance of the real world, off-air signals to my expected results at a given SNR.

Unfortunately SNR on a fading channel is a moving target. In simulation I measure the total power and noise over the entire run, and the simulated fading channel is consistent. Real world channels jump all over the place as the ionosphere bounces around. Oh well, knowing we are in the ball park is probably good enough. We just need to know if FreeDV 700D is hanging onto real world HF channels at roughly the SNRs it was designed for.

I came up with a way of measuring SNR, and tested it with a range of simulated AWGN (just noise) and fading channels. The fading bandwidth is the speed at which the fading channel evolves. Slow fading channels might change at 0.2Hz, faster channels, like samples #32 and #33, at about 1Hz.

The blue line is the ideal, and on AWGN and slowly fading channels my SNR estimator does OK. It reads a dB low as the fading bandwidth increases to 1Hz. We are interested in the -2 to 4dB SNR range.

Command Lines

With the samples in the table above and codec2-dev SVN rev 3465, you can repeat some of my decodes using Octave and C:

octave:42> ofdm_ldpc_rx("32.raw")
EsNo fixed at 3.000000 - need to est from channel
Coded BER: 0.0010 Tbits: 54992 Terrs:    55
Codec PER: 0.0097 Tpkts:  1964 Terrs:    19
Raw BER..: 0.0275 Tbits: 109984 Terrs:  3021

david@penetrator:~/codec2-dev/build_linux/src$ ./ofdm_demod ../../octave/32.raw /dev/null -t --ldpc
Warning EsNo: 3.000000 hard coded
BER......: 0.0246 Tbits: 116620 Terrs:  2866
Coded BER: 0.0009 Tbits: 54880 Terrs:    47

build_linux/src$ ./freedv_rx 700D ../../octave/32.raw /dev/null --testframes
BER......: 0.0246 Tbits: 116620 Terrs:  2866
Coded BER: 0.0009 Tbits: 54880 Terrs:    47

build_linux/src$ ./freedv_rx 700D ../../octave/33.raw  - | aplay -f S16

Next Steps

I’m working steadily towards integrating FreeDV 700D into the FreeDV GUI program so anyone can try it. This will be released in May 2018.

Reading Further

Towards FreeDV 700D
FreeDV 700D – First Over The Air Tests
Steve Ports an OFDM modem from Octave to C
Codec 2 README_ofdm.txt