1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-12-01 12:37:11 -05:00

BladeRF2 MIMO

This commit is contained in:
f4exb 2020-11-10 19:09:44 +01:00
parent e1178fd81b
commit 66fbde74a9
19 changed files with 4840 additions and 5 deletions

View File

@ -1,3 +1,7 @@
project(samplemimo) project(samplemimo)
if(ENABLE_BLADERF AND LIBBLADERF_FOUND)
add_subdirectory(bladerf2mimo)
endif()
add_subdirectory(testmi) add_subdirectory(testmi)

View File

@ -0,0 +1,66 @@
project(bladerf2mimo)
set(bladerf2mimo_SOURCES
bladerf2mimo.cpp
bladerf2mimoplugin.cpp
bladerf2mithread.cpp
bladerf2mothread.cpp
bladerf2mimosettings.cpp
bladerf2mimowebapiadapter.cpp
)
set(bladerf2mimo_HEADERS
bladerf2mimo.h
bladerf2mimoplugin.h
bladerf2mithread.h
bladerf2mothread.h
bladerf2mimosettings.h
bladerf2mimowebapiadapter.h
)
include_directories(
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
${CMAKE_SOURCE_DIR}/devices
${LIBBLADERF_INCLUDE_DIRS}
)
if (NOT SERVER_MODE)
set (bladerf2mimo_SOURCES
${bladerf2mimo_SOURCES}
bladerf2mimogui.cpp
bladerf2mimogui.ui
)
set(bladerf2mimo_HEADERS
${bladerf2mimo_HEADERS}
bladerf2mimogui.h
)
set(TARGET_NAME mimobladerf2)
set(TARGET_LIB "Qt5::Widgets")
set(TARGET_LIB_GUI "sdrgui")
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
else()
set(TARGET_NAME mimobladerf2srv)
set(TARGET_LIB "")
set(TARGET_LIB_GUI "")
set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
endif()
add_library(${TARGET_NAME} SHARED
${bladerf2mimo_SOURCES}
)
if(LIBBLADERF_EXTERNAL)
add_dependencies(${TARGET_NAME} bladerf)
endif()
target_link_libraries(${TARGET_NAME}
Qt5::Core
${TARGET_LIB}
sdrbase
${TARGET_LIB_GUI}
swagger
${LIBBLADERF_LIBRARIES}
bladerf2device
)
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,206 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef PLUGINS_SAMPLEMIMO_BLADERF2MIMO_BLADERF2MIMO_H_
#define PLUGINS_SAMPLEMIMO_BLADERF2MIMO_BLADERF2MIMO_H_
#include <stdint.h>
#include <QString>
#include <QByteArray>
#include <QNetworkRequest>
#include "dsp/devicesamplemimo.h"
#include "bladerf2/devicebladerf2shared.h"
#include "bladerf2mimosettings.h"
class QNetworkAccessManager;
class QNetworkReply;
class DeviceAPI;
class BladeRF2MIThread;
class BladeRF2MOThread;
class DeviceBladeRF2;
struct bladerf_gain_modes;
struct bladerf;
class BladeRF2MIMO : public DeviceSampleMIMO {
Q_OBJECT
public:
class MsgConfigureBladeRF2MIMO : public Message {
MESSAGE_CLASS_DECLARATION
public:
const BladeRF2MIMOSettings& getSettings() const { return m_settings; }
bool getForce() const { return m_force; }
static MsgConfigureBladeRF2MIMO* create(const BladeRF2MIMOSettings& settings, bool force)
{
return new MsgConfigureBladeRF2MIMO(settings, force);
}
private:
BladeRF2MIMOSettings m_settings;
bool m_force;
MsgConfigureBladeRF2MIMO(const BladeRF2MIMOSettings& settings, bool force) :
Message(),
m_settings(settings),
m_force(force)
{ }
};
class MsgStartStop : public Message {
MESSAGE_CLASS_DECLARATION
public:
bool getStartStop() const { return m_startStop; }
bool getRxElseTx() const { return m_rxElseTx; }
static MsgStartStop* create(bool startStop, bool rxElseTx) {
return new MsgStartStop(startStop, rxElseTx);
}
protected:
bool m_startStop;
bool m_rxElseTx;
MsgStartStop(bool startStop, bool rxElseTx) :
Message(),
m_startStop(startStop),
m_rxElseTx(rxElseTx)
{ }
};
struct GainMode
{
QString m_name;
int m_value;
};
BladeRF2MIMO(DeviceAPI *deviceAPI);
virtual ~BladeRF2MIMO();
virtual void destroy();
virtual void init();
virtual bool startRx();
virtual void stopRx();
virtual bool startTx();
virtual void stopTx();
virtual QByteArray serialize() const;
virtual bool deserialize(const QByteArray& data);
virtual void setMessageQueueToGUI(MessageQueue *queue) { m_guiMessageQueue = queue; }
virtual const QString& getDeviceDescription() const;
virtual int getSourceSampleRate(int index) const;
virtual void setSourceSampleRate(int sampleRate, int index) { (void) sampleRate; (void) index; }
virtual quint64 getSourceCenterFrequency(int index) const;
virtual void setSourceCenterFrequency(qint64 centerFrequency, int index);
virtual int getSinkSampleRate(int index) const;
virtual void setSinkSampleRate(int sampleRate, int index) { (void) sampleRate; (void) index; }
virtual quint64 getSinkCenterFrequency(int index) const;
virtual void setSinkCenterFrequency(qint64 centerFrequency, int index);
virtual quint64 getMIMOCenterFrequency() const { return getSourceCenterFrequency(0); }
virtual unsigned int getMIMOSampleRate() const { return getSourceSampleRate(0); }
virtual bool handleMessage(const Message& message);
virtual int webapiSettingsGet(
SWGSDRangel::SWGDeviceSettings& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& deviceSettingsKeys,
SWGSDRangel::SWGDeviceSettings& response, // query + response
QString& errorMessage);
virtual int webapiReportGet(
SWGSDRangel::SWGDeviceReport& response,
QString& errorMessage);
virtual int webapiRunGet(
int subsystemIndex,
SWGSDRangel::SWGDeviceState& response,
QString& errorMessage);
virtual int webapiRun(
bool run,
int subsystemIndex,
SWGSDRangel::SWGDeviceState& response,
QString& errorMessage);
static void webapiFormatDeviceSettings(
SWGSDRangel::SWGDeviceSettings& response,
const BladeRF2MIMOSettings& settings);
static void webapiUpdateDeviceSettings(
BladeRF2MIMOSettings& settings,
const QStringList& deviceSettingsKeys,
SWGSDRangel::SWGDeviceSettings& response);
bool isRecording(unsigned int istream) const { (void) istream; return false; }
void getRxFrequencyRange(uint64_t& min, uint64_t& max, int& step, float& scale);
void getRxSampleRateRange(int& min, int& max, int& step, float& scale);
void getRxBandwidthRange(int& min, int& max, int& step, float& scale);
void getRxGlobalGainRange(int& min, int& max, int& step, float& scale);
const std::vector<GainMode>& getRxGainModes() { return m_rxGainModes; }
void getTxFrequencyRange(uint64_t& min, uint64_t& max, int& step, float& scale);
void getTxSampleRateRange(int& min, int& max, int& step, float& scale);
void getTxBandwidthRange(int& min, int& max, int& step, float& scale);
void getTxGlobalGainRange(int& min, int& max, int& step, float& scale);
bool getRxRunning() const { return m_runningRx; }
bool getTxRunning() const { return m_runningTx; }
private:
DeviceAPI *m_deviceAPI;
QMutex m_mutex;
BladeRF2MIMOSettings m_settings;
BladeRF2MIThread* m_sourceThread;
BladeRF2MOThread* m_sinkThread;
QString m_deviceDescription;
bool m_runningRx;
bool m_runningTx;
QNetworkAccessManager *m_networkManager;
QNetworkRequest m_networkRequest;
DeviceBladeRF2 *m_dev;
bool m_open;
std::vector<GainMode> m_rxGainModes;
bool openDevice();
void closeDevice();
bool applySettings(const BladeRF2MIMOSettings& settings, bool force);
bool setRxDeviceCenterFrequency(struct bladerf *dev, quint64 freq_hz, int loPpmTenths);
bool setTxDeviceCenterFrequency(struct bladerf *dev, quint64 freq_hz, int loPpmTenths);
void webapiReverseSendSettings(QList<QString>& deviceSettingsKeys, const BladeRF2MIMOSettings& settings, bool force);
void webapiReverseSendStartStop(bool start);
void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response);
private slots:
void networkManagerFinished(QNetworkReply *reply);
};
#endif // PLUGINS_SAMPLEMIMO_BLADERF2MIMO_BLADERF2MIMO_H_

View File

