Software Defined Radio - how does it work? Part 6

  • Tutorial
Hi, Habr.

In the previous part, we looked at the possibility of transmitting simple signals using GNU Radio. Now we will go further and see how to convey something more complicated. Let's start with the amateur radio signals WSPR, and then create a working software QAM modem.

And as in the previous case, we will do this without writing a single line of code, the program will also be cross-platform, and will be able to work both under OSX / Linux and under Windows. I will also show how to debug a modem using GNU Radio, without any hardware at all.

Continued under the cut.

For those who have not used GNU Radio, it is recommended to read parts 4 and 5 , which described the principles of working with the program.


Let's start with a simpler one, with WSPR - this type of communication was specially made for propagation tests of weak signals, i.e. what we need is a power level of devices like LimeSDR no more than 100mW. The WSPR signal is transmitted at a very low speed (2 minutes per message of approximately 30 bytes) in a very narrow band, which allows it to be received even below the noise level. Transmit such a signal using GNU Radio.

First you need to record the signal. To do this, take the WSJT program, set all the necessary parameters (power, call sign, location, etc.). Specify Virtual Audio Cable as the output audio device in the settings, and record the signal in WAV. Pauses at the edges need to be cut off in any editor (for example, Cool Edit), as a result, we should get a file with a duration of about 2 minutes.

Now create a graph in the GNU Radio Companion.

This method does not claim to be maximum efficiency, but it is quite simple and straightforward. The WSPR signal is initially at a frequency of 1500 Hz, the recorded wav file has a sampling frequency of 22050 s / s. First, we resample the signal 57/5 times to bring the sampling frequency to the required 250.000 s / s. Then we shift the frequency up by 10KHz (the useful signal will be at the frequency of 11.5KHz), translate the signal into the complex form required by the receiver, and cut out the excess filter, leaving the frequencies 11-12KHz.

WSPR signals are time-bound and transmitted every even minute (0:00, 0:02, etc.). I started the transfer to GNU Radio manually, by eye, determining the interval by the hour, anyone who wants can add a script in Python to automatically start the transfer.

We are waiting for the right time, turn on the transmitter, receiver, and check the result.

If you wish, you can also add automatic generation of a WSPR file to the program based on input data (call sign, location, transmit power), examples of WSPR generation for Python can be taken on github.

It is interesting to note that at 432 MHz the frequency drift is already quite noticeable, although the signal is still decoded. But at a frequency of 1.3 GHz, the drift becomes so large that WSPR is no longer accepted - an external reference generator with a more stable signal is needed for SDR (or at least software frequency correction during transmission, although this is less convenient).

If the SDR allows you to transmit at low frequencies, then you can try transmission on the HF band. Thus, with HackRF, it was possible to transmit a signal at 1000 km at 14 MHz from a room antenna, which can be considered a good result. Although the high frequencies (433 MHz and 1.3 GHz) are perhaps even more interesting for experiments, the signals are transmitted only in direct line of sight, so for such experiments you need a second participant on the receiving side. The second plus of such tests is that only the lazy did not transmit to HF in wspr, but high frequencies are much less mastered. So, with boards like LimeSDR or USRP, you can carry out quite interesting experiments with reception and transmission at ultra-high frequencies.

QAM Modem

Let's go further. WSPR is a fairly simple format, let's do something more interesting - a full-fledged (well, almost) modem. With quadrature amplitude modulation, both the amplitude and phase of the signal simultaneously change, which allows data to be transmitted at a higher speed (but the occupied band is also larger).

Consider the first part - the transmitter.

As you can see, we read the data from the data.txt file, then with a sampling frequency of 25KHz we send data to the packet encoder, which converts the stream into a 4-bit code. This stream goes to the quadrature modulator, then the sampling frequency is increased to a transmitter frequency of 250KHz, and the signal is shifted upward by 80KHz (many receivers have a peak at zero frequency, and this will interfere). The Constellation Rect component sets the modulation parameters - the number of characters and the phase and amplitude shift.

The first part is ready. We start the “transfer” and see our signal.

We can test our transmitter without even having any equipment - for this there is a special Channel Model block - a communication channel model. There you can set the noise, frequency shift, etc.

This is what our signal looks like before and after the transmission. By the way, as you can see, all the "harmonics" of the transmitter below 120 dB have gone much lower than the noise level.

Now the reception. In fact, the same thing, only in reverse order.

