Created 3GPP fading channel emulator

master
Xavier Arteaga 6 years ago committed by Andre Puschmann
parent 64d50040e7
commit c42fbbe6a5

@ -0,0 +1,68 @@
/*
* Copyright 2013-2019 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSLTE_FADING_H
#define SRSLTE_FADING_H
#include "srslte/phy/dft/dft.h"
#include <inttypes.h>
#define SRSLTE_CHANNEL_FADING_MAXTAPS 9
typedef enum {
srslte_channel_fading_model_none = 0,
srslte_channel_fading_model_epa,
srslte_channel_fading_model_eva,
srslte_channel_fading_model_etu,
} srslte_channel_fading_model_t;
typedef struct {
// Configuration parameters
float srate; // Sampling rate: 1.92e6, 3.84e6, ..., 23.04e6, 30.72e6
srslte_channel_fading_model_t model; // None, EPA, EVA, ETU
float doppler; // Maximum doppler: 5, 70, 300
// Internal tap parametrization
uint32_t N; // FFT size
uint32_t path_delay; // Path delay
double coeff_w[SRSLTE_CHANNEL_FADING_MAXTAPS]; // Angular Speed, random
double coeff_a[SRSLTE_CHANNEL_FADING_MAXTAPS]; // Modulation Coefficient
double coeff_p[SRSLTE_CHANNEL_FADING_MAXTAPS]; // Initial phase, random
// Utils
srslte_dft_plan_t fft; // DFT to frequency domain
srslte_dft_plan_t ifft; // DFT to time domain
cf_t* temp; // Temporal buffer, length fft_size
cf_t* h_freq; // Channel frequency response, length fft_size
cf_t* y_freq; // Intermediate frequency domain buffer
// State variables
cf_t* state; // Length fft_size/2
} srslte_channel_fading_t;
SRSLTE_API int srslte_channel_fading_init(srslte_channel_fading_t* q, double srate, const char* model);
SRSLTE_API void srslte_channel_fading_free(srslte_channel_fading_t* q);
SRSLTE_API double srslte_channel_fading_execute(
srslte_channel_fading_t* q, const cf_t* in, cf_t* out, uint32_t nof_samples, double init_time);
#endif // SRSLTE_FADING_H

@ -20,3 +20,5 @@
file(GLOB SOURCES "*.c")
add_library(srslte_channel OBJECT ${SOURCES})
add_subdirectory(test)

@ -0,0 +1,270 @@
/*
* Copyright 2013-2019 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include "srslte/phy/channel/fading.h"
#include "srslte/phy/utils/vector.h"
#include <complex.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define COEFF_A_MIN 100
#define COEFF_A_MAX 2000
/*
* Tables provided in 36.104 R10 section B.2 Multi-path fading propagation conditions
*/
const static uint32_t nof_taps[4] = {1, 7, 9, 9};
const static float excess_tap_delay_ns[4][SRSLTE_CHANNEL_FADING_MAXTAPS] = {
/* None */ {0, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN},
/* EPA */ {0, 30, 70, 90, 110, 190, 410, NAN, NAN},
/* EVA */ {0, 30, 150, 310, 370, 710, 1090, 1730, 2510},
/* ETU */ {0, 50, 120, 200, 230, 500, 1600, 2300, 5000}};
const static float relative_power_db[4][SRSLTE_CHANNEL_FADING_MAXTAPS] = {
/* None */ {+0.0f, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN},
/* EPA */ {+0.0f, -1.0f, -2.0f, -3.0f, -8.0f, -17.2f, -20.8f, NAN, NAN},
/* EVA */ {+0.0f, -1.5f, -1.4f, -3.6f, -0.6f, -9.1f, -7.0f, -12.0f, -16.9f},
/* ETU */ {-1.0f, -1.0f, -1.0f, +0.0f, +0.0f, +0.0f, -3.0f, -5.0f, -7.0f},
};
static inline int parse_model(srslte_channel_fading_t* q, const char* str)
{
int ret = SRSLTE_SUCCESS;
uint32_t offset = 3;
if (strncmp("none", str, 4) == 0) {
q->model = srslte_channel_fading_model_none;
offset = 4;
} else if (strncmp("epa", str, 3) == 0) {
q->model = srslte_channel_fading_model_epa;
} else if (strncmp("eva", str, 3) == 0) {
q->model = srslte_channel_fading_model_eva;
} else if (strncmp("etu", str, 3) == 0) {
q->model = srslte_channel_fading_model_etu;
} else {
ret = SRSLTE_ERROR;
}
if (ret == SRSLTE_SUCCESS) {
if (strlen(str) > offset) {
q->doppler = (float)strtod(&str[offset], NULL);
if (isnan(q->doppler) || isinf(q->doppler)) {
ret = SRSLTE_ERROR;
}
} else {
ret = SRSLTE_ERROR;
}
}
return ret;
}
static inline float get_doppler_dispersion(double t, double a, double w, double p)
{
return (float)(a * sin(w * t + p));
}
static inline void
generate_tap(float delay_ns, float power_db, float srate, float phase, cf_t* buf, uint32_t N, uint32_t path_delay)
{
float amplitude = powf(10.0f, power_db / 20.0f);
float O = (delay_ns * 1e-9f * srate + path_delay) / (float)N;
cf_t a0 = amplitude * cexpf(-_Complex_I * phase) / N;
for (int n = 0; n < N; n++) {
buf[n] = a0;
}
srslte_vec_apply_cfo(buf, -O, buf, N);
}
static void generate_taps(srslte_channel_fading_t* q, double time)
{
// Initialise freq response
bzero(q->h_freq, sizeof(cf_t) * q->N);
// Generate taps
for (int i = 0; i < nof_taps[q->model]; i++) {
// Compute phase for thee doppler dispersion
float phase = get_doppler_dispersion(time, q->coeff_a[i], q->coeff_w[i], q->coeff_p[i]);
// Generate tab
generate_tap(excess_tap_delay_ns[q->model][i], relative_power_db[q->model][i], q->srate, phase, q->temp, q->N,
q->path_delay);
// Add to frequency response
srslte_vec_sum_ccc(q->h_freq, q->temp, q->h_freq, q->N);
}
// at this stage, q->h_freq should contain the frequency response
}
static void filter_segment(srslte_channel_fading_t* q, const cf_t* input, cf_t* output, uint32_t nsamples)
{
// Fill Input vector
memcpy(q->temp, input, sizeof(cf_t) * nsamples);
bzero(&q->temp[nsamples], sizeof(cf_t) * (q->N - nsamples));
// Do FFT
srslte_dft_run_c_zerocopy(&q->fft, q->temp, q->y_freq);
// Apply channel
srslte_vec_prod_ccc(q->y_freq, q->h_freq, q->y_freq, q->N);
// Do iFFT
srslte_dft_run_c_zerocopy(&q->ifft, q->y_freq, q->temp);
// Add state
srslte_vec_sum_ccc(q->temp, q->state, q->temp, q->N);
// Copy output
memcpy(output, q->temp, sizeof(cf_t) * nsamples);
// Copy state
memcpy(q->state, &q->temp[nsamples], sizeof(cf_t) * (q->N - nsamples));
bzero(&q->state[q->N - nsamples], sizeof(cf_t) * nsamples);
}
int srslte_channel_fading_init(srslte_channel_fading_t* q, double srate, const char* model)
{
int ret = SRSLTE_ERROR;
if (q) {
// Parse model
if (parse_model(q, model) != SRSLTE_SUCCESS) {
fprintf(stderr, "Error: invalid channel model '%s'\n", model);
goto clean_exit;
}
// Fill srate
q->srate = (float)srate;
// Populate internal parameters
q->N = SRSLTE_MAX((uint32_t)1 << (uint32_t)(
round(log2(excess_tap_delay_ns[q->model][nof_taps[q->model] - 1] * 1e-9 * srate)) + 3),
64);
q->path_delay = q->N / 4;
for (int i = 0; i < nof_taps[q->model]; i++) {
q->coeff_a[i] = (((double)rand() / (double)RAND_MAX) * (COEFF_A_MAX - COEFF_A_MIN)) + COEFF_A_MIN;
q->coeff_w[i] = 2.0 * M_PI * q->doppler / q->coeff_a[i];
q->coeff_p[i] = ((double)rand() / (double)RAND_MAX) * M_PI / 2.0;
}
// Plan FFT
if (srslte_dft_plan_c(&q->fft, q->N, SRSLTE_DFT_FORWARD) != SRSLTE_SUCCESS) {
fprintf(stderr, "Error: planning fft\n");
goto clean_exit;
}
// Plan iFFT
if (srslte_dft_plan_c(&q->ifft, q->N, SRSLTE_DFT_BACKWARD) != SRSLTE_SUCCESS) {
fprintf(stderr, "Error: planning ifft\n");
goto clean_exit;
}
// Allocate memory
q->temp = srslte_vec_malloc(sizeof(cf_t) * q->N);
if (!q->temp) {
fprintf(stderr, "Error: allocating h_freq\n");
goto clean_exit;
}
q->h_freq = srslte_vec_malloc(sizeof(cf_t) * q->N);
if (!q->h_freq) {
fprintf(stderr, "Error: allocating h_freq\n");
goto clean_exit;
}
q->y_freq = srslte_vec_malloc(sizeof(cf_t) * q->N);
if (!q->y_freq) {
fprintf(stderr, "Error: allocating y_freq\n");
goto clean_exit;
}
q->state = srslte_vec_malloc(sizeof(cf_t) * q->N);
if (!q->state) {
fprintf(stderr, "Error: allocating y_freq\n");
goto clean_exit;
}
bzero(q->state, sizeof(cf_t) * q->N);
}
ret = SRSLTE_SUCCESS;
clean_exit:
return ret;
}
void srslte_channel_fading_free(srslte_channel_fading_t* q)
{
if (q) {
srslte_dft_plan_free(&q->fft);
srslte_dft_plan_free(&q->ifft);
if (q->temp) {
free(q->temp);
}
if (q->h_freq) {
free(q->h_freq);
}
if (q->y_freq) {
free(q->y_freq);
}
if (q->state) {
free(q->state);
}
}
}
double srslte_channel_fading_execute(
srslte_channel_fading_t* q, const cf_t* in, cf_t* out, uint32_t nsamples, double init_time)
{
uint32_t counter = 0;
if (q) {
// Generate taps
generate_taps(q, init_time);
while (counter < nsamples) {
// Do not process more than N / 4 samples
uint32_t n = SRSLTE_MIN(q->N / 4, nsamples - counter);
// Execute
filter_segment(q, &in[counter], &out[counter], n);
// Increment time
init_time += n / q->srate;
// Increment counter
counter += n;
}
}
// Return time
return init_time;
}