@ -0,0 +1,823 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QDebug>
#include <QTime>
#include <QDateTime>
#include <QString>
#include <QMessageBox>
#include <QFileDialog>
#include "plugin/pluginapi.h"
#include "device/deviceapi.h"
#include "device/deviceuiset.h"
#include "gui/colormapper.h"
#include "gui/glspectrum.h"
#include "gui/crightclickenabler.h"
#include "gui/basicdevicesettingsdialog.h"
#include "dsp/dspengine.h"
#include "dsp/dspdevicemimoengine.h"
#include "dsp/dspcommands.h"
#include "dsp/devicesamplestatic.h"
#include "util/db.h"
#include "mainwindow.h"
#include "bladerf2mimo.h"
#include "ui_bladerf2mimogui.h"
#include "bladerf2mimogui.h"
BladeRF2MIMOGui::BladeRF2MIMOGui(DeviceUISet *deviceUISet, QWidget* parent) :
DeviceGUI(parent),
ui(new Ui::BladeRF2MIMOGui),
m_deviceUISet(deviceUISet),
m_settings(),
m_rxElseTx(true),
m_streamIndex(0),
m_spectrumRxElseTx(true),
m_spectrumStreamIndex(0),
m_gainLock(false),
m_doApplySettings(true),
m_forceSettings(true),
m_sampleMIMO(nullptr),
m_tickCount(0),
m_rxBasebandSampleRate(3072000),
m_txBasebandSampleRate(3072000),
m_rxDeviceCenterFrequency(435000*1000),
m_txDeviceCenterFrequency(435000*1000),
m_lastRxEngineState(DeviceAPI::StNotStarted),
m_lastTxEngineState(DeviceAPI::StNotStarted),
m_sampleRateMode(true)
{
qDebug("BladeRF2MIMOGui::BladeRF2MIMOGui");
ui->setupUi(this);
m_sampleMIMO = (BladeRF2MIMO*) m_deviceUISet->m_deviceAPI->getSampleMIMO();
m_sampleMIMO->getRxFrequencyRange(m_fMinRx, m_fMaxRx, m_fStepRx, m_fScaleRx);
m_sampleMIMO->getTxFrequencyRange(m_fMinTx, m_fMaxTx, m_fStepTx, m_fScaleTx);
m_sampleMIMO->getRxBandwidthRange(m_bwMinRx, m_bwMaxRx, m_bwStepRx, m_bwScaleRx);
m_sampleMIMO->getTxBandwidthRange(m_bwMinTx, m_bwMaxTx, m_bwStepTx, m_bwScaleTx);
ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
ui->sampleRate->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow));
ui->bandwidth->setColorMapper(ColorMapper(ColorMapper::GrayYellow));
int minRx, maxRx, stepRx, minTx, maxTx, stepTx;
m_sampleMIMO->getRxSampleRateRange(minRx, maxRx, stepRx, m_srScaleRx);
m_sampleMIMO->getTxSampleRateRange(minTx, maxTx, stepTx, m_srScaleTx);
m_srMin = std::max(minRx, minTx);
m_srMax = std::min(maxRx, maxTx);
m_sampleMIMO->getRxGlobalGainRange(m_gainMinRx, m_gainMaxRx, m_gainStepRx, m_gainScaleRx);
m_sampleMIMO->getTxGlobalGainRange(m_gainMinTx, m_gainMaxTx, m_gainStepTx, m_gainScaleTx);
displayGainModes();
displaySettings();
connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware()));
connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
m_statusTimer.start(500);
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection);
m_sampleMIMO->setMessageQueueToGUI(&m_inputMessageQueue);
CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStopRx);
connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &)));
sendSettings();
}
BladeRF2MIMOGui::~BladeRF2MIMOGui()
{
delete ui;
}
void BladeRF2MIMOGui::destroy()
{
delete this;
}
void BladeRF2MIMOGui::resetToDefaults()
{
m_settings.resetToDefaults();
displaySettings();
sendSettings();
}
QByteArray BladeRF2MIMOGui::serialize() const
{
return m_settings.serialize();
}
bool BladeRF2MIMOGui::deserialize(const QByteArray& data)
{
if (m_settings.deserialize(data))
{
displaySettings();
m_forceSettings = true;
sendSettings();
return true;
}
else
{
resetToDefaults();
return false;
}
}
void BladeRF2MIMOGui::displaySettings()
{
updateFrequencyLimits();
if (m_rxElseTx)
{
ui->transverter->setDeltaFrequency(m_settings.m_rxTransverterDeltaFrequency);
ui->transverter->setDeltaFrequencyActive(m_settings.m_rxTransverterMode);
ui->transverter->setIQOrder(m_settings.m_iqOrder);
ui->centerFrequency->setValueRange(7, m_fMinRx / 1000, m_fMaxRx / 1000);
ui->centerFrequency->setValue(m_settings.m_rxCenterFrequency / 1000);
ui->bandwidth->setValueRange(5, m_bwMinRx / 1000, m_bwMaxRx / 1000);
ui->bandwidth->setValue(m_settings.m_rxBandwidth / 1000);
uint32_t basebandSampleRate = m_settings.m_devSampleRate/(1<<m_settings.m_log2Decim);
ui->deviceRateText->setText(tr("%1k").arg(QString::number(basebandSampleRate / 1000.0f, 'g', 5)));
ui->dcOffset->setEnabled(true);
ui->dcOffset->setChecked(m_settings.m_dcBlock);
ui->iqImbalance->setEnabled(true);
ui->iqImbalance->setChecked(m_settings.m_iqCorrection);
ui->biasTee->setChecked(m_settings.m_rxBiasTee);
ui->decim->setCurrentIndex(m_settings.m_log2Decim);
ui->label_decim->setText(QString("Dec"));
ui->decim->setToolTip(QString("Decimation factor"));
ui->gainMode->setEnabled(true);
ui->fcPos->setCurrentIndex((int) m_settings.m_fcPosRx);
if (m_streamIndex == 0) {
ui->gainMode->setCurrentIndex(m_settings.m_rx0GainMode);
} else if (m_streamIndex == 1) {
ui->gainMode->setCurrentIndex(m_settings.m_rx1GainMode);
}
}
else
{
ui->transverter->setDeltaFrequency(m_settings.m_txTransverterDeltaFrequency);
ui->transverter->setDeltaFrequencyActive(m_settings.m_txTransverterMode);
ui->transverter->setIQOrder(m_settings.m_iqOrder);
ui->centerFrequency->setValueRange(7, m_fMinTx / 1000, m_fMaxTx / 1000);
ui->centerFrequency->setValue(m_settings.m_txCenterFrequency / 1000);
ui->bandwidth->setValueRange(5, m_bwMinTx / 1000, m_bwMaxTx / 1000);
ui->bandwidth->setValue(m_settings.m_txBandwidth / 1000);
uint32_t basebandSampleRate = m_settings.m_devSampleRate/(1<<m_settings.m_log2Interp);
ui->deviceRateText->setText(tr("%1k").arg(QString::number(basebandSampleRate / 1000.0f, 'g', 5)));
ui->dcOffset->setEnabled(false);
ui->iqImbalance->setEnabled(false);
ui->biasTee->setChecked(m_settings.m_txBiasTee);
ui->decim->setCurrentIndex(m_settings.m_log2Interp);
ui->label_decim->setText(QString("Int"));
ui->decim->setToolTip(QString("Interpolation factor"));
ui->gainMode->setEnabled(false);
ui->fcPos->setCurrentIndex((int) m_settings.m_fcPosTx);
}
displayGain();
ui->sampleRate->setValue(m_settings.m_devSampleRate);
ui->LOppm->setValue(m_settings.m_LOppmTenths);
ui->LOppmText->setText(QString("%1").arg(QString::number(m_settings.m_LOppmTenths/10.0, 'f', 1)));
displaySampleRate();
}
void BladeRF2MIMOGui::displaySampleRate()
{
ui->sampleRate->blockSignals(true);
displayFcTooltip();
quint32 log2Factor = m_rxElseTx ? m_settings.m_log2Decim : m_settings.m_log2Interp;
if (m_sampleRateMode)
{
ui->sampleRateMode->setStyleSheet("QToolButton { background:rgb(60,60,60); }");
ui->sampleRateMode->setText("SR");
// BladeRF can go as low as 80 kS/s but because of buffering in practice experience is not good below 330 kS/s
ui->sampleRate->setValueRange(8, m_srMin, m_srMax);
ui->sampleRate->setValue(m_settings.m_devSampleRate);
ui->sampleRate->setToolTip("Device to host sample rate (S/s)");
ui->deviceRateText->setToolTip("Baseband sample rate (S/s)");
uint32_t basebandSampleRate = m_settings.m_devSampleRate/(1<<log2Factor);
ui->deviceRateText->setText(tr("%1k").arg(QString::number(basebandSampleRate / 1000.0f, 'g', 5)));
}
else
{
ui->sampleRateMode->setStyleSheet("QToolButton { background:rgb(50,50,50); }");
ui->sampleRateMode->setText("BB");
// BladeRF can go as low as 80 kS/s but because of buffering in practice experience is not good below 330 kS/s
ui->sampleRate->setValueRange(8, m_srMin/(1<<log2Factor), m_srMax/(1<<log2Factor));
ui->sampleRate->setValue(m_settings.m_devSampleRate/(1<<log2Factor));
ui->sampleRate->setToolTip("Baseband sample rate (S/s)");
ui->deviceRateText->setToolTip("Device to host sample rate (S/s)");
ui->deviceRateText->setText(tr("%1k").arg(QString::number(m_settings.m_devSampleRate / 1000.0f, 'g', 5)));
}
ui->sampleRate->blockSignals(false);
}
void BladeRF2MIMOGui::displayFcTooltip()
{
int32_t fShift;
if (m_rxElseTx)
{
fShift = DeviceSampleStatic::calculateSourceFrequencyShift(
m_settings.m_log2Decim,
(DeviceSampleStatic::fcPos_t) m_settings.m_fcPosRx,
m_settings.m_devSampleRate,
DeviceSampleStatic::FrequencyShiftScheme::FSHIFT_STD
);
}
else
{
fShift = DeviceSampleStatic::calculateSinkFrequencyShift(
m_settings.m_log2Interp,
(DeviceSampleStatic::fcPos_t) m_settings.m_fcPosTx,
m_settings.m_devSampleRate
);
}
ui->fcPos->setToolTip(tr("Relative position of device center frequency: %1 kHz").arg(QString::number(fShift / 1000.0f, 'g', 5)));
}
void BladeRF2MIMOGui::displayGainModes()
{
ui->gainMode->blockSignals(true);
if (m_rxElseTx)
{
const std::vector<BladeRF2MIMO::GainMode>& modes = m_sampleMIMO->getRxGainModes();
std::vector<BladeRF2MIMO::GainMode>::const_iterator it = modes.begin();
for (; it != modes.end(); ++it) {
ui->gainMode->addItem(it->m_name);
}
}
else
{
ui->gainMode->clear();
ui->gainMode->addItem("automatic");
}
ui->gainMode->blockSignals(false);
}
void BladeRF2MIMOGui::displayGain()
{
int min, max, step, gainDB;
float scale;
if (m_rxElseTx)
{
m_sampleMIMO->getRxGlobalGainRange(min, max, step, scale);
if (m_streamIndex == 0) {
gainDB = m_settings.m_rx0GlobalGain;
} else {
gainDB = m_settings.m_rx1GlobalGain;
}
}
else
{
m_sampleMIMO->getTxGlobalGainRange(min, max, step, scale);
if (m_streamIndex == 0) {
gainDB = m_settings.m_tx0GlobalGain;
} else {
gainDB = m_settings.m_tx1GlobalGain;
}
}
ui->gain->setMinimum(min/step);
ui->gain->setMaximum(max/step);
ui->gain->setSingleStep(1);
ui->gain->setPageStep(1);
ui->gain->setValue(getGainValue(gainDB, min, max, step, scale));
ui->gainText->setText(tr("%1 dB").arg(QString::number(gainDB, 'f', 2)));
}
bool BladeRF2MIMOGui::handleMessage(const Message& message)
{
if (DSPMIMOSignalNotification::match(message))
{
const DSPMIMOSignalNotification& notif = (const DSPMIMOSignalNotification&) message;
int istream = notif.getIndex();
bool sourceOrSink = notif.getSourceOrSink();
if (sourceOrSink)
{
m_rxBasebandSampleRate = notif.getSampleRate();
m_rxDeviceCenterFrequency = notif.getCenterFrequency();
}
else
{
m_txBasebandSampleRate = notif.getSampleRate();
m_txDeviceCenterFrequency = notif.getCenterFrequency();
}
qDebug("BladeRF2MIMOGui::handleInputMessages: DSPMIMOSignalNotification: %s stream: %d SampleRate:%d, CenterFrequency:%llu",
sourceOrSink ? "source" : "sink",
istream,
notif.getSampleRate(),
notif.getCenterFrequency());
updateSampleRateAndFrequency();
return true;
}
else if (BladeRF2MIMO::MsgConfigureBladeRF2MIMO::match(message))
{
const BladeRF2MIMO::MsgConfigureBladeRF2MIMO& notif = (const BladeRF2MIMO::MsgConfigureBladeRF2MIMO&) message;
m_settings = notif.getSettings();
displaySettings();
return true;
}
return false;
}
void BladeRF2MIMOGui::handleInputMessages()
{
Message* message;
while ((message = m_inputMessageQueue.pop()) != 0)
{
if (handleMessage(*message)) {
delete message;
} else {
qDebug("BladeRF2MIMOGui::handleInputMessages: unhandled message: %s", message->getIdentifier());
}
}
}
void BladeRF2MIMOGui::sendSettings()
{
if(!m_updateTimer.isActive()) {
m_updateTimer.start(100);
}
}
void BladeRF2MIMOGui::updateHardware()
{
if (m_doApplySettings)
{
BladeRF2MIMO::MsgConfigureBladeRF2MIMO* message = BladeRF2MIMO::MsgConfigureBladeRF2MIMO::create(m_settings, m_forceSettings);
m_sampleMIMO->getInputMessageQueue()->push(message);
m_forceSettings = false;
m_updateTimer.stop();
}
}
void BladeRF2MIMOGui::updateSampleRateAndFrequency()
{
if (m_spectrumRxElseTx)
{
m_deviceUISet->getSpectrum()->setSampleRate(m_rxBasebandSampleRate);
m_deviceUISet->getSpectrum()->setCenterFrequency(m_rxDeviceCenterFrequency);
}
else
{
m_deviceUISet->getSpectrum()->setSampleRate(m_txBasebandSampleRate);
m_deviceUISet->getSpectrum()->setCenterFrequency(m_txDeviceCenterFrequency);
}
}
void BladeRF2MIMOGui::on_streamSide_currentIndexChanged(int index)
{
m_rxElseTx = index == 0;
displayGainModes();
displaySettings();
}
void BladeRF2MIMOGui::on_streamIndex_currentIndexChanged(int index)
{
m_streamIndex = index < 0 ? 0 : index > 1 ? 1 : index;
displaySettings();
}
void BladeRF2MIMOGui::on_spectrumSide_currentIndexChanged(int index)
{
m_spectrumRxElseTx = (index == 0);
m_deviceUISet->m_spectrum->setDisplayedStream(m_spectrumRxElseTx, m_spectrumStreamIndex);
m_deviceUISet->m_deviceAPI->setSpectrumSinkInput(m_spectrumRxElseTx, m_spectrumStreamIndex);
m_deviceUISet->setSpectrumScalingFactor(m_spectrumRxElseTx ? SDR_RX_SCALEF : SDR_TX_SCALEF);
updateSampleRateAndFrequency();
}
void BladeRF2MIMOGui::on_spectrumIndex_currentIndexChanged(int index)
{
m_spectrumStreamIndex = index < 0 ? 0 : index > 1 ? 1 : index;
m_deviceUISet->m_spectrum->setDisplayedStream(m_spectrumRxElseTx, m_spectrumStreamIndex);
m_deviceUISet->m_deviceAPI->setSpectrumSinkInput(m_spectrumRxElseTx, m_spectrumStreamIndex);
updateSampleRateAndFrequency();
}
void BladeRF2MIMOGui::on_startStopRx_toggled(bool checked)
{
if (m_doApplySettings)
{
BladeRF2MIMO::MsgStartStop *message = BladeRF2MIMO::MsgStartStop::create(checked, true);
m_sampleMIMO->getInputMessageQueue()->push(message);
}
}
void BladeRF2MIMOGui::on_startStopTx_toggled(bool checked)
{
if (m_doApplySettings)
{
BladeRF2MIMO::MsgStartStop *message = BladeRF2MIMO::MsgStartStop::create(checked, false);
m_sampleMIMO->getInputMessageQueue()->push(message);
}
}
void BladeRF2MIMOGui::on_centerFrequency_changed(quint64 value)
{
if (m_rxElseTx) {
m_settings.m_rxCenterFrequency = value * 1000;
} else {
m_settings.m_txCenterFrequency = value * 1000;
}
sendSettings();
}
void BladeRF2MIMOGui::on_LOppm_valueChanged(int value)
{
ui->LOppmText->setText(QString("%1").arg(QString::number(value/10.0, 'f', 1)));
m_settings.m_LOppmTenths = value;
sendSettings();
}
void BladeRF2MIMOGui::on_dcOffset_toggled(bool checked)
{
m_settings.m_dcBlock = checked;
sendSettings();
}
void BladeRF2MIMOGui::on_iqImbalance_toggled(bool checked)
{
m_settings.m_iqCorrection = checked;
sendSettings();
}
void BladeRF2MIMOGui::on_bandwidth_changed(quint64 value)
{
if (m_rxElseTx) {
m_settings.m_rxBandwidth = value * 1000;
} else {
m_settings.m_txBandwidth = value * 1000;
}
sendSettings();
}
void BladeRF2MIMOGui::on_sampleRate_changed(quint64 value)
{
if (m_sampleRateMode)
{
m_settings.m_devSampleRate = value;
}
else
{
if (m_rxElseTx) {
m_settings.m_devSampleRate = value * (1 << m_settings.m_log2Decim);
} else {
m_settings.m_devSampleRate = value * (1 << m_settings.m_log2Interp);
}
}
displaySampleRate();
displayFcTooltip();
sendSettings();
}
void BladeRF2MIMOGui::on_fcPos_currentIndexChanged(int index)
{
if (m_rxElseTx) {
m_settings.m_fcPosRx = (BladeRF2MIMOSettings::fcPos_t) (index < 0 ? 0 : index > 2 ? 2 : index);
} else {
m_settings.m_fcPosTx = (BladeRF2MIMOSettings::fcPos_t) (index < 0 ? 0 : index > 2 ? 2 : index);
}
displayFcTooltip();
sendSettings();
}
void BladeRF2MIMOGui::on_decim_currentIndexChanged(int index)
{
if ((index <0) || (index > 6)) {
return;
}
if (m_rxElseTx) {
m_settings.m_log2Decim = index;
} else {
m_settings.m_log2Interp = index;
}
displaySampleRate();
if (m_sampleRateMode) {
m_settings.m_devSampleRate = ui->sampleRate->getValueNew();
} else {
m_settings.m_devSampleRate = ui->sampleRate->getValueNew() * (1 << (m_rxElseTx ? m_settings.m_log2Decim : m_settings.m_log2Interp));
}
sendSettings();
}
void BladeRF2MIMOGui::on_gainLock_toggled(bool checked)
{
if (!m_gainLock && checked)
{
m_settings.m_rx1GlobalGain = m_settings.m_rx0GlobalGain;
m_settings.m_rx1GainMode = m_settings.m_rx0GainMode;
m_settings.m_tx1GlobalGain = m_settings.m_tx0GlobalGain;
sendSettings();
}
m_gainLock = checked;
}
void BladeRF2MIMOGui::on_gainMode_currentIndexChanged(int index)
{
if (!m_rxElseTx) { // not for Tx
return;
}
const std::vector<BladeRF2MIMO::GainMode>& modes = m_sampleMIMO->getRxGainModes();
unsigned int uindex = index < 0 ? 0 : (unsigned int) index;
if (uindex < modes.size())
{
BladeRF2MIMO::GainMode mode = modes[index];
if (m_streamIndex == 0 || m_gainLock)
{
if (m_settings.m_rx0GainMode != mode.m_value)
{
if (mode.m_value == BLADERF_GAIN_MANUAL)
{
setGainFromValue(ui->gain->value());
ui->gain->setEnabled(true);
} else {
ui->gain->setEnabled(false);
}
}
m_settings.m_rx0GainMode = mode.m_value;
}
if (m_streamIndex == 1 || m_gainLock)
{
if (m_settings.m_rx1GainMode != mode.m_value)
{
if (mode.m_value == BLADERF_GAIN_MANUAL)
{
setGainFromValue(ui->gain->value());
ui->gain->setEnabled(true);
} else {
ui->gain->setEnabled(false);
}
}
m_settings.m_rx1GainMode = mode.m_value;
}
sendSettings();
}
}
void BladeRF2MIMOGui::on_gain_valueChanged(int value)
{
float gainDB = setGainFromValue(value);
ui->gainText->setText(tr("%1 dB").arg(QString::number(gainDB, 'f', 2)));
sendSettings();
}
float BladeRF2MIMOGui::setGainFromValue(int value)
{
int min, max, step;
float scale, gainDB;
if (m_rxElseTx)
{
m_sampleMIMO->getRxGlobalGainRange(min, max, step, scale);
gainDB = getGainDB(value, min, max, step, scale);
if (m_streamIndex == 0 || m_gainLock) {
m_settings.m_rx0GlobalGain = (int) gainDB;
}
if (m_streamIndex == 1 || m_gainLock) {
m_settings.m_rx1GlobalGain = (int) gainDB;
}
}
else
{
m_sampleMIMO->getTxGlobalGainRange(min, max, step, scale);
gainDB = getGainDB(value, min, max, step, scale);
if (m_streamIndex == 0 || m_gainLock) {
m_settings.m_tx0GlobalGain = (int) gainDB;
}
if (m_streamIndex == 1 || m_gainLock) {
m_settings.m_tx1GlobalGain = (int) gainDB;
}
}
return gainDB;
}
void BladeRF2MIMOGui::on_biasTee_toggled(bool checked)
{
if (m_rxElseTx) {
m_settings.m_rxBiasTee = checked;
} else {
m_settings.m_txBiasTee = checked;
}
sendSettings();
}
void BladeRF2MIMOGui::on_transverter_clicked()
{
if (m_rxElseTx)
{
m_settings.m_rxTransverterMode = ui->transverter->getDeltaFrequencyAcive();
m_settings.m_rxTransverterDeltaFrequency = ui->transverter->getDeltaFrequency();
m_settings.m_iqOrder = ui->transverter->getIQOrder();
qDebug("BladeRF2InputGui::on_transverter_clicked: Rx: %lld Hz %s", m_settings.m_rxTransverterDeltaFrequency, m_settings.m_rxTransverterMode ? "on" : "off");
}
else
{
m_settings.m_txTransverterMode = ui->transverter->getDeltaFrequencyAcive();
m_settings.m_txTransverterDeltaFrequency = ui->transverter->getDeltaFrequency();
qDebug("BladeRF2InputGui::on_transverter_clicked: Tx: %lld Hz %s", m_settings.m_txTransverterDeltaFrequency, m_settings.m_txTransverterMode ? "on" : "off");
}
updateFrequencyLimits();
setCenterFrequencySetting(ui->centerFrequency->getValueNew());
sendSettings();
}
void BladeRF2MIMOGui::updateFrequencyLimits()
{
// values in kHz
uint64_t f_min, f_max;
int step;
float scale;
if (m_rxElseTx)
{
qint64 deltaFrequency = m_settings.m_rxTransverterMode ? m_settings.m_rxTransverterDeltaFrequency/1000 : 0;
m_sampleMIMO->getRxFrequencyRange(f_min, f_max, step, scale);
qint64 minLimit = f_min/1000 + deltaFrequency;
qint64 maxLimit = f_max/1000 + deltaFrequency;
minLimit = minLimit < 0 ? 0 : minLimit > 9999999 ? 9999999 : minLimit;
maxLimit = maxLimit < 0 ? 0 : maxLimit > 9999999 ? 9999999 : maxLimit;
qDebug("BladeRF2MIMOGui::updateFrequencyLimits: Rx: delta: %lld min: %lld max: %lld", deltaFrequency, minLimit, maxLimit);
ui->centerFrequency->setValueRange(7, minLimit, maxLimit);
}
else
{
qint64 deltaFrequency = m_settings.m_txTransverterMode ? m_settings.m_txTransverterDeltaFrequency/1000 : 0;
m_sampleMIMO->getRxFrequencyRange(f_min, f_max, step, scale);
qint64 minLimit = f_min/1000 + deltaFrequency;
qint64 maxLimit = f_max/1000 + deltaFrequency;
minLimit = minLimit < 0 ? 0 : minLimit > 9999999 ? 9999999 : minLimit;
maxLimit = maxLimit < 0 ? 0 : maxLimit > 9999999 ? 9999999 : maxLimit;
qDebug("BladeRF2MIMOGui::updateFrequencyLimits: Rx: delta: %lld min: %lld max: %lld", deltaFrequency, minLimit, maxLimit);
ui->centerFrequency->setValueRange(7, minLimit, maxLimit);
}
}
void BladeRF2MIMOGui::setCenterFrequencySetting(uint64_t kHzValue)
{
int64_t centerFrequency = kHzValue*1000;
if (m_rxElseTx) {
m_settings.m_rxCenterFrequency = centerFrequency < 0 ? 0 : (uint64_t) centerFrequency;
} else {
m_settings.m_txCenterFrequency = centerFrequency < 0 ? 0 : (uint64_t) centerFrequency;
}
ui->centerFrequency->setToolTip(QString("Main center frequency in kHz (LO: %1 kHz)").arg(centerFrequency/1000));
}
void BladeRF2MIMOGui::updateStatus()
{
int stateRx = m_deviceUISet->m_deviceAPI->state(0);
int stateTx = m_deviceUISet->m_deviceAPI->state(1);
if (m_lastRxEngineState != stateRx)
{
qDebug("BladeRF2MIMOGui::updateStatus: stateRx: %d", (int) stateRx);
switch(stateRx)
{
case DeviceAPI::StNotStarted:
ui->startStopRx->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
break;
case DeviceAPI::StIdle:
ui->startStopRx->setStyleSheet("QToolButton { background-color : blue; }");
break;
case DeviceAPI::StRunning:
ui->startStopRx->setStyleSheet("QToolButton { background-color : green; }");
break;
case DeviceAPI::StError:
ui->startStopRx->setStyleSheet("QToolButton { background-color : red; }");
QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceAPI->errorMessage(0));
break;
default:
break;
}
m_lastRxEngineState = stateRx;
}
if (m_lastTxEngineState != stateTx)
{
qDebug("BladeRF2MIMOGui::updateStatus: stateTx: %d", (int) stateTx);
switch(stateTx)
{
case DeviceAPI::StNotStarted:
ui->startStopTx->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
break;
case DeviceAPI::StIdle:
ui->startStopTx->setStyleSheet("QToolButton { background-color : blue; }");
break;
case DeviceAPI::StRunning:
ui->startStopTx->setStyleSheet("QToolButton { background-color : green; }");
break;
case DeviceAPI::StError:
ui->startStopTx->setStyleSheet("QToolButton { background-color : red; }");
QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceAPI->errorMessage(1));
break;
default:
break;
}
m_lastTxEngineState = stateTx;
}
}
void BladeRF2MIMOGui::openDeviceSettingsDialog(const QPoint& p)
{
BasicDeviceSettingsDialog dialog(this);
dialog.setUseReverseAPI(m_settings.m_useReverseAPI);
dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress);
dialog.setReverseAPIPort(m_settings.m_reverseAPIPort);
dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex);
dialog.move(p);
dialog.exec();
m_settings.m_useReverseAPI = dialog.useReverseAPI();
m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress();
m_settings.m_reverseAPIPort = dialog.getReverseAPIPort();
m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex();
sendSettings();
}
float BladeRF2MIMOGui::getGainDB(int gainValue, int gainMin, int gainMax, int gainStep, float gainScale)
{
float gain = gainValue * gainStep * gainScale;
qDebug("BladeRF2MIMOGui::getGainDB: gainValue: %d gainMin: %d gainMax: %d gainStep: %d gainScale: %f gain: %f",
gainValue, gainMin, gainMax, gainStep, gainScale, gain);
return gain;
}
int BladeRF2MIMOGui::getGainValue(float gainDB, int gainMin, int gainMax, int gainStep, float gainScale)
{
int gain = (gainDB/gainScale) / gainStep;
qDebug("BladeRF2MIMOGui::getGainValue: gainDB: %f m_gainMin: %d m_gainMax: %d m_gainStep: %d gainScale: %f gain: %d",
gainDB, gainMin, gainMax, gainStep, gainScale, gain);
return gain;
}

View File

@ -0,0 +1,127 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef _BLADERF2MIMO_BLADERF2MIMOGUI_H_
#define _BLADERF2MIMO_BLADERF2MIMOGUI_H_
#include <QTimer>
#include <QWidget>
#include "util/messagequeue.h"
#include "device/devicegui.h"
#include "bladerf2mimosettings.h"
class DeviceUISet;
class BladeRF2MIMO;
namespace Ui {
class BladeRF2MIMOGui;
}
class BladeRF2MIMOGui : public DeviceGUI {
Q_OBJECT
public:
explicit BladeRF2MIMOGui(DeviceUISet *deviceUISet, QWidget* parent = nullptr);
virtual ~BladeRF2MIMOGui();
virtual void destroy();
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
private:
Ui::BladeRF2MIMOGui* ui;
DeviceUISet* m_deviceUISet;
BladeRF2MIMOSettings m_settings;
bool m_rxElseTx; //!< Which side is being dealt with
int m_streamIndex; //!< Current stream index being dealt with
bool m_spectrumRxElseTx;
int m_spectrumStreamIndex; //!< Index of the stream displayed on main spectrum
bool m_gainLock; //!< Lock Rx or Tx channel gains (set channel gains to gain of channel 0 when engaged)
QTimer m_updateTimer;
QTimer m_statusTimer;
bool m_doApplySettings;
bool m_forceSettings;
BladeRF2MIMO* m_sampleMIMO;
std::size_t m_tickCount;
int m_rxBasebandSampleRate;
int m_txBasebandSampleRate;
quint64 m_rxDeviceCenterFrequency; //!< Center frequency in Rx device
quint64 m_txDeviceCenterFrequency; //!< Center frequency in Tx device
int m_lastRxEngineState;
int m_lastTxEngineState;
MessageQueue m_inputMessageQueue;
bool m_sampleRateMode;
int m_srMax, m_srMin, m_srStep;
int m_bwMaxRx, m_bwMinRx, m_bwStepRx;
int m_bwMaxTx, m_bwMinTx, m_bwStepTx;
uint64_t m_fMinRx, m_fMaxRx;
uint64_t m_fMinTx, m_fMaxTx;
int m_fStepRx, m_fStepTx;
int m_gainMinRx, m_gainMaxRx, m_gainStepRx;
int m_gainMinTx, m_gainMaxTx, m_gainStepTx;
float m_fScaleRx, m_fScaleTx;
float m_srScaleRx, m_srScaleTx;
float m_bwScaleRx, m_bwScaleTx;
float m_gainScaleRx, m_gainScaleTx;
void blockApplySettings(bool block) { m_doApplySettings = !block; }
void displaySettings();
void displaySampleRate();
void displayFcTooltip();
void displayGainModes();
void displayGain();
void sendSettings();
void updateSampleRateAndFrequency();
void updateFrequencyLimits();
void setCenterFrequencySetting(uint64_t kHzValue);
float getGainDB(int gainValue, int gainMin, int gainMax, int gainStep, float gainScale);
int getGainValue(float gainDB, int gainMin, int gainMax, int gainStep, float gainScale);
float setGainFromValue(int value);
bool handleMessage(const Message& message);
private slots:
void handleInputMessages();
void updateHardware();
void updateStatus();
void on_streamSide_currentIndexChanged(int index);
void on_streamIndex_currentIndexChanged(int index);
void on_spectrumSide_currentIndexChanged(int index);
void on_spectrumIndex_currentIndexChanged(int index);
void on_startStopRx_toggled(bool checked);
void on_startStopTx_toggled(bool checked);
void on_centerFrequency_changed(quint64 value);
void on_LOppm_valueChanged(int value);
void on_dcOffset_toggled(bool checked);
void on_iqImbalance_toggled(bool checked);
void on_bandwidth_changed(quint64 value);
void on_sampleRate_changed(quint64 value);
void on_fcPos_currentIndexChanged(int index);
void on_decim_currentIndexChanged(int index);
void on_gainLock_toggled(bool checked);
void on_gainMode_currentIndexChanged(int index);
void on_gain_valueChanged(int value);
void on_biasTee_toggled(bool checked);
void on_transverter_clicked();
void openDeviceSettingsDialog(const QPoint& p);
};
#endif // _BLADERF2MIMO_BLADERF2MIMOGUI_H_

View File

@ -0,0 +1,812 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BladeRF2MIMOGui</class>
<widget class="QWidget" name="BladeRF2MIMOGui">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>366</width>
<height>228</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>350</width>
<height>220</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="windowTitle">
<string>BladeRF2</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<layout class="QHBoxLayout" name="streamLayout">
<property name="topMargin">
<number>6</number>
</property>
<item>
<widget class="QLabel" name="streamLabel">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>22</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../../../sdrgui/resources/res.qrc">:/antenna.png</pixmap>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="streamSide">
<property name="maximumSize">
<size>
<width>45</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Select Rx or Tx settings</string>
</property>
<item>
<property name="text">
<string>Rx</string>
</property>
</item>
<item>
<property name="text">
<string>Tx</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QComboBox" name="streamIndex">
<property name="maximumSize">
<size>
<width>35</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Select stream index to which settings apply</string>
</property>
<item>
<property name="text">
<string>0</string>
</property>
</item>
<item>
<property name="text">
<string>1</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="spectrumLabel">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>22</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../../../sdrgui/resources/res.qrc">:/dsb.png</pixmap>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="spectrumSide">
<property name="minimumSize">
<size>
<width>45</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>45</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Select Rx or Tx spectrum</string>
</property>
<item>
<property name="text">
<string>Rx</string>
</property>
</item>
<item>
<property name="text">
<string>Tx</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QComboBox" name="spectrumIndex">
<property name="maximumSize">
<size>
<width>35</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Select which stream index to display spectrum</string>
</property>
<item>
<property name="text">
<string>0</string>
</property>
</item>
<item>
<property name="text">
<string>1</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="startStopRxLabel">
<property name="text">
<string>Rx</string>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="startStopRx">
<property name="toolTip">
<string>start/stop acquisition (Rx)</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/play.png</normaloff>
<normalon>:/stop.png</normalon>:/play.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="startStopTxLabel">
<property name="text">
<string>Tx</string>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="startStopTx">
<property name="toolTip">
<string>start/stop generation (Tx)</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/play.png</normaloff>
<normalon>:/stop.png</normalon>:/play.png</iconset>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_freq">
<property name="topMargin">
<number>4</number>
</property>
<item>
<layout class="QVBoxLayout" name="deviceUILayout">
<item>
<layout class="QHBoxLayout" name="deviceButtonsLayout"/>
</item>
<item>
<layout class="QHBoxLayout" name="deviceRateLayout">
<item>
<widget class="QLabel" name="deviceRateText">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>I/Q sample rate kS/s</string>
</property>
<property name="text">
<string>00000k</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<spacer name="freqLeftSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="ValueDial" name="centerFrequency" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Mono</family>
<pointsize>20</pointsize>
</font>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="toolTip">
<string>Tuner center frequency in kHz</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="freqUnits">
<property name="text">
<string> kHz</string>
</property>
</widget>
</item>
<item>
<spacer name="freqRightlSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="LOppm_layout">
<item>
<widget class="QLabel" name="LOppmLabel">
<property name="text">
<string>LO ppm</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="LOppm">
<property name="toolTip">
<string>Local Oscillator ppm correction</string>
</property>
<property name="minimum">
<number>-20</number>
</property>
<property name="maximum">
<number>20</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="LOppmText">
<property name="minimumSize">
<size>
<width>26</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>-0.0</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_corr">
<item row="0" column="2">
<widget class="ButtonSwitch" name="iqImbalance">
<property name="toolTip">
<string>Automatic IQ imbalance correction (Rx)</string>
</property>
<property name="text">
<string>IQ</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="ButtonSwitch" name="dcOffset">
<property name="toolTip">
<string>Automatic DC offset removal (Rx)</string>
</property>
<property name="text">
<string>DC</string>
</property>
</widget>
</item>
<item row="0" column="3">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0">
<widget class="QLabel" name="corrLabel">
<property name="text">
<string>Auto</string>
</property>
</widget>
</item>
<item row="0" column="6">
<widget class="QLabel" name="bandwidthUnit">
<property name="text">
<string>kHz</string>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QLabel" name="bandwidthLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>BW </string>
</property>
</widget>
</item>
<item row="0" column="5">
<widget class="ValueDial" name="bandwidth" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Mono</family>
<pointsize>12</pointsize>
</font>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="toolTip">
<string>RF bandwidth</string>
</property>
</widget>
</item>
<item row="0" column="7">
<widget class="TransverterButton" name="transverter">
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolTip">
<string>Transverter frequency translation dialog</string>
</property>
<property name="text">
<string>X</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_freq">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="sampleRateLayout">
<property name="topMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<widget class="QToolButton" name="sampleRateMode">
<property name="minimumSize">
<size>
<width>24</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Toggle between device to host (SR) and base band (BB) sample rate input</string>
</property>
<property name="text">
<string>SR</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="ValueDial" name="sampleRate" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Mono</family>
<pointsize>12</pointsize>
</font>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="toolTip">
<string>Device sample rate</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="samplerateUnit">
<property name="text">
<string>S/s</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_fcPos">
<property name="text">
<string>Fp</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="fcPos">
<property name="toolTip">
<string>Relative position of device center frequency (Rx)</string>
</property>
<item>
<property name="text">
<string>Inf</string>
</property>
</item>
<item>
<property name="text">
<string>Sup</string>
</property>
</item>
<item>
<property name="text">
<string>Cen</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="label_decim">
<property name="text">
<string>Dec</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="decim">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Decimation factor</string>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<item>
<property name="text">
<string>1</string>
</property>
</item>
<item>
<property name="text">
<string>2</string>
</property>
</item>
<item>
<property name="text">
<string>4</string>
</property>
</item>
<item>
<property name="text">
<string>8</string>
</property>
</item>
<item>
<property name="text">
<string>16</string>
</property>
</item>
<item>
<property name="text">
<string>32</string>
</property>
</item>
<item>
<property name="text">
<string>64</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_decim" columnstretch="0,0,0,0,0,0">
<property name="spacing">
<number>3</number>
</property>
<item row="0" column="3">
<widget class="QSlider" name="gain">
<property name="toolTip">
<string>Gain value</string>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QComboBox" name="gainMode">
<property name="toolTip">
<string>Gain mode</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="gainLabel">
<property name="text">
<string>Gain</string>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QLabel" name="gainText">
<property name="minimumSize">
<size>
<width>45</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>000 dB</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="5">
<widget class="ButtonSwitch" name="biasTee">
<property name="toolTip">
<string>Bias Tee</string>
</property>
<property name="text">
<string>BT</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QToolButton" name="gainLock">
<property name="toolTip">
<string>Lock both channels gain</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/unlocked.png</normaloff>
<normalon>:/locked.png</normalon>:/unlocked.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_lna">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="padLayout">
<item>
<spacer name="verticalPadSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ValueDial</class>
<extends>QWidget</extends>
<header>gui/valuedial.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ButtonSwitch</class>
<extends>QToolButton</extends>
<header>gui/buttonswitch.h</header>
</customwidget>
<customwidget>
<class>TransverterButton</class>
<extends>QPushButton</extends>
<header>gui/transverterbutton.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../../../sdrgui/resources/res.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -0,0 +1,141 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QtPlugin>
#include "plugin/pluginapi.h"
#include "util/simpleserializer.h"
#ifndef SERVER_MODE
#include "bladerf2mimogui.h"
#endif
#include "bladerf2mimo.h"
#include "bladerf2mimoplugin.h"
#include "bladerf2mimowebapiadapter.h"
const PluginDescriptor BladeRF2MIMOPlugin::m_pluginDescriptor = {
QString("BladeRF2"),
QString("BladeRF2 MIMO"),
QString("5.13.0"),
QString("(c) Edouard Griffiths, F4EXB"),
QString("https://github.com/f4exb/sdrangel"),
true,
QString("https://github.com/f4exb/sdrangel")
};
const QString BladeRF2MIMOPlugin::m_hardwareID = "BladeRF2";
const QString BladeRF2MIMOPlugin::m_deviceTypeID = BLADERF2MIMO_DEVICE_TYPE_ID;
BladeRF2MIMOPlugin::BladeRF2MIMOPlugin(QObject* parent) :
QObject(parent)
{
}
const PluginDescriptor& BladeRF2MIMOPlugin::getPluginDescriptor() const
{
return m_pluginDescriptor;
}
void BladeRF2MIMOPlugin::initPlugin(PluginAPI* pluginAPI)
{
pluginAPI->registerSampleMIMO(m_deviceTypeID, this);
}
void BladeRF2MIMOPlugin::enumOriginDevices(QStringList& listedHwIds, OriginDevices& originDevices)
{
if (listedHwIds.contains(m_hardwareID)) { // check if it was done
return;
}
DeviceBladeRF2::enumOriginDevices(m_hardwareID, originDevices);
listedHwIds.append(m_hardwareID);
}
PluginInterface::SamplingDevices BladeRF2MIMOPlugin::enumSampleMIMO(const OriginDevices& originDevices)
{
SamplingDevices result;
for (OriginDevices::const_iterator it = originDevices.begin(); it != originDevices.end(); ++it)
{
if (it->hardwareId == m_hardwareID)
{
QString displayedName = it->displayableName;
displayedName.replace(QString(":$1]"), QString("]"));
result.append(SamplingDevice(
displayedName,
m_hardwareID,
m_deviceTypeID,
it->serial,
it->sequence,
PluginInterface::SamplingDevice::PhysicalDevice,
PluginInterface::SamplingDevice::StreamMIMO,
1,
0
));
}
}
return result;
}
#ifdef SERVER_MODE
DeviceGUI* BladeRF2MIMOPlugin::createSampleMIMOPluginInstanceGUI(
const QString& sourceId,
QWidget **widget,
DeviceUISet *deviceUISet)
{
(void) sourceId;
(void) widget;
(void) deviceUISet;
return nullptr;
}
#else
DeviceGUI* BladeRF2MIMOPlugin::createSampleMIMOPluginInstanceGUI(
const QString& sourceId,
QWidget **widget,
DeviceUISet *deviceUISet)
{
if (sourceId == m_deviceTypeID)
{
BladeRF2MIMOGui* gui = new BladeRF2MIMOGui(deviceUISet);
*widget = gui;
return gui;
}
else
{
return nullptr;
}
}
#endif
DeviceSampleMIMO *BladeRF2MIMOPlugin::createSampleMIMOPluginInstance(const QString& mimoId, DeviceAPI *deviceAPI)
{
if (mimoId == m_deviceTypeID)
{
BladeRF2MIMO* input = new BladeRF2MIMO(deviceAPI);
return input;
}
else
{
return nullptr;
}
}
DeviceWebAPIAdapter *BladeRF2MIMOPlugin::createDeviceWebAPIAdapter() const
{
return new BladeRF2MIMOWebAPIAdapter();
}

