Got LDPC sum-product decoder working

This commit is contained in:
Karlis Goba 2018-11-13 17:16:24 +02:00
parent b1a1d1edc6
commit db8f28dd8b
2 changed files with 118 additions and 108 deletions

View file

@ -88,11 +88,10 @@ void find_sync(const uint8_t * power, int num_blocks, int num_bins, int num_cand
for (int freq_offset = 0; freq_offset < num_bins - 8; ++freq_offset) { for (int freq_offset = 0; freq_offset < num_bins - 8; ++freq_offset) {
int score = 0; int score = 0;
// Compute score over bins 0-7, 36-43, 72-79 // Compute score over Costas symbols (0-7, 36-43, 72-79)
for (int m = 0; m <= 72; m += 36) { for (int m = 0; m <= 72; m += 36) {
for (int k = 0; k < 7; ++k) { for (int k = 0; k < 7; ++k) {
int offset = ((time_offset + k + m) * 4 + alt) * num_bins + freq_offset; int offset = ((time_offset + k + m) * 4 + alt) * num_bins + freq_offset;
// score += 8 * (int)power[time_offset + k + m][alt][freq_offset + ICOS7[k]] -
score += 8 * (int)power[offset + ICOS7[k]] - score += 8 * (int)power[offset + ICOS7[k]] -
power[offset + 0] - power[offset + 1] - power[offset + 0] - power[offset + 1] -
power[offset + 2] - power[offset + 3] - power[offset + 2] - power[offset + 3] -
@ -101,17 +100,17 @@ void find_sync(const uint8_t * power, int num_blocks, int num_bins, int num_cand
} }
} }
// update the candidate list // If the heap is full AND the current candidate is better than
// the worst of the heap, we remove the worst and make space
if (heap_size == num_candidates && score > heap[0].score) { if (heap_size == num_candidates && score > heap[0].score) {
// extract the least promising candidate
heap[0] = heap[heap_size - 1]; heap[0] = heap[heap_size - 1];
--heap_size; --heap_size;
heapify_down(heap, heap_size); heapify_down(heap, heap_size);
} }
// If there's free space in the heap, we add the current candidate
if (heap_size < num_candidates) { if (heap_size < num_candidates) {
// add the current candidate
heap[heap_size].score = score; heap[heap_size].score = score;
heap[heap_size].time_offset = time_offset; heap[heap_size].time_offset = time_offset;
heap[heap_size].freq_offset = freq_offset; heap[heap_size].freq_offset = freq_offset;
@ -176,7 +175,7 @@ void extract_power(const float * signal, int num_blocks, int num_bins, uint8_t *
float db = (db1 + db2) / 2; float db = (db1 + db2) / 2;
// Scale decibels to unsigned 8-bit range // Scale decibels to unsigned 8-bit range
int scaled = (int)(0.5f + 2 * (db + 100)); int scaled = (int)(2 * (db + 100));
power[offset] = (scaled < 0) ? 0 : ((scaled > 255) ? 255 : scaled); power[offset] = (scaled < 0) ? 0 : ((scaled > 255) ? 255 : scaled);
++offset; ++offset;
} }
@ -188,6 +187,60 @@ void extract_power(const float * signal, int num_blocks, int num_bins, uint8_t *
} }
uint8_t max2(uint8_t a, uint8_t b) {
return (a >= b) ? a : b;
}
uint8_t max4(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
return max2(max2(a, b), max2(c, d));
}
// Compute log likelihood log(p(0) / p(1)) of 174 message bits
// for later use in soft-decision LDPC decoding
void extract_likelihood(const uint8_t * power, int num_bins, const Candidate & c, float * log174) {
int offset = (c.time_offset * 4 + c.time_sub * 2 + c.freq_sub) * num_bins + c.freq_offset;
int k = 0;
// Go over FSK tones and skip Costas sync symbols
for (int i = 7; i < NN - 7; ++i) {
if (i == 36) i += 7;
// Pointer to 8 bins of the current symbol
const uint8_t * ps = power + (offset + i * 4 * num_bins);
// Extract bit significance (and convert them to float)
// 8 FSK tones = 3 bits
log174[k + 0] = -(int)max4(ps[4], ps[5], ps[6], ps[7]) + (int)max4(ps[0], ps[1], ps[2], ps[3]);
log174[k + 1] = -(int)max4(ps[2], ps[3], ps[6], ps[7]) + (int)max4(ps[0], ps[1], ps[4], ps[5]);
log174[k + 2] = -(int)max4(ps[1], ps[3], ps[5], ps[7]) + (int)max4(ps[0], ps[2], ps[4], ps[6]);
// printf("%d %d %d %d %d %d %d %d : %.0f %.0f %.0f\n",
// ps[0], ps[1], ps[2], ps[3], ps[4], ps[5], ps[6], ps[7],
// log174[k + 0], log174[k + 1], log174[k + 2]);
k += 3;
}
// Compute the variance of log174
float sum = 0;
float sum2 = 0;
float inv_n = 1.0f / (3 * ND);
for (int i = 0; i < 3 * ND; ++i) {
sum += log174[i];
sum2 += log174[i] * log174[i];
}
float var = (sum2 - sum * sum * inv_n) * inv_n;
// Normalize log174 such that sigma = 2.83 (Why? It's in WSJT-X)
float norm_factor = 2.83f / sqrtf(var);
for (int i = 0; i < 3 * ND; ++i) {
log174[i] *= norm_factor;
//printf("%.1f ", log174[i]);
}
//printf("\n");
}
int main(int argc, char ** argv) { int main(int argc, char ** argv) {
// Expect one command-line argument // Expect one command-line argument
if (argc < 2) { if (argc < 2) {
@ -223,37 +276,25 @@ int main(int argc, char ** argv) {
find_sync(power, num_blocks, num_bins, num_candidates, heap); find_sync(power, num_blocks, num_bins, num_candidates, heap);
for (int i = 0; i < num_candidates; ++i) { for (int i = 0; i < num_candidates; ++i) {
float freq_offset = (heap[i].freq_offset + heap[i].freq_sub / 2.0f) * fsk_dev; float freq_hz = (heap[i].freq_offset + heap[i].freq_sub / 2.0f) * fsk_dev;
float time_offset = (heap[i].time_offset + heap[i].time_sub / 2.0f) / fsk_dev; float time_sec = (heap[i].time_offset + heap[i].time_sub / 2.0f) / fsk_dev;
// int offset = (heap[i].time_offset * 4 + heap[i].time_sub * 2 + heap[i].freq_sub) * num_bins + heap[i].freq_offset; // printf("%03d: score = %d freq = %.1f time = %.2f\n", i,
printf("%03d: score = %.1f freq = %.1f time = %.2f\n", i, heap[i].score / 7.0f / 2, freq_offset, time_offset); // heap[i].score, freq_hz, time_sec);
float log174[3 * ND];
extract_likelihood(power, num_bins, heap[i], log174);
const int num_iters = 10;
int plain[3 * ND];
int ok;
ldpc_decode(log174, num_iters, plain, &ok);
//printf("ldpc_decode() = %d\n", ok);
if (ok == 87) {
printf("%03d: score = %d freq = %.1f time = %.2f\n", i,
heap[i].score, freq_hz, time_sec);
}
} }
/*
// take absolute magnitude
s2(0:7,k)=abs(csymb(1:8))/1e3
// skip Costas sync symbols
s1(0:7,j)=s2(0:7,k)
// Normalize by median magnitude
s1=s1/xmeds1
// Extract bit significance
ps=s1(0:7,j)
bmeta(i4)=max(ps(4),ps(5),ps(6),ps(7))-max(ps(0),ps(1),ps(2),ps(3))
bmeta(i2)=max(ps(2),ps(3),ps(6),ps(7))-max(ps(0),ps(1),ps(4),ps(5))
bmeta(i1)=max(ps(1),ps(3),ps(5),ps(7))-max(ps(0),ps(2),ps(4),ps(6))
// Normalize by std. deviation
call normalizebmet(bmeta,3*ND)
// Magical fudge/scale factor
scalefac=2.83
llr0=scalefac*bmeta
*/
return 0; return 0;
} }

View file

@ -8,8 +8,6 @@
// from Sarah Johnson's Iterative Error Correction book. // from Sarah Johnson's Iterative Error Correction book.
// codeword[i] = log ( P(x=0) / P(x=1) ) // codeword[i] = log ( P(x=0) / P(x=1) )
// //
// cc -O3 libldpc.c -shared -fPIC -o libldpc.so
//
#include <stdio.h> #include <stdio.h>
#include <math.h> #include <math.h>
@ -21,14 +19,11 @@ int ldpc_check(int codeword[]);
// thank you Douglas Bagnall // thank you Douglas Bagnall
// https://math.stackexchange.com/a/446411 // https://math.stackexchange.com/a/446411
float fast_tanh(float x) float fast_tanh(float x) {
{ if (x < -4.97f) {
if (x < -4.97f)
{
return -1.0f; return -1.0f;
} }
if (x > 4.97f) if (x > 4.97f) {
{
return 1.0f; return 1.0f;
} }
float x2 = x * x; float x2 = x * x;
@ -42,86 +37,64 @@ float fast_tanh(float x)
// plain is a return value, 174 ints, to be 0 or 1. // plain is a return value, 174 ints, to be 0 or 1.
// iters is how hard to try. // iters is how hard to try.
// ok == 87 means success. // ok == 87 means success.
void ldpc_decode(float codeword[], int iters, int plain[], int *ok) void ldpc_decode(float codeword[], int iters, int plain[], int *ok) {
{
float m[87][174]; // ~60 kB float m[87][174]; // ~60 kB
float e[87][174]; // ~60 kB float e[87][174]; // ~60 kB
int best_score = -1; int best_score = -1;
int best_cw[174]; int best_cw[174];
for (int i = 0; i < 174; i++) for (int i = 0; i < 174; i++) {
for (int j = 0; j < 87; j++) for (int j = 0; j < 87; j++) {
m[j][i] = codeword[i]; m[j][i] = codeword[i];
for (int i = 0; i < 174; i++)
for (int j = 0; j < 87; j++)
e[j][i] = 0.0f; e[j][i] = 0.0f;
}
}
for (int iter = 0; iter < iters; iter++) for (int iter = 0; iter < iters; iter++) {
{ for (int j = 0; j < 87; j++) {
for (int j = 0; j < 87; j++) for (int ii1 = 0; ii1 < 7; ii1++) {
{
for (int ii1 = 0; ii1 < 7; ii1++)
{
int i1 = Nm[j][ii1] - 1; int i1 = Nm[j][ii1] - 1;
if (i1 < 0) if (i1 < 0) {
continue; continue;
}
float a = 1.0f; float a = 1.0f;
for (int ii2 = 0; ii2 < 7; ii2++) for (int ii2 = 0; ii2 < 7; ii2++) {
{
int i2 = Nm[j][ii2] - 1; int i2 = Nm[j][ii2] - 1;
if (i2 >= 0 && i2 != i1) if (i2 >= 0 && i2 != i1) {
{
a *= fast_tanh(m[j][i2] / 2.0f); a *= fast_tanh(m[j][i2] / 2.0f);
} }
} }
e[j][i1] = log((1 + a) / (1 - a)); e[j][i1] = logf((1 + a) / (1 - a));
} }
} }
int cw[174]; int cw[174];
for (int i = 0; i < 174; i++) for (int i = 0; i < 174; i++) {
{
float l = codeword[i]; float l = codeword[i];
for (int j = 0; j < 3; j++) for (int j = 0; j < 3; j++)
l += e[Mn[i][j] - 1][i]; l += e[Mn[i][j] - 1][i];
cw[i] = (l <= 0.0f); cw[i] = (l <= 0.0f);
} }
int score = ldpc_check(cw); int score = ldpc_check(cw);
if (score == 87)
{
// Found a perfect answer
#if 0
int cw1[174];
for(int i = 0; i < 174; i++)
cw1[i] = cw[colorder[i]];
for(int i = 0; i < 87; i++)
plain[i] = cw1[174-87+i];
#else
for (int i = 0; i < 174; i++)
plain[i] = cw[colorder[i]];
#endif
*ok = 87;
return;
}
if (score > best_score) if (score > best_score) {
{ for (int i = 0; i < 174; i++) {
for (int i = 0; i < 174; i++)
best_cw[i] = cw[i]; best_cw[i] = cw[i];
}
best_score = score; best_score = score;
} }
for (int i = 0; i < 174; i++) if (score == 87) {
{ // Found a perfect answer
for (int ji1 = 0; ji1 < 3; ji1++) break;
{ }
for (int i = 0; i < 174; i++) {
for (int ji1 = 0; ji1 < 3; ji1++) {
int j1 = Mn[i][ji1] - 1; int j1 = Mn[i][ji1] - 1;
float l = codeword[i]; float l = codeword[i];
for (int ji2 = 0; ji2 < 3; ji2++) for (int ji2 = 0; ji2 < 3; ji2++) {
{ if (ji1 != ji2) {
if (ji1 != ji2)
{
int j2 = Mn[i][ji2] - 1; int j2 = Mn[i][ji2] - 1;
l += e[j2][i]; l += e[j2][i];
} }
@ -131,16 +104,15 @@ void ldpc_decode(float codeword[], int iters, int plain[], int *ok)
} }
} }
// decode didn't work, return something anyway. // decode complete (perhaps partially)
#if 0 #if 0
int cw1[174]; for(int i = 0; i < 87; i++) {
for(int i = 0; i < 174; i++) plain[i] = best_cw[colorder[174-87+i]];
cw1[i] = best_cw[colorder[i]]; }
for(int i = 0; i < 87; i++)
plain[i] = cw1[174-87+i];
#else #else
for (int i = 0; i < 174; i++) for (int i = 0; i < 174; i++) {
plain[i] = best_cw[colorder[i]]; plain[i] = best_cw[colorder[i]];
}
#endif #endif
*ok = best_score; *ok = best_score;
@ -152,24 +124,21 @@ void ldpc_decode(float codeword[], int iters, int plain[], int *ok)
// returns the number of parity checks that passed. // returns the number of parity checks that passed.
// 87 means total success. // 87 means total success.
// //
int ldpc_check(int codeword[]) int ldpc_check(int codeword[]) {
{
int score = 0; int score = 0;
// Nm[87][7] // Nm[87][7]
for (int j = 0; j < 87; j++) for (int j = 0; j < 87; j++) {
{
int x = 0; int x = 0;
for (int ii1 = 0; ii1 < 7; ii1++) for (int ii1 = 0; ii1 < 7; ii1++) {
{
int i1 = Nm[j][ii1] - 1; int i1 = Nm[j][ii1] - 1;
if (i1 >= 0) if (i1 >= 0) {
{
x ^= codeword[i1]; x ^= codeword[i1];
} }
} }
if (x == 0) if (x == 0) {
score++; score++;
}
} }
return score; return score;
} }