Compare commits

...

2 commits

Author SHA1 Message Date
Ahmet Inan
77bc5fe5d8 estimate SFO and CFO via simple linear regression
and compensate by correcting constellation points
2021-09-03 20:54:03 +02:00
Ahmet Inan
a7d2bc1cbf do not resample 2021-09-03 20:54:03 +02:00

View file

@ -9,8 +9,8 @@ Copyright 2021 Ahmet Inan <inan@aicodix.de>
#include <cassert> #include <cassert>
#include <cmath> #include <cmath>
namespace DSP { using std::abs; using std::min; using std::cos; using std::sin; } namespace DSP { using std::abs; using std::min; using std::cos; using std::sin; }
#include "regression.hh"
#include "bip_buffer.hh" #include "bip_buffer.hh"
#include "resampler.hh"
#include "xorshift.hh" #include "xorshift.hh"
#include "trigger.hh" #include "trigger.hh"
#include "complex.hh" #include "complex.hh"
@ -190,7 +190,6 @@ struct Decoder
DSP::FastFourierTransform<symbol_len, cmplx, 1> bwd; DSP::FastFourierTransform<symbol_len, cmplx, 1> bwd;
DSP::BlockDC<value, value> blockdc; DSP::BlockDC<value, value> blockdc;
DSP::Hilbert<cmplx, filter_len> hilbert; DSP::Hilbert<cmplx, filter_len> hilbert;
DSP::Resampler<value, filter_len, 3> resample;
DSP::BipBuffer<cmplx, buffer_len> input_hist; DSP::BipBuffer<cmplx, buffer_len> input_hist;
SchmidlCox<value, cmplx, search_pos, symbol_len/2, guard_len> correlator; SchmidlCox<value, cmplx, search_pos, symbol_len/2, guard_len> correlator;
CODE::CRC<uint16_t> crc0; CODE::CRC<uint16_t> crc0;
@ -202,7 +201,8 @@ struct Decoder
mesg_type mesg[44096], mess[65536]; mesg_type mesg[44096], mess[65536];
code_type code[65536]; code_type code[65536];
cmplx head[symbol_len], tail[symbol_len], cons[cons_max]; cmplx head[symbol_len], tail[symbol_len], cons[cons_max];
cmplx fdom[symbol_len], tdom[buffer_len], resam[buffer_len]; cmplx fdom[symbol_len], tdom[buffer_len];
value index[cons_max], phase[cons_max];
value cfo_rad, sfo_rad; value cfo_rad, sfo_rad;
const uint32_t *frozen_bits; const uint32_t *frozen_bits;
int code_order; int code_order;
@ -239,28 +239,6 @@ struct Decoder
fdom[(i+mls0_off/2+symbol_len/2)%(symbol_len/2)] = nrz(seq0()); fdom[(i+mls0_off/2+symbol_len/2)%(symbol_len/2)] = nrz(seq0());
return fdom; return fdom;
} }
int displacement(const cmplx *sym0, const cmplx *sym1)
{
fwd(head, sym0);
fwd(tail, sym1);
for (int i = 0; i < symbol_len; ++i)
head[i] *= conj(tail[i]);
bwd(tail, head);
int idx = 0;
for (int i = 0; i < symbol_len; ++i)
if (norm(tail[i]) > norm(tail[idx]))
idx = i;
if (idx > symbol_len / 2)
idx -= symbol_len;
return -idx;
}
value frac_cfo(const cmplx *samples)
{
cmplx sum;
for (int i = 0; i < symbol_len/2; ++i)
sum += samples[i] * conj(samples[i+symbol_len/2]);
return arg(sum) / (symbol_len/2);
}
void lengthen() void lengthen()
{ {
int code_bits = 1 << code_order; int code_bits = 1 << code_order;
@ -329,7 +307,7 @@ struct Decoder
} }
} }
Decoder(uint8_t *out, DSP::ReadPCM<value> *pcm, int skip_count) : Decoder(uint8_t *out, DSP::ReadPCM<value> *pcm, int skip_count) :
pcm(pcm), resample(rate, (rate * 19) / 40, 2), correlator(mls0_seq()), crc0(0xA8F4), crc1(0xD419CC15) pcm(pcm), correlator(mls0_seq()), crc0(0xA8F4), crc1(0xD419CC15)
{ {
CODE::BoseChaudhuriHocquenghemGenerator<255, 71>::matrix(genmat, true, { CODE::BoseChaudhuriHocquenghemGenerator<255, 71>::matrix(genmat, true, {
0b100011101, 0b101110111, 0b111110011, 0b101101001, 0b100011101, 0b101110111, 0b111110011, 0b101101001,
@ -475,24 +453,9 @@ struct Decoder
int cons_rows = cons_cnt / cons_cols; int cons_rows = cons_cnt / cons_cols;
int code_off = - cons_cols / 2; int code_off = - cons_cols / 2;
int dis = displacement(buf+symbol_pos-(cons_rows+1)*(symbol_len+guard_len), buf+symbol_pos+2*(symbol_len+guard_len));
sfo_rad = (dis * Const::TwoPi()) / ((cons_rows+3)*(symbol_len+guard_len));
std::cerr << "coarse sfo: " << 1000000 * sfo_rad / Const::TwoPi() << " ppm" << std::endl;
if (dis) {
value diff = sfo_rad * (rate / Const::TwoPi());
resample(resam, buf, -diff, buffer_len);
symbol_pos = std::nearbyint(correlator.symbol_pos * (1 - sfo_rad / Const::TwoPi()));
std::cerr << "resam pos: " << symbol_pos << std::endl;
} else {
for (int i = 0; i < buffer_len; ++i)
resam[i] = buf[i];
}
cfo_rad = correlator.cfo_rad + correlator.frac_cfo - frac_cfo(resam+symbol_pos);
std::cerr << "finer cfo: " << cfo_rad * (rate / Const::TwoPi()) << " Hz " << std::endl;
osc.omega(-cfo_rad); osc.omega(-cfo_rad);
for (int i = 0; i < buffer_len; ++i) for (int i = 0; i < buffer_len; ++i)
tdom[i] = resam[i] * osc(); tdom[i] = buf[i] * osc();
cmplx *cur = tdom + symbol_pos - (cons_rows + 1) * (symbol_len + guard_len); cmplx *cur = tdom + symbol_pos - (cons_rows + 1) * (symbol_len + guard_len);
fwd(fdom, cur); fwd(fdom, cur);
@ -504,18 +467,40 @@ struct Decoder
cons[cons_cols*j+i] = demod_or_erase(fdom[bin(i+code_off)], head[bin(i+code_off)]); cons[cons_cols*j+i] = demod_or_erase(fdom[bin(i+code_off)], head[bin(i+code_off)]);
} }
if (1) { if (1) {
value sum = 0;
for (int i = 0; i < cons_cnt; ++i) { for (int i = 0; i < cons_cnt; ++i) {
code_type tmp[mod_max]; code_type tmp[mod_max];
mod_hard(tmp, cons[i]); mod_hard(tmp, cons[i]);
sum += arg(cons[i] * conj(mod_map(tmp))); index[i] = (i % cons_cols) + code_off;
phase[i] = arg(cons[i] * conj(mod_map(tmp)));
} }
value avg = sum / cons_cnt; DSP::SimpleLinearRegression<value> dirty(index, phase, cons_cnt);
cfo_rad += avg / (symbol_len+guard_len); //std::cerr << "dirty slope = " << dirty.slope() << std::endl;
std::cerr << "finer cfo: " << cfo_rad * (rate / Const::TwoPi()) << " Hz " << std::endl; //std::cerr << "dirty yint = " << dirty.yint() << std::endl;
cmplx comp = DSP::polar<value>(1, -avg); value avg_diff = 0;
for (int i = 0; i < cons_cnt; ++i) for (int i = 0; i < cons_cnt; ++i)
cons[i] *= comp; avg_diff += dirty(index[i]) - phase[i];
avg_diff /= value(cons_cnt);
value var_diff = 0;
for (int i = 0; i < cons_cnt; ++i)
var_diff += (dirty(index[i]) - phase[i] - avg_diff) * (dirty(index[i]) - phase[i] - avg_diff);
value std_dev = std::sqrt(var_diff/(cons_cnt-1));
int count = 0;
for (int i = 0; i < cons_cnt; ++i) {
if (std::abs(dirty(index[i])-phase[i]) <= std_dev) {
index[count] = index[i];
phase[count] = phase[i];
++count;
}
}
DSP::SimpleLinearRegression<value> pruned(index, phase, count);
//std::cerr << "pruned slope = " << pruned.slope() << std::endl;
//std::cerr << "pruned yint = " << pruned.yint() << std::endl;
sfo_rad -= pruned.slope() * symbol_len / value(symbol_len+guard_len);
cfo_rad += pruned.yint() / (symbol_len+guard_len);
std::cerr << "coarse sfo: " << 1000000 * sfo_rad / Const::TwoPi() << " ppm" << std::endl;
std::cerr << "finer cfo: " << cfo_rad * (rate / Const::TwoPi()) << " Hz " << std::endl;
for (int i = 0; i < cons_cnt; ++i)
cons[i] *= DSP::polar<value>(1, -pruned((i % cons_cols) + code_off));
} }
value precision = 16; value precision = 16;
if (1) { if (1) {