208 lines
4.8 KiB
C
208 lines
4.8 KiB
C
#include <liquid/liquid.h>
|
|
#include <math.h>
|
|
|
|
#include "freq_est.h"
|
|
|
|
#include "utils.h"
|
|
|
|
#if 0
|
|
// Fits a line y' = m*x' + t to the given points (x, y), where x is the
|
|
// zero-based index in the array.
|
|
static void linear_regression(size_t nx, const float *y, float *m, float *t)
|
|
{
|
|
float mean_x = (nx-1) / 2.0f;
|
|
float mean_y = 0.0f;
|
|
for(unsigned int j = 0; j < nx; j++) {
|
|
mean_y += y[j];
|
|
}
|
|
mean_y /= nx;
|
|
|
|
float numerator = 0.0f;
|
|
float denominator = 0.0f;
|
|
for(unsigned int j = 0; j < nx; j++) {
|
|
float delta_index = j - mean_x;
|
|
numerator += delta_index * (y[j] - mean_y);
|
|
denominator += delta_index*delta_index;
|
|
}
|
|
|
|
*m = numerator / denominator;
|
|
*t = mean_y - (*m) * mean_x;
|
|
}
|
|
|
|
// Calculates an estimation of the true peak frequency in the signal by
|
|
// calculating the FFT und interpolating around the bin with the highest value.
|
|
static float fft_true_peak(const float complex *recv, size_t n)
|
|
{
|
|
float complex *out = fft_malloc(n * sizeof(float complex));
|
|
|
|
fft_run(n, (float complex*)recv, out, LIQUID_FFT_FORWARD, 0);
|
|
fft_shift(out, n); // shifts by n/2
|
|
|
|
// search for the maximum-amplitude bin
|
|
size_t peak_pos = 0;
|
|
float peak_amplitude = 0.0f;
|
|
|
|
float amp[n];
|
|
for(size_t i = 0; i < n; i++) {
|
|
amp[i] = cabsf(out[i]);
|
|
if(amp[i] > peak_amplitude) {
|
|
peak_amplitude = amp[i];
|
|
peak_pos = i;
|
|
}
|
|
}
|
|
|
|
dump_array_f(amp, n, 1.0f, "/tmp/fft_amp.f32");
|
|
|
|
// interpolation does not work at the first and last bin
|
|
if(peak_pos == 0 || peak_pos == (n-1)) {
|
|
return (peak_pos - n/2) / n;
|
|
}
|
|
|
|
float a1 = amp[peak_pos - 1];
|
|
float a2 = amp[peak_pos];
|
|
float a3 = amp[peak_pos + 1];
|
|
|
|
// find fractional offset to peak_pos by quadratic interpolation.
|
|
// Source: https://dspguru.com/dsp/howtos/how-to-interpolate-fft-peak/
|
|
float d = (a3 - a1) / (2 * (2 * a2 - a1 - a3));
|
|
|
|
fft_free(out);
|
|
|
|
return ((float)peak_pos + d - (float)(n/2)) / n;
|
|
}
|
|
#endif
|
|
|
|
|
|
// Frequency estimation algorithm by Luise & Reggiannini
|
|
float freq_est_lr(const float complex *recv, size_t l0)
|
|
{
|
|
const size_t N = l0/2;
|
|
|
|
float complex R[N];
|
|
|
|
// calculate R[kappa]
|
|
for(size_t kappa = 1; kappa <= N; kappa++) {
|
|
R[kappa-1] = 0.0f;
|
|
|
|
for(size_t k = 0; k < l0 - kappa; k++) {
|
|
R[kappa-1] += recv[k + kappa] * conjf(recv[k]);
|
|
}
|
|
|
|
R[kappa-1] /= l0 - kappa;
|
|
}
|
|
|
|
// sum over R[kappa]
|
|
float complex R_total = 0.0f;
|
|
for(size_t kappa = 1; kappa <= N; kappa++) {
|
|
R_total += R[kappa-1];
|
|
}
|
|
|
|
// calculate phase angle of summed R
|
|
float arg = cargf(R_total);
|
|
|
|
// convert to frequency offset in radians per sample
|
|
float freq_offset = 2.0f * arg / (N+1); /* FIXME: check where the factor 2.0 comes from… */
|
|
|
|
return freq_offset;
|
|
}
|
|
|
|
|
|
float freq_est_unknown_bpsk(const float complex *recv, size_t n, float *final_phase)
|
|
{
|
|
float complex recv_sq[n];
|
|
|
|
// square the symbols and calculate phase
|
|
for(size_t i = 0; i < n; i++) {
|
|
float complex s = recv[i];
|
|
recv_sq[i] = s * s;
|
|
}
|
|
|
|
float freq_est = freq_est_lr(recv_sq, n);
|
|
float phi0 = cargf(recv[0]);
|
|
|
|
// outputs must be divided by 2 because squaring the symbols doubles their phase.
|
|
if(final_phase) {
|
|
*final_phase = (phi0 + n * freq_est) / 2;
|
|
}
|
|
|
|
return freq_est/2;
|
|
}
|
|
|
|
|
|
float freq_est_in_rampup(const float complex *recv, size_t n, float *final_phase)
|
|
{
|
|
float complex rotated[n];
|
|
|
|
float prev_phase = 0.0f;
|
|
|
|
// rotate every second symbol by 180°. As a plausibility check that we are
|
|
// in fact inside the ramp-up, we verify that there is no phase rotation by
|
|
// more than 90° between the symbols.
|
|
for(size_t i = 0; i < n; i++) {
|
|
if((i % 2) == 0) {
|
|
rotated[i] = recv[i];
|
|
} else {
|
|
rotated[i] = -recv[i];
|
|
}
|
|
|
|
float phase = cargf(rotated[i]);
|
|
if(i > 0) {
|
|
float phase_delta = phase - prev_phase;
|
|
if(fabsf(phase_delta) > 3.14159f/2.0f) {
|
|
// abort because we either have the wrong signal or too much noise.
|
|
if(final_phase) {
|
|
*final_phase = 0.0f;
|
|
}
|
|
return 0.0f;
|
|
}
|
|
}
|
|
prev_phase = phase;
|
|
}
|
|
|
|
// signal is clean (and all symbol information removed), so we run the estimator
|
|
|
|
return freq_est_data_free(rotated, n, final_phase);
|
|
}
|
|
|
|
|
|
float freq_est_known_symbols(const float complex *recv, const float complex *ref, size_t n, float *final_phase)
|
|
{
|
|
float complex symbols[n];
|
|
|
|
// remove the data influence from the symbols
|
|
for(size_t i = 0; i < n; i++) {
|
|
symbols[i] = conjf(ref[i]) * recv[i];
|
|
}
|
|
|
|
return freq_est_data_free(symbols, n, final_phase);
|
|
}
|
|
|
|
|
|
float freq_est_data_free(const float complex *symbols, size_t n, float *final_phase)
|
|
{
|
|
float phases[n];
|
|
|
|
for(size_t i = 0; i < n; i++) {
|
|
phases[i] = cargf(symbols[i]);
|
|
}
|
|
|
|
liquid_unwrap_phase(phases, n);
|
|
|
|
float freq, phi0;
|
|
freq = freq_est_lr(symbols, n);
|
|
|
|
// calculate the mean of unwrapped symbol phases and from that the
|
|
// de-noised phase at the end of the sequence.
|
|
phi0 = 0.0f;
|
|
for(size_t i = 0; i < n; i++) {
|
|
phi0 += phases[i];
|
|
}
|
|
phi0 /= n;
|
|
|
|
if(final_phase) {
|
|
*final_phase = phi0 + (n+1)/2.0f * freq;
|
|
}
|
|
|
|
return freq;
|
|
}
|