Compare commits

...

12 Commits

Author SHA1 Message Date
Bartosz Podrygajlo
e696734b18 Logging for beam switching 2025-10-23 16:26:02 +02:00
francescomani
5ed7833abe config file changes 2025-10-16 10:53:49 +02:00
francescomani
0612fe27c9 RU control function for beam information (unfinished) 2025-10-16 10:53:49 +02:00
Bartosz Podrygajlo
d0ca4982cd RFsim: Optimization for operation without chanmod 2025-10-15 12:16:14 +02:00
Bartosz Podrygajlo
8aed7cd4ce RFsim: read_beams function
Added a new function for reading multiple beams concurrently.
The TX to RX beam combination is simplistic and based on beam gain.
2025-10-15 12:16:14 +02:00
Bartosz Podrygajlo
9c711a9d9a Rfsim: Process received packets on demand
- Received packets are dynamically allocated and kept in a
queue for as long as they are needed.

- The layout of the packets was changed from interleaved antennas
to non-interleaved antennas.

- Generate a contiguous buffer from saved packets as needed.

There is extra work caused by this change due to recv() call no
longer saving data directly into the circular buffer, however further
processing is easier due to contiguous nature of the tx antenna buffers
prepared. The impact on performance was not measured.
2025-10-15 12:16:14 +02:00
Bartosz Podrygajlo
ca3a624678 Fix formatting for apply_channelmod.c 2025-10-15 12:16:14 +02:00
Bartosz Podrygajlo
704c67a000 rfsim: process received packet when its received completely
The simplifies some code in packet reception and is a prerequisite
for processing received without relying on circular buffer
2025-10-15 12:16:14 +02:00
Bartosz Podrygajlo
ccc8fe2dfa Beam simulation for rfsimulator
A simplified beam switching simulation for rfsimulator. This works
mainly in 1 gNB - N UEs scenarios. This is not a real beamformer with
antenna, channel and propagation model. The way it works is that the
peers inform each other about the beams they transmit in, and on
reception before channel modelling, the beam data is combined based on
preconfigured rx_beam/tx_beam gain matrix.

Usage:
 - Add --rfsimulator.enable_beams to enable beam simulation
 - Add --rfsimulator.beam_gains <comma separated Toepliz form matrix of gains in dB>.

   Example list: 0,-5,-10

   Resulting matrix:

   [[0, -5, -10]
    [-5, 0, -5]]
    [-10, -5, 0]]

 - Add --telnetsrv if you want to control the beams manually. The command in telnet
   is "rfsimu setbeam <beam_map>", where beam_map is a uint64_t controling the
   reception beam and transmission beam. If the application is switching the beams
   automatically the beams will be overwritten as soon as the application chooses to
   switch beams.

Co-authored-by: Laurent Thomas <laurent.thomas@open-cells.com>
2025-10-15 12:16:14 +02:00
Bartosz Podrygajlo
f0b23b853c Add prototype for trx_set_beams function
This function is to be called by the application to switch beams
on reception and transmission. When receiving new samples using
trx_read/trx_write the application is expected to be able to
determine which beam the samples belong to.
2025-10-15 12:16:14 +02:00
Bartosz Podrygajlo
ff6f0b3ced Change trx_write_func to add beams definition over multiple tx antennas per beam
Co-authored-by: Laurent Thomas <laurent.thomas@open-cells.com>
2025-10-15 12:16:14 +02:00
Bartosz Podrygajlo
f22fe94756 RFsimulator C++ conversion
Convert rfsimulator C source code to C++ to access to STL.
2025-10-15 12:16:14 +02:00
29 changed files with 922 additions and 446 deletions

View File

