Move frequency estimation to separate module
This commit is contained in:
parent
465d9a1c26
commit
8060137c1d
|
@ -21,6 +21,8 @@ set(sources
|
||||||
src/transmission.c
|
src/transmission.c
|
||||||
src/correlator.h
|
src/correlator.h
|
||||||
src/correlator.c
|
src/correlator.c
|
||||||
|
src/freq_est.h
|
||||||
|
src/freq_est.c
|
||||||
)
|
)
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
|
|
73
impl/src/freq_est.c
Normal file
73
impl/src/freq_est.c
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
#include <liquid/liquid.h>
|
||||||
|
|
||||||
|
#include "freq_est.h"
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
*t = y[0]; // This should work because x always starts at 0.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float freq_est_unknown_bpsk(const float complex *recv, size_t n, float *final_phase)
|
||||||
|
{
|
||||||
|
float phases[n];
|
||||||
|
|
||||||
|
// square the symbols and calculate phase
|
||||||
|
for(size_t i = 0; i < n; i++) {
|
||||||
|
float complex s = recv[i];
|
||||||
|
phases[i] = cargf(s * s);
|
||||||
|
}
|
||||||
|
|
||||||
|
liquid_unwrap_phase(phases, n);
|
||||||
|
|
||||||
|
float freq, phi0;
|
||||||
|
linear_regression(n, phases, &freq, &phi0);
|
||||||
|
|
||||||
|
// outputs must be divided by 2 because squaring the symbols doubles their phase.
|
||||||
|
if(final_phase) {
|
||||||
|
*final_phase = (phi0 + n * freq) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return freq/2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float freq_est_known_symbols(const float complex *recv, const float complex *ref, size_t n, float *final_phase)
|
||||||
|
{
|
||||||
|
float phases[n];
|
||||||
|
|
||||||
|
// square the symbols and calculate phase
|
||||||
|
for(size_t i = 0; i < n; i++) {
|
||||||
|
phases[i] = cargf(conjf(ref[i]) * recv[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
liquid_unwrap_phase(phases, n);
|
||||||
|
|
||||||
|
float freq, phi0;
|
||||||
|
linear_regression(n, phases, &freq, &phi0);
|
||||||
|
|
||||||
|
if(final_phase) {
|
||||||
|
*final_phase = phi0 + n * freq;
|
||||||
|
}
|
||||||
|
|
||||||
|
return freq;
|
||||||
|
}
|
38
impl/src/freq_est.h
Normal file
38
impl/src/freq_est.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#ifndef FREQ_EST_H
|
||||||
|
#define FREQ_EST_H
|
||||||
|
|
||||||
|
#include <complex.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Estimate the frequency of unknown BPSK symbols.
|
||||||
|
*
|
||||||
|
* This function squares all given symbols to remove data ambiguity, calculates
|
||||||
|
* the phase values and then performs linear regression on those to determine
|
||||||
|
* the frequency.
|
||||||
|
*
|
||||||
|
* \param recv Pointer to the array of received symbols.
|
||||||
|
* \param n Number of symbols given.
|
||||||
|
* \param final_phase Pointer to a float where the phase correction value is
|
||||||
|
* stored. May be NULL if that value is not required.
|
||||||
|
* \returns The estimated frequency in radians per sample.
|
||||||
|
*/
|
||||||
|
float freq_est_unknown_bpsk(const float complex *recv, size_t n, float *final_phase);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Estimate the frequency of known symbols.
|
||||||
|
*
|
||||||
|
* This function removes the data influence by calculating
|
||||||
|
* recv[k]*conj(ref[k]), calculates the phase values and then performs linear
|
||||||
|
* regression on those to determine the frequency.
|
||||||
|
*
|
||||||
|
* \param recv Pointer to the array of received symbols.
|
||||||
|
* \param ref Pointer to the array of reference symbols (e.g. a preamble).
|
||||||
|
* \param n Number of symbols given.
|
||||||
|
* \param final_phase Pointer to a float where the phase correction value is
|
||||||
|
* stored. May be NULL if that value is not required.
|
||||||
|
* \returns The estimated frequency in radians per sample.
|
||||||
|
*/
|
||||||
|
float freq_est_known_symbols(const float complex *recv, const float complex *ref, size_t n, float *final_phase);
|
||||||
|
|
||||||
|
#endif // FREQ_EST_H
|
|
@ -11,6 +11,7 @@
|
||||||
#include "preamble.h"
|
#include "preamble.h"
|
||||||
#include "transmission.h"
|
#include "transmission.h"
|
||||||
#include "correlator.h"
|
#include "correlator.h"
|
||||||
|
#include "freq_est.h"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
RX_STATE_ACQUISITION,
|
RX_STATE_ACQUISITION,
|
||||||
|
@ -109,9 +110,6 @@ int main(void)
|
||||||
unsigned int symsync_out_len = 0;
|
unsigned int symsync_out_len = 0;
|
||||||
|
|
||||||
#define FREQ_EST_L 8
|
#define FREQ_EST_L 8
|
||||||
float phase_history[FREQ_EST_L];
|
|
||||||
memset(phase_history, 0, sizeof(phase_history));
|
|
||||||
|
|
||||||
// General receiver state
|
// General receiver state
|
||||||
rx_state_t rx_state = RX_STATE_ACQUISITION;
|
rx_state_t rx_state = RX_STATE_ACQUISITION;
|
||||||
|
|
||||||
|
@ -170,63 +168,16 @@ int main(void)
|
||||||
} else {
|
} else {
|
||||||
// preamble not found.
|
// preamble not found.
|
||||||
|
|
||||||
// for all the output samples produced, run the frequency
|
// Run the frequency estimator in blocks of FREQ_EST_L samples.
|
||||||
// estimator. This is an implementation that works with unknown
|
// This is an implementation that works with unknown BPSK symbols
|
||||||
// BPSK symbols and therefore can be used during ramp-up and
|
// and therefore can be used during ramp-up and preamble.
|
||||||
// preamble.
|
|
||||||
|
|
||||||
if(out_len < FREQ_EST_L) {
|
|
||||||
memmove(phase_history,
|
|
||||||
phase_history + out_len,
|
|
||||||
(FREQ_EST_L-out_len) * sizeof(phase_history[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
for(unsigned int j = 0; j < out_len; j++) {
|
|
||||||
float complex *psymbol = symsync_out + symsync_out_len + j;
|
|
||||||
|
|
||||||
// square the symbol to remove BPSK ambiguity
|
|
||||||
float phase = cargf((*psymbol) * (*psymbol));
|
|
||||||
|
|
||||||
phase_history[FREQ_EST_L - out_len + j] = phase;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update the frequency estimate
|
|
||||||
if(((i/RRC_SPS) % FREQ_EST_L) == 0) {
|
if(((i/RRC_SPS) % FREQ_EST_L) == 0) {
|
||||||
float unwrapped_phase_history[FREQ_EST_L];
|
float freq_est = freq_est_unknown_bpsk(symsync_out + symsync_out_len - FREQ_EST_L, FREQ_EST_L, NULL);
|
||||||
memcpy(unwrapped_phase_history, phase_history, sizeof(unwrapped_phase_history));
|
|
||||||
liquid_unwrap_phase(unwrapped_phase_history, FREQ_EST_L);
|
|
||||||
|
|
||||||
// calculate slope of LMS-fitted line
|
float freq_adjustment = (freq_est / RRC_SPS) * 0.5f;
|
||||||
float mean_index = (FREQ_EST_L-1) / 2.0f;
|
|
||||||
float mean_phase = 0.0f;
|
|
||||||
for(unsigned int j = 0; j < FREQ_EST_L; j++) {
|
|
||||||
mean_phase += unwrapped_phase_history[j];
|
|
||||||
}
|
|
||||||
mean_phase /= FREQ_EST_L;
|
|
||||||
|
|
||||||
float numerator = 0.0f;
|
|
||||||
float denominator = 0.0f;
|
|
||||||
for(unsigned int j = 0; j < FREQ_EST_L; j++) {
|
|
||||||
float delta_index = j - mean_index;
|
|
||||||
numerator += delta_index * (unwrapped_phase_history[j] - mean_phase);
|
|
||||||
denominator += delta_index*delta_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
float lms_phase_change = numerator / denominator;
|
|
||||||
|
|
||||||
float freq_adjustment = (lms_phase_change / RRC_SPS / 2) * 0.5f;
|
|
||||||
nco_crcf_adjust_frequency(carrier_nco, freq_adjustment);
|
nco_crcf_adjust_frequency(carrier_nco, freq_adjustment);
|
||||||
|
|
||||||
printf("Frequency adjustment: %.6f - carrier frequency: %.6f\n", freq_adjustment, nco_crcf_get_frequency(carrier_nco));
|
printf("Frequency adjustment: %.6f - carrier frequency: %.6f\n", freq_adjustment, nco_crcf_get_frequency(carrier_nco));
|
||||||
|
|
||||||
if(i/RRC_SPS == 2*FREQ_EST_L) {
|
|
||||||
float complex tmp[FREQ_EST_L];
|
|
||||||
for(unsigned int j = 0; j < FREQ_EST_L; j++) {
|
|
||||||
tmp[j] = unwrapped_phase_history[j];
|
|
||||||
}
|
|
||||||
dump_array_cf(tmp, FREQ_EST_L, 1.0f, "/tmp/freq_est.cpx");
|
|
||||||
printf("MARK\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in a new issue