Lock TX to RX frequency for clients
Central nodes (“base stations”) do no longer do coarse frequency estimation, but do only use the preamble + PLL for fine offset tracking.
This commit is contained in:
parent
4f4128fbd3
commit
93fd8aebbc
|
@ -1,6 +1,7 @@
|
||||||
#include <liquid/liquid.h>
|
#include <liquid/liquid.h>
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
#include "correlator.h"
|
#include "correlator.h"
|
||||||
#include "preamble.h"
|
#include "preamble.h"
|
||||||
|
@ -8,6 +9,7 @@
|
||||||
#include "whitening.h"
|
#include "whitening.h"
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "options.h"
|
||||||
|
|
||||||
#include "rx.h"
|
#include "rx.h"
|
||||||
|
|
||||||
|
@ -48,7 +50,7 @@ static void update_nco_pll(nco_crcf nco, float phase_error, float bw)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool acquire_preamble(layer1_rx_t *rx, const float complex sample)
|
static bool acquire_preamble(layer1_rx_t *rx, const float complex sample, bool do_coarse_freq_est)
|
||||||
{
|
{
|
||||||
static float complex freq_est_history[FREQ_EST_L];
|
static float complex freq_est_history[FREQ_EST_L];
|
||||||
static unsigned int freq_est_history_write_idx = 0;
|
static unsigned int freq_est_history_write_idx = 0;
|
||||||
|
@ -90,7 +92,7 @@ static bool acquire_preamble(layer1_rx_t *rx, const float complex sample)
|
||||||
freq_est_history_write_idx = 0;
|
freq_est_history_write_idx = 0;
|
||||||
freq_est_holdoff_samples = FREQ_EST_L;
|
freq_est_holdoff_samples = FREQ_EST_L;
|
||||||
return true; // preamble found!
|
return true; // preamble found!
|
||||||
} else {
|
} else if(do_coarse_freq_est) {
|
||||||
// preamble not found.
|
// preamble not found.
|
||||||
//DEBUG_LOG("Preamble not found: %.3f < %.3f\n", cabsf(corr_out), 0.5f * preamble_get_symbol_count());
|
//DEBUG_LOG("Preamble not found: %.3f < %.3f\n", cabsf(corr_out), 0.5f * preamble_get_symbol_count());
|
||||||
|
|
||||||
|
@ -140,9 +142,9 @@ static bool acquire_preamble(layer1_rx_t *rx, const float complex sample)
|
||||||
freq_est_history[freq_est_history_write_idx] = sample;
|
freq_est_history[freq_est_history_write_idx] = sample;
|
||||||
freq_est_history_write_idx++;
|
freq_est_history_write_idx++;
|
||||||
freq_est_history_write_idx %= FREQ_EST_L;
|
freq_est_history_write_idx %= FREQ_EST_L;
|
||||||
|
}
|
||||||
|
|
||||||
return false; // preamble not found
|
return false; // preamble not found
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -172,8 +174,16 @@ result_t layer1_rx_process(layer1_rx_t *rx, const float complex *samples, size_t
|
||||||
// Mix the input signal with the carrier NCO, which oscillates at the
|
// Mix the input signal with the carrier NCO, which oscillates at the
|
||||||
// frequency coarsly estimated so far.
|
// frequency coarsly estimated so far.
|
||||||
float complex mixed_sample;
|
float complex mixed_sample;
|
||||||
|
|
||||||
|
if(is_central_node) {
|
||||||
|
// central nodes do not do coarse frequency tracking, as the clients
|
||||||
|
// shall lock onto the central’s carrier frequency and therefore not much
|
||||||
|
// frequency deviation is expected.
|
||||||
|
mixed_sample = agc_out;
|
||||||
|
} else {
|
||||||
nco_crcf_step(rx->carrier_coarse_nco);
|
nco_crcf_step(rx->carrier_coarse_nco);
|
||||||
nco_crcf_mix_down(rx->carrier_coarse_nco, agc_out, &mixed_sample);
|
nco_crcf_mix_down(rx->carrier_coarse_nco, agc_out, &mixed_sample);
|
||||||
|
}
|
||||||
|
|
||||||
samples2dump[nsamples2dump++] = mixed_sample;
|
samples2dump[nsamples2dump++] = mixed_sample;
|
||||||
|
|
||||||
|
@ -183,7 +193,6 @@ result_t layer1_rx_process(layer1_rx_t *rx, const float complex *samples, size_t
|
||||||
symsync_crcf_execute(rx->symsync, &mixed_sample, 1, &symsync_out, &out_len);
|
symsync_crcf_execute(rx->symsync, &mixed_sample, 1, &symsync_out, &out_len);
|
||||||
|
|
||||||
if(out_len != 0) {
|
if(out_len != 0) {
|
||||||
|
|
||||||
switch(rx->state) {
|
switch(rx->state) {
|
||||||
// Try to acquire packets by synchronizing the frequency
|
// Try to acquire packets by synchronizing the frequency
|
||||||
// (symbol-independent search) and correlating the preamble.
|
// (symbol-independent search) and correlating the preamble.
|
||||||
|
@ -196,7 +205,7 @@ result_t layer1_rx_process(layer1_rx_t *rx, const float complex *samples, size_t
|
||||||
nco_crcf_set_phase(rx->carrier_coarse_nco, 0.0f);
|
nco_crcf_set_phase(rx->carrier_coarse_nco, 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(acquire_preamble(rx, symsync_out)) {
|
if(acquire_preamble(rx, symsync_out, !is_central_node)) {
|
||||||
// Preamble found and frequency corrected!
|
// Preamble found and frequency corrected!
|
||||||
rx->callback(RX_EVT_PREAMBLE_FOUND, NULL, 0);
|
rx->callback(RX_EVT_PREAMBLE_FOUND, NULL, 0);
|
||||||
|
|
||||||
|
@ -439,3 +448,9 @@ bool layer1_rx_is_busy(const layer1_rx_t *rx)
|
||||||
{
|
{
|
||||||
return rx->state != RX_STATE_ACQUISITION;
|
return rx->state != RX_STATE_ACQUISITION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float layer1_rx_get_coarse_carrier_frequency(const layer1_rx_t *rx)
|
||||||
|
{
|
||||||
|
return nco_crcf_get_frequency(rx->carrier_coarse_nco);
|
||||||
|
}
|
||||||
|
|
|
@ -120,4 +120,15 @@ result_t layer1_rx_process(layer1_rx_t *rx, const float complex *samples, size_t
|
||||||
*/
|
*/
|
||||||
bool layer1_rx_is_busy(const layer1_rx_t *rx);
|
bool layer1_rx_is_busy(const layer1_rx_t *rx);
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Retrieve the coarsely estimated carrier frequency.
|
||||||
|
*
|
||||||
|
* The carrier frequency is measured in radians per sample in the RRC-filtered baseband domain, i.e. the sampling rate is SYMBOL_RATE * RRC_SPS.
|
||||||
|
*
|
||||||
|
* \param rx Pointer to the receiver context.
|
||||||
|
* \returns The estimated carrier frequency in the RRC-filtered domain.
|
||||||
|
*/
|
||||||
|
float layer1_rx_get_coarse_carrier_frequency(const layer1_rx_t *rx);
|
||||||
|
|
||||||
#endif // LAYER1_RX_H
|
#endif // LAYER1_RX_H
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#include <liquid/liquid.h>
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
|
|
||||||
|
@ -41,6 +43,8 @@ result_t layer1_tx_init(layer1_tx_t *tx)
|
||||||
tx->samples_allocated = 0;
|
tx->samples_allocated = 0;
|
||||||
tx->samples_used = 0;
|
tx->samples_used = 0;
|
||||||
|
|
||||||
|
tx->carrier_frequency_offset = 0.0f;
|
||||||
|
|
||||||
transmission_init(&tx->transmission);
|
transmission_init(&tx->transmission);
|
||||||
packet_mod_init(&tx->pmod);
|
packet_mod_init(&tx->pmod);
|
||||||
return OK;
|
return OK;
|
||||||
|
@ -154,6 +158,28 @@ result_t layer1_tx_finalize_burst(layer1_tx_t *tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
tx->samples_used += len;
|
tx->samples_used += len;
|
||||||
|
|
||||||
|
// mix the signal up to the carrier offset frequency
|
||||||
|
|
||||||
|
// allocate a temporary buffer
|
||||||
|
float complex *tmp = malloc(sizeof(float complex) * tx->samples_used);
|
||||||
|
if(!tmp) {
|
||||||
|
fprintf(stderr, "Could not allocate buffer for TX frequency correction.\n", len, tx->samples_used);
|
||||||
|
return ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
nco_crcf carrier_nco = nco_crcf_create(LIQUID_NCO);
|
||||||
|
nco_crcf_set_frequency(carrier_nco, tx->carrier_frequency_offset);
|
||||||
|
|
||||||
|
nco_crcf_mix_block_up(carrier_nco, tx->samples, tmp, tx->samples_used);
|
||||||
|
|
||||||
|
nco_crcf_destroy(carrier_nco);
|
||||||
|
|
||||||
|
// swap the buffers
|
||||||
|
free(tx->samples);
|
||||||
|
tx->samples = tmp;
|
||||||
|
tx->samples_allocated = tx->samples_used;
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,3 +194,9 @@ const float complex* layer1_tx_get_sample_data(const layer1_tx_t *tx)
|
||||||
{
|
{
|
||||||
return tx->samples;
|
return tx->samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void layer1_tx_set_carrier_frequency_offset(layer1_tx_t *tx, float freq)
|
||||||
|
{
|
||||||
|
tx->carrier_frequency_offset = freq;
|
||||||
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@ typedef struct
|
||||||
size_t samples_allocated;
|
size_t samples_allocated;
|
||||||
size_t samples_used;
|
size_t samples_used;
|
||||||
|
|
||||||
|
float carrier_frequency_offset;
|
||||||
|
|
||||||
transmission_ctx_t transmission;
|
transmission_ctx_t transmission;
|
||||||
packet_mod_ctx_t pmod;
|
packet_mod_ctx_t pmod;
|
||||||
} layer1_tx_t;
|
} layer1_tx_t;
|
||||||
|
@ -100,4 +102,14 @@ size_t layer1_tx_get_sample_count(const layer1_tx_t *tx);
|
||||||
*/
|
*/
|
||||||
const float complex* layer1_tx_get_sample_data(const layer1_tx_t *tx);
|
const float complex* layer1_tx_get_sample_data(const layer1_tx_t *tx);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Set the carrier frequency correction in the RRC-filtered domain.
|
||||||
|
*
|
||||||
|
* The carrier frequency is measured in radians per sample in the RRC-filtered baseband domain, i.e. the sampling rate is SYMBOL_RATE * RRC_SPS.
|
||||||
|
*
|
||||||
|
* \param tx Pointer to the transmitter context.
|
||||||
|
* \param freq The carrier frequency offset to use.
|
||||||
|
*/
|
||||||
|
void layer1_tx_set_carrier_frequency_offset(layer1_tx_t *tx, float freq);
|
||||||
|
|
||||||
#endif // LAYER1_TX_H
|
#endif // LAYER1_TX_H
|
||||||
|
|
|
@ -315,6 +315,10 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
if(!on_air) {
|
if(!on_air) {
|
||||||
RESULT_CHECK(sdr_stop_rx(&sdr));
|
RESULT_CHECK(sdr_stop_rx(&sdr));
|
||||||
|
|
||||||
|
// transmit packets on the frequency where the last packet was received.
|
||||||
|
layer1_tx_set_carrier_frequency_offset(&tx, layer1_rx_get_coarse_carrier_frequency(&rx));
|
||||||
|
|
||||||
RESULT_CHECK(sdr_start_tx(&sdr, burst_len * SDR_OVERSAMPLING));
|
RESULT_CHECK(sdr_start_tx(&sdr, burst_len * SDR_OVERSAMPLING));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue