...
 
Commits (85)
......@@ -35,6 +35,6 @@ message(STATUS "HDF5_INCLUDE_DIRS: ${HDF5_INCLUDE_DIRS}")
message(STATUS "HDF5_LIBRARIES: ${HDF5_LIBRARIES}")
include_directories(${SoapySDR_INCLUDE_DIRS} ${HDF5_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS})
add_executable(sounder main.cc config.cc receiver.cc recorder.cc sdr-lib.cc comms-lib.cc utils.cc signalHandler.cpp)
add_executable(sounder main.cc ClientRadioSet.cc config.cc Radio.cc receiver.cc recorder.cc sdr-lib.cc sdr-calibrate.cc comms-lib.cc utils.cc signalHandler.cpp)
target_link_libraries(sounder -lpthread -lhdf5_cpp --enable-threadsafe ${SoapySDR_LIBRARIES} ${HDF5_LIBRARIES} ${CMAKE_SOURCE_DIR}/mufft/libmuFFT.a ${CMAKE_SOURCE_DIR}/mufft/libmuFFT-sse.a ${CMAKE_SOURCE_DIR}/mufft/libmuFFT-sse3.a ${CMAKE_SOURCE_DIR}/mufft/libmuFFT-avx.a ${PYTHON_LIBRARIES})
#include "include/ClientRadioSet.h"
#include "include/Radio.h"
#include "include/comms-lib.h"
#include "include/macros.h"
#include "include/utils.h"
#include <SoapySDR/Errors.hpp>
#include <SoapySDR/Formats.hpp>
#include <SoapySDR/Time.hpp>
ClientRadioSet::ClientRadioSet(Config* cfg)
: _cfg(cfg)
{
//load channels
auto channels = Utils::strToChannels(_cfg->clChannel);
radios.reserve(_cfg->nClSdrs);
for (size_t i = 0; i < _cfg->nClSdrs; i++) {
SoapySDR::Kwargs args;
args["timeout"] = "1000000";
args["serial"] = _cfg->cl_sdr_ids.at(i);
try {
radios.push_back(new Radio(args, SOAPY_SDR_CF32, channels, _cfg->rate));
} catch (std::runtime_error) {
std::cerr << "Ignoring serial " << _cfg->cl_sdr_ids.at(i) << std::endl;
continue;
}
auto dev = radios.back()->dev;
SoapySDR::Kwargs info = dev->getHardwareInfo();
for (auto ch : { 0, 1 }) //channels)
{
double rxgain = _cfg->clRxgain_vec[ch][i]; // w/CBRS 3.6GHz [0:105], 2.5GHZ [0:108]
double txgain = _cfg->clTxgain_vec[ch][i]; // w/CBRS 3.6GHz [0:105], 2.5GHZ [0:105]
radios.back()->dev_init(_cfg, ch, rxgain, txgain);
}
initAGC(dev);
}
radios.shrink_to_fit();
//beaconSize + 82 (BS FE delay) + 68 (path delay) + 17 (correlator delay) + 82 (Client FE Delay)
int clTrigOffset = _cfg->beaconSize + 249;
int sf_start = clTrigOffset / _cfg->sampsPerSymbol;
int sp_start = clTrigOffset % _cfg->sampsPerSymbol;
for (size_t i = 0; i < radios.size(); i++) {
auto dev = radios[i]->dev;
std::string corrConfString = "{\"corr_enabled\":true,\"corr_threshold\":" + std::to_string(1) + "}";
dev->writeSetting("CORR_CONFIG", corrConfString);
dev->writeRegisters("CORR_COE", 0, _cfg->coeffs);
std::string tddSched = _cfg->clFrames[i];
for (size_t s = 0; s < _cfg->clFrames[i].size(); s++) {
char c = _cfg->clFrames[i].at(s);
if (c == 'B')
tddSched.replace(s, 1, "G");
else if (c == 'U')
tddSched.replace(s, 1, "T");
else if (c == 'D')
tddSched.replace(s, 1, "R");
}
std::cout << "Client " << i << " schedule: " << tddSched << std::endl;
#ifdef JSON
json tddConf;
tddConf["tdd_enabled"] = true;
tddConf["frame_mode"] = _cfg->frame_mode;
int max_frame_ = (int)(2.0 / ((_cfg->sampsPerSymbol * _cfg->symbolsPerFrame) / _cfg->rate));
tddConf["max_frame"] = max_frame_;
//std::cout << "max_frames for client " << i << " is " << max_frame_ << std::endl;
if (_cfg->clSdrCh == 2)
tddConf["dual_pilot"] = true;
tddConf["frames"] = json::array();
tddConf["frames"].push_back(tddSched);
tddConf["symbol_size"] = _cfg->sampsPerSymbol;
std::string tddConfStr = tddConf.dump();
#else
std::string tddConfStr = "{\"tdd_enabled\":true,\"frame_mode\":" + _cfg->frame_mode + ",";
tddConfStr += "\"symbol_size\":" + std::to_string(_cfg->sampsPerSymbol);
if (_cfg->clSdrCh == 2)
tddConfStr += "\"dual_pilot\":true,";
tddConfStr += ",\"frames\":[\"" + tddSched[i] + "\"]}";
std::cout << tddConfStr << std::endl;
#endif
dev->writeSetting("TDD_CONFIG", tddConfStr);
dev->setHardwareTime(SoapySDR::ticksToTimeNs((sf_start << 16) | sp_start, _cfg->rate), "TRIGGER");
dev->writeSetting("TX_SW_DELAY", "30"); // experimentally good value for dev front-end
dev->writeSetting("TDD_MODE", "true");
// write pilot to FPGA buffers
for (char const& c : _cfg->bsChannel) {
std::string tx_ram = "TX_RAM_";
dev->writeRegisters(tx_ram + c, 0, _cfg->pilot);
}
radios[i]->activateRecv();
radios[i]->activateXmit();
dev->writeSetting("CORR_START", (_cfg->bsChannel == "B") ? "B" : "A");
}
std::cout << __func__ << " done!" << std::endl;
}
ClientRadioSet::~ClientRadioSet(void)
{
for (size_t i = 0; i < radios.size(); i++)
delete radios[i];
}
void ClientRadioSet::radioStop(void)
{
std::string corrConfStr = "{\"corr_enabled\":false}";
std::string tddConfStr = "{\"tdd_enabled\":false}";
for (size_t i = 0; i < _cfg->nClSdrs; i++) {
auto dev = radios[i]->dev;
dev->writeSetting("CORR_CONFIG", corrConfStr);
const auto timeStamp = SoapySDR::timeNsToTicks(dev->getHardwareTime(""), _cfg->rate);
std::cout << "device " << i << ": Frame=" << (timeStamp >> 32)
<< ", Symbol=" << ((timeStamp && 0xFFFFFFFF) >> 16) << std::endl;
dev->writeSetting("TDD_CONFIG", tddConfStr);
dev->writeSetting("TDD_MODE", "false");
radios[i]->reset_DATA_clk_domain();
}
}
int ClientRadioSet::triggers(int i)
{
return (radios[i]->getTriggers());
}
int ClientRadioSet::radioRx(size_t radio_id, void* const* buffs, int numSamps, long long& frameTime)
{
if (radio_id < radios.size()) {
long long frameTimeNs = 0;
int ret = radios[radio_id]->recv(buffs, numSamps, frameTimeNs);
frameTime = frameTimeNs; //SoapySDR::timeNsToTicks(frameTimeNs, _rate);
return ret;
}
std::cout << "invalid radio id " << radio_id << std::endl;
return 0;
}
int ClientRadioSet::radioTx(size_t radio_id, const void* const* buffs, int numSamps, int flags, long long& frameTime)
{
return radios[radio_id]->xmit(buffs, numSamps, flags, frameTime);
}
void ClientRadioSet::initAGC(SoapySDR::Device* dev)
{
/*
* Initialize AGC parameters
*/
#ifdef JSON
json agcConf;
agcConf["agc_enabled"] = _cfg->clAgcEn;
agcConf["agc_gain_init"] = _cfg->clAgcGainInit;
std::string agcConfStr = agcConf.dump();
#else
std::string agcConfStr = "{\"agc_enabled\":" + _cfg->clAgcEn ? "true" : "false";
agcConfStr += ",\"agc_gain_init\":" + std::to_string(_cfg->clAgcGainInit);
agcConfStr += "}";
#endif
dev->writeSetting("AGC_CONFIG", agcConfStr);
}
#include "include/Radio.h"
#include "include/macros.h"
#include <SoapySDR/Errors.hpp>
#include <iostream>
void Radio::dev_init(Config* _cfg, int ch, double rxgain, double txgain)
{
// these params are sufficient to set before DC offset and IQ imbalance calibration
dev->setAntenna(SOAPY_SDR_RX, ch, "TRX");
dev->setBandwidth(SOAPY_SDR_RX, ch, _cfg->bwFilter);
dev->setBandwidth(SOAPY_SDR_TX, ch, _cfg->bwFilter);
dev->setFrequency(SOAPY_SDR_RX, ch, "RF", _cfg->radioRfFreq);
dev->setFrequency(SOAPY_SDR_RX, ch, "BB", _cfg->nco);
dev->setFrequency(SOAPY_SDR_TX, ch, "RF", _cfg->radioRfFreq);
dev->setFrequency(SOAPY_SDR_TX, ch, "BB", _cfg->nco);
// Unified gains for both lime and frontend
dev->setGain(SOAPY_SDR_RX, ch, rxgain); // w/CBRS 3.6GHz [0:105], 2.5GHZ [0:108]
dev->setGain(SOAPY_SDR_TX, ch, txgain); // w/CBRS 3.6GHz [0:105], 2.5GHZ [0:105]
// DC Offset
dev->setDCOffsetMode(SOAPY_SDR_RX, ch, true);
}
void Radio::drain_buffers(std::vector<void*> buffs, int symSamp)
{
/*
* "Drain" rx buffers during initialization
* Input:
* buffs - Vector to which we will write received IQ samples
* symSamp - Number of samples
*
* Output:
* None
*/
long long frameTime = 0;
int flags = 0, r = 0, i = 0;
while (r != -1) {
r = dev->readStream(rxs, buffs.data(), symSamp, flags, frameTime, 0);
i++;
}
//std::cout << "Number of reads needed to drain: " << i << std::endl;
}
Radio::Radio(const SoapySDR::Kwargs& args, const char soapyFmt[],
const std::vector<size_t>& channels, double rate)
{
dev = SoapySDR::Device::make(args);
for (auto ch : channels) {
dev->setSampleRate(SOAPY_SDR_RX, ch, rate);
dev->setSampleRate(SOAPY_SDR_TX, ch, rate);
}
rxs = dev->setupStream(SOAPY_SDR_RX, soapyFmt, channels);
txs = dev->setupStream(SOAPY_SDR_TX, soapyFmt, channels);
reset_DATA_clk_domain();
}
Radio::~Radio(void)
{
deactivateRecv();
deactivateXmit();
dev->closeStream(rxs);
dev->closeStream(txs);
SoapySDR::Device::unmake(dev);
}
int Radio::recv(void* const* buffs, int samples, long long& frameTime)
{
int flags(0);
return dev->readStream(rxs, buffs, samples, flags, frameTime, 1000000);
}
int Radio::activateRecv(const long long rxTime, const size_t numSamps)
{
return dev->activateStream(rxs, 0, rxTime, numSamps);
}
void Radio::deactivateRecv(void)
{
dev->deactivateStream(rxs);
}
int Radio::xmit(const void* const* buffs, int samples, int flags, long long& frameTime)
{
int soapyFlags[] = {
0,
SOAPY_SDR_HAS_TIME,
SOAPY_SDR_HAS_TIME | SOAPY_SDR_END_BURST,
SOAPY_SDR_WAIT_TRIGGER | SOAPY_SDR_END_BURST
};
int flag_args = soapyFlags[flags];
int r = dev->writeStream(txs, buffs, samples, flag_args, frameTime, 1000000);
if (r != samples)
std::cerr << "unexpected writeStream error " << SoapySDR::errToStr(r) << std::endl;
return (r);
}
void Radio::activateXmit(void)
{
dev->activateStream(txs);
}
void Radio::deactivateXmit(void)
{
dev->deactivateStream(txs);
}
int Radio::getTriggers(void) const
{
return std::stoi(dev->readSetting("TRIGGER_COUNT"));
}
void Radio::reset_DATA_clk_domain(void)
{
dev->writeSetting("RESET_DATA_LOGIC", "");
}
/*
Helper functions for signal processing and communications
Generate training sequence for pilots and preambles.
Supports:
......@@ -122,6 +122,82 @@ std::vector<double> CommsLib::convolve(std::vector<std::complex<double>> const&
return out;
}
std::vector<float> CommsLib::magnitudeFFT(std::vector<std::complex<float>> const& samps, std::vector<float> const& win, size_t fftSize)
{
std::vector<std::complex<float>> preFFT(samps.size());
for (size_t n = 0; n < fftSize; n++) {
preFFT[n] = samps[n] * win[n];
}
std::vector<std::complex<float>> fftSamps = CommsLib::FFT(preFFT, fftSize);
// compute magnitudes
std::vector<float> fftMag;
fftMag.reserve(fftSize);
for (size_t n = fftSize / 2; n < fftSize; n++) {
fftMag.push_back(std::norm(fftSamps[n]));
}
for (size_t n = 0; n < fftSize / 2; n++) {
fftMag.push_back(std::norm(fftSamps[n]));
}
std::reverse(fftMag.begin(), fftMag.end()); // not sure why we need reverse here, but this seems to give the right spectrum
return fftMag;
}
// Take ffsSize samples of (1 - cos(x)) / 2 from 0 up to 2pi
std::vector<float> CommsLib::hannWindowFunction(size_t fftSize)
{
std::vector<float> winFcn(1, 0);
double step = 2 * M_PI / fftSize;
// Compute the samples for the first half.
for (size_t n = 1; n < fftSize / 2; n++) {
winFcn.push_back((1 - std::cos(step * n)) / 2);
}
// If a sample lies at the center, just use (1-cos(pi))/2 == 1.
if (fftSize % 2 == 0)
winFcn.push_back(1);
// The second half is a mirror image of the first, so just copy.
for (size_t n = fftSize / 2 + 1; n < fftSize; n++)
winFcn.push_back(winFcn[fftSize - n]);
return winFcn;
}
double CommsLib::windowFunctionPower(std::vector<float> const& win)
{
double windowPower = (0);
size_t N = win.size();
for (size_t n = 0; n < win.size(); n++) {
windowPower += std::norm(win[n]);
}
windowPower = std::sqrt(windowPower / N);
return 20 * std::log10(N * windowPower);
}
template <typename T>
T CommsLib::findTone(std::vector<T> const& magnitude, double winGain, double fftBin, size_t fftSize, const size_t delta)
{
/*
* Find the tone level at a specific interval in the input Power Spectrum
* fftBins assumed interval is [-0.5, 0.5] which is coverted to [0, fftSize-1]
*/
// make sure we don't exceed array bounds
size_t first = std::max<size_t>(0, std::lround((fftBin + 0.5) * fftSize) - delta);
size_t last = std::min<size_t>(fftSize - 1, std::lround((fftBin + 0.5) * fftSize) + delta);
T refLevel = magnitude[last];
for (size_t n = first; n < last; n++) {
if (magnitude[n] > refLevel)
refLevel = magnitude[n];
}
return 10 * std::max(std::log10(refLevel), (T)(-20.0)) - (T)winGain;
}
float CommsLib::measureTone(std::vector<std::complex<float>> const& samps, std::vector<float> const& win, double winGain, double fftBin, size_t fftSize, const size_t delta)
{
return findTone(magnitudeFFT(samps, win, fftSize), winGain, fftBin, fftSize, delta);
}
std::vector<int> CommsLib::getDataSc(int fftSize)
{
std::vector<int> data_sc;
......@@ -157,22 +233,22 @@ std::vector<std::vector<int>> CommsLib::getPilotSc(int fftSize)
return pilot_sc;
}
std::vector<std::complex<float>> CommsLib::IFFT(std::vector<std::complex<float>> in, int fftsize)
std::vector<std::complex<float>> CommsLib::IFFT(std::vector<std::complex<float>> in, int fftSize)
{
std::vector<std::complex<float>> out(in.size());
void* fft_in = mufft_alloc(fftsize * sizeof(std::complex<float>));
void* fft_out = mufft_alloc(fftsize * sizeof(std::complex<float>));
mufft_plan_1d* mufftplan = mufft_create_plan_1d_c2c(fftsize, MUFFT_INVERSE, MUFFT_FLAG_CPU_ANY);
void* fft_in = mufft_alloc(fftSize * sizeof(std::complex<float>));
void* fft_out = mufft_alloc(fftSize * sizeof(std::complex<float>));
mufft_plan_1d* mufftplan = mufft_create_plan_1d_c2c(fftSize, MUFFT_INVERSE, MUFFT_FLAG_CPU_ANY);
memcpy(fft_in, in.data(), fftsize * sizeof(std::complex<float>));
memcpy(fft_in, in.data(), fftSize * sizeof(std::complex<float>));
mufft_execute_plan_1d(mufftplan, fft_out, fft_in);
memcpy(out.data(), fft_out, fftsize * sizeof(std::complex<float>));
memcpy(out.data(), fft_out, fftSize * sizeof(std::complex<float>));
//for (int i = 0; i < fftsize; i++) out[i] /= fftsize;
float max_val = 0;
//int max_ind = 0;
float scale = 0.5;
for (int i = 0; i < fftsize; i++) {
for (int i = 0; i < fftSize; i++) {
if (std::abs(out[i]) > max_val) {
max_val = std::abs(out[i]);
//max_ind = i;
......@@ -180,7 +256,7 @@ std::vector<std::complex<float>> CommsLib::IFFT(std::vector<std::complex<float>>
}
std::cout << "IFFT output is normalized with " << std::to_string(max_val) << std::endl;
//std::cout << "max sample is " << std::to_string(out[max_ind].real()) << "+1j*" << std::to_string(out[max_ind].imag()) << std::endl;
for (int i = 0; i < fftsize; i++)
for (int i = 0; i < fftSize; i++)
out[i] /= (max_val / scale);
mufft_free_plan_1d(mufftplan);
......@@ -189,17 +265,17 @@ std::vector<std::complex<float>> CommsLib::IFFT(std::vector<std::complex<float>>
return out;
}
std::vector<std::complex<float>> CommsLib::FFT(std::vector<std::complex<float>> in, int fftsize)
std::vector<std::complex<float>> CommsLib::FFT(std::vector<std::complex<float>> in, int fftSize)
{
std::vector<std::complex<float>> out(in.size());
void* fft_in = mufft_alloc(fftsize * sizeof(std::complex<float>));
void* fft_out = mufft_alloc(fftsize * sizeof(std::complex<float>));
mufft_plan_1d* mufftplan = mufft_create_plan_1d_c2c(fftsize, MUFFT_FORWARD, MUFFT_FLAG_CPU_ANY);
void* fft_in = mufft_alloc(fftSize * sizeof(std::complex<float>));
void* fft_out = mufft_alloc(fftSize * sizeof(std::complex<float>));
mufft_plan_1d* mufftplan = mufft_create_plan_1d_c2c(fftSize, MUFFT_FORWARD, MUFFT_FLAG_CPU_ANY);
memcpy(fft_in, in.data(), fftsize * sizeof(std::complex<float>));
memcpy(fft_in, in.data(), fftSize * sizeof(std::complex<float>));
mufft_execute_plan_1d(mufftplan, fft_out, fft_in);
memcpy(out.data(), fft_out, fftsize * sizeof(std::complex<float>));
memcpy(out.data(), fft_out, fftSize * sizeof(std::complex<float>));
mufft_free_plan_1d(mufftplan);
mufft_free(fft_in);
......@@ -273,13 +349,19 @@ std::vector<std::vector<double>> CommsLib::getSequence(int N, int type)
// STS - 802.11 Short training sequence (one symbol)
matrix.resize(2);
double sts_re[16] = { 0.04599876, -0.13244371, -0.01347272, 0.1427553, 0.09199751,
0.1427553, -0.01347272, -0.13244371, 0.04599876, 0.00233959,
-0.07852478, -0.01265117, 0, -0.01265117, -0.07852478, 0.00233959 };
double sts_im[16] = { 0.04599876, 0.00233959, -0.07852478, -0.01265117, 0.0,
-0.01265117, -0.07852478, 0.00233959, 0.04599876, -0.13244371,
-0.01347272, 0.1427553, 0.09199751, 0.1427553, -0.01347272, -0.13244371 };
// Normalized sequences
double sts_re[16] = {
0.29137692, -0.9219788, -0.04527321, 1.0,
0.63942015, 1.0, -0.04527321, -0.9219788,
0.29137692, 0.09784041, -0.53748065, -0.17586154,
-0.05666628, -0.17586154, -0.53748065, 0.09784041
};
double sts_im[16] = {
0.34804323, -0.06531818, -0.5540778, 0.0,
0.05666628, 0.0, -0.5540778, -0.06531818,
0.34804323, -0.91909665, -0.1420086, 0.9844149,
0.63942015, 0.9844149, -0.1420086, -0.91909665
};
int size = sizeof(sts_re) / sizeof(sts_re[0]);
matrix[0].resize(size);
......
......@@ -40,6 +40,9 @@ Config::Config(const std::string& jsonfile)
if (bsPresent) {
freq = tddConf.value("frequency", 3.6e9);
rate = tddConf.value("rate", 5e6);
nco = tddConf.value("nco_frequency", 0.75 * rate);
bwFilter = rate + 2 * nco;
radioRfFreq = freq - nco;
int samps = tddConf.value("subframe_size", 0);
prefix = tddConf.value("prefix", 0);
postfix = tddConf.value("postfix", 0);
......@@ -67,6 +70,7 @@ Config::Config(const std::string& jsonfile)
calTxGain[0] = tddConf.value("calTxGainA", 10);
calTxGain[1] = tddConf.value("calTxGainB", 10);
sampleCalEn = tddConf.value("sample_calibrate", false);
imbalanceCalEn = tddConf.value("imbalance_calibrate", false);
beamsweep = tddConf.value("beamsweep", false);
beacon_ant = tddConf.value("beacon_antenna", 0);
max_frame = tddConf.value("max_frame", 0);
......@@ -127,6 +131,7 @@ Config::Config(const std::string& jsonfile)
throw std::invalid_argument("error channel config: not any of A/B/AB!\n");
clSdrCh = (clChannel == "AB") ? 2 : 1;
clAgcEn = tddConfCl.value("agc_en", false);
clAgcGainInit = tddConfCl.value("agc_gain_init", 70); // 0 to 108
clDataMod = tddConfCl.value("modulation", "QPSK");
frame_mode = tddConfCl.value("frame_mode", "continuous_resync");
......@@ -150,6 +155,9 @@ Config::Config(const std::string& jsonfile)
if (!bsPresent) {
freq = tddConfCl.value("frequency", 3.6e9);
rate = tddConfCl.value("rate", 5e6);
nco = tddConfCl.value("nco_frequency", 0.75 * rate);
bwFilter = rate + 2 * nco;
radioRfFreq = freq - nco;
int samps = tddConfCl.value("subframe_size", 0);
prefix = tddConfCl.value("prefix", 0);
postfix = tddConfCl.value("postfix", 0);
......@@ -162,7 +170,6 @@ Config::Config(const std::string& jsonfile)
symbolsPerFrame = clFrames.at(0).size();
}
}
bbf_ratio = 0.75;
// Signal Generation
if (bsPresent or clPresent) {
......@@ -182,17 +189,35 @@ Config::Config(const std::string& jsonfile)
std::vector<std::vector<std::complex<float>>> txdata_freq_dom_conf = txdata_freq_dom;
// Generate Beacon STS Sequence + GOLD Sequence
srand(time(NULL));
std::vector<std::vector<double>> gold_ifft = CommsLib::getSequence(128, CommsLib::GOLD_IFFT);
beacon_ci16.resize(256);
std::vector<std::vector<double>> sts_seq = CommsLib::getSequence(0, CommsLib::STS_SEQ);
int stsSymLen = sts_seq[0].size();
int stsReps = 15;
int stsLen = stsReps * stsSymLen; // length of entire STS sequence
beaconSize = 256 + stsLen;
beacon_ci16.resize(beaconSize); // OBCH: Original gold_ifft(256)+10repsSTS(160)
// Populate gold sequence (two reps, 128 each)
for (int i = 0; i < 128; i++) {
beacon_ci16[i] = std::complex<int16_t>((int16_t)(gold_ifft[0][i] * 32768), (int16_t)(gold_ifft[1][i] * 32768));
beacon_ci16[i + 128] = beacon_ci16[i];
beacon_ci16[stsLen + i] = std::complex<int16_t>((int16_t)(gold_ifft[0][i] * 32768), (int16_t)(gold_ifft[1][i] * 32768));
beacon_ci16[stsLen + i + 128] = beacon_ci16[stsLen + i];
}
// Populate STS (stsReps repetitions)
for (int i = 0; i < stsReps; i++) {
for (int j = 0; j < stsSymLen; j++) {
int16_t re = sts_seq[0][j] * 32768;
int16_t im = sts_seq[1][j] * 32768;
beacon_ci16[stsSymLen * i + j] = std::complex<int16_t>(re, im);
}
}
if (sampsPerSymbol < beaconSize + prefix + postfix) {
std::cout << "Subframe size too small!"
<< " Increasing to "
<< beaconSize << std::endl;
sampsPerSymbol = beaconSize + prefix + postfix;
}
std::vector<std::complex<int16_t>> pre0(prefix, 0);
std::vector<std::complex<int16_t>> post0(sampsPerSymbol - 256 - prefix, 0);
beacon_ci16.insert(beacon_ci16.begin(), pre0.begin(), pre0.end());
beacon_ci16.insert(beacon_ci16.end(), post0.begin(), post0.end());
beacon = Utils::cint16_to_uint32(beacon_ci16, false, "QI");
......
{
"BaseStations" : {
"cells" : 1,
"sdr_id" : ["files/powder-iris-serials.txt"],
"hub_id" : "files/powder-hub-serials.txt",
"frequency" : 2.5e9,
"channel" : "AB",
"rxgainA" : 20,
"txgainA" : 40,
"rxgainB" : 20,
"txgainB" : 40,
"rate" : 5e6,
"frame_schedule" : [
"BGPPGGGGGGGGGGGGGGGG"
],
"max_frame" : 0,
"subframe_size" : 640,
"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"
}
}
{
"Clients" : {
"frequency" : 2.5e9,
"channel" : "A",
"agc_en" : false,
"rxgainA" : [20],
"txgainA" : [40],
"rxgainB" : [20],
"txgainB" : [40],
"rate" : 5e6,
"fft_size" : 64,
"subframe_size" : 640,
"cp_size" : 16,
"prefix" : 82,
"postfix" : 68,
"frame_schedule" : [
"GGPGGGGGGGGGGGGGGGGG"
],
"frame_mode" : "continuous_resync",
"beacon_type" : "gold_ifft",
"modulation" : "QPSK",
"pilot_seq" : "lts-half",
"sdr_id" : [
"RF3E000060"
]
}
}
{
"BaseStations" : {
"cells" : 1,
"sdr_id" : ["files/powder-iris-serials.txt"],
"hub_id" : "files/powder-hub-serials.txt",
"frequency" : 2.5e9,
"channel" : "AB",
"rxgainA" : 20,
"txgainA" : 40,
"rxgainB" : 20,
"txgainB" : 40,
"rate" : 5e6,
"frame_schedule" : [
"BGPPPGGGGGGGGGGGGGGG"
],
"max_frame" : 6000,
"subframe_size" : 640,
"fft_size" : 64,
"cp_size" : 16,
"prefix" : 82,
"postfix" : 68,
"beacon_seq" : "gold_ifft",
"beamsweep" : true,
"sample_calibrate" : true,
"beacon_antenna" : 0,
"pilot_seq" : "lts-half"
},
"Clients" : {
"sdr_id" : [
"RF3E000060"
],
"frequency" : 2.5e9,
"channel" : "AB",
"agc_en" : false,
"agc_gain_init": 70,
"rxgainA" : [20],
"txgainA" : [40],
"rxgainB" : [20],
"txgainB" : [40],
"rate" : 5e6,
"frame_schedule" : [
"GGPPGGGGGGGGGGGGGGGG"
],
"frame_mode" : "continuous_resync",
"subframe_size" : 640,
"fft_size" : 64,
"cp_size" : 16,
"prefix" : 82,
"postfix" : 68,
"beacon_seq" : "gold_ifft",
"pilot_seq" : "lts-half",
"modulation" : "16QAM"
}
}
{
"BaseStations" : {
"cells" : 1,
"sdr_id" : ["files/iris_serials.txt"],
"hub_id" : "files/hub_serials.txt",
"frequency" : 3.6e9,
"channel" : "A",
"sdr_id" : ["files/powder-iris-serials.txt"],
"hub_id" : "files/powder-hub-serials.txt",
"frequency" : 2.5e9,
"channel" : "AB",
"rxgainA" : 20,
"txgainA" : 40,
"rxgainB" : 20,
......@@ -13,8 +13,8 @@
"frame_schedule" : [
"BGPGUGGGGGGGGGGGGGGG"
],
"max_frame" : 4000,
"subframe_size" : 400,
"max_frame" : 6000,
"subframe_size" : 640,
"fft_size" : 64,
"cp_size" : 16,
"prefix" : 82,
......@@ -28,11 +28,12 @@
"Clients" : {
"sdr_id" : [
"RF3C000045"
"RF3E000060"
],
"frequency" : 3.6e9,
"frequency" : 2.5e9,
"channel" : "A",
"agc_en" : false,
"agc_gain_init": 70,
"rxgainA" : [20],
"txgainA" : [40],
"rxgainB" : [20],
......@@ -42,7 +43,7 @@
"GDPGUGGGGGGGGGGGGGGG"
],
"frame_mode" : "continuous_resync",
"subframe_size" : 400,
"subframe_size" : 640,
"fft_size" : 64,
"cp_size" : 16,
"prefix" : 82,
......
{
"BaseStations" : {
"cells" : 1,
"sdr_id" : ["files/iris_serials.txt"],
"hub_id" : "files/hub_serials.txt",
"frequency" : 3.6e9,
"channel" : "A",
"sdr_id" : ["files/powder-iris-serials.txt"],
"hub_id" : "files/powder-hub-serials.txt",
"frequency" : 2.5e9,
"channel" : "AB",
"rxgainA" : 20,
"txgainA" : 35,
"txgainA" : 40,
"rxgainB" : 20,
"txgainB" : 35,
"txgainB" : 40,
"rate" : 5e6,
"frame_schedule" : [
"BGPPGGGGGGGGGGGGGGGG"
"BGPPPGGGGGGGGGGGGGGG"
],
"max_frame" : 0,
"subframe_size" : 400,
"max_frame" : 4000,
"subframe_size" : 640,
"fft_size" : 64,
"cp_size" : 16,
"prefix" : 82,
......@@ -28,23 +28,23 @@
"Clients" : {
"sdr_id" : [
"RF3C000045",
"RF3C000025"
"RF3E000060",
"RF3E000157"
],
"frequency" : 3.6e9,
"frequency" : 2.5e9,
"channel" : "A",
"agc_en" : false,
"rxgainA" : [20, 20],
"txgainA" : [35, 35],
"txgainA" : [40, 40],
"rxgainB" : [20, 20],
"txgainB" : [35, 35],
"txgainB" : [40, 40],
"rate" : 5e6,
"frame_schedule" : [
"GGPGGGGGGGGGGGGGGGGG",
"GGGPGGGGGGGGGGGGGGGG"
],
"frame_mode" : "continuous_resync",
"subframe_size" : 400,
"subframe_size" : 640,
"fft_size" : 64,
"cp_size" : 16,
"prefix" : 82,
......
{
"BaseStations" : {
"cells" : 1,
"sdr_id" : ["files/iris_serials1.txt"],
"hub_id" : "files/hub_serials.txt",
"sdr_id" : ["files/renew-iris-serials1.txt"],
"hub_id" : "files/renew-hub-serials1.txt",
"frequency" : 3.6e9,
"channel" : "A",
"channel" : "AB",
"rxgainA" : 20,
"txgainA" : 40,
"rxgainB" : 20,
"txgainB" : 40,
"rate" : 5e6,
"frame_schedule" : [
"BGPGUGGGGGGGGGGGGGGG"
"BGPPGGGGGGGGGGGGGGGG"
],
"max_frame" : 0,
"subframe_size" : 400,
"subframe_size" : 640,
"fft_size" : 64,
"cp_size" : 16,
"prefix" : 82,
......
......@@ -9,7 +9,7 @@
"txgainB" : [40],
"rate" : 5e6,
"fft_size" : 64,
"subframe_size" : 400,
"subframe_size" : 640,
"cp_size" : 16,
"prefix" : 82,
"postfix" : 68,
......@@ -21,7 +21,7 @@
"modulation" : "QPSK",
"pilot_seq" : "lts-half",
"sdr_id" : [
"RF3C000045"
"RF3E000375"
]
}
}
{
"BaseStations" : {
"cells" : 1,
"sdr_id" : ["files/renew-iris-serials1.txt"],
"hub_id" : "files/renew-hub-serials1.txt",
"frequency" : 3.6e9,
"channel" : "AB",
"rxgainA" : 20,
"txgainA" : 40,
"rxgainB" : 20,
"txgainB" : 40,
"rate" : 5e6,
"frame_schedule" : [
"BGPPGGGGGGGGGGGGGGGG"
],
"max_frame" : 4000,
"subframe_size" : 640,
"fft_size" : 64,
"cp_size" : 16,
"prefix" : 82,
"postfix" : 68,
"beacon_seq" : "gold_ifft",
"beamsweep" : true,
"sample_calibrate" : true,
"beacon_antenna" : 0,
"pilot_seq" : "lts-half"
},
"Clients" : {
"sdr_id" : [
"RF3C000045",
"RF3C000025"
],
"frequency" : 3.6e9,
"channel" : "A",
"agc_en" : false,
"rxgainA" : [50, 50],
"txgainA" : [40, 40],
"rxgainB" : [50, 50],
"txgainB" : [40, 40],
"rate" : 5e6,
"frame_schedule" : [
"GGPGGGGGGGGGGGGGGGGG",
"GGGPGGGGGGGGGGGGGGGG"
],
"frame_mode" : "continuous_resync",
"subframe_size" : 640,
"fft_size" : 64,
"cp_size" : 16,
"prefix" : 82,
"postfix" : 68,
"beacon_seq" : "gold_ifft",
"pilot_seq" : "lts-half",
"modulation" : "16QAM"
}
}
0328
0339
0268
0282
0344
0233
0334
0402
#include "config.h"
#include <SoapySDR/Device.hpp>
class Radio;
class ClientRadioSet {
public:
ClientRadioSet(Config* cfg);
~ClientRadioSet(void);
int triggers(int i);
int radioRx(size_t radio_id, void* const* buffs, int numSamps, long long& frameTime);
int radioTx(size_t radio_id, const void* const* buffs, int numSamps, int flags, long long& frameTime);
void radioStop(void);
private:
void initAGC(SoapySDR::Device* iclSdrs);
Config* _cfg;
std::vector<Radio*> radios;
};
#include "config.h"
#include <SoapySDR/Device.hpp>
class Radio {
private:
SoapySDR::Device* dev;
SoapySDR::Stream* rxs;
SoapySDR::Stream* txs;
void reset_DATA_clk_domain(void);
void dev_init(Config* _cfg, int ch, double rxgain, double txgain);
friend class ClientRadioSet;
friend class BaseRadioSet;
public:
Radio(const SoapySDR::Kwargs& args, const char soapyFmt[], const std::vector<size_t>& channels, double rate);
~Radio(void);
int recv(void* const* buffs, int samples, long long& frameTime);
int activateRecv(const long long rxTime = 0, const size_t numSamps = 0);
void deactivateRecv(void);
int xmit(const void* const* buffs, int samples, int flags, long long& frameTime);
void activateXmit(void);
void deactivateXmit(void);
int getTriggers(void) const;
void drain_buffers(std::vector<void*> buffs, int symSamp);
};
......@@ -23,6 +23,7 @@
#include <string.h> /* for memcpy */
#include <unistd.h>
#include <vector>
#include <thread>
class CommsLib {
public:
......@@ -55,6 +56,12 @@ public:
static std::vector<double> convolve(std::vector<std::complex<double>> const& f, std::vector<std::complex<double>> const& g);
static std::vector<std::complex<double>> csign(std::vector<std::complex<double>> iq);
static inline int hadamard2(int i, int j) { return (__builtin_parity(i & j) != 0 ? -1 : 1); }
static std::vector<float> magnitudeFFT(std::vector<std::complex<float>> const&, std::vector<float> const&, size_t);
static std::vector<float> hannWindowFunction(size_t);
static double windowFunctionPower(std::vector<float> const&);
template <typename T>
static T findTone(std::vector<T> const&, double, double, size_t, const size_t delta = 10);
static float measureTone(std::vector<std::complex<float>> const&, std::vector<float> const&, double, double, size_t, const size_t delta = 10);
//private:
// static inline float** init_qpsk();
// static inline float** init_qam16();
......
......@@ -28,8 +28,10 @@ public:
// common features
double freq;
double bbf_ratio;
double nco; // baseband frequency controlled by NCO
double rate;
double radioRfFreq; // RF frequency set on the radio after NCO adjustments
double bwFilter;
int sampsPerSymbol;
int prefix;
int postfix;
......@@ -50,6 +52,9 @@ public:
std::string ref_sdr;
std::vector<std::vector<std::string>> bs_sdr_ids;
std::vector<std::string> hub_ids;
std::vector<uint32_t> beacon;
std::vector<std::complex<int16_t>> beacon_ci16;
int beaconSize;
size_t beacon_ant;
bool beamsweep;
std::vector<size_t> nBsSdrs;
......@@ -65,6 +70,7 @@ public:
double rxgain[2];
double calTxGain[2];
bool sampleCalEn;
bool imbalanceCalEn;
std::string trace_file;
// Clients features
......@@ -73,12 +79,11 @@ public:
int clSdrCh;
std::string clChannel;
bool clAgcEn;
int clAgcGainInit;
std::string clDataMod;
std::vector<int> data_ind;
std::vector<uint32_t> coeffs;
std::vector<std::complex<int16_t>> coeffs_ci16;
std::vector<uint32_t> beacon;
std::vector<std::complex<int16_t>> beacon_ci16;
std::vector<std::complex<int16_t>> pilot_ci16;
std::vector<uint32_t> pilot;
std::vector<std::vector<int>> pilot_sc;
......
......@@ -25,6 +25,7 @@
#define MAX_FRAME_INC 2000
#define TIME_DELTA 20 //ms
#define SETTLE_TIME_MS 1
// Triggers - Used for Samples Offset Calibration
#define FPGA_IRIS30_TRIGGERS 44
......@@ -32,23 +33,25 @@
#define FPGA_IRIS30_DECR_TIME (1 << 3)
// AGC and Packet Detect
#define FPGA_IRIS030_WR_AGC_ENABLE_FLAG 232
#define FPGA_IRIS030_WR_AGC_RESET_FLAG 236
#define FPGA_IRIS030_WR_IQ_THRESH 240
#define FPGA_IRIS030_WR_NUM_SAMPS_SAT 244
#define FPGA_IRIS030_WR_AGC_ENABLE_FLAG 232
#define FPGA_IRIS030_WR_AGC_RESET_FLAG 236
#define FPGA_IRIS030_WR_IQ_THRESH 240
#define FPGA_IRIS030_WR_NUM_SAMPS_SAT 244
#define FPGA_IRIS030_WR_MAX_NUM_SAMPS_AGC 248
#define FPGA_IRIS030_WR_RSSI_TARGET 252
#define FPGA_IRIS030_WR_RSSI_TARGET 252
#define FPGA_IRIS030_WR_WAIT_COUNT_THRESH 256
#define FPGA_IRIS030_WR_AGC_SMALL_JUMP 260
#define FPGA_IRIS030_WR_AGC_BIG_JUMP 264
#define FPGA_IRIS030_WR_AGC_SMALL_JUMP 260
#define FPGA_IRIS030_WR_AGC_BIG_JUMP 264
#define FPGA_IRIS030_WR_AGC_TEST_GAIN_SETTINGS 268
#define FPGA_IRIS030_WR_AGC_GAIN_INIT 316
// RSSI register Set
#define FPGA_IRIS030_RD_MEASURED_RSSI 284
#define FPGA_IRIS030_RD_MEASURED_RSSI 284
// Packet Detect Register Set
#define FPGA_IRIS030_WR_PKT_DET_THRESH 288
#define FPGA_IRIS030_WR_PKT_DET_THRESH 288
#define FPGA_IRIS030_WR_PKT_DET_NUM_SAMPS 292
#define FPGA_IRIS030_WR_PKT_DET_ENABLE 296
#define FPGA_IRIS030_WR_PKT_DET_ENABLE 296
#define FPGA_IRIS030_WR_PKT_DET_NEW_FRAME 300
#endif
......@@ -26,13 +26,6 @@
#include <sys/socket.h>
#include <sys/types.h>
typedef unsigned short ushort;
struct complex_float {
float real;
float imag;
};
struct Event_data {
int event_type;
int data;
......@@ -62,6 +55,8 @@ struct SampleBuffer {
//std::mutex d_mutex;
//std::condition_variable cond;
class ClientRadioSet;
class Receiver {
public:
// use for create pthread
......@@ -80,7 +75,6 @@ public:
int txStartSym;
unsigned txFrameDelta;
double rate;
struct Radio* radio;
std::string data_file;
int core;
Receiver* ptr;
......@@ -98,13 +92,12 @@ public:
void loopRecv(ReceiverContext* context);
static void* clientTxRx_launch(void* in_context);
void clientTxRx(dev_profile* context);
void getPathLoss();
private:
Config* config_;
struct sockaddr_in servaddr_; /* server address */
int* socket_;
RadioConfig* radioconfig_;
ClientRadioSet* clientRadioSet_;
BaseRadioSet* baseRadioSet_;
int thread_num_;
// pointer of message_queue_
......
#include "config.h"
#include <SoapySDR/Device.hpp>
#include <SoapySDR/Errors.hpp>
#include <SoapySDR/Formats.hpp>
#include <SoapySDR/Time.hpp>
#include <chrono>
#include <complex>
#include <csignal>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <string>
struct Radio {
SoapySDR::Device* dev;
SoapySDR::Stream* rxs;
SoapySDR::Stream* txs;
~Radio(void);
};
class Radio;
class RadioConfig {
class BaseRadioSet {
public:
RadioConfig(Config* cfg);
~RadioConfig();
BaseRadioSet(Config* cfg);
~BaseRadioSet(void);
void radioTx(const void* const* buffs);
void radioRx(void* const* buffs);
int radioTx(size_t, const void* const* buffs, int flags, long long& frameTime);
int radioRx(size_t, void* const* buffs, long long& frameTime);
void radioStart();
void radioStop();
void radioConfigure();
std::vector<struct Radio> radios;
int radioTx(size_t radio_id, const void* const* buffs, int flags, long long& frameTime);
int radioRx(size_t radio_id, void* const* buffs, long long& frameTime);
void radioStart(void);
void radioStop(void);
private:
// use for create pthread
struct RadioConfigContext {
RadioConfig* ptr;
struct BaseRadioContext {
BaseRadioSet* brs;
std::atomic_int* threadCount;
int tid;
int cell;
};
void initBSRadio(RadioConfigContext* context);
void init(BaseRadioContext* context);
void configure(BaseRadioContext* context);
static void* initBSRadio_launch(void* in_context);
void readSensors();
static void* init_launch(void* in_context);
static void* configure_launch(void* in_context);
void radioTrigger();
void initAGC(SoapySDR::Device* iclSdrs);
void radioTrigger(void);
void sync_delays(int cellIdx);
SoapySDR::Device* baseRadio(int cellId);
SoapySDR::Device* baseRadio(size_t cellId);
void collectCSI(bool&);
static void drain_buffers(SoapySDR::Device* ibsSdrs, SoapySDR::Stream* istream, std::vector<void*> buffs, int symSamp);
static void dciqMinimize(SoapySDR::Device*, SoapySDR::Device*, int, size_t, double, double);
static void setIQBalance(SoapySDR::Device*, int, size_t, int, int);
static void adjustCalibrationGains(std::vector<SoapySDR::Device*>, SoapySDR::Device*, size_t, double, bool plot = false);
static std::vector<std::complex<float>> snoopSamples(SoapySDR::Device*, size_t, size_t);
void dciqCalibrationProc(size_t);
void readSensors(void);
Config* _cfg;
std::vector<SoapySDR::Device*> hubs;
std::vector<std::vector<struct Radio>> bsRadios; // [cell, iris]
std::vector<int> nBsAntennas;
#if 0