From 68b07d542c70a8b7053c28b3d5bf0efa3de0ccdf Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Thu, 1 Aug 2024 12:42:06 +0200 Subject: [PATCH 01/10] rfsimulator: correctly set center frequency --- openair1/SIMULATION/TOOLS/random_channel.c | 4 ++-- openair1/SIMULATION/TOOLS/sim.h | 2 +- radio/rfsimulator/simulator.c | 8 ++++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/openair1/SIMULATION/TOOLS/random_channel.c b/openair1/SIMULATION/TOOLS/random_channel.c index e9c26e9abd..5c78a3a794 100644 --- a/openair1/SIMULATION/TOOLS/random_channel.c +++ b/openair1/SIMULATION/TOOLS/random_channel.c @@ -2281,7 +2281,7 @@ void init_channelmod(void) { } /* init_channelmod */ -int load_channellist(uint8_t nb_tx, uint8_t nb_rx, double sampling_rate, double channel_bandwidth) { +int load_channellist(uint8_t nb_tx, uint8_t nb_rx, double sampling_rate, uint64_t center_freq, double channel_bandwidth) { paramdef_t achannel_params[] = CHANNELMOD_MODEL_PARAMS_DESC; paramlist_def_t channel_list; memset(&channel_list,0,sizeof(paramlist_def_t)); @@ -2314,7 +2314,7 @@ int load_channellist(uint8_t nb_tx, uint8_t nb_rx, double sampling_rate, double nb_rx, modid, sampling_rate, - 0, + center_freq, channel_bandwidth, *(channel_list.paramarray[i][pindex_DT].dblptr), 0.0, diff --git a/openair1/SIMULATION/TOOLS/sim.h b/openair1/SIMULATION/TOOLS/sim.h index 3b55e7c4e5..9f1e74dd1f 100644 --- a/openair1/SIMULATION/TOOLS/sim.h +++ b/openair1/SIMULATION/TOOLS/sim.h @@ -543,7 +543,7 @@ int modelid_fromstrtype(char *modeltype); double channelmod_get_snr_dB(void); double channelmod_get_sinr_dB(void); void init_channelmod(void) ; -int load_channellist(uint8_t nb_tx, uint8_t nb_rx, double sampling_rate, double channel_bandwidth) ; +int load_channellist(uint8_t nb_tx, uint8_t nb_rx, double sampling_rate, uint64_t center_freq, double channel_bandwidth) ; double N_RB2sampling_rate(uint16_t N_RB); double N_RB2channel_bandwidth(uint16_t N_RB); diff --git a/radio/rfsimulator/simulator.c b/radio/rfsimulator/simulator.c index 0a2ece7d21..82561c9941 100644 --- a/radio/rfsimulator/simulator.c +++ b/radio/rfsimulator/simulator.c @@ -167,6 +167,7 @@ typedef struct { int rx_num_channels; int tx_num_channels; double sample_rate; + double rx_freq; double tx_bw; int channelmod; double chan_pathloss; @@ -342,7 +343,7 @@ static void rfsimulator_readconfig(rfsimulator_state_t *rfsimulator) { break; } 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->tx_bw); + load_channellist(rfsimulator->tx_num_channels, rfsimulator->rx_num_channels, rfsimulator->sample_rate, rfsimulator->rx_freq, rfsimulator->tx_bw); rfsimulator->channelmod=true; } else { fprintf(stderr, "unknown rfsimulator option: %s\n", rfsimu_params[p].strlistptr[i]); @@ -400,7 +401,7 @@ static int rfsimu_setchanmod_cmd(char *buff, int debug, telnet_printfunc_t prnt, t->rx_num_channels, channelmod, t->sample_rate, - 0, + t->rx_freq, t->tx_bw, 30e-9, // TDL delay-spread parameter 0.0, @@ -963,6 +964,8 @@ 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; + s->rx_freq = openair0_cfg->rx_freq[0]; return 0; } static int rfsimulator_set_gains(openair0_device *device, openair0_config_t *openair0_cfg) { @@ -980,6 +983,7 @@ int device_init(openair0_device *device, openair0_config_t *openair0_cfg) { 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) From 6a8edaf3176388b469bdf0aa7fcd794d5ba17751 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Thu, 1 Aug 2024 12:46:54 +0200 Subject: [PATCH 02/10] rfsimulator: add delay and Doppler simulation for circular orbit and add according channel models SAT_LEO_TRANS and SAT_LEO_REGEN Delay and Doppler computation is done as in Matlab function dopplerShiftCircularOrbit: https://de.mathworks.com/help/satcom/ref/dopplershiftcircularorbit.html --- openair1/SIMULATION/TOOLS/random_channel.c | 39 +++++++- openair1/SIMULATION/TOOLS/sim.h | 16 ++++ radio/rfsimulator/apply_channelmod.c | 102 ++++++++++++++++++--- radio/rfsimulator/simulator.c | 6 ++ 4 files changed, 149 insertions(+), 14 deletions(-) diff --git a/openair1/SIMULATION/TOOLS/random_channel.c b/openair1/SIMULATION/TOOLS/random_channel.c index 5c78a3a794..268d0166b1 100644 --- a/openair1/SIMULATION/TOOLS/random_channel.c +++ b/openair1/SIMULATION/TOOLS/random_channel.c @@ -128,6 +128,7 @@ void fill_channel_desc(channel_desc_t *chan_desc, chan_desc->first_run = 1; chan_desc->ip = 0.0; chan_desc->max_Doppler = max_Doppler; + chan_desc->Doppler_phase_cur = calloc(nb_rx, sizeof(double)); chan_desc->ch = calloc(nb_tx*nb_rx, sizeof(struct complexd *)); chan_desc->chF = calloc(nb_tx*nb_rx, sizeof(struct complexd *)); chan_desc->a = calloc(nb_taps, sizeof(struct complexd *)); @@ -1653,6 +1654,37 @@ channel_desc_t *new_channel_desc_scm(uint8_t nb_tx, 0); break; + case SAT_LEO_TRANS: + case SAT_LEO_REGEN: + nb_taps = 1; + Td = 0; + channel_length = 1; + ricean_factor = 0.0; + aoa = 0.0; + maxDoppler = 0; + chan_desc->sat_height = 600e3; + chan_desc->enable_dynamic_delay = true; + chan_desc->enable_dynamic_Doppler = false; // TODO: requires UE to support continuous Doppler estimation, compensation and pre-compensation + fill_channel_desc(chan_desc,nb_tx, + nb_rx, + nb_taps, + channel_length, + default_amp_lin, + NULL, + NULL, + Td, + sampling_rate, + channel_bandwidth, + ricean_factor, + aoa, + forgetting_factor, + maxDoppler, + channel_offset, + path_loss_dB, + 0); + printf("%s: satellite orbit height %f km\n", map_int_to_str(channelmod_names, channel_model), chan_desc->sat_height / 1000); + break; + default: LOG_W(OCM,"channel model not yet supported\n"); free(chan_desc); @@ -1711,6 +1743,7 @@ void free_channel_desc_scm(channel_desc_t *ch) { free(ch->R_sqrt[i]); free(ch->R_sqrt); + free(ch->Doppler_phase_cur); free(ch->ch); free(ch->chF); free(ch->a); @@ -1743,9 +1776,9 @@ int random_channel(channel_desc_t *desc, uint8_t abstraction_flag) { struct complexd phase, alpha, beta; start_meas(&desc->random_channel); - // For AWGN channel, the received signal (Srx) is equal to transmitted signal (Stx) plus noise (N), i.e., Srx = Stx + N, + // For AWGN and SAT_LEO_* channels, the received signal (Srx) is equal to transmitted signal (Stx) plus noise (N), i.e., Srx = Stx + N, // therefore, the channel matrix is the identity matrix. - if (desc->modelid == AWGN) { + if (desc->modelid == AWGN || desc->modelid == SAT_LEO_TRANS || desc->modelid == SAT_LEO_REGEN) { for (aarx=0; aarxnb_rx; aarx++) { for (aatx = 0; aatx < desc->nb_tx; aatx++) { desc->ch[aarx+(aatx*desc->nb_rx)][0].r = aarx%desc->nb_tx == aatx ? 1.0 : 0.0; @@ -2071,6 +2104,8 @@ static void display_channelmodel(channel_desc_t *cd,int debug, telnet_printfunc_ cd->forgetting_factor); prnt("Initial phase: %lf nb_path: %i \n", cd->ip, cd->nb_paths); + if (cd->modelid == SAT_LEO_TRANS || cd->modelid == SAT_LEO_REGEN) + prnt("satellite orbit height: %f\n", cd->sat_height); for (int i=0; inb_taps ; i++) { prnt("taps: %i lin. ampli. : %lf delay: %lf \n",i,cd->amps[i], cd->delays[i]); diff --git a/openair1/SIMULATION/TOOLS/sim.h b/openair1/SIMULATION/TOOLS/sim.h index 9f1e74dd1f..8297d9c365 100644 --- a/openair1/SIMULATION/TOOLS/sim.h +++ b/openair1/SIMULATION/TOOLS/sim.h @@ -126,6 +126,18 @@ typedef struct { char *model_name; /// flags to properly trigger memory free unsigned int free_flags; + /// time stamp when the time varying channel emulation starts (when client connected) + uint64_t start_TS; + /// height of LEO satellite + float sat_height; + /// flag to enable dynamic delay simulation for LEO satellite + bool enable_dynamic_delay; + /// flag to enable dynamic Doppler simulation for LEO satellite + bool enable_dynamic_Doppler; + /// Doppler phase increment (might vary over time, e.g. for LEO satellite) + float Doppler_phase_inc; + /// current Doppler phase of each RX antenna (for continuous phase from one block to the next) + float *Doppler_phase_cur; } channel_desc_t; typedef struct { @@ -218,6 +230,8 @@ typedef enum { EPA_low, EPA_medium, EPA_high, + SAT_LEO_TRANS, + SAT_LEO_REGEN, } SCM_t; #define CHANNELMOD_MAP_INIT \ {"custom",custom},\ @@ -253,6 +267,8 @@ typedef enum { {"EPA_low",EPA_low},\ {"EPA_medium",EPA_medium},\ {"EPA_high",EPA_high},\ + {"SAT_LEO_TRANS",SAT_LEO_TRANS},\ + {"SAT_LEO_REGEN",SAT_LEO_REGEN},\ {NULL, -1} #define CONFIG_HLP_SNR "Set average SNR in dB (for --siml1 option)\n" diff --git a/radio/rfsimulator/apply_channelmod.c b/radio/rfsimulator/apply_channelmod.c index 917a22924c..5bb207e5be 100644 --- a/radio/rfsimulator/apply_channelmod.c +++ b/radio/rfsimulator/apply_channelmod.c @@ -28,7 +28,7 @@ #include #include #include - +#include #include #include @@ -52,14 +52,85 @@ 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, - c16_t *after_channel_sig, - int rxAnt, - channel_desc_t *channelDesc, - int nbSamples, - uint64_t TS, - uint32_t CirSize - ) { +void rxAddInput(const c16_t *input_sig, + c16_t *after_channel_sig, + int rxAnt, + channel_desc_t *channelDesc, + int nbSamples, + uint64_t TS, + uint32_t CirSize) +{ + 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. + - Satellite moves in the clockwise direction in its circular orbit. + */ + const double radius_earth = 6371e3; // m + const double radius_sat = radius_earth + channelDesc->sat_height; + const double GM_earth = 3.986e14; // m^3/s^2 + const double w_sat = sqrt(GM_earth / (radius_sat * radius_sat * radius_sat)); // rad/s + + // start_time and end_time are when the pos_sat_z == pos_gnb_z (elevation angle == 0 and 180 degree) + const double start_phase = -acos(radius_earth / radius_sat); // SAT is just rising above the horizon + const double end_phase = -start_phase; // SAT is just falling behind the horizon + const double start_time = start_phase / w_sat; // in seconds + const double end_time = end_phase / w_sat; // in seconds + const uint64_t duration_samples = (end_time - start_time) * channelDesc->sampling_rate; + const double t = start_time + ((TS - channelDesc->start_TS) % duration_samples) / channelDesc->sampling_rate; + + const double pos_sat_x = 0; + const double pos_sat_y = radius_sat * sin(w_sat * t); + 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_z = -w_sat * radius_sat * sin(w_sat * t); + + const double pos_ue_x = 0; + const double pos_ue_y = 0; + const double pos_ue_z = radius_earth; + + const double dir_sat_ue_x = pos_ue_x - pos_sat_x; + const double dir_sat_ue_y = pos_ue_y - pos_sat_y; + const double dir_sat_ue_z = pos_ue_z - pos_sat_z; + + const double dist_sat_ue = sqrt(dir_sat_ue_x * dir_sat_ue_x + dir_sat_ue_y * dir_sat_ue_y + dir_sat_ue_z * dir_sat_ue_z); + const double vel_sat_ue = (vel_sat_x * dir_sat_ue_x + vel_sat_y * dir_sat_ue_y + vel_sat_z * dir_sat_ue_z) / dist_sat_ue; + + double dist_gnb_sat = 0; + double vel_gnb_sat = 0; + if (channelDesc->modelid == SAT_LEO_TRANS) { + const double pos_gnb_x = 0; + const double pos_gnb_y = 0; + const double pos_gnb_z = radius_earth; + + const double dir_gnb_sat_x = pos_sat_x - pos_gnb_x; + const double dir_gnb_sat_y = pos_sat_y - pos_gnb_y; + const double dir_gnb_sat_z = pos_sat_z - pos_gnb_z; + + dist_gnb_sat = sqrt(dir_gnb_sat_x * dir_gnb_sat_x + dir_gnb_sat_y * dir_gnb_sat_y + dir_gnb_sat_z * dir_gnb_sat_z); + vel_gnb_sat = (vel_sat_x * dir_gnb_sat_x + vel_sat_y * dir_gnb_sat_y + vel_sat_z * dir_gnb_sat_z) / dist_gnb_sat; + } + + const double c = 299792458; // m/s + const double prop_delay = (dist_gnb_sat + dist_sat_ue) / c; + if (channelDesc->enable_dynamic_delay) + channelDesc->channel_offset = prop_delay * channelDesc->sampling_rate; + + const double f_Doppler_shift_sat_ue = (vel_sat_ue / (c - vel_sat_ue)) * channelDesc->center_freq; + const double f_Doppler_shift_gnb_sat = (-vel_gnb_sat / c) * channelDesc->center_freq; + if (channelDesc->enable_dynamic_Doppler) + channelDesc->Doppler_phase_inc = 2 * M_PI * (f_Doppler_shift_gnb_sat + f_Doppler_shift_sat_ue) / channelDesc->sampling_rate; + + static uint64_t last_TS = 0; + if(TS - last_TS >= channelDesc->sampling_rate) { + last_TS = TS; + LOG_I(HW, "Satellite orbit: time %f s, Doppler: gNB->SAT %f kHz, SAT->UE %f kHz, Delay %f ms\n", t, f_Doppler_shift_gnb_sat / 1000, f_Doppler_shift_sat_ue / 1000, prop_delay * 1000); + } + } + // channelDesc->path_loss_dB should contain the total path gain // so, in actual RF: tx gain + path loss + rx gain (+antenna gain, ...) // UE and NB gain control to be added @@ -94,9 +165,16 @@ void rxAddInput( const c16_t *input_sig, } //l } - // Fixme: lround(), rount(), ... is detected by valgrind as error, not found why - out_ptr->r += lround(rx_tmp.r*pathLossLinear + noise_per_sample*gaussZiggurat(0.0,1.0)); - out_ptr->i += lround(rx_tmp.i*pathLossLinear + noise_per_sample*gaussZiggurat(0.0,1.0)); + if (channelDesc->Doppler_phase_inc != 0.0) { + double complex in = CMPLX(rx_tmp.r, rx_tmp.i); + double complex out = in * cexp(channelDesc->Doppler_phase_cur[rxAnt] * I); + rx_tmp.r = creal(out); + rx_tmp.i = cimag(out); + channelDesc->Doppler_phase_cur[rxAnt] += channelDesc->Doppler_phase_inc; + } + + out_ptr->r = lround(rx_tmp.r*pathLossLinear + noise_per_sample*gaussZiggurat(0.0,1.0)); + out_ptr->i = lround(rx_tmp.i*pathLossLinear + noise_per_sample*gaussZiggurat(0.0,1.0)); out_ptr++; } diff --git a/radio/rfsimulator/simulator.c b/radio/rfsimulator/simulator.c index 82561c9941..d944e028bb 100644 --- a/radio/rfsimulator/simulator.c +++ b/radio/rfsimulator/simulator.c @@ -709,6 +709,10 @@ static bool flushInput(rfsimulator_state_t *t, int timeout, int nsamps_for_initi samplesVoid[i]=(void *)&v; rfsimulator_write_internal(t, t->lastWroteTS > 1 ? t->lastWroteTS - 1 : 0, samplesVoid, 1, t->tx_num_channels, 1, false); + + buffer_t *b = &t->buf[conn_sock]; + if (b->channel_model) + b->channel_model->start_TS = t->lastWroteTS; } else { if ( events[nbEv].events & (EPOLLHUP | EPOLLERR | EPOLLRDHUP) ) { socketError(t,fd); @@ -761,6 +765,8 @@ static bool flushInput(rfsimulator_state_t *t, int timeout, int nsamps_for_initi 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; From cfb6ed4d82b2cd9419e38bdf0e2c84540779a6d8 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Thu, 1 Aug 2024 13:00:58 +0200 Subject: [PATCH 03/10] add example configuration using transparent LEO satellite model for rfsimulator --- .../CONF/channelmod_rfsimu_LEO_satellite.conf | 16 ++++++++++++++++ .../CONF/gnb.sa.band66.fr1.24PRB.usrpx300.conf | 5 +++-- .../CONF/gnb.sa.band66.fr1.25PRB.usrpx300.conf | 5 +++-- 3 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 targets/PROJECTS/GENERIC-NR-5GC/CONF/channelmod_rfsimu_LEO_satellite.conf diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/channelmod_rfsimu_LEO_satellite.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/channelmod_rfsimu_LEO_satellite.conf new file mode 100644 index 0000000000..47acf0e8b0 --- /dev/null +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/channelmod_rfsimu_LEO_satellite.conf @@ -0,0 +1,16 @@ +channelmod = { + max_chan=10; + modellist="modellist_rfsimu_1"; + modellist_rfsimu_1 = ( + { + model_name = "rfsimu_channel_enB0" + type = "SAT_LEO_TRANS"; + noise_power_dB = -100; + }, + { + model_name = "rfsimu_channel_ue0" + type = "SAT_LEO_TRANS"; + noise_power_dB = -100; + } + ); +}; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.24PRB.usrpx300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.24PRB.usrpx300.conf index de9eb1e547..9adb7ab2c5 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.24PRB.usrpx300.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.24PRB.usrpx300.conf @@ -165,7 +165,8 @@ gNBs = #ext2 #ntn_Config_r17 -# cellSpecificKoffset_r17 = 478; +# cellSpecificKoffset_r17 = 478; # GEO satellite +# cellSpecificKoffset_r17 = 40; # LEO satellite } ); @@ -255,7 +256,6 @@ rfsimulator : serveraddr = "server"; serverport = 4043; options = (); #("saviq"); or/and "chanmod" - modelname = "AWGN"; IQfile = "/tmp/rfsimulator.iqs"; }; @@ -289,3 +289,4 @@ log_config : f1ap_log_level ="debug"; }; +@include "channelmod_rfsimu_LEO_satellite.conf" diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.25PRB.usrpx300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.25PRB.usrpx300.conf index 1f757c45dd..766e51e4d5 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.25PRB.usrpx300.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.25PRB.usrpx300.conf @@ -165,7 +165,8 @@ gNBs = #ext2 #ntn_Config_r17 -# cellSpecificKoffset_r17 = 478; +# cellSpecificKoffset_r17 = 478; # GEO satellite +# cellSpecificKoffset_r17 = 40; # LEO satellite } ); @@ -255,7 +256,6 @@ rfsimulator : serveraddr = "server"; serverport = 4043; options = (); #("saviq"); or/and "chanmod" - modelname = "AWGN"; IQfile = "/tmp/rfsimulator.iqs"; }; @@ -289,3 +289,4 @@ log_config : f1ap_log_level ="debug"; }; +@include "channelmod_rfsimu_LEO_satellite.conf" From 8ab7f1dd687fbe4bf339ef80f5bac05863a9b007 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Thu, 1 Aug 2024 13:08:42 +0200 Subject: [PATCH 04/10] change returned value of UE_dl_preprocessing() in case no sampleShift change from 0 to INT_MAX This now allows to indicate "there was a new sampleShift calculation, and the actual value is 0" additionally to "there was no new sampleShift calculation". --- executables/nr-ue.c | 7 ++----- openair1/SCHED_NR_UE/phy_procedures_nr_ue.c | 2 +- openair1/SCHED_NR_UE/phy_procedures_nr_ue_sl.c | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/executables/nr-ue.c b/executables/nr-ue.c index dca05ea36a..4317f72041 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -597,7 +597,7 @@ void processSlotTX(void *arg) static int UE_dl_preprocessing(PHY_VARS_NR_UE *UE, const UE_nr_rxtx_proc_t *proc, int *tx_wait_for_dlsch, nr_phy_data_t *phy_data) { - int sampleShift = 0; + int sampleShift = INT_MAX; NR_DL_FRAME_PARMS *fp = &UE->frame_parms; if (UE->sl_mode == 2) fp = &UE->SL_UE_PHY_PARAMS.sl_frame_params; @@ -991,10 +991,7 @@ void *UE_thread(void *arg) nr_rxtx_thread_data_t *curMsgRx = (nr_rxtx_thread_data_t *)NotifiedFifoData(newRx); *curMsgRx = (nr_rxtx_thread_data_t){.proc = curMsg.proc, .UE = UE}; int ret = UE_dl_preprocessing(UE, &curMsgRx->proc, tx_wait_for_dlsch, &curMsgRx->phy_data); - if (ret) - // if ret is 0, no rx_offset has been computed, - // or the computed value is 0 = no offset to do - // we store it to apply the drift compensation at beginning of next frame + if (ret != INT_MAX) shiftForNextFrame = ret; pushTpool(&(get_nrUE_params()->Tpool), newRx); diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c index 4aff01720b..ce2c6c3153 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c @@ -877,7 +877,7 @@ int pbch_pdcch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_ fapi_nr_config_request_t *cfg = &ue->nrUE_config; NR_DL_FRAME_PARMS *fp = &ue->frame_parms; NR_UE_PDCCH_CONFIG *phy_pdcch_config = &phy_data->phy_pdcch_config; - int sampleShift = 0; + int sampleShift = INT_MAX; nr_ue_dlsch_init(phy_data->dlsch, NR_MAX_NB_LAYERS>4 ? 2:1, ue->max_ldpc_iterations); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_UE_RX, VCD_FUNCTION_IN); diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue_sl.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue_sl.c index 815b1ad4cb..8a409e8ef5 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue_sl.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue_sl.c @@ -168,7 +168,7 @@ int psbch_pscch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr int nr_slot_rx = proc->nr_slot_rx; sl_nr_ue_phy_params_t *sl_phy_params = &ue->SL_UE_PHY_PARAMS; NR_DL_FRAME_PARMS *fp = &sl_phy_params->sl_frame_params; - int sampleShift = 0; + int sampleShift = INT_MAX; // VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_UE_RX_SL, VCD_FUNCTION_IN); start_meas(&sl_phy_params->phy_proc_sl_rx); From 4039473a70b1466fd71e41df4bb63444e8c2b99f Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Thu, 1 Aug 2024 13:16:20 +0200 Subject: [PATCH 05/10] NR_UE: add PI controller to time sync this allows to compensate larger continuous time drift in DL (e.g. large Doppler from LEO satellite) --- executables/nr-ue.c | 2 +- executables/nr-uesoftmodem.h | 6 +++ .../PHY/NR_UE_ESTIMATION/nr_adjust_synch_ue.c | 42 ++++++++++--------- openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h | 3 -- openair1/PHY/defs_nr_UE.h | 3 +- 5 files changed, 31 insertions(+), 25 deletions(-) diff --git a/executables/nr-ue.c b/executables/nr-ue.c index 4317f72041..50e7b68bf5 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -947,7 +947,7 @@ void *UE_thread(void *arg) if (slot_nr == nb_slot_frame - 1) { // we shift of half of measured drift, at each beginning of frame for both rx and tx iq_shift_to_apply = shiftForNextFrame; - shiftForNextFrame = 0; // We will get a new measured offset if we decode PBCH + shiftForNextFrame = -round(UE->max_pos_acc * get_nrUE_params()->time_sync_I); } const int readBlockSize = get_readBlockSize(slot_nr, fp) - iq_shift_to_apply; diff --git a/executables/nr-uesoftmodem.h b/executables/nr-uesoftmodem.h index 08cafc8379..984058a939 100644 --- a/executables/nr-uesoftmodem.h +++ b/executables/nr-uesoftmodem.h @@ -9,6 +9,8 @@ #define CONFIG_HLP_DLSCH_PARA "number of threads for dlsch processing 0 for no parallelization\n" #define CONFIG_HLP_OFFSET_DIV "Divisor for computing OFDM symbol offset in Rx chain (num samples in CP/). Default value is 8. To set the sample offset to 0, set this value ~ 10e6\n" #define CONFIG_HLP_MAX_LDPC_ITERATIONS "Maximum LDPC decoder iterations\n" +#define CONFIG_HLP_TIME_SYNC_P "coefficient for Proportional part of time sync PI controller\n" +#define CONFIG_HLP_TIME_SYNC_I "coefficient for Integrating part of time sync PI controller\n" #define CONFIG_HLP_NTN_KOFFSET "NTN cellSpecificKoffset-r17 (number of slots for a given subcarrier spacing of 15 kHz)\n" #define CONFIG_HLP_NTN_TA_COMMON "NTN ta-Common, but given in ms\n" @@ -59,6 +61,8 @@ {"ue-timing-correction-disable", CONFIG_HLP_DISABLETIMECORR, PARAMFLAG_BOOL, .iptr=&(nrUE_params.no_timing_correction), .defintval=0, TYPE_INT, 0}, \ {"SLC", CONFIG_HLP_SLF, 0, .u64ptr=&(sidelink_frequency[0][0]), .defuintval=2600000000,TYPE_UINT64,0}, \ {"num-ues", NULL, 0, .iptr=&(NB_UE_INST), .defuintval=1, TYPE_INT, 0}, \ + {"time-sync-P", CONFIG_HLP_TIME_SYNC_P, 0, .dblptr=&(nrUE_params.time_sync_P), .defdblval=0.5, TYPE_DOUBLE, 0}, \ + {"time-sync-I", CONFIG_HLP_TIME_SYNC_I, 0, .dblptr=&(nrUE_params.time_sync_I), .defdblval=0.2, TYPE_DOUBLE, 0}, \ {"ntn-koffset", CONFIG_HLP_NTN_KOFFSET, 0, .uptr=&(nrUE_params.ntn_koffset), .defuintval=0, TYPE_UINT, 0}, \ {"ntn-ta-common", CONFIG_HLP_NTN_TA_COMMON, 0, .dblptr=&(nrUE_params.ntn_ta_common), .defdblval=0.0, TYPE_DOUBLE, 0}, \ {"agc", CONFIG_HLP_AGC, PARAMFLAG_BOOL, .iptr=&(nrUE_params.agc), .defintval=0, TYPE_INT, 0}, \ @@ -82,6 +86,8 @@ typedef struct { int N_RB_DL; int ssb_start_subcarrier; int ldpc_offload_flag; + double time_sync_P; + double time_sync_I; unsigned int ntn_koffset; double ntn_ta_common; int agc; diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_adjust_synch_ue.c b/openair1/PHY/NR_UE_ESTIMATION/nr_adjust_synch_ue.c index 61c12d145a..5b91a068ed 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/nr_adjust_synch_ue.c +++ b/openair1/PHY/NR_UE_ESTIMATION/nr_adjust_synch_ue.c @@ -24,7 +24,7 @@ #include "PHY/NR_UE_ESTIMATION/nr_estimation.h" #include "PHY/impl_defs_top.h" -#include "executables/softmodem-common.h" +#include "executables/nr-uesoftmodem.h" #include "common/utils/LOG/vcd_signal_dumper.h" //#define DEBUG_PHY @@ -43,12 +43,9 @@ int nr_adjust_synch_ue(NR_DL_FRAME_PARMS *frame_parms, short coef) { int max_val = 0, max_pos = 0; - uint8_t sync_offset = 0; VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_ADJUST_SYNCH, VCD_FUNCTION_IN); - short ncoef = 32767 - coef; - // search for maximum position within the cyclic prefix for (int i = -frame_parms->nb_prefix_samples/2; i < frame_parms->nb_prefix_samples/2; i++) { int temp = 0; @@ -67,32 +64,37 @@ int nr_adjust_synch_ue(NR_DL_FRAME_PARMS *frame_parms, } // filter position to reduce jitter - ue->max_pos_avg = ((ue->max_pos_avg * coef) >> 15) + (max_pos * ncoef); - - int diff = ue->max_pos_avg >> 15; + const int ncoef = 32767 - coef; + ue->max_pos_iir = ((ue->max_pos_iir * coef) >> 15) + (max_pos * ncoef); + const int diff = (ue->max_pos_iir + 16384) >> 15; + // FIXME: Do we really need this hysteresis for FR2? + int sampleShift = diff; if (frame_parms->freq_range == FR2) - sync_offset = 2; - else - sync_offset = 0; + if (abs(diff) <= 2) + sampleShift = 0; - int sampleShift = 0; - if (abs(diff) > (NR_SYNCH_HYST + sync_offset)) - sampleShift = diff; - - const int sample_shift = -(sampleShift / 2); - // reset IIR filter for next offset calculation - ue->max_pos_avg += sample_shift * 32768; + // PI controller + const double PID_P = get_nrUE_params()->time_sync_P; + const double PID_I = get_nrUE_params()->time_sync_I; + int sample_shift = -round(sampleShift * PID_P + ue->max_pos_acc * PID_I); LOG_D(PHY, - "Slot %d: diff = %i, rx_offset (final) = %i : max_pos = %d, max_pos filtered = %ld, max_power = %d\n", + "Frame %d, Slot %d: max_pos = %d, max_pos filtered = %f, diff = %i, sampleShift = %i, max_pos_acc = %d, sample_shift (final) = %d, max_power = %d\n", + frame, slot, + max_pos, + ue->max_pos_iir / 32768.0, diff, sampleShift, - max_pos, - ue->max_pos_avg, + ue->max_pos_acc, + sample_shift, max_val); + // reset IIR filter for next offset calculation + ue->max_pos_iir += -round(sampleShift * PID_P) * 32768; + ue->max_pos_acc += max_pos; + VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_ADJUST_SYNCH, VCD_FUNCTION_OUT); return sample_shift; } diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h b/openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h index 0ce6c0d83b..d08cc87f1d 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h +++ b/openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h @@ -29,9 +29,6 @@ * @{ */ -/*!\brief Timing drift hysterisis in samples*/ -#define NR_SYNCH_HYST 1 - /* A function to perform the channel estimation of DL PRS signal */ int nr_prs_channel_estimation(uint8_t gNB_id, uint8_t rsc_id, diff --git a/openair1/PHY/defs_nr_UE.h b/openair1/PHY/defs_nr_UE.h index 2ab028b6a9..96eb22049f 100644 --- a/openair1/PHY/defs_nr_UE.h +++ b/openair1/PHY/defs_nr_UE.h @@ -456,7 +456,8 @@ typedef struct PHY_VARS_NR_UE_s { /// temporary offset during cell search prior to MIB decoding int ssb_offset; uint16_t symbol_offset; /// offset in terms of symbols for detected ssb in sync - int64_t max_pos_avg; /// Timing offset IIR filter + int64_t max_pos_iir; /// Timing offset IIR filter + int max_pos_acc; /// Timing offset accumuluated error for PI filter /// Timing Advance updates variables /// Timing advance update computed from the TA command signalled from gNB From fc4310cda7d410d08ce09aa4c09891cde04784e2 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Thu, 1 Aug 2024 13:27:35 +0200 Subject: [PATCH 06/10] fix multi-statement marco asn1cCallocOne() --- common/utils/oai_asn1.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/common/utils/oai_asn1.h b/common/utils/oai_asn1.h index 573fa94d75..600aea1392 100644 --- a/common/utils/oai_asn1.h +++ b/common/utils/oai_asn1.h @@ -216,7 +216,10 @@ static inline uint64_t BIT_STRING_to_uint64(const BIT_STRING_t *asn) { #define asn1cSeqAdd(VaR, PtR) if (ASN_SEQUENCE_ADD(VaR,PtR)!=0) AssertFatal(false, "ASN.1 encoding error " #VaR "\n") #define asn1cCallocOne(VaR, VaLue) \ - VaR = calloc(1,sizeof(*VaR)); *VaR=VaLue + do { \ + VaR = calloc(1,sizeof(*VaR)); \ + *VaR = VaLue; \ + } while (0) #define asn1cCalloc(VaR, lOcPtr) \ typeof(VaR) lOcPtr = VaR = calloc(1,sizeof(*VaR)) #define asn1cSequenceAdd(VaR, TyPe, lOcPtr) \ From e662721310f2a3937e5441ad692668a8ca94df1e Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Thu, 1 Aug 2024 13:32:46 +0200 Subject: [PATCH 07/10] NR_UE: make NTN parameter ta_CommonDrift_r17 configurable via command line parameter --ntn-ta-commondrift While we don't have SIB19 support yet, UE gets this parameter from command line, just like --ntn-koffset and --ntn-ta-common. We use this value to initialize the UE's time-tracking PI controller accumulator. This allows to correctly track the time even if the delay is changing significantly (e.g. due to LEO satellite movement). We also use it to compensate for the time drift that happenes while performing the initial sync. --- executables/nr-ue.c | 4 +++- executables/nr-uesoftmodem.h | 3 +++ openair2/LAYER2/NR_MAC_UE/main_ue_nr.c | 6 ++++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/executables/nr-ue.c b/executables/nr-ue.c index 50e7b68bf5..9eafacee52 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -886,7 +886,9 @@ void *UE_thread(void *arg) stream_status = STREAM_STATUS_SYNCING; syncInFrame(UE, &sync_timestamp, intialSyncOffset); openair0_write_reorder_clear_context(&UE->rfdevice); - shiftForNextFrame = 0; // will be used to track clock drift + UE->max_pos_acc = get_nrUE_params()->ntn_ta_commondrift * 1e-6 * fp->samples_per_frame / get_nrUE_params()->time_sync_I; // ntn_ta_commondrift is in µs/s, max_pos_acc * time_sync_I is in samples/frame + shiftForNextFrame = -(UE->init_sync_frame + trashed_frames + 2) * UE->max_pos_acc * get_nrUE_params()->time_sync_I; // compensate for the time drift that happened during initial sync + LOG_D(PHY, "max_pos_acc = %d, shiftForNextFrame = %d\n", UE->max_pos_acc, shiftForNextFrame); // read in first symbol AssertFatal(fp->ofdm_symbol_size + fp->nb_prefix_samples0 == UE->rfdevice.trx_read_func(&UE->rfdevice, diff --git a/executables/nr-uesoftmodem.h b/executables/nr-uesoftmodem.h index 984058a939..5b745cec61 100644 --- a/executables/nr-uesoftmodem.h +++ b/executables/nr-uesoftmodem.h @@ -13,6 +13,7 @@ #define CONFIG_HLP_TIME_SYNC_I "coefficient for Integrating part of time sync PI controller\n" #define CONFIG_HLP_NTN_KOFFSET "NTN cellSpecificKoffset-r17 (number of slots for a given subcarrier spacing of 15 kHz)\n" #define CONFIG_HLP_NTN_TA_COMMON "NTN ta-Common, but given in ms\n" +#define CONFIG_HLP_NTN_TA_COMMONDRIFT "NTN ta-CommonDrift, but given in µs/s\n" /***************************************************************************************************************************************/ /* command line options definitions, CMDLINE_XXXX_DESC macros are used to initialize paramdef_t arrays which are then used as argument @@ -65,6 +66,7 @@ {"time-sync-I", CONFIG_HLP_TIME_SYNC_I, 0, .dblptr=&(nrUE_params.time_sync_I), .defdblval=0.2, TYPE_DOUBLE, 0}, \ {"ntn-koffset", CONFIG_HLP_NTN_KOFFSET, 0, .uptr=&(nrUE_params.ntn_koffset), .defuintval=0, TYPE_UINT, 0}, \ {"ntn-ta-common", CONFIG_HLP_NTN_TA_COMMON, 0, .dblptr=&(nrUE_params.ntn_ta_common), .defdblval=0.0, TYPE_DOUBLE, 0}, \ + {"ntn-ta-commondrift", CONFIG_HLP_NTN_TA_COMMONDRIFT, 0, .dblptr=&(nrUE_params.ntn_ta_commondrift), .defdblval=0.0, TYPE_DOUBLE, 0}, \ {"agc", CONFIG_HLP_AGC, PARAMFLAG_BOOL, .iptr=&(nrUE_params.agc), .defintval=0, TYPE_INT, 0}, \ } // clang-format on @@ -90,6 +92,7 @@ typedef struct { double time_sync_I; unsigned int ntn_koffset; double ntn_ta_common; + double ntn_ta_commondrift; int agc; char *usrp_args; char *tx_subdev; diff --git a/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c b/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c index fc4212d604..4eddc6b780 100644 --- a/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c +++ b/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c @@ -88,7 +88,7 @@ void nr_ue_init_mac(NR_UE_MAC_INST_t *mac) // Fake SIB19 reception for NTN // TODO: remove this and implement the actual SIB19 reception instead! - if (get_nrUE_params()->ntn_koffset || get_nrUE_params()->ntn_ta_common) { + if (get_nrUE_params()->ntn_koffset || get_nrUE_params()->ntn_ta_common || get_nrUE_params()->ntn_ta_commondrift) { NR_SIB19_r17_t *sib19_r17 = calloc(1, sizeof(*sib19_r17)); sib19_r17->ntn_Config_r17 = calloc(1, sizeof(*sib19_r17->ntn_Config_r17)); @@ -98,9 +98,11 @@ void nr_ue_init_mac(NR_UE_MAC_INST_t *mac) } // NTN ta-Common-r17 - if (get_nrUE_params()->ntn_ta_common) { + if (get_nrUE_params()->ntn_ta_common || get_nrUE_params()->ntn_ta_commondrift) { sib19_r17->ntn_Config_r17->ta_Info_r17 = calloc(1, sizeof(*sib19_r17->ntn_Config_r17->ta_Info_r17)); sib19_r17->ntn_Config_r17->ta_Info_r17->ta_Common_r17 = get_nrUE_params()->ntn_ta_common / 4.072e-6; // ta-Common-r17 is in units of 4.072e-3 µs, ntn_ta_common is in ms + if (get_nrUE_params()->ntn_ta_commondrift) + asn1cCallocOne(sib19_r17->ntn_Config_r17->ta_Info_r17->ta_CommonDrift_r17, get_nrUE_params()->ntn_ta_commondrift / 0.2e-3); // is in units of 0.2e-3 µs/s, ntn_ta_commondrift is in µs/s } nr_rrc_mac_config_req_sib19_r17(mac->ue_id, sib19_r17); From 0eb409f1149ab3c58d5e9cdc7a2dc3e8ea95f31b Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Thu, 1 Aug 2024 13:48:24 +0200 Subject: [PATCH 08/10] NR_UE: Perform autonomous timing advance to compensate for fast time drift with relatively large RTT As we don't have SIB19 with continuously updated ta-Common and ta-CommonDrift values yet, the UE can only adapt it's UL timing according to it's DL drift. This is required for the UE to keep UL sync with fast time drift and relatively long RTT (e.g. LEO satellite). We should replace this with the standard compliant way as soon as we have SIB19 with continuously updated contents. --- executables/nr-softmodem-common.h | 1 - executables/nr-ue.c | 6 ++++++ executables/nr-uesoftmodem.h | 6 +++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/executables/nr-softmodem-common.h b/executables/nr-softmodem-common.h index 205cd6072b..714d885a16 100644 --- a/executables/nr-softmodem-common.h +++ b/executables/nr-softmodem-common.h @@ -98,7 +98,6 @@ #define CONFIG_HLP_TELN "Start embedded telnet server \n" #define CONFIG_HLP_SNR "Set average SNR in dB (for --siml1 option)\n" #define CONFIG_HLP_NOS1 "Disable s1 interface\n" -#define CONFIG_HLP_AGC "Rx Gain control used for UE" /*--------------------------------------------------------------------------------------------------------------------------------*/ /* command line parameters for LOG utility */ diff --git a/executables/nr-ue.c b/executables/nr-ue.c index 9eafacee52..631c270ca1 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -889,6 +889,9 @@ void *UE_thread(void *arg) UE->max_pos_acc = get_nrUE_params()->ntn_ta_commondrift * 1e-6 * fp->samples_per_frame / get_nrUE_params()->time_sync_I; // ntn_ta_commondrift is in µs/s, max_pos_acc * time_sync_I is in samples/frame shiftForNextFrame = -(UE->init_sync_frame + trashed_frames + 2) * UE->max_pos_acc * get_nrUE_params()->time_sync_I; // compensate for the time drift that happened during initial sync LOG_D(PHY, "max_pos_acc = %d, shiftForNextFrame = %d\n", UE->max_pos_acc, shiftForNextFrame); + // TODO: remove this autonomous TA and use up-to-date values of ta-Common, ta-CommonDrift and ta-CommonDriftVariant from received SIB19 instead + if (get_nrUE_params()->autonomous_ta) + UE->timing_advance -= 2 * shiftForNextFrame; // read in first symbol AssertFatal(fp->ofdm_symbol_size + fp->nb_prefix_samples0 == UE->rfdevice.trx_read_func(&UE->rfdevice, @@ -949,6 +952,9 @@ void *UE_thread(void *arg) if (slot_nr == nb_slot_frame - 1) { // we shift of half of measured drift, at each beginning of frame for both rx and tx iq_shift_to_apply = shiftForNextFrame; + // TODO: remove this autonomous TA and use up-to-date values of ta-Common, ta-CommonDrift and ta-CommonDriftVariant from received SIB19 instead + if (get_nrUE_params()->autonomous_ta) + UE->timing_advance -= 2 * shiftForNextFrame; shiftForNextFrame = -round(UE->max_pos_acc * get_nrUE_params()->time_sync_I); } diff --git a/executables/nr-uesoftmodem.h b/executables/nr-uesoftmodem.h index 5b745cec61..5fe68ed60f 100644 --- a/executables/nr-uesoftmodem.h +++ b/executables/nr-uesoftmodem.h @@ -14,6 +14,8 @@ #define CONFIG_HLP_NTN_KOFFSET "NTN cellSpecificKoffset-r17 (number of slots for a given subcarrier spacing of 15 kHz)\n" #define CONFIG_HLP_NTN_TA_COMMON "NTN ta-Common, but given in ms\n" #define CONFIG_HLP_NTN_TA_COMMONDRIFT "NTN ta-CommonDrift, but given in µs/s\n" +#define CONFIG_HLP_AUTONOMOUS_TA "Autonomously update TA based on DL drift (useful if main contribution to DL drift is movement, e.g. LEO satellite)\n" +#define CONFIG_HLP_AGC "Rx Gain control used for UE\n" /***************************************************************************************************************************************/ /* command line options definitions, CMDLINE_XXXX_DESC macros are used to initialize paramdef_t arrays which are then used as argument @@ -67,7 +69,8 @@ {"ntn-koffset", CONFIG_HLP_NTN_KOFFSET, 0, .uptr=&(nrUE_params.ntn_koffset), .defuintval=0, TYPE_UINT, 0}, \ {"ntn-ta-common", CONFIG_HLP_NTN_TA_COMMON, 0, .dblptr=&(nrUE_params.ntn_ta_common), .defdblval=0.0, TYPE_DOUBLE, 0}, \ {"ntn-ta-commondrift", CONFIG_HLP_NTN_TA_COMMONDRIFT, 0, .dblptr=&(nrUE_params.ntn_ta_commondrift), .defdblval=0.0, TYPE_DOUBLE, 0}, \ - {"agc", CONFIG_HLP_AGC, PARAMFLAG_BOOL, .iptr=&(nrUE_params.agc), .defintval=0, TYPE_INT, 0}, \ + {"autonomous-ta", CONFIG_HLP_AUTONOMOUS_TA, PARAMFLAG_BOOL, .iptr=&(nrUE_params.autonomous_ta), .defintval=0, TYPE_INT, 0}, \ + {"agc", CONFIG_HLP_AGC, PARAMFLAG_BOOL, .iptr=&(nrUE_params.agc), .defintval=0, TYPE_INT, 0}, \ } // clang-format on @@ -93,6 +96,7 @@ typedef struct { unsigned int ntn_koffset; double ntn_ta_common; double ntn_ta_commondrift; + int autonomous_ta; int agc; char *usrp_args; char *tx_subdev; From ede6775ac3f8493ebdbedcf2c7cff8fc84101620 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Thu, 1 Aug 2024 17:19:38 +0200 Subject: [PATCH 09/10] fix for compilers that don't support the CMPLX() macro --- radio/rfsimulator/apply_channelmod.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/radio/rfsimulator/apply_channelmod.c b/radio/rfsimulator/apply_channelmod.c index 5bb207e5be..dc35a9bf73 100644 --- a/radio/rfsimulator/apply_channelmod.c +++ b/radio/rfsimulator/apply_channelmod.c @@ -166,7 +166,11 @@ void rxAddInput(const c16_t *input_sig, } if (channelDesc->Doppler_phase_inc != 0.0) { +#ifdef CMPLX double complex in = CMPLX(rx_tmp.r, rx_tmp.i); +#else + double complex in = rx_tmp.r + rx_tmp.i * I; +#endif double complex out = in * cexp(channelDesc->Doppler_phase_cur[rxAnt] * I); rx_tmp.r = creal(out); rx_tmp.i = cimag(out); From b17b44dffb2828ea8b2d042f189ca93e7e916df1 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Thu, 8 Aug 2024 12:22:41 +0200 Subject: [PATCH 10/10] add description how to run a LEO satellite rfsimulation to RUNMODEM.md --- doc/RUNMODEM.md | 70 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 64 insertions(+), 6 deletions(-) diff --git a/doc/RUNMODEM.md b/doc/RUNMODEM.md index 928935f898..378fe3be16 100644 --- a/doc/RUNMODEM.md +++ b/doc/RUNMODEM.md @@ -157,6 +157,42 @@ E.g. to perform a simple simulation of a satellite in geostationary orbit (GEO), --rfsimulator.prop_delay 238.74 ``` +For simulation of a satellite in low earth orbit (LEO), two channel models have been added to rfsimulator: +- `SAT_LEO_TRANS`: transparent LEO satellite with gNB on ground +- `SAT_LEO_REGEN`: regenerative LEO satellite with gNB on board + +Both channel models simulate the delay and Doppler for a circular orbit at 600 km height according to the Matlab function [dopplerShiftCircularOrbit](https://de.mathworks.com/help/satcom/ref/dopplershiftcircularorbit.html). +An example configuration to simulate a transparent LEO satellite with rfsimulator would be: +``` +channelmod = { + max_chan=10; + modellist="modellist_rfsimu_1"; + modellist_rfsimu_1 = ( + { + model_name = "rfsimu_channel_enB0" + type = "SAT_LEO_TRANS"; + noise_power_dB = -100; + }, + { + model_name = "rfsimu_channel_ue0" + type = "SAT_LEO_TRANS"; + noise_power_dB = -100; + } + ); +}; +``` +This configuration is also provided in the file `targets/PROJECTS/GENERIC-NR-5GC/CONF/channelmod_rfsimu_LEO_satellite.conf`. + +Additionally, rfsimulator has to be configured to apply the channel model. +This can be done by either providing this line in the conf file in section `rfsimulator`: +``` + options = ("chanmod"); +``` +Or by providing this the the command line parameters: +``` +--rfsimulator.options chanmod +``` + ### gNB The main parameter to cope with the large NTN propagation delay is the cellSpecificKoffset. @@ -166,11 +202,12 @@ The unit of the field Koffset is number of slots for a given subcarrier spacing This parameter can be provided to the gNB in the conf file as `cellSpecificKoffset_r17` in the section `servingCellConfigCommon`. ``` ... - cellSpecificKoffset_r17 = 478; + cellSpecificKoffset_r17 = 478; # GEO satellite +# cellSpecificKoffset_r17 = 40; # LEO satellite ... ``` -Besides this, some timers, e.g. `sr_ProhibitTimer_v1700`, `t300`, `t301` and `t319`, in the conf file section `gNBs.[0].TIMERS` might need to be extended. +Besides this, some timers, e.g. `sr_ProhibitTimer_v1700`, `t300`, `t301` and `t319`, in the conf file section `gNBs.[0].TIMERS` might need to be extended for GEO satellites. ``` ... TIMERS : @@ -185,7 +222,7 @@ Besides this, some timers, e.g. `sr_ProhibitTimer_v1700`, `t300`, `t301` and `t3 ... ``` -To improve the achievable UL and DL throughput in conditions with large RTT, there is a feature defined in REL17 to disable HARQ feedback. +To improve the achievable UL and DL throughput in conditions with large RTT (esp. GEO satellites), there is a feature defined in REL17 to disable HARQ feedback. This allows to reuse HARQ processes immediately, but it breaks compatibility with UEs not supporting this REL17 feature. To enable this feature, the `disable_harq` flag has to be added to the gNB conf file in the section `gNBs.[0]` ``` @@ -199,7 +236,7 @@ To enable this feature, the `disable_harq` flag has to be added to the gNB conf ... ``` -So with these modifications to the file `targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.25PRB.usrpx300.conf` an example gNB command for FDD, 5 MHz BW, 15 kHz SCS, GEO satellite 5G NR NTN is this: +So with these modifications to the file `targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.25PRB.usrpx300.conf` an example gNB command for FDD, 5 MHz BW, 15 kHz SCS, transparent GEO satellite 5G NR NTN is this: ``` cd cmake_targets sudo ./ran_build/build/nr-softmodem -O ../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.25PRB.usrpx300.conf --sa --rfsim --rfsimulator.prop_delay 238.74 @@ -213,18 +250,39 @@ To configure NTN gNB with 32 HARQ processes in downlink and uplink, add these se ... ``` +To simulate a LEO satellite channel model with rfsimulator in UL (DL is simulated at the UE side) either the `channelmod` section as shown before has to be added to the gNB conf file, or a channelmod conf file has to be included like this: +``` +@include "channelmod_rfsimu_LEO_satellite.conf" +``` + +So with these modifications to the file `targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.25PRB.usrpx300.conf` an example gNB command for FDD, 5 MHz BW, 15 kHz SCS, trasparent LEO satellite 5G NR NTN is this: +``` +cd cmake_targets +sudo ./ran_build/build/nr-softmodem -O ../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.25PRB.usrpx300.conf --sa --rfsim --rfsimulator.prop_delay 20 +``` + ### NR UE At UE side, there are two main parameters to cope with the large NTN propagation delay, cellSpecificKoffset and ta-Common. `cellSpecificKoffset` is the same as for gNB and can be provided to the UE via command line parameter `--ntn-koffset`. -`ta-Common` is a common timong advance and can be provided to the UE via command line parameter `--ntn-ta-common` in milliseconds. +`ta-Common` is a common timing advance and can be provided to the UE via command line parameter `--ntn-ta-common` in milliseconds. -So an example NR UE command for FDD, 5MHz BW, 15 kHz SCS, GEO satellite 5G NR NTN is this: +So an example NR UE command for FDD, 5MHz BW, 15 kHz SCS, transparent GEO satellite 5G NR NTN is this: ``` cd cmake_targets sudo ./ran_build/build/nr-uesoftmodem --band 66 -C 2152680000 --CO -400000000 -r 25 --numerology 0 --ssb 48 --sa --rfsim --rfsimulator.prop_delay 238.74 --ntn-koffset 478 --ntn-ta-common 477.48 ``` +For LEO satellites a third parameter specifying the NTN propagation delay drift has ben added, ta-CommonDrift. +`ta-CommonDrift` provides the drift rate of the common timing advance and can be provided to the UE via command line parameter `--ntn-ta-commondrift` in microseconds per second. +Also, to perform an autonomous TA update based on the DL drift, the boolean parameter `--autonomous-ta` should be added in case of a LEO satellite scenario. + +So an example NR UE command for FDD, 5MHz BW, 15 kHz SCS, transparent LEO satellite 5G NR NTN is this: +``` +cd cmake_targets +sudo ./ran_build/build/nr-uesoftmodem --band 66 -C 2152680000 --CO -400000000 -r 25 --numerology 0 --ssb 48 --sa --rfsim --rfsimulator.prop_delay 20 --rfsimulator.options chanmod -O ../targets/PROJECTS/GENERIC-NR-5GC/CONF/channelmod_rfsimu_LEO_satellite.conf --ntn-koffset 40 --ntn-ta-common 37.74 --ntn-ta-commondrift -50 --autonomous-ta +``` + # Specific OAI modes ## phy-test setup with OAI UE