Commit 68b29301 authored by Robert Schmidt's avatar Robert Schmidt

Merge remote-tracking branch 'origin/ntn_leo_time_comp' into integration_2024_w34

parents 922e810a b17b44df
......@@ -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) \
......
......@@ -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
......
......@@ -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 */
......
......@@ -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);
......
......@@ -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/<the value>). 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;
......
......@@ -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;
}
......@@ -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,
......
......@@ -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
......
......@@ -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);
......
......@@ -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);
......
......@@ -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; aarx<desc->nb_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; i<cd->nb_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,
......
......@@ -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);
......
......@@ -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);
......
......@@ -28,7 +28,7 @@
#include <unistd.h>
#include <stdbool.h>
#include <errno.h>
#include <complex.h>
#include <common/utils/assertions.h>
#include <common/utils/LOG/log.h>
......@@ -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++;
}
......
......@@ -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)
......
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;
}
);
};
......@@ -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"
......@@ -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"
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment