Move the transmission chain to a separate "tx" module
This commit is contained in:
parent
97772fa12b
commit
a5634ed736
|
@ -23,6 +23,8 @@ set(sources
|
||||||
src/layer1/results.h
|
src/layer1/results.h
|
||||||
src/layer1/transmission.c
|
src/layer1/transmission.c
|
||||||
src/layer1/transmission.h
|
src/layer1/transmission.h
|
||||||
|
src/layer1/tx.c
|
||||||
|
src/layer1/tx.h
|
||||||
src/layer1/whitening.c
|
src/layer1/whitening.c
|
||||||
src/layer1/whitening.h
|
src/layer1/whitening.h
|
||||||
)
|
)
|
||||||
|
|
|
@ -96,3 +96,24 @@ result_t transmission_filter_packet(
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t transmission_calculate_ramp_up_length(transmission_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
(void)ctx;
|
||||||
|
return RRC_SPS * TRANSMISSION_RAMP_UP_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t transmission_calculate_ramp_down_length(transmission_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
(void)ctx;
|
||||||
|
return RRC_SPS * TRANSMISSION_RAMP_DOWN_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t transmission_calculate_packet_length(transmission_ctx_t *ctx, size_t symbol_count)
|
||||||
|
{
|
||||||
|
(void)ctx;
|
||||||
|
return RRC_SPS * symbol_count;
|
||||||
|
}
|
||||||
|
|
|
@ -76,4 +76,29 @@ result_t transmission_filter_packet(
|
||||||
size_t *output_size);
|
size_t *output_size);
|
||||||
|
|
||||||
|
|
||||||
|
/*!\brief Calculate the number of samples required for ramp-up.
|
||||||
|
*
|
||||||
|
* \param ctx Pointer to the transmission context.
|
||||||
|
* \returns The number of samples required.
|
||||||
|
*/
|
||||||
|
size_t transmission_calculate_ramp_up_length(transmission_ctx_t *ctx);
|
||||||
|
|
||||||
|
|
||||||
|
/*!\brief Calculate the number of samples required for ramp-down.
|
||||||
|
*
|
||||||
|
* \param ctx Pointer to the transmission context.
|
||||||
|
* \returns The number of samples required.
|
||||||
|
*/
|
||||||
|
size_t transmission_calculate_ramp_down_length(transmission_ctx_t *ctx);
|
||||||
|
|
||||||
|
|
||||||
|
/*!\brief Calculate the number of samples required for the given number of packet symbols.
|
||||||
|
*
|
||||||
|
* \param ctx Pointer to the transmission context.
|
||||||
|
* \param symbol_count Size of the packet in symbols.
|
||||||
|
* \returns The number of samples required.
|
||||||
|
*/
|
||||||
|
size_t transmission_calculate_packet_length(transmission_ctx_t *ctx, size_t symbol_count);
|
||||||
|
|
||||||
|
|
||||||
#endif // LAYER1_TRANSMISSION_H
|
#endif // LAYER1_TRANSMISSION_H
|
||||||
|
|
168
impl/src/layer1/tx.c
Normal file
168
impl/src/layer1/tx.c
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
|
#include "layer1/results.h"
|
||||||
|
#include "tx.h"
|
||||||
|
|
||||||
|
static inline bool resize_buffer(layer1_tx_t *tx, size_t new_size)
|
||||||
|
{
|
||||||
|
void *tmpptr = realloc(tx->samples, new_size * sizeof(*(tx->samples)));
|
||||||
|
if(tmpptr) {
|
||||||
|
tx->samples = tmpptr;
|
||||||
|
tx->samples_allocated = new_size;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline bool can_append_n_samples(layer1_tx_t *tx, size_t samples)
|
||||||
|
{
|
||||||
|
return (tx->samples_used + samples) <= tx->samples_allocated;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline size_t get_free_space(layer1_tx_t *tx)
|
||||||
|
{
|
||||||
|
return tx->samples_allocated - tx->samples_used;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline float complex* get_write_ptr(layer1_tx_t *tx)
|
||||||
|
{
|
||||||
|
return tx->samples + tx->samples_used;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
result_t layer1_tx_init(layer1_tx_t *tx)
|
||||||
|
{
|
||||||
|
tx->samples = NULL;
|
||||||
|
tx->samples_allocated = 0;
|
||||||
|
tx->samples_used = 0;
|
||||||
|
|
||||||
|
transmission_init(&tx->transmission);
|
||||||
|
packet_mod_init(&tx->pmod);
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
result_t layer1_tx_shutdown(layer1_tx_t *tx)
|
||||||
|
{
|
||||||
|
if(tx->samples) {
|
||||||
|
free(tx->samples);
|
||||||
|
tx->samples = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
tx->samples_allocated = 0;
|
||||||
|
tx->samples_used = 0;
|
||||||
|
|
||||||
|
transmission_free(&tx->transmission);
|
||||||
|
packet_mod_free(&tx->pmod);
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
result_t layer1_tx_reset(layer1_tx_t *tx)
|
||||||
|
{
|
||||||
|
tx->samples_used = 0;
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
result_t layer1_tx_add_packet_to_burst(layer1_tx_t *tx, const uint8_t *data, size_t length)
|
||||||
|
{
|
||||||
|
if(tx->samples_used == 0) {
|
||||||
|
if(!resize_buffer(tx, 4096 + transmission_calculate_ramp_up_length(&tx->transmission))) {
|
||||||
|
return ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
// nothing is in the buffer yet, so we must first generate a ramp-up.
|
||||||
|
size_t len = get_free_space(tx);
|
||||||
|
result_t res = transmission_ramp_up(&tx->transmission, get_write_ptr(tx), &len);
|
||||||
|
|
||||||
|
if(res != OK) {
|
||||||
|
printf("Ramp-up requires %zd samples at offset %zd.\n", len, tx->samples_used);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
tx->samples_used += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// encode and modulate the packet
|
||||||
|
packet_mod_set_data(&tx->pmod, data, length);
|
||||||
|
packet_mod_encode(&tx->pmod);
|
||||||
|
packet_mod_modulate(&tx->pmod);
|
||||||
|
packet_mod_add_header(&tx->pmod);
|
||||||
|
packet_mod_add_preamble(&tx->pmod);
|
||||||
|
|
||||||
|
// determine number of symbols in the packet
|
||||||
|
size_t nsyms;
|
||||||
|
packet_mod_get_result_cf(&tx->pmod, NULL, &nsyms);
|
||||||
|
|
||||||
|
// calculate the number of filtered samples
|
||||||
|
size_t filtered_size = transmission_calculate_packet_length(&tx->transmission, nsyms);
|
||||||
|
|
||||||
|
// resize the buffer if necessary (and add some reserve)
|
||||||
|
if(!can_append_n_samples(tx, filtered_size)) {
|
||||||
|
if(!resize_buffer(tx, tx->samples_allocated + 2*filtered_size)) {
|
||||||
|
return ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// store the packet symbols into a temporary buffer
|
||||||
|
float complex msg_mod[nsyms];
|
||||||
|
packet_mod_get_result_cf(&tx->pmod, msg_mod, &nsyms);
|
||||||
|
|
||||||
|
// filter the packet and append it to the output buffer
|
||||||
|
size_t len = get_free_space(tx);
|
||||||
|
result_t res = transmission_filter_packet(&tx->transmission, msg_mod, nsyms, get_write_ptr(tx), &len);
|
||||||
|
|
||||||
|
if(res != OK) {
|
||||||
|
printf("Packet requires %zd samples at offset %zd.\n", len, tx->samples_used);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
tx->samples_used += len;
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
result_t layer1_tx_finalize_burst(layer1_tx_t *tx)
|
||||||
|
{
|
||||||
|
// calculate the number of samples for the ramp down
|
||||||
|
size_t len = transmission_calculate_ramp_down_length(&tx->transmission);
|
||||||
|
|
||||||
|
// resize the buffer if necessary
|
||||||
|
if(!can_append_n_samples(tx, len)) {
|
||||||
|
if(!resize_buffer(tx, tx->samples_allocated + len)) {
|
||||||
|
return ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate the ramp-down
|
||||||
|
len = get_free_space(tx);
|
||||||
|
result_t res = transmission_ramp_down(&tx->transmission, get_write_ptr(tx), &len);
|
||||||
|
|
||||||
|
if(res != OK) {
|
||||||
|
printf("Ramp-down requires %zd samples at offset %zd.\n", len, tx->samples_used);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
tx->samples_used += len;
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t layer1_tx_get_sample_count(const layer1_tx_t *tx)
|
||||||
|
{
|
||||||
|
return tx->samples_used;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const float complex* layer1_tx_get_sample_data(const layer1_tx_t *tx)
|
||||||
|
{
|
||||||
|
return tx->samples;
|
||||||
|
}
|
103
impl/src/layer1/tx.h
Normal file
103
impl/src/layer1/tx.h
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
#ifndef LAYER1_TX_H
|
||||||
|
#define LAYER1_TX_H
|
||||||
|
|
||||||
|
#include <complex.h>
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "results.h"
|
||||||
|
#include "transmission.h"
|
||||||
|
#include "packet_mod.h"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
float complex *samples;
|
||||||
|
size_t samples_allocated;
|
||||||
|
size_t samples_used;
|
||||||
|
|
||||||
|
transmission_ctx_t transmission;
|
||||||
|
packet_mod_ctx_t pmod;
|
||||||
|
} layer1_tx_t;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Initialize the transmitter.
|
||||||
|
*
|
||||||
|
* \param tx Pointer to the transmitter context.
|
||||||
|
* \returns The result of the initialization.
|
||||||
|
*/
|
||||||
|
result_t layer1_tx_init(layer1_tx_t *tx);
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Shut the transmitter down. Frees all memory.
|
||||||
|
*
|
||||||
|
* \param tx Pointer to the transmitter context.
|
||||||
|
* \returns The result of the shutdown.
|
||||||
|
*/
|
||||||
|
result_t layer1_tx_shutdown(layer1_tx_t *tx);
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Reset the transmitter.
|
||||||
|
*
|
||||||
|
* Logically removes all internal data, but does not free memory.
|
||||||
|
*
|
||||||
|
* \param tx Pointer to the transmitter context.
|
||||||
|
* \returns The result of the reset.
|
||||||
|
*/
|
||||||
|
result_t layer1_tx_reset(layer1_tx_t *tx);
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Add a packet to the burst.
|
||||||
|
*
|
||||||
|
* Encodes the raw data, modulates it, applies pulse filtering and appends the
|
||||||
|
* result to the internal buffer.
|
||||||
|
*
|
||||||
|
* If the buffer is empty (after init or reset), it first generates a ramp-up
|
||||||
|
* sequence.
|
||||||
|
*
|
||||||
|
* You can add arbitrarily many packets, the only limit is the available memory.
|
||||||
|
*
|
||||||
|
* \param tx Pointer to the transmitter context.
|
||||||
|
* \param data Pointer to the packet’s raw data.
|
||||||
|
* \param length The number of bytes in the data array.
|
||||||
|
* \returns The result of the operation.
|
||||||
|
*/
|
||||||
|
result_t layer1_tx_add_packet_to_burst(layer1_tx_t *tx, const uint8_t *data, size_t length);
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Generate the ramp-down sequence.
|
||||||
|
*
|
||||||
|
* The burst is considered complete after calling this function. You should
|
||||||
|
* retrieve the generated samples and call \ref layer1_tx_reset() before adding
|
||||||
|
* further packets.
|
||||||
|
*
|
||||||
|
* \param tx Pointer to the transmitter context.
|
||||||
|
* \returns The result of the finalization.
|
||||||
|
*/
|
||||||
|
result_t layer1_tx_finalize_burst(layer1_tx_t *tx);
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Retrieve the number of generated samples.
|
||||||
|
*
|
||||||
|
* \param tx Pointer to the transmitter context.
|
||||||
|
* \returns The number of samples returned by \ref layer1_tx_get_sample_data().
|
||||||
|
*/
|
||||||
|
size_t layer1_tx_get_sample_count(const layer1_tx_t *tx);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Retrieve the generated samples.
|
||||||
|
*
|
||||||
|
* Calling this after \ref layer1_tx_finalize_burst() returns the complete burst ready for transmission.
|
||||||
|
*
|
||||||
|
* \param tx Pointer to the transmitter context.
|
||||||
|
* \returns A pointer to the generated samples.
|
||||||
|
*/
|
||||||
|
const float complex* layer1_tx_get_sample_data(const layer1_tx_t *tx);
|
||||||
|
|
||||||
|
#endif // LAYER1_TX_H
|
|
@ -6,13 +6,20 @@
|
||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "layer1/results.h"
|
#include "layer1/results.h"
|
||||||
#include "layer1/packet_mod.h"
|
|
||||||
#include "layer1/config.h"
|
#include "layer1/config.h"
|
||||||
#include "layer1/preamble.h"
|
#include "layer1/preamble.h"
|
||||||
#include "layer1/transmission.h"
|
|
||||||
#include "layer1/correlator.h"
|
#include "layer1/correlator.h"
|
||||||
#include "layer1/freq_est.h"
|
#include "layer1/freq_est.h"
|
||||||
#include "layer1/whitening.h"
|
#include "layer1/whitening.h"
|
||||||
|
#include "layer1/tx.h"
|
||||||
|
|
||||||
|
#define RESULT_CHECK(stmt) { \
|
||||||
|
result_t res = stmt; \
|
||||||
|
if(res != OK) { \
|
||||||
|
printf("Error %d in %s:%d!", res, __FILE__, __LINE__); \
|
||||||
|
exit(1); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
RX_STATE_ACQUISITION,
|
RX_STATE_ACQUISITION,
|
||||||
|
@ -45,7 +52,18 @@ int main(void)
|
||||||
{
|
{
|
||||||
uint8_t msg_org[] = "Hello Liquid! This is the message to transmit. Hopefully it can be decoded correctly...";
|
uint8_t msg_org[] = "Hello Liquid! This is the message to transmit. Hopefully it can be decoded correctly...";
|
||||||
|
|
||||||
uint8_t header[4];
|
layer1_tx_t tx;
|
||||||
|
|
||||||
|
RESULT_CHECK(layer1_tx_init(&tx));
|
||||||
|
RESULT_CHECK(layer1_tx_add_packet_to_burst(&tx, msg_org, sizeof(msg_org)));
|
||||||
|
RESULT_CHECK(layer1_tx_finalize_burst(&tx));
|
||||||
|
|
||||||
|
size_t burst_len = layer1_tx_get_sample_count(&tx);
|
||||||
|
const float complex *whole_burst = layer1_tx_get_sample_data(&tx);
|
||||||
|
|
||||||
|
dump_array_cf(whole_burst, burst_len, 1.0f, "/tmp/tx.cpx");
|
||||||
|
|
||||||
|
// ** RX starts here **
|
||||||
|
|
||||||
fec hdr_fec = fec_create(HEADER_CHANNEL_CODE, NULL);
|
fec hdr_fec = fec_create(HEADER_CHANNEL_CODE, NULL);
|
||||||
fec payload_fec = fec_create(PAYLOAD_CHANNEL_CODE, NULL);
|
fec payload_fec = fec_create(PAYLOAD_CHANNEL_CODE, NULL);
|
||||||
|
@ -60,54 +78,12 @@ int main(void)
|
||||||
channel_cccf_add_carrier_offset(channel, 0.20f, 1.00f);
|
channel_cccf_add_carrier_offset(channel, 0.20f, 1.00f);
|
||||||
|
|
||||||
// precalculate encoded header length in symbols
|
// precalculate encoded header length in symbols
|
||||||
|
uint8_t header[4];
|
||||||
unsigned int hdr_len_enc_bytes = fec_get_enc_msg_length(HEADER_CHANNEL_CODE, sizeof(header));
|
unsigned int hdr_len_enc_bytes = fec_get_enc_msg_length(HEADER_CHANNEL_CODE, sizeof(header));
|
||||||
unsigned int hdr_bps = modem_get_bps(hdr_demod);
|
unsigned int hdr_bps = modem_get_bps(hdr_demod);
|
||||||
unsigned int hdr_len_symbols = (hdr_len_enc_bytes * 8 + hdr_bps - 1) / hdr_bps;
|
unsigned int hdr_len_symbols = (hdr_len_enc_bytes * 8 + hdr_bps - 1) / hdr_bps;
|
||||||
|
|
||||||
packet_mod_ctx_t pmod;
|
|
||||||
packet_mod_init(&pmod);
|
|
||||||
|
|
||||||
packet_mod_set_data(&pmod, msg_org, sizeof(msg_org));
|
|
||||||
packet_mod_encode(&pmod);
|
|
||||||
packet_mod_modulate(&pmod);
|
|
||||||
packet_mod_add_header(&pmod);
|
|
||||||
packet_mod_add_preamble(&pmod);
|
|
||||||
|
|
||||||
size_t nsyms;
|
|
||||||
packet_mod_get_result_cf(&pmod, NULL, &nsyms); // determine number of symbols for allocation
|
|
||||||
|
|
||||||
float complex msg_mod[nsyms];
|
|
||||||
packet_mod_get_result_cf(&pmod, msg_mod, &nsyms); // get the data
|
|
||||||
|
|
||||||
size_t burst_len = 0;
|
|
||||||
float complex whole_burst[65536];
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
transmission_ctx_t transm;
|
|
||||||
transmission_init(&transm);
|
|
||||||
|
|
||||||
len = 65536-burst_len;
|
|
||||||
if(transmission_ramp_up(&transm, whole_burst+burst_len, &len) != OK) {
|
|
||||||
printf("Ramp-up requires %zd symbols at offset %zd.\n", len, burst_len);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
burst_len += len;
|
|
||||||
|
|
||||||
len = 65536-burst_len;
|
|
||||||
if(transmission_filter_packet(&transm, msg_mod, nsyms, whole_burst+burst_len, &len) != OK) {
|
|
||||||
printf("Packet requires %zd symbols at offset %zd.\n", len, burst_len);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
burst_len += len;
|
|
||||||
|
|
||||||
len = 65536-burst_len;
|
|
||||||
if(transmission_ramp_down(&transm, whole_burst+burst_len, &len) != OK) {
|
|
||||||
printf("Ramp-down requires %zd symbols at offset %zd.\n", len, burst_len);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
burst_len += len;
|
|
||||||
|
|
||||||
dump_array_cf(whole_burst, burst_len, 1.0f, "/tmp/tx.cpx");
|
|
||||||
|
|
||||||
// channel
|
// channel
|
||||||
float complex msg_received[burst_len];
|
float complex msg_received[burst_len];
|
||||||
|
|
Loading…
Reference in a new issue