View File

@ -0,0 +1,56 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef _BLADERF2MIMO_BLADERF2MIMOPLUGIN_H
#define _BLADERF2MIMO_BLADERF2MIMOPLUGIN_H
#include <QObject>
#include "plugin/plugininterface.h"
class PluginAPI;
#define BLADERF2MIMO_DEVICE_TYPE_ID "sdrangel.samplemimo.bladerf2mimo"
class BladeRF2MIMOPlugin : public QObject, public PluginInterface {
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID BLADERF2MIMO_DEVICE_TYPE_ID)
public:
explicit BladeRF2MIMOPlugin(QObject* parent = nullptr);
const PluginDescriptor& getPluginDescriptor() const;
void initPlugin(PluginAPI* pluginAPI);
virtual void enumOriginDevices(QStringList& listedHwIds, OriginDevices& originDevices);
virtual SamplingDevices enumSampleMIMO(const OriginDevices& originDevices);
virtual DeviceGUI* createSampleMIMOPluginInstanceGUI(
const QString& sourceId,
QWidget **widget,
DeviceUISet *deviceUISet);
virtual DeviceSampleMIMO* createSampleMIMOPluginInstance(const QString& sourceId, DeviceAPI *deviceAPI);
virtual DeviceWebAPIAdapter* createDeviceWebAPIAdapter() const;
static const QString m_hardwareID;
static const QString m_deviceTypeID;
private:
static const PluginDescriptor m_pluginDescriptor;
};
#endif // _BLADERF2MIMO_BLADERF2MIMOPLUGIN_H

View File

