Implemented pulse shaping

The pulse shaper currently uses an RRC filter with beta=0.2.
This commit is contained in:
Thomas Kolb 2022-01-29 22:05:17 +01:00
parent a6fed80149
commit d522a1d6bd
5 changed files with 219 additions and 1 deletions

View file

@ -17,6 +17,8 @@ set(sources
src/config.h
src/preamble.h
src/preamble.c
src/transmission.h
src/transmission.c
)
include_directories(

View file

@ -13,4 +13,11 @@
#define PREAMBLE_MSEQ_POLY LIQUID_MSEQUENCE_GENPOLY_M6
#define PREAMBLE_MSEQ_INIT 0x00000001
#define RRC_SPS 4 // samples per symbol
#define RRC_DELAY 7 // delay in symbols
#define RRC_BETA 0.2f
#define TRANSMISSION_RAMP_UP_LEN 32 // symbols
#define TRANSMISSION_RAMP_DOWN_LEN 32 // symbols
#endif // CONFIG_H

View file

@ -7,6 +7,7 @@
#include "packet_mod.h"
#include "config.h"
#include "preamble.h"
#include "transmission.h"
int main(void)
{
@ -38,8 +39,38 @@ int main(void)
float complex msg_mod[nsyms];
packet_mod_get_result_cf(&pmod, msg_mod, &nsyms); // get the data
dump_array_cf(msg_mod, nsyms, 1.0f, "/tmp/tx.cpx");
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");
#if 0
// channel
float complex msg_received[nsyms];
@ -86,6 +117,7 @@ int main(void)
unsigned int bit_errors = count_bit_errors_array(msg_org, msg_dec, sizeof(msg_org));
printf("%u bit errors detected.\n", bit_errors);
#endif
fec_destroy(q);

98
impl/src/transmission.c Normal file
View file

@ -0,0 +1,98 @@
#include <math.h>
#include "transmission.h"
#include "config.h"
result_t transmission_init(transmission_ctx_t *ctx)
{
ctx->rrc_interp = firinterp_crcf_create_prototype(LIQUID_FIRFILT_RRC, RRC_SPS, RRC_DELAY, RRC_BETA, 0);
return OK;
}
result_t transmission_free(transmission_ctx_t *ctx)
{
firinterp_crcf_destroy(ctx->rrc_interp);
return OK;
}
result_t transmission_ramp_up(transmission_ctx_t *ctx, float complex *output, size_t *output_size)
{
if(*output_size < RRC_SPS*TRANSMISSION_RAMP_UP_LEN) {
*output_size = RRC_SPS*TRANSMISSION_RAMP_UP_LEN;
return ERR_SIZE;
}
// ramp up alternating ±1 symbols following a sin(t)^2 curve
for(size_t i = 0; i < TRANSMISSION_RAMP_UP_LEN; i++) {
float s = sinf((float)M_PI / 2.0f * i / TRANSMISSION_RAMP_UP_LEN);
float symbol = s*s;
if((i % 2) != 0) {
symbol = -symbol;
}
firinterp_crcf_execute(ctx->rrc_interp, symbol, output + (RRC_SPS*i));
}
*output_size = RRC_SPS*TRANSMISSION_RAMP_UP_LEN;
return OK;
}
result_t transmission_ramp_down(transmission_ctx_t *ctx, float complex *output, size_t *output_size)
{
const size_t total_symbols = TRANSMISSION_RAMP_DOWN_LEN + 2*RRC_DELAY;
if(*output_size < RRC_SPS*total_symbols) {
*output_size = RRC_SPS*total_symbols;
return ERR_SIZE;
}
// ramp down alternating ±1 symbols following a cos(t)^2 curve
for(size_t i = 0; i < TRANSMISSION_RAMP_DOWN_LEN; i++) {
float s = cosf((float)M_PI / 2.0f * i / TRANSMISSION_RAMP_DOWN_LEN);
float symbol = s*s;
if((i % 2) != 0) {
symbol = -symbol;
}
firinterp_crcf_execute(ctx->rrc_interp, symbol, output + (RRC_SPS*i));
}
// add 2*RRC_DELAY zeros to flush the filter history
for(size_t i = 0; i < 2*RRC_DELAY; i++) {
firinterp_crcf_execute(ctx->rrc_interp, 0.0f, output + (RRC_SPS*(TRANSMISSION_RAMP_DOWN_LEN+i)));
}
*output_size = RRC_SPS*total_symbols;
return OK;
}
result_t transmission_filter_packet(
transmission_ctx_t *ctx,
const float complex *packet,
size_t packet_size,
float complex *output,
size_t *output_size)
{
if(*output_size < RRC_SPS*packet_size) {
*output_size = RRC_SPS*packet_size;
return ERR_SIZE;
}
// ramp down alternating ±1 symbols following a cos(t)^2 curve
for(size_t i = 0; i < packet_size; i++) {
firinterp_crcf_execute(ctx->rrc_interp, packet[i], output + (RRC_SPS*i));
}
*output_size = RRC_SPS*TRANSMISSION_RAMP_DOWN_LEN;
return OK;
}

79
impl/src/transmission.h Normal file
View file

@ -0,0 +1,79 @@
#ifndef TRANSMISSION_H
#define TRANSMISSION_H
#include <stdlib.h>
#include <complex.h>
#include <liquid/liquid.h>
#include "results.h"
typedef struct
{
firinterp_crcf rrc_interp;
} transmission_ctx_t;
/*!\brief Initialize the transmission object.
*
* \param ctx Pointer to the context to initialize.
* \returns The result code of the initialization.
*/
result_t transmission_init(transmission_ctx_t *ctx);
/*!\brief Free all memory associated with the given context.
*
* \param ctx Pointer to the context to free.
* \returns The result code of the operation.
*/
result_t transmission_free(transmission_ctx_t *ctx);
/*!\brief Generate a signal ramp-up and write it to the output array.
*
* \param ctx Pointer to the transmission context.
* \param output Pointer to the output array.
* \param output_size Pointer to the output size. Must be set to the number
* of samples that can be stored in \ref output when
* calling the function. It will be changed to the number
* of samples actually produced.
* \returns The result code of the operation.
*/
result_t transmission_ramp_up(transmission_ctx_t *ctx, float complex *output, size_t *output_size);
/*!\brief Generate a signal ramp-down and write it to the output array.
*
* \param ctx Pointer to the transmission context.
* \param output Pointer to the output array.
* \param output_size Pointer to the output size. Must be set to the number
* of samples that can be stored in \ref output when
* calling the function. It will be changed to the number
* of samples actually produced.
* \returns The result code of the operation.
*/
result_t transmission_ramp_down(transmission_ctx_t *ctx, float complex *output, size_t *output_size);
/*!\brief Filter the given symbols.
*
* \param ctx Pointer to the transmission context.
* \param packet Pointer to the symbols to filter.
* \param packet_size Number of samples in \ref packet.
* \param output Pointer to the output array.
* \param output_size Pointer to the output size. Must be set to the number
* of samples that can be stored in \ref output when
* calling the function. It will be changed to the number
* of samples actually produced.
* \returns The result code of the operation.
*/
result_t transmission_filter_packet(
transmission_ctx_t *ctx,
const float complex *packet,
size_t packet_size,
float complex *output,
size_t *output_size);
#endif // TRANSMISSION_H