Porting a LDPC Decoder to a STM32 Microcontroller

A few months ago, FreeDV 700D was released. In that post, I asked for volunteers to help port 700D to the STM32 microcontroller used for the SM1000. Don Reid, W7DMR stepped up – and has been doing a fantastic job porting modules of C code from the x86 to the STM32.

Here is a guest post from Don, explaining how he has managed to get a powerful LDPC decoder running on the STM32.

LDPC for the STM32

The 700D mode and its LDPC function were developed and used on desktop (x86) platforms. The LDPC decoder is implemented in the mpdecode_core.c source file.

We’d like to run the decoder on the SM1000 platform which has an STM32F4 processor. This requires the following changes:

  • The code used doubles in several places, while the stm32 has only single precision floating point hardware.
  • It was thought that the memory used might be too much for a system with just 192k bytes of RAM.
  • There are 2 LDPC codes currently supported, HRA_112_112 used in 700D and, H2064_516_sparse used for Balloon Telemetry. While only the 700D configuration needed to work on the STM32 platform, any changes made to the mainstream code needed to work with the H2064_516_sparse code.

Testing

Before making changes it was important to have a well defined test process to validate new versions. This allowed each change to be validated as it was made. Without this the final debugging would likely have been very difficult.

The ldpc_enc utility can generate standard test frames and the ldpc_dec utility receive the frames and measure bit errors. So errors can be detected directly and BER computed. ldpc_enc can also output soft decision symbols to emulate what the modem would receive and pass into the LDPC decoder. A new utility ldpc_noise was written to add AWGN to the sample values between the above utilities. here is a sample run:

$ ./ldpc_enc /dev/zero - --sd --code HRA_112_112 --testframes 100 | ./ldpc_noise - - 1 | ./ldpc_dec - /dev/null --code HRA_112_112 --sd --testframes
single sided NodB = 1.000000, No = 1.258925
code: HRA_112_112
code: HRA_112_112
Nframes: 100
CodeLength: 224 offset: 0
measured double sided (real) noise power: 0.640595
total iters 3934
Raw Tbits..: 22400 Terr: 2405 BER: 0.107
Coded Tbits: 11200 Terr: 134 BER: 0.012

ldpc_noise is passed a “No” (N-zero) level of 1dB, Eb=0, so Eb/No = -1, and we get a 10% raw BER, and 1% after LDPC decoding. This is a typical operating point for 700D.

A shell script (ldpc_check) combines several runs of these utilities, checks the results, and provides a final pass/fail indication.

All changes were made to new copies of the source files (named *_test*) so that current users of codec2-dev were not disrupted, and so that the behaviour could be compared to the “released” version.

Unused Functions

The code contained several functions which are not used anywhere in the FreeDV/Codec2 system. Removing these made it easier to see the code that was used and allowed the removal of some variables and record elements to reduce the memory used.

First Compiles

The first attempt at compiling for the stm32 platform showed that the the code required more memory than was available on the processor. The STM32F405 used in the SM1000 system has 128k bytes of main RAM.

The largest single item was the DecodedBits array which was used to saved the results for each iteration, using 32 bit integers, one per decoded bit.

    int *DecodedBits = calloc( max_iter*CodeLength, sizeof( int ) );

This used almost 90k bytes!

The decode function used for FreeDV (SumProducts) used only the last decoded set. So the code was changed to save only one pass of values, using 8 bit integers. This reduced the ~90k bytes to just 224 bytes!

The FreeDV 700D mode requires on LDPC decode every 160ms. At this point the code compiled and ran but was too slow – using around 25ms per iteration, or 300 – 2500ms per frame!

C/V Nodes

The two main data structures of the LDPC decoder are c_nodes and v_nodes. Each is an array where each node contains additional arrays. In the original code these structures used over 17k bytes for the HRA_112_112 code.

Some of the elements of the c and v nodes (index, socket) are indexes into these arrays. Changing these from 32 bit to 16 bit integers and changing the sign element into a 8 bit char saved about 6k bytes.

The next problem was the run time. Each 700D frame must be fully processed in 160 ms and the decoder was taking several times this long. The CPU load was traced to the phi0() function, which was calling two maths library functions. After optimising the phi0 function (see below) the largest use of time was the index computations of the nested loops which accessed these c and v node structures.

