SIMD-VITERBI(3)
โจ SIMD-VITERBI โจ โ the section 3 (Library Functions Manual) era ๐
okay bestie, pull up a chair ๐ซถ this is the man page for a whole little squad of IA32 SIMD-assisted Viterbi decoders, and yes, that is as iconic as it sounds.
๐ธ NAME ๐ธ
Say hi to the whole crew, no cap, every single one of them:
create_viterbi27, set_viterbi27_polynomial, init_viterbi27, update_viterbi27_blk, chainback_viterbi27, delete_viterbi27, create_viterbi29, set_viterbi_29_polynomial, init_viterbi29, update_viterbi29_blk, chainback_viterbi29, delete_viterbi29, create_viterbi39, set_viterbi_39_polynomial, init_viterbi39, update_viterbi39_blk, chainback_viterbi39, delete_viterbi39, create_viterbi615, set_viterbi615_polynomial, init_viterbi615, update_viterbi615_blk, chainback_viterbi615, delete_viterbi615
โฆand together they are the IA32 SIMD-assisted Viterbi decoders. periodt. ๐
โจ SYNOPSIS โจ
#include "fec.h"
void *create_viterbi27(int blocklen);
void set_viterbi27_polynomial(int polys[2]);
int init_viterbi27(void *vp,int starting_state);
int update_viterbi27_blk(void *vp,unsigned char syms[],int nbits);
int chainback_viterbi27(void *vp, unsigned char *data,unsigned int nbits,unsigned int endstate);
void delete_viterbi27(void *vp);
void *create_viterbi29(int blocklen);
void set_viterbi29_polynomial(int polys[2]);
int init_viterbi29(void *vp,int starting_state);
int update_viterbi29_blk(void *vp,unsigned char syms[],int nbits);
int chainback_viterbi29(void *vp, unsigned char *data,unsigned int nbits,unsigned int endstate);
void delete_viterbi29(void *vp);
void *create_viterbi39(int blocklen);
void set_viterbi39_polynomial(int polys[3]);
int init_viterbi39(void *vp,int starting_state);
int update_viterbi39_blk(void *vp,unsigned char syms[],int nbits);
int chainback_viterbi39(void *vp, unsigned char *data,unsigned int nbits,unsigned int endstate);
void delete_viterbi39(void *vp);
void *create_viterbi615(int blocklen);
void set_viterbi615_polynomial(int polys[6]);
int init_viterbi615(void *vp,int starting_state);
int update_viterbi615_blk(void *vp,unsigned char syms[],int nbits);
int chainback_viterbi615(void *vp, unsigned char *data,unsigned int nbits,unsigned int endstate);
void delete_viterbi615(void *vp);
๐ฆ DESCRIPTION ๐ฆ
So these functions? They're high performance Viterbi decoders for four convolutional codes, and here's the lineup:
- a rate 1/2, constraint length 7 (k=7) code โ that's
"viterbi27" - a rate 1/2, k=9 code โ
"viterbi29" - a rate 1/3, k=9 code โ
"viterbi39" - a rate 1/6, k=15 code โ
"viterbi615"
The decoders use the Intel IA32 or PowerPC SIMD instruction sets, if available, to make decoding go zoom. ๐๏ธโจ
๐ the IA32 SIMD lore, sit down for this one
On the IA32 there are three different SIMD instruction sets, and it's giving pokรฉmon evolution line, honestly:
- MMX โ the first and most common one. Introduced on later Intel Pentiums and then on the Intel Pentium II and most Intel clones (AMD K6, Transmeta Crusoe, etc).
- SSE โ introduced on the Pentium III and later implemented in the AMD Athlon 4 (AMD calls it
"3D Now! Professional"). cute rebrand, love that for them. - SSE2 โ most recently introduced in the Intel Pentium 4, and adopted by more recent AMD CPUs.
The way that this works: the presence of SSE2 implies the existence of SSE, which in turn implies MMX. it's a whole lineage bestie ๐งฌ
๐ง the PowerPC side
Altivec is the PowerPC SIMD instruction set. It's roughly comparable to SSE2. Altivec was introduced to the general public in the Apple Macintosh G4; it is also present in the G5.
And the naming drama? Altivec is actually a Motorola trademark; Apple calls it "Velocity Engine" and IBM calls it "VMX". All three refer to the same thing. same vibe, different government name ๐
๐ฅ auto-magic
When built for the IA32 or PPC architectures, the functions automatically use the most powerful SIMD instruction set available. If no SIMD instructions are available, or if the library is built for a non-IA32, non-PPC machine, a portable C version is executed instead. she adapts. main character energy. ๐
๐ USAGE ๐
Four versions of each function are provided, one for each code. In the following discussion, change "viterbi" to "viterbi27", "viterbi29", "viterbi39" or "viterbi615" as desired. iykyk โจ
Before Viterbi decoding can begin, an instance must first be created with create_viterbi(). This function creates and returns a pointer to an internal control structure containing the path metrics and the branch decisions. create_viterbi() takes one argument that gives the length of the data block in bits.
๐จ bestie, listen: you must not attempt to decode a block longer than the length given to create_viterbi().
Before decoding a new frame, init_viterbi() must be called to reset the decoder state. It accepts the instance pointer returned by create_viterbi() and the initial starting state of the convolutional encoder (usually 0).
If the initial starting state is unknown or incorrect, the decoder will still function but the decoded data may be incorrect at the start of the block. ๐
Blocks of received symbols are processed with calls to update_viterbi_blk(). The nbits parameter specifies the number of data bits (not channel symbols) represented by the syms buffer. (For rate 1/2 codes, the number of symbols in syms is twice nbits, and so on.) ๐
Each symbol is expected to range from 0 through 255, with 0 corresponding to a "strong 0" and 255 corresponding to a "strong 1".
The caller is responsible for determining the proper pairing of input symbols (commonly known as decoder symbol phasing). that one's on you, girl ๐
At the end of the block, the data is recovered with a call to chainback_viterbi(). The arguments are:
- the pointer to the decoder instance
- a pointer to a user-supplied buffer into which the decoded data is to be written
- the number of data bits (not bytes) that are to be decoded
- the terminal state of the convolutional encoder at the end of the frame (usually
0)
If the terminal state is incorrect or unknown, the decoded data bits at the end of the frame may be unreliable. ๐ญ
The decoded data is written in big-endian order, i.e., the first bit in the frame is written into the high order bit of the first byte in the buffer. If the frame is not an integral number of bytes long, the low order bits of the last byte in the frame will be unused.
๐ Note, and this one matters: the decoders assume the use of a tail, i.e., the encoding and transmission of a sufficient number of padding bits beyond the end of the user data to force the convolutional encoder into the known terminal state given to chainback_viterbi().
The tail is always one bit less than the constraint length of the code, so:
- the k=7 code uses 6 tail bits (12 tail symbols)
- the k=9 code uses 8 tail bits (16 tail symbols)
- the k=15 code uses 14 tail bits (84 tail symbols)
The tail bits are not included in the length arguments to create_viterbi() and chainback_viterbi().
For example, if the block contains 1000 user bits, then this would be the length parameter given to create_viterbi27() and chainback_viterbi27(), and update_viterbi27_blk() would be called with a total of 2012 symbols โ the last 12 encoded symbols representing the tail bits. ๐งฎโจ
After the call to chainback_viterbi(), the decoder may be reset with a call to init_viterbi() and another block can be decoded. Alternatively, delete_viterbi() can be called to free all resources used by the Viterbi decoder. clean up after yourself, queen ๐ซถ
๐ the polynomial situation
The set_viterbi_polynomial() function allows use of other than the default code generator polynomials. Although only one set of polynomials are generally used with each code, there can are different conventions as to their order and symbol polarity, and these functions simplifies their use. (yes bestie, the original man page said "there can are" and honestly? we support her. moving on. ๐)
The default polynomials for the viterbi27 routines are those of the NASA-JPL convention without symbol inversion. The NASA-JPL convention normally inverts the first symbol. The CCSDS/NASA-GSFC convention swaps the two symbols and inverts the second.
To set the NASA-JPL convention with symbol inversion:
int polys[2] = { -V27POLYA,V27POLYB };
set_viterbi27_polynomial(polys);
and to set the CCSDS convention with symbol inversion:
int polys[2] = { V27POLYB,-V27POLYA };
set_viterbi27_polynomial(polys);
The default polynomials for the viterbi615 routines are those used by the Cassini spacecraft without symbol inversion. Mars Pathfinder (MPF) and STEREO swap the third and fourth polynomials. Both conventions invert the first, third and fifth symbols. Refer to fec.h for the polynomial constant definitions. ๐ฐ๏ธ
To set the Cassini convention with symbol inversion, do the following:
int polys[6] = { -V615POLYA,V615POLYB,-V615POLYC,V615POLYD,-V615POLYE,V615POLYF };
set_viterbi615_polynomial(polys);
and to set the MPF/STEREO convention with symbol inversion:
int polys[6] = { -V615POLYA,V615POLYB,-V615POLYD,V615POLYC,-V615POLYE,V615POLYF };
set_viterbi615_polynomial(polys);
โ ๏ธ For performance reasons, calling this function changes the code generator polynomials for all instances of corresponding Viterbi decoder, including those already created. she's global, bestie. no takebacks. ๐
๐ ERROR PERFORMANCE ๐
These decoders have all been extensively tested and found to provide performance consistent with that expected for soft-decision Viterbi decoding with 8-bit symbols. โ
Due to internal differences, the implementations vary slightly in error performance, and here's the ranking tea โ:
- the portable C versions exhibit the best error performance โ because they use full-sized branch metrics ๐
- the MMX versions exhibit the worst โ because they use 8-bit branch metrics with modulo comparisons ๐ญ
- the SSE, SSE2 and Altivec implementations of the r=1/2 k=7 and r=1/2 k=9 codes use unsigned 8-bit branch metrics, and are almost as good as the C versions โจ
The r=1/3 k=9 and r=1/6 k=15 codes are implemented with 16-bit path metrics in all SIMD versions.
๐ฅ DIRECT ACCESS TO SPECIFIC FUNCTION VERSIONS ๐ฅ
Calling the functions listed above automatically calls the appropriate version of the function depending on the CPU type and available SIMD instructions. but bestie, you can call a particular version directly by appending the appropriate suffix to the function name. ๐ฏ
The available suffixes are:
_mmxโ the MMX version_sseโ the SSE version_sse2โ the SSE2 version_avโ the Altivec version_portโ the portable version
For example, the SSE2 version of the update_viterbi27_blk() function can be invoked as update_viterbi27_blk_sse2().
Naturally, the _av functions are only available on the PowerPC and the _mmx, _sse and _sse2 versions are only available on IA-32.
๐ Calling a SIMD-enabled function on a CPU that doesn't support the appropriate set of instructions will result in an illegal instruction exception. do not do this. fr fr.
โญ RETURN VALUES โญ
create_viterbireturns a pointer to the structure containing the decoder state. ๐ซถ- The other functions return
-1on error,0otherwise.
๐ AUTHOR & COPYRIGHT ๐
Phil Karn, KA9Q (karn@ka9q.net) โ icon, legend, the moment. ๐
๐ LICENSE ๐
This software may be used under the terms of the GNU Limited General Public License (LGPL).
and that's the whole page, bestie!! this was the SIMD-VITERBI(3) page from the Library Functions Manual โ written by Phil Karn, KA9Q, and the footer says SIMD-VITERBI(3) down there too, so now you know exactly where to find her again. go decode something cute ๐โจ๐
๐ฆ where this page comes from
- debian bookworm: libfec-dev 1.0-26-gc5d935f-1 ยท LGPL
- debian trixie: libfec-dev 1.0-26-gc5d935f-1+b3 ยท LGPL
- ubuntu jammy: libfec-dev 1.0+20161003-4 ยท LGPL
- ubuntu noble: libfec-dev 1.0-26-gc5d935f-1 ยท LGPL
- ubuntu questing: libfec-dev 1.0-26-gc5d935f-1
- ubuntu resolute: libfec-dev 1.0-26-gc5d935f-1build1 ยท LGPL
- ubuntu stonking: libfec-dev 1.0-26-gc5d935f-1build1 ยท LGPL
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms