Merge remote-tracking branch 'origin/imscope-csv-export' into integration_2025_w23 (!3417)
imscope updates - Add CSV export button to imscope - Add extra cache for IQ data in imscope record thread so that UE time domain data can be kept until PDSCH is decoded. - Add metadata to UE time domain samples scope - Add UE pdsch pre-compensation IQ and PDSCH channel estimates
This commit is contained in:
+2
-1
@@ -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)) {
|
||||
|
||||
@@ -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 --------------------
|
||||
//----------------------------------------------------------
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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<enum scopeDataType>(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
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -77,6 +77,8 @@ enum scopeDataType {
|
||||
ueTimeDomainSamples,
|
||||
ueTimeDomainSamplesBeforeSync,
|
||||
gNbTimeDomainSamples,
|
||||
pdschChanEstimates,
|
||||
pdschRxdataF,
|
||||
EXTRA_SCOPE_TYPES
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user