@ -0,0 +1,170 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "bladerf2mimosettings.h"
#include "util/simpleserializer.h"
BladeRF2MIMOSettings::BladeRF2MIMOSettings()
{
resetToDefaults();
}
void BladeRF2MIMOSettings::resetToDefaults()
{
m_devSampleRate = 3072000;
m_LOppmTenths = 0;
m_rxCenterFrequency = 435000*1000;
m_log2Decim = 0;
m_fcPosRx = FC_POS_INFRA;
m_rxBandwidth = 1500000;
m_rx0GainMode = 0;
m_rx0GlobalGain = 0;
m_rx1GainMode = 0;
m_rx1GlobalGain = 0;
m_rxBiasTee = false;
m_dcBlock = false;
m_iqCorrection = false;
m_rxTransverterMode = false;
m_rxTransverterDeltaFrequency = 0;
m_iqOrder = true;
m_txCenterFrequency = 435000*1000;
m_log2Interp = 0;
m_fcPosTx = FC_POS_CENTER;
m_txBandwidth = 1500000;
m_tx0GlobalGain = -3;
m_tx1GlobalGain = -3;
m_txBiasTee = false;
m_txTransverterMode = false;
m_txTransverterDeltaFrequency = 0;
m_useReverseAPI = false;
m_reverseAPIAddress = "127.0.0.1";
m_reverseAPIPort = 8888;
m_reverseAPIDeviceIndex = 0;
}
QByteArray BladeRF2MIMOSettings::serialize() const
{
SimpleSerializer s(1);
s.writeS32(1, m_devSampleRate);
s.writeS32(2, m_LOppmTenths);
s.writeU64(10, m_rxCenterFrequency);
s.writeU32(11, m_log2Decim);
s.writeS32(12, (int) m_fcPosRx);
s.writeS32(13, m_rxBandwidth);
s.writeS32(14, m_rx0GainMode);
s.writeS32(15, m_rx0GlobalGain);
s.writeS32(16, m_rx1GainMode);
s.writeS32(17, m_rx1GlobalGain);
s.writeBool(18, m_rxBiasTee);
s.writeBool(19, m_dcBlock);
s.writeBool(20, m_iqCorrection);
s.writeBool(21, m_rxTransverterMode);
s.writeS64(22, m_rxTransverterDeltaFrequency);
s.writeBool(23, m_iqOrder);
s.writeU64(30, m_txCenterFrequency);
s.writeU32(31, m_log2Interp);
s.writeS32(32, m_txBandwidth);
s.writeS32(33, m_tx0GlobalGain);
s.writeS32(34, m_tx1GlobalGain);
s.writeBool(35, m_txBiasTee);
s.writeBool(36, m_txTransverterMode);
s.writeS64(37, m_txTransverterDeltaFrequency);
s.writeS32(38, (int) m_fcPosTx);
s.writeBool(51, m_useReverseAPI);
s.writeString(52, m_reverseAPIAddress);
s.writeU32(53, m_reverseAPIPort);
s.writeU32(54, m_reverseAPIDeviceIndex);
return s.final();
}
bool BladeRF2MIMOSettings::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if (!d.isValid())
{
resetToDefaults();
return false;
}
if (d.getVersion() == 1)
{
int intval;
uint32_t uintval;
d.readS32(1, &m_devSampleRate, 3072000);
d.readS32(2, &m_LOppmTenths, 0);
d.readU64(10, &m_rxCenterFrequency, 435000*1000);
d.readU32(11, &m_log2Decim);
d.readS32(12, &intval, 0);
m_fcPosRx = (fcPos_t) intval;
d.readS32(13, &m_rxBandwidth);
d.readS32(14, &m_rx0GainMode);
d.readS32(15, &m_rx0GlobalGain);
d.readS32(16, &m_rx1GainMode);
d.readS32(17, &m_rx1GlobalGain);
d.readBool(18, &m_rxBiasTee);
d.readBool(19, &m_dcBlock);
d.readBool(20, &m_iqCorrection);
d.readBool(21, &m_rxTransverterMode, false);
d.readS64(22, &m_rxTransverterDeltaFrequency, 0);
d.readBool(23, &m_iqOrder, true);
d.readU64(30, &m_txCenterFrequency, 435000*1000);
d.readU32(31, &m_log2Interp);
d.readS32(32, &m_txBandwidth);
d.readS32(33, &m_tx0GlobalGain);
d.readS32(34, &m_tx1GlobalGain);
d.readBool(35, &m_txBiasTee);
d.readBool(36, &m_txTransverterMode, false);
d.readS64(37, &m_txTransverterDeltaFrequency, 0);
d.readS32(38, &intval, 2);
m_fcPosTx = (fcPos_t) intval;
d.readBool(51, &m_useReverseAPI, false);
d.readString(52, &m_reverseAPIAddress, "127.0.0.1");
d.readU32(53, &uintval, 0);
if ((uintval > 1023) && (uintval < 65535)) {
m_reverseAPIPort = uintval;
} else {
m_reverseAPIPort = 8888;
}
d.readU32(54, &uintval, 0);
m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval;
return true;
}
else
{
resetToDefaults();
return false;
}
}

View File

@ -0,0 +1,73 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef PLUGINS_SAMPLEMIMO_BLADERF2MIMO_BLADERF2MIMOSETTINGS_H_
#define PLUGINS_SAMPLEMIMO_BLADERF2MIMO_BLADERF2MIMOSETTINGS_H_
#include <QtGlobal>
#include <QString>
struct BladeRF2MIMOSettings {
typedef enum {
FC_POS_INFRA = 0,
FC_POS_SUPRA,
FC_POS_CENTER
} fcPos_t;
qint32 m_devSampleRate;
qint32 m_LOppmTenths;
quint64 m_rxCenterFrequency;
quint32 m_log2Decim;
fcPos_t m_fcPosRx;
qint32 m_rxBandwidth;
int m_rx0GainMode;
int m_rx0GlobalGain;
int m_rx1GainMode;
int m_rx1GlobalGain;
bool m_rxBiasTee;
bool m_dcBlock;
bool m_iqCorrection;
bool m_rxTransverterMode;
qint64 m_rxTransverterDeltaFrequency;
bool m_iqOrder; //!< true: IQ - false: QI
quint64 m_txCenterFrequency;
quint32 m_log2Interp;
fcPos_t m_fcPosTx;
qint32 m_txBandwidth;
int m_tx0GlobalGain;
int m_tx1GlobalGain;
bool m_txBiasTee;
bool m_txTransverterMode;
qint64 m_txTransverterDeltaFrequency;
bool m_useReverseAPI;
QString m_reverseAPIAddress;
uint16_t m_reverseAPIPort;
uint16_t m_reverseAPIDeviceIndex;
BladeRF2MIMOSettings();
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
};
#endif /* PLUGINS_SAMPLEMIMO_BLADERF2MIMO_BLADERF2MIMOSETTINGS_H_ */

View File

@ -0,0 +1,51 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// //
// Implementation of static web API adapters used for preset serialization and //
// deserialization //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "SWGDeviceSettings.h"
#include "bladerf2mimo.h"
#include "bladerf2mimowebapiadapter.h"
BladeRF2MIMOWebAPIAdapter::BladeRF2MIMOWebAPIAdapter()
{}
BladeRF2MIMOWebAPIAdapter::~BladeRF2MIMOWebAPIAdapter()
{}
int BladeRF2MIMOWebAPIAdapter::webapiSettingsGet(
SWGSDRangel::SWGDeviceSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setBladeRf2MimoSettings(new SWGSDRangel::SWGBladeRF2MIMOSettings());
response.getBladeRf2MimoSettings()->init();
BladeRF2MIMO::webapiFormatDeviceSettings(response, m_settings);
return 200;
}
int BladeRF2MIMOWebAPIAdapter::webapiSettingsPutPatch(
bool force,
const QStringList& deviceSettingsKeys,
SWGSDRangel::SWGDeviceSettings& response, // query + response
QString& errorMessage)
{
(void) errorMessage;
BladeRF2MIMO::webapiUpdateDeviceSettings(m_settings, deviceSettingsKeys, response);
return 200;
}

View File

@ -0,0 +1,44 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// //
// Implementation of static web API adapters used for preset serialization and //
// deserialization //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "device/devicewebapiadapter.h"
#include "bladerf2mimosettings.h"
class BladeRF2MIMOWebAPIAdapter : public DeviceWebAPIAdapter
{
public:
BladeRF2MIMOWebAPIAdapter();
virtual ~BladeRF2MIMOWebAPIAdapter();
virtual QByteArray serialize() { return m_settings.serialize(); }
virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); }
virtual int webapiSettingsGet(
SWGSDRangel::SWGDeviceSettings& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& deviceSettingsKeys,
SWGSDRangel::SWGDeviceSettings& response, // query + response
QString& errorMessage);
private:
BladeRF2MIMOSettings m_settings;
};

View File

@ -0,0 +1,343 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <algorithm>
#include "bladerf2/devicebladerf2shared.h"
#include "dsp/samplemififo.h"
#include "bladerf2mithread.h"
BladeRF2MIThread::BladeRF2MIThread(struct bladerf* dev, QObject* parent) :
QThread(parent),
m_running(false),
m_dev(dev),
m_sampleFifo(nullptr),
m_iqOrder(true)
{
qDebug("BladeRF2MIThread::BladeRF2MIThread");
m_buf = new qint16[2*DeviceBladeRF2::blockSize*2];
for (unsigned int i = 0; i < 2; i++) {
m_convertBuffer[i].resize(DeviceBladeRF2::blockSize, Sample{0,0});
}
}
BladeRF2MIThread::~BladeRF2MIThread()
{
qDebug("BladeRF2MIThread::~BladeRF2MIThread");
if (m_running) {
stopWork();
}
delete[] m_buf;
}
void BladeRF2MIThread::startWork()
{
m_startWaitMutex.lock();
start();
while(!m_running) {
m_startWaiter.wait(&m_startWaitMutex, 100);
}
m_startWaitMutex.unlock();
}
void BladeRF2MIThread::stopWork()
{
m_running = false;
wait();
}
void BladeRF2MIThread::setLog2Decimation(unsigned int log2_decim)
{
m_log2Decim = log2_decim;
}
unsigned int BladeRF2MIThread::getLog2Decimation() const
{
return m_log2Decim;
}
void BladeRF2MIThread::setFcPos(int fcPos)
{
m_fcPos = fcPos;
}
int BladeRF2MIThread::getFcPos() const
{
return m_fcPos;
}
void BladeRF2MIThread::run()
{
int res;
m_running = true;
m_startWaiter.wakeAll();
int status = bladerf_sync_config(m_dev, BLADERF_RX_X2, BLADERF_FORMAT_SC16_Q11, 64, DeviceBladeRF2::blockSize, 32, 1500);
if (status < 0)
{
qCritical("BladeRF2MIThread::run: cannot configure streams: %s", bladerf_strerror(status));
}
else
{
qDebug("BladeRF2MIThread::run: start running loop");
while (m_running)
{
res = bladerf_sync_rx(m_dev, m_buf, DeviceBladeRF2::blockSize*2, nullptr, 1500);
if (res < 0)
{
qCritical("BladeRF2MIThread::run sync Rx error: %s", bladerf_strerror(res));
break;
}
callback(m_buf, DeviceBladeRF2::blockSize);
}
qDebug("BladeRF2MIThread::run: stop running loop");
m_running = false;
}
}
void BladeRF2MIThread::callback(const qint16* buf, qint32 samplesPerChannel)
{
int status = bladerf_deinterleave_stream_buffer(BLADERF_RX_X2, BLADERF_FORMAT_SC16_Q11 , samplesPerChannel*2, (void *) buf);
if (status < 0)
{
qCritical("BladeRF2MIThread::callback: cannot de-interleave buffer: %s", bladerf_strerror(status));
return;
}
std::vector<SampleVector::const_iterator> vbegin;
int lengths[2];
for (unsigned int channel = 0; channel < 2; channel++)
{
if (m_iqOrder) {
lengths[channel] = channelCallbackIQ(&buf[2*samplesPerChannel*channel], 2*samplesPerChannel, channel);
} else {
lengths[channel] = channelCallbackQI(&buf[2*samplesPerChannel*channel], 2*samplesPerChannel, channel);
}
vbegin.push_back(m_convertBuffer[channel].begin());
}
if (lengths[0] == lengths[1])
{
m_sampleFifo->writeSync(vbegin, lengths[0]);
}
else
{
qWarning("BladeRF2MIThread::callback: unequal channel lengths: [0]=%d [1]=%d", lengths[0], lengths[1]);
m_sampleFifo->writeSync(vbegin, (std::min)(lengths[0], lengths[1]));
}
}
int BladeRF2MIThread::channelCallbackIQ(const qint16* buf, qint32 len, int channel)
{
SampleVector::iterator it = m_convertBuffer[channel].begin();
if (m_log2Decim == 0)
{
m_decimatorsIQ[channel].decimate1(&it, buf, len);
}
else
{
if (m_fcPos == 0) // Infra
{
switch (m_log2Decim)
{
case 1:
m_decimatorsIQ[channel].decimate2_inf(&it, buf, len);
break;
case 2:
m_decimatorsIQ[channel].decimate4_inf(&it, buf, len);
break;
case 3:
m_decimatorsIQ[channel].decimate8_inf(&it, buf, len);
break;
case 4:
m_decimatorsIQ[channel].decimate16_inf(&it, buf, len);
break;
case 5:
m_decimatorsIQ[channel].decimate32_inf(&it, buf, len);
break;
case 6:
m_decimatorsIQ[channel].decimate64_inf(&it, buf, len);
break;
default:
break;
}
}
else if (m_fcPos == 1) // Supra
{
switch (m_log2Decim)
{
case 1:
m_decimatorsIQ[channel].decimate2_sup(&it, buf, len);
break;
case 2:
m_decimatorsIQ[channel].decimate4_sup(&it, buf, len);
break;
case 3:
m_decimatorsIQ[channel].decimate8_sup(&it, buf, len);
break;
case 4:
m_decimatorsIQ[channel].decimate16_sup(&it, buf, len);
break;
case 5:
m_decimatorsIQ[channel].decimate32_sup(&it, buf, len);
break;
case 6:
m_decimatorsIQ[channel].decimate64_sup(&it, buf, len);
break;
default:
break;
}
}
else if (m_fcPos == 2) // Center
{
switch (m_log2Decim)
{
case 1:
m_decimatorsIQ[channel].decimate2_cen(&it, buf, len);
break;
case 2:
m_decimatorsIQ[channel].decimate4_cen(&it, buf, len);
break;
case 3:
m_decimatorsIQ[channel].decimate8_cen(&it, buf, len);
break;
case 4:
m_decimatorsIQ[channel].decimate16_cen(&it, buf, len);
break;
case 5:
m_decimatorsIQ[channel].decimate32_cen(&it, buf, len);
break;
case 6:
m_decimatorsIQ[channel].decimate64_cen(&it, buf, len);
break;
default:
break;
}
}
}
return it - m_convertBuffer[channel].begin();
}
int BladeRF2MIThread::channelCallbackQI(const qint16* buf, qint32 len, int channel)
{
SampleVector::iterator it = m_convertBuffer[channel].begin();
if (m_log2Decim == 0)
{
m_decimatorsQI[channel].decimate1(&it, buf, len);
}
else
{
if (m_fcPos == 0) // Infra
{
switch (m_log2Decim)
{
case 1:
m_decimatorsQI[channel].decimate2_inf(&it, buf, len);
break;
case 2:
m_decimatorsQI[channel].decimate4_inf(&it, buf, len);
break;
case 3:
m_decimatorsQI[channel].decimate8_inf(&it, buf, len);
break;
case 4:
m_decimatorsQI[channel].decimate16_inf(&it, buf, len);
break;
case 5:
m_decimatorsQI[channel].decimate32_inf(&it, buf, len);
break;
case 6:
m_decimatorsQI[channel].decimate64_inf(&it, buf, len);
break;
default:
break;
}
}
else if (m_fcPos == 1) // Supra
{
switch (m_log2Decim)
{
case 1:
m_decimatorsQI[channel].decimate2_sup(&it, buf, len);
break;
case 2:
m_decimatorsQI[channel].decimate4_sup(&it, buf, len);
break;
case 3:
m_decimatorsQI[channel].decimate8_sup(&it, buf, len);
break;
case 4:
m_decimatorsQI[channel].decimate16_sup(&it, buf, len);
break;
case 5:
m_decimatorsQI[channel].decimate32_sup(&it, buf, len);
break;
case 6:
m_decimatorsQI[channel].decimate64_sup(&it, buf, len);
break;
default:
break;
}
}
else if (m_fcPos == 2) // Center
{
switch (m_log2Decim)
{
case 1:
m_decimatorsQI[channel].decimate2_cen(&it, buf, len);
break;
case 2:
m_decimatorsQI[channel].decimate4_cen(&it, buf, len);
break;
case 3:
m_decimatorsQI[channel].decimate8_cen(&it, buf, len);
break;
case 4:
m_decimatorsQI[channel].decimate16_cen(&it, buf, len);
break;
case 5:
m_decimatorsQI[channel].decimate32_cen(&it, buf, len);
break;
case 6:
m_decimatorsQI[channel].decimate64_cen(&it, buf, len);
break;
default:
break;
}
}
}
return it - m_convertBuffer[channel].begin();
}

View File

@ -0,0 +1,73 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef PLUGINS_SAMPLEMIMO_BLADERF2MIMO_BLADERF2MITHREAD_H_
#define PLUGINS_SAMPLEMIMO_BLADERF2MIMO_BLADERF2MITHREAD_H_
// BladerRF2 is a SISO/MIMO device. It can support one or two Rx. Here ww will
// configure two Rx
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include <libbladeRF.h>
#include "dsp/decimators.h"
class SampleMIFifo;
class BladeRF2MIThread : public QThread {
Q_OBJECT
public:
BladeRF2MIThread(struct bladerf* dev, QObject* parent = nullptr);
~BladeRF2MIThread();
void startWork();
void stopWork();
bool isRunning() const { return m_running; }
void setLog2Decimation(unsigned int log2_decim);
unsigned int getLog2Decimation() const;
void setFcPos(int fcPos);
int getFcPos() const;
void setFifo(SampleMIFifo *sampleFifo) { m_sampleFifo = sampleFifo; }
SampleMIFifo *getFifo() { return m_sampleFifo; }
void setIQOrder(bool iqOrder) { m_iqOrder = iqOrder; }
private:
QMutex m_startWaitMutex;
QWaitCondition m_startWaiter;
bool m_running;
struct bladerf* m_dev;
qint16 *m_buf;
SampleVector m_convertBuffer[2];
SampleMIFifo* m_sampleFifo;
Decimators<qint32, qint16, SDR_RX_SAMP_SZ, 12, true> m_decimatorsIQ[2];
Decimators<qint32, qint16, SDR_RX_SAMP_SZ, 12, false> m_decimatorsQI[2];
unsigned int m_log2Decim;
int m_fcPos;
bool m_iqOrder;
void run();
void callback(const qint16* buf, qint32 samplesPerChannel);
int channelCallbackIQ(const qint16* buf, qint32 len, int channel);
int channelCallbackQI(const qint16* buf, qint32 len, int channel);
};
#endif // PLUGINS_SAMPLEMIMO_BLADERF2MIMO_BLADERF2MITHREAD_H_

View File

@ -0,0 +1,238 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "bladerf2/devicebladerf2shared.h"
#include "dsp/samplemofifo.h"
#include "bladerf2mothread.h"
BladeRF2MOThread::BladeRF2MOThread(struct bladerf* dev, QObject* parent) :
QThread(parent),
m_running(false),
m_dev(dev),
m_log2Interp(0)
{
qDebug("BladeRF2MOThread::BladeRF2MOThread");
m_buf = new qint16[2*DeviceBladeRF2::blockSize*2];
}
BladeRF2MOThread::~BladeRF2MOThread()
{
qDebug("BladeRF2MOThread::~BladeRF2MOThread");
if (m_running) {
stopWork();
}
delete[] m_buf;
}
void BladeRF2MOThread::startWork()
{
m_startWaitMutex.lock();
start();
while(!m_running) {
m_startWaiter.wait(&m_startWaitMutex, 100);
}
m_startWaitMutex.unlock();
}
void BladeRF2MOThread::stopWork()
{
m_running = false;
wait();
}
void BladeRF2MOThread::run()
{
int res;
m_running = true;
m_startWaiter.wakeAll();
int status;
status = bladerf_sync_config(m_dev, BLADERF_TX_X2, BLADERF_FORMAT_SC16_Q11, 128, 16384, 32, 1500);
if (status < 0)
{
qCritical("BladeRF2MOThread::run: cannot configure streams: %s", bladerf_strerror(status));
}
else
{
qDebug("BladeRF2MOThread::run: start running loop");
while (m_running)
{
callback(m_buf, DeviceBladeRF2::blockSize);
res = bladerf_sync_tx(m_dev, m_buf, DeviceBladeRF2::blockSize*2, 0, 1500);
if (res < 0)
{
qCritical("BladeRF2MOThread::run sync Rx error: %s", bladerf_strerror(res));
break;
}
}
qDebug("BladeRF2MOThread::run: stop running loop");
}
m_running = false;
}
void BladeRF2MOThread::setLog2Interpolation(unsigned int log2Interp)
{
qDebug("BladeRF2MOThread::setLog2Interpolation: %u", log2Interp);
m_log2Interp = log2Interp;
}
unsigned int BladeRF2MOThread::getLog2Interpolation() const
{
return m_log2Interp;
}
void BladeRF2MOThread::setFcPos(int fcPos)
{
m_fcPos = fcPos;
}
int BladeRF2MOThread::getFcPos() const
{
return m_fcPos;
}
void BladeRF2MOThread::callback(qint16* buf, qint32 samplesPerChannel)
{
unsigned int iPart1Begin, iPart1End, iPart2Begin, iPart2End;
m_sampleFifo->readSync(samplesPerChannel/(1<<m_log2Interp), iPart1Begin, iPart1End, iPart2Begin, iPart2End);
if (iPart1Begin != iPart1End)
{
callbackPart(buf, (iPart1End - iPart1Begin)*(1<<m_log2Interp), iPart1Begin);
}
if (iPart2Begin != iPart2End)
{
unsigned int shift = (iPart1End - iPart1Begin)*(1<<m_log2Interp);
callbackPart(buf + 2*shift, (iPart2End - iPart2Begin)*(1<<m_log2Interp), iPart2Begin);
}
int status = bladerf_interleave_stream_buffer(BLADERF_TX_X2, BLADERF_FORMAT_SC16_Q11 , samplesPerChannel*2, (void *) buf);
if (status < 0)
{
qCritical("BladeRF2MOThread::callback: cannot interleave buffer: %s", bladerf_strerror(status));
return;
}
}
// Interpolate according to specified log2 (ex: log2=4 => decim=16). len is a number of samples (not a number of I or Q)
void BladeRF2MOThread::callbackPart(qint16* buf, qint32 nSamples, int iBegin)
{
for (unsigned int channel = 0; channel < 2; channel++)
{
SampleVector::iterator begin = m_sampleFifo->getData(channel).begin() + iBegin;
if (m_log2Interp == 0)
{
m_interpolators[channel].interpolate1(&begin, &buf[channel*2*nSamples], 2*nSamples);
}
else
{
if (m_fcPos == 0) // Infra
{
switch (m_log2Interp)
{
case 1:
m_interpolators[channel].interpolate2_inf(&begin, &buf[channel*2*nSamples], 2*nSamples);
break;
case 2:
m_interpolators[channel].interpolate4_inf(&begin, &buf[channel*2*nSamples], 2*nSamples);
break;
case 3:
m_interpolators[channel].interpolate8_inf(&begin, &buf[channel*2*nSamples], 2*nSamples);
break;
case 4:
m_interpolators[channel].interpolate16_inf(&begin, &buf[channel*2*nSamples], 2*nSamples);
break;
case 5:
m_interpolators[channel].interpolate32_inf(&begin, &buf[channel*2*nSamples], 2*nSamples);
break;
case 6:
m_interpolators[channel].interpolate64_inf(&begin, &buf[channel*2*nSamples], 2*nSamples);
break;
default:
break;
}
}
else if (m_fcPos == 1) // Supra
{
switch (m_log2Interp)
{
case 1:
m_interpolators[channel].interpolate2_sup(&begin, &buf[channel*2*nSamples], 2*nSamples);
break;
case 2:
m_interpolators[channel].interpolate4_sup(&begin, &buf[channel*2*nSamples], 2*nSamples);
break;
case 3:
m_interpolators[channel].interpolate8_sup(&begin, &buf[channel*2*nSamples], 2*nSamples);
break;
case 4:
m_interpolators[channel].interpolate16_sup(&begin, &buf[channel*2*nSamples], 2*nSamples);
break;
case 5:
m_interpolators[channel].interpolate32_sup(&begin, &buf[channel*2*nSamples], 2*nSamples);
break;
case 6:
m_interpolators[channel].interpolate64_sup(&begin, &buf[channel*2*nSamples], 2*nSamples);
break;
default:
break;
}
}
else if (m_fcPos == 2) // Center
{
switch (m_log2Interp)
{
case 1:
m_interpolators[channel].interpolate2_cen(&begin, &buf[channel*2*nSamples], 2*nSamples);
break;
case 2:
m_interpolators[channel].interpolate4_cen(&begin, &buf[channel*2*nSamples], 2*nSamples);
break;
case 3:
m_interpolators[channel].interpolate8_cen(&begin, &buf[channel*2*nSamples], 2*nSamples);
break;
case 4:
m_interpolators[channel].interpolate16_cen(&begin, &buf[channel*2*nSamples], 2*nSamples);
break;
case 5:
m_interpolators[channel].interpolate32_cen(&begin, &buf[channel*2*nSamples], 2*nSamples);
break;
case 6:
m_interpolators[channel].interpolate64_cen(&begin, &buf[channel*2*nSamples], 2*nSamples);
break;
default:
break;
}
}
}
}
}

