Skip to main content

Overview

Minimodem uses Frequency Shift Keying (FSK) modulation to encode digital data as audio tones. FSK is a form of frequency modulation where binary data is represented by shifting between two distinct frequencies.

How FSK Works

In FSK modulation, digital bits are represented by two different frequencies:
  • Mark frequency (f_mark): Represents binary 1
  • Space frequency (f_space): Represents binary 0
The terms “mark” and “space” originate from telegraphy, where “mark” meant current flowing (line closed) and “space” meant no current (line open).

Signal Representation

Binary: 1    0    1    1    0    0    1
        │    │    │    │    │    │    │
Audio:  mark space mark mark space space mark
        │    │    │    │    │    │    │
Freq:   1270 1070 1270 1270 1070 1070 1270  (Hz, Bell 103 example)

Frequency Detection

Minimodem uses FFT (Fast Fourier Transform) analysis to detect FSK signals. The process involves:
  1. Sampling: Audio input is sampled at a configurable rate (default 48000 Hz)
  2. FFT Analysis: Samples are transformed into frequency bands
  3. Band Detection: The magnitude of mark and space frequency bands are compared
  4. Bit Determination: The frequency with higher magnitude determines the bit value
The FSK implementation is in src/fsk.c:fsk_bit_analyze() which uses FFTW3 library for FFT computation.

FSK Plan Structure

The FSK decoder maintains a “plan” structure containing:
ParameterDescription
sample_rateAudio sample rate (e.g., 48000 Hz)
f_markMark frequency in Hz
f_spaceSpace frequency in Hz
band_widthFilter bandwidth for frequency detection
b_markMark frequency band index in FFT
b_spaceSpace frequency band index in FFT
typedef struct fsk_plan {
    float sample_rate;
    float f_mark;
    float f_space;
    float band_width;
    unsigned int b_mark;
    unsigned int b_space;
    fftwf_plan fftplan;
    // ... FFT buffers
} fsk_plan;

Confidence Calculation

Minimodem calculates a confidence value for each decoded frame based on:
  • Signal-to-Noise Ratio (SNR): Ratio of signal magnitude to noise magnitude
  • Bit Consistency: How consistently bits match expected signal characteristics
  • Amplitude Stability: Variations in signal strength across the frame
The confidence algorithm (version 6) is defined as:
confidence = SNR × (1 - divergence)
Where divergence measures how much individual bits deviate from average signal characteristics.
  • Default minimum confidence: 1.5 (configurable with -c option)
  • Confidence search limit: 2.3 (configurable with -l option)
  • Values range from 0.0 (no confidence) to INFINITY (perfect signal)

Frequency Shift Examples

Different protocols use different frequency shifts:

Bell 103 (300 bps)

  • Mark: 1270 Hz
  • Space: 1070 Hz
  • Shift: 200 Hz

Bell 202 (1200 bps)

  • Mark: 1200 Hz
  • Space: 2200 Hz
  • Shift: -1000 Hz

RTTY (45.45 bps)

  • Mark: 1585 Hz (default)
  • Space: 1415 Hz
  • Shift: -170 Hz

TDD/TTY (45.45 bps)

  • Mark: 1400 Hz
  • Space: 1800 Hz
  • Shift: 400 Hz

Inverted Frequencies

Some applications require inverted FSK, where mark and space frequencies are swapped:
minimodem --rx --inverted 300
This swaps the interpretation of mark and space tones, useful for certain radio or telephone line connections where the signal may be phase-inverted.

Custom Frequencies

You can specify custom mark and space frequencies:
# Custom FSK with mark=1200 Hz, space=2200 Hz
minimodem --tx --mark 1200 --space 2200 1200
Ensure your custom frequencies are within the Nyquist frequency limit (half the sample rate) and have sufficient separation for reliable detection.

Technical Details

FFT Configuration

The FFT size and number of bands are calculated as:
fft_half_bw = band_width / 2.0
fftsize = (sample_rate + fft_half_bw) / band_width
nbands = fftsize / 2 + 1
b_mark = (f_mark + fft_half_bw) / band_width
b_space = (f_space + fft_half_bw) / band_width

Magnitude Calculation

The magnitude of each frequency band is computed using:
magnitude = sqrt(re² + im²) × scalar
Where re and im are the real and imaginary FFT components, and scalar normalizes based on sample count.

See Also

Build docs developers (and LLMs) love