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:
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user