Skip to content
pschatzmann edited this page Oct 29, 2026 · 1 revision

Olivia is an MFSK mode with a strong forward error correction, designed by Pawel Jalocha for weak signal keyboard chat in amateur radio. Each symbol is one of 2 to 256 tones. The characters are sent in blocks: each character is a Walsh-Hadamard code word of 64 bits, which is scrambled and spread diagonally over the 64 symbols of the block, so that a disturbed symbol only damages a few bits of many characters. Contestia is the faster variant with 6 bit characters (upper case text) and blocks of 32 symbols. Both are implemented like in fldigi.

Advantages

  • Very robust: olivia-32-1000 decodes about 12 dB below the noise (in 3 kHz bandwidth).
  • Robust against frequency offsets (the receiver searches ±8 bins) and different clocks.
  • The same signal as fldigi (the tones, the symbol shape and the error correction follow fldigi's source code).
  • No configuration of the receiver beyond the mode: it finds the timing and the frequency itself.

Disadvantages

  • Very slow: 1 to 5 characters per second (olivia-32-1000: 2.4).
  • 7 bit text: Olivia sends bytes above 127 escaped with 127 (like fldigi with 8 bit characters), so the byte 127 itself can't be sent. Contestia only sends upper case letters, digits and some punctuation.
  • The receiver needs more CPU and memory than the simple modems (about 30 KB for olivia-32-1000).
  • The receiver shows random characters in pure noise, like fldigi without squelch.
  • The interoperability with fldigi has not been tested with fldigi itself.

Modes

The mode name is olivia-T-B or contestia-T-B with T tones (2 - 256, a power of 2) and a bandwidth B of 125, 250, 500, 1000 or 2000 Hz (also 1k, 2k). Common modes:

Mode Symbols/s Characters/s
olivia-4-125 31.25 0.98
olivia-8-250 31.25 1.46 popular
olivia-8-500 62.5 2.9
olivia-16-500 31.25 1.95
olivia-32-1000 (olivia) 31.25 2.44 fldigi default
olivia-64-2000 31.25 2.9
contestia-8-250 (contestia) 31.25 2.9
contestia-32-1000 31.25 4.9

The signal is centered at 1500 Hz (center_freq, -M on the command line). The signal is defined at 8000 Hz: other sample rates are converted.

Usage

I2SStream i2s;
OliviaStream modem(i2s, RXTX_MODE);

void setup() {
  OliviaConfig cfg = modem.defaultConfig();  // olivia-32-1000
  cfg.setMode("olivia-8-250");
  cfg.sample_rate = 8000;
  ...
  modem.begin(cfg);
}

void loop() {
  modem.println("CQ CQ de N0CALL");
  modem.flush();  // sends the last block and the stop tones
  while (modem.available()) Serial.write(modem.read());
}
echo "CQ CQ de N0CALL" | tinymodem --tx -f cq.wav olivia-8-250
tinymodem --rx -f cq.wav olivia-8-250

OliviaConfig, OliviaEncoder, OliviaDecoder, OliviaStream, OliviaStreamRTOS and OliviaCodec (the error correction) are the Olivia classes. Characters are collected until a block is complete (bits per symbol characters): call flush() (or endTransmission()) to send an incomplete block, which is filled with the idle character 0. On the command line, -R, -v, -M (center frequency), -q, --rx-one and --Xrxnoise are supported.

OliviaConfig field Default
tones 32 Number of tones (2 - 256)
bandwidth 1000 Bandwidth (125, 250, 500, 1000 or 2000 Hz)
center_freq 1500 Center frequency (Hz)
contestia false Contestia instead of Olivia
volume 1.0 Transmit amplitude
start_tones true Transmitter: send fldigi's start and stop tones at the band edges
sync_margin 8 Receiver: frequency search range in FFT bins (tone spacing / 2), limited to the audio band
sync_integration 4 Receiver: integration period of the synchronizer in blocks
sync_threshold 3.0 Receiver: minimum signal to noise ratio
rx_one false Receiver: stop after the first transmission
channel 0 Receiver: input channel
sample_rate, channels, bits_per_sample 48000, 1, 16 Audio format

How it works

The transmitter sends each symbol as a tone with a raised cosine envelope over two symbol periods, so consecutive symbols overlap by half. The phase continues from symbol to symbol with a random step of ±90 degrees, like fldigi. Each character (7 bits, Contestia 6 bits) becomes a Walsh-Hadamard code word of 64 (32) bits with an inverse fast Hadamard transform. The code word is scrambled with a fixed sequence and its bits are spread diagonally over the symbols of the block. A tone carries bits per symbol bits (Gray coded). A transmission starts and ends with the fldigi start/stop tones at the band edges.

The receiver converts the input to 8000 Hz. Twice per symbol it calculates the tone energies with an FFT and converts them to soft bits for each possible frequency offset. For each time slice and frequency offset, the last block is decoded after every symbol with a fast Hadamard transform: the strongest correlation is the character. The synchronizer integrates the signal and noise of these decodings over a few blocks and selects the timing and frequency with the strongest signal. A block is provided if its signal to noise ratio is above the threshold. Blocks of steady tones (like the start and stop tones) are rejected. The idle character 0 is not provided.

Measured performance

Measured at 8000 Hz:

  • Noise: olivia-32-1000 without errors up to noise 6 (about -12.5 dB SNR in 3 kHz bandwidth), Contestia contestia-8-250 up to 5.
  • Frequency offset: ±60 Hz with olivia-32-1000.
  • Different clocks: ±0.2% (olivia-16-500).
  • Speed: 270 × real time on a PC. On microcontrollers at 8000 Hz: 2.6 (ESP32-S3) and 5 (ESP32-P4) × real time for olivia-32-1000. The RP2040 (0.5) is too slow for real time decoding, see Performance.
  • Over the air on the ESP32-P4 (speaker to the onboard microphone of the same board): olivia-32-1000, olivia-8-500 and contestia-8-250 were received without errors.

Clone this wiki locally