first commit

This commit is contained in:
n1gp
2015-04-04 16:53:58 -04:00
commit 805637d104
232 changed files with 95906 additions and 0 deletions
@@ -0,0 +1,116 @@
/**
* @file audioReceiver.cpp
* @brief audio receiver class
* @author Hermann von Hasseln, DL3HVH
* @version 0.1
* @date 2011-10-02
*/
/*
* Copyright 2011 Hermann von Hasseln, DL3HVH
*
* 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_AUDIO_RECEIVER
#include "cusdr_audioReceiver.h"
AudioReceiver::AudioReceiver(THPSDRParameter *ioData)
: QObject()
, set(Settings::instance())
, io(ioData)
, m_client(0)
{
}
AudioReceiver::~AudioReceiver() {
}
void AudioReceiver::displayAudioRcvrSocketError(QAbstractSocket::SocketError error) {
AUDIO_RECEIVER << "audio client socket error:" << error;
}
void AudioReceiver::initClient() {
quint16 port = (quint16) (set->getAudioPort() + (io->audio_rx * 2));
QUdpSocket *socket = new QUdpSocket();
socket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
if (socket->bind(port, QUdpSocket::ReuseAddressHint | QUdpSocket::ShareAddress)) {
CHECKED_CONNECT(
socket,
SIGNAL(error(QAbstractSocket::SocketError)),
this,
SLOT(displayAudioRcvrSocketError(QAbstractSocket::SocketError)));
CHECKED_CONNECT(
socket,
SIGNAL(readyRead()),
this,
SLOT(readPendingAudioRcvrData()));
clientConnections.append(socket);
AUDIO_RECEIVER << "client socket binding successful.";
m_message = tr("[server]: listening for rx %1 audio on port %2.");
emit messageEvent(m_message.arg(io->audio_rx).arg(port));
//m_dataEngine->clientConnected = true;
// need to implement connection in dataEngine !!!!
emit clientConnectedEvent(true);
//rcveIQ_toggle = false;
}
else {
m_message = tr("[server]: bind socket failed for socket on port %1.");
emit messageEvent(m_message.arg(port));
}
}
void AudioReceiver::readPendingAudioRcvrData() {
QUdpSocket *socket = qobject_cast<QUdpSocket *>(sender());
while (socket->hasPendingDatagrams()) {
m_datagram.resize(socket->pendingDatagramSize());
if (socket->readDatagram(m_datagram.data(), m_datagram.size()) < 0) {
AUDIO_RECEIVER << "read client" << m_client << "socket failed.";
if (io->rcveIQ_toggle) { // toggles the rcveIQ signal
emit rcveIQEvent(this, 2);
io->rcveIQ_toggle = false;
}
}
else {
io->au_queue.enqueue(m_datagram);
if (!io->rcveIQ_toggle) { // toggles the rcveIQ signal
emit rcveIQEvent(this, 1);
io->rcveIQ_toggle = true;
}
}
}
}
@@ -0,0 +1,87 @@
/**
* @file audioReceiver.h
* @brief audio receiver header file
* @author Hermann von Hasseln, DL3HVH
* @version 0.1
* @date 2011-10-02
*/
/*
* Copyright 2011 Hermann von Hasseln, DL3HVH
*
* 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.
*/
#ifndef _CUSDR_AUDIORECEIVER_H
#define _CUSDR_AUDIORECEIVER_H
//#include <QObject>
//#include <QMutex>
//#include <QByteArray>
//#include <QBuffer>
//#include <QVector>
//#include <QList>
//#include <QWaitCondition>
//#include <QThread>
#include "cusdr_settings.h"
#ifdef LOG_AUDIO_RECEIVER
# define AUDIO_RECEIVER qDebug().nospace() << "AudioReceiver::\t"
#else
# define AUDIO_RECEIVER nullDebug()
#endif
class AudioReceiver : public QObject {
Q_OBJECT
public:
AudioReceiver(THPSDRParameter *ioData = 0);
~AudioReceiver();
//int id;
public slots:
void initClient();
private:
Settings* set;
QMutex m_mutex;
QList<QUdpSocket *> clientConnections;
QString m_message;
QByteArray m_datagram;
THPSDRParameter *io;
int m_client;
private slots:
void displayAudioRcvrSocketError(QAbstractSocket::SocketError error);
void readPendingAudioRcvrData();
signals:
void messageEvent(QString message);
void rcveIQEvent(QObject *sender, int value);
void outputBufferEvent(unsigned char* outbuffer);
void clientConnectedEvent(bool value);
void newData();
void newAudioData();
};
#endif // _CUSDR_AUDIORECEIVER_H
@@ -0,0 +1,576 @@
/**
* @file cusdr_chirpProcessor.cpp
* @brief chirp data processor class
* @author Hermann von Hasseln, DL3HVH
* @version 0.1
* @date 2011-09-22
*/
/*
* Copyright 2011 Hermann von Hasseln, DL3HVH
*
* 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_CHIRP_PROCESSOR
#include "cusdr_chirpProcessor.h"
ChirpProcessor::ChirpProcessor(THPSDRParameter *ioData)
: QObject()
, set(Settings::instance())
, io(ioData)
, m_stopped(false)
, m_specMax(0.0f)
, m_specMin(0.0f)
, m_sampleRate(set->getSampleRate())
, m_downRate(set->getChirpDownSampleRate())
, m_specAvgLength(1)
, m_filterLowerFrequency((float)set->getChirpFilterLowerFrequency())
, m_filterUpperFrequency((float)set->getChirpFilterUpperFrequency())
, m_switch(false)
, m_showChirpFFT(false)
, m_chirpBufferLength(set->getChirpBufferLength())
, m_chirpSidebandFactor(1.0f)
{
/*m_QCPXin = initQCPX(BUFFERSIZE);
m_temp0 = initQCPX(BUFFERSIZE);
m_temp2 = initQCPX(BUFFERSIZE/m_downRate);
m_temp1 = initQCPX(FULL_BUFFERSIZE);
m_temp3 = initQCPX(FULL_BUFFERSIZE);*/
/*
m_cpxIn = mallocCPX(BUFFERSIZE);
m_tmp0 = mallocCPX(BUFFERSIZE);
m_tmp2 = mallocCPX(BUFFERSIZE/m_downRate);
m_cpxInFilt = mallocCPX(FULL_BUFFERSIZE);
m_cpxRxFFT = mallocCPX(FULL_BUFFERSIZE);
m_tmp1 = mallocCPX(FULL_BUFFERSIZE);
m_tmp3 = mallocCPX(FULL_BUFFERSIZE);
m_cpxChirpIn = mallocCPX(FULL_BUFFERSIZE);
m_cpxChirpOut = mallocCPX(FULL_BUFFERSIZE);
m_cpxChirpTmp = mallocCPX(FULL_BUFFERSIZE);
m_cpxOut = mallocCPX(FULL_BUFFERSIZE);
*/
m_cpxIn.resize(BUFFERSIZE);
m_tmp0.resize(BUFFERSIZE);
m_tmp2.resize(BUFFERSIZE/m_downRate);
m_cpxInFilt.resize(FULL_BUFFERSIZE);
m_cpxRxFFT.resize(FULL_BUFFERSIZE);
m_tmp1.resize(FULL_BUFFERSIZE);
m_tmp3.resize(FULL_BUFFERSIZE);
m_cpxChirpIn.resize(FULL_BUFFERSIZE);
m_cpxChirpOut.resize(FULL_BUFFERSIZE);
m_cpxChirpTmp.resize(FULL_BUFFERSIZE);
m_cpxOut.resize(FULL_BUFFERSIZE);
/*
memset(m_cpxIn, 0, BUFFERSIZE * sizeof(CPX));
memset(m_tmp0, 0, BUFFERSIZE * sizeof(CPX));
memset(m_tmp2, 0, (BUFFERSIZE/4) * sizeof(CPX));
memset(m_cpxInFilt, 0, FULL_BUFFERSIZE * sizeof(CPX));
memset(m_cpxRxFFT, 0, FULL_BUFFERSIZE * sizeof(CPX));
memset(m_tmp1, 0, FULL_BUFFERSIZE * sizeof(CPX));
memset(m_tmp3, 0, FULL_BUFFERSIZE * sizeof(CPX));
memset(m_cpxOut, 0, FULL_BUFFERSIZE * sizeof(CPX));
memset(m_cpxChirpIn, 0, FULL_BUFFERSIZE * sizeof(CPX));
memset(m_cpxChirpOut, 0, FULL_BUFFERSIZE * sizeof(CPX));
memset(m_cpxChirpTmp, 0, FULL_BUFFERSIZE * sizeof(CPX));
*/
// FFTs by fftw
m_chirpFFT = new QFFT(FULL_BUFFERSIZE);
m_matchedFFT = new QFFT(FULL_BUFFERSIZE);
// FIR band pass filter
//m_filter = new QFilter(this, FULL_BUFFERSIZE, 2);
// 2 = FIR Bandpass, 5 = Bartlett window
m_filter = new QFilter(this, BUFFERSIZE, 2, 2);
// set the BPF
m_filter->setFilter(500.0f, 2500.0f);
m_filter->setStreamMode(true);
setupConnections();
}
ChirpProcessor::~ChirpProcessor() {
while (average_queue.length() > 0)
average_queue.dequeue();
/*
freeCPX(m_cpxIn);
freeCPX(m_cpxOut);
freeCPX(m_cpxInFilt);
freeCPX(m_cpxRxFFT);
freeCPX(m_cpxChirpIn);
freeCPX(m_cpxChirpOut);
freeCPX(m_tmp0);
freeCPX(m_tmp1);
*/
/*
delete m_cpxIn;
delete m_cpxOut;
delete m_cpxInFilt;
delete m_cpxRxFFT;
delete m_cpxChirpIn;
delete m_cpxChirpOut;
delete m_tmp0;
delete m_tmp1;
*/
delete m_chirpFFT;
delete m_matchedFFT;
}
void ChirpProcessor::stop() {
m_stopped = true;
}
void ChirpProcessor::setupConnections() {
CHECKED_CONNECT(
set,
SIGNAL(sampleRateChanged(QObject *, int)),
this,
SLOT(samplingRateChanged(QObject *, int)));
CHECKED_CONNECT_OPT(
set,
SIGNAL(chirpFilterLowerFrequencyChanged(int)),
this,
SLOT(setFilterLowerFrequency(int)),
Qt::DirectConnection);
CHECKED_CONNECT_OPT(
set,
SIGNAL(chirpFilterUpperFrequencyChanged(int)),
this,
SLOT(setFilterUpperFrequency(int)),
Qt::DirectConnection);
CHECKED_CONNECT_OPT(
set,
SIGNAL(chirpAvgLengthChanged(int)),
this,
SLOT(setDistSpectrumAvgLength(int)),
Qt::DirectConnection);
CHECKED_CONNECT_OPT(
set,
SIGNAL(chirpFFTShowChanged(bool)),
this,
SLOT(setChirpFFTShow(bool)),
Qt::DirectConnection);
CHECKED_CONNECT_OPT(
set,
SIGNAL(chirpSidebandChanged(bool)),
this,
SLOT(setChirpSideband(bool)),
Qt::DirectConnection);
}
int ChirpProcessor::setSpectrumBufferSize(int size) {
int i = 1;
while (i < size) i *= 2;
return i;
}
void ChirpProcessor::processChirpData() {
forever {
//matchedFilter(io->chirp_queue.dequeue());
matchedFilterFIRFilter(io->chirp_queue.dequeue());
m_mutex.lock();
if (m_stopped) {
m_stopped = false;
m_mutex.unlock();
break;
}
m_mutex.unlock();
}
}
void ChirpProcessor::matchedFilterFIRFilter(QList<qreal> data) {
int dataLength = data.length();
int dataIdx = 0;
int buffer = 0;
int newsize = (int)(BUFFERSIZE / m_downRate);
CHIRP_PROCESSOR_DEBUG << "start matched filter - samples:" << dataLength;
// filtering to 2kHz by constant-overlap-add (COLA)
while (dataIdx < dataLength) {
dataIdx = 2 * buffer * BUFFERSIZE;
for (int i = 0; i < 2 * BUFFERSIZE; i += 2) {
if (dataIdx + i + 1 < dataLength) {
m_cpxIn[i/2].re = data.at(dataIdx + i);
m_cpxIn[i/2].im = data.at(dataIdx + i + 1);
}
else {
dataIdx = dataLength;
break;
}
}
// BPF 2.5 kHz
m_filter->ProcessFilter(m_cpxIn, m_tmp0, BUFFERSIZE);
// decimate by 4
decimate(m_tmp0, m_tmp2, BUFFERSIZE, m_downRate);
for (int i = 0; i < newsize; i++) {
m_cpxInFilt[buffer * newsize + i].re = m_tmp2[i].re;
m_cpxInFilt[buffer * newsize + i].im = m_tmp2[i].im;
}
buffer++;
}
// Due to the COLA method the filter output gets shifet by BUFFERSIZE/2 bytes.
// In order to have a buffer in full chirp length, we save the last newsize/2 bytes
// (due to decimation by downrate) and add it to the beginning of the newbuffer.
for (int i = 0; i < FULL_BUFFERSIZE - newsize/2; i++) {
if (i < newsize/2) {
m_cpxInFilt[i].re = m_tmp3[i].re;
m_cpxInFilt[i].im = m_tmp3[i].im;
}
else {
m_cpxInFilt[i].re = m_cpxInFilt[i + newsize/2].re;
m_cpxInFilt[i].im = m_cpxInFilt[i + newsize/2].im;
}
}
// save the last buffer
for (int i = 0; i < newsize/2; i++) {
m_tmp3[i].re = m_tmp2[i].re;
m_tmp3[i].im = m_tmp2[i].im;
}
// map rx signal to the frequency domain
m_matchedFFT->DoFFTWForward(m_cpxInFilt, m_cpxRxFFT, FULL_BUFFERSIZE);
// multiply the chirp signal with the complex conjugate of the received signal
for (int i = 0; i < FULL_BUFFERSIZE; i++) {
// f * ~g
m_tmp1[i].re = (m_cpxChirpOut[i].re * m_cpxRxFFT[i].re) + (m_cpxChirpOut[i].im * m_cpxRxFFT[i].im);
m_tmp1[i].im = (m_cpxChirpOut[i].im * m_cpxRxFFT[i].re) - (m_cpxChirpOut[i].re * m_cpxRxFFT[i].im);
// ~f * g
//m_tmp1[i].re = (m_cpxRxFFT[i].re * m_cpxChirpOut[i].re) + (m_cpxRxFFT[i].im * m_cpxChirpOut[i].im);
//m_tmp1[i].im = (m_cpxRxFFT[i].im * m_cpxChirpOut[i].re) - (m_cpxRxFFT[i].re * m_cpxChirpOut[i].im);
// f * g
//m_tmp1[i].re = (m_cpxChirpOut[i].re * m_cpxRxFFT[i].re) - (m_cpxChirpOut[i].im * m_cpxRxFFT[i].im);
//m_tmp1[i].im = (m_cpxChirpOut[i].re * m_cpxRxFFT[i].im) + (m_cpxChirpOut[i].im * m_cpxRxFFT[i].re);
}
// map back to time domain
m_matchedFFT->DoFFTWInverse(m_tmp1, m_cpxOut, FULL_BUFFERSIZE);
float max = -1000;
float min = 1000;
float mean = 0.0f;
float oneOverNorm = 1.0f / FULL_BUFFERSIZE;
for (int i = 0; i < FULL_BUFFERSIZE; i++) {
if (!m_showChirpFFT) {
m_spectrumBufferFull[i] = (float)(10.0 * log10(MagCPX(ScaleCPX(m_cpxOut[i], oneOverNorm)) + 1.5E-45));
if (m_spectrumBufferFull[i] > max) max = m_spectrumBufferFull[i];
if (m_spectrumBufferFull[i] < min) min = m_spectrumBufferFull[i];
mean += m_spectrumBufferFull[i];
}
else {
//m_spectrumBufferFull[i] = (float)(10.0 * log10(SqrMagCPX(m_tmp1[i]) + 1.5E-45));
//m_spectrumBufferFull[i] = (float)(10.0 * log10(SqrMagCPX(ScaleCPX(m_tmp1[i], oneOverNorm)) + 1.5E-45));
m_spectrumBufferFull[i] = (float)(10.0 * log10(MagCPX(m_cpxRxFFT[i]) + 1.5E-45));
//m_spectrumBufferFull[i] = (float)(10.0 * log10(SqrMagCPX(m_cpxChirpOut[i]) + 1.5E-45) + 50.0f);
//m_spectrumBufferFull[i] = (float)(10.0 * log10(SqrMagCPX(SubCPX(m_cpxChirpOut[i], m_cpxRxFFT[i])) + 1.5E-45) + 50.0f);
//m_spectrumBufferFull[i] = (float)(10.0 * log10(MagCPX(m_cpxInFilt[i]) + 1.5E-45));
//m_spectrumBufferFull[i] = (float)(10.0 * log10(MagCPX(m_cpxIn[i]) + 1.5E-45));
}
}
mean *= 1.0f/FULL_BUFFERSIZE;
CHIRP_PROCESSOR_DEBUG << "dist min" << min << "max" << max << "mean" << mean;
CHIRP_PROCESSOR_DEBUG << "dist delta" << max - mean;
// we take the first half of the matched filter output to display
memcpy(m_spectrumBuffer, m_spectrumBufferFull, (FULL_BUFFERSIZE/2) * sizeof(float));
// we take the full length for the frequency spectrum,
// because we want to see positive as well as negative spectras
if (m_showChirpFFT) {
int topsize = FULL_BUFFERSIZE - 1;
// reorder the RX FFT buffer
for (int i = 0; i < FULL_BUFFERSIZE/2; i++) {
m_fftSpectrumBuffer[topsize - i] = m_spectrumBufferFull[i + FULL_BUFFERSIZE/2];
m_fftSpectrumBuffer[FULL_BUFFERSIZE/2 - i] = m_spectrumBufferFull[i];
}
}
setSpectras(m_spectrumBuffer, m_fftSpectrumBuffer);
}
void ChirpProcessor::setSpectras(const float *distance, const float *chirpfft) {
if (m_showChirpFFT)
set->setChirpSpectrumBuffer(m_sampleRate/m_downRate, FULL_BUFFERSIZE, chirpfft);
else {
if (m_specAvgLength > 1)
//spectrumAveraging(FULL_BUFFERSIZE, distance);
spectrumAveraging(FULL_BUFFERSIZE/2, distance);
else
//set->setChirpSpectrumBuffer(m_sampleRate/m_downRate, FULL_BUFFERSIZE, distance);
set->setChirpSpectrumBuffer(m_sampleRate/m_downRate, FULL_BUFFERSIZE/2, distance);
}
}
void ChirpProcessor::spectrumAveraging(qint64 length, const float *buffer) {
QVector<float> m_specBuf(length);
m_mutex.lock();
memcpy(
(float *) m_specBuf.data(),
(float *) &buffer[0],
length * sizeof(float));
average_queue.enqueue(m_specBuf);
float specMax = 0.0f;
float specMin = 0.0f;
float specMean = 0.0f;
if (average_queue.size() <= m_specAvgLength) {
for (int i = 0; i < length; i++) {
m_tmpBuf[i] += average_queue.last().data()[i];
m_outBuf[i] = m_tmpBuf[i] * (1.0f/average_queue.size());
specMean += m_outBuf[i];
if (m_outBuf[i] > specMax) specMax = m_outBuf[i];
if (m_outBuf[i] < specMin) specMin = m_outBuf[i];
}
m_mutex.unlock();
specMean *= 1.0f/FULL_BUFFERSIZE;
CHIRP_PROCESSOR_DEBUG << "distance spectrum averaging size" << average_queue.size();
CHIRP_PROCESSOR_DEBUG << "specMin =" << specMin << "specMax =" << specMax;
CHIRP_PROCESSOR_DEBUG << "distance spectrum mean value" << specMean;
set->setChirpSpectrumBuffer(m_sampleRate, FULL_BUFFERSIZE, m_outBuf);
return;
}
for (int i = 0; i < length; i++) {
m_tmpBuf[i] -= average_queue.first().at(i);
m_tmpBuf[i] += average_queue.last().at(i);
m_outBuf[i] = m_tmpBuf[i] * m_scale;
specMean += m_outBuf[i];
}
//set->setChirpSpectrumBuffer(m_sampleRate, FULL_BUFFERSIZE, m_outBuf);
set->setChirpSpectrumBuffer(m_sampleRate, FULL_BUFFERSIZE/2, m_outBuf);
average_queue.dequeue();
m_mutex.unlock();
}
void ChirpProcessor::generateLocalChirp() {
const int sampleRate = m_sampleRate / m_downRate;
//memset(m_cpxChirpIn, 0, FULL_BUFFERSIZE * sizeof(CPX));
//memset(m_cpxChirpOut, 0, FULL_BUFFERSIZE * sizeof(CPX));
m_cpxChirpIn.resize(FULL_BUFFERSIZE);
m_cpxChirpOut.resize(FULL_BUFFERSIZE);
qreal time = set->getChirpBufferDurationUs() / 1.0E6;
qint64 length = (qint64)(sampleRate * time);
qreal a = ONEPI * (set->getUpperChirpFreq() - set->getLowerChirpFreq()) / time;
qreal b = TWOPI * set->getLowerChirpFreq();
for (int i = 0; i < length; i++) {
// forward chirp
qreal t = (qreal)(1.0f * i/length);
// backward chirp
//qreal t = (qreal)(1.0f * (length-i)/length);
// complex chirp signal
m_cpxChirpIn[i].re = qSin(a * t * t + b * t);
m_cpxChirpIn[i].im = qCos(a * t * t + b * t);
//m_cpxChirpTmp[i].re = qSin(a * t * t + b * t);
//m_cpxChirpTmp[i].im = qCos(a * t * t + b * t);
//m_cpxChirpIn[i].re = qCos(a * t * t + b * t);
//m_cpxChirpIn[i].im = qSin(a * t * t + b * t);
//m_cpxChirpIn[i].re = qSin(a * t * t + b * t);
//m_cpxChirpIn[i].im = -qCos(a * t * t + b * t);
}
//m_specBufferSize = setSpectrumBufferSize(length);
// BPF 2.5 kHz
//m_filter->ProcessFilter(m_cpxChirpTmp, m_cpxChirpIn, FULL_BUFFERSIZE);
// transform chirp signal to frequency domain
m_chirpFFT->DoFFTWForward(m_cpxChirpIn, m_cpxChirpOut, FULL_BUFFERSIZE);
float dur = set->getChirpBufferDurationUs() / 1000.0f;
CHIRP_PROCESSOR_DEBUG << "chirp buffer changed:";
CHIRP_PROCESSOR_DEBUG << " bufferLength" << length;
//CHIRP_PROCESSOR_DEBUG << " spectrum buffer size" << m_specBufferSize;
CHIRP_PROCESSOR_DEBUG << " start frequency (Hz)" << set->getLowerChirpFreq();
CHIRP_PROCESSOR_DEBUG << " end frequency (Hz)" << set->getUpperChirpFreq();
CHIRP_PROCESSOR_DEBUG << " duration (ms)" << dur;
}
//void ChirpProcessor::decimate(CPX *in, CPX *out, int size, int downrate) {
void ChirpProcessor::decimate(const CPX &in, CPX &out, int size, int downrate) {
int newsize = size / downrate;
//memset(out, 0, newsize * sizeof(CPX));
out.resize(newsize);
for (int j = 0; j < newsize; j++) {
for (int k = 0; k < downrate; k++) {
if (j * downrate + k < size) {
/*out[j].re += m_tmpDec[j * downrate + k].re;
out[j].im += m_tmpDec[j * downrate + k].im;*/
out[j].re += in[j * downrate + k].re;
out[j].im += in[j * downrate + k].im;
}
}
}
}
//void ChirpProcessor::decimate(QList<qreal> data, CPX *out, int downrate) {
void ChirpProcessor::decimate(const QList<qreal> &data, CPX &out, int downrate) {
//memset(out, 0, 16 * BUFFER_SIZE * sizeof(CPX));
out.resize(16 * BUFFER_SIZE);
for (int j = 0; j < data.length()/4; j += 2) {
for (int k = 0; k < downrate; k++) {
if (j * downrate + 2*k < data.length()) {
out[j/2].re += data.at(j * downrate + 2*k);
out[j/2].im += data.at(j * downrate + 2*k + 1);
}
}
}
}
void ChirpProcessor::samplingRateChanged(QObject *sender, int value) {
Q_UNUSED(sender)
m_sampleRate = value;
}
void ChirpProcessor::setDistSpectrumAvgLength(int value) {
m_mutex.lock();
//for (int i = 0; i < SAMPLE_BUFFER_SIZE; i++) m_tmpBuf[i] = 0.0f;
memset(m_tmpBuf, 0, FULL_BUFFERSIZE * sizeof(float));
while (!average_queue.isEmpty())
average_queue.dequeue();
m_specAvgLength = value;
if (m_specAvgLength > 0)
m_scale = 1.0f / m_specAvgLength;
else
m_scale = 1.0f;
m_mutex.unlock();
}
void ChirpProcessor::setChirpFFTShow(bool value) {
m_showChirpFFT = value;
}
void ChirpProcessor::setChirpSideband(bool value) {
if (value)
m_chirpSidebandFactor = 1.0f;
else
m_chirpSidebandFactor = -1.0f;
generateLocalChirp();
}
void ChirpProcessor::setFilterLowerFrequency(int value) {
if ((int)m_filterLowerFrequency == value) return;
m_filterLowerFrequency = 1.0f * value;
m_filter->setFilter(m_filterLowerFrequency, m_filterUpperFrequency);
}
void ChirpProcessor::setFilterUpperFrequency(int value) {
if ((int)m_filterUpperFrequency == value) return;
m_filterUpperFrequency = 1.0f * value;
m_filter->setFilter(m_filterLowerFrequency, m_filterUpperFrequency);
}
@@ -0,0 +1,145 @@
/**
* @file cusdr_chirpProcessor.h
* @brief chirp processor header file
* @author Hermann von Hasseln, DL3HVH
* @version 0.1
* @date 2011-09-22
*/
/*
* Copyright 2011 Hermann von Hasseln, DL3HVH
*
* 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.
*/
#ifndef _CUSDR_CHIRPPROCESSOR_H
#define _CUSDR_CHIRPPROCESSOR_H
//#include <QObject>
//#include <QMutex>
//#include <QByteArray>
//#include <QBuffer>
//#include <QVector>
//#include <QList>
//#include <QWaitCondition>
//#include <QThread>
#include "cusdr_settings.h"
#include "QtDSP/qtdsp_qComplex.h"
#include "QtDSP/qtdsp_filter.h"
#include "QtDSP/qtdsp_fft.h"
#ifdef LOG_CHIRP_PROCESSOR
# define CHIRP_PROCESSOR_DEBUG qDebug().nospace() << "ChirpProcessor::\t"
#else
# define CHIRP_PROCESSOR_DEBUG nullDebug()
#endif
#define FULL_BUFFERSIZE 16384//65536
#define HALF_BUFFERSIZE 32768
#define BUFFERSIZE 2048
class ChirpProcessor : public QObject {
Q_OBJECT
public:
ChirpProcessor(THPSDRParameter *ioData = 0);
~ChirpProcessor();
public slots:
void stop();
void processChirpData();
void generateLocalChirp();
private slots:
void matchedFilterFIRFilter(QList<qreal> data);
void samplingRateChanged(QObject *sender, int value);
void setSpectras(const float *distance, const float *chirpfft);
void setDistSpectrumAvgLength(int value);
void setChirpFFTShow(bool value);
void setChirpSideband(bool value);
void setFilterLowerFrequency(int value);
void setFilterUpperFrequency(int value);
private:
Settings *set;
QMutex m_mutex;
QString m_message;
QFFT *m_chirpFFT;
QFFT *m_matchedFFT;
QFilter *m_filter;
CPX m_tmp0;
CPX m_tmp1;
CPX m_tmp2;
CPX m_tmp3;
CPX m_tmpDec;
CPX m_cpxRxFFT;
CPX m_cpxChirpIn;
CPX m_cpxChirpTmp;
CPX m_cpxChirpOut;
CPX m_cpxIn;
CPX m_cpxInFilt;
CPX m_cpxOut;
THPSDRParameter *io;
QQueue<QVector<float> > average_queue;
float m_tmpBuf[FULL_BUFFERSIZE];
float m_outBuf[FULL_BUFFERSIZE];
float m_spectrumBuffer[FULL_BUFFERSIZE];
float m_fftSpectrumBuffer[FULL_BUFFERSIZE];
float m_spectrumBufferFull[FULL_BUFFERSIZE];
float *m_window;
volatile bool m_stopped;
float m_specMax;
float m_specMin;
float m_scale;
int m_sampleRate;
int m_downSampleRate;
int m_downRate;
int m_specBufferSize;
int m_specAvgLength;
float m_filterLowerFrequency;
float m_filterUpperFrequency;
bool m_switch;
bool m_showChirpFFT;
qint64 m_chirpBufferLength;
qreal m_chirpSidebandFactor;
void setupConnections();
void decimate(const CPX &in, CPX &out, int size, int downrate);
void decimate(const QList<qreal> &data, CPX &out, int downrate);
void spectrumAveraging(qint64 length, const float *buffer);
int setSpectrumBufferSize(int size);
signals:
void messageEvent(QString message);
};
#endif // _CUSDR_CHIRPPROCESSOR_H
File diff suppressed because it is too large Load Diff
+534
View File
@@ -0,0 +1,534 @@
/**
* @file cusdr_dataEngine.h
* @brief cuSDR data engine header file
* @author Hermann von Hasseln, DL3HVH
* @version 0.1
* @date 2011-02-02
*/
/*
*
* Copyright 2010 Hermann von Hasseln, DL3HVH
*
* using original C code by John Melton, G0ORX/N6LYT and Dave McMcQuate, WA8YWQ
*
* 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.
*/
#ifndef _CUSDR_DATA_ENGINE_H
#define _CUSDR_DATA_ENGINE_H
//#include <QObject>
//#include <QThread>
//#include <QMetaType>
//#include <QtNetwork>
//#include <QHostAddress>
//#include <QMutexLocker>
//#include <QMutex>
//#include <QWaitCondition>
//#include <QVariant>
//#include <QElapsedTimer>
//#include <QFuture>
//#include <qtconcurrentrun.h>
#include "cusdr_settings.h"
#include "cusdr_dataIO.h"
#include "cusdr_receiver.h"
#include "cusdr_chirpProcessor.h"
#include "cusdr_audioReceiver.h"
#include "cusdr_discoverer.h"
#include "Util/qcircularbuffer.h"
#include "QtDSP/qtdsp_fft.h"
#include "QtDSP/qtdsp_filter.h"
#include "QtDSP/qtdsp_dualModeAverager.h"
#include "AudioEngine/cusdr_audio_engine.h"
#ifdef LOG_DATA_ENGINE
# define DATA_ENGINE_DEBUG qDebug().nospace() << "DataEngine::\t"
#else
# define DATA_ENGINE_DEBUG nullDebug()
#endif
#ifdef LOG_DATA_PROCESSOR
# define DATA_PROCESSOR_DEBUG qDebug().nospace() << "DataProcessor::\t"
#else
# define DATA_PROCESSOR_DEBUG nullDebug()
#endif
#ifdef LOG_WIDEBAND_PROCESSOR
# define WIDEBAND_PROCESSOR_DEBUG qDebug().nospace() << "WidebandProcessor::\t"
#else
# define WIDEBAND_PROCESSOR_DEBUG nullDebug()
#endif
class DataProcessor;
class AudioOutProcessor;
class WideBandDataProcessor;
//Q_DECLARE_METATYPE (QAbstractSocket::SocketError)
// *********************************************************************
// data engine class
class DataEngine : public QObject {
Q_OBJECT
public:
DataEngine(QObject* parent = 0);
~DataEngine();
Settings* set;
THPSDRParameter io;
QList<Receiver *> RX;
QList<qreal> chirpData;
QUdpSocket* sendSocket;
DataIO* m_dataIO;
public slots:
bool initDataEngine();
void stop();
// set Server parameter
void setRxPeerAddress(int rx, QHostAddress address);
void setRxClient(int rx, int client);
void setRx(int rx);
void setRxSocketState(int rx, const char* prop, QString);
//void setSendIQSignal(QObject *sender, int value);
void setRcveIQSignal(QObject *sender, int value);
void setAudioReceiver(QObject *sender, int rx);
//void setAudioInProcessorRunning(bool value);
void setIQPort(int rx, int port);
void setRxConnectedStatus(QObject* sender, int rx, bool value);
void setClientConnected(QObject* sender, int rx);
void setClientConnected(bool value);
void setClientDisconnected(int client);
void setFramesPerSecond(QObject *sender, int rx, int value);
void createChirpDataProcessor();
// DSP processing
void processFileBuffer(const QList<qreal> data);
// change HPSDR hardware settings
void setPenelopeVersion(QObject *sender, int version);
void setHwIOVersion(QObject *sender, int version);
void setNumberOfRx(QObject *sender, int value);
void setSampleRate(QObject *sender, int value);
void setMercuryAttenuator(QObject *sender, HamBand band, int value);
void setDither(QObject *sender, int value);
void setRandom(QObject *sender, int value);
void setTimeStamp(QObject *sender, bool value);
void set10MhzSource(QObject *sender, int source);
void set122_88MhzSource(QObject *sender, int source);
void setMicSource(QObject *sender, int source);
void setMercuryClass(QObject *sender, int value);
void setMercuryTiming(QObject* sender, int value);
void setHamBand(QObject *sender, int rx, bool byBtn, HamBand band);
void setFrequency(QObject* sender, int mode, int rx, long frequency);
void loadWavFile(const QString &fileName);
void suspend();
void startPlayback();
void showSettingsDialog();
private:
void setSystemState(
QSDR::_Error err,
QSDR::_HWInterfaceMode hwmode,
QSDR::_ServerMode mode,
QSDR::_DataEngineState state);
void initAudioEngine();
void setupConnections();
void connectDSPSlots();
void disconnectDSPSlots();
void createDiscoverer();
void createDataIO();
void createDataProcessor();
void createAudioOutProcessor();
void createWideBandDataProcessor();
//void createChirpDataProcessor();
//void createAudioReceiver(int rx);
void createAudioReceiver();
bool initReceivers(int rx);
bool start();
bool startDataEngineWithoutConnection();
bool findHPSDRDevices();
bool getFirmwareVersions();
bool checkFirmwareVersions();
bool startDiscoverer(QThread::Priority prio);
bool startDataIO(QThread::Priority prio);
bool startDataProcessor(QThread::Priority prio);
void startAudioOutProcessor(QThread::Priority prio);
bool startWideBandDataProcessor(QThread::Priority prio);
bool startChirpDataProcessor(QThread::Priority prio);
void stopDiscoverer();
void stopDataIO();
void stopDataProcessor();
void stopAudioOutProcessor();
void stopWideBandDataProcessor();
void stopChirpDataProcessor();
void setHPSDRConfig();
private:
DataProcessor* m_dataProcessor;
WideBandDataProcessor* m_wbDataProcessor;
QDSPEngine* m_chirpDspEngine;
AudioReceiver* m_audioReceiver;
AudioEngine* m_audioEngine;
AudioOutProcessor* m_audioOutProcessor;
ChirpProcessor* m_chirpProcessor;
Discoverer* m_discoverer;
QThreadEx* m_discoveryThread;
QThreadEx* m_dataIOThread;
QThreadEx* m_dataProcThread;
QThreadEx* m_wbDataProcThread;
QThreadEx* m_chirpDataProcThread;
QThreadEx* m_AudioRcvrThread;
QThreadEx* m_audioInProcThread;
QThreadEx* m_audioOutProcThread;
QList<QThreadEx* > m_dspThreadList;
QMutex m_mutex;
QString m_message;
QString m_HPSDRDevice;
QByteArray m_commandDatagram;
QByteArray m_datagram;
QSDR::_Error m_error;
QSDR::_ServerMode m_serverMode;
QSDR::_HWInterfaceMode m_hwInterface;
QSDR::_DataEngineState m_dataEngineState;
QCircularBuffer<int> audioringbuffer;
TMeterType m_meterType;
CPX cpxIn;
CPX cpxOut;
bool m_restart;
bool m_networkDeviceRunning;
bool m_soundFileLoaded;
bool m_clientConnect;
//bool m_audioProcessorRunning;
bool m_chirpInititalized;
bool m_discoveryThreadRunning;
bool m_dataIOThreadRunning;
bool m_wbDataRcvrThreadRunning;
bool m_chirpDataProcThreadRunning;
bool m_dataProcThreadRunning;
bool m_audioRcvrThreadRunning;
bool m_audioInProcThreadRunning;
bool m_audioOutProcThreadRunning;
bool m_frequencyChange;
bool m_hamBandChanged;
bool m_chirpThreadStopped;
bool m_clientConnected;
float m_mainVolume;
int m_hpsdrDevices;
int m_fwCount;
int m_configure;
int m_timeout;
int m_txFrame;
int m_bytes;
int m_remainingTime;
int m_found;
int m_RxFrequencyChange;
int m_counter;
int m_forwardPower;
int m_maxSamples;
int m_offset;
int m_rxSamples;
int m_chirpSamples;
int m_leftSample;
int m_rightSample;
int m_micSample;
int m_spectrumSize;
int m_sendState;
float m_lsample;
float m_rsample;
float m_scale;
float m_sMeterValue;
float m_sMeterCalibrationOffset;
float m_micSample_float;
float m_spectrumBuffer[SAMPLE_BUFFER_SIZE];
qint64 m_audioFileBufferPosition;
qint64 m_audioFileBufferLength;
QByteArray m_audioFileBuffer;
float getFilterSizeCalibrationOffset();
private slots:
void systemStateChanged(
QObject *sender,
QSDR::_Error err,
QSDR::_HWInterfaceMode hwmode,
QSDR::_ServerMode mode,
QSDR::_DataEngineState state);
//void setCurrentNetworkDevice(TNetworkDevicecard card);
void setHPSDRDeviceNumber(int value);
void rxListChanged(QList<Receiver *> rxList);
void searchHpsdrNetworkDevices();
void setCurrentReceiver(QObject* sender, int rx);
void setMercuryAttenuators(QObject *sender, QList<int> attn);
void setAlexConfiguration(quint16 conf);
void setAlexStates(HamBand band, const QList<int> &states);
void setPennyOCEnabled(bool value);
void setRxJ6Pins(const QList<int> &list);
void setTxJ6Pins(const QList<int> &list);
void setAudioFileFormat(QObject *sender, const QAudioFormat &format);
void setAudioFilePosition(QObject *sender, qint64 position);
void setAudioFileBuffer(QObject *sender, qint64 position, qint64 length, const QByteArray &buffer);
void setAudioFileBuffer(const QList<qreal> &buffer);
signals:
void error(QUdpSocket::SocketError error);
void masterSwitchEvent(QObject *sender, bool power);
//void messageEvent(QString message);
void penelopeVersionInfoEvent(QObject *sender, int version);
void hwIOVersionInfoEvent(QObject *sender, int version);
void sendIQEvent(QObject *sender, int sendIQ);
void rcveIQEvent(QObject *sender, int value);
//void iqDataReady(int rx);
void chirpDataReady(int samples);
void audioDataReady();
void clientConnectedEvent(int rx);
void audioRxEvent(int rx);
void outMultiplierEvent(int value);
void systemMessageEvent(const QString &str, int time);
void clearSystemMessageEvent();
void DataProcessorReadyEvent();
void audioSenderReadyEvent(bool value);
};
// *********************************************************************
// Data processor class
class DataProcessor : public QObject {
Q_OBJECT
public:
DataProcessor(
DataEngine* de = 0,
QSDR::_ServerMode serverMode = QSDR::NoServerMode,
QSDR::_HWInterfaceMode hwMode = QSDR::NoInterfaceMode);
~DataProcessor();
public slots:
void stop();
void processData();
void processDeviceData();
void externalDspProcessing(int rx);
void externalDspProcessingBig(int rx);
private slots:
void initDataProcessorSocket();
void displayDataProcessorSocketError(QAbstractSocket::SocketError error);
void processInputBuffer(const QByteArray &buffer);
void processOutputBuffer(const CPX &buffer);
void decodeCCBytes(const QByteArray &buffer);
void encodeCCBytes();
void setOutputBuffer(int rx, const CPX &buffer);
void writeData();
private:
DataEngine* de;
Settings* set;
QUdpSocket* m_dataProcessorSocket;
QSDR::_Error m_error;
QSDR::_ServerMode m_serverMode;
QSDR::_HWInterfaceMode m_hwInterface;
QSDR::_DataEngineState m_dataEngineState;
QHostAddress m_deviceAddress;
QMutex m_mutex;
QMutex m_spectrumMutex;
QByteArray m_IQDatagram;
QByteArray m_outDatagram;
QByteArray m_deviceSendDataSignature;
QString m_message;
QTime m_SyncChangedTime;
QTime m_ADCChangedTime;
bool m_socketConnected;
bool m_setNetworkDeviceHeader;
bool m_chirpGateBit;
bool m_chirpBit;
bool m_chirpStart;
int m_leftSample;
int m_rightSample;
int m_micSample;
int m_bytes;
int m_maxSamples;
int m_rxSamples;
int m_chirpSamples;
int m_fwCount;
int m_idx;
int m_sendState;
int m_chirpStartSample;
float m_lsample;
float m_rsample;
float m_micSample_float;
unsigned long m_IQSequence;
unsigned long m_sequenceHi;
unsigned short m_offset;
unsigned short m_length;
long m_sendSequence;
long m_oldSendSequence;
volatile bool m_stopped;
uchar m_ibuffer[IO_BUFFER_SIZE * IO_BUFFERS];
signals:
void messageEvent(QString message);
void connectingEvent(QString addr, quint16 port);
void connectedEvent(QString addr, quint16 port);
void disconnectedEvent();
void serverVersionEvent(QString version);
};
// *********************************************************************
// Audio out processor class
class AudioOutProcessor : public QObject {
Q_OBJECT
public:
AudioOutProcessor(DataEngine* de = 0, QSDR::_ServerMode serverMode = QSDR::NoServerMode);
~AudioOutProcessor();
public slots:
void stop();
void processData();
void processDeviceData();
private slots:
private:
DataEngine* m_dataEngine;
QMutex m_mutex;
QByteArray m_IQDatagram;
QString m_message;
QSDR::_ServerMode m_serverMode;
/*int m_bytes;
unsigned long m_IQSequence;
unsigned long m_sequenceHi;
unsigned short m_offset;
unsigned short m_length;*/
volatile bool m_stopped;
signals:
//void connectingEvent(QString addr, quint16 port);
//void connectedEvent(QString addr, quint16 port);
//void disconnectedEvent();
//void serverVersionEvent(QString version);
////void metisVersionEvent(QObject *sender, int version);
//void newData();
//void newIQData(int rx);
//void newAudioDataEvent(float *lBuf, float *rBuf);
};
// *********************************************************************
// Wide band data processor class
class WideBandDataProcessor : public QObject {
Q_OBJECT
public:
WideBandDataProcessor(THPSDRParameter *ioData = 0, QSDR::_ServerMode serverMode = QSDR::NoServerMode, int size = 0);
~WideBandDataProcessor();
public slots:
void stop();
void processWideBandData();
void setWbSpectrumAveraging(QObject* sender, int rx, bool value);
private slots:
//void initDataProcessorSocket();
//void displayDataProcessorSocketError(QAbstractSocket::SocketError error);
void processWideBandInputBuffer(const QByteArray &buffer);
private:
THPSDRParameter* io;
Settings* set;
QFFT* wbFFT;
DualModeAverager* wbAverager;
CPX cpxWBIn;
CPX cpxWBOut;
QMutex m_mutex;
QByteArray m_WBDatagram;
QString m_message;
QSDR::_ServerMode m_serverMode;
int m_size;
int m_bytes;
bool m_wbSpectrumAveraging;
volatile bool m_stopped;
unsigned char m_ibuffer[IO_BUFFER_SIZE * IO_BUFFERS];
signals:
void messageEvent(QString message);
void wbSpectrumBufferChanged(const qVectorFloat &buffer);
};
#endif // _CUSDR_DATA_ENGINE_H
+661
View File
@@ -0,0 +1,661 @@
/**
* @file cusdr_dataIO.cpp
* @brief Data IO class
* @author Hermann von Hasseln, DL3HVH
* @version 0.1
* @date 2011-10-01
*/
/*
* Copyright 2011 Hermann von Hasseln, DL3HVH
*
* 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_DATAIO
#include "cusdr_dataIO.h"
#include "soundout.h"
#if defined(Q_OS_WIN32)
#include <winsock2.h>
#endif
#include <iostream>
using namespace std;
DataIO::DataIO(THPSDRParameter *ioData)
: QObject()
, set(Settings::instance())
, io(ioData)
, m_dataIOSocketOn(false)
, m_setNetworkDeviceHeader(true)
, m_sequence(0)
, m_oldSequence(-1)
, m_sequenceWideBand(0)
, m_oldSequenceWideBand(-1)
, m_wbBuffers(set->getWidebandBuffers() - 1)
, m_wbCount(0)
, m_socketBufferSize(set->getSocketBufferSize())
, m_sendEP4(false)
, m_manualBufferSize(set->getManualSocketBufferSize())
, m_packetsToggle(true)
, m_firstFrame(true)
, m_stopped(false)
{
m_dataIOSocket = 0;
m_metisGetDataSignature.resize(3);
m_metisGetDataSignature[0] = (char)0xEF;
m_metisGetDataSignature[1] = (char)0xFE;
m_metisGetDataSignature[2] = (char)0x01;
//m_metisGetDataSignature[3] = (char)0x06;
m_datagram.resize(1032);
m_wbDatagram.resize(0);
m_twoFramesDatagram.resize(0);
m_sendSequence = 0L;
m_oldSendSequence = 0L;
m_deviceSendDataSignature.resize(4);
m_deviceSendDataSignature[0] = (char)0xEF;
m_deviceSendDataSignature[1] = (char)0xFE;
m_deviceSendDataSignature[2] = (char)0x01;
m_deviceSendDataSignature[3] = (char)0x02;
m_packetLossTime.start();
CHECKED_CONNECT(
set,
SIGNAL(sampleRateChanged(QObject *, int)),
this,
SLOT(setSampleRate(QObject *, int)));
CHECKED_CONNECT(
set,
SIGNAL(manualSocketBufferChanged(QObject*, bool)),
this,
SLOT(setManualSocketBufferSize(QObject*, bool)));
CHECKED_CONNECT(
set,
SIGNAL(socketBufferSizeChanged(QObject*, int)),
this,
SLOT(setSocketBufferSize(QObject*, int)));
m_message = "m_sendSequence = %1, bytes sent: %2";
m_pSoundCardOut = new CSoundOut(this);
//RRK pass -1 to get the systems "default" audio device
m_pSoundCardOut->Start(-1, true, 48000, false);
m_pSoundCardOut->SetVolume(80);
}
DataIO::~DataIO() {
if (m_dataIOSocketOn) {
m_dataIOSocket->close();
delete m_dataIOSocket;
m_dataIOSocket = 0;
}
}
void DataIO::stop() {
io->networkIOMutex.lock();
m_stopped = true;
io->networkIOMutex.unlock();
if(m_pSoundCardOut) {
m_pSoundCardOut->Stop();
delete m_pSoundCardOut;
}
}
void DataIO::initDataReceiverSocket() {
m_dataIOSocket = new QUdpSocket();
int newBufferSize;
if (m_manualBufferSize) {
newBufferSize = m_socketBufferSize * 1024;//m_socketBufferSize * 1032;
io->networkIOMutex.lock();
DATAIO_DEBUG << "initDataReceiverSocket socket buffer size set to " << m_socketBufferSize << " kB.";
io->networkIOMutex.unlock();
}
else {
if (io->samplerate == 384000) {
newBufferSize = 128*1024;//128 * 1032;
io->networkIOMutex.lock();
DATAIO_DEBUG << "socket buffer size set to 128 kB.";
io->networkIOMutex.unlock();
}
else if (io->samplerate == 192000) {
newBufferSize = 64*1024;//64 * 1032;
io->networkIOMutex.lock();
DATAIO_DEBUG << "socket buffer size set to 64 kB.";
io->networkIOMutex.unlock();
}
else if (io->samplerate == 96000) {
newBufferSize = 32*1024;//32 * 1032;
io->networkIOMutex.lock();
DATAIO_DEBUG << "socket buffer size set to 32 kB.";
io->networkIOMutex.unlock();
}
else if (io->samplerate == 48000) {
newBufferSize = 16*1024;//16 * 1032;
io->networkIOMutex.lock();
DATAIO_DEBUG << "socket buffer size set to 16 kB.";
io->networkIOMutex.unlock();
}
}
if (m_dataIOSocket->bind(QHostAddress(set->getHPSDRDeviceLocalAddr()),
set->getMetisPort(),
QUdpSocket::DontShareAddress))
//QUdpSocket::ReuseAddressHint | QUdpSocket::ShareAddress))
{
#if defined(Q_OS_WIN32)
if (::setsockopt(m_dataIOSocket->socketDescriptor(), SOL_SOCKET,
SO_RCVBUF, (char *)&newBufferSize, sizeof(newBufferSize)) == -1) {
io->networkIOMutex.lock();
DATAIO_DEBUG << "dataIOSocket error!";
io->networkIOMutex.unlock();
}
#endif
//m_dataIOSocket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
//m_dataIOSocket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
CHECKED_CONNECT(
m_dataIOSocket,
SIGNAL(error(QAbstractSocket::SocketError)),
this,
SLOT(displayDataReceiverSocketError(QAbstractSocket::SocketError)));
/*CHECKED_CONNECT_OPT(
m_dataIOSocket,
SIGNAL(readyRead()),
this,
SLOT(readDeviceData()),
Qt::DirectConnection);*/
CHECKED_CONNECT(
m_dataIOSocket,
SIGNAL(readyRead()),
this,
SLOT(readDeviceData()));
io->networkIOMutex.lock();
DATAIO_DEBUG << "data receiver socket bound successful to local port " << m_dataIOSocket->localPort();
io->networkIOMutex.unlock();
m_dataIOSocketOn = true;
set->setPacketLoss(1);
}
else {
io->networkIOMutex.lock();
DATAIO_DEBUG << "data receiver socket binding failed.";
io->networkIOMutex.unlock();
m_dataIOSocketOn = false;
}
}
void DataIO::readDeviceData() {
while (m_dataIOSocket->hasPendingDatagrams()) {
QMutexLocker locker(&io->networkIOMutex);
if (m_dataIOSocket->readDatagram(m_datagram.data(), m_datagram.size()) == METIS_DATA_SIZE) {
if (m_datagram.left(3) == m_metisGetDataSignature) {
if (m_datagram[3] == (char)0x06) {
m_sequence = (m_datagram[4] & 0xFF) << 24;
m_sequence += (m_datagram[5] & 0xFF) << 16;
m_sequence += (m_datagram[6] & 0xFF) << 8;
m_sequence += (m_datagram[7] & 0xFF);
if (m_sequence != m_oldSequence + 1) {
//DATAIO_DEBUG << "readData missed " << m_sequence - m_oldSequence << " packages.";
//RRK cout << "readData missed " << m_sequence - m_oldSequence << " packages." << endl;
if (m_packetLossTime.elapsed() > 100) {
set->setPacketLoss(2);
m_packetLossTime.restart();
}
}
m_oldSequence = m_sequence;
//// enqueue first half of the HPSDR frame from the HPSDR device
//io->iq_queue.enqueue(m_datagram.mid(METIS_HEADER_SIZE, BUFFER_SIZE/2));
//// enqueue second half of the HPSDR frame from the HPSDR device
//io->iq_queue.enqueue(m_datagram.right(BUFFER_SIZE/2));
// enqueue one frame from the HPSDR device
if (!io->iq_queue.isFull()) {
io->iq_queue.enqueue(m_datagram.mid(METIS_HEADER_SIZE, BUFFER_SIZE));
}
// collect two HPSDR frames
//if (m_firstFrame) {
// m_twoFramesDatagram += m_datagram.mid(METIS_HEADER_SIZE, BUFFER_SIZE);
// m_firstFrame = false;
//}
//else {
// m_twoFramesDatagram += m_datagram.mid(METIS_HEADER_SIZE, BUFFER_SIZE);
// //enqueue the two frames
// io->iq_queue.enqueue(m_twoFramesDatagram);
// m_firstFrame = true;
// m_twoFramesDatagram.resize(0);
//}
}
else if (m_datagram[3] == (char)0x04) { // wide band data
//qDebug() << "wideband data received!";
m_sequenceWideBand = (m_datagram[4] & 0xFF) << 24;
m_sequenceWideBand += (m_datagram[5] & 0xFF) << 16;
m_sequenceWideBand += (m_datagram[6] & 0xFF) << 8;
m_sequenceWideBand += (m_datagram[7] & 0xFF);
if (m_sequenceWideBand != m_oldSequenceWideBand + 1) {
DATAIO_DEBUG << "wideband readData missed " << m_sequenceWideBand - m_oldSequenceWideBand << " packages.";
if (m_packetLossTime.elapsed() > 100) {
set->setPacketLoss(2);
m_packetLossTime.restart();
}
}
m_oldSequenceWideBand = m_sequenceWideBand;
// three 'if's from KISS Konsole
if ((m_wbBuffers & m_datagram[7]) == 0)
{
m_sendEP4 = true;
m_wbCount = 0;
}
if (m_sendEP4)
{
m_wbDatagram.append(m_datagram.mid(METIS_HEADER_SIZE, BUFFER_SIZE));
}
if (m_wbCount++ == m_wbBuffers)
{
// enqueue
m_sendEP4 = false;
io->wb_queue.enqueue(m_wbDatagram);
m_wbDatagram.resize(0);
}
}
}
//DATA_RECEIVER_DEBUG << "got wrong HPSDR device signature!";
}
//DATA_RECEIVER_DEBUG << "got wrong HPSDR device data size!";
}
//DATA_RECEIVER_DEBUG << "no more pending datagrams.";
}
void DataIO::readData() {
qint64 length = io->inputBuffer.length();
//int buffers = qRound(length/(2*BUFFER_SIZE));
int buffers = qRound((float) length/128);
DATAIO_DEBUG << "input buffer length " << length << " buffers " << buffers;
while (!m_stopped) {
for (int i = 0; i < buffers; i++) {
//io->data_queue.enqueue(io->inputBuffer.mid(i*2*BUFFER_SIZE, 2*BUFFER_SIZE));
io->data_queue.enqueue(io->inputBuffer.mid(i*128, 128));
if (m_stopped) break;
}
}
m_stopped = false;
}
void DataIO::sendInitFramesToNetworkDevice(int rx) {
QByteArray initDatagram;
initDatagram.resize(1032);
initDatagram[0] = (char)0xEF;
initDatagram[1] = (char)0xFE;
initDatagram[2] = (char)0x01;
initDatagram[3] = (char)0x02;
initDatagram[4] = (char)0x00;
initDatagram[5] = (char)0x00;
initDatagram[6] = (char)0x00;
initDatagram[7] = (char)0x00;
initDatagram[8] = SYNC;
initDatagram[9] = SYNC;
initDatagram[10] = SYNC;
for (int i = 0; i < 5; i++) {
initDatagram[i + 11] = io->control_out[i];
}
for (int i = 16; i < 520; i++) {
initDatagram[i] = 0x00;
}
initDatagram[520] = SYNC;
initDatagram[521] = SYNC;
initDatagram[522] = SYNC;
initDatagram[523] = io->control_out[0] | ((rx + 2) << 1);
initDatagram[524] = set->getCtrFrequencies().at(rx) >> 24;
initDatagram[525] = set->getCtrFrequencies().at(rx) >> 16;
initDatagram[526] = set->getCtrFrequencies().at(rx) >> 8;
initDatagram[527] = set->getCtrFrequencies().at(rx) ;
for (int i = 528; i < 1032; i++) initDatagram[i] = 0x00;
// for (int i = 0; i < 5; i++) {
//
// if (m_dataIOSocket->writeDatagram(initDatagram.data(), initDatagram.size(), io->hpsdrDeviceIPAddress, DEVICE_PORT) < 0) {
//
// io->networkIOMutex.lock();
// DATAIO_DEBUG << "error sending init data to device: " << qPrintable(m_dataIOSocket->errorString());
// io->networkIOMutex.unlock();
// }
// else {
//
// if (i == 0) {
//
// io->networkIOMutex.lock();
// DATAIO_DEBUG << "init frames sent to network device.";
// io->networkIOMutex.unlock();
// }
// }
// }
if (m_dataIOSocket->writeDatagram(initDatagram.data(), initDatagram.size(), io->hpsdrDeviceIPAddress, DEVICE_PORT) < 0) {
io->networkIOMutex.lock();
DATAIO_DEBUG << "error sending init data to device: " << qPrintable(m_dataIOSocket->errorString());
io->networkIOMutex.unlock();
}
else {
io->networkIOMutex.lock();
DATAIO_DEBUG << "init frames sent to network device.";
io->networkIOMutex.unlock();
}
SleeperThread::msleep(20);
if (m_dataIOSocket->writeDatagram(initDatagram.data(), initDatagram.size(), io->hpsdrDeviceIPAddress, DEVICE_PORT) < 0) {
io->networkIOMutex.lock();
DATAIO_DEBUG << "error sending init data to device: " << qPrintable(m_dataIOSocket->errorString());
io->networkIOMutex.unlock();
}
else {
io->networkIOMutex.lock();
DATAIO_DEBUG << "init frames sent to network device.";
io->networkIOMutex.unlock();
}
}
void DataIO::networkDeviceStartStop(char value) {
TNetworkDevicecard metis = set->getCurrentMetisCard();
//QUdpSocket socket;
// if (socket.bind(QHostAddress(set->getHPSDRDeviceLocalAddr()),
// set->getMetisPort(),
// //QUdpSocket::DefaultForPlatform))
// QUdpSocket::ReuseAddressHint | QUdpSocket::ShareAddress))
// {
//DATAIO_DEBUG << "device start/stop: socket bound successful to local port " << set->getMetisPort();
m_commandDatagram.resize(64);
m_commandDatagram[0] = (char)0xEF;
m_commandDatagram[1] = (char)0xFE;
m_commandDatagram[2] = (char)0x04;
m_commandDatagram[3] = (char)value;
for (int i = 4; i < 64; i++) m_commandDatagram[i] = 0x00;
if (m_dataIOSocket->writeDatagram(m_commandDatagram, metis.ip_address, DEVICE_PORT) == 64) {
//if (value == 1) {
if (value != 0) {
io->networkIOMutex.lock();
DATAIO_DEBUG << "sent start command to device at: "<< qPrintable(metis.ip_address.toString());
io->networkIOMutex.unlock();
m_networkDeviceRunning = true;
}
else {
//DATA_ENGINE_DEBUG << "sent stop command to Metis at"<< m_metisCards[0].ip_address.toString();
io->networkIOMutex.lock();
DATAIO_DEBUG << "sent stop command to device at: "<< qPrintable(metis.ip_address.toString());
io->networkIOMutex.unlock();
m_networkDeviceRunning = false;
}
}
else
DATAIO_DEBUG << "device start/stop: sending command to device failed.";
//socket.close();
// }
// else {
//
// DATAIO_DEBUG << "device start/stop: socket binding failed.";
// }
// socket.close();
// DATAIO_DEBUG << "device start/stop: socket closed.";
}
void DataIO::sendAudio(u_char *buf) {
//RRK send audio bytes here
static TYPECPX cbuf[252];
int i, j;
short sample;
for(i = 8, j = 0; i < 512; i += 8, j++) {
//bytes are L,R,I,Q skip the I,Q
sample = buf[i] << 8 | buf[i+1]; //left
cbuf[j].re = (double)sample;
sample = buf[i+2] << 8 | buf[i+3]; //right
cbuf[j].im = (double)sample;
}
if(m_pSoundCardOut)
m_pSoundCardOut->PutOutQueue(63, cbuf);
}
void DataIO::writeData() {
if (m_setNetworkDeviceHeader) {
m_outDatagram.resize(0);
m_outDatagram += m_deviceSendDataSignature;
QByteArray seq(reinterpret_cast<const char*>(&m_sendSequence), sizeof(m_sendSequence));
m_outDatagram += seq;
m_outDatagram += io->audioDatagram;
m_sendSequence++;
m_setNetworkDeviceHeader = false;
}
else {
m_outDatagram += io->audioDatagram;
if (m_dataIOSocket->writeDatagram(m_outDatagram, set->getCurrentMetisCard().ip_address, DEVICE_PORT) < 0) {
DATAIO_DEBUG << "error sending data to device: " << m_dataIOSocket->errorString();
}
//if (m_sendSequence%100 == 0)
// DATAIO_DEBUG << m_sendSequence;
if (m_sendSequence != m_oldSendSequence + 1) {
DATAIO_DEBUG << "output sequence error: old = " << m_oldSendSequence << "; new =" << m_sendSequence;
}
m_oldSendSequence = m_sendSequence;
m_setNetworkDeviceHeader = true;
}
}
void DataIO::displayDataReceiverSocketError(QAbstractSocket::SocketError error) {
io->networkIOMutex.lock();
DATAIO_DEBUG << "data IO socket error: " << error;
io->networkIOMutex.unlock();
}
void DataIO::setManualSocketBufferSize(QObject *sender, bool value) {
Q_UNUSED (sender)
m_manualBufferSize = value;
DATAIO_DEBUG << "m_manualBufferSize to change = " << m_manualBufferSize;
int socketBufferSize = 1032 * set->getSocketBufferSize();
io->networkIOMutex.lock();
if (m_manualBufferSize) {
DATAIO_DEBUG << "set data IO socket BufferSize to " << m_socketBufferSize;
#if defined(Q_OS_WIN32)
if (::setsockopt(m_dataIOSocket->socketDescriptor(), SOL_SOCKET,
SO_RCVBUF, (char *)&socketBufferSize, sizeof(socketBufferSize)) == -1) {
io->networkIOMutex.lock();
DATAIO_DEBUG << "dataIOSocket error!";
io->networkIOMutex.unlock();
}
#endif
}
else {
DATAIO_DEBUG << "set data IO socket BufferSize to 32 kB.";
socketBufferSize = 1032 * 32;
#if defined(Q_OS_WIN32)
if (::setsockopt(m_dataIOSocket->socketDescriptor(), SOL_SOCKET,
SO_RCVBUF, (char *)&socketBufferSize, sizeof(socketBufferSize)) == -1) {
io->networkIOMutex.lock();
DATAIO_DEBUG << "dataIOSocket error!";
io->networkIOMutex.unlock();
}
#endif
}
io->networkIOMutex.unlock();
}
void DataIO::setSocketBufferSize(QObject *sender, int value) {
Q_UNUSED (sender)
int socketBufferSize = value * 1024;
DATAIO_DEBUG << "m_socketBufferSize = " << value;
io->networkIOMutex.lock();
#if defined(Q_OS_WIN32)
if (::setsockopt(m_dataIOSocket->socketDescriptor(), SOL_SOCKET,
SO_RCVBUF, (char *)&socketBufferSize, sizeof(socketBufferSize)) == -1) {
io->networkIOMutex.lock();
DATAIO_DEBUG << "dataIOSocket error!";
io->networkIOMutex.unlock();
}
#endif
io->networkIOMutex.unlock();
}
void DataIO::setSampleRate(QObject *sender, int value) {
Q_UNUSED(sender)
int bufferSize;
io->networkIOMutex.lock();
switch (value) {
case 48000:
bufferSize = 16*1024;//128 * 1032
DATAIO_DEBUG << "socket buffer size set to 16 kB.";
break;
case 96000:
bufferSize = 32*1024;//128 * 1032
DATAIO_DEBUG << "socket buffer size set to 32 kB.";
break;
case 192000:
bufferSize = 64*1024;//128 * 1032
DATAIO_DEBUG << "socket buffer size set to 64 kB.";
break;
case 384000:
bufferSize = 128*1024;//128 * 1032
DATAIO_DEBUG << "socket buffer size set to 128 kB.";
break;
default:
DATAIO_DEBUG << "invalid sample rate !\n";
break;
}
#if defined(Q_OS_WIN32)
if (::setsockopt(m_dataIOSocket->socketDescriptor(), SOL_SOCKET,
SO_RCVBUF, (char *)&bufferSize, sizeof(bufferSize)) == -1) {
DATAIO_DEBUG << "dataIOSocket error!";
}
#endif
io->networkIOMutex.unlock();
}
+120
View File
@@ -0,0 +1,120 @@
/**
* @file cusdr_dataIO.h
* @brief Data IO header file
* @author Hermann von Hasseln, DL3HVH
* @version 0.1
* @date 2011-10-01
*/
/*
* Copyright 2011 Hermann von Hasseln, DL3HVH
*
* 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.
*/
#ifndef _CUSDR_DATAIO_H
#define _CUSDR_DATAIO_H
//#include <QObject>
//#include <QMutex>
//#include <QByteArray>
//#include <QBuffer>
//#include <QVector>
//#include <QList>
//#include <QWaitCondition>
//#include <QThread>
#include "cusdr_settings.h"
#include "soundout.h"
#ifdef LOG_DATAIO
# define DATAIO_DEBUG qDebug().nospace() << "DataIO::\t"
#else
# define DATAIO_DEBUG nullDebug()
#endif
class DataIO : public QObject {
Q_OBJECT
public:
DataIO(THPSDRParameter *ioData = 0);
~DataIO();
public slots:
void stop();
void initDataReceiverSocket();
void readData();
void writeData();
void sendAudio(u_char *buf);
void sendInitFramesToNetworkDevice(int rx);
void networkDeviceStartStop(char value);
//void setWidebandBuffers(int value);
private slots:
void setSampleRate(QObject *sender, int value);
void displayDataReceiverSocketError(QAbstractSocket::SocketError error);
void setManualSocketBufferSize(QObject *sender, bool value);
void setSocketBufferSize(QObject *sender, int value);
void readDeviceData();
private:
Settings* set;
QUdpSocket* m_dataIOSocket;
//QMutex m_mutex;
QByteArray m_commandDatagram;
QByteArray m_datagram;
QByteArray m_wbDatagram;
QByteArray m_twoFramesDatagram;
QByteArray m_metisGetDataSignature;
QByteArray m_outDatagram;
QByteArray m_deviceSendDataSignature;
QString m_message;
QTime m_packetLossTime;
THPSDRParameter* io;
//TNetworkDevicecard netDevice;
bool m_dataIOSocketOn;
bool m_networkDeviceRunning;
bool m_setNetworkDeviceHeader;
long m_sequence;
long m_oldSequence;
long m_sequenceWideBand;
long m_oldSequenceWideBand;
long m_sendSequence;
long m_oldSendSequence;
int m_wbBuffers;
int m_wbCount;
int m_socketBufferSize;
bool m_sendEP4;
bool m_manualBufferSize;
bool m_packetsToggle;
bool m_firstFrame;
volatile bool m_stopped;
CSoundOut* m_pSoundCardOut;
signals:
void messageEvent(QString message);
};
#endif // _CUSDR_DATAIO_H
+271
View File
@@ -0,0 +1,271 @@
/**
* @file cusdr_discoverer.cpp
* @brief HPSDR device discoverer class
* @author Hermann von Hasseln, DL3HVH
* @version 0.1
* @date 2012-05-19
*/
/*
*
* Copyright 2012 Hermann von Hasseln, DL3HVH
*
* 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_DISCOVERER
#include "cusdr_discoverer.h"
#include "Util/cusdr_buttons.h"
//#include <QComboBox>
//#include <QDialogButtonBox>
//#include <QLabel>
//#include <QPushButton>
//#include <QVBoxLayout>
//#include <QCheckBox>
//#include <QSlider>
//#include <QSpinBox>
//#define btn_height 18
//#define btn_width 74
Discoverer::Discoverer(THPSDRParameter *ioData)
: QObject()
, set(Settings::instance())
, io(ioData)
{
m_deviceCards = set->getMetisCardsList();
}
Discoverer::~Discoverer() {
}
void Discoverer::initHPSDRDevice() {
m_searchTime.start();
int deviceNo = 0;
while (deviceNo == 0) {
deviceNo = findHPSDRDevices();
if (deviceNo > 1) {
set->setHPSDRDeviceNumber(deviceNo);
break;
}
if (deviceNo > 0) {
set->setHPSDRDeviceNumber(deviceNo);
break;
}
if (m_searchTime.elapsed() > 1000) {
set->setHPSDRDeviceNumber(0);
break;
}
io->networkIOMutex.lock();
DISCOVERER_DEBUG << "no device found - trying again...";
io->networkIOMutex.unlock();
}
io->networkIOMutex.lock();
io->devicefound.wakeAll();
io->networkIOMutex.unlock();
}
int Discoverer::findHPSDRDevices() {
int devicesFound = 0;
m_findDatagram.resize(63);
m_findDatagram[0] = (char)0xEF;
m_findDatagram[1] = (char)0xFE;
m_findDatagram[2] = (char)0x02;
for (int i = 3; i < 63; i++)
m_findDatagram[i] = (char)0x00;
QUdpSocket socket;
CHECKED_CONNECT(
&socket,
SIGNAL(error(QAbstractSocket::SocketError)),
this,
SLOT(displayDiscoverySocketError(QAbstractSocket::SocketError)));
io->networkIOMutex.lock();
DISCOVERER_DEBUG << "using " << qPrintable(QHostAddress(set->getHPSDRDeviceLocalAddr()).toString()) << " for discovery.";
io->networkIOMutex.unlock();
// clear comboBox entries in the network dialogue
set->clearNetworkIOComboBoxEntry();
#if defined(Q_OS_WIN32)
if (socket.bind(
QHostAddress(set->getHPSDRDeviceLocalAddr()), 0,
QUdpSocket::ReuseAddressHint | QUdpSocket::ShareAddress))
//QUdpSocket::ReuseAddressHint))
{
set->setMetisPort(this, socket.localPort());
io->networkIOMutex.lock();
DISCOVERER_DEBUG << "discovery_socket bound successfully to port " << socket.localPort();
io->networkIOMutex.unlock();
}
else {
io->networkIOMutex.lock();
DISCOVERER_DEBUG << "discovery_socket bind failed.";
io->networkIOMutex.unlock();
socket.close();
return 0;
}
#elif defined(Q_OS_LINUX)
if (socket.bind(
QHostAddress(set->getHPSDRDeviceLocalAddr()),
QUdpSocket::DefaultForPlatform))
{
CHECKED_CONNECT(
&socket,
SIGNAL(error(QAbstractSocket::SocketError)),
this,
SLOT(displayDiscoverySocketError(QAbstractSocket::SocketError)));
set->setMetisPort(this, socket.localPort());
io->networkIOMutex.lock();
DISCOVERER_DEBUG << "discovery_socket bound successfully to port " << socket.localPort();
io->networkIOMutex.unlock();
}
else {
io->networkIOMutex.lock();
DISCOVERER_DEBUG << "discovery_socket bind failed.";
io->networkIOMutex.unlock();
socket.close();
return 0;
}
#endif
if (socket.writeDatagram(m_findDatagram, QHostAddress::Broadcast, DEVICE_PORT) == 63) {
io->networkIOMutex.lock();
DISCOVERER_DEBUG << "discovery data sent.";
io->networkIOMutex.unlock();
}
else {
io->networkIOMutex.lock();
DISCOVERER_DEBUG << "discovery data not sent.";
io->networkIOMutex.unlock();
}
// wait a little
//SleeperThread::msleep(30);
SleeperThread::msleep(500);
while (socket.hasPendingDatagrams()) {
TNetworkDevicecard mc;
quint16 port;
m_deviceDatagram.resize(socket.pendingDatagramSize());
socket.readDatagram(m_deviceDatagram.data(), m_deviceDatagram.size(), &mc.ip_address, &port);
if (m_deviceDatagram[0] == (char)0xEF && m_deviceDatagram[1] == (char)0xFE) {
if (m_deviceDatagram[2] == (char)0x02) {
sprintf(mc.mac_address, "%02X:%02X:%02X:%02X:%02X:%02X",
m_deviceDatagram[3] & 0xFF, m_deviceDatagram[4] & 0xFF, m_deviceDatagram[5] & 0xFF,
m_deviceDatagram[6] & 0xFF, m_deviceDatagram[7] & 0xFF, m_deviceDatagram[8] & 0xFF);
io->networkIOMutex.lock();
DISCOVERER_DEBUG << "Device found at " << qPrintable(mc.ip_address.toString()) << ":" << port << "; Mac addr: [" << mc.mac_address << "]";
DISCOVERER_DEBUG << "Device code version: " << qPrintable(QString::number(m_deviceDatagram.at(9), 16));
io->networkIOMutex.unlock();
int no = m_deviceDatagram.at(10);
QString str;
if (no == 0)
str = "Metis";
else if (no == 1)
str = "Hermes";
else if (no == 2)
str = "Griffin";
else if (no == 4)
str = "Angelia";
mc.boardID = no;
mc.boardName = str;
io->networkIOMutex.lock();
DISCOVERER_DEBUG << "Device board ID: " << no;
DISCOVERER_DEBUG << "Device is: " << qPrintable(str);
io->networkIOMutex.unlock();
m_deviceCards.append(mc);
str += " (";
str += mc.ip_address.toString();
str += ")";
set->addNetworkIOComboBoxEntry(str);
devicesFound++;
}
else if (m_deviceDatagram[2] == (char)0x03) {
io->networkIOMutex.lock();
DISCOVERER_DEBUG << "Device already sending data!";
io->networkIOMutex.unlock();
}
}
}
set->setMetisCardList(m_deviceCards);
if (devicesFound == 1) {
set->setCurrentHPSDRDevice(m_deviceCards.at(0));
io->networkIOMutex.lock();
DISCOVERER_DEBUG << "Device selected: " << qPrintable(m_deviceCards.at(0).ip_address.toString());
io->networkIOMutex.unlock();
}
socket.close();
return devicesFound;
}
void Discoverer::displayDiscoverySocketError(QAbstractSocket::SocketError error) {
io->networkIOMutex.lock();
DISCOVERER_DEBUG << "discovery socket error: " << error;
io->networkIOMutex.unlock();
}
void Discoverer::clear() {
//m_metisDeviceComboBox->clear();
m_deviceCards.clear();
}
+86
View File
@@ -0,0 +1,86 @@
/**
* @file cusdr_discoverer.h
* @brief HPSDR device discoverer header file
* @author Hermann von Hasseln, DL3HVH
* @version 0.1
* @date 2012-05-19
*/
/*
*
* Copyright 2012 Hermann von Hasseln, DL3HVH
*
* 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.
*/
#ifndef _CUSDR_DISCOVERER_H
#define _CUSDR_DISCOVERER_H
//#include <QObject>
//#include <QComboBox>
//#include <QDialogButtonBox>
//#include <QLabel>
//#include <QPushButton>
//#include <QVBoxLayout>
//#include <QCheckBox>
//#include <QSlider>
//#include <QSpinBox>
//#include <QElapsedTimer>
#include "cusdr_settings.h"
#ifdef LOG_DISCOVERER
# define DISCOVERER_DEBUG qDebug().nospace() << "Discoverer::\t"
#else
# define DISCOVERER_DEBUG nullDebug()
#endif
class Discoverer : public QObject {
Q_OBJECT
public:
Discoverer(THPSDRParameter *ioData = 0);
~Discoverer();
int findHPSDRDevices();
void clear();
public slots:
void initHPSDRDevice();
private slots:
void displayDiscoverySocketError(QAbstractSocket::SocketError error);
private:
Settings* set;
THPSDRParameter* io;
QTime m_searchTime;
QByteArray m_findDatagram;
QByteArray m_deviceDatagram;
//QString m_deviceStr;
TNetworkDevicecard m_deviceCard;
QList<TNetworkDevicecard> m_deviceCards;
signals:
};
#endif // _CUSDR_DISCOVERER_H
+772
View File
@@ -0,0 +1,772 @@
/**
* @file cusdr_receiver.cpp
* @brief cuSDR receiver class
* @author Hermann von Hasseln, DL3HVH
* @version 0.1
* @date 2010-11-12
*/
/* Copyright (C)
*
* 2010 - Hermann von Hasseln, DL3HVH
*
* 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; either version 2
* of the License, or (at your option) any later version.
*
* 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 General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#define LOG_RECEIVER
// use: RECEIVER_DEBUG
#include "cusdr_receiver.h"
Receiver::Receiver(int rx)
: QObject()
, set(Settings::instance())
, m_filterMode(set->getCurrentFilterMode())
, m_stopped(false)
, m_receiver(rx)
, m_samplerate(set->getSampleRate())
, m_audioMode(1)
//, m_calOffset(63.0)
//, m_calOffset(33.0)
{
setReceiverData(set->getReceiverDataList().at(m_receiver));
InitCPX(inBuf, BUFFER_SIZE, 0.0f);
InitCPX(outBuf, BUFFER_SIZE, 0.0f);
newSpectrum.resize(BUFFER_SIZE*4);
qtdsp = 0;
setupConnections();
highResTimer = new HResTimer();
m_displayTime = (int)(1000000.0/set->getFramesPerSecond(m_receiver));
m_smeterTime.start();
}
Receiver::~Receiver() {
inBuf.clear();
outBuf.clear();
if (qtdsp) {
delete qtdsp;
qtdsp = 0;
}
if (highResTimer) {
delete highResTimer;
}
m_stopped = false;
}
void Receiver::setupConnections() {
CHECKED_CONNECT(
set,
SIGNAL(systemStateChanged(
QObject *,
QSDR::_Error,
QSDR::_HWInterfaceMode,
QSDR::_ServerMode,
QSDR::_DataEngineState)),
this,
SLOT(setSystemState(
QObject *,
QSDR::_Error,
QSDR::_HWInterfaceMode,
QSDR::_ServerMode,
QSDR::_DataEngineState)));
CHECKED_CONNECT(
set,
SIGNAL(mainVolumeChanged(QObject *, int, float)),
this,
SLOT(setAudioVolume(QObject *, int, float)));
CHECKED_CONNECT(
set,
SIGNAL(sampleRateChanged(QObject *, int)),
this,
SLOT(setSampleRate(QObject *, int)));
CHECKED_CONNECT(
set,
SIGNAL(dspModeChanged(QObject *, int, DSPMode)),
this,
SLOT(setDspMode(QObject *, int, DSPMode)));
CHECKED_CONNECT(
set,
SIGNAL(hamBandChanged(QObject *, int, bool, HamBand)),
this,
SLOT(setHamBand(QObject *, int, bool, HamBand)));
CHECKED_CONNECT(
set,
SIGNAL(agcModeChanged(QObject *, int, AGCMode, bool)),
this,
SLOT(setAGCMode(QObject *, int, AGCMode, bool)));
CHECKED_CONNECT(
set,
SIGNAL(agcGainChanged(QObject *, int, int)),
this,
SLOT(setAGCGain(QObject *, int, int)));
// CHECKED_CONNECT(
// set,
// SIGNAL(agcMaximumGain_dBmChanged(QObject *, int, int)),
// this,
// SLOT(setAGCMaximumGain_dBm(QObject *, int, int)));
CHECKED_CONNECT(
set,
SIGNAL(agcMaximumGainChanged_dB(QObject *, int, qreal)),
this,
SLOT(setAGCMaximumGain_dB(QObject *, int, qreal)));
CHECKED_CONNECT(
set,
SIGNAL(agcFixedGainChanged_dB(QObject *, int, qreal)),
this,
SLOT(setAGCFixedGain_dB(QObject *, int, qreal)));
CHECKED_CONNECT(
set,
SIGNAL(agcThresholdChanged_dB(QObject *, int, qreal)),
this,
SLOT(setAGCThreshold_dB(QObject *, int, qreal)));
CHECKED_CONNECT(
set,
SIGNAL(agcHangThresholdChanged(QObject *, int, int)),
this,
SLOT(setAGCHangThreshold(QObject *, int, int)));
CHECKED_CONNECT(
set,
SIGNAL(agcHangLevelChanged_dB(QObject *, int, qreal)),
this,
SLOT(setAGCHangLevel_dB(QObject *, int, qreal)));
CHECKED_CONNECT(
set,
SIGNAL(agcVariableGainChanged_dB(QObject *, int, qreal)),
this,
SLOT(setAGCVariableGain_dB(QObject *, int, qreal)));
CHECKED_CONNECT(
set,
SIGNAL(agcAttackTimeChanged(QObject *, int, qreal)),
this,
SLOT(setAGCAttackTime(QObject *, int, qreal)));
CHECKED_CONNECT(
set,
SIGNAL(agcDecayTimeChanged(QObject *, int, qreal)),
this,
SLOT(setAGCDecayTime(QObject *, int, qreal)));
CHECKED_CONNECT(
set,
SIGNAL(agcHangTimeChanged(QObject *, int, qreal)),
this,
SLOT(setAGCHangTime(QObject *, int, qreal)));
CHECKED_CONNECT(
set,
SIGNAL(filterFrequenciesChanged(QObject *, int, qreal, qreal)),
this,
SLOT(setFilterFrequencies(QObject *, int, qreal, qreal)));
CHECKED_CONNECT(
set,
SIGNAL(framesPerSecondChanged(QObject*, int, int)),
this,
SLOT(setFramesPerSecond(QObject*, int, int)));
/*CHECKED_CONNECT(
set,
SIGNAL(receiverDataReady()),
this,
SLOT(dspProcessing()));*/
}
void Receiver::setReceiverData(TReceiver data) {
m_receiverData = data;
//m_serverMode = m_receiverData.serverMode;
m_dspCore = m_receiverData.dspCore;
m_sampleRate = m_receiverData.sampleRate;
m_hamBand = m_receiverData.hamBand;
m_dspMode = m_receiverData.dspMode;
m_dspModeList = m_receiverData.dspModeList;
m_agcMode = m_receiverData.agcMode;
m_agcGain = m_receiverData.acgGain;
m_agcFixedGain_dB = m_receiverData.agcFixedGain_dB;
m_agcMaximumGain_dB = m_receiverData.agcMaximumGain_dB;
m_agcHangThreshold = m_receiverData.agcHangThreshold;
m_agcVariableGain = m_receiverData.agcVariableGain;
m_audioVolume = m_receiverData.audioVolume;
m_filterLo = m_receiverData.filterLo;
m_filterHi = m_receiverData.filterHi;
m_lastCtrFrequencyList = m_receiverData.lastCenterFrequencyList;
m_lastVfoFrequencyList = m_receiverData.lastVfoFrequencyList;
m_mercuryAttenuators = m_receiverData.mercuryAttenuators;
}
bool Receiver::initDSPInterface() {
if (m_dspCore == QSDR::QtDSP) {
if (!initQtDSPInterface()) return false;
}
return true;
}
bool Receiver::initQtDSPInterface() {
qtdsp = new QDSPEngine(this, m_receiver, BUFFER_SIZE);
if (qtdsp)
qtdsp->setQtDSPStatus(true);
else {
RECEIVER_DEBUG << "could not start QtDSP for receiver: " << m_receiver;
qtdsp = 0;
return false;
}
qtdsp->setVolume(m_audioVolume);
DSPMode mode = m_dspModeList.at(m_hamBand);
RECEIVER_DEBUG << "set DSP mode to: " << set->getDSPModeString(mode);
qtdsp->setDSPMode(mode);
qtdsp->filter->setFilter(
getFilterFromDSPMode(set->getDefaultFilterList(), mode).filterLo,
getFilterFromDSPMode(set->getDefaultFilterList(), mode).filterHi);
qtdsp->wpagc->setMode(m_agcMode);
qtdsp->wpagc->setAGCFixedGainDb(m_agcFixedGain_dB);
qtdsp->wpagc->setMaximumGainDb(m_agcMaximumGain_dB);
// if (m_agcMode == (AGCMode) agcOFF)
// set->setAGCFixedGain_dB(this, m_receiver, m_agcFixedGain_dB);
// else
// set->setAGCMaximumGain_dB(this, m_receiver, m_agcMaximumGain_dB);
RECEIVER_DEBUG << "QtDSP for receiver: " << m_receiver << " started.";
return true;
}
void Receiver::deleteDSPInterface() {
if (m_dspCore == QSDR::QtDSP)
deleteQtDSP();
}
void Receiver::deleteQtDSP() {
if (qtdsp) {
delete qtdsp;
qtdsp = 0;
}
}
void Receiver::enqueueData() {
inQueue.enqueue(inBuf);
if (inQueue.isFull()) {
RECEIVER_DEBUG << "inQueue full!";
}
}
void Receiver::stop() {
m_mutex.lock();
m_stopped = true;
m_mutex.unlock();
}
void Receiver::dspProcessing() {
//RECEIVER_DEBUG << "dspProcessing: " << this->thread();
//io.mutex.lock();
qtdsp->processDSP(inBuf, outBuf, BUFFER_SIZE);
//io.mutex.unlock();
// spectrum
qtdsp->getSpectrum(newSpectrum, set->getFFTMultiplicator());
if (highResTimer->getElapsedTimeInMicroSec() >= getDisplayDelay()) {
emit spectrumBufferChanged(m_receiver, newSpectrum);
highResTimer->start();
}
if (m_receiver == set->getCurrentReceiver()) {
// S-Meter
if (m_smeterTime.elapsed() > 20) {
m_sMeterValue = qtdsp->getSMeterInstValue();
emit sMeterValueChanged(m_receiver, m_sMeterValue);
m_smeterTime.restart();
}
// process output data
emit outputBufferSignal(m_receiver, outBuf);
}
}
void Receiver::setSampleRate(QObject *sender, int value) {
Q_UNUSED(sender)
if (m_samplerate == value) return;
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:
RECEIVER_DEBUG << "invalid sample rate (possible values are: 48, 96, 192, or 384 kHz)!\n";
break;
}
if (qtdsp)
qtdsp->setSampleRate(this, m_samplerate);
else
RECEIVER_DEBUG << "qtdsp down: cannot set sample rate!\n";
}
void Receiver::setServerMode(QSDR::_ServerMode mode) {
m_serverMode = mode;
}
QSDR::_ServerMode Receiver::getServerMode() const {
return m_serverMode;
}
QSDR::_DSPCore Receiver::getDSPCoreMode() const {
return m_dspCore;
}
//void Receiver::setSocketState(SocketState state) {
//
// m_socketState = state;
//}
//Receiver::SocketState Receiver::socketState() const {
//
// return m_socketState;
//}
void Receiver::setSystemState(
QObject *sender,
QSDR::_Error err,
QSDR::_HWInterfaceMode hwmode,
QSDR::_ServerMode mode,
QSDR::_DataEngineState state)
{
Q_UNUSED (sender)
Q_UNUSED (err)
if (m_hwInterface != hwmode)
m_hwInterface = hwmode;
if (m_serverMode != mode)
m_serverMode = mode;
if (m_dataEngineState != state)
m_dataEngineState = state;
}
void Receiver::setAudioMode(QObject* sender, int mode) {
if (sender != this && m_audioMode == mode) return;
m_audioMode = mode;
}
//void Receiver::setID(int value) {
//
// m_receiverID = value;
// RECEIVER_DEBUG << "This is receiver " << m_receiverID;
//}
void Receiver::setReceiver(int value) {
m_receiver = value;
}
void Receiver::setSampleRate(int value) {
m_sampleRate = value;
}
void Receiver::setHamBand(QObject *sender, int rx, bool byBtn, HamBand band) {
Q_UNUSED(sender)
Q_UNUSED(byBtn)
if (m_receiver == rx) {
if (m_hamBand == band) return;
m_hamBand = band;
}
}
void Receiver::setDspMode(QObject *sender, int rx, DSPMode mode) {
Q_UNUSED(sender)
if (m_receiver != rx) return;
if (m_dspMode == mode) return;
m_dspMode = mode;
if (qtdsp) {
qtdsp->setDSPMode(mode);
qtdsp->filter->setFilter(
getFilterFromDSPMode(set->getDefaultFilterList(), mode).filterLo,
getFilterFromDSPMode(set->getDefaultFilterList(), mode).filterHi);
}
//QString msg = "[receiver]: set mode for receiver %1 to %2";
//emit messageEvent(msg.arg(rx).arg(set->getDSPModeString(m_dspMode)));
}
void Receiver::setAGCMode(QObject *sender, int rx, AGCMode mode, bool hang) {
Q_UNUSED(sender)
Q_UNUSED(hang)
if (m_receiver != rx) return;
if (m_agcMode == mode) return;
m_agcMode = mode;
if (qtdsp) {
qtdsp->wpagc->setMode(mode);
}
}
void Receiver::setAGCGain(QObject *sender, int rx, int value) {
Q_UNUSED(sender)
if (m_receiver != rx) return;
if (m_agcGain == value) return;
m_agcGain = value;
if (qtdsp) {
//RECEIVER_DEBUG << "AGCThreshDB (plus offset) = " << m_agcGain - AGCOFFSET;
//qtdsp->wpagc->setAGCThreshDb(m_filterLo, m_filterHi, BUFFER_SIZE, m_agcGain - AGCOFFSET);
}
}
void Receiver::setAGCFixedGain_dB(QObject *sender, int rx, qreal value) {
Q_UNUSED(sender)
if (m_receiver != rx) return;
if (m_agcFixedGain_dB == value) return;
m_agcFixedGain_dB = value;
if (qtdsp) {
//RECEIVER_DEBUG << "m_agcFixedGain = " << m_agcFixedGain;
qtdsp->wpagc->setAGCFixedGainDb(m_agcFixedGain_dB);
}
}
void Receiver::setAGCMaximumGain_dB(QObject *sender, int rx, qreal value) {
Q_UNUSED(sender)
if (m_receiver != rx) return;
if (m_agcMaximumGain_dB == value) return;
m_agcMaximumGain_dB = value;
if (qtdsp) {
//RECEIVER_DEBUG << "setAGCMaximumGain_dB = " << m_agcMaximumGain_dB;
qtdsp->wpagc->setMaximumGainDb(m_agcMaximumGain_dB);
}
}
void Receiver::setAGCThreshold_dB(QObject *sender, int rx, qreal value) {
Q_UNUSED(sender)
if (m_receiver != rx) return;
if (m_agcThreshold_dBm == value) return;
m_agcThreshold_dBm = value;
if (qtdsp) {
//RECEIVER_DEBUG << "AGCThreshDB (minus offset) for Rx " << m_receiver << ": " << m_agcThreshold_dBm - AGCOFFSET;
qtdsp->wpagc->setAGCThreshDb(m_filterLo, m_filterHi, 2*BUFFER_SIZE, m_agcThreshold_dBm - AGCOFFSET);
}
}
void Receiver::setAGCHangThreshold(QObject *sender, int rx, int value) {
Q_UNUSED(sender)
if (m_receiver != rx) return;
if (m_agcHangThreshold == value) return;
m_agcHangThreshold = value;
if (qtdsp) {
RECEIVER_DEBUG << "m_agcHangThreshold =" << m_agcHangThreshold/100.0;
qtdsp->wpagc->setHangThresh(m_agcHangThreshold/100.0);
}
}
void Receiver::setAGCHangLevel_dB(QObject *sender, int rx, qreal value) {
Q_UNUSED(sender)
if (m_receiver != rx) return;
if (m_agcHangLevel == value) return;
m_agcHangLevel = value;
if (qtdsp) {
//RECEIVER_DEBUG << "m_agcHangLevel = " << m_agcHangLevel - AGCOFFSET;
qtdsp->wpagc->setHangLevelDb(m_agcHangLevel - AGCOFFSET);
}
//set->setAGCHangLeveldB(this, m_receiverID, value);
}
void Receiver::setAGCVariableGain_dB(QObject *sender, int rx, qreal value) {
Q_UNUSED(sender)
if (m_receiver != rx) return;
if (m_agcVariableGain == value) return;
m_agcVariableGain = value;
if (qtdsp) {
RECEIVER_DEBUG << "m_agcVariableGain = " << m_agcVariableGain;
qtdsp->wpagc->setVarGainDb(m_agcVariableGain);
}
}
void Receiver::setAGCAttackTime(QObject *sender, int rx, qreal value) {
Q_UNUSED(sender)
if (m_receiver != rx) return;
if (m_agcAttackTime == value) return;
m_agcAttackTime = value;
if (qtdsp) {
RECEIVER_DEBUG << "m_agcAttackTime = " << m_agcAttackTime;
qtdsp->wpagc->setTauAttack(m_agcAttackTime);
}
}
void Receiver::setAGCDecayTime(QObject *sender, int rx, qreal value) {
Q_UNUSED(sender)
if (m_receiver != rx) return;
if (m_agcDecayTime == value) return;
m_agcDecayTime = value;
if (qtdsp) {
RECEIVER_DEBUG << "m_agcDecayTime = " << m_agcDecayTime;
qtdsp->wpagc->setTauDecay(m_agcDecayTime);
}
}
void Receiver::setAGCHangTime(QObject *sender, int rx, qreal value) {
Q_UNUSED(sender)
if (m_receiver != rx) return;
if (m_agcHangTime == value) return;
m_agcHangTime = value;
if (qtdsp) {
RECEIVER_DEBUG << "m_agcHangTime = " << m_agcHangTime;
qtdsp->wpagc->setHangTime(m_agcHangTime);
}
}
void Receiver::setAudioVolume(QObject *sender, int rx, float value) {
Q_UNUSED(sender)
if (m_receiver != rx) return;
//if (m_audioVolume == value) return;
m_audioVolume = value;
if (qtdsp) {
//RECEIVER_DEBUG << "setAudioVolume =" << m_audioVolume;
qtdsp->setVolume(value);
}
}
void Receiver::setFilterFrequencies(QObject *sender, int rx, double low, double high) {
Q_UNUSED(sender)
if (m_receiver == rx) {
if (m_filterLo == low && m_filterHi == high) return;
m_filterLo = low;
m_filterHi = high;
if (qtdsp) {
qtdsp->filter->setFilter((float)low, (float)high);
qtdsp->wpagc->filterChanged();
}
}
}
void Receiver::setCtrFrequency(long frequency) {
if (m_ctrFrequency == frequency) return;
m_ctrFrequency = frequency;
HamBand band = getBandFromFrequency(set->getBandFrequencyList(), frequency);
m_lastCtrFrequencyList[(int) band] = m_ctrFrequency;
}
void Receiver::setVfoFrequency(long frequency) {
if (m_vfoFrequency == frequency) return;
m_vfoFrequency = frequency;
HamBand band = getBandFromFrequency(set->getBandFrequencyList(), frequency);
m_lastVfoFrequencyList[(int) band] = m_vfoFrequency;
}
void Receiver::setLastCtrFrequencyList(const QList<long> &fList) {
m_lastCtrFrequencyList = fList;
}
void Receiver::setLastVfoFrequencyList(const QList<long> &fList) {
m_lastVfoFrequencyList = fList;
}
void Receiver::setdBmPanScaleMin(qreal value) {
if (m_dBmPanScaleMin == value) return;
m_dBmPanScaleMin = value;
}
void Receiver::setdBmPanScaleMax(qreal value) {
if (m_dBmPanScaleMax == value) return;
m_dBmPanScaleMax = value;
}
void Receiver::setMercuryAttenuators(const QList<int> &attenuators) {
m_mercuryAttenuators = attenuators;
}
void Receiver::setFramesPerSecond(QObject *sender, int rx, int value) {
Q_UNUSED(sender)
if (m_receiver == rx)
m_displayTime = (int)(1000000.0/value);
}
void Receiver::setPeerAddress(QHostAddress addr) {
m_peerAddress = addr;
}
void Receiver::setSocketDescriptor(int value) {
m_socketDescriptor = value;
}
void Receiver::setClient(int value) {
m_client = value;
}
void Receiver::setIQPort(int value) {
m_iqPort = value;
}
void Receiver::setBSPort(int value) {
m_bsPort = value;
}
void Receiver::setConnectedStatus(bool value) {
m_connected = value;
}
+231
View File
@@ -0,0 +1,231 @@
/**
* @file cusdr_receiver.h
* @brief cuSDR receiver header file
* @author Hermann von Hasseln, DL3HVH
* @version 0.1
* @date 2010-11-12
*/
/* Copyright (C)
*
* 2010 - Hermann von Hasseln, DL3HVH
*
* 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; either version 2
* of the License, or (at your option) any later version.
*
* 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 General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#ifndef CUSDR_RECEIVER_H
#define CUSDR_RECEIVER_H
//#include <QObject>
//#include <QtNetwork>
#include "cusdr_settings.h"
#include "QtDSP/qtdsp_dspEngine.h"
#include "Util/cusdr_highResTimer.h"
#ifdef LOG_RECEIVER
# define RECEIVER_DEBUG qDebug().nospace() << "Receiver::\t"
#else
# define RECEIVER_DEBUG nullDebug()
#endif
class Receiver : public QObject {
Q_OBJECT
public:
Receiver(int rx = 0);
~Receiver();
void setupConnections();
bool initDSPInterface();
void deleteDSPInterface();
void enqueueData();
QSDR::_ServerMode getServerMode() const;
QSDR::_DSPCore getDSPCoreMode() const;
QHostAddress getPeerAddress() { return m_peerAddress; }
HamBand getHamBand() { return m_hamBand; }
AGCMode getAGCMode() { return m_agcMode; }
QList<int> getMercuryAttenuators() { return m_mercuryAttenuators; }
QList<DSPMode> getDSPModeList() { return m_dspModeList; }
int getAudioMode() { return m_audioMode; }
int getSocketDescriptor() { return m_socketDescriptor; }
int getReceiverNo() { return m_receiver; }
int getClient() { return m_client; }
int getIQPort() { return m_iqPort; }
int getBSPort() { return m_bsPort; }
//int getID() { return m_receiverID; }
int getSampleRate() { return m_sampleRate; }
int getDisplayDelay() { return m_displayTime; }
qreal getAGCGain() { return m_agcGain; }
float getAudioVolume() { return m_audioVolume; }
long getCtrFrequency() { return m_ctrFrequency; }
long getVfoFrequency() { return m_vfoFrequency; }
double getFilterLo() { return m_filterLo; }
double getFilterHi() { return m_filterHi; }
qreal getdBmPanScaleMin() { return m_dBmPanScaleMin; }
qreal getdBmPanScaleMax() { return m_dBmPanScaleMax; }
bool getConnectedStatus() { return m_connected; }
float in[BUFFER_SIZE * 2];
float out[BUFFER_SIZE * 2];
float temp[BUFFER_SIZE * 4];
float spectrum[BUFFER_SIZE * 4];
float postSpectrum[BUFFER_SIZE * 4];
QVector<float> newSpectrum;
QDSPEngine *qtdsp;
HResTimer *highResTimer;
CPX inBuf;
CPX outBuf;
QHQueue<CPX> inQueue;
public slots:
void setReceiverData(TReceiver data);
void setAudioMode(QObject* sender, int mode);
void setServerMode(QSDR::_ServerMode mode);
void setPeerAddress(QHostAddress addr);
void setSocketDescriptor(int value);
void setReceiver(int value);
void setClient(int value);
void setIQPort(int value);
void setBSPort(int value);
void setConnectedStatus(bool value);
//void setID(int value);
void setSampleRate(int value);
void setHamBand(QObject* sender, int rx, bool byBtn, HamBand band);
void setDspMode(QObject* sender, int rx, DSPMode mode);
void setAGCMode(QObject* sender, int rx, AGCMode mode, bool hang);
void setAGCGain(QObject* sender, int rx, int value);
void setAudioVolume(QObject* sender, int rx, float value);
void setCtrFrequency(long frequency);
void setVfoFrequency(long frequency);
void setFilterFrequencies(QObject* sender, int rx, qreal low, qreal high);
void setLastCtrFrequencyList(const QList<long> &frequencies);
void setLastVfoFrequencyList(const QList<long> &frequencies);
void setdBmPanScaleMin(qreal value);
void setdBmPanScaleMax(qreal value);
void setMercuryAttenuators(const QList<int> &attenuators);
void dspProcessing();
void stop();
private slots:
void setSystemState(
QObject* sender,
QSDR::_Error err,
QSDR::_HWInterfaceMode hwmode,
QSDR::_ServerMode mode,
QSDR::_DataEngineState state);
void setSampleRate(QObject *sender, int value);
void setFramesPerSecond(QObject *sender, int rx, int value);
bool initQtDSPInterface();
void deleteQtDSP();
//void setAGCMaximumGain_dBm(QObject* sender, int rx, int value);
void setAGCMaximumGain_dB(QObject* sender, int rx, qreal value);
void setAGCFixedGain_dB(QObject* sender, int rx, qreal value);
void setAGCThreshold_dB(QObject* sender, int rx, qreal value);
void setAGCHangLevel_dB(QObject* sender, int rx, qreal value);
void setAGCHangThreshold(QObject* sender, int rx, int value);
void setAGCVariableGain_dB(QObject* sender, int rx, qreal value);
void setAGCAttackTime(QObject* sender, int rx, qreal value);
void setAGCDecayTime(QObject* sender, int rx, qreal value);
void setAGCHangTime(QObject* sender, int rx, qreal value);
private:
Settings* set;
QSDR::_DSPCore m_dspCore;
QSDR::_ServerMode m_serverMode;
QSDR::_HWInterfaceMode m_hwInterface;
QSDR::_DataEngineState m_dataEngineState;
TReceiver m_receiverData;
QHostAddress m_peerAddress;
quint16 m_peerPort;
HamBand m_hamBand;
DSPMode m_dspMode;
AGCMode m_agcMode;
TDefaultFilterMode m_filterMode;
QList<long> m_lastCtrFrequencyList;
QList<long> m_lastVfoFrequencyList;
QList<DSPMode> m_dspModeList;
QList<int> m_mercuryAttenuators;
QTime m_smeterTime;
QMutex m_mutex;
volatile bool m_stopped;
int m_receiver;
int m_samplerate;
int m_audioMode; // 1 = audio on, 0 = audio off
int m_socketDescriptor;
int m_client;
int m_iqPort;
int m_bsPort;
int m_sampleRate;
int m_displayTime;
long m_ctrFrequency;
long m_vfoFrequency;
float m_audioVolume;
float m_sMeterValue;
qreal m_agcGain;
qreal m_agcFixedGain_dB;
qreal m_agcMaximumGain_dB;
qreal m_agcThreshold_dBm;
qreal m_agcHangThreshold;
qreal m_agcHangLevel;
qreal m_agcVariableGain;
qreal m_agcAttackTime;
qreal m_agcDecayTime;
qreal m_agcHangTime;
//qreal m_calOffset;
qreal m_filterLo;
qreal m_filterHi;
qreal m_dBmPanScaleMin;
qreal m_dBmPanScaleMax;
bool m_connected;
bool m_hangEnabled;
//void setupConnections();
signals:
void messageEvent(QString msg);
void spectrumBufferChanged(int rx, const qVectorFloat& buffer);
void sMeterValueChanged(int rx, float value);
void outputBufferSignal(int rx, const CPX &buffer);
//void audioReady(int rx);
};
#endif // CUSDR_RECEIVER_H
+51
View File
@@ -0,0 +1,51 @@
//////////////////////////////////////////////////////////////////////
// datatypes.h: Common data type declarations
//
// History:
// 2010-09-15 Initial creation MSW
// 2011-03-27 Initial release
//////////////////////////////////////////////////////////////////////
#ifndef DATATYPES_H
#define DATATYPES_H
#include <math.h>
#include <QtGlobal>
//define single or double precision reals and complex types
typedef float tSReal;
typedef double tDReal;
typedef struct _sCplx
{
tSReal re;
tSReal im;
}tSComplex;
typedef struct _dCplx
{
tDReal re;
tDReal im;
}tDComplex;
typedef struct _isCplx
{
qint16 re;
qint16 im;
}tStereo16;
#define TYPEREAL tDReal
#define TYPECPX tDComplex
#define TYPESTEREO16 tStereo16
#define TYPEMONO16 qint16
//#define K_2PI (8.0*atan(1)) //maybe some compilers are't too smart to optimize out
#define K_2PI (2.0 * 3.14159265358979323846)
#define K_PI (3.14159265358979323846)
#define K_PI4 (K_PI/4.0)
#define K_PI2 (K_PI/2.0)
#define K_3PI4 (3.0*K_PI4)
#endif // DATATYPES_H
+352
View File
@@ -0,0 +1,352 @@
//////////////////////////////////////////////////////////////////////
// FractResampler.cpp: implementation of the CFractResampler class.
//
// This class implements a fractional resampler that can be used to
//convert between different sample rates. A windowes sinc interpolator
// is used to create samples "in between" input samples.
//
// History:
// 2010-09-15 Initial creation MSW
// 2011-03-27 Initial release
/////////////////////////////////////////////////////////////////////
//==========================================================================================
// + + + This Software is released under the "Simplified BSD License" + + +
//Copyright 2010 Moe Wheatley. All rights reserved.
//
//Redistribution and use in source and binary forms, with or without modification, are
//permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
//
//THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED
//WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
//FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR
//CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
//CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
//SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
//ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
//ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//The views and conclusions contained in the software and documentation are those of the
//authors and should not be interpreted as representing official policies, either expressed
//or implied, of Moe Wheatley.
//==========================================================================================
#include "fractresampler.h"
#include <QDir>
#include <QFile>
#include <QDebug>
//////////////////////////////////////////////////////////////////////
// Local defines
//////////////////////////////////////////////////////////////////////
#define SINC_PERIOD_PTS 10000 //number of points in sinc table between "zero crossings"
//smaller value increases noise floor
#define SINC_PERIODS 28 //number of input sample periods("zero crossings"-1) in
//sinc function(should be even)
//decreasing reduces alias free bandwidth
#define SINC_LENGTH ( (SINC_PERIODS)*SINC_PERIOD_PTS + 1)//number of total points in sinc table
#define MAX_SOUNDCARDVAL 32767.0
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CFractResampler::CFractResampler()
{
m_pSinc = NULL;
m_pInputBuf = NULL;
}
CFractResampler::~CFractResampler()
{
if(m_pSinc)
delete m_pSinc;
if(m_pInputBuf)
delete m_pInputBuf;
}
//
//////////////////////////////////////////////////////////////////////
// Initialize resampler memory and create windowed sinc table
// MaxInputSize is the largest number of input samples expected to be processed
//////////////////////////////////////////////////////////////////////
void CFractResampler::Init(int MaxInputSize)
{
int i;
TYPEREAL fi;
TYPEREAL window;
MaxInputSize += SINC_PERIODS; //expand buffer size to include wrap around
if(NULL == m_pSinc)
m_pSinc = new TYPEREAL[SINC_LENGTH];
if(m_pInputBuf)
delete m_pInputBuf;
m_pInputBuf = new TYPECPX[MaxInputSize];
for(i=0; i<MaxInputSize; i++)
{
m_pInputBuf[i].re = 0.0;
m_pInputBuf[i].im = 0.0;
}
for(i=0; i<SINC_LENGTH; i++)
{ //calc Blackman-Harris window points
window = (0.35875
- 0.48829*cos( (K_2PI*i)/(SINC_LENGTH-1) )
+ 0.14128*cos( (2.0*K_2PI*i)/(SINC_LENGTH-1) )
- 0.01168*cos( (3.0*K_2PI*i)/(SINC_LENGTH-1) ) );
//calculate sin(x)/x sinc point * window
fi = K_PI*(double)(i - SINC_LENGTH/2)/(double)SINC_PERIOD_PTS ;
if(i != SINC_LENGTH/2)
m_pSinc[i] = window * (TYPEREAL)sin( (double)fi )/(double)fi;
else
m_pSinc[i] = 1.0;
}
m_FloatTime = 0.0; //init floating point time accumulator
#if 0 //debug hack to write m_pSinc to a file for analysis
QDir::setCurrent("d:/");
QFile File;
File.setFileName("Sinc.txt");
if(File.open(QIODevice::WriteOnly))
{
qDebug()<<"file Opened OK";
char Buf[30000];
for( i=0; i<SINC_LENGTH; i++)
{
sprintf( Buf, "%19.12g\r\n", m_pSinc[i]);
File.write(Buf);
}
}
else
qDebug()<<"file Failed to Open";
#endif
}
//////////////////////////////////////////////////////////////////////
// Resample InLength samples in pInBuf and place into pOutBuf
// using Rate = input rate / output rate
// !!!! Make sure pOutBuf from caller is large enough to hold all
// the generated samples, especially if up converting !!!!!
// COMPLEX version
//////////////////////////////////////////////////////////////////////
int CFractResampler::Resample( int InLength, TYPEREAL Rate, TYPECPX* pInBuf, TYPECPX* pOutBuf)
{
int i;
int j;
int IntegerTime = (int)m_FloatTime; //integer input time accumulator
double dt = Rate; //output delta time as function of input sample time (input rate/output rate)
int outsamples = 0;
TYPECPX acc;
//copy input samples into buffer starting at position SINC_PERIODS
j = SINC_PERIODS;
for(i=0; i<InLength; i++)
m_pInputBuf[j++] = pInBuf[i];
//now calculate output samples by looping until end of input buffer
// is reached. The output position is incremented in fractional time
// of input sample time until all the possible input samples are
//processed.
while(IntegerTime < InLength )
{ //convolve sinc function with input samples where sinc
//function is centered at the output fractional time position
acc.re = 0.0; acc.im = 0.0;
for(i=1; i<=SINC_PERIODS; i++)
{
j = IntegerTime + i; //temp integer time position for convolution loop
int sindx = (int)(( (double)j - m_FloatTime) * (double)SINC_PERIOD_PTS );
acc.re += (m_pInputBuf[j].re * m_pSinc[sindx] );
acc.im += (m_pInputBuf[j].im * m_pSinc[sindx] );
}
pOutBuf[outsamples++] = acc;
m_FloatTime += dt; //inc floating pt output time step
IntegerTime = (int)m_FloatTime; //truncate to integer
}
m_FloatTime -= (double)InLength; //move floating time position back for next call
//keeping leftover fraction
//need to copy last SINC_PERIODS input samples in buffer to beginning of buffer
// for FIR wrap around management. j points to last input sample processed
j = InLength;
for(i=0; i<SINC_PERIODS; i++)
m_pInputBuf[i] = m_pInputBuf[j++];
return outsamples; //return number of output samples processed
}
//////////////////////////////////////////////////////////////////////
// Resample InLength samples in pInBuf and place into pOutBuf
// using Rate = input rate / output rate
// !!!! Make sure pOutBuf from caller is large enough to hold all
// the generated samples, especially if up converting !!!!!
// stereo Integer version
//////////////////////////////////////////////////////////////////////
int CFractResampler::Resample( int InLength, TYPEREAL Rate, TYPECPX* pInBuf, TYPESTEREO16* pOutBuf, TYPEREAL gain)
{
int i;
int j;
int IntegerTime = (int)m_FloatTime; //integer input time accumulator
double dt = Rate; //output delta time as function of input sample time (input rate/output rate)
int outsamples = 0;
TYPECPX acc;
//copy input samples into buffer starting at position SINC_PERIODS
j = SINC_PERIODS;
for(i=0; i<InLength; i++)
{
m_pInputBuf[j++] = pInBuf[i];
}
//now calculate output samples by looping until end of input buffer
// is reached. The output position is incremented in fractional time
// of input sample time until all the possible input samples are
//processed.
while(IntegerTime < InLength )
{ //convolve sinc function with input samples where sinc
//function is centered at the output fractional time position
acc.re = 0.0; acc.im = 0.0;
for(i=1; i<=SINC_PERIODS; i++)
{
j = IntegerTime + i; //temp integer time position for convolution loop
int sindx = (int)(( (double)j - m_FloatTime) * (double)SINC_PERIOD_PTS );
acc.re += (m_pInputBuf[j].re * m_pSinc[sindx] );
acc.im += (m_pInputBuf[j].im * m_pSinc[sindx] );
}
TYPECPX tmp;
tmp.re = (acc.re * gain);;
tmp.im = (acc.im * gain);;
if(tmp.re > MAX_SOUNDCARDVAL)
tmp.re = MAX_SOUNDCARDVAL;
if(tmp.re < -MAX_SOUNDCARDVAL)
tmp.re = -MAX_SOUNDCARDVAL;
if(tmp.im>MAX_SOUNDCARDVAL)
tmp.im = MAX_SOUNDCARDVAL;
if(tmp.im < -MAX_SOUNDCARDVAL)
tmp.im = -MAX_SOUNDCARDVAL;
pOutBuf[outsamples].re = (qint16)tmp.re;
pOutBuf[outsamples++].im = (qint16)tmp.im;
m_FloatTime += dt; //inc floating pt output time step
IntegerTime = (int)m_FloatTime; //truncate to integer
}
m_FloatTime -= (double)InLength; //move floating time position back for next call
//keeping leftover fraction
//need to copy last SINC_PERIODS input samples in buffer to beginning of buffer
// for FIR wrap around management. j points to last input sample processed
j = InLength;
for(i=0; i<SINC_PERIODS; i++)
m_pInputBuf[i] = m_pInputBuf[j++];
return outsamples; //return number of output samples processed
}
//////////////////////////////////////////////////////////////////////
// Resample InLength samples in pInBuf and place into pOutBuf
// using Rate = input rate / output rate
// !!!! Make sure pOutBuf from caller is large enough to hold all
// the generated samples, especially if up converting !!!!!
// REAL version
//////////////////////////////////////////////////////////////////////
int CFractResampler::Resample( int InLength, TYPEREAL Rate, TYPEREAL* pInBuf, TYPEREAL* pOutBuf)
{
int i;
int j;
int IntegerTime = (int)m_FloatTime; //integer input time accumulator
double dt = Rate; //output delta time as function of input sample time (input rate/output rate)
int outsamples = 0;
TYPEREAL acc;
//copy input samples into buffer starting at position SINC_PERIODS
j = SINC_PERIODS;
for(i=0; i<InLength; i++)
m_pInputBuf[j++].re = pInBuf[i];
//now calculate output samples by looping until end of input buffer
// is reached. The output position is incremented in fractional time
// of input sample time until all the possible input samples are
//processed.
while(IntegerTime < InLength )
{ //convolve sinc function with input samples where sinc
//function is centered at the output fractional time position
acc = 0.0;
for(i=1; i<=SINC_PERIODS; i++)
{
j = IntegerTime + i; //temp integer time position for convolution loop
int sindx = (int)(( (double)j - m_FloatTime) * (double)SINC_PERIOD_PTS );
acc += (m_pInputBuf[j].re * m_pSinc[sindx] );
}
pOutBuf[outsamples++] = acc;
m_FloatTime += dt;
IntegerTime = (int)m_FloatTime;
}
m_FloatTime -= (double)InLength; //move floating time position back for next call
//keeping leftover fraction
//need to copy last SINC_PERIODS input samples in buffer to beginning of buffer
// for FIR wrap around management. j points to last input sample processed
j = InLength;
for(i=0; i<SINC_PERIODS; i++)
m_pInputBuf[i].re = m_pInputBuf[j++].re;
return outsamples;
}
//////////////////////////////////////////////////////////////////////
// Resample InLength samples in pInBuf and place into pOutBuf
// using Rate = input rate / output rate
// !!!! Make sure pOutBuf from caller is large enough to hold all
// the generated samples, especially if up converting !!!!!
// short Integer version
//////////////////////////////////////////////////////////////////////
int CFractResampler::Resample( int InLength, TYPEREAL Rate, TYPEREAL* pInBuf, TYPEMONO16* pOutBuf, TYPEREAL gain)
{
int i;
int j;
int IntegerTime = (int)m_FloatTime; //integer input time accumulator
double dt = Rate; //output delta time as function of input sample time (input rate/output rate)
int outsamples = 0;
TYPEREAL acc;
//copy input samples into buffer starting at position SINC_PERIODS
j = SINC_PERIODS;
for(i=0; i<InLength; i++)
m_pInputBuf[j++].re = pInBuf[i];
//now calculate output samples by looping until end of input buffer
// is reached. The output position is incremented in fractional time
// of input sample time until all the possible input samples are
//processed.
while(IntegerTime < InLength )
{ //convolve sinc function with input samples where sinc
//function is centered at the output fractional time position
acc = 0.0;
for(i=1; i<=SINC_PERIODS; i++)
{
j = IntegerTime + i; //temp integer time position for convolution loop
int sindx = (int)(( (double)j - m_FloatTime) * (double)SINC_PERIOD_PTS );
acc += (m_pInputBuf[j].re * m_pSinc[sindx] );
}
TYPEREAL tmp;
tmp = (acc * gain);;
if(tmp > MAX_SOUNDCARDVAL)
tmp = MAX_SOUNDCARDVAL;
if(tmp < -MAX_SOUNDCARDVAL)
tmp = -MAX_SOUNDCARDVAL;
pOutBuf[outsamples++] = (TYPEMONO16)tmp;
m_FloatTime += dt;
IntegerTime = (int)m_FloatTime;
}
m_FloatTime -= (double)InLength; //move floating time position back for next call
//keeping leftover fraction
//need to copy last SINC_PERIODS input samples in buffer to beginning of buffer
// for FIR wrap around management. j points to last input sample processed
j = InLength;
for(i=0; i<SINC_PERIODS; i++)
m_pInputBuf[i].re = m_pInputBuf[j++].re;
return outsamples;
}
+35
View File
@@ -0,0 +1,35 @@
//////////////////////////////////////////////////////////////////////
// fractresampler.h: interface for the CFractResampler class.
//
// This class implements a fractional resampler that can be used to
//convert between different sample rates
//
// History:
// 2010-09-15 Initial creation MSW
// 2011-03-27 Initial release
//////////////////////////////////////////////////////////////////////
#ifndef FRACTRESAMPLER_H
#define FRACTRESAMPLER_H
#include "datatypes.h"
class CFractResampler
{
public:
CFractResampler();
virtual ~CFractResampler();
void Init(int MaxInputSize);
//overloaded functions for processing different data types
int Resample( int InLength, TYPEREAL Rate, TYPEREAL* pInBuf, TYPEREAL* pOutBuf);
int Resample( int InLength, TYPEREAL Rate, TYPECPX* pInBuf, TYPECPX* pOutBuf);
int Resample( int InLength, TYPEREAL Rate, TYPEREAL* pInBuf, TYPEMONO16* pOutBuf, TYPEREAL gain);
int Resample( int InLength, TYPEREAL Rate, TYPECPX* pInBuf, TYPESTEREO16* pOutBuf, TYPEREAL gain);
private:
TYPEREAL m_FloatTime; //floating pt output time accumulator
TYPEREAL* m_pSinc; //ptr to sinc table
TYPECPX* m_pInputBuf; //internal working input sample buffer
};
#endif // FRACTRESAMPLER_H
+583
View File
@@ -0,0 +1,583 @@
/////////////////////////////////////////////////////////////////////
// soundout.cpp: implementation of the CSoundOut class.
//
// This class implements a class to output data to a soundcard.
// A fractional resampler is used to convert the users input rate to
// the sound card rate and also perform frequency lock between the
// two clock domains.
//
// History:
// 2010-09-15 Initial creation MSW
// 2011-03-27 Initial release
// 2011-08-07 Changed some debug output
// 2015-01-24 RRK, Minor mods, adding to cudaSDR
/////////////////////////////////////////////////////////////////////
//==========================================================================================
// + + + This Software is released under the "Simplified BSD License" + + +
//Copyright 2010 Moe Wheatley. All rights reserved.
//
//Redistribution and use in source and binary forms, with or without modification, are
//permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
//
//THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED
//WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
//FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR
//CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
//CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
//SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
//ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
//ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//The views and conclusions contained in the software and documentation are those of the
//authors and should not be interpreted as representing official policies, either expressed
//or implied, of Moe Wheatley.
//==========================================================================================
#include "soundout.h"
#include <QDebug>
#include <math.h>
#define SOUNDCARD_RATE 48000 //output soundcard sample rate
//#define SOUNDCARD_RATE 44100
#define FILTERQLEVEL_ALPHA 0.001
#define P_GAIN 2.38e-7 //Proportional gain
#define TRUE 1
#define FALSE 0
#define TEST_ERROR 1.0
//#define TEST_ERROR 1.001 //use to force fixed sample rate error for testing
//#define TEST_ERROR 0.999
/////////////////////////////////////////////////////////////////////
// constructor/destructor
/////////////////////////////////////////////////////////////////////
CSoundOut::CSoundOut(QObject *parent) :
QThread(parent)
{
m_pParent = parent;
m_pAudioOutput = NULL;
m_pOutput = NULL;
m_ThreadQuit = true;
m_UserDataRate = SOUNDCARD_RATE;
m_OutRatio = 1.0;
//RRK m_OutAudioFormat.setFrequency(SOUNDCARD_RATE);
m_OutAudioFormat.setSampleRate(SOUNDCARD_RATE);
m_OutResampler.Init(8192);
m_RateCorrection = 0.0;
m_Gain = 1.0;
m_Startup = true;
m_BlockingMode = false;
}
CSoundOut::~CSoundOut()
{
Stop();
}
void GetAlsaMasterVolume(long *volume)
{
long min, max;
snd_mixer_t *handle;
snd_mixer_selem_id_t *sid;
const char *card = "default";
const char *selem_name = "Master";
snd_mixer_open(&handle, 0);
snd_mixer_attach(handle, card);
snd_mixer_selem_register(handle, NULL, NULL);
snd_mixer_load(handle);
snd_mixer_selem_id_alloca(&sid);
snd_mixer_selem_id_set_index(sid, 0);
snd_mixer_selem_id_set_name(sid, selem_name);
snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid);
snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, volume);
snd_mixer_close(handle);
}
void SetAlsaMasterVolume(long volume)
{
long min, max;
snd_mixer_t *handle;
snd_mixer_selem_id_t *sid;
const char *card = "default";
const char *selem_name = "Master";
snd_mixer_open(&handle, 0);
snd_mixer_attach(handle, card);
snd_mixer_selem_register(handle, NULL, NULL);
snd_mixer_load(handle);
snd_mixer_selem_id_alloca(&sid);
snd_mixer_selem_id_set_index(sid, 0);
snd_mixer_selem_id_set_name(sid, selem_name);
snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid);
snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
snd_mixer_selem_set_playback_volume_all(elem, volume * max / 100);
snd_mixer_close(handle);
}
/////////////////////////////////////////////////////////////////////
// Starts up soundcard output thread using soundcard at list OutDevIndx
/////////////////////////////////////////////////////////////////////
bool CSoundOut::Start(int OutDevIndx, bool StereoOut, double UsrDataRate, bool BlockingMode)
{
QAudioDeviceInfo DeviceInfo;
long mvolume;
m_StereoOut = StereoOut;
m_BlockingMode = BlockingMode;
//Get required soundcard from list
m_OutDevices = DeviceInfo.availableDevices(QAudio::AudioOutput);
if (-1 == OutDevIndx) GetAlsaMasterVolume(&mvolume);
qDebug()<<"Soundcard volume" << mvolume;
if (-1 == OutDevIndx) m_OutDeviceInfo = QAudioDeviceInfo::defaultOutputDevice();
else m_OutDeviceInfo = m_OutDevices.at(OutDevIndx);
#if 0 //RRK get a list of audio devices and the default
foreach (const QAudioDeviceInfo &deviceInfo, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput)) {
qDebug() << "l:" << deviceInfo.deviceName();
}
QAudioDeviceInfo info = QAudioDeviceInfo::defaultOutputDevice();
qDebug() << "res:" << info.deviceName();
#endif
//Setup fixed format for sound ouput
m_OutAudioFormat.setCodec("audio/pcm");
//m_OutAudioFormat.setFrequency(SOUNDCARD_RATE);
m_OutAudioFormat.setSampleRate(SOUNDCARD_RATE);
m_OutAudioFormat.setSampleSize(16);
m_OutAudioFormat.setSampleType(QAudioFormat::SignedInt);
m_OutAudioFormat.setByteOrder(QAudioFormat::LittleEndian);
if(m_StereoOut)
//RRK m_OutAudioFormat.setChannels(2);
m_OutAudioFormat.setChannelCount(2);
else
m_OutAudioFormat.setChannelCount(1);
m_pAudioOutput = new QAudioOutput(m_OutDeviceInfo, m_OutAudioFormat, this);
if(!m_pAudioOutput)
{
qDebug()<<"Soundcard output error";
return false;
}
if(QAudio::NoError == m_pAudioOutput->error() )
{
//initialize the data queue variables
m_UserDataRate = 1; //force user data rate to be changed
ChangeUserDataRate(UsrDataRate);
m_pOutput = m_pAudioOutput->start(); //start QT AudioOutput
//RRK workaround for default, for some reason choosing default
//sets the master volume to max!
if (-1 == OutDevIndx) SetAlsaMasterVolume(50);
//determine how long to sleep between low level reads based on samplerate and period size
m_BlockTime = ( 250*m_pAudioOutput->periodSize() )/
( SOUNDCARD_RATE*m_OutAudioFormat.channelCount() );
//RRK ( SOUNDCARD_RATE*m_OutAudioFormat.channels() );
//qDebug()<<"periodSize "<<m_pAudioOutput->periodSize();
//qDebug()<<"BlockTime "<<m_BlockTime;
m_ThreadQuit = FALSE;
start(QThread::HighestPriority); //start worker thread and set its priority
// start(QThread::TimeCriticalPriority); //start worker thread and set its priority
return true;
}
else
{
qDebug()<<"Soundcard output error";
return false;
}
}
/////////////////////////////////////////////////////////////////////
// Closes down sound card output thread
/////////////////////////////////////////////////////////////////////
void CSoundOut::Stop()
{
if(!m_ThreadQuit)
{
m_ThreadQuit = TRUE;
m_pAudioOutput->stop();
wait(500);
}
if(NULL != m_pAudioOutput)
{
delete m_pAudioOutput;
m_pAudioOutput = NULL;
}
}
/////////////////////////////////////////////////////////////////////
// Sets/changes user data input rate
/////////////////////////////////////////////////////////////////////
void CSoundOut::ChangeUserDataRate(double UsrDataRate)
{
if(m_UserDataRate != UsrDataRate)
{
m_UserDataRate = UsrDataRate;
for(int i=0; i<OUTQSIZE ;i++) //zero buffer for data output
{
m_OutQueueMono[i] = 0;
m_OutQueueStereo[i].re = 0;
m_OutQueueStereo[i].im = 0;
}
m_OutRatio = m_UserDataRate/m_OutAudioFormat.sampleRate();
m_OutQHead = 0;
m_OutQTail = 0;
m_OutQLevel = 0;
m_AveOutQLevel = OUTQSIZE/2;
m_Startup = true;
}
qDebug()<<"SoundOutRatio Rate"<<(1.0/m_OutRatio) << m_UserDataRate;
}
/////////////////////////////////////////////////////////////////////
// Sets/changes volume control gain 0 <= vol <= 99
//range scales to attenuation(gain) of -50dB to 0dB
/////////////////////////////////////////////////////////////////////
void CSoundOut::SetVolume(qint32 vol)
{
m_Mutex.lock();
if(0==vol) //if zero make infinite attenuation
m_Gain = 0.0;
else if(vol<=99)
m_Gain = pow(10.0, ((double)vol-99.0)/39.2 );
m_Mutex.unlock();
//qDebug()<<"Volume "<<vol << m_Gain;
}
////////////////////////////////////////////////////////////////
//Called by application to put COMPLEX input into
// STEREO 2 channel soundcard output queue
////////////////////////////////////////////////////////////////
void CSoundOut::PutOutQueue(int numsamples, TYPECPX* pData )
{
TYPESTEREO16 RData[OUTQSIZE]; //buffer to hold resampled data
int i;
bool overflow = false;
if(( 0==numsamples) || m_ThreadQuit)
return;
//Call Resampler to match sample rates between radio and sound card
numsamples = m_OutResampler.Resample(numsamples, TEST_ERROR*m_OutRatio *(1.0+m_RateCorrection),
pData, RData, m_Gain);
if(m_BlockingMode) //if in Blocking Mode then wait for soundcard queue to be available
{
for( i=0; i<numsamples; i++)
{
while( ((m_OutQHead+1) & (OUTQSIZE-1)) == m_OutQTail)
msleep(10); //wait if Queue is full
m_OutQueueStereo[m_OutQHead++] = RData[i];
m_OutQHead &= (OUTQSIZE-1);
m_OutQLevel++;
}
}
else
{ //here if non-blocking mode so need to deal with possible over/under flow
m_Mutex.lock();
for( i=0; i<numsamples; i++)
{
m_OutQueueStereo[m_OutQHead++] = RData[i];
m_OutQHead &= (OUTQSIZE-1);
m_OutQLevel++;
if(m_OutQHead==m_OutQTail) //if full
{ //remove 1/4 a queue's worth of data
m_OutQTail += OUTQSIZE/4;
m_OutQTail &= (OUTQSIZE-1);
m_OutQLevel -= OUTQSIZE/4;
i = numsamples; //force break out of for loop
overflow = true;
}
}
if(overflow)
{
qDebug()<<"Snd Overflow";
m_AveOutQLevel = m_OutQLevel;
}
//calculate average Queue fill level
m_AveOutQLevel = (1.0-FILTERQLEVEL_ALPHA)*m_AveOutQLevel + FILTERQLEVEL_ALPHA*(double)m_OutQLevel;
m_Mutex.unlock();
}
}
////////////////////////////////////////////////////////////////
//Called by application to put REAL soundcard output samples
//into MONO soundcard queue
////////////////////////////////////////////////////////////////
void CSoundOut::PutOutQueue(int numsamples, TYPEREAL* pData )
{
TYPEMONO16 RData[OUTQSIZE]; //buffer to hold resampled data
int i;
bool overflow = false;
if(( 0==numsamples) || m_ThreadQuit)
return;
//Call Resampler to match sample rates between radio and sound card
numsamples = m_OutResampler.Resample(numsamples, TEST_ERROR*m_OutRatio *(1.0+m_RateCorrection),
pData, RData, m_Gain);
if(m_BlockingMode) //if in Blocking Mode then wait for soundcard queue to be available
{
for( i=0; i<numsamples; i++)
{
while( ((m_OutQHead+1) & (OUTQSIZE-1)) == m_OutQTail)
msleep(10); //wait if Queue is full
m_OutQueueMono[m_OutQHead++] = RData[i];
m_OutQHead &= (OUTQSIZE-1);
m_OutQLevel++;
}
}
else
{ //here if non-blocking mode so need to deal with possible over/under flow
m_Mutex.lock();
for( i=0; i<numsamples; i++)
{
m_OutQueueMono[m_OutQHead++] = RData[i];
m_OutQHead &= (OUTQSIZE-1);
m_OutQLevel++;
if(m_OutQHead==m_OutQTail) //if full
{ //remove 1/4 a queue's worth of data
m_OutQTail += OUTQSIZE/4;
m_OutQTail &= (OUTQSIZE-1);
m_OutQLevel -= OUTQSIZE/4;
i = numsamples; //force break out of for loop
overflow = true;
}
}
if(overflow)
{
qDebug()<<"Snd Overflow";
m_AveOutQLevel = m_OutQLevel;
}
//calculate average Queue fill level
m_AveOutQLevel = (1.0-FILTERQLEVEL_ALPHA)*m_AveOutQLevel + FILTERQLEVEL_ALPHA*(double)m_OutQLevel;
m_Mutex.unlock();
}
}
////////////////////////////////////////////////////////////////
//Called by CSoundOut worker thread to get new samples from queue
// This routine is called from a worker thread so must be careful.
// MONO version
////////////////////////////////////////////////////////////////
void CSoundOut::GetOutQueue(int numsamples, TYPEMONO16* pData )
{
int i;
bool underflow = false;
m_Mutex.lock();
if(m_Startup)
{ //if no data in queue yet just stuff in silence until something is put in queue
for( i=0; i<numsamples; i++)
pData[i] = 0;
if(m_OutQLevel>OUTQSIZE/2)
{
m_Startup = false;
m_RateUpdateCount = -5*SOUNDCARD_RATE; //delay first error update to let settle
m_PpmError = 0;
m_AveOutQLevel = m_OutQLevel;
m_UpdateToggle = true;
}
else
{
m_Mutex.unlock();
return;
}
}
for( i=0; i<numsamples; i++)
{
if(m_OutQHead!=m_OutQTail)
{
pData[i] = m_OutQueueMono[m_OutQTail++];
m_OutQTail &= (OUTQSIZE-1);
m_OutQLevel--;
}
else //queue went empty
{ //backup queue ptr and use previous data in queue
m_OutQTail -= (OUTQSIZE/4);
m_OutQTail &= (OUTQSIZE-1);
pData[i] = m_OutQueueMono[m_OutQTail];
m_OutQLevel += (OUTQSIZE/4);
underflow = true;
}
}
if(m_BlockingMode)
{ //if in blocking mode just return
m_Mutex.unlock();
return;
}
//calculate average Queue fill level
m_AveOutQLevel = (1.0-FILTERQLEVEL_ALPHA)*m_AveOutQLevel + FILTERQLEVEL_ALPHA*m_OutQLevel;
if(underflow)
{
qDebug()<<"Snd Underflow";
m_AveOutQLevel = m_OutQLevel;
}
// See if time to update rate error calculation routine
m_RateUpdateCount += numsamples;
if(m_RateUpdateCount >= SOUNDCARD_RATE) //every second
{
CalcError();
m_RateUpdateCount = 0;
}
m_Mutex.unlock();
}
////////////////////////////////////////////////////////////////
//Called by CSoundOut worker thread to get new samples from queue
// This routine is called from a worker thread so must be careful.
// STEREO version
////////////////////////////////////////////////////////////////
void CSoundOut::GetOutQueue(int numsamples, TYPESTEREO16* pData )
{
int i;
bool underflow = false;
m_Mutex.lock();
if(m_Startup)
{ //if no data in queue yet just stuff in silence until something is put in queue
for( i=0; i<numsamples; i++)
{
pData[i].re = 0;
pData[i].im = 0;
}
if(m_OutQLevel>OUTQSIZE/2)
{
m_Startup = false;
m_RateUpdateCount = -5*SOUNDCARD_RATE; //delay first error update to let settle
m_PpmError = 0;
m_AveOutQLevel = m_OutQLevel;
m_UpdateToggle = true;
}
else
{
m_Mutex.unlock();
return;
}
}
for( i=0; i<numsamples; i++)
{
if(m_OutQHead!=m_OutQTail)
{
pData[i] = m_OutQueueStereo[m_OutQTail++];
m_OutQTail &= (OUTQSIZE-1);
m_OutQLevel--;
}
else //queue went empty
{ //backup queue ptr and use previous data in queue
m_OutQTail -= (OUTQSIZE/4);
m_OutQTail &= (OUTQSIZE-1);
pData[i] = m_OutQueueStereo[m_OutQTail];
m_OutQLevel += (OUTQSIZE/4);
underflow = true;
}
}
if(m_BlockingMode)
{ //if in blocking mode just return
m_Mutex.unlock();
return;
}
//calculate average Queue fill level
m_AveOutQLevel = (1.0-FILTERQLEVEL_ALPHA)*m_AveOutQLevel + FILTERQLEVEL_ALPHA*m_OutQLevel;
if(underflow)
{
qDebug()<<"Snd Underflow";
m_AveOutQLevel = m_OutQLevel;
}
// See if time to update rate error calculation routine
m_RateUpdateCount += numsamples;
if(m_RateUpdateCount >= SOUNDCARD_RATE) //every second
{
CalcError();
m_RateUpdateCount = 0;
}
m_Mutex.unlock();
}
////////////////////////////////////////////////////////////////
// Called alternately from the Get routines to update the
// error correction process
////////////////////////////////////////////////////////////////
void CSoundOut::CalcError()
{
double error;
error = (double)(m_AveOutQLevel - OUTQSIZE/2 ); //neg==level is too low pos == level is to high
error = error * P_GAIN;
m_RateCorrection = error;
m_PpmError = (int)( m_RateCorrection*1e6 );
if( abs(m_PpmError) > 500)
{
// qDebug()<<"SoundOut "<<m_PpmError << m_AveOutQLevel;
}
}
//////////////////////////////////////////////////////////////////////////
// Worker thread polls QAudioOutput device to see if there is room
// to put more data into it and then calls GetOutQueue(..) to get more data.
// This thread was needed because the normal "pull" mechanism of Qt does
// not work very well since it depends on the main process signal-slot event
// queue and you get dropouts if the GUI gets busy.
//////////////////////////////////////////////////////////////////////////
void CSoundOut::run()
{
while(!m_ThreadQuit ) //execute loop until quit flag set
{
if( (QAudio::IdleState == m_pAudioOutput->state() ) ||
(QAudio::ActiveState == m_pAudioOutput->state() ) )
{ //Process sound data while soundcard is active and no errors
unsigned int len = m_pAudioOutput->bytesFree(); //in bytes
if( len>0 )
{
//limit size to SOUND_WRITEBUFSIZE
if(len > SOUND_WRITEBUFSIZE)
len = SOUND_WRITEBUFSIZE;
if(m_StereoOut)
{
len &= ~(0x03); //keep on 4 byte chunks
GetOutQueue( len/4, (TYPESTEREO16*)m_pData );
}
else
{
len &= ~(0x01); //keep on 2 byte chunks
GetOutQueue( len/2, (TYPEMONO16*)m_pData );
}
m_pOutput->write((char*)m_pData,len);
}
else //no room in sound card output buffer so wait
{ //not good but no other wait or blocking mechanism available
msleep(m_BlockTime);
}
}
else
{ //bail out if error occurs
qDebug()<<"SoundOut Error";
#if 0 //RRK
if(m_pParent)
((CSdrInterface*)m_pParent)->SendIOStatus(CSdrInterface::ERROR);
#endif
m_ThreadQuit = true;
}
}
qDebug()<<"sound thread exit";
}
+76
View File
@@ -0,0 +1,76 @@
//////////////////////////////////////////////////////////////////////
// soundout.h: interface for the CSoundOut class.
//
// History:
// 2010-09-15 Initial creation MSW
// 2011-03-27 Initial release
/////////////////////////////////////////////////////////////////////
#ifndef SOUNDOUT_H
#define SOUNDOUT_H
#include <QObject>
#include <QThread>
#include <QList>
#include <QMutex>
#include <QAudioOutput>
#include "fractresampler.h"
#include <alsa/asoundlib.h>
#include <alsa/mixer.h>
#define OUTQSIZE 16384 //max samples (keep power of 2 for ptr wrap around)
#define SOUND_WRITEBUFSIZE 8192
class CSoundOut : public QThread
{
Q_OBJECT
public:
explicit CSoundOut(QObject *parent = 0);
virtual ~CSoundOut();
//Exposed functions
bool Start(int OutDevIndx, bool StereoOut, double UsrDataRate, bool BlockingMode); //starts soundcard output
void Stop(); //stops soundcard output
void PutOutQueue(int numsamples, TYPEREAL* pData );
void PutOutQueue(int numsamples, TYPECPX* pData );
void ChangeUserDataRate(double UsrDataRate);
void SetVolume(qint32 vol);
int GetRateError(){return (int)m_PpmError;}
protected:
void run(); //implements worker thread loop
int m_BlockTime;
private:
void GetOutQueue(int numsamples, TYPEMONO16* pData );
void GetOutQueue(int numsamples, TYPESTEREO16* pData );
void CalcError();
QList<QAudioDeviceInfo> m_OutDevices;
QAudioDeviceInfo m_OutDeviceInfo;
QAudioFormat m_OutAudioFormat;
QAudioOutput* m_pAudioOutput;
QIODevice* m_pOutput; // ptr to internal soundout IODevice
QObject* m_pParent;
QMutex m_Mutex;
CFractResampler m_OutResampler;
TYPEMONO16 m_OutQueueMono[OUTQSIZE];
TYPESTEREO16 m_OutQueueStereo[OUTQSIZE];
bool m_BlockingMode;
bool m_ThreadQuit;
bool m_Startup;
bool m_StereoOut;
bool m_UpdateToggle;
quint32 m_OutQHead;
quint32 m_OutQTail;
char m_pData[SOUND_WRITEBUFSIZE];
int m_RateUpdateCount;
int m_OutQLevel;
int m_PpmError;
double m_Gain;
double m_UserDataRate;
double m_OutRatio;
double m_RateCorrection;
double m_AveOutQLevel;
};
#endif // SOUNDOUT_H