View File

@ -0,0 +1,67 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef PLUGINS_SAMPLEMIMO_BLADERF2MIMO_BLADERF2MOTHREAD_H_
#define PLUGINS_SAMPLEMIMO_BLADERF2MIMO_BLADERF2MOTHREAD_H_
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include <libbladeRF.h>
#include "dsp/interpolators.h"
class SampleMOFifo;
class BladeRF2MOThread : public QThread {
Q_OBJECT
public:
BladeRF2MOThread(struct bladerf* dev, QObject* parent = nullptr);
~BladeRF2MOThread();
void startWork();
void stopWork();
bool isRunning() const { return m_running; }
void setLog2Interpolation(unsigned int log2Interp);
unsigned int getLog2Interpolation() const;
void setFcPos(int fcPos);
int getFcPos() const;
void setFifo(SampleMOFifo *sampleFifo) { m_sampleFifo = sampleFifo; }
SampleMOFifo *getFifo() { return m_sampleFifo; }
private:
QMutex m_startWaitMutex;
QWaitCondition m_startWaiter;
bool m_running;
struct bladerf* m_dev;
qint16 *m_buf; //!< Full buffer for SISO or MIMO operation
SampleMOFifo* m_sampleFifo;
Interpolators<qint16, SDR_TX_SAMP_SZ, 12> m_interpolators[2];
unsigned int m_log2Interp;
int m_fcPos;
void run();
unsigned int getNbFifos();
void callbackPart(qint16* buf, qint32 nSamples, int iBegin);
void callback(qint16* buf, qint32 samplesPerChannel);
};
#endif /* PLUGINS_SAMPLEMIMO_BLADERF2MIMO_BLADERF2MOTHREAD_H_ */

View File

@ -0,0 +1,133 @@
<h1>BladeRF 2.0 micro (v2) MIMO plugin</h1>
<h2>Introduction</h2>
This is a v5 only plugin.
This MIMO plugin sends and receives its samples to/from a [BladeRF2 device](https://www.nuand.com/bladerf-2). It handles both input (Rx) and output (Tx) streams synchronously. There is no option to synchronize Rx with Tx streams.
<h2>Build</h2>
As with other BladeRF plugins this plugin will be built only if the [BladeRF host library](https://github.com/Nuand/bladeRF) is installed in your system. If you build it from source and install it in a custom location say: `/opt/install/libbladeRF` you will have to add `-DBLADERF_DIR=/opt/install/libbladeRF/include` to the cmake command line.
Note that libbladeRF v2 with git tag 2018.10-rc1 should be used (official release) thus:
- The FX3 firmware version should be v2.3.1
- The FPGA image version should be v0.9.0
The FPGA .rbf file should be copied to the folder where the `sdrangel` binary resides. You can download FPGA images from [here](https://www.nuand.com/fpga_images/)
The BladeRF Host library is also provided by many Linux distributions (check its version) and is built in the SDRangel binary releases.
<h2>Interface</h2>
![BladeRF2 MIMO plugin GUI](../../../doc/img/BladeRF2MIMO_plugin.png)
<h3>1. Rx/Tx settings selection</h3>
Use this combo to target UI to Rx or Tx streams for Rx/Tx specific items.
<h3>2. Stream settings selection</h3>
Use this combo to target UI to stream 0 or stream 1 for stream specific items.
<h3>3. Rx/Tx spectrum display selection</h3>
Use this combo to select Rx or Tx side for main spectrum display.
<h3>4. Stream spectrum display selection</h3>
Use this combo to select stream 0 or stream 1 for main spectrum display.
<h3>5. Start/Stop Rx</h3>
This button controls the start/stop of the Rx subsystem.
<h3>6. Start/Stop Tx</h3>
This button controls the start/stop of the Tx subsystem.
<h3>7. Record button</h3>
- Left click: record baseband I/Q stream toggle button (inactive: waiting for synchronous streams recording)
- Right click: choose record file
<h3>8. Center frequency</h3>
This controls the center frequency of Rx or Tx subsystems in kHz depending on the Rx/Tx settings selection (1). This frequency can effectively be different for Rx and Tx but is the same for both Rx or both Tx streams.
<h3>9. LO ppm correction</h3>
Use this slider to adjust LO correction in ppm. It can be varied from -20.0 to 20.0 in 0.1 steps and is applied in software. This applies to the oscillator that controls both the Rx and Tx frequency therefore it is not dependent on Rx/Tx or stream selection.
<h3>10. DC auto correction options</h3>
This button controls the local DSP DC auto remove DC component.
<h3>11. IQ auto correction options</h3>
This button controls the local DSP auto make I/Q balance. The DC correction must be enabled for this to be effective.
<h3>12. Analog filter bandwidth</h3>
This is the analog filter bandwidth in kHz that applies to Rx or Tx streams. Minimum and maximum values are adjusted automatically and are the same for Rx and Tx subsystems ranging from 200 kHz to 56 MHz.
<h3>13. Transverter mode open dialog</h3>
This button opens a dialog to set the transverter mode frequency translation options. The details about this dialog can be found [here](../../../sdrgui/gui/transverterdialog.md)
<h3>14. Device sample rate / Baseband sample rate input toggle</h3>
Use this toggle button to switch the sample rate input next (15) between device sample rate and baseband sample rate input. The button shows the current mode:
- **SR**: device sample rate input mode. The baseband sample rate (7A) is the device sample rate (15) divided by the decimation or multiplied by the interpolation factor (17).
- **BB**: baseband sample rate input mode. The device sample rate (7A) is the baseband sample rate (15) divided by the decimation or multiplied by the interpolation factor (17).
<h3>15. Host/Device sample rate</h3>
This controls the sample rate between Host and Device in both directions. Effectively ADC and DAC run on the same sample rate.
<h3>16. Baseband center frequency position relative the the BladeRF center frequency</h3>
Possible values are:
- **Cen**: the decimation operation takes place around the BladeRF Rx center frequency Fs
- **Inf**: the decimation operation takes place around Fs - Fc.
- **Sup**: the decimation operation takes place around Fs + Fc.
With SR as the sample rate before decimation Fc is calculated as:
- if decimation n is 4 or lower: Fc = SR/2^(log2(n)-1). The device center frequency is on the side of the baseband. You need a RF filter bandwidth at least twice the baseband.
- if decimation n is 8 or higher: Fc = SR/n. The device center frequency is half the baseband away from the side of the baseband. You need a RF filter bandwidth at least 3 times the baseband.
<h3>17. Decimation (Rx) or Interpolation (Tx) factor</h3>
For Rx streams the I/Q stream from the BladeRF ADC is downsampled by a power of two before being sent to the passband.
For Tx strams the baseband stream is interpolated by this value before being sent to the BladeRF device.
Possible values are increasing powers of two: 1 (no decimation or interpolation), 2, 4, 8, 16, 32, 64.
<h3>18. Lock both streams gains</h3>
When engaged this applies the same gain with value control (20) to both streams (Rx or Tx)
<h3>19. Gain mode</h3>
This is the gain mode control that differs between Rx and Tx streams:
- **Rx**:
- **automatic**: AGC with default behavior
- **manual**: Manual. Use control (20) to adjust gain
- **fast**: fast AGC
- **slow**: slow AGC
- **hybrid**: hybrid AGC
- **Tx:**:
- **automatic**: Manual actually. Use control (20) to adjust gain
<h3>20. Manual gain setting</h3>
This sets the gain manually per stream or for both streams if (18) is engaged.
<h3>21. Bias Tee</h3>
This sets the bias tee on all Rx or Tx ports.

View File

@ -488,7 +488,7 @@ void DeviceAPI::loadSamplingDeviceSettings(const Preset* preset)
} }
// set center frequency anyway // set center frequency anyway
if (m_deviceSourceEngine->getSource() != 0) // Server flavor if (m_deviceSourceEngine->getSource())
{ {
m_deviceSourceEngine->getSource()->setCenterFrequency(centerFrequency); m_deviceSourceEngine->getSource()->setCenterFrequency(centerFrequency);
} }
@ -510,7 +510,7 @@ void DeviceAPI::loadSamplingDeviceSettings(const Preset* preset)
qDebug("DeviceAPI::loadSamplingDeviceSettings: deserializing sink %s[%d]: %s", qDebug("DeviceAPI::loadSamplingDeviceSettings: deserializing sink %s[%d]: %s",
qPrintable(m_samplingDeviceId), m_samplingDeviceSequence, qPrintable(m_samplingDeviceSerial)); qPrintable(m_samplingDeviceId), m_samplingDeviceSequence, qPrintable(m_samplingDeviceSerial));
if (m_deviceSinkEngine->getSink() != 0) // Server flavor if (m_deviceSinkEngine->getSink())
{ {
m_deviceSinkEngine->getSink()->deserialize(*sinkConfig); m_deviceSinkEngine->getSink()->deserialize(*sinkConfig);
m_deviceSinkEngine->getSink()->setCenterFrequency(centerFrequency); m_deviceSinkEngine->getSink()->setCenterFrequency(centerFrequency);
@ -539,10 +539,11 @@ void DeviceAPI::loadSamplingDeviceSettings(const Preset* preset)
qDebug("DeviceAPI::loadSamplingDeviceSettings: deserializing MIMO %s[%d]: %s", qDebug("DeviceAPI::loadSamplingDeviceSettings: deserializing MIMO %s[%d]: %s",
qPrintable(m_samplingDeviceId), m_samplingDeviceSequence, qPrintable(m_samplingDeviceSerial)); qPrintable(m_samplingDeviceId), m_samplingDeviceSequence, qPrintable(m_samplingDeviceSerial));
if (m_deviceSinkEngine->getSink() != 0) // Server flavor if (m_deviceMIMOEngine->getMIMO())
{ {
m_deviceSinkEngine->getSink()->deserialize(*mimoConfig); m_deviceMIMOEngine->getMIMO()->deserialize(*mimoConfig);
m_deviceSinkEngine->getSink()->setCenterFrequency(centerFrequency); m_deviceMIMOEngine->getMIMO()->setSourceCenterFrequency(centerFrequency, 0);
m_deviceMIMOEngine->getMIMO()->setSinkCenterFrequency(centerFrequency, 0);
} }
else else
{ {