diff --git a/CC/Sounder/BaseRadioSet-calibrate.cc b/CC/Sounder/BaseRadioSet-calibrate.cc index 6df12f00..d84543dd 100644 --- a/CC/Sounder/BaseRadioSet-calibrate.cc +++ b/CC/Sounder/BaseRadioSet-calibrate.cc @@ -271,9 +271,9 @@ static void dciqMinimize(SoapySDR::Device* targetDev, SoapySDR::Device* refDev, //try I or Q arm based on iteration const auto dcCorr = ((iter % 2) == 0) ? std::complex( - bestDcCorr.real(), double(i) / fixedScale) + bestDcCorr.real(), double(i) / fixedScale) : std::complex( - double(i) / fixedScale, bestDcCorr.imag()); + double(i) / fixedScale, bestDcCorr.imag()); targetDev->setDCOffset(direction, channel, dcCorr); //measure the efficacy @@ -534,7 +534,7 @@ void BaseRadioSet::collectCSI(bool& adjust) // Prepend/Append vectors with prefix/postfix number of null samples std::vector> prefix_vec(_cfg->prefix(), 0); std::vector> postfix_vec( - _cfg->samps_per_symbol() - _cfg->prefix() - seqLen, 0); + _cfg->samps_per_slot() - _cfg->prefix() - seqLen, 0); pilot_cint16.insert( pilot_cint16.begin(), prefix_vec.begin(), prefix_vec.end()); pilot_cint16.insert( @@ -559,18 +559,18 @@ void BaseRadioSet::collectCSI(bool& adjust) buff.resize(R * R); for (int i = 0; i < R; i++) { for (int j = 0; j < R; j++) { - buff[i * R + j].resize(_cfg->samps_per_symbol()); + buff[i * R + j].resize(_cfg->samps_per_slot()); } } - std::vector> dummyBuff0(_cfg->samps_per_symbol()); - std::vector> dummyBuff1(_cfg->samps_per_symbol()); + std::vector> dummyBuff0(_cfg->samps_per_slot()); + std::vector> dummyBuff1(_cfg->samps_per_slot()); std::vector dummybuffs(2); dummybuffs[0] = dummyBuff0.data(); dummybuffs[1] = dummyBuff1.data(); for (int i = 0; i < R; i++) - bsRadios[0][i]->drain_buffers(dummybuffs, _cfg->samps_per_symbol()); + bsRadios[0][i]->drain_buffers(dummybuffs, _cfg->samps_per_slot()); for (int i = 0; i < R; i++) { Radio* bsRadio = bsRadios[0][i]; @@ -588,12 +588,12 @@ void BaseRadioSet::collectCSI(bool& adjust) for (int j = 0; j < R; j++) { if (j == i) { int ret = bsRadios[0][j]->xmit( - txbuff.data(), _cfg->samps_per_symbol(), 3, txTime); + txbuff.data(), _cfg->samps_per_slot(), 3, txTime); if (ret < 0) std::cout << "bad write" << std::endl; } else { int ret = bsRadios[0][j]->activateRecv( - rxTime, _cfg->samps_per_symbol(), 3); + rxTime, _cfg->samps_per_slot(), 3); if (ret < 0) std::cout << "bad activate at node " << j << std::endl; } @@ -610,7 +610,7 @@ void BaseRadioSet::collectCSI(bool& adjust) //rxbuff[1] = ant == 2 ? buff[(i*M+j)*ant+1].data() : dummyBuff.data(); rxbuff[1] = dummyBuff0.data(); int ret = bsRadios[0][j]->recv( - rxbuff.data(), _cfg->samps_per_symbol(), rxTime); + rxbuff.data(), _cfg->samps_per_slot(), rxTime); if (ret < 0) std::cout << "bad read at node " << j << std::endl; } @@ -631,12 +631,12 @@ void BaseRadioSet::collectCSI(bool& adjust) good_csi = false; #if DEBUG_PLOT - std::vector rx_I(_cfg->samps_per_symbol()); + std::vector rx_I(_cfg->samps_per_slot()); std::transform(rx.begin(), rx.end(), rx_I.begin(), [](std::complex cf) { return cf.real(); }); plt::figure_size(1200, 780); plt::plot(rx_I); - plt::xlim(0, _cfg->samps_per_symbol()); + plt::xlim(0, _cfg->samps_per_slot()); plt::ylim(-1, 1); plt::title("Sample figure"); plt::legend(); @@ -671,6 +671,6 @@ void BaseRadioSet::collectCSI(bool& adjust) bsRadio->deactivateRecv(); bsRadio->deactivateXmit(); dev->setGain(SOAPY_SDR_TX, ch, "PAD", _cfg->tx_gain().at(ch)); //[0,30] - bsRadio->drain_buffers(dummybuffs, _cfg->samps_per_symbol()); + bsRadio->drain_buffers(dummybuffs, _cfg->samps_per_slot()); } } diff --git a/CC/Sounder/BaseRadioSet.cc b/CC/Sounder/BaseRadioSet.cc index cb0eed78..351cb16d 100644 --- a/CC/Sounder/BaseRadioSet.cc +++ b/CC/Sounder/BaseRadioSet.cc @@ -233,7 +233,7 @@ BaseRadioSet::BaseRadioSet(Config* cfg) tddConf["tdd_enabled"] = true; tddConf["frame_mode"] = "free_running"; tddConf["max_frame"] = _cfg->max_frame(); - tddConf["symbol_size"] = _cfg->samps_per_symbol(); + tddConf["symbol_size"] = _cfg->samps_per_slot(); // write TDD schedule and beacons to FPFA buffers only for Iris for (size_t c = 0; c < _cfg->num_cells(); c++) { @@ -514,7 +514,7 @@ void BaseRadioSet::radioTx(const void* const* buffs) for (size_t c = 0; c < _cfg->num_cells(); c++) { for (size_t i = 0; i < bsRadios.at(c).size(); i++) { bsRadios.at(c).at(i)->xmit( - buffs, _cfg->samps_per_symbol(), 0, frameTime); + buffs, _cfg->samps_per_slot(), 0, frameTime); } } } @@ -526,12 +526,12 @@ int BaseRadioSet::radioTx(size_t radio_id, size_t cell_id, // for UHD device xmit from host using frameTimeNs if (!kUseUHD) { w = bsRadios.at(cell_id).at(radio_id)->xmit( - buffs, _cfg->samps_per_symbol(), flags, frameTime); + buffs, _cfg->samps_per_slot(), flags, frameTime); } else { long long frameTimeNs = SoapySDR::ticksToTimeNs(frameTime, _cfg->rate()); w = bsRadios.at(cell_id).at(radio_id)->xmit( - buffs, _cfg->samps_per_symbol(), flags, frameTimeNs); + buffs, _cfg->samps_per_slot(), flags, frameTimeNs); } #if DEBUG_RADIO size_t chanMask; @@ -551,8 +551,7 @@ void BaseRadioSet::radioRx(void* const* buffs) for (size_t c = 0; c < _cfg->num_cells(); c++) { for (size_t i = 0; i < bsRadios.at(c).size(); i++) { void* const* buff = buffs + (i * 2); - bsRadios.at(c).at(i)->recv( - buff, _cfg->samps_per_symbol(), frameTime); + bsRadios.at(c).at(i)->recv(buff, _cfg->samps_per_slot(), frameTime); } } } @@ -561,7 +560,7 @@ int BaseRadioSet::radioRx( size_t radio_id, size_t cell_id, void* const* buffs, long long& frameTime) { return this->radioRx( - radio_id, cell_id, buffs, _cfg->samps_per_symbol(), frameTime); + radio_id, cell_id, buffs, _cfg->samps_per_slot(), frameTime); } int BaseRadioSet::radioRx(size_t radio_id, size_t cell_id, void* const* buffs, diff --git a/CC/Sounder/CMakeLists.txt b/CC/Sounder/CMakeLists.txt index 1b4a9cb0..532b396e 100644 --- a/CC/Sounder/CMakeLists.txt +++ b/CC/Sounder/CMakeLists.txt @@ -1,10 +1,14 @@ cmake_minimum_required(VERSION 3.10) -project(Sounder) +#Allow project version +cmake_policy(SET CMP0048 NEW) +project(Sounder VERSION 1.0.1) if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0) set(GCC_COVERAGE_COMPILE_FLAGS "-faligned-new") endif() +configure_file(${CMAKE_SOURCE_DIR}/include/version_config.h.in ${CMAKE_SOURCE_DIR}/include/version_config.h) + option(FORCE_BUILD_PATH "Hardcode the build directory path to be 'build/'" ON) if(FORCE_BUILD_PATH) message(STATUS "Setting the build directory to build folder") diff --git a/CC/Sounder/ClientRadioSet.cc b/CC/Sounder/ClientRadioSet.cc index 21ccb878..80cb04fb 100644 --- a/CC/Sounder/ClientRadioSet.cc +++ b/CC/Sounder/ClientRadioSet.cc @@ -156,8 +156,8 @@ ClientRadioSet::ClientRadioSet(Config* cfg) } else { //beaconSize + 82 (BS FE delay) + 68 (path delay) + 17 (correlator delay) + 82 (Client FE Delay) int clTrigOffset = _cfg->beacon_size() + _cfg->tx_advance(); - int sf_start = clTrigOffset / _cfg->samps_per_symbol(); - int sp_start = clTrigOffset % _cfg->samps_per_symbol(); + int sf_start = clTrigOffset / _cfg->samps_per_slot(); + int sp_start = clTrigOffset % _cfg->samps_per_slot(); for (size_t i = 0; i < radios.size(); i++) { auto dev = radios.at(i)->dev; @@ -186,8 +186,8 @@ ClientRadioSet::ClientRadioSet(Config* cfg) tddConf["tdd_enabled"] = true; tddConf["frame_mode"] = _cfg->frame_mode(); int max_frame_ = (int)(2.0 - / ((_cfg->samps_per_symbol() * _cfg->symbols_per_frame()) - / _cfg->rate())); + / ((_cfg->samps_per_slot() * _cfg->slot_per_frame()) + / _cfg->rate())); tddConf["max_frame"] = _cfg->frame_mode() == "free_running" ? 0 : max_frame_; //std::cout << "max_frames for client " << i << " is " << max_frame_ << std::endl; @@ -195,7 +195,7 @@ ClientRadioSet::ClientRadioSet(Config* cfg) tddConf["dual_pilot"] = true; tddConf["frames"] = json::array(); tddConf["frames"].push_back(tddSched); - tddConf["symbol_size"] = _cfg->samps_per_symbol(); + tddConf["symbol_size"] = _cfg->samps_per_slot(); std::string tddConfStr = tddConf.dump(); dev->writeSetting("TDD_CONFIG", tddConfStr); diff --git a/CC/Sounder/config.cc b/CC/Sounder/config.cc index efda8943..6fe7fe6d 100644 --- a/CC/Sounder/config.cc +++ b/CC/Sounder/config.cc @@ -9,6 +9,7 @@ #include "include/config.h" #include "include/comms-lib.h" +#include "include/constants.h" #include "include/logger.h" #include "include/macros.h" #include "include/utils.h" @@ -20,198 +21,227 @@ static size_t kMaxSupportedFFTSize = 2048; static size_t kMinSupportedFFTSize = 64; static size_t kMaxSupportedCPSize = 128; -Config::Config(const std::string& jsonfile, const std::string& directory) +Config::Config(const std::string& jsonfile, const std::string& directory, + const bool bs_only, const bool client_only) { - std::string conf; - Utils::loadTDDConfig(jsonfile, conf); + std::string conf_str; + Utils::loadTDDConfig(jsonfile, conf_str); // Enable comments in json file - const auto jConf = json::parse(conf, nullptr, true, true); + const auto jConf = json::parse(conf_str, nullptr, true, true); std::stringstream ss; json tddConf; ss << jConf.value("BaseStations", tddConf); tddConf = json::parse(ss); - bs_present_ = (tddConf.empty() == false); - if (bs_present_ == true) { - ss.str(std::string()); - ss.clear(); - ss << tddConf << std::endl << std::endl; - MLPD_INFO("Base Stations present: %s", ss.str().c_str()); - } ss.str(std::string()); ss.clear(); json tddConfCl; ss << jConf.value("Clients", tddConfCl); tddConfCl = json::parse(ss); - client_present_ = (tddConfCl.empty() == false); - if (client_present_ == true) { + ss.str(std::string()); + ss.clear(); + + if (bs_only && client_only == true) { + MLPD_ERROR("Client-Only and BS-Only can't both be enabled!\n"); + exit(1); + } + + if (tddConf.empty() == true) { + MLPD_ERROR("Both \"BaseStations\" and \"Clients\" must be present in " + "JSON file\n"); + exit(1); + } else if (tddConfCl.empty() == true) { + reciprocal_calib_ = tddConf.value("internal_measurement", false); + if (!reciprocal_calib_) { + MLPD_ERROR( + "Both \"BaseStations\" and \"Clients\" must be present in " + "JSON file\n"); + exit(1); + } + } else { + ss << " BaseStations: " << tddConf << "\n\n"; + ss << " Clients: " << tddConfCl << "\n" << std::endl; + MLPD_INFO("\nInput config:\n\n%s", ss.str().c_str()); ss.str(std::string()); ss.clear(); - ss << tddConfCl << std::endl << std::endl; - MLPD_INFO("Clients present: %s", ss.str().c_str()); } - ss.str(std::string()); - ss.clear(); + bs_present_ = !client_only; + client_present_ = !bs_only && !reciprocal_calib_; static const int kMaxTxGainBS = 81; // common (BaseStation config overrides these) - if (bs_present_ == true) { - freq_ = tddConf.value("frequency", 2.5e9); - rate_ = tddConf.value("rate", 5e6); - nco_ = tddConf.value("nco_frequency", 0.75 * rate_); - bw_filter_ = rate_ + 2 * nco_; - radio_rf_freq_ = freq_ - nco_; - symbol_per_subframe_ = tddConf.value("ofdm_symbol_per_subframe", 1); - fft_size_ = tddConf.value("fft_size", 0); - cp_size_ = tddConf.value("cp_size", 0); - prefix_ = tddConf.value("prefix", 0); - postfix_ = tddConf.value("postfix", 0); - ofdm_symbol_size_ = fft_size_ + cp_size_; - subframe_size_ = symbol_per_subframe_ * ofdm_symbol_size_; - samps_per_symbol_ = subframe_size_ + prefix_ + postfix_; - symbol_data_subcarrier_num_ - = tddConf.value("ofdm_data_subcarrier_num", fft_size_); - tx_scale_ = tddConf.value("tx_scale", 0.5); - beacon_seq_ = tddConf.value("beacon_seq", "gold_ifft"); - pilot_seq_ = tddConf.value("pilot_seq", "lts"); - data_mod_ = tddConf.value("modulation", "QPSK"); - - // BS - if (kUseUHD == false) { - hub_file_ = tddConf.value("hub_id", "hub_serials.txt"); - } - auto sdr_id_files = tddConf.value("sdr_id", json::array()); - num_cells_ = sdr_id_files.size(); - bs_sdr_file_.assign(sdr_id_files.begin(), sdr_id_files.end()); - bs_channel_ = tddConf.value("channel", "A"); - if ((bs_channel_ != "A") && (bs_channel_ != "B") - && (bs_channel_ != "AB")) { - throw std::invalid_argument( - "error channel config: not any of A/B/AB!\n"); - } - single_gain_ = tddConf.value("single_gain", true); - - if (tddConf.value("txgainA", 20) > kMaxTxGainBS) { - std::string msg - = "ERROR: BaseStation ChanA - Maximum TX gain value is "; - msg += std::to_string(kMaxTxGainBS); - throw std::invalid_argument(msg); - } else { - tx_gain_.push_back(tddConf.value("txgainA", 20)); - } + freq_ = tddConf.value("frequency", 2.5e9); + rate_ = tddConf.value("rate", 5e6); + nco_ = tddConf.value("nco_frequency", 0.75 * rate_); + symbol_per_slot_ = tddConf.value("ofdm_symbol_per_slot", 1); + fft_size_ = tddConf.value("fft_size", 0); + cp_size_ = tddConf.value("cp_size", 0); + prefix_ = tddConf.value("prefix", 0); + postfix_ = tddConf.value("postfix", 0); + symbol_data_subcarrier_num_ + = tddConf.value("ofdm_data_subcarrier_num", fft_size_); + tx_scale_ = tddConf.value("tx_scale", 0.5); + pilot_seq_ = tddConf.value("pilot_seq", "lts"); + data_mod_ = tddConf.value("modulation", "QPSK"); + + // BS + if (kUseUHD == false) { + hub_file_ = tddConf.value("hub_id", "hub_serials.txt"); + } + auto sdr_id_files = tddConf.value("sdr_id", json::array()); + num_cells_ = sdr_id_files.size(); + bs_sdr_file_.assign(sdr_id_files.begin(), sdr_id_files.end()); + bs_channel_ = tddConf.value("channel", "A"); + if ((bs_channel_ != "A") && (bs_channel_ != "B") && (bs_channel_ != "AB")) { + throw std::invalid_argument( + "error channel config: not any of A/B/AB!\n"); + } + single_gain_ = tddConf.value("single_gain", true); - if (tddConf.value("txgainB", 20) > kMaxTxGainBS) { - std::string msg - = "ERROR: BaseStation ChanB - Maximum TX gain value is "; - msg += std::to_string(kMaxTxGainBS); - throw std::invalid_argument(msg); - } else { - tx_gain_.push_back(tddConf.value("txgainB", 20)); - } + if (tddConf.value("txgainA", 20) > kMaxTxGainBS) { + std::string msg + = "ERROR: BaseStation ChanA - Maximum TX gain value is "; + msg += std::to_string(kMaxTxGainBS); + throw std::invalid_argument(msg); + } else { + tx_gain_.push_back(tddConf.value("txgainA", 20)); + } - rx_gain_.push_back(tddConf.value("rxgainA", 20)); - rx_gain_.push_back(tddConf.value("rxgainB", 20)); - cal_tx_gain_.push_back(tddConf.value("calTxGainA", 10)); - cal_tx_gain_.push_back(tddConf.value("calTxGainB", 10)); - tx_gain_.shrink_to_fit(); - rx_gain_.shrink_to_fit(); - cal_tx_gain_.shrink_to_fit(); - - sample_cal_en_ = tddConf.value("sample_calibrate", false); - imbalance_cal_en_ = tddConf.value("imbalance_calibrate", false); - beam_sweep_ = tddConf.value("beamsweep", false); - beacon_ant_ = tddConf.value("beacon_antenna", 0); - max_frame_ = tddConf.value("max_frame", 0); - - MLPD_TRACE("Number cells: %zu\n", num_cells_); - bs_sdr_ids_.resize(num_cells_); - n_bs_sdrs_.resize(num_cells_); - n_bs_antennas_.resize(num_cells_); - num_bs_sdrs_all_ = 0; - for (size_t i = 0u; i < num_cells_; i++) { - Utils::loadDevices(bs_sdr_file_.at(i), bs_sdr_ids_.at(i)); - n_bs_sdrs_.at(i) = bs_sdr_ids_.at(i).size(); - n_bs_antennas_.at(i) = bs_channel_.length() * n_bs_sdrs_.at(i); - num_bs_sdrs_all_ += bs_sdr_ids_.at(i).size(); - MLPD_TRACE("Loading devices - cell %zu, sdrs %zu, antennas: %zu, " - "total bs srds: %zu\n", - i, n_bs_sdrs_.at(i), n_bs_antennas_.at(i), num_bs_sdrs_all_); - } + if (tddConf.value("txgainB", 20) > kMaxTxGainBS) { + std::string msg + = "ERROR: BaseStation ChanB - Maximum TX gain value is "; + msg += std::to_string(kMaxTxGainBS); + throw std::invalid_argument(msg); + } else { + tx_gain_.push_back(tddConf.value("txgainB", 20)); + } - // Array with cummulative sum of SDRs in cells - n_bs_sdrs_agg_.resize(num_cells_ + 1); - n_bs_sdrs_agg_.at(0) = 0; //n_bs_sdrs_[0]; - for (size_t i = 0; i < num_cells_; i++) { - n_bs_sdrs_agg_.at(i + 1) = n_bs_sdrs_agg_.at(i) + n_bs_sdrs_.at(i); - } + rx_gain_.push_back(tddConf.value("rxgainA", 20)); + rx_gain_.push_back(tddConf.value("rxgainB", 20)); + cal_tx_gain_.push_back(tddConf.value("calTxGainA", 10)); + cal_tx_gain_.push_back(tddConf.value("calTxGainB", 10)); + tx_gain_.shrink_to_fit(); + rx_gain_.shrink_to_fit(); + cal_tx_gain_.shrink_to_fit(); + + sample_cal_en_ = tddConf.value("sample_calibrate", false); + imbalance_cal_en_ = tddConf.value("imbalance_calibrate", false); + beam_sweep_ = tddConf.value("beamsweep", false); + beacon_ant_ = tddConf.value("beacon_antenna", 0); + max_frame_ = tddConf.value("max_frame", 0); + + MLPD_TRACE("Number cells: %zu\n", num_cells_); + bs_sdr_ids_.resize(num_cells_); + n_bs_sdrs_.resize(num_cells_); + n_bs_antennas_.resize(num_cells_); + num_bs_sdrs_all_ = 0; + for (size_t i = 0u; i < num_cells_; i++) { + Utils::loadDevices(bs_sdr_file_.at(i), bs_sdr_ids_.at(i)); + n_bs_sdrs_.at(i) = bs_sdr_ids_.at(i).size(); + n_bs_antennas_.at(i) = bs_channel_.length() * n_bs_sdrs_.at(i); + num_bs_sdrs_all_ += bs_sdr_ids_.at(i).size(); + MLPD_TRACE("Loading devices - cell %zu, sdrs %zu, antennas: %zu, " + "total bs srds: %zu\n", + i, n_bs_sdrs_.at(i), n_bs_antennas_.at(i), num_bs_sdrs_all_); + } - if (kUseUHD == false) - Utils::loadDevices(hub_file_, hub_ids_); - reciprocal_calib_ = tddConf.value("reciprocal_calibration", false); - cal_ref_sdr_id_ = tddConf.value("ref_sdr_index", num_bs_sdrs_all_ - 1); + // Array with cummulative sum of SDRs in cells + n_bs_sdrs_agg_.resize(num_cells_ + 1); + n_bs_sdrs_agg_.at(0) = 0; //n_bs_sdrs_[0]; + for (size_t i = 0; i < num_cells_; i++) { + n_bs_sdrs_agg_.at(i + 1) = n_bs_sdrs_agg_.at(i) + n_bs_sdrs_.at(i); + } - if (reciprocal_calib_ == true) { - calib_frames_.resize(num_cells_); - for (size_t c = 0; c < num_cells_; c++) { - calib_frames_[c].resize(n_bs_sdrs_[c]); - size_t num_channels = bs_channel_.size(); - size_t frame_length - = num_channels * n_bs_sdrs_[c] - (num_channels - 1); - calib_frames_[c][cal_ref_sdr_id_] - = std::string(frame_length, 'G'); - calib_frames_[c][cal_ref_sdr_id_].replace( - num_channels * cal_ref_sdr_id_, 1, "P"); - for (size_t i = 0; i < n_bs_sdrs_[c]; i++) { - if (i != cal_ref_sdr_id_) { - calib_frames_[c][i] = std::string(frame_length, 'G'); - for (size_t ch = 0; ch < num_channels; ch++) { - calib_frames_[c][i].replace( - i * num_channels + ch, 1, "P"); - calib_frames_[c][cal_ref_sdr_id_].replace( - num_channels * i + ch, 1, "R"); - } + if (kUseUHD == false) + Utils::loadDevices(hub_file_, hub_ids_); + reciprocal_calib_ = tddConf.value("reciprocal_calibration", false); + cal_ref_sdr_id_ = tddConf.value("ref_sdr_index", num_bs_sdrs_all_ - 1); + + if (reciprocal_calib_ == true) { + calib_frames_.resize(num_cells_); + for (size_t c = 0; c < num_cells_; c++) { + calib_frames_[c].resize(n_bs_sdrs_[c]); + size_t num_channels = bs_channel_.size(); + size_t frame_length + = num_channels * n_bs_sdrs_[c] - (num_channels - 1); + calib_frames_[c][cal_ref_sdr_id_] = std::string(frame_length, 'G'); + calib_frames_[c][cal_ref_sdr_id_].replace( + num_channels * cal_ref_sdr_id_, 1, "P"); + for (size_t i = 0; i < n_bs_sdrs_[c]; i++) { + if (i != cal_ref_sdr_id_) { + calib_frames_[c][i] = std::string(frame_length, 'G'); + for (size_t ch = 0; ch < num_channels; ch++) { calib_frames_[c][i].replace( - num_channels * cal_ref_sdr_id_, 1, "R"); + i * num_channels + ch, 1, "P"); + calib_frames_[c][cal_ref_sdr_id_].replace( + num_channels * i + ch, 1, "R"); } + calib_frames_[c][i].replace( + num_channels * cal_ref_sdr_id_, 1, "R"); } } - symbols_per_frame_ = calib_frames_.at(0).size(); - pilot_syms_per_frame_ = 2; // up and down reciprocity pilots - noise_syms_per_frame_ = 0; - ul_syms_per_frame_ = 0; - dl_syms_per_frame_ = 0; - } else { - auto jBsFrames = tddConf.value("frame_schedule", json::array()); - frames_.assign(jBsFrames.begin(), jBsFrames.end()); - assert(frames_.size() == num_cells_); - pilot_symbols_ = Utils::loadSymbols(frames_, 'P'); - noise_symbols_ = Utils::loadSymbols(frames_, 'N'); - ul_symbols_ = Utils::loadSymbols(frames_, 'U'); - dl_symbols_ = Utils::loadSymbols(frames_, 'D'); - symbols_per_frame_ = frames_.at(0).size(); - pilot_syms_per_frame_ = pilot_symbols_.at(0).size(); - noise_syms_per_frame_ = noise_symbols_.at(0).size(); - ul_syms_per_frame_ = ul_symbols_.at(0).size(); - dl_syms_per_frame_ = dl_symbols_.at(0).size(); - // read commons from client json config - if (client_present_ == false) { - num_cl_sdrs_ = num_cl_antennas_ = std::count( - frames_.at(0).begin(), frames_.at(0).end(), 'P'); - } + } + slot_per_frame_ = calib_frames_.at(0).size(); + pilot_slot_per_frame_ = 2; // up and down reciprocity pilots + noise_slot_per_frame_ = 0; + ul_slot_per_frame_ = 0; + dl_slot_per_frame_ = 0; + } else { + auto jBsFrames = tddConf.value("frame_schedule", json::array()); + frames_.assign(jBsFrames.begin(), jBsFrames.end()); + assert(frames_.size() == num_cells_); + pilot_slots_ = Utils::loadSlots(frames_, 'P'); + noise_slots_ = Utils::loadSlots(frames_, 'N'); + ul_slots_ = Utils::loadSlots(frames_, 'U'); + dl_slots_ = Utils::loadSlots(frames_, 'D'); + slot_per_frame_ = frames_.at(0).size(); + pilot_slot_per_frame_ = pilot_slots_.at(0).size(); + noise_slot_per_frame_ = noise_slots_.at(0).size(); + ul_slot_per_frame_ = ul_slots_.at(0).size(); + dl_slot_per_frame_ = dl_slots_.at(0).size(); + // read commons from client json config + if (client_present_ == false) { + num_cl_sdrs_ = num_cl_antennas_ + = std::count(frames_.at(0).begin(), frames_.at(0).end(), 'P'); } } - - MLPD_TRACE("Starting clients -- %zu", num_bs_sdrs_all_); + //} // Clients - assert(!(client_present_ == true && reciprocal_calib_ == true)); - if (client_present_ == true) { + if (reciprocal_calib_ == false) { + // read commons from Client json config + if (tddConf.find("frequency") == tddConf.end()) + freq_ = tddConfCl.value("frequency", 2.5e9); + if (tddConf.find("rate") == tddConf.end()) + rate_ = tddConfCl.value("rate", 5e6); + if (tddConf.find("nco_frequency") == tddConf.end()) + nco_ = tddConfCl.value("nco_frequency", 0.75 * rate_); + if (tddConf.find("ofdm_symbol_per_slot") == tddConf.end()) + symbol_per_slot_ = tddConfCl.value("ofdm_symbol_per_slot", 1); + if (tddConf.find("fft_size") == tddConf.end()) + fft_size_ = tddConfCl.value("fft_size", 0); + if (tddConf.find("cp_size") == tddConf.end()) + cp_size_ = tddConfCl.value("cp_size", 0); + if (tddConf.find("prefix") == tddConf.end()) + prefix_ = tddConfCl.value("prefix", 0); + if (tddConf.find("postfix") == tddConf.end()) + postfix_ = tddConfCl.value("postfix", 0); + if (tddConf.find("ofdm_data_subcarrier_num") == tddConf.end()) + symbol_data_subcarrier_num_ + = tddConfCl.value("ofdm_data_subcarrier_num", fft_size_); + if (tddConf.find("tx_scale") == tddConf.end()) + tx_scale_ = tddConfCl.value("tx_scale", 0.5); + if (tddConf.find("pilot_seq") == tddConf.end()) + pilot_seq_ = tddConfCl.value("pilot_seq", "lts"); + if (tddConf.find("single_gain") == tddConf.end()) + single_gain_ = tddConfCl.value("single_gain", true); + if (tddConf.find("modulation") == tddConf.end()) + data_mod_ = tddConfCl.value("modulation", "QPSK"); + auto jClSdrs = tddConfCl.value("sdr_id", json::array()); - // auto jClSdrs = tddConfCl.value("sdr_ip", json::array()); num_cl_sdrs_ = jClSdrs.size(); cl_sdr_ids_.assign(jClSdrs.begin(), jClSdrs.end()); - // cl_sdr_ips.assign(jClSdrs.begin(), jClSdrs.end()); cl_channel_ = tddConfCl.value("channel", "A"); if (cl_channel_ != "A" && cl_channel_ != "B" && cl_channel_ != "AB") throw std::invalid_argument( @@ -221,7 +251,7 @@ Config::Config(const std::string& jsonfile, const std::string& directory) cl_agc_en_ = tddConfCl.value("agc_en", false); cl_agc_gain_init_ = tddConfCl.value("agc_gain_init", 70); // 0 to 108 frame_mode_ = tddConfCl.value("frame_mode", "continuous_resync"); - hw_framer_ = tddConfCl.value("hw_framer", true); + hw_framer_ = tddConfCl.value("hw_framer", false); tx_advance_ = tddConfCl.value("tx_advance", 250); // 250 ul_data_frame_num_ = tddConfCl.value("ul_data_frame_num", 1); @@ -267,52 +297,35 @@ Config::Config(const std::string& jsonfile, const std::string& directory) auto jClFrames = tddConfCl.value("frame_schedule", json::array()); assert(jClSdrs.size() == jClFrames.size()); cl_frames_.assign(jClFrames.begin(), jClFrames.end()); - cl_pilot_symbols_ = Utils::loadSymbols(cl_frames_, 'P'); - cl_ul_symbols_ = Utils::loadSymbols(cl_frames_, 'U'); - cl_dl_symbols_ = Utils::loadSymbols(cl_frames_, 'D'); - - // read commons from Client json config - if (bs_present_ == false) { - freq_ = tddConfCl.value("frequency", 2.5e9); - rate_ = tddConfCl.value("rate", 5e6); - nco_ = tddConfCl.value("nco_frequency", 0.75 * rate_); - bw_filter_ = rate_ + 2 * nco_; - radio_rf_freq_ = freq_ - nco_; - symbol_per_subframe_ - = tddConfCl.value("ofdm_symbol_per_subframe", 1); - fft_size_ = tddConfCl.value("fft_size", 0); - cp_size_ = tddConfCl.value("cp_size", 0); - prefix_ = tddConfCl.value("prefix", 0); - postfix_ = tddConfCl.value("postfix", 0); - ofdm_symbol_size_ = fft_size_ + cp_size_; - subframe_size_ = symbol_per_subframe_ * ofdm_symbol_size_; - samps_per_symbol_ = subframe_size_ + prefix_ + postfix_; - tx_scale_ = tddConfCl.value("tx_scale", 0.5); - beacon_seq_ = tddConfCl.value("beacon_seq", "gold_ifft"); - pilot_seq_ = tddConfCl.value("pilot_seq", "lts"); - symbols_per_frame_ = cl_frames_.at(0).size(); - single_gain_ = tddConfCl.value("single_gain", true); - data_mod_ = tddConfCl.value("modulation", "QPSK"); - } + cl_pilot_slots_ = Utils::loadSlots(cl_frames_, 'P'); + cl_ul_slots_ = Utils::loadSlots(cl_frames_, 'U'); + cl_dl_slots_ = Utils::loadSlots(cl_frames_, 'D'); } - ul_data_sym_present_ = (reciprocal_calib_ == false) - && ((bs_present_ && (ul_symbols_.at(0).empty() == false)) - || (client_present_ && !cl_ul_symbols_.at(0).empty())); + bw_filter_ = rate_ + 2 * nco_; + radio_rf_freq_ = freq_ - nco_; + ofdm_symbol_size_ = fft_size_ + cp_size_; + slot_samp_size_ = symbol_per_slot_ * ofdm_symbol_size_; + samps_per_slot_ = slot_samp_size_ + prefix_ + postfix_; + assert(reciprocal_calib_ || slot_per_frame_ == cl_frames_.at(0).size()); + + ul_data_slot_present_ = (reciprocal_calib_ == false) + && ((bs_present_ && (ul_slots_.at(0).empty() == false)) + || (client_present_ && !cl_ul_slots_.at(0).empty())); std::vector> prefix_zpad(prefix_, 0); std::vector> postfix_zpad(postfix_, 0); - // compose Beacon subframe: + // compose Beacon slot: // STS Sequence (for AGC) + GOLD Sequence (for Sync) // 15reps of STS(16) + 2reps of gold_ifft(128) srand(time(NULL)); - const int seqLen = 128; + const int seq_len = 128; std::vector> gold_ifft = CommsLib::getSequence(CommsLib::GOLD_IFFT); auto gold_ifft_ci16 = Utils::float_to_cint16(gold_ifft); gold_cf32_.clear(); - for (size_t i = 0; i < seqLen; i++) { + for (size_t i = 0; i < seq_len; i++) { gold_cf32_.push_back( std::complex(gold_ifft[0][i], gold_ifft[1][i])); } @@ -337,8 +350,8 @@ Config::Config(const std::string& jsonfile, const std::string& directory) beacon_size_ = beacon_ci16_.size(); - if (samps_per_symbol_ < (beacon_size_ + prefix_ + postfix_)) { - std::string msg = "Minimum supported subframe_size is "; + if (samps_per_slot_ < (beacon_size_ + prefix_ + postfix_)) { + std::string msg = "Minimum supported slot_samp_size is "; msg += std::to_string(beacon_size_); throw std::invalid_argument(msg); } @@ -347,7 +360,7 @@ Config::Config(const std::string& jsonfile, const std::string& directory) coeffs_ = Utils::cint16_to_uint32(gold_ifft_ci16, true, "QI"); std::vector> post_beacon_zpad( - subframe_size_ - beacon_size_, 0); + slot_samp_size_ - beacon_size_, 0); beacon_ci16_.insert( beacon_ci16_.begin(), prefix_zpad.begin(), prefix_zpad.end()); beacon_ci16_.insert( @@ -355,7 +368,7 @@ Config::Config(const std::string& jsonfile, const std::string& directory) beacon_ci16_.insert( beacon_ci16_.end(), postfix_zpad.begin(), postfix_zpad.end()); - // compose pilot subframe + // compose pilot slot if (fft_size_ > kMaxSupportedFFTSize) { fft_size_ = kMaxSupportedFFTSize; std::cout << "Unsupported fft size! Setting fft size to " @@ -376,11 +389,12 @@ Config::Config(const std::string& jsonfile, const std::string& directory) if (fft_size_ == 64) { pilot_sym_f_ = CommsLib::getSequence(CommsLib::LTS_SEQ_F); - pilot_sym_ = CommsLib::getSequence(CommsLib::LTS_SEQ); + pilot_sym_t_ = CommsLib::getSequence(CommsLib::LTS_SEQ); + symbol_data_subcarrier_num_ = Consts::kNumMappedSubcarriers_80211; } else if (pilot_seq_ == "zadoff-chu") { pilot_sym_f_ = CommsLib::getSequence( CommsLib::LTE_ZADOFF_CHU_F, symbol_data_subcarrier_num_); - pilot_sym_ = CommsLib::getSequence( + pilot_sym_t_ = CommsLib::getSequence( CommsLib::LTE_ZADOFF_CHU, symbol_data_subcarrier_num_); } else std::cout @@ -388,13 +402,13 @@ Config::Config(const std::string& jsonfile, const std::string& directory) << " is not supported! Choose either LTS (64-fft) or zaddof-chu." << std::endl; - auto iq_ci16 = Utils::float_to_cint16(pilot_sym_); + auto iq_ci16 = Utils::float_to_cint16(pilot_sym_t_); iq_ci16.insert(iq_ci16.begin(), iq_ci16.end() - cp_size_, iq_ci16.end()); pilot_ci16_.clear(); pilot_ci16_.insert( pilot_ci16_.begin(), prefix_zpad.begin(), prefix_zpad.end()); - for (size_t i = 0; i < symbol_per_subframe_; i++) + for (size_t i = 0; i < symbol_per_slot_; i++) pilot_ci16_.insert(pilot_ci16_.end(), iq_ci16.begin(), iq_ci16.end()); pilot_ci16_.insert( pilot_ci16_.end(), postfix_zpad.begin(), postfix_zpad.end()); @@ -435,7 +449,7 @@ Config::Config(const std::string& jsonfile, const std::string& directory) + "x" + std::to_string(ant_num) + ".hdf5"; } else { std::string ul_present_str - = (ul_data_sym_present_ ? "uplink-" : ""); + = (ul_data_slot_present_ ? "uplink-" : ""); filename = directory + "/trace-" + ul_present_str + std::to_string(1900 + ltm->tm_year) + "-" + std::to_string(1 + ltm->tm_mon) + "-" @@ -454,7 +468,7 @@ Config::Config(const std::string& jsonfile, const std::string& directory) MLPD_INFO("Cores found %u ... \n", num_cores); core_alloc_ = num_cores > RX_THREAD_NUM; if ((bs_present_ == true) - && (pilot_syms_per_frame_ + ul_syms_per_frame_ > 0)) { + && (pilot_slot_per_frame_ + ul_slot_per_frame_ > 0)) { task_thread_num_ = tddConf.value("task_thread", TASK_THREAD_NUM); rx_thread_num_ = (num_cores >= (2 * RX_THREAD_NUM)) ? std::min(RX_THREAD_NUM, static_cast(num_bs_sdrs_all_)) @@ -464,7 +478,7 @@ Config::Config(const std::string& jsonfile, const std::string& directory) } if ((client_present_ == true) && (num_cores - < (1 + task_thread_num_ + rx_thread_num_ + num_cl_sdrs_))) { + < (1 + task_thread_num_ + rx_thread_num_ + num_cl_sdrs_))) { core_alloc_ = false; } } else { @@ -490,8 +504,8 @@ Config::Config(const std::string& jsonfile, const std::string& directory) void Config::loadULData(const std::string& directory) { - // compose data subframe - if (ul_data_sym_present_) { + // compose data slot + if (ul_data_slot_present_) { std::vector> prefix_zpad_t(prefix_, 0); std::vector> postfix_zpad_t(postfix_, 0); txdata_time_dom_.resize(num_cl_antennas_); @@ -501,8 +515,8 @@ void Config::loadULData(const std::string& directory) std::string filename_tag = data_mod_ + "_" + std::to_string(symbol_data_subcarrier_num_) + "_" + std::to_string(fft_size_) + "_" - + std::to_string(symbol_per_subframe_) + "_" - + std::to_string(cl_ul_symbols_[i].size()) + "_" + + std::to_string(symbol_per_slot_) + "_" + + std::to_string(cl_ul_slots_[i].size()) + "_" + std::to_string(ul_data_frame_num_) + "_" + cl_channel_ + "_" + std::to_string(i) + ".bin"; @@ -530,32 +544,32 @@ void Config::loadULData(const std::string& directory) } // Frame * UL Slots * Channel * Samples - for (size_t u = 0; u < cl_ul_symbols_[i].size(); u++) { + for (size_t u = 0; u < cl_ul_slots_[i].size(); u++) { for (size_t h = 0; h < cl_sdr_ch_; h++) { size_t ant_i = i * cl_sdr_ch_ + h; std::vector> data_freq_dom( - fft_size_ * symbol_per_subframe_); + fft_size_ * symbol_per_slot_); size_t read_num = std::fread(data_freq_dom.data(), 2 * sizeof(float), - fft_size_ * symbol_per_subframe_, fp_tx_f); - if (read_num != (fft_size_ * symbol_per_subframe_)) { + fft_size_ * symbol_per_slot_, fp_tx_f); + if (read_num != (fft_size_ * symbol_per_slot_)) { MLPD_WARN( "BAD Read of Uplink Freq-Domain Data: %zu/%zu\n", - read_num, fft_size_ * symbol_per_subframe_); + read_num, fft_size_ * symbol_per_slot_); } txdata_freq_dom_[ant_i].insert( txdata_freq_dom_[ant_i].end(), data_freq_dom.begin(), data_freq_dom.end()); std::vector> data_time_dom( - samps_per_symbol_); + samps_per_slot_); read_num = std::fread(data_time_dom.data(), - 2 * sizeof(float), samps_per_symbol_, fp_tx_t); - if (read_num != samps_per_symbol_) { + 2 * sizeof(float), samps_per_slot_, fp_tx_t); + if (read_num != samps_per_slot_) { MLPD_WARN( "BAD Read of Uplink Time-Domain Data: %zu/%zu\n", - read_num, samps_per_symbol_); + read_num, samps_per_slot_); } txdata_time_dom_[ant_i].insert( txdata_time_dom_[ant_i].end(), data_time_dom.begin(), @@ -620,76 +634,74 @@ size_t Config::getTotNumAntennas() Config::~Config() {} -int Config::getClientId(int frame_id, int symbol_id) +int Config::getClientId(int frame_id, int slot_id) { if (reciprocal_calib_) - return symbol_id; + return slot_id; std::vector::iterator it; int fid = frame_id % frames_.size(); - it = find(pilot_symbols_.at(fid).begin(), pilot_symbols_.at(fid).end(), - symbol_id); - if (it != pilot_symbols_.at(fid).end()) { - return (it - pilot_symbols_.at(fid).begin()); + it = find( + pilot_slots_.at(fid).begin(), pilot_slots_.at(fid).end(), slot_id); + if (it != pilot_slots_.at(fid).end()) { + return (it - pilot_slots_.at(fid).begin()); } return -1; } -int Config::getNoiseSFIndex(int frame_id, int symbol_id) +int Config::getNoiseSlotIndex(int frame_id, int slot_id) { std::vector::iterator it; int fid = frame_id % frames_.size(); - it = find(noise_symbols_.at(fid).begin(), noise_symbols_.at(fid).end(), - symbol_id); - if (it != noise_symbols_.at(fid).end()) - return (it - noise_symbols_.at(fid).begin()); + it = find( + noise_slots_.at(fid).begin(), noise_slots_.at(fid).end(), slot_id); + if (it != noise_slots_.at(fid).end()) + return (it - noise_slots_.at(fid).begin()); return -1; } -int Config::getUlSFIndex(int frame_id, int symbol_id) +int Config::getUlSlotIndex(int frame_id, int slot_id) { std::vector::iterator it; int fid = frame_id % frames_.size(); - it = find( - ul_symbols_.at(fid).begin(), ul_symbols_.at(fid).end(), symbol_id); - if (it != ul_symbols_.at(fid).end()) { - return (it - ul_symbols_.at(fid).begin()); + it = find(ul_slots_.at(fid).begin(), ul_slots_.at(fid).end(), slot_id); + if (it != ul_slots_.at(fid).end()) { + return (it - ul_slots_.at(fid).begin()); } return -1; } -int Config::getDlSFIndex(int frame_id, int symbol_id) +int Config::getDlSlotIndex(int frame_id, int slot_id) { std::vector::iterator it; int fid = frame_id % frames_.size(); - it = find( - dl_symbols_.at(fid).begin(), dl_symbols_.at(fid).end(), symbol_id); - if (it != dl_symbols_.at(fid).end()) - return (it - dl_symbols_.at(fid).begin()); + it = find(dl_slots_.at(fid).begin(), dl_slots_.at(fid).end(), slot_id); + if (it != dl_slots_.at(fid).end()) + return (it - dl_slots_.at(fid).begin()); return -1; } -bool Config::isPilot(int frame_id, int symbol_id) +bool Config::isPilot(int frame_id, int slot_id) { try { - return frames_[frame_id % frames_.size()].at(symbol_id) == 'P'; + return frames_[frame_id % frames_.size()].at(slot_id) == 'P'; } catch (const std::out_of_range&) { return false; } } -bool Config::isNoise(int frame_id, int symbol_id) +bool Config::isNoise(int frame_id, int slot_id) { try { - return frames_[frame_id % frames_.size()].at(symbol_id) == 'N'; + return frames_[frame_id % frames_.size()].at(slot_id) == 'N'; } catch (const std::out_of_range&) { return false; } } -bool Config::isData(int frame_id, int symbol_id) +bool Config::isData(int frame_id, int slot_id) { try { - return frames_[frame_id % frames_.size()].at(symbol_id) == 'U'; + return frames_[frame_id % frames_.size()].at(slot_id) == 'U'; } catch (const std::out_of_range&) { return false; } @@ -706,10 +718,10 @@ unsigned Config::getCoreCount() extern "C" { __attribute__((visibility("default"))) Config* Config_new( - char* filename, char* storepath) + char* filename, char* storepath, bool bs_only, bool client_only) { - Config* cfg = new Config(filename, storepath); + Config* cfg = new Config(filename, storepath, bs_only, client_only); return cfg; } } diff --git a/CC/Sounder/data_generator.cc b/CC/Sounder/data_generator.cc index a3a131a9..a954907f 100644 --- a/CC/Sounder/data_generator.cc +++ b/CC/Sounder/data_generator.cc @@ -15,8 +15,8 @@ void DataGenerator::GenerateData(const std::string& directory) std::string filename_tag = cfg_->data_mod() + "_" + std::to_string(cfg_->symbol_data_subcarrier_num()) + "_" + std::to_string(cfg_->fft_size()) + "_" - + std::to_string(cfg_->symbol_per_subframe()) + "_" - + std::to_string(cfg_->cl_ul_symbols()[i].size()) + "_" + + std::to_string(cfg_->symbol_per_slot()) + "_" + + std::to_string(cfg_->cl_ul_slots()[i].size()) + "_" + std::to_string(cfg_->ul_data_frame_num()) + "_" + cfg_->cl_channel() + "_" + std::to_string(i) + ".bin"; @@ -37,13 +37,13 @@ void DataGenerator::GenerateData(const std::string& directory) FILE* fp_tx_t = std::fopen(filename_ul_data_t.c_str(), "wb"); // Frame * UL Slots * Channel * Samples for (size_t f = 0; f < cfg_->ul_data_frame_num(); f++) { - for (size_t u = 0; u < cfg_->cl_ul_symbols()[i].size(); u++) { + for (size_t u = 0; u < cfg_->cl_ul_slots()[i].size(); u++) { for (size_t h = 0; h < cfg_->cl_sdr_ch(); h++) { std::vector> data_time_dom; std::vector> data_freq_dom; data_time_dom.insert(data_time_dom.begin(), prefix_zpad_t.begin(), prefix_zpad_t.end()); - for (size_t s = 0; s < cfg_->symbol_per_subframe(); s++) { + for (size_t s = 0; s < cfg_->symbol_per_slot(); s++) { std::vector data_bits; for (size_t c = 0; c < cfg_->data_ind().size(); c++) { data_bits.push_back((uint8_t)(rand() % mod_order)); @@ -77,9 +77,9 @@ void DataGenerator::GenerateData(const std::string& directory) data_time_dom.insert(data_time_dom.end(), postfix_zpad_t.begin(), postfix_zpad_t.end()); std::fwrite(data_freq_dom.data(), - cfg_->fft_size() * cfg_->symbol_per_subframe(), + cfg_->fft_size() * cfg_->symbol_per_slot(), sizeof(float) * 2, fp_tx_f); - std::fwrite(data_time_dom.data(), cfg_->samps_per_symbol(), + std::fwrite(data_time_dom.data(), cfg_->samps_per_slot(), sizeof(float) * 2, fp_tx_t); } } diff --git a/CC/Sounder/files/anechoic_conf/conf-anechoic-chamber.json b/CC/Sounder/files/anechoic_conf/conf-anechoic-chamber.json index 164900eb..8cce46f9 100644 --- a/CC/Sounder/files/anechoic_conf/conf-anechoic-chamber.json +++ b/CC/Sounder/files/anechoic_conf/conf-anechoic-chamber.json @@ -1,6 +1,5 @@ { "BaseStations" : { - "cells" : 1, "sdr_id" : ["files/anechoic_conf/iris-serials-anechoic.txt"], "hub_id" : "files/anechoic_conf/hub-serials-anechoic.txt", "frequency" : 3.55e9, @@ -14,12 +13,12 @@ "BGGGGGPPPPGGGGGNGGGG" ], "max_frame" : 2000, - "ofdm_symbol_per_subframe" : 10, + "ofdm_symbol_per_slot" : 10, "fft_size" : 64, "cp_size" : 16, "prefix" : 160, "postfix" : 160, - "beamsweep" : true, + "beamsweep" : false, "beacon_antenna" : 0 }, @@ -28,24 +27,15 @@ "RF3E000119", "RF3E000145" ], - "frequency" : 3.55e9, "channel" : "AB", "rxgainA" : [65, 65], "txgainA" : [75, 75], "rxgainB" : [65, 65], "txgainB" : [75, 75], - "rate" : 5e6, "frame_schedule" : [ "GGGGGGPPGGGGGGGGGGGG", "GGGGGGGGPPGGGGGGGGGG" ], - "frame_mode" : "continuous_resync", - "ofdm_symbol_per_subframe" : 10, - "fft_size" : 64, - "cp_size" : 16, - "prefix" : 160, - "postfix" : 160, - "tx_advance" : 135, - "hw_framer" : false + "tx_advance" : 135 } } diff --git a/CC/Sounder/files/conf-bs-and-one-client-with-uplink-data.json b/CC/Sounder/files/conf-bs-and-one-client-with-uplink-data.json index def579f8..175bb2da 100644 --- a/CC/Sounder/files/conf-bs-and-one-client-with-uplink-data.json +++ b/CC/Sounder/files/conf-bs-and-one-client-with-uplink-data.json @@ -1,51 +1,39 @@ { "BaseStations" : { - "cells" : 1, "sdr_id" : ["files/iris-serials.txt"], "hub_id" : "files/hub-serials.txt", - "frequency" : 2.5e9, + "frequency" : 3.6e9, + "rate" : 5e6, "channel" : "AB", "rxgainA" : 65, - "txgainA" : 87, + "txgainA" : 81, "rxgainB" : 65, - "txgainB" : 87, - "rate" : 5e6, + "txgainB" : 81, "frame_schedule" : [ "BGPPUGGGGGGGGGGGGGGG" ], "max_frame" : 4000, - "ofdm_symbol_per_subframe" : 10, + "ofdm_symbol_per_slot" : 10, "fft_size" : 64, "cp_size" : 16, "prefix" : 160, "postfix" : 160, - "beamsweep" : true, - "beacon_antenna" : 0, - "modulation" : "16QAM" + "beamsweep" : true }, "Clients" : { "sdr_id" : [ "RF3E000030" ], - "frequency" : 2.5e9, "channel" : "AB", "rxgainA" : [65], - "txgainA" : [87], + "txgainA" : [81], "rxgainB" : [65], - "txgainB" : [87], - "rate" : 5e6, + "txgainB" : [81], "frame_schedule" : [ "GDPPUGGGGGGGGGGGGGGG" ], - "frame_mode" : "continuous_resync", - "ofdm_symbol_per_subframe" : 10, - "fft_size" : 64, - "cp_size" : 16, - "prefix" : 160, - "postfix" : 160, "modulation" : "16QAM", - "tx_advance" : 135, - "hw_framer" : false + "tx_advance" : 135 } } diff --git a/CC/Sounder/files/conf-bs-and-one-client.json b/CC/Sounder/files/conf-bs-and-one-client.json index 955cd727..c202cf54 100644 --- a/CC/Sounder/files/conf-bs-and-one-client.json +++ b/CC/Sounder/files/conf-bs-and-one-client.json @@ -1,49 +1,38 @@ { "BaseStations" : { - "cells" : 1, "sdr_id" : ["files/iris-serials.txt"], "hub_id" : "files/hub-serials.txt", - "frequency" : 2.5e9, + "frequency" : 3.6e9, "channel" : "AB", "rxgainA" : 65, - "txgainA" : 87, + "txgainA" : 81, "rxgainB" : 65, - "txgainB" : 87, + "txgainB" : 81, "rate" : 5e6, "frame_schedule" : [ "BGPPGGGGGGGGGGGGGGGG" ], "max_frame" : 4000, - "ofdm_symbol_per_subframe" : 10, + "ofdm_symbol_per_slot" : 10, "fft_size" : 64, "cp_size" : 16, "prefix" : 160, "postfix" : 160, - "beamsweep" : true, - "beacon_antenna" : 0 + "beamsweep" : true }, "Clients" : { "sdr_id" : [ "RF3E000030" ], - "frequency" : 2.5e9, "channel" : "AB", "rxgainA" : [65], - "txgainA" : [87], + "txgainA" : [81], "rxgainB" : [65], - "txgainB" : [87], - "rate" : 5e6, + "txgainB" : [81], "frame_schedule" : [ "GGPPGGGGGGGGGGGGGGGG" ], - "frame_mode" : "continuous_resync", - "ofdm_symbol_per_subframe" : 10, - "fft_size" : 64, - "cp_size" : 16, - "prefix" : 160, - "postfix" : 160, - "tx_advance" : 135, - "hw_framer" : false + "tx_advance" : 135 } } diff --git a/CC/Sounder/files/conf-bs-and-two-client-with-uplink-data.json b/CC/Sounder/files/conf-bs-and-two-client-with-uplink-data.json index ced72b2b..18bbcf23 100644 --- a/CC/Sounder/files/conf-bs-and-two-client-with-uplink-data.json +++ b/CC/Sounder/files/conf-bs-and-two-client-with-uplink-data.json @@ -1,27 +1,24 @@ { "BaseStations" : { - "cells" : 1, "sdr_id" : ["files/iris-serials.txt"], "hub_id" : "files/hub-serials.txt", - "frequency" : 2.5e9, + "frequency" : 3.6e9, "channel" : "AB", "rxgainA" : 65, - "txgainA" : 87, + "txgainA" : 81, "rxgainB" : 65, - "txgainB" : 87, + "txgainB" : 81, "rate" : 5e6, "frame_schedule" : [ "BGGGGGPPPPUGGGGGGGGG" ], "max_frame" : 4000, - "ofdm_symbol_per_subframe" : 10, + "ofdm_symbol_per_slot" : 10, "fft_size" : 64, "cp_size" : 16, "prefix" : 160, "postfix" : 160, - "beamsweep" : true, - "beacon_antenna" : 0, - "modulation" : "16QAM" + "beamsweep" : true }, "Clients" : { @@ -29,25 +26,16 @@ "RF3E000030", "RF3E0000xx" ], - "frequency" : 2.5e9, "channel" : "AB", "rxgainA" : [65, 65], - "txgainA" : [87, 87], + "txgainA" : [81, 81], "rxgainB" : [65, 65], - "txgainB" : [87, 87], - "rate" : 5e6, + "txgainB" : [81, 81], "frame_schedule" : [ "GDGGGGPPGGUGGGGGGGGG", "GDGGGGGGPPUGGGGGGGGG" ], - "frame_mode" : "continuous_resync", - "ofdm_symbol_per_subframe" : 10, - "fft_size" : 64, - "cp_size" : 16, - "prefix" : 160, - "postfix" : 160, "modulation" : "16QAM", - "tx_advance" : 135, - "hw_framer" : false + "tx_advance" : 135 } } diff --git a/CC/Sounder/files/conf-bs-and-two-client.json b/CC/Sounder/files/conf-bs-and-two-client.json index 19915de1..183972aa 100644 --- a/CC/Sounder/files/conf-bs-and-two-client.json +++ b/CC/Sounder/files/conf-bs-and-two-client.json @@ -1,15 +1,14 @@ { "BaseStations" : { - "cells" : 1, "sdr_id" : ["files/iris-serials.txt"], "hub_id" : "files/hub-serials.txt", - "frequency" : 2.5e9, + "frequency" : 3.6e9, + "rate" : 5e6, "channel" : "AB", "rxgainA" : 65, "txgainA" : 87, "rxgainB" : 65, "txgainB" : 87, - "rate" : 5e6, "frame_schedule" : [ "BGGGGGPPPPGGGGGGGGGG" ], @@ -19,8 +18,7 @@ "cp_size" : 16, "prefix" : 160, "postfix" : 160, - "beamsweep" : true, - "beacon_antenna" : 0 + "beamsweep" : true }, "Clients" : { @@ -28,24 +26,15 @@ "RF3E000030", "RF3E0000xx" ], - "frequency" : 2.5e9, "channel" : "AB", "rxgainA" : [65, 65], "txgainA" : [87, 87], "rxgainB" : [65, 65], "txgainB" : [87, 87], - "rate" : 5e6, "frame_schedule" : [ "GGGGGGPPGGGGGGGGGGGG", "GGGGGGGGPPGGGGGGGGGG" ], - "frame_mode" : "continuous_resync", - "ofdm_symbol_per_subframe" : 10, - "fft_size" : 64, - "cp_size" : 16, - "prefix" : 160, - "postfix" : 160, - "tx_advance" : 135, - "hw_framer" : false + "tx_advance" : 135 } } diff --git a/CC/Sounder/files/conf-bs-only.json b/CC/Sounder/files/conf-bs-only.json deleted file mode 100644 index fd24a794..00000000 --- a/CC/Sounder/files/conf-bs-only.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "BaseStations" : { - "cells" : 1, - "sdr_id" : ["files/iris-serials.txt"], - "hub_id" : "files/hub-serials.txt", - "frequency" : 2.5e9, - "channel" : "AB", - "rxgainA" : 65, - "txgainA" : 87, - "rxgainB" : 65, - "txgainB" : 87, - "rate" : 5e6, - "frame_schedule" : [ - "BGPPGGGGGGGGGGGGGGGG" - ], - "max_frame" : 0, - "ofdm_symbol_per_subframe" : 10, - "fft_size" : 64, - "cp_size" : 16, - "prefix" : 160, - "postfix" : 160, - "beamsweep" : true - } - -} diff --git a/CC/Sounder/files/conf-client-only.json b/CC/Sounder/files/conf-client-only.json deleted file mode 100644 index d7fe0ec0..00000000 --- a/CC/Sounder/files/conf-client-only.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "Clients" : { - "frequency" : 2.5e9, - "channel" : "AB", - "agc_en" : false, - "rxgainA" : [65], - "txgainA" : [87], - "rxgainB" : [65], - "txgainB" : [87], - "rate" : 5e6, - "fft_size" : 64, - "ofdm_symbol_per_subframe" : 10, - "cp_size" : 16, - "prefix" : 160, - "postfix" : 160, - "frame_schedule" : [ - "GGPPGGGGGGGGGGGGGGGG" - ], - "frame_mode" : "continuous_resync", - "sdr_id" : [ - "RF3E000030" - ], - "tx_advance" : 135, - "hw_framer" : false - } -} diff --git a/CC/Sounder/files/special_conf/conf-reciprocal-calib-internal.json b/CC/Sounder/files/special_conf/conf-reciprocal-calib-internal.json index 25819d25..f3b0e8ad 100644 --- a/CC/Sounder/files/special_conf/conf-reciprocal-calib-internal.json +++ b/CC/Sounder/files/special_conf/conf-reciprocal-calib-internal.json @@ -4,19 +4,18 @@ "hub_id" : "files/renew-hub-serials1.txt", "frequency" : 3.6e9, "channel" : "A", - "rxgainA" : 20, - "txgainA" : 40, - "rxgainB" : 20, - "txgainB" : 40, + "rxgainA" : 61, + "txgainA" : 80, + "rxgainB" : 61, + "txgainB" : 80, "rate" : 5e6, "max_frame" : 4000, - "ofdm_symbol_per_subframe" : 1, + "ofdm_symbol_per_slot" : 1, "fft_size" : 512, "cp_size" : 32, "prefix" : 160, "postfix" : 160, "pilot_seq" : "zadoff-chu", - "reciprocal_calibration" : true, - "single_gain" : false + "internal_measurement" : true } } diff --git a/CC/Sounder/files/special_conf/powder-conf-cell-planning-cl.json b/CC/Sounder/files/special_conf/powder-conf-cell-planning-cl.json deleted file mode 100644 index 5a0bbdb4..00000000 --- a/CC/Sounder/files/special_conf/powder-conf-cell-planning-cl.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "Clients" : { - "frequency" : 2.5e9, - "channel" : "A", - "agc_en" : false, - "rxgainA" : [45], - "txgainA" : [95], - "rxgainB" : [45], - "txgainB" : [95], - "rate" : 5e6, - "fft_size" : 64, - "ofdm_symbol_per_subframe" : 64, - "cp_size" : 0, - "prefix" : 0, - "postfix" : 0, - "frame_schedule" : [ - "P" - ], - "frame_mode" : "free_running", - "sdr_id" : [ - "RF3E000157" - ], - "hw_framer" : true, - "single_gain" : true - } -} diff --git a/CC/Sounder/files/special_conf/powder-conf-cell-planning-bs.json b/CC/Sounder/files/special_conf/powder-conf-cell-planning.json similarity index 64% rename from CC/Sounder/files/special_conf/powder-conf-cell-planning-bs.json rename to CC/Sounder/files/special_conf/powder-conf-cell-planning.json index 36fbb022..7572216d 100644 --- a/CC/Sounder/files/special_conf/powder-conf-cell-planning-bs.json +++ b/CC/Sounder/files/special_conf/powder-conf-cell-planning.json @@ -1,6 +1,5 @@ { "BaseStations" : { - "cells" : 4, "sdr_id" : ["files/bs145.txt", "files/bs166.txt", "files/bs030.txt", "files/bs179.txt"], "hub_id" : "files/cell-planning-hub-serials.txt", "frequency" : 2.5e9, @@ -13,17 +12,28 @@ "frame_schedule" : [ "GGGGGGGPGGGGGGG" ], - "frame_mode" : "free_running", "max_frame" : 4000, - "ofdm_symbol_per_subframe" : 64, + "ofdm_symbol_per_slot" : 64, "fft_size" : 64, "cp_size" : 0, "prefix" : 0, "postfix" : 0, - "beacon_seq" : "gold_ifft", "beamsweep" : true, - "beacon_antenna" : 0, - "single_gain" : false + "beacon_antenna" : 0 + }, + "Clients" : { + "channel" : "A", + "rxgainA" : [45], + "txgainA" : [95], + "rxgainB" : [45], + "txgainB" : [95], + "frame_schedule" : [ + "P" + ], + "frame_mode" : "free_running", + "sdr_id" : [ + "RF3E000157" + ], + "hw_framer" : true } - } diff --git a/CC/Sounder/files/special_conf/usrp-bs-only.json b/CC/Sounder/files/special_conf/usrp-bs-only.json deleted file mode 100644 index dcaf9500..00000000 --- a/CC/Sounder/files/special_conf/usrp-bs-only.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "BaseStations" : { - "cells" : 1, - "sdr_id" : ["files/special_conf/usrp-bs-serials.txt"], - "frequency" : 2.5e9, - "channel" : "AB", - "rxgainA" : 10, - "txgainA" : 10, - "rxgainB" : 10, - "txgainB" : 10, - "rate" : 5e6, - "frame_schedule" : [ - "BGPPGGGGGGGGGGGGGGGG" - ], - "max_frame" : 0, - "ofdm_symbol_per_subframe" : 10, - "fft_size" : 64, - "cp_size" : 16, - "prefix" : 160, - "postfix" : 160, - "beamsweep" : true - } - -} diff --git a/CC/Sounder/files/special_conf/usrp-client-only.json b/CC/Sounder/files/special_conf/usrp-client-only.json deleted file mode 100644 index 67ccfb0b..00000000 --- a/CC/Sounder/files/special_conf/usrp-client-only.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "Clients" : { - "frequency" : 2.5e9, - "channel" : "AB", - "agc_en" : false, - "rxgainA" : [10], - "txgainA" : [10], - "rxgainB" : [10], - "txgainB" : [10], - "rate" : 5e6, - "fft_size" : 64, - "ofdm_symbol_per_subframe" : 10, - "cp_size" : 16, - "prefix" : 160, - "postfix" : 160, - "frame_schedule" : [ - "GGPPGGGGGGGGGGGGGGGG" - ], - "frame_mode" : "free_running", - "hw_framer" : false, - "tx_advance" : 100, - "sdr_id" : [ - "10.117.2.2" - ] - } -} diff --git a/CC/Sounder/files/special_conf/usrp-conf-one-client.json b/CC/Sounder/files/special_conf/usrp-conf-one-client.json index 95f55ea6..df1efbea 100644 --- a/CC/Sounder/files/special_conf/usrp-conf-one-client.json +++ b/CC/Sounder/files/special_conf/usrp-conf-one-client.json @@ -1,56 +1,38 @@ { "BaseStations" : { - "cells" : 1, - "sdr_id" : ["files/usrp-bs-serials.txt"], - "frequency" : 3.6e9, - "channel" : "B", - "rxgainA" : 20, - "txgainA" : 15, - "rxgainB" : 20, - "txgainB" : 15, - "rate" : 50e6, + "sdr_id" : ["files/special_conf/usrp-bs-serials.txt"], + "frequency" : 2.5e9, + "channel" : "A", + "rxgainA" : 10, + "txgainA" : 10, + "rxgainB" : 10, + "txgainB" : 10, + "rate" : 20e6, "frame_schedule" : [ - "BGPGGGGGGGGGGGGGGGGG" + "BGPGUGGGGGGGGGGGGGGG" ], "max_frame" : 0, - "subframe_size" : 640, + "ofdm_symbol_per_slot" : 10, "fft_size" : 64, "cp_size" : 16, - "prefix" : 82, - "postfix" : 68, - "beacon_seq" : "gold_ifft", - "beamsweep" : true, - "sample_calibrate" : false, - "beacon_antenna" : 0, - "pilot_seq" : "lts-half", - "modulation" : "QPSK", - "single_gain" : true + "prefix" : 160, + "postfix" : 160, + "beamsweep" : true }, "Clients" : { - "frequency" : 3.6e9, "channel" : "A", - "agc_en" : false, - "rxgainA" : [20], - "txgainA" : [15], - "rxgainB" : [20], - "txgainB" : [15], - "rate" : 50e6, + "rxgainA" : [10], + "txgainA" : [10], + "rxgainB" : [10], + "txgainB" : [10], "frame_schedule" : [ - "GGPGGGGGGGGGGGGGGGGG" + "GGPGUGGGGGGGGGGGGGGG" ], - "frame_mode" : "free_running", - "hw_framer" : false, - "subframe_size" : 640, - "fft_size" : 64, - "cp_size" : 16, - "prefix" : 82, - "postfix" : 68, - "beacon_seq" : "gold_ifft", - "pilot_seq" : "lts-half", - "modulation" : "QPSK", + "tx_advance" : 100, + "modulation" : "16QAM", "sdr_id" : [ - "10.118.3.2" + "10.117.2.2" ] } } diff --git a/CC/Sounder/format.sh b/CC/Sounder/format.sh new file mode 100755 index 00000000..e7223a69 --- /dev/null +++ b/CC/Sounder/format.sh @@ -0,0 +1,6 @@ +clang-format-11 -i ./*.cc +clang-format-11 -i ./*.cpp + +clang-format-11 -i include/*.h +clang-format-11 -i include/*.hpp + diff --git a/CC/Sounder/include/.gitignore b/CC/Sounder/include/.gitignore new file mode 100644 index 00000000..9c2ac05c --- /dev/null +++ b/CC/Sounder/include/.gitignore @@ -0,0 +1 @@ +/version_config.h diff --git a/CC/Sounder/include/config.h b/CC/Sounder/include/config.h index 453136b9..24e5de9c 100644 --- a/CC/Sounder/include/config.h +++ b/CC/Sounder/include/config.h @@ -17,7 +17,7 @@ class Config { public: - Config(const std::string&, const std::string&); + Config(const std::string&, const std::string&, const bool, const bool); ~Config(); //Accessors @@ -26,22 +26,13 @@ class Config { inline size_t num_bs_sdrs_all(void) const { return this->num_bs_sdrs_all_; } inline size_t num_cl_sdrs(void) const { return this->num_cl_sdrs_; } inline size_t core_alloc(void) const { return this->core_alloc_; } - inline int subframe_size(void) const { return this->subframe_size_; } - inline size_t samps_per_symbol(void) const + inline int slot_samp_size(void) const { return this->slot_samp_size_; } + inline size_t samps_per_slot(void) const { return this->samps_per_slot_; } + inline size_t slot_per_frame(void) const { return this->slot_per_frame_; } + inline size_t symbol_per_slot(void) const { return this->symbol_per_slot_; } + inline bool ul_data_slot_present(void) const { - return this->samps_per_symbol_; - } - inline size_t symbols_per_frame(void) const - { - return this->symbols_per_frame_; - } - inline size_t symbol_per_subframe(void) const - { - return this->symbol_per_subframe_; - } - inline bool ul_data_sym_present(void) const - { - return this->ul_data_sym_present_; + return this->ul_data_slot_present_; } inline size_t num_cells(void) const { return this->num_cells_; } inline bool hw_framer(void) const { return this->hw_framer_; } @@ -71,17 +62,17 @@ class Config { { return this->symbol_data_subcarrier_num_; } - inline size_t pilot_syms_per_frame(void) const + inline size_t pilot_slot_per_frame(void) const { - return this->pilot_syms_per_frame_; + return this->pilot_slot_per_frame_; } - inline size_t noise_syms_per_frame(void) const + inline size_t noise_slot_per_frame(void) const { - return this->noise_syms_per_frame_; + return this->noise_slot_per_frame_; } - inline size_t ul_syms_per_frame(void) const + inline size_t ul_slot_per_frame(void) const { - return this->ul_syms_per_frame_; + return this->ul_slot_per_frame_; } inline double rate(void) const { return this->rate_; } inline int tx_advance(void) const { return this->tx_advance_; } @@ -143,17 +134,17 @@ class Config { { return this->cl_frames_; } - inline const std::vector>& cl_pilot_symbols(void) const + inline const std::vector>& cl_pilot_slots(void) const { - return this->cl_pilot_symbols_; + return this->cl_pilot_slots_; } - inline const std::vector>& cl_ul_symbols(void) const + inline const std::vector>& cl_ul_slots(void) const { - return this->cl_ul_symbols_; + return this->cl_ul_slots_; } - inline const std::vector>& cl_dl_symbols(void) const + inline const std::vector>& cl_dl_slots(void) const { - return this->cl_dl_symbols_; + return this->cl_dl_slots_; } inline const std::vector& cl_sdr_ids(void) const { @@ -193,9 +184,9 @@ class Config { return this->beacon_; } - inline std::vector>& pilot_sym(void) + inline std::vector>& pilot_sym_t(void) { - return this->pilot_sym_; + return this->pilot_sym_t_; }; inline std::vector>& pilot_sym_f(void) { @@ -263,15 +254,15 @@ class Config { inline size_t getPackageDataLength() const { - return (2 * this->samps_per_symbol_ * sizeof(short)); + return (2 * this->samps_per_slot_ * sizeof(short)); } size_t getNumAntennas(); size_t getMaxNumAntennas(); size_t getTotNumAntennas(); int getClientId(int, int); - int getNoiseSFIndex(int, int); - int getUlSFIndex(int, int); - int getDlSFIndex(int, int); + int getNoiseSlotIndex(int, int); + int getUlSlotIndex(int, int); + int getDlSlotIndex(int, int); bool isPilot(int, int); bool isNoise(int, int); bool isData(int, int); @@ -293,20 +284,20 @@ class Config { size_t cp_size_; size_t ofdm_symbol_size_; size_t symbol_data_subcarrier_num_; - size_t symbol_per_subframe_; - size_t subframe_size_; - size_t samps_per_symbol_; + size_t symbol_per_slot_; + size_t slot_samp_size_; + size_t samps_per_slot_; size_t prefix_; size_t postfix_; - size_t symbols_per_frame_; - size_t pilot_syms_per_frame_; - size_t noise_syms_per_frame_; - size_t ul_syms_per_frame_; - size_t dl_syms_per_frame_; // No accessor + size_t slot_per_frame_; + size_t pilot_slot_per_frame_; + size_t noise_slot_per_frame_; + size_t ul_slot_per_frame_; + size_t dl_slot_per_frame_; // No accessor float tx_scale_; // No accessor std::string pilot_seq_; std::string beacon_seq_; - bool ul_data_sym_present_; + bool ul_data_slot_present_; std::string data_mod_; // BS features @@ -333,11 +324,11 @@ class Config { size_t max_frame_; size_t ul_data_frame_num_; std::vector> - pilot_symbols_; // Accessed through getClientId - std::vector> noise_symbols_; + pilot_slots_; // Accessed through getClientId + std::vector> noise_slots_; std::vector> - ul_symbols_; // Accessed through getUlSFIndex() - std::vector> dl_symbols_; // No accessor + ul_slots_; // Accessed through getUlSFIndex() + std::vector> dl_slots_; // No accessor bool single_gain_; std::vector tx_gain_; std::vector rx_gain_; @@ -366,16 +357,16 @@ class Config { std::vector pilot_; std::vector> pilot_sc_; std::vector pilot_sc_ind_; - std::vector> pilot_sym_; + std::vector> pilot_sym_t_; std::vector> pilot_sym_f_; std::vector>> tx_data_; std::vector>> txdata_freq_dom_; std::vector>> txdata_time_dom_; std::vector cl_frames_; - std::vector> cl_pilot_symbols_; - std::vector> cl_ul_symbols_; - std::vector> cl_dl_symbols_; + std::vector> cl_pilot_slots_; + std::vector> cl_ul_slots_; + std::vector> cl_dl_slots_; std::vector> cl_txgain_vec_; std::vector> cl_rxgain_vec_; diff --git a/CC/Sounder/include/constants.h b/CC/Sounder/include/constants.h index 262c71c9..55fe4fa9 100644 --- a/CC/Sounder/include/constants.h +++ b/CC/Sounder/include/constants.h @@ -2,6 +2,7 @@ class Consts { public: static constexpr size_t kFftSize_80211 = 64; + static constexpr size_t kNumMappedSubcarriers_80211 = 52; // Define freq-domain STS according to // https://standards.ieee.org/standard/802_11a-1999.html static constexpr std::complex sts_seq[64] = { { 0, 0 }, { 0, 0 }, diff --git a/CC/Sounder/include/utils.h b/CC/Sounder/include/utils.h index d3ccfbfe..3a8779d0 100644 --- a/CC/Sounder/include/utils.h +++ b/CC/Sounder/include/utils.h @@ -50,7 +50,7 @@ class Utils { static std::vector cint16_to_uint32( const std::vector>& in, bool conj, const std::string& order); - static std::vector> loadSymbols( + static std::vector> loadSlots( const std::vector& frames, char sym); static void loadDevices( const std::string& filename, std::vector& data); diff --git a/CC/Sounder/include/version_config.h.in b/CC/Sounder/include/version_config.h.in new file mode 100644 index 00000000..5c0b5bf8 --- /dev/null +++ b/CC/Sounder/include/version_config.h.in @@ -0,0 +1,12 @@ +/** + * @file version_config.h + * @brief Sounder project version configuration file + */ + +#ifndef VERSION_CONFIG_H_ +#define VERSION_CONFIG_H_ +#include + +const std::string GetSounderProjectVersion() { return "@CMAKE_PROJECT_VERSION@"; } + +#endif // VERSION_CONFIG_H_ diff --git a/CC/Sounder/main.cc b/CC/Sounder/main.cc index 28163786..d31c4855 100644 --- a/CC/Sounder/main.cc +++ b/CC/Sounder/main.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2018-2020, Rice University + Copyright (c) 2018-2021, Rice University RENEW OPEN SOURCE LICENSE: http://renew-wireless.org/license Author(s): Rahman Doost-Mohamamdy: doost@rice.edu @@ -13,17 +13,24 @@ #include "include/data_generator.h" #include "include/recorder.h" #include "include/signalHandler.hpp" +#include "include/version_config.h" #include DEFINE_bool(gen_ul_bits, false, "Generate random bits for uplink transmissions, otherwise read from file!"); DEFINE_string(conf, "files/conf.json", "JSON configuration file name"); DEFINE_string(storepath, "logs", "Dataset store path"); +DEFINE_bool(bs_only, false, "Run BS only"); +DEFINE_bool(client_only, false, "Run client only"); int main(int argc, char* argv[]) { + gflags::SetVersionString(GetSounderProjectVersion()); + gflags::SetUsageMessage( + "sounder Options: -bs_only -client_only -conf -gen_ul_bits -storepath"); gflags::ParseCommandLineFlags(&argc, &argv, true); - Config config(FLAGS_conf, FLAGS_storepath); + Config config( + FLAGS_conf, FLAGS_storepath, FLAGS_bs_only, FLAGS_client_only); int ret = EXIT_SUCCESS; if (FLAGS_gen_ul_bits) { DataGenerator dg(&config); @@ -47,5 +54,6 @@ int main(int argc, char* argv[]) ret = EXIT_FAILURE; } } + gflags::ShutDownCommandLineFlags(); return ret; } diff --git a/CC/Sounder/receiver.cc b/CC/Sounder/receiver.cc index 8cfe9759..8793a0ab 100644 --- a/CC/Sounder/receiver.cc +++ b/CC/Sounder/receiver.cc @@ -43,7 +43,7 @@ Receiver::Receiver(int n_rx_threads, Config* config, if (((this->base_radio_set_ != nullptr) && (this->base_radio_set_->getRadioNotFound())) || ((this->clientRadioSet_ != nullptr) - && (this->clientRadioSet_->getRadioNotFound()))) { + && (this->clientRadioSet_->getRadioNotFound()))) { if (this->base_radio_set_ != nullptr) { MLPD_WARN("Invalid Base Radio Setup: %d\n", this->base_radio_set_ == nullptr); @@ -214,7 +214,7 @@ void Receiver::loopRecv(int tid, int core_id, SampleBuffer* rx_buffer) // prepare BS beacon in host buffer std::vector beaconbuff(2); void* zeroes_memory - = calloc(config_->samps_per_symbol(), sizeof(int16_t) * 2); + = calloc(config_->samps_per_slot(), sizeof(int16_t) * 2); if (zeroes_memory == NULL) { throw std::runtime_error("Memory allocation error"); @@ -222,8 +222,7 @@ void Receiver::loopRecv(int tid, int core_id, SampleBuffer* rx_buffer) MLPD_SYMBOL( "Process %d -- Loop Rx Allocated memory at: %p, approx size: %lu\n", - tid, zeroes_memory, - (sizeof(int16_t) * 2) * config_->samps_per_symbol()); + tid, zeroes_memory, (sizeof(int16_t) * 2) * config_->samps_per_slot()); beaconbuff.at(0u) = config_->beacon_ci16().data(); beaconbuff.at(1u) = zeroes_memory; @@ -233,9 +232,9 @@ void Receiver::loopRecv(int tid, int core_id, SampleBuffer* rx_buffer) // read rx_offset to align the FPGA time of the BS // by performing dummy readStream() std::vector> samp_buffer0( - config_->samps_per_symbol() * config_->symbols_per_frame(), 0); + config_->samps_per_slot() * config_->slot_per_frame(), 0); std::vector> samp_buffer1( - config_->samps_per_symbol() * config_->symbols_per_frame(), 0); + config_->samps_per_slot() * config_->slot_per_frame(), 0); std::vector samp_buffer(2); samp_buffer[0] = samp_buffer0.data(); if (num_channels == 2) @@ -258,23 +257,23 @@ void Receiver::loopRecv(int tid, int core_id, SampleBuffer* rx_buffer) bs_sync_ret = -1; while (bs_sync_ret < 0) { bs_sync_ret = this->base_radio_set_->radioRx(radio_idx, cell, - samp_buffer.data(), config_->samps_per_symbol(), rxTimeBs); + samp_buffer.data(), config_->samps_per_slot(), rxTimeBs); } } } int cursor = 0; size_t frame_id = 0; - size_t symbol_id = 0; + size_t slot_id = 0; size_t ant_id = 0; cell = 0; MLPD_INFO("Start BS main recv loop in thread %d\n", tid); while (config_->running() == true) { - // Global updates of frame and symbol IDs for USRPs + // Global updates of frame and slot IDs for USRPs if (kUseUHD == true) { - if (symbol_id == config_->symbols_per_frame()) { - symbol_id = 0; + if (slot_id == config_->slot_per_frame()) { + slot_id = 0; frame_id++; } } @@ -337,27 +336,27 @@ void Receiver::loopRecv(int tid, int core_id, SampleBuffer* rx_buffer) } frame_id = (size_t)(frameTime >> 32); - symbol_id = (size_t)((frameTime >> 16) & 0xFFFF); + slot_id = (size_t)((frameTime >> 16) & 0xFFFF); if (config_->reciprocal_calib()) { if (radio_idx == config_->cal_ref_sdr_id()) { - ant_id = symbol_id < radio_idx * num_channels - ? symbol_id - : symbol_id - num_channels; - symbol_id = 0; // downlink reciprocal pilot + ant_id = slot_id < radio_idx * num_channels + ? slot_id + : slot_id - num_channels; + slot_id = 0; // downlink reciprocal pilot } else { if (radio_idx >= config_->cal_ref_sdr_id()) ant_id -= num_channels; - symbol_id = 1; // uplink reciprocal pilot + slot_id = 1; // uplink reciprocal pilot } } } else { - int rx_len = config_->samps_per_symbol(); + int rx_len = config_->samps_per_slot(); int r; // only write received pilot or data into samp // otherwise use samp_buffer as a dummy buffer - if (config_->isPilot(frame_id, symbol_id) - || config_->isData(frame_id, symbol_id)) + if (config_->isPilot(frame_id, slot_id) + || config_->isData(frame_id, slot_id)) r = this->base_radio_set_->radioRx( radio_idx, cell, samp, rxTimeBs); else @@ -376,15 +375,15 @@ void Receiver::loopRecv(int tid, int core_id, SampleBuffer* rx_buffer) // Schedule next beacon in BEACON_INTERVAL frames // FIXME?? From EACH cell or only one cell? - if (symbol_id == 0) { + if (slot_id == 0) { txTimeBs = rxTimeBs - + config_->samps_per_symbol() - * config_->symbols_per_frame() * BEACON_INTERVAL; + + config_->samps_per_slot() * config_->slot_per_frame() + * BEACON_INTERVAL; int r_tx = this->base_radio_set_->radioTx(radio_idx, cell, beaconbuff.data(), kStreamEndBurst, txTimeBs); - if (r_tx != (int)config_->samps_per_symbol()) + if (r_tx != (int)config_->samps_per_slot()) std::cerr << "BAD Transmit(" << r_tx << "/" - << config_->samps_per_symbol() << ") at Time " + << config_->samps_per_slot() << ") at Time " << txTimeBs << ", frame count " << frame_id << std::endl; } @@ -392,19 +391,18 @@ void Receiver::loopRecv(int tid, int core_id, SampleBuffer* rx_buffer) #if DEBUG_PRINT for (size_t ch = 0; ch < num_packets; ++ch) { - printf( - "receive thread %d, frame %zu, symbol %zu, cell %zu, ant " - "%zu samples: %d %d %d %d %d %d %d %d ...\n", - tid, frame_id, symbol_id, cell, ant_id + ch, - pkg[ch]->data[1], pkg[ch]->data[2], pkg[ch]->data[3], - pkg[ch]->data[4], pkg[ch]->data[5], pkg[ch]->data[6], - pkg[ch]->data[7], pkg[ch]->data[8]); + printf("receive thread %d, frame %zu, slot %zu, cell %zu, ant " + "%zu samples: %d %d %d %d %d %d %d %d ...\n", + tid, frame_id, slot_id, cell, ant_id + ch, pkg[ch]->data[1], + pkg[ch]->data[2], pkg[ch]->data[3], pkg[ch]->data[4], + pkg[ch]->data[5], pkg[ch]->data[6], pkg[ch]->data[7], + pkg[ch]->data[8]); } #endif for (size_t ch = 0; ch < num_packets; ++ch) { - // new (pkg[ch]) Package(frame_id, symbol_id, 0, ant_id + ch); - new (pkg[ch]) Package(frame_id, symbol_id, cell, ant_id + ch); + // new (pkg[ch]) Package(frame_id, slot_id, 0, ant_id + ch); + new (pkg[ch]) Package(frame_id, slot_id, cell, ant_id + ch); // push kEventRxSymbol event into the queue Event_data package_message; package_message.event_type = kEventRxSymbol; @@ -422,9 +420,9 @@ void Receiver::loopRecv(int tid, int core_id, SampleBuffer* rx_buffer) } } - // for UHD device update symbol_id on host + // for UHD device update slot_id on host if (kUseUHD == true) { - symbol_id++; + slot_id++; } } MLPD_SYMBOL( @@ -447,17 +445,17 @@ void* Receiver::clientTxRx_launch(void* in_context) void Receiver::clientTxRx(int tid) { - int txSyms = config_->cl_ul_symbols().at(tid).size(); - int rxSyms = config_->cl_dl_symbols().at(tid).size(); - int txStartSym = config_->cl_ul_symbols().at(tid).empty() + int tx_slots = config_->cl_ul_slots().at(tid).size(); + int rxSyms = config_->cl_dl_slots().at(tid).size(); + int txStartSym = config_->cl_ul_slots().at(tid).empty() ? 0 - : config_->cl_ul_symbols().at(tid).at(0); + : config_->cl_ul_slots().at(tid).at(0); - double frameTime = config_->samps_per_symbol() + double frameTime = config_->samps_per_slot() * config_->cl_frames().at(0).size() * 1e3 / config_->rate(); // miliseconds unsigned txFrameDelta = (unsigned)(std::ceil(TIME_DELTA / frameTime)); - int NUM_SAMPS = config_->samps_per_symbol(); + int NUM_SAMPS = config_->samps_per_slot(); if (config_->core_alloc() == true) { int core @@ -478,12 +476,12 @@ void Receiver::clientTxRx(int tid) rxbuff[1] = buffs.data(); std::vector txbuff(2); - if (txSyms > 0) { + if (tx_slots > 0) { size_t txIndex = tid * config_->cl_sdr_ch(); txbuff[0] = config_->txdata_time_dom().at(txIndex).data(); if (config_->cl_sdr_ch() == 2) txbuff[1] = config_->txdata_time_dom().at(txIndex + 1).data(); - std::cout << txSyms << " uplink symbols will be sent per frame..." + std::cout << tx_slots << " uplink slots will be sent per frame..." << std::endl; } @@ -527,7 +525,7 @@ void Receiver::clientTxRx(int tid) txTime += ((long long)txFrameDelta << 32); txTime += ((long long)txStartSym << 16); //printf("rxTime %llx, txTime %llx \n", firstRxTime, txTime); - for (int i = 0; i < txSyms; i++) { + for (int i = 0; i < tx_slots; i++) { int r = clientRadioSet_->radioTx( tid, txbuff.data(), NUM_SAMPS, 1, txTime); if (r == NUM_SAMPS) { @@ -554,7 +552,7 @@ void Receiver::clientSyncTxRx(int tid) } size_t frameTimeLen - = config_->samps_per_symbol() * config_->cl_frames().at(0).size(); + = config_->samps_per_slot() * config_->cl_frames().at(0).size(); size_t txFrameDelta = std::ceil(TIME_DELTA / (1e3 * frameTimeLen / config_->rate())); size_t txTimeDelta = txFrameDelta * frameTimeLen; @@ -562,9 +560,8 @@ void Receiver::clientSyncTxRx(int tid) MLPD_INFO("Scheduling TX: %zu Frames (%lf ms) in the future!\n", txFrameDelta, ((1e3 * txTimeDelta) / config_->rate())); - int NUM_SAMPS = config_->samps_per_symbol(); - int SYNC_NUM_SAMPS - = config_->samps_per_symbol() * config_->symbols_per_frame(); + int NUM_SAMPS = config_->samps_per_slot(); + int SYNC_NUM_SAMPS = config_->samps_per_slot() * config_->slot_per_frame(); std::vector> syncbuff0(SYNC_NUM_SAMPS, 0); std::vector> syncbuff1(SYNC_NUM_SAMPS, 0); @@ -596,22 +593,22 @@ void Receiver::clientSyncTxRx(int tid) std::vector txbuff(2); for (size_t ch = 0; ch < config_->cl_sdr_ch(); ch++) { txbuff.at(ch) - = std::calloc(config_->samps_per_symbol(), sizeof(float) * 2); + = std::calloc(config_->samps_per_slot(), sizeof(float) * 2); } - size_t slot_byte_size = config_->samps_per_symbol() * sizeof(float) * 2; - size_t txSyms = config_->cl_ul_symbols().at(tid).size(); - if (txSyms > 0) { + size_t slot_byte_size = config_->samps_per_slot() * sizeof(float) * 2; + size_t tx_slots = config_->cl_ul_slots().at(tid).size(); + if (tx_slots > 0) { size_t txIndex = tid * config_->cl_sdr_ch(); for (size_t ch = 0; ch < config_->cl_sdr_ch(); ch++) { std::memcpy(txbuff.at(ch), config_->txdata_time_dom().at(txIndex + ch).data(), slot_byte_size); } - MLPD_INFO("%zu uplink symbols will be sent per frame...\n", txSyms); + MLPD_INFO("%zu uplink slots will be sent per frame...\n", tx_slots); } FILE* fp = nullptr; - if (config_->ul_data_sym_present() == true) { + if (config_->ul_data_slot_present() == true) { std::printf("Opening UL time-domain data for radio %d to %s\n", tid, config_->tx_td_data_files().at(tid).c_str()); fp = std::fopen(config_->tx_td_data_files().at(tid).c_str(), "rb"); @@ -688,7 +685,7 @@ void Receiver::clientSyncTxRx(int tid) int flagsTxUlData; while (config_->running() == true) { - for (size_t sf = 0; sf < config_->symbols_per_frame(); sf++) { + for (size_t sf = 0; sf < config_->slot_per_frame(); sf++) { int rx_len = (sf == 0) ? (NUM_SAMPS + rx_offset) : NUM_SAMPS; assert((rx_len > 0) && (rx_len < SYNC_NUM_SAMPS)); int r = clientRadioSet_->radioRx( @@ -701,7 +698,7 @@ void Receiver::clientSyncTxRx(int tid) MLPD_WARN("BAD Receive(%d/%d) at Time %lld, frame count %zu\n", r, rx_len, rxTime, frame_cnt); } - // schedule all TX subframes + // schedule all TX slot if (sf == 0) { // resync every X=1000 frames: // TODO: X should be a function of sample rate and max CFO @@ -749,7 +746,7 @@ void Receiver::clientSyncTxRx(int tid) // config_->tx_advance() needs calibration based on SDR model and sampling rate txTime = rxTime + txTimeDelta - + config_->cl_pilot_symbols().at(tid).at(0) * NUM_SAMPS + + config_->cl_pilot_slots().at(tid).at(0) * NUM_SAMPS - config_->tx_advance(); r = clientRadioSet_->radioTx( @@ -759,7 +756,7 @@ void Receiver::clientSyncTxRx(int tid) } if (config_->cl_sdr_ch() == 2) { txTime = rxTime + txTimeDelta - + config_->cl_pilot_symbols().at(tid).at(1) * NUM_SAMPS + + config_->cl_pilot_slots().at(tid).at(1) * NUM_SAMPS - config_->tx_advance(); r = clientRadioSet_->radioTx(tid, pilotbuffB.data(), @@ -768,21 +765,21 @@ void Receiver::clientSyncTxRx(int tid) MLPD_WARN("BAD Write: %d/%d\n", r, NUM_SAMPS); } } - if (config_->ul_data_sym_present() == true) { - for (size_t s = 0; s < txSyms; s++) { + if (config_->ul_data_slot_present() == true) { + for (size_t s = 0; s < tx_slots; s++) { txTime = rxTime + txTimeDelta - + config_->cl_ul_symbols().at(tid).at(s) * NUM_SAMPS + + config_->cl_ul_slots().at(tid).at(s) * NUM_SAMPS - config_->tx_advance(); for (size_t ch = 0; ch < config_->cl_sdr_ch(); ch++) { size_t read_num = std::fread(txbuff.at(ch), 2 * sizeof(float), - config_->samps_per_symbol(), fp); - if (read_num != config_->samps_per_symbol()) { + config_->samps_per_slot(), fp); + if (read_num != config_->samps_per_slot()) { MLPD_WARN("BAD Uplink Data Read: %zu/%zu\n", - read_num, config_->samps_per_symbol()); + read_num, config_->samps_per_slot()); } } - if (kUseUHD && s < (txSyms - 1)) + if (kUseUHD && s < (tx_slots - 1)) flagsTxUlData = 1; // HAS_TIME else flagsTxUlData = 2; // HAS_TIME & END_BURST, fixme @@ -794,12 +791,12 @@ void Receiver::clientSyncTxRx(int tid) } // end for if (frame_cnt % config_->ul_data_frame_num() == 0) std::fseek(fp, 0, SEEK_SET); - } // end if config_->ul_data_sym_present() + } // end if config_->ul_data_slot_present() } // end if sf == 0 } // end for frame_cnt++; } // end while - if (config_->ul_data_sym_present() == true || fp != nullptr) { + if (config_->ul_data_slot_present() == true || fp != nullptr) { std::fclose(fp); } diff --git a/CC/Sounder/recorder.cc b/CC/Sounder/recorder.cc index 4a64c093..9d93f2b0 100644 --- a/CC/Sounder/recorder.cc +++ b/CC/Sounder/recorder.cc @@ -35,7 +35,7 @@ Recorder::Recorder(Config* in_cfg, unsigned int core_start) ? cfg_->getTotNumAntennas() / rx_thread_num : 1; rx_thread_buff_size_ - = kSampleBufferFrameNum * cfg_->symbols_per_frame() * ant_per_rx_thread; + = kSampleBufferFrameNum * cfg_->slot_per_frame() * ant_per_rx_thread; message_queue_ = moodycamel::ConcurrentQueue( rx_thread_buff_size_ * kQueueSize); diff --git a/CC/Sounder/recorder_worker.cc b/CC/Sounder/recorder_worker.cc index 385f058e..7eb34bac 100644 --- a/CC/Sounder/recorder_worker.cc +++ b/CC/Sounder/recorder_worker.cc @@ -215,30 +215,30 @@ herr_t RecorderWorker::initHDF5() MLPD_INFO("Creating output HD5F file: %s\n", this->hdf5_name_.c_str()); // dataset dimension - hsize_t IQ = 2 * this->cfg_->samps_per_symbol(); + hsize_t IQ = 2 * this->cfg_->samps_per_slot(); DataspaceIndex cdims = { 1, 1, 1, 1, IQ }; // pilot chunk size, TODO: optimize size this->frame_number_pilot_ = MAX_FRAME_INC; // pilots DataspaceIndex dims_pilot = { this->frame_number_pilot_, this->cfg_->num_cells(), - this->cfg_->pilot_syms_per_frame(), this->num_antennas_, IQ }; + this->cfg_->pilot_slot_per_frame(), this->num_antennas_, IQ }; DataspaceIndex max_dims_pilot = { H5S_UNLIMITED, this->cfg_->num_cells(), - this->cfg_->pilot_syms_per_frame(), this->num_antennas_, IQ }; + this->cfg_->pilot_slot_per_frame(), this->num_antennas_, IQ }; // noise this->frame_number_noise_ = MAX_FRAME_INC; DataspaceIndex dims_noise = { this->frame_number_noise_, this->cfg_->num_cells(), - this->cfg_->noise_syms_per_frame(), this->num_antennas_, IQ }; + this->cfg_->noise_slot_per_frame(), this->num_antennas_, IQ }; DataspaceIndex max_dims_noise = { H5S_UNLIMITED, this->cfg_->num_cells(), - this->cfg_->noise_syms_per_frame(), this->num_antennas_, IQ }; + this->cfg_->noise_slot_per_frame(), this->num_antennas_, IQ }; // data this->frame_number_data_ = MAX_FRAME_INC; DataspaceIndex dims_data = { this->frame_number_data_, this->cfg_->num_cells(), - this->cfg_->ul_syms_per_frame(), this->num_antennas_, IQ }; + this->cfg_->ul_slot_per_frame(), this->num_antennas_, IQ }; DataspaceIndex max_dims_data = { H5S_UNLIMITED, this->cfg_->num_cells(), - this->cfg_->ul_syms_per_frame(), this->num_antennas_, IQ }; + this->cfg_->ul_slot_per_frame(), this->num_antennas_, IQ }; try { H5::Exception::dontPrint(); @@ -258,10 +258,6 @@ herr_t RecorderWorker::initHDF5() // BW write_attribute(mainGroup, "RATE", this->cfg_->rate()); - // Number of samples on each symbol (excluding prefix/postfix) - write_attribute( - mainGroup, "SYMBOL_LEN_NO_PAD", this->cfg_->subframe_size()); - // Number of samples for prefix (padding) write_attribute(mainGroup, "PREFIX_LEN", this->cfg_->prefix()); @@ -270,7 +266,7 @@ herr_t RecorderWorker::initHDF5() // Number of samples on each symbol including prefix and postfix write_attribute( - mainGroup, "SYMBOL_LEN", this->cfg_->samps_per_symbol()); + mainGroup, "SLOT_SAMP_LEN", this->cfg_->samps_per_slot()); // Size of FFT write_attribute(mainGroup, "FFT_SIZE", this->cfg_->fft_size()); @@ -356,10 +352,10 @@ herr_t RecorderWorker::initHDF5() // Number of symbols in a frame write_attribute( - mainGroup, "BS_FRAME_LEN", this->cfg_->symbols_per_frame()); + mainGroup, "BS_FRAME_LEN", this->cfg_->slot_per_frame()); // Number of uplink symbols per frame - write_attribute(mainGroup, "UL_SYMS", this->cfg_->ul_syms_per_frame()); + write_attribute(mainGroup, "UL_SLOTS", this->cfg_->ul_slot_per_frame()); // Reciprocal Calibration Mode write_attribute(mainGroup, "RECIPROCAL_CALIB", @@ -379,16 +375,16 @@ herr_t RecorderWorker::initHDF5() // Time Domain Pilot symbols std::vector split_vec_pilot( - 2 * this->cfg_->pilot_sym().at(0).size()); - for (size_t i = 0; i < this->cfg_->pilot_sym().at(0).size(); i++) { - split_vec_pilot[2 * i + 0] = this->cfg_->pilot_sym().at(0).at(i); - split_vec_pilot[2 * i + 1] = this->cfg_->pilot_sym().at(1).at(i); + 2 * this->cfg_->pilot_sym_t().at(0).size()); + for (size_t i = 0; i < this->cfg_->pilot_sym_t().at(0).size(); i++) { + split_vec_pilot[2 * i + 0] = this->cfg_->pilot_sym_t().at(0).at(i); + split_vec_pilot[2 * i + 1] = this->cfg_->pilot_sym_t().at(1).at(i); } write_attribute(mainGroup, "OFDM_PILOT", split_vec_pilot); // Number of Pilots write_attribute( - mainGroup, "PILOT_NUM", this->cfg_->pilot_syms_per_frame()); + mainGroup, "PILOT_NUM", this->cfg_->pilot_slot_per_frame()); // Number of Client Antennas write_attribute(mainGroup, "CL_NUM", this->cfg_->num_cl_antennas()); @@ -396,7 +392,7 @@ herr_t RecorderWorker::initHDF5() // Data modulation write_attribute(mainGroup, "CL_MODULATION", this->cfg_->data_mod()); - if (this->cfg_->client_present() == true) { + if (this->cfg_->reciprocal_calib() == false) { // Client antenna polarization write_attribute( mainGroup, "CL_CH_PER_RADIO", this->cfg_->cl_sdr_ch()); @@ -439,7 +435,7 @@ herr_t RecorderWorker::initHDF5() write_attribute(mainGroup, "CL_SDR_ID", this->cfg_->cl_sdr_ids()); } - if (this->cfg_->ul_data_sym_present()) { + if (this->cfg_->ul_data_slot_present()) { // Data subcarriers if (this->cfg_->data_ind().size() > 0) write_attribute( @@ -472,7 +468,7 @@ herr_t RecorderWorker::initHDF5() // ********************* // this->pilot_prop_.close(); - if (this->cfg_->noise_syms_per_frame() > 0) { + if (this->cfg_->noise_slot_per_frame() > 0) { H5::DataSpace noise_dataspace(kDsDim, dims_noise, max_dims_noise); this->noise_prop_.setChunk(kDsDim, cdims); this->file_->createDataSet("/Data/Noise_Samples", @@ -480,7 +476,7 @@ herr_t RecorderWorker::initHDF5() this->noise_prop_.close(); } - if (this->cfg_->ul_syms_per_frame() > 0) { + if (this->cfg_->ul_slot_per_frame() > 0) { H5::DataSpace data_dataspace(kDsDim, dims_data, max_dims_data); this->data_prop_.setChunk(kDsDim, cdims); this->file_->createDataSet("/Data/UplinkData", @@ -524,12 +520,12 @@ void RecorderWorker::openHDF5() this->pilot_prop_.copy(this->pilot_dataset_->getCreatePlist()); #if DEBUG_PRINT - hsize_t IQ = 2 * this->cfg_->samps_per_symbol(); + hsize_t IQ = 2 * this->cfg_->samps_per_slot(); int cndims_pilot = 0; int ndims = pilot_filespace.getSimpleExtentNdims(); DataspaceIndex dims_pilot = { this->frame_number_pilot_, this->cfg_->num_cells(), - this->cfg_->pilot_syms_per_frame(), this->num_antennas(), IQ }; + this->cfg_->pilot_slot_per_frame(), this->num_antennas(), IQ }; if (H5D_CHUNKED == this->pilot_prop_.getLayout()) cndims_pilot = this->pilot_prop_.getChunk(ndims, dims_pilot); using std::cout; @@ -541,7 +537,7 @@ void RecorderWorker::openHDF5() #endif pilot_filespace.close(); // Get Dataset for DATA (If Enabled) and check the shape of it - if (this->cfg_->ul_syms_per_frame() > 0) { + if (this->cfg_->ul_slot_per_frame() > 0) { this->data_dataset_ = new H5::DataSet(this->file_->openDataSet("/Data/UplinkData")); @@ -559,7 +555,7 @@ void RecorderWorker::openHDF5() cout << "dim data chunk = " << cndims_data << std::endl; DataspaceIndex dims_data = { this->frame_number_data_, this->cfg_->num_cells(), - this->cfg_->ul_syms_per_frame(), this->num_antennas(), IQ }; + this->cfg_->ul_slot_per_frame(), this->num_antennas(), IQ }; cout << "New Data Dataset Dimension " << ndims << ","; for (auto i = 0; i < kDsSim - 1; ++i) cout << dims_data[i] << ","; @@ -569,7 +565,7 @@ void RecorderWorker::openHDF5() } // Get Dataset for NOISE (If Enabled) and check the shape of it - if (this->cfg_->noise_syms_per_frame() > 0) { + if (this->cfg_->noise_slot_per_frame() > 0) { this->noise_dataset_ = new H5::DataSet(this->file_->openDataSet("/Data/Noise_Samples")); H5::DataSpace noise_filespace(this->noise_dataset_->getSpace()); @@ -584,7 +580,7 @@ void RecorderWorker::openHDF5() cndims_noise = this->noise_prop_.getChunk(ndims, cdims_noise); cout << "dim noise chunk = " << cndims_noise << std::endl; DataspaceIndex dims_noise = { this->frame_number_noise_, - this->cfg_->num_cells(), this->cfg_->noise_syms_per_frame(), + this->cfg_->num_cells(), this->cfg_->noise_slot_per_frame(), this->antennas_.size(), IQ }; cout << "New Noise Dataset Dimension " << ndims << ","; for (auto i = 0; i < kDsSim - 1; ++i) @@ -604,14 +600,14 @@ void RecorderWorker::closeHDF5() this->hdf5_name_.c_str()); } else { unsigned frame_number = this->max_frame_number_; - hsize_t IQ = 2 * this->cfg_->samps_per_symbol(); + hsize_t IQ = 2 * this->cfg_->samps_per_slot(); assert(this->pilot_dataset_ != nullptr); // Resize Pilot Dataset this->frame_number_pilot_ = frame_number; DataspaceIndex dims_pilot = { this->frame_number_pilot_, this->cfg_->num_cells(), - this->cfg_->pilot_syms_per_frame(), this->num_antennas_, IQ }; + this->cfg_->pilot_slot_per_frame(), this->num_antennas_, IQ }; this->pilot_dataset_->extend(dims_pilot); this->pilot_prop_.close(); this->pilot_dataset_->close(); @@ -619,11 +615,11 @@ void RecorderWorker::closeHDF5() this->pilot_dataset_ = nullptr; // Resize Data Dataset (If Needed) - if (this->cfg_->ul_syms_per_frame() > 0) { + if (this->cfg_->ul_slot_per_frame() > 0) { assert(this->data_dataset_ != nullptr); this->frame_number_data_ = frame_number; DataspaceIndex dims_data = { this->frame_number_data_, - this->cfg_->num_cells(), this->cfg_->ul_syms_per_frame(), + this->cfg_->num_cells(), this->cfg_->ul_slot_per_frame(), this->num_antennas_, IQ }; this->data_dataset_->extend(dims_data); this->data_prop_.close(); @@ -633,11 +629,11 @@ void RecorderWorker::closeHDF5() } // Resize Noise Dataset (If Needed) - if (this->cfg_->noise_syms_per_frame() > 0) { + if (this->cfg_->noise_slot_per_frame() > 0) { assert(this->noise_dataset_ != nullptr); this->frame_number_noise_ = frame_number; DataspaceIndex dims_noise = { this->frame_number_noise_, - this->cfg_->num_cells(), this->cfg_->noise_syms_per_frame(), + this->cfg_->num_cells(), this->cfg_->noise_slot_per_frame(), this->num_antennas_, IQ }; this->noise_dataset_->extend(dims_noise); this->noise_prop_.close(); @@ -678,7 +674,7 @@ herr_t RecorderWorker::record(int tid, Package* pkg) pkg->data[2], pkg->data[3], pkg->data[4], pkg->data[5], pkg->data[6], pkg->data[7], pkg->data[8]); #endif - hsize_t IQ = 2 * this->cfg_->samps_per_symbol(); + hsize_t IQ = 2 * this->cfg_->samps_per_slot(); if ((this->cfg_->max_frame()) != 0 && (pkg->frame_id > this->cfg_->max_frame())) { closeHDF5(); @@ -702,7 +698,7 @@ herr_t RecorderWorker::record(int tid, Package* pkg) = { pkg->frame_id, pkg->cell_id, 0, antenna_index, 0 }; if ((this->cfg_->reciprocal_calib() == true) || (this->cfg_->isPilot(pkg->frame_id, pkg->symbol_id) - == true)) { + == true)) { assert(this->pilot_dataset_ != nullptr); // Are we going to extend the dataset? if (pkg->frame_id >= this->frame_number_pilot_) { @@ -714,7 +710,7 @@ herr_t RecorderWorker::record(int tid, Package* pkg) } DataspaceIndex dims_pilot = { this->frame_number_pilot_, this->cfg_->num_cells(), - this->cfg_->pilot_syms_per_frame(), + this->cfg_->pilot_slot_per_frame(), this->num_antennas_, IQ }; this->pilot_dataset_->extend(dims_pilot); #if DEBUG_PRINT @@ -749,7 +745,7 @@ herr_t RecorderWorker::record(int tid, Package* pkg) this->cfg_->max_frame() + 1); DataspaceIndex dims_data = { this->frame_number_data_, this->cfg_->num_cells(), - this->cfg_->ul_syms_per_frame(), + this->cfg_->ul_slot_per_frame(), this->num_antennas_, IQ }; this->data_dataset_->extend(dims_data); #if DEBUG_PRINT @@ -759,7 +755,7 @@ herr_t RecorderWorker::record(int tid, Package* pkg) #endif } hdfoffset[kDsSymsPerFrame] - = this->cfg_->getUlSFIndex(pkg->frame_id, pkg->symbol_id); + = this->cfg_->getUlSlotIndex(pkg->frame_id, pkg->symbol_id); // Select a hyperslab in extended portion of the dataset H5::DataSpace data_filespace(this->data_dataset_->getSpace()); DataspaceIndex count = { 1, 1, 1, 1, IQ }; @@ -783,7 +779,7 @@ herr_t RecorderWorker::record(int tid, Package* pkg) this->cfg_->max_frame() + 1); DataspaceIndex dims_noise = { this->frame_number_noise_, this->cfg_->num_cells(), - this->cfg_->noise_syms_per_frame(), + this->cfg_->noise_slot_per_frame(), this->num_antennas_, IQ }; this->noise_dataset_->extend(dims_noise); #if DEBUG_PRINT @@ -792,7 +788,7 @@ herr_t RecorderWorker::record(int tid, Package* pkg) << this->frame_number_noise_ << " Frames" << std::endl; #endif } - hdfoffset[kDsSymsPerFrame] = this->cfg_->getNoiseSFIndex( + hdfoffset[kDsSymsPerFrame] = this->cfg_->getNoiseSlotIndex( pkg->frame_id, pkg->symbol_id); // Select a hyperslab in extended portion of the dataset H5::DataSpace noise_filespace(this->noise_dataset_->getSpace()); @@ -824,7 +820,7 @@ herr_t RecorderWorker::record(int tid, Package* pkg) pkg->ant_id, IQ); DataspaceIndex dims_pilot = { this->frame_number_pilot_, - this->cfg_->num_cells(), this->cfg_->pilot_syms_per_frame(), + this->cfg_->num_cells(), this->cfg_->pilot_slot_per_frame(), this->num_antennas_, IQ }; int ndims = this->data_dataset_->getSpace().getSimpleExtentNdims(); diff --git a/CC/Sounder/signalHandler.cpp b/CC/Sounder/signalHandler.cpp index f62a3806..d703c190 100644 --- a/CC/Sounder/signalHandler.cpp +++ b/CC/Sounder/signalHandler.cpp @@ -1,7 +1,7 @@ // http://www.yolinux.com/TUTORIALS/C++Signals.html -#include #include +#include #include "include/signalHandler.hpp" @@ -10,25 +10,18 @@ bool SignalHandler::mbGotExitSignal = false; /** * Default Contructor. */ -SignalHandler::SignalHandler() -{ -} +SignalHandler::SignalHandler() {} /** * Destructor. */ -SignalHandler::~SignalHandler() -{ -} +SignalHandler::~SignalHandler() {} /** * Returns the bool flag indicating whether we received an exit signal * @return Flag indicating shutdown of program */ -bool SignalHandler::gotExitSignal() -{ - return mbGotExitSignal; -} +bool SignalHandler::gotExitSignal() { return mbGotExitSignal; } /** * Sets the bool flag indicating whether we received an exit signal @@ -43,19 +36,14 @@ void SignalHandler::setExitSignal(bool _bExitSignal) * @param[in] _ignored Not used but required by function prototype * to match required handler. */ -void SignalHandler::exitSignalHandler(int) -{ - mbGotExitSignal = true; -} +void SignalHandler::exitSignalHandler(int) { mbGotExitSignal = true; } /** * Set up the signal handlers for CTRL-C. */ void SignalHandler::setupSignalHandlers() { - if (signal((int) SIGINT, SignalHandler::exitSignalHandler) == SIG_ERR) - { + if (signal((int)SIGINT, SignalHandler::exitSignalHandler) == SIG_ERR) { throw SignalException("!!!!! Error setting up signal handlers !!!!!"); } } - diff --git a/CC/Sounder/utils.cc b/CC/Sounder/utils.cc index 8646dde6..ca8f0915 100644 --- a/CC/Sounder/utils.cc +++ b/CC/Sounder/utils.cc @@ -110,21 +110,21 @@ std::vector Utils::cint16_to_uint32( return out; } -std::vector> Utils::loadSymbols( - const std::vector& frames, char sym) +std::vector> Utils::loadSlots( + const std::vector& frames, char s) { - std::vector> symId; + std::vector> slotId; size_t frameSize = frames.size(); - symId.resize(frameSize); + slotId.resize(frameSize); for (size_t f = 0; f < frameSize; f++) { std::string fr = frames[f]; for (size_t g = 0; g < fr.size(); g++) { - if (fr[g] == sym) { - symId[f].push_back(g); + if (fr[g] == s) { + slotId[f].push_back(g); } } } - return symId; + return slotId; } void Utils::loadDevices( @@ -133,13 +133,29 @@ void Utils::loadDevices( std::string line; std::ifstream myfile(filename, std::ifstream::in); if (myfile.is_open()) { + size_t num_dev = 0; while (getline(myfile, line)) { - //line.erase( std::remove (line.begin(), line.end(), ' '), line.end()); - if (line.at(0) == '#') + std::string item; + bool word_found = false; + for (char const& ch : line) { + if (!word_found && ch == ' ') + continue; + else if (word_found && ch == ' ') + break; + else { + word_found = true; + item += ch; + } + } + if (item.empty() || item.at(0) == '#') { continue; - data.push_back(line); - std::cout << line << '\n'; + } + data.push_back(item); + std::cout << item << '\n'; + num_dev++; } + std::cout << "Number of valid devices loaded from " << filename << ": " + << num_dev << std::endl; myfile.close(); } diff --git a/PYTHON/DEMOS/MMIMO_DOWNLINK.py b/PYTHON/DEMOS/MMIMO_DOWNLINK.py deleted file mode 100644 index 7204ebe0..00000000 --- a/PYTHON/DEMOS/MMIMO_DOWNLINK.py +++ /dev/null @@ -1,751 +0,0 @@ -#!/usr/bin/python3 -""" - - MMIMO_DOWNLINK.py - - This script demonstrates a (non real-time) downlink beamforming operation. - Beamforming is peformed during 2 consecutive frame bursts! In the first frame - a reciprocity measurement among BS antennas takes place and then uplink pilots - are sent from clients. If successful, downlink beamforming vectors are computed - and downlink precoding and IFFT is performed. In the second frame, the precoded - downlink data is transmitted and received by the client(s). If successful, - equalization is done for each received client stream. - - NOTE ON GAINS: - Gain settings will vary depending on RF frontend board being used - If using CBRS: - rxgain: at 2.5GHz [3:1:105], at 3.6GHz [3:1:102] - txgain: at 2.5GHz [16:1:81], at 3.6GHz [15:1:81] - - If using only Dev Board: - rxgain: at both frequency bands [0:1:30] - txgain: at both frequency bands [0:1:42] - - - Example Usage: - python3 MMIMO_DOWNLINK.py --bnodes="../IrisUtils/data_in/bs_serials.txt" - --cnodes="../IrisUtils/data_in/cl_serials.txt" - - We have added a couple of files listing serial numbers of - base station nodes, as well as client nodes. These need to - be modified according to the nodes being used for each experiment - ---------------------------------------------------------------------- - Copyright © 2018-2019. Rice University. - RENEW OPEN SOURCE LICENSE: http://renew-wireless.org/license ---------------------------------------------------------------------- -""" - -import sys -sys.path.append('../IrisUtils/') -sys.path.append('../IrisUtils/data_in/') -import numpy as np -from optparse import OptionParser -import SoapySDR -from SoapySDR import * #SOAPY_SDR_ constants -import time -import math -import datetime -import json -import signal -from scipy.linalg import hadamard -import scipy.io as sio -from array import array -import matplotlib -matplotlib.rcParams.update({'font.size': 10}) -import matplotlib.pyplot as plt -from matplotlib import animation -from find_lts import * -from type_conv import cfloat2uint32 -from ofdmtxrx import * - -plt.style.use('ggplot') # customize your plots style - -TDD_CONF_REG = 120 -SCH_ADDR_REG = 136 -SCH_MODE_REG = 140 - -RUNNING = True -TOT_FRAMES = 1000 -LTS_THRESH = 0.8 - - -def signal_handler(signum, frame): - global RUNNING - RUNNING = False - - -def init(hub, bnodes, cnodes, ref_ant, ampl, rate, freq, txgain, rxgain, cp, plotter, numSamps, prefix_length, postfix_length, tx_advance, mod_order, threshold, use_trig): - if hub != "": hub_dev = SoapySDR.Device(dict(driver="remote", serial = hub)) # device that triggers bnodes and ref_node - bsdrs = [SoapySDR.Device(dict(driver="iris", serial = serial)) for serial in bnodes] # base station sdrs - csdrs = [SoapySDR.Device(dict(driver="iris", serial = serial)) for serial in cnodes] # client sdrs - # assume trig_sdr is part of the master nodes - trig_dev = None - if hub != "": - trig_dev = hub_dev - else: - trig_dev = bsdrs[0] - - #set params on both channels - for sdr in bsdrs+csdrs: - info = sdr.getHardwareInfo() - print("%s settings on device" % (info["frontend"])) - for ch in [0, 1]: - sdr.setBandwidth(SOAPY_SDR_TX, ch, 2.5*rate) - sdr.setBandwidth(SOAPY_SDR_RX, ch, 2.5*rate) - sdr.setSampleRate(SOAPY_SDR_TX, ch, rate) - sdr.setSampleRate(SOAPY_SDR_RX, ch, rate) - # sdr.setFrequency(SOAPY_SDR_TX, ch, freq) - # sdr.setFrequency(SOAPY_SDR_RX, ch, freq) - sdr.setFrequency(SOAPY_SDR_TX, ch, 'RF', freq-.75*rate) - sdr.setFrequency(SOAPY_SDR_RX, ch, 'RF', freq-.75*rate) - sdr.setFrequency(SOAPY_SDR_TX, ch, 'BB', .75*rate) - sdr.setFrequency(SOAPY_SDR_RX, ch, 'BB', .75*rate) - sdr.setAntenna(SOAPY_SDR_RX, ch, "TRX") - sdr.setDCOffsetMode(SOAPY_SDR_RX, ch, True) - - sdr.setGain(SOAPY_SDR_TX, ch, 'PAD', txgain) - sdr.setGain(SOAPY_SDR_RX, ch, 'LNA', rxgain) - - if "CBRS" in info["frontend"]: - sdr.setGain(SOAPY_SDR_TX, ch, 'ATTN', -6) - sdr.setGain(SOAPY_SDR_RX, ch, 'LNA2', 14) - if freq < 3e9: - sdr.setGain(SOAPY_SDR_RX, ch, 'ATTN', -12) - else: - sdr.setGain(SOAPY_SDR_RX, ch, 'ATTN', 0) - - # Read initial gain settings - readLNA = sdr.getGain(SOAPY_SDR_RX, 0, 'LNA') - readTIA = sdr.getGain(SOAPY_SDR_RX, 0, 'TIA') - readPGA = sdr.getGain(SOAPY_SDR_RX, 0, 'PGA') - print("INITIAL GAIN - LNA: {}, \t TIA:{}, \t PGA:{}".format(readLNA, readTIA, readPGA)) - - sdr.writeSetting("RESET_DATA_LOGIC", "") - sdr.writeSetting(SOAPY_SDR_RX, 1, 'ENABLE_CHANNEL', 'false') - sdr.writeSetting(SOAPY_SDR_TX, 1, 'ENABLE_CHANNEL', 'false') - - trig_dev.writeSetting("SYNC_DELAYS", "") - - sym_samps = numSamps + prefix_length + postfix_length - print("numSamps = %d"%sym_samps) - M = len(bsdrs) - K = len(csdrs) - N = 64 - D = 1 # number of downlink symbols - - pad1 = np.array([0]*(prefix_length), np.complex64) # to comprensate for front-end group delay - pad2 = np.array([0]*(postfix_length), np.complex64) # to comprensate for rf path delay - wbz = np.array([0]*(sym_samps), np.complex64) - # OFDM object - ofdm_obj = ofdmTxRx() - - - #### Generate Pilot - cp_len = 32 if cp else 0 - ofdm_len = 2*N + cp_len - lts_rep = numSamps//(ofdm_len) - zeros = np.array([0]*(numSamps-ofdm_len)) - lts_sym, lts_f = generate_training_seq(preamble_type='lts', cp=cp_len, upsample=1) - pilot = np.concatenate((lts_sym, zeros)) - wb_pilot = ampl * pilot - wb_pilot1 = np.concatenate([pad1, wb_pilot, pad2]) - lts_t = lts_sym[-64:] - lts_t_cp = np.concatenate((lts_t[len(lts_t) - 16:], lts_t)) - - #### Generate Beacon and hadamard weights - upsample = 1 - preambles_bs = generate_training_seq(preamble_type='gold_ifft', seq_length=128, cp=0, upsample=upsample) - preambles = preambles_bs[:,::upsample] - beacon = preambles[0,:] - coe = cfloat2uint32(np.conj(beacon), order='QI') - bcnz = np.array([0]*(sym_samps-prefix_length-len(beacon)), np.complex64) - beacon1 = np.concatenate([pad1,beacon*.5,bcnz]) - beacon2 = wbz - - possible_dim = [] - possible_dim.append(2**(np.ceil(np.log2(M)))) - h_dim = min(possible_dim) - hadamard_matrix = hadamard(h_dim) - beacon_weights = hadamard_matrix[0:M, 0:M] - beacon_weights = beacon_weights.astype(np.uint32) - - #### Generate Data - data_cp_len = 16 if cp else 0 - data_ofdm_len = N + data_cp_len - n_ofdm_syms = (numSamps // data_ofdm_len) - sig_t, data_const, tx_data, sc_idx_all, pilots_matrix = \ - ofdm_obj.generate_data(n_ofdm_syms - 2, mod_order, cp_length=data_cp_len) - data_sc = sc_idx_all[0] - pilot_sc = sc_idx_all[1] - tx_dl_data = np.zeros((N, n_ofdm_syms, K)).astype(complex) - for k in range(K): - tx_dl_data[data_sc, 2:, k] = data_const - tx_dl_data[pilot_sc, 2:, k] = pilots_matrix - tx_dl_data[:, 0, k] = lts_f - tx_dl_data[:, 1, k] = lts_f - tx_dl_ifft = np.zeros((M, n_ofdm_syms, N)).astype(complex) - print("n_ofdm_syms %d, data_ofdm_len %d"%(n_ofdm_syms, data_ofdm_len)) - - # received data params - lts_thresh = 0.8 - n_data_ofdm_syms = n_ofdm_syms - 2 - payload_len = n_data_ofdm_syms * data_ofdm_len - lts_len = 2 * data_ofdm_len - fft_offset = 0 - - #### Configure tdd mode - guardSize = (len(csdrs)) % 2 + 1 - frameLen = len(csdrs) + len(bsdrs)*2 + 4 + guardSize - - # BS frame config - for i,sdr in enumerate(bsdrs): - beacon_sch = "BG" - if i == ref_ant: - ref_ul_pilot_sch = "PG" - ref_dl_pilot_sch = ''.join("RG" * (M - 1)) - ul_pilot_sch = ''.join("R" * K) - else: - ref_ul_pilot_sch = "RG" - new_i = i - (i > ref_ant) - ref_dl_pilot_sch = ''.join("GG" * new_i) + "PG" + ''.join("GG" * (M-(new_i+2))) - ul_pilot_sch = ''.join("R" * K) - - frame_sch1 = beacon_sch + ref_ul_pilot_sch + ref_dl_pilot_sch + ul_pilot_sch + 'G' - - dl_data_sch = "PG" + ''.join("G" * (2 * M + K - (2 * D))) - frame_sch2 = beacon_sch + dl_data_sch + 'G' - - print("BS node %d frame schedule (%s, %s)" % (i, frame_sch1, frame_sch2)) - bconf = {"tdd_enabled": True, - "frame_mode": "triggered", - "symbol_size" : sym_samps, - "frames": [frame_sch1, frame_sch2], - "beacon_start" : prefix_length, - "beacon_stop" : prefix_length+len(beacon), - "max_frame" : 2} - sdr.writeSetting("TDD_CONFIG", json.dumps(bconf)) - sdr.writeSetting("TDD_MODE", "true") - - # Client frame config - for i, sdr in enumerate(csdrs): - det_sch = "GG" - ref_pilot_sch = ''.join("GG" * M) - ul_pilot_sch = ''.join("G" * i) + "P" + ''.join("G" * (K-(i+1))) - frame_sch1 = det_sch + ref_pilot_sch + ul_pilot_sch + 'G' - - dl_data_sch = "RG" * D + ''.join("G" * (2 * M + K - (2 * D))) - frame_sch2 = det_sch + dl_data_sch + 'G' - - print("Client %d frame schedule (%s, %s)"%(i, frame_sch1, frame_sch2)) - cconf = {"tdd_enabled": True, - "frame_mode": "triggered", - "symbol_size" : sym_samps, - "frames": [frame_sch1, frame_sch2], - "max_frame" : 0} - sdr.writeSetting("TDD_CONFIG", json.dumps(cconf)) - sdr.writeSetting("TDD_MODE", "true") - - for sdr in bsdrs+csdrs: - sdr.writeSetting("TX_SW_DELAY", str(30)) - - if not use_trig: - for sdr in csdrs: - # enable the correlator, with zeros as inputs - corr_conf = {"corr_enabled" : True, - "corr_threshold" : 1} - sdr.writeSetting("CORR_CONFIG", json.dumps(corr_conf)) - sdr.writeRegisters("CORR_COE", 0, coe.tolist()) - - # DEV: ueTrigTime = 153 (prefix_length=0), - # CBRS: ueTrigTime = 235 (prefix_length=82), tx_advance=prefix_length, - # corr delay is 17 cycles - #cl_trig_time = prefix_length + len(beacon) + postfix_length + 17 + postfix_length - cl_trig_time = 256 + 250 - sf_start = cl_trig_time // sym_samps - sp_start = cl_trig_time % sym_samps - print("UE starting symbol and sample count (%d, %d)" % (sf_start, sp_start)) - # make sure to set this after TDD mode is enabled "writeSetting("TDD_CONFIG", ..." - sdr.setHardwareTime(SoapySDR.ticksToTimeNs((sf_start << 16) | sp_start, rate), "TRIGGER") - else: - for sdr in csdrs: - sdr.setHardwareTime(0, "TRIGGER") - - for i,sdr in enumerate(bsdrs): - sdr.setHardwareTime(0, "TRIGGER") - - replay_addr = 0 - pilot_uint = cfloat2uint32(wb_pilot1, order='QI').tolist() - beacon_uint = cfloat2uint32(beacon1, order='QI').tolist() - zero_uint = cfloat2uint32(wbz, order='QI').tolist() - for i, sdr in enumerate(bsdrs): - sdr.writeRegisters("BEACON_RAM", 0, beacon_uint) - sdr.writeRegisters("BEACON_RAM_WGT_A", 0, beacon_weights[i].tolist()) - sdr.writeSetting("BEACON_START", str(M)) - - for sdr in csdrs: - sdr.writeRegisters("TX_RAM_A", replay_addr, pilot_uint) - sdr.writeRegisters("TX_RAM_B", replay_addr, zero_uint) - - # Create and activate streams - rx_stream_ul = [sdr.setupStream(SOAPY_SDR_RX, SOAPY_SDR_CF32, [0, 1]) for sdr in bsdrs] - rx_stream_dl = [sdr.setupStream(SOAPY_SDR_RX, SOAPY_SDR_CF32, [0, 1]) for sdr in csdrs] - flags = 0 - for i, sdr in enumerate(bsdrs): - sdr.activateStream(rx_stream_ul[i], flags, 0) - for i, sdr in enumerate(csdrs): - sdr.activateStream(rx_stream_dl[i], flags, 0) - - ############# - #initialize array and matrixes to hold rx and processed data - ############# - calib_rx_dl = [np.array([1]*sym_samps).astype(np.complex64) for m in range(M)] - calib_rx_ul = [np.array([1]*sym_samps).astype(np.complex64) for m in range(M)] - data_rx_dl = [[np.array([0]*sym_samps).astype(np.complex64) for k in range(K)] for m in range(D)] - pilot_rx_ul = [[np.array([0]*sym_samps).astype(np.complex64) for m in range(M)] for k in range(K)] - dummy_rx = np.array([0]*sym_samps).astype(np.complex64) - - ul_cal_offset = np.array([0]*M, np.int32) - dl_cal_offset = np.array([0]*M, np.int32) - - ul_offset = [np.array([0]*K, np.int32) for m in range(M)] - dl_offset = [np.array([0]*K, np.int32) for m in range(M)] - - ul_csi_mat = np.empty([K, M, N], dtype=np.complex64) - rx_f_cal_dl = np.empty([M, N], dtype=np.complex64) - rx_f_cal_ul = np.empty([M, N], dtype=np.complex64) - - w_zf_dl = np.empty([M, K, N], dtype=np.complex64) - w_conj_dl = np.empty([M, K, N], dtype=np.complex64) - - calib = np.empty((M, N)).astype(np.complex64) - - rxSymbols_mat = np.empty([len(data_sc), D * n_data_ofdm_syms, K], dtype=np.complex64) - - cont_plotter = plotter - - if cont_plotter: - fig1, axes1 = plt.subplots(nrows=M, ncols=2, figsize=(9, 12)) - axes1[0, 0].set_title('Pilot Uplink (Re)') - axes1[0, 1].set_title('Pilot Uplink (Im)') - for m in range(M): - axes1[m, 0].set_xlim(0, sym_samps) - axes1[m, 0].set_ylim(-1, 1) - if m == ref_ant: - axes1[m, 0].set_ylabel('Ant %d (ref)'%m) - else: - axes1[m, 0].set_ylabel('Ant %d'%m) - axes1[m, 0].legend(fontsize=10) - axes1[m, 1].set_xlim(0, sym_samps) - axes1[m, 1].set_ylim(-1, 1) - axes1[m, 1].legend(fontsize=10) - lines11 = [[axes1[m, 0].plot(range(sym_samps), np.real(pilot_rx_ul[k][m]), label="User %d (real)"%k)[0] for k in range(K)] for m in range(M)] - lines12 = [[axes1[m, 1].plot(range(sym_samps), np.imag(pilot_rx_ul[k][m]), label="User %d (imag)"%k)[0] for k in range(K)] for m in range(M)] - fig1.show() - - fig2, axes2 = plt.subplots(nrows=M, ncols=2, figsize=(9, 12)) - axes2[0, 0].set_title('Calibration Downlink') - axes2[0, 1].set_title('Calibration Uplink') - for m in range(M): - axes2[m, 0].set_xlim(0, sym_samps) - axes2[m, 0].set_ylim(-1, 1) - if m == ref_ant: - axes2[m, 0].set_ylabel('Ant %d (ref)'%m) - else: - axes2[m, 0].set_ylabel('Ant %d'%m) - axes2[m, 0].legend(fontsize=10) - axes2[m, 1].set_xlim(0, sym_samps) - axes2[m, 1].set_ylim(-1, 1) - axes2[m, 1].legend(fontsize=10) - lines20 = [axes2[m, 0].plot(range(sym_samps), calib_rx_dl[m][:sym_samps])[0] for m in range(M)] - lines21 = [axes2[m, 1].plot(range(sym_samps), calib_rx_ul[m][:sym_samps])[0] for m in range(M)] - lines24 = [axes2[m, 0].plot(range(sym_samps), calib_rx_dl[m][:sym_samps])[0] for m in range(M)] - lines25 = [axes2[m, 1].plot(range(sym_samps), calib_rx_ul[m][:sym_samps])[0] for m in range(M)] - fig2.show() - - fig3, axes3 = plt.subplots(nrows=K, ncols=1, figsize=(6, 6)) - for k in range(K): - if K == 1: - ax3 = axes3 - else: - ax3 = axes3[k] - ax3.grid(True) - ax3.set_title('TX/RX Constellation') - ax3.set_xlabel('') - ax3.set_ylabel('') - ax3.set_ylim(-5.5, 5.5) - ax3.set_xlim(-5.8, 5.8) - ax3.legend(fontsize=10) - - if K == 1: - line31, = axes3.plot([], [], 'ro', label='TXSym') - line32, = axes3.plot([], [], 'bx', label='RXSym') - else: - line31 = [axes3[k].plot([], [], 'ro', label='TXSym')[0] for k in range(K)] - line32 = [axes3[k].plot([], [], 'bx', label='RXSym')[0] for k in range(K)] - - fig3.show() - - fig4, axes4 = plt.subplots(nrows=K, ncols=1, figsize=(6, 6)) - for k in range(K): - if K == 1: - ax4 = axes4 - else: - ax4 = axes4[k] - ax4.grid(True) - ax4.set_title('Received Downlink') - ax4.set_xlabel('') - ax4.set_ylabel('') - ax4.set_ylim(-1, 1) - ax4.set_xlim(0, sym_samps) - ax4.legend(fontsize=10) - - if K == 1: - line41, = axes4.plot(range(sym_samps), data_rx_dl[0][0], label='RX Downlink') - line42, = axes4.plot(range(sym_samps), data_rx_dl[0][0]) - else: - line41 = [axes4[k].plot(range(sym_samps), data_rx_dl[0][k], label='RX Downlink')[0] for k in range(K)] - line42 = [axes4[k].plot(range(sym_samps), data_rx_dl[0][k])[0] for k in range(K)] - - fig4.show() - - - cur_frame = 0 - signal.signal(signal.SIGINT, signal_handler) - tstart = datetime.datetime.now() - - while(RUNNING): - - ## disarm correlator in the clients - if not use_trig: - for i, sdr in enumerate(csdrs): - sdr.writeSetting("CORR_CONFIG", json.dumps({"corr_enabled": False})) - - bad_recip_read = False - bad_frame = False - ## arm correlator in the clients, inputs from adc - if not use_trig: - for i, sdr in enumerate(csdrs): - sdr.writeSetting("CORR_START", "A") - - for i, sdr in enumerate(bsdrs): - sdr.writeRegisters("TX_RAM_A", 0, pilot_uint) - sdr.writeRegisters("TX_RAM_B", 0, zero_uint) - - trig_dev.writeSetting("TRIGGER_GEN", "") - - ## collect reciprocity pilots from antenna m - for m in range(M): - if bad_recip_read: break - if m != ref_ant: - sr = bsdrs[m].readStream(rx_stream_ul[m], [calib_rx_ul[m], dummy_rx], sym_samps) - if sr.ret < sym_samps: - print("Calib: m %d ret %d"%(m,sr.ret)) - bad_recip_read = True - - for m in range(M): - if bad_recip_read: break - if m != ref_ant: - sr = bsdrs[ref_ant].readStream(rx_stream_ul[ref_ant], [calib_rx_dl[m], dummy_rx], sym_samps) - if sr.ret < sym_samps: - print("Calib: m %d ret %d"%(m,sr.ret)) - bad_recip_read = True - - if bad_recip_read: - print("BAD RECIPROCAL PILOT READ... CONTINUE! ") - continue - - ## collect uplink pilots - bad_pilot_read = False - for k in range(K): - if bad_pilot_read: break - for m in range(M): - sr = bsdrs[m].readStream(rx_stream_ul[m], [pilot_rx_ul[k][m], dummy_rx], sym_samps) - if sr.ret < sym_samps: - print("PilotUP: k: %d, m %d ret %d"%(k,m,sr.ret)) - bad_pilot_read = True - - if bad_pilot_read: - print("BAD PILOT READ... CONTINUE! ") - continue - - - ## process downlink signal - # processing the received calibration samples - print("frame %d"%(cur_frame)) - for m in range(M): - if ref_ant == m: - calib[m,:] = np.array([1]*N, np.complex64) #continue - rx_f_cal_dl[m,:] = np.array([0]*N, np.complex64) - rx_f_cal_ul[m,:] = np.array([0]*N, np.complex64) - continue - calib_rx_dl[m] -= np.mean(calib_rx_dl[m]) - calib_rx_ul[m] -= np.mean(calib_rx_ul[m]) - - best_peak_dl, _, _ = find_lts(calib_rx_dl[m], thresh=LTS_THRESH) - best_peak_ul, _, _ = find_lts(calib_rx_ul[m], thresh=LTS_THRESH) - dl_cal_offset[m] = 0 if not best_peak_dl else best_peak_dl - len(lts_sym) + cp_len - ul_cal_offset[m] = 0 if not best_peak_ul else best_peak_ul - len(lts_sym) + cp_len - if (dl_cal_offset[m] < 150 or ul_cal_offset[m] < 150): - bad_frame = True - lts_dn_1 = calib_rx_dl[m][dl_cal_offset[m]:dl_cal_offset[m]+N] - lts_dn_2 = calib_rx_dl[m][dl_cal_offset[m]+N:dl_cal_offset[m]+2*N] - lts_up_1 = calib_rx_ul[m][ul_cal_offset[m]:ul_cal_offset[m]+N] - lts_up_2 = calib_rx_ul[m][ul_cal_offset[m]+N:ul_cal_offset[m]+2*N] - - rx_f_cal_dl1 = np.fft.fftshift(np.fft.fft(lts_dn_1, N, 0), 0) - rx_f_cal_dl2 = np.fft.fftshift(np.fft.fft(lts_dn_2, N, 0), 0) - rx_f_cal_ul1 = np.fft.fftshift(np.fft.fft(lts_up_1, N, 0), 0) - rx_f_cal_ul2 = np.fft.fftshift(np.fft.fft(lts_up_2, N, 0), 0) - rx_f_cal_dl[m, :] = (rx_f_cal_dl1 + rx_f_cal_dl2)/2 - rx_f_cal_ul[m, :] = (rx_f_cal_ul1 + rx_f_cal_ul2)/2 - calib[m,:] = np.divide(rx_f_cal_dl[m,:], rx_f_cal_ul[m,:]) - - # processing uplink pilot received samples - for k in range(K): - for m in range(M): - pilot_rx_ul[k][m] -= np.mean(pilot_rx_ul[k][m]) - best_peak, _, _ = find_lts(pilot_rx_ul[k][m], thresh=LTS_THRESH) - ul_offset[m][k] = 0 if not best_peak else (best_peak - len(lts_sym) + cp_len) - if ul_offset[m][k] < 150: - bad_frame = True - lts_ul_1 = pilot_rx_ul[k][m][ul_offset[m][k]:ul_offset[m][k]+N] - lts_ul_2 = pilot_rx_ul[k][m][ul_offset[m][k]+N:ul_offset[m][k]+2*N] - rx_f_ul1 = np.fft.fftshift(np.fft.fft(lts_ul_1, N, 0), 0) - rx_f_ul2 = np.fft.fftshift(np.fft.fft(lts_ul_2, N, 0), 0) - ul_csi_mat[k,m,:] = (rx_f_cal_ul1 + rx_f_cal_ul2) * lts_f / 2 - - # processing beamforming vectors and downlink transmit data - for l in range(n_ofdm_syms): - for n in range(N): - dl_csi = np.matmul(ul_csi_mat[:, :, n], np.diag(calib[:, n])) - w_zf_dl[:, :, n] = np.linalg.pinv(dl_csi) - tx_dl_ifft[:, l, n] = np.matmul(w_zf_dl[:, :, n], tx_dl_data[n, l, :]) - - tx_sym = np.fft.ifft(tx_dl_ifft, axis=2) - tx_sym = np.concatenate((tx_sym[:,:,-data_cp_len:], tx_sym), axis=2) - - # send downlink signal - for i, sdr in enumerate(bsdrs): - tx_sig = np.reshape(tx_sym[i, :, :], (1, n_ofdm_syms * data_ofdm_len)) - tx_sig = np.concatenate([pad1, tx_sig[0, :], pad2]) - sdr.writeRegisters("TX_RAM_A", 0, cfloat2uint32(tx_sig, order='QI').tolist()) - sdr.writeRegisters("TX_RAM_B", 0, zero_uint) - - trig_dev.writeSetting("TRIGGER_GEN", "") - - # collect downlink data from antenna k - bad_dl_read = False - for d in range(D): - if bad_dl_read: break - for k in range(K): - sr = csdrs[k].readStream(rx_stream_dl[k], [data_rx_dl[d][k], dummy_rx], sym_samps) - if sr.ret < sym_samps: - print("DL DATA: symbol %d, k %d, ret %d"%(d, k, sr.ret)) - bad_dl_read = True - - if bad_dl_read: - print("BAD DL READ... CONTINUE! ") - continue - - # DC removal - # Find LTS peaks (in case LTSs were sent) - bad_dl_data = False - for k in range(K): - if bad_dl_data: break - for d in range(D): - data_rx_dl[d][k] -= np.mean(data_rx_dl[d][k]) - best_peak_dl, b, peaks0 = find_lts(data_rx_dl[d][k], thresh=lts_thresh, flip=True, lts_seq=lts_t_cp) - if use_trig: - dl_offset[k] = 163 + 160 #0 if not best_peak_dl else best_peak_dl - else: - dl_offset[k] = 0 if not best_peak_dl else best_peak_dl - if dl_offset[k] < lts_len: - bad_dl_data = True - print("NO VALID DOWNLINK... CONTINUE! ") - break - payload_start = dl_offset[k] - payload_end = payload_start + payload_len # Payload_len == (n_ofdm_syms * (num_sc + data_cp_len)) - lts_start = payload_start - lts_len # where LTS-CP start - lts = data_rx_dl[d][k][lts_start: payload_start] - if len(lts) < lts_len: - print("BAD DOWNLINK PILOT... CONTINUE! ") - bad_dl_data = True - break - lts_1 = lts[16 + -fft_offset + np.array(range(0, 64))] - lts_2 = lts[96 + -fft_offset + np.array(range(0, 64))] - - # Average 2 LTS symbols to compute channel estimate - tmp = np.fft.ifftshift(lts_f) - chan_est = np.fft.ifftshift(lts_f) * (np.fft.fft(lts_1) + np.fft.fft(lts_2))/2 - if len(data_rx_dl[d][k]) >= payload_end: - # Retrieve payload symbols - payload_samples = data_rx_dl[d][k][payload_start: payload_end] - else: - bad_dl_data = True - print("TOO LATE (payload_end %d)... CONTINUE! "%payload_end) - break - payload_samples_mat_cp = np.reshape(payload_samples, (data_ofdm_len, n_data_ofdm_syms), order="F") - - # Remove cyclic prefix - payload_samples_mat = payload_samples_mat_cp[data_cp_len - fft_offset + np.array(range(0, N)), :] - - # FFT - rxSig_freq = np.fft.fft(payload_samples_mat, n=N, axis=0) - - # Equalizer - chan_est_tmp = chan_est.reshape(len(chan_est), 1, order="F") - rxSig_freq_eq = rxSig_freq / np.matlib.repmat(chan_est_tmp, 1, n_data_ofdm_syms) - phase_error = ofdm_obj.phase_correction(rxSig_freq_eq, pilot_sc, pilots_matrix) - - phase_corr_tmp = np.matlib.repmat(phase_error, N, 1) - phase_corr = np.exp(-1j * phase_corr_tmp) - rxSig_freq_eq_phase = rxSig_freq_eq * phase_corr - rxSymbols_mat[:, d * n_data_ofdm_syms : (d + 1) * n_data_ofdm_syms, k] = rxSig_freq_eq_phase[data_sc, :] - - if bad_dl_data: - continue - - evm_mat = np.power(np.abs(rxSymbols_mat - np.tile(tx_dl_data[data_sc, 2:, :], (1, D, 1))), 2) - evm_per_user = np.mean(np.reshape(evm_mat, (len(data_sc) * n_data_ofdm_syms * D, K)), axis=0) - evm_per_user_db = 10 * np.log10(evm_per_user) - print('EVM (dB) per user') - print([ "{:2.2f}".format(x) for x in evm_per_user_db ]) - print('') - - cur_frame += 1 - if cur_frame >= TOT_FRAMES: break - if cont_plotter: - - for m in range(M): - if m == ref_ant: - lines20[m].set_ydata(np.real(wb_pilot1)) - lines21[m].set_ydata(np.real(wb_pilot1)) - continue - lines20[m].set_ydata(np.real(calib_rx_dl[m])) - lines21[m].set_ydata(np.real(calib_rx_ul[m])) - lines24[m].set_data(dl_cal_offset[m], np.linspace(-1.0, 1.0, num=100)) - lines25[m].set_data(ul_cal_offset[m], np.linspace(-1.0, 1.0, num=100)) - - for m in range(M): - for k in range(K): - lines11[m][k].set_ydata(np.real(pilot_rx_ul[k][m])) - lines12[m][k].set_ydata(np.imag(pilot_rx_ul[k][m])) - - if K == 1: - txSyms_all = tx_dl_data[data_sc, 2:, 0].flatten() - rxSyms_all = rxSymbols_mat[:, :, 0].flatten() - line31.set_data(np.real(txSyms_all), np.imag(txSyms_all)) - line32.set_data(np.real(rxSyms_all), np.imag(rxSyms_all)) - line41.set_data(range(sym_samps), np.real(data_rx_dl[0][0])) - line42.set_data(dl_offset[0], np.linspace(-1.0, 1.0, num=100)) - else: - for k in range(K): - txSyms_all = tx_dl_data[data_sc, 2:, k].flatten() - rxSyms_all = rxSymbols_mat[:, :, k].flatten() - line31[k].set_data(np.real(txSyms_all), np.imag(txSyms_all)) - line32[k].set_data(np.real(rxSyms_all), np.imag(rxSyms_all)) - line41[k].set_data(range(sym_samps), np.real(data_rx_dl[0][k])) - line42[k].set_data(dl_offset[k], np.linspace(-1.0, 1.0, num=100)) - - fig1.canvas.draw() - fig1.show() - - fig2.canvas.draw() - fig2.show() - - fig3.canvas.draw() - fig3.show() - - fig4.canvas.draw() - fig4.show() - - tend = datetime.datetime.now() - c = tend - tstart - print("Elapsed %d (secs)"%c.total_seconds()) - - # clear up fpga states - tdd_conf = {"tdd_enabled" : False} - corr_conf = {"corr_enabled" : False} - for sdr in csdrs: - if not use_trig: - sdr.writeSetting("CORR_CONFIG", json.dumps(corr_conf)) - for sdr in bsdrs+csdrs: - sdr.writeSetting("TDD_CONFIG", json.dumps(tdd_conf)) - sdr.writeSetting("TDD_MODE", "false") - sdr.writeSetting("RESET_DATA_LOGIC", "") - - # close streams and exit - for i, sdr in enumerate(bsdrs): - sdr.closeStream(rx_stream_ul[i]) - for i, sdr in enumerate(csdrs): - sdr.closeStream(rx_stream_dl[i]) - -def main(): - parser = OptionParser() - parser.add_option("--args", type="string", dest="args", help="arguments", default="") - parser.add_option("--bnodes", type="string", dest="bnodes", help="file name containing serials on the base station", default="../IrisUtils/data_in/bs_serials.txt") - parser.add_option("--cnodes", type="string", dest="cnodes", help="file name containing serials to be used as clients", default="../IrisUtils/data_in/cl_serials.txt") - parser.add_option("--hub", type="string", dest="hub", help="Hub node", default="") - parser.add_option("--ref-ant", type="int", dest="ref_ant", help="Calibration reference antenna", default=0) - parser.add_option("--ampl", type="float", dest="ampl", help="Amplitude coefficient for downCal/upCal", default=0.5) - parser.add_option("--rate", type="float", dest="rate", help="Tx sample rate", default=5e6) - parser.add_option("--freq", type="float", dest="freq", help="Optional Tx freq (Hz)", default=2.5e9) - parser.add_option("--txgain", type="float", dest="txgain", help="Optional Tx gain (dB)", default=40.0) - parser.add_option("--rxgain", type="float", dest="rxgain", help="Optional Rx gain (dB)", default=20.0) - parser.add_option("--bw", type="float", dest="bw", help="Optional Tx filter bw (Hz)", default=10e6) - parser.add_option("--cp", action="store_true", dest="cp", help="adds cyclic prefix to tx symbols", default=True) - parser.add_option("--plotter", action="store_true", dest="plotter", help="continuously plots all signals and stats",default=False) - parser.add_option("--numSamps", type="int", dest="numSamps", help="Number of samples in Symbol", default=400) - parser.add_option("--prefix-length", type="int", dest="prefix_length", help="prefix padding length for beacon and pilot", default=82) - parser.add_option("--postfix-length", type="int", dest="postfix_length", help="postfix padding length for beacon and pilot", default=68) - parser.add_option("--tx-advance", type="int", dest="tx_advance", help="symbol advance for tx", default=2) - parser.add_option("--corr-threshold", type="int", dest="threshold", help="Correlator Threshold Value", default=1) - parser.add_option("--use-trig", action="store_true", dest="use_trig", help="uses chain triggers for synchronization",default=False) - parser.add_option("--modOrder", type="int", dest="modOrder", help="Modulation Order 2=BPSK/4=QPSK/16=16QAM/64=64QAM", default=2) - (options, args) = parser.parse_args() - - if options.txgain > 81: - print("[ERROR] TX gain should be between 0 and 81") - exit(0) - - bserials = [] - with open(options.bnodes, "r") as f: - for line in f.read().split(): - if line[0] != '#': - bserials.append(line) - else: - continue - - cserials = [] - with open(options.cnodes, "r") as f: - for line in f.read().split(): - if line[0] != '#': - cserials.append(line) - else: - continue - - init(hub=options.hub, - bnodes=bserials, - cnodes=cserials, - ref_ant=options.ref_ant, - ampl=options.ampl, - rate=options.rate, - freq=options.freq, - txgain=options.txgain, - rxgain=options.rxgain, - cp=options.cp, - plotter=options.plotter, - numSamps=options.numSamps, - prefix_length=options.prefix_length, - postfix_length=options.postfix_length, - tx_advance=options.tx_advance, - mod_order=options.modOrder, - threshold=options.threshold, - use_trig=options.use_trig) - -if __name__ == '__main__': - try: - main() - except KeyboardInterrupt: - exit() diff --git a/PYTHON/DEMOS/MMIMO_RECEIVER.py b/PYTHON/DEMOS/MMIMO_RECEIVER.py index ef48912c..bb22df57 100644 --- a/PYTHON/DEMOS/MMIMO_RECEIVER.py +++ b/PYTHON/DEMOS/MMIMO_RECEIVER.py @@ -370,8 +370,14 @@ def demultiplex(samples, bf_weights, user_params, metadata, chan_est, lts_start) num_ant = metadata['BS_ANT_NUM_PER_CELL'].astype(int)[0] data_cp_len = int(metadata['CP_LEN']) fft_size = int(metadata['FFT_SIZE']) - num_samps = int(metadata['SYMBOL_LEN_NO_PAD']) + if 'SYMBOL_LEN' in metadata: # to support older datasets + slot_samps_with_pad = int(metadata['SYMBOL_LEN']) + elif 'SLOT_SAMP_LEN' in metadata: + slot_samps_with_pad = int(metadata['SLOT_SAMP_LEN']) prefix_len = int(metadata['PREFIX_LEN']) + postfix_len = int(metadata['POSTFIX_LEN']) + z_padding = prefix_len + postfix_len + num_samps = slot_samps_with_pad - z_padding ofdm_size = fft_size + data_cp_len n_ofdm_syms = num_samps//ofdm_size fft_offset = user_params[5] @@ -450,7 +456,14 @@ def demodulate_data(streams, ofdm_obj, user_params, metadata): """ fft_size = int(metadata['FFT_SIZE']) data_cp_len = int(metadata['CP_LEN']) - num_samps = int(metadata['SYMBOL_LEN_NO_PAD']) + if 'SYMBOL_LEN' in metadata: # to support older datasets + slot_samps_with_pad = int(metadata['SYMBOL_LEN']) + elif 'SLOT_SAMP_LEN' in metadata: + slot_samps_with_pad = int(metadata['SLOT_SAMP_LEN']) + prefix_len = int(metadata['PREFIX_LEN']) + postfix_len = int(metadata['POSTFIX_LEN']) + z_padding = prefix_len + postfix_len + num_samps = slot_samps_with_pad - z_padding num_sc = int(metadata['FFT_SIZE']) mod_order_str = metadata['CL_MODULATION'].astype(str) data_sc = metadata['OFDM_DATA_SC'] @@ -623,16 +636,20 @@ def rx_app(filename, user_params, this_plotter): else: cl_present = False - prefix_len = int(metadata['PREFIX_LEN']) - postfix_len = int(metadata['POSTFIX_LEN']) pilot_type = metadata['PILOT_SEQ_TYPE'].astype(str)[0] num_bs_ant = metadata['BS_ANT_NUM_PER_CELL'].astype(int)[0] pilot_samples = samples['PILOT_SAMPS'] data_samples = samples['UL_DATA'] num_cells = int(metadata['BS_NUM_CELLS']) num_cl = int(metadata['CL_NUM']) - sym_len = int(metadata['SYMBOL_LEN']) - sym_len_no_pad = int(metadata['SYMBOL_LEN_NO_PAD']) + if 'SYMBOL_LEN' in metadata: # to support older datasets + slot_samps_with_pad = int(metadata['SYMBOL_LEN']) + elif 'SLOT_SAMP_LEN' in metadata: + slot_samps_with_pad = int(metadata['SLOT_SAMP_LEN']) + prefix_len = int(metadata['PREFIX_LEN']) + postfix_len = int(metadata['POSTFIX_LEN']) + z_padding = prefix_len + postfix_len + num_samps = slot_samps_with_pad - z_padding fft_size = int(metadata['FFT_SIZE']) cp_len = int(metadata['CP_LEN']) ofdm_data_sc = metadata['OFDM_DATA_SC'] @@ -652,8 +669,8 @@ def rx_app(filename, user_params, this_plotter): for idx in range(num_cl): # Freq domain TX data (Does not contain cyclic prefix or prefix/postfix) - ofdm_data.append(metadata['OFDM_DATA_CL' + str(idx)][idx]) - ofdm_data_time.append(metadata['OFDM_DATA_TIME_CL' + str(idx)][idx]) + ofdm_data.append(np.array(metadata['OFDM_DATA_CL' + str(idx)])) + ofdm_data_time.append(np.array(metadata['OFDM_DATA_TIME_CL' + str(idx)])) pilot_dim = pilot_samples.shape num_frames = pilot_dim[0] @@ -676,19 +693,19 @@ def rx_app(filename, user_params, this_plotter): assert pilot_dim[1] == num_cells assert pilot_dim[2] == num_cl assert pilot_dim[3] == num_bs_ant - assert pilot_dim[4] == 2 * sym_len # No complex values in HDF5, x2 to account for IQ + assert pilot_dim[4] == 2 * slot_samps_with_pad # No complex values in HDF5, x2 to account for IQ ########################### # Build TX signals # ########################### # Process TX freq domain samples (from HDF5). These are the samples generated for transmission and stored in file, # not what has been received - num_samps_freq_dom = (fft_size+cp_len)*(sym_len_no_pad//(fft_size+cp_len)) + num_samps_freq_dom = (fft_size+cp_len)*(num_samps//(fft_size+cp_len)) n_ofdm_syms = num_samps_freq_dom//(fft_size+cp_len) # Pilots - rep = sym_len_no_pad//len(ofdm_pilot_cp) - frac = sym_len_no_pad % len(ofdm_pilot_cp) + rep = num_samps//len(ofdm_pilot_cp) + frac = num_samps % len(ofdm_pilot_cp) full_pilot = np.concatenate((np.zeros(prefix_len), np.squeeze(np.matlib.repmat(ofdm_pilot_cp, 1, rep)), @@ -697,7 +714,7 @@ def rx_app(filename, user_params, this_plotter): # Note: # One pilot per client + overlapping data including prefix etc (for showing purposes) - tx_sig = np.zeros((num_cl, (num_cl*sym_len + len(ofdm_data_time[0]))), dtype=complex) + tx_sig = np.zeros((num_cl, (num_cl*slot_samps_with_pad + len(ofdm_data_time[0]))), dtype=complex) if cl_present: ofdm_data_mat = [] @@ -730,8 +747,8 @@ def rx_app(filename, user_params, this_plotter): ########################### # Update Plots # ########################### - fig_len = 3 * (prefix_len + sym_len_no_pad + postfix_len) - pilot_len = sym_len_no_pad + fig_len = 3 * (prefix_len + num_samps + postfix_len) + pilot_len = num_samps this_plotter.update_tx_signal_fig(fig_len) this_plotter.update_rx_signal_fig(fig_len) this_plotter.update_corr_peaks(pilot_len) @@ -747,9 +764,9 @@ def rx_app(filename, user_params, this_plotter): chan_est = np.zeros([num_cells, num_cl, num_bs_ant, num_frames, fft_size], dtype=complex) cfo_est = np.zeros([num_cells, num_cl, num_bs_ant, num_frames]) lts_evm = np.zeros([num_cells, num_cl, num_bs_ant, num_frames]) - lts_corr = np.zeros([num_cl, num_bs_ant, sym_len+fft_size-1]) + lts_corr = np.zeros([num_cl, num_bs_ant, slot_samps_with_pad+fft_size-1]) peak_index = np.zeros([num_cl, num_bs_ant, num_frames]) - IQ_pilots = np.zeros([num_cells, num_cl, num_bs_ant, sym_len], dtype=complex) + IQ_pilots = np.zeros([num_cells, num_cl, num_bs_ant, slot_samps_with_pad], dtype=complex) pilot_thresh = np.zeros([num_cl, num_bs_ant]) lts_start = np.zeros([num_cl, num_bs_ant]) corr_total = np.zeros([num_frames, num_cl]) @@ -878,7 +895,7 @@ def rx_app(filename, user_params, this_plotter): rx_data, # [numBsAnt, symLen] chan_est_vec, # [numCl][fft size] rx_H_est_plot, # rx_H_est_plot[numCl][fft_size] - lts_corr[:, ant_plot, :], # [numCl, numBsAnt, sym_len+fft_size-1] + lts_corr[:, ant_plot, :], # [numCl, numBsAnt, slot_samps_with_pad+fft_size-1] pilot_thresh[:, ant_plot], # [numCl, numBsAnt] rxSyms_vec, # [numCl, num data sc * num ofdm sym] corr_total, # [num frames, numCl] @@ -900,8 +917,8 @@ def rx_app(filename, user_params, this_plotter): # print("FRAME: {} \t Client: {} \t Antenna: {}".format(frameIdx, clIdx, antIdx)) # Put I/Q together # Dims pilots: (frames, numCells, numClients, numAntennasAtBS, numSamplesPerSymbol*2) - I = pilot_samples[frameIdx, num_cells - 1, clIdx, antIdx, 0:sym_len * 2:2] / 2 ** 15 - Q = pilot_samples[frameIdx, num_cells - 1, clIdx, antIdx, 1:sym_len * 2:2] / 2 ** 15 + I = pilot_samples[frameIdx, num_cells - 1, clIdx, antIdx, 0:slot_samps_with_pad * 2:2] / 2 ** 15 + Q = pilot_samples[frameIdx, num_cells - 1, clIdx, antIdx, 1:slot_samps_with_pad * 2:2] / 2 ** 15 IQ = I + (Q * 1j) IQ_pilots[num_cells - 1, clIdx, antIdx, :] = IQ * pilot_scaling # For 'plotter' use @@ -932,8 +949,8 @@ def rx_app(filename, user_params, this_plotter): # Get data samples # Dims data: (frames, numCells, ulSymsPerFrame, numAntennasAtBS, numSamplesPerSymbol*2) for ulSymIdx in range(num_ul_syms): - Q = data_samples[frameIdx, num_cells-1, ulSymIdx, :, 0:sym_len*2:2] / 2 ** 15 # 32768 - I = data_samples[frameIdx, num_cells-1, ulSymIdx, :, 1:sym_len*2:2] / 2 ** 15 # 32768 + Q = data_samples[frameIdx, num_cells-1, ulSymIdx, :, 0:slot_samps_with_pad*2:2] / 2 ** 15 # 32768 + I = data_samples[frameIdx, num_cells-1, ulSymIdx, :, 1:slot_samps_with_pad*2:2] / 2 ** 15 # 32768 IQ = Q + (I * 1j) # QI, not IQ # Demultiplexing - Separate streams @@ -1007,7 +1024,7 @@ def rx_app(filename, user_params, this_plotter): rx_data, # [numBsAnt, symLen] chan_est_vec, # [numCl][fft size] rx_H_est_plot, # rx_H_est_plot[numCl][fft_size] - lts_corr[:, ant_plot, :], # [numCl, numBsAnt, sym_len+fft_size-1] + lts_corr[:, ant_plot, :], # [numCl, numBsAnt, slot_samps_with_pad+fft_size-1] pilot_thresh[:, ant_plot], # [numCl, numBsAnt] rxSyms_vec, # [numCl, num data sc * num ofdm sym] corr_total, # [num frames, numCl] diff --git a/PYTHON/DEMOS/SISO_OFDM.py b/PYTHON/DEMOS/SISO_OFDM.py index 35f65eb4..54e94a7f 100644 --- a/PYTHON/DEMOS/SISO_OFDM.py +++ b/PYTHON/DEMOS/SISO_OFDM.py @@ -583,7 +583,7 @@ def main(): (options, args) = parser.parse_args() if options.freq == 0: - print("[ERROR] Please provide RF Freq (Hz). POWDER users must set to 2.5e9. i.e. --freq=2.5e9") + print("[ERROR] Please provide RF Freq (Hz). POWDER users must set to CBRS band. e.g. --freq=3.6e9") exit(0) if options.txgain > 81: diff --git a/PYTHON/DEMOS/SISO_RX.py b/PYTHON/DEMOS/SISO_RX.py index 6376dbee..44111f00 100644 --- a/PYTHON/DEMOS/SISO_RX.py +++ b/PYTHON/DEMOS/SISO_RX.py @@ -444,7 +444,7 @@ def main(): (options, args) = parser.parse_args() if options.freq == 0: - print("[ERROR] Please provide RF Freq (Hz). POWDER users must set to 2.5e9") + print("[ERROR] Please provide RF Freq (Hz). POWDER users must set to CBRS band. e.g. --freq=3.6e9") exit(0) # Verify RX Mode diff --git a/PYTHON/DEMOS/SISO_TX.py b/PYTHON/DEMOS/SISO_TX.py index 1bf52f4d..f378a3fb 100644 --- a/PYTHON/DEMOS/SISO_TX.py +++ b/PYTHON/DEMOS/SISO_TX.py @@ -221,7 +221,7 @@ def main(): (options, args) = parser.parse_args() if options.freq == 0: - print("[ERROR] Please provide Tx Freq (Hz). POWDER users must set to 2.5e9") + print("[ERROR] Please provide RF Freq (Hz). POWDER users must set to CBRS band. e.g. --freq=3.6e9") exit(0) if options.gain > 81: diff --git a/PYTHON/DEMOS/SISO_TXRX_TDD.py b/PYTHON/DEMOS/SISO_TXRX_TDD.py index 56a50ed2..e79a50c9 100755 --- a/PYTHON/DEMOS/SISO_TXRX_TDD.py +++ b/PYTHON/DEMOS/SISO_TXRX_TDD.py @@ -233,16 +233,16 @@ def main(): parser.add_option("--numSamps", type="int", dest="numSamps", help="Num samples to receive", default=512) parser.add_option("--prefix-pad", type="int", dest="prefix_length", help="prefix padding length for beacon and pilot", default=82) parser.add_option("--postfix-pad", type="int", dest="postfix_length", help="postfix padding length for beacon and pilot", default=68) + (options, args) = parser.parse_args() if options.freq == 0: - print("[ERROR] Please provide RF Freq (Hz). POWDER users must set to 2.5e9") + print("[ERROR] Please provide RF Freq (Hz). POWDER users must set to CBRS band. e.g. --freq=3.6e9") exit(0) if options.txgain > 81: print("[ERROR] TX gain should be between 0 and 81") exit(0) - (options, args) = parser.parse_args() siso_tdd_burst( serial1=options.serial1, serial2=options.serial2, diff --git a/PYTHON/DEMOS/SOUNDER_TXRX.py b/PYTHON/DEMOS/SOUNDER_TXRX.py index a51bf4aa..2cff3f8c 100644 --- a/PYTHON/DEMOS/SOUNDER_TXRX.py +++ b/PYTHON/DEMOS/SOUNDER_TXRX.py @@ -442,7 +442,7 @@ def main(): (options, args) = parser.parse_args() if options.freq == 0: - print("[ERROR] Please provide RF Freq (Hz). POWDER users must set to 2.5e9") + print("[ERROR] Please provide RF Freq (Hz). POWDER users must set to CBRS band. e.g. --freq=3.6e9") exit(0) if options.txgain > 81: diff --git a/PYTHON/DEMOS/WB_CAL_DEMO.py b/PYTHON/DEMOS/WB_CAL_DEMO.py index 55d942ad..5df58483 100644 --- a/PYTHON/DEMOS/WB_CAL_DEMO.py +++ b/PYTHON/DEMOS/WB_CAL_DEMO.py @@ -353,7 +353,7 @@ def main(): parser.add_option("--ref-ant", type="int", dest="ref_ant", help="Calibration reference antenna", default=0) parser.add_option("--ampl", type="float", dest="ampl", help="Pilot amplitude scaling coefficient", default=0.5) parser.add_option("--rate", type="float", dest="rate", help="Tx sample rate", default=5e6) - parser.add_option("--freq", type="float", dest="freq", help="Optional Tx freq (Hz)", default=3.6e9) + parser.add_option("--freq", type="float", dest="freq", help="Optional Tx freq (Hz)", default=0.0) parser.add_option("--txgain", type="float", dest="txgain", help="Optional Tx gain (dB) w/CBRS 3.6GHz [0:81], 2.5GHZ [0:81]", default=30.0) parser.add_option("--rxgain", type="float", dest="rxgain", help="Optional Rx gain (dB) w/CBRS 3.6GHz [0:105], 2.5GHZ [0:108]", default=30.0) parser.add_option("--cp", action="store_true", dest="cyc_prefix", help="adds cyclic prefix to tx symbols", default=True) @@ -365,6 +365,10 @@ def main(): parser.add_option("--plot-samps", action="store_true", dest="plotter", help="plots all rx signals",default=False) (options, args) = parser.parse_args() + if options.freq == 0: + print("[ERROR] Please provide RF Freq (Hz). POWDER users must set to CBRS band. e.g. --freq=3.6e9") + exit(0) + if options.txgain > 81: print("[ERROR] Gain should be between 0 and 81") exit(0) diff --git a/PYTHON/IrisUtils/hdf5_lib.py b/PYTHON/IrisUtils/hdf5_lib.py index 3deb104e..791ddfc6 100644 --- a/PYTHON/IrisUtils/hdf5_lib.py +++ b/PYTHON/IrisUtils/hdf5_lib.py @@ -360,12 +360,12 @@ def get_data(self): def get_metadata(self): """ -Attributes - {FREQ, RATE, SYMBOL_LEN_NO_PAD, PREFIX_LEN, POSTFIX_LEN, SYMBOL_LEN, FFT_SIZE, CP_LEN, + {FREQ, RATE, PREFIX_LEN, POSTFIX_LEN, SLOT_SAMP_LEN, FFT_SIZE, CP_LEN, BEACON_SEQ_TYPE, PILOT_SEQ_TYPE, BS_HUB_ID, BS_SDR_NUM_PER_CELL, BS_SDR_ID, BS_NUM_CELLS, BS_CH_PER_RADIO, BS_FRAME_SCHED, BS_RX_GAIN_A, BS_TX_GAIN_A, BS_RX_GAIN_B, BS_TX_GAIN_B, BS_BEAMSWEEP, BS_BEACON_ANT, BS_NUM_ANT, BS_FRAME_LEN, CL_NUM, CL_CH_PER_RADIO, CL_AGC_EN, CL_RX_GAIN_A, CL_TX_GAIN_A, CL_RX_GAIN_B, CL_TX_GAIN_B, CL_FRAME_SCHED, CL_SDR_ID, - CL_MODULATION, UL_SYMS} + CL_MODULATION, UL_SLOTS} """ # Retrieve attributes, translate into python dictionary @@ -386,12 +386,13 @@ def get_metadata(self): # (i.e., RE1,IM1,RE2,IM2...REm,IM,) # Freq-domain Pilot - pilot_vec = self.metadata['OFDM_PILOT_F'] - # some_list[start:stop:step] - I = pilot_vec[0::2] - Q = pilot_vec[1::2] - pilot_complex = I + Q * 1j - self.metadata['OFDM_PILOT_F'] = pilot_complex + if "OFDM_PILOT_F" in self.metadata.keys(): + pilot_vec = self.metadata['OFDM_PILOT_F'] + # some_list[start:stop:step] + I = pilot_vec[0::2] + Q = pilot_vec[1::2] + pilot_complex = I + Q * 1j + self.metadata['OFDM_PILOT_F'] = pilot_complex # Time-domain Pilot pilot_vec = self.metadata['OFDM_PILOT'] @@ -401,8 +402,11 @@ def get_metadata(self): pilot_complex = I + Q * 1j self.metadata['OFDM_PILOT'] = pilot_complex - n_ul_syms = self.metadata['UL_SYMS'] - if n_ul_syms > 0: + if 'UL_SYMS' in self.metadata: + n_ul_slots = self.metadata['UL_SYMS'] + elif 'UL_SLOTS' in self.metadata: + n_ul_slots = self.metadata['UL_SLOTS'] + if n_ul_slots > 0: # Phase-Tracking Pilot Subcarrier pilot_sc_vals = self.metadata['OFDM_PILOT_SC_VALS'] pilot_sc_vals_complex = pilot_sc_vals[0::2] + 1j * pilot_sc_vals[1::2] diff --git a/PYTHON/IrisUtils/ofdm_plotter.py b/PYTHON/IrisUtils/ofdm_plotter.py index 17a445ca..5d8b1104 100755 --- a/PYTHON/IrisUtils/ofdm_plotter.py +++ b/PYTHON/IrisUtils/ofdm_plotter.py @@ -45,7 +45,7 @@ def __init__(self, num_cl): self.data_syms = [] self.user_params = [] self.metadata = [] - self.subframe_size = 640 + self.slot_size = 640 self.prefix_len = 160 self.postfix_len = 160 self.phase_err = np.zeros(10) @@ -141,10 +141,14 @@ def set_data(self, frameIdx, num_cl, tx, rx, chan_est, rx_H_est_plot, lts_corr, self.data_syms = data_syms[0, :] # tx symbols [numClients, data length] self.user_params = user_params self.num_sc = int(metadata['FFT_SIZE']) - self.subframe_size = int(metadata['SYMBOL_LEN_NO_PAD']) - self.pilot_len = int(metadata['SYMBOL_LEN_NO_PAD']) + if 'SYMBOL_LEN' in metadata: # to support older datasets + slot_samps_with_pad = int(metadata['SYMBOL_LEN']) + elif 'SLOT_SAMP_LEN' in metadata: + slot_samps_with_pad = int(metadata['SLOT_SAMP_LEN']) self.prefix_len = int(metadata['PREFIX_LEN']) self.postfix_len = int(metadata['POSTFIX_LEN']) + self.slot_size = slot_samps_with_pad - self.prefix_len - self.postfix_len + self.pilot_len = self.slot_size self.phase_err = phase_error[0] self.evm = avg_evm[0] self.snr = snr_from_evm[0] @@ -171,18 +175,18 @@ def ani_update(self, i): self.line_tx_sig2.set_data(range(len(self.tx_data2)), np.real(self.tx_data2)) # RX - subframe_size = self.subframe_size # 640 + slot_size = self.slot_size # 640 prefix_len = self.prefix_len # 160 postfix_len = self.postfix_len # 160 self.line_rx_sig.set_data(range(len(self.rx_data)), np.real(self.rx_data)) if self.num_cl > 1: self.line_pilot1_start.set_data(prefix_len * np.ones(100), np.linspace(-0.5, 0.5, num=100)) - self.line_pilot2_start.set_data((prefix_len + subframe_size + postfix_len + prefix_len) * np.ones(100), np.linspace(-0.5, 0.5, num=100)) - self.line_payload_start.set_data((prefix_len+subframe_size+postfix_len+prefix_len+subframe_size+postfix_len+prefix_len) * np.ones(100), np.linspace(-0.5, 0.5, num=100)) + self.line_pilot2_start.set_data((prefix_len + slot_size + postfix_len + prefix_len) * np.ones(100), np.linspace(-0.5, 0.5, num=100)) + self.line_payload_start.set_data((prefix_len+slot_size+postfix_len+prefix_len+slot_size+postfix_len+prefix_len) * np.ones(100), np.linspace(-0.5, 0.5, num=100)) else: self.line_pilot1_start.set_data(prefix_len * np.ones(100), np.linspace(-0.5, 0.5, num=100)) self.line_pilot2_start.set_data((np.nan) * np.ones(100),np.linspace(-0.5, 0.5, num=100)) - self.line_payload_start.set_data(( prefix_len + subframe_size + postfix_len + prefix_len) * np.ones(100), np.linspace(-0.5, 0.5, num=100)) + self.line_payload_start.set_data(( prefix_len + slot_size + postfix_len + prefix_len) * np.ones(100), np.linspace(-0.5, 0.5, num=100)) # Phase error 1 self.line_phase_err.set_data(range(len(self.phase_err)), self.phase_err) diff --git a/PYTHON/IrisUtils/plot_hdf5.py b/PYTHON/IrisUtils/plot_hdf5.py index 469ba892..416cfee3 100755 --- a/PYTHON/IrisUtils/plot_hdf5.py +++ b/PYTHON/IrisUtils/plot_hdf5.py @@ -52,7 +52,10 @@ def verify_hdf5(hdf5, frame_i=100, cell_i=0, ofdm_sym_i=0, ant_i =0, n_frm_end = hdf5.n_frm_end n_frm_st = hdf5.n_frm_st metadata = hdf5.metadata - symbol_length = int(metadata['SYMBOL_LEN']) + if 'SYMBOL_LEN' in metadata: # to support older datasets + samps_per_slot = int(metadata['SYMBOL_LEN']) + elif 'SLOT_SAMP_LEN' in metadata: + samps_per_slot = int(metadata['SLOT_SAMP_LEN']) num_pilots = int(metadata['PILOT_NUM']) num_cl = int(metadata['CL_NUM']) prefix_len = int(metadata['PREFIX_LEN']) @@ -68,11 +71,21 @@ def verify_hdf5(hdf5, frame_i=100, cell_i=0, ofdm_sym_i=0, ant_i =0, if 'DATA_SUBCARRIER_NUM' in metadata: nonzero_sc_size = metadata['DATA_SUBCARRIER_NUM'] ofdm_pilot = np.array(metadata['OFDM_PILOT']) - ofdm_pilot_f = np.array(metadata['OFDM_PILOT_F']) + if "OFDM_PILOT_F" in metadata.keys(): + ofdm_pilot_f = np.array(metadata['OFDM_PILOT_F']) + else: + if pilot_type == 'zadoff-chu': + _, pilot_f = generate_training_seq(preamble_type='zadoff-chu', seq_length=nonzero_sc_size, cp=cp, upsample=1, reps=1) + else: + _, pilot_f = generate_training_seq(preamble_type='lts', cp=32, upsample=1) + ofdm_pilot_f = pilot_f reciprocal_calib = np.array(metadata['RECIPROCAL_CALIB']) - symbol_length_no_pad = symbol_length - z_padding - num_pilots_per_sym = ((symbol_length_no_pad) // (cp + fft_size)) - ul_sf_num = int(metadata['UL_SYMS']) + samps_per_slot_no_pad = samps_per_slot - z_padding + symbol_per_slot = ((samps_per_slot_no_pad) // (cp + fft_size)) + if 'UL_SYMS' in metadata: + ul_slot_num = int(metadata['UL_SYMS']) + elif 'UL_SLOTS' in metadata: + ul_slot_num = int(metadata['UL_SLOTS']) n_ue = num_cl all_bs_nodes = set(range(hdf5.pilot_samples.shape[3])) @@ -90,7 +103,7 @@ def verify_hdf5(hdf5, frame_i=100, cell_i=0, ofdm_sym_i=0, ant_i =0, # Verify frame_i does not exceed max number of collected frames ref_frame = min(frame_i - n_frm_st, pilot_samples.shape[0]) - print("symbol_length = {}, offset = {}, cp = {}, prefix_len = {}, postfix_len = {}, z_padding = {}, pilot_rep = {}".format(symbol_length, offset, cp, prefix_len, postfix_len, z_padding, num_pilots_per_sym)) + print("samps_per_slot = {}, offset = {}, cp = {}, prefix_len = {}, postfix_len = {}, z_padding = {}, pilot_rep = {}".format(samps_per_slot, offset, cp, prefix_len, postfix_len, z_padding, symbol_per_slot)) # pilot_samples dimensions: # ( #frames, #cells, #pilot subframes or cl ant sending pilots, #bs nodes or # bs ant, #samps per frame * 2 for IQ ) @@ -100,7 +113,7 @@ def verify_hdf5(hdf5, frame_i=100, cell_i=0, ofdm_sym_i=0, ant_i =0, num_bs_ants = pilot_samples.shape[3] samps_mat = np.reshape( - pilot_samples, (num_frames, num_cells, num_cl_tmp, num_bs_ants, symbol_length, 2)) + pilot_samples, (num_frames, num_cells, num_cl_tmp, num_bs_ants, samps_per_slot, 2)) samps = (samps_mat[:, :, :, :, :, 0] + samps_mat[:, :, :, :, :, 1]*1j)*2**-15 @@ -127,7 +140,7 @@ def verify_hdf5(hdf5, frame_i=100, cell_i=0, ofdm_sym_i=0, ant_i =0, # CSI: #Frames, #Cell, #Users, #Pilot Rep, #Antennas, #Subcarrier # For correlation use a fft size of 64 print("*verify_hdf5(): Calling samps2csi with fft_size = {}, offset = {}, bound = {}, cp = {} *".format(fft_size, offset, z_padding, cp)) - csi, _ = hdf5_lib.samps2csi(pilot_samples, num_cl_tmp, symbol_length, fft_size=fft_size, + csi, _ = hdf5_lib.samps2csi(pilot_samples, num_cl_tmp, samps_per_slot, fft_size=fft_size, offset=offset, bound=z_padding, cp=cp, pilot_f=ofdm_pilot_f) cellCSI = csi[:, cell_i, :, :, :, :] @@ -140,7 +153,7 @@ def verify_hdf5(hdf5, frame_i=100, cell_i=0, ofdm_sym_i=0, ant_i =0, else: print(">>> All Iris nodes are good!") - if ofdm_sym_i >= num_pilots_per_sym: # if out of range index, do average + if ofdm_sym_i >= symbol_per_slot: # if out of range index, do average userCSI = np.mean(cellCSI[:, :, :, :, :], 2) else: userCSI = cellCSI[:, :, ofdm_sym_i, :, :] @@ -188,10 +201,10 @@ def verify_hdf5(hdf5, frame_i=100, cell_i=0, ofdm_sym_i=0, ant_i =0, csi_u = csi csi_d = csi if up_calib_offset != offset: - csi_u,_ = hdf5_lib.samps2csi(pilot_samples, num_cl_tmp, symbol_length, fft_size=fft_size, + csi_u,_ = hdf5_lib.samps2csi(pilot_samples, num_cl_tmp, samps_per_slot, fft_size=fft_size, offset=up_calib_offset, bound=z_padding, cp=cp, pilot_f=ofdm_pilot_f) if dn_calib_offset != offset: - csi_d,_ = hdf5_lib.samps2csi(pilot_samples, num_cl_tmp, symbol_length, fft_size=fft_size, + csi_d,_ = hdf5_lib.samps2csi(pilot_samples, num_cl_tmp, samps_per_slot, fft_size=fft_size, offset=dn_calib_offset, bound=z_padding, cp=cp, pilot_f=ofdm_pilot_f) calib_corrected_csi = np.zeros(csi_d.shape, dtype='complex64') calib_corrected_csi[:, :, 0, :, :, :] = csi_d[:, :, 0, :, :, :] @@ -264,7 +277,7 @@ def verify_hdf5(hdf5, frame_i=100, cell_i=0, ofdm_sym_i=0, ant_i =0, # UL Samps: #Frames, #Cell, #Uplink Symbol, #Antennas, #Samples samps_mat = np.reshape( - uplink_samples, (uplink_samples.shape[0], uplink_samples.shape[1], uplink_samples.shape[2], uplink_samples.shape[3], symbol_length, 2)) + uplink_samples, (uplink_samples.shape[0], uplink_samples.shape[1], uplink_samples.shape[2], uplink_samples.shape[3], samps_per_slot, 2)) ul_samps = (samps_mat[:, :, :, :, :, 0] + samps_mat[:, :, :, :, :, 1]*1j)*2**-15 @@ -293,9 +306,9 @@ def verify_hdf5(hdf5, frame_i=100, cell_i=0, ofdm_sym_i=0, ant_i =0, n_frames = ul_samps.shape[0] ul_syms = np.empty((ul_samps.shape[0], ul_samps.shape[1], - ul_samps.shape[2], num_pilots_per_sym, fft_size), dtype='complex64') + ul_samps.shape[2], symbol_per_slot, fft_size), dtype='complex64') # UL Syms: #Frames, #Uplink SF, #Antennas, #OFDM Symbols, #Samples - for i in range(num_pilots_per_sym): + for i in range(symbol_per_slot): ul_syms[:, :, :, i, :] = ul_samps[:, :, :, offset + cp + i*fft_size:offset+cp+(i+1)*fft_size] # UL Syms: #Frames, #OFDM Symbols, #Uplink SF, #Antennas, #Samples ul_syms = np.transpose(ul_syms, (0, 3, 1, 2, 4)) @@ -325,11 +338,11 @@ def verify_hdf5(hdf5, frame_i=100, cell_i=0, ofdm_sym_i=0, ant_i =0, evm = np.empty((ul_data.shape[0], ul_data.shape[1]), dtype='complex64') ul_data_frame_num = int(metadata['UL_DATA_FRAME_NUM']) - txdata = np.empty((ul_data_frame_num, num_cl_tmp, ul_sf_num, - num_pilots_per_sym, fft_size), dtype='complex64') + txdata = np.empty((ul_data_frame_num, num_cl_tmp, ul_slot_num, + symbol_per_slot, fft_size), dtype='complex64') if ul_data_frame_num > 1: tx_file_names = metadata['TX_FD_DATA_FILENAMES'].astype(str) - read_size = 2 * ul_data_frame_num * ul_sf_num * cl_ch_num * num_pilots_per_sym * fft_size + read_size = 2 * ul_data_frame_num * ul_slot_num * cl_ch_num * symbol_per_slot * fft_size cl = 0 for fn in tx_file_names: tx_file_path = hdf5.dirpath + '/' + fn @@ -339,8 +352,8 @@ def verify_hdf5(hdf5, frame_i=100, cell_i=0, ofdm_sym_i=0, ant_i =0, I = np.array(txdata0[0::2]) Q = np.array(txdata0[1::2]) IQ = I + Q * 1j - txdata[:, cl:cl+cl_ch_num, :, :, :] = np.transpose(np.reshape(IQ, (ul_data_frame_num, ul_sf_num, - cl_ch_num, num_pilots_per_sym, fft_size)), (0, 2, 1, 3, 4)) + txdata[:, cl:cl+cl_ch_num, :, :, :] = np.transpose(np.reshape(IQ, (ul_data_frame_num, ul_slot_num, + cl_ch_num, symbol_per_slot, fft_size)), (0, 2, 1, 3, 4)) cl = cl + cl_ch_num rep = n_frames // ul_data_frame_num txdata_ext = np.tile(txdata, (rep, 1, 1, 1, 1)) @@ -352,12 +365,12 @@ def verify_hdf5(hdf5, frame_i=100, cell_i=0, ofdm_sym_i=0, ant_i =0, for f in range(ul_data_frame_num): for i in range(num_cl_tmp): tx_data0 = metadata['OFDM_DATA_CL'+str(i)] - tx_data_mat = np.reshape(tx_data0, (ul_sf_num, num_pilots_per_sym, fft_size)) + tx_data_mat = np.reshape(tx_data0, (ul_slot_num, symbol_per_slot, fft_size)) txdata[f, i, :, :, :] = tx_data_mat txdata_ext = np.tile(txdata, (n_frames, 1, 1, 1, 1)) txdata_ext = txdata_ext[:, :, :, :, data_sc_ind] - ul_sym_i = ul_sf_i * num_pilots_per_sym + ofdm_sym_i + ul_sym_i = ul_sf_i * symbol_per_slot + ofdm_sym_i fig5, axes5 = plt.subplots(nrows=num_cl_tmp//2, ncols=2, squeeze=False, figsize=(10, 8)) fig5.suptitle('Uplink User Constellations (ZF) - Frame %d - Cell %d - UL SF %d' % (ref_frame, cell_i, ul_sym_i)) fig6, axes6 = plt.subplots(nrows=1, ncols=1, squeeze=False, figsize=(10, 8)) @@ -409,15 +422,15 @@ def verify_hdf5(hdf5, frame_i=100, cell_i=0, ofdm_sym_i=0, ant_i =0, for ueIdx in range(n_ue): # UE for bsAntIdx in range(n_ant): # BS ANT - I = pilot_samples[frameIdx, cellIdx, ueIdx, bsAntIdx, 0:symbol_length * 2:2] / 2 ** 15 - Q = pilot_samples[frameIdx, cellIdx, ueIdx, bsAntIdx, 1:symbol_length * 2:2] / 2 ** 15 + I = pilot_samples[frameIdx, cellIdx, ueIdx, bsAntIdx, 0:samps_per_slot * 2:2] / 2 ** 15 + Q = pilot_samples[frameIdx, cellIdx, ueIdx, bsAntIdx, 1:samps_per_slot * 2:2] / 2 ** 15 IQ = I + (Q * 1j) tx_pilot, lts_pks, lts_corr, pilot_thresh, best_pk = pilot_finder(IQ, pilot_type, flip=True, pilot_seq=ofdm_pilot) # Find percentage of LTS peaks within a symbol - # (e.g., in a 4096-sample pilot symbol, we expect 64, 64-long sequences... assuming no CP) - # seq_found[frameIdx, cellIdx, ueIdx, bsAntIdx] = 100 * (lts_pks.size / num_pilots_per_sym) - seq_found[frameIdx, cellIdx, ueIdx, bsAntIdx] = 100 * (peak_map[frameIdx, cellIdx, ueIdx, bsAntIdx] / num_pilots_per_sym) # use matched filter analysis output + # (e.g., in a 4096-sample pilot slot, we expect 64, 64-long sequences... assuming no CP) + # seq_found[frameIdx, cellIdx, ueIdx, bsAntIdx] = 100 * (lts_pks.size / symbol_per_slot) + seq_found[frameIdx, cellIdx, ueIdx, bsAntIdx] = 100 * (peak_map[frameIdx, cellIdx, ueIdx, bsAntIdx] / symbol_per_slot) # use matched filter analysis output # Compute Power of Time Domain Signal rms = np.sqrt(np.mean(IQ * np.conj(IQ))) @@ -429,8 +442,8 @@ def verify_hdf5(hdf5, frame_i=100, cell_i=0, ofdm_sym_i=0, ant_i =0, # Noise if noise_avail: # noise_samples - In = noise_samples[frameIdx, cellIdx, 0, bsAntIdx, 0:symbol_length * 2:2] / 2 ** 15 - Qn = noise_samples[frameIdx, cellIdx, 0, bsAntIdx, 1:symbol_length * 2:2] / 2 ** 15 + In = noise_samples[frameIdx, cellIdx, 0, bsAntIdx, 0:samps_per_slot * 2:2] / 2 ** 15 + Qn = noise_samples[frameIdx, cellIdx, 0, bsAntIdx, 1:samps_per_slot * 2:2] / 2 ** 15 IQn = In + (Qn * 1j) # sio.savemat('test_pwr.mat', {'pilot_t': IQn}) @@ -603,10 +616,10 @@ def analyze_hdf5(hdf5, frame_i=10, cell_i=0, subcarrier_i=7, offset=-1, zoom=0, print('Trace-based Estimation of performance requires presense of noise samples!') print('Noise data not present. Exitting...') return - symbol_length = int(metadata['SYMBOL_LEN']) + samps_per_slot = int(metadata['SLOT_SAMP_LEN']) rate = float(metadata['RATE']) - symbol_num = int(metadata['BS_FRAME_LEN']) - timestep = symbol_length*symbol_num/rate + slot_num = int(metadata['BS_FRAME_LEN']) + timestep = samps_per_slot*slot_num/rate num_cl = int(metadata['CL_NUM']) num_pilots = int(metadata['PILOT_NUM']) prefix_len = int(metadata['PREFIX_LEN']) @@ -619,7 +632,7 @@ def analyze_hdf5(hdf5, frame_i=10, cell_i=0, subcarrier_i=7, offset=-1, zoom=0, nonzero_sc_size = metadata['DATA_SUBCARRIER_NUM'] ofdm_pilot_f = np.array(metadata['OFDM_PILOT_F']) - num_noise_syms = noise_samples.shape[2] + num_noise_slots = noise_samples.shape[2] n_frame = pilot_samples.shape[0] n_cell = pilot_samples.shape[1] n_ue = pilot_samples.shape[2] @@ -629,11 +642,11 @@ def analyze_hdf5(hdf5, frame_i=10, cell_i=0, subcarrier_i=7, offset=-1, zoom=0, # Returns csi with Frame, Cell, User, pilot repetitions, BS ant, Subcarrier # also, iq samples nicely chunked out, same dims, but subcarrier is sample. num_cl_tmp = num_pilots - csi,_ = hdf5_lib.samps2csi(pilot_samples, num_cl_tmp, symbol_length, fft_size=fft_size, + csi,_ = hdf5_lib.samps2csi(pilot_samples, num_cl_tmp, samps_per_slot, fft_size=fft_size, offset=offset, bound=z_padding, cp=cp, pilot_f=ofdm_pilot_f) csi = csi[:, cell_i, :, :, :, :] - noise,_ = hdf5_lib.samps2csi(noise_samples, num_noise_syms, symbol_length, fft_size=fft_size, + noise,_ = hdf5_lib.samps2csi(noise_samples, num_noise_slots, samps_per_slot, fft_size=fft_size, offset=offset, bound=z_padding, cp=cp, pilot_f=ofdm_pilot_f) noise = noise[:, cell_i, :, :, :, :] diff --git a/README.md b/README.md index cc1cbb09..820b8a55 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,15 @@ [![Build Status](https://falcon.ecg.rice.edu:443/buildStatus/icon?job=github_public_renewlab%2Fmaster)](https://falcon.ecg.rice.edu:443/job/github_public_renewlab/job/master/) +# Contents + * [Description](#description) + * [Components](#components) + * [Collect and Process Channel Datasets](#collect-and-process-channel-datasets) + * [Contributing and Support](#contributing-and-support) + * [Documentation](#documentation) + * [License](#license) + * [Acknowledgement](#acknowledgement) + # Description RENEWLab is an open-source software toolbox for the [RENEW massive MIMO platform](https://renew-wireless.org). It provides a user interface through a set of APIs. Users can generate, manipulate, transmit, and receive RF signals on the RENEW hardware by calling these APIs. @@ -12,7 +21,7 @@ RENEWLab is an open-source software toolbox for the [RENEW massive MIMO platform The RENEWLab software suite consists of four components. 1. [Python Development Suite](https://docs.renew-wireless.org/dev-suite/design-flows/python-design-flow/): - It provides a Python-based library which allows users to rapidly program and test the physical layer and the radio layer in real time. It also provides tools for offline data procesing. + It provides a Python-based library which allows users to rapidly program and test the physical layer and the radio layer in real time. It also provides tools for offline data processing. 2. [MATLAB Development Suite](https://docs.renew-wireless.org/dev-suite/design-flows/matlab-design-flow/): It provides a MATLAB-based library which allows users to rapidly develop physical layer algorithms using the MATLAB toolboxes with a highly simplified interface and to perform OTA tests. @@ -65,7 +74,7 @@ $ mkdir build $ cd build $ cmake .. -DCMAKE_BUILD_TYPE=Release -DLOG_LEVEL=info && make -j $ cd ../ -``` +``` Once compiled successfully, the code can be run as the following. 1. If your JSON file includes uplink data transmission, first generate the files, including a random bits source file, as below: @@ -91,6 +100,13 @@ Once compiled successfully, the code can be run as the following. Want to contribute? Great! Please email support@renew-wireless.org. +# Documentation + +Doxygen documentation generation for RENEWLab can be initiated by running the following command from the repository root directory: +`doxygen RENEWLab_doxygen.conf`. +The latest hosted output is located at [RENEWLab Doxygen](https://renew-wireless.org/renewlab-doxy/html/index.html). +Other community resources can be found at the [RENEW Wireless Wiki](https://wiki.renew-wireless.org/) + # License RENEWLab shall be used under the [RENEW OPEN SOURCE LICENSE](https://renew-wireless.org/license). diff --git a/RENEWLab_doxygen.conf b/RENEWLab_doxygen.conf new file mode 100755 index 00000000..8a07ecd7 --- /dev/null +++ b/RENEWLab_doxygen.conf @@ -0,0 +1,2480 @@ +# RENEWLab Doxyfile + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "RENEWLab" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = 1.0.1 + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "RENEW project" + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = ./doc + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 0. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 0 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO, these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = ./ ./README.md + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.inc \ + *.m \ + *.markdown \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.vhd \ + *.vhdl \ + CONTRIBUTING.md \ + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = WEBGUI \ + install_cclibs.sh \ + install_pylibs.sh \ + install_soapy.sh \ + CC/Sounder/third_party \ + CC/Sounder/mufft \ + config_ci.sh + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = README.md + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse-libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = NO + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = YES + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /