From 5864e48bd8d559223bed4a9eabbc4f894dc128ef Mon Sep 17 00:00:00 2001 From: Christian Daniel Date: Fri, 15 Aug 2014 11:37:26 +0200 Subject: [PATCH] major rewrite --- CMakeLists.txt | 4 +- include-gpl/audio/audiodeviceinfo.h | 49 ---- include-gpl/audio/audiofifo.h | 19 +- include-gpl/audio/audiooutput.h | 13 +- include-gpl/dsp/channelizer.h | 50 ---- include-gpl/dsp/dspcommands.h | 57 +++-- include-gpl/dsp/dspengine.h | 1 + include-gpl/dsp/inthalfbandfilter.h | 1 - {sdrbase => include-gpl}/dsp/pidcontroller.h | 0 include-gpl/gui/preferencesdialog.h | 12 +- include-gpl/gui/scopewindow.h | 3 +- include-gpl/mainwindow.h | 4 +- include-gpl/settings/preferences.h | 19 +- include-gpl/settings/settings.h | 2 + include/util/message.h | 3 +- plugins/channel/nfm/nfmdemod.cpp | 225 ++++++++++++++---- plugins/channel/nfm/nfmdemod.h | 42 +++- plugins/channel/nfm/nfmdemodgui.cpp | 6 +- plugins/channel/tcpsrc/tcpsrc.h | 6 +- .../samplesource/gnuradio/gnuradioinput.cpp | 3 +- plugins/samplesource/gnuradio/gnuradioinput.h | 4 +- plugins/samplesource/osmosdr/osmosdrinput.cpp | 111 +-------- plugins/samplesource/osmosdr/osmosdrinput.h | 2 +- .../samplesource/osmosdr/osmosdrthread.cpp | 18 +- plugins/samplesource/rtlsdr/rtlsdrinput.h | 4 +- sdrbase/audio/audiodeviceinfo.cpp | 28 --- sdrbase/audio/audiofifo.cpp | 4 +- sdrbase/audio/audiooutput.cpp | 71 +++++- sdrbase/dsp/channelizer.cpp | 164 +------------ sdrbase/dsp/dspcommands.cpp | 1 + sdrbase/dsp/dspengine.cpp | 97 ++------ sdrbase/dsp/fftwengine.cpp | 2 +- sdrbase/dsp/nco.cpp | 17 +- sdrbase/dsp/pidcontroller.cpp | 2 +- sdrbase/dsp/samplefifo.cpp | 31 ++- sdrbase/dsp/spectrumvis.cpp | 2 +- sdrbase/dsp/threadedsamplesink.cpp | 33 +-- sdrbase/gui/glspectrumgui.cpp | 2 +- sdrbase/gui/glspectrumgui.ui | 16 +- sdrbase/gui/preferencesdialog.cpp | 79 +++--- sdrbase/gui/preferencesdialog.ui | 110 ++++++--- sdrbase/gui/scopewindow.cpp | 8 +- sdrbase/mainwindow.cpp | 34 ++- sdrbase/plugin/pluginmanager.cpp | 6 +- sdrbase/settings/preferences.cpp | 20 +- sdrbase/util/message.cpp | 2 + 46 files changed, 647 insertions(+), 740 deletions(-) delete mode 100644 include-gpl/audio/audiodeviceinfo.h rename {sdrbase => include-gpl}/dsp/pidcontroller.h (100%) delete mode 100644 sdrbase/audio/audiodeviceinfo.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ab899f2..49323db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,7 +39,6 @@ endif() set(sdrbase_SOURCES sdrbase/mainwindow.cpp - sdrbase/audio/audiodeviceinfo.cpp sdrbase/audio/audiofifo.cpp sdrbase/audio/audiooutput.cpp @@ -100,7 +99,6 @@ set(sdrbase_SOURCES set(sdrbase_HEADERS include-gpl/mainwindow.h - include-gpl/audio/audiodeviceinfo.h include-gpl/audio/audiofifo.h include-gpl/audio/audiooutput.h @@ -119,7 +117,7 @@ set(sdrbase_HEADERS include-gpl/dsp/lowpass.h include-gpl/dsp/movingaverage.h include-gpl/dsp/nco.h - sdrbase/dsp/pidcontroller.h + include-gpl/dsp/pidcontroller.h include/dsp/samplefifo.h include/dsp/samplesink.h include-gpl/dsp/scopevis.h diff --git a/include-gpl/audio/audiodeviceinfo.h b/include-gpl/audio/audiodeviceinfo.h deleted file mode 100644 index ca285f5..0000000 --- a/include-gpl/audio/audiodeviceinfo.h +++ /dev/null @@ -1,49 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany // -// written by Christian Daniel // -// // -// This program is free software; you can redistribute it and/or modify // -// it under the terms of the GNU General Public License as published by // -// the Free Software Foundation as version 3 of the License, or // -// // -// This program is distributed in the hope that it will be useful, // -// but WITHOUT ANY WARRANTY; without even the implied warranty of // -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // -// GNU General Public License V3 for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with this program. If not, see . // -/////////////////////////////////////////////////////////////////////////////////// - -#ifndef INCLUDE_AUDIODEVICEINFO_H -#define INCLUDE_AUDIODEVICEINFO_H - -#include -#include "util/export.h" - -class SDRANGELOVE_API 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 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 index 98f35b8..385f138 100644 --- a/include-gpl/audio/audiofifo.h +++ b/include-gpl/audio/audiofifo.h @@ -36,11 +36,17 @@ public: 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; } + uint flush() { return drain(m_fill); } + uint fill() const { return m_fill; } + bool isEmpty() const { return m_fill == 0; } + bool isFull() const { return m_fill == m_size; } + uint size() const { return m_size; } + + quint32 getSampleRate() const { return m_sampleRate; } + void setSampleRate(quint32 rate) { m_sampleRate = rate; } + + bool isStopped() const { return m_stopped; } + void setStopped(bool stopped) { m_stopped = stopped; } private: QMutex m_mutex; @@ -59,6 +65,9 @@ private: QWaitCondition m_writeWaitCondition; QWaitCondition m_readWaitCondition; + quint32 m_sampleRate; + bool m_stopped; + bool create(uint sampleSize, uint numSamples); }; diff --git a/include-gpl/audio/audiooutput.h b/include-gpl/audio/audiooutput.h index 10816ac..2137ec3 100644 --- a/include-gpl/audio/audiooutput.h +++ b/include-gpl/audio/audiooutput.h @@ -33,14 +33,25 @@ public: AudioOutput(); ~AudioOutput(); - bool start(int device, int rate); + void configure(const QString& deviceName, uint rate); + + bool start(); void stop(); void addFifo(AudioFifo* audioFifo); void removeFifo(AudioFifo* audioFifo); + uint getCurrentRate(); + + const QString& getError() const { return m_error; } + private: QMutex m_mutex; + QString m_error; + + QString m_deviceName; + quint32 m_rate; + QAudioOutput* m_audioOutput; typedef std::list AudioFifos; diff --git a/include-gpl/dsp/channelizer.h b/include-gpl/dsp/channelizer.h index cf3ede3..0a76065 100644 --- a/include-gpl/dsp/channelizer.h +++ b/include-gpl/dsp/channelizer.h @@ -57,53 +57,3 @@ protected: }; #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 index 58663fe..358df9a 100644 --- a/include-gpl/dsp/dspcommands.h +++ b/include-gpl/dsp/dspcommands.h @@ -11,23 +11,23 @@ class SampleSink; class AudioFifo; class SDRANGELOVE_API DSPPing : public Message { - MESSAGE_CLASS_DECLARATION + MESSAGE_CLASS_DECLARATION(DSPPing) }; class SDRANGELOVE_API DSPExit : public Message { - MESSAGE_CLASS_DECLARATION + MESSAGE_CLASS_DECLARATION(DSPExit) }; class SDRANGELOVE_API DSPAcquisitionStart : public Message { - MESSAGE_CLASS_DECLARATION + MESSAGE_CLASS_DECLARATION(DSPAcquisitionStart) }; class SDRANGELOVE_API DSPAcquisitionStop : public Message { - MESSAGE_CLASS_DECLARATION + MESSAGE_CLASS_DECLARATION(DSPAcquisitionStop) }; class SDRANGELOVE_API DSPGetDeviceDescription : public Message { - MESSAGE_CLASS_DECLARATION + MESSAGE_CLASS_DECLARATION(DSPGetDeviceDescription) public: void setDeviceDescription(const QString& text) { m_deviceDescription = text; } @@ -38,7 +38,7 @@ private: }; class SDRANGELOVE_API DSPGetErrorMessage : public Message { - MESSAGE_CLASS_DECLARATION + MESSAGE_CLASS_DECLARATION(DSPGetErrorMessage) public: void setErrorMessage(const QString& text) { m_errorMessage = text; } @@ -49,7 +49,7 @@ private: }; class SDRANGELOVE_API DSPSetSource : public Message { - MESSAGE_CLASS_DECLARATION + MESSAGE_CLASS_DECLARATION(DSPSetSource) public: DSPSetSource(SampleSource* sampleSource) : Message(), m_sampleSource(sampleSource) { } @@ -61,7 +61,7 @@ private: }; class SDRANGELOVE_API DSPAddSink : public Message { - MESSAGE_CLASS_DECLARATION + MESSAGE_CLASS_DECLARATION(DSPAddSink) public: DSPAddSink(SampleSink* sampleSink) : Message(), m_sampleSink(sampleSink) { } @@ -73,7 +73,7 @@ private: }; class SDRANGELOVE_API DSPRemoveSink : public Message { - MESSAGE_CLASS_DECLARATION + MESSAGE_CLASS_DECLARATION(DSPRemoveSink) public: DSPRemoveSink(SampleSink* sampleSink) : Message(), m_sampleSink(sampleSink) { } @@ -85,7 +85,7 @@ private: }; class SDRANGELOVE_API DSPAddAudioSource : public Message { - MESSAGE_CLASS_DECLARATION + MESSAGE_CLASS_DECLARATION(DSPAddAudioSource) public: DSPAddAudioSource(AudioFifo* audioFifo) : Message(), m_audioFifo(audioFifo) { } @@ -97,7 +97,7 @@ private: }; class SDRANGELOVE_API DSPRemoveAudioSource : public Message { - MESSAGE_CLASS_DECLARATION + MESSAGE_CLASS_DECLARATION(DSPRemoveAudioSource) public: DSPRemoveAudioSource(AudioFifo* audioFifo) : Message(), m_audioFifo(audioFifo) { } @@ -109,7 +109,7 @@ private: }; class SDRANGELOVE_API DSPConfigureSpectrumVis : public Message { - MESSAGE_CLASS_DECLARATION + MESSAGE_CLASS_DECLARATION(DSPConfigureSpectrumVis) public: int getFFTSize() const { return m_fftSize; } @@ -135,7 +135,7 @@ private: }; class SDRANGELOVE_API DSPConfigureCorrection : public Message { - MESSAGE_CLASS_DECLARATION + MESSAGE_CLASS_DECLARATION(DSPConfigureCorrection) public: bool getDCOffsetCorrection() const { return m_dcOffsetCorrection; } @@ -157,8 +157,31 @@ private: { } }; +class SDRANGELOVE_API DSPConfigureAudioOutput : public Message { + MESSAGE_CLASS_DECLARATION(DSPConfigureAudioOutput) + +public: + const QString& getAudioOutputDevice() const { return m_audioOutputDevice; } + uint getAudioOutputRate() const { return m_audioOutputRate; } + + static DSPConfigureAudioOutput* create(const QString& audioOutputDevice, uint audioOutputRate) + { + return new DSPConfigureAudioOutput(audioOutputDevice, audioOutputRate); + } + +private: + QString m_audioOutputDevice; + uint m_audioOutputRate; + + DSPConfigureAudioOutput(const QString& audioOutputDevice, uint audioOutputRate) : + Message(), + m_audioOutputDevice(audioOutputDevice), + m_audioOutputRate(audioOutputRate) + { } +}; + class SDRANGELOVE_API DSPEngineReport : public Message { - MESSAGE_CLASS_DECLARATION + MESSAGE_CLASS_DECLARATION(DSPEngineReport) public: int getSampleRate() const { return m_sampleRate; } @@ -181,7 +204,7 @@ private: }; class SDRANGELOVE_API DSPConfigureScopeVis : public Message { - MESSAGE_CLASS_DECLARATION + MESSAGE_CLASS_DECLARATION(DSPConfigureScopeVis) public: int getTriggerChannel() const { return m_triggerChannel; } @@ -207,7 +230,7 @@ private: }; class SDRANGELOVE_API DSPSignalNotification : public Message { - MESSAGE_CLASS_DECLARATION + MESSAGE_CLASS_DECLARATION(DSPSignalNotification) public: int getSampleRate() const { return m_sampleRate; } @@ -230,7 +253,7 @@ private: }; class SDRANGELOVE_API DSPConfigureChannelizer : public Message { - MESSAGE_CLASS_DECLARATION + MESSAGE_CLASS_DECLARATION(DSPConfigureChannelizer) public: int getSampleRate() const { return m_sampleRate; } diff --git a/include-gpl/dsp/dspengine.h b/include-gpl/dsp/dspengine.h index 28d2dae..05277e1 100644 --- a/include-gpl/dsp/dspengine.h +++ b/include-gpl/dsp/dspengine.h @@ -64,6 +64,7 @@ public: void removeAudioSource(AudioFifo* audioFifo); void configureCorrections(bool dcOffsetCorrection, bool iqImbalanceCorrection); + void configureAudioOutput(const QString& audioOutput, quint32 audioOutputRate); State state() const { return m_state; } diff --git a/include-gpl/dsp/inthalfbandfilter.h b/include-gpl/dsp/inthalfbandfilter.h index ccc1bfa..2523dd0 100644 --- a/include-gpl/dsp/inthalfbandfilter.h +++ b/include-gpl/dsp/inthalfbandfilter.h @@ -281,7 +281,6 @@ protected: #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); diff --git a/sdrbase/dsp/pidcontroller.h b/include-gpl/dsp/pidcontroller.h similarity index 100% rename from sdrbase/dsp/pidcontroller.h rename to include-gpl/dsp/pidcontroller.h diff --git a/include-gpl/gui/preferencesdialog.h b/include-gpl/gui/preferencesdialog.h index 86247f3..f3c0b58 100644 --- a/include-gpl/gui/preferencesdialog.h +++ b/include-gpl/gui/preferencesdialog.h @@ -3,7 +3,7 @@ #include -class AudioDeviceInfo; +class Preferences; namespace Ui { class PreferencesDialog; @@ -13,19 +13,13 @@ class PreferencesDialog : public QDialog { Q_OBJECT public: - explicit PreferencesDialog(AudioDeviceInfo* audioDeviceInfo, QWidget* parent = NULL); + explicit PreferencesDialog(Preferences* preferences, QWidget* parent = NULL); ~PreferencesDialog(); private: - enum Audio { - ATDefault, - ATInterface, - ATDevice - }; - Ui::PreferencesDialog* ui; - AudioDeviceInfo* m_audioDeviceInfo; + Preferences* m_preferences; private slots: void accept(); diff --git a/include-gpl/gui/scopewindow.h b/include-gpl/gui/scopewindow.h index 2761dcc..984456e 100644 --- a/include-gpl/gui/scopewindow.h +++ b/include-gpl/gui/scopewindow.h @@ -32,9 +32,10 @@ class SDRANGELOVE_API ScopeWindow : public QWidget { Q_OBJECT public: - explicit ScopeWindow(DSPEngine* dspEngine, QWidget* parent = NULL); + explicit ScopeWindow(QWidget* parent = NULL); ~ScopeWindow(); + void setDSPEngine(DSPEngine* dspEngine); void setSampleRate(int sampleRate); void resetToDefaults(); diff --git a/include-gpl/mainwindow.h b/include-gpl/mainwindow.h index 0f0b481..204f197 100644 --- a/include-gpl/mainwindow.h +++ b/include-gpl/mainwindow.h @@ -27,7 +27,6 @@ class QLabel; class QTreeWidgetItem; class QDir; -class AudioDeviceInfo; class DSPEngine; class Indicator; class ScopeWindow; @@ -70,8 +69,6 @@ private: Ui::MainWindow* ui; - AudioDeviceInfo* m_audioDeviceInfo; - MessageQueue* m_messageQueue; Settings m_settings; @@ -114,6 +111,7 @@ private: private slots: void handleMessages(); void updateStatus(); + void updateEnables(bool running); void scopeWindowDestroyed(); void on_action_Start_triggered(); void on_action_Stop_triggered(); diff --git a/include-gpl/settings/preferences.h b/include-gpl/settings/preferences.h index 74de287..d37b358 100644 --- a/include-gpl/settings/preferences.h +++ b/include-gpl/settings/preferences.h @@ -11,22 +11,15 @@ public: 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 setAudioOutput(const QString& value) { m_audioOutput = value; } + const QString& getAudioOutput() const { return m_audioOutput; } - 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; } + void setAudioOutputRate(quint32 value) { m_audioOutputRate = value; } + uint getAudioOutputRate() const { return m_audioOutputRate; } protected: - QString m_sourceType; - QString m_sourceDevice; - - QString m_audioType; - QString m_audioDevice; + QString m_audioOutput; + uint m_audioOutputRate; }; #endif // INCLUDE_PREFERENCES_H diff --git a/include-gpl/settings/settings.h b/include-gpl/settings/settings.h index a6cf04f..752315e 100644 --- a/include-gpl/settings/settings.h +++ b/include-gpl/settings/settings.h @@ -22,6 +22,8 @@ public: Preset* getCurrent() { return &m_current; } + Preferences* getPreferences() { return &m_preferences; } + protected: Preferences m_preferences; Preset m_current; diff --git a/include/util/message.h b/include/util/message.h index 8cf06b2..52aebfd 100644 --- a/include/util/message.h +++ b/include/util/message.h @@ -38,11 +38,12 @@ protected: int m_result; }; -#define MESSAGE_CLASS_DECLARATION \ +#define MESSAGE_CLASS_DECLARATION(Name) \ public: \ const char* getIdentifier() const; \ bool matchIdentifier(const char* identifier) const; \ static bool match(Message* message); \ + static Name* cast(Message* message) { return match(message) ? (Name*)message : NULL; } \ protected: \ static const char* m_identifier; \ private: diff --git a/plugins/channel/nfm/nfmdemod.cpp b/plugins/channel/nfm/nfmdemod.cpp index 359bf09..a3c6d69 100644 --- a/plugins/channel/nfm/nfmdemod.cpp +++ b/plugins/channel/nfm/nfmdemod.cpp @@ -17,9 +17,11 @@ #include #include +#include #include "nfmdemod.h" #include "audio/audiooutput.h" #include "dsp/dspcommands.h" +#include "dsp/pidcontroller.h" MESSAGE_CLASS_DEFINITION(NFMDemod::MsgConfigureNFMDemod, Message) @@ -27,20 +29,17 @@ NFMDemod::NFMDemod(AudioFifo* audioFifo, SampleSink* sampleSink) : m_sampleSink(sampleSink), m_audioFifo(audioFifo) { - m_rfBandwidth = 12500; - m_volume = 2.0; - m_squelchLevel = pow(10.0, -40.0 / 20.0); - m_sampleRate = 500000; - m_frequency = 0; - m_squelchLevel *= m_squelchLevel; + m_config.m_inputSampleRate = 500000; + m_config.m_inputFrequencyOffset = 0; + m_config.m_rfBandwidth = 12500; + m_config.m_afBandwidth = 3000; + m_config.m_squelch = -40.0; + m_config.m_volume = 2.0; + m_config.m_audioSampleRate = 44100; - m_nco.setFreq(m_frequency, m_sampleRate); - m_interpolator.create(16, m_sampleRate, 12500); - m_sampleDistanceRemain = (Real)m_sampleRate / 44100.0; + apply(); - m_lowpass.create(21, 44100, 3000); - - m_audioBuffer.resize(256); + m_audioBuffer.resize(16384); m_audioBufferFill = 0; m_movingAverage.resize(16, 0); @@ -56,52 +55,145 @@ void NFMDemod::configure(MessageQueue* messageQueue, Real rfBandwidth, Real afBa cmd->submit(messageQueue, this); } +float arctan2(Real y, Real x) +{ + Real coeff_1 = M_PI / 4; + Real coeff_2 = 3 * coeff_1; + Real abs_y = fabs(y) + 1e-10; // kludge to prevent 0/0 condition + Real angle; + if( x>= 0) { + Real r = (x - abs_y) / (x + abs_y); + angle = coeff_1 - coeff_1 * r; + } else { + Real r = (x + abs_y) / (abs_y - x); + angle = coeff_2 - coeff_1 * r; + } + if(y < 0) + return(-angle); + else return(angle); +} + +Real angleDist(Real a, Real b) +{ + Real dist = b - a; + + while(dist <= M_PI) + dist += 2 * M_PI; + while(dist >= M_PI) + dist -= 2 * M_PI; + + return dist; +} + void NFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool firstOfBurst) { Complex ci; bool consumed; - for(SampleVector::const_iterator it = begin; it < end; ++it) { + if(m_audioFifo->size() <= 0) + return; + + if(m_audioFifo->getSampleRate() != m_config.m_audioSampleRate) { + m_config.m_audioSampleRate = m_audioFifo->getSampleRate(); + apply(); + } + + if(firstOfBurst) { + if(m_audioFifo->isStopped()) { + if(m_audioFifo->fill() >= m_audioFifo->size() / 8.0) { + m_audioFifo->setStopped(false); + m_interpolatorRegulation = 0.9999; + } + } else { + Real err = (Real)m_audioFifo->fill() / ((Real)m_audioFifo->size() / 9.0); + if(err < 0.999) + err = 0.999; + else if(err > 1.001) + err = 1.001; + + m_interpolatorRegulation = (m_interpolatorRegulation * 400.0 + err * 1.0) / 401.0; + + if(m_interpolatorRegulation < 0.99) + m_interpolatorRegulation = 0.99; + else if(m_interpolatorRegulation > 1.01) + m_interpolatorRegulation = 1.01; + } + m_interpolatorDistance = m_interpolatorRegulation * (Real)m_running.m_inputSampleRate / (Real)m_running.m_audioSampleRate; + } + + for(SampleVector::const_iterator it = begin; it != end; ++it) { Complex c(it->real() / 32768.0, it->imag() / 32768.0); c *= m_nco.nextIQ(); consumed = false; - if(m_interpolator.interpolate(&m_sampleDistanceRemain, c, &consumed, &ci)) { - m_sampleBuffer.push_back(Sample(ci.real() * 32768.0, ci.imag() * 32768.0)); + while(!consumed) { + if(m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &consumed, &ci)) { + m_sampleBuffer.push_back(Sample(ci.real() * 32767.0, ci.imag() * 32767.0)); - m_movingAverage.feed(ci.real() * ci.real() + ci.imag() * ci.imag()); + m_movingAverage.feed(ci.real() * ci.real() + ci.imag() * ci.imag()); + if(m_movingAverage.average() >= m_squelchLevel) + m_squelchState = m_running.m_audioSampleRate/ 20; - if(m_movingAverage.average() >= m_squelchLevel) - m_squelchState = m_sampleRate / 50; + qint16 sample; - if(m_squelchState > 0) { - m_squelchState--; - Complex d = ci * conj(m_lastSample); - m_lastSample = ci; - Real demod = atan2(d.imag(), d.real()) / M_PI; - demod = m_lowpass.filter(demod); - demod *= m_volume; - qint16 sample = demod * 32767; + m_squelchState = 999; + if(m_squelchState > 0) { + m_squelchState--; + /* + Real argument = arg(ci); + Real demod = argument - m_lastArgument; + m_lastArgument = argument; + */ + + Complex d = conj(m_lastSample) * ci; + m_lastSample = ci; + Real demod = atan2(d.imag(), d.real()); + //Real demod = arctan2(d.imag(), d.real()); +/* + Real argument1 = arg(ci);//atan2(ci.imag(), ci.real()); + Real argument2 = m_lastSample.real(); + Real demod = angleDist(argument2, argument1); + m_lastSample = Complex(argument1, 0); +*/ + + + demod /= M_PI; + + demod = m_lowpass.filter(demod); + + if(demod < -1) + demod = -1; + else if(demod > 1) + demod = 1; + + demod *= m_running.m_volume; + sample = demod * 32700; + + } else { + sample = 0; + qDebug("!!!"); + } m_audioBuffer[m_audioBufferFill].l = sample; m_audioBuffer[m_audioBufferFill].r = sample; ++m_audioBufferFill; if(m_audioBufferFill >= m_audioBuffer.size()) { uint res = m_audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 1); - /* if(res != m_audioBufferFill) - qDebug("lost %u samples", m_audioBufferFill - res); - */ + qDebug("lost %u audio samples", m_audioBufferFill - res); m_audioBufferFill = 0; } - } - m_sampleDistanceRemain += (Real)m_sampleRate / 44100.0; + m_interpolatorDistanceRemain += m_interpolatorDistance; + } } } - if(m_audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 0) != m_audioBufferFill) - ;//qDebug("lost samples"); - m_audioBufferFill = 0; + if(m_audioBufferFill > 0) { + uint res = m_audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 1); + if(res != m_audioBufferFill) + qDebug("lost %u samples", m_audioBufferFill - res); + m_audioBufferFill = 0; + } if(m_sampleSink != NULL) m_sampleSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), firstOfBurst); @@ -111,6 +203,12 @@ void NFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iter void NFMDemod::start() { m_squelchState = 0; + m_audioFifo->clear(); + m_audioFifo->setStopped(true); + m_interpolatorRegulation = 0.9999; + m_interpolatorDistance = 1.0; + m_interpolatorDistanceRemain = 0.0; + m_lastSample = 0; } void NFMDemod::stop() @@ -120,24 +218,20 @@ void NFMDemod::stop() bool NFMDemod::handleMessage(Message* cmd) { if(DSPSignalNotification::match(cmd)) { - DSPSignalNotification* signal = (DSPSignalNotification*)cmd; - qDebug("%d samples/sec, %lld Hz offset", signal->getSampleRate(), signal->getFrequencyOffset()); - m_sampleRate = signal->getSampleRate(); - m_nco.setFreq(-signal->getFrequencyOffset(), m_sampleRate); - m_interpolator.create(16, m_sampleRate, m_rfBandwidth / 2.1); - m_sampleDistanceRemain = m_sampleRate / 44100.0; - m_squelchState = 0; + DSPSignalNotification* signal = DSPSignalNotification::cast(cmd); + + m_config.m_inputSampleRate = signal->getSampleRate(); + m_config.m_inputFrequencyOffset = signal->getFrequencyOffset(); + apply(); cmd->completed(); return true; } else if(MsgConfigureNFMDemod::match(cmd)) { - MsgConfigureNFMDemod* cfg = (MsgConfigureNFMDemod*)cmd; - m_rfBandwidth = cfg->getRFBandwidth(); - m_interpolator.create(16, m_sampleRate, m_rfBandwidth / 2.1); - m_lowpass.create(21, 44100, cfg->getAFBandwidth()); - m_squelchLevel = pow(10.0, cfg->getSquelch() / 20.0); - m_squelchLevel *= m_squelchLevel; - m_volume = cfg->getVolume(); - cmd->completed(); + MsgConfigureNFMDemod* cfg = MsgConfigureNFMDemod::cast(cmd); + m_config.m_rfBandwidth = cfg->getRFBandwidth(); + m_config.m_afBandwidth = cfg->getAFBandwidth(); + m_config.m_volume = cfg->getVolume(); + m_config.m_squelch = cfg->getSquelch(); + apply(); return true; } else { if(m_sampleSink != NULL) @@ -145,3 +239,36 @@ bool NFMDemod::handleMessage(Message* cmd) else return false; } } + +void NFMDemod::apply() +{ + + if((m_config.m_inputFrequencyOffset != m_running.m_inputFrequencyOffset) || + (m_config.m_inputSampleRate != m_running.m_inputSampleRate)) { + m_nco.setFreq(-m_config.m_inputFrequencyOffset, m_config.m_inputSampleRate); + } + + if((m_config.m_inputSampleRate != m_running.m_inputSampleRate) || + (m_config.m_rfBandwidth != m_running.m_rfBandwidth)) { + m_interpolator.create(16, m_config.m_inputSampleRate, m_config.m_rfBandwidth / 2.2); + m_interpolatorDistanceRemain = 0; + m_interpolatorDistance = 1.0; + } + + if((m_config.m_afBandwidth != m_running.m_afBandwidth) || + (m_config.m_audioSampleRate != m_running.m_audioSampleRate)) { + m_lowpass.create(21, m_config.m_audioSampleRate, m_config.m_afBandwidth); + } + + if(m_config.m_squelch != m_running.m_squelch) { + m_squelchLevel = pow(10.0, m_config.m_squelch / 20.0); + m_squelchLevel *= m_squelchLevel; + } + + m_running.m_inputSampleRate = m_config.m_inputSampleRate; + m_running.m_inputFrequencyOffset = m_config.m_inputFrequencyOffset; + m_running.m_rfBandwidth = m_config.m_rfBandwidth; + m_running.m_squelch = m_config.m_squelch; + m_running.m_volume = m_config.m_volume; + m_running.m_audioSampleRate = m_config.m_audioSampleRate; +} diff --git a/plugins/channel/nfm/nfmdemod.h b/plugins/channel/nfm/nfmdemod.h index a312abb..c6b327b 100644 --- a/plugins/channel/nfm/nfmdemod.h +++ b/plugins/channel/nfm/nfmdemod.h @@ -43,7 +43,7 @@ public: private: class MsgConfigureNFMDemod : public Message { - MESSAGE_CLASS_DECLARATION + MESSAGE_CLASS_DECLARATION(MsgConfigureNFMDemod) public: Real getRFBandwidth() const { return m_rfBandwidth; } @@ -77,19 +77,45 @@ private: }; typedef std::vector AudioVector; - Real m_rfBandwidth; - Real m_volume; - Real m_squelchLevel; - int m_sampleRate; - int m_frequency; + enum RateState { + RSInitialFill, + RSRunning + }; + + struct Config { + int m_inputSampleRate; + qint64 m_inputFrequencyOffset; + Real m_rfBandwidth; + Real m_afBandwidth; + Real m_squelch; + Real m_volume; + quint32 m_audioSampleRate; + + Config() : + m_inputSampleRate(-1), + m_inputFrequencyOffset(0), + m_rfBandwidth(-1), + m_afBandwidth(-1), + m_squelch(0), + m_volume(0), + m_audioSampleRate(0) + { } + }; + + Config m_config; + Config m_running; NCO m_nco; + Real m_interpolatorRegulation; Interpolator m_interpolator; - Real m_sampleDistanceRemain; + Real m_interpolatorDistance; + Real m_interpolatorDistanceRemain; Lowpass m_lowpass; + Real m_squelchLevel; int m_squelchState; + Real m_lastArgument; Complex m_lastSample; MovingAverage m_movingAverage; @@ -99,6 +125,8 @@ private: SampleSink* m_sampleSink; SampleVector m_sampleBuffer; + + void apply(); }; #endif // INCLUDE_NFMDEMOD_H diff --git a/plugins/channel/nfm/nfmdemodgui.cpp b/plugins/channel/nfm/nfmdemodgui.cpp index 53f2de0..ce24a01 100644 --- a/plugins/channel/nfm/nfmdemodgui.cpp +++ b/plugins/channel/nfm/nfmdemodgui.cpp @@ -155,7 +155,7 @@ NFMDemodGUI::NFMDemodGUI(PluginAPI* pluginAPI, QWidget* parent) : connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(menuDoubleClickEvent()), this, SLOT(onMenuDoubleClicked())); - m_audioFifo = new AudioFifo(4, 44100 / 4); + m_audioFifo = new AudioFifo(4, 48000); m_spectrumVis = new SpectrumVis(ui->glSpectrum); m_nfmDemod = new NFMDemod(m_audioFifo, m_spectrumVis); m_channelizer = new Channelizer(m_nfmDemod); @@ -164,7 +164,7 @@ NFMDemodGUI::NFMDemodGUI(PluginAPI* pluginAPI, QWidget* parent) : m_pluginAPI->addSampleSink(m_threadedSampleSink); ui->glSpectrum->setCenterFrequency(0); - ui->glSpectrum->setSampleRate(44100); + ui->glSpectrum->setSampleRate(48000); ui->glSpectrum->setDisplayWaterfall(true); ui->glSpectrum->setDisplayMaxHold(true); m_spectrumVis->configure(m_threadedSampleSink->getMessageQueue(), 64, 10, FFTWindow::BlackmanHarris); @@ -200,7 +200,7 @@ void NFMDemodGUI::applySettings() { setTitleColor(m_channelMarker->getColor()); m_channelizer->configure(m_threadedSampleSink->getMessageQueue(), - 44100, + 48000, m_channelMarker->getCenterFrequency()); m_nfmDemod->configure(m_threadedSampleSink->getMessageQueue(), m_rfBW[ui->rfBW->value()], diff --git a/plugins/channel/tcpsrc/tcpsrc.h b/plugins/channel/tcpsrc/tcpsrc.h index 012a4df..1b926c2 100644 --- a/plugins/channel/tcpsrc/tcpsrc.h +++ b/plugins/channel/tcpsrc/tcpsrc.h @@ -32,7 +32,7 @@ public: bool handleMessage(Message* cmd); class MsgTCPSrcConnection : public Message { - MESSAGE_CLASS_DECLARATION + MESSAGE_CLASS_DECLARATION(MsgTCPSrcConnection) public: bool getConnect() const { return m_connect; } @@ -62,7 +62,7 @@ public: protected: class MsgTCPSrcConfigure : public Message { - MESSAGE_CLASS_DECLARATION + MESSAGE_CLASS_DECLARATION(MsgTCPSrcConfigure) public: SampleFormat getSampleFormat() const { return m_sampleFormat; } @@ -90,7 +90,7 @@ protected: { } }; class MsgTCPSrcSpectrum : public Message { - MESSAGE_CLASS_DECLARATION + MESSAGE_CLASS_DECLARATION(MsgTCPSrcSpectrum) public: bool getEnabled() const { return m_enabled; } diff --git a/plugins/samplesource/gnuradio/gnuradioinput.cpp b/plugins/samplesource/gnuradio/gnuradioinput.cpp index f3999e1..a2759c0 100644 --- a/plugins/samplesource/gnuradio/gnuradioinput.cpp +++ b/plugins/samplesource/gnuradio/gnuradioinput.cpp @@ -281,7 +281,8 @@ bool GNURadioInput::applySettings(const GeneralSettings& generalSettings, if((m_settings.m_sampRate != settings.m_sampRate) || force) { m_settings.m_sampRate = settings.m_sampRate; - radio->set_sample_rate( m_settings.m_sampRate ); + if(m_settings.m_sampRate != 0) + radio->set_sample_rate( m_settings.m_sampRate ); } if((m_settings.m_antenna != settings.m_antenna) || force) { diff --git a/plugins/samplesource/gnuradio/gnuradioinput.h b/plugins/samplesource/gnuradio/gnuradioinput.h index de2da43..4d4abf9 100644 --- a/plugins/samplesource/gnuradio/gnuradioinput.h +++ b/plugins/samplesource/gnuradio/gnuradioinput.h @@ -46,7 +46,7 @@ public: }; class MsgConfigureGNURadio : public Message { - MESSAGE_CLASS_DECLARATION + MESSAGE_CLASS_DECLARATION(MsgConfigureGNURadio) public: const GeneralSettings& getGeneralSettings() const { return m_generalSettings; } @@ -69,7 +69,7 @@ public: }; class MsgReportGNURadio : public Message { - MESSAGE_CLASS_DECLARATION + MESSAGE_CLASS_DECLARATION(MsgReportGNURadio) public: const std::vector< std::pair< QString, std::vector > >& getNamedGains() const { return m_namedGains; } diff --git a/plugins/samplesource/osmosdr/osmosdrinput.cpp b/plugins/samplesource/osmosdr/osmosdrinput.cpp index 989eb09..1f10d85 100644 --- a/plugins/samplesource/osmosdr/osmosdrinput.cpp +++ b/plugins/samplesource/osmosdr/osmosdrinput.cpp @@ -122,108 +122,7 @@ bool OsmoSDRInput::Settings::deserialize(const QByteArray& data) return false; } } -#if 0 -OsmoSDRInput::Settings::Settings() : -{ -} -QString OsmoSDRInput::Settings::serialize() const -{ - return QString("osmosdr:a:%1:%2:%3:%4:%5:%6:%7:%8:%9:%10:%11:%12:%13:%14:%15:%16:%17:%18") - .arg(centerFrequency) - .arg(swapIQ ? 1 : 0) - .arg(decimation) - .arg(lnaGain) - .arg(mixerGain) - .arg(mixerEnhancement) - .arg(if1gain) - .arg(if2gain) - .arg(if3gain) - .arg(if4gain) - .arg(if5gain) - .arg(if6gain) - .arg(opAmpI1) - .arg(opAmpI2) - .arg(opAmpQ1) - .arg(opAmpQ2) - .arg(dcOfsI) - .arg(dcOfsQ); -} - -bool OsmoSDRInput::Settings::deserialize(const QString& settings) -{ - QStringList list = settings.split(":"); - if(list.size() < 2) - return false; - if(list[0] != "osmosdr") - return false; - - if(list[1] == "a") { - bool ok; - if(list.size() != 20) - return false; - centerFrequency = list[2].toLongLong(&ok); - if(!ok) - return false; - swapIQ = (list[3].toInt(&ok) != 0) ? true : false; - if(!ok) - return false; - decimation = list[4].toInt(&ok); - if(!ok) - return false; - lnaGain = list[5].toInt(&ok); - if(!ok) - return false; - mixerGain = list[6].toInt(&ok); - if(!ok) - return false; - mixerEnhancement = list[7].toInt(&ok); - if(!ok) - return false; - if1gain = list[8].toInt(&ok); - if(!ok) - return false; - if2gain = list[9].toInt(&ok); - if(!ok) - return false; - if3gain = list[10].toInt(&ok); - if(!ok) - return false; - if4gain = list[11].toInt(&ok); - if(!ok) - return false; - if5gain = list[12].toInt(&ok); - if(!ok) - return false; - if6gain = list[13].toInt(&ok); - if(!ok) - return false; - opAmpI1 = list[14].toInt(&ok); - if(!ok) - return false; - opAmpI2 = list[15].toInt(&ok); - if(!ok) - return false; - opAmpQ1 = list[16].toInt(&ok); - if(!ok) - return false; - opAmpQ2 = list[17].toInt(&ok); - if(!ok) - return false; - dcOfsI = list[18].toInt(&ok); - if(!ok) - return false; - dcOfsQ = list[19].toInt(&ok); - if(!ok) - return false; - return true; - } else { - return false; - } -} - -MessageRegistrator OsmoSDRInput::MsgConfigureSourceOsmoSDR::ID("MsgConfigureSourceOsmoSDR"); -#endif OsmoSDRInput::OsmoSDRInput(MessageQueue* msgQueueToGUI) : SampleSource(msgQueueToGUI), m_settings(), @@ -250,7 +149,7 @@ bool OsmoSDRInput::startInput(int device) char serial[256]; int res; - if(!m_sampleFifo.setSize(524288)) { + if(!m_sampleFifo.setSize(128 * 1024)) { qCritical("Could not allocate SampleFifo"); return false; } @@ -341,14 +240,6 @@ bool OsmoSDRInput::handleMessage(Message* message) return false; } - /* - if(cmd->sourceType() != DSPCmdConfigureSourceOsmoSDR::SourceType) - return false; - if(!applySettings(((DSPCmdConfigureSourceOsmoSDR*)cmd)->getSettings(), false)) - qDebug("OsmoSDR config error"); - cmd->completed(); - return true; - */ return false; } diff --git a/plugins/samplesource/osmosdr/osmosdrinput.h b/plugins/samplesource/osmosdr/osmosdrinput.h index dd832ca..ec1428b 100644 --- a/plugins/samplesource/osmosdr/osmosdrinput.h +++ b/plugins/samplesource/osmosdr/osmosdrinput.h @@ -52,7 +52,7 @@ public: }; class MsgConfigureOsmoSDR : public Message { - MESSAGE_CLASS_DECLARATION + MESSAGE_CLASS_DECLARATION(MsgConfigureOsmoSDR) public: const GeneralSettings& getGeneralSettings() const { return m_generalSettings; } diff --git a/plugins/samplesource/osmosdr/osmosdrthread.cpp b/plugins/samplesource/osmosdr/osmosdrthread.cpp index 25fcfdb..c1342d3 100644 --- a/plugins/samplesource/osmosdr/osmosdrthread.cpp +++ b/plugins/samplesource/osmosdr/osmosdrthread.cpp @@ -52,13 +52,13 @@ void OsmoSDRThread::run() { int res; + m_sampleFifo->readCommit(m_sampleFifo->fill()); + m_running = true; m_startWaiter.wakeAll(); - //m_f = fopen("/tmp/samples.bin", "wb"); - while(m_running) { - if((res = osmosdr_read_async(m_dev, &OsmoSDRThread::callbackHelper, this, 16, sizeof(Sample) * 8192 * 2)) < 0) { + if((res = osmosdr_read_async(m_dev, &OsmoSDRThread::callbackHelper, this, 16, sizeof(Sample) * 16384)) < 0) { qCritical("OsmoSDRThread: async error: %s", strerror(errno)); break; } @@ -90,18 +90,6 @@ void OsmoSDRThread::checkData(const quint8* buf, qint32 len) void OsmoSDRThread::callback(const quint8* buf, qint32 len) { - /* - qDebug("%d", len); -*/ - /* - for(int i = 0; i < len / 2; i += 2) { - ((qint16*)buf)[i] = sin((float)(cntr) * 1024* 2.0 * M_PI / 65536.0) * 32000.0; - ((qint16*)buf)[i + 1] = -cos((float)(cntr++) * 1024*2.0 * M_PI / 65536.0) * 32000.0; - } - */ - - //m_sampleFifo->write((SampleVector::const_iterator)((Sample*)buf), (SampleVector::const_iterator)((Sample*)(buf + len))); - //fwrite(buf, 1, len, m_f); //checkData(buf, len); m_sampleFifo->write(buf, len); diff --git a/plugins/samplesource/rtlsdr/rtlsdrinput.h b/plugins/samplesource/rtlsdr/rtlsdrinput.h index d171af1..ec9f804 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrinput.h +++ b/plugins/samplesource/rtlsdr/rtlsdrinput.h @@ -37,7 +37,7 @@ public: }; class MsgConfigureRTLSDR : public Message { - MESSAGE_CLASS_DECLARATION + MESSAGE_CLASS_DECLARATION(MsgConfigureRTLSDR) public: const GeneralSettings& getGeneralSettings() const { return m_generalSettings; } @@ -60,7 +60,7 @@ public: }; class MsgReportRTLSDR : public Message { - MESSAGE_CLASS_DECLARATION + MESSAGE_CLASS_DECLARATION(MsgReportRTLSDR) public: const std::vector& getGains() const { return m_gains; } diff --git a/sdrbase/audio/audiodeviceinfo.cpp b/sdrbase/audio/audiodeviceinfo.cpp deleted file mode 100644 index e422878..0000000 --- a/sdrbase/audio/audiodeviceinfo.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany // -// written by Christian Daniel // -// // -// This program is free software; you can redistribute it and/or modify // -// it under the terms of the GNU General Public License as published by // -// the Free Software Foundation as version 3 of the License, or // -// // -// This program is distributed in the hope that it will be useful, // -// but WITHOUT ANY WARRANTY; without even the implied warranty of // -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // -// GNU General Public License V3 for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with this program. If not, see . // -/////////////////////////////////////////////////////////////////////////////////// - -#include "audio/audiodeviceinfo.h" - -AudioDeviceInfo::AudioDeviceInfo() -{ -} - -int AudioDeviceInfo::match(const QString& api, const QString device) const -{ - // nothing found - fall back to default - return 0; -} diff --git a/sdrbase/audio/audiofifo.cpp b/sdrbase/audio/audiofifo.cpp index dd475ca..41d19df 100644 --- a/sdrbase/audio/audiofifo.cpp +++ b/sdrbase/audio/audiofifo.cpp @@ -22,7 +22,9 @@ #define MIN(x, y) ((x) < (y) ? (x) : (y)) AudioFifo::AudioFifo() : - m_fifo(NULL) + m_fifo(NULL), + m_sampleRate(0), + m_stopped(true) { m_size = 0; m_fill = 0; diff --git a/sdrbase/audio/audiooutput.cpp b/sdrbase/audio/audiooutput.cpp index 455241a..b5e1792 100644 --- a/sdrbase/audio/audiooutput.cpp +++ b/sdrbase/audio/audiooutput.cpp @@ -24,6 +24,9 @@ AudioOutput::AudioOutput() : m_mutex(), + m_error(), + m_deviceName(), + m_rate(0), m_audioOutput(NULL), m_audioFifos() { @@ -39,17 +42,38 @@ AudioOutput::~AudioOutput() m_audioFifos.clear(); } -bool AudioOutput::start(int device, int rate) +void AudioOutput::configure(const QString& deviceName, uint rate) { QMutexLocker mutexLocker(&m_mutex); - Q_UNUSED(device); - Q_UNUSED(rate); + m_deviceName = deviceName; + m_rate = rate; +} + +bool AudioOutput::start() +{ + QMutexLocker mutexLocker(&m_mutex); QAudioFormat format; QAudioDeviceInfo devInfo(QAudioDeviceInfo::defaultOutputDevice()); - format.setSampleRate(41000); + if(!m_deviceName.isEmpty()) { + QList devices = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput); + bool found = false; + for(int i = 0; i < devices.count(); ++i) { + if(devices[i].deviceName() == m_deviceName) { + devInfo = devices[i]; + found = true; + break; + } + } + if(!found) { + m_error = tr("Audio output device %1 not available").arg(m_deviceName); + return false; + } + } + + format.setSampleRate(m_rate); format.setChannelCount(2); format.setSampleSize(16); format.setCodec("audio/pcm"); @@ -60,26 +84,33 @@ bool AudioOutput::start(int device, int rate) qDebug("default format not supported - try to use nearest"); format = devInfo.nearestFormat(format); } + qDebug("Audio output %s using samplerate %d", qPrintable(devInfo.deviceName()), format.sampleRate()); if(format.sampleSize() != 16) { - qDebug("audio device doesn't support 16 bit samples (%s)", qPrintable(devInfo.defaultOutputDevice().deviceName())); + m_error = tr("Audio output %1 doesn't support 16 bit samples").arg(devInfo.deviceName()); return false; } - m_audioOutput = new QAudioOutput(format); + m_audioOutput = new QAudioOutput(devInfo, format); QIODevice::open(QIODevice::ReadOnly); + //m_audioOutput->setBufferSize(3 * 4096); m_audioOutput->start(this); - return true; + for(AudioFifos::iterator it = m_audioFifos.begin(); it != m_audioFifos.end(); ++it) + (*it)->setSampleRate(m_audioOutput->format().sampleRate()); + return true; } void AudioOutput::stop() { QMutexLocker mutexLocker(&m_mutex); + for(AudioFifos::iterator it = m_audioFifos.begin(); it != m_audioFifos.end(); ++it) + (*it)->setSampleRate(0); + if(m_audioOutput != NULL) { m_audioOutput->stop(); delete m_audioOutput; @@ -92,6 +123,10 @@ void AudioOutput::addFifo(AudioFifo* audioFifo) { QMutexLocker mutexLocker(&m_mutex); + if(m_audioOutput == NULL) + audioFifo->setSampleRate(0); + else audioFifo->setSampleRate(m_audioOutput->format().sampleRate()); + m_audioFifos.push_back(audioFifo); } @@ -99,9 +134,19 @@ void AudioOutput::removeFifo(AudioFifo* audioFifo) { QMutexLocker mutexLocker(&m_mutex); + audioFifo->setSampleRate(0); m_audioFifos.remove(audioFifo); } +quint32 AudioOutput::getCurrentRate() +{ + QMutexLocker mutexLocker(&m_mutex); + + if(m_audioOutput == NULL) + return 0; + else return m_audioOutput->format().sampleRate(); +} + bool AudioOutput::open(OpenMode mode) { Q_UNUSED(mode); @@ -110,9 +155,6 @@ bool AudioOutput::open(OpenMode mode) qint64 AudioOutput::readData(char* data, qint64 maxLen) { - if(maxLen == 0) - return 0; - QMutexLocker mutexLocker(&m_mutex); maxLen -= maxLen % 4; @@ -120,13 +162,18 @@ qint64 AudioOutput::readData(char* data, qint64 maxLen) if((int)m_mixBuffer.size() < framesPerBuffer * 2) { m_mixBuffer.resize(framesPerBuffer * 2); // allocate 2 qint32 per frame (stereo) - if(m_mixBuffer.size() != framesPerBuffer * 2) + if(m_mixBuffer.size() != framesPerBuffer * 2) { + qDebug("KAPUTT"); return 0; + } } memset(&m_mixBuffer[0], 0x00, 2 * framesPerBuffer * sizeof(m_mixBuffer[0])); // start with silence // sum up a block from all fifos for(AudioFifos::iterator it = m_audioFifos.begin(); it != m_audioFifos.end(); ++it) { + if((*it)->isStopped()) + continue; + // use outputBuffer as temp - yes, one memcpy could be saved uint samples = (*it)->read((quint8*)data, framesPerBuffer, 1); const qint16* src = (const qint16*)data; @@ -141,7 +188,7 @@ qint64 AudioOutput::readData(char* data, qint64 maxLen) } } - // convert to int16 + // convert to int16 and do saturation std::vector::const_iterator src = m_mixBuffer.begin(); qint16* dst = (qint16*)data; for(int i = 0; i < framesPerBuffer; ++i) { diff --git a/sdrbase/dsp/channelizer.cpp b/sdrbase/dsp/channelizer.cpp index 2028a0f..41300e1 100644 --- a/sdrbase/dsp/channelizer.cpp +++ b/sdrbase/dsp/channelizer.cpp @@ -27,13 +27,15 @@ void Channelizer::feed(SampleVector::const_iterator begin, SampleVector::const_i { for(SampleVector::const_iterator sample = begin; sample != end; ++sample) { Sample s(*sample); + bool haveSample = true; FilterStages::iterator stage = m_filterStages.begin(); while(stage != m_filterStages.end()) { - if(!(*stage)->work(&s)) + haveSample = (*stage)->work(&s); + if(!haveSample) break; ++stage; } - if(stage == m_filterStages.end()) + if((stage == m_filterStages.end()) && haveSample) m_sampleBuffer.push_back(s); } @@ -58,7 +60,7 @@ void Channelizer::stop() bool Channelizer::handleMessage(Message* cmd) { if(DSPSignalNotification::match(cmd)) { - DSPSignalNotification* signal = (DSPSignalNotification*)cmd; + DSPSignalNotification* signal = DSPSignalNotification::cast(cmd); m_inputSampleRate = signal->getSampleRate(); applyConfiguration(); cmd->completed(); @@ -69,7 +71,7 @@ bool Channelizer::handleMessage(Message* cmd) } return true; } else if(DSPConfigureChannelizer::match(cmd)) { - DSPConfigureChannelizer* chan = (DSPConfigureChannelizer*)cmd; + DSPConfigureChannelizer* chan = DSPConfigureChannelizer::cast(cmd); m_requestedOutputSampleRate = chan->getSampleRate(); m_requestedCenterFrequency = chan->getCenterFrequency(); applyConfiguration(); @@ -172,157 +174,3 @@ void Channelizer::freeFilterChain() delete *it; m_filterStages.clear(); } - -#if 0 - -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany // -// written by Christian Daniel // -// // -// This program is free software; you can redistribute it and/or modify // -// it under the terms of the GNU General Public License as published by // -// the Free Software Foundation as version 3 of the License, or // -// // -// This program is distributed in the hope that it will be useful, // -// but WITHOUT ANY WARRANTY; without even the implied warranty of // -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // -// GNU General Public License V3 for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with this program. If not, see . // -/////////////////////////////////////////////////////////////////////////////////// - -#include -#include -#include "channelizer.h" -#include "hardware/audiooutput.h" - -Channelizer::Channelizer() -{ -#if 0 - m_spectrum.configure(128 , 25, FFTWindow::Bartlett); - m_buffer.resize(2048); - m_bufferFill = 0; - m_nco.setFreq(-100000, 500000); - m_interpolator.create(51, 32, 32 * 500000, 150000 / 2); - m_distance = 500000.0 / 176400.0; - - m_interpolator2.create(19, 8, 8 * 176400, 15000 / 2); - m_distance2 = 4; - - m_audioFifo.setSize(4, 44100 / 2 * 4); - m_audioOutput = new AudioOutput; - m_audioOutput->start(0, 44100, &m_audioFifo); - m_resampler = 1.0; - - m_resamplerCtrl.setup(0.00001, 0, -0.00001); -#endif -} - -Channelizer::~Channelizer() -{ -#if 0 - m_audioOutput->stop(); - delete m_audioOutput; -#endif -} - -#if 0 -void Channelizer::setGLSpectrum(GLSpectrum* glSpectrum) -{ - m_spectrum.setGLSpectrum(glSpectrum); -} -#endif - -size_t Channelizer::workUnitSize() -{ -#if 0 - return m_buffer.size(); -#endif - return 0; -} - -size_t Channelizer::work(SampleVector::const_iterator begin, SampleVector::const_iterator end) -{ -#if 0 - int buffered = m_audioOutput->bufferedSamples(); - - if(m_audioFifo.fill() < (m_audioFifo.size() / 6)) { - while(m_audioFifo.fill() < (m_audioFifo.size() / 2)) { - quint32 d = 0; - m_audioFifo.write((quint8*)&d, 4); - } - qDebug("underflow - fill %d (vs %d)", m_audioFifo.fill(), m_audioFifo.size() / 4 / 2); - } - - buffered = m_audioOutput->bufferedSamples(); - int fill = m_audioFifo.fill() / 4 + buffered; - float err = (float)fill / ((m_audioFifo.size() / 4) / 2); - - float ctrl = m_resamplerCtrl.feed(err); - //float resamplerRate = (ctrl / 1.0); - float resamplerRate = err; - - if(resamplerRate < 0.9999) - resamplerRate = 0.9999; - else if(resamplerRate > 1.0001) - resamplerRate = 1.0001; - m_resampler = m_resampler * 0.99 + resamplerRate * 0.01; - //m_resampler = resamplerRate; - - if(m_resampler < 0.995) - m_resampler = 0.995; - else if(m_resampler > 1.005) - m_resampler = 1.005; - - //qDebug("%lld %5d %f %f %f", QDateTime::currentMSecsSinceEpoch(), fill, ctrl, m_resampler, err); - - struct AudioSample { - qint16 l; - qint16 r; - }; - - size_t count = end - begin; - Complex ci; - bool consumed; - bool consumed2; - - for(SampleVector::const_iterator it = begin; it < end; it++) { - Complex c(it->real() / 32768.0, it->imag() / 32768.0); - c *= m_nco.nextIQ(); - - consumed = false; - if(m_interpolator.interpolate(&m_distance, c, &consumed, &ci)) { - - Complex d = ci * conj(m_lastSample); - m_lastSample = ci; - //Complex demod(atan2(d.imag(), d.real()) * 0.5, 0); - Real demod = atan2(d.imag(), d.real()) / M_PI; - - consumed2 = false; - c = Complex(demod, 0); - while(!consumed2) { - if(m_interpolator2.interpolate(&m_distance2, c, &consumed2, &ci)) { - m_buffer[m_bufferFill++] = Sample(ci.real() * 32767.0, 0.0); - - AudioSample s; - s.l = ci.real() * 32767.0; - s.r = s.l; - m_audioFifo.write((quint8*)&s, 4, 1); - - if(m_bufferFill >= m_buffer.size()) { - m_spectrum.feed(m_buffer.begin(), m_buffer.end()); - m_bufferFill = 0; - } - m_distance2 += 4.0 * m_resampler; - } - } - m_distance += 500000 / 176400.0; - } - } - - return count; -#endif -} - -#endif diff --git a/sdrbase/dsp/dspcommands.cpp b/sdrbase/dsp/dspcommands.cpp index 21bdddc..9e19da2 100644 --- a/sdrbase/dsp/dspcommands.cpp +++ b/sdrbase/dsp/dspcommands.cpp @@ -13,6 +13,7 @@ MESSAGE_CLASS_DEFINITION(DSPAddAudioSource, Message) MESSAGE_CLASS_DEFINITION(DSPRemoveAudioSource, Message) MESSAGE_CLASS_DEFINITION(DSPConfigureSpectrumVis, Message) MESSAGE_CLASS_DEFINITION(DSPConfigureCorrection, Message) +MESSAGE_CLASS_DEFINITION(DSPConfigureAudioOutput, Message) MESSAGE_CLASS_DEFINITION(DSPEngineReport, Message) MESSAGE_CLASS_DEFINITION(DSPConfigureScopeVis, Message) MESSAGE_CLASS_DEFINITION(DSPSignalNotification, Message) diff --git a/sdrbase/dsp/dspengine.cpp b/sdrbase/dsp/dspengine.cpp index 369403f..63b3f58 100644 --- a/sdrbase/dsp/dspengine.cpp +++ b/sdrbase/dsp/dspengine.cpp @@ -109,6 +109,12 @@ void DSPEngine::configureCorrections(bool dcOffsetCorrection, bool iqImbalanceCo cmd->submit(&m_messageQueue); } +void DSPEngine::configureAudioOutput(const QString& audioOutput, quint32 audioOutputRate) +{ + Message* cmd = DSPConfigureAudioOutput::create(audioOutput, audioOutputRate); + cmd->submit(&m_messageQueue); +} + QString DSPEngine::errorMessage() { DSPGetErrorMessage cmd; @@ -174,7 +180,6 @@ void DSPEngine::imbalance(SampleVector::iterator begin, SampleVector::iterator e qMin = it->imag(); else if(it->imag() > qMax) qMax = it->imag(); - } else { iMin = it->real(); iMax = it->real(); @@ -202,7 +207,7 @@ void DSPEngine::work() size_t samplesDone = 0; bool firstOfBurst = true; - while((sampleFifo->fill() > 0) && (m_messageQueue.countPending() == 0) && (samplesDone < m_sampleRate)) { + while((sampleFifo->fill() > 0) && (m_messageQueue.countPending() == 0) && (samplesDone < m_sampleRate / 2)) { SampleVector::iterator part1begin; SampleVector::iterator part1end; SampleVector::iterator part2begin; @@ -218,7 +223,7 @@ void DSPEngine::work() if(m_iqImbalanceCorrection) imbalance(part1begin, part1end); // feed data to handlers - for(SampleSinks::const_iterator it = m_sampleSinks.begin(); it != m_sampleSinks.end(); it++) + for(SampleSinks::const_iterator it = m_sampleSinks.begin(); it != m_sampleSinks.end(); ++it) (*it)->feed(part1begin, part1end, firstOfBurst); firstOfBurst = false; } @@ -230,7 +235,7 @@ void DSPEngine::work() if(m_iqImbalanceCorrection) imbalance(part2begin, part2end); // feed data to handlers - for(SampleSinks::const_iterator it = m_sampleSinks.begin(); it != m_sampleSinks.end(); it++) + for(SampleSinks::const_iterator it = m_sampleSinks.begin(); it != m_sampleSinks.end(); ++it) (*it)->feed(part2begin, part2end, firstOfBurst); firstOfBurst = false; } @@ -239,69 +244,6 @@ void DSPEngine::work() sampleFifo->readCommit(count); samplesDone += count; } - -#if 0 - size_t wus; - size_t maxWorkUnitSize = 0; - size_t samplesDone = 0; - - wus = m_spectrum.workUnitSize(); - if(wus > maxWorkUnitSize) - maxWorkUnitSize = wus; - for(SampleSinks::const_iterator it = m_sampleSinks.begin(); it != m_sampleSinks.end(); it++) { - wus = (*it)->workUnitSize(); - if(wus > maxWorkUnitSize) - maxWorkUnitSize = wus; - } - - while((m_sampleFifo.fill() > maxWorkUnitSize) && (m_commandQueue.countPending() == 0) && (samplesDone < m_sampleRate)) { - SampleVector::iterator part1begin; - SampleVector::iterator part1end; - SampleVector::iterator part2begin; - SampleVector::iterator part2end; - - size_t count = m_sampleFifo.readBegin(m_sampleFifo.fill(), &part1begin, &part1end, &part2begin, &part2end); - - // first part of FIFO data - if(part1begin != part1end) { - // correct stuff - if(m_settings.dcOffsetCorrection()) - dcOffset(part1begin, part1end); - if(m_settings.iqImbalanceCorrection()) - imbalance(part1begin, part1end); - // feed data to handlers - m_spectrum.feed(part1begin, part1end); - for(SampleSinks::const_iterator it = m_sampleSinks.begin(); it != m_sampleSinks.end(); it++) - (*it)->feed(part1begin, part1end); - } - // second part of FIFO data (used when block wraps around) - if(part2begin != part2end) { - // correct stuff - if(m_settings.dcOffsetCorrection()) - dcOffset(part2begin, part2end); - if(m_settings.iqImbalanceCorrection()) - imbalance(part2begin, part2end); - // feed data to handlers - m_spectrum.feed(part2begin, part2end); - for(SampleSinks::const_iterator it = m_sampleSinks.begin(); it != m_sampleSinks.end(); it++) - (*it)->feed(part1begin, part1end); - } - - // adjust FIFO pointers - m_sampleFifo.readCommit(count); - samplesDone += count; - } - - // check if the center frequency has changed (has to be responsive) - if(m_settings.isModifiedCenterFreq()) - m_sampleSource->setCenterFrequency(m_settings.centerFreq()); - // check if decimation has changed (needed to be done here, because to high a sample rate can clog the switch) - if(m_settings.isModifiedDecimation()) { - m_sampleSource->setDecimation(m_settings.decimation()); - m_sampleRate = 4000000 / (1 << m_settings.decimation()); - qDebug("New rate: %d", m_sampleRate); - } -#endif } DSPEngine::State DSPEngine::gotoIdle() @@ -357,7 +299,11 @@ DSPEngine::State DSPEngine::gotoRunning() return gotoError("Could not start sample source"); m_deviceDescription = m_sampleSource->getDeviceDescription(); - m_audioOutput.start(0, 44100); + if(!m_audioOutput.start()) { + m_sampleSource->stopInput(); + return gotoError(m_audioOutput.getError()); + } + for(SampleSinks::const_iterator it = m_sampleSinks.begin(); it != m_sampleSinks.end(); it++) (*it)->start(); m_sampleRate = 0; // make sure, report is sent @@ -447,7 +393,7 @@ void DSPEngine::handleMessages() { Message* message; while((message = m_messageQueue.accept()) != NULL) { - qDebug("Message: %s", message->getIdentifier()); + //qDebug("Message: %s", message->getIdentifier()); if(DSPPing::match(message)) { message->completed(m_state); @@ -465,16 +411,16 @@ void DSPEngine::handleMessages() m_state = gotoIdle(); message->completed(m_state); } else if(DSPGetDeviceDescription::match(message)) { - ((DSPGetDeviceDescription*)message)->setDeviceDescription(m_deviceDescription); + DSPGetDeviceDescription::cast(message)->setDeviceDescription(m_deviceDescription); message->completed(); } else if(DSPGetErrorMessage::match(message)) { ((DSPGetErrorMessage*)message)->setErrorMessage(m_errorMessage); message->completed(); } else if(DSPSetSource::match(message)) { - handleSetSource(((DSPSetSource*)message)->getSampleSource()); + handleSetSource(DSPSetSource::cast(message)->getSampleSource()); message->completed(); } else if(DSPAddSink::match(message)) { - SampleSink* sink = ((DSPAddSink*)message)->getSampleSink(); + SampleSink* sink = DSPAddSink::cast(message)->getSampleSink(); if(m_state == StRunning) { DSPSignalNotification* signal = DSPSignalNotification::create(m_sampleRate, 0); signal->submit(&m_messageQueue, sink); @@ -489,13 +435,16 @@ void DSPEngine::handleMessages() m_sampleSinks.remove(sink); message->completed(); } else if(DSPAddAudioSource::match(message)) { - m_audioOutput.addFifo(((DSPAddAudioSource*)message)->getAudioFifo()); + m_audioOutput.addFifo(DSPAddAudioSource::cast(message)->getAudioFifo()); message->completed(); } else if(DSPRemoveAudioSource::match(message)) { m_audioOutput.removeFifo(((DSPAddAudioSource*)message)->getAudioFifo()); message->completed(); + } else if(DSPConfigureAudioOutput::match(message)) { + DSPConfigureAudioOutput* conf = DSPConfigureAudioOutput::cast(message); + m_audioOutput.configure(conf->getAudioOutputDevice(), conf->getAudioOutputRate()); } else if(DSPConfigureCorrection::match(message)) { - DSPConfigureCorrection* conf = (DSPConfigureCorrection*)message; + DSPConfigureCorrection* conf = DSPConfigureCorrection::cast(message); m_iqImbalanceCorrection = conf->getIQImbalanceCorrection(); if(m_dcOffsetCorrection != conf->getDCOffsetCorrection()) { m_dcOffsetCorrection = conf->getDCOffsetCorrection(); diff --git a/sdrbase/dsp/fftwengine.cpp b/sdrbase/dsp/fftwengine.cpp index 76b189e..f5062fb 100644 --- a/sdrbase/dsp/fftwengine.cpp +++ b/sdrbase/dsp/fftwengine.cpp @@ -21,6 +21,7 @@ void FFTWEngine::configure(int n, bool inverse) } } + m_globalPlanMutex.lock(); m_currentPlan = new Plan; m_currentPlan->n = n; m_currentPlan->inverse = inverse; @@ -28,7 +29,6 @@ void FFTWEngine::configure(int n, bool inverse) m_currentPlan->out = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * n); QTime t; t.start(); - m_globalPlanMutex.lock(); m_currentPlan->plan = fftwf_plan_dft_1d(n, m_currentPlan->in, m_currentPlan->out, inverse ? FFTW_BACKWARD : FFTW_FORWARD, FFTW_PATIENT); m_globalPlanMutex.unlock(); qDebug("FFT: creating FFTW plan (n=%d,%s) took %dms", n, inverse ? "inverse" : "forward", t.elapsed()); diff --git a/sdrbase/dsp/nco.cpp b/sdrbase/dsp/nco.cpp index 1143cf8..1982750 100644 --- a/sdrbase/dsp/nco.cpp +++ b/sdrbase/dsp/nco.cpp @@ -30,7 +30,7 @@ void NCO::initTable() return; for(int i = 0; i < TableSize; i++) - m_table[i] = cos((2.0 * M_PI * i) / TableSize); + m_table[i] = cos((2.0 * M_PI * (Real)i) / ((Real)TableSize)); m_tableInitialized = true; } @@ -43,8 +43,15 @@ NCO::NCO() void NCO::setFreq(Real freq, Real sampleRate) { - m_phaseIncrement = (freq * TableSize) / sampleRate; - qDebug("NCO phase inc %d", m_phaseIncrement); + if(sampleRate > 0) { + m_phaseIncrement = (freq * TableSize) / sampleRate; + if(m_phaseIncrement != 0) + qDebug("NCO phase inc %d (period %f)", m_phaseIncrement, (Real)TableSize / (Real)m_phaseIncrement); + else qDebug("NCO phase inc %d (period oo)", m_phaseIncrement); + } else { + qDebug("cannot calculate NCO phase increment since samplerate is 0"); + m_phaseIncrement = 1; + } } float NCO::next() @@ -66,5 +73,7 @@ Complex NCO::nextIQ() while(m_phase < 0) m_phase += TableSize; - return Complex(m_table[m_phase], -m_table[(m_phase + TableSize / 4) % TableSize]); + int idxQuad = (m_phase + (TableSize / 4) + (TableSize / 2)) % TableSize; + + return Complex(m_table[m_phase], m_table[idxQuad]); } diff --git a/sdrbase/dsp/pidcontroller.cpp b/sdrbase/dsp/pidcontroller.cpp index 8b015f6..a5d5356 100644 --- a/sdrbase/dsp/pidcontroller.cpp +++ b/sdrbase/dsp/pidcontroller.cpp @@ -1,4 +1,4 @@ -#include "pidcontroller.h" +#include "dsp/pidcontroller.h" PIDController::PIDController() : m_p(0.0), diff --git a/sdrbase/dsp/samplefifo.cpp b/sdrbase/dsp/samplefifo.cpp index caa4969..e9d29b8 100644 --- a/sdrbase/dsp/samplefifo.cpp +++ b/sdrbase/dsp/samplefifo.cpp @@ -64,17 +64,21 @@ bool SampleFifo::setSize(int size) { create(size); - return m_data.size() == (uint)size; + return m_size == (uint)size; } uint SampleFifo::write(const quint8* data, uint count) { + return write(SampleVector::const_iterator((Sample*)data), SampleVector::const_iterator((Sample*)(data + count))); +#if 0 QMutexLocker mutexLocker(&m_mutex); uint total; uint remaining; uint len; const Sample* begin = (const Sample*)data; - count /= 4; + count /= sizeof(Sample); + + //qDebug("write pre count %d %u", count, m_fill); total = MIN(count, m_size - m_fill); if(total < count) { @@ -96,6 +100,7 @@ uint SampleFifo::write(const quint8* data, uint count) remaining = total; while(remaining > 0) { len = MIN(remaining, m_size - m_tail); + //qDebug("write remaining %u, len %u", remaining, len); std::copy(begin, begin + len, m_data.begin() + m_tail); m_tail += len; m_tail %= m_size; @@ -104,10 +109,13 @@ uint SampleFifo::write(const quint8* data, uint count) remaining -= len; } + //qDebug("write post count %d %u [%u;%u]", count, m_fill, m_head, m_tail); + if(m_fill > 0) emit dataReady(); return total; +#endif } uint SampleFifo::write(SampleVector::const_iterator begin, SampleVector::const_iterator end) @@ -139,8 +147,7 @@ uint SampleFifo::write(SampleVector::const_iterator begin, SampleVector::const_i while(remaining > 0) { len = MIN(remaining, m_size - m_tail); std::copy(begin, begin + len, m_data.begin() + m_tail); - m_tail += len; - m_tail %= m_size; + m_tail = (m_tail + len) % m_size; m_fill += len; begin += len; remaining -= len; @@ -151,7 +158,7 @@ uint SampleFifo::write(SampleVector::const_iterator begin, SampleVector::const_i return total; } - +/* uint SampleFifo::read(SampleVector::iterator begin, SampleVector::iterator end) { QMutexLocker mutexLocker(&m_mutex); @@ -177,13 +184,14 @@ uint SampleFifo::read(SampleVector::iterator begin, SampleVector::iterator end) return total; } - +*/ uint SampleFifo::readBegin(uint count, SampleVector::iterator* part1Begin, SampleVector::iterator* part1End, SampleVector::iterator* part2Begin, SampleVector::iterator* part2End) { QMutexLocker mutexLocker(&m_mutex); uint total; + uint done = 0; uint remaining; uint len; uint head = m_head; @@ -196,10 +204,10 @@ uint SampleFifo::readBegin(uint count, if(remaining > 0) { len = MIN(remaining, m_size - head); *part1Begin = m_data.begin() + head; - *part1End = m_data.begin() + head + len; - head += len; - head %= m_size; + *part1End = *part1Begin + len; + head = (head + len) % m_size; remaining -= len; + done += len; } else { *part1Begin = m_data.end(); *part1End = m_data.end(); @@ -207,13 +215,14 @@ uint SampleFifo::readBegin(uint count, if(remaining > 0) { len = MIN(remaining, m_size - head); *part2Begin = m_data.begin() + head; - *part2End = m_data.begin() + head + len; + *part2End = *part2Begin + len; + done += len; } else { *part2Begin = m_data.end(); *part2End = m_data.end(); } - return total; + return done; } uint SampleFifo::readCommit(uint count) diff --git a/sdrbase/dsp/spectrumvis.cpp b/sdrbase/dsp/spectrumvis.cpp index 01c7dbe..627a093 100644 --- a/sdrbase/dsp/spectrumvis.cpp +++ b/sdrbase/dsp/spectrumvis.cpp @@ -3,7 +3,7 @@ #include "dsp/dspcommands.h" #include "util/messagequeue.h" -#define MAX_FFT_SIZE 4096 +#define MAX_FFT_SIZE 8192 #ifdef _WIN32 double log2f(double n) diff --git a/sdrbase/dsp/threadedsamplesink.cpp b/sdrbase/dsp/threadedsamplesink.cpp index ad157b9..313ed24 100644 --- a/sdrbase/dsp/threadedsamplesink.cpp +++ b/sdrbase/dsp/threadedsamplesink.cpp @@ -15,7 +15,7 @@ ThreadedSampleSink::ThreadedSampleSink(SampleSink* sampleSink) : m_sampleFifo.moveToThread(m_thread); connect(&m_sampleFifo, SIGNAL(dataReady()), this, SLOT(handleData())); - m_sampleFifo.setSize(262144); + m_sampleFifo.setSize(128 * 1024); sampleSink->moveToThread(m_thread); } @@ -55,8 +55,11 @@ bool ThreadedSampleSink::handleMessage(Message* cmd) void ThreadedSampleSink::handleData() { bool firstOfBurst = true; + QTime time; - while((m_sampleFifo.fill() > 0) && (m_messageQueue.countPending() == 0)) { + time.start(); + + while((m_sampleFifo.fill() > 0) && (m_messageQueue.countPending() == 0) && (time.elapsed() < 250)) { SampleVector::iterator part1begin; SampleVector::iterator part1end; SampleVector::iterator part2begin; @@ -64,19 +67,19 @@ void ThreadedSampleSink::handleData() size_t count = m_sampleFifo.readBegin(m_sampleFifo.fill(), &part1begin, &part1end, &part2begin, &part2end); - // first part of FIFO data - if(part1begin != part1end) { - // handle data - if(m_sampleSink != NULL) + if(m_sampleSink != NULL) { + // first part of FIFO data + if(part1begin != part1end) { + // handle data m_sampleSink->feed(part1begin, part1end, firstOfBurst); - firstOfBurst = false; - } - // second part of FIFO data (used when block wraps around) - if(part2begin != part2end) { - // handle data - if(m_sampleSink != NULL) - m_sampleSink->feed(part1begin, part1end, firstOfBurst); - firstOfBurst = false; + firstOfBurst = false; + } + // second part of FIFO data (used when block wraps around) + if(part2begin != part2end) { + // handle data + m_sampleSink->feed(part2begin, part2end, firstOfBurst); + firstOfBurst = false; + } } // adjust FIFO pointers @@ -88,7 +91,7 @@ void ThreadedSampleSink::handleMessages() { Message* message; while((message = m_messageQueue.accept()) != NULL) { - qDebug("CMD: %s", message->getIdentifier()); + //qDebug("CMD: %s", message->getIdentifier()); if(m_sampleSink != NULL) { if(!m_sampleSink->handleMessage(message)) message->completed(); diff --git a/sdrbase/gui/glspectrumgui.cpp b/sdrbase/gui/glspectrumgui.cpp index 1fbaaf2..6231e8d 100644 --- a/sdrbase/gui/glspectrumgui.cpp +++ b/sdrbase/gui/glspectrumgui.cpp @@ -112,7 +112,7 @@ bool GLSpectrumGUI::deserialize(const QByteArray& data) void GLSpectrumGUI::applySettings() { ui->fftWindow->setCurrentIndex(m_fftWindow); - for(int i = 0; i < 6; i++) { + for(int i = 0; i < 7; i++) { if(m_fftSize == (1 << (i + 7))) { ui->fftSize->setCurrentIndex(i); break; diff --git a/sdrbase/gui/glspectrumgui.ui b/sdrbase/gui/glspectrumgui.ui index 2a9babe..a738255 100644 --- a/sdrbase/gui/glspectrumgui.ui +++ b/sdrbase/gui/glspectrumgui.ui @@ -14,7 +14,16 @@ Oscilloscope - + + 2 + + + 2 + + + 2 + + 2 @@ -162,6 +171,11 @@ 4096 + + + 8192 + + diff --git a/sdrbase/gui/preferencesdialog.cpp b/sdrbase/gui/preferencesdialog.cpp index 077ef45..5394150 100644 --- a/sdrbase/gui/preferencesdialog.cpp +++ b/sdrbase/gui/preferencesdialog.cpp @@ -1,47 +1,61 @@ #include +#include #include "gui/preferencesdialog.h" #include "ui_preferencesdialog.h" -#include "audio/audiodeviceinfo.h" +#include "settings/preferences.h" -PreferencesDialog::PreferencesDialog(AudioDeviceInfo* audioDeviceInfo, QWidget* parent) : +PreferencesDialog::PreferencesDialog(Preferences* preferences, QWidget* parent) : QDialog(parent), ui(new Ui::PreferencesDialog), - m_audioDeviceInfo(audioDeviceInfo) + m_preferences(preferences) { ui->setupUi(this); - const AudioDeviceInfo::Devices& devices = audioDeviceInfo->getDevices(); - - QTreeWidgetItem* api; QStringList sl; - sl.append(tr("Default (use first suitable device)")); - api = new QTreeWidgetItem(ui->audioTree, sl, ATDefault); - api->setFirstColumnSpanned(true); - for(AudioDeviceInfo::Devices::const_iterator it = devices.begin(); it != devices.end(); ++it) { - int apiIndex; + bool found; + + QList devices = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput); + + sl.clear(); + sl.append(tr("Default output device (use first suitable)")); + QTreeWidgetItem* dev = new QTreeWidgetItem(ui->audioTree, sl); + dev->setFirstColumnSpanned(true); + dev->setData(0, Qt::UserRole, ""); + for(QList::ConstIterator it = devices.begin(); it != devices.end(); ++it) { sl.clear(); - - for(apiIndex = 0; apiIndex < ui->audioTree->topLevelItemCount(); ++apiIndex) { - if(ui->audioTree->topLevelItem(apiIndex)->text(0) == it->api) - break; - } - if(apiIndex >= ui->audioTree->topLevelItemCount()) { - sl.append(it->api); - api = new QTreeWidgetItem(ui->audioTree, sl, ATInterface); - api->setExpanded(true); - api->setFirstColumnSpanned(true); - sl.clear(); - } else { - api = ui->audioTree->topLevelItem(apiIndex); - } - - sl.append(it->name); - new QTreeWidgetItem(api, sl, ATDevice); + sl.append(it->deviceName()); + QTreeWidgetItem* dev = new QTreeWidgetItem(ui->audioTree, sl); + dev->setFirstColumnSpanned(true); + dev->setData(0, Qt::UserRole, it->deviceName()); } - if(ui->audioTree->currentItem() == NULL) + found = false; + for(int i = 0; i < ui->audioTree->topLevelItemCount(); ++i) { + if(ui->audioTree->topLevelItem(i)->data(0, Qt::UserRole).toString() == m_preferences->getAudioOutput()) { + ui->audioTree->setCurrentItem(ui->audioTree->topLevelItem(i)); + found = true; + break; + } + } + if(!found) ui->audioTree->setCurrentItem(ui->audioTree->topLevelItem(0)); - ui->tabWidget->setCurrentIndex(0); + ui->audioRate->addItem(tr("48000 Hz"), 48000); + ui->audioRate->addItem(tr("44100 Hz"), 44100); + ui->audioRate->addItem(tr("24000 Hz"), 24000); + ui->audioRate->addItem(tr("22050 Hz"), 22050); + found = false; + for(int i = 0; i < ui->audioRate->count(); ++i) { + if(ui->audioRate->itemData(i).toInt() == m_preferences->getAudioOutputRate()) { + ui->audioRate->setCurrentIndex(i); + found = true; + break; + } + } + if(!found) + ui->audioRate->setCurrentIndex(1); + + ui->stackedWidget->setCurrentIndex(0); + ui->configTree->setCurrentItem(ui->configTree->topLevelItem(0)); } PreferencesDialog::~PreferencesDialog() @@ -51,5 +65,10 @@ PreferencesDialog::~PreferencesDialog() void PreferencesDialog::accept() { + if(ui->audioTree->currentItem() != NULL) + m_preferences->setAudioOutput(ui->audioTree->currentItem()->data(0, Qt::UserRole).toString()); + else m_preferences->setAudioOutput(QString()); + m_preferences->setAudioOutputRate(ui->audioRate->itemData(ui->audioRate->currentIndex()).toInt()); + QDialog::accept(); } diff --git a/sdrbase/gui/preferencesdialog.ui b/sdrbase/gui/preferencesdialog.ui index 0545931..f98a149 100644 --- a/sdrbase/gui/preferencesdialog.ui +++ b/sdrbase/gui/preferencesdialog.ui @@ -6,42 +6,55 @@ 0 0 - 400 - 300 + 454 + 435 Dialog - - - - - 0 + + + + + false - - - Receiver Hardware - - - - - - - 1 - - - - - - - - + + false + + + + Section + + + + Audio Output - - + + + ItemIsSelectable|ItemIsEnabled + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + + + false + Device @@ -49,11 +62,49 @@ + + + + + + Sample rate + + + + + + + + 0 + 0 + + + + QComboBox::AdjustToContents + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + - + Qt::Horizontal @@ -67,9 +118,6 @@ buttonBox - tabWidget - treeWidget - audioTree diff --git a/sdrbase/gui/scopewindow.cpp b/sdrbase/gui/scopewindow.cpp index 2f6200f..486242a 100644 --- a/sdrbase/gui/scopewindow.cpp +++ b/sdrbase/gui/scopewindow.cpp @@ -19,14 +19,13 @@ #include "ui_scopewindow.h" #include "util/simpleserializer.h" -ScopeWindow::ScopeWindow(DSPEngine* dspEngine, QWidget* parent) : +ScopeWindow::ScopeWindow(QWidget* parent) : QWidget(parent), ui(new Ui::ScopeWindow), m_sampleRate(0), m_timeBase(1) { ui->setupUi(this); - ui->scope->setDSPEngine(dspEngine); } ScopeWindow::~ScopeWindow() @@ -34,6 +33,11 @@ ScopeWindow::~ScopeWindow() delete ui; } +void ScopeWindow::setDSPEngine(DSPEngine* dspEngine) +{ + ui->scope->setDSPEngine(dspEngine); +} + void ScopeWindow::setSampleRate(int sampleRate) { m_sampleRate = sampleRate; diff --git a/sdrbase/mainwindow.cpp b/sdrbase/mainwindow.cpp index 9cbfb0f..904b3c9 100644 --- a/sdrbase/mainwindow.cpp +++ b/sdrbase/mainwindow.cpp @@ -20,7 +20,6 @@ #include #include "mainwindow.h" #include "ui_mainwindow.h" -#include "audio/audiodeviceinfo.h" #include "gui/indicator.h" #include "gui/presetitem.h" #include "gui/scopewindow.h" @@ -39,7 +38,6 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow), - m_audioDeviceInfo(new AudioDeviceInfo), m_messageQueue(new MessageQueue), m_settings(), m_dspEngine(new DSPEngine(m_messageQueue)), @@ -196,6 +194,8 @@ void MainWindow::loadSettings(const Preset* preset) m_pluginManager->loadSettings(preset); + m_dspEngine->configureAudioOutput(m_settings.getPreferences()->getAudioOutput(), m_settings.getPreferences()->getAudioOutputRate()); + // has to be last step restoreState(preset->getLayout()); } @@ -306,12 +306,12 @@ void MainWindow::handleMessages() { Message* message; while((message = m_messageQueue->accept()) != NULL) { - qDebug("Message: %s", message->getIdentifier()); + //qDebug("Message: %s", message->getIdentifier()); if(DSPEngineReport::match(message)) { - DSPEngineReport* rep = (DSPEngineReport*)message; + DSPEngineReport* rep = DSPEngineReport::cast(message); m_sampleRate = rep->getSampleRate(); m_centerFrequency = rep->getCenterFrequency(); - qDebug("SampleRate:%d, CenterFrequency:%llu", rep->getSampleRate(), rep->getCenterFrequency()); + //qDebug("SampleRate:%d, CenterFrequency:%llu", rep->getSampleRate(), rep->getCenterFrequency()); updateCenterFreqDisplay(); updateSampleRate(); message->completed(); @@ -332,6 +332,7 @@ void MainWindow::updateStatus() m_engineRunning->setColor(Qt::gray); m_engineError->setColor(Qt::gray); statusBar()->clearMessage(); + updateEnables(false); break; case DSPEngine::StIdle: @@ -339,6 +340,7 @@ void MainWindow::updateStatus() m_engineRunning->setColor(Qt::gray); m_engineError->setColor(Qt::gray); statusBar()->clearMessage(); + updateEnables(false); if(m_startOsmoSDRUpdateAfterStop) on_actionOsmoSDR_Firmware_Upgrade_triggered(); break; @@ -348,6 +350,7 @@ void MainWindow::updateStatus() m_engineRunning->setColor(Qt::green); m_engineError->setColor(Qt::gray); statusBar()->showMessage(tr("Sampling from %1").arg(m_dspEngine->deviceDescription())); + updateEnables(true); break; case DSPEngine::StError: @@ -355,6 +358,8 @@ void MainWindow::updateStatus() m_engineRunning->setColor(Qt::gray); m_engineError->setColor(Qt::red); statusBar()->showMessage(tr("Error: %1").arg(m_dspEngine->errorMessage())); + QMessageBox::critical(this, tr("Engine Error"), tr("%1").arg(m_dspEngine->errorMessage()), QMessageBox::Ok); + updateEnables(false); if(m_startOsmoSDRUpdateAfterStop) on_actionOsmoSDR_Firmware_Upgrade_triggered(); break; @@ -363,6 +368,15 @@ void MainWindow::updateStatus() } } +void MainWindow::updateEnables(bool running) +{ + if(running) { + ui->action_Preferences->setEnabled(false); + } else { + ui->action_Preferences->setEnabled(true); + } +} + void MainWindow::scopeWindowDestroyed() { ui->action_Oscilloscope->setChecked(false); @@ -490,7 +504,9 @@ void MainWindow::on_action_Oscilloscope_triggered() QDockWidget* dock = new QDockWidget(tr("Signalscope"), this); dock->setObjectName(QString::fromUtf8("scopeDock")); - m_scopeWindow = new ScopeWindow(m_dspEngine); + + m_scopeWindow = new ScopeWindow(); + m_scopeWindow->setDSPEngine(m_dspEngine); connect(m_scopeWindow, SIGNAL(destroyed()), this, SLOT(scopeWindowDestroyed())); m_scopeWindow->setSampleRate(m_sampleRate); dock->setWidget(m_scopeWindow); @@ -509,9 +525,11 @@ void MainWindow::on_action_Loaded_Plugins_triggered() void MainWindow::on_action_Preferences_triggered() { - PreferencesDialog preferencesDialog(m_audioDeviceInfo, this); + PreferencesDialog preferencesDialog(m_settings.getPreferences(), this); - preferencesDialog.exec(); + if(preferencesDialog.exec() == QDialog::Accepted) { + m_dspEngine->configureAudioOutput(m_settings.getPreferences()->getAudioOutput(), m_settings.getPreferences()->getAudioOutputRate()); + } } void MainWindow::on_sampleSource_currentIndexChanged(int index) diff --git a/sdrbase/plugin/pluginmanager.cpp b/sdrbase/plugin/pluginmanager.cpp index 3338c01..325fb8b 100644 --- a/sdrbase/plugin/pluginmanager.cpp +++ b/sdrbase/plugin/pluginmanager.cpp @@ -72,7 +72,7 @@ void PluginManager::registerSampleSource(const QString& sourceName, PluginInterf void PluginManager::loadSettings(const Preset* preset) { - qDebug("-------- [%s | %s] --------", qPrintable(preset->getGroup()), qPrintable(preset->getDescription())); + //qDebug("-------- [%s | %s] --------", qPrintable(preset->getGroup()), qPrintable(preset->getDescription())); // copy currently open channels and clear list ChannelInstanceRegistrations openChannels = m_channelInstanceRegistrations; @@ -83,7 +83,7 @@ void PluginManager::loadSettings(const Preset* preset) ChannelInstanceRegistration reg; // if we have one instance available already, use it for(int i = 0; i < openChannels.count(); i++) { - qDebug("compare [%s] vs [%s]", qPrintable(openChannels[i].m_channelName), qPrintable(channelConfig.m_channel)); + //qDebug("compare [%s] vs [%s]", qPrintable(openChannels[i].m_channelName), qPrintable(channelConfig.m_channel)); if(openChannels[i].m_channelName == channelConfig.m_channel) { qDebug("channel [%s] found", qPrintable(openChannels[i].m_channelName)); reg = openChannels.takeAt(i); @@ -235,7 +235,7 @@ int PluginManager::selectSampleSource(const QString& source) qDebug("finding sample source [%s]", qPrintable(source)); for(int i = 0; i < m_sampleSourceDevices.count(); i++) { - qDebug("*** %s vs %s", qPrintable(m_sampleSourceDevices[i].m_sourceName), qPrintable(source)); + //qDebug("*** %s vs %s", qPrintable(m_sampleSourceDevices[i].m_sourceName), qPrintable(source)); if(m_sampleSourceDevices[i].m_sourceName == source) { index = i; break; diff --git a/sdrbase/settings/preferences.cpp b/sdrbase/settings/preferences.cpp index c649cda..e1391fb 100644 --- a/sdrbase/settings/preferences.cpp +++ b/sdrbase/settings/preferences.cpp @@ -8,19 +8,15 @@ Preferences::Preferences() void Preferences::resetToDefaults() { - m_sourceType.clear(); - m_sourceDevice.clear(); - m_audioType.clear(); - m_audioDevice.clear(); + m_audioOutput.clear(); + m_audioOutputRate = 44100; } QByteArray Preferences::serialize() const { SimpleSerializer s(1); - s.writeString(1, m_sourceType); - s.writeString(2, m_sourceDevice); - s.writeString(3, m_audioType); - s.writeString(4, m_audioDevice); + s.writeString(1, m_audioOutput); + s.writeU32(2, m_audioOutputRate); return s.final(); } @@ -34,10 +30,10 @@ bool Preferences::deserialize(const QByteArray& data) } if(d.getVersion() == 1) { - d.readString(1, &m_sourceType); - d.readString(2, &m_sourceDevice); - d.readString(3, &m_audioType); - d.readString(4, &m_audioDevice); + d.readString(1, &m_audioOutput); + quint32 tmp; + d.readU32(2, &tmp, 44100); + m_audioOutputRate = tmp; return true; } else { resetToDefaults(); diff --git a/sdrbase/util/message.cpp b/sdrbase/util/message.cpp index 8431ca7..1ddf7af 100644 --- a/sdrbase/util/message.cpp +++ b/sdrbase/util/message.cpp @@ -29,6 +29,8 @@ const char* Message::getIdentifier() const bool Message::matchIdentifier(const char* identifier) const { + // Warning: this compares POINTERS - make sure, that each message has a unique name + // otherwise the linker might "optimize" stuff return m_identifier == identifier; }