major rewrite

This commit is contained in:
Christian Daniel 2014-08-15 11:37:26 +02:00
parent 7820104666
commit 5864e48bd8
46 changed files with 647 additions and 740 deletions

View File

@ -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

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_AUDIODEVICEINFO_H
#define INCLUDE_AUDIODEVICEINFO_H
#include <QStringList>
#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<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

View File

@ -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);
};

View File

@ -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<AudioFifo*> AudioFifos;

View File

@ -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

View File

@ -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; }

View File

@ -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; }

View File

@ -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);

View File

@ -3,7 +3,7 @@
#include <QDialog>
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();

View File

@ -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();

View File

@ -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();

View File

@ -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

View File

@ -22,6 +22,8 @@ public:
Preset* getCurrent() { return &m_current; }
Preferences* getPreferences() { return &m_preferences; }
protected:
Preferences m_preferences;
Preset m_current;

View File

@ -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:

View File

@ -17,9 +17,11 @@
#include <QTime>
#include <stdio.h>
#include <complex.h>
#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;
}

View File

@ -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<AudioSample> 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<Real> 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

View File

@ -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()],

View File

@ -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; }

View File

@ -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) {

View File

@ -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<double> > >& getNamedGains() const { return m_namedGains; }

View File

@ -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;
}

View File

@ -52,7 +52,7 @@ public:
};
class MsgConfigureOsmoSDR : public Message {
MESSAGE_CLASS_DECLARATION
MESSAGE_CLASS_DECLARATION(MsgConfigureOsmoSDR)
public:
const GeneralSettings& getGeneralSettings() const { return m_generalSettings; }

View File

@ -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);

View File

@ -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<int>& getGains() const { return m_gains; }

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "audio/audiodeviceinfo.h"
AudioDeviceInfo::AudioDeviceInfo()
{
}
int AudioDeviceInfo::match(const QString& api, const QString device) const
{
// nothing found - fall back to default
return 0;
}

View File

@ -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;

View File

@ -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<QAudioDeviceInfo> 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);