2016-04-07 11:05:53 +00:00
///////////////////////////////////////////////////////////////////////////////////
2016-04-08 16:14:50 +00:00
// Copyright (C) 2015 F4EXB //
// written by Edouard Griffiths //
2016-04-07 11:05:53 +00:00
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
# include <QTime>
# include <QDebug>
# include <stdio.h>
# include <complex.h>
# include "dsddemod.h"
# include "dsddemodgui.h"
# include "audio/audiooutput.h"
# include "dsp/channelizer.h"
# include "dsp/pidcontroller.h"
# include "dsp/dspengine.h"
static const Real afSqTones [ 2 ] = { 1200.0 , 6400.0 } ; // {1200.0, 8000.0};
MESSAGE_CLASS_DEFINITION ( DSDDemod : : MsgConfigureDSDDemod , Message )
DSDDemod : : DSDDemod ( SampleSink * sampleSink ) :
m_sampleCount ( 0 ) ,
m_squelchCount ( 0 ) ,
m_squelchOpen ( false ) ,
m_audioFifo ( 4 , 48000 ) ,
m_fmExcursion ( 24 ) ,
m_settingsMutex ( QMutex : : Recursive ) ,
m_scope ( sampleSink ) ,
2016-04-08 16:14:50 +00:00
m_scopeEnabled ( true ) ,
m_dsdDecoder ( )
2016-04-07 11:05:53 +00:00
{
setObjectName ( " DSDDemod " ) ;
2016-04-08 16:14:50 +00:00
m_dsdInBuffer = new qint16 [ 1 < < 18 ] ; // 128 k Samples is the maximum size of all input devices sample buffers (Airspy or HackRF) = 2^(17+1) for 2 byte samples
2016-04-07 11:05:53 +00:00
m_config . m_inputSampleRate = 96000 ;
m_config . m_inputFrequencyOffset = 0 ;
m_config . m_rfBandwidth = 100 ;
m_config . m_demodGain = 100 ;
m_config . m_fmDeviation = 100 ;
m_config . m_squelchGate = 5 ; // 10s of ms at 48000 Hz sample rate. Corresponds to 2400 for AGC attack
m_config . m_squelch = - 30.0 ;
m_config . m_volume = 1.0 ;
m_config . m_audioMute = false ;
m_config . m_audioSampleRate = DSPEngine : : instance ( ) - > getAudioSampleRate ( ) ;
apply ( ) ;
m_audioBuffer . resize ( 1 < < 14 ) ;
m_audioBufferFill = 0 ;
2016-04-09 02:02:18 +00:00
m_movingAverage . resize ( 16 , 0 ) ;
2016-04-07 11:05:53 +00:00
DSPEngine : : instance ( ) - > addAudioSink ( & m_audioFifo ) ;
}
DSDDemod : : ~ DSDDemod ( )
{
DSPEngine : : instance ( ) - > removeAudioSink ( & m_audioFifo ) ;
2016-04-08 16:14:50 +00:00
delete [ ] m_dsdInBuffer ;
2016-04-07 11:05:53 +00:00
}
void DSDDemod : : configure ( MessageQueue * messageQueue ,
int rfBandwidth ,
int demodGain ,
int fmDeviation ,
int volume ,
int squelchGate ,
Real squelch ,
bool audioMute )
{
Message * cmd = MsgConfigureDSDDemod : : create ( rfBandwidth ,
demodGain ,
fmDeviation ,
volume ,
squelchGate ,
squelch ,
audioMute ) ;
messageQueue - > push ( cmd ) ;
}
void DSDDemod : : feed ( const SampleVector : : const_iterator & begin , const SampleVector : : const_iterator & end , bool firstOfBurst )
{
Complex ci ;
m_settingsMutex . lock ( ) ;
2016-04-08 16:14:50 +00:00
m_dsdInCount = 0 ;
2016-04-07 11:05:53 +00:00
m_scopeSampleBuffer . clear ( ) ;
for ( SampleVector : : const_iterator it = begin ; it ! = end ; + + it )
{
Complex c ( it - > real ( ) , it - > imag ( ) ) ;
c * = m_nco . nextIQ ( ) ;
2016-04-09 17:14:19 +00:00
if ( m_interpolator . interpolate ( & m_interpolatorDistanceRemain , c , & ci ) )
{
qint16 sample ;
m_magsq = ( ( ci . real ( ) * ci . real ( ) + ci . imag ( ) * ci . imag ( ) ) ) / ( Real ) ( 1 < < 30 ) ;
m_movingAverage . feed ( m_magsq ) ;
Real demod = 32768.0f * m_phaseDiscri . phaseDiscriminator ( ci ) * ( ( float ) m_running . m_demodGain / 100.0f ) ;
m_sampleCount + + ;
// AF processing
if ( getMagSq ( ) > m_squelchLevel )
{
if ( m_squelchCount < m_squelchGate )
{
m_squelchCount + + ;
}
}
else
{
m_squelchCount = 0 ;
}
m_squelchOpen = m_squelchCount = = m_squelchGate ;
if ( m_squelchOpen )
{
sample = demod ;
}
else
{
sample = 0 ;
}
Sample s ( sample , 0.0 ) ;
m_scopeSampleBuffer . push_back ( s ) ;
m_dsdInBuffer [ m_dsdInCount + + ] = sample ;
2016-04-07 11:05:53 +00:00
2016-04-09 11:12:48 +00:00
// if (m_running.m_audioMute)
// {
// m_audioBuffer[m_audioBufferFill].l = 0;
// m_audioBuffer[m_audioBufferFill].r = 0;
// }
// else
// {
// m_audioBuffer[m_audioBufferFill].l = sample;
// m_audioBuffer[m_audioBufferFill].r = sample;
// }
//
// ++m_audioBufferFill;
//
// if (m_audioBufferFill >= m_audioBuffer.size())
// {
// uint res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 10);
//
// if (res != m_audioBufferFill)
// {
// qDebug("DSDDemod::feed: %u/%u audio samples written", res, m_audioBufferFill);
// }
//
// m_audioBufferFill = 0;
// }
2016-04-07 11:05:53 +00:00
2016-04-09 17:14:19 +00:00
m_interpolatorDistanceRemain + = m_interpolatorDistance ;
}
2016-04-07 11:05:53 +00:00
}
2016-04-09 11:12:48 +00:00
// if (m_audioBufferFill > 0)
// {
// uint res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 10);
//
// if (res != m_audioBufferFill)
// {
// qDebug("NFMDemod::feed: %u/%u tail samples written", res, m_audioBufferFill);
// }
//
// m_audioBufferFill = 0;
// }
m_dsdDecoder . popAudioSamples ( & m_audioFifo , m_running . m_audioMute ) ;
2016-04-09 17:14:19 +00:00
m_dsdDecoder . pushSamples ( m_dsdInBuffer , m_dsdInCount ) ;
2016-04-08 16:14:50 +00:00
2016-04-09 17:14:19 +00:00
if ( ( m_scope ! = 0 ) & & ( m_scopeEnabled ) )
2016-04-07 11:05:53 +00:00
{
m_scope - > feed ( m_scopeSampleBuffer . begin ( ) , m_scopeSampleBuffer . end ( ) , true ) ; // true = real samples for what it's worth
}
m_settingsMutex . unlock ( ) ;
}
void DSDDemod : : start ( )
{
m_audioFifo . clear ( ) ;
m_phaseDiscri . reset ( ) ;
2016-04-09 00:27:32 +00:00
m_dsdDecoder . start ( ) ;
2016-04-07 11:05:53 +00:00
}
void DSDDemod : : stop ( )
{
2016-04-09 00:27:32 +00:00
m_dsdDecoder . stop ( ) ;
2016-04-07 11:05:53 +00:00
}
bool DSDDemod : : handleMessage ( const Message & cmd )
{
qDebug ( ) < < " DSDDemod::handleMessage " ;
if ( Channelizer : : MsgChannelizerNotification : : match ( cmd ) )
{
Channelizer : : MsgChannelizerNotification & notif = ( Channelizer : : MsgChannelizerNotification & ) cmd ;
m_config . m_inputSampleRate = notif . getSampleRate ( ) ;
m_config . m_inputFrequencyOffset = notif . getFrequencyOffset ( ) ;
apply ( ) ;
qDebug ( ) < < " DSDDemod::handleMessage: MsgChannelizerNotification: m_inputSampleRate: " < < m_config . m_inputSampleRate
< < " m_inputFrequencyOffset: " < < m_config . m_inputFrequencyOffset ;
return true ;
}
else if ( MsgConfigureDSDDemod : : match ( cmd ) )
{
MsgConfigureDSDDemod & cfg = ( MsgConfigureDSDDemod & ) cmd ;
m_config . m_rfBandwidth = cfg . getRFBandwidth ( ) ;
m_config . m_demodGain = cfg . getDemodGain ( ) ;
m_config . m_fmDeviation = cfg . getFMDeviation ( ) ;
m_config . m_volume = cfg . getVolume ( ) ;
m_config . m_squelchGate = cfg . getSquelchGate ( ) ;
m_config . m_squelch = cfg . getSquelch ( ) ;
m_config . m_audioMute = cfg . getAudioMute ( ) ;
apply ( ) ;
qDebug ( ) < < " DSDDemod::handleMessage: MsgConfigureDSDDemod: m_rfBandwidth: " < < m_config . m_rfBandwidth * 100
< < " m_demodGain: " < < m_config . m_demodGain / 100.0
< < " m_fmDeviation: " < < m_config . m_fmDeviation * 100
< < " m_volume: " < < m_config . m_volume / 10.0
< < " m_squelchGate " < < m_config . m_squelchGate
< < " m_squelch: " < < m_config . m_squelch
< < " m_audioMute: " < < m_config . m_audioMute ;
return true ;
}
else
{
return false ;
}
}
void DSDDemod : : apply ( )
{
if ( ( m_config . m_inputFrequencyOffset ! = m_running . m_inputFrequencyOffset ) | |
( m_config . m_inputSampleRate ! = m_running . m_inputSampleRate ) )
{
m_nco . setFreq ( - m_config . m_inputFrequencyOffset , m_config . m_inputSampleRate ) ;
}
if ( ( m_config . m_inputSampleRate ! = m_running . m_inputSampleRate ) | |
( m_config . m_rfBandwidth ! = m_running . m_rfBandwidth ) )
{
m_settingsMutex . lock ( ) ;
m_interpolator . create ( 16 , m_config . m_inputSampleRate , ( m_config . m_rfBandwidth * 100 ) / 2.2 ) ;
m_interpolatorDistanceRemain = 0 ;
m_interpolatorDistance = ( Real ) m_config . m_inputSampleRate / ( Real ) m_config . m_audioSampleRate ;
m_phaseDiscri . setFMScaling ( ( float ) m_config . m_rfBandwidth / ( float ) m_config . m_fmDeviation ) ;
m_settingsMutex . unlock ( ) ;
}
if ( m_config . m_fmDeviation ! = m_running . m_fmDeviation )
{
m_phaseDiscri . setFMScaling ( ( float ) m_config . m_rfBandwidth / ( float ) m_config . m_fmDeviation ) ;
}
if ( m_config . m_squelchGate ! = m_running . m_squelchGate )
{
2016-04-09 02:02:18 +00:00
m_squelchGate = 480 * m_config . m_squelchGate ; // gate is given in 10s of ms at 48000 Hz audio sample rate
2016-04-07 11:05:53 +00:00
}
if ( m_config . m_squelch ! = m_running . m_squelch )
{
// input is a value in tenths of dB
2016-04-09 02:02:18 +00:00
m_squelchLevel = std : : pow ( 10.0 , m_config . m_squelch / 100.0 ) ;
2016-04-07 11:05:53 +00:00
//m_squelchLevel *= m_squelchLevel;
}
m_running . m_inputSampleRate = m_config . m_inputSampleRate ;
m_running . m_inputFrequencyOffset = m_config . m_inputFrequencyOffset ;
m_running . m_rfBandwidth = m_config . m_rfBandwidth ;
m_running . m_demodGain = m_config . m_demodGain ;
m_running . m_fmDeviation = m_config . m_fmDeviation ;
m_running . m_squelchGate = m_config . m_squelchGate ;
m_running . m_squelch = m_config . m_squelch ;
m_running . m_volume = m_config . m_volume ;
m_running . m_audioSampleRate = m_config . m_audioSampleRate ;
m_running . m_audioMute = m_config . m_audioMute ;
}