Merge remote-tracking branch 'origin/ntn_leo_freq_comp' into integration_2025_w15 (!3355)
NR UE: add support for continuous frequency offset compensation (in particular NTN LEO Doppler)
- move function add_noise() to file
openair1/SIMULATION/TOOLS/phase_noise.c
- refactor (co)sinus calculation out of phase_noise() into get_sin_cos()
in nr_common.c
- NR UE: continuously estimate the frequency offset using PBCH DMRS
- NR UE: add command line parameter --initial-fo to specify known
initial frequency offset
- NR UE: add support for continuous DL frequency offset compensation
--cont-fo-comp
- NR UE: add support for continuous UL frequency offset
pre-compensation. Depending on the main source of frequency offset,
pre-compensate the UL frequency offset (FO) accordingly. Therefore
modify the flag --cont-fo-comp to accept an integer value, where
* 1 specifies, that the main FO contribution comes from the accuracy of the local oscillator (LO)
* 2 specifies, that the main FO contribution comes from Doppler shift
- NR UE: add PI controller to continuous frequency offset compensation
- rfsimulator: enable and correct NTN LEO doppler simulation
- use SIMD to speed up nr_fo_compensation()
- Extend NTN LEO example in RUNMODEM.md to include continuous Doppler
compensation
- NR UE: in nr_adjust_synch_ue() extend the peak search to +/- cyclic
prefix size, esp. for LEO NTN scenarios the timing drifts during
initial sync, which is mostly compensated with
--ntn-initial-time-drift. But there might be a residual time offset,
and by extending the peak search window we make this more tolerant.
- NR UE: remove unused nr_slot_tx_offset, it is unused since commit
f3e60a14
- NR UE: remove now unnecessary local variable
update_ntn_system_information, directly use
UE->ntn_config_message->update instead
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
#include "nr_modulation.h"
|
||||
#include "PHY/LTE_ESTIMATION/lte_estimation.h"
|
||||
#include "PHY/NR_UE_ESTIMATION/nr_estimation.h"
|
||||
#include "PHY/nr_phy_common/inc/nr_phy_common.h"
|
||||
#include <common/utils/LOG/log.h>
|
||||
|
||||
//#define DEBUG_FEP
|
||||
@@ -108,6 +109,12 @@ int nr_slot_fep(PHY_VARS_NR_UE *ue,
|
||||
rxdata_ptr = (int16_t *)tmp_dft_in;
|
||||
}
|
||||
|
||||
if (ue && ue->cont_fo_comp) {
|
||||
start_meas_nr_ue_phy(ue, RX_FO_COMPENSATION_STATS);
|
||||
nr_fo_compensation(ue->freq_offset, frame_parms->samples_per_subframe, rx_offset, (c16_t *)rxdata_ptr, frame_parms->ofdm_symbol_size);
|
||||
stop_meas_nr_ue_phy(ue, RX_FO_COMPENSATION_STATS);
|
||||
}
|
||||
|
||||
if (ue)
|
||||
start_meas_nr_ue_phy(ue, RX_DFT_STATS);
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ int nr_adjust_synch_ue(NR_DL_FRAME_PARMS *frame_parms,
|
||||
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_ADJUST_SYNCH, VCD_FUNCTION_IN);
|
||||
|
||||
// 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++) {
|
||||
for (int i = -frame_parms->nb_prefix_samples; i < frame_parms->nb_prefix_samples; i++) {
|
||||
int temp = 0;
|
||||
|
||||
int j = (i < 0) ? (i + frame_parms->ofdm_symbol_size) : i;
|
||||
|
||||
@@ -199,6 +199,9 @@ void nr_scan_ssb(void *arg)
|
||||
// initial sync performed on two successive frames, if pbch passes on first frame, no need to process second frame
|
||||
// only one frame is used for simulation tools
|
||||
for (int frame_id = 0; frame_id < ssbInfo->nFrames && !ssbInfo->syncRes.cell_detected; frame_id++) {
|
||||
if (ssbInfo->freqOffset)
|
||||
compensate_freq_offset(rxdata, fp, ssbInfo->freqOffset, frame_id);
|
||||
|
||||
/* process pss search on received buffer */
|
||||
ssbInfo->syncRes.frame_id = frame_id;
|
||||
int nid2;
|
||||
@@ -268,7 +271,7 @@ void nr_scan_ssb(void *arg)
|
||||
phase_tdd_ncp,
|
||||
ssbInfo->syncRes.rx_offset);
|
||||
#endif
|
||||
ssbInfo->freqOffset = freq_offset_pss + freq_offset_sss;
|
||||
ssbInfo->freqOffset += freq_offset_pss + freq_offset_sss;
|
||||
|
||||
if (ssbInfo->syncRes.cell_detected) { // we got sss channel
|
||||
ssbInfo->syncRes.cell_detected = nr_pbch_detection(ssbInfo->proc,
|
||||
@@ -319,6 +322,7 @@ nr_initial_sync_t nr_initial_sync(UE_nr_rxtx_proc_t *proc,
|
||||
.syncRes.cell_detected = false,
|
||||
.nFrames = n_frames,
|
||||
.foFlag = ue->UE_fo_compensation,
|
||||
.freqOffset = ue->initial_fo,
|
||||
.targetNidCell = ue->target_Nid_cell};
|
||||
ssbInfo->rxdata = malloc16_clear(fp->nb_antennas_rx * sizeof(c16_t *));
|
||||
for (int ant = 0; ant < fp->nb_antennas_rx; ant++) {
|
||||
|
||||
@@ -525,3 +525,21 @@ int nr_rx_pbch(PHY_VARS_NR_UE *ue,
|
||||
TracyCZoneEnd(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
double nr_ue_pbch_freq_offset(const NR_DL_FRAME_PARMS *frame_parms,
|
||||
int estimateSz,
|
||||
const c16_t dl_ch_estimates[][estimateSz])
|
||||
{
|
||||
const int i_ssb = frame_parms->ssb_index;
|
||||
const int symbol_offset = nr_get_ssb_start_symbol(frame_parms, i_ssb) % frame_parms->symbols_per_slot;
|
||||
const c16_t *dl_ch_est_symb1 = &dl_ch_estimates[0][(symbol_offset + 1) * frame_parms->ofdm_symbol_size];
|
||||
const c16_t *dl_ch_est_symb3 = &dl_ch_estimates[0][(symbol_offset + 3) * frame_parms->ofdm_symbol_size];
|
||||
const int nb_re = 240;
|
||||
const c32_t dot_prod_res = dot_product(dl_ch_est_symb1, dl_ch_est_symb3, nb_re, 8);
|
||||
const double res_phase = atan2(dot_prod_res.i, dot_prod_res.r);
|
||||
const int samples_per_symbol = frame_parms->ofdm_symbol_size + frame_parms->nb_prefix_samples;
|
||||
const double t_ofdm = samples_per_symbol / (frame_parms->samples_per_subframe * 1000.0); // symbol duration in sec
|
||||
const double freq_offset = res_phase / (2 * M_PI * (3 - 1) * t_ofdm);
|
||||
|
||||
return freq_offset;
|
||||
}
|
||||
|
||||
@@ -204,6 +204,10 @@ int nr_rx_pbch(PHY_VARS_NR_UE *ue,
|
||||
int rxdataFSize,
|
||||
const struct complex16 rxdataF[][rxdataFSize]);
|
||||
|
||||
double nr_ue_pbch_freq_offset(const NR_DL_FRAME_PARMS *frame_parms,
|
||||
int estimateSz,
|
||||
const c16_t dl_ch_estimates[][estimateSz]);
|
||||
|
||||
#ifndef modOrder
|
||||
#define modOrder(I_MCS,I_TBS) ((I_MCS-I_TBS)*2+2) // Find modulation order from I_TBS and I_MCS
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include "tools_defs.h"
|
||||
|
||||
#define LUT_SIN_RESOL 128
|
||||
// look-up table for the sine (cosine) function
|
||||
static uint16_t LUTSin[LUT_SIN_RESOL + 1];
|
||||
|
||||
void InitSinLUT(void)
|
||||
{
|
||||
for (int i = 0; i < (LUT_SIN_RESOL + 1); i++) {
|
||||
LUTSin[i] = round(sin((M_PI * i) / (2 * LUT_SIN_RESOL)) * (1 << 14)); // Format: Q14
|
||||
}
|
||||
}
|
||||
|
||||
// fast calculation of e^{i*2*pi*phase}
|
||||
// ret.r == cosinus << 14
|
||||
// ret.i == sinus << 14
|
||||
c16_t get_sin_cos(double phase)
|
||||
{
|
||||
int index;
|
||||
if (phase < 0) {
|
||||
index = (int)(-phase * 4 * LUT_SIN_RESOL + 0.5) % (4 * LUT_SIN_RESOL);
|
||||
index = 4 * LUT_SIN_RESOL - index;
|
||||
} else {
|
||||
index = (int)(phase * 4 * LUT_SIN_RESOL + 0.5) % (4 * LUT_SIN_RESOL);
|
||||
}
|
||||
|
||||
c16_t ret;
|
||||
if (index < 2 * LUT_SIN_RESOL) { // check for 1st and 2nd Quadrant
|
||||
if (index < LUT_SIN_RESOL) { // 1st Quadrant
|
||||
ret.r = LUTSin[LUT_SIN_RESOL - index];
|
||||
ret.i = LUTSin[index];
|
||||
} else { // 2nd Quadrant
|
||||
ret.r = -LUTSin[index - LUT_SIN_RESOL];
|
||||
ret.i = LUTSin[2 * LUT_SIN_RESOL - index];
|
||||
}
|
||||
} else { // 3rd and 4th Quadrant
|
||||
if (index < 3 * LUT_SIN_RESOL) { // 3rd Quadrant
|
||||
ret.r = -LUTSin[3 * LUT_SIN_RESOL - index];
|
||||
ret.i = -LUTSin[index - 2 * LUT_SIN_RESOL];
|
||||
} else { // 4th Quadrant
|
||||
ret.r = LUTSin[index - 3 * LUT_SIN_RESOL];
|
||||
ret.i = -LUTSin[4 * LUT_SIN_RESOL - index];
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -792,6 +792,13 @@ double interp(double x, double *xs, double *ys, int count);
|
||||
void simde_mm128_separate_real_imag_parts(simde__m128i *out_re, simde__m128i *out_im, simde__m128i in0, simde__m128i in1);
|
||||
void simde_mm256_separate_real_imag_parts(simde__m256i *out_re, simde__m256i *out_im, simde__m256i in0, simde__m256i in1);
|
||||
|
||||
void InitSinLUT(void);
|
||||
|
||||
// fast calculation of e^{i*2*pi*phase}
|
||||
// ret.r == cosinus << 14
|
||||
// ret.i == sinus << 14
|
||||
c16_t get_sin_cos(double phase);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -460,6 +460,11 @@ typedef struct PHY_VARS_NR_UE_s {
|
||||
int64_t max_pos_iir; /// Timing offset IIR filter
|
||||
int max_pos_acc; /// Timing offset accumuluated error for PI filter
|
||||
|
||||
double initial_fo; /// initial frequency offset provided by the user
|
||||
int cont_fo_comp; /// flag enabling the continuous frequency offset estimation and compensation
|
||||
double freq_offset; /// currently compensated frequency offset
|
||||
double freq_off_acc; /// accumulated frequency error (for PI controller)
|
||||
|
||||
/// Timing Advance updates variables
|
||||
/// Timing advance update computed from the TA command signalled from gNB
|
||||
int timing_advance;
|
||||
@@ -525,10 +530,6 @@ typedef struct {
|
||||
int gNB_id;
|
||||
/// NR slot index within frame_tx [0 .. slots_per_frame - 1] to act upon for transmission
|
||||
int nr_slot_tx;
|
||||
/// NR slot index tx offset to resume
|
||||
/// in case of NTN, tx_offset can be changed dynamically via SIB19
|
||||
/// we need to notify the right tx thread slot based on TX offset change
|
||||
int nr_slot_tx_offset;
|
||||
int rx_slot_type;
|
||||
/// NR slot index within frame_rx [0 .. slots_per_frame - 1] to act upon for transmission
|
||||
int nr_slot_rx;
|
||||
|
||||
@@ -80,4 +80,6 @@ void nr_256qam_llr(int32_t *rxdataF_comp, int32_t *ch_mag, int32_t *ch_mag2, int
|
||||
void freq2time(uint16_t ofdm_symbol_size, int16_t *freq_signal, int16_t *time_signal);
|
||||
void nr_est_delay(int ofdm_symbol_size, const c16_t *ls_est, c16_t *ch_estimates_time, delay_t *delay);
|
||||
unsigned int nr_get_tx_amp(int power_dBm, int power_max_dBm, int total_nb_rb, int nb_rb);
|
||||
void nr_fo_compensation(double fo_Hz, int samples_per_ms, int sample_offset, c16_t *rxdata_ptr, int size);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#define FOREACH_NR_PHY_CPU_MEAS(FN) \
|
||||
FN(RX_PDSCH_STATS),\
|
||||
FN(DLSCH_RX_PDCCH_STATS),\
|
||||
FN(RX_FO_COMPENSATION_STATS),\
|
||||
FN(RX_DFT_STATS),\
|
||||
FN(DLSCH_CHANNEL_ESTIMATION_STATS),\
|
||||
FN(DLSCH_DECODING_STATS),\
|
||||
|
||||
@@ -453,3 +453,40 @@ unsigned int nr_get_tx_amp(int power_dBm, int power_max_dBm, int total_nb_rb, in
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
void nr_fo_compensation(double fo_Hz, int samples_per_ms, int sample_offset, c16_t *rxdata_ptr, int size)
|
||||
{
|
||||
const double phase_inc = -fo_Hz / (samples_per_ms * 1000);
|
||||
double phase = sample_offset * phase_inc;
|
||||
phase -= (int)phase;
|
||||
#if 1
|
||||
// The bottleneck is the calculation of the complex rotation values using get_sin_cos().
|
||||
// This code path does not compute these values for the complete OFDM symbol, but only for a smaller CHUNK size.
|
||||
// After applying the rotation to a CHUNK size of the output, these rotation values are efficiently rotated further by `rot_vec`.
|
||||
// Unfortunately, this propagates small errors from one chunk to the next.
|
||||
// Therefore, there is a tradeoff between speed (better with small CHUNK sizes) and accuracy (better with large CHUNK sizes).
|
||||
#define CHUNK 128
|
||||
c16_t rot[CHUNK] __attribute__((aligned(32)));
|
||||
for (int i = 0; i < CHUNK; i++) {
|
||||
rot[i] = get_sin_cos(phase);
|
||||
phase += phase_inc;
|
||||
}
|
||||
const c16_t rot_vec = get_sin_cos(CHUNK * phase_inc);
|
||||
while (size > CHUNK) {
|
||||
mult_complex_vectors(rxdata_ptr, rot, rxdata_ptr, CHUNK, 14);
|
||||
rxdata_ptr += CHUNK;
|
||||
rotate_cpx_vector(rot, &rot_vec, rot, CHUNK, 14);
|
||||
size -= CHUNK;
|
||||
}
|
||||
mult_complex_vectors(rxdata_ptr, rot, rxdata_ptr, size, 14);
|
||||
#else
|
||||
// This code path computes the complex rotation values for the complete OFDM symbol using get_sin_cos().
|
||||
// This is more accurate, but also slower than the code path above.
|
||||
c16_t rot[size] __attribute__((aligned(32)));
|
||||
for (int i = 0; i < size; i++) {
|
||||
rot[i] = get_sin_cos(phase);
|
||||
phase += phase_inc;
|
||||
}
|
||||
mult_complex_vectors(rxdata_ptr, rot, rxdata_ptr, size, 14);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -929,6 +929,17 @@ int pbch_pdcch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_
|
||||
sampleShift =
|
||||
nr_adjust_synch_ue(fp, ue, gNB_id, fp->ofdm_symbol_size, dl_ch_estimates_time, frame_rx, nr_slot_rx, 16384);
|
||||
}
|
||||
|
||||
if (get_nrUE_params()->cont_fo_comp && pbchSuccess == 0) {
|
||||
double freq_offset = nr_ue_pbch_freq_offset(fp, estimateSz, dl_ch_estimates);
|
||||
LOG_D(PHY,"compensated frequency offset = %.3f Hz, detected residual frequency offset = %.3f Hz, accumulated frequency offset = %.3f Hz\n", ue->freq_offset, freq_offset, ue->freq_off_acc);
|
||||
|
||||
// PI controller
|
||||
const double PID_P = get_nrUE_params()->freq_sync_P;
|
||||
const double PID_I = get_nrUE_params()->freq_sync_I;
|
||||
ue->freq_offset += freq_offset * PID_P + ue->freq_off_acc * PID_I;
|
||||
ue->freq_off_acc += freq_offset;
|
||||
}
|
||||
}
|
||||
LOG_D(PHY, "Doing N0 measurements in %s\n", __FUNCTION__);
|
||||
nr_ue_rrc_measurements(ue, proc, rxdataF);
|
||||
|
||||
@@ -143,31 +143,6 @@ void __attribute__ ((no_sanitize_address)) multipath_channel(channel_desc_t *des
|
||||
|
||||
#else
|
||||
|
||||
void add_noise(c16_t **rxdata,
|
||||
const double **r_re,
|
||||
const double **r_im,
|
||||
const double sigma,
|
||||
const int length,
|
||||
const int slot_offset,
|
||||
const double ts,
|
||||
const int delay,
|
||||
const uint16_t pdu_bit_map,
|
||||
const uint16_t ptrs_bit_map,
|
||||
const uint8_t nb_antennas_rx)
|
||||
{
|
||||
for (int i = 0; i < length; i++) {
|
||||
for (int ap = 0; ap < nb_antennas_rx; ap++) {
|
||||
c16_t *rxd = &rxdata[ap][slot_offset + i + delay];
|
||||
rxd->r = r_re[ap][i] + sqrt(sigma / 2) * gaussZiggurat(0.0, 1.0); // convert to fixed point
|
||||
rxd->i = r_im[ap][i] + sqrt(sigma / 2) * gaussZiggurat(0.0, 1.0);
|
||||
/* Add phase noise if enabled */
|
||||
if (pdu_bit_map & ptrs_bit_map) {
|
||||
phase_noise(ts, &rxdata[ap][slot_offset + i + delay].r, &rxdata[ap][slot_offset + i + delay].i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void __attribute__ ((no_sanitize_address)) multipath_channel(channel_desc_t *desc,
|
||||
double *tx_sig_re[NB_ANTENNAS_TX],
|
||||
double *tx_sig_im[NB_ANTENNAS_TX],
|
||||
|
||||
@@ -23,47 +23,41 @@
|
||||
#include "sim.h"
|
||||
|
||||
|
||||
static uint16_t LUTSin[ResolSinCos+1];
|
||||
/* linear phase noise model */
|
||||
void phase_noise(double ts, int16_t * InRe, int16_t * InIm)
|
||||
void phase_noise(double ts, int16_t *InRe, int16_t *InIm)
|
||||
{
|
||||
static uint64_t i=0;
|
||||
int32_t x=0 ,y=0;
|
||||
double fd = 300;//0.01*30000
|
||||
int16_t SinValue = 0, CosValue= 0;
|
||||
double IdxDouble = (double)(i*fd * ts * ResolSinCos * 4);
|
||||
int16_t IdxModulo = ((int32_t)(IdxDouble>0 ? IdxDouble+0.5 : IdxDouble-0.5)) % (ResolSinCos*4);
|
||||
IdxModulo = IdxModulo<0 ? IdxModulo+ResolSinCos*4 : IdxModulo;
|
||||
|
||||
if(IdxModulo<2*ResolSinCos) {//< 2 check for 1st and 2nd
|
||||
if(IdxModulo < ResolSinCos) {// 1st Quadrant
|
||||
SinValue = LUTSin[IdxModulo];
|
||||
CosValue = LUTSin[ResolSinCos-IdxModulo];
|
||||
}
|
||||
else {// 2nd Quadrant
|
||||
SinValue = LUTSin[2*ResolSinCos-IdxModulo];
|
||||
CosValue = -LUTSin[IdxModulo-ResolSinCos];
|
||||
}
|
||||
}
|
||||
else {// 3rd and 4th Quadrant
|
||||
if(IdxModulo < 3*ResolSinCos) {// 3rd Quadrant
|
||||
SinValue = -LUTSin[IdxModulo-2*ResolSinCos];
|
||||
CosValue = -LUTSin[3*ResolSinCos-IdxModulo];
|
||||
}
|
||||
else {//4th Quadrant
|
||||
SinValue = -LUTSin[4*ResolSinCos-IdxModulo];
|
||||
CosValue = LUTSin[IdxModulo-3*ResolSinCos];
|
||||
}
|
||||
}
|
||||
x = ( ((int32_t)InRe[0] * CosValue) - ((int32_t)InIm[0] * SinValue ));
|
||||
y = ( ((int32_t)InIm[0] * CosValue) + ((int32_t)InRe[0] * SinValue ));
|
||||
InRe[0]= (int16_t)(x>>14);
|
||||
InIm[0]= (int16_t)(y>>14);
|
||||
double phase = (double)(i * fd * ts);
|
||||
c16_t rot = get_sin_cos(phase);
|
||||
c16_t val = {.r = *InRe, .i = *InIm};
|
||||
val = c16mulShift(val, rot, 14);
|
||||
*InRe = val.r;
|
||||
*InIm = val.i;
|
||||
i++;
|
||||
}
|
||||
/* Initialisation function for SIN table values */
|
||||
void InitSinLUT( void ) {
|
||||
for ( int i=0; i<(ResolSinCos+1); i++ ) {
|
||||
LUTSin[i] = sin((double)(M_PI*i)/(2*ResolSinCos)) * (1<<14); //Format: Q14
|
||||
|
||||
void add_noise(c16_t **rxdata,
|
||||
const double **r_re,
|
||||
const double **r_im,
|
||||
const double sigma,
|
||||
const int length,
|
||||
const int slot_offset,
|
||||
const double ts,
|
||||
const int delay,
|
||||
const uint16_t pdu_bit_map,
|
||||
const uint16_t ptrs_bit_map,
|
||||
const uint8_t nb_antennas_rx)
|
||||
{
|
||||
for (int i = 0; i < length; i++) {
|
||||
for (int ap = 0; ap < nb_antennas_rx; ap++) {
|
||||
c16_t *rxd = &rxdata[ap][slot_offset + i + delay];
|
||||
rxd->r = r_re[ap][i] + sqrt(sigma / 2) * gaussZiggurat(0.0, 1.0); // convert to fixed point
|
||||
rxd->i = r_im[ap][i] + sqrt(sigma / 2) * gaussZiggurat(0.0, 1.0);
|
||||
/* Add phase noise if enabled */
|
||||
if (pdu_bit_map & ptrs_bit_map) {
|
||||
phase_noise(ts, &rxdata[ap][slot_offset + i + delay].r, &rxdata[ap][slot_offset + i + delay].i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1663,7 +1663,7 @@ channel_desc_t *new_channel_desc_scm(uint8_t nb_tx,
|
||||
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
|
||||
chan_desc->enable_dynamic_Doppler = true;
|
||||
fill_channel_desc(chan_desc,nb_tx,
|
||||
nb_rx,
|
||||
nb_taps,
|
||||
@@ -1762,6 +1762,11 @@ void set_channeldesc_name(channel_desc_t *cdesc,char *modelname) {
|
||||
cdesc->model_name=strdup(modelname);
|
||||
}
|
||||
|
||||
void set_channeldesc_direction(channel_desc_t *cdesc, bool is_uplink)
|
||||
{
|
||||
cdesc->is_uplink = is_uplink;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_CH_POWER
|
||||
double accumulated_ch_power = 0;
|
||||
int ch_power_count = 0;
|
||||
|
||||
@@ -139,6 +139,8 @@ typedef struct {
|
||||
float Doppler_phase_inc;
|
||||
/// current Doppler phase of each RX antenna (for continuous phase from one block to the next)
|
||||
float *Doppler_phase_cur;
|
||||
/// flag indicating if channel direction is UL or DL
|
||||
bool is_uplink;
|
||||
} channel_desc_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -358,6 +360,7 @@ void free_channel_desc_scm(channel_desc_t *ch);
|
||||
\param module_id identifies the channel model. should be define as a macro in simu.h
|
||||
*/
|
||||
void set_channeldesc_owner(channel_desc_t *cdesc, channelmod_moduleid_t module_id);
|
||||
|
||||
/**
|
||||
\brief This function set a model name to a model descriptor, can be later used to identify a allocated channel model
|
||||
\param cdesc points to the model descriptor
|
||||
@@ -365,6 +368,13 @@ void set_channeldesc_owner(channel_desc_t *cdesc, channelmod_moduleid_t module_i
|
||||
*/
|
||||
void set_channeldesc_name(channel_desc_t *cdesc,char *modelname);
|
||||
|
||||
/**
|
||||
\brief This function set a channel model direction to either uplink or downlink
|
||||
\param cdesc points to the model descriptor
|
||||
\param is_uplink indicates if this channel is applied in uplink (not downlink) direction
|
||||
*/
|
||||
void set_channeldesc_direction(channel_desc_t *cdesc, bool is_uplink);
|
||||
|
||||
/** \fn void get_cexp_doppler(struct complexd *cexp_doppler, channel_desc_t *chan_desc, const uint32_t length)
|
||||
\brief This routine generates the complex exponential to apply the Doppler shift
|
||||
\param cexp_doppler Output with the complex exponential of Doppler shift
|
||||
@@ -379,32 +389,6 @@ void get_cexp_doppler(struct complexd *cexp_doppler, channel_desc_t *chan_desc,
|
||||
*/
|
||||
int random_channel(channel_desc_t *desc, uint8_t abstraction_flag);
|
||||
|
||||
/**
|
||||
\brief Add AWGN noise and phase noise if enabled
|
||||
\param rxdata output data with noise
|
||||
\param r_re real part of input data without noise
|
||||
\param r_im imaginary part of input data without noise
|
||||
\param sigma noise power
|
||||
\param length number of samples to apply the noise
|
||||
\param slot_offset slot offset to start applying the noise
|
||||
\param ts sampling time
|
||||
\param delay introduce delay in terms of number of samples
|
||||
\param pdu_bit_map bitmap indicating presence of optional PDUs
|
||||
\param ptrs_bit_map
|
||||
\param nb_antennas_rx number of receive antennas
|
||||
*/
|
||||
void add_noise(c16_t **rxdata,
|
||||
const double **r_re,
|
||||
const double **r_im,
|
||||
const double sigma,
|
||||
const int length,
|
||||
const int slot_offset,
|
||||
const double ts,
|
||||
const int delay,
|
||||
const uint16_t pdu_bit_map,
|
||||
const uint16_t ptrs_bit_map,
|
||||
const uint8_t nb_antennas_rx);
|
||||
|
||||
/**\fn void multipath_channel(channel_desc_t *desc,
|
||||
double tx_sig_re[NB_ANTENNAS_TX],
|
||||
double tx_sig_im[NB_ANTENANS_TX],
|
||||
@@ -570,16 +554,38 @@ double N_RB2sampling_rate(uint16_t N_RB);
|
||||
double N_RB2channel_bandwidth(uint16_t N_RB);
|
||||
|
||||
/* Linear phase noise model */
|
||||
/*!
|
||||
/**
|
||||
\brief This function produce phase noise and add to input signal
|
||||
\param ts Sampling time
|
||||
\param ts Sampling time
|
||||
\param *Re *Im Real and Imag part of the signal
|
||||
*/
|
||||
//look-up table for the sine (cosine) function
|
||||
#define ResolSinCos 100
|
||||
void InitSinLUT( void );
|
||||
void phase_noise(double ts, int16_t * InRe, int16_t * InIm);
|
||||
void phase_noise(double ts, int16_t *InRe, int16_t *InIm);
|
||||
|
||||
/**
|
||||
\brief Add AWGN noise and phase noise if enabled
|
||||
\param rxdata output data with noise
|
||||
\param r_re real part of input data without noise
|
||||
\param r_im imaginary part of input data without noise
|
||||
\param sigma noise power
|
||||
\param length number of samples to apply the noise
|
||||
\param slot_offset slot offset to start applying the noise
|
||||
\param ts sampling time
|
||||
\param delay introduce delay in terms of number of samples
|
||||
\param pdu_bit_map bitmap indicating presence of optional PDUs
|
||||
\param ptrs_bit_map
|
||||
\param nb_antennas_rx number of receive antennas
|
||||
*/
|
||||
void add_noise(c16_t **rxdata,
|
||||
const double **r_re,
|
||||
const double **r_im,
|
||||
const double sigma,
|
||||
const int length,
|
||||
const int slot_offset,
|
||||
const double ts,
|
||||
const int delay,
|
||||
const uint16_t pdu_bit_map,
|
||||
const uint16_t ptrs_bit_map,
|
||||
const uint8_t nb_antennas_rx);
|
||||
|
||||
void do_DL_sig(sim_t *sim,
|
||||
uint16_t subframe,
|
||||
|
||||
Reference in New Issue
Block a user