1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2026-07-30 14:04:18 -04:00

git clone git://git.osmocom.org/sdrangelove.git

This commit is contained in:
Hexameron
2014-05-18 16:52:39 +01:00
commit 7d3bfb26fc
203 changed files with 27958 additions and 0 deletions
+328
View File
@@ -0,0 +1,328 @@
#include "dsp/channelizer.h"
#include "dsp/inthalfbandfilter.h"
#include "dsp/dspcommands.h"
Channelizer::Channelizer(SampleSink* sampleSink) :
m_sampleSink(sampleSink),
m_inputSampleRate(100000),
m_requestedOutputSampleRate(100000),
m_requestedCenterFrequency(0),
m_currentOutputSampleRate(100000),
m_currentCenterFrequency(0)
{
}
Channelizer::~Channelizer()
{
freeFilterChain();
}
void Channelizer::configure(MessageQueue* messageQueue, int sampleRate, int centerFrequency)
{
Message* cmd = DSPConfigureChannelizer::create(sampleRate, centerFrequency);
cmd->submit(messageQueue, this);
}
void Channelizer::feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool firstOfBurst)
{
for(SampleVector::const_iterator sample = begin; sample != end; ++sample) {
Sample s(*sample);
FilterStages::iterator stage = m_filterStages.begin();
while(stage != m_filterStages.end()) {
if(!(*stage)->work(&s))
break;
++stage;
}
if(stage == m_filterStages.end())
m_sampleBuffer.push_back(s);
}
if(m_sampleSink != NULL)
m_sampleSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), firstOfBurst);
m_sampleBuffer.clear();
}
void Channelizer::start()
{
if(m_sampleSink != NULL)
m_sampleSink->start();
}
void Channelizer::stop()
{
if(m_sampleSink != NULL)
m_sampleSink->stop();
}
bool Channelizer::handleMessage(Message* cmd)
{
if(DSPSignalNotification::match(cmd)) {
DSPSignalNotification* signal = (DSPSignalNotification*)cmd;
m_inputSampleRate = signal->getSampleRate();
applyConfiguration();
cmd->completed();
if(m_sampleSink != NULL) {
signal = DSPSignalNotification::create(m_currentOutputSampleRate, m_currentCenterFrequency);
if(!m_sampleSink->handleMessage(signal))
signal->completed();
}
return true;
} else if(DSPConfigureChannelizer::match(cmd)) {
DSPConfigureChannelizer* chan = (DSPConfigureChannelizer*)cmd;
m_requestedOutputSampleRate = chan->getSampleRate();
m_requestedCenterFrequency = chan->getCenterFrequency();
applyConfiguration();
cmd->completed();
if(m_sampleSink != NULL) {
DSPSignalNotification* signal = DSPSignalNotification::create(m_currentOutputSampleRate, m_currentCenterFrequency);
if(!m_sampleSink->handleMessage(signal))
signal->completed();
}
return true;
} else {
if(m_sampleSink != NULL)
return m_sampleSink->handleMessage(cmd);
else return false;
}
}
void Channelizer::applyConfiguration()
{
freeFilterChain();
m_currentCenterFrequency = createFilterChain(
m_inputSampleRate / -2, m_inputSampleRate / 2,
m_requestedCenterFrequency - m_requestedOutputSampleRate / 2, m_requestedCenterFrequency + m_requestedOutputSampleRate / 2);
m_currentOutputSampleRate = m_inputSampleRate / (1 << m_filterStages.size());
}
Channelizer::FilterStage::FilterStage(Mode mode) :
m_filter(new IntHalfbandFilter),
m_workFunction(NULL)
{
switch(mode) {
case ModeCenter:
m_workFunction = &IntHalfbandFilter::workDecimateCenter;
break;
case ModeLowerHalf:
m_workFunction = &IntHalfbandFilter::workDecimateLowerHalf;
break;
case ModeUpperHalf:
m_workFunction = &IntHalfbandFilter::workDecimateUpperHalf;
break;
}
}
Channelizer::FilterStage::~FilterStage()
{
delete m_filter;
}
bool Channelizer::signalContainsChannel(Real sigStart, Real sigEnd, Real chanStart, Real chanEnd) const
{
//qDebug(" testing signal [%f, %f], channel [%f, %f]", sigStart, sigEnd, chanStart, chanEnd);
if(sigEnd <= sigStart)
return false;
if(chanEnd <= chanStart)
return false;
return (sigStart <= chanStart) && (sigEnd >= chanEnd);
}
Real Channelizer::createFilterChain(Real sigStart, Real sigEnd, Real chanStart, Real chanEnd)
{
Real sigBw = sigEnd - sigStart;
Real safetyMargin = sigBw / 20;
Real rot = sigBw / 4;
safetyMargin = 0;
//qDebug("Signal [%f, %f] (BW %f), Channel [%f, %f], Rot %f, Safety %f", sigStart, sigEnd, sigBw, chanStart, chanEnd, rot, safetyMargin);
#if 1
// check if it fits into the left half
if(signalContainsChannel(sigStart + safetyMargin, sigStart + sigBw / 2.0 - safetyMargin, chanStart, chanEnd)) {
//qDebug("-> take left half (rotate by +1/4 and decimate by 2)");
m_filterStages.push_back(new FilterStage(FilterStage::ModeLowerHalf));
return createFilterChain(sigStart, sigStart + sigBw / 2.0, chanStart, chanEnd);
}
// check if it fits into the right half
if(signalContainsChannel(sigEnd - sigBw / 2.0f + safetyMargin, sigEnd - safetyMargin, chanStart, chanEnd)) {
//qDebug("-> take right half (rotate by -1/4 and decimate by 2)");
m_filterStages.push_back(new FilterStage(FilterStage::ModeUpperHalf));
return createFilterChain(sigEnd - sigBw / 2.0f, sigEnd, chanStart, chanEnd);
}
// check if it fits into the center
if(signalContainsChannel(sigStart + rot + safetyMargin, sigStart + rot + sigBw / 2.0f - safetyMargin, chanStart, chanEnd)) {
//qDebug("-> take center half (decimate by 2)");
m_filterStages.push_back(new FilterStage(FilterStage::ModeCenter));
return createFilterChain(sigStart + rot, sigStart + sigBw / 2.0f + rot, chanStart, chanEnd);
}
#endif
Real ofs = ((chanEnd - chanStart) / 2.0 + chanStart) - ((sigEnd - sigStart) / 2.0 + sigStart);
qDebug("-> complete (final BW %f, frequency offset %f)", sigBw, ofs);
return ofs;
}
void Channelizer::freeFilterChain()
{
for(FilterStages::iterator it = m_filterStages.begin(); it != m_filterStages.end(); ++it)
delete *it;
m_filterStages.clear();
}
#if 0
///////////////////////////////////////////////////////////////////////////////////
// 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 "channelizer.h"
#include "hardware/audiooutput.h"
Channelizer::Channelizer()
{
#if 0
m_spectrum.configure(128 , 25, FFTWindow::Bartlett);
m_buffer.resize(2048);
m_bufferFill = 0;
m_nco.setFreq(-100000, 500000);
m_interpolator.create(51, 32, 32 * 500000, 150000 / 2);
m_distance = 500000.0 / 176400.0;
m_interpolator2.create(19, 8, 8 * 176400, 15000 / 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);
#endif
}
Channelizer::~Channelizer()
{
#if 0
m_audioOutput->stop();
delete m_audioOutput;
#endif
}
#if 0
void Channelizer::setGLSpectrum(GLSpectrum* glSpectrum)
{
m_spectrum.setGLSpectrum(glSpectrum);
}
#endif
size_t Channelizer::workUnitSize()
{
#if 0
return m_buffer.size();
#endif
return 0;
}
size_t Channelizer::work(SampleVector::const_iterator begin, SampleVector::const_iterator end)
{
#if 0
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;
#endif
}
#endif
+68
View File
@@ -0,0 +1,68 @@
#include "dsp/channelmarker.h"
QRgb ChannelMarker::m_colorTable[] = {
qRgb(0xc0, 0x00, 0x00),
qRgb(0x00, 0xc0, 0x00),
qRgb(0x00, 0x00, 0xc0),
qRgb(0xc0, 0xc0, 0x00),
qRgb(0xc0, 0x00, 0xc0),
qRgb(0x00, 0xc0, 0xc0),
qRgb(0xc0, 0x60, 0x00),
qRgb(0xc0, 0x00, 0x60),
qRgb(0x60, 0x00, 0xc0),
qRgb(0x60, 0x00, 0x00),
qRgb(0x00, 0x60, 0x00),
qRgb(0x00, 0x00, 0x60),
qRgb(0x60, 0x60, 0x00),
qRgb(0x60, 0x00, 0x60),
qRgb(0x00, 0x60, 0x60),
0
};
int ChannelMarker::m_nextColor = 0;
ChannelMarker::ChannelMarker(QObject* parent) :
QObject(parent),
m_centerFrequency(0),
m_bandwidth(0),
m_visible(false),
m_color(m_colorTable[m_nextColor])
{
++m_nextColor;
if(m_colorTable[m_nextColor] == 0)
m_nextColor = 0;
}
void ChannelMarker::setTitle(const QString& title)
{
m_title = title;
emit changed();
}
void ChannelMarker::setCenterFrequency(int centerFrequency)
{
m_centerFrequency = centerFrequency;
emit changed();
}
void ChannelMarker::setBandwidth(int bandwidth)
{
m_bandwidth = bandwidth;
emit changed();
}
void ChannelMarker::setVisible(bool visible)
{
m_visible = visible;
emit changed();
}
void ChannelMarker::setColor(const QColor& color)
{
m_color = color;
emit changed();
}
+19
View File
@@ -0,0 +1,19 @@
#include "dsp/dspcommands.h"
MESSAGE_CLASS_DEFINITION(DSPPing, Message)
MESSAGE_CLASS_DEFINITION(DSPExit, Message)
MESSAGE_CLASS_DEFINITION(DSPAcquisitionStart, Message)
MESSAGE_CLASS_DEFINITION(DSPAcquisitionStop, Message)
MESSAGE_CLASS_DEFINITION(DSPGetDeviceDescription, Message)
MESSAGE_CLASS_DEFINITION(DSPGetErrorMessage, Message)
MESSAGE_CLASS_DEFINITION(DSPSetSource, Message)
MESSAGE_CLASS_DEFINITION(DSPAddSink, Message)
MESSAGE_CLASS_DEFINITION(DSPRemoveSink, Message)
MESSAGE_CLASS_DEFINITION(DSPAddAudioSource, Message)
MESSAGE_CLASS_DEFINITION(DSPRemoveAudioSource, Message)
MESSAGE_CLASS_DEFINITION(DSPConfigureSpectrumVis, Message)
MESSAGE_CLASS_DEFINITION(DSPConfigureCorrection, Message)
MESSAGE_CLASS_DEFINITION(DSPEngineReport, Message)
MESSAGE_CLASS_DEFINITION(DSPConfigureScopeVis, Message)
MESSAGE_CLASS_DEFINITION(DSPSignalNotification, Message)
MESSAGE_CLASS_DEFINITION(DSPConfigureChannelizer, Message)
+517
View File
@@ -0,0 +1,517 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 "dsp/dspengine.h"
#include "dsp/channelizer.h"
#include "dsp/samplefifo.h"
#include "dsp/samplesink.h"
#include "dsp/dspcommands.h"
#include "dsp/samplesource/samplesource.h"
DSPEngine::DSPEngine(MessageQueue* reportQueue, QObject* parent) :
QThread(parent),
m_messageQueue(),
m_reportQueue(reportQueue),
m_state(StNotStarted),
m_sampleSource(NULL),
m_sampleSinks(),
m_sampleRate(0),
m_centerFrequency(0),
m_dcOffsetCorrection(false),
m_iqImbalanceCorrection(false),
m_iOffset(0),
m_qOffset(0),
m_iRange(1 << 16),
m_qRange(1 << 16),
m_imbalance(65536)
{
moveToThread(this);
}
DSPEngine::~DSPEngine()
{
wait();
}
void DSPEngine::start()
{
DSPPing cmd;
QThread::start();
cmd.execute(&m_messageQueue);
}
void DSPEngine::stop()
{
DSPExit cmd;
cmd.execute(&m_messageQueue);
}
bool DSPEngine::startAcquisition()
{
DSPAcquisitionStart cmd;
return cmd.execute(&m_messageQueue) == StRunning;
}
void DSPEngine::stopAcquistion()
{
DSPAcquisitionStop cmd;
cmd.execute(&m_messageQueue);
}
void DSPEngine::setSource(SampleSource* source)
{
DSPSetSource cmd(source);
cmd.execute(&m_messageQueue);
}
void DSPEngine::addSink(SampleSink* sink)
{
DSPAddSink cmd(sink);
cmd.execute(&m_messageQueue);
}
void DSPEngine::removeSink(SampleSink* sink)
{
DSPRemoveSink cmd(sink);
cmd.execute(&m_messageQueue);
}
void DSPEngine::addAudioSource(AudioFifo* audioFifo)
{
DSPAddAudioSource cmd(audioFifo);
cmd.execute(&m_messageQueue);
}
void DSPEngine::removeAudioSource(AudioFifo* audioFifo)
{
DSPRemoveAudioSource cmd(audioFifo);
cmd.execute(&m_messageQueue);
}
void DSPEngine::configureCorrections(bool dcOffsetCorrection, bool iqImbalanceCorrection)
{
Message* cmd = DSPConfigureCorrection::create(dcOffsetCorrection, iqImbalanceCorrection);
cmd->submit(&m_messageQueue);
}
QString DSPEngine::errorMessage()
{
DSPGetErrorMessage cmd;
cmd.execute(&m_messageQueue);
return cmd.getErrorMessage();
}
QString DSPEngine::deviceDescription()
{
DSPGetDeviceDescription cmd;
cmd.execute(&m_messageQueue);
return cmd.getDeviceDescription();
}
void DSPEngine::run()
{
connect(&m_messageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleMessages()), Qt::QueuedConnection);
m_state = StIdle;
handleMessages();
exec();
}
void DSPEngine::dcOffset(SampleVector::iterator begin, SampleVector::iterator end)
{
int count = end - begin;
int io = 0;
int qo = 0;
// sum all sample components
for(SampleVector::iterator it = begin; it < end; it++) {
io += it->real();
qo += it->imag();
}
// build a sliding average (el cheapo style)
m_iOffset = (m_iOffset * 3 + io / count) >> 2;
m_qOffset = (m_qOffset * 3 + qo / count) >> 2;
// correct samples
Sample corr(m_iOffset, m_qOffset);
for(SampleVector::iterator it = begin; it < end; it++)
*it -= corr;
}
void DSPEngine::imbalance(SampleVector::iterator begin, SampleVector::iterator end)
{
int iMin = 0;
int iMax = 0;
int qMin = 0;
int qMax = 0;
// find value ranges for both I and Q
// both intervals should be same same size (for a perfect circle)
for(SampleVector::iterator it = begin; it < end; it++) {
if(it != begin) {
if(it->real() < iMin)
iMin = it->real();
else if(it->real() > iMax)
iMax = it->real();
if(it->imag() < qMin)
qMin = it->imag();
else if(it->imag() > qMax)
qMax = it->imag();
} else {
iMin = it->real();
iMax = it->real();
qMin = it->imag();
qMax = it->imag();
}
}
// sliding average (el cheapo again)
m_iRange = (m_iRange * 15 + (iMax - iMin)) >> 4;
m_qRange = (m_qRange * 15 + (qMax - qMin)) >> 4;
// calculate imbalance as Q15.16
if(m_qRange != 0)
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++)
it->m_imag = (it->m_imag * m_imbalance) >> 16;
}
void DSPEngine::work()
{
SampleFifo* sampleFifo = m_sampleSource->getSampleFifo();
size_t samplesDone = 0;
bool firstOfBurst = true;
while((sampleFifo->fill() > 0) && (m_messageQueue.countPending() == 0) && (samplesDone < m_sampleRate)) {
SampleVector::iterator part1begin;
SampleVector::iterator part1end;
SampleVector::iterator part2begin;
SampleVector::iterator part2end;
size_t count = sampleFifo->readBegin(sampleFifo->fill(), &part1begin, &part1end, &part2begin, &part2end);
// first part of FIFO data
if(part1begin != part1end) {
// correct stuff
if(m_dcOffsetCorrection)
dcOffset(part1begin, part1end);
if(m_iqImbalanceCorrection)
imbalance(part1begin, part1end);
// feed data to handlers
for(SampleSinks::const_iterator it = m_sampleSinks.begin(); it != m_sampleSinks.end(); it++)
(*it)->feed(part1begin, part1end, firstOfBurst);
firstOfBurst = false;
}
// second part of FIFO data (used when block wraps around)
if(part2begin != part2end) {
// correct stuff
if(m_dcOffsetCorrection)
dcOffset(part2begin, part2end);
if(m_iqImbalanceCorrection)
imbalance(part2begin, part2end);
// feed data to handlers
for(SampleSinks::const_iterator it = m_sampleSinks.begin(); it != m_sampleSinks.end(); it++)
(*it)->feed(part2begin, part2end, firstOfBurst);
firstOfBurst = false;
}
// adjust FIFO pointers
sampleFifo->readCommit(count);
samplesDone += count;
}
#if 0
size_t wus;
size_t maxWorkUnitSize = 0;
size_t samplesDone = 0;
wus = m_spectrum.workUnitSize();
if(wus > maxWorkUnitSize)
maxWorkUnitSize = wus;
for(SampleSinks::const_iterator it = m_sampleSinks.begin(); it != m_sampleSinks.end(); it++) {
wus = (*it)->workUnitSize();
if(wus > maxWorkUnitSize)
maxWorkUnitSize = wus;
}
while((m_sampleFifo.fill() > maxWorkUnitSize) && (m_commandQueue.countPending() == 0) && (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);
// 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(SampleSinks::const_iterator it = m_sampleSinks.begin(); it != m_sampleSinks.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(SampleSinks::const_iterator it = m_sampleSinks.begin(); it != m_sampleSinks.end(); it++)
(*it)->feed(part1begin, part1end);
}
// 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);
}
#endif
}
DSPEngine::State DSPEngine::gotoIdle()
{
switch(m_state) {
case StNotStarted:
return StNotStarted;
case StIdle:
case StError:
return StIdle;
case StRunning:
break;
}
if(m_sampleSource == NULL)
return StIdle;
for(SampleSinks::const_iterator it = m_sampleSinks.begin(); it != m_sampleSinks.end(); it++)
(*it)->stop();
m_sampleSource->stopInput();
m_deviceDescription.clear();
m_audioOutput.stop();
m_sampleRate = 0;
return StIdle;
}
DSPEngine::State DSPEngine::gotoRunning()
{
switch(m_state) {
case StNotStarted:
return StNotStarted;
case StRunning:
return StRunning;
case StIdle:
case StError:
break;
}
if(m_sampleSource == NULL)
return gotoError("No sample source configured");
m_iOffset = 0;
m_qOffset = 0;
m_iRange = 1 << 16;
m_qRange = 1 << 16;
if(!m_sampleSource->startInput(0))
return gotoError("Could not start sample source");
m_deviceDescription = m_sampleSource->getDeviceDescription();
m_audioOutput.start(0, 44100);
for(SampleSinks::const_iterator it = m_sampleSinks.begin(); it != m_sampleSinks.end(); it++)
(*it)->start();
m_sampleRate = 0; // make sure, report is sent
generateReport();
return StRunning;
}
DSPEngine::State DSPEngine::gotoError(const QString& errorMessage)
{
m_errorMessage = errorMessage;
m_deviceDescription.clear();
m_state = StError;
return StError;
}
void DSPEngine::handleSetSource(SampleSource* source)
{
gotoIdle();
if(m_sampleSource != NULL)
disconnect(m_sampleSource->getSampleFifo(), SIGNAL(dataReady()), this, SLOT(handleData()));
m_sampleSource = source;
if(m_sampleSource != NULL)
connect(m_sampleSource->getSampleFifo(), SIGNAL(dataReady()), this, SLOT(handleData()), Qt::QueuedConnection);
generateReport();
}
void DSPEngine::generateReport()
{
bool needReport = false;
int sampleRate;
quint64 centerFrequency;
if(m_sampleSource != NULL) {
sampleRate = m_sampleSource->getSampleRate();
centerFrequency = m_sampleSource->getCenterFrequency();
} else {
sampleRate = 100000;
centerFrequency = 100000000;
}
if(sampleRate != m_sampleRate) {
m_sampleRate = sampleRate;
needReport = true;
for(SampleSinks::const_iterator it = m_sampleSinks.begin(); it != m_sampleSinks.end(); it++) {
DSPSignalNotification* signal = DSPSignalNotification::create(m_sampleRate, 0);
signal->submit(&m_messageQueue, *it);
}
}
if(centerFrequency != m_centerFrequency) {
m_centerFrequency = centerFrequency;
needReport = true;
}
if(needReport) {
Message* rep = DSPEngineReport::create(m_sampleRate, m_centerFrequency);
rep->submit(m_reportQueue);
}
}
bool DSPEngine::distributeMessage(Message* message)
{
if(m_sampleSource != NULL) {
if((message->getDestination() == NULL) || (message->getDestination() == m_sampleSource)) {
if(m_sampleSource->handleMessage(message)) {
generateReport();
return true;
}
}
}
for(SampleSinks::const_iterator it = m_sampleSinks.begin(); it != m_sampleSinks.end(); it++) {
if((message->getDestination() == NULL) || (message->getDestination() == *it)) {
if((*it)->handleMessage(message))
return true;
}
}
return false;
}
void DSPEngine::handleData()
{
if(m_state == StRunning)
work();
}
void DSPEngine::handleMessages()
{
Message* message;
while((message = m_messageQueue.accept()) != NULL) {
qDebug("Message: %s", message->getIdentifier());
if(DSPPing::match(message)) {
message->completed(m_state);
} else if(DSPExit::match(message)) {
gotoIdle();
m_state = StNotStarted;
exit();
message->completed(m_state);
} else if(DSPAcquisitionStart::match(message)) {
m_state = gotoIdle();
if(m_state == StIdle)
m_state = gotoRunning();
message->completed(m_state);
} else if(DSPAcquisitionStop::match(message)) {
m_state = gotoIdle();
message->completed(m_state);
} else if(DSPGetDeviceDescription::match(message)) {
((DSPGetDeviceDescription*)message)->setDeviceDescription(m_deviceDescription);
message->completed();
} else if(DSPGetErrorMessage::match(message)) {
((DSPGetErrorMessage*)message)->setErrorMessage(m_errorMessage);
message->completed();
} else if(DSPSetSource::match(message)) {
handleSetSource(((DSPSetSource*)message)->getSampleSource());
message->completed();
} else if(DSPAddSink::match(message)) {
SampleSink* sink = ((DSPAddSink*)message)->getSampleSink();
if(m_state == StRunning) {
DSPSignalNotification* signal = DSPSignalNotification::create(m_sampleRate, 0);
signal->submit(&m_messageQueue, sink);
sink->start();
}
m_sampleSinks.push_back(sink);
message->completed();
} else if(DSPRemoveSink::match(message)) {
SampleSink* sink = ((DSPAddSink*)message)->getSampleSink();
if(m_state == StRunning)
sink->stop();
m_sampleSinks.remove(sink);
message->completed();
} else if(DSPAddAudioSource::match(message)) {
m_audioOutput.addFifo(((DSPAddAudioSource*)message)->getAudioFifo());
message->completed();
} else if(DSPRemoveAudioSource::match(message)) {
m_audioOutput.removeFifo(((DSPAddAudioSource*)message)->getAudioFifo());
message->completed();
} else if(DSPConfigureCorrection::match(message)) {
DSPConfigureCorrection* conf = (DSPConfigureCorrection*)message;
m_iqImbalanceCorrection = conf->getIQImbalanceCorrection();
if(m_dcOffsetCorrection != conf->getDCOffsetCorrection()) {
m_dcOffsetCorrection = conf->getDCOffsetCorrection();
m_iOffset = 0;
m_qOffset = 0;
}
if(m_iqImbalanceCorrection != conf->getIQImbalanceCorrection()) {
m_iqImbalanceCorrection = conf->getIQImbalanceCorrection();
m_iRange = 1 << 16;
m_qRange = 1 << 16;
m_imbalance = 65536;
}
message->completed();
} else {
if(!distributeMessage(message))
message->completed();
}
}
}
+26
View File
@@ -0,0 +1,26 @@
#include "dsp/fftengine.h"
#ifdef USE_KISSFFT
#include "dsp/kissengine.h"
#endif
#ifdef USE_FFTW
#include "dsp/fftwengine.h"
#endif // USE_FFTW
FFTEngine::~FFTEngine()
{
}
FFTEngine* FFTEngine::create()
{
#ifdef USE_FFTW
qDebug("FFT: using FFTW engine");
return new FFTWEngine;
#endif // USE_FFTW
#ifdef USE_KISSFFT
qDebug("FFT: using KissFFT engine");
return new KissEngine;
#endif // USE_KISSFFT
qCritical("FFT: no engine built");
return NULL;
}
+69
View File
@@ -0,0 +1,69 @@
#include <QTime>
#include "dsp/fftwengine.h"
FFTWEngine::FFTWEngine() :
m_plans(),
m_currentPlan(NULL)
{
}
FFTWEngine::~FFTWEngine()
{
freeAll();
}
void FFTWEngine::configure(int n, bool inverse)
{
for(Plans::const_iterator it = m_plans.begin(); it != m_plans.end(); ++it) {
if(((*it)->n == n) && ((*it)->inverse == inverse)) {
m_currentPlan = *it;
return;
}
}
m_currentPlan = new Plan;
m_currentPlan->n = n;
m_currentPlan->inverse = inverse;
m_currentPlan->in = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * n);
m_currentPlan->out = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * n);
QTime t;
t.start();
m_globalPlanMutex.lock();
m_currentPlan->plan = fftwf_plan_dft_1d(n, m_currentPlan->in, m_currentPlan->out, inverse ? FFTW_BACKWARD : FFTW_FORWARD, FFTW_PATIENT);
m_globalPlanMutex.unlock();
qDebug("FFT: creating FFTW plan (n=%d,%s) took %dms", n, inverse ? "inverse" : "forward", t.elapsed());
m_plans.push_back(m_currentPlan);
}
void FFTWEngine::transform()
{
if(m_currentPlan != NULL)
fftwf_execute(m_currentPlan->plan);
}
Complex* FFTWEngine::in()
{
if(m_currentPlan != NULL)
return reinterpret_cast<Complex*>(m_currentPlan->in);
else return NULL;
}
Complex* FFTWEngine::out()
{
if(m_currentPlan != NULL)
return reinterpret_cast<Complex*>(m_currentPlan->out);
else return NULL;
}
QMutex FFTWEngine::m_globalPlanMutex;
void FFTWEngine::freeAll()
{
for(Plans::iterator it = m_plans.begin(); it != m_plans.end(); ++it) {
fftwf_destroy_plan((*it)->plan);
fftwf_free((*it)->in);
fftwf_free((*it)->out);
delete *it;
}
m_plans.clear();
}
+73
View File
@@ -0,0 +1,73 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 "dsp/fftwindow.h"
void FFTWindow::create(Function function, int n)
{
Real (*wFunc)(Real n, Real i);
m_window.clear();
switch(function) {
case Flattop:
wFunc = flatTop;
break;
case Bartlett:
wFunc = bartlett;
break;
case BlackmanHarris:
wFunc = blackmanHarris;
break;
case Hamming:
wFunc = hamming;
break;
case Hanning:
wFunc = hanning;
break;
case Rectangle:
default:
wFunc = rectangle;
break;
}
for(int i = 0; i < n; i++)
m_window.push_back(wFunc(n, i));
}
void FFTWindow::apply(const std::vector<Real>& in, std::vector<Real>* out)
{
for(size_t i = 0; i < m_window.size(); i++)
(*out)[i] = in[i] * m_window[i];
}
void FFTWindow::apply(const std::vector<Complex>& in, std::vector<Complex>* out)
{
for(size_t i = 0; i < m_window.size(); i++)
(*out)[i] = in[i] * m_window[i];
}
void FFTWindow::apply(const Complex* in, Complex* out)
{
for(size_t i = 0; i < m_window.size(); i++)
out[i] = in[i] * m_window[i];
}
+120
View File
@@ -0,0 +1,120 @@
#define _USE_MATH_DEFINES
#include <math.h>
#include <vector>
#include "dsp/interpolator.h"
static std::vector<Real> createPolyphaseLowPass(
int phaseSteps,
double gain,
double sampleRateHz,
double cutoffFreqHz,
double transitionWidthHz,
double oobAttenuationdB)
{
int ntaps = (int)(oobAttenuationdB * sampleRateHz / (22.0 * transitionWidthHz));
if((ntaps % 2) != 0)
ntaps++;
ntaps *= phaseSteps;
std::vector<float> taps(ntaps);
std::vector<float> window(ntaps);
for(int n = 0; n < ntaps; n++)
window[n] = 0.54 - 0.46 * cos ((2 * M_PI * n) / (ntaps - 1));
int M = (ntaps - 1) / 2;
double fwT0 = 2 * M_PI * cutoffFreqHz / sampleRateHz;
for(int n = -M; n <= M; n++) {
if(n == 0) taps[n + M] = fwT0 / M_PI * window[n + M];
else taps[n + M] = sin (n * fwT0) / (n * M_PI) * window[n + M];
}
double max = taps[0 + M];
for(int n = 1; n <= M; n++)
max += 2.0 * taps[n + M];
gain /= max;
for(int i = 0; i < ntaps; i++)
taps[i] *= gain;
return taps;
}
Interpolator::Interpolator() :
m_taps(NULL),
m_alignedTaps(NULL)
{
}
Interpolator::~Interpolator()
{
free();
}
void Interpolator::create(int phaseSteps, double sampleRate, double cutoff)
{
free();
std::vector<Real> taps = createPolyphaseLowPass(
phaseSteps, // number of polyphases
1.0, // gain
phaseSteps * sampleRate, // sampling frequency
cutoff, // hz beginning of transition band
sampleRate / 5.0, // hz width of transition band
20.0); // out of band attenuation
// init state
m_ptr = 0;
m_nTaps = taps.size() / phaseSteps;
m_phaseSteps = phaseSteps;
m_samples.resize(m_nTaps + 2);
for(int i = 0; i < m_nTaps + 2; i++)
m_samples[i] = 0;
// reorder into polyphase
std::vector<Real> polyphase(taps.size());
for(int phase = 0; phase < phaseSteps; phase++) {
for(int i = 0; i < m_nTaps; i++)
polyphase[phase * m_nTaps + i] = taps[i * phaseSteps + phase];
}
// normalize phase filters
for(int phase = 0; phase < phaseSteps; phase++) {
Real sum = 0;
for(int i = phase * m_nTaps; i < phase * m_nTaps + m_nTaps; i++)
sum += polyphase[i];
for(int i = phase * m_nTaps; i < phase * m_nTaps + m_nTaps; i++)
polyphase[i] /= sum;
}
// move taps around to match sse storage requirements
m_taps = new float[2 * taps.size() + 8];
for(int i = 0; i < 2 * taps.size() + 8; ++i)
m_taps[i] = 0;
m_alignedTaps = (float*)((((quint64)m_taps) + 15) & ~15);
for(int i = 0; i < taps.size(); ++i) {
m_alignedTaps[2 * i + 0] = polyphase[i];
m_alignedTaps[2 * i + 1] = polyphase[i];
}
m_taps2 = new float[2 * taps.size() + 8];
for(int i = 0; i < 2 * taps.size() + 8; ++i)
m_taps2[i] = 0;
m_alignedTaps2 = (float*)((((quint64)m_taps2) + 15) & ~15);
for(int i = 1; i < taps.size(); ++i) {
m_alignedTaps2[2 * (i - 1) + 0] = polyphase[i];
m_alignedTaps2[2 * (i - 1) + 1] = polyphase[i];
}
}
void Interpolator::free()
{
if(m_taps != NULL) {
delete[] m_taps;
m_taps = NULL;
m_alignedTaps = NULL;
delete[] m_taps2;
m_taps2 = NULL;
m_alignedTaps2 = NULL;
}
}
+11
View File
@@ -0,0 +1,11 @@
#include "dsp/inthalfbandfilter.h"
IntHalfbandFilter::IntHalfbandFilter()
{
for(int i = 0; i < HB_FILTERORDER + 1; i++) {
m_samples[i][0] = 0;
m_samples[i][1] = 0;
}
m_ptr = 0;
m_state = 0;
}
+25
View File
@@ -0,0 +1,25 @@
#include "dsp/kissengine.h"
void KissEngine::configure(int n, bool inverse)
{
m_fft.configure(n, inverse);
if(n > m_in.size())
m_in.resize(n);
if(n > m_out.size())
m_out.resize(n);
}
void KissEngine::transform()
{
m_fft.transform(&m_in[0], &m_out[0]);
}
Complex* KissEngine::in()
{
return &m_in[0];
}
Complex* KissEngine::out()
{
return &m_out[0];
}
+6
View File
@@ -0,0 +1,6 @@
#include <stdio.h>
#include <QtGlobal>
#define _USE_MATH_DEFINES
#include <math.h>
#include <vector>
#include "dsp/lowpass.h"
+1
View File
@@ -0,0 +1 @@
#include "dsp/movingaverage.h"
+70
View File
@@ -0,0 +1,70 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 <QtGlobal>
#include <stdio.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include "dsp/nco.h"
Real NCO::m_table[NCO::TableSize];
bool NCO::m_tableInitialized = false;
void NCO::initTable()
{
if(m_tableInitialized)
return;
for(int i = 0; i < TableSize; i++)
m_table[i] = cos((2.0 * M_PI * i) / TableSize);
m_tableInitialized = true;
}
NCO::NCO()
{
initTable();
m_phase = 0;
}
void NCO::setFreq(Real freq, Real sampleRate)
{
m_phaseIncrement = (freq * TableSize) / sampleRate;
qDebug("NCO phase inc %d", m_phaseIncrement);
}
float NCO::next()
{
m_phase += m_phaseIncrement;
while(m_phase >= TableSize)
m_phase -= TableSize;
while(m_phase < 0)
m_phase += TableSize;
return m_table[m_phase];
}
Complex NCO::nextIQ()
{
m_phase += m_phaseIncrement;
while(m_phase >= TableSize)
m_phase -= TableSize;
while(m_phase < 0)
m_phase += TableSize;
return Complex(m_table[m_phase], -m_table[(m_phase + TableSize / 4) % TableSize]);
}
+17
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
View File
@@ -0,0 +1,28 @@
#ifndef INCLUDE_PIDCONTROLLER_H
#define INCLUDE_PIDCONTROLLER_H
#include "dsp/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
+231
View File
@@ -0,0 +1,231 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 "dsp/samplefifo.h"
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
void SampleFifo::create(uint s)
{
m_size = 0;
m_fill = 0;
m_head = 0;
m_tail = 0;
m_data.resize(s);
m_size = m_data.size();
if(m_size != s)
qCritical("SampleFifo: out of memory");
}
SampleFifo::SampleFifo(QObject* parent) :
QObject(parent),
m_data()
{
m_suppressed = -1;
m_size = 0;
m_fill = 0;
m_head = 0;
m_tail = 0;
}
SampleFifo::SampleFifo(int size, QObject* parent) :
QObject(parent),
m_data()
{
m_suppressed = -1;
create(size);
}
SampleFifo::~SampleFifo()
{
QMutexLocker mutexLocker(&m_mutex);
m_size = 0;
}
bool SampleFifo::setSize(int size)
{
create(size);
return m_data.size() == (uint)size;
}
uint SampleFifo::write(const quint8* data, uint count)
{
QMutexLocker mutexLocker(&m_mutex);
uint total;
uint remaining;
uint 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 %u samples", count - total);
} else {
if(m_msgRateTimer.elapsed() > 2500) {
qCritical("SampleFifo: %u messages dropped", m_suppressed);
qCritical("SampleFifo: overflow - dropping %u 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;
}
uint SampleFifo::write(SampleVector::const_iterator begin, SampleVector::const_iterator end)
{
QMutexLocker mutexLocker(&m_mutex);
uint count = end - begin;
uint total;
uint remaining;
uint len;
total = MIN(count, m_size - m_fill);
if(total < count) {
if(m_suppressed < 0) {
m_suppressed = 0;
m_msgRateTimer.start();
qCritical("SampleFifo: overflow - dropping %u samples", count - total);
} else {
if(m_msgRateTimer.elapsed() > 2500) {
qCritical("SampleFifo: %u messages dropped", m_suppressed);
qCritical("SampleFifo: overflow - dropping %u 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;
}
uint SampleFifo::read(SampleVector::iterator begin, SampleVector::iterator end)
{
QMutexLocker mutexLocker(&m_mutex);
uint count = end - begin;
uint total;
uint remaining;
uint len;
total = MIN(count, m_fill);
if(total < count)
qCritical("SampleFifo: underflow - missing %u samples", count - total);
remaining = total;
while(remaining > 0) {
len = MIN(remaining, m_size - m_head);
std::copy(m_data.begin() + m_head, m_data.begin() + m_head + len, begin);
m_head += len;
m_head %= m_size;
m_fill -= len;
begin += len;
remaining -= len;
}
return total;
}
uint SampleFifo::readBegin(uint count,
SampleVector::iterator* part1Begin, SampleVector::iterator* part1End,
SampleVector::iterator* part2Begin, SampleVector::iterator* part2End)
{
QMutexLocker mutexLocker(&m_mutex);
uint total;
uint remaining;
uint len;
uint head = m_head;
total = MIN(count, m_fill);
if(total < count)
qCritical("SampleFifo: underflow - missing %u samples", count - total);
remaining = total;
if(remaining > 0) {
len = MIN(remaining, m_size - head);
*part1Begin = m_data.begin() + head;
*part1End = m_data.begin() + head + len;
head += len;
head %= m_size;
remaining -= len;
} else {
*part1Begin = m_data.end();
*part1End = m_data.end();
}
if(remaining > 0) {
len = MIN(remaining, m_size - head);
*part2Begin = m_data.begin() + head;
*part2End = m_data.begin() + head + len;
} else {
*part2Begin = m_data.end();
*part2End = m_data.end();
}
return total;
}
uint SampleFifo::readCommit(uint count)
{
QMutexLocker mutexLocker(&m_mutex);
if(count > m_fill) {
qCritical("SampleFifo: cannot commit more than available samples");
count = m_fill;
}
m_head = (m_head + count) % m_size;
m_fill -= count;
return count;
}
+66
View File
@@ -0,0 +1,66 @@
#include "dsp/samplesink.h"
SampleSink::SampleSink()
{
}
SampleSink::~SampleSink()
{
}
#if 0
#include "samplesink.h"
SampleSink::SampleSink() :
m_sinkBuffer(),
m_sinkBufferFill(0)
{
}
void SampleSink::feed(SampleVector::const_iterator begin, SampleVector::const_iterator end)
{
size_t wus = workUnitSize();
// make sure our buffer is big enough for at least one work unit
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_sinkBufferFill > 0) && (m_sinkBufferFill < wus)) {
// check number if missing samples, but don't copy more than we have
size_t len = wus - m_sinkBufferFill;
if(len > (size_t)(end - begin))
len = end - begin;
// copy
std::copy(begin, begin + len, m_sinkBuffer.begin() + m_sinkBufferFill);
// adjust pointers
m_sinkBufferFill += len;
begin += len;
}
// if one complete work unit is in the buffer, feed it to the worker
if(m_sinkBufferFill >= wus) {
size_t done = 0;
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_sinkBufferFill - done > 0) {
qDebug("error in SampleSink buffer management");
std::copy(m_sinkBuffer.begin() + done, m_sinkBuffer.begin() + m_sinkBufferFill, m_sinkBuffer.begin());
}
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_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_sinkBuffer.begin());
m_sinkBufferFill = end - begin;
begin += m_sinkBufferFill;
}
}
}
#endif
+64
View File
@@ -0,0 +1,64 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 "dsp/samplesource/samplesource.h"
#include "util/simpleserializer.h"
SampleSource::GeneralSettings::GeneralSettings() :
m_centerFrequency(100000000)
{
}
void SampleSource::GeneralSettings::resetToDefaults()
{
m_centerFrequency = 100000000;
}
QByteArray SampleSource::GeneralSettings::serialize() const
{
SimpleSerializer s(1);
s.writeU64(1, m_centerFrequency);
return s.final();
}
bool SampleSource::GeneralSettings::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if(!d.isValid()) {
resetToDefaults();
return false;
}
if(d.getVersion() == 1) {
d.readU64(1, &m_centerFrequency, 100000000);
return true;
} else {
resetToDefaults();
return false;
}
}
SampleSource::SampleSource(MessageQueue* guiMessageQueue) :
m_sampleFifo(),
m_guiMessageQueue(guiMessageQueue)
{
}
SampleSource::~SampleSource()
{
}
+141
View File
@@ -0,0 +1,141 @@
#include "dsp/scopevis.h"
#include "gui/glscope.h"
#include "dsp/dspcommands.h"
#include "util/messagequeue.h"
ScopeVis::ScopeVis(GLScope* glScope) :
m_glScope(glScope),
m_trace(100000),
m_fill(0),
m_triggerState(Untriggered),
m_triggerChannel(TriggerFreeRun),
m_triggerLevelHigh(0.01 * 32768),
m_triggerLevelLow(0.01 * 32768 - 1024),
m_sampleRate(0)
{
}
void ScopeVis::configure(MessageQueue* msgQueue, TriggerChannel triggerChannel, Real triggerLevelHigh, Real triggerLevelLow)
{
Message* cmd = DSPConfigureScopeVis::create(triggerChannel, triggerLevelHigh, triggerLevelLow);
cmd->submit(msgQueue, this);
}
void ScopeVis::feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool firstOfBurst)
{
while(begin < end) {
if(m_triggerChannel == TriggerChannelI) {
if(m_triggerState == Untriggered) {
while(begin < end) {
if(begin->real() >= m_triggerLevelHigh) {
m_triggerState = Triggered;
break;
}
++begin;
}
}
if(m_triggerState == Triggered) {
int count = end - begin;
if(count > (int)(m_trace.size() - m_fill))
count = m_trace.size() - m_fill;
std::vector<Complex>::iterator it = m_trace.begin() + m_fill;
for(int i = 0; i < count; ++i) {
*it++ = Complex(begin->real() / 32768.0, begin->imag() / 32768.0);
++begin;
}
m_fill += count;
if(m_fill >= m_trace.size()) {
m_glScope->newTrace(m_trace, m_sampleRate);
m_fill = 0;
m_triggerState = WaitForReset;
}
}
if(m_triggerState == WaitForReset) {
while(begin < end) {
if(begin->real() < m_triggerLevelLow) {
m_triggerState = Untriggered;
break;
}
++begin;
}
}
} else if(m_triggerChannel == TriggerChannelQ) {
if(m_triggerState == Untriggered) {
while(begin < end) {
if(begin->imag() >= m_triggerLevelHigh) {
m_triggerState = Triggered;
break;
}
++begin;
}
}
if(m_triggerState == Triggered) {
int count = end - begin;
if(count > (int)(m_trace.size() - m_fill))
count = m_trace.size() - m_fill;
std::vector<Complex>::iterator it = m_trace.begin() + m_fill;
for(int i = 0; i < count; ++i) {
*it++ = Complex(begin->real() / 32768.0, begin->imag() / 32768.0);
++begin;
}
m_fill += count;
if(m_fill >= m_trace.size()) {
m_glScope->newTrace(m_trace, m_sampleRate);
m_fill = 0;
m_triggerState = WaitForReset;
}
}
if(m_triggerState == WaitForReset) {
while(begin < end) {
if(begin->imag() < m_triggerLevelLow) {
m_triggerState = Untriggered;
break;
}
++begin;
}
}
} else {
int count = end - begin;
if(count > (int)(m_trace.size() - m_fill))
count = m_trace.size() - m_fill;
std::vector<Complex>::iterator it = m_trace.begin() + m_fill;
for(int i = 0; i < count; ++i) {
*it++ = Complex(begin->real() / 32768.0, begin->imag() / 32768.0);
++begin;
}
m_fill += count;
if(m_fill >= m_trace.size()) {
m_glScope->newTrace(m_trace, m_sampleRate);
m_fill = 0;
}
}
}
}
void ScopeVis::start()
{
}
void ScopeVis::stop()
{
}
bool ScopeVis::handleMessage(Message* message)
{
if(DSPSignalNotification::match(message)) {
DSPSignalNotification* signal = (DSPSignalNotification*)message;
m_sampleRate = signal->getSampleRate();
message->completed();
return true;
} else if(DSPConfigureScopeVis::match(message)) {
DSPConfigureScopeVis* conf = (DSPConfigureScopeVis*)message;
m_triggerState = Untriggered;
m_triggerChannel = (TriggerChannel)conf->getTriggerChannel();
m_triggerLevelHigh = conf->getTriggerLevelHigh() * 32767;
m_triggerLevelLow = conf->getTriggerLevelLow() * 32767;
message->completed();
return true;
} else {
return false;
}
}
+125
View File
@@ -0,0 +1,125 @@
#include "dsp/spectrumvis.h"
#include "gui/glspectrum.h"
#include "dsp/dspcommands.h"
#include "util/messagequeue.h"
#define MAX_FFT_SIZE 4096
#ifdef _WIN32
double log2f(double n)
{
return log(n) / log(2.0);
}
#endif
SpectrumVis::SpectrumVis(GLSpectrum* glSpectrum) :
SampleSink(),
m_fft(FFTEngine::create()),
m_fftBuffer(MAX_FFT_SIZE),
m_logPowerSpectrum(MAX_FFT_SIZE),
m_fftBufferFill(0),
m_glSpectrum(glSpectrum)
{
handleConfigure(1024, 10, FFTWindow::BlackmanHarris);
}
SpectrumVis::~SpectrumVis()
{
delete m_fft;
}
void SpectrumVis::configure(MessageQueue* msgQueue, int fftSize, int overlapPercent, FFTWindow::Function window)
{
Message* cmd = DSPConfigureSpectrumVis::create(fftSize, overlapPercent, window);
cmd->submit(msgQueue, this);
}
void SpectrumVis::feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool firstOfBurst)
{
// if no visualisation is set, send the samples to /dev/null
if(m_glSpectrum == NULL)
return;
while(begin < end) {
size_t todo = end - begin;
size_t samplesNeeded = m_refillSize - m_fftBufferFill;
if(todo >= samplesNeeded) {
// fill up the buffer
std::vector<Complex>::iterator it = m_fftBuffer.begin() + m_fftBufferFill;
for(size_t i = 0; i < samplesNeeded; ++i, ++begin)
*it++ = Complex(begin->real() / 32768.0, begin->imag() / 32768.0);
// apply fft window (and copy from m_fftBuffer to m_fftIn)
m_window.apply(&m_fftBuffer[0], m_fft->in());
// calculate FFT
m_fft->transform();
// extract power spectrum and reorder buckets
Real ofs = 20.0f * log10f(1.0f / m_fftSize);
Real mult = (10.0f / log2f(10.0f));
const Complex* fftOut = m_fft->out();
for(size_t i = 0; i < m_fftSize; i++) {
Complex c = fftOut[((i + (m_fftSize >> 1)) & (m_fftSize - 1))];
Real v = c.real() * c.real() + c.imag() * c.imag();
v = mult * log2f(v) + ofs;
m_logPowerSpectrum[i] = v;
}
// send new data to visualisation
m_glSpectrum->newSpectrum(m_logPowerSpectrum, m_fftSize);
// advance buffer respecting the fft overlap factor
std::copy(m_fftBuffer.begin() + m_refillSize, m_fftBuffer.end(), m_fftBuffer.begin());
// start over
m_fftBufferFill = m_overlapSize;
} else {
// not enough samples for FFT - just fill in new data and return
for(std::vector<Complex>::iterator it = m_fftBuffer.begin() + m_fftBufferFill; begin < end; ++begin)
*it++ = Complex(begin->real() / 32768.0, begin->imag() / 32768.0);
m_fftBufferFill += todo;
}
}
}
void SpectrumVis::start()
{
}
void SpectrumVis::stop()
{
}
bool SpectrumVis::handleMessage(Message* message)
{
if(DSPConfigureSpectrumVis::match(message)) {
DSPConfigureSpectrumVis* conf = (DSPConfigureSpectrumVis*)message;
handleConfigure(conf->getFFTSize(), conf->getOverlapPercent(), conf->getWindow());
message->completed();
return true;
} else {
return false;
}
}
void SpectrumVis::handleConfigure(int fftSize, int overlapPercent, FFTWindow::Function window)
{
if(fftSize > MAX_FFT_SIZE)
fftSize = MAX_FFT_SIZE;
else if(fftSize < 64)
fftSize = 64;
if(overlapPercent > 100)
m_overlapPercent = 100;
else if(overlapPercent < 0)
m_overlapPercent = 0;
m_fftSize = fftSize;
m_overlapPercent = overlapPercent;
m_fft->configure(m_fftSize, false);
m_window.create(window, m_fftSize);
m_overlapSize = (m_fftSize * m_overlapPercent) / 100;
m_refillSize = m_fftSize - m_overlapSize;
m_fftBufferFill = m_overlapSize;
}
+111
View File
@@ -0,0 +1,111 @@
#include <QThread>
#include "dsp/threadedsamplesink.h"
#include "util/message.h"
ThreadedSampleSink::ThreadedSampleSink(SampleSink* sampleSink) :
m_thread(new QThread),
m_sampleSink(sampleSink)
{
moveToThread(m_thread);
connect(m_thread, SIGNAL(started()), this, SLOT(threadStarted()));
connect(m_thread, SIGNAL(finished()), this, SLOT(threadFinished()));
m_messageQueue.moveToThread(m_thread);
connect(&m_messageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleMessages()));
m_sampleFifo.moveToThread(m_thread);
connect(&m_sampleFifo, SIGNAL(dataReady()), this, SLOT(handleData()));
m_sampleFifo.setSize(262144);
sampleSink->moveToThread(m_thread);
}
ThreadedSampleSink::~ThreadedSampleSink()
{
m_thread->exit();
m_thread->wait();
delete m_thread;
}
void ThreadedSampleSink::feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool firstOfBurst)
{
Q_UNUSED(firstOfBurst);
m_sampleFifo.write(begin, end);
}
void ThreadedSampleSink::start()
{
m_thread->start();
}
void ThreadedSampleSink::stop()
{
m_thread->exit();
m_thread->wait();
m_sampleFifo.readCommit(m_sampleFifo.fill());
}
bool ThreadedSampleSink::handleMessage(Message* cmd)
{
// called from other thread
m_messageQueue.submit(cmd);
return true;
}
void ThreadedSampleSink::handleData()
{
bool firstOfBurst = true;
while((m_sampleFifo.fill() > 0) && (m_messageQueue.countPending() == 0)) {
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);
// first part of FIFO data
if(part1begin != part1end) {
// handle data
if(m_sampleSink != NULL)
m_sampleSink->feed(part1begin, part1end, firstOfBurst);
firstOfBurst = false;
}
// second part of FIFO data (used when block wraps around)
if(part2begin != part2end) {
// handle data
if(m_sampleSink != NULL)
m_sampleSink->feed(part1begin, part1end, firstOfBurst);
firstOfBurst = false;
}
// adjust FIFO pointers
m_sampleFifo.readCommit(count);
}
}
void ThreadedSampleSink::handleMessages()
{
Message* message;
while((message = m_messageQueue.accept()) != NULL) {
qDebug("CMD: %s", message->getIdentifier());
if(m_sampleSink != NULL) {
if(!m_sampleSink->handleMessage(message))
message->completed();
} else {
message->completed();
}
}
}
void ThreadedSampleSink::threadStarted()
{
if(m_sampleSink != NULL)
m_sampleSink->start();
}
void ThreadedSampleSink::threadFinished()
{
if(m_sampleSink != NULL)
m_sampleSink->stop();
}