diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c index b06b690c0b..bf5b619528 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c @@ -8,6 +8,7 @@ #include "PHY/NR_ESTIMATION/nr_ul_estimation.h" #include "PHY/defs_nr_common.h" #include "common/utils/nr/nr_common.h" +#include //#define DEBUG_CH_COMP //#define DEBUG_RB_EXT @@ -2015,6 +2016,7 @@ void nr_rx_pusch(PHY_VARS_gNB *gNB, ad_shift = -3; // For 2-layers, we are already doing a bit shift in the nr_ulsch_mmse_2layers() function, so we can use more bits } + int num_re_total = 0; for(uint8_t symbol = rel15_ul->start_symbol_index; symbol < (rel15_ul->start_symbol_index + rel15_ul->nr_of_symbols); symbol++) { uint8_t dmrs_symbol_flag = (rel15_ul->ul_dmrs_symb_pos >> symbol) & 0x01; if (dmrs_symbol_flag == 1) { @@ -2037,6 +2039,7 @@ void nr_rx_pusch(PHY_VARS_gNB *gNB, nb_re_pusch = rel15_ul->rb_size * NR_NB_SC_PER_RB; } + num_re_total += nb_re_pusch; pusch_vars->ul_valid_re_per_slot[symbol] = nb_re_pusch; LOG_D(PHY, "symbol %d: nb_re_pusch %d, DMRS symbl used for Chest :%d \n", symbol, nb_re_pusch, pusch_vars->dmrs_symbol); @@ -2214,4 +2217,9 @@ void nr_rx_pusch(PHY_VARS_gNB *gNB, rxdataF_ext_offset += pusch_vars->ul_valid_re_per_slot[symbol]; } } // symbol loop + if (!(frame % 128)) { + int num_llr = num_re_total*rel15_ul->qam_mod_order; + GnbScopeUpdate(gNB, puschLLRe, num_llr); + GnbScopeUpdate(gNB, puschIQe, num_re_total); + } } diff --git a/openair1/PHY/TOOLS/nr_phy_qt_scope.cpp b/openair1/PHY/TOOLS/nr_phy_qt_scope.cpp index 4f73978448..4151a7e4cd 100644 --- a/openair1/PHY/TOOLS/nr_phy_qt_scope.cpp +++ b/openair1/PHY/TOOLS/nr_phy_qt_scope.cpp @@ -70,6 +70,22 @@ float Limits_KPI_ue[2][2] = { {0.2, 10} // Throughput in Mbs }; +// Plot updater +PlotUpdater puschLlrUpdater; +PlotUpdater puschIqUpdater; + +void scopeUpdaterGnb(enum PlotTypeGnbIf plotType, int numElt) +{ + switch (plotType) { + case puschLLRe: + puschLlrUpdater.updatePlot(numElt); + break; + case puschIQe: + puschIqUpdater.updatePlot(numElt); + break; + } +} + /* This class creates the window when choosing the option 'Configs' to configure the threshold values. */ ConfigBoxFloat::ConfigBoxFloat(float *valuePtr, QWidget *parent) : QLineEdit(parent), valuePtr(valuePtr) { @@ -178,7 +194,8 @@ KPIListSelectUE::KPIListSelectUE(QWidget *parent) : QComboBox(parent) this->addItem("Configs", static_cast(PlotTypeUE::config)); } -WaterFall::WaterFall(complex16 *values, NR_DL_FRAME_PARMS *frame_parms, QWidget *parent) : QWidget(parent), values(values), frame_parms(frame_parms) +WaterFall::WaterFall(complex16 *values, NR_DL_FRAME_PARMS *frame_parms, QWidget *parent) + : QWidget(parent), values(values), frame_parms(frame_parms) { this->iteration = 0; this->image = nullptr; @@ -187,8 +204,8 @@ WaterFall::WaterFall(complex16 *values, NR_DL_FRAME_PARMS *frame_parms, QWidget startTimer(100); } -/* this function to plot the waterfall graph for the RX signal in time domain for one frame. x-axis shows the frame divided into slots - and the y-axis is a color map depending on the SquaredNorm of the received signal at the correspoinding slot. */ +/* this function to plot the waterfall graph for the RX signal in time domain for one frame. x-axis shows the frame divided into + slots and the y-axis is a color map depending on the SquaredNorm of the received signal at the correspoinding slot. */ void WaterFall::timerEvent(QTimerEvent *event) { if (!this->isVisible()) @@ -340,17 +357,19 @@ void CIRPlotUE::timerEvent(QTimerEvent *event) maxY = std::max(maxY, value); } + this->axisX->setRange(-this->len / 2, this->len / 2); this->axisY->setMax(maxY); this->series->replace(points); } } -LLRPlot::LLRPlot(int16_t *data, int len) : data(data), len(len) +LLRPlot::LLRPlot(int16_t *data, int len, int interval, PlotUpdater *plotUpdater) : data(data), len(len), plotUpdater(plotUpdater) { this->legend()->hide(); // add new series to the chart this->series = new QScatterSeries(); + this->series->setUseOpenGL(); this->series->setMarkerSize(3); this->series->setMarkerShape(QScatterSeries::MarkerShapeRectangle); this->series->setColor(Qt::blue); @@ -360,7 +379,6 @@ LLRPlot::LLRPlot(int16_t *data, int len) : data(data), len(len) // add new X axis this->axisX = new QValueAxis(); this->axisX->setLabelFormat("%d"); - this->axisX->setRange(0, len); this->addAxis(this->axisX, Qt::AlignBottom); this->series->attachAxis(this->axisX); @@ -369,26 +387,44 @@ LLRPlot::LLRPlot(int16_t *data, int len) : data(data), len(len) this->addAxis(this->axisY, Qt::AlignLeft); this->series->attachAxis(this->axisY); - startTimer(1000); + if (interval) + startTimer(interval); + + if (plotUpdater) + connect(plotUpdater, &PlotUpdater::updatePlot, this, &LLRPlot::updatePlot, Qt::QueuedConnection); } -void LLRPlot::timerEvent(QTimerEvent *event) +LLRPlot::~LLRPlot() { + if (this->plotUpdater) + disconnect(this->plotUpdater, &PlotUpdater::updatePlot, this, &LLRPlot::updatePlot); +} + +void LLRPlot::updatePlot(int len) +{ + this->len = len; + if (!this->isVisible()) return; - QVector points(this->len); + QVector points(len); int maxY = this->axisY->max(); - for (int i = 0; i < this->len; i++) { + for (int i = 0; i < len; i++) { points[i] = QPointF(i, this->data[i]); maxY = std::max(maxY, abs(this->data[i])); } + this->axisX->setRange(0, len); this->axisY->setRange(-maxY, maxY); this->series->replace(points); } +void LLRPlot::timerEvent(QTimerEvent *event) +{ + this->updatePlot(this->len); +} + void LLRPlotUE::timerEvent(QTimerEvent *event) { if (!this->isVisible()) @@ -406,17 +442,19 @@ void LLRPlotUE::timerEvent(QTimerEvent *event) maxY = std::max(maxY, abs(this->data[i])); } + this->axisX->setRange(0, this->len); this->axisY->setRange(-maxY, maxY); this->series->replace(points); } } -IQPlot::IQPlot(complex16 *data, int len) : data(data), len(len) +IQPlot::IQPlot(complex16 *data, int len, int interval, PlotUpdater *plotUpdater) : data(data), len(len), plotUpdater(plotUpdater) { this->legend()->hide(); // add new series to the chart this->series = new QScatterSeries(); + this->series->setUseOpenGL(); this->series->setMarkerSize(3); this->series->setMarkerShape(QScatterSeries::MarkerShapeRectangle); this->series->setColor(Qt::blue); @@ -433,19 +471,31 @@ IQPlot::IQPlot(complex16 *data, int len) : data(data), len(len) this->addAxis(this->axisY, Qt::AlignLeft); this->series->attachAxis(this->axisY); - startTimer(1000); + if (interval) + startTimer(interval); + + if (plotUpdater) + connect(plotUpdater, &PlotUpdater::updatePlot, this, &IQPlot::updatePlot, Qt::QueuedConnection); } -void IQPlot::timerEvent(QTimerEvent *event) +IQPlot::~IQPlot() { + if (this->plotUpdater) + disconnect(this->plotUpdater, &PlotUpdater::updatePlot, this, &IQPlot::updatePlot); +} + +void IQPlot::updatePlot(int len) +{ + this->len = len; + if (!this->isVisible()) return; - QVector points(this->len); + QVector points(len); int maxX = this->axisX->max(); int maxY = this->axisY->max(); - for (int i = 0; i < this->len; i++) { + for (int i = 0; i < len; i++) { points[i] = QPointF(this->data[i].r, this->data[i].i); maxX = std::max(maxX, abs(this->data[i].r)); @@ -457,6 +507,11 @@ void IQPlot::timerEvent(QTimerEvent *event) this->series->replace(points); } +void IQPlot::timerEvent(QTimerEvent *event) +{ + this->updatePlot(this->len); +} + void IQPlotUE::timerEvent(QTimerEvent *event) { if (!this->isVisible()) @@ -667,7 +722,10 @@ PainterWidgetGnb::PainterWidgetGnb(QWidget *config, QComboBox *comboBox, scopeDa this->plotType = PlotTypeGnb::empty; makeConnections(this->comboBox->currentIndex()); - connect(this->comboBox, static_cast(&QComboBox::currentIndexChanged), this, &PainterWidgetGnb::makeConnections); + connect(this->comboBox, + static_cast(&QComboBox::currentIndexChanged), + this, + &PainterWidgetGnb::makeConnections); } float PainterWidgetGnb::getValue() @@ -778,15 +836,13 @@ void PainterWidgetGnb::makeConnections(int type) } case PlotTypeGnb::puschLLR: { - int num_re = frame_parms->N_RB_UL * 12 * frame_parms->symbols_per_slot; - int Qm = 2; - int coded_bits_per_codeword = num_re * Qm; - newChart = new LLRPlot((int16_t *)p->gNB->pusch_vars[0].llr, coded_bits_per_codeword); + int init_coded_bits_per_codeword = 100; + newChart = new LLRPlot((int16_t *)p->gNB->pusch_vars[0].llr, init_coded_bits_per_codeword, 0, &puschLlrUpdater); break; } case PlotTypeGnb::puschIQ: { - int num_re = frame_parms->N_RB_UL * 12 * frame_parms->symbols_per_slot; - newChart = new IQPlot((complex16 *)p->gNB->pusch_vars[0].rxdataF_comp[0], num_re); + int init_num_re = 100; + newChart = new IQPlot((complex16 *)p->gNB->pusch_vars[0].rxdataF_comp[0], init_num_re, 0, &puschIqUpdater); break; } case PlotTypeGnb::puschSNR: { @@ -846,7 +902,8 @@ void PainterWidgetGnb::makeConnections(int type) /* @UE: This is the main function of the UE sub-widgets, i.e., for each KPI. This function will be called only once when the the sub-widget is created, and it mainly initializes the widget variables and structures. */ -PainterWidgetUE::PainterWidgetUE(QWidget *config, QComboBox *comboBox, PHY_VARS_NR_UE *ue) : config(config), comboBox(comboBox), ue(ue) +PainterWidgetUE::PainterWidgetUE(QWidget *config, QComboBox *comboBox, PHY_VARS_NR_UE *ue) + : config(config), comboBox(comboBox), ue(ue) { this->chartView = new QChartView(this); this->chartView->hide(); @@ -854,7 +911,10 @@ PainterWidgetUE::PainterWidgetUE(QWidget *config, QComboBox *comboBox, PHY_VARS_ this->plotType = PlotTypeUE::empty; makeConnections(this->comboBox->currentIndex()); - connect(this->comboBox, static_cast(&QComboBox::currentIndexChanged), this, &PainterWidgetUE::makeConnections); + connect(this->comboBox, + static_cast(&QComboBox::currentIndexChanged), + this, + &PainterWidgetUE::makeConnections); } float PainterWidgetUE::getValue() @@ -891,7 +951,6 @@ float PainterWidgetUE::getValue() case PlotTypeUE::timingAdvance: return (float)this->ue->timing_advance; - default: return 0; } @@ -1240,6 +1299,7 @@ void nrgNBinitQtScope(scopeParms_t *p) scope->argc = p->argc; scope->argv = p->argv; scope->ru = p->ru; + scope->scopeUpdater = scopeUpdaterGnb; scope->copyData = copyData; copyDataMutexInit(scope); diff --git a/openair1/PHY/TOOLS/nr_phy_qt_scope.h b/openair1/PHY/TOOLS/nr_phy_qt_scope.h index 8a00d5fa96..08296e03d7 100644 --- a/openair1/PHY/TOOLS/nr_phy_qt_scope.h +++ b/openair1/PHY/TOOLS/nr_phy_qt_scope.h @@ -94,11 +94,20 @@ class ValueProvider { class ValueProviderUE : public ValueProvider { public: /// This pure virtual function is meant to provide the graph values to be plotted - virtual scopeGraphData_t *getPlotValue() { + virtual scopeGraphData_t *getPlotValue() + { return nullptr; } }; +/// Class for emitting a queued signal to update plots +class PlotUpdater : public QObject { + Q_OBJECT + + signals: + void updatePlot(int numElt); +}; + /// An editable GUI field for a dialog box to set certain KPI configurations class ConfigBoxFloat : public QLineEdit { Q_OBJECT @@ -234,7 +243,9 @@ class CIRPlotUE : public CIRPlot { Q_OBJECT public: - CIRPlotUE(complex16 *data, int len, ValueProviderUE *valueProvider) : CIRPlot(data, len), valueProvider(valueProvider) {} + CIRPlotUE(complex16 *data, int len, ValueProviderUE *valueProvider) : CIRPlot(data, len), valueProvider(valueProvider) + { + } protected: /// This function is triggered when the own timer expires. It updates the plotted CIR @@ -253,10 +264,19 @@ class LLRPlot : public QChart { /// Constructor /// \param data Pointer to the LLR data /// \param len Length of the LLR data - LLRPlot(int16_t *data, int len); + /// \param interval update interval in ms (0 means no timer-triggered updates) + /// \param plotUpdater pointer to a PlotUpdater for update notifications + LLRPlot(int16_t *data, int len, int interval = 1000, PlotUpdater *plotUpdater = nullptr); + + /// Destructor + ~LLRPlot(); + + public slots: + /// This function updates the plotted LLR + void updatePlot(int len); protected: - /// This function is triggered when the own timer expires. It updates the plotted LLR + /// This function is triggered when the own timer expires. It calls updatePlot() to update the plotted LLR /// \param event Pointer to the timer event virtual void timerEvent(QTimerEvent *event) override; @@ -266,6 +286,9 @@ class LLRPlot : public QChart { /// Length of the LLR data int len; + /// Pointer to a PlotUpdater for update notifications + PlotUpdater *plotUpdater; + /// Scatter series used to plot the LLR in the chart QScatterSeries *series; @@ -280,7 +303,9 @@ class LLRPlotUE : public LLRPlot { Q_OBJECT public: - LLRPlotUE(int16_t *data, int len, ValueProviderUE *valueProvider) : LLRPlot(data, len), valueProvider(valueProvider) {} + LLRPlotUE(int16_t *data, int len, ValueProviderUE *valueProvider) : LLRPlot(data, len), valueProvider(valueProvider) + { + } protected: /// This function is triggered when the own timer expires. It updates the plotted I/Q constellation diagram @@ -299,10 +324,19 @@ class IQPlot : public QChart { /// Constructor /// \param data Pointer to the complex I/Q data /// \param len Length of the I/Q data - IQPlot(complex16 *data, int len); + /// \param interval update interval in ms (0 means no timer-triggered updates) + /// \param plotUpdater pointer to a PlotUpdater for update notifications + IQPlot(complex16 *data, int len, int interval = 1000, PlotUpdater *plotUpdater = nullptr); + + /// Destructor + ~IQPlot(); + + public slots: + /// This function updates the plotted I/Q constellation diagram + void updatePlot(int len); protected: - /// This function is triggered when the own timer expires. It updates the plotted I/Q constellation diagram + /// This function is triggered when the own timer expires. It calls updatePlot() to update the plotted I/Q constellation diagram /// \param event Pointer to the timer event virtual void timerEvent(QTimerEvent *event) override; @@ -312,6 +346,9 @@ class IQPlot : public QChart { /// Length of the I/Q data int len; + /// Pointer to a PlotUpdater for update notifications + PlotUpdater *plotUpdater; + /// Scatter series used to plot the I/Q constellation diagram QScatterSeries *series; @@ -326,7 +363,9 @@ class IQPlotUE : public IQPlot { Q_OBJECT public: - IQPlotUE(complex16 *data, int len, ValueProviderUE *valueProvider) : IQPlot(data, len), valueProvider(valueProvider) {} + IQPlotUE(complex16 *data, int len, ValueProviderUE *valueProvider) : IQPlot(data, len), valueProvider(valueProvider) + { + } protected: /// This function is triggered when the own timer expires. It updates the plotted I/Q constellation diagram diff --git a/openair1/PHY/TOOLS/nr_phy_scope.c b/openair1/PHY/TOOLS/nr_phy_scope.c index 9b0d152c6d..d07bf07076 100644 --- a/openair1/PHY/TOOLS/nr_phy_scope.c +++ b/openair1/PHY/TOOLS/nr_phy_scope.c @@ -738,6 +738,18 @@ static void *scope_thread_gNB(void *arg) { } #endif +static void scopeUpdaterGnb(enum PlotTypeGnbIf plotType, int numElt) +{ + switch (plotType) { + case puschLLRe: + /* update PUSCH LLR plot */ + break; + case puschIQe: + /* update PUSCH IQ plot */ + break; + } +} + STATICFORXSCOPE void gNBinitScope(scopeParms_t *p) { AssertFatal(p->gNB->scopeData = calloc(sizeof(scopeData_t), 1), ""); @@ -746,6 +758,7 @@ STATICFORXSCOPE void gNBinitScope(scopeParms_t *p) scope->argv=p->argv; scope->ru=p->ru; scope->gNB=p->gNB; + scope->scopeUpdater = scopeUpdaterGnb; scope->copyData = copyData; #ifndef WEBSRVSCOPE pthread_t forms_thread; diff --git a/openair1/PHY/TOOLS/phy_scope_interface.h b/openair1/PHY/TOOLS/phy_scope_interface.h index 3c520513cf..ca0f14e019 100644 --- a/openair1/PHY/TOOLS/phy_scope_interface.h +++ b/openair1/PHY/TOOLS/phy_scope_interface.h @@ -70,6 +70,11 @@ enum scopeDataType { MAX_SCOPE_TYPES }; +enum PlotTypeGnbIf { + puschLLRe, + puschIQe, +}; + #define COPIES_MEM 4 typedef struct { @@ -89,6 +94,7 @@ typedef struct scopeData_s { pthread_mutex_t copyDataMutex; scopeGraphData_t *copyDataBufs[MAX_SCOPE_TYPES][COPIES_MEM]; int copyDataBufsIdx[MAX_SCOPE_TYPES]; + void (*scopeUpdater)(enum PlotTypeGnbIf plotType, int numElements); } scopeData_t; int load_softscope(char *exectype, void *initarg); @@ -102,6 +108,9 @@ void copyData(void *, enum scopeDataType type, void *dataIn, int elementSz, int #define gNBscopeCopy(gnb, type, ...) \ if (gnb->scopeData) \ ((scopeData_t *)gnb->scopeData)->copyData((scopeData_t *)gNB->scopeData, type, ##__VA_ARGS__); +#define GnbScopeUpdate(gnb, type, numElt) \ + if (gnb->scopeData) \ + ((scopeData_t *)gnb->scopeData)->scopeUpdater(type, numElt); extended_kpi_ue* getKPIUE();