396 lines
8.9 KiB
C++
396 lines
8.9 KiB
C++
/**
|
|
* @file qtdsp_dspEngine.cpp
|
|
* @brief QtDSP DSP engine class
|
|
* @author Hermann von Hasseln, DL3HVH
|
|
* @version 0.1
|
|
* @date 2012-04-07
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2007, 2008, 2009, 2010 Philip A Covington, N8VB
|
|
*
|
|
* adapted for QtDSP by (C) 2012 Hermann von Hasseln, DL3HVH
|
|
*
|
|
* The ProcessFrequencyShift method is adpated from cuteSDR by (C) Moe Wheatley, AE4JY.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Library General Public License version 2 as
|
|
* published by the Free Software Foundation
|
|
*
|
|
* 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 for more details
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this program; if not, write to the
|
|
* Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
#define LOG_DSP_ENGINE
|
|
|
|
// use: DSP_ENGINE_DEBUG << "debug message";
|
|
|
|
#include "qtdsp_dspEngine.h"
|
|
|
|
double myLog(double x, double base) {
|
|
|
|
return log(x) / log(base);
|
|
}
|
|
|
|
|
|
QDSPEngine::QDSPEngine(QObject *parent, int rx, int size)
|
|
: QObject(parent)
|
|
, set(Settings::instance())
|
|
, m_qtdspOn(false)
|
|
, m_rx(rx)
|
|
, m_size(size)
|
|
, m_samplerate(set->getSampleRate())
|
|
, m_fftMultiplier(1)
|
|
, m_volume(0.0f)
|
|
{
|
|
qRegisterMetaType<QVector<cpx> >();
|
|
qRegisterMetaType<CPX>();
|
|
|
|
fft = new QFFT(m_size); // m_size = 1024
|
|
filter = new QFilter(this, m_size, 2, 12);//8);
|
|
wpagc = new QWPAGC(this, m_size);
|
|
//spectrum = new PowerSpectrum(this, m_size*2);
|
|
|
|
/*QString str = "Initializing DSP engine for rx %1: 8k FFT ...please wait";
|
|
set->setSystemMessage(str.arg(m_rx), 0);
|
|
|
|
if (m_rx == 0) {
|
|
|
|
spectrum2 = new PowerSpectrum(this, m_size*4);
|
|
|
|
str = "Initializing DSP engine for rx %1: 16k FFT ...please wait";
|
|
set->setSystemMessage(str.arg(m_rx), 0);
|
|
spectrum4 = new PowerSpectrum(this, m_size*8);
|
|
|
|
str = "Initializing DSP engine for rx %1: 32k FFT ...please wait";
|
|
set->setSystemMessage(str.arg(m_rx), 0);
|
|
spectrum8 = new PowerSpectrum(this, m_size*16);
|
|
|
|
str = "Initializing DSP engine for rx %1: 64k FFT ...please wait";
|
|
set->setSystemMessage(str.arg(m_rx), 0);
|
|
spectrum16 = new PowerSpectrum(this, m_size*32);
|
|
}*/
|
|
|
|
int factor = 2;
|
|
|
|
while (factor <= 128) {
|
|
|
|
PowerSpectrum* spec = new PowerSpectrum(this, m_size*factor);
|
|
powerSpectraList << spec;
|
|
|
|
factor *= 2;
|
|
}
|
|
|
|
m_spectrumSize = m_size*4;
|
|
|
|
signalmeter = new SignalMeter(this, m_size);
|
|
demod = new Demodulation(this, m_size);
|
|
|
|
|
|
m_rxData = set->getReceiverDataList().at(rx);
|
|
m_agcMode = m_rxData.agcMode;
|
|
|
|
wpagc->setReceiver(m_rx);
|
|
|
|
InitCPX(tmp1CPX, m_size, 0.0f);
|
|
InitCPX(tmp2CPX, m_size, 0.0f);
|
|
|
|
osc1cpx.re = 1.0f;
|
|
osc1cpx.im = 0.0f;
|
|
|
|
m_NcoInc = 0.0;
|
|
m_NcoTime = 0.0;
|
|
m_NcoFreq = 0.0;
|
|
m_CWoffset = 0.0;
|
|
|
|
//DSP_ENGINE_DEBUG << "set NCO to " << m_rxData.vfoFrequency - m_rxData.ctrFrequency;
|
|
setNCOFrequency(m_rx, m_rxData.vfoFrequency - m_rxData.ctrFrequency);
|
|
|
|
DSP_ENGINE_DEBUG << "init DSPEngine with size: " << m_size;
|
|
SleeperThread::msleep(100);
|
|
|
|
setupConnections();
|
|
}
|
|
|
|
QDSPEngine::~QDSPEngine() {
|
|
|
|
tmp1CPX.clear();
|
|
tmp2CPX.clear();
|
|
|
|
//if (agc)
|
|
// delete agc;
|
|
|
|
if (fft)
|
|
delete fft;
|
|
|
|
if (filter)
|
|
delete filter;
|
|
|
|
if (wpagc)
|
|
delete wpagc;
|
|
|
|
//if (spectrum)
|
|
// delete spectrum;
|
|
|
|
if (!powerSpectraList.empty()) {
|
|
|
|
powerSpectraList.clear();
|
|
}
|
|
|
|
if (signalmeter)
|
|
delete signalmeter;
|
|
|
|
if (demod)
|
|
delete demod;
|
|
}
|
|
|
|
void QDSPEngine::setupConnections() {
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(ncoFrequencyChanged(int, long)),
|
|
this,
|
|
SLOT(setNCOFrequency(int, long)));
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(sampleSizeChanged(int, int)),
|
|
this,
|
|
SLOT(setSampleSize(int, int)));
|
|
|
|
CHECKED_CONNECT(
|
|
wpagc,
|
|
SIGNAL(agcMaximumGainChanged(qreal)),
|
|
this,
|
|
SLOT(setAGCMaximumGain(qreal)));
|
|
|
|
CHECKED_CONNECT(
|
|
wpagc,
|
|
SIGNAL(agcHangThresholdChanged(qreal)),
|
|
this,
|
|
SLOT(setAGCHangThreshold(qreal)));
|
|
|
|
// CHECKED_CONNECT(
|
|
// wpagc,
|
|
// SIGNAL(hangLeveldBLineChanged(qreal)),
|
|
// this,
|
|
// SLOT(setAGCHangLeveldBLine(qreal)));
|
|
//
|
|
// CHECKED_CONNECT(
|
|
// wpagc,
|
|
// SIGNAL(minimumVoltageChanged(QObject *, int, qreal)),
|
|
// this,
|
|
// SLOT(setAGCThresholdLine(QObject *, int, qreal)));
|
|
|
|
CHECKED_CONNECT(
|
|
wpagc,
|
|
SIGNAL(displayValues(QObject *, int, qreal, qreal)),
|
|
this,
|
|
SLOT(setAGCLineValues(QObject *, int, qreal, qreal)));
|
|
}
|
|
|
|
void QDSPEngine::processDSP(CPX &in, CPX &out, int size) {
|
|
|
|
m_mutex.lock();
|
|
|
|
/*switch (m_fftMultiplcator) {
|
|
|
|
case 1:
|
|
spectrum->ProcessSpectrum(in, size*2, 1);
|
|
break;
|
|
|
|
case 2:
|
|
spectrum2->ProcessSpectrum(in, size*4, 3);
|
|
break;
|
|
|
|
case 4:
|
|
spectrum4->ProcessSpectrum(in, size*8, 7);
|
|
break;
|
|
|
|
case 8:
|
|
spectrum8->ProcessSpectrum(in, size*16, 15);
|
|
break;
|
|
|
|
case 16:
|
|
spectrum16->ProcessSpectrum(in, size*32, 31);
|
|
break;
|
|
}*/
|
|
|
|
int idx = (int)(myLog(m_fftMultiplier, 2));
|
|
powerSpectraList.at(idx)->ProcessSpectrum(in, size * m_fftMultiplier * 2, m_fftMultiplier*2-1);
|
|
|
|
if (m_NcoFreq != 0)
|
|
ProcessFrequencyShift(in, in, size);
|
|
|
|
filter->ProcessFilter(in, tmp1CPX, size);
|
|
signalmeter->ProcessBlock(tmp1CPX, size);
|
|
wpagc->ProcessAGC(tmp1CPX, tmp2CPX, size);
|
|
demod->ProcessBlock(tmp2CPX, out, size);
|
|
|
|
//memcpy(out.data(), in.data(), size * sizeof(cpx));
|
|
//out = in;
|
|
|
|
for (int i = 0; i < size; i++) {
|
|
|
|
out[i] = ScaleCPX(out.at(i), m_volume);
|
|
}
|
|
m_mutex.unlock();
|
|
}
|
|
|
|
int QDSPEngine::getSpectrum(qVectorFloat &buffer, int mult) {
|
|
|
|
m_fftMultiplier = mult;
|
|
int idx = (int)(myLog(m_fftMultiplier, 2));
|
|
|
|
return powerSpectraList.at(idx)->spectrumResult(buffer, m_size * m_fftMultiplier * 2 - 2048);
|
|
}
|
|
|
|
float QDSPEngine::getSMeterInstValue() {
|
|
|
|
return signalmeter->getInstFValue();
|
|
}
|
|
|
|
void QDSPEngine::setVolume(float value) {
|
|
|
|
if (m_volume == value) return;
|
|
|
|
m_volume = value;
|
|
}
|
|
|
|
void QDSPEngine::setQtDSPStatus(bool value) {
|
|
|
|
m_qtdspOn = value;
|
|
}
|
|
|
|
void QDSPEngine::setDSPMode(DSPMode mode) {
|
|
|
|
demod->setDemodMode(mode);
|
|
}
|
|
|
|
void QDSPEngine::setAGCMode(AGCMode mode) {
|
|
|
|
wpagc->setMode(mode);
|
|
}
|
|
|
|
void QDSPEngine::setAGCMaximumGain(qreal value) {
|
|
|
|
qreal maxGain = 20.0 * log10(value);
|
|
set->setAGCMaximumGain_dB(this, m_rx, maxGain);
|
|
}
|
|
|
|
void QDSPEngine::setAGCHangThreshold(qreal value) {
|
|
|
|
set->setAGCHangThresholdSlider(this, m_rx, value);
|
|
}
|
|
|
|
void QDSPEngine::setAGCLineValues(QObject *sender, int rx, qreal thresh, qreal hang) {
|
|
|
|
if (m_rx != rx) return;
|
|
|
|
qreal noiseOffset = 10.0 * log10(qAbs(filter->filterHi() - filter->filterLo()) * 2 * m_size / m_samplerate);
|
|
qreal threshold = 20.0 * log10(thresh) - noiseOffset + AGCOFFSET;
|
|
|
|
set->setAGCLineLevels(sender, m_rx, threshold, hang + AGCOFFSET);
|
|
}
|
|
|
|
void QDSPEngine::setSampleRate(QObject *sender, int value) {
|
|
|
|
Q_UNUSED(sender)
|
|
|
|
if (m_samplerate == value) return;
|
|
|
|
m_mutex.lock();
|
|
switch (value) {
|
|
|
|
case 48000:
|
|
m_samplerate = value;
|
|
break;
|
|
|
|
case 96000:
|
|
m_samplerate = value;
|
|
break;
|
|
|
|
case 192000:
|
|
m_samplerate = value;
|
|
break;
|
|
|
|
case 384000:
|
|
m_samplerate = value;
|
|
break;
|
|
|
|
default:
|
|
DSP_ENGINE_DEBUG << "invalid sample rate (possible values are: 48, 96, 192, or 384 kHz)!\n";
|
|
break;
|
|
}
|
|
|
|
//DSP_ENGINE_DEBUG << "set sample rate to " << m_samplerate;
|
|
//setNCOFrequency(m_rx, m_rxData.vfoFrequency - m_rxData.ctrFrequency);
|
|
m_NcoInc = TWOPI * m_NcoFreq/m_samplerate;
|
|
m_OscCos = qCos(m_NcoInc);
|
|
m_OscSin = qSin(m_NcoInc);
|
|
|
|
filter->setSampleRate(this, m_samplerate);
|
|
demod->setSampleRate(this, m_samplerate);
|
|
wpagc->setSampleRate(this, m_samplerate);
|
|
|
|
m_mutex.unlock();
|
|
|
|
}
|
|
|
|
void QDSPEngine::setNCOFrequency(int rx, long ncoFreq) {
|
|
|
|
if (m_rx != rx) return;
|
|
|
|
qreal tmp = ncoFreq + m_CWoffset;
|
|
|
|
m_NcoFreq = tmp;
|
|
m_NcoInc = TWOPI * m_NcoFreq/m_samplerate;
|
|
m_OscCos = qCos(m_NcoInc);
|
|
m_OscSin = qSin(m_NcoInc);
|
|
|
|
//DSP_ENGINE_DEBUG << "NCO: " << m_NcoFreq;
|
|
}
|
|
|
|
void QDSPEngine::setSampleSize(int rx, int size) {
|
|
|
|
if (m_rx == rx) {
|
|
|
|
m_mutex.lock();
|
|
m_spectrumSize = size;
|
|
m_mutex.unlock();
|
|
}
|
|
}
|
|
|
|
void QDSPEngine::ProcessFrequencyShift(CPX &in, CPX &out, int size) {
|
|
|
|
cpx tmp;
|
|
CPX Osc;
|
|
|
|
Osc.resize(size);
|
|
|
|
for (int i = 0; i < size; i++) {
|
|
|
|
tmp = in.at(i);
|
|
|
|
qreal OscGn;
|
|
Osc[i].re = osc1cpx.re * m_OscCos - osc1cpx.im * m_OscSin;
|
|
Osc[i].im = osc1cpx.im * m_OscCos + osc1cpx.re * m_OscSin;
|
|
|
|
OscGn = 1.95 - (osc1cpx.re * osc1cpx.re + osc1cpx.im * osc1cpx.im);
|
|
|
|
osc1cpx.re = OscGn * Osc.at(i).re;
|
|
osc1cpx.im = OscGn * Osc.at(i).im;
|
|
|
|
//Cpx multiply by shift frequency
|
|
out[i].re = ((tmp.re * Osc.at(i).re) - (tmp.im * Osc.at(i).im));
|
|
out[i].im = ((tmp.re * Osc.at(i).im) + (tmp.im * Osc.at(i).re));
|
|
}
|
|
}
|
|
|