Raspberry Pi based PSK31 transmitter

Theory and foreword

I wrote a previous post (in Spanish) about PSK31 modulation and coding. In this post I show a working prototype of a PSK31 transmitter based on Raspbery Pi (from now on, RPi). This is a small size, low weight, power and cost microcomputer capable of running a Debian-like Linux distribution. Many amateurs use RPi as a personal computer or as a plataform for automated, autonomous or remote systems. Those are, in my opinion, the uses where RPi is most advantageous.

RPi has analog audio and video outputs and a dual row pin header for general purpose (GPIO), serial port, I2C, SPI signals and, most interesting for us, a built-in PLL synthesizer capable of generating frequencies between 250 kHz and 250 MHz. Its output is a 3,3V square CMOS waveform.

Input and output ports in RPi model B.

There are programs which can modulate the frequency and amplitude of this PLL for generating FM, SSB, WSPR or even digital TV:

[https://github.com/F5OEO/rpitx] Set of programs for almost any modulation.

[https://github.com/fotografAle/NBFM] Simple program for narrow band FM.

[https://www.tapr.org/kits_20M-wspr-pi.html] WSPR beacon.

[https://github.com/F5OEO/rpidatv] Digital television.

Summarizing, we want to multiply an RF carrier by a slow modulator signal consisting of 32 ms long symbols which can be constant ‘1’, constant ‘-1’ or a half cycle 15,625 Hz cosine transition:

RPi’s output audio can not generate the base band modulator because, as shown in its schematics, there is a series capacitor which blocks DC and low frequencies:

Esquema de la salida de audio
Audio output schematics

In theory it would be feasible to shorten capacitors C34 and C48 and get the modulator signal through the audio jack. I have not tried this. I had a software made which can generate the signal by means of GPIO pins as sigma delta digital to analog converter (from now on, DAC): it works by quickly toggling its value between ‘0’ and ‘1’ in such way that its average changes with time according to the desired level. A low pass filter rejects the high frequency components and obtains the smooth modulator waveform. A sigma delta is a kind of 1 bit DAC commonly used in audio apliances. Its working principles are well explained in this web.

Sigma delta DAC. Simulated graph.

The hardware

The modulator signal is differentially generated on pins 11 and 12 on the header, which correspond to GPIO signals 17 and 18 in the CPU. These pins always have complementary or negated signals between each other: when one is ‘0’ the other one is ‘1’. Their switching frequency is about 35 kHz:

Sigma-delta DAC pulses. Real measurement on pins 11 and 12.


The previous signal is fed on the following circuit. DAC pulses are filtered with an RC network with a 4.7 ms time constant. I chose to couple the modulator signal by means of an AD8132 which is an FDA (fully differential amplifier), as it has two balanced outputs:

PSK31 modulator


This is the waveform on the leads of the 470 nF capacitor:

Filtered modulator

The carrier can be generated with some of the existing programs shown above or with a custom one which reuses the code portion for controlling the PLL. A commonly used mixer is SA602; it contains the necesary blocks and it can be powered from 4.5 V; it is therefore compatible with the 5 V rail present in the pin header. If we look at SA602 datasheet, it seems mandatory to AC couple the signal in order not to alter the bias point:

The RF inputs (Pins 1 and 2) are biased internally. They are symmetrical. The equivalent AC input impedance is approximately 1.5k || 3pF through 50MHz. Pins 1 and 2 can be used interchangeably, but they should not be DC biased externally.

But if we read AD8132 documentation we can find the way to DC couple the signal without disturbing the bias, if the common mode impedance is high:

The modulator is differentially generated, filtered and coupled into SA602 pins 1 and 2; the signal is balanced around a 1.9V common level imposed by SA602. It would be more dificult to do this in single ended mode than differentially due to the required DC bias level. SA602 has a notable gain so the modulator signal is previously attenuated; it is enough to feed 200 mVpp per pin (400 mVpp differential swing).

Measured signal on SA602 pins 1 and 2.


SA602 outputs about 500 mVpp in pins 4 and 5 (1 Vpp differential) with a 1500 Ω impedance. Gain between “RF” and “OUT” depends on the local oscillator amplitude. This signal is fed into a two stage amplifier consisting of an emitter follower and a class A common emitter amp. It is able to deliver about 20 and 50 mW in 14 and 7 MHz respectively. This circuit can surely be improved.


Decoupling capacitors have been omitted for simplicity. All 5V points must have a 100 nF ceramic capacitor to ground.

I built the circuit shown in the picture below. The board has a female header which fits into the RPi male pins. It is powered at 5 V, available from header pins 1 and 2, and it draws 130 mA (the same as a usb WiFi dongle). The PLL carrier comes from pin 7 (GPIO 4) and the differential modulator from pins 11 and 12 (GPIO signals 17 and 18).

PSK31 transmitter prototype for Raspberry Pi

The software

Download files Makefile.txt, varicode.c and psk31.c into a new folder in your RPi. Rename “Makefile.txt” for “Makefile” without extension.

Makefile.txt md5sum: 04636b0d03a7f4b4ec675ba25c832f2c

varicode.c md5sum: 923d027791bca3dbcd4bf6d5cee10d94

psk31.c md5sum: 1d94cf3c893014de1b0b9f6255394b3c

Run “make”. It takes 8 seconds to compile and generate two executables: “psk31” and “varicode”.

The service must be run with root permissions. Help is shown with this command:

pi@raspberrypi ~/psk31 $ ./psk31 --help
 --amplitude=<n> Signal amplitude (0 .. 1]
 --clock-div=<n> Fractional divisor for carrier [4096 .. 16773120]
 Note: frequency = 500 MHz / (clock-div / 4096)
 --frequency=<f> Carrier frequency, in MHz [0.125 .. 500]
 Note: this is overridden by clock-div
 --help Show this help
 --mash=<n> Set number of MASH stages [0 .. 3]
 --pcm Use PCM clock instead of PWM clock for signal generation
 --rc=<f> Set signal filter RC value (s)
 --timeout=<n> Number of zeros before switching off. 0 for infinite.

Amplitude should be the highest. “mash” parameter should be set to ‘1’. A ‘0’ value disables fractional pll values above 25 MHz. “rc” time constant should be that of the filter we have built. If we run the program with these parameters, an unmodulated carrier is sent:

sudo ./psk31 --mash=1 --rc=0.0047 --timeout=20 --frequency=7.042

No root permissions are needed for sending text. We can dump the content on the transmission buffer through /dev/psk31.data device as shown:

echo "CQ CQ" > /dev/psk31.data

cat fichero.txt > /dev/psk31.data

In this way:

cat > /dev/psk31.data

we enter an interactive mode. We can write in the console and <Enter> sends the line to the buffer.

We can run this command to know the state of the service:

nc -U /dev/psk31.stat

Which returns:

amplitude 0.900000
rc 0.004700
clock_div 290826
clock_mash 1
clock_freq 7.042011
timeout 20
pending_char 53

To stop the service:

sudo killall psk31

The actual divider value is not the “clock_div” number. The pll has a 500 MHz reference which is divided by a number with integer and fractional parts each represented with 12 bits. That is to say, it can divide fractions 2^12 or 4096 times smaller than one. In this case, 290826 means 500 is divided by 71 + 10/4096 (as 71·4096=290816). We have launched the service for 7.042 MHz which is obtained as 500 · 4096 / 290826 = 7.042.

This also means the resolution (the frequency step) is not fixed, but dependent on the starting frequency. Being ‘N’ an integer number between 2^13 and 2^23 that is 8.192 and 8.388.608, by means of this equation:

F = 500e6/(N/4096) [Hz]

we get frequencies between 244 kHz and 250 MHz. The rest of dividing N by 4096 (2¹²) is the fractional part.

The resolution is:

f1-f2 = 500e6/(N/4096) – 500e6/((N+1)/4096))

After some algebra:

f1 – f2 = f1² / (500e6*4096) [Hz]

Which means the resolution step is proportional to the square of the frequency.

For example, in 145 MHz  10 kHz.
In 50 MHz the step is 1,2 kHz.
In 28 MHz the step is 382 Hz.
In 7 MHz the step is 24 Hz.

This video shows the first trials of the prototype, without amplification, transmitting a short text across the room:

Conclusions and future prospects

My signal in 20 and 40 meters bands was heard 5 km away. This circuit can be improved regarding the schematic, pcb design and component election, but its goal was proving the feasibility of the idea. It is not supposed to be a convinient way to make QSOs because, among other things, a separate receiver and decoder would be needed. But it could be useful as an autonomous beacon for telemetry or propagation study. Moreover, the sigma-delta and the baseband coupling are tools which can be utilized in different projects.

The circuit could be improved in the following ways: pin 16 (gpio 23) is set at high level when the service is running. It could be used for biasing the base of 2N2222 transistor and prevent it from drawing current when unnecessary. The oscillator has a square waveshape and a dedicate filter for each band should be used. Likewise, SA602 differential output would be better coupled by means of a transformer in order to reduce distorsion and duplicate power. The same circuit can be used with the programs indicated above for transmitting other modulations, save for rpitx which makes use of pin 12 (gpio 18) for the PLL output instead of pin 7 (gpio 4); the connections should be made interchangeable.