Separately, you can stop at the last UDP Sink block. It is not clear why, but in GNU Radio there is no component for viewing text data. Therefore, we simply send data via UDP to any local port (I chose 999).

To receive, we write a simple program in Python.

import socket
UDP_IP = ""
UDP_PORT = 999
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
sock.bind((UDP_IP, UDP_PORT))
while True:
    data, addr = sock.recvfrom(64) # buffer size is 64 bytes
    print("Msg:", data)
  except socket.timeout:

Result: run the script, run GNU Radio, and see the received messages in the console.

As you can see, everything works, and you can not have either a receiver or antennas :)

For those who want to repeat the experiments, the project grc-file is under the spoiler. It should work on both Linux and Windows.

Mon May 27 21:52:42 2019optionsauthorwindow_sizecategory[GRC Hier Blocks]commentdescription_enabledTrue_coordinate(8, 8)_rotation0generate_optionsqt_guihier_block_src_path.:idtop_blockmax_nouts0qt_qss_themerealtime_schedulingrun_command{python} -u {filename}run_optionspromptrunTruethread_safe_setterstitlevariablecomment_enabledTrue_coordinate(1144, 172)_rotation0idexcess_bwvalue0.35variablecomment_enabledTrue_coordinate(1104, 436)_rotation0idnfiltsvalue32variablecomment_enabledTrue_coordinate(1096, 588)_rotation0idnfilts_0value32variable_constellation_rectcommentconst_points[0.707+0.707j, -0.707+0.707j, -0.707-0.707j, 0.707-0.707j]_enabledTrue_coordinate(1104, 16)_rotation0idqpskimag_sect2real_sect2rot_sym4soft_dec_lutNoneprecision8sym_map[0, 1, 2, 3]w_imag_sect1w_real_sect1variablecomment_enabledTrue_coordinate(1104, 372)_rotation0idrrc_tapsvaluefirdes.root_raised_cosine(nfilts, nfilts, 1.0/float(sps), 0.35, 45*nfilts)variablecomment_enabledTrue_coordinate(1112, 508)_rotation0idrrc_taps_0valuefirdes.root_raised_cosine(nfilts, nfilts, 1.0/float(sps), 0.35, 45*nfilts)variablecomment_enabledTrue_coordinate(168, 12)_rotation0idsamp_ratevalue250000variablecomment_enabledTrue_coordinate(1144, 244)_rotation0idspsvalue4variablecomment_enabledTrue_coordinate(1104, 308)_rotation0idtiming_loop_bwvalue6.28/100.0analog_sig_source_xamp1aliascommentaffinity_enabledTruefreq80000_coordinate(664, 20)_rotation0idanalog_sig_source_x_0maxoutbuf0minoutbuf0offset0typecomplexsamp_ratesamp_ratewaveformanalog.GR_COS_WAVEanalog_sig_source_xamp1aliascommentaffinity_enabledTruefreq-80000_coordinate(48, 540)_rotation0idanalog_sig_source_x_1maxoutbuf0minoutbuf0offset0typecomplexsamp_ratesamp_ratewaveformanalog.GR_COS_WAVEblks2_packet_decoderaccess_codealiascommentaffinity_enabled1_coordinate(296, 676)_rotation0idblks2_packet_decoder_0maxoutbuf0minoutbuf0typebytethreshold-1blks2_packet_encoderaccess_codebits_per_symbol4aliascommentaffinity_enabled1_coordinate(224, 76)_rotation0idblks2_packet_encoder_0typebytemaxoutbuf0minoutbuf0pad_for_usrpTruepayload_length0preamblesamples_per_symbol4blocks_file_sourcealiascommentaffinity_enabled1fileD:\MyProjects\GNURadio\data.txt_coordinate(8, 92)_rotation0idblocks_file_source_0maxoutbuf0minoutbuf0typebyterepeatTruevlen1blocks_multiply_xxaliascommentaffinity_enabledTrue_coordinate(920, 88)_rotation0idblocks_multiply_xx_0typecomplexmaxoutbuf0minoutbuf0num_inputs2vlen1blocks_multiply_xxaliascommentaffinity_enabledTrue_coordinate(224, 496)_rotation0idblocks_multiply_xx_1typecomplexmaxoutbuf0minoutbuf0num_inputs2vlen1blocks_throttlealiascommentaffinity_enabled1_coordinate(96, 196)_rotation0idblocks_throttle_1ignoretagTruemaxoutbuf0minoutbuf0samples_per_second25000typebytevlen1blocks_udp_sinkaliascommentaffinityipaddr127.0.0.1port999_enabled1_coordinate(680, 660)_rotation0idblocks_udp_sink_0typebytepsize64eofTruevlen1channels_channel_modelaliasblock_tagsFalsecommentaffinity_enabled1epsilon1.0freq_offset0.0_coordinate(504, 284)_rotation180idchannels_channel_model_0maxoutbuf0minoutbuf0noise_voltage0.1seed0taps1.0 + 1.0jdigital_qam_demodaliascommentaffinitydifferentialTrue_enabled1excess_bw0.35freq_bw6.28/100.0_coordinate(672, 456)_rotation0mod_code"gray"iddigital_qam_demod_0logFalsemaxoutbuf0minoutbuf0constellation_points4phase_bw6.28/100.0samples_per_symbol4timing_bw6.28/100.0verboseFalsedigital_qam_modaliascommentaffinitydifferentialTrue_enabledTrueexcess_bw0.35_coordinate(384, 116)_rotation0mod_code"gray"iddigital_qam_mod_0logFalsemaxoutbuf0minoutbuf0constellation_points4samples_per_symbol4verboseFalselow_pass_filterbeta6.76aliascommentaffinitycutoff_freq12000decim1_enabledTruetypefir_filter_ccf_coordinate(320, 460)_rotation0gain1idlow_pass_filter_0interp1maxoutbuf0minoutbuf0samp_ratesamp_ratewidth1000winfirdes.WIN_HAMMINGqtgui_const_sink_xautoscaleFalseaxislabelsTruealiascommentaffinity_enabled1_coordinate(456, 20)gui_hint_rotation0gridFalseidqtgui_const_sink_x_0legendTruealpha11.0color1"blue"label1marker10style10width11alpha101.0color10"red"label10marker100style100width101alpha21.0color2"red"label2marker20style20width21alpha31.0color3"red"label3marker30style30width31alpha41.0color4"red"label4marker40style40width41alpha51.0color5"red"label5marker50style50width51alpha61.0color6"red"label6marker60style60width61alpha71.0color7"red"label7marker70style70width71alpha81.0color8"red"label8marker80style80width81alpha91.0color9"red"label9marker90style90width91name""nconnections1size1024tr_chan0tr_level0.0tr_modeqtgui.TRIG_MODE_FREEtr_slopeqtgui.TRIG_SLOPE_POStr_tag""typecomplexupdate_time0.10xmax2xmin-2ymax2ymin-2rational_resampler_xxxaliascommentaffinitydecim1_enabledTruefbw0_coordinate(648, 148)_rotation0idrational_resampler_xxx_0interp10maxoutbuf0minoutbuf0tapstypecccrational_resampler_xxxaliascommentaffinitydecim10_enabledTruefbw0_coordinate(480, 484)_rotation0idrational_resampler_xxx_0_0interp1maxoutbuf0minoutbuf0tapstypecccanalog_sig_source_x_0blocks_multiply_xx_000analog_sig_source_x_1blocks_multiply_xx_101blks2_packet_decoder_0blocks_udp_sink_000blks2_packet_encoder_0digital_qam_mod_000blocks_file_source_0blocks_throttle_100blocks_multiply_xx_0channels_channel_model_000blocks_multiply_xx_1low_pass_filter_000blocks_throttle_1blks2_packet_encoder_000channels_channel_model_0blocks_multiply_xx_100digital_qam_demod_0blks2_packet_decoder_000digital_qam_mod_0qtgui_const_sink_x_000digital_qam_mod_0rational_resampler_xxx_000low_pass_filter_0rational_resampler_xxx_0_000rational_resampler_xxx_0blocks_multiply_xx_001rational_resampler_xxx_0_0digital_qam_demod_000

A good guide to creating a more sophisticated modem is on the GNU Radio website , but they use a custom block for demodulation, which was only launched under Linux. There is no problem with this in the above example.


As you can see, GNU Radio is a rather interesting program for working with signals in different ways, in which you can do many different interesting things. If the audience does not lose interest (there is a feeling that I delve into narrow topics that are of little interest to most), you can try to consider transmitting something more interesting, for example, a video.

Also popular now: