major rework and move to CMake

This commit is contained in:
Christian Daniel 2012-10-29 21:42:52 +01:00
parent aad7684645
commit cb5ca682c2
44 changed files with 2101 additions and 361 deletions

11
.gitignore vendored
View File

@ -1,10 +1 @@
tmp
Makefile
local.pri
sdrangelove
*.o
*.pro.user
portaudio
dsp/kiss_fft129
hardware/unused
CMakeLists.txt.user

123
CMakeLists.txt Normal file
View File

@ -0,0 +1,123 @@
cmake_minimum_required(VERSION 2.6)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules)
project(sdrangelove)
set(CMAKE_BUILD_TYPE "Release")
set(QT_USE_QTOPENGL TRUE)
find_package(Qt4 REQUIRED)
find_package(OpenGL REQUIRED)
find_package(PkgConfig)
find_package(LibOsmoSDR REQUIRED)
find_package(Portaudio REQUIRED)
set(sdrangelove_SOURCES
main.cpp
mainwindow.cpp
osdrupgrade.cpp
settings.cpp
dsp/channelizer.cpp
dsp/dspengine.cpp
dsp/fftwindow.cpp
dsp/interpolator.cpp
dsp/lowpass.cpp
dsp/nco.cpp
dsp/pidcontroller.cpp
dsp/samplesink.cpp
dsp/spectrum.cpp
gui/glspectrum.cpp
gui/indicator.cpp
gui/scale.cpp
gui/scaleengine.cpp
gui/valuedial.cpp
gui/viewtoolbox.cpp
hardware/audiofifo.cpp
hardware/audiooutput.cpp
hardware/osmosdrinput.cpp
hardware/osmosdrthread.cpp
hardware/samplefifo.cpp
hardware/samplesource.cpp
hardware/soundcardinfo.cpp
)
set(sdrangelove_HEADERS
mainwindow.h
osdrupgrade.h
settings.h
dsp/channelizer.h
dsp/dspengine.h
dsp/dsptypes.h
dsp/fftwindow.h
dsp/interpolator.h
dsp/kissfft.h
dsp/lowpass.h
dsp/nco.h
dsp/pidcontroller.h
dsp/samplesink.h
dsp/spectrum.h
gui/glspectrum.h
gui/indicator.h
gui/physicalunit.h
gui/scale.h
gui/scaleengine.h
gui/valuedial.h
gui/viewtoolbox.h
hardware/audiofifo.h
hardware/audiooutput.h
hardware/osmosdrinput.h
hardware/osmosdrthread.h
hardware/samplefifo.h
hardware/samplesource.h
hardware/soundcardinfo.h
)
set(sdrangelove_FORMS
mainwindow.ui
osdrupgrade.ui
gui/viewtoolbox.ui
)
set(sdrangelove_RESOURCES
resources/res.qrc
)
include_directories(
.
portaudio/include
${CMAKE_CURRENT_BINARY_DIR}
${OPENGL_INCLUDE_DIR}
${LIBOSMOSDR_INCLUDE_DIR}
${PORTAUDIO_INCLUDE_DIRS}
)
include(${QT_USE_FILE})
add_definitions(${QT_DEFINITIONS})
qt4_wrap_cpp(sdrangelove_HEADERS_MOC ${sdrangelove_HEADERS})
qt4_wrap_cpp(sdrangelove_HEADERS_MOC ${sdrangelove_HEADERS})
qt4_wrap_ui(sdrangelove_FORMS_HEADERS ${sdrangelove_FORMS})
qt4_add_resources(sdrangelove_RESOURCES_RCC ${sdrangelove_RESOURCES})
add_executable(sdrangelove
${sdrangelove_SOURCES}
${sdrangelove_HEADERS_MOC}
${sdrangelove_HEADERS_MOC}
${sdrangelove_FORMS_HEADERS}
${sdrangelove_RESOURCES_RCC}
)
target_link_libraries(sdrangelove
${QT_LIBRARIES}
${OPENGL_LIBRARIES}
${LIBOSMOSDR_LIBRARIES}
${PORTAUDIO_LIBRARIES}
)

View File

@ -0,0 +1,27 @@
if(NOT LIBOSMOSDR_FOUND)
pkg_check_modules (LIBOSMOSDR_PKG libosmosdr)
find_path(LIBOSMOSDR_INCLUDE_DIR NAMES osmosdr.h
PATHS
${LIBOSMOSDR_PKG_INCLUDE_DIRS}
/usr/include
/usr/local/include
)
find_library(LIBOSMOSDR_LIBRARIES NAMES osmosdr
PATHS
${LIBOSMOSDR_PKG_LIBRARY_DIRS}
/usr/lib
/usr/local/lib
)
if(LIBOSMOSDR_INCLUDE_DIR AND LIBOSMOSDR_LIBRARIES)
set(LIBOSMOSDR_FOUND TRUE CACHE INTERNAL "libosmosdr found")
message(STATUS "Found libosmosdr: ${LIBOSMOSDR_INCLUDE_DIR}, ${LIBOSMOSDR_LIBRARIES}")
else(LIBOSMOSDR_INCLUDE_DIR AND LIBOSMOSDR_LIBRARIES)
set(LIBOSMOSDR_FOUND FALSE CACHE INTERNAL "libosmosdr found")
message(STATUS "libosmosdr not found.")
endif(LIBOSMOSDR_INCLUDE_DIR AND LIBOSMOSDR_LIBRARIES)
mark_as_advanced(LIBOSMOSDR_INCLUDE_DIR LIBOSMOSDR_LIBRARIES)
endif(NOT LIBOSMOSDR_FOUND)

View File

@ -0,0 +1,35 @@
# - Try to find Portaudio
# Once done this will define
#
# PORTAUDIO_FOUND - system has Portaudio
# PORTAUDIO_INCLUDE_DIRS - the Portaudio include directory
# PORTAUDIO_LIBRARIES - Link these to use Portaudio
include(FindPkgConfig)
pkg_check_modules(PC_PORTAUDIO portaudio)
find_path(PORTAUDIO_INCLUDE_DIRS
NAMES
portaudio.h
PATHS
/usr/local/include
/usr/include
HINTS
${PC_PORTAUDIO_INCLUDE_DIR}
)
find_library(PORTAUDIO_LIBRARIES
NAMES
portaudio
PATHS
/usr/local/lib
/usr/lib
/usr/lib64
HINTS
${PC_PORTAUDIO_LIBDIR}
)
mark_as_advanced(PORTAUDIO_INCLUDE_DIRS PORTAUDIO_LIBRARIES)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(PORTAUDIO DEFAULT_MSG PORTAUDIO_INCLUDE_DIRS PORTAUDIO_LIBRARIES)

View File

@ -1,5 +1,122 @@
#include <QTime>
#include <stdio.h>
#include "channelizer.h"
#include "hardware/audiooutput.h"
Channelizer::Channelizer()
{
m_spectrum.configure(128 , 25, FFTWindow::Bartlett);
m_buffer.resize(2048);
m_bufferFill = 0;
m_nco.setFreq(125000, 500000);
m_interpolator.create(51, 32, 32 * 500000, 150000 / 2);
m_distance = 500000.0 / 176400.0;
m_interpolator2.create(19, 8, 8 * 176400, 16000 / 2);
m_distance2 = 4;
m_audioFifo.setSize(4, 44100 / 2 * 4);
m_audioOutput = new AudioOutput;
m_audioOutput->start(0, 44100, &m_audioFifo);
m_resampler = 1.0;
m_resamplerCtrl.setup(0.00001, 0, -0.00001);
}
Channelizer::~Channelizer()
{
m_audioOutput->stop();
delete m_audioOutput;
}
void Channelizer::setGLSpectrum(GLSpectrum* glSpectrum)
{
m_spectrum.setGLSpectrum(glSpectrum);
}
size_t Channelizer::workUnitSize()
{
return m_buffer.size();
}
size_t Channelizer::work(SampleVector::const_iterator begin, SampleVector::const_iterator end)
{
int buffered = m_audioOutput->bufferedSamples();
if(m_audioFifo.fill() < (m_audioFifo.size() / 6)) {
while(m_audioFifo.fill() < (m_audioFifo.size() / 2)) {
quint32 d = 0;
m_audioFifo.write((quint8*)&d, 4);
}
qDebug("underflow - fill %d (vs %d)", m_audioFifo.fill(), m_audioFifo.size() / 4 / 2);
}
buffered = m_audioOutput->bufferedSamples();
int fill = m_audioFifo.fill() / 4 + buffered;
float err = (float)fill / ((m_audioFifo.size() / 4) / 2);
float ctrl = m_resamplerCtrl.feed(err);
//float resamplerRate = (ctrl / 1.0);
float resamplerRate = err;
if(resamplerRate < 0.9999)
resamplerRate = 0.9999;
else if(resamplerRate > 1.0001)
resamplerRate = 1.0001;
m_resampler = m_resampler * 0.99 + resamplerRate * 0.01;
//m_resampler = resamplerRate;
if(m_resampler < 0.995)
m_resampler = 0.995;
else if(m_resampler > 1.005)
m_resampler = 1.005;
//qDebug("%lld %5d %f %f %f", QDateTime::currentMSecsSinceEpoch(), fill, ctrl, m_resampler, err);
struct AudioSample {
qint16 l;
qint16 r;
};
size_t count = end - begin;
Complex ci;
bool consumed;
bool consumed2;
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_distance, c, &consumed, &ci)) {
Complex d = ci * conj(m_lastSample);
m_lastSample = ci;
//Complex demod(atan2(d.imag(), d.real()) * 0.5, 0);
Real demod = atan2(d.imag(), d.real()) / M_PI;
consumed2 = false;
c = Complex(demod, 0);
while(!consumed2) {
if(m_interpolator2.interpolate(&m_distance2, c, &consumed2, &ci)) {
m_buffer[m_bufferFill++] = Sample(ci.real() * 32767.0, 0.0);
AudioSample s;
s.l = ci.real() * 32767.0;
s.r = s.l;
m_audioFifo.write((quint8*)&s, 4, 1);
if(m_bufferFill >= m_buffer.size()) {
m_spectrum.feed(m_buffer.begin(), m_buffer.end());
m_bufferFill = 0;
}
m_distance2 += 4.0 * m_resampler;
}
}
m_distance += 500000 / 176400.0;
}
}
return count;
}

View File

@ -1,11 +1,42 @@
#ifndef INCLUDE_CHANNELIZER_H
#define INCLUDE_CHANNELIZER_H
class Channelizer {
#include "samplesink.h"
#include "spectrum.h"
#include "nco.h"
#include "interpolator.h"
#include "pidcontroller.h"
#include "hardware/audiofifo.h"
class AudioOutput;
class Channelizer : public SampleSink {
public:
Channelizer();
~Channelizer();
void setGLSpectrum(GLSpectrum* glSpectrum);
size_t workUnitSize();
size_t work(SampleVector::const_iterator begin, SampleVector::const_iterator end);
private:
NCO m_nco;
Interpolator m_interpolator;
Real m_distance;
Interpolator m_interpolator2;
Real m_distance2;
SampleVector m_buffer;
size_t m_bufferFill;
Complex m_lastSample;
AudioOutput* m_audioOutput;
AudioFifo m_audioFifo;
Real m_resampler;
PIDController m_resamplerCtrl;
Spectrum m_spectrum;
};
#endif // INCLUDE_CHANNELIZER_H

View File

@ -18,44 +18,20 @@
#include <stdio.h>
#include "dspengine.h"
#include "settings.h"
#include "channelizer.h"
#include "hardware/osmosdrinput.h"
#include "hardware/samplefifo.h"
#include "gui/glspectrum.h"
#if 0
static int isqrt(int n)
{
int i, j;
if(n <= 0)
return 0;
if(n > 0xffffff)
i = n >> 16;
else if(n > 0xffff)
i = n >> 12;
else if(n > 0xff)
i = n >> 8;
else i = n >> 4;
if(i <= 0)
i = 1;
do {
j = i;
i = (j + n / j) / 2;
} while (((i - j) >= 2) || ((j-i) >= 2));
return i;
}
#endif
DSPEngine::DSPEngine(Settings* settings, QObject* parent) :
QThread(parent),
m_debugEvent(false),
m_settings(settings),
m_state(StNotStarted),
m_nextState(StIdle),
m_sampleFifo(NULL),
m_channelizerToAdd(NULL),
m_channelizerToRemove(NULL),
m_sampleFifo(),
m_sampleSource(NULL)
{
moveToThread(this);
@ -114,6 +90,32 @@ void DSPEngine::stopAcquistion()
m_stateWaitMutex.unlock();
}
bool DSPEngine::addChannelizer(Channelizer* channelizer)
{
if(!isRunning())
return false;
m_stateWaitMutex.lock();
m_channelizerToAdd = channelizer;
while(m_channelizerToAdd != NULL)
m_stateWaiter.wait(&m_stateWaitMutex, 100);
m_stateWaitMutex.unlock();
return true;
}
bool DSPEngine::removeChannelizer(Channelizer* channelizer)
{
if(!isRunning())
return false;
m_stateWaitMutex.lock();
m_channelizerToRemove = channelizer;
while(m_channelizerToRemove != NULL)
m_stateWaiter.wait(&m_stateWaitMutex, 100);
m_stateWaitMutex.unlock();
return true;
}
void DSPEngine::triggerDebug()
{
m_debugEvent = true;
@ -127,8 +129,18 @@ QString DSPEngine::errorMsg()
return res;
}
QString DSPEngine::deviceDesc()
{
QMutexLocker mutexLocker(&m_deviceDescMutex);
QString res = m_deviceDesc;
res.detach();
return res;
}
void DSPEngine::run()
{
connect(&m_sampleFifo, SIGNAL(dataReady()), this, SLOT(handleData()), Qt::QueuedConnection);
m_ready = createMembers();
m_state = StIdle;
@ -193,7 +205,7 @@ void DSPEngine::imbalance(SampleVector::iterator begin, SampleVector::iterator e
// calculate imbalance as Q15.16
if(m_qRange != 0)
m_imbalance = (m_iRange << 16) / m_qRange;
m_imbalance = ((uint)m_iRange << 16) / (uint)m_qRange;
// correct imbalance and convert back to signed int 16
for(SampleVector::iterator it = begin; it < end; it++)
@ -204,40 +216,79 @@ void DSPEngine::work()
{
size_t wus;
size_t maxWorkUnitSize = 0;
int count = 0;
size_t samplesDone = 0;
wus = m_spectrum.workUnitSize();
if(wus > maxWorkUnitSize)
maxWorkUnitSize = wus;
for(Channelizers::const_iterator it = m_channelizers.begin(); it != m_channelizers.end(); it++) {
wus = (*it)->workUnitSize();
if(wus > maxWorkUnitSize)
maxWorkUnitSize = wus;
}
while((m_sampleFifo->fill() > maxWorkUnitSize) && (m_state == m_nextState) && (count < m_sampleRate)) {
while((m_sampleFifo.fill() > maxWorkUnitSize) && (m_state == m_nextState) && (samplesDone < m_sampleRate)) {
SampleVector::iterator part1begin;
SampleVector::iterator part1end;
SampleVector::iterator part2begin;
SampleVector::iterator part2end;
size_t count = m_sampleFifo->readBegin(m_sampleFifo->fill(), &part1begin, &part1end, &part2begin, &part2end);
size_t count = m_sampleFifo.readBegin(m_sampleFifo.fill(), &part1begin, &part1end, &part2begin, &part2end);
// first part of FIFO data
if(part1begin != part1end) {
// correct stuff
if(m_settings.dcOffsetCorrection())
dcOffset(part1begin, part1end);
if(m_settings.iqImbalanceCorrection())
imbalance(part1begin, part1end);
// feed data to handlers
m_spectrum.feed(part1begin, part1end);
for(Channelizers::const_iterator it = m_channelizers.begin(); it != m_channelizers.end(); it++)
(*it)->feed(part1begin, part1end);
}
// second part of FIFO data (used when block wraps around)
if(part2begin != part2end) {
// correct stuff
if(m_settings.dcOffsetCorrection())
dcOffset(part2begin, part2end);
if(m_settings.iqImbalanceCorrection())
imbalance(part2begin, part2end);
// feed data to handlers
m_spectrum.feed(part2begin, part2end);
for(Channelizers::const_iterator it = m_channelizers.begin(); it != m_channelizers.end(); it++)
(*it)->feed(part1begin, part1end);
}
m_sampleFifo->readCommit(count);
// adjust FIFO pointers
m_sampleFifo.readCommit(count);
samplesDone += count;
}
// check if the center frequency has changed (has to be responsive)
if(m_settings.isModifiedCenterFreq())
m_sampleSource->setCenterFrequency(m_settings.centerFreq());
// check if decimation has changed (needed to be done here, because to high a sample rate can clog the switch)
if(m_settings.isModifiedDecimation()) {
m_sampleSource->setDecimation(m_settings.decimation());
m_sampleRate = 4000000 / (1 << m_settings.decimation());
qDebug("New rate: %d", m_sampleRate);
}
}
void DSPEngine::applyChannelizers()
{
// check for channelizers to add or remove
if(m_channelizerToAdd != NULL) {
m_channelizers.push_back(m_channelizerToAdd);
m_channelizerToAdd = NULL;
m_stateWaiter.wakeAll();
}
if(m_channelizerToRemove != NULL) {
m_channelizers.remove(m_channelizerToRemove);
m_channelizerToRemove = NULL;
m_stateWaiter.wakeAll();
}
}
void DSPEngine::applyConfig()
@ -246,12 +297,6 @@ void DSPEngine::applyConfig()
if(m_settings.isModifiedIQSwap())
m_sampleSource->setIQSwap(m_settings.iqSwap());
if(m_settings.isModifiedDecimation()) {
m_sampleSource->setDecimation(m_settings.decimation());
m_sampleRate = 4000000 / (1 << m_settings.decimation());
qDebug("New rate: %d", m_sampleRate);
}
if(m_settings.isModifiedFFTSize() || m_settings.isModifiedFFTOverlap() || m_settings.isModifiedFFTWindow()) {
m_spectrum.configure(m_settings.fftSize(), m_settings.fftOverlap(), (FFTWindow::Function)m_settings.fftWindow());
}
@ -285,6 +330,14 @@ void DSPEngine::applyConfig()
((OsmoSDRInput*)m_sampleSource)->setE4000ifStageGain(5, m_settings.e4000if5());
if(m_settings.isModifiedE4000if6())
((OsmoSDRInput*)m_sampleSource)->setE4000ifStageGain(6, m_settings.e4000if6());
if(m_settings.isModifiedFilterI1() || m_settings.isModifiedFilterI2() || m_settings.isModifiedFilterQ1() || m_settings.isModifiedFilterQ2()) {
((OsmoSDRInput*)m_sampleSource)->setFilter(
m_settings.filterI1(),
m_settings.filterI2(),
m_settings.filterQ1(),
m_settings.filterQ2());
}
}
void DSPEngine::changeState()
@ -330,6 +383,9 @@ DSPEngine::State DSPEngine::gotoIdle()
return StIdle;
m_sampleSource->stopInput();
m_deviceDescMutex.lock();
m_deviceDesc.clear();
m_deviceDescMutex.unlock();
return StIdle;
}
@ -366,6 +422,10 @@ DSPEngine::State DSPEngine::gotoRunning()
m_settings.isModifiedE4000if4();
m_settings.isModifiedE4000if5();
m_settings.isModifiedE4000if6();
m_settings.isModifiedFilterI1();
m_settings.isModifiedFilterI2();
m_settings.isModifiedFilterQ1();
m_settings.isModifiedFilterQ2();
m_sampleRate = 4000000 / (1 << m_settings.decimation());
qDebug("current rate: %d", m_sampleRate);
@ -377,11 +437,15 @@ DSPEngine::State DSPEngine::gotoRunning()
m_iRange = 1 << 16;
m_qRange = 1 << 16;
if(!m_sampleFifo->setSize(2 * 500000))
return gotoError("could not allocate SampleFifo");
if(!m_sampleFifo.setSize(2 * 500000))
return gotoError("Could not allocate SampleFifo");
if(!m_sampleSource->startInput(0, 4000000))
return gotoError("could not start OsmoSDR");
return gotoError("Could not start OsmoSDR");
m_deviceDescMutex.lock();
m_deviceDesc = m_sampleSource->deviceDesc();
m_deviceDescMutex.unlock();
m_sampleSource->setCenterFrequency(m_settings.centerFreq());
m_sampleSource->setIQSwap(m_settings.iqSwap());
@ -395,6 +459,11 @@ DSPEngine::State DSPEngine::gotoRunning()
((OsmoSDRInput*)m_sampleSource)->setE4000ifStageGain(4, m_settings.e4000if4());
((OsmoSDRInput*)m_sampleSource)->setE4000ifStageGain(5, m_settings.e4000if5());
((OsmoSDRInput*)m_sampleSource)->setE4000ifStageGain(6, m_settings.e4000if6());
((OsmoSDRInput*)m_sampleSource)->setFilter(
m_settings.filterI1(),
m_settings.filterI2(),
m_settings.filterQ1(),
m_settings.filterQ2());
return StRunning;
}
@ -404,21 +473,20 @@ DSPEngine::State DSPEngine::gotoError(const QString& errorMsg)
QMutexLocker mutexLocker(&m_errorMsgMutex);
m_errorMsg = errorMsg;
m_state = StError;
m_deviceDescMutex.lock();
m_deviceDesc.clear();
m_deviceDescMutex.unlock();
return StError;
}
bool DSPEngine::createMembers()
{
if((m_sampleFifo = new SampleFifo(this)) == NULL)
return false;
connect(m_sampleFifo, SIGNAL(dataReady()), this, SLOT(handleData()), Qt::QueuedConnection);
if((m_timer = new QTimer(this)) == NULL)
return false;
connect(m_timer, SIGNAL(timeout()), this, SLOT(tick()));
m_timer->start(250);
if((m_sampleSource = new OsmoSDRInput(m_sampleFifo)) == NULL)
if((m_sampleSource = new OsmoSDRInput(&m_sampleFifo)) == NULL)
return false;
return true;
@ -430,10 +498,9 @@ void DSPEngine::destroyMembers()
delete m_sampleSource;
m_sampleSource = NULL;
}
if(m_sampleFifo != NULL) {
delete m_sampleFifo;
m_sampleFifo = NULL;
}
m_deviceDescMutex.lock();
m_deviceDesc.clear();
m_deviceDescMutex.unlock();
}
void DSPEngine::handleData()
@ -456,6 +523,8 @@ void DSPEngine::tick()
qDebug("New state: %d: %s", m_state, stateNames[m_state]);
}
applyChannelizers();
switch(m_state) {
case StNotStarted:
exit();

View File

@ -26,11 +26,13 @@
#include "kissfft.h"
#include "fftwindow.h"
#include "settings.h"
#include "hardware/samplefifo.h"
#include "spectrum.h"
class SampleSource;
class SampleFifo;
class GLSpectrum;
class Channelizer;
class DSPEngine : public QThread {
Q_OBJECT
@ -54,15 +56,19 @@ public:
bool startAcquisition();
void stopAcquistion();
bool addChannelizer(Channelizer* channelizer);
bool removeChannelizer(Channelizer* channelizer);
void triggerDebug();
State state() const { return m_state; }
QString errorMsg();
private:
typedef kissfft<Real, Complex> KissFFT;
QString deviceDesc();
private:
bool m_debugEvent;
Settings m_settings;
State m_state;
@ -75,8 +81,16 @@ private:
QMutex m_errorMsgMutex;
QString m_errorMsg;
QMutex m_deviceDescMutex;
QString m_deviceDesc;
Channelizer* m_channelizerToAdd;
Channelizer* m_channelizerToRemove;
typedef std::list<Channelizer*> Channelizers;
SampleFifo m_sampleFifo;
Channelizers m_channelizers;
QTimer* m_timer;
SampleFifo* m_sampleFifo;
SampleSource* m_sampleSource;
int m_sampleRate;
@ -95,6 +109,7 @@ private:
void imbalance(SampleVector::iterator begin, SampleVector::iterator end);
void work();
void applyChannelizers();
void applyConfig();
void changeState();

View File

@ -21,7 +21,7 @@ void FFTWindow::create(Function function, int n)
{
Real (*wFunc)(Real n, Real i);
m_window.resize(n);
m_window.clear();
switch(function) {
case Flattop:
@ -51,7 +51,7 @@ void FFTWindow::create(Function function, int n)
}
for(int i = 0; i < n; i++)
m_window[i] = wFunc(n, i);
m_window.push_back(wFunc(n, i));
}
void FFTWindow::apply(const std::vector<Real>& in, std::vector<Real>* out)

View File

@ -15,7 +15,7 @@ void Interpolator::create(int nTaps, int phaseSteps, double sampleRate, double c
Real sum;
// make room
m_samples.resize(nTaps);
m_samples.resize(nTaps * 2);
for(int i = 0; i < nTaps; i++)
m_samples[i] = 0;
m_ptr = 0;

View File

@ -20,7 +20,7 @@ public:
return false;
}
}
interpolate((int)floor(*distance * m_phaseSteps), result);
doInterpolate((int)floor(*distance * (Real)m_phaseSteps), result);
return true;
}
@ -42,7 +42,7 @@ private:
m_samples[m_ptr] = next;
}
void interpolate(int phase, Complex* result)
void doInterpolate(int phase, Complex* result)
{
int sample = m_ptr;
const Real* coeff = &m_taps[phase * m_nTaps];

View File

@ -25,12 +25,10 @@ bool NCO::m_tableInitialized = false;
void NCO::initTable()
{
int i;
if(m_tableInitialized)
return;
for(i = 0; i < TableSize; i++)
for(int i = 0; i < TableSize; i++)
m_table[i] = cos((2.0 * M_PI * i) / TableSize);
m_tableInitialized = true;
@ -51,7 +49,7 @@ void NCO::setFreq(Real freq, Real sampleRate)
float NCO::next()
{
m_phase += m_phaseIncrement;
while(m_phase > TableSize)
while(m_phase >= TableSize)
m_phase -= TableSize;
while(m_phase < 0)
m_phase += TableSize;
@ -62,7 +60,7 @@ float NCO::next()
Complex NCO::nextIQ()
{
m_phase += m_phaseIncrement;
while(m_phase > TableSize)
while(m_phase >= TableSize)
m_phase -= TableSize;
while(m_phase < 0)
m_phase += TableSize;

17
dsp/pidcontroller.cpp Normal file
View File

@ -0,0 +1,17 @@
#include "pidcontroller.h"
PIDController::PIDController() :
m_p(0.0),
m_i(0.0),
m_d(0.0),
m_int(0.0),
m_diff(0.0)
{
}
void PIDController::setup(Real p, Real i, Real d)
{
m_p = p;
m_i = i;
m_d = d;
}

28
dsp/pidcontroller.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef INCLUDE_PIDCONTROLLER_H
#define INCLUDE_PIDCONTROLLER_H
#include "dsptypes.h"
class PIDController {
private:
Real m_p;
Real m_i;
Real m_d;
Real m_int;
Real m_diff;
public:
PIDController();
void setup(Real p, Real i, Real d);
Real feed(Real v)
{
m_int += v * m_i;
Real d = m_d * (m_diff - v);
m_diff = v;
return (v * m_p) + m_int + d;
}
};
#endif // INCLUDE_PIDCONTROLLER_H

View File

@ -1,8 +1,8 @@
#include "samplesink.h"
SampleSink::SampleSink() :
m_buffer(),
m_bufferFill(0)
m_sinkBuffer(),
m_sinkBufferFill(0)
{
}
@ -11,44 +11,44 @@ void SampleSink::feed(SampleVector::const_iterator begin, SampleVector::const_it
size_t wus = workUnitSize();
// make sure our buffer is big enough for at least one work unit
if(m_buffer.size() < wus)
m_buffer.resize(wus);
if(m_sinkBuffer.size() < wus)
m_sinkBuffer.resize(wus);
while(begin < end) {
// if the buffer contains something, keep filling it until it contains one complete work unit
if((m_bufferFill > 0) && (m_bufferFill < wus)) {
if((m_sinkBufferFill > 0) && (m_sinkBufferFill < wus)) {
// check number if missing samples, but don't copy more than we have
size_t len = wus - m_bufferFill;
size_t len = wus - m_sinkBufferFill;
if(len > (size_t)(end - begin))
len = end - begin;
// copy
std::copy(begin, begin + len, m_buffer.begin() + m_bufferFill);
std::copy(begin, begin + len, m_sinkBuffer.begin() + m_sinkBufferFill);
// adjust pointers
m_bufferFill += len;
m_sinkBufferFill += len;
begin += len;
}
// if one complete work unit is in the buffer, feed it to the worker
if(m_bufferFill >= wus) {
if(m_sinkBufferFill >= wus) {
size_t done = 0;
while(m_bufferFill - done >= wus)
done += work(m_buffer.begin() + done, m_buffer.begin() + done + wus);
while(m_sinkBufferFill - done >= wus)
done += work(m_sinkBuffer.begin() + done, m_sinkBuffer.begin() + done + wus);
// now copy the remaining data to the front of the buffer (should be zero)
if(m_bufferFill - done > 0) {
if(m_sinkBufferFill - done > 0) {
qDebug("error in SampleSink buffer management");
std::copy(m_buffer.begin() + done, m_buffer.begin() + m_bufferFill, m_buffer.begin());
std::copy(m_sinkBuffer.begin() + done, m_sinkBuffer.begin() + m_sinkBufferFill, m_sinkBuffer.begin());
}
m_bufferFill -= done;
m_sinkBufferFill -= done;
}
// if no remainder from last run is buffered and we have at least one work unit left, feed the data to the worker
if(m_bufferFill == 0) {
if(m_sinkBufferFill == 0) {
while((size_t)(end - begin) > wus)
begin += work(begin, begin + wus);
// copy any remaining data to the buffer
std::copy(begin, end, m_buffer.begin());
m_bufferFill = end - begin;
begin += m_bufferFill;
std::copy(begin, end, m_sinkBuffer.begin());
m_sinkBufferFill = end - begin;
begin += m_sinkBufferFill;
}
}
}

View File

@ -12,9 +12,9 @@ public:
void feed(SampleVector::const_iterator begin, SampleVector::const_iterator end);
virtual size_t work(SampleVector::const_iterator begin, SampleVector::const_iterator end) = 0;
protected:
SampleVector m_buffer;
size_t m_bufferFill;
private:
SampleVector m_sinkBuffer;
size_t m_sinkBufferFill;
};
#endif // INCLUDE_SAMPLESINK_H

View File

@ -46,7 +46,7 @@ GLSpectrum::GLSpectrum(QWidget* parent) :
setAttribute(Qt::WA_NoSystemBackground, true);
setMouseTracking(true);
setMinimumHeight(200);
setMinimumSize(200, 200);
m_waterfallShare = 0.5;

View File

@ -29,7 +29,7 @@ static double trunc(double d)
QString ScaleEngine::formatTick(double value, int decimalPlaces, bool fancyTime)
{
if((m_physicalUnit != Unit::Time) || (!fancyTime)) {
if((m_physicalUnit != Unit::Time) || (!fancyTime) || 1) {
return QString("%1").arg(value, 0, 'f', decimalPlaces);
} else {
QString str;
@ -158,7 +158,15 @@ void ScaleEngine::calcScaleFactor()
m_unitStr = QString("°");
case Unit::Time:
m_unitStr = QString("s");
if(median < 0.001) {
m_unitStr = QString("µs");
m_scale = 0.000001;
} else if(median < 1.0) {
m_unitStr = QString("ms");
m_scale = 0.001;
} else {
m_unitStr = QString("s");
}
break;
}
}
@ -179,7 +187,7 @@ double ScaleEngine::calcMajorTickUnits(double distance, int* retDecimalPlaces)
exponent = floor(log10x);
base = pow(10.0, log10x - exponent);
decimalPlaces = (int)(-exponent);
/*
if((m_physicalUnit == Unit::Time) && (distance >= 1.0)) {
if(retDecimalPlaces != NULL)
*retDecimalPlaces = 0;
@ -224,7 +232,7 @@ double ScaleEngine::calcMajorTickUnits(double distance, int* retDecimalPlaces)
else if(distance < 30.0 * 86000.0)
return 30.0 * 86000.0;
else return 90.0 * 86000.0;
} else {
} else {*/
if(base <= 1.0) {
base = 1.0;
} else if(base <= 2.0) {
@ -237,8 +245,8 @@ double ScaleEngine::calcMajorTickUnits(double distance, int* retDecimalPlaces)
base = 5.0;
} else {
base = 10.0;
}
}
}/*
}*/
if(retDecimalPlaces != NULL) {
if(decimalPlaces < 0)

99
gui/viewtoolbox.cpp Normal file
View File

@ -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/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QCloseEvent>
#include "viewtoolbox.h"
#include "ui_viewtoolbox.h"
ViewToolBox::ViewToolBox(QWidget* parent) :
QWidget(parent, Qt::Tool),
ui(new Ui::ViewToolBox)
{
ui->setupUi(this);
// align the QComboBox exactly below the QCheckBox
QStyleOption o;
ui->wfDirectionSpacer->changeSize(
style()->subElementRect(QStyle::SE_CheckBoxIndicator, &o, this).width() - style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing, &o, this),
0,
QSizePolicy::Fixed,
QSizePolicy::Fixed);
setFixedSize(sizeHint());
}
ViewToolBox::~ViewToolBox()
{
delete ui;
}
void ViewToolBox::setViewWaterfall(bool checked)
{
ui->viewWaterfall->setChecked(checked);
if(checked)
ui->waterfallUpward->setEnabled(true);
else ui->waterfallUpward->setEnabled(false);
}
void ViewToolBox::setWaterfallUpward(bool checked)
{
if(checked)
ui->waterfallUpward->setCurrentIndex(0);
else ui->waterfallUpward->setCurrentIndex(1);
}
void ViewToolBox::setViewHistogram(bool checked)
{
ui->viewHistogram->setChecked(checked);
}
void ViewToolBox::setViewLiveSpectrum(bool checked)
{
ui->viewLiveSpectrum->setChecked(checked);
}
void ViewToolBox::closeEvent(QCloseEvent* event)
{
emit closed();
event->accept();
}
void ViewToolBox::on_viewWaterfall_toggled(bool checked)
{
if(checked)
ui->waterfallUpward->setEnabled(true);
else ui->waterfallUpward->setEnabled(false);
emit viewWaterfall(checked);
}
void ViewToolBox::on_waterfallUpward_currentIndexChanged(int index)
{
if(index == 0)
emit waterfallUpward(true);
else emit waterfallUpward(false);
}
void ViewToolBox::on_viewHistogram_toggled(bool checked)
{
emit viewHistogram(checked);
}
void ViewToolBox::on_viewLiveSpectrum_toggled(bool checked)
{
emit viewLiveSpectrum(checked);
}

58
gui/viewtoolbox.h Normal file
View File

@ -0,0 +1,58 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_VIEWTOOLBOX_H
#define INCLUDE_VIEWTOOLBOX_H
#include <QWidget>
namespace Ui {
class ViewToolBox;
}
class ViewToolBox : public QWidget {
Q_OBJECT
public:
explicit ViewToolBox(QWidget* parent = NULL);
~ViewToolBox();
void setViewWaterfall(bool checked);
void setWaterfallUpward(bool checked);
void setViewHistogram(bool checked);
void setViewLiveSpectrum(bool checked);
signals:
void closed();
void viewWaterfall(bool checked);
void waterfallUpward(bool checked);
void viewHistogram(bool checked);
void viewLiveSpectrum(bool checked);
private slots:
void on_viewWaterfall_toggled(bool checked);
void on_waterfallUpward_currentIndexChanged(int index);
void on_viewHistogram_toggled(bool checked);
void on_viewLiveSpectrum_toggled(bool checked);
private:
Ui::ViewToolBox* ui;
void closeEvent(QCloseEvent*);
};
#endif // INCLUDE_VIEWTOOLBOX_H

91
gui/viewtoolbox.ui Normal file
View File

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ViewToolBox</class>
<widget class="QWidget" name="ViewToolBox">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>191</width>
<height>117</height>
</rect>
</property>
<property name="windowTitle">
<string>View Toolbox</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<spacer name="wfDirectionSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="2">
<widget class="QComboBox" name="waterfallUpward">
<item>
<property name="text">
<string>Downward</string>
</property>
</item>
<item>
<property name="text">
<string>Upward</string>
</property>
</item>
</widget>
</item>
<item row="2" column="3">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0" colspan="4">
<widget class="QCheckBox" name="viewHistogram">
<property name="text">
<string>Display &amp;Histogram</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="4">
<widget class="QCheckBox" name="viewLiveSpectrum">
<property name="text">
<string>Paint &amp;Live Spectrum</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="4">
<widget class="QCheckBox" name="viewWaterfall">
<property name="text">
<string>Display &amp;Waterfall</string>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>viewWaterfall</tabstop>
<tabstop>waterfallUpward</tabstop>
<tabstop>viewHistogram</tabstop>
<tabstop>viewLiveSpectrum</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

211
hardware/audiofifo.cpp Normal file
View File

@ -0,0 +1,211 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 <QTime>
#include "audiofifo.h"
#define MIN(x, y) ((x) < (y) ? (x) : (y))
bool AudioFifo::create(uint sampleSize, uint size)
{
if(m_fifo != NULL) {
delete[] m_fifo;
m_fifo = NULL;
}
m_sampleSize = sampleSize;
m_size = 0;
m_fill = 0;
m_head = 0;
m_tail = 0;
// only allow space for full samples
size -= size % sampleSize;
if((m_fifo = new qint8[size]) == NULL) {
qDebug("out of memory");
return false;
}
m_size = size;
return true;
}
AudioFifo::AudioFifo() :
m_fifo(NULL)
{
m_size = 0;
m_fill = 0;
m_head = 0;
m_tail = 0;
}
AudioFifo::AudioFifo(uint sampleSize, uint size) :
m_fifo(NULL)
{
create(sampleSize, size);
}
AudioFifo::~AudioFifo()
{
QMutexLocker mutexLocker(&m_mutex);
if(m_fifo != NULL) {
delete[] m_fifo;
m_fifo = NULL;
}
m_writeWaitCondition.wakeOne();
m_readWaitCondition.wakeOne();
m_size = 0;
}
bool AudioFifo::setSize(uint sampleSize, uint size)
{
QMutexLocker mutexLocker(&m_mutex);
return create(sampleSize, size);
}
uint AudioFifo::write(const quint8* data, uint len, int timeout)
{
QTime time;
uint total;
uint remaining;
uint copyLen;
if(m_fifo == NULL)
return 0;
time.start();
m_mutex.lock();
if(timeout == 0)
total = MIN(len, m_size - m_fill);
else total = len;
// make sure, only complete samples are handled
total -= total % m_sampleSize;
remaining = total;
while(remaining > 0) {
if(isFull()) {
if(time.elapsed() < timeout) {
m_writeWaitLock.lock();
m_mutex.unlock();
int ms = timeout - time.elapsed();
if(ms < 1)
ms = 1;
bool ok = m_writeWaitCondition.wait(&m_writeWaitLock, ms);
m_writeWaitLock.unlock();
if(!ok)
return total - remaining;
m_mutex.lock();
if(m_fifo == NULL) {
m_mutex.unlock();
return 0;
}
} else {
m_mutex.unlock();
return total - remaining;
}
}
copyLen = MIN(remaining, m_size - m_fill);
copyLen = MIN(copyLen, m_size - m_tail);
memcpy(m_fifo + m_tail, data, copyLen);
m_tail += copyLen;
m_tail %= m_size;
m_fill += copyLen;
data += copyLen;
remaining -= copyLen;
m_readWaitCondition.wakeOne();
}
m_mutex.unlock();
return total;
}
uint AudioFifo::read(quint8* data, uint len, int timeout)
{
QTime time;
uint total;
uint remaining;
uint copyLen;
if(m_fifo == NULL)
return 0;
time.start();
m_mutex.lock();
if(timeout == 0)
total = MIN(len, m_fill);
else total = len;
remaining = total;
while(remaining > 0) {
if(isEmpty()) {
if(time.elapsed() < timeout) {
m_readWaitLock.lock();
m_mutex.unlock();
int ms = timeout - time.elapsed();
if(ms < 1)
ms = 1;
bool ok = m_readWaitCondition.wait(&m_readWaitLock, ms);
m_readWaitLock.unlock();
if(!ok)
return total - remaining;
m_mutex.lock();
if(m_fifo == NULL) {
m_mutex.unlock();
return 0;
}
} else {
m_mutex.unlock();
return total - remaining;
}
}
copyLen = MIN(remaining, m_fill);
copyLen = MIN(copyLen, m_size - m_head);
memcpy(data, m_fifo + m_head, copyLen);
m_head += copyLen;
m_head %= m_size;
m_fill -= copyLen;
data += copyLen;
remaining -= copyLen;
m_writeWaitCondition.wakeOne();
}
m_mutex.unlock();
return total;
}
uint AudioFifo::drain(uint len)
{
QMutexLocker mutexLocker(&m_mutex);
if(len > m_fill)
len = m_fill;
m_head = (m_head + len) % m_size;
m_fill -= len;
m_writeWaitCondition.wakeOne();
return len;
}

63
hardware/audiofifo.h Normal file
View File

@ -0,0 +1,63 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_AUDIOFIFO_H
#define INCLUDE_AUDIOFIFO_H
#include <QMutex>
#include <QWaitCondition>
class AudioFifo {
private:
QMutex m_mutex;
qint8* m_fifo;
uint m_sampleSize;
uint m_size;
uint m_fill;
uint m_head;
uint m_tail;
QMutex m_writeWaitLock;
QMutex m_readWaitLock;
QWaitCondition m_writeWaitCondition;
QWaitCondition m_readWaitCondition;
bool create(uint sampleSize, uint size);
public:
AudioFifo();
AudioFifo(uint sampleSize, uint size);
~AudioFifo();
bool setSize(uint sampleSize, uint size);
uint write(const quint8* data, uint len, int timeout = INT_MAX);
uint read(quint8* data, uint len, int timeout = INT_MAX);
uint drain(uint count);
inline uint flush() { return drain(m_fill); }
inline int fill() const { return m_fill; }
inline bool isEmpty() const { return m_fill == 0; }
inline bool isFull() const { return m_fill == m_size; }
inline uint size() const { return m_size; }
};
#endif // INCLUDE_AUDIOFIFO_H

124
hardware/audiooutput.cpp Normal file
View File

@ -0,0 +1,124 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 <QMessageBox>
#include "audiooutput.h"
#include "audiofifo.h"
int AudioOutput::callbackHelper(
const void *inputBuffer,
void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData)
{
AudioOutput* audioOutput = (AudioOutput*)userData;
if(outputBuffer == NULL)
return 0;
return audioOutput->callback(inputBuffer, outputBuffer, framesPerBuffer, timeInfo, statusFlags);
}
int AudioOutput::callback(
const void* inputBuffer,
void* outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo*
timeInfo, PaStreamCallbackFlags statusFlags)
{
Q_UNUSED(inputBuffer);
Q_UNUSED(timeInfo);
Q_UNUSED(statusFlags);
int needed = framesPerBuffer * 4;
int done = m_audioFifo->read((quint8*)outputBuffer, needed, 1);
memset(((quint8*)outputBuffer) + done, 0x00, needed - done);
m_streamStartTime += (PaTime)framesPerBuffer / (PaTime)m_sampleRate;
return 0;
}
AudioOutput::AudioOutput() :
m_stream(NULL),
m_audioFifo(NULL),
m_sampleRate(0)
{
}
AudioOutput::~AudioOutput()
{
stop();
}
bool AudioOutput::start(int device, int rate, AudioFifo* audioFifo)
{
if(m_stream != NULL)
stop();
PaStreamParameters outputParameters;
const PaStreamInfo* streamInfo;
PaError err;
m_audioFifo = audioFifo;
outputParameters.device = device;
outputParameters.channelCount = 2;
outputParameters.sampleFormat = paInt16;
outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
qDebug("AudioOutput: open");
if((err = Pa_OpenStream(&m_stream, NULL, &outputParameters, rate, 1024, paClipOff, callbackHelper, this)) != paNoError)
goto failed;
qDebug("AudioOutput: start");
m_streamStartTime = Pa_GetStreamTime(m_stream);
if((err = Pa_StartStream(m_stream)) != paNoError)
goto failed;
streamInfo = Pa_GetStreamInfo(m_stream);
m_sampleRate = streamInfo->sampleRate;
qDebug("AudioOutput: playback has started (%s @ %d Hz)", Pa_GetDeviceInfo(outputParameters.device)->name, rate);
return true;
failed:
qCritical("AudioOutput: playback failed: %s (%d)", Pa_GetErrorText(err), err);
Pa_CloseStream(m_stream);
m_stream = NULL;
m_sampleRate = 0;
return false;
}
void AudioOutput::stop()
{
if(m_stream != NULL) {
Pa_StopStream(m_stream);
Pa_CloseStream(m_stream);
m_stream = NULL;
m_sampleRate = 0;
qDebug("AudioOutput: stopped");
}
}
int AudioOutput::bufferedSamples()
{
return (Pa_GetStreamTime(m_stream) - m_streamStartTime) * m_sampleRate;
}

59
hardware/audiooutput.h Normal file
View File

@ -0,0 +1,59 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_AUDIOOUTPUT_H
#define INCLUDE_AUDIOOUTPUT_H
#include <QMutex>
#include "portaudio.h"
class AudioFifo;
class AudioOutput {
private:
PaStream* m_stream;
AudioFifo* m_audioFifo;
int m_sampleRate;
PaTime m_streamStartTime;
static int callbackHelper(
const void* inputBuffer,
void* outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void* userData);
int callback(
const void* inputBuffer,
void* outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags);
public:
AudioOutput();
~AudioOutput();
bool start(int device, int rate, AudioFifo* audioFifo);
void stop();
int bufferedSamples();
};
#endif // INCLUDE_AUDIOOUTPUT_H

View File

@ -23,7 +23,8 @@
OsmoSDRInput::OsmoSDRInput(SampleFifo* sampleFifo) :
SampleSource(sampleFifo),
m_dev(NULL),
m_osmoSDRThread(NULL)
m_osmoSDRThread(NULL),
m_deviceDesc()
{
}
@ -55,6 +56,7 @@ bool OsmoSDRInput::startInput(int device, int rate)
goto failed;
}
qDebug("OsmoSDRInput open: %s %s, SN: %s", vendor, product, serial);
m_deviceDesc = QString("%1 (SN %2)").arg(product).arg(serial);
if((res = osmosdr_set_sample_rate(m_dev, rate)) < 0) {
qCritical("error setting sample rate");
@ -96,6 +98,7 @@ void OsmoSDRInput::stopInput()
osmosdr_close(m_dev);
m_dev = NULL;
}
m_deviceDesc.clear();
}
bool OsmoSDRInput::setCenterFrequency(qint64 freq)
@ -119,6 +122,11 @@ bool OsmoSDRInput::setDecimation(int dec)
return osmosdr_set_fpga_decimation(m_dev, dec);
}
const QString& OsmoSDRInput::deviceDesc() const
{
return m_deviceDesc;
}
bool OsmoSDRInput::setE4000LNAGain(int gain)
{
if(m_dev == NULL)
@ -146,3 +154,10 @@ bool OsmoSDRInput::setE4000ifStageGain(int stage, int gain)
return false;
return osmosdr_set_tuner_if_gain(m_dev, stage, gain);
}
bool OsmoSDRInput::setFilter(quint8 i1, quint8 i2, quint8 q1, quint8 q2)
{
if(m_dev == NULL)
return false;
return osmosdr_set_iq_amp(m_dev, i1, i2, q1, q2);
}

View File

@ -20,6 +20,7 @@
#include "samplesource.h"
#include <osmosdr.h>
#include <QString>
class OsmoSDRThread;
@ -35,14 +36,18 @@ public:
bool setIQSwap(bool sw);
bool setDecimation(int dec);
const QString& deviceDesc() const;
bool setE4000LNAGain(int gain);
bool setE4000MixerGain(int gain);
bool setE4000MixerEnh(int gain);
bool setE4000ifStageGain(int stage, int gain);
bool setFilter(quint8 i1, quint8 i2, quint8 q1, quint8 q2);
private:
osmosdr_dev_t* m_dev;
OsmoSDRThread* m_osmoSDRThread;
QString m_deviceDesc;
};
#endif // INCLUDE_OSMOSDRINPUT_H

View File

@ -15,6 +15,7 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <errno.h>
#include "osmosdrthread.h"
#include "samplefifo.h"
@ -54,6 +55,8 @@ void OsmoSDRThread::run()
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, 8, sizeof(Sample) * 16384)) < 0) {
qCritical("OsmoSDRThread: async error: %s", strerror(errno));
@ -66,7 +69,17 @@ void OsmoSDRThread::run()
void OsmoSDRThread::callback(quint8* buf, qint32 len)
{
m_sampleFifo->write((SampleVector::const_iterator)((Sample*)buf), (SampleVector::const_iterator)((Sample*)(buf + len)));
/*
qDebug("%d", len);
for(int i = 0; i < len / 2; i += 2) {
((qint16*)buf)[i] = sin((float)(cntr) * 16384* 2.0 * M_PI / 65536.0) * 5000.0;
((qint16*)buf)[i + 1] = -cos((float)(cntr++) * 16384*2.0 * M_PI / 65536.0) * 5000.0;
}*/
//m_sampleFifo->write((SampleVector::const_iterator)((Sample*)buf), (SampleVector::const_iterator)((Sample*)(buf + len)));
//fwrite(buf, 1, len, m_f);
m_sampleFifo->write(buf, len);
if(!m_running)
osmosdr_cancel_async(m_dev);
}

View File

@ -39,6 +39,7 @@ private:
QMutex m_startWaitMutex;
QWaitCondition m_startWaiter;
bool m_running;
FILE* m_f;
osmosdr_dev_t* m_dev;
SampleFifo* m_sampleFifo;

View File

@ -67,6 +67,49 @@ bool SampleFifo::setSize(int size)
return m_data.size() == (size_t)size;
}
size_t SampleFifo::write(const quint8* data, size_t count)
{
QMutexLocker mutexLocker(&m_mutex);
size_t total;
int remaining;
int len;
const Sample* begin = (const Sample*)data;
count /= 4;
total = MIN(count, m_size - m_fill);
if(total < count) {
if(m_suppressed < 0) {
m_suppressed = 0;
m_msgRateTimer.start();
qCritical("SampleFifo: overflow - dropping %d samples", count - total);
} else {
if(m_msgRateTimer.elapsed() > 2500) {
qCritical("SampleFifo: %d messages dropped", m_suppressed);
qCritical("SampleFifo: overflow - dropping %d samples", count - total);
m_suppressed = -1;
} else {
m_suppressed++;
}
}
}
remaining = total;
while(remaining > 0) {
len = MIN(remaining, m_size - m_tail);
std::copy(begin, begin + len, m_data.begin() + m_tail);
m_tail += len;
m_tail %= m_size;
m_fill += len;
begin += len;
remaining -= len;
}
if(m_fill > 0)
emit dataReady();
return total;
}
size_t SampleFifo::write(SampleVector::const_iterator begin, SampleVector::const_iterator end)
{
QMutexLocker mutexLocker(&m_mutex);

View File

@ -45,9 +45,13 @@ public:
SampleFifo(int size, QObject* parent = NULL);
~SampleFifo();
SampleFifo(const SampleFifo&);
SampleFifo& operator=(const SampleFifo&);
bool setSize(int size);
inline size_t fill() const { return m_fill; }
size_t write(const quint8* data, size_t count);
size_t write(SampleVector::const_iterator begin, SampleVector::const_iterator end);
size_t read(SampleVector::iterator begin, SampleVector::iterator end);

View File

@ -33,6 +33,8 @@ public:
virtual bool setIQSwap(bool sw) = 0;
virtual bool setDecimation(int dec) = 0;
virtual const QString& deviceDesc() const = 0;
protected:
SampleFifo* m_sampleFifo;
};

View File

@ -0,0 +1,87 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 <QMessageBox>
#include "soundcardinfo.h"
#include "portaudio.h"
SoundcardInfo::SoundcardInfo()
{
const PaDeviceInfo *deviceInfo;
const PaHostApiInfo *apiInfo;
PaError err;
int numDevices;
int i;
QString name;
if((numDevices = Pa_GetDeviceCount()) < 0) {
err = numDevices;
goto failed;
}
m_devices.clear();
qDebug("Audio initialisation: %d devices found", numDevices);
for(i = 0; i < numDevices; i++) {
deviceInfo = Pa_GetDeviceInfo(i);
apiInfo = Pa_GetHostApiInfo(deviceInfo->hostApi);
name = QString("%1 (record %2, playblack %3, API %4)").
arg(QString::fromLatin1(deviceInfo->name)).
arg(deviceInfo->maxInputChannels).
arg(deviceInfo->maxOutputChannels).
arg(QString::fromLatin1(apiInfo->name));
m_devices.append(name);
qDebug(" Device #%d: %s", i, qPrintable(name));
}
return;
failed:
if(err != paNoError)
qCritical("Audio initialisation failed: %s (%d)", Pa_GetErrorText(err), err);
}
#if 0
int SoundcardInfo::getInputDevice()
{
// find device
for(i = 0; i < m_soundCardInfo->getDeviceCount(); i++) {
if(m_soundCardInfo->getDevice(i) == dev) {
device = i;
break;
}
}
// if no device is found or configured, find first DirectSound input device
if(device < 0) {
int numHostApis = Pa_GetHostApiCount();
const PaHostApiInfo *apiInfo;
int i;
for(i = 0; i < numHostApis; i++) {
apiInfo = Pa_GetHostApiInfo(i);
if(strcmp(apiInfo->name, defaultDev) == 0) {
device = apiInfo->defaultInputDevice;
if(device >= 0)
break;
}
}
if(device < 0)
device = Pa_GetDefaultInputDevice();
}
}
#endif

36
hardware/soundcardinfo.h Normal file
View File

@ -0,0 +1,36 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_SOUNDCARDINFO_H
#define INCLUDE_SOUNDCARDINFO_H
#include <QStringList>
class SoundcardInfo {
private:
QStringList m_devices;
public:
SoundcardInfo();
inline const QStringList& getDevices() { return m_devices; }
inline const QString& getDevice(int idx) { return m_devices.at(idx); }
inline int getDeviceCount() { return m_devices.count(); }
};
#endif // INCLUDE_SOUNDCARDINFO_H

View File

@ -19,9 +19,8 @@
#include <QTextCodec>
#include <QMessageBox>
#include "mainwindow.h"
//#include "portaudio.h"
#include "portaudio.h"
#if 0
static void initPortAudio()
{
PaError err;
@ -29,10 +28,9 @@ static void initPortAudio()
if((err = Pa_Initialize()) != paNoError) {
qCritical("PortAudio: could not initialise: %s (%d)", Pa_GetErrorText(err), err);
QString error = QObject::tr("PortAudio could not be initialised: %1 (%2)").arg(Pa_GetErrorText(err)).arg(err);
QMessageBox::critical(NULL, "PortAudio", error);
QMessageBox::critical(NULL, "PortAudio failure", error);
}
}
#endif
int main(int argc, char* argv[])
{
@ -41,7 +39,7 @@ int main(int argc, char* argv[])
QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
//initPortAudio();
initPortAudio();
MainWindow w;
w.show();

View File

@ -18,17 +18,27 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "gui/indicator.h"
#include "gui/viewtoolbox.h"
#include "dsp/channelizer.h"
#include "osdrupgrade.h"
MainWindow::MainWindow(QWidget* parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
m_settings(),
m_dspEngine(&m_settings)
m_dspEngine(&m_settings),
m_startOSDRUpdateAfterStop(false)
{
m_settings.load();
ui->setupUi(this);
createStatusBar();
m_viewToolBox = new ViewToolBox(this);
connect(m_viewToolBox, SIGNAL(closed()), this, SLOT(viewToolBoxClosed()));
connect(m_viewToolBox, SIGNAL(viewWaterfall(bool)), this, SLOT(on_action_View_Waterfall_toggled(bool)));
connect(m_viewToolBox, SIGNAL(waterfallUpward(bool)), this, SLOT(viewToolBoxWaterfallUpward(bool)));
connect(m_viewToolBox, SIGNAL(viewHistogram(bool)), this, SLOT(on_action_View_Histogram_toggled(bool)));
connect(m_viewToolBox, SIGNAL(viewLiveSpectrum(bool)), this, SLOT(on_action_View_LiveSpectrum_toggled(bool)));
m_dspEngine.setGLSpectrum(ui->glSpectrum);
m_dspEngine.start();
@ -37,8 +47,6 @@ MainWindow::MainWindow(QWidget* parent) :
connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
m_statusTimer.start(500);
statusBar()->showMessage("Welcome to SDRangelove", 3000);
ui->centerFrequency->setValueRange(7, 20000U, 2200000U);
for(int i = 0; i < ui->fftSize->count(); i++) {
@ -49,17 +57,20 @@ MainWindow::MainWindow(QWidget* parent) :
}
ui->fftWindow->setCurrentIndex(m_settings.fftWindow());
ui->dispWaterfall->setChecked(m_settings.displayWaterfall());
ui->action_View_Waterfall->setChecked(m_settings.displayWaterfall());
m_viewToolBox->setViewWaterfall(m_settings.displayWaterfall());
ui->glSpectrum->setDisplayWaterfall(m_settings.displayWaterfall());
ui->waterfallInverted->setChecked(m_settings.invertedWaterfall());
m_viewToolBox->setWaterfallUpward(m_settings.invertedWaterfall());
ui->glSpectrum->setInvertedWaterfall(m_settings.invertedWaterfall());
ui->dispLiveSpectrum->setChecked(m_settings.displayLiveSpectrum());
ui->action_View_LiveSpectrum->setChecked(m_settings.displayLiveSpectrum());
m_viewToolBox->setViewLiveSpectrum(m_settings.displayLiveSpectrum());
ui->glSpectrum->setDisplayLiveSpectrum(m_settings.displayLiveSpectrum());
ui->dispHistogram->setChecked(m_settings.displayHistogram());
ui->action_View_Histogram->setChecked(m_settings.displayHistogram());
m_viewToolBox->setViewHistogram(m_settings.displayHistogram());
ui->glSpectrum->setDisplayHistogram(m_settings.displayHistogram());
ui->iqSwap->setChecked(m_settings.iqSwap());
ui->decimation->setCurrentIndex(m_settings.decimation() - 2);
ui->decimation->setCurrentIndex(m_settings.decimation());
ui->dcOffset->setChecked(m_settings.dcOffsetCorrection());
ui->iqImbalance->setChecked(m_settings.iqImbalanceCorrection());
ui->e4000LNAGain->setCurrentIndex((m_settings.e4000LNAGain() + 50) / 25);
@ -74,9 +85,18 @@ MainWindow::MainWindow(QWidget* parent) :
ui->e4000if4->setCurrentIndex(m_settings.e4000if4() / 10);
ui->e4000if5->setCurrentIndex(m_settings.e4000if5() / 30 - 1);
ui->e4000if6->setCurrentIndex(m_settings.e4000if6() / 30 - 1);
ui->filterI1->setValue(m_settings.filterI1());
ui->filterI2->setValue(m_settings.filterI2());
ui->filterQ1->setValue(m_settings.filterQ1());
ui->filterQ2->setValue(m_settings.filterQ2());
updateSampleRate();
updateCenterFreqDisplay();
/*
Channelizer* channelizer = new Channelizer;
channelizer->setGLSpectrum(ui->chanSpectrum);
m_dspEngine.addChannelizer(channelizer);
*/
}
MainWindow::~MainWindow()
@ -89,6 +109,10 @@ MainWindow::~MainWindow()
void MainWindow::createStatusBar()
{
m_sampleRateWidget = new QLabel(tr("Rate: 0 kHz"), this);
m_sampleRateWidget->setToolTip(tr("Sample Rate"));
statusBar()->addPermanentWidget(m_sampleRateWidget);
m_engineIdle = new Indicator(tr("Idle"), this);
m_engineIdle->setToolTip(tr("DSP engine is idle"));
statusBar()->addPermanentWidget(m_engineIdle);
@ -113,13 +137,14 @@ void MainWindow::updateSampleRate()
{
m_sampleRate = 4000000 / (1 << m_settings.decimation());
ui->glSpectrum->setSampleRate(m_sampleRate);
ui->sampleRate->setText(tr("%1k").arg((float)m_sampleRate / 1000));
m_sampleRateWidget->setText(tr("Rate: %1 kHz").arg((float)m_sampleRate / 1000));
}
void MainWindow::updateStatus()
{
if(m_lastEngineState != m_dspEngine.state()) {
switch(m_dspEngine.state()) {
DSPEngine::State state = m_dspEngine.state();
if(m_lastEngineState != state) {
switch(state) {
case DSPEngine::StNotStarted:
m_engineIdle->setColor(Qt::gray);
m_engineRunning->setColor(Qt::gray);
@ -132,26 +157,41 @@ void MainWindow::updateStatus()
m_engineRunning->setColor(Qt::gray);
m_engineError->setColor(Qt::gray);
statusBar()->clearMessage();
if(m_startOSDRUpdateAfterStop)
on_actionOsmoSDR_Firmware_Upgrade_triggered();
break;
case DSPEngine::StRunning:
m_engineIdle->setColor(Qt::gray);
m_engineRunning->setColor(Qt::green);
m_engineError->setColor(Qt::gray);
statusBar()->clearMessage();
statusBar()->showMessage(tr("Sampling from %1").arg(m_dspEngine.deviceDesc()));
break;
case DSPEngine::StError:
m_engineIdle->setColor(Qt::gray);
m_engineRunning->setColor(Qt::gray);
m_engineError->setColor(Qt::red);
statusBar()->showMessage(m_dspEngine.errorMsg());
statusBar()->showMessage(tr("Error: %1").arg(m_dspEngine.errorMsg()));
if(m_startOSDRUpdateAfterStop)
on_actionOsmoSDR_Firmware_Upgrade_triggered();
break;
}
m_lastEngineState = m_dspEngine.state();
m_lastEngineState = state;
}
}
void MainWindow::viewToolBoxClosed()
{
ui->action_View_Toolbox->setChecked(false);
}
void MainWindow::viewToolBoxWaterfallUpward(bool checked)
{
ui->glSpectrum->setInvertedWaterfall(checked);
m_settings.setInvertedWaterfall(checked);
}
void MainWindow::on_action_Start_triggered()
{
m_dspEngine.startAcquisition();
@ -179,7 +219,7 @@ void MainWindow::on_iqSwap_toggled(bool checked)
void MainWindow::on_decimation_currentIndexChanged(int index)
{
m_settings.setDecimation(index + 2);
m_settings.setDecimation(index);
updateSampleRate();
}
@ -251,26 +291,74 @@ void MainWindow::on_iqImbalance_toggled(bool checked)
m_settings.setIQImbalanceCorrection(checked);
}
void MainWindow::on_dispLiveSpectrum_toggled(bool checked)
void MainWindow::on_filterI1_valueChanged(int value)
{
ui->glSpectrum->setDisplayLiveSpectrum(checked);
m_settings.setDisplayLiveSpectrum(checked);
m_settings.setFilterI1(value);
}
void MainWindow::on_dispHistogram_toggled(bool checked)
void MainWindow::on_filterI2_valueChanged(int value)
{
ui->glSpectrum->setDisplayHistogram(checked);
m_settings.setDisplayHistogram(checked);
m_settings.setFilterI2(value);
}
void MainWindow::on_dispWaterfall_toggled(bool checked)
void MainWindow::on_filterQ1_valueChanged(int value)
{
m_settings.setFilterQ1(value);
}
void MainWindow::on_filterQ2_valueChanged(int value)
{
m_settings.setFilterQ2(value);
}
void MainWindow::on_action_View_Waterfall_toggled(bool checked)
{
ui->action_View_Waterfall->setChecked(checked);
m_viewToolBox->setViewWaterfall(checked);
ui->glSpectrum->setDisplayWaterfall(checked);
m_settings.setDisplayWaterfall(checked);
}
void MainWindow::on_waterfallInverted_toggled(bool checked)
void MainWindow::on_action_View_Histogram_toggled(bool checked)
{
ui->glSpectrum->setInvertedWaterfall(checked);
m_settings.setInvertedWaterfall(checked);
ui->action_View_Histogram->setChecked(checked);
m_viewToolBox->setViewHistogram(checked);
ui->glSpectrum->setDisplayHistogram(checked);
m_settings.setDisplayHistogram(checked);
}
void MainWindow::on_action_View_LiveSpectrum_toggled(bool checked)
{
ui->action_View_LiveSpectrum->setChecked(checked);
m_viewToolBox->setViewLiveSpectrum(checked);
ui->glSpectrum->setDisplayLiveSpectrum(checked);
m_settings.setDisplayLiveSpectrum(checked);
}
void MainWindow::on_action_View_Toolbox_toggled(bool checked)
{
if(checked)
m_viewToolBox->show();
else m_viewToolBox->hide();
}
void MainWindow::on_action_View_Fullscreen_toggled(bool checked)
{
if(checked)
showFullScreen();
else showNormal();
}
void MainWindow::on_actionOsmoSDR_Firmware_Upgrade_triggered()
{
DSPEngine::State engineState = m_dspEngine.state();
if((engineState != DSPEngine::StIdle) && (engineState != DSPEngine::StError)) {
m_startOSDRUpdateAfterStop = true;
m_dspEngine.stopAcquistion();
return;
}
m_startOSDRUpdateAfterStop = false;
OSDRUpgrade osdrUpgrade;
osdrUpgrade.exec();
}

View File

@ -23,8 +23,10 @@
#include "settings.h"
#include "dsp/dspengine.h"
class QLabel;
class DSPEngine;
class Indicator;
class ViewToolBox;
namespace Ui {
class MainWindow;
@ -47,18 +49,24 @@ private:
DSPEngine::State m_lastEngineState;
QLabel* m_sampleRateWidget;
Indicator* m_engineIdle;
Indicator* m_engineRunning;
Indicator* m_engineError;
ViewToolBox* m_viewToolBox;
int m_sampleRate;
bool m_startOSDRUpdateAfterStop;
void createStatusBar();
void updateCenterFreqDisplay();
void updateSampleRate();
private slots:
void updateStatus();
void viewToolBoxClosed();
void viewToolBoxWaterfallUpward(bool checked);
void on_action_Start_triggered();
void on_action_Stop_triggered();
void on_fftSize_currentIndexChanged(const QString& str);
@ -78,10 +86,16 @@ private slots:
void on_action_Debug_triggered();
void on_dcOffset_toggled(bool checked);
void on_iqImbalance_toggled(bool checked);
void on_dispLiveSpectrum_toggled(bool checked);
void on_dispHistogram_toggled(bool checked);
void on_dispWaterfall_toggled(bool checked);
void on_waterfallInverted_toggled(bool checked);
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_action_View_Waterfall_toggled(bool checked);
void on_action_View_Histogram_toggled(bool checked);
void on_action_View_LiveSpectrum_toggled(bool checked);
void on_action_View_Toolbox_toggled(bool checked);
void on_action_View_Fullscreen_toggled(bool checked);
void on_actionOsmoSDR_Firmware_Upgrade_triggered();
};
#endif // INCLUDE_MAINWINDOW_H

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>920</width>
<height>660</height>
<width>936</width>
<height>707</height>
</rect>
</property>
<property name="windowTitle">
@ -39,7 +39,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>920</width>
<width>936</width>
<height>21</height>
</rect>
</property>
@ -49,13 +49,11 @@
</property>
<addaction name="action_Exit"/>
</widget>
<widget class="QMenu" name="menu_Settings">
<widget class="QMenu" name="menu_Options">
<property name="title">
<string>&amp;Settings</string>
<string>&amp;Options</string>
</property>
<addaction name="actionE4000_Stages"/>
<addaction name="separator"/>
<addaction name="action_Hardware"/>
<addaction name="actionOsmoSDR_Firmware_Upgrade"/>
</widget>
<widget class="QMenu" name="menu_Acquisition">
<property name="title">
@ -66,9 +64,22 @@
<addaction name="separator"/>
<addaction name="action_Debug"/>
</widget>
<widget class="QMenu" name="menu_View">
<property name="title">
<string>&amp;View</string>
</property>
<addaction name="action_View_Fullscreen"/>
<addaction name="separator"/>
<addaction name="action_View_Waterfall"/>
<addaction name="action_View_Histogram"/>
<addaction name="action_View_LiveSpectrum"/>
<addaction name="separator"/>
<addaction name="action_View_Toolbox"/>
</widget>
<addaction name="menu_File"/>
<addaction name="menu_Acquisition"/>
<addaction name="menu_Settings"/>
<addaction name="menu_View"/>
<addaction name="menu_Options"/>
</widget>
<widget class="QToolBar" name="mainToolBar">
<attribute name="toolBarArea">
@ -289,6 +300,16 @@
</item>
<item row="0" column="1">
<widget class="QComboBox" name="decimation">
<item>
<property name="text">
<string>1:1 (none)</string>
</property>
</item>
<item>
<property name="text">
<string>1:2</string>
</property>
</item>
<item>
<property name="text">
<string>1:4</string>
@ -309,11 +330,6 @@
<string>1:32</string>
</property>
</item>
<item>
<property name="text">
<string>1:64</string>
</property>
</item>
</widget>
</item>
<item row="0" column="2">
@ -355,7 +371,7 @@
<property name="spacing">
<number>0</number>
</property>
<item row="0" column="4">
<item row="0" column="8">
<widget class="QLabel" name="label_10">
<property name="text">
<string>IF4</string>
@ -365,7 +381,7 @@
</property>
</widget>
</item>
<item row="0" column="1">
<item row="0" column="5">
<widget class="QComboBox" name="e4000LNAGain">
<property name="maximumSize">
<size>
@ -450,7 +466,7 @@
</item>
</widget>
</item>
<item row="0" column="2">
<item row="0" column="6">
<widget class="QLabel" name="label_7">
<property name="text">
<string>IF1</string>
@ -460,7 +476,7 @@
</property>
</widget>
</item>
<item row="2" column="1">
<item row="2" column="5">
<widget class="QComboBox" name="e4000MixerEnh">
<property name="maximumSize">
<size>
@ -495,7 +511,7 @@
</item>
</widget>
</item>
<item row="2" column="0">
<item row="2" column="4">
<widget class="QLabel" name="label_6">
<property name="text">
<string>MixerEnh</string>
@ -505,7 +521,7 @@
</property>
</widget>
</item>
<item row="0" column="3">
<item row="0" column="7">
<widget class="QComboBox" name="e4000if1">
<property name="maximumSize">
<size>
@ -525,7 +541,7 @@
</item>
</widget>
</item>
<item row="1" column="1">
<item row="1" column="5">
<widget class="QComboBox" name="e4000MixerGain">
<property name="maximumSize">
<size>
@ -545,7 +561,7 @@
</item>
</widget>
</item>
<item row="0" column="0">
<item row="0" column="4">
<widget class="QLabel" name="label_4">
<property name="text">
<string>LNA</string>
@ -555,7 +571,7 @@
</property>
</widget>
</item>
<item row="1" column="0">
<item row="1" column="4">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Mixer</string>
@ -565,7 +581,7 @@
</property>
</widget>
</item>
<item row="0" column="5">
<item row="0" column="9">
<widget class="QComboBox" name="e4000if4">
<property name="maximumSize">
<size>
@ -590,7 +606,7 @@
</item>
</widget>
</item>
<item row="1" column="2">
<item row="1" column="6">
<widget class="QLabel" name="label_8">
<property name="text">
<string>IF2</string>
@ -600,7 +616,7 @@
</property>
</widget>
</item>
<item row="1" column="3">
<item row="1" column="7">
<widget class="QComboBox" name="e4000if2">
<property name="maximumSize">
<size>
@ -630,7 +646,7 @@
</item>
</widget>
</item>
<item row="1" column="4">
<item row="1" column="8">
<widget class="QLabel" name="label_21">
<property name="text">
<string>IF5</string>
@ -640,7 +656,37 @@
</property>
</widget>
</item>
<item row="1" column="5">
<item row="2" column="7">
<widget class="QComboBox" name="e4000if3">
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</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="9">
<widget class="QComboBox" name="e4000if5">
<property name="maximumSize">
<size>
@ -675,47 +721,7 @@
</item>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="label_9">
<property name="text">
<string>IF3</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QComboBox" name="e4000if3">
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</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="2" column="4">
<item row="2" column="8">
<widget class="QLabel" name="label_22">
<property name="text">
<string>IF6</string>
@ -725,7 +731,17 @@
</property>
</widget>
</item>
<item row="2" column="5">
<item row="2" column="6">
<widget class="QLabel" name="label_9">
<property name="text">
<string>IF3</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="9">
<widget class="QComboBox" name="e4000if6">
<property name="maximumSize">
<size>
@ -760,59 +776,80 @@
</item>
</widget>
</item>
<item row="0" column="10" rowspan="3">
<widget class="QSlider" name="filterI1">
<property name="maximum">
<number>255</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="0" column="11" rowspan="3">
<widget class="QSlider" name="filterI2">
<property name="maximum">
<number>255</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="0" column="12" rowspan="3">
<widget class="QSlider" name="filterQ1">
<property name="maximum">
<number>255</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="0" column="13" rowspan="3">
<widget class="QSlider" name="filterQ2">
<property name="maximum">
<number>255</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<widget class="QDockWidget" name="dockWidget_2">
<widget class="QDockWidget" name="dockWidget_3">
<property name="windowTitle">
<string>Spectrum Display</string>
<string>Channel</string>
</property>
<attribute name="dockWidgetArea">
<number>8</number>
<number>2</number>
</attribute>
<widget class="QWidget" name="dockWidgetContents_4">
<layout class="QGridLayout" name="gridLayout_4">
<widget class="QWidget" name="dockWidgetContents_5">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="margin">
<number>2</number>
</property>
<property name="spacing">
<number>0</number>
</property>
<item row="2" column="0">
<widget class="QCheckBox" name="dispLiveSpectrum">
<property name="text">
<string>Live Spectrum</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="sampleRate">
<property name="text">
<string>0k</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="dispHistogram">
<property name="text">
<string>Histogram</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="dispWaterfall">
<property name="text">
<string>Waterfall</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="waterfallInverted">
<property name="text">
<string>Inverted Waterf.</string>
<item>
<widget class="GLSpectrum" name="chanSpectrum" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
@ -861,6 +898,54 @@
<string>F10</string>
</property>
</action>
<action name="action_View_Waterfall">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>&amp;Waterfall</string>
</property>
</action>
<action name="action_View_Histogram">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>&amp;Histogram</string>
</property>
</action>
<action name="action_View_LiveSpectrum">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>&amp;Live Spectrum</string>
</property>
</action>
<action name="action_View_Toolbox">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Show Toolbox</string>
</property>
</action>
<action name="action_View_Fullscreen">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>&amp;Fullscreen</string>
</property>
<property name="shortcut">
<string>F11</string>
</property>
</action>
<action name="actionOsmoSDR_Firmware_Upgrade">
<property name="text">
<string>OsmoSDR &amp;Firmware Upgrade...</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
@ -894,10 +979,6 @@
<tabstop>e4000if4</tabstop>
<tabstop>e4000if5</tabstop>
<tabstop>e4000if6</tabstop>
<tabstop>dispWaterfall</tabstop>
<tabstop>waterfallInverted</tabstop>
<tabstop>dispLiveSpectrum</tabstop>
<tabstop>dispHistogram</tabstop>
</tabstops>
<resources>
<include location="resources/res.qrc"/>

14
osdrupgrade.cpp Normal file
View File

@ -0,0 +1,14 @@
#include "osdrupgrade.h"
#include "ui_osdrupgrade.h"
OSDRUpgrade::OSDRUpgrade(QWidget* parent) :
QDialog(parent),
ui(new Ui::OSDRUpgrade)
{
ui->setupUi(this);
}
OSDRUpgrade::~OSDRUpgrade()
{
delete ui;
}

21
osdrupgrade.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef INCLUDE_OSDRUPGRADE_H
#define INCLUDE_OSDRUPGRADE_H
#include <QDialog>
namespace Ui {
class OSDRUpgrade;
}
class OSDRUpgrade : public QDialog {
Q_OBJECT
public:
explicit OSDRUpgrade(QWidget* parent = NULL);
~OSDRUpgrade();
private:
Ui::OSDRUpgrade* ui;
};
#endif // INCLUDE_OSDRUPGRADE_H

128
osdrupgrade.ui Normal file
View File

@ -0,0 +1,128 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OSDRUpgrade</class>
<widget class="QDialog" name="OSDRUpgrade">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>324</width>
<height>250</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="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</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="groupBox_2">
<property name="title">
<string>Firmware</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Radio App</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>FPGA Image</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>DFU App</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Size</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Signed</string>
</property>
</widget>
</item>
<item row="4" column="1" colspan="2">
<widget class="QPushButton" name="flash">
<property name="text">
<string>Flash!</string>
</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/>
</ui>

View File

@ -1,97 +0,0 @@
#-------------------------------------------------
#
# Project created by QtCreator 2012-05-19T10:01:31
#
#-------------------------------------------------
!exists(local.pri) {
warning(Please create a local.pri)
}
exists(local.pri) {
include(local.pri)
}
include(local.pri)
QT += core gui opengl
CONFIG += silent
UI_DIR = tmp
MOC_DIR = tmp
RCC_DIR = tmp
OBJECTS_DIR = tmp
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = sdrangelove
TEMPLATE = app
SOURCES += main.cpp\
mainwindow.cpp \
hardware/samplefifo.cpp \
dsp/dspengine.cpp \
gui/indicator.cpp \
dsp/fftwindow.cpp \
dsp/lowpass.cpp \
dsp/interpolator.cpp \
dsp/channelizer.cpp \
dsp/spectrum.cpp \
dsp/samplesink.cpp \
settings.cpp \
hardware/osmosdrinput.cpp \
hardware/osmosdrthread.cpp \
hardware/samplesource.cpp \
gui/scale.cpp \
dsp/nco.cpp \
gui/glspectrum.cpp \
gui/scaleengine.cpp \
gui/valuedial.cpp
HEADERS += mainwindow.h \
hardware/samplefifo.h \
dsp/dspengine.h \
gui/indicator.h \
dsp/kissfft.h \
dsp/dsptypes.h \
dsp/fftwindow.h \
dsp/lowpass.h \
dsp/interpolator.h \
dsp/channelizer.h \
dsp/spectrum.h \
dsp/samplesink.h \
settings.h \
hardware/osmosdrinput.h \
hardware/osmosdrthread.h \
hardware/samplesource.h \
gui/scale.h \
gui/physicalunit.h \
dsp/nco.h \
gui/glspectrum.h \
gui/scaleengine.h \
gui/valuedial.h
FORMS += mainwindow.ui
RESOURCES += \
resources/res.qrc
RC_FILE = resources/sdrangelove.rc
unix {
LIBS += -lrt -lGLU
}
# Portaudio - currently not in use
unix:portaudio {
LIBS += -lasound
LIBS += portaudio/libportaudio.a
INCLUDEPATH += portaudio/portaudio/include
}
# libosmosdr
unix {
CONFIG += link_pkgconfig
PKGCONFIG += libosmosdr
}

View File

@ -35,7 +35,7 @@ Settings::Settings(Settings* reference) :
void Settings::defaults()
{
m_fftSize = 1024;
m_fftOverlap = 25;
m_fftOverlap = 10;
m_fftWindow = 3;
m_displayWaterfall = true;
m_invertedWaterfall = false;
@ -55,6 +55,10 @@ void Settings::defaults()
m_e4000if4 = 20;
m_e4000if5 = 150;
m_e4000if6 = 150;
m_filterI1 = 255;
m_filterI2 = 255;
m_filterQ1 = 255;
m_filterQ2 = 255;
}
void Settings::load()
@ -66,7 +70,7 @@ void Settings::load()
return;
m_fftSize = s.value("fftsize", 512).toInt();
m_fftOverlap = s.value("fftoverlap", 25).toInt();
m_fftOverlap = s.value("fftoverlap", 10).toInt();
m_fftWindow = s.value("fftwindow", 3).toInt();
m_displayWaterfall = s.value("displaywaterfall", true).toBool();
m_invertedWaterfall = s.value("invertedwaterfall", false).toBool();
@ -86,6 +90,10 @@ void Settings::load()
m_e4000if4 = s.value("e4000_if4", 20).toInt();
m_e4000if5 = s.value("e4000_if5", 150).toInt();
m_e4000if6 = s.value("e4000_if6", 150).toInt();
m_filterI1 = s.value("filter_i1", 255).toInt();
m_filterI2 = s.value("filter_i2", 255).toInt();
m_filterQ1 = s.value("filter_q1", 255).toInt();
m_filterQ2 = s.value("filter_q2", 255).toInt();
s.remove("livespectrumalpha");
}
@ -117,6 +125,10 @@ void Settings::save()
s.setValue("e4000_if4", m_e4000if4);
s.setValue("e4000_if5", m_e4000if5);
s.setValue("e4000_if6", m_e4000if6);
s.setValue("filter_i1", m_filterI1);
s.setValue("filter_i2", m_filterI2);
s.setValue("filter_q1", m_filterQ1);
s.setValue("filter_q2", m_filterQ2);
}
int Settings::fftSize() const
@ -189,8 +201,10 @@ bool Settings::displayWaterfall() const
void Settings::setDisplayWaterfall(bool v)
{
m_displayWaterfall = v;
m_changed = true;
if(v != m_displayWaterfall) {
m_displayWaterfall = v;
m_changed = true;
}
}
bool Settings::isModifiedDisplayWaterfall()
@ -210,8 +224,10 @@ bool Settings::invertedWaterfall() const
void Settings::setInvertedWaterfall(bool v)
{
m_invertedWaterfall = v;
m_changed = true;
if(v != m_invertedWaterfall) {
m_invertedWaterfall = v;
m_changed = true;
}
}
bool Settings::isModifiedInvertedWaterfall()
@ -231,8 +247,10 @@ bool Settings::displayLiveSpectrum() const
void Settings::setDisplayLiveSpectrum(bool v)
{
m_displayLiveSpectrum = v;
m_changed = true;
if(v != m_displayLiveSpectrum) {
m_displayLiveSpectrum = v;
m_changed = true;
}
}
bool Settings::isModifiedDisplayLiveSpectrum()
@ -559,3 +577,87 @@ bool Settings::isModifiedE4000if6()
return false;
}
}
quint8 Settings::filterI1() const
{
return m_filterI1;
}
void Settings::setFilterI1(quint8 v)
{
m_filterI1 = v;
m_changed = true;
}
bool Settings::isModifiedFilterI1()
{
if(m_reference->m_filterI1 != m_filterI1) {
m_filterI1 = m_reference->m_filterI1;
return true;
} else {
return false;
}
}
quint8 Settings::filterI2() const
{
return m_filterI2;
}
void Settings::setFilterI2(quint8 v)
{
m_filterI2 = v;
m_changed = true;
}
bool Settings::isModifiedFilterI2()
{
if(m_reference->m_filterI2 != m_filterI2) {
m_filterI2 = m_reference->m_filterI2;
return true;
} else {
return false;
}
}
quint8 Settings::filterQ1() const
{
return m_filterQ1;
}
void Settings::setFilterQ1(quint8 v)
{
m_filterQ1 = v;
m_changed = true;
}
bool Settings::isModifiedFilterQ1()
{
if(m_reference->m_filterQ1 != m_filterQ1) {
m_filterQ1 = m_reference->m_filterQ1;
return true;
} else {
return false;
}
}
quint8 Settings::filterQ2() const
{
return m_filterQ2;
}
void Settings::setFilterQ2(quint8 v)
{
m_filterQ2 = v;
m_changed = true;
}
bool Settings::isModifiedFilterQ2()
{
if(m_reference->m_filterQ2 != m_filterQ2) {
m_filterQ2 = m_reference->m_filterQ2;
return true;
} else {
return false;
}
}

View File

@ -113,6 +113,22 @@ public:
void setE4000if6(int v);
bool isModifiedE4000if6();
quint8 filterI1() const;
void setFilterI1(quint8 v);
bool isModifiedFilterI1();
quint8 filterI2() const;
void setFilterI2(quint8 v);
bool isModifiedFilterI2();
quint8 filterQ1() const;
void setFilterQ1(quint8 v);
bool isModifiedFilterQ1();
quint8 filterQ2() const;
void setFilterQ2(quint8 v);
bool isModifiedFilterQ2();
private:
bool m_changed;
const Settings* m_reference;
@ -139,6 +155,11 @@ private:
int m_e4000if4;
int m_e4000if5;
int m_e4000if6;
quint8 m_filterI1;
quint8 m_filterI2;
quint8 m_filterQ1;
quint8 m_filterQ2;
};
#endif // INCLUDE_SETTINGS_H