From f4e2743690ba0e5441b4725fb76c991d4c9bbc7a Mon Sep 17 00:00:00 2001 From: Kumi Date: Mon, 5 Feb 2024 10:23:09 +0100 Subject: [PATCH] Fix limedriver.cpp location --- limedriver.cpp | 1848 -------------------------------------------- src/limedriver.cpp | 3 +- 2 files changed, 1 insertion(+), 1850 deletions(-) delete mode 100644 limedriver.cpp diff --git a/limedriver.cpp b/limedriver.cpp deleted file mode 100644 index f5d54c0..0000000 --- a/limedriver.cpp +++ /dev/null @@ -1,1848 +0,0 @@ -/** - @file N pulses - @author Andrin Doll - @brief N pulses on TX and collection on RX - - -requirements: -LimeSuite -HDF5 library - -compilation: -$(h5c++ -show) limedriver.cpp -std=c++11 $(pkg-config --cflags --libs LimeSuite) -o limedriver - - */ - -#include "H5Cpp.h" -#include "lime/LimeSuite.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#include // errno, ENOENT, EEXIST -#include // stat -#include -#if defined(_WIN32) -#include // _mkdir -#endif - -using namespace std; - -// structure that holds all the relevant parameters for a N-pulse experiment. -// See initialization below for description -const int maxNpulse = 50; -struct LimeConfig_t { - - float srate; - float frq; - float frq_set; - float RX_LPF; - float TX_LPF; - int RX_gain; - int TX_gain; - int TX_IcorrDC; - int TX_QcorrDC; - int RX_gain_rback[4]; - int TX_gain_rback[3]; - - int Npulses; - double *p_dur; - int *p_dur_smp; - int *p_offs; - double *p_amp; - double *p_frq; - double *p_frq_smp; - double *p_pha; - int *p_phacyc_N; - int *p_phacyc_lev; - - int *p_c0_en; - int *p_c1_en; - int *p_c2_en; - int *p_c3_en; - - int c0_tim[4]; - int c1_tim[4]; - int c2_tim[4]; - int c3_tim[4]; - - int c0_synth[5]; - int c1_synth[5]; - int c2_synth[5]; - int c3_synth[5]; - - int averages; - int repetitions; - bool pcyc_bef_avg; - double reptime_secs; - double rectime_secs; - int reptime_smps; - int rectime_smps; - int buffersize; - - string file_pattern; - string file_stamp; - string save_path; - int override_save; - - string stamp_start; - string stamp_end; -}; - -// structure that will be used to map LimeConfig to HDF attribute -struct Config2HDFattr_t { - string arg; - H5std_string Name; - H5::DataType dType; - void *Value; - hsize_t dim; -}; - -// Device structure, should be initialize to NULL -static lms_device_t *device = NULL; - -// LMS error function -int error() { - if (device != NULL) - LMS_Close(device); - exit(-1); -} - -// 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) { -#if defined(_WIN32) - struct _stat info; - 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) { - return false; - } - return (info.st_mode & S_IFDIR) != 0; -#endif -} - -bool makePath(const std::string &path) { -#if defined(_WIN32) - int ret = _mkdir(path.c_str()); -#else - mode_t mode = 0755; - int ret = mkdir(path.c_str(), mode); -#endif - if (ret == 0) - return true; - - switch (errno) { - case ENOENT: - // parent didn't exist, try to create it - { - int pos = path.find_last_of('/'); - if (pos == std::string::npos) -#if defined(_WIN32) - pos = path.find_last_of('\\'); - if (pos == std::string::npos) -#endif - return false; - if (!makePath(path.substr(0, pos))) - return false; - } - // now, try to create again -#if defined(_WIN32) - return 0 == _mkdir(path.c_str()); -#else - return 0 == mkdir(path.c_str(), mode); -#endif - - case EEXIST: - // done! - return isDirExist(path); - - default: - return false; - } -} - -inline bool file_exists(const std::string &name) { - struct stat buffer; - return (stat(name.c_str(), &buffer) == 0); -} - -// Custom function to read back the gain of the RX/TX channels. The API function -// 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) { - // 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) - error(); - if (LMS_ReadParam(device, LMS7_G_TIA_RFE, &gain_tia) != 0) - error(); - if (LMS_ReadParam(device, LMS7_G_PGA_RBB, &gain_pga) != 0) - error(); - // convert to actual gain - - // TX gain: PAD and TBB - uint16_t gain_pad, gain_tbb; - if (LMS_ReadParam(device, LMS7_LOSS_LIN_TXPAD_TRF, &gain_pad) != 0) - error(); - if (LMS_ReadParam(device, LMS7_CG_IAMP_TBB, &gain_tbb) != 0) - error(); - - // convert to actual gain - // TXpad - const int pmax = 52; - if (gain_pad > 10) - TXgain[1] = pmax - 10 - 2 * (gain_pad - 10); - else - TXgain[1] = pmax - gain_pad; - - // TBB gain: linear to dB. Impossible to obtain like this. It is calibrated to - // an optimum value, called opt_gain_tbb. This is only available if a - // calibration is done. However, as long as the TXgain is below 43+12 = 55 dB, - // gain is fully determined by TX PAD - TXgain[2] = gain_tbb; - // TXgain[2] = 20.0*log10((float_type)gain_tbb / (float_type) opt_gain_tbb); - // from LMS7002.cpp - - // RFE LNA - int gmax = 30; - if (gain_lna >= 9) - RXgain[1] = gmax + (gain_lna - 15); - else - RXgain[1] = gmax + 3 * (gain_lna - 11); - - // RFE TIA - gmax = 12; - switch (gain_tia) { - case 3: - RXgain[2] = gmax - 0; - break; - case 2: - RXgain[2] = gmax - 3; - break; - case 1: - RXgain[2] = gmax - 12; - break; - } - - // RBB PGA - RXgain[3] = gain_pga - 12; - - // Sum to first element, adding that mysterious +12+0.5 as it is done in the - // API - RXgain[0] = RXgain[1] + RXgain[2] + RXgain[3] + 12 + 0.5; - TXgain[0] = TXgain[1] + 0 * TXgain[2] + 12 + 0.5; - - // Print in function to ease debugging - cout << "TX: " << TXgain[0] << " dB : " << TXgain[1] << " dB PAD, " - << TXgain[2] << " setting of BB + 12 dB" << endl; - cout << "RX: " << RXgain[0] << " dB : " << RXgain[1] << " dB LNA, " - << RXgain[2] << " dB TIA, " << RXgain[3] << " dB PGA + 12 dB" << endl; - - return 0; -} - -int main(int argc, char **argv) { - const double pi = acos(-1); - - LimeConfig_t LimeCfg; - - LimeCfg.Npulses = 2; // Number of pulses, default value - - // 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) { - LimeCfg.Npulses = atoi(argv[ii_arg + 1]); - break; - } - } - - // ---------------------------------------------------------------------------------- - // set all the DEFAULT parameters. Command line arguments allow for - // modification! - LimeCfg.srate = 30.72e6; // sample rate of the IF DAC/ADC - LimeCfg.frq = 50e6; // LO carrier frequency - LimeCfg.RX_gain = 20; // total gain of the receiver - LimeCfg.TX_gain = 30; // total gain of the transmitter - LimeCfg.RX_LPF = 5e6; // IF lowpass of the receiver - 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 - - // 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 - - // and set standard values - 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_amp[ii] = 1.0; - LimeCfg.p_frq[ii] = 4.0 / LimeCfg.p_dur[0]; - LimeCfg.p_pha[ii] = 0.0; - LimeCfg.p_phacyc_N[ii] = 1; - LimeCfg.p_phacyc_lev[ii] = 0; - LimeCfg.p_c0_en[ii] = 1; - LimeCfg.p_c1_en[ii] = 1; - LimeCfg.p_c2_en[ii] = 1; - LimeCfg.p_c3_en[ii] = 1; - } - - // Timing of TTL controls: [enabled? , pre, offs, post] - int c0_tim[4] = {0, 70, 56, -5}; - int c1_tim[4] = {0, 70, 56, -5}; - int c2_tim[4] = {0, 70, 56, -5}; - int c3_tim[4] = {0, 70, 56, -5}; - - // Use TTL channel as synth: [enabled? , half-period, strt, PSK shift, PSK - // adv] - int c0_synth[5] = {0, 500, 0, 0, 0}; - int c1_synth[5] = {0, 500, 0, 0, 0}; - int c2_synth[5] = {0, 500, 0, 0, 0}; - int c3_synth[5] = {0, 500, 0, 0, 0}; - - LimeCfg.averages = 6; // number of averages - LimeCfg.repetitions = 4; // number of repetions - LimeCfg.reptime_secs = 4e-3; // repetition time - LimeCfg.rectime_secs = 0.2e-3; // duration of acquisition window - LimeCfg.buffersize = 4080 * 3; // number of samples in buffer - LimeCfg.pcyc_bef_avg = 0; // phase cycle before average - - LimeCfg.file_pattern = "test"; // identifier when saving the file - LimeCfg.save_path = "./asdf/"; // path to save the file to - LimeCfg.override_save = 0; // default: save data - - // that's it for the parameters - // ---------------------------------------------------------------------------------- - - // .. copy here those arrays ... - memcpy(LimeCfg.c0_tim, c0_tim, 4 * sizeof *LimeCfg.c0_tim); - memcpy(LimeCfg.c1_tim, c1_tim, 4 * sizeof *LimeCfg.c1_tim); - memcpy(LimeCfg.c2_tim, c2_tim, 4 * sizeof *LimeCfg.c2_tim); - memcpy(LimeCfg.c3_tim, c3_tim, 4 * sizeof *LimeCfg.c3_tim); - memcpy(LimeCfg.c0_synth, c0_synth, 5 * sizeof *LimeCfg.c0_synth); - memcpy(LimeCfg.c1_synth, c1_synth, 5 * sizeof *LimeCfg.c1_synth); - memcpy(LimeCfg.c2_synth, c2_synth, 5 * sizeof *LimeCfg.c2_synth); - memcpy(LimeCfg.c3_synth, c3_synth, 5 * sizeof *LimeCfg.c3_synth); - - // and add the timestamp for the file - auto now = std::chrono::system_clock::now(); - auto itt = std::chrono::system_clock::to_time_t(now); - std::ostringstream stringstream; - stringstream << std::put_time(localtime(&itt), "%G%m%d_%H%M%S"); - LimeCfg.file_stamp = stringstream.str(); - LimeCfg.stamp_start = stringstream.str(); - LimeCfg.stamp_end = - stringstream.str(); // will be overwritten just before data is written - - // allocate other variables that depend on Npulses - LimeCfg.p_dur_smp = new int[LimeCfg.Npulses]; - LimeCfg.p_frq_smp = new double[LimeCfg.Npulses]; - - // LimeCfg as attributes for writing to HDF and for parsing command line input - // This is all done 'manually', since there is no reflection in cpp.. at least - // not by default - struct Config2HDFattr_t HDFattr[] = { - {"sra", "SampleRate [Hz]", H5::PredType::IEEE_F32LE, &LimeCfg.srate, 1}, - {"lof", "LO Frequency [Hz]", H5::PredType::IEEE_F32LE, &LimeCfg.frq, 1}, - {"rlp", "RX LowPass BW [Hz]", H5::PredType::IEEE_F32LE, &LimeCfg.RX_LPF, - 1}, - {"tlp", "TX LowPass BW [Hz]", H5::PredType::IEEE_F32LE, &LimeCfg.TX_LPF, - 1}, - {"rgn", "RX Gain [dB]", H5::PredType::NATIVE_INT, &LimeCfg.RX_gain, 1}, - {"tgn", "TX Gain [dB]", H5::PredType::NATIVE_INT, &LimeCfg.TX_gain, 1}, - {"///", "RX Gain readback [dB]", H5::PredType::NATIVE_INT, - &LimeCfg.RX_gain_rback, 4}, - {"///", "TX Gain readback [dB]", H5::PredType::NATIVE_INT, - &LimeCfg.TX_gain_rback, 3}, - {"tdq", "TX DC-correction Q", H5::PredType::NATIVE_INT, - &LimeCfg.TX_QcorrDC, 1}, - {"tdi", "TX DC-correction I", H5::PredType::NATIVE_INT, - &LimeCfg.TX_IcorrDC, 1}, - {"npu", "Number of Pulses", H5::PredType::NATIVE_INT, &LimeCfg.Npulses, - 1}, - {"pdr", "Pulse Duration [s]", H5::PredType::IEEE_F64LE, LimeCfg.p_dur, - (hsize_t)LimeCfg.Npulses}, - {"pof", "Pulse Offset [Sa]", H5::PredType::NATIVE_INT, LimeCfg.p_offs, - (hsize_t)LimeCfg.Npulses}, - {"pam", "IF Pulse Amplitude", H5::PredType::IEEE_F64LE, LimeCfg.p_amp, - (hsize_t)LimeCfg.Npulses}, - {"pfr", "IF Pulse Frequency [Hz]", H5::PredType::IEEE_F64LE, - LimeCfg.p_frq, (hsize_t)LimeCfg.Npulses}, - {"pph", "IF Pulse Phase", H5::PredType::IEEE_F64LE, LimeCfg.p_pha, - (hsize_t)LimeCfg.Npulses}, - {"pcn", "Nmbr of Phasecycles", H5::PredType::NATIVE_INT, - LimeCfg.p_phacyc_N, (hsize_t)LimeCfg.Npulses}, - {"pcl", "Level of Phasecycle", H5::PredType::NATIVE_INT, - LimeCfg.p_phacyc_lev, (hsize_t)LimeCfg.Npulses}, - {"///", "Pulse Duration [Sa]", H5::PredType::NATIVE_INT, - 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}, - {"t0d", "Trigger0 Timing [Sa]", H5::PredType::NATIVE_INT, &LimeCfg.c0_tim, - 4}, - {"t1d", "Trigger1 Timing [Sa]", H5::PredType::NATIVE_INT, &LimeCfg.c1_tim, - 4}, - {"t2d", "Trigger2 Timing [Sa]", H5::PredType::NATIVE_INT, &LimeCfg.c2_tim, - 4}, - {"t3d", "Trigger3 Timing [Sa]", H5::PredType::NATIVE_INT, &LimeCfg.c3_tim, - 4}, - {"t0s", "Trigger0 Synth [Sa]", H5::PredType::NATIVE_INT, - &LimeCfg.c0_synth, 5}, - {"t1s", "Trigger1 Synth [Sa]", H5::PredType::NATIVE_INT, - &LimeCfg.c1_synth, 5}, - {"t2s", "Trigger2 Synth [Sa]", H5::PredType::NATIVE_INT, - &LimeCfg.c2_synth, 5}, - {"t3s", "Trigger3 Synth [Sa]", H5::PredType::NATIVE_INT, - &LimeCfg.c3_synth, 5}, - {"t0p", "Trigger0 Enable", H5::PredType::NATIVE_INT, LimeCfg.p_c0_en, - (hsize_t)LimeCfg.Npulses}, - {"t1p", "Trigger1 Enable", H5::PredType::NATIVE_INT, LimeCfg.p_c1_en, - (hsize_t)LimeCfg.Npulses}, - {"t2p", "Trigger2 Enable", H5::PredType::NATIVE_INT, LimeCfg.p_c2_en, - (hsize_t)LimeCfg.Npulses}, - {"t3p", "Trigger3 Enable", H5::PredType::NATIVE_INT, LimeCfg.p_c3_en, - (hsize_t)LimeCfg.Npulses}, - {"nrp", "Nmbr of Repetitions", H5::PredType::NATIVE_INT, - &LimeCfg.repetitions, 1}, - {"nav", "Nmbr of Averages", H5::PredType::NATIVE_INT, &LimeCfg.averages, - 1}, - {"trp", "Repetition Time [s]", H5::PredType::IEEE_F64LE, - &LimeCfg.reptime_secs, 1}, - {"tac", "Acquisition Time [s]", H5::PredType::IEEE_F64LE, - &LimeCfg.rectime_secs, 1}, - {"///", "Repetition Time [Sa]", H5::PredType::NATIVE_INT, - &LimeCfg.reptime_smps, 1}, - {"///", "Acquisition Time [Sa]", H5::PredType::NATIVE_INT, - &LimeCfg.rectime_smps, 1}, - {"bsz", "Buffersize", H5::PredType::NATIVE_INT, &LimeCfg.buffersize, 1}, - {"pba", "Pcyc before Avg if >0", H5::PredType::NATIVE_INT, - &LimeCfg.pcyc_bef_avg, 1}, - {"fpa", "Filename Pattern", - H5::StrType(H5::PredType::C_S1, LimeCfg.file_pattern.length() + 1), - (void *)LimeCfg.file_pattern.c_str(), 1}, - {"spt", "Save Path", - H5::StrType(H5::PredType::C_S1, LimeCfg.save_path.length() + 1), - (void *)LimeCfg.save_path.c_str(), 1}, - {"nos", "Don't save if >0", H5::PredType::NATIVE_INT, - &LimeCfg.override_save, 1}, - {"fst", "Filename Timestamp", - H5::StrType(H5::PredType::C_S1, LimeCfg.file_stamp.length() + 1), - (void *)LimeCfg.file_stamp.c_str(), 1}, - {"///", "Exp Start Timestamp", - H5::StrType(H5::PredType::C_S1, LimeCfg.stamp_start.length() + 1), - (void *)LimeCfg.stamp_start.c_str(), 1}, - {"///", "Exp End Timestamp", - H5::StrType(H5::PredType::C_S1, LimeCfg.stamp_end.length() + 1), - (void *)LimeCfg.stamp_end.c_str(), 1}}; - int no_of_attr = sizeof(HDFattr) / sizeof(Config2HDFattr_t); - - // iterate through arguments to parse eventual user input - // (exposing the actual content of the struct to python would be nicer...) - bool parse_prob = false; - int curr_attr = -1; - int curr_attr_last = -1; - int attr2read = 0; - int attr2read_last = 0; - int attr_read = 0; - for (int ii_arg = 1; ii_arg < argc; ii_arg++) { - - // get the attribute for the argument based on '-' (which also is there for - // negative numbers..) - if (argv[ii_arg][0] == '-') { - - if ((strlen(argv[ii_arg] + 1) != 3) && (attr2read == 0)) { - cout << "Invalid argument " << ii_arg << ": " << argv[ii_arg] << endl; - parse_prob = true; - continue; - } - // find matching attribute - curr_attr_last = curr_attr; - attr2read_last = attr2read; - curr_attr = -1; - for (int ii_attr = 0; ii_attr < no_of_attr; ii_attr++) { - if (strcmp(argv[ii_arg] + 1, HDFattr[ii_attr].arg.c_str()) == 0) { - curr_attr = ii_attr; - attr2read = HDFattr[ii_attr].dim; - attr_read = 0; - cout << "Found argument " << HDFattr[curr_attr].arg << ": " - << HDFattr[curr_attr].Name << endl; - } - } - // found nothing - if (curr_attr == -1 && attr2read_last == 0) { - cout << "Could not find valid attribute for argument " << ii_arg << ": " - << argv[ii_arg] << endl; - parse_prob = true; - // found something, but did not read the previous arguments - } else if (curr_attr > -1 && attr2read_last > 0) { - cout << "Missing argument: " << attr2read_last - << " value missing for argument " << HDFattr[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) { - // restore the attribute and it as number - curr_attr = curr_attr_last; - attr2read = attr2read_last; - } else - // all other cases: jump to the next argument - continue; - } - - // parse the value from the current attribute - if (curr_attr != -1 && attr2read != 0) { - - // differentiate between the different types of input based on the - // H5::DataType float values - if (HDFattr[curr_attr].dType == H5::PredType::IEEE_F32LE) { - *((float *)HDFattr[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 (HDFattr[curr_attr].dType == H5::PredType::IEEE_F64LE) { - *((double *)HDFattr[curr_attr].Value + attr_read) = - (double)atof(argv[ii_arg]); - attr2read--; - attr_read++; - // cout << "Got value " << (double) atof(argv[ii_arg]) << " from " << - // argv[ii_arg] << endl; - } - // integer values - if (HDFattr[curr_attr].dType == H5::PredType::NATIVE_INT) { - *((int *)HDFattr[curr_attr].Value + attr_read) = atoi(argv[ii_arg]); - attr2read--; - attr_read++; - // cout << "Got value " << atoi(argv[ii_arg]) << " from " << - // argv[ii_arg] << endl; - } - // strings: stored as std::string in LimeCfg and as Cstring in HDFattr.. - // --> explicitly treat strings, these are anyhow just a few for file/path - // info - if (strcmp(HDFattr[curr_attr].arg.c_str(), "spt") == 0) { - LimeCfg.save_path = argv[ii_arg]; - HDFattr[curr_attr].dType = - H5::StrType(H5::PredType::C_S1, LimeCfg.save_path.length() + 1); - HDFattr[curr_attr].Value = (void *)LimeCfg.save_path.c_str(); - attr2read--; - attr_read++; - } - if (strcmp(HDFattr[curr_attr].arg.c_str(), "fpa") == 0) { - LimeCfg.file_pattern = argv[ii_arg]; - HDFattr[curr_attr].dType = - H5::StrType(H5::PredType::C_S1, LimeCfg.file_pattern.length() + 1); - HDFattr[curr_attr].Value = (void *)LimeCfg.file_pattern.c_str(); - attr2read--; - attr_read++; - } - if (strcmp(HDFattr[curr_attr].arg.c_str(), "fst") == 0) { - LimeCfg.file_stamp = argv[ii_arg]; - HDFattr[curr_attr].dType = - H5::StrType(H5::PredType::C_S1, LimeCfg.file_stamp.length() + 1); - HDFattr[curr_attr].Value = (void *)LimeCfg.file_stamp.c_str(); - attr2read--; - attr_read++; - } - } else if (attr2read == 0) { - cout << "Problem with argument " << HDFattr[curr_attr].arg - << ": There is an input that is not clear, probably one input more " - "than required! " - << endl; - parse_prob = true; - } - } - // check if the last argument had all the values - if (attr2read > 0) { - cout << "Missing argument: " << attr2read << " value missing for argument " - << HDFattr[curr_attr].arg << endl; - parse_prob = true; - } - 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++) { - - // get the datatype - if (HDFattr[ii_attr].dType == H5::PredType::IEEE_F32LE) - datatype = "float"; - else if (HDFattr[ii_attr].dType == H5::PredType::IEEE_F64LE) - datatype = "double"; - else if (HDFattr[ii_attr].dType == H5::PredType::NATIVE_INT) - datatype = "int"; - else - datatype = "string"; - cout << "-" << HDFattr[ii_attr].arg << " " << left << setw(30) - << HDFattr[ii_attr].Name << ": " << HDFattr[ii_attr].dim << "x " - << datatype << endl; - } - return 1; - } - - // convert input in seconds/Hz to samples - 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; - } - - // check directory first - if (makePath(LimeCfg.save_path) == 0) { - cout << "Problem entering the specified path: " << LimeCfg.save_path - << endl; - return 1; - } - - // Find devices - int n; - lms_info_str_t list[8]; // should be large enough to hold all detected devices - if ((n = LMS_GetDeviceList(list)) < - 0) // NULL can be passed to only get number of devices - error(); - - cout << "Devices found: " << n << endl; // print number of devices - if (n < 1) - return -1; - - // open the first device - if (LMS_Open(&device, list[0], NULL)) - error(); - - /* - //print available antennae names - //select antenna port - lms_name_t antenna_list[10]; //large enough list for antenna names. - //Alternatively, NULL can be passed to LMS_GetAntennaList() to obtain - number of antennae if ((n = LMS_GetAntennaList(device, LMS_CH_RX, 0, - antenna_list)) < 0) error(); - - // get and print antenna index and name - if ((n = LMS_GetAntenna(device, LMS_CH_RX, 0)) < 0) error(); - cout << "Automatically selected RX LNA: " << n << ": " << - antenna_list[n] << endl; - */ - - // Get number of channels - if ((n = LMS_GetNumChannels(device, LMS_CH_RX)) < 0) - error(); - cout << "Number of RX channels: " << n << endl; - if ((n = LMS_GetNumChannels(device, LMS_CH_TX)) < 0) - error(); - cout << "Number of TX channels: " << n << endl; - - // check if the settings are already there - float_type frq_read; - if (LMS_GetLOFrequency(device, LMS_CH_RX, 0, &frq_read) != 0) - error(); - - float_type srate_read, rf_rate; - if (LMS_GetSampleRate(device, LMS_CH_RX, 0, &srate_read, &rf_rate) != 0) - error(); - - bool frqdev = fabs(frq_read - LimeCfg.frq) > 1.0; - bool sratedev = fabs(srate_read - LimeCfg.srate) > 1.0; - - // Getting the gain - int RXgain[4]; - int TXgain[3]; - GetGainRXTX(RXgain, TXgain); - - bool rgndev = RXgain[0] != LimeCfg.RX_gain; - bool tgndev = TXgain[0] != LimeCfg.TX_gain; - 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 " - "RF performance. Eventual changes in the TXgain are thus not taken " - "into account." - << endl; - } - - /* - // Similar as with the built in GetGaindB function, the GetLPFBW function is - also affecting the actual reading of the LPF. It is actually not entirely - clear why this happens, as compared to the GetGaindB function, where it is - evident that some calibration is done.. - // Accordingly, one must take care that the LPFBW is set right at the - beginning when opening the device float_type LPFBW, LPFBW2; // lowpass - bandwidth if (LMS_GetLPFBW(device, LMS_CH_RX, 0, &LPFBW) != 0) error(); bool - rlpfdev = LPFBW != LimeCfg.RX_LPF; if (LMS_GetLPFBW(device, LMS_CH_TX, 0, - &LPFBW2) != 0) error(); bool tlpfdev = LPFBW2 != LimeCfg.TX_LPF; - */ - - // initialize if the frequency is different - // if (frqdev || sratedev || tgndev || rgndev || rlpfdev || tlpfdev || true) { - if (frqdev || sratedev || tgndev || rgndev) { - - // just to re-assure why there is another setup - cout << "Re-initialization of parameters ... " << endl; - if (frqdev) - cout << "... due to LOfrequency deviation by " << frq_read - LimeCfg.frq - << " from " << LimeCfg.frq << endl; - if (sratedev) - cout << "... due to samplerate deviation by " - << srate_read - LimeCfg.srate << " from " << LimeCfg.srate << endl; - if (rgndev) - cout << "... due to RX gain deviation by " << RXgain[0] - LimeCfg.RX_gain - << " from " << LimeCfg.RX_gain << endl; - if (tgndev) - cout << "... due to TX gain deviation by " << TXgain[0] - LimeCfg.TX_gain - << " from " << LimeCfg.TX_gain << endl; - // if (rlpfdev) cout << "... due to RX LPF deviation by " << LPFBW - - // LimeCfg.RX_LPF << " from " << LimeCfg.RX_LPF << endl; if (tlpfdev) cout - // << "... due to TX LPF deviation by " << LPFBW2 - LimeCfg.TX_LPF << " from - // " << LimeCfg.TX_LPF << endl; - - // 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) { - - cout << "Initializing device first!" << endl; - - // this might fail for a freshly connected device - // --> init the device - if (LMS_Init(device) != 0) - error(); - // retry - if (LMS_SetGaindB(device, LMS_CH_TX, 0, 0) != 0) - error(); - } - if (LMS_SetNormalizedGain(device, LMS_CH_TX, 0, 0.0) != 0) - error(); - - // Set RX center frequency - if (LMS_SetLOFrequency(device, LMS_CH_RX, 0, LimeCfg.frq) != 0) - error(); - - // Set TX center frequency - if (LMS_SetLOFrequency(device, LMS_CH_TX, 0, LimeCfg.frq) != 0) - error(); - - // Read back the updated frequency for later storage - if (LMS_GetLOFrequency(device, LMS_CH_RX, 0, &frq_read) != 0) - error(); - - // Enable RX channel - // Channels are numbered starting at 0 - if (LMS_EnableChannel(device, LMS_CH_RX, 0, true) != 0) - error(); - // Enable TX channels - if (LMS_EnableChannel(device, LMS_CH_TX, 0, true) != 0) - error(); - - // apply DC offset in TxTSP - uint16_t DC_I, DC_Q, DC_EN; - DC_EN = 0; - if (LMS_WriteParam(device, LMS7_DCCORRI_TXTSP, LimeCfg.TX_IcorrDC) != 0) - error(); - if (LMS_WriteParam(device, LMS7_DCCORRQ_TXTSP, LimeCfg.TX_QcorrDC) != 0) - error(); - if (LMS_WriteParam(device, LMS7_DC_BYP_TXTSP, DC_EN) != 0) - error(); - - /* - // read back DC offset in TxTSP - if (LMS_ReadParam(device, LMS7_DCCORRI_TXTSP, &DC_I) != 0) error(); - if (LMS_ReadParam(device, LMS7_DCCORRQ_TXTSP, &DC_Q) != 0) error(); - if (LMS_ReadParam(device, LMS7_DC_BYP_TXTSP, &DC_EN) != 0) error(); - cout << "TxTSP DC corr (EN, I, Q): " << DC_EN << ", " << DC_I << ", " << - DC_Q << endl; - */ - - // print available antennae names - // select antenna port - lms_name_t antenna_list[10]; // large enough list for antenna names. - // Alternatively, NULL can be passed to LMS_GetAntennaList() to obtain - // number of antennae - if ((n = LMS_GetAntennaList(device, LMS_CH_RX, 0, antenna_list)) < 0) - error(); - - cout << "Available RX LNAs:\n"; // print available antennae names - for (int i = 0; i < n; i++) - cout << i << ": " << antenna_list[i] << endl; - // get and print antenna index and name - if ((n = LMS_GetAntenna(device, LMS_CH_RX, 0)) < 0) - error(); - cout << "Automatically selected RX LNA: " << n << ": " << antenna_list[n] - << endl; - - // manually select antenna - if (LMS_SetAntenna(device, LMS_CH_RX, 0, LMS_PATH_LNAL) != 0) - error(); - - // get and print antenna index and name - if ((n = LMS_GetAntenna(device, LMS_CH_RX, 0)) < 0) - error(); - cout << "Manually selected RX LNA: " << n << ": " << antenna_list[n] - << endl; - - // select antenna port - // Alternatively, NULL can be passed to LMS_GetAntennaList() to obtain - // number of antennae - if ((n = LMS_GetAntennaList(device, LMS_CH_TX, 0, antenna_list)) < 0) - error(); - - cout << "Available TX pathways:\n"; // print available antennae names - for (int i = 0; i < n; i++) - cout << i << ": " << antenna_list[i] << endl; - - // get and print print antenna index and name - if ((n = LMS_GetAntenna(device, LMS_CH_TX, 0)) < 0) - error(); - cout << "Automatically selected TX pathway: " << n << ": " - << antenna_list[n] << endl; - - // manually select antenna - int mychoice = LMS_PATH_TX1; - if (LimeCfg.frq > 1500e6) - mychoice = LMS_PATH_TX2; - if (LMS_SetAntenna(device, LMS_CH_TX, 0, mychoice) != 0) - error(); - - // get and print print antenna index and name - if ((n = LMS_GetAntenna(device, LMS_CH_TX, 0)) < 0) - error(); - cout << "Manually selected TX pathway: " << n << ": " << antenna_list[n] - << endl; - - // Set sample rate, w/o oversampling, so that we can remove the invsinc - // filter - if (LMS_SetSampleRate(device, LimeCfg.srate, 1) != 0) - error(); - // Invsinc, which removes that non-causal wiggle in timedomain - if (LMS_WriteParam(device, LMS7_ISINC_BYP_TXTSP, 1) != 0) - error(); - // CMIX: Disable, as it is not used - if (LMS_WriteParam(device, LMS7_CMIX_BYP_TXTSP, 1) != 0) - error(); - if (LMS_WriteParam(device, LMS7_CMIX_BYP_RXTSP, 1) != 0) - error(); - - // experiment with the GFIR filters - // if (LMS_SetGFIRLPF(device, LMS_CH_RX, 0, 1, 0.5e6) != 0) error(); // - // Works nicely. Allows, for instance, to perform narrowband observation - // together with CMIX - - // Remute the TX output here, as the init commands create a lot of garbage - if (LMS_WriteParam(device, LMS7_PD_TLOBUF_TRF, 1) != 0) - error(); - - // Set RX and TX to the gain values - if (LMS_SetGaindB(device, LMS_CH_TX, 0, LimeCfg.TX_gain) != 0) - error(); - if (LMS_SetGaindB(device, LMS_CH_RX, 0, LimeCfg.RX_gain) != 0) - error(); - - cout << "After gain setting: " << endl; - GetGainRXTX(RXgain, TXgain); - - // special for low frequency operation: LNA gain saturates rather early -> - // reduce lna gain and increase pga ( and even though we have that function - // GetGainRXTX(), we need to re-read the values here explicitly, since - // we need to operate on the actual settings of the LMS Parameter, and not - // the gain values ) - uint16_t gain_lna, gain_tia, gain_pga; - if (LMS_ReadParam(device, LMS7_G_LNA_RFE, &gain_lna) != 0) - error(); - if (LMS_ReadParam(device, LMS7_G_TIA_RFE, &gain_tia) != 0) - error(); - if (LMS_ReadParam(device, LMS7_G_PGA_RBB, &gain_pga) != 0) - error(); - // cout << "Indiv gain addr: " << gain_lna << " LNA, " << gain_tia << " TIA, - // " << gain_pga << " PGA" << endl; gain_lna > 7 means a gain beyond - // gmax-12. Convert that to gains in dB - uint16_t crit_val = 7; - uint16_t gain_corr = (gain_lna - crit_val); - if (gain_corr > 2) - gain_corr += 4; // gain steps of 1 dB for gain_lna > 9 - 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 (LMS_WriteParam(device, LMS7_G_LNA_RFE, crit_val) != 0) - error(); - if (LMS_WriteParam(device, LMS7_G_PGA_RBB, gain_pga + gain_corr) != 0) - error(); - } - GetGainRXTX(RXgain, TXgain); - - // Get allowed LPF bandwidth range - lms_range_t range; - if (LMS_GetLPFBWRange(device, LMS_CH_RX, &range) != 0) - error(); - cout << "RX LPF bandwitdh range: " << range.min / 1e6 << " - " - << range.max / 1e6 << " MHz\n\n"; - - if (LMS_GetLPFBWRange(device, LMS_CH_TX, &range) != 0) - error(); - cout << "TX LPF bandwitdh range: " << range.min / 1e6 << " - " - << range.max / 1e6 << " MHz\n\n"; - - if (LMS_SetLPFBW(device, LMS_CH_RX, 0, LimeCfg.RX_LPF) != 0) - error(); - if (LMS_SetLPFBW(device, LMS_CH_TX, 0, LimeCfg.TX_LPF) != 0) - error(); - - float_type LPFBW; // lowpass bandwidth - if (LMS_GetLPFBW(device, LMS_CH_RX, 0, &LPFBW) != 0) - error(); - cout << "RX LPFBW: " << LPFBW / 1e6 << " MHz" << endl; - if (LMS_GetLPFBW(device, LMS_CH_TX, 0, &LPFBW) != 0) - error(); - cout << "TX LPFBW: " << LPFBW / 1e6 << " MHz" << endl; - - // Set limelight interface to TRXIQ, as the std value (JESD) will not - // communicate - if (LMS_WriteParam(device, LMS7_LML1_MODE, 0) != 0) - error(); - if (LMS_WriteParam(device, LMS7_LML2_MODE, 0) != 0) - error(); - - // Unmute the TX output, as the init commands are now written - if (LMS_WriteParam(device, LMS7_PD_TLOBUF_TRF, 0) != 0) - error(); - } - - // read back values that tend to depend on the specific configuration - memcpy(LimeCfg.TX_gain_rback, TXgain, 3 * sizeof *LimeCfg.TX_gain_rback); - memcpy(LimeCfg.RX_gain_rback, RXgain, 4 * sizeof *LimeCfg.RX_gain_rback); - - const int chCount = 1; // number of RX/TX streams - - // Initialize acquisition data buffer - int buffersize = LimeCfg.buffersize; // complex samples per buffer - // 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) { - - cout << "Problem with requested buffersize of " << LimeCfg.buffersize - << ", as it is not a multiple of 4080." << endl; - LMS_Close(device); - return 1; - } - - 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) { - buffers[ii] = new int16_t[buffersize * - 2]; // buffer to hold complex values (2*samples) - } - - // Streaming Setup - lms_stream_t rx_streams[chCount]; - lms_stream_t tx_streams[chCount]; - - int N_buffers_per_fifo = - 96; // Number of buffers that can be put onto the fifo - - // 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) { - 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 - rx_streams[ii].dataFmt = lms_stream_t::LMS_FMT_I12; // 12-bit integers - - if (LMS_SetupStream(device, &rx_streams[ii]) != 0) - error(); - - 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 - tx_streams[ii].dataFmt = lms_stream_t::LMS_FMT_I16; // 16-bit float - - if (LMS_SetupStream(device, &tx_streams[ii]) != 0) - error(); - } - - // gather parameters for the TX pulse - - // first get all the phase-cycles - // which at first requires few maximum quantities.... - // .... the number of levels ... - int max_lev = 0; - for (int ii_pls = 0; ii_pls < LimeCfg.Npulses; ii_pls++) - if (LimeCfg.p_phacyc_lev[ii_pls] > max_lev) - max_lev = LimeCfg.p_phacyc_lev[ii_pls]; - - // 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++) { - found_level[ii] = false; - for (int ii_pls = 0; ii_pls < LimeCfg.Npulses; ii_pls++) - if (LimeCfg.p_phacyc_lev[ii_pls] == ii) - found_level[ii] = true; - if (!found_level[ii]) - level_problem = true; - } - 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]; - cout << endl; - cout << "A consecutive level numbering is required, but level/s "; - for (int ii = 0; ii < max_lev + 1; ii++) - if (!found_level[ii]) - cout << setw(2) << left << ii; - cout << " is/are missing!" << endl; - - LMS_Close(device); - return 1; - } - // ... the maximum number of phase cycles per level ... - int *steps_per_lev = new int[max_lev + 1]; - for (int ii = 0; ii < max_lev + 1; ii++) - steps_per_lev[ii] = 0; - - int curr_lev_steps; // to make the code more readable... - 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]; - } - - // ... which gives the total number of phase variations ... - 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++) { - if (ii > 0) - steps_incr[ii] = steps_incr[ii - 1] * steps_per_lev[ii - 1]; - num_phavar *= steps_per_lev[ii]; - } - - // ... which allows to construct the entire phase table ... - double pha_tab[num_phavar][LimeCfg.Npulses]; - - double pha_incr, curr_pha; - int step_incr = 1; - - 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]; - else - pha_incr = 1.0; - - curr_pha = 0; - - // get the step increment - 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++) { - // eventually increment the phase - if ((ii_pha > 0) && (ii_pha % step_incr == 0)) - curr_pha += pha_incr; - - pha_tab[ii_pha][ii_pls] = fmod(curr_pha, 1.0); - } - } - - // 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++) { - cout << setw(10) << left << pha_tab[ii_pha][ii_pls]; - } - cout << endl; - } - - // get the number of buffers that are required in order to fit the entire - // experiment - long exc_len = 0; - int exc_buffers; - int pulsedur, pulseoffs; - pulseoffs = 0; - 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 - - if (pulseoffs + pulsedur > exc_len) - 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; - - // TX buffers - // int16_t tx_buffer[num_phavar][exc_buffers][2*buffersize]; // buffer to - // hold complex values (2* samples), including phase cycles - // 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) { - tx_buffer[ii][jj] = new int16_t[2 * buffersize]; - } - } - int16_t tx_buffer_1st[2 * buffersize]; // buffer to hold complex values - float fsmpI, fsmpQ; - 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 ll = 0; ll < num_phavar; ll++) - tx_buffer[ll][jj][ii] = 0; - } - } - - // pulse parameters - double pulsefrq, pulseamp, pulsepha; - double w; - int buffoffs; - - pulseoffs = 0; - 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 - pulsefrq = LimeCfg.p_frq_smp[ii_pls]; // frequency of pulse in samples - pulseamp = LimeCfg.p_amp[ii_pls]; // relative amplitude of pulses - pulsepha = LimeCfg.p_pha[ii_pls]; // phase of pulse - - for (int ll = 0; ll < num_phavar; - ll++) { // generate TX Pulse for different phases - buffoffs = 0; - for (int jj = 0; jj < exc_buffers; - jj++) { // distribute 'long experiment' amongst buffers - for (int ii = 0; ii < buffersize; - ii++) { // generate TX Pulse point by point - if ((ii + buffoffs >= pulseoffs) & - (ii + buffoffs < pulsedur + pulseoffs)) { - - 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]); - - // 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) { - 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 { - // fsmpI = 0.0; - // fsmpQ = 0.0; - } - } - buffoffs += buffersize; // jump to next buffer - } - } - - // 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) { - 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 - // get the proper configuration of the trigger channel - switch (ii_c) { - case 0: - curr_marker = LimeCfg.c0_tim; - curr_marker_en = LimeCfg.p_c0_en; - break; - case 1: - curr_marker = LimeCfg.c1_tim; - curr_marker_en = LimeCfg.p_c1_en; - break; - case 2: - curr_marker = LimeCfg.c2_tim; - curr_marker_en = LimeCfg.p_c2_en; - break; - case 3: - curr_marker = LimeCfg.c3_tim; - curr_marker_en = LimeCfg.p_c3_en; - break; - default: - break; - } - - // check if the channel is activated - if (curr_marker[0] == 0 || curr_marker_en[ii_pls] == 0) - continue; - - // set trigger with bitset operation - for (int ll = 0; ll < num_phavar; ll++) { - buffoffs = 0; - for (int jj = 0; jj < exc_buffers; - jj++) { // distribute 'long experiment' amongst buffers - for (int ii = 0; ii < 2 * buffersize; - 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])) { - tx_buffer[ll][jj][ii] |= 1 << ii_c; - } - } - buffoffs += 2 * buffersize; // jump to next buffer - } - } - } - } - } - - // Synth Marker for CW sync stuff - // Use TTL channel as synth: [enabled? , half-period, strt, PSK shift, PSK - // adv] - int synthstart = 0; - int wrapped_phase = 0; - 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 - // get the proper configuration of the trigger channel - switch (ii_c) { - case 0: - curr_synth = LimeCfg.c0_synth; - break; - case 1: - curr_synth = LimeCfg.c1_synth; - break; - case 2: - curr_synth = LimeCfg.c2_synth; - break; - case 3: - curr_synth = LimeCfg.c3_synth; - break; - default: - break; - } - - cout << curr_synth[1] << endl; - // check if the channel is activated - if (curr_synth[0] == 0) - continue; - - // set trigger with bitset operation - synthstart = curr_synth[2]; - 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 ((ll % curr_synth[4]) == 0) - synthstart += curr_synth[3]; - } - - for (int jj = 0; jj < exc_buffers; - jj++) { // distribute 'long experiment' amongst buffers - for (int ii = 0; ii < 2 * buffersize; - 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]) { - tx_buffer[ll][jj][ii] |= 1 << ii_c; - } - } - buffoffs += 2 * buffersize; // jump to next buffer - } - } - } - } - - // generate empty TX Pulse at beginning - for (int ii = 0; ii < buffersize; ii++) { - - tx_buffer_1st[2 * ii] = 0.0; - tx_buffer_1st[2 * ii + 1] = 0.0; - } - - // calculate the repetition and recording time in samples as mutliple of - // buffer size in this way, we do not need to do any exhaustive sample - // alignment strategies - long rep_offset, rec_len; - rep_offset = ceil(LimeCfg.reptime_secs * LimeCfg.srate / (double)buffersize) * - buffersize; - rec_len = ceil(LimeCfg.rectime_secs * LimeCfg.srate / (double)buffersize) * - buffersize; - - // write back to LimeCfg so that these are stored in the file - LimeCfg.reptime_smps = rep_offset; - LimeCfg.rectime_smps = rec_len; - - if (rec_len > rep_offset) { - cout << "Recording length 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; - - // 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 - // buffer for some reason, there are segfaults here when acqbuf_size becomes - // something on the order of 100 - int acqbuf_size = LimeCfg.repetitions * num_phavar; - // int acqbuf[acqbuf_size][2*rec_len]; // causes segfaults with large - // arrays... - - // 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) { - 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++) { - acqbuf[ii][jj] = 0; - } - } - - // Streaming - 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 - tx_metadata.waitForTimestamp = true; // Enable synchronization to HW timestamp - - lms_stream_status_t status; // To check the FIFO - - // counters to keep track of the transmission FIFO - int TXFIFO_slots = N_buffers_per_fifo; - int ii_TXavg = 0; - int ii_TXpcyc = 0; - int ii_TXoffset = 0; - int ii_TXrep = 0; - int ii_sent = 0; - double init_delay_s = 10e-3; // delay in seconds until TX packets are - // forwarded from FPGA to the FPRF - long next_TXtimestamp = - ceil((init_delay_s * LimeCfg.srate) / (double)buffersize) * buffersize; - long last_TXtimestamp = - 0; // this one stores the last timestamp of the beginning of a repetition - - // Timestamps to schedule the acquisition - long next_RXtimestamp = 0; - long last_RXtimestamp = 0; - - // Acquisition loop - auto t1 = chrono::high_resolution_clock::now(); - auto t2 = t1; - - int samplesRead; - int samplesReadSum = 0; - int rcvattempts = 0; - - 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 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 - - bool acquiring = false; // RX stream to acqbuffer? - bool acquire = true; // disable one single acquisition in acq loop - bool delayedAcqbufFwd = false; // to delay the forwarding of the acqbuffer - - int ndebug = 100; - - // Start streaming - 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++) { - - // 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) { - LMS_SendStream(&tx_streams[0], tx_buffer_1st, buffersize, &tx_metadata, - 1000); // so we put zeros - TXFIFO_slots--; - - // advance TX timestamp for the next packet - next_TXtimestamp += rep_offset; - - next_RXtimestamp = next_TXtimestamp; // ... and do not wait for it - last_TXtimestamp = next_TXtimestamp; - continue; // proceed the for loop with the first actual TX packet - } - - // Put data to FIFO - LMS_SendStream(&tx_streams[0], tx_buffer[ii_TXpcyc][ii_TXoffset], - buffersize, &tx_metadata, 1000); - - // Update TX FIFO counters - TXFIFO_slots--; - ii_sent++; - - // advance the tx_buffer counter - ii_TXoffset++; - next_TXtimestamp += buffersize; - if (ii_TXoffset == exc_buffers) { - ii_TXoffset = 0; - next_TXtimestamp = last_TXtimestamp + rep_offset; - last_TXtimestamp = next_TXtimestamp; - - if (LimeCfg.pcyc_bef_avg > 0) { - ii_TXpcyc++; - if (ii_TXpcyc == num_phavar) { - ii_TXpcyc = 0; - ii_TXavg++; - 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) - break; - } - } - } else { - ii_TXavg++; - if (ii_TXavg == LimeCfg.averages) { - ii_TXavg = 0; - ii_TXpcyc++; - 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) - break; - } - } - } - } else { - // if there is still data to be put on the buffer - } - } - - /* - // 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; - */ - - // Main acquisition loop - while (ii_acq < LimeCfg.repetitions * LimeCfg.averages * num_phavar) { - - // Receive samples - if (acquire) { - samplesRead = LMS_RecvStream(&rx_streams[0], buffers[0], buffersize, - &rx_metadata, 1000); - rcvattempts++; - samplesReadSum += samplesRead; - } - - if (ndebug < 10) { - cout << rx_metadata.timestamp << ", " << samplesReadSum << endl; - ndebug++; - LMS_GetStreamStatus(rx_streams, &status); // Obtain RX stream stats - cout << "Rx stream info: " << status.overrun << ", " << status.underrun - << status.droppedPackets << ", " << status.overrun << ", actually " - << status.fifoFilledCount << endl; - } - - // check if the scheduled timestamp is coming here - // if (rx_metadata.timestamp >= next_RXtimestamp) { - if ((rx_metadata.timestamp >= next_RXtimestamp - samplesRead + 1) && - acquire) { - - // 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) { - acqbuf_pos = acqbuf[ii_rep * num_phavar + ii_pcyc]; - samples2Acquire = rec_len; - } 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++; - } - } - } - - // 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; - } - - // advance to the forthcoming RX timestamp and keep the current one - next_RXtimestamp += rep_offset; - last_RXtimestamp = rx_metadata.timestamp; - } - acquire = true; - - // copy RX data into acquisition buffer - if (acquiring) { - - // standard case: copy everything, without offset - bufferOffset = 0; - validSamples = buffersize; - - // first packet: consider eventual timestamp offset - if (samples2Acquire == rec_len) { - bufferOffset = timestampOffset; - validSamples = buffersize - bufferOffset; - // last packet with timestamp offset: just get the tail without offset - } else if (samples2Acquire < buffersize) { - validSamples = samples2Acquire; - } - - for (int ii_acqbuf = 0; ii_acqbuf < 2 * (validSamples); ii_acqbuf++) - acqbuf_pos[ii_acqbuf] += (int)buffers[0][ii_acqbuf + 2 * bufferOffset]; - samples2Acquire -= validSamples; - - // advance position in acquisition buffer - acqbuf_pos += 2 * validSamples; - - if (samples2Acquire == 0) { - ii_acq++; - - // check for continuous RX with timestamp offset, where we would - // actually still have valid samples to copy in the buffer - if (delayedAcqbufFwd) { - // put pointer to right place - acqbuf_pos = delayedacqbuf_pos; - samples2Acquire = rec_len; - - delayedAcqbufFwd = false; - // important: rerun this entire block without getting a new data or - // forwarding the timestamp packet. We still need to copy the part - // beyond the validsamples to the next acqbuf position - acquire = false; - } else { - // standard case: signal that the acquisition is finished and wait for - // the next scheduled timestamp - acquiring = false; - } - } - } - - // Check for the TX buffer and keep it filled - if (ii_TXrep < LimeCfg.repetitions) { - LMS_GetStreamStatus(tx_streams, &status); // Obtain TX stream stats - TXFIFO_slots = (status.fifoSize - status.fifoFilledCount) / buffersize; - } - - /* - // debug - if (TXFIFO_slots > 0) cout << TXFIFO_slots << " free fifo slots to fill" << - endl; - */ - - // re-fill the TX fifo - while (TXFIFO_slots > 0) { - - // save the TX timestamp to the current packet - tx_metadata.timestamp = next_TXtimestamp; - - // Put data to FIFO - LMS_SendStream(&tx_streams[0], tx_buffer[ii_TXpcyc][ii_TXoffset], - buffersize, &tx_metadata, 1000); - - // Update TX counters - TXFIFO_slots--; - ii_sent++; - - // advance the tx_buffer counter - ii_TXoffset++; - next_TXtimestamp += buffersize; - if (ii_TXoffset == exc_buffers) { - ii_TXoffset = 0; - next_TXtimestamp = last_TXtimestamp + rep_offset; - last_TXtimestamp = next_TXtimestamp; - - if (LimeCfg.pcyc_bef_avg > 0) { - ii_TXpcyc++; - if (ii_TXpcyc == num_phavar) { - ii_TXpcyc = 0; - ii_TXavg++; - if (ii_TXavg == LimeCfg.averages) { - ii_TXavg = 0; - ii_TXrep++; - // in case the experiment is finished - if (ii_TXrep == LimeCfg.repetitions) - ; - TXFIFO_slots = 0; - } - } - } else { - ii_TXavg++; - if (ii_TXavg == LimeCfg.averages) { - ii_TXavg = 0; - ii_TXpcyc++; - if (ii_TXpcyc == num_phavar) { - ii_TXpcyc = 0; - ii_TXrep++; - // in case the experiment is finished - if (ii_TXrep == LimeCfg.repetitions) - TXFIFO_slots = 0; - } - } - } - } - } - } - - // Stop streaming - 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) { - LMS_DestroyStream( - device, - &rx_streams[i]); // stream is deallocated and can no longer be used - LMS_DestroyStream(device, &tx_streams[i]); - delete[] buffers[i]; - } - - //------------------------------------------------------------------------------------- - // SAVE TO HDF5 - //------------------------------------------------------------------------------------- - - if (LimeCfg.override_save == 0) { - - // Open HDF5 file - string filename; - // check for save_path delimiter - if (LimeCfg.save_path.back() == '/') - filename = LimeCfg.save_path + LimeCfg.file_stamp + "_" + - LimeCfg.file_pattern + ".h5"; - else - filename = LimeCfg.save_path + '/' + LimeCfg.file_stamp + "_" + - LimeCfg.file_pattern + ".h5"; - cout << filename << endl; - H5::H5File *h5f; - - // create or open - if (file_exists(filename)) - h5f = new H5::H5File(filename, H5F_ACC_RDWR); - else - h5f = new H5::H5File(filename, H5F_ACC_EXCL); - - // Check for the number of datasets already in there (only >0 if an external - // file_stamp is provided or if the program is called more than once within - // a second..) - hsize_t num_obj = 0; - H5Gget_num_objs(h5f->getId(), - &num_obj); // if success, num_obj will be assigned the - // number of objects in the group - - // write dataset to HDF5 file - // 1. specify datatype and dimensions and dataset name - H5::DataType saveDataType(H5::PredType::NATIVE_INT); - hsize_t saveDataDim[] = {(hsize_t)acqbuf_size, (hsize_t)2 * rec_len}; - string DataName = "Acqbuf_"; - std::ostringstream oss; - oss << setfill('0') << setw(2) << num_obj; - DataName += oss.str(); - - H5std_string saveDataName(DataName.c_str()); - - // 2. allocate dataspace and init - // set parameters to have a chunked file, so that each acquired trace is one - // chunk - H5::DSetCreatPropList cparms; - hsize_t chunk_dims[2] = {1, saveDataDim[1]}; - cparms.setChunk(2, chunk_dims); - - int fill_val = 0; - cparms.setFillValue(H5::PredType::NATIVE_INT, &fill_val); - - H5::DataSpace mspace1(2, saveDataDim); - H5::DataSet dataset = - h5f->createDataSet(saveDataName, saveDataType, mspace1, cparms); - - // write with standard procedure - // dataset.write( acqbuf, saveDataType); // requires contiguous memory of - // the entire acqbuf, which does not work for large buffers - // write row-wise - H5::DataSpace fspace_row = dataset.getSpace(); - hsize_t offset[2] = {0, 0}; - 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++) { - 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 - } - - // get the timestamp at the end of the experiment ... - now = std::chrono::system_clock::now(); - itt = std::chrono::system_clock::to_time_t(now); - stringstream.str(""); - stringstream.clear(); - 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) { - LimeCfg.stamp_end = stringstream.str(); - HDFattr[ii_attr].dType = - H5::StrType(H5::PredType::C_S1, LimeCfg.stamp_end.length() + 1); - HDFattr[ii_attr].Value = (void *)LimeCfg.stamp_end.c_str(); - } - } - - // write the attributes - for (int ii = 0; ii < no_of_attr; ii++) { - - H5::DataSpace *tmpSpace = new H5::DataSpace(); - // special case: arrays - if (HDFattr[ii].dim > 1) { - delete tmpSpace; - H5::DataSpace *tmpSpace = new H5::DataSpace(1, &HDFattr[ii].dim); - } - H5std_string concat("-" + HDFattr[ii].arg + " " + HDFattr[ii].Name); - // H5::Attribute attribute = h5f->createAttribute(concat, - // HDFattr[ii].dType, *tmpSpace); // write the attribute to the file - H5::Attribute attribute = dataset.createAttribute( - concat, HDFattr[ii].dType, - *tmpSpace); // write the attribute to the dataset - attribute.write(HDFattr[ii].dType, HDFattr[ii].Value); - delete tmpSpace; - } - - // special attribute for N pulses: the phase table, which is certainly of - // use in evaluation - hsize_t phatab_len = LimeCfg.Npulses * num_phavar; - H5::DataSpace *tmpSpace = new H5::DataSpace(1, &phatab_len); - H5std_string concat("-/// Phase Table"); - H5::Attribute attribute = dataset.createAttribute( - concat, H5::PredType::IEEE_F64LE, - *tmpSpace); // write the attribute to the dataset - attribute.write(H5::PredType::IEEE_F64LE, &pha_tab); - delete tmpSpace; - - // close file - h5f->close(); - delete h5f; - - cout << "Written to HDFfile as " << saveDataDim[0] << " by " - << saveDataDim[1] << " array" << endl; - } - - // Close device - LMS_Close(device); - - return 0; -} diff --git a/src/limedriver.cpp b/src/limedriver.cpp index 1ebe79f..f5d54c0 100644 --- a/src/limedriver.cpp +++ b/src/limedriver.cpp @@ -9,8 +9,7 @@ LimeSuite HDF5 library compilation: -g++ limedriver.cpp -std=c++11 $(pkg-config --cflags --libs LimeSuite) -o limedriver \ -$(h5c++ -show) +$(h5c++ -show) limedriver.cpp -std=c++11 $(pkg-config --cflags --libs LimeSuite) -o limedriver */