Commit 827558d7 authored by Titouan Soulard's avatar Titouan Soulard

libtrx: allow recording samples in VCR driver

parent 9f3b704d
......@@ -5,7 +5,7 @@ LIB_FLAGS =-lc -ldl -lm -libverbs -lpthread -L./out
FORCE_LIB_FLAGS = -Wl,--no-as-needed -lm -lpthread
all: trx out/rdma-test out/generate-iq-samples
trx: out/libtrx_rdma.so out/libtrx_play.so out/trx-bridge
trx: out/libtrx_rdma.so out/libtrx_vcr.so out/trx-bridge
install: all
ln -sfr out/libcapulet.so /usr/local/lib/
......@@ -43,7 +43,7 @@ out/libpotoml.so: common/hashtable.lo libpotoml/toml.lo
out/libtrx_rdma.so: out/libcapulet.so common/circular_buffer.lo libtrx/trx_rdma.lo
gcc -shared -fPIC -DPIC -o $@ common/circular_buffer.lo libtrx/trx_rdma.lo $(LIB_FLAGS) -lcapulet
out/libtrx_play.so: common/circular_buffer.lo libtrx/trx_play.lo
out/libtrx_vcr.so: common/circular_buffer.lo libtrx/trx_vcr.lo
gcc -shared -fPIC -DPIC -o $@ $^ $(LIB_FLAGS)
out/rdma-test: example/rdma_test.o out/libcapulet.so
......
......@@ -9,8 +9,14 @@
#include "amarisoft/trx_driver.h"
#include "common/circular_buffer.h"
struct TRXPlayContext {
struct CommonCBBuffer *samples_buffer;
struct TRXVCRContext {
struct CommonCBBuffer *play_buffer;
TRXComplex *record_buffer;
trx_timestamp_t last_timestamp;
char *samples_file_path;
int record_file_length;
int recorded_samples;
char *record_file_path;
char *play_file_path;
};
#include "libtrx/trx_play.h"
int trx_play_start(TRXState *s, const TRXDriverParams2 *p) {
struct TRXPlayContext *trx_play_ctx;
FILE *samples_file_handle;
size_t result;
uint32_t samples_file_size;
char *samples_file_content;
trx_play_ctx = s->opaque;
samples_file_handle = fopen(trx_play_ctx->samples_file_path, "r");
if(!samples_file_handle) {
fprintf(stderr, "trx_play: file %s does not exist\n", trx_play_ctx->samples_file_path);
return -1;
}
// Allocate the right size
fseek(samples_file_handle, 0, SEEK_END);
samples_file_size = ftell(samples_file_handle);
rewind(samples_file_handle);
samples_file_content = malloc(samples_file_size);
result = fread(samples_file_content, samples_file_size, 1, samples_file_handle);
if(result != 1) {
fprintf(stderr, "trx_play: could not read samples from file\n");
return -1;
}
// Use an infinite circular buffer with same length as the file
trx_play_ctx->samples_buffer = common_circular_buffer_create(samples_file_size, true);
common_circular_buffer_write(trx_play_ctx->samples_buffer, (void *) samples_file_content, samples_file_size);
printf("trx_play: looping %lu frames from %s\n", samples_file_size / sizeof(TRXComplex), trx_play_ctx->samples_file_path);
fclose(samples_file_handle);
free(trx_play_ctx->samples_file_path);
return 0;
}
void trx_play_write(TRXState *s, trx_timestamp_t timestamp, const void **samples, int count, int tx_port_index, TRXWriteMetadata *md) {
// Nothing to do
}
int trx_play_read(TRXState *s, trx_timestamp_t *ptimestamp, void **psamples, int count, int rx_port_index, TRXReadMetadata *md) {
struct TRXPlayContext *trx_play_ctx;
TRXComplex **channels;
trx_play_ctx = s->opaque;
channels = (TRXComplex **) psamples;
// Ignoring result: reading an infinite buffer will always work
common_circular_buffer_read(trx_play_ctx->samples_buffer, (void *) channels[0], count * sizeof(TRXComplex));
memcpy((void *) channels[1], (void *) channels[0], count * sizeof(TRXComplex));
trx_play_ctx->last_timestamp += count;
*ptimestamp = trx_play_ctx->last_timestamp;
return count;
}
static void trx_play_end(TRXState *s) {
struct TRXPlayContext *trx_play_ctx;
trx_play_ctx = s->opaque;
common_circular_buffer_free(trx_play_ctx->samples_buffer);
free(trx_play_ctx);
}
int trx_driver_init(TRXState *s) {
struct TRXPlayContext *trx_play_ctx = malloc(sizeof(struct TRXPlayContext));
if(s->trx_api_version != TRX_API_VERSION) {
fprintf(stderr, "trx_play: ABI compatibility mismatch between LTEENB and TRX driver (LTEENB ABI version=%d, TRX driver ABI version=%d)\n", s->trx_api_version, TRX_API_VERSION);
return -1;
}
trx_play_ctx->samples_file_path = trx_get_param_string(s, "file_path");
if(!trx_play_ctx->samples_file_path) {
fprintf(stderr, "trx_play: missing `file_path` parameter\n");
return -1;
}
s->opaque = trx_play_ctx;
s->trx_start_func2 = trx_play_start;
s->trx_write_func2 = trx_play_write;
s->trx_read_func2 = trx_play_read;
s->trx_end_func = trx_play_end;
return 0;
}
#include "libtrx/trx_vcr.h"
int trx_vcr_start(TRXState *s, const TRXDriverParams2 *p) {
struct TRXVCRContext *trx_vcr_ctx;
size_t block_count;
FILE *play_file_handle;
uint32_t play_file_size;
char *play_file_content;
trx_vcr_ctx = s->opaque;
// Play mode: allocate a buffer and fill it from file
play_file_handle = fopen(trx_vcr_ctx->play_file_path, "r");
if(!play_file_handle) {
fprintf(stderr, "trx_vcr: file %s does not exist\n", trx_vcr_ctx->play_file_path);
return -1;
}
// Allocate the right size
fseek(play_file_handle, 0, SEEK_END);
play_file_size = ftell(play_file_handle);
rewind(play_file_handle);
play_file_content = malloc(play_file_size);
block_count = fread(play_file_content, play_file_size, 1, play_file_handle);
if(block_count != 1) {
fprintf(stderr, "trx_vcr: could not read samples from file\n");
return -1;
}
// Use an infinite circular buffer with same length as the file
trx_vcr_ctx->play_buffer = common_circular_buffer_create(play_file_size, true);
common_circular_buffer_write(trx_vcr_ctx->play_buffer, (void *) play_file_content, play_file_size);
printf("trx_vcr: looping %lu frames from %s\n", play_file_size / sizeof(TRXComplex), trx_vcr_ctx->play_file_path);
fclose(play_file_handle);
free(trx_vcr_ctx->play_file_path);
// Record mode: allocate a buffer of given length
if(trx_vcr_ctx->record_file_length && trx_vcr_ctx->record_file_path) {
trx_vcr_ctx->record_buffer = malloc(trx_vcr_ctx->record_file_length * sizeof(TRXComplex));
}
return 0;
}
void trx_vcr_write(TRXState *s, trx_timestamp_t timestamp, const void **samples, int count, int tx_port_index, TRXWriteMetadata *md) {
struct TRXVCRContext *trx_vcr_ctx;
TRXComplex **channels;
FILE *record_file_handle;
trx_vcr_ctx = s->opaque;
channels = (TRXComplex **) samples;
// Early return when recording is not needed
if(!trx_vcr_ctx->record_buffer) return;
if(trx_vcr_ctx->record_file_length > trx_vcr_ctx->recorded_samples) {
int sample_count = MIN(count, trx_vcr_ctx->record_file_length - trx_vcr_ctx->recorded_samples);
// XXX: handle multiple channels
memcpy(trx_vcr_ctx->record_buffer + trx_vcr_ctx->recorded_samples, (void *) channels[0], sample_count * sizeof(TRXComplex));
trx_vcr_ctx->recorded_samples += sample_count;
} else {
// Write to file at the end to avoid disturbing sample collection
record_file_handle = fopen(trx_vcr_ctx->record_file_path, "w");
fwrite(trx_vcr_ctx->record_buffer, trx_vcr_ctx->record_file_length * sizeof(TRXComplex), 1, record_file_handle);
fclose(record_file_handle);
printf("trx_vcr: saving %u frames to %s\n", trx_vcr_ctx->record_file_length, trx_vcr_ctx->record_file_path);
// Make sure to early return on next call to write
trx_vcr_ctx->record_buffer = NULL;
}
}
int trx_vcr_read(TRXState *s, trx_timestamp_t *ptimestamp, void **psamples, int count, int rx_port_index, TRXReadMetadata *md) {
struct TRXVCRContext *trx_vcr_ctx;
TRXComplex **channels;
trx_vcr_ctx = s->opaque;
channels = (TRXComplex **) psamples;
// Ignoring result: reading an infinite buffer will always work
common_circular_buffer_read(trx_vcr_ctx->play_buffer, (void *) channels[0], count * sizeof(TRXComplex));
memcpy((void *) channels[1], (void *) channels[0], count * sizeof(TRXComplex));
trx_vcr_ctx->last_timestamp += count;
*ptimestamp = trx_vcr_ctx->last_timestamp;
return count;
}
static void trx_vcr_end(TRXState *s) {
struct TRXVCRContext *trx_vcr_ctx;
trx_vcr_ctx = s->opaque;
common_circular_buffer_free(trx_vcr_ctx->play_buffer);
free(trx_vcr_ctx);
}
int trx_driver_init(TRXState *s) {
struct TRXVCRContext *trx_vcr_ctx = malloc(sizeof(struct TRXVCRContext));
char *record_file_length_string;
if(s->trx_api_version != TRX_API_VERSION) {
fprintf(stderr, "trx_vcr: ABI compatibility mismatch between LTEENB and TRX driver (LTEENB ABI version=%d, TRX driver ABI version=%d)\n", s->trx_api_version, TRX_API_VERSION);
return -1;
}
record_file_length_string = trx_get_param_string(s, "record_length");
if(record_file_length_string) {
trx_vcr_ctx->record_file_length = atoi(record_file_length_string);
free(record_file_length_string);
}
trx_vcr_ctx->record_buffer = NULL;
trx_vcr_ctx->recorded_samples = 0;
trx_vcr_ctx->record_file_path = trx_get_param_string(s, "record_file");
trx_vcr_ctx->play_file_path = trx_get_param_string(s, "play_file");
if(!trx_vcr_ctx->play_file_path) {
fprintf(stderr, "trx_vcr: missing `play_file` parameter\n");
return -1;
}
s->opaque = trx_vcr_ctx;
s->trx_start_func2 = trx_vcr_start;
s->trx_write_func2 = trx_vcr_write;
s->trx_read_func2 = trx_vcr_read;
s->trx_end_func = trx_vcr_end;
return 0;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment