Merge remote-tracking branch 'origin/fix_ntn_leo' into integration_2025_w07 (!3194)
fix NTN LEO scenarios - gNB: add support for NTN parameter ta-CommonDrift-r17 - NR UE: fix application of NTN TA information - NR UE: add command line parameter ntn-initial-time-drift to compensate time drift during initial sync - NR UE: fix writeTimestamp inconsistencies - NR UE: add MAC state UE_RECEIVING_SIB ensuring to start RA only after successfully receiving SIBs - rfsimulator: update earth radius for LEO simulation to match UE position in ue.conf file - update conf files for NTN LEO scenario - update description how to run NTN scenarios in RUNMODEM.md - fix: ta-Common is a round-trip-time, not a one-way delay - NR UE: simplify calculation of next_tx_slot_and_frame by moving up assignment of duration_rx_to_tx - cellbarredNTN indicates notbarred in SIB1 if NTN access is available - Removed b66 NTN and enabled B254 NTN conf file in CI - NR UE: RRC layer now explicitly tells MAC layer when the RA procedure can be started - move NTN LEO config to band 254 and update doc/RUNMODEM.md Closes #901
This commit is contained in:
+62
-58
@@ -491,9 +491,10 @@ static void RU_write(nr_rxtx_thread_data_t *rxtxD, bool sl_tx_action)
|
||||
|
||||
openair0_timestamp writeTimestamp = proc->timestamp_tx;
|
||||
int writeBlockSize = rxtxD->writeBlockSize;
|
||||
if ( writeBlockSize > fp->get_samples_per_slot(proc->nr_slot_tx, fp)) {
|
||||
// if writeBlockSize gets longer that slot size, fill with dummy
|
||||
const int dummyBlockSize = writeBlockSize - fp->get_samples_per_slot(proc->nr_slot_tx, fp);
|
||||
// if writeBlockSize gets longer that slot size, fill with dummy
|
||||
const int maxWriteBlockSize = fp->get_samples_per_slot(proc->nr_slot_tx, fp);
|
||||
while (writeBlockSize > maxWriteBlockSize) {
|
||||
const int dummyBlockSize = min(writeBlockSize - maxWriteBlockSize, maxWriteBlockSize);
|
||||
int tmp = openair0_write_reorder(&UE->rfdevice, writeTimestamp, txp, dummyBlockSize, fp->nb_antennas_tx, flags);
|
||||
AssertFatal(tmp == dummyBlockSize, "");
|
||||
|
||||
@@ -741,29 +742,32 @@ void readFrame(PHY_VARS_NR_UE *UE, openair0_timestamp *timestamp, bool toTrash)
|
||||
}
|
||||
|
||||
void *rxp[NB_ANTENNAS_RX];
|
||||
if (toTrash)
|
||||
for (int i = 0; i < fp->nb_antennas_rx; i++)
|
||||
rxp[i] = malloc16(fp->get_samples_per_slot(0, fp) * 4);
|
||||
|
||||
for (int x = 0; x < num_frames * NR_NUMBER_OF_SUBFRAMES_PER_FRAME; x++) { // two frames for initial sync
|
||||
for (int slot = 0; slot < fp->slots_per_subframe; slot++) {
|
||||
for (int i = 0; i < fp->nb_antennas_rx; i++) {
|
||||
if (toTrash)
|
||||
rxp[i] = malloc16(fp->get_samples_per_slot(slot, fp) * 4);
|
||||
else
|
||||
if (!toTrash)
|
||||
for (int i = 0; i < fp->nb_antennas_rx; i++)
|
||||
rxp[i] = ((void *)&UE->common_vars.rxdata[i][0])
|
||||
+ 4 * ((x * fp->samples_per_subframe) + fp->get_samples_slot_timestamp(slot, fp, 0));
|
||||
}
|
||||
|
||||
int read_block_size = fp->get_samples_per_slot(slot, fp);
|
||||
int tmp = UE->rfdevice.trx_read_func(&UE->rfdevice, timestamp, rxp, read_block_size, fp->nb_antennas_rx);
|
||||
UEscopeCopy(UE, ueTimeDomainSamplesBeforeSync, rxp[0], sizeof(c16_t), 1, read_block_size, 0);
|
||||
AssertFatal(read_block_size == tmp, "");
|
||||
|
||||
if (IS_SOFTMODEM_RFSIM)
|
||||
dummyWrite(UE, *timestamp, fp->get_samples_per_slot(slot, fp));
|
||||
if (toTrash)
|
||||
for (int i = 0; i < fp->nb_antennas_rx; i++)
|
||||
free(rxp[i]);
|
||||
if (IS_SOFTMODEM_RFSIM) {
|
||||
const openair0_timestamp writeTimestamp = *timestamp + fp->get_samples_slot_timestamp(slot, fp, NR_UE_CAPABILITY_SLOT_RX_TO_TX) - UE->N_TA_offset;
|
||||
dummyWrite(UE, writeTimestamp, fp->get_samples_per_slot(slot, fp));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (toTrash)
|
||||
for (int i = 0; i < fp->nb_antennas_rx; i++)
|
||||
free(rxp[i]);
|
||||
}
|
||||
|
||||
static void syncInFrame(PHY_VARS_NR_UE *UE, openair0_timestamp *timestamp, openair0_timestamp rx_offset)
|
||||
@@ -774,27 +778,18 @@ static void syncInFrame(PHY_VARS_NR_UE *UE, openair0_timestamp *timestamp, opena
|
||||
|
||||
LOG_I(PHY, "Resynchronizing RX by %ld samples\n", rx_offset);
|
||||
|
||||
if (IS_SOFTMODEM_IQPLAYER || IS_SOFTMODEM_IQRECORDER) {
|
||||
// Resynchonize by slot (will work with numerology 1 only)
|
||||
for (int size = rx_offset; size > 0; size -= fp->samples_per_subframe / 2) {
|
||||
int unitTransfer = size > fp->samples_per_subframe / 2 ? fp->samples_per_subframe / 2 : size;
|
||||
int tmp =
|
||||
UE->rfdevice.trx_read_func(&UE->rfdevice, timestamp, (void **)UE->common_vars.rxdata, unitTransfer, fp->nb_antennas_rx);
|
||||
DevAssert(unitTransfer == tmp);
|
||||
}
|
||||
} else {
|
||||
*timestamp += fp->get_samples_per_slot(1, fp);
|
||||
for (int size = rx_offset; size > 0; size -= fp->samples_per_subframe) {
|
||||
int unitTransfer = size > fp->samples_per_subframe ? fp->samples_per_subframe : size;
|
||||
// we write before read because gNB waits for UE to write and both executions halt
|
||||
// this happens here as the read size is samples_per_subframe which is very much larger than samp_per_slot
|
||||
if (IS_SOFTMODEM_RFSIM)
|
||||
dummyWrite(UE, *timestamp, unitTransfer);
|
||||
int res =
|
||||
UE->rfdevice.trx_read_func(&UE->rfdevice, timestamp, (void **)UE->common_vars.rxdata, unitTransfer, fp->nb_antennas_rx);
|
||||
DevAssert(unitTransfer == res);
|
||||
*timestamp += unitTransfer; // this does not affect the read but needed for RFSIM write
|
||||
int slot = 0;
|
||||
int size = rx_offset;
|
||||
while (size > 0) {
|
||||
const int unitTransfer = min(fp->get_samples_per_slot(slot, fp), size);
|
||||
const int res = UE->rfdevice.trx_read_func(&UE->rfdevice, timestamp, (void **)UE->common_vars.rxdata, unitTransfer, fp->nb_antennas_rx);
|
||||
DevAssert(unitTransfer == res);
|
||||
if (IS_SOFTMODEM_RFSIM) {
|
||||
const openair0_timestamp writeTimestamp = *timestamp + fp->get_samples_slot_timestamp(slot, fp, NR_UE_CAPABILITY_SLOT_RX_TO_TX) - UE->N_TA_offset;
|
||||
dummyWrite(UE, writeTimestamp, unitTransfer);
|
||||
}
|
||||
slot = (slot + 1) % fp->slots_per_subframe;
|
||||
size -= unitTransfer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -814,6 +809,33 @@ static inline int get_readBlockSize(uint16_t slot, NR_DL_FRAME_PARMS *fp) {
|
||||
return rem_samples + next_slot_first_symbol;
|
||||
}
|
||||
|
||||
static inline void apply_ntn_config(PHY_VARS_NR_UE *UE,
|
||||
NR_DL_FRAME_PARMS *fp,
|
||||
int slot_nr,
|
||||
bool *update_ntn_system_information,
|
||||
int *duration_rx_to_tx,
|
||||
int *timing_advance,
|
||||
int *ntn_koffset)
|
||||
{
|
||||
if (*update_ntn_system_information) {
|
||||
*update_ntn_system_information = false;
|
||||
|
||||
*duration_rx_to_tx = NR_UE_CAPABILITY_SLOT_RX_TO_TX + UE->ntn_config_message->ntn_config_params.cell_specific_k_offset;
|
||||
UE->timing_advance = fp->samples_per_subframe * UE->ntn_config_message->ntn_config_params.ntn_total_time_advance_ms;
|
||||
*timing_advance +=
|
||||
fp->get_samples_slot_timestamp(slot_nr,
|
||||
fp,
|
||||
UE->ntn_config_message->ntn_config_params.cell_specific_k_offset - *ntn_koffset);
|
||||
*ntn_koffset = UE->ntn_config_message->ntn_config_params.cell_specific_k_offset;
|
||||
|
||||
LOG_I(PHY,
|
||||
"cell_specific_k_offset = %d ms, ntn_total_time_advance_ms = %f ms (%d samples)\n",
|
||||
*ntn_koffset,
|
||||
UE->ntn_config_message->ntn_config_params.ntn_total_time_advance_ms,
|
||||
UE->timing_advance);
|
||||
}
|
||||
}
|
||||
|
||||
void *UE_thread(void *arg)
|
||||
{
|
||||
//this thread should be over the processing thread to keep in real time
|
||||
@@ -869,7 +891,7 @@ void *UE_thread(void *arg)
|
||||
readFrame(UE, &tmp, true);
|
||||
}
|
||||
|
||||
double ntn_ta_common = 0;
|
||||
double ntn_init_time_drift = get_nrUE_params()->ntn_init_time_drift;
|
||||
int ntn_koffset = 0;
|
||||
|
||||
int duration_rx_to_tx = NR_UE_CAPABILITY_SLOT_RX_TO_TX;
|
||||
@@ -958,8 +980,8 @@ void *UE_thread(void *arg)
|
||||
syncInFrame(UE, &sync_timestamp, intialSyncOffset);
|
||||
openair0_write_reorder_clear_context(&UE->rfdevice);
|
||||
if (get_nrUE_params()->time_sync_I)
|
||||
// ntn_ta_commondrift is in µs/s, max_pos_acc * time_sync_I is in samples/frame
|
||||
UE->max_pos_acc = mac->ntn_ta.ntn_ta_commondrift * 1e-6 * fp->samples_per_frame / get_nrUE_params()->time_sync_I;
|
||||
// ntn_init_time_drift is in µs/s, max_pos_acc * time_sync_I is in samples/frame
|
||||
UE->max_pos_acc = ntn_init_time_drift * 1e-6 * fp->samples_per_frame / get_nrUE_params()->time_sync_I;
|
||||
else
|
||||
UE->max_pos_acc = 0;
|
||||
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
|
||||
@@ -1000,16 +1022,6 @@ void *UE_thread(void *arg)
|
||||
absolute_slot++;
|
||||
TracyCFrameMark;
|
||||
|
||||
if (update_ntn_system_information) {
|
||||
update_ntn_system_information = false;
|
||||
int ta_offset = UE->frame_parms.samples_per_subframe * (UE->ntn_config_message->ntn_config_params.ntn_total_time_advance_ms - ntn_ta_common);
|
||||
|
||||
UE->timing_advance += ta_offset;
|
||||
ntn_ta_common = UE->ntn_config_message->ntn_config_params.ntn_total_time_advance_ms;
|
||||
ntn_koffset = UE->ntn_config_message->ntn_config_params.cell_specific_k_offset;
|
||||
timing_advance = ntn_koffset * (UE->frame_parms.samples_per_subframe >> mac->current_UL_BWP->scs);
|
||||
}
|
||||
|
||||
if (UE->ntn_config_message->update) {
|
||||
UE->ntn_config_message->update = false;
|
||||
update_ntn_system_information = true;
|
||||
@@ -1098,6 +1110,9 @@ void *UE_thread(void *arg)
|
||||
shiftForNextFrame = ret;
|
||||
pushNotifiedFIFO(&UE->dl_actors[curMsg.proc.nr_slot_rx % NUM_DL_ACTORS].fifo, newRx);
|
||||
|
||||
// apply new duration next run to avoid thread dead lock
|
||||
apply_ntn_config(UE, fp, slot_nr, &update_ntn_system_information, &duration_rx_to_tx, &timing_advance, &ntn_koffset);
|
||||
|
||||
// Start TX slot processing here. It runs in parallel with RX slot processing
|
||||
// in current code, DURATION_RX_TO_TX constant is the limit to get UL data to encode from a RX slot
|
||||
notifiedFIFO_elt_t *newTx = newNotifiedFIFO_elt(sizeof(nr_rxtx_thread_data_t), 0, 0, processSlotTX);
|
||||
@@ -1111,16 +1126,9 @@ void *UE_thread(void *arg)
|
||||
|
||||
int slot = curMsgTx->proc.nr_slot_tx;
|
||||
int slot_and_frame = slot + curMsgTx->proc.frame_tx * UE->frame_parms.slots_per_frame;
|
||||
|
||||
int next_tx_slot_and_frame = absolute_slot + duration_rx_to_tx + 1;
|
||||
int wait_for_prev_slot = stream_status == STREAM_STATUS_SYNCED ? 1 : 0;
|
||||
|
||||
int next_duration_rx_to_tx =
|
||||
update_ntn_system_information
|
||||
? NR_UE_CAPABILITY_SLOT_RX_TO_TX + UE->ntn_config_message->ntn_config_params.cell_specific_k_offset
|
||||
: duration_rx_to_tx;
|
||||
int next_nr_slot_tx = (absolute_slot + next_duration_rx_to_tx) % nb_slot_frame;
|
||||
int next_frame_tx = ((absolute_slot + next_duration_rx_to_tx) / nb_slot_frame) % MAX_FRAME_NUMBER;
|
||||
int next_tx_slot_and_frame = next_nr_slot_tx + next_frame_tx * UE->frame_parms.slots_per_frame + 1;
|
||||
dynamic_barrier_t *next_barrier = &UE->process_slot_tx_barriers[next_tx_slot_and_frame % NUM_PROCESS_SLOT_TX_BARRIERS];
|
||||
curMsgTx->next_barrier = next_barrier;
|
||||
dynamic_barrier_update(&UE->process_slot_tx_barriers[slot_and_frame % NUM_PROCESS_SLOT_TX_BARRIERS],
|
||||
@@ -1129,10 +1137,6 @@ void *UE_thread(void *arg)
|
||||
newTx);
|
||||
stream_status = STREAM_STATUS_SYNCED;
|
||||
tx_wait_for_dlsch[slot] = 0;
|
||||
// apply new duration next run to avoid thread dead lock
|
||||
if (update_ntn_system_information) {
|
||||
duration_rx_to_tx = NR_UE_CAPABILITY_SLOT_RX_TO_TX + UE->ntn_config_message->ntn_config_params.cell_specific_k_offset;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#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_INIT_TIME_DRIFT "Initial NTN DL time drift (feeder link and service link), 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"
|
||||
|
||||
@@ -62,6 +63,7 @@
|
||||
{"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.0, TYPE_DOUBLE, 0}, \
|
||||
{"ntn-initial-time-drift", CONFIG_HLP_NTN_INIT_TIME_DRIFT, 0, .dblptr=&(nrUE_params.ntn_init_time_drift), .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}, \
|
||||
}
|
||||
@@ -85,6 +87,7 @@ typedef struct {
|
||||
int ssb_start_subcarrier;
|
||||
double time_sync_P;
|
||||
double time_sync_I;
|
||||
double ntn_init_time_drift;
|
||||
int autonomous_ta;
|
||||
int agc;
|
||||
char *usrp_args;
|
||||
|
||||
Reference in New Issue
Block a user