Added correlator + small test program
This commit is contained in:
parent
6c21618f1f
commit
9a1107ff38
|
@ -38,3 +38,5 @@ target_link_libraries(
|
||||||
fftw3f
|
fftw3f
|
||||||
fec
|
fec
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_subdirectory(test)
|
||||||
|
|
113
impl/src/correlator.c
Normal file
113
impl/src/correlator.c
Normal 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
116
impl/src/correlator.h
Normal 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
11
impl/test/CMakeLists.txt
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
add_executable(
|
||||||
|
test_correlator
|
||||||
|
../src/correlator.c
|
||||||
|
../src/correlator.h
|
||||||
|
test_correlator.c
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(
|
||||||
|
test_correlator
|
||||||
|
m
|
||||||
|
)
|
51
impl/test/test_correlator.c
Normal file
51
impl/test/test_correlator.c
Normal 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;
|
||||||
|
}
|
Loading…
Reference in a new issue