...
 
Commits (17)
#include "include/BaseRadioSet.h"
#include "include/Radio.h"
#include "include/comms-lib.h"
#include "include/macros.h"
#include "include/matplotlibcpp.h"
#include "include/sdr-lib.h"
#include "include/utils.h"
namespace plt = matplotlibcpp;
std::vector<std::complex<float>> BaseRadioSet::snoopSamples(SoapySDR::Device* dev, size_t channel, size_t readSize)
static std::vector<std::complex<float>> snoopSamples(SoapySDR::Device* dev, size_t channel, size_t readSize)
{
std::vector<uint32_t> samps_int = dev->readRegisters("RX_SNOOPER", channel, readSize);
std::vector<std::complex<float>> samps = Utils::uint32tocfloat(samps_int, "IQ");
return samps;
}
void BaseRadioSet::adjustCalibrationGains(std::vector<SoapySDR::Device*> rxDevs, SoapySDR::Device* txDev, size_t channel, double fftBin, bool plot)
static void adjustCalibrationGains(std::vector<SoapySDR::Device*> rxDevs, SoapySDR::Device* txDev,
size_t channel, double fftBin, bool plot = false)
{
using std::cout;
using std::endl;
......@@ -51,7 +52,7 @@ void BaseRadioSet::adjustCalibrationGains(std::vector<SoapySDR::Device*> rxDevs,
std::vector<float> toneLevels(rxDevsSize, 0);
size_t remainingRadios = adjustedRadios.size();
for (size_t r = 0; r < rxDevsSize; r++) {
const auto samps = BaseRadioSet::snoopSamples(rxDevs[r], channel, N);
const auto samps = snoopSamples(rxDevs[r], channel, N);
auto toneLevel = CommsLib::measureTone(samps, win, windowGain, fftBin, N);
if (toneLevel >= targetLevel) {
adjustedRadios[r] = true;
......@@ -85,7 +86,7 @@ void BaseRadioSet::adjustCalibrationGains(std::vector<SoapySDR::Device*> rxDevs,
for (size_t r = 0; r < rxDevsSize; r++) {
if (adjustedRadios[r])
continue;
const auto samps = BaseRadioSet::snoopSamples(rxDevs[r], channel, N);
const auto samps = snoopSamples(rxDevs[r], channel, N);
float toneLevel = CommsLib::measureTone(samps, win, windowGain, fftBin, N);
if (toneLevel >= targetLevel) {
adjustedRadios[r] = true;
......@@ -117,7 +118,7 @@ void BaseRadioSet::adjustCalibrationGains(std::vector<SoapySDR::Device*> rxDevs,
for (size_t r = 0; r < rxDevsSize; r++) {
if (adjustedRadios[r])
continue;
const auto samps = BaseRadioSet::snoopSamples(rxDevs[r], channel, N);
const auto samps = snoopSamples(rxDevs[r], channel, N);
auto toneLevel = CommsLib::measureTone(samps, win, windowGain, fftBin, N);
if (toneLevel > targetLevel) {
adjustedRadios[r] = true;
......@@ -145,7 +146,7 @@ void BaseRadioSet::adjustCalibrationGains(std::vector<SoapySDR::Device*> rxDevs,
for (size_t r = 0; r < rxDevsSize; r++) {
if (adjustedRadios[r])
continue;
auto samps = BaseRadioSet::snoopSamples(rxDevs[r], channel, N);
auto samps = snoopSamples(rxDevs[r], channel, N);
float toneLevel = CommsLib::measureTone(samps, win, windowGain, fftBin, N);
if (toneLevel > targetLevel) {
adjustedRadios[r] = true;
......@@ -180,7 +181,7 @@ void BaseRadioSet::adjustCalibrationGains(std::vector<SoapySDR::Device*> rxDevs,
std::cout << rxDevsSize - remainingRadios << " radios reached target level" << std::endl;
}
void BaseRadioSet::setIQBalance(SoapySDR::Device* dev, int direction, size_t channel, int gcorr, int iqcorr)
static void setIQBalance(SoapySDR::Device* dev, int direction, size_t channel, int gcorr, int iqcorr)
{
auto gcorri = (gcorr < 0) ? 2047 - std::abs(gcorr) : 2047;
auto gcorrq = (gcorr > 0) ? 2047 - std::abs(gcorr) : 2047;
......@@ -190,7 +191,7 @@ void BaseRadioSet::setIQBalance(SoapySDR::Device* dev, int direction, size_t cha
dev->setIQBalance(direction, channel, IQcorr);
}
void BaseRadioSet::dciqMinimize(SoapySDR::Device* targetDev, SoapySDR::Device* refDev, int direction, size_t channel, double rxCenterTone, double txCenterTone)
static void dciqMinimize(SoapySDR::Device* targetDev, SoapySDR::Device* refDev, int direction, size_t channel, double rxCenterTone, double txCenterTone)
{
size_t N = 1024;
std::vector<float> win = CommsLib::hannWindowFunction(N);
......@@ -276,7 +277,7 @@ void BaseRadioSet::dciqMinimize(SoapySDR::Device* targetDev, SoapySDR::Device* r
for (int i = start; i < stop; i += step) {
const int gcorr = ((iter % 2) == 0) ? i : bestgcorr;
const int iqcorr = ((iter % 2) == 1) ? i : bestiqcorr;
BaseRadioSet::setIQBalance(targetDev, direction, channel, gcorr, iqcorr);
setIQBalance(targetDev, direction, channel, gcorr, iqcorr);
//measure the efficacy
std::this_thread::sleep_for(std::chrono::milliseconds(SETTLE_TIME_MS));
......@@ -293,7 +294,7 @@ void BaseRadioSet::dciqMinimize(SoapySDR::Device* targetDev, SoapySDR::Device* r
}
//apply the ideal correction
BaseRadioSet::setIQBalance(targetDev, direction, channel, bestgcorr, bestiqcorr);
setIQBalance(targetDev, direction, channel, bestgcorr, bestiqcorr);
auto gcorri = (bestgcorr < 0) ? 2047 - std::abs(bestgcorr) : 2047;
auto gcorrq = (bestgcorr > 0) ? 2047 - std::abs(bestgcorr) : 2047;
std::cout << "Optimized IQ Imbalance Setting: GCorr (" << gcorri << "," << gcorrq << "), iqcorr=" << bestiqcorr << "\n";
......@@ -350,12 +351,12 @@ void BaseRadioSet::dciqCalibrationProc(size_t channel)
// Tune rx gains for calibration on all radios except reference radio
// Tune tx gain on reference radio
BaseRadioSet::adjustCalibrationGains(allButRefDevs, refDev, channel, toneBBFreq / sampleRate, true);
adjustCalibrationGains(allButRefDevs, refDev, channel, toneBBFreq / sampleRate, true);
// Minimize Rx DC offset and IQ Imbalance on all receiving radios
// TODO: Parallelize this loop
for (size_t r = 0; r < radioSize - 1; r++) {
BaseRadioSet::dciqMinimize(allButRefDevs[r], allButRefDevs[r], SOAPY_SDR_RX, channel, 0.0, toneBBFreq / sampleRate);
dciqMinimize(allButRefDevs[r], allButRefDevs[r], SOAPY_SDR_RX, channel, 0.0, toneBBFreq / sampleRate);
}
refDev->writeSetting(SOAPY_SDR_TX, channel, "TSP_TSG_CONST", "NONE");
......@@ -382,8 +383,8 @@ void BaseRadioSet::dciqCalibrationProc(size_t channel)
// Tune rx gain for calibraion on reference radio
// Tune tx gain on neighboring radio to reference radio
BaseRadioSet::adjustCalibrationGains(refDevContainer, refRefDev, channel, toneBBFreq / sampleRate);
BaseRadioSet::dciqMinimize(refDev, refDev, SOAPY_SDR_RX, channel, 0.0, toneBBFreq / sampleRate);
adjustCalibrationGains(refDevContainer, refRefDev, channel, toneBBFreq / sampleRate);
dciqMinimize(refDev, refDev, SOAPY_SDR_RX, channel, 0.0, toneBBFreq / sampleRate);
refRefDev->writeSetting(SOAPY_SDR_TX, channel, "TSP_TSG_CONST", "NONE");
refRefDev->writeSetting(SOAPY_SDR_TX, channel, "TX_ENB_OVERRIDE", "false");
......@@ -407,8 +408,8 @@ void BaseRadioSet::dciqCalibrationProc(size_t channel)
// Tune tx gain for calibraion on reference antenna
// Tune rx gain on neighboring radio to reference radio
BaseRadioSet::adjustCalibrationGains(refRefDevContainer, refDev, channel, (toneBBFreq + txToneBBFreq) / sampleRate);
BaseRadioSet::dciqMinimize(refDev, refRefDev, SOAPY_SDR_TX, channel, toneBBFreq / sampleRate, txToneBBFreq / sampleRate);
adjustCalibrationGains(refRefDevContainer, refDev, channel, (toneBBFreq + txToneBBFreq) / sampleRate);
dciqMinimize(refDev, refRefDev, SOAPY_SDR_TX, channel, toneBBFreq / sampleRate, txToneBBFreq / sampleRate);
// kill TX on ref at the end
refDev->writeSetting(SOAPY_SDR_TX, channel, "TSP_TSG_CONST", "NONE");
......@@ -429,8 +430,8 @@ void BaseRadioSet::dciqCalibrationProc(size_t channel)
allButRefDevs[r]->writeSetting(SOAPY_SDR_TX, channel, "TX_ENB_OVERRIDE", "true");
// Tune tx gain for calibraion of the current radio
// Tune rx gain on the reference radio
BaseRadioSet::adjustCalibrationGains(refDevContainer, allButRefDevs[r], channel, (toneBBFreq + txToneBBFreq) / sampleRate);
BaseRadioSet::dciqMinimize(allButRefDevs[r], refDev, SOAPY_SDR_TX, channel, toneBBFreq / sampleRate, txToneBBFreq / sampleRate);
adjustCalibrationGains(refDevContainer, allButRefDevs[r], channel, (toneBBFreq + txToneBBFreq) / sampleRate);
dciqMinimize(allButRefDevs[r], refDev, SOAPY_SDR_TX, channel, toneBBFreq / sampleRate, txToneBBFreq / sampleRate);
allButRefDevs[r]->writeSetting(SOAPY_SDR_TX, channel, "TX_ENB_OVERRIDE", "false");
allButRefDevs[r]->writeSetting(SOAPY_SDR_TX, channel, "TSP_TSG_CONST", "NONE");
allButRefDevs[r]->setFrequency(SOAPY_SDR_TX, channel, "BB", 0);
......
......@@ -10,7 +10,7 @@
---------------------------------------------------------------------
*/
#include "include/sdr-lib.h"
#include "include/BaseRadioSet.h"
#include "include/Radio.h"
#include "include/comms-lib.h"
#include "include/macros.h"
......
......@@ -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 ClientRadioSet.cc config.cc Radio.cc receiver.cc recorder.cc sdr-lib.cc sdr-calibrate.cc comms-lib.cc utils.cc signalHandler.cpp)
add_executable(sounder main.cc ClientRadioSet.cc config.cc Radio.cc receiver.cc recorder.cc BaseRadioSet.cc BaseRadioSet-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})
......@@ -7,6 +7,8 @@
#include <SoapySDR/Formats.hpp>
#include <SoapySDR/Time.hpp>
static void initAGC(SoapySDR::Device* dev, Config* cfg);
ClientRadioSet::ClientRadioSet(Config* cfg)
: _cfg(cfg)
{
......@@ -33,7 +35,7 @@ ClientRadioSet::ClientRadioSet(Config* cfg)
radios.back()->dev_init(_cfg, ch, rxgain, txgain);
}
initAGC(dev);
initAGC(dev, _cfg);
}
radios.shrink_to_fit();
......@@ -113,7 +115,7 @@ void ClientRadioSet::radioStop(void)
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;
<< ", Symbol=" << ((timeStamp & 0xFFFFFFFF) >> 16) << std::endl;
dev->writeSetting("TDD_CONFIG", tddConfStr);
dev->writeSetting("TDD_MODE", "false");
radios[i]->reset_DATA_clk_domain();
......@@ -142,19 +144,19 @@ int ClientRadioSet::radioTx(size_t radio_id, const void* const* buffs, int numSa
return radios[radio_id]->xmit(buffs, numSamps, flags, frameTime);
}
void ClientRadioSet::initAGC(SoapySDR::Device* dev)
static void initAGC(SoapySDR::Device* dev, Config* cfg)
{
/*
* Initialize AGC parameters
*/
#ifdef JSON
json agcConf;
agcConf["agc_enabled"] = _cfg->clAgcEn;
agcConf["agc_gain_init"] = _cfg->clAgcGainInit;
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);
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);
......
......@@ -16,8 +16,8 @@ void Radio::dev_init(Config* _cfg, int ch, double rxgain, double txgain)
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]
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);
}
......
......@@ -41,10 +41,6 @@ private:
void sync_delays(int cellIdx);
SoapySDR::Device* baseRadio(size_t cellId);
void collectCSI(bool&);
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);
......
......@@ -13,7 +13,6 @@ public:
void radioStop(void);
private:
void initAGC(SoapySDR::Device* iclSdrs);
Config* _cfg;
std::vector<Radio*> radios;
};
......@@ -21,9 +21,9 @@
#include <stdio.h> /* for fprintf */
#include <stdlib.h>
#include <string.h> /* for memcpy */
#include <thread>
#include <unistd.h>
#include <vector>
#include <thread>
class CommsLib {
public:
......
......@@ -101,9 +101,9 @@ public:
std::vector<double> clTxgain_vec[2];
std::vector<double> clRxgain_vec[2];
size_t getPackageLength()
size_t getPackageDataLength()
{
return sizeof(int) * 4 + sizeof(ushort) * (size_t)sampsPerSymbol * 2;
return (2 * sampsPerSymbol * sizeof(short));
}
size_t getNumAntennas();
int getClientId(int, int);
......
......@@ -12,8 +12,8 @@
#ifndef DATARECEIVER_HEADER
#define DATARECEIVER_HEADER
#include "BaseRadioSet.h"
#include "concurrentqueue.h"
#include "sdr-lib.h"
#include <algorithm>
#include <arpa/inet.h>
#include <cassert>
......@@ -69,14 +69,6 @@ public:
struct dev_profile {
int tid;
int nsamps;
int txSyms;
int rxSyms;
int txStartSym;
unsigned txFrameDelta;
double rate;
std::string data_file;
int core;
Receiver* ptr;
};
......@@ -89,9 +81,9 @@ public:
std::vector<pthread_t> startClientThreads();
void go();
static void* loopRecv_launch(void* in_context);
void loopRecv(ReceiverContext* context);
void loopRecv(int tid, int core_id, SampleBuffer* rx_buffer);
static void* clientTxRx_launch(void* in_context);
void clientTxRx(dev_profile* context);
void clientTxRx(int tid);
void getPathLoss();
private:
......
......@@ -56,13 +56,6 @@ std::vector<pthread_t> Receiver::startClientThreads()
// record the thread id
dev_profile* profile = new dev_profile;
profile->tid = i;
profile->rate = config_->rate;
profile->nsamps = config_->sampsPerSymbol;
profile->txSyms = config_->clULSymbols[i].size();
profile->rxSyms = config_->clDLSymbols[i].size();
profile->txStartSym = config_->clULSymbols[i].empty() ? 0 : config_->clULSymbols[i][0];
profile->txFrameDelta = frameTimeDelta;
profile->core = i + 1 + config_->rx_thread_num + config_->task_thread_num;
profile->ptr = this;
// start socket thread
if (pthread_create(&cl_thread_, NULL, Receiver::clientTxRx_launch, profile) != 0) {
......@@ -86,9 +79,9 @@ std::vector<pthread_t> Receiver::startRecvThreads(SampleBuffer* rx_buffer, unsig
// record the thread id
ReceiverContext* context = new ReceiverContext;
context->ptr = this;
context->buffer = rx_buffer;
context->core_id = in_core_id;
context->tid = i;
context->buffer = rx_buffer;
// start socket thread
if (pthread_create(&created_threads[i], NULL, Receiver::loopRecv_launch, context) != 0) {
perror("socket recv thread create failed");
......@@ -118,17 +111,17 @@ void Receiver::go()
void* Receiver::loopRecv_launch(void* in_context)
{
ReceiverContext* context = (ReceiverContext*)in_context;
context->ptr->loopRecv(context);
auto me = context->ptr;
auto tid = context->tid;
auto core_id = context->core_id;
auto buffer = context->buffer;
delete context;
me->loopRecv(tid, core_id, buffer);
return 0;
}
void Receiver::loopRecv(ReceiverContext* context)
void Receiver::loopRecv(int tid, int core_id, SampleBuffer* rx_buffer)
{
SampleBuffer* rx_buffer = context->buffer;
int tid = context->tid;
int core_id = context->core_id;
delete context;
if (config_->core_alloc) {
printf("pinning thread %d to core %d\n", tid, core_id + tid);
if (pin_to_core(core_id + tid) != 0) {
......@@ -150,7 +143,8 @@ void Receiver::loopRecv(ReceiverContext* context)
moodycamel::ProducerToken local_ptok(*message_queue_);
const int bsSdrCh = config_->bsChannel.length();
int buffer_chunk_size = rx_buffer[0].buffer.size() / config_->getPackageLength();
size_t packageLength = sizeof(Package) + config_->getPackageDataLength();
int buffer_chunk_size = rx_buffer[0].buffer.size() / packageLength;
// handle two channels at each radio
// this is assuming buffer_chunk_size is at least 2
......@@ -183,8 +177,9 @@ void Receiver::loopRecv(ReceiverContext* context)
}
// Receive data into buffers
size_t packageLength = sizeof(Package) + config_->getPackageDataLength();
for (auto ch = 0; ch < bsSdrCh; ++ch) {
pkg[ch] = (Package*)(buffer + (cursor + ch) * config_->getPackageLength());
pkg[ch] = (Package*)(buffer + (cursor + ch) * packageLength);
samp[ch] = pkg[ch]->data;
}
long long frameTime;
......@@ -228,34 +223,37 @@ void* Receiver::clientTxRx_launch(void* in_context)
{
dev_profile* context = (dev_profile*)in_context;
Receiver* receiver = context->ptr;
receiver->clientTxRx(context);
int tid = context->tid;
delete context;
receiver->clientTxRx(tid);
return 0;
}
void Receiver::clientTxRx(dev_profile* context)
void Receiver::clientTxRx(int tid)
{
int tid = context->tid;
int txSyms = context->txSyms;
int rxSyms = context->rxSyms;
int txStartSym = context->txStartSym;
unsigned txFrameDelta = context->txFrameDelta;
int NUM_SAMPS = context->nsamps;
int txSyms = config_->clULSymbols[tid].size();
int rxSyms = config_->clDLSymbols[tid].size();
int txStartSym = config_->clULSymbols[tid].empty() ? 0 : config_->clULSymbols[tid][0];
double frameTime = config_->sampsPerSymbol * config_->clFrames[0].size() * 1e3 / config_->rate; // miliseconds
unsigned txFrameDelta = (unsigned)(std::ceil(TIME_DELTA / frameTime));
int NUM_SAMPS = config_->sampsPerSymbol;
if (config_->core_alloc) {
printf("pinning client thread %d to core %d\n", tid, context->core);
if (pin_to_core(context->core) != 0) {
printf("pin client thread %d to core %d failed\n", tid, context->core);
int core = tid + 1 + config_->rx_thread_num + config_->task_thread_num;
printf("pinning client thread %d to core %d\n", tid, core);
if (pin_to_core(core) != 0) {
printf("pin client thread %d to core %d failed\n", tid, core);
exit(0);
}
}
//while(!d_mutex.try_lock()){}
//thread_count++;
//std::cout << "Thread " << tid << ", txSyms " << txSyms << ", rxSyms " << rxSyms << ", txStartSym " << txStartSym << ", rate " << context->rate << ", txFrameDelta " << txFrameDelta << ", nsamps " << NUM_SAMPS << std::endl;
//std::cout << "Thread " << tid << ", txSyms " << txSyms << ", rxSyms " << rxSyms << ", txStartSym " << txStartSym << ", rate " << config_->rate << ", txFrameDelta " << txFrameDelta << ", nsamps " << NUM_SAMPS << std::endl;
//d_mutex.unlock();
delete context;
std::vector<std::complex<float> > buffs(NUM_SAMPS, 0);
std::vector<std::complex<float>> buffs(NUM_SAMPS, 0);
std::vector<void*> rxbuff(2);
rxbuff[0] = buffs.data();
rxbuff[1] = buffs.data();
......
This diff is collapsed.
......@@ -2,8 +2,8 @@ function y = getRxVec(tx_data, n_bs, n_ue, chan_type, snr, bs_param, ue_param, h
%%% Returns Rx vector passed through the channel type given in the input
%%% list.
assert(length(bs_param) == n_bs);
assert(length(ue_param) == n_ue);
assert(length(bs_param.id) == n_bs);
assert(length(ue_param.id) == n_ue);
if chan_type == "awgn"
% AWGN
......@@ -210,8 +210,8 @@ elseif chan_type == "iris"
node_bs.sdrrxsetup();
tdd_sched_index = 1; % for uplink only one frame schedule is sufficient
node_bs.set_tddconfig(1, tdd_sched_index); % configure the BS: schedule etc.
node_ue.set_tddconfig(0, tdd_sched_index);
node_bs.set_tddconfig(1, bs_param.tdd_sched(tdd_sched_index)); % configure the BS: schedule etc.
node_ue.set_tddconfig(0, ue_param.tdd_sched(tdd_sched_index));
node_bs.sdr_setupbeacon(); % Burn beacon to the BS(1) RAM
......
......@@ -22,25 +22,25 @@ classdef iris_py < handle
use_hub = 0;
py_obj_hub;
% Parameters to feed python (pun very intended!)
serial_ids;
n_sdrs; % number of Iris boards in a chain
sample_rate;
tx_freq;q
rx_freq;
bw;
tx_gain;
rx_gain;
n_samp;
tdd_sched;
n_zpad_samp; %number of zero-padding samples
serial_ids = [];
n_sdrs = 0; % number of Iris boards in a chain
sample_rate = 0;
tx_freq = 0;
rx_freq = 0;
bw = 0;
tx_gain = 0;
rx_gain = 0;
n_samp = 0;
tdd_sched = "";
n_zpad_samp = 0; %number of zero-padding samples
n_frame = 10;
is_bs = 1;
end
methods
function obj = iris_py(sdr_params, hub_id) % User must put 0 in hub_params in case hub is not used!
if nargin > 0
if ~isempty(hub_id)
if ~isempty(hub_id)
disp('Using hub with ID:');
id_str = convertStringsToChars(hub_id);
obj.py_obj_hub = py.hub_py.Hub_py( pyargs('serial_id',id_str));
......@@ -62,25 +62,28 @@ classdef iris_py < handle
obj.n_zpad_samp = sdr_params.n_zpad_samp;
for ipy=1:obj.n_sdrs
id_str = convertStringsToChars( obj.serial_ids(ipy));
id_str = convertStringsToChars(obj.serial_ids(ipy));
py_obj = py.iris_py.Iris_py( pyargs('serial_id',id_str,...
'tx_freq', obj.tx_freq, 'rx_freq', obj.rx_freq,...
'tx_gain',obj.tx_gain,'rx_gain',obj.rx_gain,...
'sample_rate',obj.sample_rate, 'n_samp', obj.n_samp) );%,...
%'max_frames',obj.n_frame) );
'sample_rate',obj.sample_rate, 'n_samp', obj.n_samp) );
obj.py_obj_array(ipy,:) = {py_obj};
end
end
end
function sdr_set_n_frame(obj, n_frame)
obj.n_frame = n_frame;
end
% NB: Need to redefine py_obj as array to be parsed in all the
% functions that follow
function sdrtrigger(obj, trig)
function sdrtrigger(obj)
if ~obj.use_hub
obj.py_obj_array{1}.set_trigger(pyargs('trig',trig)); % The trigger is set only on the fist node
obj.py_obj_array{1}.set_trigger(); % The trigger is set only on the fist node
else
obj.py_obj_hub.set_trigger(pyargs('trig',trig));
obj.py_obj_hub.set_trigger();
end
end
......@@ -106,57 +109,62 @@ classdef iris_py < handle
function sdr_setupbeacon(obj)
% Assume Beacon only from the first Iris board in the BS array
obj.py_obj_array{1}.burn_beacon();
for ipy = 1:obj.n_sdrs
obj.py_obj_array{ipy}.burn_beacon();
end
end
function set_tddconfig(obj, is_bs, index)
sched = convertStringsToChars(obj.tdd_sched(index));
function set_tddconfig(obj, is_bs, tdd_sched)
obj.is_bs = is_bs;
sched = convertStringsToChars(tdd_sched);
for ipy = 1:obj.n_sdrs
obj.py_obj_array{ipy}.config_sdr_tdd( pyargs('tdd_sched',sched, ...
'is_bs', is_bs, 'prefix_len', obj.n_zpad_samp));
obj.py_obj_array{ipy}.config_sdr_tdd( pyargs('tdd_sched', sched, ...
'is_bs', is_bs, 'prefix_len', obj.n_zpad_samp, 'max_frames', 1));
end
end
function sdrrxsetup(obj)
function set_tddconfig_single(obj, is_bs, tdd_sched, index)
sched = convertStringsToChars(tdd_sched);
obj.py_obj_array{index}.config_sdr_tdd( pyargs('tdd_sched', sched, ...
'is_bs', is_bs, 'prefix_len', obj.n_zpad_samp, 'max_frames', 1));
end
function sdrrxsetup(obj)
for ipy = 1:obj.n_sdrs
obj.py_obj_array{ipy}.setup_stream_rx();
end
end
end
function sdr_activate_rx(obj)
function sdr_activate_rx(obj)
for ipy=1:obj.n_sdrs
obj.py_obj_array{ipy}.activate_stream_rx();
end
end
end
%Assume same data is Tx-ed from all memebers of the array
function sdrtx(obj, data)
re = real(data).';
im = imag(data).';
for ipy = 1:obj.n_sdrs
obj.py_obj_array{ipy}.burn_data( pyargs('data_r', re, 'data_i', im) );
obj.py_obj_array{ipy}.burn_data( pyargs('data_r', real(data), 'data_i', imag(data)) );
end
end
function sdrtx_single(obj, data, index)
re = real(data).';
im = imag(data).';
obj.py_obj_array{index}.burn_data( pyargs('data_r', re, 'data_i', im) );
obj.py_obj_array{index}.burn_data( pyargs('data_r', real(data), 'data_i', imag(data)) );
end
% Read n_frame x n_samp data
function [data, len] = sdrrx(obj, n_samp)
data_raw = zeros(obj.n_sdrs, obj.n_frame*n_samp); % Change this to max frame!
data_raw = zeros(obj.n_sdrs, obj.n_frame * n_samp); % Change this to max frame!
for jf=1:obj.n_frame
%trigger beacon TX
trig = 1;
if ~obj.use_hub
obj.py_obj_array{1}.set_trigger(pyargs('trig',trig)); % The trigger is set only on the fist node
else
obj.py_obj_hub.set_trigger(pyargs('trig',trig));
disp('trig hub!!!!')
%trigger base station
if obj.is_bs
if ~obj.use_hub
obj.py_obj_array{1}.set_trigger(); % The trigger is set only on the fist node
else
obj.py_obj_hub.set_trigger();
disp('trig hub!!!!')
end
end
for ipy = 1:obj.n_sdrs
......
......@@ -88,7 +88,6 @@ class Iris_py:
bw=None,
sample_rate=None,
n_samp=None, # Total number of samples, including zero-pads
max_frames=1,
both_channels=False,
agc_en=False,
):
......@@ -107,7 +106,7 @@ class Iris_py:
self.agc_en = agc_en
self.both_channels = both_channels
self.max_frames = int(max_frames)
self.max_frames = 1
### Setup channel rates, ports, gains, and filters ###
info = self.sdr.getHardwareInfo()
......@@ -161,23 +160,19 @@ class Iris_py:
self.sdr.writeSetting(SOAPY_SDR_TX, 1, 'ENABLE_CHANNEL', 'false')
# Set trigger:
def set_trigger(self, trig=False):
if bool(trig) is True:
self.sdr.writeSetting("TRIGGER_GEN", "")
def set_trigger(self):
self.sdr.writeSetting("TRIGGER_GEN", "")
def set_corr(self):
'''enable the correlator, with inputs from adc'''
self.sdr.writeSetting("CORR_START", "A")
def config_sdr_tdd(self, tdd_sched=None, is_bs=True, prefix_len=0):
def config_sdr_tdd(self, is_bs=True, tdd_sched="G", prefix_len=0, max_frames=1):
'''Configure the TDD schedule and functionality when unchained. Set up the correlator.'''
global corr_threshold, beacon
coe = cfloat2uint32(np.conj(beacon), order='QI')
if tdd_sched is not None:
self.tdd_sched = tdd_sched
else:
self.tdd_sched = "G"
max_frames = self.max_frames
self.tdd_sched = tdd_sched
self.max_frames = int(max_frames)
if bool(is_bs):
conf_str = {"tdd_enabled": True,
"frame_mode": "free_running",
......@@ -322,17 +317,17 @@ if __name__ == '__main__':
parser.add_argument("--rate", type=float, dest="rate",
help="Sample rate", default=5e6)
parser.add_argument("--txGain", type=float, dest="txGain",
help="Optional Tx gain (dB)", default=43)
help="Optional Tx gain (dB)", default=70)
parser.add_argument("--rxGain", type=float, dest="rxGain",
help="Optional Rx gain (dB)", default=20)
help="Optional Rx gain (dB)", default=50)
parser.add_argument("--freq", type=float, dest="freq",
help="Optional Tx freq (Hz)", default=2.5e9)
help="Optional Tx freq (Hz)", default=3.6e9)
parser.add_argument("--bw", type=float, dest="bw",
help="Optional filter bw (Hz)", default=None)
args = parser.parse_args()
siso_bs = Iris_py(
serial_id="0339",
serial_id="0268",
sample_rate=args.rate,
tx_freq=args.freq,
rx_freq=args.freq,
......@@ -343,7 +338,7 @@ if __name__ == '__main__':
)
siso_ue = Iris_py(
serial_id="RF3C000045",
serial_id="RF3C000025",
sample_rate=args.rate,
tx_freq=args.freq,
rx_freq=args.freq,
......@@ -393,7 +388,7 @@ if __name__ == '__main__':
siso_bs.activate_stream_rx()
siso_ue.set_corr()
siso_bs.set_trigger(True)
siso_bs.set_trigger()
wave_rx_a_bs_mn = siso_bs.recv_stream_tdd()
......
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Author(s): C. Nicolas Barati nicobarati@rice.edu
% Rahman Doost-Mohamamdy: doost@rice.edu
%
%---------------------------------------------------------------------
% Original code copyright Mango Communications, Inc.
% Distributed under the WARP License http://warpproject.org/license
% Copyright (c) 2018-2019, Rice University
% RENEW OPEN SOURCE LICENSE: http://renew-wireless.org/license
% ---------------------------------------------------------------------
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
clear
close all;
[version, executable, isloaded] = pyversion;
if ~isloaded
pyversion /usr/bin/python
py.print() %weird bug where py isn't loaded in an external script
end
% Params:
WRITE_PNG_FILES = 0; % Enable writing plots to PNG
%Iris params:
USE_HUB = 0;
TX_FRQ = 3.6e9;
RX_FRQ = TX_FRQ;
TX_GN = 70;
RX_GN = 50;
SMPL_RT = 5e6;
N_FRM = 1;
bs_ids = string.empty();
bs_sched = string.empty();
% Waveform params
% OFDM params
SC_IND_PILOTS = [8 22 44 58]; % Pilot subcarrier indices
SC_IND_DATA = [2:7 9:21 23:27 39:43 45:57 59:64]; % Data subcarrier indices
SC_IND_DATA_PILOT = [2:27 39:64]';
N_SC = 64; % Number of subcarriers
CP_LEN = 16; % Cyclic prefix length
N_SYM_SAMP = N_SC + CP_LEN; % Number of samples that will go over the air
N_ZPAD_PRE = 100; % Zero-padding prefix for Iris
N_ZPAD_POST = 76; % Zero-padding postfix for Iris
N_OFDM_SYM = floor((4096 - N_ZPAD_PRE - N_ZPAD_POST) / N_SYM_SAMP);% Number of OFDM symbols for burst, it needs to be less than 47
% Rx processing params
FFT_OFFSET = 16; % Number of CP samples to use in FFT (on average)
%% Define the preamble
% LTS for fine CFO and channel estimation
lts_f = [0 1 -1 -1 1 1 -1 1 -1 1 -1 -1 -1 -1 -1 1 1 -1 -1 1 -1 1 -1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 ...
1 1 -1 -1 1 1 -1 1 -1 1 1 1 1 1 1 -1 -1 1 1 -1 1 -1 1 1 1 1];
lts_t = ifft(lts_f, 64); %time domain
%% Init Iris nodes
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Set up the Iris experiment
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Create BS Hub and UE objects. Note: BS object is a collection of Iris
% nodes.
if USE_HUB
hub_id = "FH4A000002";
else
hub_id = [];
end
bs_ids = ["RF3E000208", "RF3E000636", "RF3E000632", "RF3E000568", "RF3E000558", "RF3E000633", "RF3E000566", "RF3E000635"];
bs_sched = ["PGRG", "RGPG"]; % All BS schedule, Ref Schedule
N_BS_NODE = length(bs_ids);
REF_ANT = floor(N_BS_NODE/2);
sched_id = ones(1, N_BS_NODE);
sched_id(REF_ANT) = 2;
lts = [lts_t(49:64) lts_t];
N_BS = N_BS_NODE - 1;
DATA_REP = floor(N_OFDM_SYM / N_BS);
data_len = N_BS * DATA_REP * N_SYM_SAMP;
preamble = zeros(N_BS_NODE, data_len);
for jp = 1:N_BS
nid = jp + (jp >= REF_ANT);
for rp = 1:DATA_REP
start_index = ((rp - 1) + (jp - 1) * DATA_REP) * N_SYM_SAMP;
preamble(nid, start_index + 1: start_index + N_SYM_SAMP) = lts;
preamble(REF_ANT, start_index + 1: start_index + N_SYM_SAMP) = lts;
end
end
n_samp = N_ZPAD_PRE + data_len + N_ZPAD_POST;
payload_rx = zeros(N_BS_NODE, data_len);
rx_fft = zeros(N_BS, N_SC);
rx_fft_ref = zeros(N_BS, N_SC);
cal_mat = zeros(N_BS, N_SC);
% Iris nodes' parameters
bs_sdr_params = struct(...
'id', bs_ids, ...
'n_sdrs', N_BS_NODE, ...
'txfreq', TX_FRQ, ...
'rxfreq', RX_FRQ, ...
'txgain', TX_GN, ...
'rxgain', RX_GN, ...
'sample_rate', SMPL_RT, ...
'n_samp', n_samp, ... % number of samples per frame time.
'n_frame', 1, ...
'tdd_sched', bs_sched, ... % number of zero-paddes samples
'n_zpad_samp', N_ZPAD_PRE ...
);
node_bs = iris_py(bs_sdr_params, hub_id); % initialize BS
node_bs.sdrsync(); % synchronize delays only for BS
node_bs.sdrrxsetup();
for i=1:N_BS_NODE
node_bs.set_tddconfig_single(1, bs_sched(sched_id(i)), i); % configure the BS: schedule etc.
tx_data = [zeros(1, N_ZPAD_PRE) preamble(i, :) zeros(1, N_ZPAD_POST)];
node_bs.sdrtx_single(tx_data, i); % Burn data to the UE RAM
end
node_bs.sdr_activate_rx(); % activate reading stream
[rx_vec_iris, data0_len] = node_bs.sdrrx(n_samp); % read data
% process received reciprocity pilots
a = 1;
unos = ones(size(conj(lts)));
for ibs = 1:N_BS_NODE
% Correlation through filtering
v0 = filter(fliplr(conj(preamble(REF_ANT, end - DATA_REP * N_SYM_SAMP: end))), a, rx_vec_iris(ibs, end - DATA_REP * N_SYM_SAMP: end));
v1 = filter(unos, a, abs(rx_vec_iris(ibs, end - DATA_REP * N_SYM_SAMP: end)) .^ 2);
lts_corr = (abs(v0) .^ 2) ./ v1; % normalized correlation
% position of the last peak
[~, max_idx] = max(abs(lts_corr));
rx_data_start = max_idx + (n_samp - DATA_REP * N_SYM_SAMP) - data_len
if rx_data_start < 0
display('bad receive!');
break;
end
payload_rx(ibs, :) = rx_vec_iris(ibs, rx_data_start: rx_data_start + data_len - 1);
for sid = 1:N_BS
for rid = 1:DATA_REP
% nid = sid + (sid >= REF_ANT);
start_index = CP_LEN + ((rid - 1) + (sid - 1) * DATA_REP) * N_SYM_SAMP;
if (ibs == REF_ANT)
rx_fft_ref(sid, :) = rx_fft_ref(sid, :) + fft(payload_rx(ibs, 1 + start_index : start_index + N_SC), N_SC);
else
rx_fft(sid, :) = rx_fft(sid, :) + fft(payload_rx(ibs, 1 + start_index : start_index + N_SC), N_SC);
end
end
end
end
for sid = 1:N_BS
cal_mat(sid, :) = (rx_fft_ref(sid, :) / DATA_REP) ./ (rx_fft(sid, :) / (N_BS * DATA_REP));
end
node_bs.sdr_close();
cf = 0;
cf = cf + 1;
figure(cf);clf;
for i = 1:N_BS
subplot(N_BS, 1, i);
plot(-32:1:31, abs(cal_mat(i, :)));
axis([-40 40 0 5])
grid on;
title('Calibration MAGNITUDE');
end
cf = cf + 1;
figure(cf);clf;
for i = 1:N_BS
subplot(N_BS, 1, i);
plot(-32:1:31, angle(cal_mat(i, :)));
axis([-40 40 -pi pi])
grid on;
title('Calibration ANGLE');
end
This diff is collapsed.
......@@ -21,8 +21,8 @@ if ~isloaded
end
% Params:
N_BS_NODE = 8; % Number of nodes/antennas at the BS
N_UE = 2; % Number of UE nodes
N_BS_NODE = 8;
N_UE = 2;
WRITE_PNG_FILES = 0; % Enable writing plots to PNG
SIM_MOD = 0;
DEBUG = 0;
......@@ -35,21 +35,42 @@ else
%Iris params:
TX_SCALE = 0.5; % Scale for Tx waveform ([0:1])
chan_type = "iris";
USE_HUB = 1;
USE_HUB = 0;
TX_FRQ = 2.5e9;
RX_FRQ = TX_FRQ;
TX_GN = 45;
TX_GN_ue = 48;
RX_GN = 23;
TX_GN = 70;
TX_GN_ue = 70;
RX_GN = 60;
SMPL_RT = 5e6;
N_FRM = 50;
bs_ids = string.empty();
ue_ids = string.empty();
ue_scheds = string.empty();
end
fprintf("Channel type: %s \n",chan_type);
if USE_HUB
% Using chains of different size requires some internal
% calibration on the BS. This functionality will be added later.
% For now, we use only the 4-node chains:
bs_ids = ["RF3E000134", "RF3E000191", "RF3E000171", "RF3E000105",...
"RF3E000053", "RF3E000177", "RF3E000192", "RF3E000117",...
"RF3E000183", "RF3E000152", "RF3E000123", "RF3E000178", "RF3E000113", "RF3E000176", "RF3E000132", "RF3E000108", ...
"RF3E000143", "RF3E000160", "RF3E000025", "RF3E000034",...
"RF3E000189", "RF3E000024", "RF3E000139", "RF3E000032", "RF3E000154", "RF3E000182", "RF3E000038", "RF3E000137", ...
"RF3E000103", "RF3E000180", "RF3E000181", "RF3E000188"];
hub_id = "FH4A000001";
else
bs_ids = ["RF3E000189", "RF3E000024", "RF3E000139", "RF3E000032", "RF3E000154", "RF3E000182", "RF3E000038", "RF3E000137"];
end
ue_ids= ["RF3E000060", "RF3E000157"];
N_BS_NODE = length(bs_ids); % Number of nodes/antennas at the BS
N_UE = length(ue_ids); % Number of UE nodes
end
fprintf("Channel type: %s \n",chan_type);
MIMO_ALG = 'ZF'; % MIMO ALGORITHM: ZF or Conjugate
......@@ -147,25 +168,7 @@ if (SIM_MOD)
else
% Create BS Hub and UE objects. Note: BS object is a collection of Iris
% nodes.
if USE_HUB
% Using chains of different size requires some internal
% calibration on the BS. This functionality will be added later.
% For now, we use only the 4-node chains:
bs_ids = ["RF3E000134", "RF3E000191", "RF3E000171", "RF3E000105",...
"RF3E000053", "RF3E000177", "RF3E000192", "RF3E000117",...
"RF3E000183", "RF3E000152", "RF3E000123", "RF3E000178", "RF3E000113", "RF3E000176", "RF3E000132", "RF3E000108", ...
"RF3E000143", "RF3E000160", "RF3E000025", "RF3E000034",...
"RF3E000189", "RF3E000024", "RF3E000139", "RF3E000032", "RF3E000154", "RF3E000182", "RF3E000038", "RF3E000137", ...
"RF3E000103", "RF3E000180", "RF3E000181", "RF3E000188"];
hub_id = "FH4A000001";
else
bs_ids = ["RF3E000189", "RF3E000024", "RF3E000139", "RF3E000032", "RF3E000154", "RF3E000182", "RF3E000038", "RF3E000137"];
end
ue_ids= ["RF3E000060", "RF3E000145"];
bs_sched = ["BGGGGGGGGGGRGGGG"]; % BS schedule
ue_sched = ["GGGGGGGGGGGPGGGG"]; % UE schedule
......
......@@ -23,8 +23,7 @@ end
WRITE_PNG_FILES = 0; % Enable writing plots to PNG
CHANNEL = 11; % Channel to tune Tx and Rx radios
SIM_MOD = 0;
N_BS_NODE = 32;
N_UE = 1;
if SIM_MOD
chan_type = "awgn";
......@@ -42,11 +41,11 @@ else
%Iris params:
USE_HUB = 1;
TX_FRQ = 2.5e9;
USE_HUB = 0;
TX_FRQ = 2.5e9;
RX_FRQ = TX_FRQ;
TX_GN = 40;
RX_GN = 20;
TX_GN = 70;
RX_GN = 60;
SMPL_RT = 5e6;
N_FRM = 10;
bs_ids = string.empty();
......@@ -163,10 +162,13 @@ else
else
bs_ids = ["RF3E000189", "RF3E000024", "RF3E000139", "RF3E000032", "RF3E000154", "RF3E000182", "RF3E000038", "RF3E000137"];
end
ue_id= ["RF3E000060"];
ue_ids= ["RF3E000060"];
N_BS_NODE = length(bs_ids);
N_UE = length(ue_ids);
bs_sched = ["BGGGGGRG"]; % BS schedule
ue_sched = ["GGGGGGPG"]; % UE schedule
......
......@@ -49,8 +49,8 @@ N_BS_NODE = 1;
N_UE = 1;
TX_FRQ = 2.5e9;
RX_FRQ = TX_FRQ;
TX_GN = 43;
RX_GN = 20;
TX_GN = 70;
RX_GN = 60;
SMPL_RT = 5e6;
N_FRM = 10;
......