@@ -50,7 +50,7 @@ gNBs =
initialDLBWPsubcarrierSpacing = 3;
#pdcch-ConfigCommon
initialDLBWPcontrolResourceSetZero = 1;
initialDLBWPsearchSpaceZero = 0;
initialDLBWPsearchSpaceZero = 1;
#uplinkConfigCommon
#frequencyInfoUL
@@ -70,7 +70,7 @@ gNBs =
initialULBWPsubcarrierSpacing = 3;
#rach-ConfigCommon
#rach-ConfigGeneric
prach_ConfigurationIndex = 52;
prach_ConfigurationIndex = 70;
#prach_msg1_FDM
#0 = one, 1=two, 2=four, 3=eight
prach_msg1_FDM = 0;
@@ -112,7 +112,7 @@ gNBs =
hoppingId = 40;
p0_nominal = -90;
ssb_PositionsInBurst_Bitmap = 1;
ssb_PositionsInBurst_Bitmap = 85;
# ssb_periodicityServingCell
# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
@@ -175,7 +175,7 @@ MACRLCs = ({
set_analog_beamforming = 1;
beam_duration = 1;
beams_per_period = 1;
beam_weights = [0]; // single SSB -> one analog beam
beam_weights = [0, 1, 2, 3]; // single SSB -> one analog beam
});
L1s = (

View File

@@ -213,7 +213,7 @@ int config_check_unknown_cmdlineopt(configmodule_interface_t *cfg, char *prefix)
return unknowndetected;
} /* config_check_unknown_cmdlineopt */
int config_process_cmdline(configmodule_interface_t *cfg, paramdef_t *cfgoptions, int numoptions, char *prefix)
int config_process_cmdline(configmodule_interface_t *cfg, paramdef_t *cfgoptions, int numoptions, const char *prefix)
{
int c = cfg->argc;
int i,j;

View File

@@ -118,7 +118,7 @@ void *config_allocate_new(configmodule_interface_t *cfg, int sz, bool autoFree)
return ptr;
}
int config_setdefault_int(configmodule_interface_t *cfg, paramdef_t *cfgoptions, char *prefix)
int config_setdefault_int(configmodule_interface_t *cfg, paramdef_t *cfgoptions, const char *prefix)
{
int status = 0;
config_check_valptr(cfg, cfgoptions, sizeof(*cfgoptions->iptr), 1);
@@ -132,7 +132,7 @@ int config_setdefault_int(configmodule_interface_t *cfg, paramdef_t *cfgoptions,
return status;
}
int config_setdefault_int64(configmodule_interface_t *cfg, paramdef_t *cfgoptions, char *prefix)
int config_setdefault_int64(configmodule_interface_t *cfg, paramdef_t *cfgoptions, const char *prefix)
{
int status = 0;
config_check_valptr(cfg, cfgoptions, sizeof(*cfgoptions->i64ptr), 1);
@@ -150,7 +150,7 @@ int config_setdefault_int64(configmodule_interface_t *cfg, paramdef_t *cfgoption
return status;
}
int config_setdefault_intlist(configmodule_interface_t *cfg, paramdef_t *cfgoptions, char *prefix)
int config_setdefault_intlist(configmodule_interface_t *cfg, paramdef_t *cfgoptions, const char *prefix)
{
int status = 0;
@@ -167,7 +167,7 @@ int config_setdefault_intlist(configmodule_interface_t *cfg, paramdef_t *cfgopti
return status;
}
int config_setdefault_string(configmodule_interface_t *cfg, paramdef_t *cfgoptions, char *prefix)
int config_setdefault_string(configmodule_interface_t *cfg, paramdef_t *cfgoptions, const char *prefix)
{
int status = 0;
@@ -190,7 +190,7 @@ int config_setdefault_string(configmodule_interface_t *cfg, paramdef_t *cfgoptio
return status;
}
int config_setdefault_stringlist(configmodule_interface_t *cfg, paramdef_t *cfgoptions, char *prefix)
int config_setdefault_stringlist(configmodule_interface_t *cfg, paramdef_t *cfgoptions, const char *prefix)
{
int status = 0;
@@ -210,7 +210,7 @@ int config_setdefault_stringlist(configmodule_interface_t *cfg, paramdef_t *cfgo
return status;
}
int config_setdefault_double(configmodule_interface_t *cfg, paramdef_t *cfgoptions, char *prefix)
int config_setdefault_double(configmodule_interface_t *cfg, paramdef_t *cfgoptions, const char *prefix)
{
int status = 0;
config_check_valptr(cfg, cfgoptions, sizeof(*cfgoptions->dblptr), 1);
@@ -224,7 +224,7 @@ int config_setdefault_double(configmodule_interface_t *cfg, paramdef_t *cfgoptio
return status;
}
int config_assign_ipv4addr(configmodule_interface_t *cfg, paramdef_t *cfgoptions, char *ipv4addr)
int config_assign_ipv4addr(configmodule_interface_t *cfg, paramdef_t *cfgoptions, const char *ipv4addr)
{
config_check_valptr(cfg, cfgoptions, sizeof(*cfgoptions->uptr), 1);
int rst=inet_pton(AF_INET, ipv4addr,cfgoptions->uptr );
@@ -246,7 +246,7 @@ int config_assign_ipv4addr(configmodule_interface_t *cfg, paramdef_t *cfgoptions
return 0;
}
int config_setdefault_ipv4addr(configmodule_interface_t *cfg, paramdef_t *cfgoptions, char *prefix)
int config_setdefault_ipv4addr(configmodule_interface_t *cfg, paramdef_t *cfgoptions, const char *prefix)
{
int status = 0;
@@ -322,7 +322,7 @@ void config_assign_int(configmodule_interface_t *cfg, paramdef_t *cfgoptions, ch
* @param[in] prefix Optional prefix for the parameter name (can be NULL).
* @return 1 if the default value was set, 0 if ignored, or -1 on error/unsupported type.
*/
int config_common_getdefault(configmodule_interface_t *cfg, paramdef_t *cfgoption, char *prefix)
int config_common_getdefault(configmodule_interface_t *cfg, paramdef_t *cfgoption, const char *prefix)
{
if( (cfgoption->paramflags & PARAMFLAG_DONOTREAD) != 0) {
return 0;

View File

@@ -41,16 +41,16 @@ extern "C"
#endif
void config_check_valptr(configmodule_interface_t *cfg, paramdef_t *cfgoptions, int elt_sz, int nb_elt);
/* functions to set a parameter to its default value */
int config_setdefault_int(configmodule_interface_t *cfg, paramdef_t *cfgoptions, char *prefix);
int config_setdefault_int64(configmodule_interface_t *cfg, paramdef_t *cfgoptions, char *prefix);
int config_setdefault_intlist(configmodule_interface_t *cfg, paramdef_t *cfgoptions, char *prefix);
int config_setdefault_string(configmodule_interface_t *cfg, paramdef_t *cfgoptions, char *prefix);
int config_setdefault_stringlist(configmodule_interface_t *cfg, paramdef_t *cfgoptions, char *prefix);
int config_setdefault_double(configmodule_interface_t *cfg, paramdef_t *cfgoptions, char *prefix);
int config_setdefault_ipv4addr(configmodule_interface_t *cfg, paramdef_t *cfgoptions, char *prefix);
int config_setdefault_int(configmodule_interface_t *cfg, paramdef_t *cfgoptions, const char *prefix);
int config_setdefault_int64(configmodule_interface_t *cfg, paramdef_t *cfgoptions, const char *prefix);
int config_setdefault_intlist(configmodule_interface_t *cfg, paramdef_t *cfgoptions, const char *prefix);
int config_setdefault_string(configmodule_interface_t *cfg, paramdef_t *cfgoptions, const char *prefix);
int config_setdefault_stringlist(configmodule_interface_t *cfg, paramdef_t *cfgoptions, const char *prefix);
int config_setdefault_double(configmodule_interface_t *cfg, paramdef_t *cfgoptions, const char *prefix);
int config_setdefault_ipv4addr(configmodule_interface_t *cfg, paramdef_t *cfgoptions, const char *prefix);
void *config_allocate_new(configmodule_interface_t *cfg, int sz, bool autoFree);
void config_assign_int(configmodule_interface_t *cfg, paramdef_t *cfgoptions, char *fullname, int val);
int config_common_getdefault(configmodule_interface_t *cfg, paramdef_t *cfgoption, char *prefix);
int config_common_getdefault(configmodule_interface_t *cfg, paramdef_t *cfgoption, const char *prefix);
#ifdef __cplusplus
}

View File

@@ -150,7 +150,7 @@ int config_cmdlineonly_getlist(configmodule_interface_t *cfg,
return 0;
}
int config_cmdlineonly_get(configmodule_interface_t *cfg, paramdef_t *cfgoptions, int numoptions, char *prefix)
int config_cmdlineonly_get(configmodule_interface_t *cfg, paramdef_t *cfgoptions, int numoptions, const char *prefix)
{
int defval;
int fatalerror=0;

View File

@@ -59,10 +59,10 @@
#define CONFIG_ABORT (1<<21) // config failed,abort execution
#define CONFIG_NOOOPT (1<<22) // no -O option found when parsing command line
struct configmodule_interface;
typedef int (*configmodule_getfunc_t)(struct configmodule_interface *, paramdef_t *, int numparams, char *prefix);
typedef int (*configmodule_getfunc_t)(struct configmodule_interface *, paramdef_t *, int numparams, const char *prefix);
typedef int (
*configmodule_getlistfunc_t)(struct configmodule_interface *, paramlist_def_t *, paramdef_t *, int numparams, char *prefix);
typedef int (*configmodule_setfunc_t)(paramdef_t *cfgoptions, int numoptions, char *prefix);
typedef int (*configmodule_setfunc_t)(paramdef_t *cfgoptions, int numoptions, const char *prefix);
typedef void (*configmodule_endfunc_t)(struct configmodule_interface *cfg);
typedef int (*configmodule_initfunc_t)(struct configmodule_interface *cfg);

View File

@@ -105,7 +105,7 @@ typedef union checkedparam {
#define DEFAULT_EXTRA_SZ 256
typedef struct paramdef {
char optname[MAX_OPTNAME_SIZE]; /* parameter name, can be used as long command line option */
char *helpstr; /* help string */
const char *helpstr; /* help string */
unsigned int paramflags; /* value is a "ored" combination of above PARAMFLAG_XXXX values */
union { /* pointer to the parameter value, completed by the config module */
char **strptr;
@@ -122,7 +122,7 @@ typedef struct paramdef {
void *voidptr;
} ;
union { /* default parameter value, to be used when PARAMFLAG_MANDATORY is not specified */
char *defstrval;
const char *defstrval;
char **defstrlistval;
uint32_t defuintval;
int defintval;

View File

@@ -71,7 +71,7 @@ int config_get_processedint(configmodule_interface_t *cfg, paramdef_t *cfgoption
return ret;
}
void config_printhelp(paramdef_t *params,int numparams, char *prefix) {
void config_printhelp(paramdef_t *params,int numparams, const char *prefix) {
printf("\n-----Help for section %-26s: %03i entries------\n",(prefix==NULL)?"(root section)":prefix,numparams);
for (int i=0 ; i<numparams ; i++) {
@@ -84,7 +84,7 @@ void config_printhelp(paramdef_t *params,int numparams, char *prefix) {
printf("--------------------------------------------------------------------\n\n");
}
int config_execcheck(configmodule_interface_t *cfg, paramdef_t *params, int numparams, char *prefix)
int config_execcheck(configmodule_interface_t *cfg, paramdef_t *params, int numparams, const char *prefix)
{
int st=0;
@@ -105,7 +105,7 @@ int config_execcheck(configmodule_interface_t *cfg, paramdef_t *params, int nump
return st;
}
int config_paramidx_fromname(paramdef_t *params, int numparams, char *name) {
int config_paramidx_fromname(paramdef_t *params, int numparams, const char *name) {
for (int i=0; i<numparams ; i++) {
if (strcmp(name,params[i].optname) == 0)
return i;
@@ -115,7 +115,7 @@ int config_paramidx_fromname(paramdef_t *params, int numparams, char *name) {
return -1;
}
int config_get(configmodule_interface_t *cfgif, paramdef_t *params, int numparams, char *prefix)
int config_get(configmodule_interface_t *cfgif, paramdef_t *params, int numparams, const char *prefix)
{
int ret= -1;

View File

@@ -49,17 +49,17 @@ extern configmodule_interface_t *uniqCfg;
#define CONFIG_SETRTFLAG(P) if (config_get_if()) { config_get_if()->rtflags |= P; }
#define CONFIG_CLEARRTFLAG(P) if (config_get_if()) { config_get_if()->rtflags &= (~P); }
#define CONFIG_ISPARAMFLAGSET(P,F) ( !!(P.paramflags & F))
int config_paramidx_fromname(paramdef_t *params, int numparams, char *name);
int config_paramidx_fromname(paramdef_t *params, int numparams, const char *name);
/* utility functions, to be used by configuration module and/or configuration libraries */
void config_printhelp(paramdef_t *, int numparams, char *prefix);
int config_process_cmdline(configmodule_interface_t *cfg, paramdef_t *params, int numparams, char *prefix);
int config_assign_ipv4addr(configmodule_interface_t *cfg, paramdef_t *cfgoptions, char *ipv4addr);
void config_printhelp(paramdef_t *, int numparams, const char *prefix);
int config_process_cmdline(configmodule_interface_t *cfg, paramdef_t *params, int numparams, const char *prefix);
int config_assign_ipv4addr(configmodule_interface_t *cfg, paramdef_t *cfgoptions, const char *ipv4addr);
/* apis to get/check parameters, to be used by oai modules, at configuration time */
#define CONFIG_CHECKALLSECTIONS "ALLSECTIONS"
int config_check_unknown_cmdlineopt(configmodule_interface_t *cfg, char *prefix);
int config_get(configmodule_interface_t *cfg, paramdef_t *params, int numparams, char *prefix);
int config_get(configmodule_interface_t *cfg, paramdef_t *params, int numparams, const char *prefix);
int config_getlist(configmodule_interface_t *cfg, paramlist_def_t *ParamList, paramdef_t *params, int numparams, char *prefix);
/* apis to set some of the paramdef_t fields before using the get/getlist api's */

View File

@@ -411,7 +411,7 @@ TEST(yaml_config, test_read_ipv4) {
config_yaml_get(cfg, &p, 1, prefix);
printf("%x\n", addr);
p.defstrval = strdup("10.0.0.1");
p.defstrval = "10.0.0.1";
strncpy(p.optname, "ipv4_3", sizeof(p.optname) - 1);
config_yaml_get(cfg, &p, 1, prefix);
printf("%x\n", addr);
@@ -419,7 +419,6 @@ TEST(yaml_config, test_read_ipv4) {
config_yaml_end(cfg);
free(cfg->cfgP[0]);
end_configmodule(cfg);
free(p.defstrval);
}
TEST(yaml_config, yaml_read_str_as_int) {

View File

@@ -252,7 +252,7 @@ load_module_shlib_exit:
return ret;
}
void * get_shlibmodule_fptr(char *modname, char *fname)
void * get_shlibmodule_fptr(const char *modname, const char *fname)
{
for (int i=0; i<loader_data.numshlibs && loader_data.shlibs[i].name != NULL; i++) {
if ( strcmp(loader_data.shlibs[i].name, modname) == 0) {

View File

@@ -82,7 +82,7 @@ extern loader_data_t loader_data;
// clang-format on
int load_module_version_shlib(char *modname, char *version, loader_shlibfunc_t *farray, int numf, void *initfunc_arg);
void *get_shlibmodule_fptr(char *modname, char *fname);
void *get_shlibmodule_fptr(const char *modname, const char *fname);
#define load_module_shlib(M, F, N, I) load_module_version_shlib(M, NULL, F, N, I)
void loader_reset();
#endif

View File

@@ -76,7 +76,7 @@ typedef struct webdatadef {
typedef void(*telnet_printfunc_t)(const char* format, ...);
typedef int(*cmdfunc_t)(char*, int, telnet_printfunc_t prnt);
typedef int (*webfunc_t)(char *cmdbuff, int debug, telnet_printfunc_t prnt, ...);
typedef int (*webfunc_getdata_t)(char *cmdbuff, int debug, void *data, telnet_printfunc_t prnt);
typedef int (*webfunc_getdata_t)(const char *cmdbuff, int debug, void *data, telnet_printfunc_t prnt);
typedef int(*qcmdfunc_t)(char*, int, telnet_printfunc_t prnt,void *arg);
#define TELNETSRV_CMDFLAG_PUSHINTPOOLQ (1 << 0) // ask the telnet server to push the command in a thread pool queue
@@ -195,7 +195,7 @@ VT escape sequence definition, for smarter display....
#define TELNET_ADDCMD_FNAME "add_telnetcmd"
#define TELNET_POLLCMDQ_FNAME "poll_telnetcmdq"
#define TELNET_PUSHCMD_FNAME "telnet_pushcmd"
typedef int(*add_telnetcmd_func_t)(char *, telnetshell_vardef_t *, telnetshell_cmddef_t *);
typedef int(*add_telnetcmd_func_t)(const char *, telnetshell_vardef_t *, telnetshell_cmddef_t *);
typedef void(*poll_telnetcmdq_func_t)(void *qid,void *arg);
typedef void (*push_telnetcmd_func_t)(telnetshell_cmddef_t *cmd, char *cmdbuff, telnet_printfunc_t prnt);
#ifdef TELNETSERVERCODE

View File

@@ -112,18 +112,17 @@ static void tx_func(processingData_L1tx_t *info)
syncMsg->timestamp_tx = info->timestamp_tx;
res->key = slot_rx;
pushNotifiedFIFO(&gNB->resp_L1, res);
processingData_RU_t syncMsgRU;
syncMsgRU.frame_tx = frame_tx;
syncMsgRU.slot_tx = slot_tx;
syncMsgRU.ru = gNB->RU_list[0];
syncMsgRU.timestamp_tx = info->timestamp_tx;
ru_ctrl_func((void *)&syncMsgRU);
int tx_slot_type = nr_slot_select(cfg, frame_tx, slot_tx);
if (tx_slot_type == NR_DOWNLINK_SLOT || tx_slot_type == NR_MIXED_SLOT || get_softmodem_params()->continuous_tx || IS_SOFTMODEM_RFSIM) {
start_meas(&info->gNB->phy_proc_tx);
phy_procedures_gNB_TX(info, frame_tx, slot_tx, 1);
PHY_VARS_gNB *gNB = info->gNB;
processingData_RU_t syncMsgRU;
syncMsgRU.frame_tx = frame_tx;
syncMsgRU.slot_tx = slot_tx;
syncMsgRU.ru = gNB->RU_list[0];
syncMsgRU.timestamp_tx = info->timestamp_tx;
LOG_D(PHY, "gNB: %d.%d : calling RU TX function\n", syncMsgRU.frame_tx, syncMsgRU.slot_tx);
ru_tx_func((void *)&syncMsgRU);
stop_meas(&info->gNB->phy_proc_tx);

View File

@@ -714,7 +714,36 @@ static radio_tx_gpio_flag_t get_gpio_flags(RU_t *ru, int slot)
return flags_gpio;
}
void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
static void ctrl_rf(RU_t *ru, int frame, int slot, uint64_t timestamp)
{
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
uint64_t beam_map = 0;
for (int i = 0; i < ru->num_beams_period; i++) {
int beam = -1;
for (int j = 0; j < fp->symbols_per_slot; j++) {
if (ru->common.beam_id[i][slot * fp->symbols_per_slot + j] == -1)
continue;
AssertFatal(beam == -1 || beam == ru->common.beam_id[i][slot * fp->symbols_per_slot + j],
"Cannot handle more than 1 beam per slot");
beam = ru->common.beam_id[i][slot * fp->symbols_per_slot + j];
}
if (beam != -1)
beam_map += 1 << beam;
}
// TODO in TX function we have timestamp + ru->ts_offset - sf_extension
// do I need to do the same?
if (beam_map != 0) {
static uint64_t old_beam_map = 0;
if (beam_map != old_beam_map) {
LOG_I(HW, "frame %d slot %d, beam_map changed from 0x%lx to 0x%lx\n", frame, slot, old_beam_map, beam_map);
old_beam_map = beam_map;
}
ru->rfdevice.trx_set_beams(&ru->rfdevice, beam_map, timestamp);
}
}
static void tx_rf(RU_t *ru, int frame, int slot, uint64_t timestamp)
{
RU_proc_t *proc = &ru->proc;
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
@@ -729,7 +758,6 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
int sf_extension = 0;
int siglen=fp->get_samples_per_slot(slot,fp);
radio_tx_burst_flag_t flags_burst = TX_BURST_INVALID;
radio_tx_gpio_flag_t flags_gpio = 0;
if (cfg->cell_config.frame_duplex_type.value == TDD && !get_softmodem_params()->continuous_tx && !IS_SOFTMODEM_RFSIM) {
int slot_type = nr_slot_select(cfg,frame,slot%fp->slots_per_frame);
@@ -773,30 +801,31 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
flags_burst = proc->first_tx == 1 ? TX_BURST_START : TX_BURST_MIDDLE;
}
if (ru->openair0_cfg.gpio_controller != RU_GPIO_CONTROL_NONE)
flags_gpio = get_gpio_flags(ru, slot);
const int flags = flags_burst | (flags_gpio << 4);
proc->first_tx = 0;
VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_TRX_WRITE_FLAGS, flags);
VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_TRX_WRITE_FLAGS, flags_burst);
VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_TX0_RU, frame);
VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_TTI_NUMBER_TX0_RU, slot);
int nt = ru->nb_tx * ru->num_beams_period;
void *txp[nt];
for (int i = 0; i < nt; i++)
txp[i] = (void *)&ru->common.txdata[i][fp->get_samples_slot_timestamp(slot, fp, 0)] - sf_extension * sizeof(int32_t);
void **txpBeam[ru->num_beams_period];
void *txp[ru->num_beams_period][ru->nb_tx];
for (int b = 0; b < ru->num_beams_period; b++) {
txpBeam[b] = txp[b];
for (int i = 0; i < ru->nb_tx; i++)
txp[b][i] = (void *)&ru->common.txdata[i * b][fp->get_samples_slot_timestamp(slot, fp, 0)] - sf_extension * sizeof(int32_t);
}
VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, (timestamp + ru->ts_offset) & 0xffffffff);
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_WRITE, 1);
// prepare tx buffer pointers
uint32_t txs = ru->rfdevice.trx_write_func(&ru->rfdevice,
timestamp + ru->ts_offset - sf_extension,
txp,
siglen + sf_extension,
nt,
flags);
uint32_t txs = ru->rfdevice.trx_write_beams(&ru->rfdevice,
timestamp + ru->ts_offset - sf_extension,
(void ***)txpBeam,
siglen + sf_extension,
nt,
1,
flags_burst);
LOG_D(PHY,
"[TXPATH] RU %d tx_rf, writing to TS %lu, %d.%d, unwrapped_frame %d, slot %d, flags %d, siglen+sf_extension %d, "
"returned %d, E %f\n",
@@ -806,10 +835,10 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
slot,
proc->frame_tx_unwrap,
slot,
flags,
flags_burst,
siglen + sf_extension,
txs,
10 * log10((double)signal_energy(txp[0], siglen + sf_extension)));
10 * log10((double)signal_energy(txp[0][0], siglen + sf_extension)));
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_WRITE, 0);
}
@@ -965,6 +994,25 @@ int setup_RU_buffers(RU_t *ru)
return(0);
}
void ru_ctrl_func(void *param)
{
processingData_RU_t *info = (processingData_RU_t *) param;
int frame = info->frame_tx;
int slot = info->slot_tx;
LOG_D(PHY,"ru_ctrl_func: frame = %d, slot = %d\n", frame, slot);
RU_t *ru = info->ru;
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
if (ru->gNB_list[0]->common_vars.analog_bf) {
for (int i = 0; i < ru->num_beams_period; i++) {
memcpy((void*) &ru->common.beam_id[i][slot * fp->symbols_per_slot],
(void*) &ru->gNB_list[0]->common_vars.beam_id[i][slot * fp->symbols_per_slot],
(fp->symbols_per_slot) * sizeof(int));
}
}
if (ru->fh_south_ctrl)
ru->fh_south_ctrl(ru, frame, slot, info->timestamp_tx);
}
void ru_tx_func(void *param)
{
processingData_RU_t *info = (processingData_RU_t *) param;
@@ -1498,6 +1546,7 @@ void set_function_spec_param(RU_t *ru)
ru->rfdevice.host_type = RAU_HOST;
ru->fh_south_in = rx_rf; // local synchronous RF RX
ru->fh_south_out = tx_rf; // local synchronous RF TX
ru->fh_south_ctrl = ctrl_rf; // local sunchronous RF control
ru->start_rf = start_rf; // need to start the local RF interface
ru->stop_rf = stop_rf;
ru->start_write_thread = start_write_thread; // starting RF TX in different thread

View File

@@ -574,7 +574,8 @@ static void RU_write(nr_rxtx_thread_data_t *rxtxD, bool sl_tx_action, c16_t **tx
const int maxWriteBlockSize = fp->get_samples_per_slot(proc->nr_slot_tx, fp);
while (writeBlockSize > maxWriteBlockSize) {
const int dummyBlockSize = min(writeBlockSize - maxWriteBlockSize, maxWriteBlockSize);
int tmp = openair0_write_reorder(&UE->rfdevice, writeTimestamp, (void **)txp, dummyBlockSize, fp->nb_antennas_tx, flags);
void **tx = (void **)txp;
int tmp = openair0_write_reorder(&UE->rfdevice, writeTimestamp, &tx, dummyBlockSize, fp->nb_antennas_tx, 1, flags);
AssertFatal(tmp == dummyBlockSize, "");
writeTimestamp += dummyBlockSize;
@@ -589,8 +590,8 @@ static void RU_write(nr_rxtx_thread_data_t *rxtxD, bool sl_tx_action, c16_t **tx
for (int i = 0; i < fp->nb_antennas_tx; i++)
nr_fo_compensation(ul_freq_offset, fp->samples_per_subframe, writeTimestamp, txp[i], txp[i], writeBlockSize);
}
int tmp = openair0_write_reorder(&UE->rfdevice, writeTimestamp, (void **)txp, writeBlockSize, fp->nb_antennas_tx, flags);
void **tx = (void **)txp;
int tmp = openair0_write_reorder(&UE->rfdevice, writeTimestamp, &tx, writeBlockSize, fp->nb_antennas_tx, 1, flags);
AssertFatal(tmp == writeBlockSize, "");
}

View File

@@ -354,7 +354,7 @@ extern int32_t uplink_frequency_offset[MAX_NUM_CCs][4];
extern int usrp_tx_thread;
extern int sf_ahead;
extern int oai_exit;
void ru_ctrl_func(void *param);
void ru_tx_func(void *param);
void configure_ru(void *, void *arg);
void configure_rru(void *, void *arg);

View File

@@ -220,9 +220,11 @@ void nr_ue_ssb_rsrp_measurements(PHY_VARS_NR_UE *ue,
int SNRtimes10 = dB_fixed_x10(signal_pwr) - dB_fixed_x10(ue->measurements.n0_power_avg);
ue->measurements.ssb_sinr_dB[ssb_index] = SNRtimes10 / 10.0;
LOG_D(PHY,
"[UE %d] ssb %d SS-RSRP: %d dBm/RE (%f dB/RE), SS-SINR: %f dB\n",
LOG_I(PHY,
"[UE %d] frame.slot %d.%d ssb %d SS-RSRP: %d dBm/RE (%f dB/RE), SS-SINR: %f dB\n",
ue->Mod_id,
proc->frame_rx,
proc->nr_slot_rx,
ssb_index,
ue->measurements.ssb_rsrp_dBm[ssb_index],
rsrp_db_per_re,

View File

@@ -533,6 +533,8 @@ typedef struct RU_t_s {
void (*fh_south_in)(struct RU_t_s *ru, int *frame, int *subframe);
/// function pointer to synchronous TX fronthaul function
void (*fh_south_out)(struct RU_t_s *ru, int frame_tx, int tti_tx, uint64_t timestamp_tx);
/// function pointer to synchronous control fronthaul function
void (*fh_south_ctrl)(struct RU_t_s *ru, int frame, int slot, uint64_t timestamp);
/// function pointer to synchronous RX fronthaul function (RRU)
void (*fh_north_in)(struct RU_t_s *ru, int *frame, int *subframe);
/// function pointer to synchronous RX fronthaul function (RRU)

View File

@@ -188,14 +188,6 @@ void nr_feptx_prec(RU_t *ru, int frame_tx, int slot_tx)
if (nr_slot_select(cfg,frame_tx,slot_tx) == NR_UPLINK_SLOT)
return;
if (gNB->common_vars.analog_bf) {
for (int i = 0; i < ru->num_beams_period; i++) {
memcpy((void*) &ru->common.beam_id[i][slot_tx * fp->symbols_per_slot],
(void*) &gNB->common_vars.beam_id[i][slot_tx * fp->symbols_per_slot],
(fp->symbols_per_slot) * sizeof(int));
}
}
// If there is no digital beamforming we just need to copy the data to RU
if (ru->config.dbt_config.num_dig_beams == 0 || ru->gNB_list[0]->common_vars.analog_bf) {
for (int b = 0; b < ru->num_beams_period; b++) {
@@ -234,12 +226,6 @@ void nr_feptx(void *arg)
if (tx_idx == 0)
start_meas(&ru->precoding_stats);
if (ru->gNB_list[0]->common_vars.analog_bf) {
memcpy(&ru->common.beam_id[bb][slot * fp->symbols_per_slot],
&ru->gNB_list[0]->common_vars.beam_id[bb][slot * fp->symbols_per_slot],
(fp->symbols_per_slot) * sizeof(int));
}
// If there is no digital beamforming we just need to copy the data to RU
if (ru->config.dbt_config.num_dig_beams == 0 || ru->gNB_list[0]->common_vars.analog_bf)
memcpy((void*)&ru->common.txdataF_BF[tx_idx][txdataF_BF_offset],

View File

@@ -48,8 +48,8 @@ static int channelmod_show_cmd(char *buff, int debug, telnet_printfunc_t prnt);
static int channelmod_modify_cmd(char *buff, int debug, telnet_printfunc_t prnt);
static int channelmod_print_help(char *buff, int debug, telnet_printfunc_t prnt);
int get_modchannel_index(char *buf, int debug, void *vdata, telnet_printfunc_t prnt);
int get_channel_params(char *buf, int debug, void *tdata, telnet_printfunc_t prnt);
int get_currentchannels_type(char *buf, int debug, void *vdata, telnet_printfunc_t prnt);
int get_channel_params(const char *buf, int debug, void *tdata, telnet_printfunc_t prnt);
int get_currentchannels_type(const char *buf, int debug, void *vdata, telnet_printfunc_t prnt);
#define HELP_WEBIF_MODIFCHAN_STRING "<channel index>"
static telnetshell_cmddef_t channelmod_cmdarray[] = {
@@ -1701,7 +1701,7 @@ channel_desc_t *new_channel_desc_scm(uint8_t nb_tx,
return(chan_desc);
} /* channel_desc_t *new_channel_desc_scm */
channel_desc_t *find_channel_desc_fromname( char *modelname ) {
channel_desc_t *find_channel_desc_fromname(const char *modelname) {
for(int i=0; i<max_chan; i++) {
if (defined_channels[i] != NULL) {
if (strcmp(defined_channels[i]->model_name,modelname) == 0)
@@ -2034,7 +2034,7 @@ static int channelmod_print_help(char *buff, int debug, telnet_printfunc_t prnt
static char *pnames[] = {"riceanf", "aoa", "randaoa", "ploss", "noise_power_dB", "offset", "forgetf", NULL};
static char *pformat[] = {"%lf", "%lf", "%i", "%lf", "%lf", "%i", "%lf", NULL};
int get_channel_params(char *buf, int debug, void *vdata, telnet_printfunc_t prnt)
int get_channel_params(const char *buf, int debug, void *vdata, telnet_printfunc_t prnt)
{
if (buf == NULL) {
LOG_I(UTIL, "%s received NULL buffer\n", __FUNCTION__);
@@ -2116,7 +2116,7 @@ static void display_channelmodel(channel_desc_t *cd,int debug, telnet_printfunc_
}
}
int get_currentchannels_type(char *buf, int debug, void *vdata, telnet_printfunc_t prnt)
int get_currentchannels_type(const char *buf, int debug, void *vdata, telnet_printfunc_t prnt)
{
webdatadef_t *tdata;
if (buf == NULL) {

View File

@@ -347,7 +347,7 @@ channel_desc_t *new_channel_desc_scm(uint8_t nb_tx,
double path_loss_dB,
float noise_power_dB);
channel_desc_t *find_channel_desc_fromname( char *modelname );
channel_desc_t *find_channel_desc_fromname(const char *modelname);
/**

View File

@@ -175,7 +175,8 @@ int openair0_transport_load(openair0_device *device,
return rc;
}
static void writerEnqueue(re_order_t *ctx, openair0_timestamp timestamp, void **txp, int nsamps, int nbAnt, int flags)
static void
writerEnqueue(re_order_t *ctx, openair0_timestamp timestamp, void ***txp, int nsamps, int nbAnt, int num_beams, int flags)
{
pthread_mutex_lock(&ctx->mutex_store);
LOG_D(HW, "Enqueue write for TS: %lu\n", timestamp);
@@ -187,9 +188,11 @@ static void writerEnqueue(re_order_t *ctx, openair0_timestamp timestamp, void **
ctx->queue[i].nsamps = nsamps;
ctx->queue[i].nbAnt = nbAnt;
ctx->queue[i].flags = flags;
ctx->queue[i].num_beams = num_beams;
AssertFatal(nbAnt <= NB_ANTENNAS_TX, "");
for (int j = 0; j < nbAnt; j++)
ctx->queue[i].txp[j] = txp[j];
for (int b = 0; b < num_beams; b++)
for (int j = 0; j < nbAnt; j++)
ctx->queue[i].txp[b][j] = txp[b][j];
break;
}
AssertFatal(i < WRITE_QUEUE_SZ, "Write queue full\n");
@@ -210,15 +213,19 @@ static void writerProcessWaitingQueue(openair0_device *device)
int nsamps = ctx->queue[i].nsamps;
int nbAnt = ctx->queue[i].nbAnt;
int flags = ctx->queue[i].flags;
void *txp[NB_ANTENNAS_TX];
AssertFatal(nbAnt <= NB_ANTENNAS_TX, "");
for (int j = 0; j < nbAnt; j++)
txp[j] = ctx->queue[i].txp[j];
int num_beams = ctx->queue[i].num_beams;
void **txpBeams[num_beams];
void *txp[num_beams][nbAnt];
for (int b = 0; b < num_beams; b++) {
txpBeams[b] = txp[b];
for (int j = 0; j < nbAnt; j++)
txp[b][j] = ctx->queue[i].txp[b][j];
}
ctx->queue[i].active = false;
pthread_mutex_unlock(&ctx->mutex_store);
found = true;
if (flags || IS_SOFTMODEM_RFSIM) {
int wroteSamples = device->trx_write_func(device, timestamp, txp, nsamps, nbAnt, flags);
int wroteSamples = device->trx_write_beams(device, timestamp, txpBeams, nsamps, nbAnt, num_beams, flags);
if (wroteSamples != nsamps)
LOG_E(HW, "Failed to write to rf\n");
}
@@ -235,7 +242,13 @@ static void writerProcessWaitingQueue(openair0_device *device)
// but to make zerocopy and agnostic design, we need to make a proper ring buffer with mutex protection
// mutex (or atomic flags) will be mandatory because this out order system root cause is there are several writer threads
int openair0_write_reorder(openair0_device *device, openair0_timestamp timestamp, void **txp, int nsamps, int nbAnt, int flags)
int openair0_write_reorder(openair0_device *device,
openair0_timestamp timestamp,
void ***txp,
int nsamps,
int nbAnt,
int num_beams,
int flags)
{
int wroteSamples = 0;
re_order_t *ctx = &device->reOrder;
@@ -250,19 +263,19 @@ int openair0_write_reorder(openair0_device *device, openair0_timestamp timestamp
// We have the write exclusivity
if (llabs(timestamp - ctx->nextTS) < MAX_GAP) { // We are writing in sequence of the previous write
if (flags || IS_SOFTMODEM_RFSIM)
wroteSamples = device->trx_write_func(device, timestamp, txp, nsamps, nbAnt, flags);
wroteSamples = device->trx_write_beams(device, timestamp, txp, nsamps, nbAnt, num_beams, flags);
else
wroteSamples = nsamps;
ctx->nextTS = timestamp + nsamps;
} else {
writerEnqueue(ctx, timestamp, txp, nsamps, nbAnt, flags);
writerEnqueue(ctx, timestamp, txp, nsamps, nbAnt, num_beams, flags);
}
writerProcessWaitingQueue(device);
pthread_mutex_unlock(&ctx->mutex_write);
return wroteSamples ? wroteSamples : nsamps;
}
writerEnqueue(ctx, timestamp, txp, nsamps, nbAnt, flags);
writerEnqueue(ctx, timestamp, txp, nsamps, nbAnt, num_beams, flags);
if (pthread_mutex_trylock(&ctx->mutex_write) == 0) {
writerProcessWaitingQueue(device);
pthread_mutex_unlock(&ctx->mutex_write);

View File

@@ -401,9 +401,10 @@ typedef struct {
struct {
bool active;
openair0_timestamp timestamp;
void *txp[NB_ANTENNAS_TX];
void *txp[64][NB_ANTENNAS_TX];
int nsamps;
int nbAnt;
int num_beams;
int flags;
} queue[WRITE_QUEUE_SZ];
} re_order_t;
@@ -493,10 +494,27 @@ struct openair0_device_t {
@param timestamp The timestamp at whicch the first sample MUST be sent
@param buff Buffer which holds the samples (2 dimensional)
@param nsamps number of samples to be sent
@param number of antennas
@param antenna_id
@param num_beams
@param flags flags must be set to true if timestamp parameter needs to be applied
*/
int (*trx_write_func)(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps,int antenna_id, int flags);
int (*trx_write_beams)(openair0_device *device,
openair0_timestamp timestamp,
void ***buff,
int nsamps,
int antenna_id,
int num_beams,
int flags);
/*! \brief Called to send samples to the RF target
@param device pointer to the device structure specific to the RF hardware target
@param timestamp The timestamp at whicch the first sample MUST be sent
@param buff Buffer which holds the samples (2 dimensional)
@param nsamps number of samples to be sent
@param number of antennas
@param flags flags must be set to true if timestamp parameter needs to be applied
*/
int (*trx_write_func)(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int antenna_id, int flags);
/*! \brief Called to send samples to the RF target
@param device pointer to the device structure specific to the RF hardware target
@@ -518,7 +536,27 @@ struct openair0_device_t {
* antennas from which to receive samples \returns the number of sample read
*/
int (*trx_read_func)(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps,int num_antennas);
int (*trx_read_func)(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps, int num_antennas);
/*! \brief Receive samples from hardware.
* Read nsamps samples from each channel to buffers. buff[0] is the array for
* the first channel. *ptimestamp is the time at which the first sample
* was received.
* \param device the hardware to use
* \param[out] ptimestamp the time at which the first sample was received.
* \param[out] buff An array of pointers to buffers for received samples. The buffers must be large enough to hold the number of
* samples nsamps.
* \param nsamps Number of samples. One sample is 2 byte I + 2 byte Q => 4 byte.
* \param num_antennas number of antennas from which to receive samples
* \param num_beams number of beams from which to receive samples
* \returns the number of sample read
*/
int (*trx_read_beams_func)(openair0_device *device,
openair0_timestamp *ptimestamp,
void ***buff,
int nsamps,
int num_antennas,
int num_beams);
/*! \brief Receive samples from hardware, this version provides a single antenna at a time and returns.
* Read nsamps samples from each channel to buffers. buff[0] is the array for
@@ -571,6 +609,21 @@ struct openair0_device_t {
*/
int (*trx_set_gains_func)(openair0_device *device, openair0_config_t *openair0_cfg);
/*! \brief Set tx/rx beams
*
* Set the tx/rx beams. This has to be done in advance of the reception in order to
* allow the underlying device to change receiver configuration. The exact time depends
* on the device.
*
* NOTICE: the samples returned from trx_read_func may belong to more than one beam. It is up
* to the application to determine the beam of the received samples.
*
* \param device the hardware to use
* \param beam_map the beams to receive
* \return 0 on success
*/
int (*trx_set_beams)(openair0_device *device, uint64_t beam_map, openair0_timestamp timestamp);
/*! \brief RRU Configuration callback
* \param idx RU index
* \param arg pointer to capabilities or configuration
@@ -629,6 +682,7 @@ typedef struct {
uint64_t timestamp; // Timestamp value of first sample
uint32_t option_value; // Option value
uint32_t option_flag; // Option flag
uint64_t beam_map;
} samplesBlockHeader_t;
#ifdef __cplusplus
@@ -678,7 +732,13 @@ extern int read_recplayconfig(recplay_conf_t **recplay_conf, recplay_state_t **r
/*! \brief store recorded iqs from memory to file. */
extern void iqrecorder_end(openair0_device *device);
int openair0_write_reorder(openair0_device *device, openair0_timestamp timestamp, void **txp, int nsamps, int nbAnt, int flags);
int openair0_write_reorder(openair0_device *device,
openair0_timestamp timestamp,
void ***txp,
int nsamps,
int nbAnt,
int num_beams,
int flags);
void openair0_write_reorder_clear_context(openair0_device *device);
#include <unistd.h>
#ifndef gettid

View File

@@ -423,12 +423,15 @@ static void trx_usrp_end(openair0_device *device) {
@param antenna_id index of the antenna if the device has multiple antennas
@param flags flags must be set to true if timestamp parameter needs to be applied
*/
static int trx_usrp_write(openair0_device *device,
openair0_timestamp timestamp,
void **buff,
int nsamps,
int cc,
int flags) {
static int usrp_write_beams(openair0_device *device,
openair0_timestamp timestamp,
void ***buff,
int nsamps,
int cc,
int num_beams,
int flags)
{
int ret=0;
usrp_state_t *s = (usrp_state_t *)device->priv;
timestamp -= device->openair0_cfg->command_line_sample_advance + device->openair0_cfg->tx_sample_advance;
@@ -481,10 +484,10 @@ static int trx_usrp_write(openair0_device *device,
// bring RX data into 12 LSBs for softmodem RX
for (int i = 0; i < cc; i++) {
for (int j = 0; j < nsamps2; j++) {
if ((((uintptr_t)buff[i]) & 0x1F) == 0) {
buff_tx[i][j] = simde_mm256_slli_epi16(((simde__m256i *)buff[i])[j], 4);
if ((((uintptr_t)buff[0][i]) & 0x1F) == 0) {
buff_tx[i][j] = simde_mm256_slli_epi16(((simde__m256i *)buff[0][i])[j], 4);
} else {
simde__m256i tmp = simde_mm256_loadu_si256(((simde__m256i *)buff[i]) + j);
simde__m256i tmp = simde_mm256_loadu_si256(((simde__m256i *)buff[0][i]) + j);
buff_tx[i][j] = simde_mm256_slli_epi16(tmp, 4);
}
}
@@ -541,7 +544,7 @@ VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_BEAM_SWITCHI
write_package[end].last_packet = last_packet_state;
write_package[end].flags_gpio = flags_gpio;
for (int i = 0; i < cc; i++)
write_package[end].buff[i] = buff[i];
write_package[end].buff[i] = buff[0][i];
write_thread->count_write++;
write_thread->end = (write_thread->end + 1) % MAX_WRITE_THREAD_PACKAGE;
LOG_D(HW, "Signaling TX TS %llu\n", (unsigned long long)timestamp);
@@ -550,7 +553,11 @@ VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_BEAM_SWITCHI
return 0;
}
}
static int trx_usrp_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int cc, int flags)
{
void **tmp = buff;
return usrp_write_beams(device, timestamp, &tmp, nsamps, cc, 1, flags);
}
//-----------------------start--------------------------
/*! \brief Called to send samples to the USRP RF target
@param device pointer to the device structure specific to the RF hardware target
@@ -1537,6 +1544,7 @@ extern "C" {
std::cout << boost::format("Using Device: %s") % s->usrp->get_pp_string() << std::endl;
LOG_I(HW,"Device timestamp: %f...\n", s->usrp->get_time_now().get_real_secs());
device->trx_write_func = trx_usrp_write;
device->trx_write_beams = usrp_write_beams;
device->trx_read_func = trx_usrp_read;
s->sample_rate = openair0_cfg[0].sample_rate;

View File

@@ -1,5 +1,5 @@
add_library(rfsimulator MODULE
simulator.c
simulator.cpp
apply_channelmod.c
../../openair1/PHY/TOOLS/signal_energy.c
)

View File

@@ -1,26 +1,25 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Author and copyright: Laurent Thomas, open-cells.com
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Author and copyright: Laurent Thomas, open-cells.com
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include <complex.h>
#include <common/utils/LOG/log.h>
@@ -42,22 +41,23 @@
either we regenerate the channel (call again random_channel(desc,0)), or we keep it over subframes
legacy: we regenerate each sub frame in UL, and each frame only in DL
*/
void rxAddInput(const c16_t *input_sig,
void rxAddInput(const c16_t **input_sig,
cf_t *after_channel_sig,
int rxAnt,
channel_desc_t *channelDesc,
int nbSamples,
uint64_t TS,
uint32_t CirSize,
bool add_noise)
uint64_t TS)
{
static uint64_t last_TS = 0;
if ((channelDesc->sat_height > 0) && (channelDesc->enable_dynamic_delay || channelDesc->enable_dynamic_Doppler)) { // model for transparent satellite on circular orbit
if ((channelDesc->sat_height > 0)
&& (channelDesc->enable_dynamic_delay
|| channelDesc->enable_dynamic_Doppler)) { // model for transparent satellite on circular orbit
/* assumptions:
- The Earth is spherical, the ground station is static, and that the Earth does not rotate.
- An access or link is possible from the satellite to the ground station at all times.
- The ground station is located at the North Pole (positive Zaxis), and the satellite starts from the initial elevation angle 0° in the second quadrant of the YZplane.
- The ground station is located at the North Pole (positive Zaxis), and the satellite starts from the initial elevation angle
0° in the second quadrant of the YZplane.
- Satellite moves in the clockwise direction in its circular orbit.
*/
const double radius_earth = 6377900; // m
@@ -78,7 +78,7 @@ void rxAddInput(const c16_t *input_sig,
const double pos_sat_z = radius_sat * cos(w_sat * t);
const double vel_sat_x = 0;
const double vel_sat_y = w_sat * radius_sat * cos(w_sat * t);
const double vel_sat_y = w_sat * radius_sat * cos(w_sat * t);
const double vel_sat_z = -w_sat * radius_sat * sin(w_sat * t);
const double pos_ue_x = 0;
@@ -124,9 +124,17 @@ void rxAddInput(const c16_t *input_sig,
if (channelDesc->enable_dynamic_Doppler)
channelDesc->Doppler_phase_inc = 2 * M_PI * f_Doppler_shift_ue_sat / channelDesc->sampling_rate;
if(TS - last_TS >= channelDesc->sampling_rate) {
if (TS - last_TS >= channelDesc->sampling_rate) {
last_TS = TS;
LOG_I(HW, "Satellite orbit: time %f s, Position = (%f, %f, %f), Velocity = (%f, %f, %f)\n", t, pos_sat_x, pos_sat_y, pos_sat_z, vel_sat_x, vel_sat_y, vel_sat_z);
LOG_I(HW,
"Satellite orbit: time %f s, Position = (%f, %f, %f), Velocity = (%f, %f, %f)\n",
t,
pos_sat_x,
pos_sat_y,
pos_sat_z,
vel_sat_x,
vel_sat_y,
vel_sat_z);
LOG_I(HW, "Uplink delay %f ms, Doppler shift UE->SAT %f kHz\n", prop_delay * 1000, f_Doppler_shift_ue_sat / 1000);
LOG_I(HW, "Satellite velocity towards gNB: %f m/s, acceleration towards gNB: %f m/s²\n", vel_sat_gnb, acc_sat_gnb);
}
@@ -183,9 +191,17 @@ void rxAddInput(const c16_t *input_sig,
if (channelDesc->enable_dynamic_Doppler)
channelDesc->Doppler_phase_inc = 2 * M_PI * f_Doppler_shift_sat_ue / channelDesc->sampling_rate;
if(TS - last_TS >= channelDesc->sampling_rate) {
if (TS - last_TS >= channelDesc->sampling_rate) {
last_TS = TS;
LOG_I(HW, "Satellite orbit: time %f s, Position = (%f, %f, %f), Velocity = (%f, %f, %f)\n", t, pos_sat_x, pos_sat_y, pos_sat_z, vel_sat_x, vel_sat_y, vel_sat_z);
LOG_I(HW,
"Satellite orbit: time %f s, Position = (%f, %f, %f), Velocity = (%f, %f, %f)\n",
t,
pos_sat_x,
pos_sat_y,
pos_sat_z,
vel_sat_x,
vel_sat_y,
vel_sat_z);
LOG_I(HW, "Downlink delay %f ms, Doppler shift SAT->UE %f kHz\n", prop_delay * 1000, f_Doppler_shift_sat_ue / 1000);
}
}
@@ -195,36 +211,28 @@ void rxAddInput(const c16_t *input_sig,
// so, in actual RF: tx gain + path loss + rx gain (+antenna gain, ...)
// UE and NB gain control to be added
// Fixme: not sure when it is "volts" so dB is 20*log10(...) or "power", so dB is 10*log10(...)
const double pathLossLinear = pow(10,channelDesc->path_loss_dB/20.0);
const double pathLossLinear = pow(10, channelDesc->path_loss_dB / 20.0);
// Energy in one sample to calibrate input noise
// the normalized OAI value seems to be 256 as average amplitude (numerical amplification = 1)
const double noise_per_sample = add_noise ? pow(10,channelDesc->noise_power_dB/10.0) * 256 : 0;
const uint64_t dd = channelDesc->channel_offset;
const int nbTx=channelDesc->nb_tx;
const double noise_per_sample = pow(10, channelDesc->noise_power_dB / 10.0) * 256;
const int nbTx = channelDesc->nb_tx;
double Doppler_phase_cur = channelDesc->Doppler_phase_cur[rxAnt];
Doppler_phase_cur -= 2 * M_PI * round(Doppler_phase_cur / (2 * M_PI));
for (int i=0; i<nbSamples; i++) {
for (int i = 0; i < nbSamples; i++) {
cf_t *out_ptr = after_channel_sig + i;
struct complexd rx_tmp= {0};
struct complexd rx_tmp = {0};
for (int txAnt=0; txAnt < nbTx; txAnt++) {
const struct complexd *channelModel= channelDesc->ch[rxAnt+(txAnt*channelDesc->nb_rx)];
for (int txAnt = 0; txAnt < nbTx; txAnt++) {
const struct complexd *channelModel = channelDesc->ch[rxAnt + (txAnt * channelDesc->nb_rx)];
//const struct complex *channelModelEnd=channelModel+channelDesc->channel_length;
for (int l = 0; l<(int)channelDesc->channel_length; l++) {
// let's assume TS+i >= l
// fixme: the rfsimulator current structure is interleaved antennas
// this has been designed to not have to wait a full block transmission
// but it is not very usefull
// it would be better to split out each antenna in a separate flow
// that will allow to mix ru antennas freely
// (X + cirSize) % cirSize to ensure that index is positive
const int idx = ((TS + i - l - dd) * nbTx + txAnt + CirSize) % CirSize;
const struct complex16 tx16 = input_sig[idx];
// const struct complex *channelModelEnd=channelModel+channelDesc->channel_length;
for (int l = 0; l < (int)channelDesc->channel_length; l++) {
const int idx = i - l + channelDesc->channel_length - 1;
const struct complex16 tx16 = input_sig[txAnt][idx];
rx_tmp.r += tx16.r * channelModel[l].r - tx16.i * channelModel[l].i;
rx_tmp.i += tx16.i * channelModel[l].r + tx16.r * channelModel[l].i;
} //l
} // l
}
if (channelDesc->Doppler_phase_inc != 0.0) {
@@ -246,12 +254,11 @@ void rxAddInput(const c16_t *input_sig,
channelDesc->Doppler_phase_cur[rxAnt] = Doppler_phase_cur;
if ( (TS*nbTx)%CirSize+nbSamples <= CirSize )
// Cast to a wrong type for compatibility !
LOG_D(HW,"Input power %f, output power: %f, channel path loss %f, noise coeff: %f \n",
10*log10((double)signal_energy((int32_t *)&input_sig[(TS*nbTx)%CirSize], nbSamples)),
10*log10((double)signal_energy((int32_t *)after_channel_sig, nbSamples)),
channelDesc->path_loss_dB,
10*log10(noise_per_sample));
// Cast to a wrong type for compatibility !
LOG_D(HW,
"Input power %f, output power: %f, channel path loss %f, noise coeff: %f \n",
10 * log10((double)signal_energy((int32_t *)&input_sig[0], nbSamples)),
10 * log10((double)signal_energy((int32_t *)after_channel_sig, nbSamples)),
channelDesc->path_loss_dB,
10 * log10(noise_per_sample));
}

View File

@@ -1,37 +1,34 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Author and copyright: Laurent Thomas, open-cells.com
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Author and copyright: Laurent Thomas, open-cells.com
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#ifndef __RFSIMULATOR_H
#define __RFSIMULATOR_H
#define __RFSIMULATOR_H
#include <stdbool.h>
void rxAddInput( const c16_t *input_sig,
cf_t *after_channel_sig,
int rxAnt,
channel_desc_t *channelDesc,
int nbSamples,
uint64_t TS,
uint32_t CirSize,
bool apply_noise
);
void rxAddInput(const c16_t **input_sig,
cf_t *after_channel_sig,
int rxAnt,
channel_desc_t *channelDesc,
int nbSamples,
uint64_t TS);
#endif