Added correlator + small test program

This commit is contained in:
Thomas Kolb 2022-01-22 22:42:05 +01:00
parent 6c21618f1f
commit 9a1107ff38
5 changed files with 293 additions and 0 deletions

View file

@ -38,3 +38,5 @@ target_link_libraries(
fftw3f fftw3f
fec fec
) )
add_subdirectory(test)

113
impl/src/correlator.c Normal file
View file

@ -0,0 +1,113 @@
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include "correlator.h"
bool correlator_init(correlator_ctx_t *ctx, const float complex *search_seq, size_t nsymbols)
{
// copy arguments and initialize variables
ctx->search_sequence_len = nsymbols;
ctx->input_history = NULL;
ctx->search_sequence = NULL;
ctx->history_ptr = 0;
// Determine the smallest power of two greater than nsymbols
ctx->buffer_size = 1;
while(nsymbols > 0) {
ctx->buffer_size <<= 1;
nsymbols >>= 1;
}
// If buffer_size = 0b1000, then buffer_size_mask=0b0111
ctx->buffer_size_mask = ctx->buffer_size - 1;
// Allocate the buffers
ctx->input_history = malloc(ctx->buffer_size * sizeof(ctx->input_history[0]));
if(!ctx->input_history) {
fprintf(stderr, "correlator: malloc() failed!\n");
goto fail;
}
ctx->search_sequence = malloc(ctx->buffer_size * sizeof(ctx->search_sequence[0]));
if(!ctx->search_sequence) {
fprintf(stderr, "correlator: malloc() failed!\n");
goto fail;
}
// Clear the history buffer
memset(ctx->input_history, 0, ctx->buffer_size * sizeof(ctx->input_history[0]));
// Prepare the search sequence.
// The complex values are conjugated in the process.
for(size_t i = 0; i < ctx->search_sequence_len; i++) {
ctx->search_sequence[i] = conjf(search_seq[i]);
}
// Success!
return true;
fail:
correlator_free(ctx);
return false;
}
void correlator_free(correlator_ctx_t *ctx)
{
if(ctx->input_history) {
free(ctx->input_history);
ctx->input_history = NULL;
}
if(ctx->search_sequence) {
free(ctx->search_sequence);
ctx->input_history = NULL;
}
ctx->buffer_size = 0;
ctx->search_sequence_len = 0;
}
float complex correlator_step(correlator_ctx_t *ctx, float complex sample)
{
float complex result = 0;
// increment and wrap the history pointer
ctx->history_ptr++;
ctx->history_ptr &= ctx->buffer_size_mask;
// copy the input sample to the history
ctx->input_history[ctx->history_ptr] = sample;
size_t n = (ctx->history_ptr - ctx->search_sequence_len + 1) & ctx->buffer_size_mask;
// calculate the correlation
for(size_t m = 0; m < ctx->search_sequence_len; m++) {
size_t nm = (n + m) & ctx->buffer_size_mask;
result += ctx->search_sequence[m] * ctx->input_history[nm];
}
return result;
}
void correlator_get_input_history(correlator_ctx_t *ctx, float complex *history)
{
size_t n = (ctx->history_ptr - ctx->search_sequence_len + 1) & ctx->buffer_size_mask;
for(size_t m = 0; m < ctx->search_sequence_len; m++) {
size_t nm = (n + m) & ctx->buffer_size_mask;
history[m] = ctx->input_history[nm];
}
}
const float complex* correlator_get_conj_search_sequence(correlator_ctx_t *ctx)
{
return ctx->search_sequence;
}

116
impl/src/correlator.h Normal file
View file

@ -0,0 +1,116 @@
#ifndef CORRELATOR_H
#define CORRELATOR_H
/*!
* \file
*
* \brief A cross-correlator.
*
* \details
* This module implements a cross-correlator that correlates a static sequence
* (like a preamble) with a continuous stream of symbols. For example, it can
* be used to detect the start of a packet in a stream of samples.
*/
#include <stdbool.h>
#include <stdlib.h>
#include <complex.h>
/*!
* \brief Context information for the correlator.
*/
typedef struct
{
float complex *search_sequence;
float complex *input_history;
/*!
* \brief The history pointer.
*
* \details
* Always points to the last updated sample in the input_history. On every
* written sample, the pointer is incremented and wrapped using
* buffer_size_mask. Therefore, older samples have lower indices in the
* array.
*/
size_t history_ptr;
size_t search_sequence_len;
size_t buffer_size;
size_t buffer_size_mask;
} correlator_ctx_t;
/*!
* \brief Initialize a correlator.
*
* \details
* Initializes all variables in the given context and imports the search sequence.
*
* The search sequence needs to be given such that index 0 contains the
* oldest correlated sample.
*
* \param ctx The correlator context to initialize.
* \param search_seq The search sequence.
* \param nsymbols Length of the search sequence.
*
* \returns Whether the correlator was initialized successfully.
*/
bool correlator_init(correlator_ctx_t *ctx, const float complex *search_seq, size_t nsymbols);
/*!
* \brief Delete a correlator.
*
* \param ctx The correlator context to free.
*/
void correlator_free(correlator_ctx_t *ctx);
/*!
* \brief Process one sample in the correlator.
*
* The input sample will be stored in the internal history buffer and then the
* following calculation will be executed, where k determines the age of the
* sample and n is the size of the search sequence:
*
* result = sum{k = 0..n-1} (input_history[k] * search_sequence[k])
*
* \param ctx The correlator context to use.
* \param sample The input sample to process.
*
* \returns The new correlator output value.
*/
float complex correlator_step(correlator_ctx_t *ctx, float complex sample);
/*!
* \brief Retrieve the history of input values.
*
* \details
* This function gives the last n input values, where n is the size of the
* search sequence.
*
* The values will be ordered such that the oldest value has index 0.
*
* Note that this function is rather expensive as it copies all the data.
* However it might releave the user of keeping track of the input samples in
* case the history is only used occasionally.
*
* \param ctx The correlator context to use.
* \param history Pointer to the output array. Must have space for n values.
*/
void correlator_get_input_history(correlator_ctx_t *ctx, float complex *history);
/*!
* \brief Retrieve the conjugated search sequence.
*
* \details
* Returns a pointer to the internally used search sequence, which corresponds
* to the sequence given to \ref correlator_init(), but all values are
* conjugated.
*
* \param ctx The correlator context to use.
* \returns A pointer to the internal search sequence.
*/
const float complex* correlator_get_conj_search_sequence(correlator_ctx_t *ctx);
#endif // CORRELATOR_H

11
impl/test/CMakeLists.txt Normal file
View file

@ -0,0 +1,11 @@
add_executable(
test_correlator
../src/correlator.c
../src/correlator.h
test_correlator.c
)
target_link_libraries(
test_correlator
m
)

View file

@ -0,0 +1,51 @@
#include <stdio.h>
#include <correlator.h>
#define ARRAY_LEN(x) (sizeof(x) / sizeof(x[0]))
static const float complex SEARCH_SEQ[5] = {-1, 1, 1, -1, 1};
static const float complex INPUT_SEQ[10] = {1, 1, -1, 1, -1, 1, 1, -1, 1, -1};
int main(void)
{
correlator_ctx_t c;
float complex output_seq[ARRAY_LEN(INPUT_SEQ)];
if(!correlator_init(&c, SEARCH_SEQ, ARRAY_LEN(SEARCH_SEQ))) {
fprintf(stderr, "Failed to initialize the correlator!\n");
return 1;
}
for(size_t i = 0; i < ARRAY_LEN(INPUT_SEQ); i++) {
output_seq[i] = correlator_step(&c, INPUT_SEQ[i]);
printf("%3zu %.3f+%.3fj %.3f+%.3fj", i,
creal(INPUT_SEQ[i]), cimag(INPUT_SEQ[i]),
creal(output_seq[i]), cimag(output_seq[i]));
if(cabsf(output_seq[i]) > ((size_t)ARRAY_LEN(SEARCH_SEQ) - 0.5f)) {
printf(" <<< found!\n");
const complex float *search_seq_conj = correlator_get_conj_search_sequence(&c);
complex float history[ARRAY_LEN(SEARCH_SEQ)];
correlator_get_input_history(&c, history);
printf(" Search sequence: ");
for(size_t i = 0; i < ARRAY_LEN(SEARCH_SEQ); i++) {
printf("%.3f%+.3fj ", crealf(search_seq_conj[i]), cimagf(search_seq_conj[i]));
}
printf("\n Input history: ");
for(size_t i = 0; i < ARRAY_LEN(SEARCH_SEQ); i++) {
printf("%.3f%+.3fj ", crealf(history[i]), cimagf(history[i]));
}
}
printf("\n");
}
correlator_free(&c);
return 0;
}