2017-02-23 02:18:56 -05:00
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2017 F4HKW //
// for F4EXB / SDRAngel //
// //
// 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 "atvdemod.h"
# include <QTime>
# include <QDebug>
# include <stdio.h>
# include <complex.h>
# include "audio/audiooutput.h"
# include "dsp/dspengine.h"
# include "dsp/pidcontroller.h"
MESSAGE_CLASS_DEFINITION ( ATVDemod : : MsgConfigureATVDemod , Message )
2017-03-17 04:39:20 -04:00
MESSAGE_CLASS_DEFINITION ( ATVDemod : : MsgConfigureRFATVDemod , Message )
2017-03-18 05:04:57 -04:00
MESSAGE_CLASS_DEFINITION ( ATVDemod : : MsgReportEffectiveSampleRate , Message )
2017-02-23 02:18:56 -05:00
2017-03-17 06:46:38 -04:00
const int ATVDemod : : m_ssbFftLen = 1024 ;
2017-03-16 18:13:51 -04:00
2017-03-20 06:57:52 -04:00
ATVDemod : : ATVDemod ( BasebandSampleSink * objScopeSink ) :
m_objScopeSink ( objScopeSink ) ,
m_objSettingsMutex ( QMutex : : Recursive ) ,
2017-02-23 02:18:56 -05:00
m_objRegisteredATVScreen ( NULL ) ,
m_intImageIndex ( 0 ) ,
m_intColIndex ( 0 ) ,
m_intRowIndex ( 0 ) ,
m_intSynchroPoints ( 0 ) ,
m_blnSynchroDetected ( false ) ,
2017-02-24 17:25:09 -05:00
m_blnLineSynchronized ( false ) ,
2017-02-23 02:18:56 -05:00
m_blnVerticalSynchroDetected ( false ) ,
m_intRowsLimit ( 0 ) ,
m_blnImageDetecting ( false ) ,
m_fltEffMin ( 2000000000.0f ) ,
m_fltEffMax ( - 2000000000.0f ) ,
m_fltAmpMin ( - 2000000000.0f ) ,
m_fltAmpMax ( 2000000000.0f ) ,
m_fltAmpDelta ( 1.0 ) ,
2017-02-25 01:12:41 -05:00
m_fltAmpLineAverage ( 0.0f ) ,
2017-03-17 06:46:38 -04:00
m_intNumberSamplePerTop ( 0 ) ,
2017-03-19 05:28:54 -04:00
m_bfoPLL ( 200 / 1000000 , 100 / 1000000 , 0.01 ) ,
m_bfoFilter ( 200.0 , 1000000.0 , 0.9 ) ,
2017-03-17 06:46:38 -04:00
m_interpolatorDistanceRemain ( 0.0f ) ,
m_interpolatorDistance ( 1.0f ) ,
m_DSBFilter ( 0 ) ,
m_DSBFilterBuffer ( 0 ) ,
m_DSBFilterBufferIndex ( 0 )
2017-02-23 02:18:56 -05:00
{
2017-03-05 21:36:02 -05:00
setObjectName ( " ATVDemod " ) ;
2017-02-23 02:18:56 -05:00
//*************** ATV PARAMETERS ***************
m_intNumberSamplePerLine = 0 ;
m_intSynchroPoints = 0 ;
m_intNumberOfLines = 0 ;
m_intNumberOfRowsToDisplay = 0 ;
2017-03-17 06:17:15 -04:00
m_objMagSqAverage . resize ( 32 , 1.0 ) ;
2017-03-16 13:56:20 -04:00
2017-03-17 06:46:38 -04:00
m_DSBFilter = new fftfilt ( ( 2.0f * m_objRFConfig . m_fltRFBandwidth ) / 1000000 , 2 * m_ssbFftLen ) ; // arbitrary 1 MS/s sample rate
m_DSBFilterBuffer = new Complex [ m_ssbFftLen ] ;
memset ( m_DSBFilterBuffer , 0 , sizeof ( Complex ) * ( m_ssbFftLen ) ) ;
2017-02-23 02:18:56 -05:00
memset ( ( void * ) m_fltBufferI , 0 , 6 * sizeof ( float ) ) ;
memset ( ( void * ) m_fltBufferQ , 0 , 6 * sizeof ( float ) ) ;
2017-03-22 18:55:47 -04:00
m_objPhaseDiscri . setFMScaling ( 1.0f ) ;
2017-03-26 20:29:02 -04:00
applyStandard ( ) ;
2017-02-23 02:18:56 -05:00
}
ATVDemod : : ~ ATVDemod ( )
2017-02-25 01:12:41 -05:00
{
2017-02-23 02:18:56 -05:00
}
2017-03-18 05:04:57 -04:00
void ATVDemod : : setATVScreen ( ATVScreen * objScreen )
2017-02-23 02:18:56 -05:00
{
2017-02-25 01:12:41 -05:00
m_objRegisteredATVScreen = objScreen ;
2017-02-23 02:18:56 -05:00
}
2017-03-16 19:12:12 -04:00
void ATVDemod : : configure (
MessageQueue * objMessageQueue ,
float fltLineDurationUs ,
float fltTopDurationUs ,
float fltFramePerS ,
2017-03-26 20:29:02 -04:00
ATVStd enmATVStandard ,
2017-03-16 19:12:12 -04:00
float fltRatioOfRowsToDisplay ,
float fltVoltLevelSynchroTop ,
float fltVoltLevelSynchroBlack ,
bool blnHSync ,
2017-03-19 14:10:48 -04:00
bool blnVSync ,
2017-03-24 07:57:49 -04:00
bool blnInvertVideo ,
int intVideoTabIndex )
2017-02-23 02:18:56 -05:00
{
2017-03-16 19:12:12 -04:00
Message * msgCmd = MsgConfigureATVDemod : : create (
fltLineDurationUs ,
fltTopDurationUs ,
fltFramePerS ,
2017-03-26 20:29:02 -04:00
enmATVStandard ,
2017-03-16 19:12:12 -04:00
fltRatioOfRowsToDisplay ,
fltVoltLevelSynchroTop ,
fltVoltLevelSynchroBlack ,
blnHSync ,
2017-03-19 14:10:48 -04:00
blnVSync ,
2017-03-24 07:57:49 -04:00
blnInvertVideo ,
intVideoTabIndex ) ;
2017-02-23 02:18:56 -05:00
objMessageQueue - > push ( msgCmd ) ;
}
2017-03-17 04:39:20 -04:00
void ATVDemod : : configureRF (
MessageQueue * objMessageQueue ,
ATVModulation enmModulation ,
float fltRFBandwidth ,
2017-03-17 05:04:00 -04:00
float fltRFOppBandwidth ,
2017-03-17 06:46:38 -04:00
bool blnFFTFiltering ,
2017-03-19 05:28:54 -04:00
bool blnDecimatorEnable ,
2017-03-22 18:55:47 -04:00
float fltBFOFrequency ,
float fmDeviation )
2017-03-17 04:39:20 -04:00
{
Message * msgCmd = MsgConfigureRFATVDemod : : create (
enmModulation ,
fltRFBandwidth ,
2017-03-17 05:04:00 -04:00
fltRFOppBandwidth ,
2017-03-17 06:46:38 -04:00
blnFFTFiltering ,
2017-03-19 05:28:54 -04:00
blnDecimatorEnable ,
2017-03-22 18:55:47 -04:00
fltBFOFrequency ,
fmDeviation ) ;
2017-03-17 04:39:20 -04:00
objMessageQueue - > push ( msgCmd ) ;
}
2017-02-23 02:18:56 -05:00
void ATVDemod : : feed ( const SampleVector : : const_iterator & begin , const SampleVector : : const_iterator & end , bool firstOfBurst )
{
2017-03-16 19:33:14 -04:00
float fltDivSynchroBlack = 1.0f - m_objRunning . m_fltVoltLevelSynchroBlack ;
2017-02-23 02:18:56 -05:00
float fltI ;
float fltQ ;
float fltNormI ;
float fltNormQ ;
2017-03-18 00:37:52 -04:00
Complex ci ;
2017-02-23 02:18:56 -05:00
float fltNorm = 0.00f ;
float fltVal ;
int intVal ;
2017-03-18 00:37:52 -04:00
qint16 * ptrBufferToRelease = 0 ;
2017-02-23 02:18:56 -05:00
bool blnComputeImage = false ;
int intSynchroTimeSamples = ( 3 * m_intNumberSamplePerLine ) / 4 ;
2017-03-16 19:33:14 -04:00
float fltSynchroTrameLevel = 0.5f * ( ( float ) intSynchroTimeSamples ) * m_objRunning . m_fltVoltLevelSynchroBlack ;
2017-02-23 02:18:56 -05:00
//********** Let's rock and roll buddy ! **********
m_objSettingsMutex . lock ( ) ;
//********** Accessing ATV Screen context **********
if ( m_intImageIndex = = 0 )
{
if ( m_intNumberOfLines % 2 = = 1 )
{
m_intRowsLimit = m_intNumberOfLines ;
}
else
{
m_intRowsLimit = m_intNumberOfLines - 2 ;
}
}
# ifdef EXTENDED_DIRECT_SAMPLE
qint16 * ptrBuffer ;
qint32 intLen ;
//********** Reading direct samples **********
SampleVector : : const_iterator it = begin ;
intLen = it - > intLen ;
ptrBuffer = it - > ptrBuffer ;
ptrBufferToRelease = ptrBuffer ;
+ + it ;
for ( qint32 intInd = 0 ; intInd < intLen - 1 ; intInd + = 2 )
{
fltI = ( ( qint32 ) ( * ptrBuffer ) ) < < 4 ;
ptrBuffer + + ;
fltQ = ( ( qint32 ) ( * ptrBuffer ) ) < < 4 ;
ptrBuffer + + ;
2017-03-05 21:36:02 -05:00
2017-02-23 02:18:56 -05:00
# else
for ( SampleVector : : const_iterator it = begin ; it ! = end ; + + it /* ++it **/ )
{
fltI = it - > real ( ) ;
fltQ = it - > imag ( ) ;
# endif
2017-03-18 00:37:52 -04:00
Complex c ( fltI , fltQ ) ;
2017-03-18 08:19:24 -04:00
if ( m_objRFRunning . m_intFrequencyOffset ! = 0 )
2017-03-18 00:37:52 -04:00
{
2017-03-18 08:19:24 -04:00
c * = m_nco . nextIQ ( ) ;
}
2017-03-18 04:19:51 -04:00
2017-03-18 08:19:24 -04:00
if ( m_objRFRunning . m_blndecimatorEnable )
{
2017-03-18 00:37:52 -04:00
if ( m_interpolator . decimate ( & m_interpolatorDistanceRemain , c , & ci ) )
{
demod ( ci ) ;
m_interpolatorDistanceRemain + = m_interpolatorDistance ;
}
}
else
2017-02-23 02:18:56 -05:00
{
2017-03-18 00:37:52 -04:00
demod ( c ) ;
}
}
2017-02-23 02:18:56 -05:00
2017-03-24 07:57:49 -04:00
if ( ( m_objRunning . m_intVideoTabIndex = = 1 ) & & ( m_objScopeSink ! = 0 ) ) // do only if scope tab is selected and scope is available
2017-03-20 06:57:52 -04:00
{
m_objScopeSink - > feed ( m_objScopeSampleBuffer . begin ( ) , m_objScopeSampleBuffer . end ( ) , false ) ; // m_ssb = positive only
}
m_objScopeSampleBuffer . clear ( ) ;
2017-03-18 00:37:52 -04:00
if ( ptrBufferToRelease ! = 0 )
{
delete ptrBufferToRelease ;
}
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
m_objSettingsMutex . unlock ( ) ;
}
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
void ATVDemod : : demod ( Complex & c )
{
float fltDivSynchroBlack = 1.0f - m_objRunning . m_fltVoltLevelSynchroBlack ;
int intSynchroTimeSamples = ( 3 * m_intNumberSamplePerLine ) / 4 ;
float fltSynchroTrameLevel = 0.5f * ( ( float ) intSynchroTimeSamples ) * m_objRunning . m_fltVoltLevelSynchroBlack ;
float fltNormI ;
float fltNormQ ;
float fltNorm ;
float fltVal ;
int intVal ;
2017-02-23 02:18:56 -05:00
2017-03-18 08:19:24 -04:00
//********** FFT filtering **********
if ( m_objRFRunning . m_blnFFTFiltering )
{
int n_out ;
fftfilt : : cmplx * filtered ;
2017-03-19 05:28:54 -04:00
n_out = m_DSBFilter - > runAsym ( c , & filtered , m_objRFRunning . m_enmModulation ! = ATV_LSB ) ; // all usb except explicitely lsb
2017-03-18 08:19:24 -04:00
if ( n_out > 0 )
{
memcpy ( ( void * ) m_DSBFilterBuffer , ( const void * ) filtered , n_out * sizeof ( Complex ) ) ;
m_DSBFilterBufferIndex = 0 ;
}
m_DSBFilterBufferIndex + + ;
}
2017-03-18 00:37:52 -04:00
//********** demodulation **********
2017-02-23 02:18:56 -05:00
2017-03-19 15:18:44 -04:00
# if defined(_WINDOWS_)
float fltI = m_objRFRunning . m_blnFFTFiltering ? m_DSBFilterBuffer [ m_DSBFilterBufferIndex - 1 ] . real ( ) : c . real ( ) ;
float fltQ = m_objRFRunning . m_blnFFTFiltering ? m_DSBFilterBuffer [ m_DSBFilterBufferIndex - 1 ] . imag ( ) : c . imag ( ) ;
# else
2017-03-18 08:19:24 -04:00
float & fltI = m_objRFRunning . m_blnFFTFiltering ? m_DSBFilterBuffer [ m_DSBFilterBufferIndex - 1 ] . real ( ) : c . real ( ) ;
float & fltQ = m_objRFRunning . m_blnFFTFiltering ? m_DSBFilterBuffer [ m_DSBFilterBufferIndex - 1 ] . imag ( ) : c . imag ( ) ;
2017-03-19 15:18:44 -04:00
# endif
2017-03-22 18:55:47 -04:00
double magSq ;
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
if ( ( m_objRFRunning . m_enmModulation = = ATV_FM1 ) | | ( m_objRFRunning . m_enmModulation = = ATV_FM2 ) )
{
//Amplitude FM
2017-03-22 18:55:47 -04:00
magSq = fltI * fltI + fltQ * fltQ ;
m_objMagSqAverage . feed ( magSq ) ;
fltNorm = sqrt ( magSq ) ;
2017-03-18 00:37:52 -04:00
fltNormI = fltI / fltNorm ;
fltNormQ = fltQ / fltNorm ;
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
//-2 > 2 : 0 -> 1 volt
//0->0.3 synchro 0.3->1 image
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
if ( m_objRFRunning . m_enmModulation = = ATV_FM1 )
{
//YDiff Cd
fltVal = m_fltBufferI [ 0 ] * ( fltNormQ - m_fltBufferQ [ 1 ] ) ;
fltVal - = m_fltBufferQ [ 0 ] * ( fltNormI - m_fltBufferI [ 1 ] ) ;
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
fltVal + = 2.0f ;
2017-03-22 20:15:10 -04:00
fltVal / = 4.0f ;
2017-02-23 02:18:56 -05:00
}
2017-03-18 00:37:52 -04:00
else
2017-02-23 02:18:56 -05:00
{
2017-03-18 00:37:52 -04:00
//YDiff Folded
fltVal = m_fltBufferI [ 2 ] * ( ( m_fltBufferQ [ 5 ] - fltNormQ ) / 16.0f + m_fltBufferQ [ 1 ] - m_fltBufferQ [ 3 ] ) ;
fltVal - = m_fltBufferQ [ 2 ] * ( ( m_fltBufferI [ 5 ] - fltNormI ) / 16.0f + m_fltBufferI [ 1 ] - m_fltBufferI [ 3 ] ) ;
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
fltVal + = 2.125f ;
2017-03-22 20:15:10 -04:00
fltVal / = 4.25f ;
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
m_fltBufferI [ 5 ] = m_fltBufferI [ 4 ] ;
m_fltBufferQ [ 5 ] = m_fltBufferQ [ 4 ] ;
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
m_fltBufferI [ 4 ] = m_fltBufferI [ 3 ] ;
m_fltBufferQ [ 4 ] = m_fltBufferQ [ 3 ] ;
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
m_fltBufferI [ 3 ] = m_fltBufferI [ 2 ] ;
m_fltBufferQ [ 3 ] = m_fltBufferQ [ 2 ] ;
m_fltBufferI [ 2 ] = m_fltBufferI [ 1 ] ;
m_fltBufferQ [ 2 ] = m_fltBufferQ [ 1 ] ;
2017-03-16 13:56:20 -04:00
}
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
m_fltBufferI [ 1 ] = m_fltBufferI [ 0 ] ;
m_fltBufferQ [ 1 ] = m_fltBufferQ [ 0 ] ;
m_fltBufferI [ 0 ] = fltNormI ;
m_fltBufferQ [ 0 ] = fltNormQ ;
2017-03-22 20:15:10 -04:00
if ( m_objRFRunning . m_fmDeviation ! = 1.0f )
{
fltVal = ( ( fltVal - 0.5f ) / m_objRFRunning . m_fmDeviation ) + 0.5f ;
}
2017-03-18 00:37:52 -04:00
}
2017-03-19 05:28:54 -04:00
else if ( m_objRFRunning . m_enmModulation = = ATV_AM )
2017-03-18 00:37:52 -04:00
{
//Amplitude AM
2017-03-22 18:55:47 -04:00
magSq = fltI * fltI + fltQ * fltQ ;
m_objMagSqAverage . feed ( magSq ) ;
fltNorm = sqrt ( magSq ) ;
2017-03-18 00:37:52 -04:00
fltVal = fltNorm ;
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
//********** Mini and Maxi Amplitude tracking **********
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
if ( fltVal < m_fltEffMin )
2017-02-23 02:18:56 -05:00
{
2017-03-18 00:37:52 -04:00
m_fltEffMin = fltVal ;
2017-02-23 02:18:56 -05:00
}
2017-03-18 00:37:52 -04:00
if ( fltVal > m_fltEffMax )
2017-02-23 02:18:56 -05:00
{
2017-03-18 00:37:52 -04:00
m_fltEffMax = fltVal ;
2017-02-23 02:18:56 -05:00
}
2017-03-18 00:37:52 -04:00
//Normalisation
fltVal - = m_fltAmpMin ;
fltVal / = m_fltAmpDelta ;
}
2017-03-19 05:28:54 -04:00
else if ( ( m_objRFRunning . m_enmModulation = = ATV_USB ) | | ( m_objRFRunning . m_enmModulation = = ATV_LSB ) )
{
2017-03-22 18:55:47 -04:00
magSq = fltI * fltI + fltQ * fltQ ;
m_objMagSqAverage . feed ( magSq ) ;
fltNorm = sqrt ( magSq ) ;
2017-03-19 05:28:54 -04:00
Real bfoValues [ 2 ] ;
float fltFiltered = m_bfoFilter . run ( fltI ) ;
m_bfoPLL . process ( fltFiltered , bfoValues ) ;
// do the mix
float mixI = fltI * bfoValues [ 0 ] - fltQ * bfoValues [ 1 ] ;
float mixQ = fltI * bfoValues [ 1 ] + fltQ * bfoValues [ 0 ] ;
if ( m_objRFRunning . m_enmModulation = = ATV_USB ) {
fltVal = ( mixI + mixQ ) ;
} else {
fltVal = ( mixI - mixQ ) ;
}
//********** Mini and Maxi Amplitude tracking **********
if ( fltVal < m_fltEffMin )
{
m_fltEffMin = fltVal ;
}
if ( fltVal > m_fltEffMax )
{
m_fltEffMax = fltVal ;
}
//Normalisation
fltVal - = m_fltAmpMin ;
fltVal / = m_fltAmpDelta ;
}
2017-03-22 18:55:47 -04:00
else if ( m_objRFRunning . m_enmModulation = = ATV_FM3 )
{
float rawDeviation ;
2017-03-22 20:15:10 -04:00
fltVal = m_objPhaseDiscri . phaseDiscriminatorDelta ( c , magSq , rawDeviation ) + 0.5f ;
//fltVal = fltVal < 0.0f ? 0.0f : fltVal > 1.0f ? 1.0f : fltVal;
2017-03-22 18:55:47 -04:00
m_objMagSqAverage . feed ( magSq ) ;
fltNorm = sqrt ( magSq ) ;
}
2017-03-18 00:37:52 -04:00
else
{
2017-03-22 18:55:47 -04:00
magSq = fltI * fltI + fltQ * fltQ ;
m_objMagSqAverage . feed ( magSq ) ;
fltNorm = sqrt ( magSq ) ;
2017-03-18 00:37:52 -04:00
fltVal = 0.0f ;
}
2017-02-23 02:18:56 -05:00
2017-03-22 18:55:47 -04:00
m_objScopeSampleBuffer . push_back ( Sample ( fltVal * 32767.0f , 0.0f ) ) ;
2017-03-20 06:57:52 -04:00
2017-03-19 14:10:48 -04:00
fltVal = m_objRunning . m_blnInvertVideo ? 1.0f - fltVal : fltVal ;
2017-03-18 00:37:52 -04:00
m_fltAmpLineAverage + = fltVal ;
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
//********** gray level **********
//-0.3 -> 0.7
intVal = ( int ) 255.0 * ( fltVal - m_objRunning . m_fltVoltLevelSynchroBlack ) / fltDivSynchroBlack ;
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
//0 -> 255
if ( intVal < 0 )
{
intVal = 0 ;
}
else if ( intVal > 255 )
{
intVal = 255 ;
}
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
//********** Filling pixels **********
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
bool blnComputeImage = ( m_objRunning . m_fltRatioOfRowsToDisplay ! = 0.5f ) ;
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
if ( ! blnComputeImage )
{
blnComputeImage = ( ( m_intImageIndex / 2 ) % 2 = = 0 ) ;
}
if ( blnComputeImage )
{
2017-03-28 13:02:03 -04:00
m_objRegisteredATVScreen - > setDataColor ( m_intColIndex - m_intNumberSamplePerTop , intVal , intVal , intVal ) ; // TODO: the subtraction should be made with back porch number of samples
2017-03-18 00:37:52 -04:00
}
m_intColIndex + + ;
//////////////////////
m_blnSynchroDetected = false ;
2017-03-28 13:02:03 -04:00
2017-03-18 00:37:52 -04:00
if ( ( m_objRunning . m_blnHSync ) & & ( m_intRowIndex > 1 ) )
{
//********** Line Synchro 0-0-0 -> 0.3-0.3 0.3 **********
if ( m_blnImageDetecting = = false )
2017-02-23 02:18:56 -05:00
{
2017-03-18 00:37:52 -04:00
//Floor Detection 0
if ( fltVal < = m_objRunning . m_fltVoltLevelSynchroTop )
2017-02-23 02:18:56 -05:00
{
2017-03-18 00:37:52 -04:00
m_intSynchroPoints + + ;
}
else
{
m_intSynchroPoints = 0 ;
}
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
if ( m_intSynchroPoints > = m_intNumberSamplePerTop )
{
m_blnSynchroDetected = true ;
m_blnImageDetecting = true ;
m_intSynchroPoints = 0 ;
}
}
else
{
//Image detection Sub Black 0.3
if ( fltVal > = m_objRunning . m_fltVoltLevelSynchroBlack )
{
m_intSynchroPoints + + ;
2017-02-23 02:18:56 -05:00
}
else
{
2017-03-18 00:37:52 -04:00
m_intSynchroPoints = 0 ;
}
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
if ( m_intSynchroPoints > = m_intNumberSamplePerTop )
{
m_blnSynchroDetected = false ;
m_blnImageDetecting = false ;
m_intSynchroPoints = 0 ;
2017-02-23 02:18:56 -05:00
}
}
2017-03-18 00:37:52 -04:00
}
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
//********** Rendering if necessary **********
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
// Vertical Synchro : 3/4 a line necessary
if ( ! m_blnVerticalSynchroDetected & & m_objRunning . m_blnVSync )
{
if ( m_intColIndex > = intSynchroTimeSamples )
{
if ( m_fltAmpLineAverage < = fltSynchroTrameLevel ) //(m_fltLevelSynchroBlack*(float)(m_intColIndex-((m_intNumberSamplePerLine*12)/64)))) //75
{
m_blnVerticalSynchroDetected = true ;
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
m_intRowIndex = m_intImageIndex % 2 ;
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
if ( blnComputeImage )
{
2017-03-26 20:29:02 -04:00
m_objRegisteredATVScreen - > selectRow ( m_intRowIndex - m_intNumberOfSyncLines ) ;
2017-02-23 02:18:56 -05:00
}
}
}
2017-03-18 00:37:52 -04:00
}
2017-02-23 02:18:56 -05:00
2017-03-28 13:02:03 -04:00
// TODO: stabilize horizontal sync by (exponential) averaging the column index at sync detection.
// Then on the next block trigger on this column index average.
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
//Horizontal Synchro
if ( ( m_intColIndex > = m_intNumberSamplePerLine )
| | ( m_blnSynchroDetected = = true ) )
{
2017-03-28 13:27:30 -04:00
m_intColIndex = 0 ;
2017-03-18 00:37:52 -04:00
m_blnImageDetecting = true ;
2017-03-28 13:27:30 -04:00
m_blnSynchroDetected = false ;
m_blnLineSynchronized = false ;
m_fltAmpLineAverage = 0.0f ;
2017-02-23 02:18:56 -05:00
2017-03-28 13:27:30 -04:00
//New line + Interleaving
m_intRowIndex + + ;
m_intRowIndex + + ;
2017-02-24 17:25:09 -05:00
2017-03-28 13:27:30 -04:00
if ( m_intRowIndex < m_intNumberOfLines )
2017-03-18 00:37:52 -04:00
{
2017-03-28 13:27:30 -04:00
m_objRegisteredATVScreen - > selectRow ( m_intRowIndex - m_intNumberOfSyncLines ) ;
2017-02-23 02:18:56 -05:00
}
2017-03-18 00:37:52 -04:00
}
//////////////////////
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
if ( m_intRowIndex > = m_intRowsLimit )
{
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
m_blnVerticalSynchroDetected = false ;
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
m_fltAmpLineAverage = 0.0f ;
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
//Interleave Odd/Even images
m_intRowIndex = m_intImageIndex % 2 ;
m_intColIndex = 0 ;
if ( blnComputeImage )
{
2017-03-26 20:29:02 -04:00
m_objRegisteredATVScreen - > selectRow ( m_intRowIndex - m_intNumberOfSyncLines ) ;
2017-03-18 00:37:52 -04:00
}
//Rendering when odd image processed
if ( m_intImageIndex % 2 = = 1 )
{
//interleave
2017-02-23 02:18:56 -05:00
if ( blnComputeImage )
{
2017-03-18 00:37:52 -04:00
m_objRegisteredATVScreen - > renderImage ( NULL ) ;
2017-02-23 02:18:56 -05:00
}
2017-03-18 00:37:52 -04:00
m_intRowsLimit = m_intNumberOfLines - 1 ;
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
if ( m_objRFRunning . m_enmModulation = = ATV_AM )
{
m_fltAmpMin = m_fltEffMin ;
m_fltAmpMax = m_fltEffMax ;
m_fltAmpDelta = m_fltEffMax - m_fltEffMin ;
2017-02-23 02:18:56 -05:00
2017-03-18 00:37:52 -04:00
if ( m_fltAmpDelta < = 0.0 )
2017-02-23 02:18:56 -05:00
{
2017-03-18 00:37:52 -04:00
m_fltAmpDelta = 1.0f ;
2017-02-23 02:18:56 -05:00
}
2017-03-18 00:37:52 -04:00
//Reset extrema
m_fltEffMin = 2000000.0f ;
m_fltEffMax = - 2000000.0f ;
}
}
else
{
if ( m_intNumberOfLines % 2 = = 1 )
{
m_intRowsLimit = m_intNumberOfLines ;
2017-02-23 02:18:56 -05:00
}
else
2017-02-25 01:12:41 -05:00
{
2017-03-18 00:37:52 -04:00
m_intRowsLimit = m_intNumberOfLines - 2 ;
2017-02-23 02:18:56 -05:00
}
}
2017-03-18 00:37:52 -04:00
m_intImageIndex + + ;
2017-02-23 02:18:56 -05:00
}
}
void ATVDemod : : start ( )
{
2017-02-25 01:12:41 -05:00
//m_objTimer.start();
2017-02-23 02:18:56 -05:00
}
void ATVDemod : : stop ( )
{
}
bool ATVDemod : : handleMessage ( const Message & cmd )
2017-02-25 01:12:41 -05:00
{
2017-03-05 21:36:02 -05:00
qDebug ( ) < < " ATVDemod::handleMessage " ;
2017-02-23 02:18:56 -05:00
if ( DownChannelizer : : MsgChannelizerNotification : : match ( cmd ) )
2017-03-05 21:36:02 -05:00
{
2017-02-23 02:18:56 -05:00
DownChannelizer : : MsgChannelizerNotification & objNotif = ( DownChannelizer : : MsgChannelizerNotification & ) cmd ;
2017-03-16 20:42:15 -04:00
m_objConfig . m_intSampleRate = objNotif . getSampleRate ( ) ;
2017-03-17 19:11:48 -04:00
m_objRFConfig . m_intFrequencyOffset = objNotif . getFrequencyOffset ( ) ;
2017-02-23 02:18:56 -05:00
qDebug ( ) < < " ATVDemod::handleMessage: MsgChannelizerNotification: "
2017-03-17 19:11:48 -04:00
< < " m_intSampleRate: " < < m_objConfig . m_intSampleRate
< < " m_intFrequencyOffset: " < < m_objRFConfig . m_intFrequencyOffset ;
2017-02-23 02:18:56 -05:00
2017-03-16 21:05:13 -04:00
applySettings ( ) ;
2017-03-16 21:00:16 -04:00
2017-03-05 21:36:02 -05:00
return true ;
}
else if ( MsgConfigureATVDemod : : match ( cmd ) )
{
2017-02-23 02:18:56 -05:00
MsgConfigureATVDemod & objCfg = ( MsgConfigureATVDemod & ) cmd ;
2017-03-17 05:06:58 -04:00
m_objConfig = objCfg . m_objMsgConfig ;
2017-03-16 16:07:51 -04:00
2017-03-16 19:58:12 -04:00
qDebug ( ) < < " ATVDemod::handleMessage: MsgConfigureATVDemod: "
2017-03-19 05:28:54 -04:00
< < " m_fltVoltLevelSynchroBlack: " < < m_objConfig . m_fltVoltLevelSynchroBlack
< < " m_fltVoltLevelSynchroTop: " < < m_objConfig . m_fltVoltLevelSynchroTop
< < " m_fltFramePerS: " < < m_objConfig . m_fltFramePerS
2017-03-20 17:37:11 -04:00
< < " m_fltLineDurationUs: " < < m_objConfig . m_fltLineDuration
2017-03-19 05:28:54 -04:00
< < " m_fltRatioOfRowsToDisplay: " < < m_objConfig . m_fltRatioOfRowsToDisplay
2017-03-26 17:47:21 -04:00
< < " m_fltTopDurationUs: " < < m_objConfig . m_fltTopDuration
2017-03-19 05:28:54 -04:00
< < " m_blnHSync: " < < m_objConfig . m_blnHSync
< < " m_blnVSync: " < < m_objConfig . m_blnVSync ;
2017-03-16 19:58:12 -04:00
2017-03-16 21:05:13 -04:00
applySettings ( ) ;
2017-02-23 02:18:56 -05:00
2017-03-05 21:36:02 -05:00
return true ;
}
2017-03-17 05:04:00 -04:00
else if ( MsgConfigureRFATVDemod : : match ( cmd ) )
{
MsgConfigureRFATVDemod & objCfg = ( MsgConfigureRFATVDemod & ) cmd ;
m_objRFConfig = objCfg . m_objMsgConfig ;
qDebug ( ) < < " ATVDemod::handleMessage: MsgConfigureRFATVDemod: "
2017-03-19 05:28:54 -04:00
< < " m_enmModulation: " < < m_objRFConfig . m_enmModulation
< < " m_fltRFBandwidth: " < < m_objRFConfig . m_fltRFBandwidth
< < " m_fltRFOppBandwidth: " < < m_objRFConfig . m_fltRFOppBandwidth
< < " m_blnFFTFiltering: " < < m_objRFConfig . m_blnFFTFiltering
< < " m_blnDecimatorEnable: " < < m_objRFConfig . m_blndecimatorEnable
2017-03-22 18:55:47 -04:00
< < " m_fltBFOFrequency: " < < m_objRFConfig . m_fltBFOFrequency
< < " m_fmDeviation: " < < m_objRFConfig . m_fmDeviation ;
2017-03-17 05:04:00 -04:00
applySettings ( ) ;
return true ;
}
2017-03-05 21:36:02 -05:00
else
{
2017-03-20 06:57:52 -04:00
if ( m_objScopeSink ! = 0 )
{
return m_objScopeSink - > handleMessage ( cmd ) ;
}
else
{
return false ;
}
2017-03-05 21:36:02 -05:00
}
2017-02-23 02:18:56 -05:00
}
2017-03-16 21:05:13 -04:00
void ATVDemod : : applySettings ( )
2017-02-25 01:12:41 -05:00
{
2017-02-23 02:18:56 -05:00
2017-03-16 20:42:15 -04:00
if ( m_objConfig . m_intSampleRate = = 0 )
2017-02-23 02:18:56 -05:00
{
return ;
}
2017-03-20 00:06:33 -04:00
if ( ( m_objRFConfig . m_intFrequencyOffset ! = m_objRFRunning . m_intFrequencyOffset )
| | ( m_objRFConfig . m_enmModulation ! = m_objRFRunning . m_enmModulation )
| | ( m_objConfig . m_intSampleRate ! = m_objRunning . m_intSampleRate ) )
2017-03-17 19:11:48 -04:00
{
m_nco . setFreq ( - m_objRFConfig . m_intFrequencyOffset , m_objConfig . m_intSampleRate ) ;
}
if ( ( m_objConfig . m_intSampleRate ! = m_objRunning . m_intSampleRate )
| | ( m_objRFConfig . m_fltRFBandwidth ! = m_objRFRunning . m_fltRFBandwidth ) )
{
2017-03-18 04:19:51 -04:00
m_objSettingsMutex . lock ( ) ;
2017-03-17 19:11:48 -04:00
2017-03-23 21:42:36 -04:00
m_objConfigPrivate . m_intTVSampleRate = ( m_objConfig . m_intSampleRate / 500000 ) * 500000 ; // make sure working sample rate is a multiple of rate units
2017-03-18 05:04:57 -04:00
if ( m_objConfigPrivate . m_intTVSampleRate > 0 )
2017-03-17 19:11:48 -04:00
{
2017-03-18 05:04:57 -04:00
m_interpolatorDistance = ( Real ) m_objConfigPrivate . m_intTVSampleRate / ( Real ) m_objConfig . m_intSampleRate ;
2017-03-17 19:11:48 -04:00
}
else
{
2017-03-18 05:04:57 -04:00
m_objConfigPrivate . m_intTVSampleRate = m_objConfig . m_intSampleRate ;
2017-03-17 19:11:48 -04:00
m_interpolatorDistance = 1.0f ;
}
m_interpolatorDistanceRemain = 0 ;
2017-03-20 00:06:33 -04:00
m_interpolator . create ( 24 ,
m_objConfigPrivate . m_intTVSampleRate ,
m_objRFConfig . m_fltRFBandwidth / getRFBandwidthDivisor ( m_objRFConfig . m_enmModulation ) ,
3.0 ) ;
2017-03-18 04:19:51 -04:00
m_objSettingsMutex . unlock ( ) ;
2017-03-17 19:11:48 -04:00
}
2017-03-16 20:42:15 -04:00
if ( ( m_objConfig . m_fltFramePerS ! = m_objRunning . m_fltFramePerS )
2017-03-20 17:37:11 -04:00
| | ( m_objConfig . m_fltLineDuration ! = m_objRunning . m_fltLineDuration )
2017-03-16 21:00:16 -04:00
| | ( m_objConfig . m_intSampleRate ! = m_objRunning . m_intSampleRate )
2017-03-26 17:47:21 -04:00
| | ( m_objConfig . m_fltTopDuration ! = m_objRunning . m_fltTopDuration )
2017-03-26 20:29:02 -04:00
| | ( m_objConfig . m_fltRatioOfRowsToDisplay ! = m_objRunning . m_fltRatioOfRowsToDisplay )
| | ( m_objConfig . m_enmATVStandard ! = m_objRunning . m_enmATVStandard ) )
2017-03-16 20:42:15 -04:00
{
m_objSettingsMutex . lock ( ) ;
2017-03-23 21:42:36 -04:00
m_intNumberOfLines = ( int ) ( 1.0f / ( m_objConfig . m_fltLineDuration * m_objConfig . m_fltFramePerS ) ) ;
2017-03-20 17:37:11 -04:00
m_intNumberSamplePerLine = ( int ) ( m_objConfig . m_fltLineDuration * m_objConfig . m_intSampleRate ) ;
m_intNumberOfRowsToDisplay = ( int ) ( m_objConfig . m_fltRatioOfRowsToDisplay * m_objConfig . m_fltLineDuration * m_objConfig . m_intSampleRate ) ;
2017-03-26 20:29:02 -04:00
m_intNumberSamplePerTop = ( int ) ( m_objConfig . m_fltTopDuration * m_objConfig . m_intSampleRate ) ;
applyStandard ( ) ;
m_objRegisteredATVScreen - > resizeATVScreen (
m_intNumberSamplePerLine - ( m_intNumberSamplePerTop + m_intNumberSamplePerEndOfLine ) ,
m_intNumberOfLines - m_intNumberOfBlackLines ) ;
2017-03-23 20:01:53 -04:00
qDebug ( ) < < " ATVDemod::applySettings: "
< < " m_fltLineDuration: " < < m_objConfig . m_fltLineDuration
< < " m_fltFramePerS: " < < m_objConfig . m_fltFramePerS
< < " m_intNumberOfLines: " < < m_intNumberOfLines
< < " m_intNumberSamplePerLine: " < < m_intNumberSamplePerLine
2017-03-26 20:29:02 -04:00
< < " m_intNumberOfRowsToDisplay: " < < m_intNumberOfRowsToDisplay
< < " m_intNumberOfBlackLines: " < < m_intNumberOfBlackLines ;
2017-03-16 20:42:15 -04:00
2017-03-16 21:00:16 -04:00
m_intRowsLimit = m_intNumberOfLines - 1 ;
m_intImageIndex = 0 ;
m_intColIndex = 0 ;
m_intRowIndex = 0 ;
m_intRowsLimit = 0 ;
2017-03-16 20:42:15 -04:00
2017-03-16 20:47:16 -04:00
m_objSettingsMutex . unlock ( ) ;
2017-03-23 21:42:36 -04:00
int sampleRate = m_objRFConfig . m_blndecimatorEnable ? m_objConfigPrivate . m_intTVSampleRate : m_objConfig . m_intSampleRate ;
MsgReportEffectiveSampleRate * report ;
report = MsgReportEffectiveSampleRate : : create ( sampleRate , m_intNumberSamplePerLine ) ;
getOutputMessageQueue ( ) - > push ( report ) ;
2017-03-16 20:47:16 -04:00
}
2017-03-18 05:04:57 -04:00
if ( ( m_objConfigPrivate . m_intTVSampleRate ! = m_objRunningPrivate . m_intTVSampleRate )
| | ( m_objConfig . m_intSampleRate ! = m_objRunning . m_intSampleRate )
| | ( m_objRFConfig . m_blndecimatorEnable ! = m_objRFRunning . m_blndecimatorEnable ) )
{
int sampleRate = m_objRFConfig . m_blndecimatorEnable ? m_objConfigPrivate . m_intTVSampleRate : m_objConfig . m_intSampleRate ;
MsgReportEffectiveSampleRate * report ;
2017-03-23 21:42:36 -04:00
report = MsgReportEffectiveSampleRate : : create ( sampleRate , m_intNumberSamplePerLine ) ;
2017-03-18 05:04:57 -04:00
getOutputMessageQueue ( ) - > push ( report ) ;
}
2017-03-18 08:19:24 -04:00
if ( ( m_objConfigPrivate . m_intTVSampleRate ! = m_objRunningPrivate . m_intTVSampleRate )
| | ( m_objRFConfig . m_fltRFBandwidth ! = m_objRFRunning . m_fltRFBandwidth )
| | ( m_objRFConfig . m_fltRFOppBandwidth ! = m_objRFRunning . m_fltRFOppBandwidth ) )
{
m_objSettingsMutex . lock ( ) ;
m_DSBFilter - > create_asym_filter ( m_objRFConfig . m_fltRFOppBandwidth / m_objConfigPrivate . m_intTVSampleRate ,
m_objRFConfig . m_fltRFBandwidth / m_objConfigPrivate . m_intTVSampleRate ) ;
memset ( m_DSBFilterBuffer , 0 , sizeof ( Complex ) * ( m_ssbFftLen ) ) ;
m_DSBFilterBufferIndex = 0 ;
m_objSettingsMutex . unlock ( ) ;
}
2017-03-19 05:28:54 -04:00
if ( ( m_objConfigPrivate . m_intTVSampleRate ! = m_objRunningPrivate . m_intTVSampleRate )
| | ( m_objRFConfig . m_fltBFOFrequency ! = m_objRFRunning . m_fltBFOFrequency ) )
{
m_bfoPLL . configure ( m_objRFConfig . m_fltBFOFrequency / m_objConfigPrivate . m_intTVSampleRate ,
100.0 / m_objConfigPrivate . m_intTVSampleRate ,
0.01 ) ;
m_bfoFilter . setFrequencies ( m_objRFConfig . m_fltBFOFrequency , m_objConfigPrivate . m_intTVSampleRate ) ;
}
2017-03-18 08:19:24 -04:00
2017-03-22 20:15:10 -04:00
if ( m_objRFConfig . m_fmDeviation ! = m_objRFRunning . m_fmDeviation )
{
m_objPhaseDiscri . setFMScaling ( 1.0f / m_objRFConfig . m_fmDeviation ) ;
}
2017-03-22 18:55:47 -04:00
2017-03-17 05:06:58 -04:00
m_objRunning = m_objConfig ;
2017-03-17 05:04:00 -04:00
m_objRFRunning = m_objRFConfig ;
2017-03-18 05:04:57 -04:00
m_objRunningPrivate = m_objConfigPrivate ;
2017-02-23 02:18:56 -05:00
}
2017-03-26 20:29:02 -04:00
void ATVDemod : : applyStandard ( )
{
switch ( m_objConfig . m_enmATVStandard )
{
case ATVStd405 : // Follows loosely the 405 lines standard
// what is left in a 64 us line for the image
m_intNumberOfSyncLines = 24 ; // (15+7)*2 - 20
m_intNumberOfBlackLines = 28 ; // above + 4
break ;
case ATVStdPAL525 : // Follows PAL-M standard
// what is left in a 64/1.008 us line for the image
m_intNumberOfSyncLines = 40 ; // (15+15)*2 - 20
m_intNumberOfBlackLines = 44 ; // above + 4
break ;
case ATVStdPAL625 : // Follows PAL-B/G/H standard
default :
// what is left in a 64 us line for the image
m_intNumberOfSyncLines = 44 ; // (15+17)*2 - 20
m_intNumberOfBlackLines = 48 ; // above + 4
}
// for now all standards apply this
m_intNumberSamplePerEndOfLine = ( int ) ( ( 7.3f / 64.0f ) * m_objConfig . m_fltLineDuration * m_objConfig . m_intSampleRate ) ;
}
2017-03-16 21:05:13 -04:00
int ATVDemod : : getSampleRate ( )
2017-02-23 02:18:56 -05:00
{
2017-03-16 18:29:22 -04:00
return m_objRunning . m_intSampleRate ;
2017-02-23 02:18:56 -05:00
}
2017-03-05 21:36:02 -05:00
2017-03-18 06:05:28 -04:00
int ATVDemod : : getEffectiveSampleRate ( )
{
return m_objRFRunning . m_blndecimatorEnable ? m_objRunningPrivate . m_intTVSampleRate : m_objRunning . m_intSampleRate ;
}
2017-03-19 05:28:54 -04:00
bool ATVDemod : : getBFOLocked ( )
{
if ( ( m_objRFRunning . m_enmModulation = = ATV_USB ) | | ( m_objRFRunning . m_enmModulation = = ATV_LSB ) )
{
return m_bfoPLL . locked ( ) ;
}
else
{
return false ;
}
}
2017-03-20 00:06:33 -04:00
float ATVDemod : : getRFBandwidthDivisor ( ATVModulation modulation )
{
switch ( modulation )
{
case ATV_USB :
case ATV_LSB :
return 1.05f ;
break ;
case ATV_FM1 :
case ATV_FM2 :
case ATV_AM :
default :
return 2.2f ;
}
}