diff --git a/executables/nr-ue.c b/executables/nr-ue.c index 8849dad1b5..286b71ece3 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -1161,7 +1161,8 @@ void *UE_thread(void *arg) const int readBlockSize = get_readBlockSize(slot_nr, fp) - iq_shift_to_apply; openair0_timestamp rx_timestamp; int tmp = UE->rfdevice.trx_read_func(&UE->rfdevice, &rx_timestamp, rxp, readBlockSize, fp->nb_antennas_rx); - UEscopeCopy(UE, ueTimeDomainSamples, rxp[0], sizeof(c16_t), 1, readBlockSize, 0); + metadata meta = {.slot = curMsg.proc.nr_slot_rx, .frame = curMsg.proc.frame_rx}; + UEscopeCopyWithMetadata(UE, ueTimeDomainSamples, rxp[0] - firstSymSamp * sizeof(c16_t), sizeof(c16_t), 1, readBlockSize, 0, &meta); AssertFatal(readBlockSize == tmp, ""); struct timespec current_time; if (clock_gettime(CLOCK_REALTIME, ¤t_time)) { diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c index a690c85ee3..d266d443e9 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c @@ -323,7 +323,8 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, c16_t ptrs_phase_per_slot[][NR_SYMBOLS_PER_SLOT], int32_t ptrs_re_per_slot[][NR_SYMBOLS_PER_SLOT], int G, - uint32_t nvar) + uint32_t nvar, + pdsch_scope_req_t *scope_req) { const int nl = dlsch[0].Nl; const int matrixSz = ue->frame_parms.nb_antennas_rx * nl; @@ -463,6 +464,12 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, csi_res_bitmap, ue->chest_time); stop_meas_nr_ue_phy(ue, DLSCH_EXTRACT_RBS_STATS); + if (scope_req->copy_chanest_to_scope) { + size_t size = sizeof(c16_t) * dlsch_config->number_rbs * NR_NB_SC_PER_RB; + int copy_index = symbol - dlsch_config->start_symbol; + int offset = copy_index * size; + UEscopeCopyUnsafe(ue, pdschChanEstimates, dl_ch_estimates_ext[0], size, offset, copy_index); + } if (meas_enabled) { LOG_D(PHY, "[AbsSFN %u.%d] Slot%d Symbol %d: Pilot/Data extraction %5.2f \n", @@ -481,6 +488,12 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, nb_re_pdsch = (pilots == 1) ? ((config_type == NFAPI_NR_DMRS_TYPE1) ? nb_rb_pdsch * (12 - 6 * dlsch_config->n_dmrs_cdm_groups) : nb_rb_pdsch * (12 - 4 * dlsch_config->n_dmrs_cdm_groups)) : (nb_rb_pdsch * 12); + if (scope_req->copy_rxdataF_to_scope) { + size_t size = sizeof(c16_t) * nb_re_pdsch; + int copy_index = symbol - dlsch_config->start_symbol; + UEscopeCopyUnsafe(ue, pdschRxdataF, rxdataF_ext[0], size, scope_req->scope_rxdataF_offset, copy_index); + scope_req->scope_rxdataF_offset += size; + } //---------------------------------------------------------- //--------------------- Channel Scaling -------------------- //---------------------------------------------------------- diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h index 9c6e0c1454..8e266a57a3 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h @@ -41,6 +41,13 @@ #define NR_PUSCH_x 2 // UCI placeholder bit TS 38.212 V15.4.0 subclause 5.3.3.1 #define NR_PUSCH_y 3 // UCI placeholder bit +// Specifies the data that should be copied to the scope during PDSCH RX +typedef struct pdsch_scope_req_s { + bool copy_chanest_to_scope; + bool copy_rxdataF_to_scope; + size_t scope_rxdataF_offset; +} pdsch_scope_req_t; + // Functions below implement 36-211 and 36-212 /** @addtogroup _PHY_TRANSPORT_ @@ -314,7 +321,8 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, c16_t ptrs_phase_per_slot[][NR_SYMBOLS_PER_SLOT], int32_t ptrs_re_per_slot[][NR_SYMBOLS_PER_SLOT], int G, - uint32_t nvar); + uint32_t nvar, + pdsch_scope_req_t *scope_req); int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, int frame, uint8_t slot, c16_t **txData); diff --git a/openair1/PHY/TOOLS/imscope/imscope.cpp b/openair1/PHY/TOOLS/imscope/imscope.cpp index 48010e5883..cce7a595f7 100644 --- a/openair1/PHY/TOOLS/imscope/imscope.cpp +++ b/openair1/PHY/TOOLS/imscope/imscope.cpp @@ -248,6 +248,29 @@ class IQHist { if (!ss.str().empty()) { ImGui::Text("Data for %s", ss.str().c_str()); } + if (ImGui::Button("Save IQ")) { + std::stringstream ss; + ss << "iq_"; + if (iq_data->meta.frame != -1) { + ss << iq_data->meta.frame << "_"; + } + if (iq_data->meta.slot != -1) { + ss << iq_data->meta.slot << "_"; + } + ss << scope_id_to_string(static_cast(iq_data->scope_id)); + ss << ".csv"; + std::ofstream file(ss.str()); + if (file.is_open()) { + file << "real;imag\n"; + for (int i = 0; i < iq_data->len; i++) { + file << iq_data->real[i] << ";" << iq_data->imag[i] << "\n"; + } + file.close(); + std::cout << "Saved IQ to file: " << ss.str() << std::endl; + } else { + std::cerr << "Unable to open file"; + } + } ImGui::EndGroup(); } }; @@ -432,6 +455,29 @@ void ShowUeScope(void *data_void_ptr, float t) } ImGui::End(); + if (ImGui::Begin("UE PDSCH Chan est")) { + static auto iq_data = new IQData(); + static auto iq_hist = new IQHist("PDSCH Chan est IQ"); + bool new_data = false; + if (iq_hist->ShouldReadData()) { + new_data = iq_data->TryCollect(&scope_array[pdschChanEstimates], t, iq_hist->GetEpsilon(), iq_procedure_timer); + } + iq_hist->Draw(iq_data, t, new_data); + } + ImGui::End(); + + if (ImGui::Begin("UE PDSCH IQ before compensation")) { + static auto iq_data = new IQData(); + static auto iq_hist = new IQHist("PDSCH IQ before compensation"); + bool new_data = false; + if (iq_hist->ShouldReadData()) { + new_data = iq_data->TryCollect(&scope_array[pdschRxdataF], t, iq_hist->GetEpsilon(), iq_procedure_timer); + } + iq_hist->Draw(iq_data, t, new_data); + } + ImGui::End(); + + if (ImGui::Begin("Time domain samples")) { static auto iq_data = new IQData(); // Issue with imgui deferring draw calls until the end of the frame - cases segfault if scatterplot has too many points diff --git a/openair1/PHY/TOOLS/imscope/imscope_common.cpp b/openair1/PHY/TOOLS/imscope/imscope_common.cpp index b1bdda0905..a480abbf5e 100644 --- a/openair1/PHY/TOOLS/imscope/imscope_common.cpp +++ b/openair1/PHY/TOOLS/imscope/imscope_common.cpp @@ -131,12 +131,9 @@ void unlockScopeData(enum scopeDataType type) total_size += scope_data.data.data_copied_per_offset[i]; } if (total_size != (uint64_t)scope_data.data.scope_graph_data->dataSize) { - LOG_E(PHY, - "Scope is missing data - not all data that was expected was copied - possibly missed copyDataUnsafeWithOffset call. " - "scope = %s, total_size = %lu, dataSize = %d\n", - scope_id_to_string(type), - total_size, - scope_data.data.scope_graph_data->dataSize); + scope_data.data.scope_graph_data->dataSize = total_size; + scope_data.data.scope_graph_data->lineSz = + total_size / scope_data.data.scope_graph_data->elementSz / scope_data.data.scope_graph_data->colSz; } scope_data.data.scope_id = type; scope_data.is_data_ready = true; @@ -179,8 +176,12 @@ const char *scope_id_to_string(scopeDataType type) { return "ueTimeDomainSamplesBeforeSync"; case gNbTimeDomainSamples: return "gNbTimeDomainSamples"; + case pdschChanEstimates: + return "pdschChanEstimates"; + case pdschRxdataF: + return "pdschRxdataF"; default: - return "unknown type"; + return "unknown scope type"; } } diff --git a/openair1/PHY/TOOLS/imscope/imscope_record.cpp b/openair1/PHY/TOOLS/imscope/imscope_record.cpp index 6855e1b69d..a11b609054 100644 --- a/openair1/PHY/TOOLS/imscope/imscope_record.cpp +++ b/openair1/PHY/TOOLS/imscope/imscope_record.cpp @@ -35,7 +35,7 @@ constexpr std::size_t hardware_destructive_interference_size = 64; #endif -#define NUM_CACHED_DATA 4 +#define NUM_CACHED_DATA 8 // Set maximum number of kilobytes written per recording session to 1GB. This is a safeguard against // recoding thread writing too many files and exhausting disk space. #define MAX_KBYTES_WRITTEN_PER_SESSION (1000 * 1000) diff --git a/openair1/PHY/TOOLS/phy_scope_interface.h b/openair1/PHY/TOOLS/phy_scope_interface.h index 181bf5985b..731a6250e5 100644 --- a/openair1/PHY/TOOLS/phy_scope_interface.h +++ b/openair1/PHY/TOOLS/phy_scope_interface.h @@ -77,6 +77,8 @@ enum scopeDataType { ueTimeDomainSamples, ueTimeDomainSamplesBeforeSync, gNbTimeDomainSamples, + pdschChanEstimates, + pdschRxdataF, EXTRA_SCOPE_TYPES }; diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c index 8484a13101..976a5f8d88 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c @@ -606,6 +606,24 @@ static int nr_ue_pdsch_procedures(PHY_VARS_NR_UE *ue, int32_t log2_maxh = 0; start_meas_nr_ue_phy(ue, RX_PDSCH_STATS); + pdsch_scope_req_t scope_req = { .copy_chanest_to_scope = false, + .copy_rxdataF_to_scope = false, + .scope_rxdataF_offset = 0 }; + if (UEScopeHasTryLock(ue)) { + metadata mt = {.frame = proc->frame_rx, .slot = proc->nr_slot_rx}; + scope_req.copy_chanest_to_scope = UETryLockScopeData(ue, + pdschChanEstimates, + sizeof(c16_t), + 1, + dlschCfg->number_rbs * NR_NB_SC_PER_RB * dlschCfg->number_symbols, + &mt); + scope_req.copy_rxdataF_to_scope = UETryLockScopeData(ue, + pdschRxdataF, + sizeof(c16_t), + 1, + dlschCfg->number_rbs * NR_NB_SC_PER_RB * dlschCfg->number_symbols, + &mt); + } for (int m = dlschCfg->start_symbol; m < (dlschCfg->number_symbols + dlschCfg->start_symbol); m++) { bool first_symbol_flag = false; if (m == first_symbol_with_data) @@ -632,11 +650,25 @@ static int nr_ue_pdsch_procedures(PHY_VARS_NR_UE *ue, ptrs_phase_per_slot, ptrs_re_per_slot, G, - nvar) - < 0) + nvar, + &scope_req) + < 0) { + if (scope_req.copy_chanest_to_scope) { + UEunlockScopeData(ue, pdschChanEstimates); + } + if (scope_req.copy_rxdataF_to_scope) { + UEunlockScopeData(ue, pdschRxdataF); + } return -1; + } } // CRNTI active stop_meas_nr_ue_phy(ue, RX_PDSCH_STATS); + if (scope_req.copy_chanest_to_scope) { + UEunlockScopeData(ue, pdschChanEstimates); + } + if (scope_req.copy_rxdataF_to_scope) { + UEunlockScopeData(ue, pdschRxdataF); + } free(toFree); free(toFree2); }