1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2026-06-09 09:25:07 -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
+109
View File
@@ -0,0 +1,109 @@
#ifndef INCLUDE_CHANNELIZER_H
#define INCLUDE_CHANNELIZER_H
#include <list>
#include "dsp/samplesink.h"
#include "util/export.h"
class MessageQueue;
class IntHalfbandFilter;
class SDRANGELOVE_API Channelizer : public SampleSink {
public:
Channelizer(SampleSink* sampleSink);
~Channelizer();
void configure(MessageQueue* messageQueue, int sampleRate, int centerFrequency);
void feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool firstOfBurst);
void start();
void stop();
bool handleMessage(Message* cmd);
protected:
struct FilterStage {
enum Mode {
ModeCenter,
ModeLowerHalf,
ModeUpperHalf
};
typedef bool (IntHalfbandFilter::*WorkFunction)(Sample* s);
IntHalfbandFilter* m_filter;
WorkFunction m_workFunction;
FilterStage(Mode mode);
~FilterStage();
bool work(Sample* sample)
{
return (m_filter->*m_workFunction)(sample);
}
};
typedef std::list<FilterStage*> FilterStages;
FilterStages m_filterStages;
SampleSink* m_sampleSink;
int m_inputSampleRate;
int m_requestedOutputSampleRate;
int m_requestedCenterFrequency;
int m_currentOutputSampleRate;
int m_currentCenterFrequency;
SampleVector m_sampleBuffer;
void applyConfiguration();
bool signalContainsChannel(Real sigStart, Real sigEnd, Real chanStart, Real chanEnd) const;
Real createFilterChain(Real sigStart, Real sigEnd, Real chanStart, Real chanEnd);
void freeFilterChain();
};
#endif // INCLUDE_CHANNELIZER_H
#if 0
#ifndef INCLUDE_CHANNELIZER_H
#define INCLUDE_CHANNELIZER_H
#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();
#if 0
void setGLSpectrum(GLSpectrum* glSpectrum);
#endif
size_t workUnitSize();
size_t work(SampleVector::const_iterator begin, SampleVector::const_iterator end);
private:
#if 0
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
};
#endif // INCLUDE_CHANNELIZER_H
#endif
+255
View File
@@ -0,0 +1,255 @@
#ifndef INCLUDE_DSPCOMMANDS_H
#define INCLUDE_DSPCOMMANDS_H
#include <QString>
#include "util/message.h"
#include "fftwindow.h"
#include "util/export.h"
class SampleSource;
class SampleSink;
class AudioFifo;
class SDRANGELOVE_API DSPPing : public Message {
MESSAGE_CLASS_DECLARATION
};
class SDRANGELOVE_API DSPExit : public Message {
MESSAGE_CLASS_DECLARATION
};
class SDRANGELOVE_API DSPAcquisitionStart : public Message {
MESSAGE_CLASS_DECLARATION
};
class SDRANGELOVE_API DSPAcquisitionStop : public Message {
MESSAGE_CLASS_DECLARATION
};
class SDRANGELOVE_API DSPGetDeviceDescription : public Message {
MESSAGE_CLASS_DECLARATION
public:
void setDeviceDescription(const QString& text) { m_deviceDescription = text; }
const QString& getDeviceDescription() const { return m_deviceDescription; }
private:
QString m_deviceDescription;
};
class SDRANGELOVE_API DSPGetErrorMessage : public Message {
MESSAGE_CLASS_DECLARATION
public:
void setErrorMessage(const QString& text) { m_errorMessage = text; }
const QString& getErrorMessage() const { return m_errorMessage; }
private:
QString m_errorMessage;
};
class SDRANGELOVE_API DSPSetSource : public Message {
MESSAGE_CLASS_DECLARATION
public:
DSPSetSource(SampleSource* sampleSource) : Message(), m_sampleSource(sampleSource) { }
SampleSource* getSampleSource() const { return m_sampleSource; }
private:
SampleSource* m_sampleSource;
};
class SDRANGELOVE_API DSPAddSink : public Message {
MESSAGE_CLASS_DECLARATION
public:
DSPAddSink(SampleSink* sampleSink) : Message(), m_sampleSink(sampleSink) { }
SampleSink* getSampleSink() const { return m_sampleSink; }
private:
SampleSink* m_sampleSink;
};
class SDRANGELOVE_API DSPRemoveSink : public Message {
MESSAGE_CLASS_DECLARATION
public:
DSPRemoveSink(SampleSink* sampleSink) : Message(), m_sampleSink(sampleSink) { }
SampleSink* getSampleSink() const { return m_sampleSink; }
private:
SampleSink* m_sampleSink;
};
class SDRANGELOVE_API DSPAddAudioSource : public Message {
MESSAGE_CLASS_DECLARATION
public:
DSPAddAudioSource(AudioFifo* audioFifo) : Message(), m_audioFifo(audioFifo) { }
AudioFifo* getAudioFifo() const { return m_audioFifo; }
private:
AudioFifo* m_audioFifo;
};
class SDRANGELOVE_API DSPRemoveAudioSource : public Message {
MESSAGE_CLASS_DECLARATION
public:
DSPRemoveAudioSource(AudioFifo* audioFifo) : Message(), m_audioFifo(audioFifo) { }
AudioFifo* getAudioFifo() const { return m_audioFifo; }
private:
AudioFifo* m_audioFifo;
};
class SDRANGELOVE_API DSPConfigureSpectrumVis : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getFFTSize() const { return m_fftSize; }
int getOverlapPercent() const { return m_overlapPercent; }
FFTWindow::Function getWindow() const { return m_window; }
static DSPConfigureSpectrumVis* create(int fftSize, int overlapPercent, FFTWindow::Function window)
{
return new DSPConfigureSpectrumVis(fftSize, overlapPercent, window);
}
private:
int m_fftSize;
int m_overlapPercent;
FFTWindow::Function m_window;
DSPConfigureSpectrumVis(int fftSize, int overlapPercent, FFTWindow::Function window) :
Message(),
m_fftSize(fftSize),
m_overlapPercent(overlapPercent),
m_window(window)
{ }
};
class SDRANGELOVE_API DSPConfigureCorrection : public Message {
MESSAGE_CLASS_DECLARATION
public:
bool getDCOffsetCorrection() const { return m_dcOffsetCorrection; }
bool getIQImbalanceCorrection() const { return m_iqImbalanceCorrection; }
static DSPConfigureCorrection* create(bool dcOffsetCorrection, bool iqImbalanceCorrection)
{
return new DSPConfigureCorrection(dcOffsetCorrection, iqImbalanceCorrection);
}
private:
bool m_dcOffsetCorrection;
bool m_iqImbalanceCorrection;
DSPConfigureCorrection(bool dcOffsetCorrection, bool iqImbalanceCorrection) :
Message(),
m_dcOffsetCorrection(dcOffsetCorrection),
m_iqImbalanceCorrection(iqImbalanceCorrection)
{ }
};
class SDRANGELOVE_API DSPEngineReport : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getSampleRate() const { return m_sampleRate; }
quint64 getCenterFrequency() const { return m_centerFrequency; }
static DSPEngineReport* create(int sampleRate, quint64 centerFrequency)
{
return new DSPEngineReport(sampleRate, centerFrequency);
}
private:
int m_sampleRate;
quint64 m_centerFrequency;
DSPEngineReport(int sampleRate, quint64 centerFrequency) :
Message(),
m_sampleRate(sampleRate),
m_centerFrequency(centerFrequency)
{ }
};
class SDRANGELOVE_API DSPConfigureScopeVis : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getTriggerChannel() const { return m_triggerChannel; }
Real getTriggerLevelHigh() const { return m_triggerLevelHigh; }
Real getTriggerLevelLow() const { return m_triggerLevelLow; }
static DSPConfigureScopeVis* create(int triggerChannel, Real triggerLevelHigh, Real triggerLevelLow)
{
return new DSPConfigureScopeVis(triggerChannel, triggerLevelHigh, triggerLevelLow);
}
private:
int m_triggerChannel;
Real m_triggerLevelHigh;
Real m_triggerLevelLow;
DSPConfigureScopeVis(int triggerChannel, Real triggerLevelHigh, Real triggerLevelLow) :
Message(),
m_triggerChannel(triggerChannel),
m_triggerLevelHigh(triggerLevelHigh),
m_triggerLevelLow(triggerLevelLow)
{ }
};
class SDRANGELOVE_API DSPSignalNotification : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getSampleRate() const { return m_sampleRate; }
qint64 getFrequencyOffset() const { return m_frequencyOffset; }
static DSPSignalNotification* create(int sampleRate, quint64 frequencyOffset)
{
return new DSPSignalNotification(sampleRate, frequencyOffset);
}
private:
int m_sampleRate;
qint64 m_frequencyOffset;
DSPSignalNotification(int samplerate, qint64 frequencyOffset) :
Message(),
m_sampleRate(samplerate),
m_frequencyOffset(frequencyOffset)
{ }
};
class SDRANGELOVE_API DSPConfigureChannelizer : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getSampleRate() const { return m_sampleRate; }
int getCenterFrequency() const { return m_centerFrequency; }
static DSPConfigureChannelizer* create(int sampleRate, int centerFrequency)
{
return new DSPConfigureChannelizer(sampleRate, centerFrequency);
}
private:
int m_sampleRate;
int m_centerFrequency;
DSPConfigureChannelizer(int sampleRate, int centerFrequency) :
Message(),
m_sampleRate(sampleRate),
m_centerFrequency(centerFrequency)
{ }
};
#endif // INCLUDE_DSPCOMMANDS_H
+119
View File
@@ -0,0 +1,119 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_DSPENGINE_H
#define INCLUDE_DSPENGINE_H
#include <QThread>
#include <QTimer>
#include <QMutex>
#include <QWaitCondition>
#include "dsp/dsptypes.h"
#include "dsp/fftwindow.h"
#include "dsp/samplefifo.h"
#include "audio/audiooutput.h"
#include "util/messagequeue.h"
#include "util/export.h"
class SampleSource;
class SampleSink;
class AudioFifo;
class SDRANGELOVE_API DSPEngine : public QThread {
Q_OBJECT
public:
enum State {
StNotStarted,
StIdle,
StRunning,
StError
};
DSPEngine(MessageQueue* reportQueue, QObject* parent = NULL);
~DSPEngine();
MessageQueue* getMessageQueue() { return &m_messageQueue; }
void start();
void stop();
bool startAcquisition();
void stopAcquistion();
void setSource(SampleSource* source);
void addSink(SampleSink* sink);
void removeSink(SampleSink* sink);
void addAudioSource(AudioFifo* audioFifo);
void removeAudioSource(AudioFifo* audioFifo);
void configureCorrections(bool dcOffsetCorrection, bool iqImbalanceCorrection);
State state() const { return m_state; }
QString errorMessage();
QString deviceDescription();
private:
MessageQueue m_messageQueue;
MessageQueue* m_reportQueue;
State m_state;
QString m_errorMessage;
QString m_deviceDescription;
SampleSource* m_sampleSource;
typedef std::list<SampleSink*> SampleSinks;
SampleSinks m_sampleSinks;
AudioOutput m_audioOutput;
uint m_sampleRate;
quint64 m_centerFrequency;
bool m_dcOffsetCorrection;
bool m_iqImbalanceCorrection;
qint32 m_iOffset;
qint32 m_qOffset;
qint32 m_iRange;
qint32 m_qRange;
qint32 m_imbalance;
void run();
void dcOffset(SampleVector::iterator begin, SampleVector::iterator end);
void imbalance(SampleVector::iterator begin, SampleVector::iterator end);
void work();
State gotoIdle();
State gotoRunning();
State gotoError(const QString& errorMsg);
void handleSetSource(SampleSource* source);
void generateReport();
bool distributeMessage(Message* message);
private slots:
void handleData();
void handleMessages();
};
#endif // INCLUDE_DSPENGINE_H
+20
View File
@@ -0,0 +1,20 @@
#ifndef INCLUDE_FFTENGINE_H
#define INCLUDE_FFTENGINE_H
#include "dsp/dsptypes.h"
#include "util/export.h"
class SDRANGELOVE_API FFTEngine {
public:
virtual ~FFTEngine();
virtual void configure(int n, bool inverse) = 0;
virtual void transform() = 0;
virtual Complex* in() = 0;
virtual Complex* out() = 0;
static FFTEngine* create();
};
#endif // INCLUDE_FFTENGINE_H
+37
View File
@@ -0,0 +1,37 @@
#ifndef INCLUDE_FFTWENGINE_H
#define INCLUDE_FFTWENGINE_H
#include <QMutex>
#include <fftw3.h>
#include <list>
#include "dsp/fftengine.h"
class FFTWEngine : public FFTEngine {
public:
FFTWEngine();
~FFTWEngine();
void configure(int n, bool inverse);
void transform();
Complex* in();
Complex* out();
protected:
static QMutex m_globalPlanMutex;
struct Plan {
int n;
bool inverse;
fftwf_plan plan;
fftwf_complex* in;
fftwf_complex* out;
};
typedef std::list<Plan*> Plans;
Plans m_plans;
Plan* m_currentPlan;
void freeAll();
};
#endif // INCLUDE_FFTWENGINE_H
+82
View File
@@ -0,0 +1,82 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_FFTWINDOW_H
#define INCLUDE_FFTWINDOW_H
#include <vector>
#define _USE_MATH_DEFINES
#include <math.h>
#include "dsp/dsptypes.h"
#include "util/export.h"
class SDRANGELOVE_API FFTWindow {
public:
enum Function {
Bartlett,
BlackmanHarris,
Flattop,
Hamming,
Hanning,
Rectangle
};
void create(Function function, int n);
void apply(const std::vector<Real>& in, std::vector<Real>* out);
void apply(const std::vector<Complex>& in, std::vector<Complex>* out);
void apply(const Complex* in, Complex* out);
private:
std::vector<float> m_window;
static inline Real flatTop(Real n, Real i)
{
// correction ?
return 1.0 - 1.93 * cos((2.0 * M_PI * i) / n) + 1.29 * cos((4.0 * M_PI * i) / n) - 0.388 * cos((6.0 * M_PI * i) / n) + 0.03222 * cos((8.0 * M_PI * i) / n);
}
static inline Real bartlett(Real n, Real i)
{
// amplitude correction = 2.0
return (2.0 / (n - 1.0)) * ( (n - 1.0) / 2.0 - fabs(i - (n - 1.0) / 2.0)) * 2.0;
}
static inline Real blackmanHarris(Real n, Real i)
{
// amplitude correction = 2.79
return (0.35875 - 0.48829 * cos((2.0 * M_PI * i) / n) + 0.14128 * cos((4.0 * M_PI * i) / n) - 0.01168 * cos((6.0 * M_PI * i) / n)) * 2.79;
}
static inline Real hamming(Real n, Real i)
{
// amplitude correction = 1.855, energy correction = 1.586
return (0.54 - 0.46 * cos((2.0 * M_PI * i) / n)) * 1.855;
}
static inline Real hanning(Real n, Real i)
{
// amplitude correction = 2.0, energy correction = 1.633
return (0.5 - 0.5 * cos((2.0 * M_PI * i) / n)) * 2.0;
}
static inline Real rectangle(Real, Real)
{
return 1.0;
}
};
#endif // INCLUDE_FFTWINDOWS_H
+130
View File
@@ -0,0 +1,130 @@
#ifndef INCLUDE_INTERPOLATOR_H
#define INCLUDE_INTERPOLATOR_H
#include <immintrin.h>
#include "dsp/dsptypes.h"
#include "util/export.h"
#include <stdio.h>
#ifndef WIN32
#include <unistd.h>
#endif
class SDRANGELOVE_API Interpolator {
public:
Interpolator();
~Interpolator();
void create(int phaseSteps, double sampleRate, double cutoff);
void free();
bool interpolate(Real* distance, const Complex& next, bool* consumed, Complex* result)
{
while(*distance >= 1.0) {
if(!(*consumed)) {
advanceFilter(next);
*distance -= 1.0;
*consumed = true;
} else {
return false;
}
}
doInterpolate((int)floor(*distance * (Real)m_phaseSteps), result);
return true;
}
private:
float* m_taps;
float* m_alignedTaps;
float* m_taps2;
float* m_alignedTaps2;
std::vector<Complex> m_samples;
int m_ptr;
int m_phaseSteps;
int m_nTaps;
void createTaps(int nTaps, double sampleRate, double cutoff, std::vector<Real>* taps);
void advanceFilter(const Complex& next)
{
m_ptr--;
if(m_ptr < 0)
m_ptr = m_nTaps - 1;
m_samples[m_ptr] = next;
}
void doInterpolate(int phase, Complex* result)
{
#if 1
// beware of the ringbuffer
if(m_ptr == 0) {
// only one straight block
const float* src = (const float*)&m_samples[0];
const __m128* filter = (const __m128*)&m_alignedTaps[phase * m_nTaps * 2];
__m128 sum = _mm_setzero_ps();
int todo = m_nTaps / 2;
for(int i = 0; i < todo; i++) {
sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadu_ps(src), *filter));
src += 4;
filter += 1;
}
// add upper half to lower half and store
_mm_storel_pi((__m64*)result, _mm_add_ps(sum, _mm_shuffle_ps(sum, _mm_setzero_ps(), _MM_SHUFFLE(1, 0, 3, 2))));
} else {
// two blocks
const float* src = (const float*)&m_samples[m_ptr];
const __m128* filter = (const __m128*)&m_alignedTaps[phase * m_nTaps * 2];
__m128 sum = _mm_setzero_ps();
// first block
int block = m_nTaps - m_ptr;
int todo = block / 2;
if(block & 1)
todo++;
for(int i = 0; i < todo; i++) {
sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadu_ps(src), *filter));
src += 4;
filter += 1;
}
if(block & 1) {
// one sample beyond the end -> switch coefficient table
filter = (const __m128*)&m_alignedTaps2[phase * m_nTaps * 2 + todo * 4 - 4];
}
// second block
src = (const float*)&m_samples[0];
block = m_ptr;
todo = block / 2;
for(int i = 0; i < todo; i++) {
sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadu_ps(src), *filter));
src += 4;
filter += 1;
}
if(block & 1) {
// one sample remaining
sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadl_pi(_mm_setzero_ps(), (const __m64*)src), filter[0]));
}
// add upper half to lower half and store
_mm_storel_pi((__m64*)result, _mm_add_ps(sum, _mm_shuffle_ps(sum, _mm_setzero_ps(), _MM_SHUFFLE(1, 0, 3, 2))));
}
#else
int sample = m_ptr;
const Real* coeff = &m_alignedTaps[phase * m_nTaps * 2];
Real rAcc = 0;
Real iAcc = 0;
for(int i = 0; i < m_nTaps; i++) {
rAcc += *coeff * m_samples[sample].real();
iAcc += *coeff * m_samples[sample].imag();
sample = (sample + 1) % m_nTaps;
coeff += 2;
}
*result = Complex(rAcc, iAcc);
#endif
}
};
#endif // INCLUDE_INTERPOLATOR_H
+314
View File
@@ -0,0 +1,314 @@
#ifndef INCLUDE_INTHALFBANDFILTER_H
#define INCLUDE_INTHALFBANDFILTER_H
#include <QtGlobal>
#include "dsp/dsptypes.h"
#include "util/export.h"
// uses Q1.14 format internally, input and output are S16
/*
* supported filter orders: 64, 48, 32
*/
#define HB_FILTERORDER 32
#define HB_SHIFT 14
class SDRANGELOVE_API IntHalfbandFilter {
public:
IntHalfbandFilter();
// downsample by 2, return center part of original spectrum
bool workDecimateCenter(Sample* sample)
{
// insert sample into ring-buffer
m_samples[m_ptr][0] = sample->real();
m_samples[m_ptr][1] = sample->imag();
switch(m_state) {
case 0:
// advance write-pointer
m_ptr = (m_ptr + HB_FILTERORDER) % (HB_FILTERORDER + 1);
// next state
m_state = 1;
// tell caller we don't have a new sample
return false;
default:
// save result
doFIR(sample);
// advance write-pointer
m_ptr = (m_ptr + HB_FILTERORDER) % (HB_FILTERORDER + 1);
// next state
m_state = 0;
// tell caller we have a new sample
return true;
}
}
// downsample by 2, return edges of spectrum rotated into center
bool workDecimateFullRotate(Sample* sample)
{
switch(m_state) {
case 0:
// insert sample into ring-buffer
m_samples[m_ptr][0] = sample->real();
m_samples[m_ptr][1] = sample->imag();
// advance write-pointer
m_ptr = (m_ptr + HB_FILTERORDER) % (HB_FILTERORDER + 1);
// next state
m_state = 1;
// tell caller we don't have a new sample
return false;
default:
// insert sample into ring-buffer
m_samples[m_ptr][0] = -sample->real();
m_samples[m_ptr][1] = sample->imag();
// save result
doFIR(sample);
// advance write-pointer
m_ptr = (m_ptr + HB_FILTERORDER) % (HB_FILTERORDER + 1);
// next state
m_state = 0;
// tell caller we have a new sample
return true;
}
}
// downsample by 2, return lower half of original spectrum
bool workDecimateLowerHalf(Sample* sample)
{
switch(m_state) {
case 0:
// insert sample into ring-buffer
m_samples[m_ptr][0] = -sample->imag();
m_samples[m_ptr][1] = sample->real();
// advance write-pointer
m_ptr = (m_ptr + HB_FILTERORDER) % (HB_FILTERORDER + 1);
// next state
m_state = 1;
// tell caller we don't have a new sample
return false;
case 1:
// insert sample into ring-buffer
m_samples[m_ptr][0] = -sample->real();
m_samples[m_ptr][1] = -sample->imag();
// save result
doFIR(sample);
// advance write-pointer
m_ptr = (m_ptr + HB_FILTERORDER) % (HB_FILTERORDER + 1);
// next state
m_state = 2;
// tell caller we have a new sample
return true;
case 2:
// insert sample into ring-buffer
m_samples[m_ptr][0] = sample->imag();
m_samples[m_ptr][1] = -sample->real();
// advance write-pointer
m_ptr = (m_ptr + HB_FILTERORDER) % (HB_FILTERORDER + 1);
// next state
m_state = 3;
// tell caller we don't have a new sample
return false;
default:
// insert sample into ring-buffer
m_samples[m_ptr][0] = sample->real();
m_samples[m_ptr][1] = sample->imag();
// save result
doFIR(sample);
// advance write-pointer
m_ptr = (m_ptr + HB_FILTERORDER) % (HB_FILTERORDER + 1);
// next state
m_state = 0;
// tell caller we have a new sample
return true;
}
}
// downsample by 2, return upper half of original spectrum
bool workDecimateUpperHalf(Sample* sample)
{
switch(m_state) {
case 0:
// insert sample into ring-buffer
m_samples[m_ptr][0] = sample->imag();
m_samples[m_ptr][1] = -sample->real();
// advance write-pointer
m_ptr = (m_ptr + HB_FILTERORDER) % (HB_FILTERORDER + 1);
// next state
m_state = 1;
// tell caller we don't have a new sample
return false;
case 1:
// insert sample into ring-buffer
m_samples[m_ptr][0] = -sample->real();
m_samples[m_ptr][1] = -sample->imag();
// save result
doFIR(sample);
// advance write-pointer
m_ptr = (m_ptr + HB_FILTERORDER) % (HB_FILTERORDER + 1);
// next state
m_state = 2;
// tell caller we have a new sample
return true;
case 2:
// insert sample into ring-buffer
m_samples[m_ptr][0] = -sample->imag();
m_samples[m_ptr][1] = sample->real();
// advance write-pointer
m_ptr = (m_ptr + HB_FILTERORDER) % (HB_FILTERORDER + 1);
// next state
m_state = 3;
// tell caller we don't have a new sample
return false;
default:
// insert sample into ring-buffer
m_samples[m_ptr][0] = sample->real();
m_samples[m_ptr][1] = sample->imag();
// save result
doFIR(sample);
// advance write-pointer
m_ptr = (m_ptr + HB_FILTERORDER) % (HB_FILTERORDER + 1);
// next state
m_state = 0;
// tell caller we have a new sample
return true;
}
}
protected:
qint16 m_samples[HB_FILTERORDER + 1][2];
int m_ptr;
int m_state;
void doFIR(Sample* sample)
{
// coefficents
#if HB_FILTERORDER == 64
static const qint32 COEFF[16] = {
-0.001114417441601693505720538368564120901 * (1 << HB_SHIFT),
0.001268007827185253051302527005361753254 * (1 << HB_SHIFT),
-0.001959831378850490895410230152151598304 * (1 << HB_SHIFT),
0.002878308307661380308073439948657323839 * (1 << HB_SHIFT),
-0.004071361818258721100571850826099762344 * (1 << HB_SHIFT),
0.005597288494657440618973431867289036745 * (1 << HB_SHIFT),
-0.007532345003308904551886371336877346039 * (1 << HB_SHIFT),
0.009980346844667375288961963519795972388 * (1 << HB_SHIFT),
-0.013092614174300500062830820979797863401 * (1 << HB_SHIFT),
0.01710934914871829748417297878404497169 * (1 << HB_SHIFT),
-0.022443558692997273018576720460259821266 * (1 << HB_SHIFT),
0.029875811511593811098386197500076377764 * (1 << HB_SHIFT),
-0.041086352085710403647667021687084343284 * (1 << HB_SHIFT),
0.060465467462665789533104998554335907102 * (1 << HB_SHIFT),
-0.104159517495977321788203084906854201108 * (1 << HB_SHIFT),
0.317657589850154464805598308885237202048 * (1 << HB_SHIFT),
};
#elif HB_FILTERORDER == 48
static const qint32 COEFF[12] = {
-0.004102576237611492253332112767338912818 * (1 << HB_SHIFT),
0.003950551047979387886410762575906119309 * (1 << HB_SHIFT),
-0.005807875789391703583164350277456833282 * (1 << HB_SHIFT),
0.00823497890520805998770814682075069868 * (1 << HB_SHIFT),
-0.011372226513199541059195851744334504474 * (1 << HB_SHIFT),
0.015471557140973646315984524335362948477 * (1 << HB_SHIFT),
-0.020944996398689276484450516591095947661 * (1 << HB_SHIFT),
0.028568078132034283034279553703527199104 * (1 << HB_SHIFT),
-0.040015143905614086738964374490024056286 * (1 << HB_SHIFT),
0.059669519431831075095828964549582451582 * (1 << HB_SHIFT),
-0.103669138691865420076609893840213771909 * (1 << HB_SHIFT),
0.317491986549921390015072120149852707982 * (1 << HB_SHIFT)
};
#elif HB_FILTERORDER == 32
static const qint32 COEFF[8] = {
-0.015956912844043127236437484839370881673 * (1 << HB_SHIFT),
0.013023031678944928940522274274371739011 * (1 << HB_SHIFT),
-0.01866942273717486777684371190844103694 * (1 << HB_SHIFT),
0.026550887571157304190005987720724078827 * (1 << HB_SHIFT),
-0.038350314277854319344740474662103224546 * (1 << HB_SHIFT),
0.058429248652825838128421764849917963147 * (1 << HB_SHIFT),
-0.102889802028955756885153505209018476307 * (1 << HB_SHIFT),
0.317237706405931241260276465254719369113 * (1 << HB_SHIFT)
};
#else
#error unsupported filter order
#endif
// init read-pointer
int a = (m_ptr + 1) % (HB_FILTERORDER + 1);
int b = (m_ptr + (HB_FILTERORDER - 1)) % (HB_FILTERORDER + 1);
// go through samples in buffer
qint32 iAcc = 0;
qint32 qAcc = 0;
for(int i = 0; i < HB_FILTERORDER / 4; i++) {
// do multiply-accumulate
qint32 iTmp = m_samples[a][0] + m_samples[b][0];
qint32 qTmp = m_samples[a][1] + m_samples[b][1];
iAcc += iTmp * COEFF[i];
qAcc += qTmp * COEFF[i];
// update read-pointer
a = (a + 2) % (HB_FILTERORDER + 1);
b = (b + (HB_FILTERORDER - 1)) % (HB_FILTERORDER + 1);
}
a = (a + HB_FILTERORDER) % (HB_FILTERORDER + 1);
iAcc += m_samples[a][0] * (qint32)(0.5 * (1 << HB_SHIFT));
qAcc += m_samples[a][1] * (qint32)(0.5 * (1 << HB_SHIFT));
// done, save result
sample->setReal((iAcc + (qint32)(0.5 * (1 << HB_SHIFT))) >> HB_SHIFT);
sample->setImag((qAcc + (qint32)(0.5 * (1 << HB_SHIFT))) >> HB_SHIFT);
}
};
#endif // INCLUDE_INTHALFBANDFILTER_H
+23
View File
@@ -0,0 +1,23 @@
#ifndef INCLUDE_KISSENGINE_H
#define INCLUDE_KISSENGINE_H
#include "dsp/fftengine.h"
#include "dsp/kissfft.h"
class KissEngine : public FFTEngine {
public:
void configure(int n, bool inverse);
void transform();
Complex* in();
Complex* out();
protected:
typedef kissfft<Real, Complex> KissFFT;
KissFFT m_fft;
std::vector<Complex> m_in;
std::vector<Complex> m_out;
};
#endif // INCLUDE_KISSENGINE_H
+88
View File
@@ -0,0 +1,88 @@
#ifndef INCLUDE_LOWPASS_H
#define INCLUDE_LOWPASS_H
#define _USE_MATH_DEFINES
#include <math.h>
#include "dsp/dsptypes.h"
template <class Type> class Lowpass {
public:
Lowpass() { }
void create(int nTaps, double sampleRate, double cutoff)
{
double wc = 2.0 * M_PI * cutoff;
double Wc = wc / sampleRate;
int i;
// check constraints
if(!(nTaps & 1)) {
qDebug("Lowpass filter has to have an odd number of taps");
nTaps++;
}
// make room
m_samples.resize(nTaps);
for(int i = 0; i < nTaps; i++)
m_samples[i] = 0;
m_ptr = 0;
m_taps.resize(nTaps / 2 + 1);
// generate Sinc filter core
for(i = 0; i < nTaps / 2 + 1; i++) {
if(i == (nTaps - 1) / 2)
m_taps[i] = Wc / M_PI;
else
m_taps[i] = sin(((double)i - ((double)nTaps - 1.0) / 2.0) * Wc) / (((double)i - ((double)nTaps - 1.0) / 2.0) * M_PI);
}
// apply Hamming window
for(i = 0; i < nTaps / 2 + 1; i++)
m_taps[i] *= 0.54 + 0.46 * cos((2.0 * M_PI * ((double)i - ((double)nTaps - 1.0) / 2.0)) / (double)nTaps);
// normalize
Real sum = 0;
for(i = 0; i < (int)m_taps.size() - 1; i++)
sum += m_taps[i] * 2;
sum += m_taps[i];
for(i = 0; i < (int)m_taps.size(); i++)
m_taps[i] /= sum;
}
Type filter(Type sample)
{
Type acc = 0;
int a = m_ptr;
int b = a - 1;
int i;
m_samples[m_ptr] = sample;
while(b < 0)
b += m_samples.size();
for(i = 0; i < (int)m_taps.size() - 1; i++) {
acc += (m_samples[a] + m_samples[b]) * m_taps[i];
a++;
while(a >= (int)m_samples.size())
a -= m_samples.size();
b--;
while(b < 0)
b += m_samples.size();
}
acc += m_samples[a] * m_taps[i];
m_ptr++;
while(m_ptr >= (int)m_samples.size())
m_ptr -= m_samples.size();
return acc;
}
private:
std::vector<Real> m_taps;
std::vector<Type> m_samples;
int m_ptr;
};
#endif // INCLUDE_LOWPASS_H
+53
View File
@@ -0,0 +1,53 @@
#ifndef INCLUDE_MOVINGAVERAGE_H
#define INCLUDE_MOVINGAVERAGE_H
#include <vector>
#include "dsp/dsptypes.h"
class MovingAverage {
public:
MovingAverage() :
m_history(),
m_sum(0),
m_ptr(0)
{
}
MovingAverage(int historySize, Real initial) :
m_history(historySize, initial),
m_sum(historySize * initial),
m_ptr(0)
{
}
void resize(int historySize, Real initial)
{
m_history.resize(historySize);
for(size_t i = 0; i < m_history.size(); i++)
m_history[i] = initial;
m_sum = m_history.size() * initial;
m_ptr = 0;
}
void feed(Real value)
{
m_sum -= m_history[m_ptr];
m_history[m_ptr] = value;
m_sum += value;
m_ptr++;
if(m_ptr >= m_history.size())
m_ptr = 0;
}
Real average() const
{
return m_sum / (Real)m_history.size();
}
protected:
std::vector<Real> m_history;
Real m_sum;
uint m_ptr;
};
#endif // INCLUDE_MOVINGAVERAGE_H
+45
View File
@@ -0,0 +1,45 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_NCO_H
#define INCLUDE_NCO_H
#include "dsp/dsptypes.h"
#include "util/export.h"
class SDRANGELOVE_API NCO {
private:
enum {
TableSize = (1 << 12),
};
static Real m_table[TableSize];
static bool m_tableInitialized;
static void initTable();
int m_phaseIncrement;
int m_phase;
public:
NCO();
void setFreq(Real freq, Real sampleRate);
Real next();
Complex nextIQ();
};
#endif // INCLUDE_NCO_H
+44
View File
@@ -0,0 +1,44 @@
#ifndef INCLUDE_SCOPEVIS_H
#define INCLUDE_SCOPEVIS_H
#include "dsp/samplesink.h"
#include "util/export.h"
class GLScope;
class MessageQueue;
class SDRANGELOVE_API ScopeVis : public SampleSink {
public:
enum TriggerChannel {
TriggerFreeRun,
TriggerChannelI,
TriggerChannelQ
};
ScopeVis(GLScope* glScope = NULL);
void configure(MessageQueue* msgQueue, TriggerChannel triggerChannel, Real triggerLevelHigh, Real triggerLevelLow);
void feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool firstOfBurst);
void start();
void stop();
bool handleMessage(Message* message);
private:
enum TriggerState {
Untriggered,
Triggered,
WaitForReset
};
GLScope* m_glScope;
std::vector<Complex> m_trace;
uint m_fill;
TriggerState m_triggerState;
TriggerChannel m_triggerChannel;
FixReal m_triggerLevelHigh;
FixReal m_triggerLevelLow;
int m_sampleRate;
};
#endif // INCLUDE_SCOPEVIS_H
+42
View File
@@ -0,0 +1,42 @@
#ifndef INCLUDE_SPECTRUMVIS_H
#define INCLUDE_SPECTRUMVIS_H
#include "dsp/samplesink.h"
#include "dsp/fftengine.h"
#include "fftwindow.h"
#include "util/export.h"
class GLSpectrum;
class MessageQueue;
class SDRANGELOVE_API SpectrumVis : public SampleSink {
public:
SpectrumVis(GLSpectrum* glSpectrum = NULL);
~SpectrumVis();
void configure(MessageQueue* msgQueue, int fftSize, int overlapPercent, FFTWindow::Function window);
void feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool firstOfBurst);
void start();
void stop();
bool handleMessage(Message* message);
private:
FFTEngine* m_fft;
FFTWindow m_window;
std::vector<Complex> m_fftBuffer;
std::vector<Real> m_logPowerSpectrum;
size_t m_fftSize;
size_t m_overlapPercent;
size_t m_overlapSize;
size_t m_refillSize;
size_t m_fftBufferFill;
GLSpectrum* m_glSpectrum;
void handleConfigure(int fftSize, int overlapPercent, FFTWindow::Function window);
};
#endif // INCLUDE_SPECTRUMVIS_H