@ -0,0 +1,30 @@
#
# Copyright 2013-2019 Software Radio Systems Limited
#
# This file is part of srsLTE
#
# srsLTE is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.
#
# srsLTE is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# A copy of the GNU Affero General Public License can be found in
# the LICENSE file in the top-level directory of this distribution
# and at http://www.gnu.org/licenses/.
#
add_executable(fading_channel_test fading_channel_test.c)
if(SRSGUI_FOUND)
target_link_libraries(fading_channel_test ${SRSGUI_LIBRARIES})
endif(SRSGUI_FOUND)
target_link_libraries(fading_channel_test srslte_phy srslte_common srslte_phy ${SEC_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
add_test(fading_channel_test_epa5 fading_channel_test -m epa5 -s 26.04e6 -t 1000)
add_test(fading_channel_test_eva70 fading_channel_test -m eva70 -s 23.04e6 -t 1000)
add_test(fading_channel_test_etu300 fading_channel_test -m etu70 -s 23.04e6 -t 1000)

@ -0,0 +1,247 @@
/*
* Copyright 2013-2019 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include "srslte/phy/channel/fading.h"
#include "srslte/phy/utils/vector.h"
#include <complex.h>
#include <math.h>
#include <memory.h>
#include <srslte/phy/utils/debug.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
#ifdef ENABLE_GUI
#include "srsgui/srsgui.h"
static bool enable_gui = false;
#endif /* ENABLE_GUI */
static srslte_channel_fading_t channel_fading;
static char default_model[] = "epa5";
static uint32_t duration_ms = 1000;
static char* model = default_model;
static uint32_t srate = (uint32_t)30.72e6;
#define INPUT_TYPE 0 /* 0: Dirac Delta; Otherwise: Random*/
static void usage(char* prog)
{
printf("Usage: %s [mts]\n", prog);
printf("\t-m Channel model: epa5, eva70, etu300 [Default %s]\n", model);
printf("\t-t Simulation time in ms: [Default %d]\n", duration_ms);
printf("\t-s Sampling rate in Hz: [Default %d]\n", srate);
#ifdef ENABLE_GUI
printf("\t-g Enable GUI: [Default %s]\n", enable_gui ? "enabled" : "disabled");
#endif /* ENABLE_GUI */
}
static void parse_args(int argc, char** argv)
{
int opt;
while ((opt = getopt(argc, argv, "mtsg")) != -1) {
switch (opt) {
case 'm':
model = argv[optind];
break;
case 't':
duration_ms = (uint32_t)atof(argv[optind]);
break;
case 's':
srate = (uint32_t)atof(argv[optind]);
break;
#ifdef ENABLE_GUI
case 'g':
enable_gui ^= true;
break;
#endif /* ENABLE_GUI */
default:
usage(argv[0]);
exit(-1);
}
}
}
int main(int argc, char** argv)
{
int ret = SRSLTE_ERROR;
cf_t* input_buffer = NULL;
cf_t* output_buffer = NULL;
struct timeval t[3] = {};
uint64_t time_usec = 0;
parse_args(argc, argv);
srslte_dft_plan_t ifft;
srslte_dft_plan_c(&ifft, srate / 1000, SRSLTE_DFT_BACKWARD);
#ifdef ENABLE_GUI
plot_real_t plot_fft = NULL;
plot_real_t plot_h = NULL;
plot_real_t plot_imp = NULL;
srslte_dft_plan_t fft = {};
cf_t* fft_buffer = NULL;
float* fft_mag = NULL;
float* imp = NULL;
if (enable_gui) {
sdrgui_init();
sdrgui_init_title("SRS Fading channel");
plot_real_init(&plot_h);
plot_real_setTitle(&plot_h, "Ideal channel");
plot_real_setYAxisScale(&plot_h, -60, 20);
plot_real_addToWindowGrid(&plot_h, (char*)"fading", 0, 0);
plot_real_init(&plot_fft);
plot_real_setTitle(&plot_fft, "FFT measured");
plot_real_setYAxisScale(&plot_fft, -60, 20);
plot_real_addToWindowGrid(&plot_fft, (char*)"fading", 0, 1);
plot_real_init(&plot_imp);
plot_real_setTitle(&plot_imp, "Impulse");
plot_real_setYAxisScale(&plot_imp, 0, 5);
plot_real_addToWindowGrid(&plot_imp, (char*)"fading", 0, 2);
srslte_dft_plan_c(&fft, srate / 1000, SRSLTE_DFT_FORWARD);
fft_buffer = srslte_vec_malloc(sizeof(cf_t) * srate / 1000);
if (!fft_buffer) {
fprintf(stderr, "Error: malloc\n");
goto clean_exit;
}
fft_mag = srslte_vec_malloc(sizeof(float) * srate / 1000);
if (!fft_mag) {
fprintf(stderr, "Error: malloc\n");
goto clean_exit;
}
imp = srslte_vec_malloc(sizeof(float) * srate / 1000);
if (!imp) {
fprintf(stderr, "Error: malloc\n");
goto clean_exit;
}
}
#endif /* ENABLE_GUI */
// Initialise channel
if (srslte_channel_fading_init(&channel_fading, srate, model)) {
fprintf(stderr, "Error: initialising fading channel. model=%s, srate=%d\n", model, srate);
goto clean_exit;
}
// Allocate buffers
input_buffer = srslte_vec_malloc(sizeof(cf_t) * srate / 1000);
if (!input_buffer) {
fprintf(stderr, "Error: allocating input buffer\n");
goto clean_exit;
}
#if INPUT_TYPE == 0
bzero(input_buffer, sizeof(cf_t) * srate / 1000);
input_buffer[0] = 1;
#else
for (int i = 0; i < srate / 1000; i++) {
input_buffer[i] = cexpf(_Complex_I * ((double)rand() / (double)RAND_MAX) * M_PI / 2.0) / ((double)srate / 1000.0);
}
srslte_dft_run_c(&ifft, input_buffer, input_buffer);
#endif
output_buffer = srslte_vec_malloc(sizeof(cf_t) * srate / 1000);
if (!output_buffer) {
fprintf(stderr, "Error: allocating output buffer\n");
goto clean_exit;
}
printf("-- Starting Fading channel simulator. srate=%.2fMHz; model=%s; duration=%dms\n", (double)srate / 1e6, model,
duration_ms);
for (int i = 0; i < duration_ms; i++) {
gettimeofday(&t[1], NULL);
srslte_channel_fading_execute(&channel_fading, input_buffer, output_buffer, srate / 1000, (double)i / 1000.0);
gettimeofday(&t[2], NULL);
get_time_interval(t);
time_usec += (uint64_t)(t->tv_sec * 1e6 + t->tv_usec);
#ifdef ENABLE_GUI
if (enable_gui) {
srslte_dft_run_c_zerocopy(&fft, output_buffer, fft_buffer);
srslte_vec_prod_conj_ccc(fft_buffer, fft_buffer, fft_buffer, srate / 1000);
for (int i = 0; i < srate / 1000; i++) {
fft_mag[i] = 10.0f * log10f(__real__ fft_buffer[i]);
}
plot_real_setNewData(&plot_fft, fft_mag, srate / 1000);
for (int i = 0; i < channel_fading.N; i++) {
fft_mag[i] = 20.0f * log10f(cabsf(channel_fading.h_freq[i]));
}
plot_real_setNewData(&plot_h, fft_mag, channel_fading.N);
for (int i = 0; i < srate / 1000; i++) {
imp[i] = cabsf(output_buffer[i]);
}
plot_real_setNewData(&plot_imp, imp, channel_fading.N);
usleep(1000);
}
#endif /* ENABLE_GUI */
}
ret = SRSLTE_SUCCESS;
clean_exit:
if (ret) {
printf("Error\n");
} else {
printf("Ok ... %.1f MSps\n", duration_ms * (srate / 1000.0) / (double)time_usec);
}
srslte_dft_plan_free(&ifft);
#ifdef ENABLE_GUI
if (enable_gui) {
if (fft_mag) {
free(fft_mag);
}
if (imp) {
free(imp);
}
if (fft_buffer) {
free(fft_buffer);
}
srslte_dft_plan_free(&fft);
sdrgui_exit();
}
#endif /* ENABLE_GUI */
if (input_buffer) {
free(input_buffer);
}
if (output_buffer) {
free(output_buffer);
}
srslte_channel_fading_free(&channel_fading);
srslte_dft_exit();
exit(ret);
}
Loading…
Cancel
Save