With each node having separate arrays for index, socket, sign, and message these indexes had to be computed separately. By changing the node structures to hold an array of sub-nodes instead this index computation time was significantly reduced. An additional benefit was about a 4x reduction in the number of memory blocks allocated. Each allocation block includes additional memory used by malloc() and free() so reducing the number of blocks reduces memory use and possible heap fragmentation.

Additional time was saved by only calculating the degree elements of the c and v nodes at start-up rather than for every frame. That data is kept in memory that is statically allocated when the decoder is initialized. This costs some memory but saves time.

This still left the code calling malloc several hundred times for each frame and then freeing that memory later. This sort of memory allocation activity has been known to cause troubles in some embedded systems and is usually avoided. However the LDPC decoder needed too much memory to allow it to be statically allocated at startup and not shared with other parts of the code.

Instead of allocating an array of sub-nodes for each c or v node, a single array of bytes is passed in from the parent. The initialization function which calculates the degree elements of the nodes also counts up the memory space needed and reports this to its caller. When the decoder is called for a frame, the node’s pointers are set to use the space of this array.

Other arrays that the decoder needs were added to this to further reduce the number of separate allocation blocks.

This leaves the decisions of how to allocate and share this memory up to a higher level of the code. The plan is to continue to use malloc() and free() at a higher level initially. Further testing can be done to look for memory leakage and optimise overall memory usage on the STM32.

PHI0

There is a non linear function named “phi0” which is called inside several levels of nested loops within the decoder. The basic operation is:

   phi0(x) = ln( (e^x + 1) / (e^x - 1) )

The original code used double precision exp() and log(), even though the input, output, and intermediate values are all floats. This was probably an oversight. Changing to the single single precision versions expf() and logf() provided some improvements, but not enough to meet our CPU load goal.

The original code used piecewise approximation for some input values. This was extended to cover the full range of inputs. The code was also structured differently to make it faster. The original code had a sequence of if () else if () else if () … This can take a long time when there are many steps in the approximation. Instead two ranges of input values are covered with linear steps that is implemented with table lookups.

The third range of inputs in non linear and is handled by a binary tree of comparisons to reduce the number of levels. All of this code is implemented in a separate file to allow the original or optimised version of phi0 to be used.

The ranges of inputs are:

             x >= 10      result always 0
      10   > x >=  5      steps of 1/2
       5   > x >= 1/16    steps of 1/16
    1/16   > x >= 1/4096  use 1/32, 1/64, 1/128, .., 1/4096
    1/4096 > x            result always 10

The range of values that will appear as inputs to phi0() can be represented with as fixed point value stored in a 32 bit integer. By converting to this format at the beginning of the function the code for all of the comparisons and lookups is reduced and uses shifts and integer operations. The step levels use powers of 2 which let the table index computations use shifts and make the fraction constants of the comparisons simple ones that the ARM instruction set can create efficiently.

Misc

Two of the configuration values are scale factors that get multiplied inside the nested loops. These values are 1.0 in both of the current configurations so that floating point multiply was removed.

Results

The optimised LDPC decoder produces the same output BER as the original.

The optimised decoder uses 12k of heap at init time and needs another 12k of heap at run time. The original decoder just used heap at run time, that was returned after each call. We have traded off the use of static heap to clean up the many small heap allocations and reduce execution time. It is probably possible to reduce the static space further perhaps at the cost of longer run times.

The maximum time to decode a frame using 100 iterations is 60.3 ms and the median time is 8.8 ms, far below our budget of 160ms!

Future Possibilities

The remaining floating point computations in the decoder are addition and subtraction so the values could be represented with fix point values to eliminate the floating point operations.

Some values which are computed from the configuration (degree, index, socket) are constants and could be generated at compile time using a utility called by cmake. However this might actually slow down the operation as the index computations might become slower.

The index and socket elements of C and V nodes could be pointers instead of indexes into arrays.

Experiments would be required to ensure these changes actually speed up the decoder.

Bio

Don got his first amateur license in high school but was soon distracted with getting an engineering degree (BSEE, Univ. of Washington), then family and life. He started his IC design career with the CPU for the HP-41C calculator. Then came ICs for printers and cameras, work on IC design tools, and some firmware for embedded systems. Exposure to ARES public service lead to a new amateur license – W7DMR and active involvement with ARES. He recently retired after 42 years and wanted to find an open project that combined radio, embedded systems and DSP.

Don lives in Corvallis, Oregon, USA a small city with the state technical university and several high tech companies.

Open Source Projects and Volunteers

Hi it’s David back again ….

