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) \ 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 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 7d643e634e..d80385f847 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; @@ -886,7 +886,12 @@ 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); + // 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, @@ -947,7 +952,10 @@ 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 + // 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); } const int readBlockSize = get_readBlockSize(slot_nr, fp) - iq_shift_to_apply; @@ -991,10 +999,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/executables/nr-uesoftmodem.h b/executables/nr-uesoftmodem.h index 08cafc8379..5fe68ed60f 100644 --- a/executables/nr-uesoftmodem.h +++ b/executables/nr-uesoftmodem.h @@ -9,8 +9,13 @@ #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" +#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 @@ -59,9 +64,13 @@ {"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}, \ + {"ntn-ta-commondrift", CONFIG_HLP_NTN_TA_COMMONDRIFT, 0, .dblptr=&(nrUE_params.ntn_ta_commondrift), .defdblval=0.0, TYPE_DOUBLE, 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 @@ -82,8 +91,12 @@ 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; + double ntn_ta_commondrift; + int autonomous_ta; int agc; char *usrp_args; char *tx_subdev; 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 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); diff --git a/openair1/SIMULATION/TOOLS/random_channel.c b/openair1/SIMULATION/TOOLS/random_channel.c index e9c26e9abd..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]); @@ -2281,7 +2316,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 +2349,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..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" @@ -543,7 +559,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/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); diff --git a/radio/rfsimulator/apply_channelmod.c b/radio/rfsimulator/apply_channelmod.c index 917a22924c..dc35a9bf73 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,20 @@ 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) { +#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); + 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 0a2ece7d21..d944e028bb 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, @@ -708,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); @@ -760,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; @@ -963,6 +970,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 +989,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) 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"