From 98e6d60337ee8699149f686d7cfb4c3f0724bb83 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Tue, 27 May 2025 15:21:34 +0200 Subject: [PATCH 1/7] RF simulator: Fix concurrency issues during new client connections Fix concurrency issues happening when a new client connects, sometimes leading to a total softmodem deadlock. Key changes: - Fix sample sending on new client connection: send the first sample only to the newly connected client. - Remove the --rfsimulator.hanging-workaround option - Replace global nb_ue and Sockmutex with nb_cnx and Sockmutex in rfsimulator_state_t for better concurrency and clarity. --- .../5g_f1_rfsimulator/docker-compose.yaml | 1 - radio/rfsimulator/README.md | 1 - radio/rfsimulator/simulator.c | 135 +++++++----------- 3 files changed, 53 insertions(+), 84 deletions(-) diff --git a/ci-scripts/yaml_files/5g_f1_rfsimulator/docker-compose.yaml b/ci-scripts/yaml_files/5g_f1_rfsimulator/docker-compose.yaml index fb1d5a95ce..6dd2dec52a 100644 --- a/ci-scripts/yaml_files/5g_f1_rfsimulator/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_f1_rfsimulator/docker-compose.yaml @@ -179,7 +179,6 @@ services: --uicc0.imsi 208990100001100 --rfsimulator.options chanmod --rfsimulator.serveraddr server - --rfsimulator.hanging-workaround 1 --telnetsrv --telnetsrv.shrmod ciUE --telnetsrv.listenaddr 192.168.71.181 --telnetsrv.listenport 8091 --channelmod.modellist_rfsimu_1.[0].model_name rfsimu_channel_ue0 --channelmod.modellist_rfsimu_1.[1].model_name rfsimu_channel_ue1 diff --git a/radio/rfsimulator/README.md b/radio/rfsimulator/README.md index ae46034cec..40688b755a 100644 --- a/radio/rfsimulator/README.md +++ b/radio/rfsimulator/README.md @@ -79,7 +79,6 @@ The RF simulator is using the configuration module, and its parameters are defin |`--rfsimulator.IQfile ` | path to a file to store the IQ samples to (only with `saviq`) | `/tmp/rfsimulator.iqs` | |`--rfsimulator.prop_delay` | simulated receive-path (gNB: UL, UE: DL) propagation delay in ms | 0 | |`--rfsimulator.wait_timeout` | wait timeout when no UE is connected | 1 | -|`--rfismulator.hanging-workaround`| Enable workaround to de-block potentially hanging server on new client connection. | 0 | Please refer to this document [`SIMULATION/TOOLS/DOC/channel_simulation.md`](../../openair1/SIMULATION/TOOLS/DOC/channel_simulation.md) for information about using the RFSimulator options to run the simulator with a channel model. diff --git a/radio/rfsimulator/simulator.c b/radio/rfsimulator/simulator.c index 32648758c8..1cfc7dd7cc 100644 --- a/radio/rfsimulator/simulator.c +++ b/radio/rfsimulator/simulator.c @@ -80,31 +80,30 @@ typedef enum { SIMU_ROLE_SERVER = 1, SIMU_ROLE_CLIENT } simuRole; #define RFSIMU_SECTION "rfsimulator" #define RFSIMU_OPTIONS_PARAMNAME "options" - -#define RFSIM_CONFIG_HELP_OPTIONS " list of comma separated options to enable rf simulator functionalities. Available options: \n"\ - " chanmod: enable channel modelisation\n"\ +#define RFSIM_CONFIG_HELP_OPTIONS \ + " list of comma separated options to enable rf simulator functionalities. Available options: \n" \ + " chanmod: enable channel modelisation\n" \ " saviq: enable saving written iqs to a file\n" -#define CONFIG_HELP_HANG_WORKAROUND "Enable workaroud for server mode hanging on new client connection.\n" /*-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /* configuration parameters for the rfsimulator device */ /* optname helpstr paramflags XXXptr defXXXval type numelt */ /*-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ #define simOpt PARAMFLAG_NOFREE|PARAMFLAG_CMDLINE_NOPREFIXENABLED +// clang-format off #define RFSIMULATOR_PARAMS_DESC { \ {"serveraddr", "\n", simOpt, .strptr=&rfsimulator->ip, .defstrval="127.0.0.1", TYPE_STRING, 0 },\ - {"serverport", "\n", simOpt, .u16ptr=&(rfsimulator->port), .defuintval=PORT, TYPE_UINT16, 0 },\ - {RFSIMU_OPTIONS_PARAMNAME, RFSIM_CONFIG_HELP_OPTIONS, 0, .strlistptr=NULL, .defstrlistval=NULL, TYPE_STRINGLIST,0 },\ + {"serverport", "\n", simOpt, .u16ptr = &(rfsimulator->port), .defuintval = PORT, TYPE_UINT16, 0}, \ + {RFSIMU_OPTIONS_PARAMNAME, RFSIM_CONFIG_HELP_OPTIONS, 0, .strlistptr = NULL, .defstrlistval = NULL, TYPE_STRINGLIST, 0}, \ {"IQfile", "\n",simOpt, .strptr=&saveF, .defstrval="/tmp/rfsimulator.iqs",TYPE_STRING, 0 },\ - {"modelname", "\n", simOpt, .strptr=&modelname, .defstrval="AWGN", TYPE_STRING, 0 },\ - {"ploss", "\n", simOpt, .dblptr=&(rfsimulator->chan_pathloss), .defdblval=0, TYPE_DOUBLE, 0 },\ + {"modelname", "\n", simOpt, .strptr = &modelname, .defstrval = "AWGN", TYPE_STRING, 0}, \ + {"ploss", "\n", simOpt, .dblptr = &(rfsimulator->chan_pathloss), .defdblval = 0, TYPE_DOUBLE, 0}, \ {"forgetfact", "\n", simOpt, .dblptr=&(rfsimulator->chan_forgetfact),.defdblval=0, TYPE_DOUBLE, 0 },\ - {"offset", "\n", simOpt, .u64ptr=&(rfsimulator->chan_offset), .defint64val=0, TYPE_UINT64, 0 },\ + {"offset", "\n", simOpt, .u64ptr = &(rfsimulator->chan_offset), .defint64val = 0, TYPE_UINT64, 0}, \ {"prop_delay", "\n", simOpt, .dblptr=&(rfsimulator->prop_delay_ms), .defdblval=0.0, TYPE_DOUBLE, 0 },\ {"wait_timeout", "\n", simOpt, .iptr=&(rfsimulator->wait_timeout), .defintval=1, TYPE_INT, 0 },\ - {"hanging-workaround", CONFIG_HELP_HANG_WORKAROUND, simOpt, .iptr=&rfsimulator->hanging_workaround, .defintval=0, TYPE_INT, 0 },\ }; - +// clang-format on static void getset_currentchannels_type(char *buf, int debug, webdatadef_t *tdata, telnet_printfunc_t prnt); extern int get_currentchannels_type(char *buf, int debug, webdatadef_t *tdata, telnet_printfunc_t prnt); // in random_channel.c static int rfsimu_setchanmod_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg); @@ -122,11 +121,7 @@ static telnetshell_cmddef_t rfsimu_cmdarray[] = { static telnetshell_cmddef_t *setmodel_cmddef = &(rfsimu_cmdarray[1]); static telnetshell_vardef_t rfsimu_vardef[] = {{"", 0, 0, NULL}}; -pthread_mutex_t Sockmutex; -unsigned int nb_ue = 0; - static uint64_t CirSize = minCirSize; - typedef c16_t sample_t; // 2*16 bits complex number typedef struct buffer_s { @@ -144,6 +139,8 @@ typedef struct buffer_s { typedef struct { int listen_sock, epollfd; + pthread_mutex_t Sockmutex; + unsigned int nb_cnx; openair0_timestamp nextRxTstamp; openair0_timestamp lastWroteTS; simuRole role; @@ -165,7 +162,6 @@ typedef struct { poll_telnetcmdq_func_t poll_telnetcmdq; int wait_timeout; double prop_delay_ms; - int hanging_workaround; } rfsimulator_state_t; static buffer_t *allocCirBuf(rfsimulator_state_t *bridge, int sock) @@ -214,7 +210,11 @@ static buffer_t *allocCirBuf(rfsimulator_state_t *bridge, int sock) init_done=true; } char modelname[30]; - snprintf(modelname, sizeofArray(modelname), "rfsimu_channel_%s%d", (bridge->role == SIMU_ROLE_SERVER) ? "ue" : "enB", nb_ue); + snprintf(modelname, + sizeofArray(modelname), + "rfsimu_channel_%s%d", + (bridge->role == SIMU_ROLE_SERVER) ? "ue" : "enB", + bridge->nb_cnx); ptr->channel_model = find_channel_desc_fromname(modelname); // path_loss in dB if (!ptr->channel_model) { // Use legacy method to find channel model - this will use the same channel model for all clients @@ -246,7 +246,7 @@ static void removeCirBuf(rfsimulator_state_t *bridge, buffer_t *buf) // free(bridge->buf[sock].channel_model); memset(buf, 0, sizeof(buffer_t)); buf->conn_sock = -1; - nb_ue--; + bridge->nb_cnx--; } static void socketError(rfsimulator_state_t *bridge, buffer_t *buf) @@ -286,8 +286,6 @@ static int setblocking(int sock, enum blocking_t active) return 0; } -static bool flushInput(rfsimulator_state_t *t, int timeout, int nsamps); - static void fullwrite(int fd, void *_buf, ssize_t count, rfsimulator_state_t *t) { if (t->saveIQfile != -1) { if (write(t->saveIQfile, _buf, count) != count ) @@ -450,25 +448,6 @@ static void getset_currentchannels_type(char *buf, int debug, webdatadef_t *tdat get_currentchannels_type("modify type", debug, tdata, prnt); } } -/*getset_currentchannels_type */ - -//static void print_cirBuf(struct complex16 *circularBuf, -// uint64_t firstSample, -// uint32_t cirSize, -// int neg, -// int pos, -// int nbTx) -//{ -// for (int i = -neg; i < pos ; ++i) { -// for (int txAnt = 0; txAnt < nbTx; txAnt++) { -// const int idx = ((firstSample + i) * nbTx + txAnt + cirSize) % cirSize; -// if (i == 0) -// printf("->"); -// printf("%08x%08x\n", circularBuf[idx].r, circularBuf[idx].i); -// } -// } -// printf("\n"); -//} static int rfsimu_setdistance_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg) { @@ -681,10 +660,14 @@ static int startClient(openair0_device *device) return allocCirBuf(t, sock) == NULL ? -1 : 0; } -static int rfsimulator_write_internal(rfsimulator_state_t *t, openair0_timestamp timestamp, void **samplesVoid, int nsamps, int nbAnt, int flags, bool alreadyLocked) { - if (!alreadyLocked) - pthread_mutex_lock(&Sockmutex); - +static int rfsimulator_write_internal(rfsimulator_state_t *t, + openair0_timestamp timestamp, + void **samplesVoid, + int nsamps, + int nbAnt, + int flags) +{ + mutexlock(t->Sockmutex); LOG_D(HW, "Sending %d samples at time: %ld, nbAnt %d\n", nsamps, timestamp, nbAnt); for (int i = 0; i < MAX_FD_RFSIMU; i++) { @@ -692,24 +675,17 @@ static int rfsimulator_write_internal(rfsimulator_state_t *t, openair0_timestamp if (b->conn_sock >= 0 ) { samplesBlockHeader_t header = {nsamps, nbAnt, timestamp}; - fullwrite(b->conn_sock,&header, sizeof(header), t); - sample_t tmpSamples[nsamps][nbAnt]; - + fullwrite(b->conn_sock, &header, sizeof(header), t); if (nbAnt == 1) { - if (b->conn_sock >= 0) { - fullwrite(b->conn_sock, samplesVoid[0], sampleToByte(nsamps, nbAnt), t); - } + fullwrite(b->conn_sock, samplesVoid[0], sampleToByte(nsamps, nbAnt), t); } else { + sample_t tmpSamples[nsamps][nbAnt]; for (int a = 0; a < nbAnt; a++) { sample_t *in = (sample_t *)samplesVoid[a]; - for (int s = 0; s < nsamps; s++) tmpSamples[s][a] = in[s]; } - - if (b->conn_sock >= 0) { - fullwrite(b->conn_sock, (void *)tmpSamples, sampleToByte(nsamps, nbAnt), t); - } + fullwrite(b->conn_sock, (void *)tmpSamples, sampleToByte(nsamps, nbAnt), t); } } } @@ -727,10 +703,8 @@ static int rfsimulator_write_internal(rfsimulator_state_t *t, openair0_timestamp timestamp, timestamp - t->lastWroteTS); - t->lastWroteTS=timestamp+nsamps; - - if (!alreadyLocked) - pthread_mutex_unlock(&Sockmutex); + t->lastWroteTS = timestamp + nsamps; + mutexunlock(t->Sockmutex); LOG_D(HW, "Sent %d samples at time: %ld->%ld, energy in first antenna: %d\n", @@ -743,8 +717,7 @@ static int rfsimulator_write_internal(rfsimulator_state_t *t, openair0_timestamp static int rfsimulator_write(openair0_device *device, openair0_timestamp timestamp, void **samplesVoid, int nsamps, int nbAnt, int flags) { timestamp -= device->openair0_cfg->command_line_sample_advance; - return rfsimulator_write_internal(device->priv, timestamp, samplesVoid, nsamps, nbAnt, flags, false); // false = with lock - // return rfsimulator_write_internal(device->priv, timestamp, samplesVoid, nsamps, nbAnt, flags, true); + return rfsimulator_write_internal(device->priv, timestamp, samplesVoid, nsamps, nbAnt, flags); } static bool flushInput(rfsimulator_state_t *t, int timeout, int nsamps_for_initial) { @@ -766,8 +739,9 @@ static bool flushInput(rfsimulator_state_t *t, int timeout, int nsamps_for_initi buffer_t *b = events[nbEv].data.ptr; if (events[nbEv].events & EPOLLIN && b == NULL) { - int conn_sock; - conn_sock = accept(t->listen_sock, NULL, NULL); + struct sockaddr_storage sa = {0}; + socklen_t socklen = sizeof(sa); + int conn_sock = accept(t->listen_sock, (struct sockaddr *)&sa, &socklen); if (conn_sock == -1) { LOG_E(HW, "accept() failed, errno(%d)\n", errno); return false; @@ -775,22 +749,28 @@ static bool flushInput(rfsimulator_state_t *t, int timeout, int nsamps_for_initi if (setblocking(conn_sock, notBlocking)) { return false; } + mutexlock(t->Sockmutex); buffer_t *new_buf = allocCirBuf(t, conn_sock); if (new_buf == NULL) { + mutexunlock(t->Sockmutex); return false; } - LOG_I(HW, "A client connects, sending the current time\n"); - c16_t v= {0}; - nb_ue++; + t->nb_cnx++; + char ip[INET6_ADDRSTRLEN]; + getnameinfo((struct sockaddr*)&sa, socklen, ip, sizeof(ip), NULL, 0, NI_NUMERICHOST); + uint16_t port = ((struct sockaddr_in *)&sa)->sin_port; + LOG_I(HW, "Client connects from %s:%d\n", ip, port); + c16_t v = {0}; void *samplesVoid[t->tx_num_channels]; - for ( int i=0; i < t->tx_num_channels; i++) - samplesVoid[i]=(void *)&v; - - rfsimulator_write_internal(t, t->lastWroteTS > 1 ? t->lastWroteTS - 1 : 0, samplesVoid, 1, t->tx_num_channels, 1, false); + samplesVoid[i] = (void *)&v; + samplesBlockHeader_t header = {1, t->tx_num_channels, t->lastWroteTS}; + fullwrite(conn_sock, &header, sizeof(header), t); + fullwrite(conn_sock, samplesVoid, sampleToByte(1, t->tx_num_channels), t); if (new_buf->channel_model) new_buf->channel_model->start_TS = t->lastWroteTS; + mutexunlock(t->Sockmutex); } else { if ( events[nbEv].events & (EPOLLHUP | EPOLLERR | EPOLLRDHUP) ) { socketError(t, b); @@ -867,12 +847,11 @@ static bool flushInput(rfsimulator_state_t *t, int timeout, int nsamps_for_initi b->trashingPacket=true; } - pthread_mutex_lock(&Sockmutex); - + mutexlock(t->Sockmutex); if (t->lastWroteTS != 0 && (fabs((double)t->lastWroteTS-b->lastReceivedTS) > (double)CirSize)) LOG_W(HW, "UEsock(%d) Tx/Rx shift too large Tx:%lu, Rx:%lu\n", b->conn_sock, t->lastWroteTS, b->lastReceivedTS); + mutexunlock(t->Sockmutex); - pthread_mutex_unlock(&Sockmutex); b->transferPtr=(char *)&b->circularBuf[(b->lastReceivedTS*b->th.nbAnt)%CirSize]; b->remainToTransfer=sampleToByte(b->th.size, b->th.nbAnt); } @@ -929,7 +908,6 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest } } else { bool have_to_wait; - int loops = 0; do { have_to_wait=false; @@ -951,16 +929,9 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest t->nextRxTstamp + nsamps); flushInput(t, 3, nsamps); } - if (t->hanging_workaround && loops++ > 10 && t->role == SIMU_ROLE_SERVER && nb_ue > 1) { - // Just start producing samples. The clients will catch up. - have_to_wait = false; - LOG_W(HW, - "No longer waiting for clients to catch up, starting to produce samples\n"); - } } while (have_to_wait); } - struct timespec start_time; int ret = clock_gettime(CLOCK_REALTIME, &start_time); AssertFatal(ret == 0, "clock_gettime() failed: errno %d, %s\n", errno, strerror(errno)); @@ -1005,7 +976,7 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest int nbAnt_tx = ptr->th.nbAnt; // number of Tx antennas int firstIndex = (CirSize + t->nextRxTstamp - t->chan_offset) % CirSize; sample_t *out = (sample_t *)samplesVoid[a]; - if ((nbAnt_tx == 1) && ((nb_ue == 1) || (t->role == SIMU_ROLE_CLIENT))) { // optimized for 1 Tx and 1 UE + if ((nbAnt_tx == 1) && ((t->nb_cnx == 1) || (t->role == SIMU_ROLE_CLIENT))) { // optimized for 1 Tx and 1 UE sample_t *firstSample = (sample_t *)&(ptr->circularBuf[firstIndex]); if (firstIndex + nsamps > CirSize) { int tailSz = CirSize - firstIndex; @@ -1137,7 +1108,7 @@ int device_init(openair0_device *device, openair0_config_t *openair0_cfg) { rfsimulator->prop_delay_ms = rfsimulator->chan_offset * 1000 / rfsimulator->sample_rate; LOG_I(HW, "propagation delay %f ms, %lu samples\n", rfsimulator->prop_delay_ms, rfsimulator->chan_offset); } - pthread_mutex_init(&Sockmutex, NULL); + mutexinit(rfsimulator->Sockmutex); LOG_I(HW, "Running as %s\n", rfsimulator->role == SIMU_ROLE_SERVER ? "server waiting opposite rfsimulators to connect" From b9c066329ed072e170faea6ff61a01b1e57b9e71 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Fri, 30 May 2025 12:20:30 +0200 Subject: [PATCH 2/7] trace in 4G RAR processing to debug ci result --- openair1/PHY/LTE_UE_TRANSPORT/rar_tools_ue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair1/PHY/LTE_UE_TRANSPORT/rar_tools_ue.c b/openair1/PHY/LTE_UE_TRANSPORT/rar_tools_ue.c index ad513a424e..778b9ecfa0 100644 --- a/openair1/PHY/LTE_UE_TRANSPORT/rar_tools_ue.c +++ b/openair1/PHY/LTE_UE_TRANSPORT/rar_tools_ue.c @@ -105,7 +105,7 @@ int generate_ue_ulsch_params_from_rar(PHY_VARS_UE *ue, cqireq=rar[3]&1; if (rballoc>RIV_max) { - LOG_D(PHY,"rar_tools.c: ERROR: rb_alloc (%x) > RIV_max\n",rballoc); + LOG_W(PHY,"rar_tools.c: ERROR: rb_alloc (%x) > RIV_max\n",rballoc); return(-1); } From 68857114a68d1fb48e85f999c33bd9085868ec25 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Mon, 16 Jun 2025 10:36:44 +0200 Subject: [PATCH 3/7] split rfsimulator too long function in smaller ones. Clean code for bad "UE" instead of rfsim client/server. Simplify logic --- radio/rfsimulator/simulator.c | 574 +++++++++++++++++----------------- 1 file changed, 293 insertions(+), 281 deletions(-) diff --git a/radio/rfsimulator/simulator.c b/radio/rfsimulator/simulator.c index 1cfc7dd7cc..1b40b66aa7 100644 --- a/radio/rfsimulator/simulator.c +++ b/radio/rfsimulator/simulator.c @@ -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 + */ /* * Open issues and limitations @@ -51,7 +50,7 @@ #include #include "rfsimulator.h" -#define PORT 4043 //default TCP port for this simulator +#define PORT 4043 // default TCP port for this simulator // // CirSize defines the number of samples inquired for a read cycle // It is bounded by a slot read capability (which depends on bandwidth and numerology) @@ -65,8 +64,8 @@ // The previous value is kept below in comment it was computed for 100ms 1x 20MHz // #define CirSize 6144000 // 100ms SiSo 20MHz LTE #define minCirSize 460800 // 10ms SiSo 40Mhz 3/4 sampling NR78 FR1 -#define sampleToByte(a,b) ((a)*(b)*sizeof(sample_t)) -#define byteToSample(a,b) ((a)/(sizeof(sample_t)*(b))) +#define sampleToByte(a, b) ((a) * (b) * sizeof(sample_t)) +#define byteToSample(a, b) ((a) / (sizeof(sample_t) * (b))) #define GENERATE_CHANNEL 10 // each frame (or slot?) in DL #define MAX_FD_RFSIMU 250 @@ -77,7 +76,7 @@ typedef enum { SIMU_ROLE_SERVER = 1, SIMU_ROLE_CLIENT } simuRole; // -#define RFSIMU_SECTION "rfsimulator" +#define RFSIMU_SECTION "rfsimulator" #define RFSIMU_OPTIONS_PARAMNAME "options" #define RFSIM_CONFIG_HELP_OPTIONS \ @@ -85,12 +84,12 @@ typedef enum { SIMU_ROLE_SERVER = 1, SIMU_ROLE_CLIENT } simuRole; " chanmod: enable channel modelisation\n" \ " saviq: enable saving written iqs to a file\n" +// clang-format off /*-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ -/* configuration parameters for the rfsimulator device */ +/* configuration parameters for the rfsimulator device */ /* optname helpstr paramflags XXXptr defXXXval type numelt */ /*-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ -#define simOpt PARAMFLAG_NOFREE|PARAMFLAG_CMDLINE_NOPREFIXENABLED -// clang-format off +#define simOpt PARAMFLAG_NOFREE | PARAMFLAG_CMDLINE_NOPREFIXENABLED #define RFSIMULATOR_PARAMS_DESC { \ {"serveraddr", "\n", simOpt, .strptr=&rfsimulator->ip, .defstrval="127.0.0.1", TYPE_STRING, 0 },\ {"serverport", "\n", simOpt, .u16ptr = &(rfsimulator->port), .defuintval = PORT, TYPE_UINT16, 0}, \ @@ -110,6 +109,7 @@ static int rfsimu_setchanmod_cmd(char *buff, int debug, telnet_printfunc_t prnt, static int rfsimu_setdistance_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg); static int rfsimu_getdistance_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg); static int rfsimu_vtime_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg); +// clang-format off static telnetshell_cmddef_t rfsimu_cmdarray[] = { {"show models", "", (cmdfunc_t)rfsimu_setchanmod_cmd, {(webfunc_t)getset_currentchannels_type}, TELNETSRV_CMDFLAG_WEBSRVONLY | TELNETSRV_CMDFLAG_GETWEBTBLDATA, NULL}, {"setmodel", " ", (cmdfunc_t)rfsimu_setchanmod_cmd, {NULL}, TELNETSRV_CMDFLAG_PUSHINTPOOLQ | TELNETSRV_CMDFLAG_TELNETONLY, NULL}, @@ -118,6 +118,7 @@ static telnetshell_cmddef_t rfsimu_cmdarray[] = { {"vtime", "", (cmdfunc_t)rfsimu_vtime_cmd, {NULL}, TELNETSRV_CMDFLAG_PUSHINTPOOLQ | TELNETSRV_CMDFLAG_AUTOUPDATE}, {"", "", NULL}, }; +// clang-format on static telnetshell_cmddef_t *setmodel_cmddef = &(rfsimu_cmdarray[1]); static telnetshell_vardef_t rfsimu_vardef[] = {{"", 0, 0, NULL}}; @@ -167,25 +168,25 @@ typedef struct { static buffer_t *allocCirBuf(rfsimulator_state_t *bridge, int sock) { uint64_t buff_index = bridge->next_buf++ % MAX_FD_RFSIMU; - buffer_t *ptr=&bridge->buf[buff_index]; + buffer_t *ptr = &bridge->buf[buff_index]; ptr->circularBuf = calloc(1, sampleToByte(CirSize, 1)); if (ptr->circularBuf == NULL) { LOG_E(HW, "malloc(%lu) failed\n", sampleToByte(CirSize, 1)); return NULL; } - ptr->circularBufEnd=((char *)ptr->circularBuf)+sampleToByte(CirSize,1); - ptr->conn_sock=sock; - ptr->lastReceivedTS=0; - ptr->headerMode=true; - ptr->trashingPacket=false; - ptr->transferPtr=(char *)&ptr->th; - ptr->remainToTransfer=sizeof(samplesBlockHeader_t); + ptr->circularBufEnd = ((char *)ptr->circularBuf) + sampleToByte(CirSize, 1); + ptr->conn_sock = sock; + ptr->lastReceivedTS = 0; + ptr->headerMode = true; + ptr->trashingPacket = false; + ptr->transferPtr = (char *)&ptr->th; + ptr->remainToTransfer = sizeof(samplesBlockHeader_t); int sendbuff = SEND_BUFF_SIZE; if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sendbuff, sizeof(sendbuff)) != 0) { LOG_E(HW, "setsockopt(SO_SNDBUF) failed\n"); return NULL; } - struct epoll_event ev= {0}; + struct epoll_event ev = {0}; ev.events = EPOLLIN | EPOLLRDHUP; ev.data.ptr = ptr; if (epoll_ctl(bridge->epollfd, EPOLL_CTL_ADD, sock, &ev) != 0) { @@ -193,21 +194,21 @@ static buffer_t *allocCirBuf(rfsimulator_state_t *bridge, int sock) return NULL; } - if ( bridge->channelmod > 0) { + if (bridge->channelmod > 0) { // create channel simulation model for this mode reception - static bool init_done=false; + static bool init_done = false; if (!init_done) { uint64_t rand; - FILE *h=fopen("/dev/random","r"); + FILE *h = fopen("/dev/random", "r"); - if ( 1 != fread(&rand,sizeof(rand),1,h) ) + if (1 != fread(&rand, sizeof(rand), 1, h)) LOG_W(HW, "Can't read /dev/random\n"); fclose(h); randominit(rand); tableNor(rand); - init_done=true; + init_done = true; } char modelname[30]; snprintf(modelname, @@ -228,7 +229,7 @@ static buffer_t *allocCirBuf(rfsimulator_state_t *bridge, int sock) set_channeldesc_owner(ptr->channel_model, RFSIMU_MODULEID); set_channeldesc_direction(ptr->channel_model, bridge->role == SIMU_ROLE_SERVER); - random_channel(ptr->channel_model,false); + random_channel(ptr->channel_model, false); LOG_I(HW, "Random channel %s in rfsimulator activated\n", modelname); } return ptr; @@ -273,7 +274,7 @@ static int setblocking(int sock, enum blocking_t active) return -1; } - if (active==blocking) + if (active == blocking) opts = opts & ~O_NONBLOCK; else opts = opts | O_NONBLOCK; @@ -288,7 +289,7 @@ static int setblocking(int sock, enum blocking_t active) static void fullwrite(int fd, void *_buf, ssize_t count, rfsimulator_state_t *t) { if (t->saveIQfile != -1) { - if (write(t->saveIQfile, _buf, count) != count ) + if (write(t->saveIQfile, _buf, count) != count) LOG_E(HW, "write() in save iq file failed (%d)\n", errno); } @@ -299,12 +300,12 @@ static void fullwrite(int fd, void *_buf, ssize_t count, rfsimulator_state_t *t) l = write(fd, buf, count); if (l == 0) { - LOG_E(HW, "write() failed, returned 0\n"); - return; + LOG_E(HW, "write() failed, returned 0\n"); + return; } if (l < 0) { - if (errno==EINTR) + if (errno == EINTR) continue; if (errno == EAGAIN) { @@ -323,8 +324,8 @@ static void fullwrite(int fd, void *_buf, ssize_t count, rfsimulator_state_t *t) } static void rfsimulator_readconfig(rfsimulator_state_t *rfsimulator) { - char *saveF=NULL; - char *modelname=NULL; + char *saveF = NULL; + char *modelname = NULL; paramdef_t rfsimu_params[] = RFSIMULATOR_PARAMS_DESC; int p = config_paramidx_fromname(rfsimu_params, sizeofArray(rfsimu_params), RFSIMU_OPTIONS_PARAMNAME); int ret = config_get(config_get_if(), rfsimu_params, sizeofArray(rfsimu_params), RFSIMU_SECTION); @@ -332,11 +333,11 @@ static void rfsimulator_readconfig(rfsimulator_state_t *rfsimulator) { rfsimulator->saveIQfile = -1; - for(int i=0; isaveIQfile=open(saveF,O_APPEND| O_CREAT|O_TRUNC | O_WRONLY, 0666); + for (int i = 0; i < rfsimu_params[p].numelt; i++) { + if (strcmp(rfsimu_params[p].strlistptr[i], "saviq") == 0) { + rfsimulator->saveIQfile = open(saveF, O_APPEND | O_CREAT | O_TRUNC | O_WRONLY, 0666); - if ( rfsimulator->saveIQfile != -1 ) + if (rfsimulator->saveIQfile != -1) LOG_I(HW, "Will save written IQ samples in %s\n", saveF); else { LOG_E(HW, "open(%s) failed for IQ saving, errno(%d)\n", saveF, errno); @@ -344,10 +345,10 @@ static void rfsimulator_readconfig(rfsimulator_state_t *rfsimulator) { } break; - } else if (strcmp(rfsimu_params[p].strlistptr[i],"chanmod") == 0) { + } else if (strcmp(rfsimu_params[p].strlistptr[i], "chanmod") == 0) { init_channelmod(); load_channellist(rfsimulator->tx_num_channels, rfsimulator->rx_num_channels, rfsimulator->sample_rate, rfsimulator->rx_freq, rfsimulator->tx_bw); - rfsimulator->channelmod=true; + rfsimulator->channelmod = true; } else { fprintf(stderr, "unknown rfsimulator option: %s\n", rfsimu_params[p].strlistptr[i]); exit(-1); @@ -355,8 +356,8 @@ static void rfsimulator_readconfig(rfsimulator_state_t *rfsimulator) { } /* for compatibility keep environment variable usage */ - if ( getenv("RFSIMULATOR") != NULL ) { - rfsimulator->ip=getenv("RFSIMULATOR"); + if (getenv("RFSIMULATOR") != NULL) { + rfsimulator->ip = getenv("RFSIMULATOR"); LOG_W(HW, "The RFSIMULATOR environment variable is deprecated and support will be removed in the future. Instead, add parameter --rfsimulator.serveraddr %s to set the server address. Note: the default is \"server\"; for the gNB/eNB, you don't have to set any configuration.\n", rfsimulator->ip); LOG_I(HW, "Remove RFSIMULATOR environment variable to get rid of this message and the sleep.\n"); sleep(10); @@ -370,8 +371,8 @@ static void rfsimulator_readconfig(rfsimulator_state_t *rfsimulator) { } static int rfsimu_setchanmod_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg) { - char *modelname=NULL; - char *modeltype=NULL; + char *modelname = NULL; + char *modeltype = NULL; rfsimulator_state_t *t = (rfsimulator_state_t *)arg; if (t->channelmod == false) { prnt("%s: ERROR channel modelisation disabled...\n", __func__); @@ -383,23 +384,23 @@ static int rfsimu_setchanmod_cmd(char *buff, int debug, telnet_printfunc_t prnt, } if (debug) prnt("%s: rfsimu_setchanmod_cmd buffer \"%s\"\n", __func__, buff); - int s = sscanf(buff,"%m[^ ] %ms\n",&modelname, &modeltype); + int s = sscanf(buff, "%m[^ ] %ms\n", &modelname, &modeltype); if (s == 2) { - int channelmod=modelid_fromstrtype(modeltype); + int channelmod = modelid_fromstrtype(modeltype); - if (channelmod<0) + if (channelmod < 0) prnt("%s: ERROR: model type %s unknown\n", __func__, modeltype); else { rfsimulator_state_t *t = (rfsimulator_state_t *)arg; - int found=0; + int found = 0; for (int i = 0; i < MAX_FD_RFSIMU; i++) { - buffer_t *b=&t->buf[i]; - if ( b->channel_model==NULL) + buffer_t *b = &t->buf[i]; + if (b->channel_model == NULL) continue; - if (b->channel_model->model_name==NULL) + if (b->channel_model->model_name == NULL) continue; - if (b->conn_sock >= 0 && (strcmp(b->channel_model->model_name,modelname)==0)) { + if (b->conn_sock >= 0 && (strcmp(b->channel_model->model_name, modelname) == 0)) { channel_desc_t *newmodel = new_channel_desc_scm(t->tx_num_channels, t->rx_num_channels, channelmod, @@ -415,17 +416,17 @@ static int rfsimu_setchanmod_cmd(char *buff, int debug, telnet_printfunc_t prnt, 0); // noise_power set_channeldesc_owner(newmodel, RFSIMU_MODULEID); set_channeldesc_direction(newmodel, t->role == SIMU_ROLE_SERVER); - set_channeldesc_name(newmodel,modelname); - random_channel(newmodel,false); - channel_desc_t *oldmodel=b->channel_model; - b->channel_model=newmodel; + set_channeldesc_name(newmodel, modelname); + random_channel(newmodel, false); + channel_desc_t *oldmodel = b->channel_model; + b->channel_model = newmodel; free_channel_desc_scm(oldmodel); prnt("%s: New model type %s applied to channel %s connected to sock %d\n", __func__, modeltype, modelname, i); - found=1; + found = 1; break; } } /* for */ - if (found==0) + if (found == 0) prnt("%s: Channel %s not found or not currently used\n", __func__, modelname); } } else { @@ -456,7 +457,7 @@ static int rfsimu_setdistance_cmd(char *buff, int debug, telnet_printfunc_t prnt char *modelname; int distance; - int s = sscanf(buff,"%m[^ ] %d\n", &modelname, &distance); + int s = sscanf(buff, "%m[^ ] %d\n", &modelname, &distance); if (s != 2) { prnt("%s: require exact two parameters\n", __func__); return CMDSTATUS_VARNOTFOUND; @@ -466,8 +467,8 @@ static int rfsimu_setdistance_cmd(char *buff, int debug, telnet_printfunc_t prnt const double sample_rate = t->sample_rate; const double c = 299792458; /* 3e8 */ - const uint64_t new_offset = (double) distance * sample_rate / c; - const double new_distance = (double) new_offset * c / sample_rate; + const uint64_t new_offset = (double)distance * sample_rate / c; + const double new_distance = (double)new_offset * c / sample_rate; const double new_delay_ms = new_offset * 1000.0 / sample_rate; prnt("\n%s: new_offset %lu, new (exact) distance %.3f m, new delay %f ms\n", __func__, new_offset, new_distance, new_delay_ms); @@ -476,7 +477,7 @@ static int rfsimu_setdistance_cmd(char *buff, int debug, telnet_printfunc_t prnt /* Set distance in rfsim and channel model, update channel and ringbuffer */ for (int i = 0; i < MAX_FD_RFSIMU; i++) { - buffer_t *b=&t->buf[i]; + buffer_t *b = &t->buf[i]; if (b->conn_sock <= 0 || b->channel_model == NULL || b->channel_model->model_name == NULL || strcmp(b->channel_model->model_name, modelname) != 0) { if (b->channel_model != NULL && b->channel_model->model_name != NULL) prnt(" %s: model %s unmodified\n", __func__, b->channel_model->model_name); @@ -502,13 +503,13 @@ static int rfsimu_getdistance_cmd(char *buff, int debug, telnet_printfunc_t prnt const double c = 299792458; /* 3e8 */ for (int i = 0; i < MAX_FD_RFSIMU; i++) { - buffer_t *b=&t->buf[i]; + buffer_t *b = &t->buf[i]; if (b->conn_sock <= 0 || b->channel_model == NULL || b->channel_model->model_name == NULL) continue; channel_desc_t *cd = b->channel_model; const uint64_t offset = cd->channel_offset; - const double distance = (double) offset * c / sample_rate; + const double distance = (double)offset * c / sample_rate; prnt("%s: %s offset %lu distance %.3f m\n", __func__, cd->model_name, offset, distance); } prnt("%s: offset %lu delay %f ms\n", __func__, t->chan_offset, t->prop_delay_ms); @@ -529,7 +530,7 @@ static int startServer(openair0_device *device) { int sock = -1; struct addrinfo *results = NULL; - struct addrinfo *rp= NULL; + struct addrinfo *rp = NULL; rfsimulator_state_t *t = (rfsimulator_state_t *)device->priv; t->role = SIMU_ROLE_SERVER; @@ -538,9 +539,9 @@ static int startServer(openair0_device *device) snprintf(port, sizeof(port), "%d", t->port); struct addrinfo hints = { - .ai_family = AF_INET6, - .ai_socktype = SOCK_STREAM, - .ai_flags = AI_PASSIVE, + .ai_family = AF_INET6, + .ai_socktype = SOCK_STREAM, + .ai_flags = AI_PASSIVE, }; int s = getaddrinfo(NULL, port, &hints, &results); @@ -607,8 +608,8 @@ static int client_try_connect(const char *host, uint16_t port) snprintf(dport, sizeof(dport), "%d", port); struct addrinfo hints = { - .ai_family = AF_UNSPEC, - .ai_socktype = SOCK_STREAM, + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_STREAM, }; s = getaddrinfo(host, dport, &hints, &result); @@ -671,9 +672,9 @@ static int rfsimulator_write_internal(rfsimulator_state_t *t, LOG_D(HW, "Sending %d samples at time: %ld, nbAnt %d\n", nsamps, timestamp, nbAnt); for (int i = 0; i < MAX_FD_RFSIMU; i++) { - buffer_t *b=&t->buf[i]; + buffer_t *b = &t->buf[i]; - if (b->conn_sock >= 0 ) { + if (b->conn_sock >= 0) { samplesBlockHeader_t header = {nsamps, nbAnt, timestamp}; fullwrite(b->conn_sock, &header, sizeof(header), t); if (nbAnt == 1) { @@ -690,7 +691,7 @@ static int rfsimulator_write_internal(rfsimulator_state_t *t, } } - if ( t->lastWroteTS != 0 && fabs((double)t->lastWroteTS-timestamp) > (double)CirSize) + if (t->lastWroteTS != 0 && fabs((double)t->lastWroteTS - timestamp) > (double)CirSize) LOG_W(HW, "Discontinuous TX gap too large Tx:%lu, %lu\n", t->lastWroteTS, timestamp); if (t->lastWroteTS > timestamp) @@ -715,211 +716,223 @@ static int rfsimulator_write_internal(rfsimulator_state_t *t, return nsamps; } -static int rfsimulator_write(openair0_device *device, openair0_timestamp timestamp, void **samplesVoid, int nsamps, int nbAnt, int flags) { +static int rfsimulator_write(openair0_device *device, + openair0_timestamp timestamp, + void **samplesVoid, + int nsamps, + int nbAnt, + int flags) +{ timestamp -= device->openair0_cfg->command_line_sample_advance; return rfsimulator_write_internal(device->priv, timestamp, samplesVoid, nsamps, nbAnt, flags); } -static bool flushInput(rfsimulator_state_t *t, int timeout, int nsamps_for_initial) { +static bool add_client(rfsimulator_state_t *t) +{ + struct sockaddr_storage sa = {0}; + socklen_t socklen = sizeof(sa); + int conn_sock = accept(t->listen_sock, (struct sockaddr *)&sa, &socklen); + if (conn_sock == -1) { + LOG_E(HW, "accept() failed, errno(%d)\n", errno); + return false; + } + if (setblocking(conn_sock, notBlocking)) { + return false; + } + mutexlock(t->Sockmutex); + buffer_t *new_buf = allocCirBuf(t, conn_sock); + if (new_buf == NULL) { + mutexunlock(t->Sockmutex); + return false; + } + t->nb_cnx++; + new_buf->lastReceivedTS = t->lastWroteTS; + char ip[INET6_ADDRSTRLEN]; + getnameinfo((struct sockaddr *)&sa, socklen, ip, sizeof(ip), NULL, 0, NI_NUMERICHOST); + uint16_t port = ((struct sockaddr_in *)&sa)->sin_port; + LOG_I(HW, "Client connects from %s:%d\n", ip, port); + c16_t v = {0}; + void *samplesVoid[t->tx_num_channels]; + for (int i = 0; i < t->tx_num_channels; i++) + samplesVoid[i] = (void *)&v; + samplesBlockHeader_t header = {1, t->tx_num_channels, t->lastWroteTS}; + fullwrite(conn_sock, &header, sizeof(header), t); + fullwrite(conn_sock, samplesVoid, sampleToByte(1, t->tx_num_channels), t); + + if (new_buf->channel_model) + new_buf->channel_model->start_TS = t->lastWroteTS; + mutexunlock(t->Sockmutex); + return true; +} + +static void process_recv(rfsimulator_state_t *t, buffer_t *b, int size_for_first_pack) +{ + if (b->transferPtr == b->circularBufEnd) + b->transferPtr = (char *)b->circularBuf; + + // check the header and start block transfer + if (b->headerMode == true) { + if (b->remainToTransfer == 0) { + b->headerMode = false; // We got the header + if (t->nextRxTstamp == 0) { // First block in a client, resync with the server current TS + t->nextRxTstamp = b->th.timestamp; + // we need to bootstrap from one side, else each side will wait forever + b->lastReceivedTS = b->th.timestamp + size_for_first_pack; + LOG_D(HW, "Client got first timestamp: starting at %lu\n", t->nextRxTstamp); + b->trashingPacket = true; + if (b->channel_model) + b->channel_model->start_TS = t->nextRxTstamp; + } else if (b->lastReceivedTS < b->th.timestamp) { + // We have a transmission hole to fill, like TDD + // we create no signal samples up to the beginning of this reception + int nbAnt = b->th.nbAnt; + if (b->th.timestamp - b->lastReceivedTS < CirSize) { + for (uint64_t index = b->lastReceivedTS; index < b->th.timestamp; index++) { + for (int a = 0; a < nbAnt; a++) { + b->circularBuf[(index * nbAnt + a) % CirSize].r = 0; + b->circularBuf[(index * nbAnt + a) % CirSize].i = 0; + } + } + } else { + memset(b->circularBuf, 0, sampleToByte(CirSize, 1)); + } + b->lastReceivedTS = b->th.timestamp; + } else if (b->lastReceivedTS > b->th.timestamp) { + if (b->th.size == 1) + LOG_W(HW, "Received Rx/Tx synchro out of order\n"); + else + LOG_W(HW, "Received data in past: current is %lu, new reception: %lu!\n", b->lastReceivedTS, b->th.timestamp); + b->trashingPacket = true; + } + // normal case, we have the header + // verify the time gap is not too large + mutexlock(t->Sockmutex); + if (t->lastWroteTS != 0 && (fabs((double)t->lastWroteTS - b->lastReceivedTS) > (double)CirSize)) + LOG_W(HW, "UEsock(%d) Tx/Rx shift too large Tx:%lu, Rx:%lu\n", b->conn_sock, t->lastWroteTS, b->lastReceivedTS); + mutexunlock(t->Sockmutex); + + // move back the pointer to overwrite the header, we don't need it anymore + b->transferPtr = (char *)&b->circularBuf[(b->lastReceivedTS * b->th.nbAnt) % CirSize]; + // we now need to read the samples + b->remainToTransfer = sampleToByte(b->th.size, b->th.nbAnt); + } + return; + } + + if (b->headerMode == false) { + if (!b->trashingPacket) { + b->lastReceivedTS = b->th.timestamp + b->th.size - byteToSample(b->remainToTransfer, b->th.nbAnt); + LOG_D(HW, "UEsock: %d Set b->lastReceivedTS %ld\n", b->conn_sock, b->lastReceivedTS); + } + if (b->remainToTransfer == 0) { + LOG_D(HW, "UEsock: %d Completed block reception: %ld\n", b->conn_sock, b->lastReceivedTS); + b->headerMode = true; + b->transferPtr = (char *)&b->th; + b->remainToTransfer = sizeof(samplesBlockHeader_t); + b->trashingPacket = false; + } + } +} + +static bool flushInput(rfsimulator_state_t *t, int timeout, int first_time_sz) +{ // Process all incoming events on sockets // store the data in lists struct epoll_event events[MAX_FD_RFSIMU] = {{0}}; int nfds = epoll_wait(t->epollfd, events, MAX_FD_RFSIMU, timeout); - if ( nfds==-1 ) { - if ( errno==EINTR || errno==EAGAIN ) { - return false; - } else { + if (nfds == -1) { + if (!(errno == EINTR || errno == EAGAIN)) LOG_W(HW, "epoll_wait() failed, errno(%d)\n", errno); - return false; - } + return false; } for (int nbEv = 0; nbEv < nfds; ++nbEv) { buffer_t *b = events[nbEv].data.ptr; if (events[nbEv].events & EPOLLIN && b == NULL) { - struct sockaddr_storage sa = {0}; - socklen_t socklen = sizeof(sa); - int conn_sock = accept(t->listen_sock, (struct sockaddr *)&sa, &socklen); - if (conn_sock == -1) { - LOG_E(HW, "accept() failed, errno(%d)\n", errno); - return false; - } - if (setblocking(conn_sock, notBlocking)) { - return false; - } - mutexlock(t->Sockmutex); - buffer_t *new_buf = allocCirBuf(t, conn_sock); - if (new_buf == NULL) { - mutexunlock(t->Sockmutex); - return false; - } - t->nb_cnx++; - char ip[INET6_ADDRSTRLEN]; - getnameinfo((struct sockaddr*)&sa, socklen, ip, sizeof(ip), NULL, 0, NI_NUMERICHOST); - uint16_t port = ((struct sockaddr_in *)&sa)->sin_port; - LOG_I(HW, "Client connects from %s:%d\n", ip, port); - c16_t v = {0}; - void *samplesVoid[t->tx_num_channels]; - for ( int i=0; i < t->tx_num_channels; i++) - samplesVoid[i] = (void *)&v; - samplesBlockHeader_t header = {1, t->tx_num_channels, t->lastWroteTS}; - fullwrite(conn_sock, &header, sizeof(header), t); - fullwrite(conn_sock, samplesVoid, sampleToByte(1, t->tx_num_channels), t); - - if (new_buf->channel_model) - new_buf->channel_model->start_TS = t->lastWroteTS; - mutexunlock(t->Sockmutex); + bool ret = add_client(t); + if (!ret) + return ret; + continue; } else { - if ( events[nbEv].events & (EPOLLHUP | EPOLLERR | EPOLLRDHUP) ) { + if (events[nbEv].events & (EPOLLHUP | EPOLLERR | EPOLLRDHUP)) { socketError(t, b); continue; } - if ( b->circularBuf == NULL ) { - LOG_E(HW, "Received data on not connected socket %d\n", events[nbEv].data.fd); - continue; - } - - ssize_t blockSz; - - if ( b->headerMode) - blockSz=b->remainToTransfer; - else - blockSz= b->transferPtr + b->remainToTransfer <= b->circularBufEnd ? - b->remainToTransfer : - b->circularBufEnd - b->transferPtr ; - - ssize_t sz = recv(b->conn_sock, b->transferPtr, blockSz, MSG_DONTWAIT); - - if ( sz < 0 ) { - if ( errno != EAGAIN ) { - LOG_E(HW, "recv() failed, errno(%d)\n", errno); - //abort(); - } - } else if ( sz == 0 ) - continue; - - LOG_D(HW, "Socket rcv %zd bytes\n", sz); - b->remainToTransfer -= sz; - b->transferPtr+=sz; - - if (b->transferPtr==b->circularBufEnd ) - b->transferPtr=(char *)b->circularBuf; - - // check the header and start block transfer - if ( b->headerMode==true && b->remainToTransfer==0) { - b->headerMode = false; - - if (t->nextRxTstamp == 0) { // First block in UE, resync with the gNB current TS - t->nextRxTstamp=b->th.timestamp> nsamps_for_initial ? - b->th.timestamp - nsamps_for_initial : - 0; - b->lastReceivedTS=b->th.timestamp> nsamps_for_initial ? - b->th.timestamp : - nsamps_for_initial; - LOG_D(HW, "UE got first timestamp: starting at %lu\n", t->nextRxTstamp); - b->trashingPacket=true; - if (b->channel_model) - b->channel_model->start_TS = t->nextRxTstamp; - } else if (b->lastReceivedTS < b->th.timestamp) { - int nbAnt= b->th.nbAnt; - - if ( b->th.timestamp-b->lastReceivedTS < CirSize ) { - for (uint64_t index=b->lastReceivedTS; index < b->th.timestamp; index++ ) { - for (int a=0; a < nbAnt; a++) { - b->circularBuf[(index*nbAnt+a)%CirSize].r = 0; - b->circularBuf[(index*nbAnt+a)%CirSize].i = 0; - } - } - } else { - memset(b->circularBuf, 0, sampleToByte(CirSize,1)); - } - - b->lastReceivedTS=b->th.timestamp; - } else if (b->lastReceivedTS > b->th.timestamp && b->th.size == 1) { - LOG_W(HW, "Received Rx/Tx synchro out of order\n"); - b->trashingPacket=true; - } else if (b->lastReceivedTS == b->th.timestamp) { - // normal case - } else { - LOG_W(HW, "Received data in past: current is %lu, new reception: %lu!\n", b->lastReceivedTS, b->th.timestamp); - b->trashingPacket=true; - } - - mutexlock(t->Sockmutex); - if (t->lastWroteTS != 0 && (fabs((double)t->lastWroteTS-b->lastReceivedTS) > (double)CirSize)) - LOG_W(HW, "UEsock(%d) Tx/Rx shift too large Tx:%lu, Rx:%lu\n", b->conn_sock, t->lastWroteTS, b->lastReceivedTS); - mutexunlock(t->Sockmutex); - - b->transferPtr=(char *)&b->circularBuf[(b->lastReceivedTS*b->th.nbAnt)%CirSize]; - b->remainToTransfer=sampleToByte(b->th.size, b->th.nbAnt); - } - - if ( b->headerMode==false ) { - if ( ! b->trashingPacket ) { - b->lastReceivedTS=b->th.timestamp+b->th.size-byteToSample(b->remainToTransfer,b->th.nbAnt); - LOG_D(HW, "UEsock: %d Set b->lastReceivedTS %ld\n", b->conn_sock, b->lastReceivedTS); - } - - if ( b->remainToTransfer==0) { - LOG_D(HW, "UEsock: %d Completed block reception: %ld\n", b->conn_sock, b->lastReceivedTS); - b->headerMode=true; - b->transferPtr=(char *)&b->th; - b->remainToTransfer = sizeof(samplesBlockHeader_t); - b->trashingPacket=false; - } - } } - } - return nfds>0; + if (b->circularBuf == NULL) { + LOG_E(HW, "Received data on not connected socket %d\n", events[nbEv].data.fd); + continue; + } + + ssize_t blockSz; + if (b->headerMode) + blockSz = b->remainToTransfer; + else + blockSz = + b->transferPtr + b->remainToTransfer <= b->circularBufEnd ? b->remainToTransfer : b->circularBufEnd - b->transferPtr; + + ssize_t sz = recv(b->conn_sock, b->transferPtr, blockSz, MSG_DONTWAIT); + if (sz <= 0) { + if (sz < 0 && errno != EAGAIN) + LOG_E(HW, "recv() failed, errno(%d)\n", errno); + continue; + } + LOG_D(HW, "Socket rcv %zd bytes\n", sz); + b->remainToTransfer -= sz; + b->transferPtr += sz; + process_recv(t, b, first_time_sz); + } + return nfds > 0; } static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimestamp, void **samplesVoid, int nsamps, int nbAnt) { rfsimulator_state_t *t = device->priv; - LOG_D(HW, "Enter rfsimulator_read, expect %d samples, will release at TS: %ld, nbAnt %d\n", nsamps, t->nextRxTstamp+nsamps, nbAnt); + LOG_D(HW, + "Enter rfsimulator_read, expect %d samples, will release at TS: %ld, nbAnt %d\n", + nsamps, + t->nextRxTstamp + nsamps, + nbAnt); // deliver data from received data // check if a UE is connected int first_sock; for (first_sock = 0; first_sock < MAX_FD_RFSIMU; first_sock++) - if (t->buf[first_sock].circularBuf != NULL ) + if (t->buf[first_sock].circularBuf != NULL) break; if (first_sock == MAX_FD_RFSIMU) { // no connected device (we are eNB, no UE is connected) - if ( t->nextRxTstamp == 0) + if (t->nextRxTstamp == 0) LOG_I(HW, "No connected device, generating void samples...\n"); - if (!flushInput(t, t->wait_timeout, nsamps)) { - for (int x=0; x < nbAnt; x++) - memset(samplesVoid[x],0,sampleToByte(nsamps,1)); + if (!flushInput(t, t->wait_timeout, nsamps)) { + for (int x = 0; x < nbAnt; x++) + memset(samplesVoid[x], 0, sampleToByte(nsamps, 1)); - t->nextRxTstamp+=nsamps; + t->nextRxTstamp += nsamps; - if ( ((t->nextRxTstamp/nsamps)%100) == 0) + if (((t->nextRxTstamp / nsamps) % 100) == 0) LOG_D(HW, "No UE, Generating void samples for Rx: %ld\n", t->nextRxTstamp); - *ptimestamp = t->nextRxTstamp-nsamps; + *ptimestamp = t->nextRxTstamp - nsamps; return nsamps; } } else { bool have_to_wait; do { - have_to_wait=false; - + have_to_wait = false; buffer_t *b = NULL; for (int sock = 0; sock < MAX_FD_RFSIMU; sock++) { b = &t->buf[sock]; - - if ( b->circularBuf ) - if ( t->nextRxTstamp+nsamps > b->lastReceivedTS ) { - have_to_wait=true; + if (b->circularBuf && (t->nextRxTstamp + nsamps) > b->lastReceivedTS) { + have_to_wait = true; break; - } + } } if (have_to_wait) { @@ -937,28 +950,28 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest AssertFatal(ret == 0, "clock_gettime() failed: errno %d, %s\n", errno, strerror(errno)); // Clear the output buffer - for (int a=0; abuf[sock]; + buffer_t *ptr = &t->buf[sock]; - if ( ptr->circularBuf ) { - bool reGenerateChannel=false; + if (ptr->circularBuf) { + bool reGenerateChannel = false; - //fixme: when do we regenerate - // it seems legacy behavior is: never in UL, each frame in DL + // fixme: when do we regenerate + // it seems legacy behavior is: never in UL, each frame in DL if (reGenerateChannel) - random_channel(ptr->channel_model,0); + random_channel(ptr->channel_model, 0); if (t->poll_telnetcmdq) - t->poll_telnetcmdq(t->telnetcmd_qid,t); + t->poll_telnetcmdq(t->telnetcmd_qid, t); - for (int a=0; achannel_model != NULL ) { // apply a channel model + for (int a = 0; a < nbAnt; a++) { // loop over number of Rx antennas + if (ptr->channel_model != NULL) { // apply a channel model if (num_chanmod_channels == 0) { memset(temp_array, 0, sizeof(temp_array)); } @@ -971,12 +984,11 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest t->nextRxTstamp, CirSize, apply_noise_per_channel); - } - else { // no channel modeling + } else { // no channel modeling int nbAnt_tx = ptr->th.nbAnt; // number of Tx antennas int firstIndex = (CirSize + t->nextRxTstamp - t->chan_offset) % CirSize; sample_t *out = (sample_t *)samplesVoid[a]; - if ((nbAnt_tx == 1) && ((t->nb_cnx == 1) || (t->role == SIMU_ROLE_CLIENT))) { // optimized for 1 Tx and 1 UE + if (nbAnt_tx == 1 && t->nb_cnx == 1) { // optimized for 1 Tx and 1 UE sample_t *firstSample = (sample_t *)&(ptr->circularBuf[firstIndex]); if (firstIndex + nsamps > CirSize) { int tailSz = CirSize - firstIndex; @@ -1030,14 +1042,14 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest AssertFatal(ret == 0, "clock_gettime() failed: errno %d, %s\n", errno, strerror(errno)); double diff_ns = (end_time.tv_sec - start_time.tv_sec) * 1000000000 + (end_time.tv_nsec - start_time.tv_nsec); static double average = 0.0; - average = (average * 0.98) + ( nsamps / (diff_ns / 1e9) * 0.02); + average = (average * 0.98) + (nsamps / (diff_ns / 1e9) * 0.02); static int calls = 0; if (calls++ % 10000 == 0) { LOG_D(HW, "Rfsimulator: velocity %.2f Msps, realtime requirements %.2f Msps\n", average / 1e6, t->sample_rate / 1e6); } *ptimestamp = t->nextRxTstamp; // return the time of the first sample - t->nextRxTstamp+=nsamps; + t->nextRxTstamp += nsamps; LOG_D(HW, "Rx to upper layer: %d from %ld to %ld, energy in first antenna %d\n", nsamps, @@ -1054,10 +1066,10 @@ static int rfsimulator_reset_stats(openair0_device *device) { return 0; } static void rfsimulator_end(openair0_device *device) { - rfsimulator_state_t* s = device->priv; + rfsimulator_state_t *s = device->priv; for (int i = 0; i < MAX_FD_RFSIMU; i++) { buffer_t *b = &s->buf[i]; - if (b->conn_sock >= 0 ) + if (b->conn_sock >= 0) removeCirBuf(s, b); } close(s->epollfd); @@ -1065,7 +1077,7 @@ static void rfsimulator_end(openair0_device *device) { } static void stopServer(openair0_device *device) { - rfsimulator_state_t *t = (rfsimulator_state_t *) device->priv; + rfsimulator_state_t *t = (rfsimulator_state_t *)device->priv; DevAssert(t != NULL); close(t->listen_sock); rfsimulator_end(device); @@ -1075,7 +1087,7 @@ static int rfsimulator_stop(openair0_device *device) { return 0; } static int rfsimulator_set_freq(openair0_device *device, openair0_config_t *openair0_cfg) { - rfsimulator_state_t* s = device->priv; + rfsimulator_state_t *s = device->priv; s->rx_freq = openair0_cfg->rx_freq[0]; return 0; } @@ -1092,11 +1104,11 @@ int device_init(openair0_device *device, openair0_config_t *openair0_cfg) { // --log_config.hw_log_level debug rfsimulator_state_t *rfsimulator = calloc(sizeof(rfsimulator_state_t), 1); // initialize channel simulation - rfsimulator->tx_num_channels=openair0_cfg->tx_num_channels; - rfsimulator->rx_num_channels=openair0_cfg->rx_num_channels; - rfsimulator->sample_rate=openair0_cfg->sample_rate; - rfsimulator->rx_freq=openair0_cfg->rx_freq[0]; - rfsimulator->tx_bw=openair0_cfg->tx_bw; + rfsimulator->tx_num_channels = openair0_cfg->tx_num_channels; + rfsimulator->rx_num_channels = openair0_cfg->rx_num_channels; + rfsimulator->sample_rate = openair0_cfg->sample_rate; + rfsimulator->rx_freq = openair0_cfg->rx_freq[0]; + rfsimulator->tx_bw = openair0_cfg->tx_bw; rfsimulator_readconfig(rfsimulator); if (rfsimulator->prop_delay_ms > 0.0) rfsimulator->chan_offset = ceil(rfsimulator->sample_rate * rfsimulator->prop_delay_ms / 1000); @@ -1114,23 +1126,23 @@ int device_init(openair0_device *device, openair0_config_t *openair0_cfg) { rfsimulator->role == SIMU_ROLE_SERVER ? "server waiting opposite rfsimulators to connect" : "client: will connect to a rfsimulator server side"); device->trx_start_func = rfsimulator->role == SIMU_ROLE_SERVER ? startServer : startClient; - device->trx_get_stats_func = rfsimulator_get_stats; + device->trx_get_stats_func = rfsimulator_get_stats; device->trx_reset_stats_func = rfsimulator_reset_stats; - device->trx_end_func = rfsimulator->role == SIMU_ROLE_SERVER ? stopServer : rfsimulator_end; - device->trx_stop_func = rfsimulator_stop; - device->trx_set_freq_func = rfsimulator_set_freq; - device->trx_set_gains_func = rfsimulator_set_gains; - device->trx_write_func = rfsimulator_write; - device->trx_read_func = rfsimulator_read; + device->trx_end_func = rfsimulator->role == SIMU_ROLE_SERVER ? stopServer : rfsimulator_end; + device->trx_stop_func = rfsimulator_stop; + device->trx_set_freq_func = rfsimulator_set_freq; + device->trx_set_gains_func = rfsimulator_set_gains; + device->trx_write_func = rfsimulator_write; + device->trx_read_func = rfsimulator_read; /* let's pretend to be a b2x0 */ device->type = RFSIMULATOR; openair0_cfg[0].rx_gain[0] = 0; - device->openair0_cfg=&openair0_cfg[0]; + device->openair0_cfg = &openair0_cfg[0]; device->priv = rfsimulator; device->trx_write_init = rfsimulator_write_init; for (int i = 0; i < MAX_FD_RFSIMU; i++) - rfsimulator->buf[i].conn_sock=-1; + rfsimulator->buf[i].conn_sock = -1; rfsimulator->next_buf = 0; AssertFatal((rfsimulator->epollfd = epoll_create1(0)) != -1, "epoll_create1() failed, errno(%d)", errno); @@ -1141,11 +1153,11 @@ int device_init(openair0_device *device, openair0_config_t *openair0_cfg) { add_telnetcmd_func_t addcmd = (add_telnetcmd_func_t)get_shlibmodule_fptr("telnetsrv", TELNET_ADDCMD_FNAME); if (addcmd != NULL) { - rfsimulator->poll_telnetcmdq = (poll_telnetcmdq_func_t)get_shlibmodule_fptr("telnetsrv", TELNET_POLLCMDQ_FNAME); - addcmd("rfsimu",rfsimu_vardef,rfsimu_cmdarray); + rfsimulator->poll_telnetcmdq = (poll_telnetcmdq_func_t)get_shlibmodule_fptr("telnetsrv", TELNET_POLLCMDQ_FNAME); + addcmd("rfsimu", rfsimu_vardef, rfsimu_cmdarray); - for(int i=0; rfsimu_cmdarray[i].cmdfunc != NULL; i++) { - if ( rfsimu_cmdarray[i].qptr != NULL) { + for (int i = 0; rfsimu_cmdarray[i].cmdfunc != NULL; i++) { + if (rfsimu_cmdarray[i].qptr != NULL) { rfsimulator->telnetcmd_qid = rfsimu_cmdarray[i].qptr; break; } From f7598bf528fbfcb157cdd24603bcabbd666dd76b Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Mon, 16 Jun 2025 13:28:43 +0200 Subject: [PATCH 4/7] clarify first transmission to client, split process_recv in 2 functions --- radio/rfsimulator/simulator.c | 147 +++++++++++++++++++--------------- 1 file changed, 82 insertions(+), 65 deletions(-) diff --git a/radio/rfsimulator/simulator.c b/radio/rfsimulator/simulator.c index 1b40b66aa7..ca8540c821 100644 --- a/radio/rfsimulator/simulator.c +++ b/radio/rfsimulator/simulator.c @@ -165,6 +165,8 @@ typedef struct { double prop_delay_ms; } rfsimulator_state_t; +static bool flushInput(rfsimulator_state_t *t, int timeout, bool first_time); + static buffer_t *allocCirBuf(rfsimulator_state_t *bridge, int sock) { uint64_t buff_index = bridge->next_buf++ % MAX_FD_RFSIMU; @@ -658,7 +660,24 @@ static int startClient(openair0_device *device) if (setblocking(sock, notBlocking) == -1) { return -1; } - return allocCirBuf(t, sock) == NULL ? -1 : 0; + buffer_t *b = allocCirBuf(t, sock); + if (!b) + return -1; + // read a 1 sample block to initialize the current time + bool have_to_wait; + do { + have_to_wait = true; + flushInput(t, 3, true); + if (b->lastReceivedTS) + have_to_wait = false; + } while (have_to_wait); + if (b->lastReceivedTS > 0) + b->lastReceivedTS--; + t->nextRxTstamp = b->lastReceivedTS; + LOG_D(HW, "Client got first timestamp: starting at %lu\n", t->nextRxTstamp); + if (b->channel_model) + b->channel_model->start_TS = t->nextRxTstamp; + return 0; } static int rfsimulator_write_internal(rfsimulator_state_t *t, @@ -765,76 +784,71 @@ static bool add_client(rfsimulator_state_t *t) return true; } -static void process_recv(rfsimulator_state_t *t, buffer_t *b, int size_for_first_pack) +static void process_recv_header(rfsimulator_state_t *t, buffer_t *b, bool first_time) +{ + // check the header and start block transfer + if (b->remainToTransfer != 0) + return; + + b->headerMode = false; // We got the header + + if (first_time) { + b->lastReceivedTS = b->th.timestamp; + b->trashingPacket = true; + } else { + if (b->lastReceivedTS < b->th.timestamp) { + // We have a transmission hole to fill, like TDD + // we create no signal samples up to the beginning of this reception + int nbAnt = b->th.nbAnt; + if (b->th.timestamp - b->lastReceivedTS < CirSize) { + // case we wrap at circular buffer end + for (uint64_t index = b->lastReceivedTS; index < b->th.timestamp; index++) { + for (int a = 0; a < nbAnt; a++) { + b->circularBuf[(index * nbAnt + a) % CirSize] = (c16_t){0}; + } + } + } else { + // case no circular buffer wrap + memset(b->circularBuf, 0, sampleToByte(CirSize, 1)); + } + b->lastReceivedTS = b->th.timestamp; + } else if (b->lastReceivedTS > b->th.timestamp) { + LOG_W(HW, "Received data in past: current is %lu, new reception: %lu!\n", b->lastReceivedTS, b->th.timestamp); + b->trashingPacket = true; + } + // verify the time gap is not too large + mutexlock(t->Sockmutex); + if (t->lastWroteTS != 0 && (fabs((double)t->lastWroteTS - b->lastReceivedTS) > (double)CirSize)) + LOG_W(HW, "UEsock(%d) Tx/Rx shift too large Tx:%lu, Rx:%lu\n", b->conn_sock, t->lastWroteTS, b->lastReceivedTS); + mutexunlock(t->Sockmutex); + } + // move back the pointer to overwrite the header, we don't need it anymore + b->transferPtr = (char *)&b->circularBuf[(b->lastReceivedTS * b->th.nbAnt) % CirSize]; + // we now need to read the samples + b->remainToTransfer = sampleToByte(b->th.size, b->th.nbAnt); + return; +} + +static void process_recv(rfsimulator_state_t *t, buffer_t *b, bool first_time) { if (b->transferPtr == b->circularBufEnd) b->transferPtr = (char *)b->circularBuf; - // check the header and start block transfer - if (b->headerMode == true) { - if (b->remainToTransfer == 0) { - b->headerMode = false; // We got the header - if (t->nextRxTstamp == 0) { // First block in a client, resync with the server current TS - t->nextRxTstamp = b->th.timestamp; - // we need to bootstrap from one side, else each side will wait forever - b->lastReceivedTS = b->th.timestamp + size_for_first_pack; - LOG_D(HW, "Client got first timestamp: starting at %lu\n", t->nextRxTstamp); - b->trashingPacket = true; - if (b->channel_model) - b->channel_model->start_TS = t->nextRxTstamp; - } else if (b->lastReceivedTS < b->th.timestamp) { - // We have a transmission hole to fill, like TDD - // we create no signal samples up to the beginning of this reception - int nbAnt = b->th.nbAnt; - if (b->th.timestamp - b->lastReceivedTS < CirSize) { - for (uint64_t index = b->lastReceivedTS; index < b->th.timestamp; index++) { - for (int a = 0; a < nbAnt; a++) { - b->circularBuf[(index * nbAnt + a) % CirSize].r = 0; - b->circularBuf[(index * nbAnt + a) % CirSize].i = 0; - } - } - } else { - memset(b->circularBuf, 0, sampleToByte(CirSize, 1)); - } - b->lastReceivedTS = b->th.timestamp; - } else if (b->lastReceivedTS > b->th.timestamp) { - if (b->th.size == 1) - LOG_W(HW, "Received Rx/Tx synchro out of order\n"); - else - LOG_W(HW, "Received data in past: current is %lu, new reception: %lu!\n", b->lastReceivedTS, b->th.timestamp); - b->trashingPacket = true; - } - // normal case, we have the header - // verify the time gap is not too large - mutexlock(t->Sockmutex); - if (t->lastWroteTS != 0 && (fabs((double)t->lastWroteTS - b->lastReceivedTS) > (double)CirSize)) - LOG_W(HW, "UEsock(%d) Tx/Rx shift too large Tx:%lu, Rx:%lu\n", b->conn_sock, t->lastWroteTS, b->lastReceivedTS); - mutexunlock(t->Sockmutex); - - // move back the pointer to overwrite the header, we don't need it anymore - b->transferPtr = (char *)&b->circularBuf[(b->lastReceivedTS * b->th.nbAnt) % CirSize]; - // we now need to read the samples - b->remainToTransfer = sampleToByte(b->th.size, b->th.nbAnt); - } - return; + if (!b->trashingPacket) { + b->lastReceivedTS = b->th.timestamp + b->th.size - byteToSample(b->remainToTransfer, b->th.nbAnt); + LOG_D(HW, "UEsock: %d Set b->lastReceivedTS %ld\n", b->conn_sock, b->lastReceivedTS); } - if (b->headerMode == false) { - if (!b->trashingPacket) { - b->lastReceivedTS = b->th.timestamp + b->th.size - byteToSample(b->remainToTransfer, b->th.nbAnt); - LOG_D(HW, "UEsock: %d Set b->lastReceivedTS %ld\n", b->conn_sock, b->lastReceivedTS); - } - if (b->remainToTransfer == 0) { - LOG_D(HW, "UEsock: %d Completed block reception: %ld\n", b->conn_sock, b->lastReceivedTS); - b->headerMode = true; - b->transferPtr = (char *)&b->th; - b->remainToTransfer = sizeof(samplesBlockHeader_t); - b->trashingPacket = false; - } + if (b->remainToTransfer == 0) { + LOG_D(HW, "UEsock: %d Completed block reception: %ld\n", b->conn_sock, b->lastReceivedTS); + b->headerMode = true; + b->transferPtr = (char *)&b->th; + b->remainToTransfer = sizeof(samplesBlockHeader_t); + b->trashingPacket = false; } } -static bool flushInput(rfsimulator_state_t *t, int timeout, int first_time_sz) +static bool flushInput(rfsimulator_state_t *t, int timeout, bool first_time) { // Process all incoming events on sockets // store the data in lists @@ -883,7 +897,10 @@ static bool flushInput(rfsimulator_state_t *t, int timeout, int first_time_sz) LOG_D(HW, "Socket rcv %zd bytes\n", sz); b->remainToTransfer -= sz; b->transferPtr += sz; - process_recv(t, b, first_time_sz); + if (b->headerMode) + process_recv_header(t, b, first_time); + else + process_recv(t, b, first_time); } return nfds > 0; } @@ -910,7 +927,7 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest if (t->nextRxTstamp == 0) LOG_I(HW, "No connected device, generating void samples...\n"); - if (!flushInput(t, t->wait_timeout, nsamps)) { + if (!flushInput(t, t->wait_timeout, false)) { for (int x = 0; x < nbAnt; x++) memset(samplesVoid[x], 0, sampleToByte(nsamps, 1)); @@ -940,7 +957,7 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest "Waiting on socket, current last ts: %ld, expected at least : %ld\n", b->lastReceivedTS, t->nextRxTstamp + nsamps); - flushInput(t, 3, nsamps); + flushInput(t, 3, false); } } while (have_to_wait); } From 1540ca3ae5905a73b09d3d8e3f1f410a6519b6d2 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Mon, 23 Jun 2025 15:51:34 +0200 Subject: [PATCH 5/7] fix bug in rfsim->nb_cnx for client side. it has no usage except a cpu optimisation in case one single rfsim socket and SISO case --- radio/rfsimulator/simulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/rfsimulator/simulator.c b/radio/rfsimulator/simulator.c index ca8540c821..ca28dd2e79 100644 --- a/radio/rfsimulator/simulator.c +++ b/radio/rfsimulator/simulator.c @@ -171,6 +171,7 @@ static buffer_t *allocCirBuf(rfsimulator_state_t *bridge, int sock) { uint64_t buff_index = bridge->next_buf++ % MAX_FD_RFSIMU; buffer_t *ptr = &bridge->buf[buff_index]; + bridge->nb_cnx++; ptr->circularBuf = calloc(1, sampleToByte(CirSize, 1)); if (ptr->circularBuf == NULL) { LOG_E(HW, "malloc(%lu) failed\n", sampleToByte(CirSize, 1)); @@ -764,7 +765,6 @@ static bool add_client(rfsimulator_state_t *t) mutexunlock(t->Sockmutex); return false; } - t->nb_cnx++; new_buf->lastReceivedTS = t->lastWroteTS; char ip[INET6_ADDRSTRLEN]; getnameinfo((struct sockaddr *)&sa, socklen, ip, sizeof(ip), NULL, 0, NI_NUMERICHOST); From 0523bcfbde7407f2e7126d08a33597d19169c200 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Mon, 23 Jun 2025 15:58:03 +0200 Subject: [PATCH 6/7] fix gNB that should always perform tx in case of rfsim (like continuous-tx --- executables/nr-gnb.c | 2 +- executables/nr-ru.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/executables/nr-gnb.c b/executables/nr-gnb.c index acab30751b..9e96ce6aed 100644 --- a/executables/nr-gnb.c +++ b/executables/nr-gnb.c @@ -114,7 +114,7 @@ static void tx_func(processingData_L1tx_t *info) pushNotifiedFIFO(&gNB->resp_L1, res); 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) { + 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); diff --git a/executables/nr-ru.c b/executables/nr-ru.c index 411caf9a63..2bacf04aa3 100644 --- a/executables/nr-ru.c +++ b/executables/nr-ru.c @@ -759,7 +759,7 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp) 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) { + 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); if(slot_type == NR_MIXED_SLOT) { int txsymb = 0; From 90cf2490184b612a08d20db97cacbeddd64090e7 Mon Sep 17 00:00:00 2001 From: Laurent THOMAS Date: Fri, 4 Jul 2025 09:34:17 +0200 Subject: [PATCH 7/7] add telnet commands trace --- common/utils/telnetsrv/telnetsrv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/utils/telnetsrv/telnetsrv.c b/common/utils/telnetsrv/telnetsrv.c index de1c26d2e1..1d1a5be410 100644 --- a/common/utils/telnetsrv/telnetsrv.c +++ b/common/utils/telnetsrv/telnetsrv.c @@ -692,8 +692,8 @@ void run_telnetsrv(void) { break; } - if (telnetparams.telnetdbg > 0) - TELNET_LOG("Command received: readc %i filled %i \"%s\"\n", readc, filled, buf); + //if (telnetparams.telnetdbg > 0) + TELNET_LOG("Command received: readc %i filled %i \"%s\"\n", readc, filled, buf); if (buf[0] == '!') { if (buf[1] == '!') {