diff --git a/lib/include/srslte/interfaces/ue_interfaces.h b/lib/include/srslte/interfaces/ue_interfaces.h index fcd219d3e..4fabaad91 100644 --- a/lib/include/srslte/interfaces/ue_interfaces.h +++ b/lib/include/srslte/interfaces/ue_interfaces.h @@ -35,6 +35,7 @@ #include "srslte/common/common.h" #include "srslte/common/interfaces_common.h" #include "srslte/common/security.h" +#include "srslte/phy/channel/channel.h" #include "srslte/phy/rf/rf.h" #include "srslte/upper/rlc_interface.h" @@ -758,6 +759,7 @@ typedef struct { uint32_t intra_freq_meas_len_ms; uint32_t intra_freq_meas_period_ms; bool pregenerate_signals; + srslte::channel::args_t dl_channel_args; } phy_args_t; diff --git a/lib/include/srslte/phy/channel/channel.h b/lib/include/srslte/phy/channel/channel.h new file mode 100644 index 000000000..8df7fafe6 --- /dev/null +++ b/lib/include/srslte/phy/channel/channel.h @@ -0,0 +1,66 @@ +/* + * 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_CHANNEL_H +#define SRSLTE_CHANNEL_H + +#include "delay.h" +#include "fading.h" +#include +#include +#include + +namespace srslte { + +class channel +{ +public: + typedef struct { + // General + bool enable = false; + + // Fading options + std::string fading_model = ""; + + // Delay options + float delay_min_us = 0; + float delay_max_us = 0; + uint32_t delay_period_s = 0; + } args_t; + + channel(const args_t& channel_args, uint32_t _nof_ports); + ~channel(); + void set_srate(uint32_t srate); + void run(cf_t* in[SRSLTE_MAX_PORTS], cf_t* out[SRSLTE_MAX_PORTS], uint32_t len, const srslte_timestamp_t& t); + +private: + srslte_channel_fading_t* fading[SRSLTE_MAX_PORTS]; + srslte_channel_delay_t* delay[SRSLTE_MAX_PORTS]; + cf_t* buffer_in = nullptr; + cf_t* buffer_out = nullptr; + uint32_t nof_ports = 0; + uint32_t current_srate = 0; + args_t args; +}; + +} // namespace srslte + +#endif // SRSLTE_CHANNEL_H diff --git a/lib/include/srslte/phy/channel/delay.h b/lib/include/srslte/phy/channel/delay.h new file mode 100644 index 000000000..c98e7e44f --- /dev/null +++ b/lib/include/srslte/phy/channel/delay.h @@ -0,0 +1,58 @@ +/* + * 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_DELAY_H +#define SRSLTE_DELAY_H + +#include +#include +#include + +typedef struct { + float delay_min_us; + float delay_max_us; + uint32_t period_s; + uint32_t srate_max_hz; + uint32_t srate_hz; + + srslte_ringbuffer_t rb; + cf_t* zero_buffer; +} srslte_channel_delay_t; + +#ifdef __cplusplus +extern "C" { +#endif + +SRSLTE_API int srslte_channel_delay_init( + srslte_channel_delay_t* q, float delay_min_ns, float delay_max_ns, uint32_t period_s, uint32_t srate_max_hz); + +SRSLTE_API void srslte_channel_delay_update_srate(srslte_channel_delay_t* q, uint32_t srate_hz); + +SRSLTE_API void srslte_channel_delay_free(srslte_channel_delay_t* q); + +SRSLTE_API void srslte_channel_delay_execute( + srslte_channel_delay_t* q, const cf_t* in, cf_t* out, uint32_t len, const srslte_timestamp_t* ts); + +#ifdef __cplusplus +} +#endif + +#endif // SRSLTE_DELAY_H diff --git a/lib/include/srslte/phy/channel/fading.h b/lib/include/srslte/phy/channel/fading.h index c9eddd185..37a6b2708 100644 --- a/lib/include/srslte/phy/channel/fading.h +++ b/lib/include/srslte/phy/channel/fading.h @@ -58,6 +58,10 @@ typedef struct { cf_t* state; // Length fft_size/2 } srslte_channel_fading_t; +#ifdef __cplusplus +extern "C" { +#endif + SRSLTE_API int srslte_channel_fading_init(srslte_channel_fading_t* q, double srate, const char* model, uint32_t seed); SRSLTE_API void srslte_channel_fading_free(srslte_channel_fading_t* q); @@ -65,4 +69,8 @@ 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); +#ifdef __cplusplus +} +#endif + #endif // SRSLTE_FADING_H diff --git a/lib/include/srslte/phy/common/timestamp.h b/lib/include/srslte/phy/common/timestamp.h index 2c574e8b6..7a75add12 100644 --- a/lib/include/srslte/phy/common/timestamp.h +++ b/lib/include/srslte/phy/common/timestamp.h @@ -42,24 +42,21 @@ typedef struct SRSLTE_API{ double frac_secs; }srslte_timestamp_t; -SRSLTE_API int srslte_timestamp_init(srslte_timestamp_t *t, - time_t full_secs, - double frac_secs); +#ifdef __cplusplus +extern "C" { +#endif + +SRSLTE_API int srslte_timestamp_init(srslte_timestamp_t* t, time_t full_secs, double frac_secs); SRSLTE_API void srslte_timestamp_init_uint64(srslte_timestamp_t* ts_time, uint64_t ts_count, double base_srate); -SRSLTE_API int srslte_timestamp_copy(srslte_timestamp_t *dest, - srslte_timestamp_t *src); +SRSLTE_API int srslte_timestamp_copy(srslte_timestamp_t* dest, srslte_timestamp_t* src); SRSLTE_API int srslte_timestamp_compare(srslte_timestamp_t* a, srslte_timestamp_t* b); -SRSLTE_API int srslte_timestamp_add(srslte_timestamp_t *t, - time_t full_secs, - double frac_secs); +SRSLTE_API int srslte_timestamp_add(srslte_timestamp_t* t, time_t full_secs, double frac_secs); -SRSLTE_API int srslte_timestamp_sub(srslte_timestamp_t *t, - time_t full_secs, - double frac_secs); +SRSLTE_API int srslte_timestamp_sub(srslte_timestamp_t* t, time_t full_secs, double frac_secs); SRSLTE_API double srslte_timestamp_real(srslte_timestamp_t *t); @@ -69,4 +66,8 @@ SRSLTE_API uint32_t srslte_timestamp_uint32(srslte_timestamp_t *t); SRSLTE_API uint64_t srslte_timestamp_uint64(const srslte_timestamp_t* t, double srate); +#ifdef __cplusplus +} +#endif + #endif // SRSLTE_TIMESTAMP_H diff --git a/lib/include/srslte/phy/utils/ringbuffer.h b/lib/include/srslte/phy/utils/ringbuffer.h index 1d875e055..6329b542a 100644 --- a/lib/include/srslte/phy/utils/ringbuffer.h +++ b/lib/include/srslte/phy/utils/ringbuffer.h @@ -30,38 +30,40 @@ typedef struct { uint8_t *buffer; bool active; - int capacity; - int count; - int wpm; - int rpm; - pthread_mutex_t mutex; - pthread_cond_t cvar; -} srslte_ringbuffer_t; + int capacity; + int count; + int wpm; + int rpm; + pthread_mutex_t mutex; + pthread_cond_t cvar; +} srslte_ringbuffer_t; +#ifdef __cplusplus +extern "C" { +#endif -SRSLTE_API int srslte_ringbuffer_init(srslte_ringbuffer_t *q, - int capacity); +SRSLTE_API int srslte_ringbuffer_init(srslte_ringbuffer_t* q, int capacity); SRSLTE_API void srslte_ringbuffer_free(srslte_ringbuffer_t *q); SRSLTE_API void srslte_ringbuffer_reset(srslte_ringbuffer_t *q); -SRSLTE_API int srslte_ringbuffer_status(srslte_ringbuffer_t *q); +SRSLTE_API int srslte_ringbuffer_status(srslte_ringbuffer_t* q); SRSLTE_API int srslte_ringbuffer_space(srslte_ringbuffer_t *q); -SRSLTE_API int srslte_ringbuffer_write(srslte_ringbuffer_t *q, - void *ptr, - int nof_bytes); +SRSLTE_API int srslte_ringbuffer_write(srslte_ringbuffer_t* q, void* ptr, int nof_bytes); -SRSLTE_API int srslte_ringbuffer_read(srslte_ringbuffer_t *q, - void *ptr, - int nof_bytes); +SRSLTE_API int srslte_ringbuffer_read(srslte_ringbuffer_t* q, void* ptr, int nof_bytes); SRSLTE_API int srslte_ringbuffer_read_timed(srslte_ringbuffer_t* q, void* p, int nof_bytes, uint32_t timeout_ms); SRSLTE_API void srslte_ringbuffer_stop(srslte_ringbuffer_t *q); +#ifdef __cplusplus +} +#endif + #endif // SRSLTE_RINGBUFFER_H diff --git a/lib/src/phy/channel/CMakeLists.txt b/lib/src/phy/channel/CMakeLists.txt index a24cd69d2..a87cbee80 100644 --- a/lib/src/phy/channel/CMakeLists.txt +++ b/lib/src/phy/channel/CMakeLists.txt @@ -18,7 +18,7 @@ # and at http://www.gnu.org/licenses/. # -file(GLOB SOURCES "*.c") +file(GLOB SOURCES "*.c" "*.cc") add_library(srslte_channel OBJECT ${SOURCES}) add_subdirectory(test) \ No newline at end of file diff --git a/lib/src/phy/channel/channel.cc b/lib/src/phy/channel/channel.cc new file mode 100644 index 000000000..09775d31c --- /dev/null +++ b/lib/src/phy/channel/channel.cc @@ -0,0 +1,130 @@ +/* + * 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 +#include +#include + +using namespace srslte; + +channel::channel(const channel::args_t& channel_args, uint32_t _nof_ports) +{ + int ret = SRSLTE_SUCCESS; + uint32_t srate_max = (uint32_t)srslte_symbol_sz(SRSLTE_MAX_PRB) * 15000; + uint32_t buffer_size = (uint32_t)SRSLTE_SF_LEN_PRB(SRSLTE_MAX_PRB) * 5; // be safe, 5 Subframes + + // Copy args + args = channel_args; + + // Allocate internal buffers + buffer_in = (cf_t*)srslte_vec_malloc(sizeof(cf_t) * buffer_size); + buffer_out = (cf_t*)srslte_vec_malloc(sizeof(cf_t) * buffer_size); + if (!buffer_out || !buffer_in) { + ret = SRSLTE_ERROR; + } + + nof_ports = _nof_ports; + for (uint32_t i = 0; i < nof_ports; i++) { + // Create fading channel + if (!channel_args.fading_model.empty() && channel_args.fading_model != "none" && ret == SRSLTE_SUCCESS) { + fading[i] = (srslte_channel_fading_t*)calloc(sizeof(srslte_channel_fading_t), 1); + ret = srslte_channel_fading_init(fading[i], srate_max, channel_args.fading_model.c_str(), 0x1234 * i); + } else { + fading[i] = nullptr; + } + + // Create delay + if (channel_args.delay_period_s && ret == SRSLTE_SUCCESS) { + delay[i] = (srslte_channel_delay_t*)calloc(sizeof(srslte_channel_delay_t), 1); + ret = srslte_channel_delay_init( + delay[i], channel_args.delay_min_us, channel_args.delay_max_us, channel_args.delay_period_s, srate_max); + } else { + delay[i] = nullptr; + } + } + + if (ret != SRSLTE_SUCCESS) { + fprintf(stderr, "Error: Creating channel\n\n"); + } +} + +channel::~channel() +{ + if (buffer_in) { + free(buffer_in); + } + + if (buffer_out) { + free(buffer_out); + } + + for (uint32_t i = 0; i < nof_ports; i++) { + if (fading[i]) { + srslte_channel_fading_free(fading[i]); + free(fading[i]); + } + + if (delay[i]) { + srslte_channel_delay_free(delay[i]); + free(delay[i]); + } + } +} + +void channel::run(cf_t* in[SRSLTE_MAX_PORTS], cf_t* out[SRSLTE_MAX_PORTS], uint32_t len, const srslte_timestamp_t& t) +{ + + for (uint32_t i = 0; i < nof_ports; i++) { + // Copy input buffer + memcpy(buffer_in, in[i], sizeof(cf_t) * len); + + if (fading[i]) { + srslte_channel_fading_execute(fading[i], buffer_in, buffer_out, len, t.full_secs + t.frac_secs); + memcpy(buffer_in, buffer_out, sizeof(cf_t) * len); + } + + if (delay[i]) { + srslte_channel_delay_execute(delay[i], buffer_in, buffer_out, len, &t); + memcpy(buffer_in, buffer_out, sizeof(cf_t) * len); + } + + // Copy output buffer + memcpy(out[i], buffer_out, sizeof(cf_t) * len); + } +} + +void channel::set_srate(uint32_t srate) +{ + if (current_srate != srate) { + for (uint32_t i = 0; i < nof_ports; i++) { + if (fading[i]) { + srslte_channel_fading_free(fading[i]); + + srslte_channel_fading_init(fading[i], srate, args.fading_model.c_str(), 0x1234 * i); + } + + if (delay[i]) { + srslte_channel_delay_update_srate(delay[i], srate); + } + current_srate = srate; + } + } +} diff --git a/lib/src/phy/channel/delay.c b/lib/src/phy/channel/delay.c new file mode 100644 index 000000000..3554f5ce2 --- /dev/null +++ b/lib/src/phy/channel/delay.c @@ -0,0 +1,112 @@ +/* + * 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 +#include +#include + +static inline double caulculate_delay_us(srslte_channel_delay_t* q, const srslte_timestamp_t* ts) +{ + uint32_t mod_secs = (uint32_t)(ts->full_secs % q->period_s); + double arg = 2.0 * M_PI * ((double)mod_secs + ts->frac_secs) / (double)q->period_s; + double delay_us = q->delay_min_us + (q->delay_max_us - q->delay_min_us) * (1.0 + sin(arg)) / 2.0; + + return delay_us; +} + +static inline uint32_t caulculate_delay_nsamples(srslte_channel_delay_t* q, double delay_us) +{ + return (uint32_t)round(delay_us * (double)q->srate_hz / 1e6); +} + +static inline uint32_t ringbuffer_available_nsamples(srslte_channel_delay_t* q) +{ + return srslte_ringbuffer_status(&q->rb) / sizeof(cf_t); +} + +int srslte_channel_delay_init( + srslte_channel_delay_t* q, float delay_min_us, float delay_max_us, uint32_t period_s, uint32_t srate_max_hz) +{ + // Calculate buffer size + uint32_t buff_size = (uint32_t)ceilf(delay_max_us * (float)srate_max_hz); + + // Create ring buffer + int ret = srslte_ringbuffer_init(&q->rb, sizeof(cf_t) * buff_size); + + // Create zero buffer + q->zero_buffer = srslte_vec_malloc(sizeof(cf_t) * buff_size); + if (!q->zero_buffer) { + ret = SRSLTE_ERROR; + } + + // Load initial parameters + q->delay_min_us = delay_min_us; + q->delay_max_us = delay_max_us; + q->srate_max_hz = srate_max_hz; + q->srate_hz = srate_max_hz; + q->period_s = period_s; + + return ret; +} + +void srslte_channel_delay_update_srate(srslte_channel_delay_t* q, uint32_t srate_hz) +{ + srslte_ringbuffer_reset(&q->rb); + q->srate_hz = srate_hz; +} + +void srslte_channel_delay_free(srslte_channel_delay_t* q) +{ + srslte_ringbuffer_free(&q->rb); + + if (q->zero_buffer) { + free(q->zero_buffer); + } +} + +void srslte_channel_delay_execute( + srslte_channel_delay_t* q, const cf_t* in, cf_t* out, uint32_t len, const srslte_timestamp_t* ts) +{ + double delay_us = caulculate_delay_us(q, ts); + uint32_t delay_nsamples = caulculate_delay_nsamples(q, delay_us); + uint32_t available_nsamples = ringbuffer_available_nsamples(q); + uint32_t read_nsamples = SRSLTE_MIN(delay_nsamples, len); + uint32_t copy_nsamples = (len > read_nsamples) ? (len - read_nsamples) : 0; + + if (available_nsamples < delay_nsamples) { + uint32_t nzeros = delay_nsamples - available_nsamples; + bzero(q->zero_buffer, sizeof(cf_t) * nzeros); + srslte_ringbuffer_write(&q->rb, q->zero_buffer, sizeof(cf_t) * nzeros); + } else if (available_nsamples > delay_nsamples) { + srslte_ringbuffer_read(&q->rb, q->zero_buffer, sizeof(cf_t) * (available_nsamples - delay_nsamples)); + } + + // Read buffered samples + srslte_ringbuffer_read(&q->rb, out, sizeof(cf_t) * read_nsamples); + + // Read other samples + if (copy_nsamples) { + memcpy(&out[read_nsamples], in, sizeof(cf_t) * copy_nsamples); + } + + // Write new sampels + srslte_ringbuffer_write(&q->rb, (void*)&in[copy_nsamples], sizeof(cf_t) * read_nsamples); +} diff --git a/srsue/hdr/phy/sync.h b/srsue/hdr/phy/sync.h index 66b1020b1..30c6307fa 100644 --- a/srsue/hdr/phy/sync.h +++ b/srsue/hdr/phy/sync.h @@ -24,6 +24,7 @@ #include #include +#include #include "async_scell_recv.h" #include "phy_common.h" @@ -281,6 +282,7 @@ private: phy_common* worker_com; prach* prach_buffer; async_scell_recv_vector* scell_sync; + srslte::channel* channel_emulator = nullptr; // Object for synchronization of the primary cell srslte_ue_sync_t ue_sync; diff --git a/srsue/src/main.cc b/srsue/src/main.cc index e63783a59..d9ab908f4 100644 --- a/srsue/src/main.cc +++ b/srsue/src/main.cc @@ -147,6 +147,13 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("gw.ip_devname", bpo::value(&args->stack.gw.tun_dev_name)->default_value("tun_srsue"), "Name of the tun_srsue device") ("gw.ip_netmask", bpo::value(&args->stack.gw.tun_dev_netmask)->default_value("255.255.255.0"), "Netmask of the tun_srsue device") + /* Channel emulator section */ + ("channel.dl.enable", bpo::value(&args->phy.dl_channel_args.enable)->default_value(false), "Enable/Disable internal Downlink channel emulator") + ("channel.dl.fading_model", bpo::value(&args->phy.dl_channel_args.fading_model)->default_value("none"), "Fading model (none, EPA5, EVA70, ETU300, etc)") + ("channel.dl.delay_period", bpo::value(&args->phy.dl_channel_args.delay_period_s)->default_value(3600), "Delay period in seconds (integer)") + ("channel.dl.delay_maximum_us", bpo::value(&args->phy.dl_channel_args.delay_max_us)->default_value(100.0f), "Maximum delay in microseconds") + ("channel.dl.delay_minimum_us", bpo::value(&args->phy.dl_channel_args.delay_min_us)->default_value(0.0f), "Maximum delay in microseconds") + /* Expert section */ ("expert.phy.worker_cpu_mask", bpo::value(&args->phy.worker_cpu_mask)->default_value(-1), diff --git a/srsue/src/phy/sync.cc b/srsue/src/phy/sync.cc index 12cb4817e..6c1495843 100644 --- a/srsue/src/phy/sync.cc +++ b/srsue/src/phy/sync.cc @@ -21,6 +21,7 @@ #include "srsue/hdr/phy/sync.h" #include "srslte/common/log.h" +#include "srslte/phy/channel/channel.h" #include "srslte/srslte.h" #include "srsue/hdr/phy/sf_worker.h" #include @@ -91,6 +92,11 @@ void sync::init(radio_interface_phy* _radio, return; } + if (worker_com->args->dl_channel_args.enable) { + channel_emulator = new srslte::channel(worker_com->args->dl_channel_args, + worker_com->args->nof_rx_ant * worker_com->args->nof_rx_ant); + } + nof_workers = workers_pool->get_nof_workers(); worker_com->set_nof_workers(nof_workers); @@ -128,6 +134,11 @@ sync::~sync() } pthread_mutex_destroy(&rrc_mutex); srslte_ue_sync_free(&ue_sync); + + // Destroy channel emulator if created + if (channel_emulator) { + delete channel_emulator; + } } } @@ -925,6 +936,11 @@ void sync::get_current_cell(srslte_cell_t* cell, uint32_t* earfcn) int sync::radio_recv_fnc(cf_t* data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t* rx_time) { if (radio_h->rx_now(0, data, nsamples, rx_time)) { + if (channel_emulator && rx_time) { + channel_emulator->set_srate(current_srate); + channel_emulator->run(data, data, nsamples, *rx_time); + } + int offset = nsamples - current_sflen; if (abs(offset) < 10 && offset != 0) { next_radio_offset[0] = offset; diff --git a/srsue/ue.conf.example b/srsue/ue.conf.example index d17cc28ad..fe3ad20a0 100644 --- a/srsue/ue.conf.example +++ b/srsue/ue.conf.example @@ -179,6 +179,25 @@ imei = 353490069873319 [gui] enable = false +##################################################################### +# Channel emulator options: +# dl.enable: Enable/Disable internal Downlink channel emulator +# +# -- Fading emulator +# dl.fading_model: Fading model (none, EPA5, EVA70, ETU300, etc) +# +# -- Delay Emulator: d(t) = d_min + (d_max - d_min) * sin(2pi*t/period) / 2 +# dl.delay_period: Delay period in seconds (integer). +# dl.delay_maximum_us: Maximum delay in microseconds +# dl.delay_minumum_us: Minimum delay in microseconds +##################################################################### +[channel] +#dl.enable = false +#dl.fading_model = none +#dl.delay_period = 3600 +#dl.delay_maximum_us = 100 +#dl.delay_minimum_us = 10 + ##################################################################### # Expert configuration options #