-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathfano.c
239 lines (218 loc) · 6.84 KB
/
fano.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
/*
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
}