WSJT-X/lib/wsprd/fano.c
Bill Somerville 4de778c4e7 Reverting r6531 and replacing with correct fix
The WSPR  message is unpacked  into a symbol  per byte array  which is
processed  by  encoding a  whole  number  of  bytes of  message,  this
requires the output  array to be bigger than the  number of symbols to
accommodate  extra values  that are  not part  of the  message due  to
rounding up to whole bytes. I.e.  176 (11*8*2) elements to contain the
162 symbols.

git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@6532 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
2016-03-15 11:46:26 +00:00

240 lines
6.8 KiB
C

/*
This file is part of wsprd.
File name: fano.c
Description: Soft decision Fano sequential decoder for K=32 r=1/2
convolutional code.
Copyright 1994, Phil Karn, KA9Q
Minor modifications by Joe Taylor, K1JT
*/
#define LL 1 // Select Layland-Lushbaugh code
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "fano.h"
struct node {
unsigned long encstate; // Encoder state of next node
long gamma; // Cumulative metric to this node
int metrics[4]; // Metrics indexed by all possible tx syms
int tm[2]; // Sorted metrics for current hypotheses
int i; // Current branch being tested
};
// Convolutional coding polynomials. All are rate 1/2, K=32
#ifdef NASA_STANDARD
/* "NASA standard" code by Massey & Costello
* Nonsystematic, quick look-in, dmin=11, dfree=23
* used on Pioneer 10-12, Helios A,B
*/
#define POLY1 0xbbef6bb7
#define POLY2 0xbbef6bb5
#endif
#ifdef MJ
/* Massey-Johannesson code
* Nonsystematic, quick look-in, dmin=13, dfree>=23
* Purported to be more computationally efficient than Massey-Costello
*/
#define POLY1 0xb840a20f
#define POLY2 0xb840a20d
#endif
#ifdef LL
/* Layland-Lushbaugh code
* Nonsystematic, non-quick look-in, dmin=?, dfree=?
*/
#define POLY1 0xf2d05351
#define POLY2 0xe4613c47
#endif
/* Convolutionally encode a packet. The input data bytes are read
* high bit first and the encoded packet is written into 'symbols',
* one symbol per byte. The first symbol is generated from POLY1,
* the second from POLY2.
*
* Storing only one symbol per byte uses more space, but it is faster
* and easier than trying to pack them more compactly.
*/
int encode(
unsigned char *symbols, // Output buffer, 2*nbytes*8
unsigned char *data, // Input buffer, nbytes
unsigned int nbytes) // Number of bytes in data
{
unsigned long encstate;
int sym;
int i;
encstate = 0;
while(nbytes-- != 0) {
for(i=7;i>=0;i--) {
encstate = (encstate << 1) | ((*data >> i) & 1);
ENCODE(sym,encstate);
*symbols++ = sym >> 1;
*symbols++ = sym & 1;
}
data++;
}
return 0;
}
/* Decode packet with the Fano algorithm.
* Return 0 on success, -1 on timeout
*/
int fano(
unsigned int *metric, // Final path metric (returned value)
unsigned int *cycles, // Cycle count (returned value)
unsigned int *maxnp, // Progress before timeout (returned value)
unsigned char *data, // Decoded output data
unsigned char *symbols, // Raw deinterleaved input symbols
unsigned int nbits, // Number of output bits
int mettab[2][256], // Metric table, [sent sym][rx symbol]
int delta, // Threshold adjust parameter
unsigned int maxcycles) // Decoding timeout in cycles per bit
{
struct node *nodes; // First node
struct node *np; // Current node
struct node *lastnode; // Last node
struct node *tail; // First node of tail
int t; // Threshold
int m0,m1;
int ngamma;
unsigned int lsym;
unsigned int i;
if((nodes = (struct node *)malloc((nbits+1)*sizeof(struct node))) == NULL) {
printf("malloc failed\n");
return 0;
}
lastnode = &nodes[nbits-1];
tail = &nodes[nbits-31];
*maxnp = 0;
/* Compute all possible branch metrics for each symbol pair
* This is the only place we actually look at the raw input symbols
*/
for(np=nodes;np <= lastnode;np++) {
np->metrics[0] = mettab[0][symbols[0]] + mettab[0][symbols[1]];
np->metrics[1] = mettab[0][symbols[0]] + mettab[1][symbols[1]];
np->metrics[2] = mettab[1][symbols[0]] + mettab[0][symbols[1]];
np->metrics[3] = mettab[1][symbols[0]] + mettab[1][symbols[1]];
symbols += 2;
}
np = nodes;
np->encstate = 0;
// Compute and sort branch metrics from root node */
ENCODE(lsym,np->encstate); // 0-branch (LSB is 0)
m0 = np->metrics[lsym];
/* Now do the 1-branch. To save another ENCODE call here and
* inside the loop, we assume that both polynomials are odd,
* providing complementary pairs of branch symbols.
* This code should be modified if a systematic code were used.
*/
m1 = np->metrics[3^lsym];
if(m0 > m1) {
np->tm[0] = m0; // 0-branch has better metric
np->tm[1] = m1;
} else {
np->tm[0] = m1; // 1-branch is better
np->tm[1] = m0;
np->encstate++; // Set low bit
}
np->i = 0; // Start with best branch
maxcycles *= nbits;
np->gamma = t = 0;
// Start the Fano decoder
for(i=1;i <= maxcycles;i++) {
if((int)(np-nodes) > (int)*maxnp) *maxnp=(int)(np-nodes);
#ifdef debug
printf("k=%ld, g=%ld, t=%d, m[%d]=%d, maxnp=%d, encstate=%lx\n",
np-nodes,np->gamma,t,np->i,np->tm[np->i],*maxnp,np->encstate);
#endif
// Look forward */
ngamma = np->gamma + np->tm[np->i];
if(ngamma >= t) {
if(np->gamma < t + delta) { // Node is acceptable
/* First time we've visited this node;
* Tighten threshold.
*
* This loop could be replaced with
* t += delta * ((ngamma - t)/delta);
* but the multiply and divide are slower.
*/
while(ngamma >= t + delta) t += delta;
}
np[1].gamma = ngamma; // Move forward
np[1].encstate = np->encstate << 1;
if( ++np == (lastnode+1) ) {
break; // Done!
}
/* Compute and sort metrics, starting with the
* zero branch
*/
ENCODE(lsym,np->encstate);
if(np >= tail) {
/* The tail must be all zeroes, so don't
* bother computing the 1-branches here.
*/
np->tm[0] = np->metrics[lsym];
} else {
m0 = np->metrics[lsym];
m1 = np->metrics[3^lsym];
if(m0 > m1) {
np->tm[0] = m0; // 0-branch is better
np->tm[1] = m1;
} else {
np->tm[0] = m1; // 1-branch is better
np->tm[1] = m0;
np->encstate++; // Set low bit
}
}
np->i = 0; // Start with best branch
continue;
}
// Threshold violated, can't go forward
for(;;) { // Look backward
if(np == nodes || np[-1].gamma < t) {
/* Can't back up either.
* Relax threshold and and look
* forward again to better branch.
*/
t -= delta;
if(np->i != 0) {
np->i = 0;
np->encstate ^= 1;
}
break;
}
// Back up
if(--np < tail && np->i != 1) {
np->i++; // Search next best branch
np->encstate ^= 1;
break;
} // else keep looking back
}
}
*metric = np->gamma; // Return the final path metric
// Copy decoded data to user's buffer
nbits >>= 3;
np = &nodes[7];
while(nbits-- != 0) {
*data++ = np->encstate;
np += 8;
}
*cycles = i+1;
free(nodes);
if(i >= maxcycles) return -1; // Decoder timed out
return 0; // Successful completion
}