diff --git a/src/limedriver.cpp b/src/limedriver.cpp index 783eded..a7e84a1 100644 --- a/src/limedriver.cpp +++ b/src/limedriver.cpp @@ -23,8 +23,7 @@ using namespace std; const int maxNpulse = 50; // LMS error function -int error() -{ +int error() { if (device != NULL) LMS_Close(device); exit(-1); @@ -32,27 +31,23 @@ int error() // portable way to check and create directory (from stackexchange) // https://stackoverflow.com/questions/675039/how-can-i-create-directory-tree-in-c-linux -bool isDirExist(const std::string &path) -{ +bool isDirExist(const std::string &path) { #if defined(_WIN32) struct _stat info; - if (_stat(path.c_str(), &info) != 0) - { + if (_stat(path.c_str(), &info) != 0) { return false; } return (info.st_mode & _S_IFDIR) != 0; #else struct stat info; - if (stat(path.c_str(), &info) != 0) - { + if (stat(path.c_str(), &info) != 0) { return false; } return (info.st_mode & S_IFDIR) != 0; #endif } -bool makePath(const std::string &path) -{ +bool makePath(const std::string &path) { #if defined(_WIN32) int ret = _mkdir(path.c_str()); #else @@ -62,8 +57,7 @@ bool makePath(const std::string &path) if (ret == 0) return true; - switch (errno) - { + switch (errno) { case ENOENT: // parent didn't exist, try to create it { @@ -93,8 +87,7 @@ bool makePath(const std::string &path) } } -inline bool file_exists(const std::string &name) -{ +inline bool file_exists(const std::string &name) { struct stat buffer; return (stat(name.c_str(), &buffer) == 0); } @@ -103,8 +96,7 @@ inline bool file_exists(const std::string &name) // GetGaindB has to be avoided, as it also modifies the gain, which is useless // and dangerous.. // int GetGainRXTX(array< int, 4>* RXgain, array* TXgain) { -int GetGainRXTX(int *RXgain, int *TXgain) -{ +int GetGainRXTX(int *RXgain, int *TXgain) { // RX gain: LNA, TIA and PGA uint16_t gain_lna, gain_tia, gain_pga; if (LMS_ReadParam(device, LMS7_G_LNA_RFE, &gain_lna) != 0) @@ -147,8 +139,7 @@ int GetGainRXTX(int *RXgain, int *TXgain) // RFE TIA gmax = 12; - switch (gain_tia) - { + switch (gain_tia) { case 3: RXgain[2] = gmax - 0; break; @@ -177,8 +168,7 @@ int GetGainRXTX(int *RXgain, int *TXgain) return 0; } -std::vector getHDFAttributes(LimeConfig_t &LimeCfg) -{ +std::vector getHDFAttributes(LimeConfig_t &LimeCfg) { std::vector HDFattr = { {"sra", "SampleRate [Hz]", H5::PredType::IEEE_F32LE, &LimeCfg.srate, 1}, {"lof", "LO Frequency [Hz]", H5::PredType::IEEE_F32LE, &LimeCfg.frq, 1}, @@ -196,6 +186,18 @@ std::vector getHDFAttributes(LimeConfig_t &LimeCfg) &LimeCfg.TX_QcorrDC, 1}, {"tdi", "TX DC-correction I", H5::PredType::NATIVE_INT, &LimeCfg.TX_IcorrDC, 1}, + {"tgi", "TX I Gain correction", H5::PredType::NATIVE_INT, + &LimeCfg.TX_IcorrGain, 1}, + {"tgq", "TX Q Gain correction", H5::PredType::NATIVE_INT, + &LimeCfg.TX_QcorrGain, 1}, + {"tpc", "TX Phase correction", H5::PredType::NATIVE_INT, + &LimeCfg.TX_IQcorrPhase, 1}, + {"rgi", "RX I Gain correction", H5::PredType::NATIVE_INT, + &LimeCfg.RX_IcorrGain, 1}, + {"rgq", "RX Q Gain correction", H5::PredType::NATIVE_INT, + &LimeCfg.RX_QcorrGain, 1}, + {"rpc", "RX Phase correction", H5::PredType::NATIVE_INT, + &LimeCfg.RX_IQcorrPhase, 1}, {"npu", "Number of Pulses", H5::PredType::NATIVE_INT, &LimeCfg.Npulses, 1}, {"pdr", "Pulse Duration [s]", H5::PredType::IEEE_F64LE, LimeCfg.p_dur, @@ -216,6 +218,26 @@ std::vector getHDFAttributes(LimeConfig_t &LimeCfg) LimeCfg.p_dur_smp, (hsize_t)LimeCfg.Npulses}, {"///", "IF Pulse Frequency [1/Sa]", H5::PredType::IEEE_F64LE, LimeCfg.p_frq_smp, (hsize_t)LimeCfg.Npulses}, + {"amf", "AM Frequency [Hz]", H5::PredType::IEEE_F64LE, LimeCfg.am_frq, + (hsize_t)LimeCfg.Npulses}, + {"amp", "AM Phase [rad]", H5::PredType::IEEE_F64LE, LimeCfg.am_pha, + (hsize_t)LimeCfg.Npulses}, + {"amd", "AM Depth", H5::PredType::IEEE_F64LE, LimeCfg.am_depth, + (hsize_t)LimeCfg.Npulses}, + {"amm", "AM Mode", H5::PredType::NATIVE_INT, LimeCfg.am_mode, + (hsize_t)LimeCfg.Npulses}, + {"///", "AM Frequency [1/Sa]", H5::PredType::IEEE_F64LE, + LimeCfg.am_frq_smp, (hsize_t)LimeCfg.Npulses}, + {"fmf", "FM Frequency [Hz]", H5::PredType::IEEE_F64LE, LimeCfg.fm_frq, + (hsize_t)LimeCfg.Npulses}, + {"fmp", "FM Phase [rad]", H5::PredType::IEEE_F64LE, LimeCfg.fm_pha, + (hsize_t)LimeCfg.Npulses}, + {"fmw", "FM width [Hz]", H5::PredType::IEEE_F64LE, LimeCfg.fm_width, + (hsize_t)LimeCfg.Npulses}, + {"fmm", "FM Mode", H5::PredType::NATIVE_INT, LimeCfg.fm_mode, + (hsize_t)LimeCfg.Npulses}, + {"///", "FM Frequency [1/Sa]", H5::PredType::IEEE_F64LE, + LimeCfg.fm_frq_smp, (hsize_t)LimeCfg.Npulses}, {"t0d", "Trigger0 Timing [Sa]", H5::PredType::NATIVE_INT, &LimeCfg.c0_tim, 4}, {"t1d", "Trigger1 Timing [Sa]", H5::PredType::NATIVE_INT, &LimeCfg.c1_tim, @@ -263,6 +285,8 @@ std::vector getHDFAttributes(LimeConfig_t &LimeCfg) (void *)LimeCfg.save_path.c_str(), 1}, {"nos", "Don't save if >0", H5::PredType::NATIVE_INT, &LimeCfg.override_save, 1}, + {"noi", "Don't init if >0", H5::PredType::NATIVE_INT, + &LimeCfg.override_init, 1}, {"fst", "Filename Timestamp", H5::StrType(H5::PredType::C_S1, LimeCfg.file_stamp.length() + 1), (void *)LimeCfg.file_stamp.c_str(), 1}, @@ -275,8 +299,7 @@ std::vector getHDFAttributes(LimeConfig_t &LimeCfg) return HDFattr; } -void dumpConfig(std::vector &config) -{ +void dumpConfig(std::vector &config) { /* Dump the configuration to stdout in JSON format @param config: Array of Config2HDFattr_t @@ -288,15 +311,13 @@ void dumpConfig(std::vector &config) std::cout << "{" << std::endl; - for (size_t i = 0; i < size; ++i) - { + for (size_t i = 0; i < size; ++i) { // Handle the "///" arguments string arg = config[i].arg; - if (strcmp(config[i].arg.c_str(), "///") == 0) - { + if (strcmp(config[i].arg.c_str(), "///") == 0) { arg = "//" + std::to_string(ii_oupargs); ii_oupargs++; } @@ -311,20 +332,13 @@ void dumpConfig(std::vector &config) // Need to cast void* data pointer to the correct type // TODO: Do we lose precision here? - if (config[i].dType == H5::PredType::NATIVE_INT) - { + if (config[i].dType == H5::PredType::NATIVE_INT) { std::cout << *(static_cast(config[i].Value)); - } - else if (config[i].dType == H5::PredType::IEEE_F32LE) - { + } else if (config[i].dType == H5::PredType::IEEE_F32LE) { std::cout << *(static_cast(config[i].Value)); - } - else if (config[i].dType == H5::PredType::IEEE_F64LE) - { + } else if (config[i].dType == H5::PredType::IEEE_F64LE) { std::cout << *(static_cast(config[i].Value)); - } - else - { + } else { std::cout << static_cast(config[i].Value); } @@ -334,8 +348,7 @@ void dumpConfig(std::vector &config) std::cout << "}"; - if (i < size - 1) - { + if (i < size - 1) { std::cout << ","; } @@ -345,8 +358,7 @@ void dumpConfig(std::vector &config) std::cout << "}" << std::endl; } -LimeConfig_t initializeLimeConfig(int Npulses) -{ +LimeConfig_t initializeLimeConfig(int Npulses) { /* Initialize the LimeConfig_t struct @param Npulses: Number of pulses @@ -371,36 +383,50 @@ LimeConfig_t initializeLimeConfig(int Npulses) LimeCfg.TX_LPF = 130e6; // IF lowpass of the transmitter LimeCfg.TX_IcorrDC = - -32; // DC corr to TX mixer at IF (evaluate with LimeSuiteGUI) - LimeCfg.TX_QcorrDC = 50; // DC corr to TX mixer at IF + -32; // DC corr to TX mixer at IF (evaluate with LimeSuiteGUI) + LimeCfg.TX_QcorrDC = 50; // DC corr to TX mixer at IF + LimeCfg.TX_IcorrGain = 2047; // I Gain corr of TX mixer + LimeCfg.TX_QcorrGain = 2039; // Q Gain corr of TX mixer + LimeCfg.TX_IQcorrPhase = 10; // Phase corr of TX mixer + LimeCfg.RX_IcorrGain = 2047; // I Gain corr of RX mixer + LimeCfg.RX_QcorrGain = 2047; // Q Gain corr of RX mixer + LimeCfg.RX_IQcorrPhase = 0; // Phase corr of RX mixer // Allocate the arrays with pulse parametes - LimeCfg.p_dur = new double[LimeCfg.Npulses]; // pulse duration (secs) - LimeCfg.p_offs = new int[LimeCfg.Npulses]; // pulse time offset - LimeCfg.p_amp = new double[LimeCfg.Npulses]; // pulse digital IF amplitude - LimeCfg.p_frq = new double[LimeCfg.Npulses]; // pulse digital IF frequency (unit: Hz) - LimeCfg.p_pha = new double[LimeCfg.Npulses]; // pulse digital IF phase - LimeCfg.p_phacyc_N = new int[LimeCfg.Npulses]; // number of pulse phases (cycled within 2*pi, must be at least 1) - LimeCfg.p_phacyc_lev = new int[LimeCfg.Npulses]; // stacking level of phase cycle (for eventual coupling) - LimeCfg.p_c0_en = new int[LimeCfg.Npulses]; // pulse-wise enable of marker c0 - LimeCfg.p_c1_en = new int[LimeCfg.Npulses]; // pulse-wise enable of marker c1 - LimeCfg.p_c2_en = new int[LimeCfg.Npulses]; // pulse-wise enable of marker c2 - LimeCfg.p_c3_en = new int[LimeCfg.Npulses]; // pulse-wise enable of marker c3 - LimeCfg.am_frq = new double[LimeCfg.Npulses]; // pulse AM frequency - LimeCfg.am_pha = new double[LimeCfg.Npulses]; // pulse AM phase - LimeCfg.am_depth = new double[LimeCfg.Npulses]; // pulse AM depth - LimeCfg.am_mode = new int[LimeCfg.Npulses]; // pulse AM mode (0: sinus, 1: triangle, 2: square) - LimeCfg.fm_frq = new double[LimeCfg.Npulses]; // pulse FM frequency - LimeCfg.fm_pha = new double[LimeCfg.Npulses]; // pulse FM phase - LimeCfg.fm_width = new double[LimeCfg.Npulses]; // pulse FM width - LimeCfg.fm_mode = new int[LimeCfg.Npulses]; // pulse FM mode (0: sinus, 1: triangle, 2: square) + LimeCfg.p_dur = new double[LimeCfg.Npulses]; // pulse duration (secs) + LimeCfg.p_offs = new int[LimeCfg.Npulses]; // pulse time offset + LimeCfg.p_amp = new double[LimeCfg.Npulses]; // pulse digital IF amplitude + LimeCfg.p_frq = + new double[LimeCfg.Npulses]; // pulse digital IF frequency (unit: Hz) + LimeCfg.p_pha = new double[LimeCfg.Npulses]; // pulse digital IF phase + LimeCfg.p_phacyc_N = + new int[LimeCfg.Npulses]; // number of pulse phases (cycled within 2*pi, + // must be at least 1) + LimeCfg.p_phacyc_lev = + new int[LimeCfg.Npulses]; // stacking level of phase cycle (for eventual + // coupling) + LimeCfg.p_c0_en = new int[LimeCfg.Npulses]; // pulse-wise enable of marker c0 + LimeCfg.p_c1_en = new int[LimeCfg.Npulses]; // pulse-wise enable of marker c1 + LimeCfg.p_c2_en = new int[LimeCfg.Npulses]; // pulse-wise enable of marker c2 + LimeCfg.p_c3_en = new int[LimeCfg.Npulses]; // pulse-wise enable of marker c3 + LimeCfg.am_frq = new double[LimeCfg.Npulses]; // pulse AM frequency + LimeCfg.am_pha = new double[LimeCfg.Npulses]; // pulse AM phase + LimeCfg.am_depth = new double[LimeCfg.Npulses]; // pulse AM depth + LimeCfg.am_mode = new int[LimeCfg.Npulses]; // pulse AM mode (0: sinus, 1: + // triangle, 2: square) + LimeCfg.fm_frq = new double[LimeCfg.Npulses]; // pulse FM frequency + LimeCfg.fm_pha = new double[LimeCfg.Npulses]; // pulse FM phase + LimeCfg.fm_width = new double[LimeCfg.Npulses]; // pulse FM width + LimeCfg.fm_mode = new int[LimeCfg.Npulses]; // pulse FM mode (0: sinus, 1: + // triangle, 2: square) // and set standard values - for (int ii = 0; ii < LimeCfg.Npulses; ii++) - { + for (int ii = 0; ii < LimeCfg.Npulses; ii++) { LimeCfg.p_dur[ii] = 2e-6; - LimeCfg.p_offs[ii] = (4080 * 3) / (LimeCfg.Npulses + 1); // distribute them evenly within the buffer... + LimeCfg.p_offs[ii] = + (4080 * 3) / + (LimeCfg.Npulses + 1); // distribute them evenly within the buffer... LimeCfg.p_amp[ii] = 1.0; LimeCfg.p_frq[ii] = 4.0 / LimeCfg.p_dur[0]; LimeCfg.p_pha[ii] = 0.0; @@ -443,6 +469,7 @@ LimeConfig_t initializeLimeConfig(int Npulses) LimeCfg.file_pattern = "test"; // identifier when saving the file LimeCfg.save_path = "./data/"; // path to save the file to LimeCfg.override_save = 0; // default: save data + LimeCfg.override_init = 0; // default: init LimeSDR // that's it for the parameters // ---------------------------------------------------------------------------------- @@ -475,8 +502,8 @@ LimeConfig_t initializeLimeConfig(int Npulses) return LimeCfg; } -int parseArguments(int argc, char **argv, LimeConfig_t &LimeCfg, std::vector &HDFattrVector) -{ +int parseArguments(int argc, char **argv, LimeConfig_t &LimeCfg, + std::vector &HDFattrVector) { /* Parse command line arguments @param argc: Number of arguments @@ -498,16 +525,13 @@ int parseArguments(int argc, char **argv, LimeConfig_t &LimeCfg, std::vector -1 && attr2read_last > 0) - { + } else if (curr_attr > -1 && attr2read_last > 0) { cout << "Missing argument: " << attr2read_last - << " value missing for argument " << HDFattrVector[curr_attr_last].arg - << endl; + << " value missing for argument " + << HDFattrVector[curr_attr_last].arg << endl; parse_prob = true; } // found nothing and did not read the previous arguments: a negative // number - if (curr_attr == -1 && attr2read_last > 0) - { + if (curr_attr == -1 && attr2read_last > 0) { // restore the attribute and it as number curr_attr = curr_attr_last; attr2read = attr2read_last; - } - else + } else // all other cases: jump to the next argument continue; } // parse the value from the current attribute - if (curr_attr != -1 && attr2read != 0) - { + if (curr_attr != -1 && attr2read != 0) { // differentiate between the different types of input based on the // H5::DataType float values - if (HDFattrVector[curr_attr].dType == H5::PredType::IEEE_F32LE) - { - *((float *)HDFattrVector[curr_attr].Value + attr_read) = atof(argv[ii_arg]); + if (HDFattrVector[curr_attr].dType == H5::PredType::IEEE_F32LE) { + *((float *)HDFattrVector[curr_attr].Value + attr_read) = + atof(argv[ii_arg]); attr2read--; attr_read++; // cout << "Got value " << atof(argv[ii_arg]) << " from " << // argv[ii_arg] << endl; } // double values - if (HDFattrVector[curr_attr].dType == H5::PredType::IEEE_F64LE) - { + if (HDFattrVector[curr_attr].dType == H5::PredType::IEEE_F64LE) { *((double *)HDFattrVector[curr_attr].Value + attr_read) = (double)atof(argv[ii_arg]); attr2read--; @@ -580,9 +595,9 @@ int parseArguments(int argc, char **argv, LimeConfig_t &LimeCfg, std::vector explicitly treat strings, these are anyhow just a few for file/path // info - if (strcmp(HDFattrVector[curr_attr].arg.c_str(), "spt") == 0) - { + if (strcmp(HDFattrVector[curr_attr].arg.c_str(), "spt") == 0) { LimeCfg.save_path = argv[ii_arg]; HDFattrVector[curr_attr].dType = H5::StrType(H5::PredType::C_S1, LimeCfg.save_path.length() + 1); @@ -600,8 +614,7 @@ int parseArguments(int argc, char **argv, LimeConfig_t &LimeCfg, std::vector 0) - { + if (attr2read > 0) { cout << "Missing argument: " << attr2read << " value missing for argument " << HDFattrVector[curr_attr].arg << endl; parse_prob = true; } - if (parse_prob) - { + if (parse_prob) { cout << "Exiting due to problem with provided arguments! Valid arguments " "are (exept -///, which cannot be set by the user):" << endl; string datatype; - for (int ii_attr = 0; ii_attr < no_of_attr; ii_attr++) - { + for (int ii_attr = 0; ii_attr < no_of_attr; ii_attr++) { // get the datatype if (HDFattrVector[ii_attr].dType == H5::PredType::IEEE_F32LE) @@ -654,8 +661,8 @@ int parseArguments(int argc, char **argv, LimeConfig_t &LimeCfg, std::vector &HDFattrVector) -{ +int run_experiment(LimeConfig_t LimeCfg, + std::vector &HDFattrVector) { /* Run the experiment @param LimeCfg: LimeConfig_t struct @@ -724,8 +725,7 @@ int run_experiment(LimeConfig_t LimeCfg, std::vector &HDFattrV std::copy(HDFattrVector.begin(), HDFattrVector.end(), HDFattr); // convert input in seconds/Hz to samples - for (int ii = 0; ii < LimeCfg.Npulses; ii++) - { + for (int ii = 0; ii < LimeCfg.Npulses; ii++) { LimeCfg.p_dur_smp[ii] = round(LimeCfg.p_dur[ii] * LimeCfg.srate); LimeCfg.p_frq_smp[ii] = LimeCfg.p_frq[ii] / LimeCfg.srate; LimeCfg.am_frq_smp[ii] = LimeCfg.am_frq[ii] / LimeCfg.srate; @@ -733,8 +733,7 @@ int run_experiment(LimeConfig_t LimeCfg, std::vector &HDFattrV } // check directory first - if (makePath(LimeCfg.save_path) == 0) - { + if (makePath(LimeCfg.save_path) == 0) { cout << "Problem entering the specified path: " << LimeCfg.save_path << endl; return 1; @@ -796,8 +795,7 @@ int run_experiment(LimeConfig_t LimeCfg, std::vector &HDFattrV bool rgndev = RXgain[0] != LimeCfg.RX_gain; bool tgndev = TXgain[0] != LimeCfg.TX_gain; - if (TXgain[0] > 55 && LimeCfg.TX_gain > 55) - { + if (TXgain[0] > 55 && LimeCfg.TX_gain > 55) { tgndev = false; cout << "Unable to check for variation in TXgain setting, since it is " "impossible to retrieve it for TXgain > 55 dB without altering the " @@ -818,10 +816,15 @@ int run_experiment(LimeConfig_t LimeCfg, std::vector &HDFattrV &LPFBW2) != 0) error(); bool tlpfdev = LPFBW2 != LimeCfg.TX_LPF; */ - // initialize if the frequency is different + // initialize LimeSDR if there is a deviation in relevant parameters or if it + // is enforced to init, not init // if (frqdev || sratedev || tgndev || rgndev || rlpfdev || tlpfdev || true) { - if (frqdev || sratedev || tgndev || rgndev) - { + bool override_init = + LimeCfg.override_init > 0; // override the initialization if -noi > 0 + bool enforce_init = LimeCfg.override_init < 0; // enforce init if -noi < 0 + + if (((frqdev || sratedev || tgndev || rgndev) && !override_init) || + enforce_init) { // just to re-assure why there is another setup cout << "Re-initialization of parameters ... " << endl; @@ -845,8 +848,7 @@ int run_experiment(LimeConfig_t LimeCfg, std::vector &HDFattrV // First mute the TX output, as the init commands create a lot of garbage if (LMS_WriteParam(device, LMS7_PD_TLOBUF_TRF, 1) != 0) error(); - if (LMS_SetGaindB(device, LMS_CH_TX, 0, 0) != 0) - { + if (LMS_SetGaindB(device, LMS_CH_TX, 0, 0) != 0) { cout << "Initializing device first!" << endl; @@ -891,6 +893,23 @@ int run_experiment(LimeConfig_t LimeCfg, std::vector &HDFattrV if (LMS_WriteParam(device, LMS7_DC_BYP_TXTSP, DC_EN) != 0) error(); + if (LMS_WriteParam(device, LMS7_GCORRI_TXTSP, LimeCfg.TX_IcorrGain) != 0) + error(); + if (LMS_WriteParam(device, LMS7_GCORRQ_TXTSP, LimeCfg.TX_QcorrGain) != 0) + error(); + if (LMS_WriteParam(device, LMS7_IQCORR_TXTSP, LimeCfg.TX_IQcorrPhase) != 0) + error(); + if (LMS_WriteParam(device, LMS7_GCORRI_RXTSP, LimeCfg.RX_IcorrGain) != 0) + error(); + if (LMS_WriteParam(device, LMS7_GCORRQ_RXTSP, LimeCfg.RX_QcorrGain) != 0) + error(); + if (LMS_WriteParam(device, LMS7_IQCORR_RXTSP, LimeCfg.RX_IQcorrPhase) != 0) + error(); + + // added by me as the IQ calibration did not happen on the chip from python + // or c++ + if (LMS_WriteParam(device, LMS7_MAC, 1) != 0) + error(); /* // read back DC offset in TxTSP if (LMS_ReadParam(device, LMS7_DCCORRI_TXTSP, &DC_I) != 0) error(); @@ -1009,8 +1028,7 @@ DC_Q << endl; else gain_corr *= 3; // gain steps of 3 dB for gain_lna <= 9 // eventually put this to the pga gain - if (gain_corr > 0) - { + if (gain_corr > 0) { if (LMS_WriteParam(device, LMS7_G_LNA_RFE, crit_val) != 0) error(); if (LMS_WriteParam(device, LMS7_G_PGA_RBB, gain_pga + gain_corr) != 0) @@ -1066,8 +1084,7 @@ DC_Q << endl; // note that proper scheduling requires buffersize that is a multiple of 1360 // (12bit RX) and 1020 (16bit TX) accordingly, buffersize needs to be a // multiple of 4080, which is 3*1360 and 4*1020 - if (buffersize % 4080 != 0) - { + if (buffersize % 4080 != 0) { cout << "Problem with requested buffersize of " << LimeCfg.buffersize << ", as it is not a multiple of 4080." << endl; @@ -1078,8 +1095,7 @@ DC_Q << endl; int timestampOffset = 0; // for offsets between TX and RX timestamps int bufferOffset = 0; // to correct for those offsets int16_t *buffers[chCount]; - for (int ii = 0; ii < chCount; ++ii) - { + for (int ii = 0; ii < chCount; ++ii) { buffers[ii] = new int16_t[buffersize * 2]; // buffer to hold complex values (2*samples) } @@ -1094,13 +1110,13 @@ DC_Q << endl; // Initialize streams // All streams setups should be done before starting streams. New streams // cannot be set-up if at least stream is running. - for (int ii = 0; ii < chCount; ++ii) - { + for (int ii = 0; ii < chCount; ++ii) { rx_streams[ii].channel = ii; // channel number rx_streams[ii].fifoSize = - buffersize * N_buffers_per_fifo; // fifo size in samples - rx_streams[ii].throughputVsLatency = 0.5; // some middle ground - rx_streams[ii].isTx = false; // RX channel + buffersize * N_buffers_per_fifo; // fifo size in samples + rx_streams[ii].throughputVsLatency = + 1.0; // 1.0 max throuhput, 0.0 min latency + rx_streams[ii].isTx = false; // RX channel rx_streams[ii].dataFmt = lms_stream_t::LMS_FMT_I12; // 12-bit integers if (LMS_SetupStream(device, &rx_streams[ii]) != 0) @@ -1108,9 +1124,10 @@ DC_Q << endl; tx_streams[ii].channel = ii; // channel number tx_streams[ii].fifoSize = - buffersize * N_buffers_per_fifo; // fifo size in samples - tx_streams[ii].throughputVsLatency = 0.5; // some middle ground - tx_streams[ii].isTx = true; // TX channel + buffersize * N_buffers_per_fifo; // fifo size in samples + tx_streams[ii].throughputVsLatency = + 1.0; // 1.0 max throuhput, 0.0 min latency + tx_streams[ii].isTx = true; // TX channel tx_streams[ii].dataFmt = lms_stream_t::LMS_FMT_I16; // 16-bit float if (LMS_SetupStream(device, &tx_streams[ii]) != 0) @@ -1130,8 +1147,7 @@ DC_Q << endl; // check if there are no gaps in the level specification bool found_level[max_lev + 1]; bool level_problem = false; - for (int ii = 0; ii < max_lev + 1; ii++) - { + for (int ii = 0; ii < max_lev + 1; ii++) { found_level[ii] = false; for (int ii_pls = 0; ii_pls < LimeCfg.Npulses; ii_pls++) if (LimeCfg.p_phacyc_lev[ii_pls] == ii) @@ -1139,8 +1155,7 @@ DC_Q << endl; if (!found_level[ii]) level_problem = true; } - if (level_problem) - { + if (level_problem) { cout << "Problem with specified phase cycle levels: "; for (int ii_pls = 0; ii_pls < LimeCfg.Npulses; ii_pls++) cout << setw(5) << left << LimeCfg.p_phacyc_lev[ii_pls]; @@ -1160,8 +1175,7 @@ DC_Q << endl; steps_per_lev[ii] = 0; int curr_lev_steps; // to make the code more readable... - for (int ii = 0; ii < LimeCfg.Npulses; ii++) - { + for (int ii = 0; ii < LimeCfg.Npulses; ii++) { curr_lev_steps = steps_per_lev[LimeCfg.p_phacyc_lev[ii]]; if (LimeCfg.p_phacyc_N[ii] > curr_lev_steps) steps_per_lev[LimeCfg.p_phacyc_lev[ii]] = LimeCfg.p_phacyc_N[ii]; @@ -1171,12 +1185,26 @@ DC_Q << endl; int num_phavar = 1; int steps_incr[max_lev + 1] = { 1}; // .. and the number of steps where phase is constant ... - for (int ii = 0; ii < max_lev + 1; ii++) - { + for (int ii = 0; ii < max_lev + 1; ii++) { if (ii > 0) steps_incr[ii] = steps_incr[ii - 1] * steps_per_lev[ii - 1]; num_phavar *= steps_per_lev[ii]; } + if (num_phavar < 1) { + cout << "Problem with specified number of phases (pcn) for pulses 0 to " + << LimeCfg.Npulses - 1 << ": " << endl; + for (int ii_pls = 0; ii_pls < LimeCfg.Npulses; ii_pls++) + cout << setw(5) << left << LimeCfg.p_phacyc_N[ii_pls]; + cout << endl; + cout << "Number of pulse phases must be >0 for pulses:"; + for (int ii = 0; ii < LimeCfg.Npulses; ii++) + if (LimeCfg.p_phacyc_N[ii] < 1) + cout << setw(2) << left << ii; + cout << endl; + + LMS_Close(device); + return 1; + } // ... which allows to construct the entire phase table ... double pha_tab[num_phavar][LimeCfg.Npulses]; @@ -1184,8 +1212,7 @@ DC_Q << endl; double pha_incr, curr_pha; int step_incr = 1; - for (int ii_pls = 0; ii_pls < LimeCfg.Npulses; ii_pls++) - { + for (int ii_pls = 0; ii_pls < LimeCfg.Npulses; ii_pls++) { // retrieve the phase increment if (LimeCfg.p_phacyc_N[ii_pls] != 0) pha_incr = 1.0 / LimeCfg.p_phacyc_N[ii_pls]; @@ -1198,8 +1225,7 @@ DC_Q << endl; step_incr = steps_incr[LimeCfg.p_phacyc_lev[ii_pls]]; // start to fill the table - for (int ii_pha = 0; ii_pha < num_phavar; ii_pha++) - { + for (int ii_pha = 0; ii_pha < num_phavar; ii_pha++) { // eventually increment the phase if ((ii_pha > 0) && (ii_pha % step_incr == 0)) curr_pha += pha_incr; @@ -1210,10 +1236,8 @@ DC_Q << endl; // debug: print that phase table cout << "Phase Table : " << endl; - for (int ii_pha = 0; ii_pha < num_phavar; ii_pha++) - { - for (int ii_pls = 0; ii_pls < LimeCfg.Npulses; ii_pls++) - { + for (int ii_pha = 0; ii_pha < num_phavar; ii_pha++) { + for (int ii_pls = 0; ii_pls < LimeCfg.Npulses; ii_pls++) { cout << setw(10) << left << pha_tab[ii_pha][ii_pls]; } cout << endl; @@ -1225,8 +1249,7 @@ DC_Q << endl; int exc_buffers; int pulsedur, pulseoffs; pulseoffs = 0; - for (int ii_pls = 0; ii_pls < LimeCfg.Npulses; ii_pls++) - { + for (int ii_pls = 0; ii_pls < LimeCfg.Npulses; ii_pls++) { pulsedur = LimeCfg.p_dur_smp[ii_pls]; // duration of pulse in samples pulseoffs += LimeCfg.p_offs[ii_pls]; // offset of pulse in samples @@ -1235,9 +1258,8 @@ DC_Q << endl; exc_len = pulseoffs + pulsedur; } exc_buffers = ceil((double)exc_len / (double)buffersize); - cout << "Exc_len " << exc_len << " and buffersize " << buffersize << endl; - cout << "A total of " << exc_buffers - << " excitation buffers is required to fit the experiment" << endl; + cout << "Excitation pattern: " << exc_len << " samples (" << exc_buffers + << " buffers with " << buffersize << " samples each)" << endl; // TX buffers // int16_t tx_buffer[num_phavar][exc_buffers][2*buffersize]; // @@ -1245,10 +1267,8 @@ DC_Q << endl; // TODO: put in the same way as the acq buffer, i.e. as an array of pointers. // Otherwise, there is a limitation in space that can be used int16_t *tx_buffer[num_phavar][exc_buffers]; - for (int ii = 0; ii < num_phavar; ++ii) - { - for (int jj = 0; jj < exc_buffers; ++jj) - { + for (int ii = 0; ii < num_phavar; ++ii) { + for (int jj = 0; jj < exc_buffers; ++jj) { tx_buffer[ii][jj] = new int16_t[2 * buffersize]; } } @@ -1257,23 +1277,20 @@ DC_Q << endl; int16_t smpI, smpQ; // init with zero, as we add to it - for (int ii = 0; ii < 2 * buffersize; ii++) - { - for (int jj = 0; jj < exc_buffers; jj++) - { + for (int ii = 0; ii < 2 * buffersize; ii++) { + for (int jj = 0; jj < exc_buffers; jj++) { for (int ll = 0; ll < num_phavar; ll++) tx_buffer[ll][jj][ii] = 0; } } // pulse parameters - double pulsefrq, pulseamp, pulsepha; - double w; + double pulsefrq, pulseamp, pulsepha, pulsepha_inst, pulseamp_inst; + double w, w_mod, mlt_mod, pha_acc, fm_width_smp; int buffoffs; pulseoffs = 0; - for (int ii_pls = 0; ii_pls < LimeCfg.Npulses; ii_pls++) - { + for (int ii_pls = 0; ii_pls < LimeCfg.Npulses; ii_pls++) { pulsedur = LimeCfg.p_dur_smp[ii_pls]; // duration of pulse in samples pulseoffs += LimeCfg.p_offs[ii_pls]; // offset of pulse in samples @@ -1281,46 +1298,93 @@ DC_Q << endl; pulseamp = LimeCfg.p_amp[ii_pls]; // relative amplitude of pulses pulsepha = LimeCfg.p_pha[ii_pls]; // phase of pulse + pha_acc = 0; // phase accumulation under FM + fm_width_smp = + 2 * pi * LimeCfg.fm_width[ii_pls] / + LimeCfg.srate; // width of FM in rad/sample for correct accumulation + for (int ll = 0; ll < num_phavar; - ll++) - { // generate TX Pulse for different phases + ll++) { // generate TX Pulse for different phases buffoffs = 0; for (int jj = 0; jj < exc_buffers; - jj++) - { // distribute 'long experiment' amongst buffers + jj++) { // distribute 'long experiment' amongst buffers for (int ii = 0; ii < buffersize; - ii++) - { // generate TX Pulse point by point + ii++) { // generate TX Pulse point by point if ((ii + buffoffs >= pulseoffs) & - (ii + buffoffs < pulsedur + pulseoffs)) - { + (ii + buffoffs < pulsedur + pulseoffs)) { + + // w = 2*pi*(ii+buffoffs-pulseoffs)*pulsefrq; // frequency*time, + // such that each pulse begins at zero phase - w = 2 * pi * (ii + buffoffs) * pulsefrq; // frequency*time w = 2 * pi * (ii - pulseoffs) * pulsefrq; // re-put absolute phase, which is better suited for // pulsed experiments. The inclusion of buffoffs above // was actually intended for CW type experiments, // which were put separately in CW_AFC_engine. - fsmpI = pulseamp * cos(w + pulsepha + 2 * pi * pha_tab[ll][ii_pls]); - fsmpQ = pulseamp * sin(w + pulsepha + 2 * pi * pha_tab[ll][ii_pls]); + // instantaneous pulseamp/pha + pulsepha_inst = pulsepha; + pulseamp_inst = pulseamp; + + // implement AM + if (LimeCfg.am_frq[ii_pls] != 0) { + w_mod = 2 * pi * (ii + buffoffs - pulseoffs) * + LimeCfg.am_frq_smp[ii_pls]; // inst modphase + mlt_mod = Modfunction(w_mod + LimeCfg.am_pha[ii_pls], + LimeCfg.am_mode[ii_pls]); + mlt_mod = ((mlt_mod - 1.0) * LimeCfg.am_depth[ii_pls]) + 1.0; + pulseamp_inst = pulseamp * mlt_mod; + } + + // implement FM, currently by implementing the corresponding phase + // integral analytically + if (LimeCfg.fm_frq[ii_pls] != 0) { + w_mod = 2 * pi * (ii + buffoffs - pulseoffs) * + LimeCfg.fm_frq_smp[ii_pls]; // inst modphase + if (LimeCfg.fm_mode[ii_pls] == 0) { // cosine FM -> sine PM + mlt_mod = Modfunction(w_mod + LimeCfg.fm_pha[ii_pls] - pi / 2, + LimeCfg.fm_mode[ii_pls]); + } else if (LimeCfg.fm_mode[ii_pls] == + 1) { // tirangluar FM --> quadratic PM + mlt_mod = Modfunction(w_mod + LimeCfg.fm_pha[ii_pls] - pi / 2, + LimeCfg.fm_mode[ii_pls]); + + } else if (LimeCfg.fm_mode[ii_pls] == + 2) { // square FM --> triangular PM + mlt_mod = + Modfunction(w_mod + LimeCfg.fm_pha[ii_pls] - pi / 2, 1); + } + mlt_mod = LimeCfg.fm_width[ii_pls] / 2 * mlt_mod / + LimeCfg.fm_frq[ii_pls]; + pulsepha_inst = pulsepha + mlt_mod; + + if (LimeCfg.fm_mode[ii_pls] > 90) { // method with accumulator + mlt_mod = Modfunction(w_mod + LimeCfg.fm_pha[ii_pls], + LimeCfg.fm_mode[ii_pls] - 100); + mlt_mod = fm_width_smp / 2 * mlt_mod; + pha_acc += mlt_mod; + pulsepha_inst = pulsepha + pha_acc; + } + } + + fsmpI = pulseamp_inst * + cos(w + pulsepha_inst + 2 * pi * pha_tab[ll][ii_pls]); + fsmpQ = pulseamp_inst * + sin(w + pulsepha_inst + 2 * pi * pha_tab[ll][ii_pls]); // convert to int16 ... smpI = 2047.0 * fsmpI; smpQ = 2047.0 * fsmpQ; // ... with 4 LSB at 0 for marker - if (tx_streams[0].dataFmt == lms_stream_t::LMS_FMT_I16) - { + if (tx_streams[0].dataFmt == lms_stream_t::LMS_FMT_I16) { smpI = smpI << 4; smpQ = smpQ << 4; } // add to buffer tx_buffer[ll][jj][2 * ii] += smpI; tx_buffer[ll][jj][2 * ii + 1] += smpQ; - } - else - { + } else { // fsmpI = 0.0; // fsmpQ = 0.0; } @@ -1332,17 +1396,14 @@ DC_Q << endl; // Pulse Marker for timing relative to pulse flanks // TODO: we might need some gate joining and one should also warn if the // triggers do not fit into the buffer - if (tx_streams[0].dataFmt == lms_stream_t::LMS_FMT_I16) - { + if (tx_streams[0].dataFmt == lms_stream_t::LMS_FMT_I16) { int *curr_marker; int *curr_marker_en; // marker channel for that offset for (int ii_c = 0; ii_c < 4; - ii_c++) - { // iterate through the four marker channels + ii_c++) { // iterate through the four marker channels // get the proper configuration of the trigger channel - switch (ii_c) - { + switch (ii_c) { case 0: curr_marker = LimeCfg.c0_tim; curr_marker_en = LimeCfg.p_c0_en; @@ -1368,20 +1429,16 @@ DC_Q << endl; continue; // set trigger with bitset operation - for (int ll = 0; ll < num_phavar; ll++) - { + for (int ll = 0; ll < num_phavar; ll++) { buffoffs = 0; for (int jj = 0; jj < exc_buffers; - jj++) - { // distribute 'long experiment' amongst buffers + jj++) { // distribute 'long experiment' amongst buffers for (int ii = 0; ii < 2 * buffersize; - ii++) - { // set trigger point by point + ii++) { // set trigger point by point if ((ii + buffoffs >= 2 * pulseoffs + curr_marker[2] - curr_marker[1]) & (ii + buffoffs < 2 * pulsedur + 2 * pulseoffs + - curr_marker[2] + curr_marker[3])) - { + curr_marker[2] + curr_marker[3])) { tx_buffer[ll][jj][ii] |= 1 << ii_c; } } @@ -1397,16 +1454,13 @@ DC_Q << endl; // adv] int synthstart = 0; int wrapped_phase = 0; - if (tx_streams[0].dataFmt == lms_stream_t::LMS_FMT_I16) - { + if (tx_streams[0].dataFmt == lms_stream_t::LMS_FMT_I16) { int *curr_synth; // marker channel for that offset for (int ii_c = 0; ii_c < 4; - ii_c++) - { // iterate through the four marker channels + ii_c++) { // iterate through the four marker channels // get the proper configuration of the trigger channel - switch (ii_c) - { + switch (ii_c) { case 0: curr_synth = LimeCfg.c0_synth; break; @@ -1423,38 +1477,35 @@ DC_Q << endl; break; } - cout << curr_synth[1] << endl; // check if the channel is activated if (curr_synth[0] == 0) continue; + cout << "TTL-synth t" << ii_c << " toggles state after " << curr_synth[1] + << " samples" << endl; + // set trigger with bitset operation synthstart = curr_synth[2]; - for (int ll = 0; ll < num_phavar; ll++) - { + for (int ll = 0; ll < num_phavar; ll++) { buffoffs = 0; // eventually advance the synth coupled to the phase cycle - if (curr_synth[4] > 0) - { + if (curr_synth[4] > 0) { if ((ll % curr_synth[4]) == 0) synthstart += curr_synth[3]; } for (int jj = 0; jj < exc_buffers; - jj++) - { // distribute 'long experiment' amongst buffers + jj++) { // distribute 'long experiment' amongst buffers for (int ii = 0; ii < 2 * buffersize; - ii++) - { // set trigger point by point + ii++) { // set trigger point by point // wrap the phase counter wrapped_phase = int(int(ii + buffoffs + synthstart) % int(2 * curr_synth[1])); // wrapped_phase = 0; // test = (wrapped_phase < curr_synth[1]); - if (wrapped_phase < curr_synth[1]) - { + if (wrapped_phase < curr_synth[1]) { tx_buffer[ll][jj][ii] |= 1 << ii_c; } } @@ -1465,8 +1516,7 @@ DC_Q << endl; } // generate empty TX Pulse at beginning - for (int ii = 0; ii < buffersize; ii++) - { + for (int ii = 0; ii < buffersize; ii++) { tx_buffer_1st[2 * ii] = 0.0; tx_buffer_1st[2 * ii + 1] = 0.0; @@ -1485,17 +1535,17 @@ DC_Q << endl; LimeCfg.reptime_smps = rep_offset; LimeCfg.rectime_smps = rec_len; - if (rec_len > rep_offset) - { - cout << "Recording length of " << rec_len + if (rec_len > rep_offset) { + cout << "Acquisition time of " << rec_len << " samples cannot be longer than repetition time (" << rep_offset << " Samples)" << endl; error(); } - cout << "Rep and rectimes: " << rep_offset << ", " << rec_len << endl; - cout << "Rep and rectimes in bufperiods: " << rep_offset / buffersize << ", " - << rec_len / buffersize << endl; + cout << "Repetition and acquisition times: " << rep_offset << " Sa (" + << rep_offset / buffersize << " buffers), " << rec_len << " Sa (" + << rec_len / buffersize << " buffers with " << buffersize << " Sa each)" + << endl; // Buffer for acqisition signal: integer datatype, so that we can have a // sufficient number of averages of acquired 16 bit data into the 32 bit @@ -1508,31 +1558,37 @@ DC_Q << endl; // init as non-contiguous memory (which will require writing as chunks to // HDF5) int *acqbuf[acqbuf_size]; - for (int ii = 0; ii < acqbuf_size; ++ii) - { + for (int ii = 0; ii < acqbuf_size; ++ii) { acqbuf[ii] = new int[2 * rec_len]; } // brute-force initialization by zero ( memset(acqbuf, 0, // acqbuf_size*2*rec_len); ) did not work... - for (int ii = 0; ii < acqbuf_size; ii++) - { - for (int jj = 0; jj < 2 * rec_len; jj++) - { + for (int ii = 0; ii < acqbuf_size; ii++) { + for (int jj = 0; jj < 2 * rec_len; jj++) { acqbuf[ii][jj] = 0; } } + // mirror buffer: just holds a copy of the entire current record, without 32 + // to 16 bit conversion + int16_t *mirror_buf; + mirror_buf = new int16_t[2 * rec_len]; + + // number of lost data due to packet loss + int lost[acqbuf_size] = {0}; + std::list lost_acqs = {}; + // Streaming - lms_stream_meta_t rx_metadata; // Use metadata for additional control over - // sample receive function behavior + lms_stream_meta_t rx_metadata; // Use metadata for additional control over + // sample receive function behavior rx_metadata.flushPartialPacket = false; // currently has no effect in RX rx_metadata.waitForTimestamp = false; // currently has no effect in RX lms_stream_meta_t tx_metadata; // Use metadata for additional control over // sample send function behavior tx_metadata.flushPartialPacket = - false; // do not force sending of incomplete packet + false; // do not force sending of incomplete packet tx_metadata.waitForTimestamp = true; // Enable synchronization to HW timestamp lms_stream_status_t status; // To check the FIFO @@ -1566,12 +1622,15 @@ DC_Q << endl; int ii_rep = 0; // number of repetions int ii_pcyc = 0; // number of phase cycle int ii_avg = 0; // number of averages - int ii_acq = 0; // number of complete acquisitions + int ii_acqd = 0; // number of complete acquisitions + int ii_acq = -1; // acquisition index (== ii_acqd if there is no packet loss) int samples2Acquire = 0; // number of samples to acquire in current acquisition int validSamples = 0; // number of valid samples in current datapacket int *acqbuf_pos; // pointer to acqbuffer int *delayedacqbuf_pos; // pointer to acqbuffer for delayed fwd + int reps_btw_stamps; // number of repetions between last valid timestamp + int16_t *mirbuf_pos; // pointer to mirror buffer bool acquiring = false; // RX stream to acqbuffer? bool acquire = true; // disable one single acquisition in acq loop @@ -1580,22 +1639,19 @@ DC_Q << endl; int ndebug = 100; // Start streaming - for (int i = 0; i < chCount; ++i) - { + for (int i = 0; i < chCount; ++i) { LMS_StartStream(&rx_streams[i]); LMS_StartStream(&tx_streams[i]); } // pre-fill the TX fifo - for (int ii_TXbuff = 0; ii_TXbuff < N_buffers_per_fifo; ii_TXbuff++) - { + for (int ii_TXbuff = 0; ii_TXbuff < N_buffers_per_fifo; ii_TXbuff++) { // save the TX timestamp to the current packet tx_metadata.timestamp = next_TXtimestamp; // First packet is special, since it is cut off in some weird way - if (ii_TXbuff == 0) - { + if (ii_TXbuff == 0) { LMS_SendStream(&tx_streams[0], tx_buffer_1st, buffersize, &tx_metadata, 1000); // so we put zeros TXFIFO_slots--; @@ -1619,49 +1675,43 @@ DC_Q << endl; // advance the tx_buffer counter ii_TXoffset++; next_TXtimestamp += buffersize; - if (ii_TXoffset == exc_buffers) - { + if (ii_TXoffset == exc_buffers) { ii_TXoffset = 0; next_TXtimestamp = last_TXtimestamp + rep_offset; last_TXtimestamp = next_TXtimestamp; - if (LimeCfg.pcyc_bef_avg > 0) - { + if (LimeCfg.pcyc_bef_avg > 0) { ii_TXpcyc++; - if (ii_TXpcyc == num_phavar) - { + if (ii_TXpcyc == num_phavar) { ii_TXpcyc = 0; ii_TXavg++; - if (ii_TXavg == LimeCfg.averages) - { + if (ii_TXavg == LimeCfg.averages) { ii_TXavg = 0; ii_TXrep++; // in case the entire experiment fits within the TX FIFO - if (ii_TXrep == LimeCfg.repetitions) + if (ii_TXrep == LimeCfg.repetitions) { + TXFIFO_slots = 0; break; + } } } - } - else - { + } else { ii_TXavg++; - if (ii_TXavg == LimeCfg.averages) - { + if (ii_TXavg == LimeCfg.averages) { ii_TXavg = 0; ii_TXpcyc++; - if (ii_TXpcyc == num_phavar) - { + if (ii_TXpcyc == num_phavar) { ii_TXpcyc = 0; ii_TXrep++; // in case the entire experiment fits within the TX FIFO - if (ii_TXrep == LimeCfg.repetitions) + if (ii_TXrep == LimeCfg.repetitions) { + TXFIFO_slots = 0; break; + } } } } - } - else - { + } else { // if there is still data to be put on the buffer } } @@ -1669,27 +1719,24 @@ DC_Q << endl; /* // Check for the TX buffer and keep it filled LMS_GetStreamStatus(tx_streams, &status); //Obtain TX stream stats - if (status.fifoFilledCount != 0) cout << TXFIFO_slots <<" TXFIFO slots - free before start: " << status.fifoFilledCount << " samples of " << - status.fifoSize << " with HW stamp " << status.timestamp <<" at RX - timestamp" << rx_metadata.timestamp << endl; + if (status.fifoFilledCount != 0) cout << TXFIFO_slots <<" TXFIFO slots free + before start: " << status.fifoFilledCount << " samples of " << status.fifoSize + << " with HW stamp " << status.timestamp <<" at RX timestamp" << + rx_metadata.timestamp << endl; */ // Main acquisition loop - while (ii_acq < LimeCfg.repetitions * LimeCfg.averages * num_phavar) - { + while (ii_acq < LimeCfg.repetitions * LimeCfg.averages * num_phavar) { // Receive samples - if (acquire) - { + if (acquire) { samplesRead = LMS_RecvStream(&rx_streams[0], buffers[0], buffersize, &rx_metadata, 1000); rcvattempts++; samplesReadSum += samplesRead; } - if (ndebug < 10) - { + if (ndebug < 100) { cout << rx_metadata.timestamp << ", " << samplesReadSum << endl; ndebug++; LMS_GetStreamStatus(rx_streams, &status); // Obtain RX stream stats @@ -1701,73 +1748,167 @@ DC_Q << endl; // check if the scheduled timestamp is coming here // if (rx_metadata.timestamp >= next_RXtimestamp) { if ((rx_metadata.timestamp >= next_RXtimestamp - samplesRead + 1) && - acquire) - { + acquire) { + + // first check out scheduling + timestampOffset = + (signed long)next_RXtimestamp - (signed long)rx_metadata.timestamp; + + // normal case: scheduling as it should, where one reptime passed between + // the current and the last stamp + reps_btw_stamps = 1; + + // abnoral case: packet loss, i.e. more than one reptime passed since last + // stamp + if (timestampOffset < 0) { + reps_btw_stamps = + ceil(-(double)timestampOffset / (double)rep_offset) + 1; + // cout << "Acq: rep " << ii_rep << ", avg " << ii_avg << ", pcyc " << + // ii_pcyc << " : sched/act tstamp: " << next_RXtimestamp << ", " << + // rx_metadata.timestamp << " Diff to last: " << next_RXtimestamp - + // last_RXtimestamp << " Offset: " << timestampOffset << ", which + // corresponds to " << -(double) timestampOffset /(double) rep_offset << + // " repetitions, currently processing " << samplesRead << " RX samples + // with delayed fwd " << delayedAcqbufFwd << endl; + + // shift the timestamp scheduling correspondingly + timestampOffset += (reps_btw_stamps - 1) * rep_offset; + next_RXtimestamp += (reps_btw_stamps - 1) * rep_offset; + + // abort any ongoing acquisition, including delayed acquisition due to + // offsets + if (acquiring) { + cout << "Packet loss during rep " << ii_rep << ", avg " << ii_avg + << ", pcyc " << ii_pcyc << ", acq " << ii_acq << ", acqd " + << ii_acqd << ": " << samples2Acquire + << " Samples were not written and only the first few samples " + "are non-corrupted with certainty. " + << endl; + + // substract the content of the mirror_buf from the acqbuf, just to + // avoid that crap is being kept on the main buffer. + acqbuf_pos = acqbuf[ii_rep * num_phavar + ii_pcyc]; + for (int ii_acqbuf = 0; ii_acqbuf < 2 * (rec_len - samples2Acquire); + ii_acqbuf++) + acqbuf_pos[ii_acqbuf] -= (int)mirror_buf[ii_acqbuf]; + + // store that this is lost + lost[ii_rep * num_phavar + ii_pcyc]++; + lost_acqs.push_back(ii_acq); + + // ii_acq++; // for an ongoing acquisition, we still need to increment + // the acquisition counter + // this is actually weird here. It seems that we land here sometimes + // with ii_acq already advanced and sometimes with ii_acq still + // needing to be incremented. in fact, when resuming, we are + // sometimes having ii_acq one before ii_avg, or aligned, as it + // should so we do lose one acq and are moreover shifting the entire + // indexing. However, if ii_acq is uncommented, there is a segfault + // from advancing ii_acq once too few, so that it is better to keep + // this one it is probably related to delayed acqbuf fwd. the + // cleanest would eventually be to make a new ii_acq counter that + // counts the memory location and is incremented together with the + // ii_pcyc counters, etc. The actual ii_acq that is incremented after + // having acquired data, could be named ii_acqd to count for the + // acquired data packets. + } + acquiring = false; // this will be reset to true just below, which will + // cause those unwritten samples + delayedAcqbufFwd = false; // ... and make sure that these unwritten + // samples do not go anywhere to the buffer + } + + // Advance counters to new position + for (int ii = 0; ii < reps_btw_stamps; ii++) { + + // except at the very first acquisition, where the counters are already + // at the right position + // if (ii_acq == 0 & ii == 0) break; + + // book keep any of the lost acquisitions + if (ii > 0) { + lost[ii_rep * num_phavar + ii_pcyc]++; + lost_acqs.push_back(ii_acq); + } + + ii_acq++; + + // except at the very first acquisition, where the counters are already + // at the right position + if (ii_acq == 0) + break; + + // advance counters + if (LimeCfg.pcyc_bef_avg > 0) { + ii_pcyc++; + if (ii_pcyc == num_phavar) { + ii_pcyc = 0; + ii_avg++; + if (ii_avg == LimeCfg.averages) { + ii_avg = 0; + ii_rep++; + } + } + } else { + ii_avg++; + if (ii_avg == LimeCfg.averages) { + ii_avg = 0; + ii_pcyc++; + if (ii_pcyc == num_phavar) { + ii_pcyc = 0; + ii_rep++; + } + } + } + } + + // additional reporting in case of packet loss + if (reps_btw_stamps > 1) { + + cout << " Resuming acq at rep " << ii_rep << ", avg " << ii_avg + << ", pcyc " << ii_pcyc << ", acq " << ii_acq << ": " + << "Skipped " << reps_btw_stamps - 1 + << " acquisitions to restore a timestamp offset of " + << timestampOffset << endl; + } // Advance acqbuf in case that there is not an ongoing acquisition (just // in the case of gap-free acquisition, that has usually a timestamp // offset) - if (acquiring == false) - { + if (acquiring == false) { acqbuf_pos = acqbuf[ii_rep * num_phavar + ii_pcyc]; samples2Acquire = rec_len; - } - else - { + } else { delayedacqbuf_pos = acqbuf[ii_rep * num_phavar + ii_pcyc]; delayedAcqbufFwd = true; } acquiring = true; - // advance counters - if (LimeCfg.pcyc_bef_avg > 0) - { - ii_pcyc++; - if (ii_pcyc == num_phavar) - { - ii_pcyc = 0; - ii_avg++; - if (ii_avg == LimeCfg.averages) - { - ii_avg = 0; - ii_rep++; - } - } - } - else - { - ii_avg++; - if (ii_avg == LimeCfg.averages) - { - ii_avg = 0; - ii_pcyc++; - if (ii_pcyc == num_phavar) - { - ii_pcyc = 0; - ii_rep++; - } - } - } + // cout << "Now at rep " << ii_rep << ", avg " << ii_avg << ", pcyc " << + // ii_pcyc << ", acq " << ii_acq << ", acqd " << ii_acqd << "."<< endl; - // store the offset - timestampOffset = - (signed long)next_RXtimestamp - (signed long)rx_metadata.timestamp; - - // Important debug message in general, will however only be printed in - // case the offset gets positive (which will inevidably run into a - // segfault and is related to a too fast samplingrate combined with a too - // small rectime_secs) - if (timestampOffset < 0) - { - cout << "Next acq: rep " << ii_rep << ", avg " << ii_avg << ", pcyc " - << ii_pcyc << " : sched/act tstamp: " << next_RXtimestamp << ", " - << rx_metadata.timestamp - << " Diff to last: " << next_RXtimestamp - last_RXtimestamp - << " Offset: " - << (signed long)rx_metadata.timestamp - - (signed long)next_RXtimestamp - << " currently processing " << samplesRead - << " RX samples with delayed fwd " << delayedAcqbufFwd << endl; + // Warn in case of packet loss + // Important: We exclude here packet loss at the very beginning of the RX + // stream. This is actually rather normal and the reason for the offset in + // the RX buffer + LMS_GetStreamStatus(rx_streams, &status); // Obtain RX stream stats + if ((status.droppedPackets > 0 | status.overrun > 0 | + status.underrun > 0) & + ii_acq > 0) { + cout << "Rx stream trouble! Status (overrun, underrun, dropped " + "Packets): " + << status.overrun << ", " << status.underrun << ", " + << status.droppedPackets << ". Currently " + << status.fifoFilledCount << " samples in RX stream buffer (" + << 100.0 * ((float)status.fifoFilledCount) / + ((float)status.fifoSize) + << "%)" << endl; + // cout << "Next acq: rep " << ii_rep << ", avg " << ii_avg << ", pcyc " + // << ii_pcyc << " : sched/act tstamp: " << next_RXtimestamp << ", " << + // rx_metadata.timestamp << " Diff to last: " << next_RXtimestamp - + // last_RXtimestamp << " Offset: " << timestampOffset << " currently + // processing " << samplesRead << " RX samples with delayed fwd " << + // delayedAcqbufFwd << endl; } // advance to the forthcoming RX timestamp and keep the current one @@ -1776,23 +1917,26 @@ DC_Q << endl; } acquire = true; + // Do not enter into acquiring for the case that the experiment is at its + // end + if (ii_rep == LimeCfg.repetitions & delayedAcqbufFwd == false) + break; + // copy RX data into acquisition buffer - if (acquiring) - { + if (acquiring) { // standard case: copy everything, without offset bufferOffset = 0; validSamples = buffersize; // first packet: consider eventual timestamp offset - if (samples2Acquire == rec_len) - { + if (samples2Acquire == rec_len) { bufferOffset = timestampOffset; validSamples = buffersize - bufferOffset; + + mirbuf_pos = mirror_buf; // last packet with timestamp offset: just get the tail without offset - } - else if (samples2Acquire < buffersize) - { + } else if (samples2Acquire < buffersize) { validSamples = samples2Acquire; } @@ -1800,17 +1944,19 @@ DC_Q << endl; acqbuf_pos[ii_acqbuf] += (int)buffers[0][ii_acqbuf + 2 * bufferOffset]; samples2Acquire -= validSamples; + // memcopy mirrorbuf + memcpy(mirbuf_pos, acqbuf_pos, 2 * validSamples * sizeof(int16_t)); + // advance position in acquisition buffer acqbuf_pos += 2 * validSamples; + mirbuf_pos += 2 * validSamples; - if (samples2Acquire == 0) - { - ii_acq++; + if (samples2Acquire == 0) { + ii_acqd++; // check for continuous RX with timestamp offset, where we would // actually still have valid samples to copy in the buffer - if (delayedAcqbufFwd) - { + if (delayedAcqbufFwd) { // put pointer to right place acqbuf_pos = delayedacqbuf_pos; samples2Acquire = rec_len; @@ -1820,9 +1966,7 @@ DC_Q << endl; // forwarding the timestamp packet. We still need to copy the part // beyond the validsamples to the next acqbuf position acquire = false; - } - else - { + } else { // standard case: signal that the acquisition is finished and wait for // the next scheduled timestamp acquiring = false; @@ -1831,8 +1975,7 @@ DC_Q << endl; } // Check for the TX buffer and keep it filled - if (ii_TXrep < LimeCfg.repetitions) - { + if (ii_TXrep < LimeCfg.repetitions) { LMS_GetStreamStatus(tx_streams, &status); // Obtain TX stream stats TXFIFO_slots = (status.fifoSize - status.fifoFilledCount) / buffersize; } @@ -1844,8 +1987,7 @@ DC_Q << endl; */ // re-fill the TX fifo - while (TXFIFO_slots > 0) - { + while (TXFIFO_slots > 0) { // save the TX timestamp to the current packet tx_metadata.timestamp = next_TXtimestamp; @@ -1861,39 +2003,30 @@ DC_Q << endl; // advance the tx_buffer counter ii_TXoffset++; next_TXtimestamp += buffersize; - if (ii_TXoffset == exc_buffers) - { + if (ii_TXoffset == exc_buffers) { ii_TXoffset = 0; next_TXtimestamp = last_TXtimestamp + rep_offset; last_TXtimestamp = next_TXtimestamp; - if (LimeCfg.pcyc_bef_avg > 0) - { + if (LimeCfg.pcyc_bef_avg > 0) { ii_TXpcyc++; - if (ii_TXpcyc == num_phavar) - { + if (ii_TXpcyc == num_phavar) { ii_TXpcyc = 0; ii_TXavg++; - if (ii_TXavg == LimeCfg.averages) - { + if (ii_TXavg == LimeCfg.averages) { ii_TXavg = 0; ii_TXrep++; // in case the experiment is finished if (ii_TXrep == LimeCfg.repetitions) - ; - TXFIFO_slots = 0; + TXFIFO_slots = 0; } } - } - else - { + } else { ii_TXavg++; - if (ii_TXavg == LimeCfg.averages) - { + if (ii_TXavg == LimeCfg.averages) { ii_TXavg = 0; ii_TXpcyc++; - if (ii_TXpcyc == num_phavar) - { + if (ii_TXpcyc == num_phavar) { ii_TXpcyc = 0; ii_TXrep++; // in case the experiment is finished @@ -1907,14 +2040,12 @@ DC_Q << endl; } // Stop streaming - for (int i = 0; i < chCount; ++i) - { + for (int i = 0; i < chCount; ++i) { LMS_StopStream(&rx_streams[i]); // stream is stopped but can be started // again with LMS_StartStream() LMS_StopStream(&tx_streams[i]); } - for (int i = 0; i < chCount; ++i) - { + for (int i = 0; i < chCount; ++i) { LMS_DestroyStream( device, &rx_streams[i]); // stream is deallocated and can no longer be used @@ -1922,12 +2053,24 @@ DC_Q << endl; delete[] buffers[i]; } + delete mirror_buf; + + cout << "Lost acquisitions: "; + for (int ii = 0; ii < acqbuf_size; ii++) { + cout << lost[ii] << ", "; + } + cout << endl; + + // Iterate and print values of the list + for (int n : lost_acqs) { + std::cout << n << '\n'; + } + //------------------------------------------------------------------------------------- // SAVE TO HDF5 //------------------------------------------------------------------------------------- - if (LimeCfg.override_save == 0) - { + if (LimeCfg.override_save == 0) { // Open HDF5 file string filename; @@ -1989,8 +2132,7 @@ DC_Q << endl; hsize_t dims_row[2] = {1, saveDataDim[1]}; H5::DataSpace mspace_row( 1, &saveDataDim[1]); // contiguous memory space of data to write - for (int ii = 0; ii < acqbuf_size; ii++) - { + for (int ii = 0; ii < acqbuf_size; ii++) { fspace_row.selectHyperslab(H5S_SELECT_SET, dims_row, offset); dataset.write(acqbuf[ii], saveDataType, mspace_row, fspace_row); offset[0] += 1; // advance to next row @@ -2004,10 +2146,8 @@ DC_Q << endl; stringstream << std::put_time(localtime(&itt), "%G%m%d_%H%M%S"); // ... and write it to the appropriate index - for (int ii_attr = 0; ii_attr < no_of_attr; ii_attr++) - { - if (strcmp("Exp End Timestamp", HDFattr[ii_attr].Name.c_str()) == 0) - { + for (int ii_attr = 0; ii_attr < no_of_attr; ii_attr++) { + if (strcmp("Exp End Timestamp", HDFattr[ii_attr].Name.c_str()) == 0) { LimeCfg.stamp_end = stringstream.str(); HDFattr[ii_attr].dType = H5::StrType(H5::PredType::C_S1, LimeCfg.stamp_end.length() + 1); @@ -2016,13 +2156,11 @@ DC_Q << endl; } // write the attributes - for (int ii = 0; ii < no_of_attr; ii++) - { + for (int ii = 0; ii < no_of_attr; ii++) { H5::DataSpace *tmpSpace = new H5::DataSpace(); // special case: arrays - if (HDFattr[ii].dim > 1) - { + if (HDFattr[ii].dim > 1) { delete tmpSpace; H5::DataSpace *tmpSpace = new H5::DataSpace(1, &HDFattr[ii].dim); } @@ -2061,8 +2199,7 @@ DC_Q << endl; return 0; } -int run_experiment_from_LimeCfg(LimeConfig_t LimeCfg) -{ +int run_experiment_from_LimeCfg(LimeConfig_t LimeCfg) { int Npulses = LimeCfg.Npulses; // Number of pulses from the LimeCfg @@ -2073,17 +2210,14 @@ int run_experiment_from_LimeCfg(LimeConfig_t LimeCfg) return status; } -int main(int argc, char **argv) -{ +int main(int argc, char **argv) { const double pi = acos(-1); int Npulses = 2; // default number of pulses // check if nPulses has been given as argument, so that all the arrays are // initialized with proper size - for (int ii_arg = 1; ii_arg < argc; ii_arg++) - { - if (strcmp(argv[ii_arg], "-npu") == 0 && ii_arg + 1 < argc) - { + for (int ii_arg = 1; ii_arg < argc; ii_arg++) { + if (strcmp(argv[ii_arg], "-npu") == 0 && ii_arg + 1 < argc) { Npulses = atoi(argv[ii_arg + 1]); break; } @@ -2099,24 +2233,20 @@ int main(int argc, char **argv) bool dumpFlag = false; // Checking for dump flag - for (int i = 1; i < argc; ++i) - { - if (strcmp(argv[i], "--dump") == 0) - { + for (int i = 1; i < argc; ++i) { + if (strcmp(argv[i], "--dump") == 0) { dumpFlag = true; } } // If dump flag is set, dump the config and exit - if (dumpFlag) - { + if (dumpFlag) { dumpConfig(HDFattrVector); std::exit(0); } // Parse command line arguments - if (parseArguments(argc, argv, LimeCfg, HDFattrVector) != 0) - { + if (parseArguments(argc, argv, LimeCfg, HDFattrVector) != 0) { return 1; }