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