PhosphorSSTV — Decoder Test Results

Independent accuracy testing of the PhosphorSSTV decoder using the CLI test harness (SSTVDecode). The CLI uses identical DSP code to the iOS app via symlinks — same source files, same binary logic.

Methodology

Decoder Settings (defaults)

Test Files

File Source Expected Mode Format Type
scottie_s1_colorbars.wav PySSTV Scottie S1 48 kHz, 16-bit, mono Synthetic colour bars
martin_m1_colorbars.wav PySSTV Martin M1 48 kHz, 16-bit, mono Synthetic colour bars
robot_36_colorbars.wav PySSTV Robot 36 48 kHz, 16-bit, mono Synthetic colour bars (YCbCr)
desstv_Scottie1.wav boholder/desstv Scottie S1 48 kHz, 16-bit, mono (from MP3) Encoded photograph
desstv_Martin1.wav boholder/desstv Martin M1 48 kHz, 16-bit, mono (from MP3) Encoded photograph (BBC test card)
desstv_Robot36.wav boholder/desstv Robot 36 48 kHz, 16-bit, mono (from MP3) Encoded photograph (YCbCr)
colaclanth_martin_m1.wav colaclanth/sstv Martin M1 48 kHz, 16-bit, mono (from OGG) Encoded photograph
picoctf_m00nwalk_message.wav picoCTF 2021 Scottie S1 48 kHz, 16-bit, mono CTF challenge (hidden message)

Results

File Mode VIS Code Lines State Quality
scottie_s1_colorbars Scottie S1 60 ✓ 255/256 receiving Perfect
martin_m1_colorbars Martin M1 44 ✓ 255/256 receiving Perfect
robot_36_colorbars Robot 36 8 ✓ 239/240 receiving Good — clean colour bars, correct YCbCr conversion
desstv_Scottie1 Scottie S1 timeout 206/256 receiving Partial — MP3 compression artefacts
desstv_Martin1 Martin M1 timeout 206/256 receiving Good — BBC test card recognisable
desstv_Robot36 Robot 36 timeout 236/240 receiving Partial — YCbCr + MP3 artefacts
colaclanth_martin_m1 Martin M1 timeout 126/256 receiving Half image — OGG compression losses
picoctf_m00nwalk Scottie S1 timeout 174/256 receiving Partial decode — CTF signal

Decoded Image Samples

Synthetic Test Patterns (PySSTV — clean signal, known-good reference)

Scottie S1 colour bars
Scottie S1 — VIS 60 detected, 255/256 lines, zero slant
Martin M1 colour bars
Martin M1 — VIS 44 detected, 255/256 lines, zero slant
Robot 36 colour bars
Robot 36 — VIS 8 detected, 239/240 lines, correct YCbCr→RGB conversion

Third-Party Encoded Images (lossy sources — MP3/OGG transcoded to WAV)

desstv Scottie S1
desstv Scottie S1 — 206/256 lines, VIS timeout, MP3 artefacts
desstv Martin M1
desstv Martin M1 — 206/256 lines, BBC test card recognisable
desstv Robot 36
desstv Robot 36 — 236/240 lines, YCbCr + lossy compression
colaclanth Martin M1
colaclanth Martin M1 — 126/256 lines, OGG source
picoCTF m00nwalk
picoCTF m00nwalk — 174/256 lines, CTF challenge signal

Notes

VIS Detection

VIS codes were correctly detected for all PySSTV-generated files (clean signals with standard VIS headers). Third-party files mostly failed VIS detection and fell through to the 5-second timeout. This is expected — many encoders produce non-standard VIS headers, and MP3/OGG lossy compression distorts the precise 1100/1300 Hz VIS bit tones. The timeout mechanism ensures decoding proceeds regardless.

Sync-Aligned Slant Correction

All decoded images from clean sources show zero slant. The sync-aligned approach (seeking the 1200 Hz sync pulse at each line boundary rather than counting fixed samples) eliminates cumulative clock drift entirely. This matches the standard approach used by MMSSTV, QSSTV, and fldigi.

Robot 36 YCbCr

Robot 36 colour bars now decode with correct chrominance. The Cb/Cr channels alternate per line — even lines carry Cr, odd lines carry Cb. The decoder duplicates each chrominance channel to the adjacent line and reconverts the line pair after each update. BT.601 YCbCr→RGB conversion produces accurate colour reproduction.

Lossy Compression Impact

Files sourced from MP3 or OGG suffer significant quality loss. SSTV encodes image data as precise FM tones (1500–2300 Hz). Lossy audio compression introduces spectral smearing that corrupts the frequency-to-pixel mapping. This is not a decoder limitation — the source signal is damaged before decoding begins. For accurate testing, always use lossless WAV sources.

Line Count: 255/256

Clean files consistently decode 255 of 256 lines. The final line is not completed because the file ends before the next sync pulse triggers the line-complete condition. This is a boundary condition, not a decoder error.

Summary