Added correlator + small test program
This commit is contained in:
parent
6c21618f1f
commit
9a1107ff38
|
@ -38,3 +38,5 @@ target_link_libraries(
|
|||
fftw3f
|
||||
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