diff options
author | Christian Daniel <cd@maintech.de> | 2013-03-22 11:18:30 +0100 |
---|---|---|
committer | Christian Daniel <cd@maintech.de> | 2013-03-22 11:18:30 +0100 |
commit | eca56e35be841396f6c57bab540a3e1503253d56 (patch) | |
tree | 4a0a29cd53a84339e56754c32bf67391c83494fe /include-gpl | |
parent | 2c8c930b39fe069b36d81caa00401d9ac182a9d8 (diff) |
monster rework
- pluginify whole project
- reorganize directory structure
- fix PortAudio detection script
- implement generic channelizer
- fix several OpenGL problems
- rework presets
- add audio mixing
- too many more
Diffstat (limited to 'include-gpl')
35 files changed, 2619 insertions, 0 deletions
diff --git a/include-gpl/audio/audiodeviceinfo.h b/include-gpl/audio/audiodeviceinfo.h new file mode 100644 index 0000000..7b88870 --- /dev/null +++ b/include-gpl/audio/audiodeviceinfo.h @@ -0,0 +1,48 @@ +///////////////////////////////////////////////////////////////////////////////////
+// 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_AUDIODEVICEINFO_H
+#define INCLUDE_AUDIODEVICEINFO_H
+
+#include <QStringList>
+
+class AudioDeviceInfo {
+public:
+ struct Device {
+ QString name;
+ QString api;
+ int id;
+
+ Device(const QString& _name, const QString& _api, int _id) :
+ name(_name),
+ api(_api),
+ id(_id)
+ { }
+ };
+ typedef QList<Device> Devices;
+
+ AudioDeviceInfo();
+
+ int match(const QString& api, const QString device) const;
+
+ const Devices& getDevices() const { return m_devices; }
+
+private:
+ Devices m_devices;
+};
+
+#endif // INCLUDE_AUDIODEVICEINFO_H
diff --git a/include-gpl/audio/audiofifo.h b/include-gpl/audio/audiofifo.h new file mode 100644 index 0000000..8a06e99 --- /dev/null +++ b/include-gpl/audio/audiofifo.h @@ -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/>. // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_AUDIOFIFO_H +#define INCLUDE_AUDIOFIFO_H + +#include <QMutex> +#include <QWaitCondition> + +class AudioFifo { +public: + AudioFifo(); + AudioFifo(uint sampleSize, uint numSamples); + ~AudioFifo(); + + bool setSize(uint sampleSize, uint numSamples); + + uint write(const quint8* data, uint numSamples, int timeout = INT_MAX); + uint read(quint8* data, uint numSamples, int timeout = INT_MAX); + + uint drain(uint numSamples); + void clear(); + + inline uint flush() { return drain(m_fill); } + inline uint fill() const { return m_fill; } + inline bool isEmpty() const { return m_fill == 0; } + inline bool isFull() const { return m_fill == m_size; } + inline uint size() const { return m_size; } + +private: + QMutex m_mutex; + + qint8* m_fifo; + + uint m_sampleSize; + + uint m_size; + uint m_fill; + uint m_head; + uint m_tail; + + QMutex m_writeWaitLock; + QMutex m_readWaitLock; + QWaitCondition m_writeWaitCondition; + QWaitCondition m_readWaitCondition; + + bool create(uint sampleSize, uint numSamples); +}; + +#endif // INCLUDE_AUDIOFIFO_H diff --git a/include-gpl/audio/audiooutput.h b/include-gpl/audio/audiooutput.h new file mode 100644 index 0000000..347c246 --- /dev/null +++ b/include-gpl/audio/audiooutput.h @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany // +// written by Christian Daniel // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see <http://www.gnu.org/licenses/>. // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_AUDIOOUTPUT_H +#define INCLUDE_AUDIOOUTPUT_H + +#include <QMutex> +#include <list> +#include <vector> +#include "portaudio.h" + +class AudioFifo; + +class AudioOutput { +public: + AudioOutput(); + ~AudioOutput(); + + bool start(int device, int rate); + void stop(); + + void addFifo(AudioFifo* audioFifo); + void removeFifo(AudioFifo* audioFifo); + + //int bufferedSamples(); + +private: + QMutex m_mutex; + PaStream* m_stream; + //AudioFifo* m_audioFifo; + typedef std::list<AudioFifo*> AudioFifos; + AudioFifos m_audioFifos; + std::vector<qint32> m_mixBuffer; + + int m_sampleRate; + //PaTime m_streamStartTime; + + static int callbackHelper( + const void* inputBuffer, + void* outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void* userData); + + int callback( + const void* inputBuffer, + void* outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags); +}; + +#endif // INCLUDE_AUDIOOUTPUT_H diff --git a/include-gpl/audio/portaudioholder.h b/include-gpl/audio/portaudioholder.h new file mode 100644 index 0000000..9f6bd50 --- /dev/null +++ b/include-gpl/audio/portaudioholder.h @@ -0,0 +1,13 @@ +#ifndef INCLUDE_PORTAUDIOHOLDER_H +#define INCLUDE_PORTAUDIOHOLDER_H + +class PortAudioHolder { +public: + PortAudioHolder(); + ~PortAudioHolder(); + +private: + bool m_initialized; +}; + +#endif // INCLUDE_PORTAUDIOHOLDER_H diff --git a/include-gpl/dsp/channelizer.h b/include-gpl/dsp/channelizer.h new file mode 100644 index 0000000..223625e --- /dev/null +++ b/include-gpl/dsp/channelizer.h @@ -0,0 +1,108 @@ +#ifndef INCLUDE_CHANNELIZER_H +#define INCLUDE_CHANNELIZER_H + +#include <list> +#include "dsp/samplesink.h" + +class MessageQueue; +class IntHalfbandFilter; + +class 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 diff --git a/include-gpl/dsp/dspcommands.h b/include-gpl/dsp/dspcommands.h new file mode 100644 index 0000000..7377324 --- /dev/null +++ b/include-gpl/dsp/dspcommands.h @@ -0,0 +1,270 @@ +#ifndef INCLUDE_DSPCOMMANDS_H +#define INCLUDE_DSPCOMMANDS_H + +#include <QString> +#include "util/message.h" +#include "fftwindow.h" + +class SampleSource; +class SampleSink; +class AudioFifo; + +class DSPPing : public Message { +public: + static MessageRegistrator ID; + + DSPPing() : Message(ID()) { } +}; + +class DSPExit : public Message { +public: + static MessageRegistrator ID; + + DSPExit() : Message(ID()) { } +}; + +class DSPAcquisitionStart : public Message { +public: + static MessageRegistrator ID; + + DSPAcquisitionStart() : Message(ID()) { } +}; + +class DSPAcquisitionStop : public Message { +public: + static MessageRegistrator ID; + + DSPAcquisitionStop() : Message(ID()) { } +}; + +class DSPGetDeviceDescription : public Message { +public: + static MessageRegistrator ID; + + DSPGetDeviceDescription() : Message(ID()) { } + + void setDeviceDescription(const QString& text) { m_deviceDescription = text; } + const QString& getDeviceDescription() const { return m_deviceDescription; } + +private: + QString m_deviceDescription; +}; + +class DSPGetErrorMessage : public Message { +public: + static MessageRegistrator ID; + + DSPGetErrorMessage() : Message(ID()) { } + + void setErrorMessage(const QString& text) { m_errorMessage = text; } + const QString& getErrorMessage() const { return m_errorMessage; } + +private: + QString m_errorMessage; +}; + +class DSPSetSource : public Message { +public: + static MessageRegistrator ID; + + DSPSetSource(SampleSource* sampleSource) : Message(ID()), m_sampleSource(sampleSource) { } + + SampleSource* getSampleSource() const { return m_sampleSource; } + +private: + SampleSource* m_sampleSource; +}; + +class DSPAddSink : public Message { +public: + static MessageRegistrator ID; + + DSPAddSink(SampleSink* sampleSink) : Message(ID()), m_sampleSink(sampleSink) { } + + SampleSink* getSampleSink() const { return m_sampleSink; } + +private: + SampleSink* m_sampleSink; +}; + +class DSPRemoveSink : public Message { +public: + static MessageRegistrator ID; + + DSPRemoveSink(SampleSink* sampleSink) : Message(ID()), m_sampleSink(sampleSink) { } + + SampleSink* getSampleSink() const { return m_sampleSink; } + +private: + SampleSink* m_sampleSink; +}; + +class DSPAddAudioSource : public Message { +public: + static MessageRegistrator ID; + + DSPAddAudioSource(AudioFifo* audioFifo) : Message(ID()), m_audioFifo(audioFifo) { } + + AudioFifo* getAudioFifo() const { return m_audioFifo; } + +private: + AudioFifo* m_audioFifo; +}; + +class DSPRemoveAudioSource : public Message { +public: + static MessageRegistrator ID; + + DSPRemoveAudioSource(AudioFifo* audioFifo) : Message(ID()), m_audioFifo(audioFifo) { } + + AudioFifo* getAudioFifo() const { return m_audioFifo; } + +private: + AudioFifo* m_audioFifo; +}; + +class DSPConfigureSpectrumVis : public Message { +public: + static MessageRegistrator ID; + + 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(ID()), + m_fftSize(fftSize), + m_overlapPercent(overlapPercent), + m_window(window) + { } +}; + +class DSPConfigureCorrection : public Message { +public: + static MessageRegistrator ID; + + 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(ID()), + m_dcOffsetCorrection(dcOffsetCorrection), + m_iqImbalanceCorrection(iqImbalanceCorrection) + { } +}; + +class DSPEngineReport : public Message { +public: + static MessageRegistrator ID; + + 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(ID()), + m_sampleRate(sampleRate), + m_centerFrequency(centerFrequency) + { } +}; + +class DSPConfigureScopeVis : public Message { +public: + static MessageRegistrator ID; + + 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(ID()), + m_triggerChannel(triggerChannel), + m_triggerLevelHigh(triggerLevelHigh), + m_triggerLevelLow(triggerLevelLow) + { } +}; + +class DSPSignalNotification : public Message { +public: + static MessageRegistrator ID; + + 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(ID()), + m_sampleRate(samplerate), + m_frequencyOffset(frequencyOffset) + { } +}; + +class DSPConfigureChannelizer : public Message { +public: + static MessageRegistrator ID; + + 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(ID()), + m_sampleRate(sampleRate), + m_centerFrequency(centerFrequency) + { } +}; + +#endif // INCLUDE_DSPCOMMANDS_H diff --git a/include-gpl/dsp/dspengine.h b/include-gpl/dsp/dspengine.h new file mode 100644 index 0000000..1c3fd42 --- /dev/null +++ b/include-gpl/dsp/dspengine.h @@ -0,0 +1,118 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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" + +class SampleSource; +class SampleSink; +class AudioFifo; + +class 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; + + int 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 diff --git a/include-gpl/dsp/fftengine.h b/include-gpl/dsp/fftengine.h new file mode 100644 index 0000000..1139059 --- /dev/null +++ b/include-gpl/dsp/fftengine.h @@ -0,0 +1,19 @@ +#ifndef INCLUDE_FFTENGINE_H +#define INCLUDE_FFTENGINE_H + +#include "dsp/dsptypes.h" + +class 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 diff --git a/include-gpl/dsp/fftwengine.h b/include-gpl/dsp/fftwengine.h new file mode 100644 index 0000000..2da5317 --- /dev/null +++ b/include-gpl/dsp/fftwengine.h @@ -0,0 +1,34 @@ +#ifndef INCLUDE_FFTWENGINE_H +#define INCLUDE_FFTWENGINE_H + +#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: + 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 diff --git a/include-gpl/dsp/fftwindow.h b/include-gpl/dsp/fftwindow.h new file mode 100644 index 0000000..179a0b7 --- /dev/null +++ b/include-gpl/dsp/fftwindow.h @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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" + +class 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 diff --git a/include-gpl/dsp/interpolator.h b/include-gpl/dsp/interpolator.h new file mode 100644 index 0000000..22541cb --- /dev/null +++ b/include-gpl/dsp/interpolator.h @@ -0,0 +1,64 @@ +#ifndef INCLUDE_INTERPOLATOR_H +#define INCLUDE_INTERPOLATOR_H + +#include "dsp/dsptypes.h" + +class Interpolator { +public: + Interpolator(); + + void create(int nTaps, int phaseSteps, double sampleRate, double cutoff); + + 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: + std::vector<Real> m_taps; + 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; + m_samples[m_ptr] = next; + } + + void doInterpolate(int phase, Complex* result) + { + int sample = m_ptr; + const Real* coeff = &m_taps[phase * m_nTaps]; + 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++; + if(sample >= m_nTaps) + sample = 0; + coeff++; + } + *result = Complex(rAcc, iAcc); + } +}; + +#endif // INCLUDE_INTERPOLATOR_H diff --git a/include-gpl/dsp/inthalfbandfilter.h b/include-gpl/dsp/inthalfbandfilter.h new file mode 100644 index 0000000..ae49793 --- /dev/null +++ b/include-gpl/dsp/inthalfbandfilter.h @@ -0,0 +1,306 @@ +#ifndef INCLUDE_INTHALFBANDFILTER_H +#define INCLUDE_INTHALFBANDFILTER_H + +#include <QtGlobal> +#include "dsp/dsptypes.h" + +// uses Q1.14 format internally, input and output are S16 + +/* + * supported filter orders: 64, 48, 32 + */ +#define HB_FILTERORDER 48 +#define HB_SHIFT 14 + +class 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); + if(m_ptr >= (HB_FILTERORDER + 1)) + m_ptr -= (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); + if(m_ptr >= (HB_FILTERORDER + 1)) + m_ptr -= (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); + if(m_ptr >= (HB_FILTERORDER + 1)) + m_ptr -= (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); + if(m_ptr >= (HB_FILTERORDER + 1)) + m_ptr -= (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); + if(m_ptr >= (HB_FILTERORDER + 1)) + m_ptr -= (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); + if(m_ptr >= (HB_FILTERORDER + 1)) + m_ptr -= (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); + if(m_ptr >= (HB_FILTERORDER + 1)) + m_ptr -= (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); + if(m_ptr >= (HB_FILTERORDER + 1)) + m_ptr -= (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); + if(m_ptr >= (HB_FILTERORDER + 1)) + m_ptr -= (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); + if(m_ptr >= (HB_FILTERORDER + 1)) + m_ptr -= (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); + if(a >= (HB_FILTERORDER + 1)) + a -= (HB_FILTERORDER + 1); + int b = (m_ptr + (HB_FILTERORDER - 1)); + if(b >= (HB_FILTERORDER + 1)) + b -= (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); + if(a >= (HB_FILTERORDER + 1)) + a -= (HB_FILTERORDER + 1); + b = b + (HB_FILTERORDER - 1); + if(b >= (HB_FILTERORDER + 1)) + b -= (HB_FILTERORDER + 1); + } + + a = (a + HB_FILTERORDER); + if(a >= (HB_FILTERORDER + 1)) + a -= (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 diff --git a/include-gpl/dsp/kissengine.h b/include-gpl/dsp/kissengine.h new file mode 100644 index 0000000..ad8e53c --- /dev/null +++ b/include-gpl/dsp/kissengine.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 diff --git a/include-gpl/dsp/lowpass.h b/include-gpl/dsp/lowpass.h new file mode 100644 index 0000000..a9ed168 --- /dev/null +++ b/include-gpl/dsp/lowpass.h @@ -0,0 +1,86 @@ +#ifndef INCLUDE_LOWPASS_H +#define INCLUDE_LOWPASS_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 diff --git a/include-gpl/dsp/nco.h b/include-gpl/dsp/nco.h new file mode 100644 index 0000000..7bc9f01 --- /dev/null +++ b/include-gpl/dsp/nco.h @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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" + +class 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 diff --git a/include-gpl/dsp/scopevis.h b/include-gpl/dsp/scopevis.h new file mode 100644 index 0000000..5631a58 --- /dev/null +++ b/include-gpl/dsp/scopevis.h @@ -0,0 +1,43 @@ +#ifndef INCLUDE_SCOPEVIS_H +#define INCLUDE_SCOPEVIS_H + +#include "dsp/samplesink.h" + +class GLScope; +class MessageQueue; + +class 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; + int m_fill; + TriggerState m_triggerState; + TriggerChannel m_triggerChannel; + FixReal m_triggerLevelHigh; + FixReal m_triggerLevelLow; + int m_sampleRate; +}; + +#endif // INCLUDE_SCOPEVIS_H diff --git a/include-gpl/dsp/spectrumvis.h b/include-gpl/dsp/spectrumvis.h new file mode 100644 index 0000000..0b13c6c --- /dev/null +++ b/include-gpl/dsp/spectrumvis.h @@ -0,0 +1,41 @@ +#ifndef INCLUDE_SPECTRUMVIS_H +#define INCLUDE_SPECTRUMVIS_H + +#include "dsp/samplesink.h" +#include "dsp/fftengine.h" +#include "fftwindow.h" + +class GLSpectrum; +class MessageQueue; + +class 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 diff --git a/include-gpl/gui/addpresetdialog.h b/include-gpl/gui/addpresetdialog.h new file mode 100644 index 0000000..8d3a7ee --- /dev/null +++ b/include-gpl/gui/addpresetdialog.h @@ -0,0 +1,30 @@ +#ifndef INCLUDE_ADDPRESETDIALOG_H +#define INCLUDE_ADDPRESETDIALOG_H + +#include <QDialog> + +namespace Ui { + class AddPresetDialog; +} + +class AddPresetDialog : public QDialog { + Q_OBJECT + +public: + explicit AddPresetDialog(const QStringList& groups, const QString& group, QWidget* parent = NULL); + ~AddPresetDialog(); + + QString group() const; + QString description() const; + +private: + enum Audio { + ATDefault, + ATInterface, + ATDevice + }; + + Ui::AddPresetDialog* ui; +}; + +#endif // INCLUDE_ADDPRESETDIALOG_H diff --git a/include-gpl/gui/glscope.h b/include-gpl/gui/glscope.h new file mode 100644 index 0000000..00a3acb --- /dev/null +++ b/include-gpl/gui/glscope.h @@ -0,0 +1,108 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_GLSCOPE_H +#define INCLUDE_GLSCOPE_H + +#include <QGLWidget> +#include <QPen> +#include <QTimer> +#include <QMutex> +#include "dsp/dsptypes.h" +#include "dsp/scopevis.h" + +class DSPEngine; +class ScopeVis; + +class GLScope: public QGLWidget { + Q_OBJECT + +public: + enum Mode { + ModeIQ, + ModeMagLinPha, + ModeMagdBPha, + ModeDerived12 + }; + + GLScope(QWidget* parent = NULL); + ~GLScope(); + + void setDSPEngine(DSPEngine* dspEngine); + void setAmp(Real amp); + void setTimeBase(int timeBase); + void setTimeOfsProMill(int timeOfsProMill); + void setMode(Mode mode); + void setOrientation(Qt::Orientation orientation); + + void newTrace(const std::vector<Complex>& trace, int sampleRate); + + int getTraceSize() const { return m_rawTrace.size(); } + +signals: + void traceSizeChanged(int); + +private: + // state + QTimer m_timer; + QMutex m_mutex; + bool m_dataChanged; + bool m_configChanged; + Mode m_mode; + Qt::Orientation m_orientation; + + // traces + std::vector<Complex> m_rawTrace; + std::vector<Complex> m_mathTrace; + std::vector<Complex>* m_displayTrace; + int m_oldTraceSize; + int m_sampleRate; + Real m_amp1; + Real m_amp2; + Real m_ofs1; + Real m_ofs2; + + // sample sink + DSPEngine* m_dspEngine; + ScopeVis* m_scopeVis; + + // config + Real m_amp; + int m_timeBase; + int m_timeOfsProMill; + ScopeVis::TriggerChannel m_triggerChannel; + Real m_triggerLevelHigh; + Real m_triggerLevelLow; + + // graphics stuff + QRectF m_glScopeRect1; + QRectF m_glScopeRect2; + + void initializeGL(); + void resizeGL(int width, int height); + void paintGL(); + + void mousePressEvent(QMouseEvent*); + + void handleMode(); + void applyConfig(); + +protected slots: + void tick(); +}; + +#endif // INCLUDE_GLSCOPE_H diff --git a/include-gpl/gui/glspectrum.h b/include-gpl/gui/glspectrum.h new file mode 100644 index 0000000..d1b07f5 --- /dev/null +++ b/include-gpl/gui/glspectrum.h @@ -0,0 +1,149 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_GLSPECTRUM_H +#define INCLUDE_GLSPECTRUM_H + +#include <QGLWidget> +#include <QTimer> +#include <QMutex> +#include "dsp/dsptypes.h" +#include "gui/scaleengine.h" +#include "dsp/channelmarker.h" + +class GLSpectrum : public QGLWidget { + Q_OBJECT + +public: + GLSpectrum(QWidget* parent = NULL); + ~GLSpectrum(); + + void setCenterFrequency(quint64 frequency); + void setSampleRate(qint32 sampleRate); + void setReferenceLevel(Real referenceLevel); + void setPowerRange(Real powerRange); + void setDisplayWaterfall(bool display); + void setInvertedWaterfall(bool inv); + void setDisplayLiveSpectrum(bool display); + void setDisplayHistogram(bool display); + + void addChannelMarker(ChannelMarker* channelMarker); + void removeChannelMarker(ChannelMarker* channelMarker); + + void newSpectrum(const std::vector<Real>& spectrum, int fftSize); + +private: + struct ChannelMarkerState { + ChannelMarker* m_channelMarker; + QRectF m_glRect; + QRect m_rect; + + ChannelMarkerState(ChannelMarker* channelMarker) : + m_channelMarker(channelMarker), + m_glRect() + { } + }; + QList<ChannelMarkerState*> m_channelMarkerStates; + + enum CursorState { + CSNormal, + CSSplitter, + CSSplitterMoving, + CSChannel, + CSChannelMoving + }; + + CursorState m_cursorState; + int m_cursorChannel; + + QTimer m_timer; + QMutex m_mutex; + bool m_changesPending; + + qint64 m_centerFrequency; + Real m_referenceLevel; + Real m_powerRange; + quint32 m_sampleRate; + + int m_fftSize; + + bool m_invertedWaterfall; + + std::vector<Real> m_liveSpectrum; + bool m_displayLiveSpectrum; + bool m_liveSpectrumChanged; + + Real m_waterfallShare; + + QPixmap m_leftMarginPixmap; + bool m_leftMarginTextureAllocated; + GLuint m_leftMarginTexture; + QPixmap m_frequencyPixmap; + bool m_frequencyTextureAllocated; + GLuint m_frequencyTexture; + ScaleEngine m_timeScale; + ScaleEngine m_powerScale; + ScaleEngine m_frequencyScale; + QRectF m_glLeftScaleRect; + QRectF m_glFrequencyScaleRect; + QRect m_frequencyScaleRect; + + QRgb m_waterfallPalette[240]; + QImage* m_waterfallBuffer; + int m_waterfallBufferPos; + bool m_waterfallTextureAllocated; + GLuint m_waterfallTexture; + int m_waterfallTextureHeight; + int m_waterfallTexturePos; + QRectF m_glWaterfallRect; + bool m_displayWaterfall; + + QRgb m_histogramPalette[240]; + QImage* m_histogramBuffer; + quint8* m_histogram; + quint8* m_histogramHoldoff; + bool m_histogramTextureAllocated; + GLuint m_histogramTexture; + int m_histogramHoldoffBase; + int m_histogramHoldoffCount; + int m_histogramLateHoldoff; + QRectF m_glHistogramRect; + bool m_displayHistogram; + + bool m_displayChanged; + + void updateWaterfall(const std::vector<Real>& spectrum); + void updateHistogram(const std::vector<Real>& spectrum); + + void initializeGL(); + void resizeGL(int width, int height); + void paintGL(); + + void stopDrag(); + void applyChanges(); + + void mouseMoveEvent(QMouseEvent* event); + void mousePressEvent(QMouseEvent* event); + void mouseReleaseEvent(QMouseEvent* event); + +private slots: + void tick(); + void channelMarkerChanged(); + void channelMarkerDestroyed(QObject* object); +}; + +#endif // INCLUDE_GLSPECTRUM_H diff --git a/include-gpl/gui/glspectrumgui.h b/include-gpl/gui/glspectrumgui.h new file mode 100644 index 0000000..0a274dc --- /dev/null +++ b/include-gpl/gui/glspectrumgui.h @@ -0,0 +1,58 @@ +#ifndef INCLUDE_GLSPECTRUMGUI_H +#define INCLUDE_GLSPECTRUMGUI_H + +#include <QWidget> +#include "dsp/dsptypes.h" + +namespace Ui { + class GLSpectrumGUI; +} + +class MessageQueue; +class SpectrumVis; +class GLSpectrum; + +class GLSpectrumGUI : public QWidget { + Q_OBJECT + +public: + explicit GLSpectrumGUI(QWidget* parent = NULL); + ~GLSpectrumGUI(); + + void setBuddies(MessageQueue* messageQueue, SpectrumVis* spectrumVis, GLSpectrum* glSpectrum); + + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + +private: + Ui::GLSpectrumGUI* ui; + + MessageQueue* m_messageQueue; + SpectrumVis* m_spectrumVis; + GLSpectrum* m_glSpectrum; + + qint32 m_fftSize; + qint32 m_fftOverlap; + qint32 m_fftWindow; + Real m_refLevel; + Real m_powerRange; + bool m_displayWaterfall; + bool m_invertedWaterfall; + bool m_displayLiveSpectrum; + bool m_displayHistogram; + + void applySettings(); + +private slots: + void on_fftSize_valueChanged(int value); + void on_fftWindow_currentIndexChanged(int index); + void on_refLevel_valueChanged(int value); + void on_levelRange_valueChanged(int value); + void on_decay_valueChanged(int value); + void on_waterfall_toggled(bool checked); + void on_histogram_toggled(bool checked); + void on_liveSpectrum_toggled(bool checked); +}; + +#endif // INCLUDE_GLSPECTRUMGUI_H diff --git a/include-gpl/gui/indicator.h b/include-gpl/gui/indicator.h new file mode 100644 index 0000000..a451469 --- /dev/null +++ b/include-gpl/gui/indicator.h @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_INDICATOR_H +#define INCLUDE_INDICATOR_H + +#include <QWidget> + +class Indicator : public QWidget { +private: + Q_OBJECT; + + QColor m_color; + QString m_text; + +protected: + void paintEvent(QPaintEvent* event); + QSize sizeHint() const; + +public: + Indicator(const QString& text, QWidget* parent = NULL); + + void setColor(const QColor& color); +}; + +#endif // INCLUDE_INDICATOR_H diff --git a/include-gpl/gui/physicalunit.h b/include-gpl/gui/physicalunit.h new file mode 100644 index 0000000..e8eda2b --- /dev/null +++ b/include-gpl/gui/physicalunit.h @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_PHYSICALUNIT_H +#define INCLUDE_PHYSICALUNIT_H + +namespace Unit { + enum Physical { + None, + Frequency, + Information, + Percent, + Decibel, + DecibelMilliWatt, + DecibelMicroVolt, + AngleDegrees, + Time + }; +}; + +#endif // INCLUDE_PHYSICALUNIT_H diff --git a/include-gpl/gui/pluginsdialog.h b/include-gpl/gui/pluginsdialog.h new file mode 100644 index 0000000..d0f7305 --- /dev/null +++ b/include-gpl/gui/pluginsdialog.h @@ -0,0 +1,22 @@ +#ifndef INCLUDE_PLUGINSDIALOG_H +#define INCLUDE_PLUGINSDIALOG_H + +#include <QDialog> +#include "plugin/pluginmanager.h" + +namespace Ui { + class PluginsDialog; +} + +class PluginsDialog : public QDialog { + Q_OBJECT + +public: + explicit PluginsDialog(PluginManager* pluginManager, QWidget* parent = NULL); + ~PluginsDialog(); + +private: + Ui::PluginsDialog* ui; +}; + +#endif // INCLUDE_PLUGINSDIALOG_H diff --git a/include-gpl/gui/preferencesdialog.h b/include-gpl/gui/preferencesdialog.h new file mode 100644 index 0000000..86247f3 --- /dev/null +++ b/include-gpl/gui/preferencesdialog.h @@ -0,0 +1,34 @@ +#ifndef INCLUDE_PREFERENCESDIALOG_H +#define INCLUDE_PREFERENCESDIALOG_H + +#include <QDialog> + +class AudioDeviceInfo; + +namespace Ui { + class PreferencesDialog; +} + +class PreferencesDialog : public QDialog { + Q_OBJECT + +public: + explicit PreferencesDialog(AudioDeviceInfo* audioDeviceInfo, QWidget* parent = NULL); + ~PreferencesDialog(); + +private: + enum Audio { + ATDefault, + ATInterface, + ATDevice + }; + + Ui::PreferencesDialog* ui; + + AudioDeviceInfo* m_audioDeviceInfo; + +private slots: + void accept(); +}; + +#endif // INCLUDE_PREFERENCESDIALOG_H diff --git a/include-gpl/gui/presetitem.h b/include-gpl/gui/presetitem.h new file mode 100644 index 0000000..fa45543 --- /dev/null +++ b/include-gpl/gui/presetitem.h @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 <QTreeWidgetItem> + +class PresetItem : public QTreeWidgetItem { +public: + PresetItem(QTreeWidgetItem* parent, const QStringList& strings, quint64 frequency, int type); + bool operator<(const QTreeWidgetItem& other) const; + +private: + quint64 m_frequency; +}; diff --git a/include-gpl/gui/scale.h b/include-gpl/gui/scale.h new file mode 100644 index 0000000..b9c3b34 --- /dev/null +++ b/include-gpl/gui/scale.h @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany // +// written by Christian Daniel // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see <http://www.gnu.org/licenses/>. // +/////////////////////////////////////////////////////////////////////////////////// + +#include <QWidget> +#include "gui/scaleengine.h" + +class Scale : public QWidget { + Q_OBJECT + +public: + Scale(QWidget* parent = NULL); + + void setOrientation(Qt::Orientation orientation); + void setRange(Unit::Physical physicalUnit, float rangeMin, float rangeMax); + +private: + Qt::Orientation m_orientation; + ScaleEngine m_scaleEngine; + + void paintEvent(QPaintEvent*); + void resizeEvent(QResizeEvent*); +}; diff --git a/include-gpl/gui/scaleengine.h b/include-gpl/gui/scaleengine.h new file mode 100644 index 0000000..3774116 --- /dev/null +++ b/include-gpl/gui/scaleengine.h @@ -0,0 +1,89 @@ +///////////////////////////////////////////////////////////////////////////////////
+// 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_SCALEENGINE_H
+#define INCLUDE_SCALEENGINE_H
+
+#include <QFont>
+#include <QString>
+#include <QList>
+#include "physicalunit.h"
+
+class ScaleEngine {
+public:
+ struct Tick {
+ float pos;
+ bool major;
+ float textPos;
+ float textSize;
+ QString text;
+ };
+ typedef QList<Tick> TickList;
+
+private:
+ // base configuration
+ Qt::Orientation m_orientation;
+ QFont m_font;
+ float m_charSize;
+
+ // graph configuration
+ float m_size;
+ Unit::Physical m_physicalUnit;
+ float m_rangeMin;
+ float m_rangeMax;
+
+ // calculated values
+ bool m_recalc;
+ double m_scale;
+ QString m_unitStr;
+ TickList m_tickList;
+ double m_majorTickValueDistance;
+ double m_firstMajorTickValue;
+ int m_numMinorTicks;
+ int m_decimalPlaces;
+
+ QString formatTick(double value, int decimalPlaces, bool fancyTime = true);
+ void calcCharSize();
+ void calcScaleFactor();
+ double calcMajorTickUnits(double distance, int* retDecimalPlaces);
+ int calcTickTextSize();
+ void forceTwoTicks();
+ void reCalc();
+
+ double majorTickValue(int tick);
+ double minorTickValue(int tick);
+
+public:
+ ScaleEngine();
+
+ void setOrientation(Qt::Orientation orientation);
+ void setFont(const QFont& font);
+ void setSize(float size);
+ float getSize() { return m_size; }
+ void setRange(Unit::Physical physicalUnit, float rangeMin, float rangeMax);
+
+ float getPosFromValue(double value);
+ float getValueFromPos(double pos);
+ const TickList& getTickList();
+
+ QString getRangeMinStr();
+ QString getRangeMaxStr();
+
+ float getScaleWidth();
+};
+
+#endif // INCLUDE_SCALEENGINE_H
diff --git a/include-gpl/gui/scopewindow.h b/include-gpl/gui/scopewindow.h new file mode 100644 index 0000000..281a848 --- /dev/null +++ b/include-gpl/gui/scopewindow.h @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany // +// written by Christian Daniel // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see <http://www.gnu.org/licenses/>. // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_SCOPEWINDOW_H +#define INCLUDE_SCOPEWINDOW_H + +#include <QWidget> +#include "dsp/dsptypes.h" + +class DSPEngine; + +namespace Ui { + class ScopeWindow; +} + +class ScopeWindow : public QWidget { + Q_OBJECT + +public: + explicit ScopeWindow(DSPEngine* dspEngine, QWidget* parent = NULL); + ~ScopeWindow(); + + void setSampleRate(int sampleRate); + + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + +private slots: + void on_amp_valueChanged(int value); + void on_scope_traceSizeChanged(int value); + void on_time_valueChanged(int value); + void on_timeOfs_valueChanged(int value); + void on_displayMode_currentIndexChanged(int index); + + void on_horizView_clicked(); + void on_vertView_clicked(); + +private: + Ui::ScopeWindow *ui; + int m_sampleRate; + + qint32 m_displayData; + qint32 m_displayOrientation; + qint32 m_timeBase; + qint32 m_timeOffset; + qint32 m_amplification; + + void applySettings(); +}; + +#endif // INCLUDE_SCOPEWINDOW_H diff --git a/include-gpl/gui/valuedial.h b/include-gpl/gui/valuedial.h new file mode 100644 index 0000000..8e660b9 --- /dev/null +++ b/include-gpl/gui/valuedial.h @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 <QWidget> +#include <QTimer> + +class ValueDial : public QWidget { + Q_OBJECT + +public: + ValueDial(QWidget* parent = NULL); + + void setValue(quint64 value); + void setValueRange(uint numDigits, quint64 min, quint64 max); + void setFont(const QFont& font); + +signals: + void changed(quint64 value); + +private: + QLinearGradient m_background; + int m_numDigits; + int m_numDecimalPoints; + int m_digitWidth; + int m_digitHeight; + int m_hightlightedDigit; + int m_cursor; + bool m_cursorState; + quint64 m_value; + quint64 m_valueMax; + quint64 m_valueMin; + QString m_text; + + quint64 m_valueNew; + QString m_textNew; + int m_animationState; + QTimer m_animationTimer; + QTimer m_blinkTimer; + + quint64 findExponent(int digit); + QChar digitNeigh(QChar c, bool dir); + QString formatText(quint64 value); + + void paintEvent(QPaintEvent*); + + void mousePressEvent(QMouseEvent*); + void mouseMoveEvent(QMouseEvent*); + void wheelEvent(QWheelEvent*); + void leaveEvent(QEvent*); + void keyPressEvent(QKeyEvent*); + void focusInEvent(QFocusEvent*); + void focusOutEvent(QFocusEvent*); + +private slots: + void animate(); + void blink(); +}; diff --git a/include-gpl/mainwindow.h b/include-gpl/mainwindow.h new file mode 100644 index 0000000..9848962 --- /dev/null +++ b/include-gpl/mainwindow.h @@ -0,0 +1,135 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_MAINWINDOW_H +#define INCLUDE_MAINWINDOW_H + +#include <QMainWindow> +#include <QTimer> +#include "audio/portaudioholder.h" +#include "settings/settings.h" + +class QLabel; +class QTreeWidgetItem; +class QDir; + +class AudioDeviceInfo; +class DSPEngine; +class Indicator; +class ScopeWindow; +class SpectrumVis; +class SampleSource; +class PluginAPI; +class PluginGUI; +class ChannelMarker; +class MessageQueue; +class PluginManager; +class PluginInterface; + +namespace Ui { + class MainWindow; +} + +class MainWindow : public QMainWindow { + Q_OBJECT + +public: + explicit MainWindow(QWidget* parent = NULL); + ~MainWindow(); + + MessageQueue* getMessageQueue() { return m_messageQueue; } + + void addDemodCreateAction(QAction* action); + void addViewAction(QAction* action); + + void addChannelMarker(ChannelMarker* channelMarker); + void removeChannelMarker(ChannelMarker* channelMarker); + + void setInputGUI(QWidget* gui); + +private: + enum { + PGroup, + PItem + }; + + Ui::MainWindow *ui; + + PortAudioHolder m_portAudioHolder; + AudioDeviceInfo* m_audioDeviceInfo; + + MessageQueue* m_messageQueue; + + Settings m_settings; + + SpectrumVis* m_spectrumVis; + + DSPEngine* m_dspEngine; + + QTimer m_statusTimer; + int m_lastEngineState; + + QLabel* m_sampleRateWidget; + Indicator* m_engineIdle; + Indicator* m_engineRunning; + Indicator* m_engineError; + + bool m_startOsmoSDRUpdateAfterStop; + + ScopeWindow* m_scopeWindow; + QWidget* m_inputGUI; + + int m_sampleRate; + quint64 m_centerFrequency; + + PluginManager* m_pluginManager; + + void loadSettings(); + void loadSettings(const Preset* preset); + void saveSettings(Preset* preset); + void saveSettings(); + + void createStatusBar(); + void closeEvent(QCloseEvent*); + void updateCenterFreqDisplay(); + void updateSampleRate(); + void updatePresets(); + QTreeWidgetItem* addPresetToTree(const Preset* preset); + void applySettings(); + +private slots: + void handleMessages(); + void updateStatus(); + void scopeWindowDestroyed(); + void on_action_Start_triggered(); + void on_action_Stop_triggered(); + void on_dcOffset_toggled(bool checked); + void on_iqImbalance_toggled(bool checked); + void on_action_View_Fullscreen_toggled(bool checked); + void on_actionOsmoSDR_Firmware_Upgrade_triggered(); + void on_presetSave_clicked(); + void on_presetLoad_clicked(); + void on_presetDelete_clicked(); + void on_presetTree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous); + void on_presetTree_itemActivated(QTreeWidgetItem *item, int column); + void on_action_Oscilloscope_triggered(); + void on_action_Loaded_Plugins_triggered(); + void on_action_Preferences_triggered(); + void on_sampleSource_currentIndexChanged(int index); +}; + +#endif // INCLUDE_MAINWINDOW_H diff --git a/include-gpl/plugin/pluginmanager.h b/include-gpl/plugin/pluginmanager.h new file mode 100644 index 0000000..50f39f3 --- /dev/null +++ b/include-gpl/plugin/pluginmanager.h @@ -0,0 +1,127 @@ +#ifndef INCLUDE_PLUGINMANAGER_H +#define INCLUDE_PLUGINMANAGER_H + +#include <QObject> +#include <QDir> +#include "plugin/plugininterface.h" +#include "plugin/pluginapi.h" + +class QAction; +class QComboBox; +class Preset; +class MainWindow; +class SampleSource; +class Message; + +class PluginManager : public QObject { + Q_OBJECT + +public: + struct Plugin { + QString filename; + PluginInterface* plugin; + Plugin(const QString& _filename, PluginInterface* _plugin) : + filename(_filename), + plugin(_plugin) + { } + }; + typedef QList<Plugin> Plugins; + + explicit PluginManager(MainWindow* mainWindow, DSPEngine* dspEngine, QObject* parent = NULL); + ~PluginManager(); + void loadPlugins(); + + const Plugins& getPlugins() const { return m_plugins; } + + void registerDemodulator(const QString& demodName, PluginInterface* plugin, QAction* action); + void registerDemodulatorInstance(const QString& demodName, PluginGUI* pluginGUI); + + void registerSampleSource(const QString& sourceName, PluginInterface* plugin); + + void loadSettings(const Preset* preset); + void saveSettings(Preset* preset) const; + + void freeAll(); + + bool handleMessage(Message* message); + + void updateSampleSourceDevices(); + void fillSampleSourceSelector(QComboBox* comboBox); + int selectSampleSource(int index); + +private slots: + void demodInstanceDestroyed(QObject* object); + +private: + struct DemodRegistration { + QString m_demodName; + PluginInterface* m_plugin; + DemodRegistration(const QString& demodName, PluginInterface* plugin) : + m_demodName(demodName), + m_plugin(plugin) + { } + }; + typedef QList<DemodRegistration> DemodRegistrations; + + struct DemodInstanceRegistration { + QString m_demodName; + PluginGUI* m_gui; + DemodInstanceRegistration() : + m_demodName(), + m_gui(NULL) + { } + DemodInstanceRegistration(const QString& demodName, PluginGUI* pluginGUI) : + m_demodName(demodName), + m_gui(pluginGUI) + { } + }; + typedef QList<DemodInstanceRegistration> DemodInstanceRegistrations; + + struct SampleSourceRegistration { + QString m_sourceName; + PluginInterface* m_plugin; + SampleSourceRegistration(const QString& sourceName, PluginInterface* plugin) : + m_sourceName(sourceName), + m_plugin(plugin) + { } + }; + typedef QList<SampleSourceRegistration> SampleSourceRegistrations; + + struct SampleSourceDevice { + PluginInterface* m_plugin; + QString m_displayName; + QString m_sourceName; + QByteArray m_address; + + SampleSourceDevice(PluginInterface* plugin, const QString& displayName, const QString& sourceName, const QByteArray& address) : + m_plugin(plugin), + m_displayName(displayName), + m_sourceName(sourceName), + m_address(address) + { } + }; + typedef QList<SampleSourceDevice> SampleSourceDevices; + + PluginAPI m_pluginAPI; + MainWindow* m_mainWindow; + DSPEngine* m_dspEngine; + Plugins m_plugins; + + DemodRegistrations m_demodRegistrations; + DemodInstanceRegistrations m_demodInstanceRegistrations; + SampleSourceRegistrations m_sampleSourceRegistrations; + SampleSourceDevices m_sampleSourceDevices; + + QString m_sampleSource; + PluginGUI* m_sampleSourceInstance; + + void loadPlugins(const QDir& dir); + void renameDemodInstances(); +}; + +static inline bool operator<(const PluginManager::Plugin& a, const PluginManager::Plugin& b) +{ + return a.plugin->getPluginDescriptor().displayedName < b.plugin->getPluginDescriptor().displayedName; +} + +#endif // INCLUDE_PLUGINMANAGER_H diff --git a/include-gpl/settings/preferences.h b/include-gpl/settings/preferences.h new file mode 100644 index 0000000..74de287 --- /dev/null +++ b/include-gpl/settings/preferences.h @@ -0,0 +1,32 @@ +#ifndef INCLUDE_PREFERENCES_H +#define INCLUDE_PREFERENCES_H + +#include <QString> + +class Preferences { +public: + Preferences(); + + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + + void setSourceType(const QString& value) { m_sourceType = value; } + const QString& getSourceType() const { return m_sourceType; } + void setSourceDevice(const QString& value) { m_sourceDevice= value; } + const QString& getSourceDevice() const { return m_sourceDevice; } + + void setAudioType(const QString& value) { m_audioType = value; } + const QString& getAudioType() const { return m_audioType; } + void setAudioDevice(const QString& value) { m_audioDevice= value; } + const QString& getAudioDevice() const { return m_audioDevice; } + +protected: + QString m_sourceType; + QString m_sourceDevice; + + QString m_audioType; + QString m_audioDevice; +}; + +#endif // INCLUDE_PREFERENCES_H diff --git a/include-gpl/settings/preset.h b/include-gpl/settings/preset.h new file mode 100644 index 0000000..f872941 --- /dev/null +++ b/include-gpl/settings/preset.h @@ -0,0 +1,98 @@ +#ifndef INCLUDE_PRESET_H +#define INCLUDE_PRESET_H + +#include <QString> +#include <QList> +#include <QMetaType> + +class Preset { +public: + struct DemodConfig { + QString m_demod; + QByteArray m_config; + + DemodConfig(const QString& demod, const QByteArray& config) : + m_demod(demod), + m_config(config) + { } + }; + typedef QList<DemodConfig> DemodConfigs; + + Preset(); + + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + + void setGroup(const QString& group) { m_group = group; } + const QString& getGroup() const { return m_group; } + void setDescription(const QString& description) { m_description = description; } + const QString& getDescription() const { return m_description; } + void setCenterFrequency(const quint64 centerFrequency) { m_centerFrequency = centerFrequency; } + quint64 getCenterFrequency() const { return m_centerFrequency; } + + void setSpectrumConfig(const QByteArray& data) { m_spectrumConfig = data; } + const QByteArray& getSpectrumConfig() const { return m_spectrumConfig; } + + void setShowScope(bool value) { m_showScope = value; } + bool getShowScope() const { return m_showScope; } + + void setLayout(const QByteArray& data) { m_layout = data; } + const QByteArray& getLayout() const { return m_layout; } + + void setDCOffsetCorrection(bool value) { m_dcOffsetCorrection = value; } + bool getDCOffsetCorrection() const { return m_dcOffsetCorrection; } + + void setIQImbalanceCorrection(bool value) { m_iqImbalanceCorrection = value; } + bool getIQImbalanceCorrection() const { return m_iqImbalanceCorrection; } + + void setScopeConfig(const QByteArray& data) { m_scopeConfig = data; } + const QByteArray& getScopeConfig() const { return m_scopeConfig; } + + void clearDemods() { m_demodConfigs.clear(); } + void addDemod(const QString& demod, const QByteArray& config) { m_demodConfigs.append(DemodConfig(demod, config)); } + int getDemodCount() const { return m_demodConfigs.count(); } + const DemodConfig& getDemodConfig(int index) const { return m_demodConfigs.at(index); } + + void setSourceConfig(const QString& source, const QByteArray& generalConfig, const QByteArray& config) + { + m_source = source; + m_sourceGeneralConfig = generalConfig; + m_sourceConfig = config; + } + const QString& getSource() const { return m_source; } + const QByteArray& getSourceGeneralConfig() const { return m_sourceGeneralConfig; } + const QByteArray& getSourceConfig() const { return m_sourceConfig; } + +protected: + // group and preset description + QString m_group; + QString m_description; + quint64 m_centerFrequency; + + // general configuration + QByteArray m_spectrumConfig; + QByteArray m_scopeConfig; + + // dc offset and i/q imbalance correction + bool m_dcOffsetCorrection; + bool m_iqImbalanceCorrection; + + // display scope dock + bool m_showScope; + + // sample source and sample source configuration + QString m_source; + QByteArray m_sourceGeneralConfig; + QByteArray m_sourceConfig; + + // demodulators and configurations + DemodConfigs m_demodConfigs; + + // screen and dock layout + QByteArray m_layout; +}; + +Q_DECLARE_METATYPE(const Preset*) + +#endif // INCLUDE_PRESET_H diff --git a/include-gpl/settings/settings.h b/include-gpl/settings/settings.h new file mode 100644 index 0000000..a6cf04f --- /dev/null +++ b/include-gpl/settings/settings.h @@ -0,0 +1,32 @@ +#ifndef INCLUDE_SETTINGS_H +#define INCLUDE_SETTINGS_H + +#include <QString> +#include "preferences.h" +#include "preset.h" + +class Settings { +public: + Settings(); + ~Settings(); + + void load(); + void save() const; + + void resetToDefaults(); + + Preset* newPreset(const QString& group, const QString& description); + void deletePreset(const Preset* preset); + int getPresetCount() const { return m_presets.count(); } + const Preset* getPreset(int index) const { return m_presets[index]; } + + Preset* getCurrent() { return &m_current; } + +protected: + Preferences m_preferences; + Preset m_current; + typedef QList<Preset*> Presets; + Presets m_presets; +}; + +#endif // INCLUDE_SETTINGS_H |