mirror of
https://github.com/f4exb/sdrangel.git
synced 2026-06-01 21:54:55 -04:00
git clone git://git.osmocom.org/sdrangelove.git
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
project(plugins)
|
||||
|
||||
add_subdirectory(channel)
|
||||
add_subdirectory(samplesource)
|
||||
@@ -0,0 +1,5 @@
|
||||
project(demod)
|
||||
|
||||
add_subdirectory(nfm)
|
||||
add_subdirectory(tcpsrc)
|
||||
#add_subdirectory(tetra)
|
||||
@@ -0,0 +1,47 @@
|
||||
project(nfm)
|
||||
|
||||
set(nfm_SOURCES
|
||||
nfmdemod.cpp
|
||||
nfmdemodgui.cpp
|
||||
nfmplugin.cpp
|
||||
)
|
||||
|
||||
set(nfm_HEADERS
|
||||
nfmdemod.h
|
||||
nfmdemodgui.h
|
||||
nfmplugin.h
|
||||
)
|
||||
|
||||
set(nfm_FORMS
|
||||
nfmdemodgui.ui
|
||||
)
|
||||
|
||||
include_directories(
|
||||
.
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_SOURCE_DIR}/include
|
||||
${CMAKE_SOURCE_DIR}/include-gpl
|
||||
${OPENGL_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
#include(${QT_USE_FILE})
|
||||
add_definitions(${QT_DEFINITIONS})
|
||||
add_definitions(-DQT_PLUGIN)
|
||||
add_definitions(-DQT_SHARED)
|
||||
|
||||
#qt5_wrap_cpp(nfm_HEADERS_MOC ${nfm_HEADERS})
|
||||
qt5_wrap_ui(nfm_FORMS_HEADERS ${nfm_FORMS})
|
||||
|
||||
add_library(demodnfm SHARED
|
||||
${nfm_SOURCES}
|
||||
${nfm_HEADERS_MOC}
|
||||
${nfm_FORMS_HEADERS}
|
||||
)
|
||||
|
||||
target_link_libraries(demodnfm
|
||||
${QT_LIBRARIES}
|
||||
${OPENGL_LIBRARIES}
|
||||
sdrbase
|
||||
)
|
||||
|
||||
qt5_use_modules(demodnfm Core Widgets OpenGL Multimedia)
|
||||
@@ -0,0 +1,147 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// //
|
||||
// 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 //
|
||||
// //
|
||||
// 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 <QTime>
|
||||
#include <stdio.h>
|
||||
#include "nfmdemod.h"
|
||||
#include "audio/audiooutput.h"
|
||||
#include "dsp/dspcommands.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(NFMDemod::MsgConfigureNFMDemod, Message)
|
||||
|
||||
NFMDemod::NFMDemod(AudioFifo* audioFifo, SampleSink* sampleSink) :
|
||||
m_sampleSink(sampleSink),
|
||||
m_audioFifo(audioFifo)
|
||||
{
|
||||
m_rfBandwidth = 12500;
|
||||
m_volume = 2.0;
|
||||
m_squelchLevel = pow(10.0, -40.0 / 20.0);
|
||||
m_sampleRate = 500000;
|
||||
m_frequency = 0;
|
||||
m_squelchLevel *= m_squelchLevel;
|
||||
|
||||
m_nco.setFreq(m_frequency, m_sampleRate);
|
||||
m_interpolator.create(16, m_sampleRate, 12500);
|
||||
m_sampleDistanceRemain = (Real)m_sampleRate / 44100.0;
|
||||
|
||||
m_lowpass.create(21, 44100, 3000);
|
||||
|
||||
m_audioBuffer.resize(256);
|
||||
m_audioBufferFill = 0;
|
||||
|
||||
m_movingAverage.resize(16, 0);
|
||||
}
|
||||
|
||||
NFMDemod::~NFMDemod()
|
||||
{
|
||||
}
|
||||
|
||||
void NFMDemod::configure(MessageQueue* messageQueue, Real rfBandwidth, Real afBandwidth, Real volume, Real squelch)
|
||||
{
|
||||
Message* cmd = MsgConfigureNFMDemod::create(rfBandwidth, afBandwidth, volume, squelch);
|
||||
cmd->submit(messageQueue, this);
|
||||
}
|
||||
|
||||
void NFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool firstOfBurst)
|
||||
{
|
||||
Complex ci;
|
||||
bool consumed;
|
||||
|
||||
for(SampleVector::const_iterator it = begin; it < end; ++it) {
|
||||
Complex c(it->real() / 32768.0, it->imag() / 32768.0);
|
||||
c *= m_nco.nextIQ();
|
||||
|
||||
consumed = false;
|
||||
if(m_interpolator.interpolate(&m_sampleDistanceRemain, c, &consumed, &ci)) {
|
||||
m_sampleBuffer.push_back(Sample(ci.real() * 32768.0, ci.imag() * 32768.0));
|
||||
|
||||
m_movingAverage.feed(ci.real() * ci.real() + ci.imag() * ci.imag());
|
||||
|
||||
if(m_movingAverage.average() >= m_squelchLevel)
|
||||
m_squelchState = m_sampleRate / 50;
|
||||
|
||||
if(m_squelchState > 0) {
|
||||
m_squelchState--;
|
||||
Complex d = ci * conj(m_lastSample);
|
||||
m_lastSample = ci;
|
||||
Real demod = atan2(d.imag(), d.real()) / M_PI;
|
||||
demod = m_lowpass.filter(demod);
|
||||
demod *= m_volume;
|
||||
qint16 sample = demod * 32767;
|
||||
|
||||
m_audioBuffer[m_audioBufferFill].l = sample;
|
||||
m_audioBuffer[m_audioBufferFill].r = sample;
|
||||
++m_audioBufferFill;
|
||||
if(m_audioBufferFill >= m_audioBuffer.size()) {
|
||||
uint res = m_audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 1);
|
||||
/*
|
||||
if(res != m_audioBufferFill)
|
||||
qDebug("lost %u samples", m_audioBufferFill - res);
|
||||
*/
|
||||
m_audioBufferFill = 0;
|
||||
}
|
||||
}
|
||||
|
||||
m_sampleDistanceRemain += (Real)m_sampleRate / 44100.0;
|
||||
}
|
||||
}
|
||||
if(m_audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 0) != m_audioBufferFill)
|
||||
;//qDebug("lost samples");
|
||||
m_audioBufferFill = 0;
|
||||
|
||||
if(m_sampleSink != NULL)
|
||||
m_sampleSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), firstOfBurst);
|
||||
m_sampleBuffer.clear();
|
||||
}
|
||||
|
||||
void NFMDemod::start()
|
||||
{
|
||||
m_squelchState = 0;
|
||||
}
|
||||
|
||||
void NFMDemod::stop()
|
||||
{
|
||||
}
|
||||
|
||||
bool NFMDemod::handleMessage(Message* cmd)
|
||||
{
|
||||
if(DSPSignalNotification::match(cmd)) {
|
||||
DSPSignalNotification* signal = (DSPSignalNotification*)cmd;
|
||||
qDebug("%d samples/sec, %lld Hz offset", signal->getSampleRate(), signal->getFrequencyOffset());
|
||||
m_sampleRate = signal->getSampleRate();
|
||||
m_nco.setFreq(-signal->getFrequencyOffset(), m_sampleRate);
|
||||
m_interpolator.create(16, m_sampleRate, m_rfBandwidth / 2.1);
|
||||
m_sampleDistanceRemain = m_sampleRate / 44100.0;
|
||||
m_squelchState = 0;
|
||||
cmd->completed();
|
||||
return true;
|
||||
} else if(MsgConfigureNFMDemod::match(cmd)) {
|
||||
MsgConfigureNFMDemod* cfg = (MsgConfigureNFMDemod*)cmd;
|
||||
m_rfBandwidth = cfg->getRFBandwidth();
|
||||
m_interpolator.create(16, m_sampleRate, m_rfBandwidth / 2.1);
|
||||
m_lowpass.create(21, 44100, cfg->getAFBandwidth());
|
||||
m_squelchLevel = pow(10.0, cfg->getSquelch() / 20.0);
|
||||
m_squelchLevel *= m_squelchLevel;
|
||||
m_volume = cfg->getVolume();
|
||||
cmd->completed();
|
||||
return true;
|
||||
} else {
|
||||
if(m_sampleSink != NULL)
|
||||
return m_sampleSink->handleMessage(cmd);
|
||||
else return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// //
|
||||
// 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 //
|
||||
// //
|
||||
// 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 INCLUDE_NFMDEMOD_H
|
||||
#define INCLUDE_NFMDEMOD_H
|
||||
|
||||
#include <vector>
|
||||
#include "dsp/samplesink.h"
|
||||
#include "dsp/nco.h"
|
||||
#include "dsp/interpolator.h"
|
||||
#include "dsp/lowpass.h"
|
||||
#include "dsp/movingaverage.h"
|
||||
#include "audio/audiofifo.h"
|
||||
#include "util/message.h"
|
||||
|
||||
class AudioFifo;
|
||||
|
||||
class NFMDemod : public SampleSink {
|
||||
public:
|
||||
NFMDemod(AudioFifo* audioFifo, SampleSink* sampleSink);
|
||||
~NFMDemod();
|
||||
|
||||
void configure(MessageQueue* messageQueue, Real rfBandwidth, Real afBandwidth, Real volume, Real squelch);
|
||||
|
||||
void feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool firstOfBurst);
|
||||
void start();
|
||||
void stop();
|
||||
bool handleMessage(Message* cmd);
|
||||
|
||||
private:
|
||||
class MsgConfigureNFMDemod : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
Real getRFBandwidth() const { return m_rfBandwidth; }
|
||||
Real getAFBandwidth() const { return m_afBandwidth; }
|
||||
Real getVolume() const { return m_volume; }
|
||||
Real getSquelch() const { return m_squelch; }
|
||||
|
||||
static MsgConfigureNFMDemod* create(Real rfBandwidth, Real afBandwidth, Real volume, Real squelch)
|
||||
{
|
||||
return new MsgConfigureNFMDemod(rfBandwidth, afBandwidth, volume, squelch);
|
||||
}
|
||||
|
||||
private:
|
||||
Real m_rfBandwidth;
|
||||
Real m_afBandwidth;
|
||||
Real m_volume;
|
||||
Real m_squelch;
|
||||
|
||||
MsgConfigureNFMDemod(Real rfBandwidth, Real afBandwidth, Real volume, Real squelch) :
|
||||
Message(),
|
||||
m_rfBandwidth(rfBandwidth),
|
||||
m_afBandwidth(afBandwidth),
|
||||
m_volume(volume),
|
||||
m_squelch(squelch)
|
||||
{ }
|
||||
};
|
||||
|
||||
struct AudioSample {
|
||||
qint16 l;
|
||||
qint16 r;
|
||||
};
|
||||
typedef std::vector<AudioSample> AudioVector;
|
||||
|
||||
Real m_rfBandwidth;
|
||||
Real m_volume;
|
||||
Real m_squelchLevel;
|
||||
int m_sampleRate;
|
||||
int m_frequency;
|
||||
|
||||
NCO m_nco;
|
||||
Interpolator m_interpolator;
|
||||
Real m_sampleDistanceRemain;
|
||||
Lowpass<Real> m_lowpass;
|
||||
|
||||
int m_squelchState;
|
||||
|
||||
Complex m_lastSample;
|
||||
MovingAverage m_movingAverage;
|
||||
|
||||
AudioVector m_audioBuffer;
|
||||
uint m_audioBufferFill;
|
||||
AudioFifo* m_audioFifo;
|
||||
|
||||
SampleSink* m_sampleSink;
|
||||
SampleVector m_sampleBuffer;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_NFMDEMOD_H
|
||||
@@ -0,0 +1,210 @@
|
||||
#include <QDockWidget>
|
||||
#include <QMainWindow>
|
||||
#include "nfmdemodgui.h"
|
||||
#include "ui_nfmdemodgui.h"
|
||||
#include "nfmdemodgui.h"
|
||||
#include "ui_nfmdemodgui.h"
|
||||
#include "dsp/threadedsamplesink.h"
|
||||
#include "dsp/channelizer.h"
|
||||
#include "nfmdemod.h"
|
||||
#include "dsp/spectrumvis.h"
|
||||
#include "gui/glspectrum.h"
|
||||
#include "plugin/pluginapi.h"
|
||||
#include "util/simpleserializer.h"
|
||||
#include "gui/basicchannelsettingswidget.h"
|
||||
|
||||
const int NFMDemodGUI::m_rfBW[] = {
|
||||
5000, 6250, 8330, 10000, 12500, 15000, 20000, 25000, 40000
|
||||
};
|
||||
|
||||
NFMDemodGUI* NFMDemodGUI::create(PluginAPI* pluginAPI)
|
||||
{
|
||||
NFMDemodGUI* gui = new NFMDemodGUI(pluginAPI);
|
||||
return gui;
|
||||
}
|
||||
|
||||
void NFMDemodGUI::destroy()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void NFMDemodGUI::setName(const QString& name)
|
||||
{
|
||||
setObjectName(name);
|
||||
}
|
||||
|
||||
void NFMDemodGUI::resetToDefaults()
|
||||
{
|
||||
ui->rfBW->setValue(4);
|
||||
ui->afBW->setValue(3);
|
||||
ui->volume->setValue(20);
|
||||
ui->squelch->setValue(-40);
|
||||
ui->spectrumGUI->resetToDefaults();
|
||||
applySettings();
|
||||
}
|
||||
|
||||
QByteArray NFMDemodGUI::serialize() const
|
||||
{
|
||||
SimpleSerializer s(1);
|
||||
s.writeS32(1, m_channelMarker->getCenterFrequency());
|
||||
s.writeS32(2, ui->rfBW->value());
|
||||
s.writeS32(3, ui->afBW->value());
|
||||
s.writeS32(4, ui->volume->value());
|
||||
s.writeS32(5, ui->squelch->value());
|
||||
s.writeBlob(6, ui->spectrumGUI->serialize());
|
||||
s.writeU32(7, m_channelMarker->getColor().rgb());
|
||||
return s.final();
|
||||
}
|
||||
|
||||
bool NFMDemodGUI::deserialize(const QByteArray& data)
|
||||
{
|
||||
SimpleDeserializer d(data);
|
||||
|
||||
if(!d.isValid()) {
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(d.getVersion() == 1) {
|
||||
QByteArray bytetmp;
|
||||
quint32 u32tmp;
|
||||
qint32 tmp;
|
||||
d.readS32(1, &tmp, 0);
|
||||
m_channelMarker->setCenterFrequency(tmp);
|
||||
d.readS32(2, &tmp, 4);
|
||||
ui->rfBW->setValue(tmp);
|
||||
d.readS32(3, &tmp, 3);
|
||||
ui->afBW->setValue(tmp);
|
||||
d.readS32(4, &tmp, 20);
|
||||
ui->volume->setValue(tmp);
|
||||
d.readS32(5, &tmp, -40);
|
||||
ui->squelch->setValue(tmp);
|
||||
d.readBlob(6, &bytetmp);
|
||||
ui->spectrumGUI->deserialize(bytetmp);
|
||||
if(d.readU32(7, &u32tmp))
|
||||
m_channelMarker->setColor(u32tmp);
|
||||
applySettings();
|
||||
return true;
|
||||
} else {
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool NFMDemodGUI::handleMessage(Message* message)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void NFMDemodGUI::viewChanged()
|
||||
{
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void NFMDemodGUI::on_rfBW_valueChanged(int value)
|
||||
{
|
||||
ui->rfBWText->setText(QString("%1 kHz").arg(m_rfBW[value] / 1000.0));
|
||||
m_channelMarker->setBandwidth(m_rfBW[value]);
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void NFMDemodGUI::on_afBW_valueChanged(int value)
|
||||
{
|
||||
ui->afBWText->setText(QString("%1 kHz").arg(value));
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void NFMDemodGUI::on_volume_valueChanged(int value)
|
||||
{
|
||||
ui->volumeText->setText(QString("%1").arg(value / 10.0, 0, 'f', 1));
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void NFMDemodGUI::on_squelch_valueChanged(int value)
|
||||
{
|
||||
ui->squelchText->setText(QString("%1 dB").arg(value));
|
||||
applySettings();
|
||||
}
|
||||
|
||||
|
||||
void NFMDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown)
|
||||
{
|
||||
/*
|
||||
if((widget == ui->spectrumContainer) && (m_nfmDemod != NULL))
|
||||
m_nfmDemod->setSpectrum(m_threadedSampleSink->getMessageQueue(), rollDown);
|
||||
*/
|
||||
}
|
||||
|
||||
void NFMDemodGUI::onMenuDoubleClicked()
|
||||
{
|
||||
if(!m_basicSettingsShown) {
|
||||
m_basicSettingsShown = true;
|
||||
BasicChannelSettingsWidget* bcsw = new BasicChannelSettingsWidget(m_channelMarker, this);
|
||||
bcsw->show();
|
||||
}
|
||||
}
|
||||
|
||||
NFMDemodGUI::NFMDemodGUI(PluginAPI* pluginAPI, QWidget* parent) :
|
||||
RollupWidget(parent),
|
||||
ui(new Ui::NFMDemodGUI),
|
||||
m_pluginAPI(pluginAPI),
|
||||
m_basicSettingsShown(false)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
setAttribute(Qt::WA_DeleteOnClose, true);
|
||||
connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
|
||||
connect(this, SIGNAL(menuDoubleClickEvent()), this, SLOT(onMenuDoubleClicked()));
|
||||
|
||||
m_audioFifo = new AudioFifo(4, 44100 / 4);
|
||||
m_spectrumVis = new SpectrumVis(ui->glSpectrum);
|
||||
m_nfmDemod = new NFMDemod(m_audioFifo, m_spectrumVis);
|
||||
m_channelizer = new Channelizer(m_nfmDemod);
|
||||
m_threadedSampleSink = new ThreadedSampleSink(m_channelizer);
|
||||
m_pluginAPI->addAudioSource(m_audioFifo);
|
||||
m_pluginAPI->addSampleSink(m_threadedSampleSink);
|
||||
|
||||
ui->glSpectrum->setCenterFrequency(0);
|
||||
ui->glSpectrum->setSampleRate(44100);
|
||||
ui->glSpectrum->setDisplayWaterfall(true);
|
||||
ui->glSpectrum->setDisplayMaxHold(true);
|
||||
m_spectrumVis->configure(m_threadedSampleSink->getMessageQueue(), 64, 10, FFTWindow::BlackmanHarris);
|
||||
|
||||
m_channelMarker = new ChannelMarker(this);
|
||||
m_channelMarker->setColor(Qt::red);
|
||||
m_channelMarker->setBandwidth(12500);
|
||||
m_channelMarker->setCenterFrequency(0);
|
||||
m_channelMarker->setVisible(true);
|
||||
connect(m_channelMarker, SIGNAL(changed()), this, SLOT(viewChanged()));
|
||||
m_pluginAPI->addChannelMarker(m_channelMarker);
|
||||
|
||||
ui->spectrumGUI->setBuddies(m_threadedSampleSink->getMessageQueue(), m_spectrumVis, ui->glSpectrum);
|
||||
|
||||
applySettings();
|
||||
}
|
||||
|
||||
NFMDemodGUI::~NFMDemodGUI()
|
||||
{
|
||||
m_pluginAPI->removeChannelInstance(this);
|
||||
m_pluginAPI->removeAudioSource(m_audioFifo);
|
||||
m_pluginAPI->removeSampleSink(m_threadedSampleSink);
|
||||
delete m_threadedSampleSink;
|
||||
delete m_channelizer;
|
||||
delete m_nfmDemod;
|
||||
delete m_spectrumVis;
|
||||
delete m_audioFifo;
|
||||
delete m_channelMarker;
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void NFMDemodGUI::applySettings()
|
||||
{
|
||||
setTitleColor(m_channelMarker->getColor());
|
||||
m_channelizer->configure(m_threadedSampleSink->getMessageQueue(),
|
||||
44100,
|
||||
m_channelMarker->getCenterFrequency());
|
||||
m_nfmDemod->configure(m_threadedSampleSink->getMessageQueue(),
|
||||
m_rfBW[ui->rfBW->value()],
|
||||
ui->afBW->value() * 1000.0,
|
||||
ui->volume->value() / 10.0,
|
||||
ui->squelch->value());
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#ifndef INCLUDE_NFMDEMODGUI_H
|
||||
#define INCLUDE_NFMDEMODGUI_H
|
||||
|
||||
#include "gui/rollupwidget.h"
|
||||
#include "plugin/plugingui.h"
|
||||
|
||||
class PluginAPI;
|
||||
class ChannelMarker;
|
||||
|
||||
class AudioFifo;
|
||||
class ThreadedSampleSink;
|
||||
class Channelizer;
|
||||
class NFMDemod;
|
||||
class SpectrumVis;
|
||||
|
||||
namespace Ui {
|
||||
class NFMDemodGUI;
|
||||
}
|
||||
|
||||
class NFMDemodGUI : public RollupWidget, public PluginGUI {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static NFMDemodGUI* create(PluginAPI* pluginAPI);
|
||||
void destroy();
|
||||
|
||||
void setName(const QString& name);
|
||||
|
||||
void resetToDefaults();
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
|
||||
bool handleMessage(Message* message);
|
||||
|
||||
private slots:
|
||||
void viewChanged();
|
||||
void on_rfBW_valueChanged(int value);
|
||||
void on_afBW_valueChanged(int value);
|
||||
void on_volume_valueChanged(int value);
|
||||
void on_squelch_valueChanged(int value);
|
||||
void onWidgetRolled(QWidget* widget, bool rollDown);
|
||||
void onMenuDoubleClicked();
|
||||
|
||||
private:
|
||||
Ui::NFMDemodGUI* ui;
|
||||
PluginAPI* m_pluginAPI;
|
||||
ChannelMarker* m_channelMarker;
|
||||
bool m_basicSettingsShown;
|
||||
|
||||
AudioFifo* m_audioFifo;
|
||||
ThreadedSampleSink* m_threadedSampleSink;
|
||||
Channelizer* m_channelizer;
|
||||
NFMDemod* m_nfmDemod;
|
||||
SpectrumVis* m_spectrumVis;
|
||||
|
||||
static const int m_rfBW[];
|
||||
|
||||
explicit NFMDemodGUI(PluginAPI* pluginAPI, QWidget* parent = NULL);
|
||||
~NFMDemodGUI();
|
||||
|
||||
void applySettings();
|
||||
};
|
||||
|
||||
#endif // INCLUDE_NFMDEMODGUI_H
|
||||
@@ -0,0 +1,250 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>NFMDemodGUI</class>
|
||||
<widget class="RollupWidget" name="NFMDemodGUI">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>302</width>
|
||||
<height>410</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>NFM Demodulator</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="settingsContainer" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>35</x>
|
||||
<y>35</y>
|
||||
<width>242</width>
|
||||
<height>96</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Settings</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="margin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>RF Bandwidth</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSlider" name="rfBW">
|
||||
<property name="maximum">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QLabel" name="rfBWText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>12.5kHz</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>AF Bandwidth</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QSlider" name="afBW">
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QLabel" name="afBWText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>3 kHz</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Volume</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QSlider" name="volume">
|
||||
<property name="maximum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QLabel" name="volumeText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>2.0</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Squelch</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QSlider" name="squelch">
|
||||
<property name="minimum">
|
||||
<number>-100</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>-40</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="2">
|
||||
<widget class="QLabel" name="squelchText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>-40dB</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="spectrumContainer" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>40</x>
|
||||
<y>140</y>
|
||||
<width>218</width>
|
||||
<height>184</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Channel Spectrum</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="GLSpectrum" name="glSpectrum" native="true">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>150</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="GLSpectrumGUI" name="spectrumGUI" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>GLSpectrum</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/glspectrum.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>GLSpectrumGUI</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/glspectrumgui.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>RollupWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/rollupwidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,53 @@
|
||||
#include <QtPlugin>
|
||||
#include <QAction>
|
||||
#include "plugin/pluginapi.h"
|
||||
#include "nfmplugin.h"
|
||||
#include "nfmdemodgui.h"
|
||||
|
||||
const PluginDescriptor NFMPlugin::m_pluginDescriptor = {
|
||||
QString("NFM Demodulator"),
|
||||
QString("---"),
|
||||
QString("(c) maintech GmbH (written by Christian Daniel)"),
|
||||
QString("http://www.maintech.de"),
|
||||
true,
|
||||
QString("http://www.maintech.de")
|
||||
};
|
||||
|
||||
NFMPlugin::NFMPlugin(QObject* parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
const PluginDescriptor& NFMPlugin::getPluginDescriptor() const
|
||||
{
|
||||
return m_pluginDescriptor;
|
||||
}
|
||||
|
||||
void NFMPlugin::initPlugin(PluginAPI* pluginAPI)
|
||||
{
|
||||
m_pluginAPI = pluginAPI;
|
||||
|
||||
// register NFM demodulator
|
||||
QAction* action = new QAction(tr("&NFM Demodulator"), this);
|
||||
connect(action, SIGNAL(triggered()), this, SLOT(createInstanceNFM()));
|
||||
m_pluginAPI->registerChannel("de.maintech.sdrangelove.channel.nfm", this, action);
|
||||
}
|
||||
|
||||
PluginGUI* NFMPlugin::createChannel(const QString& channelName)
|
||||
{
|
||||
if(channelName == "de.maintech.sdrangelove.channel.nfm") {
|
||||
NFMDemodGUI* gui = NFMDemodGUI::create(m_pluginAPI);
|
||||
m_pluginAPI->registerChannelInstance("de.maintech.sdrangelove.channel.nfm", gui);
|
||||
m_pluginAPI->addChannelRollup(gui);
|
||||
return gui;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void NFMPlugin::createInstanceNFM()
|
||||
{
|
||||
NFMDemodGUI* gui = NFMDemodGUI::create(m_pluginAPI);
|
||||
m_pluginAPI->registerChannelInstance("de.maintech.sdrangelove.channel.nfm", gui);
|
||||
m_pluginAPI->addChannelRollup(gui);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef INCLUDE_NFMPLUGIN_H
|
||||
#define INCLUDE_NFMPLUGIN_H
|
||||
|
||||
#include <QObject>
|
||||
#include "plugin/plugininterface.h"
|
||||
|
||||
class NFMPlugin : public QObject, PluginInterface {
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(PluginInterface)
|
||||
Q_PLUGIN_METADATA(IID "de.maintech.sdrangelove.channel.nfm")
|
||||
|
||||
public:
|
||||
explicit NFMPlugin(QObject* parent = NULL);
|
||||
|
||||
const PluginDescriptor& getPluginDescriptor() const;
|
||||
void initPlugin(PluginAPI* pluginAPI);
|
||||
|
||||
PluginGUI* createChannel(const QString& channelName);
|
||||
|
||||
private:
|
||||
static const PluginDescriptor m_pluginDescriptor;
|
||||
|
||||
PluginAPI* m_pluginAPI;
|
||||
|
||||
private slots:
|
||||
void createInstanceNFM();
|
||||
};
|
||||
|
||||
#endif // INCLUDE_NFMPLUGIN_H
|
||||
@@ -0,0 +1,47 @@
|
||||
project(tcpsrc)
|
||||
|
||||
set(tcpsrc_SOURCES
|
||||
tcpsrc.cpp
|
||||
tcpsrcgui.cpp
|
||||
tcpsrcplugin.cpp
|
||||
)
|
||||
|
||||
set(tcpsrc_HEADERS
|
||||
tcpsrc.h
|
||||
tcpsrcgui.h
|
||||
tcpsrcplugin.h
|
||||
)
|
||||
|
||||
set(tcpsrc_FORMS
|
||||
tcpsrcgui.ui
|
||||
)
|
||||
|
||||
include_directories(
|
||||
.
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_SOURCE_DIR}/include
|
||||
${CMAKE_SOURCE_DIR}/include-gpl
|
||||
${OPENGL_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
#include(${QT_USE_FILE})
|
||||
add_definitions(${QT_DEFINITIONS})
|
||||
add_definitions(-DQT_PLUGIN)
|
||||
add_definitions(-DQT_SHARED)
|
||||
|
||||
#qt5_wrap_cpp(tcpsrc_HEADERS_MOC ${tcpsrc_HEADERS})
|
||||
qt5_wrap_ui(tcpsrc_FORMS_HEADERS ${tcpsrc_FORMS})
|
||||
|
||||
add_library(demodtcpsrc SHARED
|
||||
${tcpsrc_SOURCES}
|
||||
${tcpsrc_HEADERS_MOC}
|
||||
${tcpsrc_FORMS_HEADERS}
|
||||
)
|
||||
|
||||
target_link_libraries(demodtcpsrc
|
||||
${QT_LIBRARIES}
|
||||
${OPENGL_LIBRARIES}
|
||||
sdrbase
|
||||
)
|
||||
|
||||
qt5_use_modules(demodtcpsrc Core Widgets OpenGL Network)
|
||||
@@ -0,0 +1,205 @@
|
||||
#include <QTcpServer>
|
||||
#include <QTcpSocket>
|
||||
#include <QThread>
|
||||
#include "tcpsrc.h"
|
||||
#include "tcpsrcgui.h"
|
||||
#include "dsp/dspcommands.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(TCPSrc::MsgTCPSrcConfigure, Message)
|
||||
MESSAGE_CLASS_DEFINITION(TCPSrc::MsgTCPSrcConnection, Message)
|
||||
MESSAGE_CLASS_DEFINITION(TCPSrc::MsgTCPSrcSpectrum, Message)
|
||||
|
||||
TCPSrc::TCPSrc(MessageQueue* uiMessageQueue, TCPSrcGUI* tcpSrcGUI, SampleSink* spectrum)
|
||||
{
|
||||
m_inputSampleRate = 100000;
|
||||
m_sampleFormat = FormatS8;
|
||||
m_outputSampleRate = 50000;
|
||||
m_rfBandwidth = 50000;
|
||||
m_tcpPort = 9999;
|
||||
m_nco.setFreq(0, m_inputSampleRate);
|
||||
m_interpolator.create(16, m_inputSampleRate, m_rfBandwidth / 2.1);
|
||||
m_sampleDistanceRemain = m_inputSampleRate / m_outputSampleRate;
|
||||
m_uiMessageQueue = uiMessageQueue;
|
||||
m_tcpSrcGUI = tcpSrcGUI;
|
||||
m_spectrum = spectrum;
|
||||
m_spectrumEnabled = false;
|
||||
m_nextS8Id = 0;
|
||||
m_nextS16leId = 0;
|
||||
}
|
||||
|
||||
TCPSrc::~TCPSrc()
|
||||
{
|
||||
}
|
||||
|
||||
void TCPSrc::configure(MessageQueue* messageQueue, SampleFormat sampleFormat, Real outputSampleRate, Real rfBandwidth, int tcpPort)
|
||||
{
|
||||
Message* cmd = MsgTCPSrcConfigure::create(sampleFormat, outputSampleRate, rfBandwidth, tcpPort);
|
||||
cmd->submit(messageQueue, this);
|
||||
}
|
||||
|
||||
void TCPSrc::setSpectrum(MessageQueue* messageQueue, bool enabled)
|
||||
{
|
||||
Message* cmd = MsgTCPSrcSpectrum::create(enabled);
|
||||
cmd->submit(messageQueue, this);
|
||||
}
|
||||
|
||||
void TCPSrc::feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool firstOfBurst)
|
||||
{
|
||||
Complex ci;
|
||||
bool consumed;
|
||||
|
||||
for(SampleVector::const_iterator it = begin; it < end; ++it) {
|
||||
Complex c(it->real() / 32768.0, it->imag() / 32768.0);
|
||||
c *= m_nco.nextIQ();
|
||||
|
||||
consumed = false;
|
||||
if(m_interpolator.interpolate(&m_sampleDistanceRemain, c, &consumed, &ci)) {
|
||||
m_sampleBuffer.push_back(Sample(ci.real() * 32768.0, ci.imag() * 32768.0));
|
||||
m_sampleDistanceRemain += m_inputSampleRate / m_outputSampleRate;
|
||||
}
|
||||
}
|
||||
|
||||
if((m_spectrum != NULL) && (m_spectrumEnabled))
|
||||
m_spectrum->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), firstOfBurst);
|
||||
|
||||
for(int i = 0; i < m_s16leSockets.count(); i++)
|
||||
m_s16leSockets[i].socket->write((const char*)&m_sampleBuffer[0], m_sampleBuffer.size() * 4);
|
||||
|
||||
if(m_s8Sockets.count() > 0) {
|
||||
for(SampleVector::const_iterator it = m_sampleBuffer.begin(); it != m_sampleBuffer.end(); ++it) {
|
||||
m_sampleBufferS8.push_back(it->real() >> 8);
|
||||
m_sampleBufferS8.push_back(it->imag() >> 8);
|
||||
}
|
||||
for(int i = 0; i < m_s8Sockets.count(); i++)
|
||||
m_s8Sockets[i].socket->write((const char*)&m_sampleBufferS8[0], m_sampleBufferS8.size());
|
||||
}
|
||||
|
||||
m_sampleBuffer.clear();
|
||||
m_sampleBufferS8.clear();
|
||||
}
|
||||
|
||||
void TCPSrc::start()
|
||||
{
|
||||
m_tcpServer = new QTcpServer();
|
||||
connect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(onNewConnection()));
|
||||
m_tcpServer->listen(QHostAddress::Any, m_tcpPort);
|
||||
}
|
||||
|
||||
void TCPSrc::stop()
|
||||
{
|
||||
closeAllSockets(&m_s8Sockets);
|
||||
closeAllSockets(&m_s16leSockets);
|
||||
|
||||
if(m_tcpServer->isListening())
|
||||
m_tcpServer->close();
|
||||
delete m_tcpServer;
|
||||
}
|
||||
|
||||
bool TCPSrc::handleMessage(Message* cmd)
|
||||
{
|
||||
if(DSPSignalNotification::match(cmd)) {
|
||||
DSPSignalNotification* signal = (DSPSignalNotification*)cmd;
|
||||
qDebug("%d samples/sec, %lld Hz offset", signal->getSampleRate(), signal->getFrequencyOffset());
|
||||
m_inputSampleRate = signal->getSampleRate();
|
||||
m_nco.setFreq(-signal->getFrequencyOffset(), m_inputSampleRate);
|
||||
m_interpolator.create(16, m_inputSampleRate, m_rfBandwidth / 2.1);
|
||||
m_sampleDistanceRemain = m_inputSampleRate / m_outputSampleRate;
|
||||
cmd->completed();
|
||||
return true;
|
||||
} else if(DSPSignalNotification::match(cmd)) {
|
||||
MsgTCPSrcConfigure* cfg = (MsgTCPSrcConfigure*)cmd;
|
||||
m_sampleFormat = cfg->getSampleFormat();
|
||||
m_outputSampleRate = cfg->getOutputSampleRate();
|
||||
m_rfBandwidth = cfg->getRFBandwidth();
|
||||
if(cfg->getTCPPort() != m_tcpPort) {
|
||||
m_tcpPort = cfg->getTCPPort();
|
||||
if(m_tcpServer->isListening())
|
||||
m_tcpServer->close();
|
||||
m_tcpServer->listen(QHostAddress::Any, m_tcpPort);
|
||||
}
|
||||
m_interpolator.create(16, m_inputSampleRate, m_rfBandwidth / 2.1);
|
||||
m_sampleDistanceRemain = m_inputSampleRate / m_outputSampleRate;
|
||||
cmd->completed();
|
||||
return true;
|
||||
} else if(MsgTCPSrcSpectrum::match(cmd)) {
|
||||
MsgTCPSrcSpectrum* spc = (MsgTCPSrcSpectrum*)cmd;
|
||||
m_spectrumEnabled = spc->getEnabled();
|
||||
cmd->completed();
|
||||
return true;
|
||||
} else {
|
||||
if(m_spectrum != NULL)
|
||||
return m_spectrum->handleMessage(cmd);
|
||||
else return false;
|
||||
}
|
||||
}
|
||||
|
||||
void TCPSrc::closeAllSockets(Sockets* sockets)
|
||||
{
|
||||
for(int i = 0; i < sockets->count(); ++i) {
|
||||
MsgTCPSrcConnection* msg = MsgTCPSrcConnection::create(false, sockets->at(i).id, QHostAddress(), 0);
|
||||
msg->submit(m_uiMessageQueue, (PluginGUI*)m_tcpSrcGUI);
|
||||
sockets->at(i).socket->close();
|
||||
}
|
||||
}
|
||||
|
||||
void TCPSrc::onNewConnection()
|
||||
{
|
||||
while(m_tcpServer->hasPendingConnections()) {
|
||||
QTcpSocket* connection = m_tcpServer->nextPendingConnection();
|
||||
connect(connection, SIGNAL(disconnected()), this, SLOT(onDisconnected()));
|
||||
|
||||
switch(m_sampleFormat) {
|
||||
case FormatS8: {
|
||||
quint32 id = (FormatS8 << 24) | m_nextS8Id;
|
||||
MsgTCPSrcConnection* msg = MsgTCPSrcConnection::create(true, id, connection->peerAddress(), connection->peerPort());
|
||||
m_nextS8Id = (m_nextS8Id + 1) & 0xffffff;
|
||||
m_s8Sockets.push_back(Socket(id, connection));
|
||||
msg->submit(m_uiMessageQueue, (PluginGUI*)m_tcpSrcGUI);
|
||||
break;
|
||||
}
|
||||
|
||||
case FormatS16LE: {
|
||||
quint32 id = (FormatS16LE << 24) | m_nextS16leId;
|
||||
MsgTCPSrcConnection* msg = MsgTCPSrcConnection::create(true, id, connection->peerAddress(), connection->peerPort());
|
||||
m_nextS16leId = (m_nextS16leId + 1) & 0xffffff;
|
||||
m_s16leSockets.push_back(Socket(id, connection));
|
||||
msg->submit(m_uiMessageQueue, (PluginGUI*)m_tcpSrcGUI);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
delete connection;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TCPSrc::onDisconnected()
|
||||
{
|
||||
quint32 id;
|
||||
QTcpSocket* socket = NULL;
|
||||
|
||||
for(int i = 0; i < m_s8Sockets.count(); i++) {
|
||||
if(m_s8Sockets[i].socket == sender()) {
|
||||
id = m_s8Sockets[i].id;
|
||||
socket = m_s8Sockets[i].socket;
|
||||
m_s8Sockets.removeAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(socket == NULL) {
|
||||
for(int i = 0; i < m_s16leSockets.count(); i++) {
|
||||
if(m_s16leSockets[i].socket == sender()) {
|
||||
id = m_s16leSockets[i].id;
|
||||
socket = m_s16leSockets[i].socket;
|
||||
m_s16leSockets.removeAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(socket != NULL) {
|
||||
MsgTCPSrcConnection* msg = MsgTCPSrcConnection::create(false, id, QHostAddress(), 0);
|
||||
msg->submit(m_uiMessageQueue, (PluginGUI*)m_tcpSrcGUI);
|
||||
socket->deleteLater();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
#ifndef INCLUDE_TCPSRC_H
|
||||
#define INCLUDE_TCPSRC_H
|
||||
|
||||
#include <QHostAddress>
|
||||
#include "dsp/samplesink.h"
|
||||
#include "dsp/nco.h"
|
||||
#include "dsp/interpolator.h"
|
||||
#include "util/message.h"
|
||||
|
||||
class QTcpServer;
|
||||
class QTcpSocket;
|
||||
class TCPSrcGUI;
|
||||
|
||||
class TCPSrc : public SampleSink {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum SampleFormat {
|
||||
FormatS8,
|
||||
FormatS16LE
|
||||
};
|
||||
|
||||
TCPSrc(MessageQueue* uiMessageQueue, TCPSrcGUI* tcpSrcGUI, SampleSink* spectrum);
|
||||
~TCPSrc();
|
||||
|
||||
void configure(MessageQueue* messageQueue, SampleFormat sampleFormat, Real outputSampleRate, Real rfBandwidth, int tcpPort);
|
||||
void setSpectrum(MessageQueue* messageQueue, bool enabled);
|
||||
|
||||
void feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool firstOfBurst);
|
||||
void start();
|
||||
void stop();
|
||||
bool handleMessage(Message* cmd);
|
||||
|
||||
class MsgTCPSrcConnection : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
bool getConnect() const { return m_connect; }
|
||||
quint32 getID() const { return m_id; }
|
||||
const QHostAddress& getPeerAddress() const { return m_peerAddress; }
|
||||
int getPeerPort() const { return m_peerPort; }
|
||||
|
||||
static MsgTCPSrcConnection* create(bool connect, quint32 id, const QHostAddress& peerAddress, int peerPort)
|
||||
{
|
||||
return new MsgTCPSrcConnection(connect, id, peerAddress, peerPort);
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_connect;
|
||||
quint32 m_id;
|
||||
QHostAddress m_peerAddress;
|
||||
int m_peerPort;
|
||||
|
||||
MsgTCPSrcConnection(bool connect, quint32 id, const QHostAddress& peerAddress, int peerPort) :
|
||||
Message(),
|
||||
m_connect(connect),
|
||||
m_id(id),
|
||||
m_peerAddress(peerAddress),
|
||||
m_peerPort(peerPort)
|
||||
{ }
|
||||
};
|
||||
|
||||
protected:
|
||||
class MsgTCPSrcConfigure : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
SampleFormat getSampleFormat() const { return m_sampleFormat; }
|
||||
Real getOutputSampleRate() const { return m_outputSampleRate; }
|
||||
Real getRFBandwidth() const { return m_rfBandwidth; }
|
||||
int getTCPPort() const { return m_tcpPort; }
|
||||
|
||||
static MsgTCPSrcConfigure* create(SampleFormat sampleFormat, Real sampleRate, Real rfBandwidth, int tcpPort)
|
||||
{
|
||||
return new MsgTCPSrcConfigure(sampleFormat, sampleRate, rfBandwidth, tcpPort);
|
||||
}
|
||||
|
||||
private:
|
||||
SampleFormat m_sampleFormat;
|
||||
Real m_outputSampleRate;
|
||||
Real m_rfBandwidth;
|
||||
int m_tcpPort;
|
||||
|
||||
MsgTCPSrcConfigure(SampleFormat sampleFormat, Real outputSampleRate, Real rfBandwidth, int tcpPort) :
|
||||
Message(),
|
||||
m_sampleFormat(sampleFormat),
|
||||
m_outputSampleRate(outputSampleRate),
|
||||
m_rfBandwidth(rfBandwidth),
|
||||
m_tcpPort(tcpPort)
|
||||
{ }
|
||||
};
|
||||
class MsgTCPSrcSpectrum : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
bool getEnabled() const { return m_enabled; }
|
||||
|
||||
static MsgTCPSrcSpectrum* create(bool enabled)
|
||||
{
|
||||
return new MsgTCPSrcSpectrum(enabled);
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_enabled;
|
||||
|
||||
MsgTCPSrcSpectrum(bool enabled) :
|
||||
Message(),
|
||||
m_enabled(enabled)
|
||||
{ }
|
||||
};
|
||||
|
||||
MessageQueue* m_uiMessageQueue;
|
||||
TCPSrcGUI* m_tcpSrcGUI;
|
||||
|
||||
int m_inputSampleRate;
|
||||
|
||||
int m_sampleFormat;
|
||||
Real m_outputSampleRate;
|
||||
Real m_rfBandwidth;
|
||||
int m_tcpPort;
|
||||
|
||||
NCO m_nco;
|
||||
Interpolator m_interpolator;
|
||||
Real m_sampleDistanceRemain;
|
||||
|
||||
SampleVector m_sampleBuffer;
|
||||
std::vector<qint8> m_sampleBufferS8;
|
||||
SampleSink* m_spectrum;
|
||||
bool m_spectrumEnabled;
|
||||
|
||||
QTcpServer* m_tcpServer;
|
||||
struct Socket {
|
||||
quint32 id;
|
||||
QTcpSocket* socket;
|
||||
Socket(quint32 _id, QTcpSocket* _socket) :
|
||||
id(_id),
|
||||
socket(_socket)
|
||||
{ }
|
||||
};
|
||||
typedef QList<Socket> Sockets;
|
||||
Sockets m_s8Sockets;
|
||||
Sockets m_s16leSockets;
|
||||
quint32 m_nextS8Id;
|
||||
quint32 m_nextS16leId;
|
||||
|
||||
void closeAllSockets(Sockets* sockets);
|
||||
|
||||
protected slots:
|
||||
void onNewConnection();
|
||||
void onDisconnected();
|
||||
};
|
||||
|
||||
#endif // INCLUDE_TCPSRC_H
|
||||
@@ -0,0 +1,278 @@
|
||||
#include "tcpsrcgui.h"
|
||||
#include "plugin/pluginapi.h"
|
||||
#include "tcpsrc.h"
|
||||
#include "dsp/channelizer.h"
|
||||
#include "dsp/spectrumvis.h"
|
||||
#include "dsp/threadedsamplesink.h"
|
||||
#include "util/simpleserializer.h"
|
||||
#include "gui/basicchannelsettingswidget.h"
|
||||
#include "ui_tcpsrcgui.h"
|
||||
|
||||
TCPSrcGUI* TCPSrcGUI::create(PluginAPI* pluginAPI)
|
||||
{
|
||||
TCPSrcGUI* gui = new TCPSrcGUI(pluginAPI);
|
||||
return gui;
|
||||
}
|
||||
|
||||
void TCPSrcGUI::destroy()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void TCPSrcGUI::setName(const QString& name)
|
||||
{
|
||||
setObjectName(name);
|
||||
}
|
||||
|
||||
void TCPSrcGUI::resetToDefaults()
|
||||
{
|
||||
ui->sampleFormat->setCurrentIndex(0);
|
||||
ui->sampleRate->setText("25000");
|
||||
ui->rfBandwidth->setText("20000");
|
||||
ui->tcpPort->setText("9999");
|
||||
ui->spectrumGUI->resetToDefaults();
|
||||
applySettings();
|
||||
}
|
||||
|
||||
QByteArray TCPSrcGUI::serialize() const
|
||||
{
|
||||
SimpleSerializer s(1);
|
||||
s.writeBlob(1, saveState());
|
||||
s.writeS32(2, m_channelMarker->getCenterFrequency());
|
||||
s.writeS32(3, m_sampleFormat);
|
||||
s.writeReal(4, m_outputSampleRate);
|
||||
s.writeReal(5, m_rfBandwidth);
|
||||
s.writeS32(6, m_tcpPort);
|
||||
s.writeBlob(7, ui->spectrumGUI->serialize());
|
||||
s.writeU32(8, m_channelMarker->getColor().rgb());
|
||||
return s.final();
|
||||
}
|
||||
|
||||
bool TCPSrcGUI::deserialize(const QByteArray& data)
|
||||
{
|
||||
SimpleDeserializer d(data);
|
||||
|
||||
if(!d.isValid()) {
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(d.getVersion() == 1) {
|
||||
QByteArray bytetmp;
|
||||
qint32 s32tmp;
|
||||
quint32 u32tmp;
|
||||
Real realtmp;
|
||||
d.readBlob(1, &bytetmp);
|
||||
restoreState(bytetmp);
|
||||
d.readS32(2, &s32tmp, 0);
|
||||
m_channelMarker->setCenterFrequency(s32tmp);
|
||||
d.readS32(3, &s32tmp, TCPSrc::FormatS8);
|
||||
switch(s32tmp) {
|
||||
case TCPSrc::FormatS8:
|
||||
ui->sampleFormat->setCurrentIndex(0);
|
||||
break;
|
||||
case TCPSrc::FormatS16LE:
|
||||
ui->sampleFormat->setCurrentIndex(1);
|
||||
break;
|
||||
default:
|
||||
ui->sampleFormat->setCurrentIndex(0);
|
||||
break;
|
||||
}
|
||||
d.readReal(4, &realtmp, 25000);
|
||||
ui->sampleRate->setText(QString("%1").arg(realtmp, 0));
|
||||
d.readReal(5, &realtmp, 20000);
|
||||
ui->rfBandwidth->setText(QString("%1").arg(realtmp, 0));
|
||||
d.readS32(6, &s32tmp, 9999);
|
||||
ui->tcpPort->setText(QString("%1").arg(s32tmp));
|
||||
d.readBlob(7, &bytetmp);
|
||||
ui->spectrumGUI->deserialize(bytetmp);
|
||||
if(d.readU32(8, &u32tmp))
|
||||
m_channelMarker->setColor(u32tmp);
|
||||
applySettings();
|
||||
return true;
|
||||
} else {
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool TCPSrcGUI::handleMessage(Message* message)
|
||||
{
|
||||
if(TCPSrc::MsgTCPSrcConnection::match(message)) {
|
||||
TCPSrc::MsgTCPSrcConnection* con = (TCPSrc::MsgTCPSrcConnection*)message;
|
||||
if(con->getConnect())
|
||||
addConnection(con->getID(), con->getPeerAddress(), con->getPeerPort());
|
||||
else delConnection(con->getID());
|
||||
message->completed();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void TCPSrcGUI::channelMarkerChanged()
|
||||
{
|
||||
applySettings();
|
||||
}
|
||||
|
||||
TCPSrcGUI::TCPSrcGUI(PluginAPI* pluginAPI, QWidget* parent) :
|
||||
RollupWidget(parent),
|
||||
ui(new Ui::TCPSrcGUI),
|
||||
m_pluginAPI(pluginAPI),
|
||||
m_tcpSrc(NULL),
|
||||
m_basicSettingsShown(false)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->connectedClientsBox->hide();
|
||||
connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
|
||||
connect(this, SIGNAL(menuDoubleClickEvent()), this, SLOT(onMenuDoubleClicked()));
|
||||
setAttribute(Qt::WA_DeleteOnClose, true);
|
||||
|
||||
m_spectrumVis = new SpectrumVis(ui->glSpectrum);
|
||||
m_tcpSrc = new TCPSrc(m_pluginAPI->getMainWindowMessageQueue(), this, m_spectrumVis);
|
||||
m_channelizer = new Channelizer(m_tcpSrc);
|
||||
m_threadedSampleSink = new ThreadedSampleSink(m_channelizer);
|
||||
m_pluginAPI->addSampleSink(m_threadedSampleSink);
|
||||
|
||||
ui->glSpectrum->setCenterFrequency(0);
|
||||
ui->glSpectrum->setSampleRate(ui->sampleRate->text().toInt());
|
||||
ui->glSpectrum->setDisplayWaterfall(true);
|
||||
ui->glSpectrum->setDisplayMaxHold(true);
|
||||
m_spectrumVis->configure(m_threadedSampleSink->getMessageQueue(), 64, 10, FFTWindow::BlackmanHarris);
|
||||
|
||||
m_channelMarker = new ChannelMarker(this);
|
||||
m_channelMarker->setBandwidth(25000);
|
||||
m_channelMarker->setCenterFrequency(0);
|
||||
m_channelMarker->setVisible(true);
|
||||
connect(m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged()));
|
||||
m_pluginAPI->addChannelMarker(m_channelMarker);
|
||||
|
||||
ui->spectrumGUI->setBuddies(m_threadedSampleSink->getMessageQueue(), m_spectrumVis, ui->glSpectrum);
|
||||
|
||||
applySettings();
|
||||
}
|
||||
|
||||
TCPSrcGUI::~TCPSrcGUI()
|
||||
{
|
||||
m_pluginAPI->removeChannelInstance(this);
|
||||
m_pluginAPI->removeSampleSink(m_threadedSampleSink);
|
||||
delete m_threadedSampleSink;
|
||||
delete m_channelizer;
|
||||
delete m_tcpSrc;
|
||||
delete m_spectrumVis;
|
||||
delete m_channelMarker;
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void TCPSrcGUI::applySettings()
|
||||
{
|
||||
bool ok;
|
||||
|
||||
Real outputSampleRate = ui->sampleRate->text().toDouble(&ok);
|
||||
if((!ok) || (outputSampleRate < 100))
|
||||
outputSampleRate = 25000;
|
||||
Real rfBandwidth = ui->rfBandwidth->text().toDouble(&ok);
|
||||
if((!ok) || (rfBandwidth > outputSampleRate))
|
||||
rfBandwidth = outputSampleRate;
|
||||
int tcpPort = ui->tcpPort->text().toInt(&ok);
|
||||
if((!ok) || (tcpPort < 1) || (tcpPort > 65535))
|
||||
tcpPort = 9999;
|
||||
|
||||
setTitleColor(m_channelMarker->getColor());
|
||||
ui->sampleRate->setText(QString("%1").arg(outputSampleRate, 0));
|
||||
ui->rfBandwidth->setText(QString("%1").arg(rfBandwidth, 0));
|
||||
ui->tcpPort->setText(QString("%1").arg(tcpPort));
|
||||
m_channelMarker->disconnect(this, SLOT(channelMarkerChanged()));
|
||||
m_channelMarker->setBandwidth((int)rfBandwidth);
|
||||
connect(m_channelMarker, SIGNAL(changed()), this, SLOT(channelMarkerChanged()));
|
||||
ui->glSpectrum->setSampleRate(outputSampleRate);
|
||||
|
||||
m_channelizer->configure(m_threadedSampleSink->getMessageQueue(),
|
||||
outputSampleRate,
|
||||
m_channelMarker->getCenterFrequency());
|
||||
|
||||
TCPSrc::SampleFormat sampleFormat;
|
||||
switch(ui->sampleFormat->currentIndex()) {
|
||||
case 0:
|
||||
sampleFormat = TCPSrc::FormatS8;
|
||||
break;
|
||||
case 1:
|
||||
sampleFormat = TCPSrc::FormatS16LE;
|
||||
break;
|
||||
default:
|
||||
sampleFormat = TCPSrc::FormatS8;
|
||||
break;
|
||||
}
|
||||
|
||||
m_sampleFormat = sampleFormat;
|
||||
m_outputSampleRate = outputSampleRate;
|
||||
m_rfBandwidth = rfBandwidth;
|
||||
m_tcpPort = tcpPort;
|
||||
|
||||
m_tcpSrc->configure(m_threadedSampleSink->getMessageQueue(),
|
||||
sampleFormat,
|
||||
outputSampleRate,
|
||||
rfBandwidth,
|
||||
tcpPort);
|
||||
|
||||
ui->applyBtn->setEnabled(false);
|
||||
}
|
||||
|
||||
void TCPSrcGUI::on_sampleFormat_currentIndexChanged(int index)
|
||||
{
|
||||
ui->applyBtn->setEnabled(true);
|
||||
}
|
||||
|
||||
void TCPSrcGUI::on_sampleRate_textEdited(const QString& arg1)
|
||||
{
|
||||
ui->applyBtn->setEnabled(true);
|
||||
}
|
||||
|
||||
void TCPSrcGUI::on_rfBandwidth_textEdited(const QString& arg1)
|
||||
{
|
||||
ui->applyBtn->setEnabled(true);
|
||||
}
|
||||
|
||||
void TCPSrcGUI::on_tcpPort_textEdited(const QString& arg1)
|
||||
{
|
||||
ui->applyBtn->setEnabled(true);
|
||||
}
|
||||
|
||||
void TCPSrcGUI::on_applyBtn_clicked()
|
||||
{
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void TCPSrcGUI::onWidgetRolled(QWidget* widget, bool rollDown)
|
||||
{
|
||||
if((widget == ui->spectrumBox) && (m_tcpSrc != NULL))
|
||||
m_tcpSrc->setSpectrum(m_threadedSampleSink->getMessageQueue(), rollDown);
|
||||
}
|
||||
|
||||
void TCPSrcGUI::onMenuDoubleClicked()
|
||||
{
|
||||
if(!m_basicSettingsShown) {
|
||||
m_basicSettingsShown = true;
|
||||
BasicChannelSettingsWidget* bcsw = new BasicChannelSettingsWidget(m_channelMarker, this);
|
||||
bcsw->show();
|
||||
}
|
||||
}
|
||||
|
||||
void TCPSrcGUI::addConnection(quint32 id, const QHostAddress& peerAddress, int peerPort)
|
||||
{
|
||||
QStringList l;
|
||||
l.append(QString("%1:%2").arg(peerAddress.toString()).arg(peerPort));
|
||||
new QTreeWidgetItem(ui->connections, l, id);
|
||||
ui->connectedClientsBox->setWindowTitle(tr("Connected Clients (%1)").arg(ui->connections->topLevelItemCount()));
|
||||
}
|
||||
|
||||
void TCPSrcGUI::delConnection(quint32 id)
|
||||
{
|
||||
for(int i = 0; i < ui->connections->topLevelItemCount(); i++) {
|
||||
if(ui->connections->topLevelItem(i)->type() == id) {
|
||||
delete ui->connections->topLevelItem(i);
|
||||
ui->connectedClientsBox->setWindowTitle(tr("Connected Clients (%1)").arg(ui->connections->topLevelItemCount()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
#ifndef INCLUDE_TCPSRCGUI_H
|
||||
#define INCLUDE_TCPSRCGUI_H
|
||||
|
||||
#include <QHostAddress>
|
||||
#include "gui/rollupwidget.h"
|
||||
#include "plugin/plugingui.h"
|
||||
#include "tcpsrc.h"
|
||||
|
||||
class PluginAPI;
|
||||
class ChannelMarker;
|
||||
class ThreadedSampleSink;
|
||||
class Channelizer;
|
||||
class TCPSrc;
|
||||
class SpectrumVis;
|
||||
|
||||
namespace Ui {
|
||||
class TCPSrcGUI;
|
||||
}
|
||||
|
||||
class TCPSrcGUI : public RollupWidget, public PluginGUI {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static TCPSrcGUI* create(PluginAPI* pluginAPI);
|
||||
void destroy();
|
||||
|
||||
void setName(const QString& name);
|
||||
|
||||
void resetToDefaults();
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
|
||||
bool handleMessage(Message* message);
|
||||
|
||||
private slots:
|
||||
void channelMarkerChanged();
|
||||
void on_sampleFormat_currentIndexChanged(int index);
|
||||
void on_sampleRate_textEdited(const QString& arg1);
|
||||
void on_rfBandwidth_textEdited(const QString& arg1);
|
||||
void on_tcpPort_textEdited(const QString& arg1);
|
||||
void on_applyBtn_clicked();
|
||||
void onWidgetRolled(QWidget* widget, bool rollDown);
|
||||
void onMenuDoubleClicked();
|
||||
|
||||
private:
|
||||
Ui::TCPSrcGUI* ui;
|
||||
PluginAPI* m_pluginAPI;
|
||||
ChannelMarker* m_channelMarker;
|
||||
|
||||
// settings
|
||||
TCPSrc::SampleFormat m_sampleFormat;
|
||||
Real m_outputSampleRate;
|
||||
Real m_rfBandwidth;
|
||||
int m_tcpPort;
|
||||
bool m_basicSettingsShown;
|
||||
|
||||
// RF path
|
||||
ThreadedSampleSink* m_threadedSampleSink;
|
||||
Channelizer* m_channelizer;
|
||||
TCPSrc* m_tcpSrc;
|
||||
SpectrumVis* m_spectrumVis;
|
||||
|
||||
explicit TCPSrcGUI(PluginAPI* pluginAPI, QWidget* parent = NULL);
|
||||
~TCPSrcGUI();
|
||||
|
||||
void applySettings();
|
||||
|
||||
void addConnection(quint32 id, const QHostAddress& peerAddress, int peerPort);
|
||||
void delConnection(quint32 id);
|
||||
};
|
||||
|
||||
#endif // INCLUDE_TCPSRCGUI_H
|
||||
@@ -0,0 +1,213 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TCPSrcGUI</class>
|
||||
<widget class="RollupWidget" name="TCPSrcGUI">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>443</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>TCP Sample Source</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>5</y>
|
||||
<width>201</width>
|
||||
<height>142</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Settings</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="margin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Sample Format</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QComboBox" name="sampleFormat">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>S8 I/Q</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>S16LE I/Q</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLineEdit" name="rfBandwidth">
|
||||
<property name="text">
|
||||
<string>20000</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>RF Bandwidth (Hz)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Samplerate (Hz)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="sampleRate">
|
||||
<property name="text">
|
||||
<string>25000</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>TCP Port</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLineEdit" name="tcpPort">
|
||||
<property name="text">
|
||||
<string>9999</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="applyBtn">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Apply</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="spectrumBox" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>15</x>
|
||||
<y>160</y>
|
||||
<width>231</width>
|
||||
<height>156</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Channel Spectrum</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="GLSpectrum" name="glSpectrum" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="GLSpectrumGUI" name="spectrumGUI" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="connectedClientsBox" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>15</x>
|
||||
<y>330</y>
|
||||
<width>274</width>
|
||||
<height>101</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Connected Clients (0)</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="connections">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="itemsExpandable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="headerVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>IP:Port</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>GLSpectrum</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/glspectrum.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>GLSpectrumGUI</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/glspectrumgui.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>RollupWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/rollupwidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>sampleFormat</tabstop>
|
||||
<tabstop>tcpPort</tabstop>
|
||||
<tabstop>sampleRate</tabstop>
|
||||
<tabstop>rfBandwidth</tabstop>
|
||||
<tabstop>applyBtn</tabstop>
|
||||
<tabstop>connections</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,53 @@
|
||||
#include <QtPlugin>
|
||||
#include <QAction>
|
||||
#include "plugin/pluginapi.h"
|
||||
#include "tcpsrcplugin.h"
|
||||
#include "tcpsrcgui.h"
|
||||
|
||||
const PluginDescriptor TCPSrcPlugin::m_pluginDescriptor = {
|
||||
QString("TCP Channel Source"),
|
||||
QString("---"),
|
||||
QString("(c) maintech GmbH (written by Christian Daniel)"),
|
||||
QString("http://www.maintech.de"),
|
||||
true,
|
||||
QString("http://www.maintech.de")
|
||||
};
|
||||
|
||||
TCPSrcPlugin::TCPSrcPlugin(QObject* parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
const PluginDescriptor& TCPSrcPlugin::getPluginDescriptor() const
|
||||
{
|
||||
return m_pluginDescriptor;
|
||||
}
|
||||
|
||||
void TCPSrcPlugin::initPlugin(PluginAPI* pluginAPI)
|
||||
{
|
||||
m_pluginAPI = pluginAPI;
|
||||
|
||||
// register TCP Channel Source
|
||||
QAction* action = new QAction(tr("&TCP Source"), this);
|
||||
connect(action, SIGNAL(triggered()), this, SLOT(createInstanceTCPSrc()));
|
||||
m_pluginAPI->registerChannel("de.maintech.sdrangelove.channel.tcpsrc", this, action);
|
||||
}
|
||||
|
||||
PluginGUI* TCPSrcPlugin::createChannel(const QString& channelName)
|
||||
{
|
||||
if(channelName == "de.maintech.sdrangelove.channel.tcpsrc") {
|
||||
TCPSrcGUI* gui = TCPSrcGUI::create(m_pluginAPI);
|
||||
m_pluginAPI->registerChannelInstance("de.maintech.sdrangelove.channel.tcpsrc", gui);
|
||||
m_pluginAPI->addChannelRollup(gui);
|
||||
return gui;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void TCPSrcPlugin::createInstanceTCPSrc()
|
||||
{
|
||||
TCPSrcGUI* gui = TCPSrcGUI::create(m_pluginAPI);
|
||||
m_pluginAPI->registerChannelInstance("de.maintech.sdrangelove.channel.tcpsrc", gui);
|
||||
m_pluginAPI->addChannelRollup(gui);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef INCLUDE_TCPSRCPLUGIN_H
|
||||
#define INCLUDE_TCPSRCPLUGIN_H
|
||||
|
||||
#include <QObject>
|
||||
#include "plugin/plugininterface.h"
|
||||
|
||||
class TCPSrcPlugin : public QObject, PluginInterface {
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(PluginInterface)
|
||||
Q_PLUGIN_METADATA(IID "de.maintech.sdrangelove.demod.tcpsrc")
|
||||
|
||||
public:
|
||||
explicit TCPSrcPlugin(QObject* parent = NULL);
|
||||
|
||||
const PluginDescriptor& getPluginDescriptor() const;
|
||||
void initPlugin(PluginAPI* pluginAPI);
|
||||
|
||||
PluginGUI* createChannel(const QString& channelName);
|
||||
|
||||
private:
|
||||
static const PluginDescriptor m_pluginDescriptor;
|
||||
|
||||
PluginAPI* m_pluginAPI;
|
||||
|
||||
private slots:
|
||||
void createInstanceTCPSrc();
|
||||
};
|
||||
|
||||
#endif // INCLUDE_TCPSRCPLUGIN_H
|
||||
@@ -0,0 +1,47 @@
|
||||
project(tetra)
|
||||
|
||||
set(tetra_SOURCES
|
||||
tetrademod.cpp
|
||||
tetrademodgui.cpp
|
||||
tetraplugin.cpp
|
||||
)
|
||||
|
||||
set(tetra_HEADERS
|
||||
tetrademod.h
|
||||
tetrademodgui.h
|
||||
tetraplugin.h
|
||||
)
|
||||
|
||||
set(tetra_FORMS
|
||||
tetrademodgui.ui
|
||||
)
|
||||
|
||||
include_directories(
|
||||
.
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_SOURCE_DIR}/include
|
||||
${CMAKE_SOURCE_DIR}/include-gpl
|
||||
${OPENGL_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
#include(${QT_USE_FILE})
|
||||
add_definitions(${QT_DEFINITIONS})
|
||||
add_definitions(-DQT_PLUGIN)
|
||||
add_definitions(-DQT_SHARED)
|
||||
|
||||
#qt5_wrap_cpp(tetra_HEADERS_MOC ${tetra_HEADERS})
|
||||
qt5_wrap_ui(tetra_FORMS_HEADERS ${tetra_FORMS})
|
||||
|
||||
add_library(demodtetra SHARED
|
||||
${tetra_SOURCES}
|
||||
${tetra_HEADERS_MOC}
|
||||
${tetra_FORMS_HEADERS}
|
||||
)
|
||||
|
||||
target_link_libraries(demodtetra
|
||||
${QT_LIBRARIES}
|
||||
${OPENGL_LIBRARIES}
|
||||
sdrbase
|
||||
)
|
||||
|
||||
qt5_use_modules(demodtetra Core Widgets OpenGL Multimedia)
|
||||
@@ -0,0 +1,106 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// //
|
||||
// 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 //
|
||||
// //
|
||||
// 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 <stdio.h>
|
||||
#include "tetrademod.h"
|
||||
#include "dsp/dspcommands.h"
|
||||
|
||||
MessageRegistrator TetraDemod::MsgConfigureTetraDemod::ID("MsgConfigureTetraDemod");
|
||||
|
||||
static FILE* f = NULL;
|
||||
|
||||
TetraDemod::TetraDemod(SampleSink* sampleSink) :
|
||||
m_sampleSink(sampleSink)
|
||||
{
|
||||
m_sampleRate = 500000;
|
||||
m_frequency = 0;
|
||||
|
||||
m_nco.setFreq(m_frequency, m_sampleRate);
|
||||
m_interpolator.create(32, 32 * m_sampleRate, 36000);
|
||||
m_sampleDistanceRemain = (Real)m_sampleRate / 36000.0;
|
||||
}
|
||||
|
||||
TetraDemod::~TetraDemod()
|
||||
{
|
||||
}
|
||||
|
||||
void TetraDemod::configure(MessageQueue* messageQueue)
|
||||
{
|
||||
Message* cmd = MsgConfigureTetraDemod::create();
|
||||
cmd->submit(messageQueue, this);
|
||||
}
|
||||
|
||||
void TetraDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool firstOfBurst)
|
||||
{
|
||||
size_t count = end - begin;
|
||||
|
||||
Complex ci;
|
||||
bool consumed;
|
||||
|
||||
for(SampleVector::const_iterator it = begin; it < end; ++it) {
|
||||
Complex c(it->real() / 32768.0, it->imag() / 32768.0);
|
||||
c *= m_nco.nextIQ();
|
||||
|
||||
consumed = false;
|
||||
if(m_interpolator.interpolate(&m_sampleDistanceRemain, c, &consumed, &ci)) {
|
||||
m_sampleBuffer.push_back(Sample(ci.real() * 32768.0, ci.imag() * 32768.0));
|
||||
|
||||
m_sampleDistanceRemain += (Real)m_sampleRate / 36000.0;
|
||||
}
|
||||
}
|
||||
|
||||
if(f != NULL) {
|
||||
fwrite(&m_sampleBuffer[0], m_sampleBuffer.size(), sizeof(m_sampleBuffer[0]), f);
|
||||
}
|
||||
|
||||
if(m_sampleSink != NULL)
|
||||
m_sampleSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), firstOfBurst);
|
||||
m_sampleBuffer.clear();
|
||||
}
|
||||
|
||||
void TetraDemod::start()
|
||||
{
|
||||
}
|
||||
|
||||
void TetraDemod::stop()
|
||||
{
|
||||
}
|
||||
|
||||
bool TetraDemod::handleMessage(Message* cmd)
|
||||
{
|
||||
if(cmd->id() == DSPSignalNotification::ID()) {
|
||||
DSPSignalNotification* signal = (DSPSignalNotification*)cmd;
|
||||
qDebug("%d samples/sec, %lld Hz offset", signal->getSampleRate(), signal->getFrequencyOffset());
|
||||
m_sampleRate = signal->getSampleRate();
|
||||
m_nco.setFreq(-signal->getFrequencyOffset(), m_sampleRate);
|
||||
m_interpolator.create(32, m_sampleRate, 25000 / 2);
|
||||
m_sampleDistanceRemain = m_sampleRate / 36000.0;
|
||||
cmd->completed();
|
||||
return true;
|
||||
} else if(cmd->id() == MsgConfigureTetraDemod::ID()) {
|
||||
if(f == NULL) {
|
||||
f = fopen("/tmp/tetra.iq", "wb");
|
||||
qDebug("started writing samples");
|
||||
} else {
|
||||
fclose(f);
|
||||
f = NULL;
|
||||
qDebug("stopped writing samples");
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// //
|
||||
// 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 //
|
||||
// //
|
||||
// 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 INCLUDE_TETRADEMOD_H
|
||||
#define INCLUDE_TETRADEMOD_H
|
||||
|
||||
#include "dsp/samplesink.h"
|
||||
#include "dsp/nco.h"
|
||||
#include "dsp/interpolator.h"
|
||||
#include "util/message.h"
|
||||
|
||||
class MessageQueue;
|
||||
|
||||
class TetraDemod : public SampleSink {
|
||||
public:
|
||||
TetraDemod(SampleSink* sampleSink);
|
||||
~TetraDemod();
|
||||
|
||||
void configure(MessageQueue* messageQueue);
|
||||
|
||||
void feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool firstOfBurst);
|
||||
void start();
|
||||
void stop();
|
||||
bool handleMessage(Message* cmd);
|
||||
|
||||
private:
|
||||
class MsgConfigureTetraDemod : public Message {
|
||||
public:
|
||||
static MessageRegistrator ID;
|
||||
|
||||
static MsgConfigureTetraDemod* create()
|
||||
{
|
||||
return new MsgConfigureTetraDemod();
|
||||
}
|
||||
|
||||
private:
|
||||
MsgConfigureTetraDemod() :
|
||||
Message(ID())
|
||||
{ }
|
||||
};
|
||||
|
||||
int m_sampleRate;
|
||||
int m_frequency;
|
||||
|
||||
NCO m_nco;
|
||||
Interpolator m_interpolator;
|
||||
Real m_sampleDistanceRemain;
|
||||
|
||||
SampleSink* m_sampleSink;
|
||||
SampleVector m_sampleBuffer;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_TETRADEMOD_H
|
||||
@@ -0,0 +1,110 @@
|
||||
#include <QDockWidget>
|
||||
#include <QMainWindow>
|
||||
#include "tetrademodgui.h"
|
||||
#include "ui_tetrademodgui.h"
|
||||
#include "dsp/threadedsamplesink.h"
|
||||
#include "dsp/channelizer.h"
|
||||
#include "tetrademod.h"
|
||||
#include "dsp/spectrumvis.h"
|
||||
#include "gui/glspectrum.h"
|
||||
#include "plugin/pluginapi.h"
|
||||
|
||||
TetraDemodGUI* TetraDemodGUI::create(PluginAPI* pluginAPI)
|
||||
{
|
||||
QDockWidget* dock = pluginAPI->createMainWindowDock(Qt::RightDockWidgetArea, tr("Tetra Demodulator"));
|
||||
dock->setObjectName(QString::fromUtf8("Tetra Demodulator"));
|
||||
TetraDemodGUI* gui = new TetraDemodGUI(pluginAPI, dock);
|
||||
dock->setWidget(gui);
|
||||
return gui;
|
||||
}
|
||||
|
||||
void TetraDemodGUI::destroy()
|
||||
{
|
||||
delete m_dockWidget;
|
||||
}
|
||||
|
||||
void TetraDemodGUI::setWidgetName(const QString& name)
|
||||
{
|
||||
m_dockWidget->setObjectName(name);
|
||||
}
|
||||
|
||||
void TetraDemodGUI::resetToDefaults()
|
||||
{
|
||||
}
|
||||
|
||||
QByteArray TetraDemodGUI::serializeGeneral() const
|
||||
{
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
bool TetraDemodGUI::deserializeGeneral(const QByteArray& data)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray TetraDemodGUI::serialize() const
|
||||
{
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
bool TetraDemodGUI::deserialize(const QByteArray& data)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TetraDemodGUI::handleMessage(Message* message)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void TetraDemodGUI::viewChanged()
|
||||
{
|
||||
m_channelizer->configure(m_threadedSampleSink->getMessageQueue(), 36000, m_channelMarker->getCenterFrequency());
|
||||
}
|
||||
|
||||
TetraDemodGUI::TetraDemodGUI(PluginAPI* pluginAPI, QDockWidget* dockWidget, QWidget* parent) :
|
||||
PluginGUI(parent),
|
||||
ui(new Ui::TetraDemodGUI),
|
||||
m_pluginAPI(pluginAPI),
|
||||
m_dockWidget(dockWidget)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
m_spectrumVis = new SpectrumVis(ui->glSpectrum);
|
||||
m_tetraDemod = new TetraDemod(m_spectrumVis);
|
||||
m_channelizer = new Channelizer(m_tetraDemod);
|
||||
m_threadedSampleSink = new ThreadedSampleSink(m_channelizer);
|
||||
m_pluginAPI->addSampleSink(m_threadedSampleSink);
|
||||
|
||||
ui->glSpectrum->setCenterFrequency(0);
|
||||
ui->glSpectrum->setSampleRate(36000);
|
||||
ui->glSpectrum->setDisplayWaterfall(true);
|
||||
ui->glSpectrum->setDisplayMaxHold(true);
|
||||
m_spectrumVis->configure(m_threadedSampleSink->getMessageQueue(), 64, 10, FFTWindow::BlackmanHarris);
|
||||
|
||||
m_channelMarker = new ChannelMarker(this);
|
||||
m_channelMarker->setColor(Qt::darkGreen);
|
||||
m_channelMarker->setBandwidth(25000);
|
||||
m_channelMarker->setCenterFrequency(0);
|
||||
m_channelMarker->setVisible(true);
|
||||
connect(m_channelMarker, SIGNAL(changed()), this, SLOT(viewChanged()));
|
||||
m_pluginAPI->addChannelMarker(m_channelMarker);
|
||||
|
||||
viewChanged();
|
||||
}
|
||||
|
||||
TetraDemodGUI::~TetraDemodGUI()
|
||||
{
|
||||
m_pluginAPI->removeSampleSink(m_threadedSampleSink);
|
||||
delete m_threadedSampleSink;
|
||||
delete m_channelizer;
|
||||
delete m_tetraDemod;
|
||||
delete m_spectrumVis;
|
||||
delete m_channelMarker;
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void TetraDemodGUI::on_test_clicked()
|
||||
{
|
||||
m_tetraDemod->configure(m_threadedSampleSink->getMessageQueue());
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
#ifndef INCLUDE_TETRADEMODGUI_H
|
||||
#define INCLUDE_TETRADEMODGUI_H
|
||||
|
||||
#include "plugin/plugingui.h"
|
||||
|
||||
class QDockWidget;
|
||||
|
||||
class PluginAPI;
|
||||
class ChannelMarker;
|
||||
|
||||
class ThreadedSampleSink;
|
||||
class Channelizer;
|
||||
class TetraDemod;
|
||||
class SpectrumVis;
|
||||
|
||||
namespace Ui {
|
||||
class TetraDemodGUI;
|
||||
}
|
||||
|
||||
class TetraDemodGUI : public PluginGUI {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static TetraDemodGUI* create(PluginAPI* pluginAPI);
|
||||
void destroy();
|
||||
|
||||
void setWidgetName(const QString& name);
|
||||
|
||||
void resetToDefaults();
|
||||
QByteArray serializeGeneral() const;
|
||||
bool deserializeGeneral(const QByteArray& data);
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
|
||||
bool handleMessage(Message* message);
|
||||
|
||||
private slots:
|
||||
void on_test_clicked();
|
||||
void viewChanged();
|
||||
|
||||
private:
|
||||
explicit TetraDemodGUI(PluginAPI* pluginAPI, QDockWidget* dockWidget, QWidget* parent = NULL);
|
||||
~TetraDemodGUI();
|
||||
|
||||
Ui::TetraDemodGUI* ui;
|
||||
PluginAPI* m_pluginAPI;
|
||||
QDockWidget* m_dockWidget;
|
||||
ChannelMarker* m_channelMarker;
|
||||
|
||||
ThreadedSampleSink* m_threadedSampleSink;
|
||||
Channelizer* m_channelizer;
|
||||
TetraDemod* m_tetraDemod;
|
||||
SpectrumVis* m_spectrumVis;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_TETRADEMODGUI_H
|
||||
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TetraDemodGUI</class>
|
||||
<widget class="QWidget" name="TetraDemodGUI">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>200</width>
|
||||
<height>179</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="GLSpectrum" name="glSpectrum" native="true">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>150</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="test">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>GLSpectrum</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/glspectrum.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,50 @@
|
||||
#include <QtPlugin>
|
||||
#include <QAction>
|
||||
#include "plugin/pluginapi.h"
|
||||
#include "tetraplugin.h"
|
||||
#include "tetrademodgui.h"
|
||||
|
||||
const PluginDescriptor TetraPlugin::m_pluginDescriptor = {
|
||||
QString("Tetra Demodulator"),
|
||||
QString("---"),
|
||||
QString("(c) maintech GmbH (written by Christian Daniel)"),
|
||||
QString("http://www.maintech.de"),
|
||||
true,
|
||||
QString("http://www.maintech.de")
|
||||
};
|
||||
|
||||
TetraPlugin::TetraPlugin(QObject* parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
const PluginDescriptor& TetraPlugin::getPluginDescriptor() const
|
||||
{
|
||||
return m_pluginDescriptor;
|
||||
}
|
||||
|
||||
void TetraPlugin::initPlugin(PluginAPI* pluginAPI)
|
||||
{
|
||||
m_pluginAPI = pluginAPI;
|
||||
|
||||
// register Tetra demodulator
|
||||
QAction* action = new QAction(tr("&Tetra"), this);
|
||||
connect(action, SIGNAL(triggered()), this, SLOT(createInstanceTetra()));
|
||||
m_pluginAPI->registerDemodulator("de.maintech.sdrangelove.demod.tetra", this, action);
|
||||
}
|
||||
|
||||
PluginGUI* TetraPlugin::createDemod(const QString& demodName)
|
||||
{
|
||||
if(demodName == "de.maintech.sdrangelove.demod.tetra") {
|
||||
PluginGUI* gui = TetraDemodGUI::create(m_pluginAPI);
|
||||
m_pluginAPI->registerDemodulatorInstance("de.maintech.sdrangelove.demod.tetra", gui);
|
||||
return gui;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void TetraPlugin::createInstanceTetra()
|
||||
{
|
||||
m_pluginAPI->registerDemodulatorInstance("de.maintech.sdrangelove.demod.tetra", TetraDemodGUI::create(m_pluginAPI));
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef INCLUDE_TETRAPLUGIN_H
|
||||
#define INCLUDE_TETRAPLUGIN_H
|
||||
|
||||
#include <QObject>
|
||||
#include "plugin/plugininterface.h"
|
||||
|
||||
class TetraPlugin : public QObject, PluginInterface {
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(PluginInterface)
|
||||
Q_PLUGIN_METADATA(IID "de.maintech.sdrangelove.demod.tetra")
|
||||
|
||||
public:
|
||||
explicit TetraPlugin(QObject* parent = NULL);
|
||||
|
||||
const PluginDescriptor& getPluginDescriptor() const;
|
||||
void initPlugin(PluginAPI* pluginAPI);
|
||||
|
||||
PluginGUI* createDemod(const QString& demodName);
|
||||
|
||||
private:
|
||||
static const PluginDescriptor m_pluginDescriptor;
|
||||
|
||||
PluginAPI* m_pluginAPI;
|
||||
|
||||
private slots:
|
||||
void createInstanceTetra();
|
||||
};
|
||||
|
||||
#endif // INCLUDE_TETRAPLUGIN_H
|
||||
@@ -0,0 +1,15 @@
|
||||
project(samplesource)
|
||||
|
||||
find_package(LibUSB)
|
||||
find_package(LibOsmoSDR)
|
||||
find_package(LibRTLSDR)
|
||||
|
||||
add_subdirectory(gnuradio)
|
||||
|
||||
if(LIBUSB_FOUND AND LIBOSMOSDR_FOUND)
|
||||
add_subdirectory(osmosdr)
|
||||
endif(LIBUSB_FOUND AND LIBOSMOSDR_FOUND)
|
||||
|
||||
if(LIBUSB_FOUND AND LIBRTLSDR_FOUND)
|
||||
add_subdirectory(rtlsdr)
|
||||
endif(LIBUSB_FOUND AND LIBRTLSDR_FOUND)
|
||||
@@ -0,0 +1,87 @@
|
||||
project(gnuradio)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules)
|
||||
|
||||
find_package(GnuradioRuntime)
|
||||
find_package(GnuradioOsmosdr)
|
||||
|
||||
if(UNIX AND NOT BOOST_ROOT AND EXISTS "/usr/lib64")
|
||||
list(APPEND BOOST_LIBRARYDIR "/usr/lib64") #fedora 64-bit fix
|
||||
endif(UNIX AND NOT BOOST_ROOT AND EXISTS "/usr/lib64")
|
||||
|
||||
find_package(Boost COMPONENTS system)
|
||||
|
||||
if(NOT GNURADIO_RUNTIME_FOUND AND GNURADIO_OSMOSDR_FOUND)
|
||||
|
||||
message(STATUS "GNU Radio not found, assuming built-in gr-osmosdr runtime.")
|
||||
|
||||
set(GNURADIO_RUNTIME_FOUND TRUE)
|
||||
set(GNURADIO_RUNTIME_INCLUDE_DIRS "")
|
||||
set(GNURADIO_RUNTIME_LIBRARIES "")
|
||||
|
||||
FOREACH(inc ${GNURADIO_OSMOSDR_INCLUDE_DIRS})
|
||||
LIST(APPEND GNURADIO_RUNTIME_INCLUDE_DIRS "${inc}/osmosdr/runtime")
|
||||
ENDFOREACH(inc)
|
||||
|
||||
LIST(APPEND GNURADIO_RUNTIME_LIBRARIES ${GNURADIO_OSMOSDR_LIBRARIES})
|
||||
|
||||
endif(NOT GNURADIO_RUNTIME_FOUND AND GNURADIO_OSMOSDR_FOUND)
|
||||
|
||||
if(Boost_FOUND AND GNURADIO_RUNTIME_FOUND AND GNURADIO_OSMOSDR_FOUND)
|
||||
|
||||
set(gnuradio_SOURCES
|
||||
gnuradiogui.cpp
|
||||
gnuradioinput.cpp
|
||||
gnuradioplugin.cpp
|
||||
gnuradiothread.cpp
|
||||
)
|
||||
|
||||
set(gnuradio_HEADERS
|
||||
gnuradiogui.h
|
||||
gnuradioinput.h
|
||||
gnuradioplugin.h
|
||||
gnuradiothread.h
|
||||
)
|
||||
|
||||
set(gnuradio_FORMS
|
||||
gnuradiogui.ui
|
||||
)
|
||||
|
||||
include_directories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_SOURCE_DIR}/include
|
||||
${CMAKE_SOURCE_DIR}/include-gpl
|
||||
${Boost_INCLUDE_DIRS}
|
||||
${GNURADIO_RUNTIME_INCLUDE_DIRS}
|
||||
${GNURADIO_OSMOSDR_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
add_definitions(${QT_DEFINITIONS})
|
||||
add_definitions(-DQT_PLUGIN)
|
||||
add_definitions(-DQT_SHARED)
|
||||
add_definitions(-DBOOST_ALL_NO_LIB)
|
||||
|
||||
if(MSVC)
|
||||
add_definitions(-DNOMINMAX)
|
||||
endif()
|
||||
|
||||
qt5_wrap_ui(gnuradio_FORMS_HEADERS ${gnuradio_FORMS})
|
||||
|
||||
add_library(inputgnuradio SHARED
|
||||
${gnuradio_SOURCES}
|
||||
${gnuradio_HEADERS_MOC}
|
||||
${gnuradio_FORMS_HEADERS}
|
||||
)
|
||||
|
||||
target_link_libraries(inputgnuradio
|
||||
${QT_LIBRARIES}
|
||||
${Boost_LIBRARIES}
|
||||
${GNURADIO_RUNTIME_LIBRARIES}
|
||||
${GNURADIO_OSMOSDR_LIBRARIES}
|
||||
sdrbase
|
||||
)
|
||||
|
||||
qt5_use_modules(inputgnuradio Core Widgets OpenGL Multimedia)
|
||||
|
||||
endif(Boost_FOUND AND GNURADIO_RUNTIME_FOUND AND GNURADIO_OSMOSDR_FOUND)
|
||||
@@ -0,0 +1,29 @@
|
||||
INCLUDE(FindPkgConfig)
|
||||
PKG_CHECK_MODULES(PC_GNURADIO_OSMOSDR gnuradio-osmosdr)
|
||||
|
||||
FIND_PATH(
|
||||
GNURADIO_OSMOSDR_INCLUDE_DIRS
|
||||
NAMES osmosdr/api.h
|
||||
HINTS $ENV{GNURADIO_OSMOSDR_DIR}/include
|
||||
${PC_GNURADIO_OSMOSDR_INCLUDEDIR}
|
||||
${CMAKE_INSTALL_PREFIX}/include
|
||||
PATHS /usr/local/include
|
||||
/usr/include
|
||||
)
|
||||
|
||||
FIND_LIBRARY(
|
||||
GNURADIO_OSMOSDR_LIBRARIES
|
||||
NAMES gnuradio-osmosdr
|
||||
HINTS $ENV{GNURADIO_OSMOSDR_DIR}/lib
|
||||
${PC_GNURADIO_OSMOSDR_LIBDIR}
|
||||
${CMAKE_INSTALL_PREFIX}/lib64
|
||||
${CMAKE_INSTALL_PREFIX}/lib
|
||||
PATHS /usr/local/lib
|
||||
/usr/local/lib64
|
||||
/usr/lib
|
||||
/usr/lib64
|
||||
)
|
||||
|
||||
INCLUDE(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(GNURADIO_OSMOSDR DEFAULT_MSG GNURADIO_OSMOSDR_LIBRARIES GNURADIO_OSMOSDR_INCLUDE_DIRS)
|
||||
MARK_AS_ADVANCED(GNURADIO_OSMOSDR_LIBRARIES GNURADIO_OSMOSDR_INCLUDE_DIRS)
|
||||
@@ -0,0 +1,29 @@
|
||||
INCLUDE(FindPkgConfig)
|
||||
PKG_CHECK_MODULES(PC_GNURADIO_RUNTIME gnuradio-runtime)
|
||||
|
||||
FIND_PATH(
|
||||
GNURADIO_RUNTIME_INCLUDE_DIRS
|
||||
NAMES gnuradio/api.h
|
||||
HINTS $ENV{GNURADIO_RUNTIME_DIR}/include
|
||||
${PC_GNURADIO_RUNTIME_INCLUDEDIR}
|
||||
${CMAKE_INSTALL_PREFIX}/include
|
||||
PATHS /usr/local/include
|
||||
/usr/include
|
||||
)
|
||||
|
||||
FIND_LIBRARY(
|
||||
GNURADIO_RUNTIME_LIBRARIES
|
||||
NAMES gnuradio-runtime
|
||||
HINTS $ENV{GNURADIO_RUNTIME_DIR}/lib
|
||||
${PC_GNURADIO_RUNTIME_LIBDIR}
|
||||
${CMAKE_INSTALL_PREFIX}/lib64
|
||||
${CMAKE_INSTALL_PREFIX}/lib
|
||||
PATHS /usr/local/lib
|
||||
/usr/local/lib64
|
||||
/usr/lib
|
||||
/usr/lib64
|
||||
)
|
||||
|
||||
INCLUDE(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(GNURADIO_RUNTIME DEFAULT_MSG GNURADIO_RUNTIME_LIBRARIES GNURADIO_RUNTIME_INCLUDE_DIRS)
|
||||
MARK_AS_ADVANCED(GNURADIO_RUNTIME_LIBRARIES GNURADIO_RUNTIME_INCLUDE_DIRS)
|
||||
@@ -0,0 +1,422 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// Copyright (C) 2013 by Dimitri Stolnikov <horiz0n@gmx.net> //
|
||||
// //
|
||||
// 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 //
|
||||
// //
|
||||
// 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 "gnuradiogui.h"
|
||||
#include "ui_gnuradiogui.h"
|
||||
|
||||
#include <osmosdr/device.h>
|
||||
#include <iostream>
|
||||
#include <plugin/pluginapi.h>
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QSlider>
|
||||
|
||||
GNURadioGui::GNURadioGui(PluginAPI* pluginAPI, QWidget* parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::GNURadioGui),
|
||||
m_pluginAPI(pluginAPI),
|
||||
m_settings(),
|
||||
m_sampleSource(NULL)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware()));
|
||||
displaySettings();
|
||||
|
||||
m_sampleSource = new GNURadioInput(m_pluginAPI->getMainWindowMessageQueue());
|
||||
m_pluginAPI->setSampleSource(m_sampleSource);
|
||||
}
|
||||
|
||||
GNURadioGui::~GNURadioGui()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void GNURadioGui::destroy()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void GNURadioGui::setName(const QString& name)
|
||||
{
|
||||
setObjectName(name);
|
||||
}
|
||||
|
||||
void GNURadioGui::resetToDefaults()
|
||||
{
|
||||
m_generalSettings.resetToDefaults();
|
||||
m_settings.resetToDefaults();
|
||||
displaySettings();
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
QByteArray GNURadioGui::serializeGeneral() const
|
||||
{
|
||||
return m_generalSettings.serialize();
|
||||
}
|
||||
|
||||
bool GNURadioGui::deserializeGeneral(const QByteArray&data)
|
||||
{
|
||||
if(m_generalSettings.deserialize(data)) {
|
||||
displaySettings();
|
||||
sendSettings();
|
||||
return true;
|
||||
} else {
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
quint64 GNURadioGui::getCenterFrequency() const
|
||||
{
|
||||
return m_generalSettings.m_centerFrequency;
|
||||
}
|
||||
|
||||
QByteArray GNURadioGui::serialize() const
|
||||
{
|
||||
return m_settings.serialize();
|
||||
}
|
||||
|
||||
bool GNURadioGui::deserialize(const QByteArray& data)
|
||||
{
|
||||
if(m_settings.deserialize(data)) {
|
||||
displaySettings();
|
||||
sendSettings();
|
||||
return true;
|
||||
} else {
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool GNURadioGui::handleMessage(Message* message)
|
||||
{
|
||||
if(GNURadioInput::MsgReportGNURadio::match(message)) {
|
||||
GNURadioInput::MsgReportGNURadio* rep = (GNURadioInput::MsgReportGNURadio*)message;
|
||||
m_namedGains = rep->getNamedGains();
|
||||
m_freqMin = rep->getFreqMin();
|
||||
m_freqMax = rep->getFreqMax();
|
||||
m_freqCorr = rep->getFreqCorr();
|
||||
m_sampRates = rep->getSampRates();
|
||||
m_antennas = rep->getAntennas();
|
||||
m_dcoffs = rep->getDCOffs();
|
||||
m_iqbals = rep->getIQBals();
|
||||
m_bandwidths = rep->getBandwidths();
|
||||
/* insert 0 which will become "Auto" in the combo box */
|
||||
m_bandwidths.insert(m_bandwidths.begin(), 0);
|
||||
displaySettings();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void GNURadioGui::displaySettings()
|
||||
{
|
||||
int oldIndex = 0;
|
||||
|
||||
oldIndex = ui->cboDevices->currentIndex();
|
||||
ui->cboDevices->clear();
|
||||
|
||||
QString oldArgs = ui->txtDeviceArgs->text();
|
||||
|
||||
osmosdr::devices_t devices = osmosdr::device::find();
|
||||
|
||||
for ( int i = 0; i < devices.size(); i++ )
|
||||
{
|
||||
osmosdr::device_t dev = devices[i];
|
||||
|
||||
QString label;
|
||||
|
||||
if ( dev.count( "label" ) )
|
||||
{
|
||||
label = QString(dev[ "label" ].c_str());
|
||||
dev.erase("label");
|
||||
}
|
||||
|
||||
QPair< QString, QString > pair(label, dev.to_string().c_str());
|
||||
m_devs.append(pair);
|
||||
|
||||
ui->cboDevices->addItem(label);
|
||||
}
|
||||
|
||||
if ( ui->cboDevices->count() && oldIndex >= 0 )
|
||||
{
|
||||
if ( oldIndex > ui->cboDevices->count() - 1 )
|
||||
oldIndex = 0;
|
||||
|
||||
ui->cboDevices->setCurrentIndex(oldIndex);
|
||||
|
||||
if ( oldArgs.length() == 0 )
|
||||
ui->txtDeviceArgs->setText( m_devs[oldIndex].second );
|
||||
}
|
||||
|
||||
if ( oldArgs.length() )
|
||||
ui->txtDeviceArgs->setText( oldArgs );
|
||||
|
||||
ui->centerFrequency->setValueRange(7,
|
||||
unsigned(m_freqMin / 1000.0),
|
||||
unsigned(m_freqMax / 1000.0));
|
||||
|
||||
ui->centerFrequency->setValue(m_generalSettings.m_centerFrequency / 1000);
|
||||
|
||||
ui->sldFreqCorr->setRange(-100, +100);
|
||||
ui->sldFreqCorr->setValue( m_freqCorr );
|
||||
ui->lblFreqCorr->setText(tr("%1").arg(ui->sldFreqCorr->value()));
|
||||
|
||||
m_gainControls.clear();
|
||||
QVBoxLayout *layoutGains = ui->verticalLayoutGains;
|
||||
QLayoutItem *layoutItem;
|
||||
|
||||
while ( ( layoutItem = layoutGains->takeAt( 0 ) ) != NULL )
|
||||
{
|
||||
QLayout *layout = layoutItem->layout();
|
||||
|
||||
if ( !layout )
|
||||
continue;
|
||||
|
||||
while ( ( layoutItem = layout->takeAt( 0 ) ) != NULL )
|
||||
{
|
||||
delete layoutItem->widget();
|
||||
delete layoutItem;
|
||||
}
|
||||
|
||||
delete layout;
|
||||
}
|
||||
|
||||
for ( int i = 0; i < m_namedGains.size(); i++ )
|
||||
{
|
||||
std::pair< QString, std::vector<double> > pair = m_namedGains[i];
|
||||
|
||||
QHBoxLayout *layout = new QHBoxLayout();
|
||||
QLabel *gainName = new QLabel( pair.first + " Gain" );
|
||||
QSlider *gainSlider = new QSlider(Qt::Horizontal);
|
||||
QLabel *gainLabel = new QLabel("0");
|
||||
gainLabel->setMinimumWidth(30);
|
||||
gainLabel->setAlignment(Qt::AlignHCenter | Qt::AlignHCenter);
|
||||
|
||||
QPair< QSlider*, QLabel* > pair2( gainSlider, gainLabel );
|
||||
m_gainControls.push_back( pair2 );
|
||||
|
||||
connect(gainSlider, SIGNAL(valueChanged(int)),
|
||||
this, SLOT(on_sldGain_valueChanged(int)));
|
||||
|
||||
layout->addWidget(gainName);
|
||||
layout->addWidget(gainSlider);
|
||||
layout->addWidget(gainLabel);
|
||||
|
||||
layoutGains->addLayout(layout);
|
||||
|
||||
std::vector<double> gain_values = pair.second;
|
||||
|
||||
if ( gain_values.size() ) {
|
||||
gainSlider->setRange(0, gain_values.size() - 1);
|
||||
gainSlider->setValue(gain_values.size() / 4);
|
||||
gainSlider->setEnabled(true);
|
||||
} else {
|
||||
gainSlider->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
oldIndex = ui->cboSampleRate->currentIndex();
|
||||
ui->cboSampleRate->clear();
|
||||
|
||||
for ( int i = 0; i < m_sampRates.size(); i++ )
|
||||
ui->cboSampleRate->addItem( QString::number(m_sampRates[i] / 1e6) );
|
||||
|
||||
if ( oldIndex > ui->cboSampleRate->count() - 1 )
|
||||
oldIndex = 0;
|
||||
|
||||
if ( ui->cboSampleRate->count() && oldIndex >= 0 )
|
||||
ui->cboSampleRate->setCurrentIndex(oldIndex);
|
||||
|
||||
if ( ui->cboSampleRate->count() ) {
|
||||
ui->cboSampleRate->setEnabled(true);
|
||||
} else {
|
||||
ui->cboSampleRate->setEnabled(false);
|
||||
}
|
||||
|
||||
oldIndex = ui->cboAntennas->currentIndex();
|
||||
ui->cboAntennas->clear();
|
||||
|
||||
if ( m_antennas.size() ) {
|
||||
for ( int i = 0; i < m_antennas.size(); i++ )
|
||||
ui->cboAntennas->addItem( m_antennas[i] );
|
||||
|
||||
if ( oldIndex > ui->cboAntennas->count() - 1 )
|
||||
oldIndex = 0;
|
||||
|
||||
if ( ui->cboAntennas->count() && oldIndex >= 0 )
|
||||
ui->cboAntennas->setCurrentIndex(oldIndex);
|
||||
|
||||
ui->cboAntennas->setEnabled(true);
|
||||
} else {
|
||||
ui->cboAntennas->setEnabled(false);
|
||||
}
|
||||
|
||||
oldIndex = ui->cboDCOffset->currentIndex();
|
||||
ui->cboDCOffset->clear();
|
||||
|
||||
if ( m_dcoffs.size() ) {
|
||||
for ( int i = 0; i < m_dcoffs.size(); i++ )
|
||||
ui->cboDCOffset->addItem( m_dcoffs[i] );
|
||||
|
||||
if ( ui->cboDCOffset->count() && oldIndex >= 0 )
|
||||
ui->cboDCOffset->setCurrentIndex(oldIndex);
|
||||
|
||||
ui->cboDCOffset->setEnabled(true);
|
||||
} else {
|
||||
ui->cboDCOffset->setEnabled(false);
|
||||
}
|
||||
|
||||
oldIndex = ui->cboIQBalance->currentIndex();
|
||||
ui->cboIQBalance->clear();
|
||||
|
||||
if ( m_iqbals.size() ) {
|
||||
for ( int i = 0; i < m_iqbals.size(); i++ )
|
||||
ui->cboIQBalance->addItem( m_iqbals[i] );
|
||||
|
||||
if ( ui->cboIQBalance->count() && oldIndex >= 0 )
|
||||
ui->cboIQBalance->setCurrentIndex(oldIndex);
|
||||
|
||||
ui->cboIQBalance->setEnabled(true);
|
||||
} else {
|
||||
ui->cboIQBalance->setEnabled(false);
|
||||
}
|
||||
|
||||
oldIndex = ui->cboBandwidth->currentIndex();
|
||||
ui->cboBandwidth->clear();
|
||||
|
||||
for ( int i = 0; i < m_bandwidths.size(); i++ )
|
||||
if ( 0.0 == m_bandwidths[i] )
|
||||
ui->cboBandwidth->addItem( "Auto" );
|
||||
else
|
||||
ui->cboBandwidth->addItem( QString::number(m_bandwidths[i] / 1e6) );
|
||||
|
||||
if ( oldIndex > ui->cboBandwidth->count() - 1 )
|
||||
oldIndex = 0;
|
||||
|
||||
if ( ui->cboBandwidth->count() && oldIndex >= 0 )
|
||||
ui->cboBandwidth->setCurrentIndex(oldIndex);
|
||||
|
||||
if ( ui->cboBandwidth->count() ) {
|
||||
ui->cboBandwidth->setEnabled(true);
|
||||
} else {
|
||||
ui->cboBandwidth->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void GNURadioGui::sendSettings()
|
||||
{
|
||||
if(!m_updateTimer.isActive())
|
||||
m_updateTimer.start(100);
|
||||
}
|
||||
|
||||
void GNURadioGui::updateHardware()
|
||||
{
|
||||
m_updateTimer.stop();
|
||||
GNURadioInput::MsgConfigureGNURadio* msg = GNURadioInput::MsgConfigureGNURadio::create(m_generalSettings, m_settings);
|
||||
msg->submit(m_pluginAPI->getDSPEngineMessageQueue());
|
||||
}
|
||||
|
||||
void GNURadioGui::on_cboDevices_currentIndexChanged(int index)
|
||||
{
|
||||
if ( index < 0 || index >= m_devs.count() )
|
||||
return;
|
||||
|
||||
ui->txtDeviceArgs->setText( m_devs[index].second );
|
||||
}
|
||||
|
||||
void GNURadioGui::on_txtDeviceArgs_textChanged(const QString &arg1)
|
||||
{
|
||||
m_settings.m_args = arg1;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void GNURadioGui::on_centerFrequency_changed(quint64 value)
|
||||
{
|
||||
m_generalSettings.m_centerFrequency = value * 1000;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void GNURadioGui::on_sldFreqCorr_valueChanged(int value)
|
||||
{
|
||||
ui->lblFreqCorr->setText(tr("%1").arg(value));
|
||||
m_settings.m_freqCorr = value;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void GNURadioGui::on_sldGain_valueChanged(int value)
|
||||
{
|
||||
m_settings.m_namedGains.clear();
|
||||
|
||||
for ( int i = 0; i < m_gainControls.size(); i++ )
|
||||
{
|
||||
QPair< QSlider*, QLabel* > controls = m_gainControls[i];
|
||||
|
||||
QSlider *slider = controls.first;
|
||||
QLabel *label = controls.second;
|
||||
|
||||
std::pair< QString, std::vector<double> > named_gain = m_namedGains[ i ];
|
||||
|
||||
int index = slider->value();
|
||||
double gain = named_gain.second[index];
|
||||
label->setText(tr("%1").arg(gain));
|
||||
|
||||
QPair< QString, double > named_gain2( named_gain.first, gain );
|
||||
m_settings.m_namedGains.push_back( named_gain2 );
|
||||
}
|
||||
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void GNURadioGui::on_cboSampleRate_currentIndexChanged(int index)
|
||||
{
|
||||
if ( index < 0 || index >= m_sampRates.size() )
|
||||
return;
|
||||
|
||||
m_settings.m_sampRate = m_sampRates[index];
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void GNURadioGui::on_cboAntennas_currentIndexChanged(const QString &arg1)
|
||||
{
|
||||
m_settings.m_antenna = arg1;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void GNURadioGui::on_cboDCOffset_currentIndexChanged(const QString &arg1)
|
||||
{
|
||||
m_settings.m_dcoff = arg1;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void GNURadioGui::on_cboIQBalance_currentIndexChanged(const QString &arg1)
|
||||
{
|
||||
m_settings.m_iqbal = arg1;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void GNURadioGui::on_cboBandwidth_currentIndexChanged(int index)
|
||||
{
|
||||
if ( index < 0 || index >= m_bandwidths.size() )
|
||||
return;
|
||||
|
||||
m_settings.m_bandwidth = m_bandwidths[index];
|
||||
sendSettings();
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// Copyright (C) 2013 by Dimitri Stolnikov <horiz0n@gmx.net> //
|
||||
// //
|
||||
// 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 //
|
||||
// //
|
||||
// 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 INCLUDE_GNURADIOGUI_H
|
||||
#define INCLUDE_GNURADIOGUI_H
|
||||
|
||||
#include <QTimer>
|
||||
#include <QPair>
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QSlider>
|
||||
#include <QLabel>
|
||||
#include "plugin/plugingui.h"
|
||||
#include "gnuradioinput.h"
|
||||
|
||||
namespace Ui {
|
||||
class GNURadioGui;
|
||||
}
|
||||
|
||||
class PluginAPI;
|
||||
|
||||
class GNURadioGui : public QWidget, public PluginGUI {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit GNURadioGui(PluginAPI* pluginAPI, QWidget* parent = NULL);
|
||||
~GNURadioGui();
|
||||
void destroy();
|
||||
|
||||
void setName(const QString& name);
|
||||
|
||||
void resetToDefaults();
|
||||
QByteArray serializeGeneral() const;
|
||||
bool deserializeGeneral(const QByteArray&data);
|
||||
quint64 getCenterFrequency() const;
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
bool handleMessage(Message* message);
|
||||
|
||||
private:
|
||||
Ui::GNURadioGui* ui;
|
||||
PluginAPI* m_pluginAPI;
|
||||
SampleSource* m_sampleSource;
|
||||
QList< QPair<QString, QString> > m_devs;
|
||||
std::vector< std::pair< QString, std::vector<double> > > m_namedGains;
|
||||
double m_freqMin;
|
||||
double m_freqMax;
|
||||
double m_freqCorr;
|
||||
std::vector<double> m_sampRates;
|
||||
std::vector<QString> m_antennas;
|
||||
std::vector<QString> m_dcoffs;
|
||||
std::vector<QString> m_iqbals;
|
||||
std::vector<double> m_bandwidths;
|
||||
|
||||
std::vector< QSlider* > m_gainSliders;
|
||||
std::vector< QLabel* > m_gainLabels;
|
||||
|
||||
QList< QPair< QSlider*, QLabel* > > m_gainControls;
|
||||
|
||||
SampleSource::GeneralSettings m_generalSettings;
|
||||
GNURadioInput::Settings m_settings;
|
||||
QTimer m_updateTimer;
|
||||
|
||||
void displaySettings();
|
||||
void sendSettings();
|
||||
|
||||
private slots:
|
||||
void updateHardware();
|
||||
|
||||
void on_cboDevices_currentIndexChanged(int index);
|
||||
void on_txtDeviceArgs_textChanged(const QString &arg1);
|
||||
void on_centerFrequency_changed(quint64 value);
|
||||
void on_sldFreqCorr_valueChanged(int value);
|
||||
|
||||
void on_sldGain_valueChanged(int value);
|
||||
|
||||
void on_cboSampleRate_currentIndexChanged(int index);
|
||||
void on_cboAntennas_currentIndexChanged(const QString &arg1);
|
||||
void on_cboDCOffset_currentIndexChanged(const QString &arg1);
|
||||
void on_cboIQBalance_currentIndexChanged(const QString &arg1);
|
||||
void on_cboBandwidth_currentIndexChanged(int index);
|
||||
};
|
||||
|
||||
#endif // INCLUDE_GNURADIOGUI_H
|
||||
@@ -0,0 +1,301 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>GNURadioGui</class>
|
||||
<widget class="QWidget" name="GNURadioGui">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>186</width>
|
||||
<height>261</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Gnuradio Source</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QComboBox" name="cboDevices">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QComboBox::AdjustToMinimumContentsLength</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="txtDeviceArgs">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<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>Monospace</family>
|
||||
<pointsize>20</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Tuner center frequency in kHz</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<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>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Freq. Corr.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="sldFreqCorr">
|
||||
<property name="minimum">
|
||||
<number>-100</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="tracking">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lblFreqCorr">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayoutGains"/>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Sample Rate (MHz)</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::AutoText</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="cboSampleRate">
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QComboBox::AdjustToMinimumContentsLength</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Selected Antenna</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="cboAntennas">
|
||||
<property name="frame">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>DC Offset Corr. </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="cboDCOffset"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_8">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>IQ Imbalance Corr.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="cboIQBalance"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Bandwidth (MHz)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="cboBandwidth"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>ValueDial</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/valuedial.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,332 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// Copyright (C) 2013 by Dimitri Stolnikov <horiz0n@gmx.net> //
|
||||
// //
|
||||
// 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 //
|
||||
// //
|
||||
// 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 <string.h>
|
||||
#include <errno.h>
|
||||
#include "util/simpleserializer.h"
|
||||
#include "gnuradioinput.h"
|
||||
#include "gnuradiothread.h"
|
||||
#include "gnuradiogui.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(GNURadioInput::MsgConfigureGNURadio, Message)
|
||||
MESSAGE_CLASS_DEFINITION(GNURadioInput::MsgReportGNURadio, Message)
|
||||
|
||||
GNURadioInput::Settings::Settings() :
|
||||
m_args(""),
|
||||
m_freqCorr(0),
|
||||
m_sampRate(0),
|
||||
m_antenna(""),
|
||||
m_dcoff(""),
|
||||
m_iqbal(""),
|
||||
m_bandwidth(0)
|
||||
{
|
||||
}
|
||||
|
||||
void GNURadioInput::Settings::resetToDefaults()
|
||||
{
|
||||
m_args = "";
|
||||
m_sampRate = 0;
|
||||
m_freqCorr = 0;
|
||||
m_antenna = "";
|
||||
m_dcoff = "";
|
||||
m_iqbal = "";
|
||||
m_bandwidth = 0;
|
||||
}
|
||||
|
||||
QByteArray GNURadioInput::Settings::serialize() const
|
||||
{
|
||||
SimpleSerializer s(1);
|
||||
// s.writeString(1, m_args);
|
||||
// s.writeDouble(2, m_freqCorr);
|
||||
// s.writeDouble(3, m_sampRate);
|
||||
// s.writeString(4, m_antenna);
|
||||
// s.writeString(5, m_dcoff);
|
||||
// s.writeString(5, m_iqbal);
|
||||
return s.final();
|
||||
}
|
||||
|
||||
bool GNURadioInput::Settings::deserialize(const QByteArray& data)
|
||||
{
|
||||
SimpleDeserializer d(data);
|
||||
|
||||
if(!d.isValid()) {
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(d.getVersion() == 1) {
|
||||
// d.readString(1, &m_args, "");
|
||||
// d.readDouble(2, &m_freqCorr, 0);
|
||||
// d.readDouble(3, &m_sampRate, 0);
|
||||
// d.readString(4, &m_antenna, "");
|
||||
// d.readString(5, &m_dcoff, "");
|
||||
// d.readString(5, &m_iqbal, "");
|
||||
return true;
|
||||
} else {
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
GNURadioInput::GNURadioInput(MessageQueue* msgQueueToGUI) :
|
||||
SampleSource(msgQueueToGUI),
|
||||
m_settings(),
|
||||
m_GnuradioThread(NULL),
|
||||
m_deviceDescription()
|
||||
{
|
||||
}
|
||||
|
||||
GNURadioInput::~GNURadioInput()
|
||||
{
|
||||
stopInput();
|
||||
}
|
||||
|
||||
bool GNURadioInput::startInput(int device)
|
||||
{
|
||||
double freqMin = 0, freqMax = 0, freqCorr = 0;
|
||||
std::vector< std::pair< QString, std::vector<double> > > namedGains;
|
||||
std::vector< double > sampRates;
|
||||
std::vector< QString > antennas;
|
||||
std::vector< double > bandwidths;
|
||||
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
if(m_GnuradioThread != NULL)
|
||||
stopInput();
|
||||
|
||||
if(!m_sampleFifo.setSize( 2 * 1024 * 1024 )) {
|
||||
qCritical("Could not allocate SampleFifo");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_deviceDescription = m_settings.m_args;
|
||||
|
||||
// pass device arguments from the gui
|
||||
m_GnuradioThread = new GnuradioThread(m_settings.m_args, &m_sampleFifo);
|
||||
if(m_GnuradioThread == NULL) {
|
||||
qFatal("out of memory");
|
||||
goto failed;
|
||||
}
|
||||
m_GnuradioThread->startWork();
|
||||
|
||||
mutexLocker.unlock();
|
||||
applySettings(m_generalSettings, m_settings, true);
|
||||
|
||||
if(m_GnuradioThread != NULL) {
|
||||
osmosdr::source::sptr radio = m_GnuradioThread->radio();
|
||||
|
||||
try {
|
||||
osmosdr::freq_range_t freq_rage = radio->get_freq_range();
|
||||
freqMin = freq_rage.start();
|
||||
freqMax = freq_rage.stop();
|
||||
} catch ( std::exception &ex ) {
|
||||
qDebug("%s", ex.what());
|
||||
}
|
||||
|
||||
freqCorr = radio->get_freq_corr();
|
||||
|
||||
namedGains.clear();
|
||||
m_settings.m_namedGains.clear();
|
||||
std::vector< std::string > gain_names = radio->get_gain_names();
|
||||
for ( int i = 0; i < gain_names.size(); i++ )
|
||||
{
|
||||
std::string gain_name = gain_names[i];
|
||||
|
||||
try {
|
||||
std::vector< double > gain_values = \
|
||||
radio->get_gain_range( gain_name ).values();
|
||||
|
||||
std::pair< QString, std::vector<double> > pair( gain_name.c_str(),
|
||||
gain_values );
|
||||
|
||||
namedGains.push_back( pair );
|
||||
|
||||
QPair< QString, double > pair2( gain_name.c_str(), 0 );
|
||||
|
||||
m_settings.m_namedGains.push_back( pair2 );
|
||||
} catch ( std::exception &ex ) {
|
||||
qDebug("%s", ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
sampRates = radio->get_sample_rates().values();
|
||||
} catch ( std::exception &ex ) {
|
||||
qDebug("%s", ex.what());
|
||||
}
|
||||
|
||||
antennas.clear();
|
||||
std::vector< std::string > ant = radio->get_antennas();
|
||||
for ( int i = 0; i < ant.size(); i++ )
|
||||
antennas.push_back( QString( ant[i].c_str() ) );
|
||||
|
||||
m_dcoffs.clear();
|
||||
m_dcoffs.push_back( "Off" );
|
||||
m_dcoffs.push_back( "Keep" );
|
||||
m_dcoffs.push_back( "Auto" );
|
||||
|
||||
m_iqbals.clear();
|
||||
m_iqbals.push_back( "Off" );
|
||||
m_iqbals.push_back( "Keep" );
|
||||
m_iqbals.push_back( "Auto" );
|
||||
|
||||
try {
|
||||
bandwidths = radio->get_bandwidth_range().values();
|
||||
} catch ( std::exception &ex ) {
|
||||
qDebug("%s", ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
qDebug("GnuradioInput: start");
|
||||
MsgReportGNURadio::create(freqMin, freqMax, freqCorr, namedGains,
|
||||
sampRates, antennas, m_dcoffs, m_iqbals,
|
||||
bandwidths)
|
||||
->submit(m_guiMessageQueue);
|
||||
|
||||
return true;
|
||||
|
||||
failed:
|
||||
stopInput();
|
||||
return false;
|
||||
}
|
||||
|
||||
void GNURadioInput::stopInput()
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
if(m_GnuradioThread != NULL) {
|
||||
m_GnuradioThread->stopWork();
|
||||
delete m_GnuradioThread;
|
||||
m_GnuradioThread = NULL;
|
||||
}
|
||||
|
||||
m_deviceDescription.clear();
|
||||
}
|
||||
|
||||
const QString& GNURadioInput::getDeviceDescription() const
|
||||
{
|
||||
return m_deviceDescription;
|
||||
}
|
||||
|
||||
int GNURadioInput::getSampleRate() const
|
||||
{
|
||||
return m_settings.m_sampRate;
|
||||
}
|
||||
|
||||
quint64 GNURadioInput::getCenterFrequency() const
|
||||
{
|
||||
return m_generalSettings.m_centerFrequency;
|
||||
}
|
||||
|
||||
bool GNURadioInput::handleMessage(Message* message)
|
||||
{
|
||||
if(MsgConfigureGNURadio::match(message)) {
|
||||
MsgConfigureGNURadio* conf = (MsgConfigureGNURadio*)message;
|
||||
if(!applySettings(conf->getGeneralSettings(), conf->getSettings(), false))
|
||||
qDebug("Gnuradio config error");
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool GNURadioInput::applySettings(const GeneralSettings& generalSettings,
|
||||
const Settings& settings,
|
||||
bool force)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
m_settings.m_args = settings.m_args;
|
||||
|
||||
if ( NULL == m_GnuradioThread )
|
||||
return true;
|
||||
|
||||
osmosdr::source::sptr radio = m_GnuradioThread->radio();
|
||||
|
||||
try {
|
||||
|
||||
if((m_settings.m_freqCorr != settings.m_freqCorr) || force) {
|
||||
m_settings.m_freqCorr = settings.m_freqCorr;
|
||||
radio->set_freq_corr( m_settings.m_freqCorr );
|
||||
}
|
||||
|
||||
if((m_generalSettings.m_centerFrequency != generalSettings.m_centerFrequency) || force) {
|
||||
m_generalSettings.m_centerFrequency = generalSettings.m_centerFrequency;
|
||||
radio->set_center_freq( m_generalSettings.m_centerFrequency );
|
||||
}
|
||||
|
||||
for ( int i = 0; i < settings.m_namedGains.size(); i++ )
|
||||
{
|
||||
if((m_settings.m_namedGains[i].second != settings.m_namedGains[i].second) || force) {
|
||||
m_settings.m_namedGains[i].second = settings.m_namedGains[i].second;
|
||||
radio->set_gain( settings.m_namedGains[i].second,
|
||||
settings.m_namedGains[i].first.toStdString() );
|
||||
}
|
||||
}
|
||||
|
||||
if((m_settings.m_sampRate != settings.m_sampRate) || force) {
|
||||
m_settings.m_sampRate = settings.m_sampRate;
|
||||
radio->set_sample_rate( m_settings.m_sampRate );
|
||||
}
|
||||
|
||||
if((m_settings.m_antenna != settings.m_antenna) || force) {
|
||||
m_settings.m_antenna = settings.m_antenna;
|
||||
radio->set_antenna( m_settings.m_antenna.toStdString() );
|
||||
}
|
||||
|
||||
if((m_settings.m_dcoff != settings.m_dcoff) || force) {
|
||||
m_settings.m_dcoff = settings.m_dcoff;
|
||||
|
||||
for ( int i = 0; i < m_dcoffs.size(); i++ )
|
||||
{
|
||||
if ( m_dcoffs[i] != m_settings.m_dcoff )
|
||||
continue;
|
||||
|
||||
radio->set_dc_offset_mode( i );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if((m_settings.m_iqbal != settings.m_iqbal) || force) {
|
||||
m_settings.m_iqbal = settings.m_iqbal;
|
||||
|
||||
for ( int i = 0; i < m_iqbals.size(); i++ )
|
||||
{
|
||||
if ( m_iqbals[i] != m_settings.m_iqbal )
|
||||
continue;
|
||||
|
||||
radio->set_iq_balance_mode( i );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if((m_settings.m_bandwidth != settings.m_bandwidth) ||
|
||||
(0.0f == settings.m_bandwidth) || force) {
|
||||
m_settings.m_bandwidth = settings.m_bandwidth;
|
||||
/* setting the BW to 0.0 triggers automatic bandwidth
|
||||
* selection when supported by device */
|
||||
radio->set_bandwidth( m_settings.m_bandwidth );
|
||||
}
|
||||
|
||||
} catch ( std::exception &ex ) {
|
||||
qDebug("%s", ex.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// Copyright (C) 2013 by Dimitri Stolnikov <horiz0n@gmx.net> //
|
||||
// //
|
||||
// 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 //
|
||||
// //
|
||||
// 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 INCLUDE_GNURADIOINPUT_H
|
||||
#define INCLUDE_GNURADIOINPUT_H
|
||||
|
||||
#include "dsp/samplesource/samplesource.h"
|
||||
#include <QString>
|
||||
#include <QPair>
|
||||
|
||||
class GnuradioThread;
|
||||
|
||||
class GNURadioInput : public SampleSource {
|
||||
public:
|
||||
struct Settings {
|
||||
QString m_args;
|
||||
double m_freqMin;
|
||||
double m_freqMax;
|
||||
double m_freqCorr;
|
||||
QList< QPair< QString, double > > m_namedGains;
|
||||
double m_sampRate;
|
||||
QString m_antenna;
|
||||
QString m_dcoff;
|
||||
QString m_iqbal;
|
||||
double m_bandwidth;
|
||||
|
||||
Settings();
|
||||
void resetToDefaults();
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
};
|
||||
|
||||
class MsgConfigureGNURadio : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
const GeneralSettings& getGeneralSettings() const { return m_generalSettings; }
|
||||
const Settings& getSettings() const { return m_settings; }
|
||||
|
||||
static MsgConfigureGNURadio* create(const GeneralSettings& generalSettings, const Settings& settings)
|
||||
{
|
||||
return new MsgConfigureGNURadio(generalSettings, settings);
|
||||
}
|
||||
|
||||
protected:
|
||||
GeneralSettings m_generalSettings;
|
||||
Settings m_settings;
|
||||
|
||||
MsgConfigureGNURadio(const GeneralSettings& generalSettings, const Settings& settings) :
|
||||
Message(),
|
||||
m_generalSettings(generalSettings),
|
||||
m_settings(settings)
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgReportGNURadio : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
const std::vector< std::pair< QString, std::vector<double> > >& getNamedGains() const { return m_namedGains; }
|
||||
const double getFreqMin() const { return m_freqMin; }
|
||||
const double getFreqMax() const { return m_freqMax; }
|
||||
const double getFreqCorr() const { return m_freqCorr; }
|
||||
const std::vector<double>& getSampRates() const { return m_sampRates; }
|
||||
const std::vector<QString>& getAntennas() const { return m_antennas; }
|
||||
const std::vector<QString>& getDCOffs() const { return m_dcoffs; }
|
||||
const std::vector<QString>& getIQBals() const { return m_iqbals; }
|
||||
const std::vector<double>& getBandwidths() const { return m_bandwidths; }
|
||||
|
||||
static MsgReportGNURadio* create(const double freqMin,
|
||||
const double freqMax,
|
||||
const double freqCorr,
|
||||
const std::vector< std::pair< QString, std::vector<double> > >& namedGains,
|
||||
const std::vector<double>& sampRates,
|
||||
const std::vector<QString>& antennas,
|
||||
const std::vector<QString>& dcoffs,
|
||||
const std::vector<QString>& iqbals,
|
||||
const std::vector<double>& bandwidths)
|
||||
{
|
||||
return new MsgReportGNURadio(freqMin, freqMax, freqCorr, namedGains, sampRates, antennas, dcoffs, iqbals, bandwidths);
|
||||
}
|
||||
|
||||
protected:
|
||||
double m_freqMin;
|
||||
double m_freqMax;
|
||||
double m_freqCorr;
|
||||
std::vector< std::pair< QString, std::vector<double> > > m_namedGains;
|
||||
std::vector<double> m_sampRates;
|
||||
std::vector<QString> m_antennas;
|
||||
std::vector<QString> m_dcoffs;
|
||||
std::vector<QString> m_iqbals;
|
||||
std::vector<double> m_bandwidths;
|
||||
|
||||
MsgReportGNURadio(const double freqMin,
|
||||
const double freqMax,
|
||||
const double freqCorr,
|
||||
const std::vector< std::pair< QString, std::vector<double> > >& namedGains,
|
||||
const std::vector<double>& sampRates,
|
||||
const std::vector<QString>& antennas,
|
||||
const std::vector<QString>& dcoffs,
|
||||
const std::vector<QString>& iqbals,
|
||||
const std::vector<double>& bandwidths) :
|
||||
Message(),
|
||||
m_freqMin(freqMin),
|
||||
m_freqMax(freqMax),
|
||||
m_freqCorr(freqCorr),
|
||||
m_namedGains(namedGains),
|
||||
m_sampRates(sampRates),
|
||||
m_antennas(antennas),
|
||||
m_dcoffs(dcoffs),
|
||||
m_iqbals(iqbals),
|
||||
m_bandwidths(bandwidths)
|
||||
{ }
|
||||
};
|
||||
|
||||
GNURadioInput(MessageQueue* msgQueueToGUI);
|
||||
~GNURadioInput();
|
||||
|
||||
bool startInput(int device);
|
||||
void stopInput();
|
||||
|
||||
const QString& getDeviceDescription() const;
|
||||
int getSampleRate() const;
|
||||
quint64 getCenterFrequency() const;
|
||||
|
||||
bool handleMessage(Message* message);
|
||||
|
||||
private:
|
||||
QMutex m_mutex;
|
||||
Settings m_settings;
|
||||
GnuradioThread* m_GnuradioThread;
|
||||
QString m_deviceDescription;
|
||||
std::vector< QString > m_dcoffs;
|
||||
std::vector< QString > m_iqbals;
|
||||
|
||||
bool applySettings(const GeneralSettings& generalSettings,
|
||||
const Settings& settings,
|
||||
bool force);
|
||||
};
|
||||
|
||||
#endif // INCLUDE_GNURADIOINPUT_H
|
||||
@@ -0,0 +1,52 @@
|
||||
#include <QtPlugin>
|
||||
#include <QAction>
|
||||
#include "plugin/pluginapi.h"
|
||||
#include "util/simpleserializer.h"
|
||||
#include "gnuradioplugin.h"
|
||||
#include "gnuradiogui.h"
|
||||
|
||||
const PluginDescriptor GNURadioPlugin::m_pluginDescriptor = {
|
||||
QString("GR-OsmoSDR Input"),
|
||||
QString("1.0"),
|
||||
QString("(c) Dimitri Stolnikov <horiz0n@gmx.net>"),
|
||||
QString("http://sdr.osmocom.org/trac/wiki/GrOsmoSDR"),
|
||||
true,
|
||||
QString("http://cgit.osmocom.org/cgit/gr-osmosdr")
|
||||
};
|
||||
|
||||
GNURadioPlugin::GNURadioPlugin(QObject* parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
const PluginDescriptor& GNURadioPlugin::getPluginDescriptor() const
|
||||
{
|
||||
return m_pluginDescriptor;
|
||||
}
|
||||
|
||||
void GNURadioPlugin::initPlugin(PluginAPI* pluginAPI)
|
||||
{
|
||||
m_pluginAPI = pluginAPI;
|
||||
|
||||
m_pluginAPI->registerSampleSource("org.osmocom.sdr.samplesource.gr-osmosdr", this);
|
||||
}
|
||||
|
||||
PluginInterface::SampleSourceDevices GNURadioPlugin::enumSampleSources()
|
||||
{
|
||||
SampleSourceDevices result;
|
||||
|
||||
result.append(SampleSourceDevice("GNURadio OsmoSDR Driver", "org.osmocom.sdr.samplesource.gr-osmosdr", QByteArray()));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
PluginGUI* GNURadioPlugin::createSampleSource(const QString& sourceName, const QByteArray& address)
|
||||
{
|
||||
if(sourceName == "org.osmocom.sdr.samplesource.gr-osmosdr") {
|
||||
GNURadioGui* gui = new GNURadioGui(m_pluginAPI);
|
||||
m_pluginAPI->setInputGUI(gui);
|
||||
return gui;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef INCLUDE_GNURADIOPLUGIN_H
|
||||
#define INCLUDE_GNURADIOPLUGIN_H
|
||||
|
||||
#include <QObject>
|
||||
#include "plugin/plugininterface.h"
|
||||
|
||||
class GNURadioPlugin : public QObject, PluginInterface {
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(PluginInterface)
|
||||
Q_PLUGIN_METADATA(IID "org.osmocom.sdr.samplesource.gr-osmosdr")
|
||||
|
||||
public:
|
||||
explicit GNURadioPlugin(QObject* parent = NULL);
|
||||
|
||||
const PluginDescriptor& getPluginDescriptor() const;
|
||||
void initPlugin(PluginAPI* pluginAPI);
|
||||
|
||||
SampleSourceDevices enumSampleSources();
|
||||
PluginGUI* createSampleSource(const QString& sourceName, const QByteArray& address);
|
||||
|
||||
private:
|
||||
static const PluginDescriptor m_pluginDescriptor;
|
||||
|
||||
PluginAPI* m_pluginAPI;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_GNURADIOPLUGIN_H
|
||||
@@ -0,0 +1,144 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// Copyright (C) 2013 by Dimitri Stolnikov <horiz0n@gmx.net> //
|
||||
// //
|
||||
// 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 //
|
||||
// //
|
||||
// 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 <stdio.h>
|
||||
#include <errno.h>
|
||||
#include "gnuradiothread.h"
|
||||
#include "dsp/samplefifo.h"
|
||||
|
||||
#include <gnuradio/sync_block.h>
|
||||
#include <gnuradio/io_signature.h>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
class gr_adaptor;
|
||||
|
||||
typedef boost::shared_ptr< gr_adaptor > gr_adaptor_sptr;
|
||||
|
||||
gr_adaptor_sptr make_gr_adaptor (SampleFifo* sampleFifo);
|
||||
|
||||
class gr_adaptor : public gr::sync_block
|
||||
{
|
||||
public:
|
||||
gr_adaptor (SampleFifo* sampleFifo);
|
||||
~gr_adaptor ();
|
||||
|
||||
int work (int noutput_items,
|
||||
gr_vector_const_void_star &input_items,
|
||||
gr_vector_void_star &output_items);
|
||||
|
||||
private:
|
||||
SampleFifo *m_sampleFifo;
|
||||
};
|
||||
|
||||
gr_adaptor_sptr
|
||||
make_gr_adaptor (SampleFifo *sampleFifo)
|
||||
{
|
||||
return gr_adaptor_sptr (new gr_adaptor (sampleFifo));
|
||||
}
|
||||
|
||||
gr_adaptor::gr_adaptor (SampleFifo *sampleFifo)
|
||||
: gr::sync_block("gr_adaptor",
|
||||
gr::io_signature::make(1, 1, sizeof (gr_complex)),
|
||||
gr::io_signature::make(0, 0, 0)),
|
||||
m_sampleFifo(sampleFifo)
|
||||
{
|
||||
}
|
||||
|
||||
gr_adaptor::~gr_adaptor ()
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
gr_adaptor::work (int noutput_items,
|
||||
gr_vector_const_void_star &input_items,
|
||||
gr_vector_void_star &output_items)
|
||||
{
|
||||
const gr_complex *in = (const gr_complex *) input_items[0];
|
||||
|
||||
std::vector<qint16> buffer(noutput_items * 2, 0);
|
||||
std::vector<qint16>::iterator it = buffer.begin();
|
||||
|
||||
for (int i = 0; i < noutput_items; i++)
|
||||
{
|
||||
*it++ = in[i].real() * 32000;
|
||||
*it++ = in[i].imag() * 32000;
|
||||
}
|
||||
|
||||
// we must push at least 4 bytes into the fifo
|
||||
m_sampleFifo->write((const quint8*)buffer.data(),
|
||||
(it - buffer.begin()) * sizeof(qint16));
|
||||
|
||||
// Tell runtime system how many input items we consumed on
|
||||
// each input stream.
|
||||
consume_each(noutput_items);
|
||||
|
||||
// Tell runtime system how many output items we produced.
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
GnuradioThread::GnuradioThread(QString args, SampleFifo* sampleFifo, QObject* parent) :
|
||||
QThread(parent),
|
||||
m_running(false),
|
||||
m_args(args),
|
||||
m_sampleFifo(sampleFifo)
|
||||
{
|
||||
}
|
||||
|
||||
GnuradioThread::~GnuradioThread()
|
||||
{
|
||||
stopWork();
|
||||
}
|
||||
|
||||
void GnuradioThread::startWork()
|
||||
{
|
||||
m_startWaitMutex.lock();
|
||||
|
||||
start();
|
||||
while(!m_running)
|
||||
m_startWaiter.wait(&m_startWaitMutex, 100);
|
||||
|
||||
m_startWaitMutex.unlock();
|
||||
}
|
||||
|
||||
void GnuradioThread::stopWork()
|
||||
{
|
||||
m_running = false;
|
||||
|
||||
m_top->stop();
|
||||
|
||||
wait();
|
||||
}
|
||||
|
||||
void GnuradioThread::run()
|
||||
{
|
||||
m_top = gr::make_top_block( "flowgraph" );
|
||||
m_src = osmosdr::source::make( m_args.toStdString() );
|
||||
|
||||
/* now since we've constructed our shared objects, we allow the calling
|
||||
* thread to continue it's work and send some radio settings to us. */
|
||||
m_running = true;
|
||||
m_startWaiter.wakeAll();
|
||||
|
||||
gr_adaptor_sptr adaptor = make_gr_adaptor(m_sampleFifo);
|
||||
m_top->connect(m_src, 0, adaptor, 0);
|
||||
|
||||
m_top->run();
|
||||
|
||||
m_running = false;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// Copyright (C) 2013 by Dimitri Stolnikov <horiz0n@gmx.net> //
|
||||
// //
|
||||
// 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 //
|
||||
// //
|
||||
// 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 INCLUDE_GNURADIOTHREAD_H
|
||||
#define INCLUDE_GNURADIOTHREAD_H
|
||||
|
||||
#include <QThread>
|
||||
#include <QMutex>
|
||||
#include <QWaitCondition>
|
||||
|
||||
#include <gnuradio/top_block.h>
|
||||
#include <osmosdr/source.h>
|
||||
|
||||
class SampleFifo;
|
||||
|
||||
class GnuradioThread : public QThread {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GnuradioThread(QString args, SampleFifo* sampleFifo, QObject* parent = NULL);
|
||||
~GnuradioThread();
|
||||
|
||||
void startWork();
|
||||
void stopWork();
|
||||
|
||||
osmosdr::source::sptr radio() { return m_src; }
|
||||
|
||||
private:
|
||||
#pragma pack(push, 1)
|
||||
struct Sample {
|
||||
qint16 i;
|
||||
qint16 q;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
QMutex m_startWaitMutex;
|
||||
QWaitCondition m_startWaiter;
|
||||
bool m_running;
|
||||
|
||||
QString m_args;
|
||||
SampleFifo* m_sampleFifo;
|
||||
|
||||
gr::top_block_sptr m_top;
|
||||
osmosdr::source::sptr m_src;
|
||||
|
||||
void run();
|
||||
};
|
||||
|
||||
#endif // INCLUDE_GNURADIOTHREAD_H
|
||||
@@ -0,0 +1,54 @@
|
||||
project(osmosdr)
|
||||
|
||||
set(osmosdr_SOURCES
|
||||
osmosdrgui.cpp
|
||||
osmosdrinput.cpp
|
||||
osmosdrplugin.cpp
|
||||
osmosdrthread.cpp
|
||||
)
|
||||
|
||||
set(osmosdr_HEADERS
|
||||
osmosdrgui.h
|
||||
osmosdrinput.h
|
||||
osmosdrplugin.h
|
||||
osmosdrthread.h
|
||||
)
|
||||
|
||||
set(osmosdr_FORMS
|
||||
osmosdrgui.ui
|
||||
)
|
||||
|
||||
include_directories(
|
||||
.
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_SOURCE_DIR}/include
|
||||
${CMAKE_SOURCE_DIR}/include-gpl
|
||||
${LIBOSMOSDR_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
#include(${QT_USE_FILE})
|
||||
add_definitions(${QT_DEFINITIONS})
|
||||
add_definitions(-DQT_PLUGIN)
|
||||
add_definitions(-DQT_SHARED)
|
||||
|
||||
if(MSVC)
|
||||
add_definitions(-DNOMINMAX)
|
||||
endif()
|
||||
|
||||
#qt4_wrap_cpp(osmosdr_HEADERS_MOC ${osmosdr_HEADERS})
|
||||
qt5_wrap_ui(osmosdr_FORMS_HEADERS ${osmosdr_FORMS})
|
||||
|
||||
add_library(inputosmosdr SHARED
|
||||
${osmosdr_SOURCES}
|
||||
${osmosdr_HEADERS_MOC}
|
||||
${osmosdr_FORMS_HEADERS}
|
||||
)
|
||||
|
||||
target_link_libraries(inputosmosdr
|
||||
${QT_LIBRARIES}
|
||||
${LIBOSMOSDR_LIBRARIES}
|
||||
${LIBUSB_LIBRARIES}
|
||||
sdrbase
|
||||
)
|
||||
|
||||
qt5_use_modules(inputosmosdr Core Widgets OpenGL Multimedia)
|
||||
@@ -0,0 +1,310 @@
|
||||
#include "osmosdrgui.h"
|
||||
#include "ui_osmosdrgui.h"
|
||||
#include "plugin/pluginapi.h"
|
||||
|
||||
OsmoSDRGui::OsmoSDRGui(PluginAPI* pluginAPI, QWidget* parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::OsmoSDRGui),
|
||||
m_pluginAPI(pluginAPI),
|
||||
m_settings(),
|
||||
m_sampleSource(NULL)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->centerFrequency->setValueRange(7, 20000U, 2200000U);
|
||||
connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware()));
|
||||
displaySettings();
|
||||
|
||||
m_sampleSource = new OsmoSDRInput(m_pluginAPI->getMainWindowMessageQueue());
|
||||
m_pluginAPI->setSampleSource(m_sampleSource);
|
||||
}
|
||||
|
||||
OsmoSDRGui::~OsmoSDRGui()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void OsmoSDRGui::destroy()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void OsmoSDRGui::setName(const QString& name)
|
||||
{
|
||||
setObjectName(name);
|
||||
}
|
||||
|
||||
void OsmoSDRGui::resetToDefaults()
|
||||
{
|
||||
m_generalSettings.resetToDefaults();
|
||||
m_settings.resetToDefaults();
|
||||
displaySettings();
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
QByteArray OsmoSDRGui::serializeGeneral() const
|
||||
{
|
||||
return m_generalSettings.serialize();
|
||||
}
|
||||
|
||||
bool OsmoSDRGui::deserializeGeneral(const QByteArray&data)
|
||||
{
|
||||
if(m_generalSettings.deserialize(data)) {
|
||||
displaySettings();
|
||||
sendSettings();
|
||||
return true;
|
||||
} else {
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
quint64 OsmoSDRGui::getCenterFrequency() const
|
||||
{
|
||||
return m_generalSettings.m_centerFrequency;
|
||||
}
|
||||
|
||||
QByteArray OsmoSDRGui::serialize() const
|
||||
{
|
||||
return m_settings.serialize();
|
||||
}
|
||||
|
||||
bool OsmoSDRGui::deserialize(const QByteArray& data)
|
||||
{
|
||||
if(m_settings.deserialize(data)) {
|
||||
displaySettings();
|
||||
sendSettings();
|
||||
return true;
|
||||
} else {
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool OsmoSDRGui::handleMessage(Message* message)
|
||||
{
|
||||
return false;
|
||||
/*
|
||||
if(message->id() == OsmoSDRInput::MsgReportOsmoSDR::ID()) {
|
||||
m_gains = ((RTLSDRInput::MsgReportRTLSDR*)message)->getGains();
|
||||
displaySettings();
|
||||
message->completed();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}*/
|
||||
}
|
||||
#if 0
|
||||
|
||||
OsmoSDRGui::OsmoSDRGui(MessageQueue* msgQueue, QWidget* parent) :
|
||||
SampleSourceGUI(parent),
|
||||
ui(new Ui::OsmoSDRGui),
|
||||
m_msgQueue(msgQueue),
|
||||
m_settings()
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->centerFrequency->setValueRange(7, 20000U, 2200000U);
|
||||
connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware()));
|
||||
displaySettings();
|
||||
}
|
||||
|
||||
OsmoSDRGui::~OsmoSDRGui()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
QString OsmoSDRGui::serializeSettings() const
|
||||
{
|
||||
return m_settings.serialize();
|
||||
}
|
||||
|
||||
bool OsmoSDRGui::deserializeSettings(const QString& settings)
|
||||
{
|
||||
if(m_settings.deserialize(settings)) {
|
||||
displaySettings();
|
||||
sendSettings();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool OsmoSDRGui::handleSourceMessage(DSPCmdSourceToGUI* cmd)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
void OsmoSDRGui::displaySettings()
|
||||
{
|
||||
ui->centerFrequency->setValue(m_generalSettings.m_centerFrequency / 1000);
|
||||
ui->iqSwap->setChecked(m_settings.m_swapIQ);
|
||||
ui->decimation->setValue(m_settings.m_decimation);
|
||||
ui->e4000LNAGain->setValue(e4kLNAGainToIdx(m_settings.m_lnaGain));
|
||||
|
||||
ui->e4000MixerGain->setCurrentIndex((m_settings.m_mixerGain - 40) / 80);
|
||||
if(m_settings.m_mixerEnhancement == 0)
|
||||
ui->e4000MixerEnh->setCurrentIndex(0);
|
||||
else ui->e4000MixerEnh->setCurrentIndex((m_settings.m_mixerEnhancement + 10) / 20);
|
||||
|
||||
ui->e4000if1->setCurrentIndex((m_settings.m_if1gain + 30) / 90);
|
||||
ui->e4000if2->setCurrentIndex(m_settings.m_if2gain / 30);
|
||||
ui->e4000if3->setCurrentIndex(m_settings.m_if3gain / 30);
|
||||
ui->e4000if4->setCurrentIndex(m_settings.m_if4gain / 10);
|
||||
ui->e4000if5->setCurrentIndex(m_settings.m_if5gain / 30 - 1);
|
||||
ui->e4000if6->setCurrentIndex(m_settings.m_if6gain / 30 - 1);
|
||||
ui->filterI1->setValue(m_settings.m_opAmpI1);
|
||||
ui->filterI2->setValue(m_settings.m_opAmpI2);
|
||||
ui->filterQ1->setValue(m_settings.m_opAmpQ1);
|
||||
ui->filterQ2->setValue(m_settings.m_opAmpQ2);
|
||||
|
||||
ui->e4kI->setValue(m_settings.m_dcOfsI);
|
||||
ui->e4kQ->setValue(m_settings.m_dcOfsQ);
|
||||
}
|
||||
|
||||
void OsmoSDRGui::sendSettings()
|
||||
{
|
||||
if(!m_updateTimer.isActive())
|
||||
m_updateTimer.start(100);
|
||||
}
|
||||
|
||||
int OsmoSDRGui::e4kLNAGainToIdx(int gain) const
|
||||
{
|
||||
static const quint32 gainList[13] = {
|
||||
-50, -25, 0, 25, 50, 75, 100, 125, 150, 175, 200, 250, 300
|
||||
};
|
||||
for(int i = 0; i < 13; i++) {
|
||||
if(gainList[i] == gain)
|
||||
return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int OsmoSDRGui::e4kIdxToLNAGain(int idx) const
|
||||
{
|
||||
static const quint32 gainList[13] = {
|
||||
-50, -25, 0, 25, 50, 75, 100, 125, 150, 175, 200, 250, 300
|
||||
};
|
||||
if((idx < 0) || (idx >= 13))
|
||||
return -50;
|
||||
else return gainList[idx];
|
||||
}
|
||||
|
||||
void OsmoSDRGui::on_iqSwap_toggled(bool checked)
|
||||
{
|
||||
m_settings.m_swapIQ = checked;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void OsmoSDRGui::on_e4000MixerGain_currentIndexChanged(int index)
|
||||
{
|
||||
m_settings.m_mixerGain = index * 80 + 40;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void OsmoSDRGui::on_e4000MixerEnh_currentIndexChanged(int index)
|
||||
{
|
||||
if(index == 0)
|
||||
m_settings.m_mixerEnhancement = 0;
|
||||
else m_settings.m_mixerEnhancement = index * 20 - 10;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void OsmoSDRGui::on_e4000if1_currentIndexChanged(int index)
|
||||
{
|
||||
m_settings.m_if1gain = index * 90 - 30;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void OsmoSDRGui::on_e4000if2_currentIndexChanged(int index)
|
||||
{
|
||||
m_settings.m_if2gain = index * 30;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void OsmoSDRGui::on_e4000if3_currentIndexChanged(int index)
|
||||
{
|
||||
m_settings.m_if3gain = index * 30;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void OsmoSDRGui::on_e4000if4_currentIndexChanged(int index)
|
||||
{
|
||||
m_settings.m_if4gain = index * 10;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void OsmoSDRGui::on_e4000if5_currentIndexChanged(int index)
|
||||
{
|
||||
m_settings.m_if5gain = (index + 1) * 30;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void OsmoSDRGui::on_e4000if6_currentIndexChanged(int index)
|
||||
{
|
||||
m_settings.m_if6gain = (index + 1) * 30;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void OsmoSDRGui::on_centerFrequency_changed(quint64 value)
|
||||
{
|
||||
m_generalSettings.m_centerFrequency = value * 1000;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void OsmoSDRGui::on_filterI1_valueChanged(int value)
|
||||
{
|
||||
m_settings.m_opAmpI1 = value;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void OsmoSDRGui::on_filterI2_valueChanged(int value)
|
||||
{
|
||||
m_settings.m_opAmpI2 = value;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void OsmoSDRGui::on_filterQ1_valueChanged(int value)
|
||||
{
|
||||
m_settings.m_opAmpQ1 = value;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void OsmoSDRGui::on_filterQ2_valueChanged(int value)
|
||||
{
|
||||
m_settings.m_opAmpQ2 = value;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void OsmoSDRGui::on_decimation_valueChanged(int value)
|
||||
{
|
||||
ui->decimationDisplay->setText(tr("1:%1").arg(1 << value));
|
||||
m_settings.m_decimation = value;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void OsmoSDRGui::on_e4000LNAGain_valueChanged(int value)
|
||||
{
|
||||
int gain = e4kIdxToLNAGain(value);
|
||||
ui->e4000LNAGainDisplay->setText(tr("%1.%2").arg(gain / 10).arg(abs(gain % 10)));
|
||||
m_settings.m_lnaGain = gain;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void OsmoSDRGui::on_e4kI_valueChanged(int value)
|
||||
{
|
||||
m_settings.m_dcOfsI = value;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void OsmoSDRGui::on_e4kQ_valueChanged(int value)
|
||||
{
|
||||
m_settings.m_dcOfsQ = value;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void OsmoSDRGui::updateHardware()
|
||||
{
|
||||
m_updateTimer.stop();
|
||||
Message* msg = OsmoSDRInput::MsgConfigureOsmoSDR::create(m_generalSettings, m_settings);
|
||||
msg->submit(m_pluginAPI->getDSPEngineMessageQueue());
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
#ifndef INCLUDE_OSMOSDRGUI_H
|
||||
#define INCLUDE_OSMOSDRGUI_H
|
||||
|
||||
#include <QTimer>
|
||||
#include "plugin/plugingui.h"
|
||||
#include "osmosdrinput.h"
|
||||
|
||||
class PluginAPI;
|
||||
|
||||
namespace Ui {
|
||||
class OsmoSDRGui;
|
||||
}
|
||||
|
||||
class OsmoSDRGui : public QWidget, public PluginGUI {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit OsmoSDRGui(PluginAPI* pluginAPI, QWidget* parent = NULL);
|
||||
~OsmoSDRGui();
|
||||
void destroy();
|
||||
|
||||
void setName(const QString& name);
|
||||
|
||||
void resetToDefaults();
|
||||
QByteArray serializeGeneral() const;
|
||||
bool deserializeGeneral(const QByteArray&data);
|
||||
quint64 getCenterFrequency() const;
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
bool handleMessage(Message* message);
|
||||
|
||||
private:
|
||||
Ui::OsmoSDRGui* ui;
|
||||
|
||||
PluginAPI* m_pluginAPI;
|
||||
SampleSource::GeneralSettings m_generalSettings;
|
||||
OsmoSDRInput::Settings m_settings;
|
||||
QTimer m_updateTimer;
|
||||
std::vector<int> m_gains;
|
||||
SampleSource* m_sampleSource;
|
||||
|
||||
void displaySettings();
|
||||
void sendSettings();
|
||||
int e4kLNAGainToIdx(int gain) const;
|
||||
int e4kIdxToLNAGain(int idx) const;
|
||||
|
||||
private slots:
|
||||
void on_iqSwap_toggled(bool checked);
|
||||
void on_e4000MixerGain_currentIndexChanged(int index);
|
||||
void on_e4000MixerEnh_currentIndexChanged(int index);
|
||||
void on_e4000if1_currentIndexChanged(int index);
|
||||
void on_e4000if2_currentIndexChanged(int index);
|
||||
void on_e4000if3_currentIndexChanged(int index);
|
||||
void on_e4000if4_currentIndexChanged(int index);
|
||||
void on_e4000if5_currentIndexChanged(int index);
|
||||
void on_e4000if6_currentIndexChanged(int index);
|
||||
void on_centerFrequency_changed(quint64 value);
|
||||
void on_filterI1_valueChanged(int value);
|
||||
void on_filterI2_valueChanged(int value);
|
||||
void on_filterQ1_valueChanged(int value);
|
||||
void on_filterQ2_valueChanged(int value);
|
||||
void on_decimation_valueChanged(int value);
|
||||
void on_e4000LNAGain_valueChanged(int value);
|
||||
void on_e4kI_valueChanged(int value);
|
||||
void on_e4kQ_valueChanged(int value);
|
||||
|
||||
void updateHardware();
|
||||
};
|
||||
|
||||
#endif // INCLUDE_OSMOSDRGUI_H
|
||||
@@ -0,0 +1,770 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>OsmoSDRGui</class>
|
||||
<widget class="QWidget" name="OsmoSDRGui">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>253</width>
|
||||
<height>229</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>OsmoSDR</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<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>Monospace</family>
|
||||
<pointsize>20</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Tuner center frequency in kHz</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<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>
|
||||
<widget class="Line" name="line_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_13">
|
||||
<property name="text">
|
||||
<string>Decimation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="decimation">
|
||||
<property name="toolTip">
|
||||
<string>Signal decimation factor</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="tickPosition">
|
||||
<enum>QSlider::TicksBelow</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="decimationDisplay">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>1:1</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_11">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>LNA</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="e4000LNAGain">
|
||||
<property name="toolTip">
|
||||
<string>LNA amplification</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="e4000LNAGainDisplay">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>-5.0</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<property name="leftMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_19">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Mix</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="e4000MixerGain">
|
||||
<property name="toolTip">
|
||||
<string>Tuner mixer amplification</string>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QComboBox::AdjustToMinimumContentsLength</enum>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>4</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>12</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="label_20">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enh.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="e4000MixerEnh">
|
||||
<property name="toolTip">
|
||||
<string>Tuner LNA amplification enhancement</string>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QComboBox::AdjustToMinimumContentsLength</enum>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>off</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>1</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>3</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>5</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>7</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="iqSwap">
|
||||
<property name="text">
|
||||
<string>Swap IQ</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="e4000if2">
|
||||
<property name="toolTip">
|
||||
<string>Tuner IF 2 amplification</string>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QComboBox::AdjustToContents</enum>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>3</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>6</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>9</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="QComboBox" name="e4000if4">
|
||||
<property name="toolTip">
|
||||
<string>Tuner IF 4 amplification</string>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QComboBox::AdjustToContents</enum>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>1</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>2</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QLabel" name="label_18">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>IF4</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="4">
|
||||
<widget class="QLabel" name="label_25">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>IF5</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="5">
|
||||
<widget class="QComboBox" name="e4000if5">
|
||||
<property name="toolTip">
|
||||
<string>Tuner IF 5 amplification</string>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QComboBox::AdjustToContents</enum>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>3</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>6</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>9</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>12</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>15</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="4">
|
||||
<widget class="QLabel" name="label_26">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>IF6</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QComboBox" name="e4000if3">
|
||||
<property name="toolTip">
|
||||
<string>Tuner IF 3 amplification</string>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QComboBox::AdjustToContents</enum>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>3</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>6</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>9</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_23">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>IF2</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="5">
|
||||
<widget class="QComboBox" name="e4000if6">
|
||||
<property name="toolTip">
|
||||
<string>Tuner IF 6 amplification</string>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QComboBox::AdjustToContents</enum>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>3</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>6</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>9</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>12</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>15</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QLabel" name="label_24">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>IF3</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="e4000if1">
|
||||
<property name="toolTip">
|
||||
<string>Tuner IF 1 amplification</string>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QComboBox::AdjustToContents</enum>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>-3</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>6</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_12">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>IF1</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item row="0" column="2">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Poti 2</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>I</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QSlider" name="filterI2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>I DC offset adjustment</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>255</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QSlider" name="filterI1">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>I path amplification</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>255</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QSlider" name="filterQ1">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Q path amplification</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>255</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QSlider" name="filterQ2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Q DC offset adjustment</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>255</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="3">
|
||||
<widget class="QSlider" name="e4kQ">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Q DC offset adjustment</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>255</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>E4000</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Q</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Poti 1</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="QSlider" name="e4kI">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Q path amplification</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>255</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>ValueDial</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/valuedial.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,479 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// //
|
||||
// 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 //
|
||||
// //
|
||||
// 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 <string.h>
|
||||
#include <errno.h>
|
||||
#include "osmosdrinput.h"
|
||||
#include "osmosdrthread.h"
|
||||
#include "osmosdrgui.h"
|
||||
#include "util/simpleserializer.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(OsmoSDRInput::MsgConfigureOsmoSDR, Message)
|
||||
|
||||
OsmoSDRInput::Settings::Settings() :
|
||||
m_swapIQ(false),
|
||||
m_decimation(3),
|
||||
m_lnaGain(-50),
|
||||
m_mixerGain(40),
|
||||
m_mixerEnhancement(0),
|
||||
m_if1gain(-30),
|
||||
m_if2gain(0),
|
||||
m_if3gain(0),
|
||||
m_if4gain(0),
|
||||
m_if5gain(30),
|
||||
m_if6gain(30),
|
||||
m_opAmpI1(0),
|
||||
m_opAmpI2(0),
|
||||
m_opAmpQ1(0),
|
||||
m_opAmpQ2(0),
|
||||
m_dcOfsI(0),
|
||||
m_dcOfsQ(0)
|
||||
{
|
||||
}
|
||||
|
||||
void OsmoSDRInput::Settings::resetToDefaults()
|
||||
{
|
||||
m_swapIQ = false;
|
||||
m_decimation = 3;
|
||||
m_lnaGain = -50;
|
||||
m_mixerGain = 40;
|
||||
m_mixerEnhancement = 0;
|
||||
m_if1gain = -30;
|
||||
m_if2gain = 0;
|
||||
m_if3gain = 0;
|
||||
m_if4gain = 0;
|
||||
m_if5gain = 30;
|
||||
m_if6gain = 30;
|
||||
m_opAmpI1 = 0;
|
||||
m_opAmpI2 = 0;
|
||||
m_opAmpQ1 = 0;
|
||||
m_opAmpQ2 = 0;
|
||||
m_dcOfsI = 0;
|
||||
m_dcOfsQ = 0;
|
||||
}
|
||||
|
||||
QByteArray OsmoSDRInput::Settings::serialize() const
|
||||
{
|
||||
SimpleSerializer s(1);
|
||||
s.writeBool(1, m_swapIQ);
|
||||
s.writeS32(2, m_decimation);
|
||||
s.writeS32(3, m_lnaGain);
|
||||
s.writeS32(4, m_mixerGain);
|
||||
s.writeS32(5, m_mixerEnhancement);
|
||||
s.writeS32(6, m_if1gain);
|
||||
s.writeS32(7, m_if2gain);
|
||||
s.writeS32(8, m_if3gain);
|
||||
s.writeS32(9, m_if4gain);
|
||||
s.writeS32(10, m_if5gain);
|
||||
s.writeS32(11, m_if6gain);
|
||||
s.writeS32(12, m_opAmpI1);
|
||||
s.writeS32(13, m_opAmpI2);
|
||||
s.writeS32(14, m_opAmpQ1);
|
||||
s.writeS32(15, m_opAmpQ2);
|
||||
s.writeS32(16, m_dcOfsI);
|
||||
s.writeS32(17, m_dcOfsQ);
|
||||
return s.final();
|
||||
}
|
||||
|
||||
bool OsmoSDRInput::Settings::deserialize(const QByteArray& data)
|
||||
{
|
||||
SimpleDeserializer d(data);
|
||||
|
||||
if(!d.isValid()) {
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(d.getVersion() == 1) {
|
||||
d.readBool(1, &m_swapIQ, false);
|
||||
d.readS32(2, &m_decimation, 3);
|
||||
d.readS32(3, &m_lnaGain, -50);
|
||||
d.readS32(4, &m_mixerGain, 40);
|
||||
d.readS32(5, &m_mixerEnhancement, 0);
|
||||
d.readS32(6, &m_if1gain, -30);
|
||||
d.readS32(7, &m_if2gain, 0);
|
||||
d.readS32(8, &m_if3gain, 0);
|
||||
d.readS32(9, &m_if4gain, 0);
|
||||
d.readS32(10, &m_if5gain, 30);
|
||||
d.readS32(11, &m_if6gain, 30);
|
||||
d.readS32(12, &m_opAmpI1, 0);
|
||||
d.readS32(13, &m_opAmpI2, 0);
|
||||
d.readS32(14, &m_opAmpQ1, 0);
|
||||
d.readS32(15, &m_opAmpQ2, 0);
|
||||
d.readS32(16, &m_dcOfsI, 0);
|
||||
d.readS32(17, &m_dcOfsQ, 0);
|
||||
return true;
|
||||
} else {
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
OsmoSDRInput::Settings::Settings() :
|
||||
{
|
||||
}
|
||||
|
||||
QString OsmoSDRInput::Settings::serialize() const
|
||||
{
|
||||
return QString("osmosdr:a:%1:%2:%3:%4:%5:%6:%7:%8:%9:%10:%11:%12:%13:%14:%15:%16:%17:%18")
|
||||
.arg(centerFrequency)
|
||||
.arg(swapIQ ? 1 : 0)
|
||||
.arg(decimation)
|
||||
.arg(lnaGain)
|
||||
.arg(mixerGain)
|
||||
.arg(mixerEnhancement)
|
||||
.arg(if1gain)
|
||||
.arg(if2gain)
|
||||
.arg(if3gain)
|
||||
.arg(if4gain)
|
||||
.arg(if5gain)
|
||||
.arg(if6gain)
|
||||
.arg(opAmpI1)
|
||||
.arg(opAmpI2)
|
||||
.arg(opAmpQ1)
|
||||
.arg(opAmpQ2)
|
||||
.arg(dcOfsI)
|
||||
.arg(dcOfsQ);
|
||||
}
|
||||
|
||||
bool OsmoSDRInput::Settings::deserialize(const QString& settings)
|
||||
{
|
||||
QStringList list = settings.split(":");
|
||||
if(list.size() < 2)
|
||||
return false;
|
||||
if(list[0] != "osmosdr")
|
||||
return false;
|
||||
|
||||
if(list[1] == "a") {
|
||||
bool ok;
|
||||
if(list.size() != 20)
|
||||
return false;
|
||||
centerFrequency = list[2].toLongLong(&ok);
|
||||
if(!ok)
|
||||
return false;
|
||||
swapIQ = (list[3].toInt(&ok) != 0) ? true : false;
|
||||
if(!ok)
|
||||
return false;
|
||||
decimation = list[4].toInt(&ok);
|
||||
if(!ok)
|
||||
return false;
|
||||
lnaGain = list[5].toInt(&ok);
|
||||
if(!ok)
|
||||
return false;
|
||||
mixerGain = list[6].toInt(&ok);
|
||||
if(!ok)
|
||||
return false;
|
||||
mixerEnhancement = list[7].toInt(&ok);
|
||||
if(!ok)
|
||||
return false;
|
||||
if1gain = list[8].toInt(&ok);
|
||||
if(!ok)
|
||||
return false;
|
||||
if2gain = list[9].toInt(&ok);
|
||||
if(!ok)
|
||||
return false;
|
||||
if3gain = list[10].toInt(&ok);
|
||||
if(!ok)
|
||||
return false;
|
||||
if4gain = list[11].toInt(&ok);
|
||||
if(!ok)
|
||||
return false;
|
||||
if5gain = list[12].toInt(&ok);
|
||||
if(!ok)
|
||||
return false;
|
||||
if6gain = list[13].toInt(&ok);
|
||||
if(!ok)
|
||||
return false;
|
||||
opAmpI1 = list[14].toInt(&ok);
|
||||
if(!ok)
|
||||
return false;
|
||||
opAmpI2 = list[15].toInt(&ok);
|
||||
if(!ok)
|
||||
return false;
|
||||
opAmpQ1 = list[16].toInt(&ok);
|
||||
if(!ok)
|
||||
return false;
|
||||
opAmpQ2 = list[17].toInt(&ok);
|
||||
if(!ok)
|
||||
return false;
|
||||
dcOfsI = list[18].toInt(&ok);
|
||||
if(!ok)
|
||||
return false;
|
||||
dcOfsQ = list[19].toInt(&ok);
|
||||
if(!ok)
|
||||
return false;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
MessageRegistrator OsmoSDRInput::MsgConfigureSourceOsmoSDR::ID("MsgConfigureSourceOsmoSDR");
|
||||
#endif
|
||||
OsmoSDRInput::OsmoSDRInput(MessageQueue* msgQueueToGUI) :
|
||||
SampleSource(msgQueueToGUI),
|
||||
m_settings(),
|
||||
m_dev(NULL),
|
||||
m_osmoSDRThread(NULL),
|
||||
m_deviceDescription()
|
||||
{
|
||||
}
|
||||
|
||||
OsmoSDRInput::~OsmoSDRInput()
|
||||
{
|
||||
stopInput();
|
||||
}
|
||||
|
||||
bool OsmoSDRInput::startInput(int device)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
if(m_dev != NULL)
|
||||
stopInput();
|
||||
|
||||
char vendor[256];
|
||||
char product[256];
|
||||
char serial[256];
|
||||
int res;
|
||||
|
||||
if(!m_sampleFifo.setSize(524288)) {
|
||||
qCritical("Could not allocate SampleFifo");
|
||||
return false;
|
||||
}
|
||||
|
||||
if((res = osmosdr_open(&m_dev, device)) < 0) {
|
||||
qCritical("could not open OsmoSDR #%d: %s", device, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
vendor[0] = '\0';
|
||||
product[0] = '\0';
|
||||
serial[0] = '\0';
|
||||
if((res = osmosdr_get_usb_strings(m_dev, vendor, product, serial)) < 0) {
|
||||
qCritical("error accessing USB device");
|
||||
goto failed;
|
||||
}
|
||||
qDebug("OsmoSDRInput open: %s %s, SN: %s", vendor, product, serial);
|
||||
m_deviceDescription = QString("%1 (SN %2)").arg(product).arg(serial);
|
||||
|
||||
if((res = osmosdr_set_tuner_gain_mode(m_dev, 1)) < 0) {
|
||||
qCritical("error setting tuner gain mode");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if((res = osmosdr_reset_buffer(m_dev)) < 0) {
|
||||
qCritical("could not reset USB EP buffers: %s", strerror(errno));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if((m_osmoSDRThread = new OsmoSDRThread(m_dev, &m_sampleFifo)) == NULL) {
|
||||
qFatal("out of memory");
|
||||
goto failed;
|
||||
}
|
||||
m_osmoSDRThread->startWork();
|
||||
|
||||
mutexLocker.unlock();
|
||||
applySettings(m_generalSettings, m_settings, true);
|
||||
|
||||
qDebug("OsmoSDRInput: start");
|
||||
|
||||
return true;
|
||||
|
||||
failed:
|
||||
stopInput();
|
||||
return false;
|
||||
}
|
||||
|
||||
void OsmoSDRInput::stopInput()
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
if(m_osmoSDRThread != NULL) {
|
||||
m_osmoSDRThread->stopWork();
|
||||
delete m_osmoSDRThread;
|
||||
m_osmoSDRThread = NULL;
|
||||
}
|
||||
if(m_dev != NULL) {
|
||||
osmosdr_close(m_dev);
|
||||
m_dev = NULL;
|
||||
}
|
||||
m_deviceDescription.clear();
|
||||
}
|
||||
|
||||
const QString& OsmoSDRInput::getDeviceDescription() const
|
||||
{
|
||||
return m_deviceDescription;
|
||||
}
|
||||
|
||||
int OsmoSDRInput::getSampleRate() const
|
||||
{
|
||||
return 4000000 / (1 << m_settings.m_decimation);
|
||||
}
|
||||
|
||||
quint64 OsmoSDRInput::getCenterFrequency() const
|
||||
{
|
||||
return m_generalSettings.m_centerFrequency;
|
||||
}
|
||||
|
||||
bool OsmoSDRInput::handleMessage(Message* message)
|
||||
{
|
||||
if(MsgConfigureOsmoSDR::match(message)) {
|
||||
MsgConfigureOsmoSDR* conf = (MsgConfigureOsmoSDR*)message;
|
||||
if(!applySettings(conf->getGeneralSettings(), conf->getSettings(), false))
|
||||
qDebug("OsmoSDR config error");
|
||||
message->completed();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
if(cmd->sourceType() != DSPCmdConfigureSourceOsmoSDR::SourceType)
|
||||
return false;
|
||||
if(!applySettings(((DSPCmdConfigureSourceOsmoSDR*)cmd)->getSettings(), false))
|
||||
qDebug("OsmoSDR config error");
|
||||
cmd->completed();
|
||||
return true;
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OsmoSDRInput::applySettings(const GeneralSettings& generalSettings, const Settings& settings, bool force)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
if((m_generalSettings.m_centerFrequency != generalSettings.m_centerFrequency) || force) {
|
||||
m_generalSettings.m_centerFrequency = generalSettings.m_centerFrequency;
|
||||
if(m_dev != NULL) {
|
||||
if(osmosdr_set_center_freq(m_dev, m_generalSettings.m_centerFrequency) != 0)
|
||||
qDebug("osmosdr_set_center_freq(%lld) failed", m_generalSettings.m_centerFrequency);
|
||||
}
|
||||
}
|
||||
|
||||
if((m_settings.m_swapIQ != settings.m_swapIQ) || force) {
|
||||
m_settings.m_swapIQ = settings.m_swapIQ;
|
||||
if(m_dev != NULL) {
|
||||
if(osmosdr_set_fpga_iq_swap(m_dev, m_settings.m_swapIQ ? 1 : 0) == 0)
|
||||
qDebug("osmosdr_set_fpga_iq_swap() failed");
|
||||
}
|
||||
}
|
||||
|
||||
if((m_settings.m_decimation != settings.m_decimation) || force) {
|
||||
m_settings.m_decimation = settings.m_decimation;
|
||||
if(m_dev != NULL) {
|
||||
if(!osmosdr_set_fpga_decimation(m_dev, m_settings.m_decimation))
|
||||
qDebug("osmosdr_set_fpga_decimation() failed");
|
||||
}
|
||||
}
|
||||
|
||||
if((m_settings.m_lnaGain != settings.m_lnaGain) || force) {
|
||||
m_settings.m_lnaGain = settings.m_lnaGain;
|
||||
if(m_dev != NULL) {
|
||||
if(!osmosdr_set_tuner_lna_gain(m_dev, m_settings.m_lnaGain))
|
||||
qDebug("osmosdr_set_tuner_lna_gain() failed");
|
||||
}
|
||||
}
|
||||
|
||||
if((m_settings.m_mixerGain != settings.m_mixerGain) || force) {
|
||||
m_settings.m_mixerGain = settings.m_mixerGain;
|
||||
if(m_dev != NULL) {
|
||||
if(!osmosdr_set_tuner_mixer_gain(m_dev, m_settings.m_mixerGain))
|
||||
qDebug("osmosdr_set_tuner_mixer_gain() failed");
|
||||
}
|
||||
}
|
||||
|
||||
if((m_settings.m_mixerEnhancement != settings.m_mixerEnhancement) || force) {
|
||||
m_settings.m_mixerEnhancement = settings.m_mixerEnhancement;
|
||||
if(m_dev != NULL) {
|
||||
if(!osmosdr_set_tuner_mixer_enh(m_dev, m_settings.m_mixerEnhancement))
|
||||
qDebug("osmosdr_set_tuner_mixer_enh() failed");
|
||||
}
|
||||
}
|
||||
|
||||
if((m_settings.m_if1gain != settings.m_if1gain) || force) {
|
||||
m_settings.m_if1gain = settings.m_if1gain;
|
||||
if(m_dev != NULL) {
|
||||
if(!osmosdr_set_tuner_if_gain(m_dev, 1, m_settings.m_if1gain))
|
||||
qDebug("osmosdr_set_tuner_if_gain(1) failed");
|
||||
}
|
||||
}
|
||||
|
||||
if((m_settings.m_if2gain != settings.m_if2gain) || force) {
|
||||
m_settings.m_if2gain = settings.m_if2gain;
|
||||
if(m_dev != NULL) {
|
||||
if(!osmosdr_set_tuner_if_gain(m_dev, 2, m_settings.m_if2gain))
|
||||
qDebug("osmosdr_set_tuner_if_gain(2) failed");
|
||||
}
|
||||
}
|
||||
|
||||
if((m_settings.m_if3gain != settings.m_if3gain) || force) {
|
||||
m_settings.m_if3gain = settings.m_if3gain;
|
||||
if(m_dev != NULL) {
|
||||
if(!osmosdr_set_tuner_if_gain(m_dev, 3, m_settings.m_if3gain))
|
||||
qDebug("osmosdr_set_tuner_if_gain(3) failed");
|
||||
}
|
||||
}
|
||||
|
||||
if((m_settings.m_if4gain != settings.m_if4gain) || force) {
|
||||
m_settings.m_if4gain = settings.m_if4gain;
|
||||
if(m_dev != NULL) {
|
||||
if(!osmosdr_set_tuner_if_gain(m_dev, 4, m_settings.m_if4gain))
|
||||
qDebug("osmosdr_set_tuner_if_gain(4) failed");
|
||||
}
|
||||
}
|
||||
|
||||
if((m_settings.m_if5gain != settings.m_if5gain) || force) {
|
||||
m_settings.m_if5gain = settings.m_if5gain;
|
||||
if(m_dev != NULL) {
|
||||
if(!osmosdr_set_tuner_if_gain(m_dev, 5, m_settings.m_if5gain))
|
||||
qDebug("osmosdr_set_tuner_if_gain(5) failed");
|
||||
}
|
||||
}
|
||||
|
||||
if((m_settings.m_if6gain != settings.m_if6gain) || force) {
|
||||
m_settings.m_if6gain = settings.m_if6gain;
|
||||
if(m_dev != NULL) {
|
||||
if(!osmosdr_set_tuner_if_gain(m_dev, 6, m_settings.m_if6gain))
|
||||
qDebug("osmosdr_set_tuner_if_gain(6) failed");
|
||||
}
|
||||
}
|
||||
|
||||
if((m_settings.m_opAmpI1 != settings.m_opAmpI1) || (m_settings.m_opAmpI2 != settings.m_opAmpI2) ||
|
||||
(m_settings.m_opAmpQ1 != settings.m_opAmpQ1) || (m_settings.m_opAmpQ2 != settings.m_opAmpQ2) ||
|
||||
force) {
|
||||
m_settings.m_opAmpI1 = settings.m_opAmpI1;
|
||||
m_settings.m_opAmpI2 = settings.m_opAmpI2;
|
||||
m_settings.m_opAmpQ1 = settings.m_opAmpQ1;
|
||||
m_settings.m_opAmpQ2 = settings.m_opAmpQ2;
|
||||
if(m_dev != NULL) {
|
||||
if(!osmosdr_set_iq_amp(m_dev, m_settings.m_opAmpI1, m_settings.m_opAmpI2, m_settings.m_opAmpQ1, m_settings.m_opAmpQ2))
|
||||
qDebug("osmosdr_set_iq_amp(1) failed");
|
||||
}
|
||||
}
|
||||
|
||||
if((m_settings.m_dcOfsI != settings.m_dcOfsI) || (m_settings.m_dcOfsQ != settings.m_dcOfsQ) ||
|
||||
force) {
|
||||
m_settings.m_dcOfsI = settings.m_dcOfsI;
|
||||
m_settings.m_dcOfsQ = settings.m_dcOfsQ;
|
||||
if(m_dev != NULL) {
|
||||
if(!osmosdr_set_tuner_dc_offset(m_dev, m_settings.m_dcOfsI, m_settings.m_dcOfsQ))
|
||||
qDebug("osmosdr_set_tuner_dc_offset() failed");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// //
|
||||
// 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 //
|
||||
// //
|
||||
// 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 INCLUDE_OSMOSDRINPUT_H
|
||||
#define INCLUDE_OSMOSDRINPUT_H
|
||||
|
||||
#include "dsp/samplesource/samplesource.h"
|
||||
#include <osmosdr.h>
|
||||
#include <QString>
|
||||
|
||||
class OsmoSDRThread;
|
||||
|
||||
class OsmoSDRInput : public SampleSource {
|
||||
public:
|
||||
struct Settings {
|
||||
bool m_swapIQ;
|
||||
qint32 m_decimation;
|
||||
qint32 m_lnaGain;
|
||||
qint32 m_mixerGain;
|
||||
qint32 m_mixerEnhancement;
|
||||
qint32 m_if1gain;
|
||||
qint32 m_if2gain;
|
||||
qint32 m_if3gain;
|
||||
qint32 m_if4gain;
|
||||
qint32 m_if5gain;
|
||||
qint32 m_if6gain;
|
||||
qint32 m_opAmpI1;
|
||||
qint32 m_opAmpI2;
|
||||
qint32 m_opAmpQ1;
|
||||
qint32 m_opAmpQ2;
|
||||
qint32 m_dcOfsI;
|
||||
qint32 m_dcOfsQ;
|
||||
|
||||
Settings();
|
||||
void resetToDefaults();
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
};
|
||||
|
||||
class MsgConfigureOsmoSDR : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
const GeneralSettings& getGeneralSettings() const { return m_generalSettings; }
|
||||
const Settings& getSettings() const { return m_settings; }
|
||||
|
||||
static MsgConfigureOsmoSDR* create(const GeneralSettings& generalSettings, const Settings& settings)
|
||||
{
|
||||
return new MsgConfigureOsmoSDR(generalSettings, settings);
|
||||
}
|
||||
|
||||
protected:
|
||||
GeneralSettings m_generalSettings;
|
||||
Settings m_settings;
|
||||
|
||||
MsgConfigureOsmoSDR(const GeneralSettings& generalSettings, const Settings& settings) :
|
||||
Message(),
|
||||
m_generalSettings(generalSettings),
|
||||
m_settings(settings)
|
||||
{ }
|
||||
};
|
||||
|
||||
OsmoSDRInput(MessageQueue* msgQueueToGUI);
|
||||
~OsmoSDRInput();
|
||||
|
||||
bool startInput(int device);
|
||||
void stopInput();
|
||||
|
||||
const QString& getDeviceDescription() const;
|
||||
int getSampleRate() const;
|
||||
quint64 getCenterFrequency() const;
|
||||
|
||||
bool handleMessage(Message* message);
|
||||
|
||||
private:
|
||||
QMutex m_mutex;
|
||||
Settings m_settings;
|
||||
osmosdr_dev_t* m_dev;
|
||||
OsmoSDRThread* m_osmoSDRThread;
|
||||
QString m_deviceDescription;
|
||||
|
||||
bool applySettings(const GeneralSettings& generalSettings, const Settings& settings, bool force);
|
||||
};
|
||||
|
||||
#endif // INCLUDE_OSMOSDRINPUT_H
|
||||
@@ -0,0 +1,69 @@
|
||||
#include <QtPlugin>
|
||||
#include <QAction>
|
||||
#include <osmosdr.h>
|
||||
#include "plugin/pluginapi.h"
|
||||
#include "util/simpleserializer.h"
|
||||
#include "osmosdrplugin.h"
|
||||
#include "osmosdrgui.h"
|
||||
|
||||
const PluginDescriptor OsmoSDRPlugin::m_pluginDescriptor = {
|
||||
QString("OsmoSDR Input"),
|
||||
QString("---"),
|
||||
QString("(c) Christian Daniel"),
|
||||
QString("http://sdr.osmocom.org/trac/wiki/osmo-sdr"),
|
||||
true,
|
||||
QString("http://cgit.osmocom.org/cgit/osmo-sdr")
|
||||
};
|
||||
|
||||
OsmoSDRPlugin::OsmoSDRPlugin(QObject* parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
const PluginDescriptor& OsmoSDRPlugin::getPluginDescriptor() const
|
||||
{
|
||||
return m_pluginDescriptor;
|
||||
}
|
||||
|
||||
void OsmoSDRPlugin::initPlugin(PluginAPI* pluginAPI)
|
||||
{
|
||||
m_pluginAPI = pluginAPI;
|
||||
|
||||
m_pluginAPI->registerSampleSource("org.osmocom.sdr.samplesource.osmo-sdr", this);
|
||||
}
|
||||
|
||||
PluginInterface::SampleSourceDevices OsmoSDRPlugin::enumSampleSources()
|
||||
{
|
||||
SampleSourceDevices result;
|
||||
int count = osmosdr_get_device_count();
|
||||
char vendor[256];
|
||||
char product[256];
|
||||
char serial[256];
|
||||
|
||||
for(int i = 0; i < count; i++) {
|
||||
vendor[0] = '\0';
|
||||
product[0] = '\0';
|
||||
serial[0] = '\0';
|
||||
|
||||
if(osmosdr_get_device_usb_strings(i, vendor, product, serial) != 0)
|
||||
continue;
|
||||
QString displayedName(QString("OsmoSDR #%1 (#%2)").arg(i + 1).arg(serial));
|
||||
qDebug("found %s", qPrintable(displayedName));
|
||||
SimpleSerializer s(1);
|
||||
s.writeS32(1, i);
|
||||
result.append(SampleSourceDevice(displayedName, "org.osmocom.sdr.samplesource.osmo-sdr", s.final()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
PluginGUI* OsmoSDRPlugin::createSampleSource(const QString& sourceName, const QByteArray& address)
|
||||
{
|
||||
if(sourceName == "org.osmocom.sdr.samplesource.osmo-sdr") {
|
||||
OsmoSDRGui* gui = new OsmoSDRGui(m_pluginAPI);
|
||||
m_pluginAPI->setInputGUI(gui);
|
||||
return gui;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef INCLUDE_OSMOSDRPLUGIN_H
|
||||
#define INCLUDE_OSMOSDRPLUGIN_H
|
||||
|
||||
#include <QObject>
|
||||
#include "plugin/plugininterface.h"
|
||||
|
||||
class OsmoSDRPlugin : public QObject, PluginInterface {
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(PluginInterface)
|
||||
Q_PLUGIN_METADATA(IID "org.osmocom.sdr.samplesource.osmo-sdr")
|
||||
|
||||
public:
|
||||
explicit OsmoSDRPlugin(QObject* parent = NULL);
|
||||
|
||||
const PluginDescriptor& getPluginDescriptor() const;
|
||||
void initPlugin(PluginAPI* pluginAPI);
|
||||
|
||||
SampleSourceDevices enumSampleSources();
|
||||
PluginGUI* createSampleSource(const QString& sourceName, const QByteArray& address);
|
||||
|
||||
private:
|
||||
static const PluginDescriptor m_pluginDescriptor;
|
||||
|
||||
PluginAPI* m_pluginAPI;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_OSMOSDRPLUGIN_H
|
||||
@@ -0,0 +1,116 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// //
|
||||
// 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 //
|
||||
// //
|
||||
// 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 <stdio.h>
|
||||
#include <errno.h>
|
||||
#include "osmosdrthread.h"
|
||||
#include "dsp/samplefifo.h"
|
||||
|
||||
OsmoSDRThread::OsmoSDRThread(osmosdr_dev_t* dev, SampleFifo* sampleFifo, QObject* parent) :
|
||||
QThread(parent),
|
||||
m_running(false),
|
||||
m_dev(dev),
|
||||
m_sampleFifo(sampleFifo)
|
||||
{
|
||||
}
|
||||
|
||||
OsmoSDRThread::~OsmoSDRThread()
|
||||
{
|
||||
stopWork();
|
||||
}
|
||||
|
||||
void OsmoSDRThread::startWork()
|
||||
{
|
||||
m_startWaitMutex.lock();
|
||||
start();
|
||||
while(!m_running)
|
||||
m_startWaiter.wait(&m_startWaitMutex, 100);
|
||||
m_startWaitMutex.unlock();
|
||||
}
|
||||
|
||||
void OsmoSDRThread::stopWork()
|
||||
{
|
||||
m_running = false;
|
||||
wait();
|
||||
}
|
||||
|
||||
void OsmoSDRThread::run()
|
||||
{
|
||||
int res;
|
||||
|
||||
m_running = true;
|
||||
m_startWaiter.wakeAll();
|
||||
|
||||
//m_f = fopen("/tmp/samples.bin", "wb");
|
||||
|
||||
while(m_running) {
|
||||
if((res = osmosdr_read_async(m_dev, &OsmoSDRThread::callbackHelper, this, 16, sizeof(Sample) * 8192 * 2)) < 0) {
|
||||
qCritical("OsmoSDRThread: async error: %s", strerror(errno));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_running = false;
|
||||
}
|
||||
|
||||
void OsmoSDRThread::checkData(const quint8* buf, qint32 len)
|
||||
{
|
||||
const Sample* s = (const Sample*)buf;
|
||||
len /= sizeof(Sample);
|
||||
|
||||
while(len) {
|
||||
if((s->i != m_nextI) || (s->q != m_nextQ)) {
|
||||
qDebug("continuity error after %llu samples", m_samplePos);
|
||||
m_samplePos = 0;
|
||||
m_nextI = s->i - 1;
|
||||
m_nextQ = s->q + 1;
|
||||
} else {
|
||||
m_nextI--;
|
||||
m_nextQ++;
|
||||
m_samplePos++;
|
||||
}
|
||||
len--;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
void OsmoSDRThread::callback(const quint8* buf, qint32 len)
|
||||
{
|
||||
/*
|
||||
qDebug("%d", len);
|
||||
*/
|
||||
/*
|
||||
for(int i = 0; i < len / 2; i += 2) {
|
||||
((qint16*)buf)[i] = sin((float)(cntr) * 1024* 2.0 * M_PI / 65536.0) * 32000.0;
|
||||
((qint16*)buf)[i + 1] = -cos((float)(cntr++) * 1024*2.0 * M_PI / 65536.0) * 32000.0;
|
||||
}
|
||||
*/
|
||||
|
||||
//m_sampleFifo->write((SampleVector::const_iterator)((Sample*)buf), (SampleVector::const_iterator)((Sample*)(buf + len)));
|
||||
//fwrite(buf, 1, len, m_f);
|
||||
//checkData(buf, len);
|
||||
|
||||
m_sampleFifo->write(buf, len);
|
||||
if(!m_running)
|
||||
osmosdr_cancel_async(m_dev);
|
||||
}
|
||||
|
||||
void OsmoSDRThread::callbackHelper(unsigned char* buf, uint32_t len, void* ctx)
|
||||
{
|
||||
OsmoSDRThread* thread = (OsmoSDRThread*)ctx;
|
||||
thread->callback(buf, len);
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// //
|
||||
// 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 //
|
||||
// //
|
||||
// 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 INCLUDE_OSMOSDRTHREAD_H
|
||||
#define INCLUDE_OSMOSDRTHREAD_H
|
||||
|
||||
#include <QThread>
|
||||
#include <QMutex>
|
||||
#include <QWaitCondition>
|
||||
#include <osmosdr.h>
|
||||
|
||||
class SampleFifo;
|
||||
|
||||
class OsmoSDRThread : public QThread {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
OsmoSDRThread(osmosdr_dev_t* dev, SampleFifo* sampleFifo, QObject* parent = NULL);
|
||||
~OsmoSDRThread();
|
||||
|
||||
void startWork();
|
||||
void stopWork();
|
||||
|
||||
private:
|
||||
#pragma pack(push, 1)
|
||||
struct Sample {
|
||||
qint16 i;
|
||||
qint16 q;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
qint16 m_nextI;
|
||||
qint16 m_nextQ;
|
||||
quint64 m_samplePos;
|
||||
|
||||
QMutex m_startWaitMutex;
|
||||
QWaitCondition m_startWaiter;
|
||||
bool m_running;
|
||||
FILE* m_f;
|
||||
|
||||
osmosdr_dev_t* m_dev;
|
||||
SampleFifo* m_sampleFifo;
|
||||
|
||||
void run();
|
||||
|
||||
void checkData(const quint8* buf, qint32 len);
|
||||
void callback(const quint8* buf, qint32 len);
|
||||
|
||||
static void callbackHelper(unsigned char* buf, uint32_t len, void* ctx);
|
||||
};
|
||||
|
||||
#endif // INCLUDE_OSMOSDRTHREAD_H
|
||||
@@ -0,0 +1,518 @@
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QCursor>
|
||||
#include <QThread>
|
||||
#include <libusb.h>
|
||||
#include "osmosdrupgrade.h"
|
||||
#include "ui_osmosdrupgrade.h"
|
||||
|
||||
#define OSMOSDR_USB_VID 0x16c0
|
||||
#define OSMOSDR_USB_PID 0x0763
|
||||
|
||||
// DFU commands
|
||||
#define DFU_DETACH 0x00
|
||||
#define DFU_DNLOAD 0x01
|
||||
#define DFU_UPLOAD 0x02
|
||||
#define DFU_GETSTATUS 0x03
|
||||
#define DFU_CLRSTATUS 0x04
|
||||
#define DFU_GETSTATE 0x05
|
||||
#define DFU_ABORT 0x06
|
||||
|
||||
// DFU states
|
||||
#define ST_appIDLE 0
|
||||
#define ST_appDETACH 1
|
||||
#define ST_dfuIDLE 2
|
||||
#define ST_dfuDNLOAD_SYNC 3
|
||||
#define ST_dfuDNBUSY 4
|
||||
#define ST_dfuDNLOAD_IDLE 5
|
||||
#define ST_dfuMANIFEST_SYNC 6
|
||||
#define ST_dfuMANIFEST 7
|
||||
#define ST_dfuMANIFEST_WAIT_RST 8
|
||||
#define ST_dfuUPLOAD_IDLE 9
|
||||
#define ST_dfuERROR 10
|
||||
|
||||
#define DFU_PACKETSIZE 512
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct dfu_getstatus {
|
||||
quint8 bStatus;
|
||||
quint8 bwPollTimeout[3];
|
||||
quint8 bState;
|
||||
quint8 iString;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
|
||||
OsmoSDRUpgrade::OsmoSDRUpgrade(QWidget* parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::OsmoSDRUpgrade),
|
||||
m_usb(NULL)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
connect(&m_searchDeviceTimer, SIGNAL(timeout()), this, SLOT(searchDeviceTick()));
|
||||
}
|
||||
|
||||
OsmoSDRUpgrade::~OsmoSDRUpgrade()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void OsmoSDRUpgrade::on_browse_clicked()
|
||||
{
|
||||
QString filename = QFileDialog::getOpenFileName(this, tr("OsmoSDR Firmware File"), QString(), tr("OsmoSDR Firmware (*.ofw);;All Files (*)"));
|
||||
if(filename.isEmpty())
|
||||
return;
|
||||
|
||||
QFile file(filename);
|
||||
if(file.size() > 10 * 1024 * 1024) {
|
||||
QMessageBox::critical(this, tr("File Open Error"), tr("File is too big to be a firmware update for OsmoSDR."));
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray dfuApp;
|
||||
QByteArray radioApp;
|
||||
QByteArray fpgaBin;
|
||||
mz_zip_archive zip;
|
||||
memset(&zip, 0x00, sizeof(zip));
|
||||
|
||||
if(!mz_zip_reader_init_file(&zip, qPrintable(filename), 0)) {
|
||||
mz_zip_reader_end(&zip);
|
||||
QMessageBox::critical(this, tr("File Open Error"), tr("File is not a valid OsmoSDR update."));
|
||||
return;
|
||||
}
|
||||
|
||||
int dfuAppIndex = mz_zip_reader_locate_file(&zip, "dfuapp.bin", NULL, 0);
|
||||
int radioAppIndex = mz_zip_reader_locate_file(&zip, "radioapp.bin", NULL, 0);
|
||||
int fpgaIndex = mz_zip_reader_locate_file(&zip, "fpga.bin", NULL, 0);
|
||||
|
||||
if((dfuAppIndex < 0) && (radioAppIndex < 0) && (fpgaIndex < 0)) {
|
||||
mz_zip_reader_end(&zip);
|
||||
QMessageBox::critical(this, tr("File Open Error"), tr("File is not a valid OsmoSDR update."));
|
||||
return;
|
||||
}
|
||||
|
||||
if(!mz_zip_reader_extract_to_callback(&zip, dfuAppIndex, zipHelper, &dfuApp, 0)) {
|
||||
mz_zip_reader_end(&zip);
|
||||
QMessageBox::critical(this, tr("File Open Error"), tr("File is not a valid OsmoSDR update."));
|
||||
return;
|
||||
}
|
||||
|
||||
if(!mz_zip_reader_extract_to_callback(&zip, radioAppIndex, zipHelper, &radioApp, 0)) {
|
||||
mz_zip_reader_end(&zip);
|
||||
QMessageBox::critical(this, tr("File Open Error"), tr("File is not a valid OsmoSDR update."));
|
||||
return;
|
||||
}
|
||||
|
||||
if(!mz_zip_reader_extract_to_callback(&zip, fpgaIndex, zipHelper, &fpgaBin, 0)) {
|
||||
mz_zip_reader_end(&zip);
|
||||
QMessageBox::critical(this, tr("File Open Error"), tr("File is not a valid OsmoSDR update."));
|
||||
return;
|
||||
}
|
||||
|
||||
mz_zip_reader_end(&zip);
|
||||
m_dfuApp = dfuApp;
|
||||
m_radioApp = radioApp;
|
||||
m_fpgaBin = fpgaBin;
|
||||
|
||||
if(dfuAppIndex >= 0) {
|
||||
ui->dfu->setEnabled(true);
|
||||
ui->dfuSize->setText(tr("%1").arg(m_dfuApp.size()));
|
||||
} else {
|
||||
ui->dfu->setEnabled(false);
|
||||
ui->dfuSize->setText(tr("-"));
|
||||
}
|
||||
if(radioAppIndex >= 0) {
|
||||
ui->radio->setEnabled(true);
|
||||
ui->radioSize->setText(tr("%1").arg(m_radioApp.size()));
|
||||
} else {
|
||||
ui->radio->setEnabled(false);
|
||||
ui->dfuSize->setText(tr("-"));
|
||||
}
|
||||
if(fpgaIndex >= 0) {
|
||||
ui->fpga->setEnabled(true);
|
||||
ui->fpgaSize->setText(tr("%1").arg(m_fpgaBin.size()));
|
||||
} else {
|
||||
ui->fpga->setEnabled(false);
|
||||
ui->fpgaSize->setText(tr("-"));
|
||||
}
|
||||
|
||||
|
||||
if((dfuAppIndex >= 0) && (radioAppIndex < 0) && (fpgaIndex < 0)) {
|
||||
ui->dfu->setChecked(true);
|
||||
ui->radio->setChecked(false);
|
||||
ui->fpga->setChecked(false);
|
||||
} else {
|
||||
ui->dfu->setChecked(false);
|
||||
ui->radio->setChecked(radioAppIndex >= 0);
|
||||
ui->fpga->setChecked(fpgaIndex >= 0);
|
||||
}
|
||||
|
||||
ui->fwBox->setEnabled(true);
|
||||
ui->progressBox->setEnabled(true);
|
||||
|
||||
QFileInfo fi(file.fileName());
|
||||
ui->filename->setText(fi.fileName());
|
||||
}
|
||||
|
||||
void OsmoSDRUpgrade::on_dfu_toggled(bool checked)
|
||||
{
|
||||
updateFlashButton();
|
||||
}
|
||||
|
||||
void OsmoSDRUpgrade::on_radio_toggled(bool checked)
|
||||
{
|
||||
updateFlashButton();
|
||||
}
|
||||
|
||||
void OsmoSDRUpgrade::on_fpga_toggled(bool checked)
|
||||
{
|
||||
updateFlashButton();
|
||||
}
|
||||
|
||||
void OsmoSDRUpgrade::updateFlashButton()
|
||||
{
|
||||
if(ui->dfu->isChecked() || ui->radio->isChecked() || ui->fpga->isChecked())
|
||||
ui->progressBox->setEnabled(true);
|
||||
else ui->progressBox->setEnabled(false);
|
||||
}
|
||||
|
||||
void OsmoSDRUpgrade::on_start_clicked()
|
||||
{
|
||||
ui->close->setEnabled(false);
|
||||
ui->start->setEnabled(false);
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
|
||||
ui->log->clear();
|
||||
log(tr("Starting flash operation..."));
|
||||
|
||||
if(libusb_init(&m_usb) < 0) {
|
||||
fail(tr("Could not initialize libusb."));
|
||||
return;
|
||||
}
|
||||
|
||||
switchToDFU();
|
||||
|
||||
m_searchTries = 0;
|
||||
m_searchDeviceTimer.start(250);
|
||||
}
|
||||
|
||||
void OsmoSDRUpgrade::searchDeviceTick()
|
||||
{
|
||||
m_searchTries++;
|
||||
log(tr("Searching device (try %1)").arg(m_searchTries));
|
||||
|
||||
libusb_device_handle* device = libusb_open_device_with_vid_pid(m_usb, OSMOSDR_USB_VID, OSMOSDR_USB_PID);
|
||||
if(device == NULL) {
|
||||
if(m_searchTries >= 10) {
|
||||
m_searchDeviceTimer.stop();
|
||||
finish();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
m_searchDeviceTimer.stop();
|
||||
|
||||
libusb_device_descriptor deviceDescriptor;
|
||||
|
||||
if(libusb_get_descriptor(device, LIBUSB_DT_DEVICE, 0, (unsigned char*)&deviceDescriptor, sizeof(deviceDescriptor)) < 0) {
|
||||
libusb_close(device);
|
||||
fail(tr("Could not read device descriptor."));
|
||||
return;
|
||||
}
|
||||
|
||||
char sn[64];
|
||||
memset(sn, 0x00, sizeof(sn));
|
||||
libusb_get_string_descriptor_ascii(device, deviceDescriptor.iSerialNumber, (unsigned char*)sn, sizeof(sn));
|
||||
sn[sizeof(sn) - 1] = '\0';
|
||||
|
||||
log(tr("OsmoSDR found (SN %1)").arg(sn));
|
||||
|
||||
quint8 buffer[255];
|
||||
if(libusb_get_descriptor(device, LIBUSB_DT_CONFIG, 0, buffer, sizeof(buffer)) < 16) {
|
||||
libusb_close(device);
|
||||
fail(tr("Could not get a configuration descriptor"));
|
||||
return;
|
||||
}
|
||||
|
||||
if(buffer[15] != 1) {
|
||||
libusb_close(device);
|
||||
fail(tr("Device not in DFU mode as it should be"));
|
||||
return;
|
||||
}
|
||||
|
||||
quint8 state;
|
||||
|
||||
if(dfuGetState(device, &state) < 0) {
|
||||
libusb_reset_device(device);
|
||||
if(dfuGetState(device, &state) < 0) {
|
||||
libusb_close(device);
|
||||
fail(tr("Cannot get device DFU state"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if((state != ST_dfuIDLE) && (state != ST_dfuDNLOAD_IDLE)) {
|
||||
dfuGetStatus(device);
|
||||
libusb_close(device);
|
||||
fail(tr("Device is not in dfuIDLE or dfuDNLOAD_IDLE state"));
|
||||
return;
|
||||
}
|
||||
|
||||
if(libusb_claim_interface(device, 0) < 0) {
|
||||
libusb_close(device);
|
||||
fail(tr("Could not claim interface"));
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray* image;
|
||||
int altSetting;
|
||||
|
||||
while(true) {
|
||||
if(ui->dfu->isChecked()) {
|
||||
log(tr("Starting download of DFU application"));
|
||||
image = &m_dfuApp;
|
||||
altSetting = 1;
|
||||
} else if(ui->radio->isChecked()) {
|
||||
log(tr("Starting download of radio application"));
|
||||
image = &m_radioApp;
|
||||
altSetting = 0;
|
||||
} else if(ui->fpga->isChecked()) {
|
||||
log(tr("Starting download of FPGA image"));
|
||||
image = &m_fpgaBin;
|
||||
altSetting = 2;
|
||||
} else {
|
||||
log(tr("Switching back to radio application mode"));
|
||||
dfuDetach(device);
|
||||
break;
|
||||
}
|
||||
|
||||
if(libusb_set_interface_alt_setting(device, 0, altSetting) < 0) {
|
||||
libusb_close(device);
|
||||
fail(tr("Could not set alternate interface setting to %1").arg(altSetting));
|
||||
return;
|
||||
}
|
||||
|
||||
ui->progress->setMaximum((image->size() / DFU_PACKETSIZE) + ((image->size() % DFU_PACKETSIZE) ? 1 : 0));
|
||||
|
||||
int blocknum = 0;
|
||||
while((state == ST_dfuIDLE) || (state == ST_dfuDNLOAD_IDLE)) {
|
||||
int offset = blocknum * DFU_PACKETSIZE;
|
||||
int blocklen = (int)image->size() - offset;
|
||||
if(blocklen < 0)
|
||||
blocklen = 0;
|
||||
if(blocklen > DFU_PACKETSIZE)
|
||||
blocklen = DFU_PACKETSIZE;
|
||||
if(dfuDownloadBlock(device, blocknum, (const quint8*)(image->data() + offset), blocklen) < 0) {
|
||||
if(dfuGetState(device, &state) < 0) {
|
||||
if(ui->dfu->isChecked())
|
||||
break;
|
||||
libusb_close(device);
|
||||
fail(tr("Cannot get device DFU state"));
|
||||
return;
|
||||
}
|
||||
dfuGetStatus(device);
|
||||
libusb_close(device);
|
||||
fail(tr("Error downloading block %1").arg(blocknum));
|
||||
return;
|
||||
}
|
||||
dfuGetStatus(device);
|
||||
if(blocklen == 0)
|
||||
break;
|
||||
blocknum++;
|
||||
ui->progress->setValue(blocknum);
|
||||
QApplication::processEvents();
|
||||
}
|
||||
log(tr("Download complete (state %1)").arg(state));
|
||||
|
||||
do {
|
||||
if(dfuGetState(device, &state) < 0) {
|
||||
if(ui->dfu->isChecked()) {
|
||||
break;
|
||||
} else {
|
||||
libusb_close(device);
|
||||
fail(tr("Cannot get device DFU state"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
dfuGetStatus(device);
|
||||
} while(state == ST_dfuMANIFEST);
|
||||
|
||||
if(ui->dfu->isChecked()) {
|
||||
ui->dfu->setChecked(false);
|
||||
m_searchTries = 0;
|
||||
m_searchDeviceTimer.start(250);
|
||||
libusb_close(device);
|
||||
return;
|
||||
} else if(ui->radio->isChecked()) {
|
||||
ui->radio->setChecked(false);
|
||||
} else if(ui->fpga->isChecked()) {
|
||||
ui->fpga->setChecked(false);
|
||||
}
|
||||
|
||||
QApplication::processEvents();
|
||||
}
|
||||
|
||||
log(tr("Upgrade finished successfully"));
|
||||
libusb_close(device);
|
||||
finish();
|
||||
}
|
||||
|
||||
void OsmoSDRUpgrade::switchToDFU()
|
||||
{
|
||||
libusb_device_handle* device = libusb_open_device_with_vid_pid(m_usb, OSMOSDR_USB_VID, OSMOSDR_USB_PID);
|
||||
if(device == NULL) {
|
||||
log(tr("No OsmoSDR VID:PID %1:%2 found").arg(OSMOSDR_USB_VID, 4, 16, QChar('0')).arg(OSMOSDR_USB_PID, 4, 16, QChar('0')));
|
||||
return;
|
||||
}
|
||||
|
||||
quint8 buffer[255];
|
||||
if(libusb_get_descriptor(device, LIBUSB_DT_CONFIG, 0, buffer, sizeof(buffer)) < 16) {
|
||||
log(tr("Could not get a valid configuration descriptor"));
|
||||
libusb_close(device);
|
||||
return;
|
||||
}
|
||||
|
||||
if(buffer[15] != 1) {
|
||||
if(libusb_claim_interface(device, 0) < 0) {
|
||||
log(tr("Could not claim interface"));
|
||||
libusb_close(device);
|
||||
return;
|
||||
}
|
||||
|
||||
log(tr("Switching device to DFU mode"));
|
||||
libusb_control_transfer(device, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT, 0x07, 0x0003, 0, NULL, 0, 50);
|
||||
libusb_release_interface(device, 0);
|
||||
} else {
|
||||
log(tr("Device is already in DFU mode"));
|
||||
}
|
||||
|
||||
libusb_close(device);
|
||||
}
|
||||
|
||||
int OsmoSDRUpgrade::dfuGetState(libusb_device_handle* device, quint8* state)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = libusb_control_transfer(
|
||||
device,
|
||||
0xa1,
|
||||
DFU_GETSTATE,
|
||||
0,
|
||||
0,
|
||||
(unsigned char*)state,
|
||||
1,
|
||||
1000);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int OsmoSDRUpgrade::dfuGetStatus(libusb_device_handle* device)
|
||||
{
|
||||
int res;
|
||||
struct dfu_getstatus getstatus;
|
||||
/*
|
||||
static const char* statedesc[] = {
|
||||
"appIDLE",
|
||||
"appDETACH",
|
||||
"dfuIDLE",
|
||||
"dfuDNLOAD-SYNC",
|
||||
"dfuDNBUSY",
|
||||
"dfuDNLOAD-IDLE",
|
||||
"dfuMANIFEST-SYNC",
|
||||
"dfuMANIFEST",
|
||||
"dfuMANIFEST-WAIT-RESET",
|
||||
"dfuUPLOAD-IDLE",
|
||||
"dfuERROR"
|
||||
};
|
||||
*/
|
||||
|
||||
res = libusb_control_transfer(
|
||||
device,
|
||||
0xa1,
|
||||
DFU_GETSTATUS,
|
||||
0,
|
||||
0,
|
||||
(unsigned char*)&getstatus,
|
||||
sizeof(getstatus),
|
||||
1000);
|
||||
/*
|
||||
if(res >= 0)
|
||||
log(tr("OsmoSDR bStatus: %1, bState: %2 (%3)").arg(getstatus.bStatus).arg(statedesc[getstatus.bState]).arg(getstatus.bState));
|
||||
*/
|
||||
return res;
|
||||
}
|
||||
|
||||
int OsmoSDRUpgrade::dfuClrStatus(libusb_device_handle* device)
|
||||
{
|
||||
return libusb_control_transfer(
|
||||
device,
|
||||
0x21,
|
||||
DFU_CLRSTATUS,
|
||||
0,
|
||||
0,
|
||||
NULL,
|
||||
0,
|
||||
1000);
|
||||
}
|
||||
|
||||
int OsmoSDRUpgrade::dfuDownloadBlock(libusb_device_handle* device, quint16 block, const quint8* data, quint16 len)
|
||||
{
|
||||
return libusb_control_transfer(
|
||||
device,
|
||||
0x21,
|
||||
DFU_DNLOAD,
|
||||
block,
|
||||
0,
|
||||
(unsigned char*)data,
|
||||
len,
|
||||
10000);
|
||||
}
|
||||
|
||||
int OsmoSDRUpgrade::dfuDetach(libusb_device_handle* device)
|
||||
{
|
||||
return libusb_control_transfer(
|
||||
device,
|
||||
0x21,
|
||||
DFU_DETACH,
|
||||
0,
|
||||
0,
|
||||
NULL,
|
||||
0,
|
||||
1000);
|
||||
}
|
||||
|
||||
void OsmoSDRUpgrade::fail(const QString& msg)
|
||||
{
|
||||
log(tr("Fatal error: %1").arg(msg));
|
||||
QMessageBox::critical(this, tr("OsmoSDR Upgrade Failed"), msg);
|
||||
finish();
|
||||
}
|
||||
|
||||
void OsmoSDRUpgrade::finish()
|
||||
{
|
||||
if(m_usb != NULL) {
|
||||
libusb_exit(m_usb);
|
||||
m_usb = NULL;
|
||||
}
|
||||
QApplication::restoreOverrideCursor();
|
||||
ui->start->setEnabled(true);
|
||||
ui->close->setEnabled(true);
|
||||
}
|
||||
|
||||
void OsmoSDRUpgrade::log(const QString& msg)
|
||||
{
|
||||
ui->log->appendPlainText(msg);
|
||||
ui->log->moveCursor(QTextCursor::End);
|
||||
}
|
||||
|
||||
void OsmoSDRUpgrade::reject()
|
||||
{
|
||||
if(ui->close->isEnabled())
|
||||
return QDialog::reject();
|
||||
}
|
||||
|
||||
size_t OsmoSDRUpgrade::zipHelper(void* pOpaque, mz_uint64 file_ofs, const void* pBuf, size_t n)
|
||||
{
|
||||
QByteArray* bytes = (QByteArray*)pOpaque;
|
||||
bytes->append((const char*)pBuf, n);
|
||||
return n;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#ifndef INCLUDE_OSMOSDRUPGRADE_H
|
||||
#define INCLUDE_OSMOSDRUPGRADE_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QTimer>
|
||||
#include "util/miniz.h"
|
||||
|
||||
typedef struct libusb_context libusb_context;
|
||||
typedef struct libusb_device_handle libusb_device_handle;
|
||||
|
||||
namespace Ui {
|
||||
class OsmoSDRUpgrade;
|
||||
}
|
||||
|
||||
class OsmoSDRUpgrade : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit OsmoSDRUpgrade(QWidget* parent = NULL);
|
||||
~OsmoSDRUpgrade();
|
||||
|
||||
private slots:
|
||||
void on_browse_clicked();
|
||||
void on_dfu_toggled(bool checked);
|
||||
void on_radio_toggled(bool checked);
|
||||
void on_fpga_toggled(bool checked);
|
||||
void on_start_clicked();
|
||||
void searchDeviceTick();
|
||||
|
||||
private:
|
||||
Ui::OsmoSDRUpgrade* ui;
|
||||
|
||||
libusb_context* m_usb;
|
||||
|
||||
QByteArray m_dfuApp;
|
||||
QByteArray m_radioApp;
|
||||
QByteArray m_fpgaBin;
|
||||
|
||||
int m_searchTries;
|
||||
QTimer m_searchDeviceTimer;
|
||||
|
||||
void updateFlashButton();
|
||||
|
||||
void switchToDFU();
|
||||
int dfuGetState(libusb_device_handle* device, quint8* state);
|
||||
int dfuGetStatus(libusb_device_handle* device);
|
||||
int dfuClrStatus(libusb_device_handle* device);
|
||||
int dfuDownloadBlock(libusb_device_handle* device, quint16 block, const quint8* data, quint16 len);
|
||||
int dfuDetach(libusb_device_handle* device);
|
||||
|
||||
void fail(const QString& msg);
|
||||
void finish();
|
||||
void log(const QString& msg);
|
||||
|
||||
void reject();
|
||||
|
||||
static size_t zipHelper(void* pOpaque, mz_uint64 file_ofs, const void* pBuf, size_t n);
|
||||
};
|
||||
|
||||
#endif // INCLUDE_OSMOSDRUPGRADE_H
|
||||
@@ -0,0 +1,213 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>OsmoSDRUpgrade</class>
|
||||
<widget class="QDialog" name="OsmoSDRUpgrade">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>382</width>
|
||||
<height>503</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>OsmoSDR Firmware Upgrade</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Firmware Archive</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="filename">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>256</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::PlainText</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="browse">
|
||||
<property name="text">
|
||||
<string>Browse...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="fwBox">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Firmware</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="radioSize">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="fpgaSize">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Size (Bytes)</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="dfu">
|
||||
<property name="text">
|
||||
<string>DFU Application</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="radio">
|
||||
<property name="text">
|
||||
<string>Radio Application</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="fpga">
|
||||
<property name="text">
|
||||
<string>FPGA Image</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="dfuSize">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="progressBox">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Progress</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QProgressBar" name="progress">
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="start">
|
||||
<property name="text">
|
||||
<string>Flash!</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="log">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<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>
|
||||
<item>
|
||||
<widget class="QPushButton" name="close">
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>close</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>OsmoSDRUpgrade</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>331</x>
|
||||
<y>247</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>190</x>
|
||||
<y>134</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
@@ -0,0 +1,50 @@
|
||||
project(rtlsdr)
|
||||
|
||||
set(rtlsdr_SOURCES
|
||||
rtlsdrgui.cpp
|
||||
rtlsdrinput.cpp
|
||||
rtlsdrplugin.cpp
|
||||
rtlsdrthread.cpp
|
||||
)
|
||||
|
||||
set(rtlsdr_HEADERS
|
||||
rtlsdrgui.h
|
||||
rtlsdrinput.h
|
||||
rtlsdrplugin.h
|
||||
rtlsdrthread.h
|
||||
)
|
||||
|
||||
set(rtlsdr_FORMS
|
||||
rtlsdrgui.ui
|
||||
)
|
||||
|
||||
include_directories(
|
||||
.
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_SOURCE_DIR}/include
|
||||
${CMAKE_SOURCE_DIR}/include-gpl
|
||||
${LIBRTLSDR_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
#include(${QT_USE_FILE})
|
||||
add_definitions(${QT_DEFINITIONS})
|
||||
add_definitions(-DQT_PLUGIN)
|
||||
add_definitions(-DQT_SHARED)
|
||||
|
||||
#qt4_wrap_cpp(rtlsdr_HEADERS_MOC ${rtlsdr_HEADERS})
|
||||
qt5_wrap_ui(rtlsdr_FORMS_HEADERS ${rtlsdr_FORMS})
|
||||
|
||||
add_library(inputrtlsdr SHARED
|
||||
${rtlsdr_SOURCES}
|
||||
${rtlsdr_HEADERS_MOC}
|
||||
${rtlsdr_FORMS_HEADERS}
|
||||
)
|
||||
|
||||
target_link_libraries(inputrtlsdr
|
||||
${QT_LIBRARIES}
|
||||
${LIBRTLSDR_LIBRARIES}
|
||||
${LIBUSB_LIBRARIES}
|
||||
sdrbase
|
||||
)
|
||||
|
||||
qt5_use_modules(inputrtlsdr Core Widgets OpenGL Multimedia)
|
||||
@@ -0,0 +1,154 @@
|
||||
#include "rtlsdrgui.h"
|
||||
#include "ui_rtlsdrgui.h"
|
||||
#include "plugin/pluginapi.h"
|
||||
|
||||
RTLSDRGui::RTLSDRGui(PluginAPI* pluginAPI, QWidget* parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::RTLSDRGui),
|
||||
m_pluginAPI(pluginAPI),
|
||||
m_settings(),
|
||||
m_sampleSource(NULL)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->centerFrequency->setValueRange(7, 20000U, 2200000U);
|
||||
connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware()));
|
||||
displaySettings();
|
||||
|
||||
m_sampleSource = new RTLSDRInput(m_pluginAPI->getMainWindowMessageQueue());
|
||||
m_pluginAPI->setSampleSource(m_sampleSource);
|
||||
}
|
||||
|
||||
RTLSDRGui::~RTLSDRGui()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void RTLSDRGui::destroy()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void RTLSDRGui::setName(const QString& name)
|
||||
{
|
||||
setObjectName(name);
|
||||
}
|
||||
|
||||
void RTLSDRGui::resetToDefaults()
|
||||
{
|
||||
m_generalSettings.resetToDefaults();
|
||||
m_settings.resetToDefaults();
|
||||
displaySettings();
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
QByteArray RTLSDRGui::serializeGeneral() const
|
||||
{
|
||||
return m_generalSettings.serialize();
|
||||
}
|
||||
|
||||
bool RTLSDRGui::deserializeGeneral(const QByteArray&data)
|
||||
{
|
||||
if(m_generalSettings.deserialize(data)) {
|
||||
displaySettings();
|
||||
sendSettings();
|
||||
return true;
|
||||
} else {
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
quint64 RTLSDRGui::getCenterFrequency() const
|
||||
{
|
||||
return m_generalSettings.m_centerFrequency;
|
||||
}
|
||||
|
||||
QByteArray RTLSDRGui::serialize() const
|
||||
{
|
||||
return m_settings.serialize();
|
||||
}
|
||||
|
||||
bool RTLSDRGui::deserialize(const QByteArray& data)
|
||||
{
|
||||
if(m_settings.deserialize(data)) {
|
||||
displaySettings();
|
||||
sendSettings();
|
||||
return true;
|
||||
} else {
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool RTLSDRGui::handleMessage(Message* message)
|
||||
{
|
||||
if(RTLSDRInput::MsgReportRTLSDR::match(message)) {
|
||||
m_gains = ((RTLSDRInput::MsgReportRTLSDR*)message)->getGains();
|
||||
displaySettings();
|
||||
message->completed();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void RTLSDRGui::displaySettings()
|
||||
{
|
||||
ui->centerFrequency->setValue(m_generalSettings.m_centerFrequency / 1000);
|
||||
ui->decimation->setValue(m_settings.m_decimation);
|
||||
|
||||
if(m_gains.size() > 0) {
|
||||
int dist = abs(m_settings.m_gain - m_gains[0]);
|
||||
int pos = 0;
|
||||
for(uint i = 1; i < m_gains.size(); i++) {
|
||||
if(abs(m_settings.m_gain - m_gains[i]) < dist) {
|
||||
dist = abs(m_settings.m_gain - m_gains[i]);
|
||||
pos = i;
|
||||
}
|
||||
}
|
||||
ui->gainText->setText(tr("%1.%2").arg(m_gains[pos] / 10).arg(abs(m_gains[pos] % 10)));
|
||||
ui->gain->setMaximum(m_gains.size() - 1);
|
||||
ui->gain->setEnabled(true);
|
||||
ui->gain->setValue(pos);
|
||||
} else {
|
||||
ui->gain->setMaximum(0);
|
||||
ui->gain->setEnabled(false);
|
||||
ui->gain->setValue(0);
|
||||
}
|
||||
}
|
||||
|
||||
void RTLSDRGui::sendSettings()
|
||||
{
|
||||
if(!m_updateTimer.isActive())
|
||||
m_updateTimer.start(100);
|
||||
}
|
||||
|
||||
void RTLSDRGui::on_centerFrequency_changed(quint64 value)
|
||||
{
|
||||
m_generalSettings.m_centerFrequency = value * 1000;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RTLSDRGui::on_gain_valueChanged(int value)
|
||||
{
|
||||
if(value > (int)m_gains.size())
|
||||
return;
|
||||
int gain = m_gains[value];
|
||||
ui->gainText->setText(tr("%1.%2").arg(gain / 10).arg(abs(gain % 10)));
|
||||
m_settings.m_gain = gain;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RTLSDRGui::on_decimation_valueChanged(int value)
|
||||
{
|
||||
ui->decimationText->setText(tr("1:%1").arg(1 << value));
|
||||
m_settings.m_decimation = value;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RTLSDRGui::updateHardware()
|
||||
{
|
||||
RTLSDRInput::MsgConfigureRTLSDR* message = RTLSDRInput::MsgConfigureRTLSDR::create(m_generalSettings, m_settings);
|
||||
message->submit(m_pluginAPI->getDSPEngineMessageQueue());
|
||||
m_updateTimer.stop();
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#ifndef INCLUDE_RTLSDRGUI_H
|
||||
#define INCLUDE_RTLSDRGUI_H
|
||||
|
||||
#include <QTimer>
|
||||
#include "plugin/plugingui.h"
|
||||
#include "rtlsdrinput.h"
|
||||
|
||||
class PluginAPI;
|
||||
|
||||
namespace Ui {
|
||||
class RTLSDRGui;
|
||||
}
|
||||
|
||||
class RTLSDRGui : public QWidget, public PluginGUI {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit RTLSDRGui(PluginAPI* pluginAPI, QWidget* parent = NULL);
|
||||
~RTLSDRGui();
|
||||
void destroy();
|
||||
|
||||
void setName(const QString& name);
|
||||
|
||||
void resetToDefaults();
|
||||
QByteArray serializeGeneral() const;
|
||||
bool deserializeGeneral(const QByteArray&data);
|
||||
quint64 getCenterFrequency() const;
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
bool handleMessage(Message* message);
|
||||
|
||||
private:
|
||||
Ui::RTLSDRGui* ui;
|
||||
|
||||
PluginAPI* m_pluginAPI;
|
||||
SampleSource::GeneralSettings m_generalSettings;
|
||||
RTLSDRInput::Settings m_settings;
|
||||
QTimer m_updateTimer;
|
||||
std::vector<int> m_gains;
|
||||
SampleSource* m_sampleSource;
|
||||
|
||||
void displaySettings();
|
||||
void sendSettings();
|
||||
|
||||
private slots:
|
||||
void on_centerFrequency_changed(quint64 value);
|
||||
void on_gain_valueChanged(int value);
|
||||
void on_decimation_valueChanged(int value);
|
||||
|
||||
void updateHardware();
|
||||
};
|
||||
|
||||
#endif // INCLUDE_RTLSDRGUI_H
|
||||
@@ -0,0 +1,229 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>RTLSDRGui</class>
|
||||
<widget class="QWidget" name="RTLSDRGui">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>132</width>
|
||||
<height>82</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>RTL-SDR</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="horizontalLayout_3">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<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>Monospace</family>
|
||||
<pointsize>20</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Tuner center frequency in kHz</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<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>
|
||||
<widget class="Line" name="line_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSlider" name="decimation">
|
||||
<property name="toolTip">
|
||||
<string>Signal decimation factor</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_12">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Decimation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QLabel" name="decimationText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>1:1</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_11">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Gain</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSlider" name="gain">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>LNA amplification</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>0</number>
|
||||
</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="QLabel" name="gainText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>---</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>ValueDial</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/valuedial.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,230 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// //
|
||||
// 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 //
|
||||
// //
|
||||
// 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 <string.h>
|
||||
#include <errno.h>
|
||||
#include "rtlsdrinput.h"
|
||||
#include "rtlsdrthread.h"
|
||||
#include "rtlsdrgui.h"
|
||||
#include "util/simpleserializer.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(RTLSDRInput::MsgConfigureRTLSDR, Message)
|
||||
MESSAGE_CLASS_DEFINITION(RTLSDRInput::MsgReportRTLSDR, Message)
|
||||
|
||||
RTLSDRInput::Settings::Settings() :
|
||||
m_gain(0),
|
||||
m_decimation(0)
|
||||
{
|
||||
}
|
||||
|
||||
void RTLSDRInput::Settings::resetToDefaults()
|
||||
{
|
||||
m_gain = 0;
|
||||
m_decimation = 0;
|
||||
}
|
||||
|
||||
QByteArray RTLSDRInput::Settings::serialize() const
|
||||
{
|
||||
SimpleSerializer s(1);
|
||||
s.writeS32(1, m_gain);
|
||||
s.writeS32(2, m_decimation);
|
||||
return s.final();
|
||||
}
|
||||
|
||||
bool RTLSDRInput::Settings::deserialize(const QByteArray& data)
|
||||
{
|
||||
SimpleDeserializer d(data);
|
||||
|
||||
if(!d.isValid()) {
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(d.getVersion() == 1) {
|
||||
d.readS32(1, &m_gain, 0);
|
||||
d.readS32(2, &m_decimation, 0);
|
||||
return true;
|
||||
} else {
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
RTLSDRInput::RTLSDRInput(MessageQueue* msgQueueToGUI) :
|
||||
SampleSource(msgQueueToGUI),
|
||||
m_settings(),
|
||||
m_dev(NULL),
|
||||
m_rtlSDRThread(NULL),
|
||||
m_deviceDescription()
|
||||
{
|
||||
}
|
||||
|
||||
RTLSDRInput::~RTLSDRInput()
|
||||
{
|
||||
stopInput();
|
||||
}
|
||||
|
||||
bool RTLSDRInput::startInput(int device)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
if(m_dev != NULL)
|
||||
stopInput();
|
||||
|
||||
char vendor[256];
|
||||
char product[256];
|
||||
char serial[256];
|
||||
int res;
|
||||
int numberOfGains;
|
||||
|
||||
if(!m_sampleFifo.setSize(524288)) {
|
||||
qCritical("Could not allocate SampleFifo");
|
||||
return false;
|
||||
}
|
||||
|
||||
if((res = rtlsdr_open(&m_dev, device)) < 0) {
|
||||
qCritical("could not open RTLSDR #%d: %s", device, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
vendor[0] = '\0';
|
||||
product[0] = '\0';
|
||||
serial[0] = '\0';
|
||||
if((res = rtlsdr_get_usb_strings(m_dev, vendor, product, serial)) < 0) {
|
||||
qCritical("error accessing USB device");
|
||||
goto failed;
|
||||
}
|
||||
qDebug("RTLSDRInput open: %s %s, SN: %s", vendor, product, serial);
|
||||
m_deviceDescription = QString("%1 (SN %2)").arg(product).arg(serial);
|
||||
|
||||
if((res = rtlsdr_set_sample_rate(m_dev, 2000000)) < 0) {
|
||||
qCritical("could not set sample rate: %s", strerror(errno));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if((res = rtlsdr_set_tuner_gain_mode(m_dev, 1)) < 0) {
|
||||
qCritical("error setting tuner gain mode");
|
||||
goto failed;
|
||||
}
|
||||
if((res = rtlsdr_set_agc_mode(m_dev, 0)) < 0) {
|
||||
qCritical("error setting agc mode");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
numberOfGains = rtlsdr_get_tuner_gains(m_dev, NULL);
|
||||
if(numberOfGains < 0) {
|
||||
qCritical("error getting number of gain values supported");
|
||||
goto failed;
|
||||
}
|
||||
m_gains.resize(numberOfGains);
|
||||
if(rtlsdr_get_tuner_gains(m_dev, &m_gains[0]) < 0) {
|
||||
qCritical("error getting gain values");
|
||||
goto failed;
|
||||
}
|
||||
if((res = rtlsdr_reset_buffer(m_dev)) < 0) {
|
||||
qCritical("could not reset USB EP buffers: %s", strerror(errno));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if((m_rtlSDRThread = new RTLSDRThread(m_dev, &m_sampleFifo)) == NULL) {
|
||||
qFatal("out of memory");
|
||||
goto failed;
|
||||
}
|
||||
m_rtlSDRThread->startWork();
|
||||
|
||||
mutexLocker.unlock();
|
||||
applySettings(m_generalSettings, m_settings, true);
|
||||
|
||||
qDebug("RTLSDRInput: start");
|
||||
MsgReportRTLSDR::create(m_gains)->submit(m_guiMessageQueue);
|
||||
|
||||
return true;
|
||||
|
||||
failed:
|
||||
stopInput();
|
||||
return false;
|
||||
}
|
||||
|
||||
void RTLSDRInput::stopInput()
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
if(m_rtlSDRThread != NULL) {
|
||||
m_rtlSDRThread->stopWork();
|
||||
delete m_rtlSDRThread;
|
||||
m_rtlSDRThread = NULL;
|
||||
}
|
||||
if(m_dev != NULL) {
|
||||
rtlsdr_close(m_dev);
|
||||
m_dev = NULL;
|
||||
}
|
||||
m_deviceDescription.clear();
|
||||
}
|
||||
|
||||
const QString& RTLSDRInput::getDeviceDescription() const
|
||||
{
|
||||
return m_deviceDescription;
|
||||
}
|
||||
|
||||
int RTLSDRInput::getSampleRate() const
|
||||
{
|
||||
return 2000000 / (1 << m_settings.m_decimation);
|
||||
}
|
||||
|
||||
quint64 RTLSDRInput::getCenterFrequency() const
|
||||
{
|
||||
return m_generalSettings.m_centerFrequency;
|
||||
}
|
||||
|
||||
bool RTLSDRInput::handleMessage(Message* message)
|
||||
{
|
||||
if(MsgConfigureRTLSDR::match(message)) {
|
||||
MsgConfigureRTLSDR* conf = (MsgConfigureRTLSDR*)message;
|
||||
if(!applySettings(conf->getGeneralSettings(), conf->getSettings(), false))
|
||||
qDebug("RTLSDR config error");
|
||||
message->completed();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool RTLSDRInput::applySettings(const GeneralSettings& generalSettings, const Settings& settings, bool force)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
if((m_generalSettings.m_centerFrequency != generalSettings.m_centerFrequency) || force) {
|
||||
m_generalSettings.m_centerFrequency = generalSettings.m_centerFrequency;
|
||||
if(m_dev != NULL) {
|
||||
if(rtlsdr_set_center_freq(m_dev, m_generalSettings.m_centerFrequency) != 0)
|
||||
qDebug("osmosdr_set_center_freq(%lld) failed", m_generalSettings.m_centerFrequency);
|
||||
}
|
||||
}
|
||||
if((m_settings.m_gain != settings.m_gain) || force) {
|
||||
m_settings.m_gain = settings.m_gain;
|
||||
if(m_dev != NULL) {
|
||||
if(rtlsdr_set_tuner_gain(m_dev, m_settings.m_gain) != 0)
|
||||
qDebug("rtlsdr_set_tuner_gain() failed");
|
||||
}
|
||||
}
|
||||
if((m_settings.m_decimation != settings.m_decimation) || force) {
|
||||
m_settings.m_decimation = settings.m_decimation;
|
||||
if(m_dev != NULL)
|
||||
m_rtlSDRThread->setDecimation(m_settings.m_decimation);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// //
|
||||
// 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 //
|
||||
// //
|
||||
// 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 INCLUDE_RTLSDRINPUT_H
|
||||
#define INCLUDE_RTLSDRINPUT_H
|
||||
|
||||
#include "dsp/samplesource/samplesource.h"
|
||||
#include <rtl-sdr.h>
|
||||
#include <QString>
|
||||
|
||||
class RTLSDRThread;
|
||||
|
||||
class RTLSDRInput : public SampleSource {
|
||||
public:
|
||||
struct Settings {
|
||||
qint32 m_gain;
|
||||
qint32 m_decimation;
|
||||
|
||||
Settings();
|
||||
void resetToDefaults();
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
};
|
||||
|
||||
class MsgConfigureRTLSDR : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
const GeneralSettings& getGeneralSettings() const { return m_generalSettings; }
|
||||
const Settings& getSettings() const { return m_settings; }
|
||||
|
||||
static MsgConfigureRTLSDR* create(const GeneralSettings& generalSettings, const Settings& settings)
|
||||
{
|
||||
return new MsgConfigureRTLSDR(generalSettings, settings);
|
||||
}
|
||||
|
||||
private:
|
||||
GeneralSettings m_generalSettings;
|
||||
Settings m_settings;
|
||||
|
||||
MsgConfigureRTLSDR(const GeneralSettings& generalSettings, const Settings& settings) :
|
||||
Message(),
|
||||
m_generalSettings(generalSettings),
|
||||
m_settings(settings)
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgReportRTLSDR : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
const std::vector<int>& getGains() const { return m_gains; }
|
||||
|
||||
static MsgReportRTLSDR* create(const std::vector<int>& gains)
|
||||
{
|
||||
return new MsgReportRTLSDR(gains);
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<int> m_gains;
|
||||
|
||||
MsgReportRTLSDR(const std::vector<int>& gains) :
|
||||
Message(),
|
||||
m_gains(gains)
|
||||
{ }
|
||||
};
|
||||
|
||||
RTLSDRInput(MessageQueue* msgQueueToGUI);
|
||||
~RTLSDRInput();
|
||||
|
||||
bool startInput(int device);
|
||||
void stopInput();
|
||||
|
||||
const QString& getDeviceDescription() const;
|
||||
int getSampleRate() const;
|
||||
quint64 getCenterFrequency() const;
|
||||
|
||||
bool handleMessage(Message* message);
|
||||
|
||||
private:
|
||||
QMutex m_mutex;
|
||||
Settings m_settings;
|
||||
rtlsdr_dev_t* m_dev;
|
||||
RTLSDRThread* m_rtlSDRThread;
|
||||
QString m_deviceDescription;
|
||||
std::vector<int> m_gains;
|
||||
|
||||
bool applySettings(const GeneralSettings& generalSettings, const Settings& settings, bool force);
|
||||
};
|
||||
|
||||
#endif // INCLUDE_RTLSDRINPUT_H
|
||||
@@ -0,0 +1,67 @@
|
||||
#include <QtPlugin>
|
||||
#include <QAction>
|
||||
#include <rtl-sdr.h>
|
||||
#include "plugin/pluginapi.h"
|
||||
#include "util/simpleserializer.h"
|
||||
#include "rtlsdrplugin.h"
|
||||
#include "rtlsdrgui.h"
|
||||
|
||||
const PluginDescriptor RTLSDRPlugin::m_pluginDescriptor = {
|
||||
QString("RTL-SDR Input"),
|
||||
QString("---"),
|
||||
QString("(c) librtlsdr Authors (see source URL)"),
|
||||
QString("http://sdr.osmocom.org/trac/wiki/rtl-sdr"),
|
||||
true,
|
||||
QString("http://cgit.osmocom.org/cgit/rtl-sdr")
|
||||
};
|
||||
|
||||
RTLSDRPlugin::RTLSDRPlugin(QObject* parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
const PluginDescriptor& RTLSDRPlugin::getPluginDescriptor() const
|
||||
{
|
||||
return m_pluginDescriptor;
|
||||
}
|
||||
|
||||
void RTLSDRPlugin::initPlugin(PluginAPI* pluginAPI)
|
||||
{
|
||||
m_pluginAPI = pluginAPI;
|
||||
|
||||
m_pluginAPI->registerSampleSource("org.osmocom.sdr.samplesource.rtl-sdr", this);
|
||||
}
|
||||
|
||||
PluginInterface::SampleSourceDevices RTLSDRPlugin::enumSampleSources()
|
||||
{
|
||||
SampleSourceDevices result;
|
||||
int count = rtlsdr_get_device_count();
|
||||
char vendor[256];
|
||||
char product[256];
|
||||
char serial[256];
|
||||
|
||||
for(int i = 0; i < count; i++) {
|
||||
vendor[0] = '\0';
|
||||
product[0] = '\0';
|
||||
serial[0] = '\0';
|
||||
|
||||
if(rtlsdr_get_device_usb_strings(i, vendor, product, serial) != 0)
|
||||
continue;
|
||||
QString displayedName(QString("RTL-SDR #%1 (%2 #%3)").arg(i + 1).arg(product).arg(serial));
|
||||
SimpleSerializer s(1);
|
||||
s.writeS32(1, i);
|
||||
result.append(SampleSourceDevice(displayedName, "org.osmocom.sdr.samplesource.rtl-sdr", s.final()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
PluginGUI* RTLSDRPlugin::createSampleSource(const QString& sourceName, const QByteArray& address)
|
||||
{
|
||||
if(sourceName == "org.osmocom.sdr.samplesource.rtl-sdr") {
|
||||
RTLSDRGui* gui = new RTLSDRGui(m_pluginAPI);
|
||||
m_pluginAPI->setInputGUI(gui);
|
||||
return gui;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef INCLUDE_RTLSDRPLUGIN_H
|
||||
#define INCLUDE_RTLSDRPLUGIN_H
|
||||
|
||||
#include <QObject>
|
||||
#include "plugin/plugininterface.h"
|
||||
|
||||
class RTLSDRPlugin : public QObject, PluginInterface {
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(PluginInterface)
|
||||
Q_PLUGIN_METADATA(IID "org.osmocom.sdr.samplesource.rtl-sdr")
|
||||
|
||||
public:
|
||||
explicit RTLSDRPlugin(QObject* parent = NULL);
|
||||
|
||||
const PluginDescriptor& getPluginDescriptor() const;
|
||||
void initPlugin(PluginAPI* pluginAPI);
|
||||
|
||||
SampleSourceDevices enumSampleSources();
|
||||
PluginGUI* createSampleSource(const QString& sourceName, const QByteArray& address);
|
||||
|
||||
private:
|
||||
static const PluginDescriptor m_pluginDescriptor;
|
||||
|
||||
PluginAPI* m_pluginAPI;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_RTLSDRPLUGIN_H
|
||||
@@ -0,0 +1,172 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// //
|
||||
// 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 //
|
||||
// //
|
||||
// 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 <stdio.h>
|
||||
#include <errno.h>
|
||||
#include "rtlsdrthread.h"
|
||||
#include "dsp/samplefifo.h"
|
||||
|
||||
#define BLOCKSIZE 16384
|
||||
|
||||
RTLSDRThread::RTLSDRThread(rtlsdr_dev_t* dev, SampleFifo* sampleFifo, QObject* parent) :
|
||||
QThread(parent),
|
||||
m_running(false),
|
||||
m_dev(dev),
|
||||
m_convertBuffer(BLOCKSIZE),
|
||||
m_sampleFifo(sampleFifo),
|
||||
m_decimation(1)
|
||||
{
|
||||
}
|
||||
|
||||
RTLSDRThread::~RTLSDRThread()
|
||||
{
|
||||
stopWork();
|
||||
}
|
||||
|
||||
void RTLSDRThread::startWork()
|
||||
{
|
||||
m_startWaitMutex.lock();
|
||||
start();
|
||||
while(!m_running)
|
||||
m_startWaiter.wait(&m_startWaitMutex, 100);
|
||||
m_startWaitMutex.unlock();
|
||||
}
|
||||
|
||||
void RTLSDRThread::stopWork()
|
||||
{
|
||||
m_running = false;
|
||||
wait();
|
||||
}
|
||||
|
||||
void RTLSDRThread::setDecimation(int decimation)
|
||||
{
|
||||
m_decimation = decimation;
|
||||
}
|
||||
|
||||
void RTLSDRThread::run()
|
||||
{
|
||||
int res;
|
||||
|
||||
m_running = true;
|
||||
m_startWaiter.wakeAll();
|
||||
|
||||
while(m_running) {
|
||||
if((res = rtlsdr_read_async(m_dev, &RTLSDRThread::callbackHelper, this, 16, 2 * BLOCKSIZE)) < 0) {
|
||||
qCritical("RTLSDRThread: async error: %s", strerror(errno));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_running = false;
|
||||
}
|
||||
|
||||
void RTLSDRThread::decimate2(SampleVector::iterator* it, const quint8* buf, qint32 len)
|
||||
{
|
||||
for(int pos = 0; pos < len; pos += 2) {
|
||||
Sample s((((qint8)buf[pos]) - 128) << 8, (((qint8)buf[pos + 1]) - 128) << 8);
|
||||
if(m_decimator2.workDecimateCenter(&s)) {
|
||||
**it = s;
|
||||
++(*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RTLSDRThread::decimate4(SampleVector::iterator* it, const quint8* buf, qint32 len)
|
||||
{
|
||||
for(int pos = 0; pos < len; pos += 2) {
|
||||
Sample s((((qint8)buf[pos]) - 128) << 8, (((qint8)buf[pos + 1]) - 128) << 8);
|
||||
if(m_decimator2.workDecimateCenter(&s)) {
|
||||
if(m_decimator4.workDecimateCenter(&s)) {
|
||||
**it = s;
|
||||
++(*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RTLSDRThread::decimate8(SampleVector::iterator* it, const quint8* buf, qint32 len)
|
||||
{
|
||||
for(int pos = 0; pos < len; pos += 2) {
|
||||
Sample s((((qint8)buf[pos]) - 128) << 8, (((qint8)buf[pos + 1]) - 128) << 8);
|
||||
if(m_decimator2.workDecimateCenter(&s)) {
|
||||
if(m_decimator4.workDecimateCenter(&s)) {
|
||||
if(m_decimator8.workDecimateCenter(&s)) {
|
||||
**it = s;
|
||||
++(*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RTLSDRThread::decimate16(SampleVector::iterator* it, const quint8* buf, qint32 len)
|
||||
{
|
||||
for(int pos = 0; pos < len; pos += 2) {
|
||||
Sample s((((qint8)buf[pos]) - 128) << 8, (((qint8)buf[pos + 1]) - 128) << 8);
|
||||
if(m_decimator2.workDecimateCenter(&s)) {
|
||||
if(m_decimator4.workDecimateCenter(&s)) {
|
||||
if(m_decimator8.workDecimateCenter(&s)) {
|
||||
if(m_decimator16.workDecimateCenter(&s)) {
|
||||
**it = s;
|
||||
++(*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RTLSDRThread::callback(const quint8* buf, qint32 len)
|
||||
{
|
||||
SampleVector::iterator it = m_convertBuffer.begin();
|
||||
|
||||
switch(m_decimation) {
|
||||
case 0: // 1:1 = no decimation
|
||||
for(int pos = 0; pos < len; pos += 2) {
|
||||
*it = Sample((((qint8)buf[pos]) - 128) << 8, (((qint8)buf[pos + 1]) - 128) << 8);
|
||||
++it;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1: // 1:2
|
||||
decimate2(&it, buf, len);
|
||||
break;
|
||||
|
||||
case 2: // 1:4
|
||||
decimate4(&it, buf, len);
|
||||
break;
|
||||
|
||||
case 3: // 1:8
|
||||
decimate8(&it, buf, len);
|
||||
break;
|
||||
|
||||
case 4: // 1:16
|
||||
decimate16(&it, buf, len);
|
||||
break;
|
||||
}
|
||||
|
||||
m_sampleFifo->write(m_convertBuffer.begin(), it);
|
||||
|
||||
if(!m_running)
|
||||
rtlsdr_cancel_async(m_dev);
|
||||
}
|
||||
|
||||
void RTLSDRThread::callbackHelper(unsigned char* buf, uint32_t len, void* ctx)
|
||||
{
|
||||
RTLSDRThread* thread = (RTLSDRThread*)ctx;
|
||||
thread->callback(buf, len);
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// //
|
||||
// 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 //
|
||||
// //
|
||||
// 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 INCLUDE_RTLSDRTHREAD_H
|
||||
#define INCLUDE_RTLSDRTHREAD_H
|
||||
|
||||
#include <QThread>
|
||||
#include <QMutex>
|
||||
#include <QWaitCondition>
|
||||
#include <rtl-sdr.h>
|
||||
#include "dsp/samplefifo.h"
|
||||
#include "dsp/inthalfbandfilter.h"
|
||||
|
||||
class RTLSDRThread : public QThread {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RTLSDRThread(rtlsdr_dev_t* dev, SampleFifo* sampleFifo, QObject* parent = NULL);
|
||||
~RTLSDRThread();
|
||||
|
||||
void startWork();
|
||||
void stopWork();
|
||||
|
||||
void setDecimation(int decimation);
|
||||
|
||||
private:
|
||||
QMutex m_startWaitMutex;
|
||||
QWaitCondition m_startWaiter;
|
||||
bool m_running;
|
||||
|
||||
rtlsdr_dev_t* m_dev;
|
||||
SampleVector m_convertBuffer;
|
||||
SampleFifo* m_sampleFifo;
|
||||
|
||||
int m_decimation;
|
||||
|
||||
IntHalfbandFilter m_decimator2;
|
||||
IntHalfbandFilter m_decimator4;
|
||||
IntHalfbandFilter m_decimator8;
|
||||
IntHalfbandFilter m_decimator16;
|
||||
|
||||
void run();
|
||||
|
||||
void decimate2(SampleVector::iterator* it, const quint8* buf, qint32 len);
|
||||
void decimate4(SampleVector::iterator* it, const quint8* buf, qint32 len);
|
||||
void decimate8(SampleVector::iterator* it, const quint8* buf, qint32 len);
|
||||
void decimate16(SampleVector::iterator* it, const quint8* buf, qint32 len);
|
||||
void callback(const quint8* buf, qint32 len);
|
||||
|
||||
static void callbackHelper(unsigned char* buf, uint32_t len, void* ctx);
|
||||
};
|
||||
|
||||
#endif // INCLUDE_RTLSDRTHREAD_H
|
||||
Reference in New Issue
Block a user