Open source projects like FreeDV and Codec 2 rely on volunteers to make them happen. The typical pattern is people get excited, start some work, then drift away after a few weeks. Gold is the volunteer that consistently works week in, week out until their particular project is done. The number of hours/week doesn’t matter – it’s the consistency that is really helpful to the projects. I have a few contributors/testers/users of FreeDV in this category and I appreciate you all deeply – thank you.

If you would like to help out, please contact me. You’ll learn a lot and get to work towards an open source future for HF radio.

If you can’t help out technically, but would like to support this work, please consider Patreon or PayPal.

Reading Further

LDPC using Octave and the CML library. Our LDPC decoder comes from Coded Modulation Library (CML), which was originally used to support Matlab/Octave simulations.

Horus 37 – High Speed SSTV Images. The CML LDPC decoder was converted to a regular C library, and used for sending images from High Altitude Balloons.

Steve Ports an OFDM modem from Octave to C. Steve is another volunteer who put in a fine effort on the C coding of the OFDM modem. He recently modified the modem to handle high bit rates for voice and HF data applications.

Rick Barnich KA8BMA did a fantastic job of designing the SM1000 hardware. Leading edge, HF digital voice hardware, designed by volunteers.

Tony K2MO Tests FreeDV

Tony, K2MO, has recently published some fine videos of FreeDV 1600, 700C, and 700D passing through simulated HF channels. The results are quite interesting.

This video shows the 700C mode having the ability to decode with 50% of it’s carriers removed:

This 700C modem sends two copies of the tx signal at high and low frequencies, a form of diversity to help overcome selective fading. These are the combined at the receiver.

Tony’s next video shows three FreeDV modes passing through a selective fading HF channel simulation:

This particular channel has slow fading, a notch gradually creeps across the spectrum.

Tony originally started testing to determine which FreeDV mode worked best on NVIS paths. He used path parameters based on VOACAP prediction models which show the relative time delay and signal power for the each propagation mode i.e., F1, F2:

Note the long delay paths (5ms). The CCIR NVIS path model also suggests a path delay of 7ms. That much delay puts the F-layer at 1000 km (well out into space), which is a bit of a puzzle.

This video shows the results of the VOCAP NVIS path:

In this case 700C does better than 700D. The 700C modem (COHPSK) is a parallel tone design, which is more robust to long multipath delays. The OFDM modem used for 700D is configured for multipath delays of up to 2ms, but tends to fall over after that as the “O” for Orthogonal assumption breaks down. It can be configured for longer delays, at a small cost in low SNR performance.

The OFDM modem gives much tighter packing for carriers, which allows us to include enough bits for powerful FEC, and have a very narrow RF bandwidth compared to 700C. FreeDV 700D has the ability to perform interleaving (Tools-Options “FreeDV 700 Options”), which is a form of time diversity. This feature is not widely used at present, but simulations suggest it is worth up to 4dB.

It would be interesting to combine frequency diversity, LDPC, and OFDM in a wider bandwidth signal. If anyone is interested in doing a little C coding to try this let me know.

I’ve actually seen long delay on NVIS paths in the “real world”. Here is a 40M 700D contact between myself and Mark, VK5QI, who is about 40km away from me. Note at times there are notches on the waterfall 200Hz apart, indicating a round trip path delay of 1500km:

Reading Further

Modems for HF Digital Voice Part 1
, explaining the frequency diversity used in 700C
Testing FreeDV 700C, shows how to use some built in test features like noise insertion and interfering carriers.
FreeDV 700D
FreeDV User Guide, including new 700D features like interleaving

Simple Keras “Hello World” Example – Mean Removal

Inspired by the Wavenet work with Codec 2 I’m dipping my toe into the word of Deep Learning (DL) using Keras. I’ve read Deep Learning with Python (quite an enjoyable read) and set up a Linux box with a GTX graphics card that is making my teenage sons weep with jealousy.

So after a couple of days of messing about here is my first “hello world” Keras example: mean_removal.py. It might be helpful for other Keras noobs. Assuming you have all the packages installed, it runs with either Python 2:

$ python mean_removal.py

Or Python 3:

$ python3 mean_removal.py

It removes the mean from vectors, using just a single layer regression model. The script runs OK on a regular PC without a chunky graphics card.

So I start by generating vectors from random numbers with a zero mean. I then add a random offset to each sample in the vector. Here are 5 vectors with random offsets added to them:

The Keras script is then trained to estimate and remove the offsets, so the output vectors look like:

Estimating the offset is the same as finding the “mean” of the vector. Yes I know we can do that with a “mean” function, but where’s the fun in that!

Here are some other plots that show the training and validation measures, and error metrics at the output:



The last two plots show pretty much all of the offset is removed and it restores the original (non offset) vectors with just a tiny bit of noise. I had to wind back the learning rate to get it to converge without “NAN” losses, possibly as I’m using fairly big input numbers. I’m familiar with the idea of learning rate from NLMS adaptive filters, such as those used for my work in echo cancellation.

Deep Learning for Codec 2

My initial ambitions for DL are somewhat more modest than the sample-by-sample synthesis used in the Wavenet work. I have some problems with Vector Quantisation (VQ) in the low rate Codec 2 modes. The VQ is used to compactly describe the speech spectrum, which carries the intelligibility of the signal.

The VQ gets upset with different microphones, speakers, or minor spectral shaping like gentle high pass/low pass filtering. This shaping often causes a poor vector to be chosen, which results in crappy speech quality. The current VQ error measure can’t tell the difference between spectral features that matter and those that don’t.

So I’d like to try DL to address those issues, and train a system to say “look, this speech and this speech are actually the same. Yes I know one of them has a 3dB/octave low pass filter, please ignore that”.

As emphasised in the text book above, some feature extraction can help with DL problems. For my first pass I’ll be working on parameters extracted by the Codec 2 model (like a compact version of the speech spectrum) rather than speech samples like Wavenet. This will reduce my CPU load significantly, at the expense of speech quality, which will be limited by the unquantised Codec 2 model. But that’s OK as a first step. A notch or two up on Codec 2 at 700 bit/s would be very useful, especially if it can run on a CPU from the first two decades of the 21st century.

Mean Removal on Speech Vectors

So to get started with Keras I chose mean removal. The mean level or constant offset is like the volume or energy in a speech signal, its the simplest form of spectral shaping I could imagine. I trained and tested it with vectors of random numbers, using numbers in the range of the speech spectral samples that Codec 2 plays with.

It’s a bit like an equaliser, vectors with arbitrary spectral shaping go in, “flat” unshaped vectors come out. They can then be sent to a Vector Quantiser. There are probably smarter ways to do this, but I need to start somewhere.

So as a next step I tried offset removal with vectors that represent the spectrum of 40ms speech frame:


This is pretty cool – the network was trained on random numbers but works well with real speech frames. You can also see the spectral slope I mentioned above, the speech energy gradually falls off at high frequencies. This doesn’t affect the intelligibility of the speech but tends to upset traditional Vector Quantisers. Especially mine.

Now that I have something super-basic working, the next step is to train and test networks to deal with some non-trivial spectral shaping.

Reading Further

Deep Learning with Python
WaveNet and Codec 2
Codec 2 700C, the current Codec 2 700 bit/s mode. With better VQ we can improve on this.
Codec 2 at 450 bit/s, some fine work from Thomas and Stefan, that uses a form of machine learning to synthesise 16 kHz speech from 8 kHz inputs.
FreeDV 700D, the recently released FreeDV mode that uses Codec 2 700C. A FreeDV Mode also includes a modem, FEC, protocol.
RNNoise: Learning Noise Suppression, Jean-Marc’s DL network for noise reduction. Thanks Jean-Marc for the brainstorming emails!

Codec 2 2200

FreeDV 700D has shown digital voice can outperform SSB at low SNRs over HF multipath channels. With practice the speech quality is quite usable, but it’s not high quality and is sensitive to different speakers and microphones.

However at times it’s like magic! The modem signal is buried in all sorts of HF noise, getting hammered by fading – then one of my friends 800km away breaks the FreeDV squelch (at -2dB!) and it’s like they are in the room. No more hunched-over-the-radio listening to my neighbours plasma TV power supply.

There is a lot of promise in FreeDV, and it’s starting to come together.

So now I’d like to see what we can do at a higher SNR, where SSB is an armchair copy. What sort of quality can we develop at 10dB SNR with light fading?

The OFDM modem and LDPC Forward Error Correction (FEC) used for 700D are working well, so the idea is to develop a high bit rate/high(er) quality speech codec, and adapt the OFDM modem and LPDC code.

So over the last few months I have been jointly designed a new speech codec, OFDM modem, and LDPC forward error correction scheme called “FreeDV 2200”. The speech codec mode is called “Codec 2 2200”.

Coarse Quantisation of Spectral Magnitudes

I started with the design used for Codec 2 700C. The resampling step is the key, it converts the time varying number of harmonic amplitudes to fixed number (K=20) of samples covering 100 to 4000 Hz. They are sampled using the “mel” scale, which means we take more finely spaced samples at low frequencies, with coarser steps at high frequencies. This matches the log frequency response of the ear. I arrived at K=20 by experiment.

This Mel-resampling is not perfect. Some samples, like hts1a, have narrow formants at higher frequencies that get undersampled by the Mel resampling, leading to some distortion. Linear Predictive Coding (LPC) (as used in many Codec 2 modes) does a better job for some of these samples. More work required here.

In Codec 2 700C, we vector quantise the K=20 Mel sample vectors. For this new Codec 2 mode, I experimented with directly quantising each sample. To my surprise, I found very little difference in speech quality with coarsely quantised 6dB steps. Furthermore, I found we can limit the rate of change between samples to a maximum of +/-12 dB. This allows each sample to be delta-coded in frequency using a step of 0, -6, +6, -12, or +12dB. Using a 2 or 3 bit Huffman coding approach it turns out we need 45-ish bit/s frame for good quality speech.

After some experimentation with bit errors, I ended up using a fixed bit allocation rather than Huffman coding. Huffman coding uses variable length symbols (in my case 2 and 3 bits). After a bit error you don’t know where the next variable length symbol in the string starts and ends. So a single bit error early in the bits describing the spectrum can corrupt the entire spectrum.

Parameter Bits Per Frame
Spectral Magnitude 44
Energy 4
Pitch 7
Voicing 1
Total 56

At a 25ms frame update rate this is 56/0.025 = 2240 bits/s.

Here are some samples that show the effect of the processing stages. During development it’s useful to listen to the effect each stage has on the codec speech quality, for example to diagnose problems with a sample that codes poorly.

Original Listen
Codec 2 Unquantised 12.5ms frames Listen
Synthesised phases Listen
K=20 Mel Listen
Quantised to 6dB steps Listen
Fully Quantised with 44 bits/25ms frame Listen

Here is a plot of 50 frames of coarsely quantised amplitude samples, the 6dB steps are quite apparent:

Listening Tests

To test the prototype 2200 mode I performed listening tests using 15 samples. I have collected these samples over time as examples that tend to challenge speech codecs and in particular code poorly using FreeDV 1600 (i.e. Codec 2 1300). During my listening tests I use a small set of powered speakers and for each sample chose which codec algorithm I prefer, then average the results over the 15 samples.

Over the 15 samples I felt 2200 was just in front of 2400 (on average), and well in front to 1300. There were some exceptions of course – it would useful to explore them some time. I was hoping that I could make 2200 significantly better than 2400, but ran out of time. However I am pleased that I have developed a new method of spectral quantisation for Codec 2 and come up with a fully quantised speech codec based on it at such an early stage.

Here are a few samples processed with various speech codecs. On some of them 2200 isn’t as good as 2400 or even 1300. I’m presenting some of the poorer samples on purpose – the poorly coded samples are the interesting ones worth investigating. Remember the aims of this work are significant improvements over (i) Codec 2 1300, and (ii) SSB at around 10dB SNR. See what you think.

Sample 1300 2400 2200 SSB 10dB
hts1a Listen Listen Listen Listen
ve9qrp Listen Listen Listen Listen
mmt1 Listen Listen Listen Listen
vk5qi Listen Listen Listen Listen
1 vk5local Listen Listen Listen Listen
2 vk5local Listen Listen Listen Listen

Further Work

I feel the coarse quantisation trick is a neat way to reduce the information in the speech spectrum and is worth exploring further. There must be more efficient, and higher quality ways to encode this information. I did try some vector quantisation but wasn’t happy with the results (I always seem to struggle with VQ). However I just don’t have the time to explore every R&D path this work presents.

Quantisers can be stored very efficiently, as the dynamic range of the spectral sample is low (a few bits/sample) compared to floating point numbers. This simplifies storage requirements, even for large VQs.

Machine learning techniques could also be applied, using some of the ideas in this post as “pre-processing” steps. However that’s a medium term project which I haven’t found time for yet.

In the next post, I’ll talk about the modem and LDPC codec required to build FreeDV 2200.

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:

Samples

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

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.

Results

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)

Significance

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
README_fdmdv.txt
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