Show HN: SIMD Viterbi Decoder in Rust

Hacker News Top Tools

Summary

A Rust crate implementing Viterbi and Reed-Solomon forward error correction with SIMD acceleration, achieving faster throughput than the C library libfec on supported codecs.

I wrote libcorrect in C in 2016 and wanted to revisit it in Rust. Instead of doing just a direct conversion, I went down the rabbit hole of making Rust&#x27;s std::simd work for me. I ended up with a templated, generic Viterbi decoder for convolutional codes that dispatches the decode at runtime depending on which instruction sets are available. For small rates and orders, the entire decode lives in registers. Larger codes work through memory but take advantage of some acceleration structures.<p>I also spent some time building a tool to find optimal (max d_free) conv codes for a given rate and order. Of course, there are better mechanisms available today, but I&#x27;m happy to talk through anything I learned in the process.
Original Article
View Cached Full Text

Cached at: 08/05/26, 01:49 AM

brian-armstrong/fec

Source: https://github.com/brian-armstrong/fec

fec

Crates.io Docs.rs CI

Forward error correction for SDR, space, and satellite applications.

fec implements two error-correcting codes that show up throughout software-defined radio and spacecraft links:

  • Convolutional codes with a Viterbi decoder (hard and soft decision), including the common rate-1/2 k=7, rate-1/2 k=9, rate-1/3 k=9, and rate-1/6 k=15 codes. Supports any rate from 1/2 to 1/8 and any order from k=4 to k=16. On nightly Rust, the simd feature enables a Viterbi decoder with acceleration on SSE/AVX2/AVX512.
  • Reed–Solomon codes over GF(2⁸) with error and erasure decoding, including the standard CCSDS (255,223) code in both the conventional and the on-the-wire dual-basis (Berlekamp) representations.

fec started as and draws heavy inspiration from the author’s own libcorrect, a C library for forward error correction. This crate also credits Phil Karn’s libfec C library for offering an original implementation of these codes, although this crate does not borrow any source or have any relationship with that library, and the name is purely coincidental.

Standard parameters (primitive polynomials, the CCSDS dual-basis transform) are derived from the published CCSDS standard (CCSDS 131.0-B, Annex D for the dual basis).

Performance

With the simd feature, fec decodes faster than libfec on every code. Measured through libfec’s own test programs with only the codec library swapped, on a Zen4 laptop (Ryzen 7840HS). Higher is better.

codefec (64-bit)libfec (32-bit)libfec (64-bit)
conv, rate 1/2, k=7158 Mbps148 Mbps117 Mbps
conv, rate 1/2, k=966 Mbps65 Mbps13 Mbps
conv, rate 1/3, k=961 Mbps23 Mbps12 Mbps
conv, rate 1/6, k=151187 Kbps415 Kbps140 Kbps
RS (255,223), general568 Mbps123 Mbps157 Mbps
RS (255,223), CCSDS568 Mbps198 Mbps223 Mbps
RS (255,223), general, 2 err445 Mbps108 Mbps150 Mbps
RS (255,223), CCSDS, 2 err443 Mbps165 Mbps207 Mbps

Convolutional throughput is decoded payload bits per second. The Reed-Solomon rows decode a (255,223) block, first with no errors (syndromes only) and then with two symbol errors. Reed-Solomon uses no SIMD in either library, so its 32-bit and 64-bit rows differ only by pointer width.

1

libfec’s SIMD kernels are gated behind #ifdef __i386__, so its fastest convolutional build is the 32-bit one, running Karn’s SSE2 assembly. A 64-bit libfec build has no SIMD path at all. fec’s SIMD works on both.

See shim/BENCH.md for the full tables, the bit error rate comparison, the 32-bit numbers, and how to reproduce them.

Quick start

Convolutional (Viterbi)

use fec::{ConvEncoder, ConvDecoder};

// Rate-1/2, order-7 NASA code.
let polys = [0o161, 0o127];
let mut enc = ConvEncoder::new(2, 7, &polys);
let mut dec = ConvDecoder::new(2, 7, &polys);

let msg = b"hello, error correction";
let mut encoded = vec![0u8; enc.encode_len(msg.len())];
let num_bits = enc.encode(msg, &mut encoded).unwrap();

// ... encoded is corrupted in transit ...

let mut recovered = vec![0u8; msg.len()];
dec.decode_hard(&encoded, num_bits, &mut recovered).unwrap();

decode_soft takes 8-bit soft symbols instead, which corrects more errors when the demodulator can report its confidence.

Reed–Solomon

use fec::{RsEncoder, RsDecoder};

// Standard CCSDS (255,223) code.
let mut enc = RsEncoder::new_ccsds();
let mut dec = RsDecoder::new_ccsds();

let msg: Vec<u8> = (0..223).collect();
let mut block = vec![0u8; 255];
enc.encode(&msg, &mut block).unwrap();

// ... block is corrupted in transit ...

let mut recovered = vec![0u8; 223];
let corrected = dec.decode(&block, &mut recovered).unwrap();
println!("corrected {corrected} symbol error(s)");

For real spacecraft telemetry (dual-basis symbols on the wire), use encode_ccsds_dual / decode_ccsds_dual.

Compatibility

The codes are bit-compatible with libfec (Phil Karn, KA9Q), so fec can decode data Karn’s library produced and vice versa. A companion shim crate, fec-shim, exposes fec under libfec’s C ABI (init_rs_char, create_viterbi27, encode_rs_ccsds, etc) as a drop-in for existing C codebases.

Roadmap

  • More widths for the Reed-Solomon encoder/decoder (narrower than GF(2⁸) and as wide as GF(2¹⁶))
  • Hard-decision erasures in the convolutional (Viterbi) decoder
  • Punctured codes for the convolutional encoder and decoder

License

BSD-3-Clause.

Similar Articles

Safe SIMD in Rust, even on the inside

Lobsters Hottest

Rust's SIMD abstractions now allow safe usage without unsafe code by leveraging CPU feature tokens introduced in Rust 1.87, enabling concise and portable vector operations.

Show HN: Hsrs – Type-Safe Haskell Bindings Generator for Rust

Hacker News Top

Hsrs is a type-safe FFI bindings generator that allows Rust code to be called from Haskell with automatic memory management, type conversions, and Borsh serialization. It provides annotations in Rust and generates idiomatic Haskell wrappers.

How (and why) we rewrote our production C++ frontend infrastructure in Rust

Lobsters Hottest

NearlyFreeSpeech.NET rewrote their production C++ frontend infrastructure (nfsncore) in Rust, a critical system that handles routing, caching, and access control for all incoming requests. The migration was motivated by Rust's safety guarantees, performance, ecosystem strength, and the aging C++ codebase's limitations.