240 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			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
 | |
| }
 | 
