ATV Demod: include in build tree

This commit is contained in:
f4exb 2017-02-23 08:18:56 +01:00
parent 763d486514
commit 2e93f68e39
15 changed files with 2543 additions and 1 deletions

View File

@ -10,7 +10,7 @@ add_subdirectory(udpsrc)
add_subdirectory(demodwfm)
add_subdirectory(chanalyzer)
add_subdirectory(chanalyzerng)
#add_subdirectory(demodatv)
add_subdirectory(demodatv)
if(LIBDSDCC_FOUND AND LIBMBE_FOUND)
add_subdirectory(demoddsd)

View File

@ -0,0 +1,49 @@
project(atv)
set(atv_SOURCES
atvdemod.cpp
atvdemodgui.cpp
atvdemodplugin.cpp
atvscreen.cpp
glshaderarray.cpp
)
set(atv_HEADERS
atvdemod.h
atvdemodgui.h
atvdemodplugin.h
atvscreen.h
glshaderarray.h
)
set(atv_FORMS
atvdemodgui.ui
)
include_directories(
.
${CMAKE_CURRENT_BINARY_DIR}
)
#include(${QT_USE_FILE})
add_definitions(${QT_DEFINITIONS})
add_definitions(-DQT_PLUGIN)
add_definitions(-DQT_SHARED)
#qt5_wrap_cpp(nfm_HEADERS_MOC ${nfm_HEADERS})
qt5_wrap_ui(atv_FORMS_HEADERS ${atv_FORMS})
add_library(demodatv SHARED
${atv_SOURCES}
${atv_HEADERS_MOC}
${atv_FORMS_HEADERS}
)
target_link_libraries(demodatv
${QT_LIBRARIES}
sdrbase
)
qt5_use_modules(demodatv Core Widgets)
install(TARGETS demodatv DESTINATION lib/plugins/channelrx)

View File

@ -0,0 +1,586 @@
///////////////////////////////////////////////////////////////////////////////////
// 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)
ATVDemod::ATVDemod() :
m_objSettingsMutex(QMutex::NonRecursive),
m_objRegisteredATVScreen(NULL),
m_intImageIndex(0),
m_intColIndex(0),
m_intRowIndex(0),
m_intSynchroPoints(0),
m_blnSynchroDetected(false),
m_blnVerticalSynchroDetected(false),
m_fltLevelSynchroTop(0.0),
m_fltLevelSynchroBlack(1.0),
m_enmModulation(ATV_FM1),
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),
m_fltAmpLineAverage(0.0f)
{
setObjectName("ATVDemod");
//*************** ATV PARAMETERS ***************
m_intNumberSamplePerLine=0;
m_intSynchroPoints=0;
m_intNumberOfLines=0;
m_blnInitialized=false;
m_intNumberOfRowsToDisplay=0;
memset((void*)m_fltBufferI,0,6*sizeof(float));
memset((void*)m_fltBufferQ,0,6*sizeof(float));
}
ATVDemod::~ATVDemod()
{
}
bool ATVDemod::SetATVScreen(ATVScreen *objScreen)
{
m_objRegisteredATVScreen = objScreen;
}
void ATVDemod::configure(MessageQueue* objMessageQueue, int intLineDurationUs, int intTopDurationUs, int intFramePerS, int intPercentOfRowsToDisplay, float fltVoltLevelSynchroTop, float fltVoltLevelSynchroBlack, ATVModulation enmModulation, bool blnHSync, bool blnVSync)
{
Message* msgCmd = MsgConfigureATVDemod::create(intLineDurationUs, intTopDurationUs, intFramePerS, intPercentOfRowsToDisplay, fltVoltLevelSynchroTop, fltVoltLevelSynchroBlack, enmModulation,blnHSync,blnVSync);
objMessageQueue->push(msgCmd);
}
void ATVDemod::InitATVParameters(int intMsps, int intLineDurationUs, int intTopDurationUs, int intFramePerS, int intPercentOfRowsToDisplay, float fltVoltLevelSynchroTop, float fltVoltLevelSynchroBlack, ATVModulation enmModulation, bool blnHSync, bool blnVSync)
{
float fltSecondToUs = 1000000.0f;
float fltSampling=(float) intMsps;
float fltLineTimeUs=((float) intLineDurationUs)/10.0f;
float fltLineSynchroTop=(float) intTopDurationUs;
float fltImagesPerSeconds=(float) intFramePerS;
int intNumberSamplePerLine;
int intNumberOfLines;
bool blnNewOpenGLScreen=false;
m_blnInitialized=false;
m_objSettingsMutex.lock();
if(m_objRegisteredATVScreen==NULL)
{
m_intNumberSamplePerLine=0;
m_intNumberSamplePerTop=0;
m_intNumberOfLines=0;
m_fltLevelSynchroTop=0.0;
m_fltLevelSynchroBlack=1.0;
m_blnInitialized=false;
m_objSettingsMutex.unlock();
return;
}
m_fltLevelSynchroTop = fltVoltLevelSynchroTop;
m_fltLevelSynchroBlack = fltVoltLevelSynchroBlack;
intNumberSamplePerLine=(int)((fltLineTimeUs*fltSampling)/fltSecondToUs);
intNumberOfLines=(int)((fltSecondToUs/fltImagesPerSeconds)/round(fltLineTimeUs));
if((intNumberSamplePerLine!=m_intNumberSamplePerLine)
|| (intNumberOfLines!=m_intNumberOfLines))
{
blnNewOpenGLScreen=true;
}
m_intNumberSamplePerLine= intNumberSamplePerLine;
m_intNumberSamplePerTop=(int)((fltLineSynchroTop*fltSampling)/fltSecondToUs);
m_intNumberOfLines = intNumberOfLines;
m_intNumberOfRowsToDisplay = (int)((((float)intPercentOfRowsToDisplay)*fltLineTimeUs*fltSampling)/(fltSecondToUs*100.0f));
m_intRowsLimit = m_intNumberOfLines-1;
m_intImageIndex = 0;
m_enmModulation = enmModulation;
m_intColIndex=0;
m_intRowIndex=0;
m_intRowsLimit=0;
if(blnNewOpenGLScreen)
{
m_objRegisteredATVScreen->resizeATVScreen(m_intNumberSamplePerLine,m_intNumberOfLines);
}
//Mise à jour de la config
m_objRunning.m_enmModulation = m_enmModulation;
m_objRunning.m_fltVoltLevelSynchroBlack = m_fltLevelSynchroBlack;
m_objRunning.m_fltVoltLevelSynchroTop = m_fltLevelSynchroTop;
m_objRunning.m_intFramePerS = intFramePerS;
m_objRunning.m_intLineDurationUs = intLineDurationUs;
m_objRunning.m_intTopDurationUs = intTopDurationUs;
m_objRunning.m_intMsps = intMsps;
m_objRunning.m_intPercentOfRowsToDisplay = intPercentOfRowsToDisplay;
m_objRunning.m_blnHSync = blnHSync;
m_objRunning.m_blnVSync = blnVSync;
qDebug() << "ATV Settings "
<< " - Msps: " << intMsps
<< " - Line us: " << intLineDurationUs
<< " - Top us: " << intTopDurationUs
<< " - Frame/s: " << intFramePerS
<< " <=> "
<< " - Samples per Line: " << m_intNumberSamplePerLine
<< " - Samples per Top: " << m_intNumberSamplePerTop
<< " - Lines per Frame: " << m_intNumberOfLines
<< " - Rows to Display: " << m_intNumberOfRowsToDisplay
<< " - Modulation: " << ((m_enmModulation==ATV_AM)?"AM" : "FM");
m_objSettingsMutex.unlock();
m_blnInitialized=true;
}
void ATVDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst)
{
float fltDivSynchroBlack=1.0f-m_fltLevelSynchroBlack;
float fltI;
float fltQ;
float fltNormI;
float fltNormQ;
float fltNorm=0.00f;
float fltVal;
int intVal;
qint16 * ptrBufferToRelease=NULL;
bool blnComputeImage=false;
int intSynchroTimeSamples= (3*m_intNumberSamplePerLine)/4;
float fltSynchroTrameLevel = 0.5f*((float)intSynchroTimeSamples)*m_fltLevelSynchroBlack;
//********** 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 ++;
#else
for (SampleVector::const_iterator it = begin; it != end; ++it /* ++it **/)
{
fltI = it->real();
fltQ = it->imag();
#endif
//********** demodulation **********
fltNorm = sqrt(fltI*fltI + fltQ*fltQ);
if(m_enmModulation!=ATV_AM)
{
//Amplitude FM
fltNormI= fltI/fltNorm;
fltNormQ= fltQ/fltNorm;
//-2 > 2 : 0 -> 1 volt
//0->0.3 synchro 0.3->1 image
if(m_enmModulation==ATV_FM1)
{
//YDiff Cd
fltVal = m_fltBufferI[0]*(fltNormQ - m_fltBufferQ[1]);
fltVal -= m_fltBufferQ[0]*(fltNormI - m_fltBufferI[1]);
fltVal += 2.0f;
fltVal /=4.0f;
}
else
{
//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]);
fltVal += 2.125f;
fltVal /=4.25f;
m_fltBufferI[5]=m_fltBufferI[4];
m_fltBufferQ[5]=m_fltBufferQ[4];
m_fltBufferI[4]=m_fltBufferI[3];
m_fltBufferQ[4]=m_fltBufferQ[3];
m_fltBufferI[3]=m_fltBufferI[2];
m_fltBufferQ[3]=m_fltBufferQ[2];
m_fltBufferI[2]=m_fltBufferI[1];
m_fltBufferQ[2]=m_fltBufferQ[1];
}
m_fltBufferI[1]=m_fltBufferI[0];
m_fltBufferQ[1]=m_fltBufferQ[0];
m_fltBufferI[0]=fltNormI;
m_fltBufferQ[0]=fltNormQ;
}
else
{
//Amplitude AM
fltVal = fltNorm;
//********** 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;
}
m_fltAmpLineAverage += fltVal;
//********** gray level **********
//-0.3 -> 0.7
intVal = (int) 255.0*(fltVal-m_fltLevelSynchroBlack)/fltDivSynchroBlack;
//0 -> 255
if(intVal<0)
{
intVal=0;
}
else if(intVal>255)
{
intVal=255;
}
//********** Filling pixels **********
blnComputeImage=m_blnInitialized;
if(m_blnInitialized==true)
{
blnComputeImage=(m_objRunning.m_intPercentOfRowsToDisplay!=50);
if(!blnComputeImage)
{
blnComputeImage=((m_intImageIndex/2)%2==0);
}
}
if(blnComputeImage)
{
m_objRegisteredATVScreen->setDataColor(m_intColIndex,intVal, intVal, intVal);
}
m_intColIndex++;
//////////////////////
m_blnSynchroDetected=false;
if((m_objRunning.m_blnHSync) && (m_intRowIndex>1))
{
//********** Line Synchro 0-0-0 -> 0.3-0.3 0.3 **********
if(m_blnImageDetecting==false)
{
//Floor Detection 0
if(fltVal<=m_fltLevelSynchroTop)
{
m_intSynchroPoints ++;
}
else
{
m_intSynchroPoints=0;
}
if(m_intSynchroPoints>=m_intNumberSamplePerTop)
{
m_blnSynchroDetected=true;
m_blnImageDetecting=true;
m_intSynchroPoints=0;
}
}
else
{
//Image detection Sub Black 0.3
if(fltVal>=m_fltLevelSynchroBlack)
{
m_intSynchroPoints ++;
}
else
{
m_intSynchroPoints=0;
}
if(m_intSynchroPoints>=m_intNumberSamplePerTop)
{
m_blnSynchroDetected=false;
m_blnImageDetecting=false;
m_intSynchroPoints=0;
}
}
}
//********** Rendering if necessary **********
// 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;
m_intRowIndex=m_intImageIndex%2;
if(blnComputeImage)
{
m_objRegisteredATVScreen->selectRow(m_intRowIndex);
}
}
}
}
//Horizontal Synchro
if((m_intColIndex>=m_intNumberSamplePerLine)
|| (m_blnSynchroDetected==true))
{
m_blnSynchroDetected=false;
m_blnImageDetecting=true;
//New line + Interleaving
m_intRowIndex ++;
m_intRowIndex ++;
m_intColIndex=0;
if(m_intRowIndex<m_intNumberOfLines)
{
m_objRegisteredATVScreen->selectRow(m_intRowIndex);
}
m_fltAmpLineAverage=0.0f;
}
//////////////////////
if(m_intRowIndex>=m_intRowsLimit)
{
m_blnVerticalSynchroDetected=false;
m_fltAmpLineAverage=0.0f;
//Interleave Odd/Even images
m_intRowIndex=m_intImageIndex%2;
m_intColIndex=0;
if(blnComputeImage)
{
m_objRegisteredATVScreen->selectRow(m_intRowIndex);
}
//Rendering when odd image processed
if(m_intImageIndex%2==1)
{
//interleave
if(blnComputeImage)
{
m_objRegisteredATVScreen->renderImage(NULL);
}
m_intRowsLimit = m_intNumberOfLines-1;
if(m_objRunning.m_enmModulation==ATV_AM)
{
m_fltAmpMin=m_fltEffMin;
m_fltAmpMax=m_fltEffMax;
m_fltAmpDelta=m_fltEffMax-m_fltEffMin;
if(m_fltAmpDelta<=0.0)
{
m_fltAmpDelta=1.0f;
}
}
}
else
{
if(m_intNumberOfLines%2==1)
{
m_intRowsLimit = m_intNumberOfLines;
}
else
{
m_intRowsLimit = m_intNumberOfLines-2;
}
}
m_intImageIndex ++;
}
//////////////////////
}
if(ptrBufferToRelease!=NULL)
{
delete ptrBufferToRelease;
}
m_objSettingsMutex.unlock();
}
void ATVDemod::start()
{
//m_objTimer.start();
}
void ATVDemod::stop()
{
}
bool ATVDemod::handleMessage(const Message& cmd)
{
qDebug() << "ATVDemod::handleMessage";
if (DownChannelizer::MsgChannelizerNotification::match(cmd))
{
DownChannelizer::MsgChannelizerNotification& objNotif = (DownChannelizer::MsgChannelizerNotification&) cmd;
if(m_objRunning.m_intMsps!=objNotif.getSampleRate())
{
m_objRunning.m_intMsps = objNotif.getSampleRate();
ApplySettings();
}
qDebug() << "ATVDemod::handleMessage: MsgChannelizerNotification:"
<< " m_intMsps: " << m_objRunning.m_intMsps;
return true;
}
else if (MsgConfigureATVDemod::match(cmd))
{
MsgConfigureATVDemod& objCfg = (MsgConfigureATVDemod&) cmd;
if((objCfg.m_objMsgConfig.m_enmModulation != m_objRunning.m_enmModulation)
|| (objCfg.m_objMsgConfig.m_fltVoltLevelSynchroBlack != m_objRunning.m_fltVoltLevelSynchroBlack)
|| (objCfg.m_objMsgConfig.m_fltVoltLevelSynchroTop != m_objRunning.m_fltVoltLevelSynchroTop)
|| (objCfg.m_objMsgConfig.m_intFramePerS != m_objRunning.m_intFramePerS)
|| (objCfg.m_objMsgConfig.m_intLineDurationUs != m_objRunning.m_intLineDurationUs)
|| (objCfg.m_objMsgConfig.m_intPercentOfRowsToDisplay != m_objRunning.m_intPercentOfRowsToDisplay)
|| (objCfg.m_objMsgConfig.m_intTopDurationUs != m_objRunning.m_intTopDurationUs)
|| (objCfg.m_objMsgConfig.m_blnHSync != m_objRunning.m_blnHSync)
|| (objCfg.m_objMsgConfig.m_blnVSync != m_objRunning.m_blnVSync))
{
m_objRunning.m_enmModulation = objCfg.m_objMsgConfig.m_enmModulation;
m_objRunning.m_fltVoltLevelSynchroBlack = objCfg.m_objMsgConfig.m_fltVoltLevelSynchroBlack;
m_objRunning.m_fltVoltLevelSynchroTop = objCfg.m_objMsgConfig.m_fltVoltLevelSynchroTop;
m_objRunning.m_intFramePerS = objCfg.m_objMsgConfig.m_intFramePerS;
m_objRunning.m_intLineDurationUs = objCfg.m_objMsgConfig.m_intLineDurationUs;
m_objRunning.m_intPercentOfRowsToDisplay = objCfg.m_objMsgConfig.m_intPercentOfRowsToDisplay;
m_objRunning.m_intTopDurationUs = objCfg.m_objMsgConfig.m_intTopDurationUs;
m_objRunning.m_blnHSync = objCfg.m_objMsgConfig.m_blnHSync;
m_objRunning.m_blnVSync = objCfg.m_objMsgConfig.m_blnVSync;
ApplySettings();
}
return true;
}
else
{
return false;
}
}
void ATVDemod::ApplySettings()
{
if(m_objRunning.m_intMsps==0)
{
return;
}
InitATVParameters(m_objRunning.m_intMsps,m_objRunning.m_intLineDurationUs,m_objRunning.m_intTopDurationUs,m_objRunning.m_intFramePerS,m_objRunning.m_intPercentOfRowsToDisplay,m_objRunning.m_fltVoltLevelSynchroTop,m_objRunning.m_fltVoltLevelSynchroBlack,m_objRunning.m_enmModulation,m_objRunning.m_blnHSync, m_objRunning.m_blnVSync);
}
int ATVDemod::GetSampleRate()
{
return m_objRunning.m_intMsps;
}

View File

@ -0,0 +1,168 @@
///////////////////////////////////////////////////////////////////////////////////
// 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_ATVDEMOD_H
#define INCLUDE_ATVDEMOD_H
#include <dsp/basebandsamplesink.h>
#include <dsp/devicesamplesource.h>
#include <dsp/dspcommands.h>
#include <dsp/downchannelizer.h>
#include <QMutex>
#include <QElapsedTimer>
#include <vector>
#include "dsp/nco.h"
#include "dsp/interpolator.h"
#include "dsp/movingaverage.h"
#include "dsp/agc.h"
#include "audio/audiofifo.h"
#include "util/message.h"
#include "atvscreen.h"
enum ATVModulation { ATV_AM, ATV_FM1 , ATV_FM2 };
struct ATVConfig
{
int m_intMsps;
int m_intLineDurationUs;
int m_intTopDurationUs;
int m_intFramePerS;
int m_intPercentOfRowsToDisplay;
float m_fltVoltLevelSynchroTop;
float m_fltVoltLevelSynchroBlack;
ATVModulation m_enmModulation;
bool m_blnHSync;
bool m_blnVSync;
ATVConfig() :
m_intMsps(0),
m_intLineDurationUs(0),
m_intTopDurationUs(0),
m_intFramePerS(0),
m_intPercentOfRowsToDisplay(0),
m_fltVoltLevelSynchroTop(0),
m_fltVoltLevelSynchroBlack(0),
m_enmModulation(ATV_FM1),
m_blnHSync(false),
m_blnVSync(false)
{
}
};
class ATVDemod : public BasebandSampleSink
{
Q_OBJECT
public:
ATVDemod();
~ATVDemod();
void configure(MessageQueue* objMessageQueue, int intLineDurationUs, int intTopDurationUs, int intFramePerS, int intPercentOfRowsToDisplay, float fltVoltLevelSynchroTop, float fltVoltLevelSynchroBlack, ATVModulation enmModulation, bool blnHSync, bool blnVSync);
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po);
virtual void start();
virtual void stop();
virtual bool handleMessage(const Message& cmd);
bool SetATVScreen(ATVScreen *objScreen);
void InitATVParameters(int intMsps, int intLineDurationUs, int intTopDurationUs, int intFramePerS, int intPercentOfRowsToDisplay, float fltVoltLevelSynchroTop, float fltVoltLevelSynchroBlack, ATVModulation enmModulation, bool blnHSync, bool blnVSync);
int GetSampleRate();
private:
//*************** ATV PARAMETERS ***************
ATVScreen * m_objRegisteredATVScreen;
int m_intNumberSamplePerLine;
int m_intNumberSamplePerTop;
int m_intNumberOfLines;
bool m_blnInitialized;
int m_intNumberOfRowsToDisplay;
float m_fltLevelSynchroTop;
float m_fltLevelSynchroBlack;
ATVModulation m_enmModulation;
//*************** PROCESSING ***************
int m_intImageIndex;
int m_intRowsLimit;
int m_intSynchroPoints;
bool m_blnSynchroDetected;
bool m_blnImageDetecting;
bool m_blnVerticalSynchroDetected;
float m_fltAmpLineAverage;
float m_fltEffMin;
float m_fltEffMax;
float m_fltAmpMin;
float m_fltAmpMax;
float m_fltAmpDelta;
float m_fltBufferI[6];
float m_fltBufferQ[6];
int m_intColIndex;
int m_intRowIndex;
//QElapsedTimer m_objTimer;
private:
class MsgConfigureATVDemod : public Message
{
MESSAGE_CLASS_DECLARATION
public:
static MsgConfigureATVDemod* create(int intLineDurationUs, int intTopDurationUs, int intFramePerS, int intPercentOfRowsToDisplay, float fltVoltLevelSynchroTop, float fltVoltLevelSynchroBlack, ATVModulation enmModulation, bool blnHSync, bool blnVSync)
{
return new MsgConfigureATVDemod(intLineDurationUs, intTopDurationUs, intFramePerS, intPercentOfRowsToDisplay, fltVoltLevelSynchroTop, fltVoltLevelSynchroBlack, enmModulation, blnHSync, blnVSync);
}
ATVConfig m_objMsgConfig;
private:
MsgConfigureATVDemod(int intLineDurationUs, int intTopDurationUs, int intFramePerS, int intPercentOfRowsToDisplay, float fltVoltLevelSynchroTop, float fltVoltLevelSynchroBlack, ATVModulation enmModulation, bool blnHSync, bool blnVSync) :
Message()
{
m_objMsgConfig.m_enmModulation = enmModulation;
m_objMsgConfig.m_fltVoltLevelSynchroBlack = fltVoltLevelSynchroBlack;
m_objMsgConfig.m_fltVoltLevelSynchroTop = fltVoltLevelSynchroTop;
m_objMsgConfig.m_intFramePerS = intFramePerS;
m_objMsgConfig.m_intLineDurationUs = intLineDurationUs;
m_objMsgConfig.m_intTopDurationUs = intTopDurationUs;
m_objMsgConfig.m_intPercentOfRowsToDisplay = intPercentOfRowsToDisplay;
m_objMsgConfig.m_blnHSync = blnHSync;
m_objMsgConfig.m_blnVSync = blnVSync;
}
};
ATVConfig m_objRunning;
QMutex m_objSettingsMutex;
void ApplySettings();
};
#endif // INCLUDE_ATVDEMOD_H

View File

@ -0,0 +1,337 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 <QDockWidget>
#include <QMainWindow>
#include "atvdemodgui.h"
#include "device/devicesourceapi.h"
#include "dsp/downchannelizer.h"
#include "dsp/threadedbasebandsamplesink.h"
#include "ui_atvdemodgui.h"
#include "plugin/pluginapi.h"
#include "util/simpleserializer.h"
#include "util/db.h"
#include "gui/basicchannelsettingswidget.h"
#include "dsp/dspengine.h"
#include "mainwindow.h"
#include "atvdemod.h"
const QString ATVDemodGUI::m_strChannelID = "sdrangel.channel.demodatv";
ATVDemodGUI* ATVDemodGUI::create(PluginAPI* objPluginAPI, DeviceSourceAPI *objDeviceAPI)
{
ATVDemodGUI* gui = new ATVDemodGUI(objPluginAPI, objDeviceAPI);
return gui;
}
void ATVDemodGUI::destroy()
{
delete this;
}
void ATVDemodGUI::setName(const QString& strName)
{
setObjectName(strName);
}
QString ATVDemodGUI::getName() const
{
return objectName();
}
qint64 ATVDemodGUI::getCenterFrequency() const
{
return m_objChannelMarker.getCenterFrequency();
}
void ATVDemodGUI::setCenterFrequency(qint64 intCenterFrequency)
{
m_objChannelMarker.setCenterFrequency(intCenterFrequency);
applySettings();
}
void ATVDemodGUI::resetToDefaults()
{
blockApplySettings(true);
//ui->rfBW->setValue(50);
blockApplySettings(false);
applySettings();
}
QByteArray ATVDemodGUI::serialize() const
{
SimpleSerializer s(1);
//s.writeS32(2, ui->rfBW->value());
return s.final();
}
bool ATVDemodGUI::deserialize(const QByteArray& arrData)
{
SimpleDeserializer d(arrData);
if(!d.isValid())
{
resetToDefaults();
return false;
}
if(d.getVersion() == 1)
{
QByteArray bytetmp;
quint32 u32tmp;
qint32 tmp;
blockApplySettings(true);
m_objChannelMarker.blockSignals(true);
d.readS32(1, &tmp, 0);
m_objChannelMarker.setCenterFrequency(tmp);
//d.readS32(2, &tmp, 4);
//ui->rfBW->setValue(tmp);
if(d.readU32(7, &u32tmp))
{
m_objChannelMarker.setColor(u32tmp);
}
blockApplySettings(false);
m_objChannelMarker.blockSignals(false);
applySettings();
return true;
}
else
{
resetToDefaults();
return false;
}
}
bool ATVDemodGUI::handleMessage(const Message& objMessage)
{
return false;
}
void ATVDemodGUI::viewChanged()
{
applySettings();
}
void ATVDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown)
{
}
void ATVDemodGUI::onMenuDoubleClicked()
{
if(!m_blnBasicSettingsShown)
{
m_blnBasicSettingsShown = true;
BasicChannelSettingsWidget* bcsw = new BasicChannelSettingsWidget(&m_objChannelMarker, this);
bcsw->show();
}
}
ATVDemodGUI::ATVDemodGUI(PluginAPI* objPluginAPI, DeviceSourceAPI *objDeviceAPI, QWidget* objParent) :
RollupWidget(objParent),
ui(new Ui::ATVDemodGUI),
m_objPluginAPI(objPluginAPI),
m_objDeviceAPI(objDeviceAPI),
m_objChannelMarker(this),
m_blnBasicSettingsShown(false),
m_blnDoApplySettings(true)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose, true);
connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
connect(this, SIGNAL(menuDoubleClickEvent()), this, SLOT(onMenuDoubleClicked()));
m_objATVDemod = new ATVDemod();
m_objATVDemod->SetATVScreen(ui->screenTV);
m_objChannelizer = new DownChannelizer(m_objATVDemod);
m_objThreadedChannelizer = new ThreadedBasebandSampleSink(m_objChannelizer, this);
m_objDeviceAPI->addThreadedSink(m_objThreadedChannelizer);
//m_objPluginAPI->addThreadedSink(m_objThreadedChannelizer);
//connect(&m_objPluginAPI->getMainWindow()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); // 50 ms
m_objChannelMarker.setColor(Qt::white);
m_objChannelMarker.setBandwidth(6000000);
m_objChannelMarker.setCenterFrequency(0);
m_objChannelMarker.setVisible(true);
//connect(&m_objchannelMarker, SIGNAL(changed()), this, SLOT(viewChanged()));
m_objDeviceAPI->registerChannelInstance(m_strChannelID, this);
m_objDeviceAPI->addChannelMarker(&m_objChannelMarker);
m_objDeviceAPI->addRollupWidget(this);
ui->screenTV->connectTimer(m_objPluginAPI->getMainWindow()->getMasterTimer());
//********** ATV Default values **********
ui->horizontalSlider->setValue(100);
ui->horizontalSlider_2->setValue(310);
ui->horizontalSlider_3->setValue(640);
ui->horizontalSlider_4->setValue(3);
ui->comboBox_2->setCurrentIndex(0);
ui->comboBox->setCurrentIndex(0);
ui->checkBox->setChecked(true);
ui->checkBox_2->setChecked(true);
applySettings();
}
ATVDemodGUI::~ATVDemodGUI()
{
m_objDeviceAPI->removeChannelInstance(this);
m_objDeviceAPI->removeThreadedSink(m_objThreadedChannelizer);
delete m_objThreadedChannelizer;
delete m_objChannelizer;
delete m_objATVDemod;
delete ui;
}
void ATVDemodGUI::blockApplySettings(bool blnBlock)
{
m_blnDoApplySettings = !blnBlock;
}
void ATVDemodGUI::applySettings()
{
ATVModulation enmSelectedModulation;
if (m_blnDoApplySettings)
{
setTitleColor(m_objChannelMarker.getColor());
m_objChannelizer->configure(m_objChannelizer->getInputMessageQueue(), m_objATVDemod->GetSampleRate(), m_objChannelMarker.getCenterFrequency());
switch(ui->comboBox_2->currentIndex())
{
case 0:
enmSelectedModulation=ATV_FM1;
break;
case 1:
enmSelectedModulation=ATV_FM2;
break;
case 2:
enmSelectedModulation=ATV_AM;
break;
default:
enmSelectedModulation=ATV_FM1;
break;
}
m_objATVDemod->configure(m_objATVDemod->getInputMessageQueue(),
ui->horizontalSlider_3->value(),
ui->horizontalSlider_4->value(),
(ui->comboBox->currentIndex()==0)?25:30,
(ui->checkBox_3->checkState()==Qt::Checked)?50:100,
((float)(ui->horizontalSlider->value()))/1000.0f,
((float)(ui->horizontalSlider_2->value()))/1000.0f,
enmSelectedModulation,
ui->checkBox->isChecked(),
ui->checkBox_2->isChecked());
m_objChannelMarker.setBandwidth(m_objATVDemod->GetSampleRate());
}
}
void ATVDemodGUI::leaveEvent(QEvent*)
{
blockApplySettings(true);
m_objChannelMarker.setHighlighted(false);
blockApplySettings(false);
}
void ATVDemodGUI::enterEvent(QEvent*)
{
blockApplySettings(true);
m_objChannelMarker.setHighlighted(true);
blockApplySettings(false);
}
void ATVDemodGUI::tick()
{
return ;
}
void ATVDemodGUI::on_horizontalSlider_valueChanged(int value)
{
applySettings();
ui->label_2->setText(QString("%1 mV").arg(value));
}
void ATVDemodGUI::on_horizontalSlider_2_valueChanged(int value)
{
applySettings();
ui->label_4->setText(QString("%1 mV").arg(value));
}
void ATVDemodGUI::on_horizontalSlider_3_valueChanged(int value)
{
ui->label_6->setText(QString("%1 uS").arg(((float)value)/10.0f));
applySettings();
}
void ATVDemodGUI::on_horizontalSlider_4_valueChanged(int value)
{
ui->label_8->setText(QString("%1 uS").arg(value));
applySettings();
}
void ATVDemodGUI::on_checkBox_clicked()
{
applySettings();
}
void ATVDemodGUI::on_checkBox_2_clicked()
{
applySettings();
}
void ATVDemodGUI::on_checkBox_3_clicked()
{
applySettings();
}
void ATVDemodGUI::on_comboBox_currentIndexChanged(int index)
{
applySettings();
}
void ATVDemodGUI::on_comboBox_2_currentIndexChanged(int index)
{
applySettings();
}

View File

@ -0,0 +1,96 @@
///////////////////////////////////////////////////////////////////////////////////
// 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_ATVDEMODGUI_H
#define INCLUDE_ATVDEMODGUI_H
#include "gui/rollupwidget.h"
#include "plugin/plugingui.h"
#include "dsp/channelmarker.h"
#include "dsp/movingaverage.h"
class PluginAPI;
class DeviceSourceAPI;
class ThreadedBasebandSampleSink;
class DownChannelizer;
class ATVDemod;
namespace Ui
{
class ATVDemodGUI;
}
class ATVDemodGUI : public RollupWidget, public PluginGUI
{
Q_OBJECT
public:
static ATVDemodGUI* create(PluginAPI* objPluginAPI, DeviceSourceAPI *objDeviceAPI);
void destroy();
void setName(const QString& strName);
QString getName() const;
virtual qint64 getCenterFrequency() const;
virtual void setCenterFrequency(qint64 intCenterFrequency);
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& arrData);
virtual bool handleMessage(const Message& objMessage);
static const QString m_strChannelID;
private slots:
void viewChanged();
void onWidgetRolled(QWidget* widget, bool rollDown);
void onMenuDoubleClicked();
void tick();
void on_horizontalSlider_valueChanged(int value);
void on_horizontalSlider_2_valueChanged(int value);
void on_horizontalSlider_3_valueChanged(int value);
void on_horizontalSlider_4_valueChanged(int value);
void on_checkBox_clicked();
void on_checkBox_2_clicked();
void on_checkBox_3_clicked();
void on_comboBox_currentIndexChanged(int index);
void on_comboBox_2_currentIndexChanged(int index);
private:
Ui::ATVDemodGUI* ui;
PluginAPI* m_objPluginAPI;
DeviceSourceAPI* m_objDeviceAPI;
ChannelMarker m_objChannelMarker;
ThreadedBasebandSampleSink* m_objThreadedChannelizer;
DownChannelizer* m_objChannelizer;
ATVDemod* m_objATVDemod;
bool m_blnBasicSettingsShown;
bool m_blnDoApplySettings;
explicit ATVDemodGUI(PluginAPI* objPluginAPI, DeviceSourceAPI *objDeviceAPI, QWidget* objParent = NULL);
virtual ~ATVDemodGUI();
void blockApplySettings(bool blnBlock);
void applySettings();
void leaveEvent(QEvent*);
void enterEvent(QEvent*);
};
#endif // INCLUDE_ATVDEMODGUI_H

View File

@ -0,0 +1,419 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ATVDemodGUI</class>
<widget class="RollupWidget" name="ATVDemodGUI">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>500</width>
<height>500</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>500</width>
<height>450</height>
</size>
</property>
<property name="font">
<font>
<family>Sans Serif</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="windowTitle">
<string>ATV Demodulator</string>
</property>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>481</width>
<height>361</height>
</rect>
</property>
<layout class="QHBoxLayout" name="signalLevelLayout">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<item>
<widget class="ATVScreen" name="screenTV" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>460</width>
<height>345</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>460</width>
<height>345</height>
</size>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QGroupBox" name="groupBox">
<property name="geometry">
<rect>
<x>10</x>
<y>380</y>
<width>481</width>
<height>105</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>461</width>
<height>105</height>
</size>
</property>
<property name="title">
<string>ATV Settings</string>
</property>
<widget class="QSlider" name="horizontalSlider_2">
<property name="geometry">
<rect>
<x>310</x>
<y>60</y>
<width>101</width>
<height>20</height>
</rect>
</property>
<property name="maximum">
<number>999</number>
</property>
<property name="singleStep">
<number>1</number>
</property>
<property name="pageStep">
<number>100</number>
</property>
<property name="value">
<number>0</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
<widget class="QComboBox" name="comboBox">
<property name="geometry">
<rect>
<x>140</x>
<y>30</y>
<width>72</width>
<height>22</height>
</rect>
</property>
<item>
<property name="text">
<string comment="25">25 Fps</string>
</property>
</item>
<item>
<property name="text">
<string comment="30">30 Fps</string>
</property>
</item>
</widget>
<widget class="QSlider" name="horizontalSlider">
<property name="geometry">
<rect>
<x>50</x>
<y>60</y>
<width>101</width>
<height>16</height>
</rect>
</property>
<property name="maximum">
<number>999</number>
</property>
<property name="singleStep">
<number>1</number>
</property>
<property name="pageStep">
<number>100</number>
</property>
<property name="value">
<number>0</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
<widget class="QCheckBox" name="checkBox">
<property name="geometry">
<rect>
<x>250</x>
<y>30</y>
<width>61</width>
<height>20</height>
</rect>
</property>
<property name="text">
<string>HSync</string>
</property>
</widget>
<widget class="QCheckBox" name="checkBox_2">
<property name="geometry">
<rect>
<x>340</x>
<y>30</y>
<width>61</width>
<height>20</height>
</rect>
</property>
<property name="text">
<string>VSync</string>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>10</x>
<y>60</y>
<width>41</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Synch&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>160</x>
<y>60</y>
<width>51</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;right&quot;&gt;mV&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="label_3">
<property name="geometry">
<rect>
<x>250</x>
<y>60</y>
<width>61</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Black Lvl&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
<widget class="QLabel" name="label_4">
<property name="geometry">
<rect>
<x>420</x>
<y>60</y>
<width>51</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;right&quot;&gt;mV&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QCheckBox" name="checkBox_3">
<property name="geometry">
<rect>
<x>420</x>
<y>30</y>
<width>51</width>
<height>20</height>
</rect>
</property>
<property name="text">
<string>Half</string>
</property>
</widget>
<widget class="QLabel" name="label_5">
<property name="geometry">
<rect>
<x>10</x>
<y>80</y>
<width>31</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Line</string>
</property>
</widget>
<widget class="QSlider" name="horizontalSlider_3">
<property name="geometry">
<rect>
<x>50</x>
<y>80</y>
<width>101</width>
<height>16</height>
</rect>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>1000</number>
</property>
<property name="singleStep">
<number>1</number>
</property>
<property name="pageStep">
<number>100</number>
</property>
<property name="value">
<number>1</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
<widget class="QLabel" name="label_6">
<property name="geometry">
<rect>
<x>160</x>
<y>80</y>
<width>51</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;right&quot;&gt;uS&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="label_7">
<property name="geometry">
<rect>
<x>250</x>
<y>80</y>
<width>31</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Top&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
<widget class="QSlider" name="horizontalSlider_4">
<property name="geometry">
<rect>
<x>310</x>
<y>80</y>
<width>101</width>
<height>20</height>
</rect>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>30</number>
</property>
<property name="singleStep">
<number>1</number>
</property>
<property name="pageStep">
<number>100</number>
</property>
<property name="value">
<number>1</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
<widget class="QLabel" name="label_8">
<property name="geometry">
<rect>
<x>420</x>
<y>80</y>
<width>51</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;right&quot;&gt;uS&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QComboBox" name="comboBox_2">
<property name="geometry">
<rect>
<x>10</x>
<y>30</y>
<width>72</width>
<height>22</height>
</rect>
</property>
<item>
<property name="text">
<string>FM 1</string>
</property>
</item>
<item>
<property name="text">
<string>FM 2</string>
</property>
</item>
<item>
<property name="text">
<string>AM</string>
</property>
</item>
</widget>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>RollupWidget</class>
<extends>QWidget</extends>
<header>gui/rollupwidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ATVScreen</class>
<extends>QWidget</extends>
<header>atvscreen.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,72 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 <QtPlugin>
#include <QAction>
#include "plugin/pluginapi.h"
#include "atvdemodgui.h"
#include "atvdemodplugin.h"
const PluginDescriptor ATVDemodPlugin::m_ptrPluginDescriptor =
{
QString("ATV Demodulator"),
QString("3.2.0"),
QString("(c) F4HKW for F4EXB / SDRAngel"),
QString("https://github.com/f4exb/sdrangel"),
true,
QString("https://github.com/f4exb/sdrangel")
};
ATVDemodPlugin::ATVDemodPlugin(QObject* ptrParent) :
QObject(ptrParent),
m_ptrPluginAPI(NULL)
{
}
const PluginDescriptor& ATVDemodPlugin::getPluginDescriptor() const
{
return m_ptrPluginDescriptor;
}
void ATVDemodPlugin::initPlugin(PluginAPI* ptrPluginAPI)
{
m_ptrPluginAPI = ptrPluginAPI;
// register ATV demodulator
m_ptrPluginAPI->registerRxChannel(ATVDemodGUI::m_strChannelID, this);
}
PluginGUI* ATVDemodPlugin::createRxChannel(const QString& strChannelName, DeviceSourceAPI *ptrDeviceAPI)
{
if(strChannelName == ATVDemodGUI::m_strChannelID)
{
ATVDemodGUI* ptrGui = ATVDemodGUI::create(m_ptrPluginAPI, ptrDeviceAPI);
return ptrGui;
}
else
{
return NULL;
}
}
void ATVDemodPlugin::createInstanceDemodATV(DeviceSourceAPI *ptrDeviceAPI)
{
ATVDemodGUI::create(m_ptrPluginAPI, ptrDeviceAPI);
}

View File

@ -0,0 +1,49 @@
///////////////////////////////////////////////////////////////////////////////////
// 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_ATVPLUGIN_H
#define INCLUDE_ATVPLUGIN_H
#include <QObject>
#include "plugin/plugininterface.h"
class DeviceSourceAPI;
class ATVDemodPlugin : public QObject, PluginInterface
{
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID "sdrangel.channel.demodatv")
public:
explicit ATVDemodPlugin(QObject* ptrParent = NULL);
const PluginDescriptor& getPluginDescriptor() const;
void initPlugin(PluginAPI* ptrPluginAPI);
PluginGUI* createRxChannel(const QString& strChannelName, DeviceSourceAPI *ptrDeviceAPI);
private:
static const PluginDescriptor m_ptrPluginDescriptor;
PluginAPI* m_ptrPluginAPI;
private slots:
void createInstanceDemodATV(DeviceSourceAPI *ptrDeviceAPI);
};
#endif // INCLUDE_ATVPLUGIN_H

View File

@ -0,0 +1,205 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2017 F4HKW //
// for F4EXB / SDRAngel //
// //
// OpenGL interface modernization. //
// //
// 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 <QPainter>
#include <QMouseEvent>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QSurface>
#include "atvscreen.h"
#include <algorithm>
#include <QDebug>
ATVScreen::ATVScreen(QWidget* parent) :
QGLWidget(parent),
m_objMutex(QMutex::NonRecursive)
{
setAttribute(Qt::WA_OpaquePaintEvent);
//connect(&m_objTimer, SIGNAL(timeout()), this, SLOT(tick()));
//m_objTimer.start(50);
m_chrLastData=NULL;
m_blnDataChanged=false;
m_blnGLContextInitialized=false;
m_intAskedCols=0;
m_intAskedRows=0;
}
ATVScreen::~ATVScreen()
{
cleanup();
}
QRgb* ATVScreen::getRowBuffer(int intRow)
{
if(m_blnGLContextInitialized==false)
{
return NULL;
}
return m_objGLShaderArray.GetRowBuffer(intRow);
}
void ATVScreen::renderImage(unsigned char * objData)
{
m_chrLastData=objData;
update();
}
void ATVScreen::resetImage()
{
m_objGLShaderArray.ResetPixels();
}
void ATVScreen::resizeATVScreen(int intCols, int intRows)
{
m_intAskedCols=intCols;
m_intAskedRows=intRows;
}
void ATVScreen::initializeGL()
{
m_objMutex.lock();
QOpenGLContext *objGlCurrentContext = QOpenGLContext::currentContext();
if (objGlCurrentContext)
{
if (QOpenGLContext::currentContext()->isValid())
{
qDebug() << "ATVScreen::initializeGL: context:"
<< " major: " << (QOpenGLContext::currentContext()->format()).majorVersion()
<< " minor: " << (QOpenGLContext::currentContext()->format()).minorVersion()
<< " ES: " << (QOpenGLContext::currentContext()->isOpenGLES() ? "yes" : "no");
}
else
{
qDebug() << "ATVScreen::initializeGL: current context is invalid";
}
}
else
{
qCritical() << "ATVScreen::initializeGL: no current context";
return;
}
QSurface *objSurface = objGlCurrentContext->surface();
if (objSurface == NULL)
{
qCritical() << "ATVScreen::initializeGL: no surface attached";
return;
}
else
{
if (objSurface->surfaceType() != QSurface::OpenGLSurface)
{
qCritical() << "ATVScreen::initializeGL: surface is not an OpenGLSurface: " << objSurface->surfaceType() << " cannot use an OpenGL context";
return;
}
else
{
qDebug() << "ATVScreen::initializeGL: OpenGL surface:" << " class: " << (objSurface->surfaceClass() == QSurface::Window ? "Window" : "Offscreen");
}
}
connect(objGlCurrentContext, &QOpenGLContext::aboutToBeDestroyed, this, &ATVScreen::cleanup); // TODO: when migrating to QOpenGLWidget
//Par défaut
m_intAskedCols = ATV_COLS;
m_intAskedRows = ATV_ROWS;
m_blnGLContextInitialized=true;
m_objMutex.unlock();
}
void ATVScreen::resizeGL(int intWidth, int intHeight)
{
QOpenGLFunctions *ptrF = QOpenGLContext::currentContext()->functions();
ptrF->glViewport(0, 0, intWidth, intHeight);
m_blnConfigChanged = true;
}
void ATVScreen::paintGL()
{
m_objMutex.lock();
if(m_blnGLContextInitialized)
{
if((m_intAskedCols!=0) && (m_intAskedRows!=0))
{
m_objGLShaderArray.InitializeGL(m_intAskedCols, m_intAskedRows);
m_intAskedCols=0;
m_intAskedRows=0;
}
m_objGLShaderArray.RenderPixels(m_chrLastData);
}
m_objMutex.unlock();
}
void ATVScreen::mousePressEvent(QMouseEvent* event)
{
}
void ATVScreen::tick()
{
}
void ATVScreen::connectTimer(const QTimer& objTimer)
{
/*
qDebug() << "ATVScreen::connectTimer";
disconnect(&m_objTimer, SIGNAL(timeout()), this, SLOT(tick()));
connect(&objTimer, SIGNAL(timeout()), this, SLOT(tick()));
m_objTimer.stop();
*/
}
void ATVScreen::cleanup()
{
if(m_blnGLContextInitialized)
{
m_objGLShaderArray.Cleanup();
}
}
bool ATVScreen::selectRow(int intLine)
{
if(m_blnGLContextInitialized)
{
return m_objGLShaderArray.SelectRow(intLine);
}
}
bool ATVScreen::setDataColor(int intCol,int intRed, int intGreen, int intBlue)
{
if(m_blnGLContextInitialized)
{
return m_objGLShaderArray.SetDataColor(intCol, qRgb(intRed, intGreen, intBlue));
}
}

View File

@ -0,0 +1,98 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2017 F4HKW //
// for F4EXB / SDRAngel //
// //
// OpenGL interface modernization. //
// See: http://doc.qt.io/qt-5/qopenglshaderprogram.html //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_ATVSCREEN_H
#define INCLUDE_ATVSCREEN_H
#include <QGLWidget>
#include <QPen>
#include <QTimer>
#include <QMutex>
#include <QFont>
#include <QMatrix4x4>
#include "dsp/dsptypes.h"
#include "dsp/scopevis.h"
#include "gui/scaleengine.h"
#include "glshaderarray.h"
#include "gui/glshadertextured.h"
#include "util/export.h"
#include "util/bitfieldindex.h"
class ScopeVis;
class QPainter;
class SDRANGEL_API ATVScreen: public QGLWidget
{
Q_OBJECT
public:
ATVScreen(QWidget* parent = NULL);
~ATVScreen();
void resizeATVScreen(int intCols, int intRows);
void renderImage(unsigned char * objData);
QRgb* getRowBuffer(int intRow);
void resetImage();
bool selectRow(int intLine);
bool setDataColor(int intCol,int intRed, int intGreen, int intBlue);
void connectTimer(const QTimer& timer);
//Valeurs par défaut
static const int ATV_COLS=192;
static const int ATV_ROWS=625;
signals:
void traceSizeChanged(int);
void sampleRateChanged(int);
private:
bool m_blnGLContextInitialized;
int m_intAskedCols;
int m_intAskedRows;
// state
QTimer m_objTimer;
QMutex m_objMutex;
bool m_blnDataChanged;
bool m_blnConfigChanged;
GLShaderArray m_objGLShaderArray;
void initializeGL();
void resizeGL(int width, int height);
void paintGL();
void mousePressEvent(QMouseEvent*);
unsigned char *m_chrLastData;
protected slots:
void cleanup();
void tick();
};
#endif // INCLUDE_ATVSCREEN_H

View File

@ -0,0 +1,41 @@
#--------------------------------------------------------
#
# Pro file for Android and Windows builds with Qt Creator
#
#--------------------------------------------------------
TEMPLATE = lib
CONFIG += plugin
QT += core gui widgets multimedia
TARGET = demodam
DEFINES += USE_SSE2=1
QMAKE_CXXFLAGS += -msse2
DEFINES += USE_SSE4_1=1
QMAKE_CXXFLAGS += -msse4.1
INCLUDEPATH += $$PWD
INCLUDEPATH += ../../../sdrbase
CONFIG(Release):build_subdir = release
CONFIG(Debug):build_subdir = debug
SOURCES += atvdemod.cpp\
atvdemodgui.cpp\
atvdemodplugin.cpp\
atvscreen.cpp\
glshaderarray.cpp
HEADERS += atvdemod.h\
atvdemodgui.h\
atvdemodplugin.h\
atvscreen.h\
glshaderarray.h
FORMS += atvdemodgui.ui
LIBS += -L../../../sdrbase/$${build_subdir} -lsdrbase
RESOURCES = ../../../sdrbase/resources/res.qrc

View File

@ -0,0 +1,303 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 "glshaderarray.h"
const QString GLShaderArray::m_strVertexShaderSourceArray = QString(
"uniform highp mat4 uMatrix;\n"
"attribute highp vec4 vertex;\n"
"attribute highp vec2 texCoord;\n"
"varying mediump vec2 texCoordVar;\n"
"void main() {\n"
" gl_Position = uMatrix * vertex;\n"
" texCoordVar = texCoord;\n"
"}\n"
);
const QString GLShaderArray::m_strFragmentShaderSourceColored = QString(
"uniform lowp sampler2D uTexture;\n"
"varying mediump vec2 texCoordVar;\n"
"void main() {\n"
" gl_FragColor = texture2D(uTexture, texCoordVar);\n"
"}\n"
);
GLShaderArray::GLShaderArray()
{
m_objProgram=NULL;
m_objImage = NULL;
m_objTexture = NULL;
m_intCols=0;
m_intRows=0;
m_blnInitialized=false;
m_objCurrentRow=NULL;
}
GLShaderArray::~GLShaderArray()
{
Cleanup();
}
void GLShaderArray::InitializeGL(int intCols, int intRows)
{
QMatrix4x4 objQMatrix;
m_blnInitialized=false;
m_intCols=0;
m_intRows=0;
m_objCurrentRow=NULL;
if(m_objProgram==NULL)
{
m_objProgram = new QOpenGLShaderProgram();
if (!m_objProgram->addShaderFromSourceCode(QOpenGLShader::Vertex, m_strVertexShaderSourceArray))
{
qDebug() << "GLShaderArray::initializeGL: error in vertex shader: " << m_objProgram->log();
}
if (!m_objProgram->addShaderFromSourceCode(QOpenGLShader::Fragment, m_strFragmentShaderSourceColored))
{
qDebug() << "GLShaderArray::initializeGL: error in fragment shader: " << m_objProgram->log();
}
m_objProgram->bindAttributeLocation("vertex", 0);
if (!m_objProgram->link())
{
qDebug() << "GLShaderArray::initializeGL: error linking shader: " << m_objProgram->log();
}
m_objProgram->bind();
m_objProgram->setUniformValue(m_objMatrixLoc, objQMatrix);
m_objProgram->setUniformValue(m_objTextureLoc, 0);
m_objProgram->release();
}
m_objMatrixLoc = m_objProgram->uniformLocation("uMatrix");
m_objTextureLoc = m_objProgram->uniformLocation("uTexture");
m_objColorLoc = m_objProgram->uniformLocation("uColour");
if(m_objTexture!=NULL)
{
delete m_objTexture;
m_objTexture=NULL;
}
//Image container
m_objImage = new QImage(intCols,intRows,QImage::Format_RGBA8888);
m_objImage->fill(QColor(0,0,0));
m_objTexture = new QOpenGLTexture(*m_objImage);
m_objTexture->setMinificationFilter(QOpenGLTexture::Linear);
m_objTexture->setMagnificationFilter(QOpenGLTexture::Linear);
m_objTexture->setWrapMode(QOpenGLTexture::ClampToEdge);
m_intCols = intCols;
m_intRows = intRows;
m_blnInitialized=true;
}
QRgb * GLShaderArray::GetRowBuffer(int intRow)
{
if(m_blnInitialized==false)
{
return NULL;
}
if(m_objImage==NULL)
{
return NULL;
}
if(intRow>m_intRows)
{
return NULL;
}
return (QRgb *)m_objImage->scanLine(intRow);
}
void GLShaderArray::RenderPixels(unsigned char *chrData)
{
QOpenGLFunctions *ptrF;
int intI;
int intJ;
int intNbVertices=4;
QMatrix4x4 objQMatrix;
GLfloat arrVertices[] =
{
-1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, 1.0f,
1.0f, -1.0f
};
GLfloat arrTextureCoords[] =
{
0.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f,
1.0f, 1.0f
};
QRgb *ptrLine;
int intVal;
if(m_blnInitialized==false)
{
return;
}
if(m_objImage==NULL)
{
return ;
}
if(chrData!=NULL)
{
for(intJ=0; intJ<m_intRows; intJ++)
{
ptrLine = (QRgb *)m_objImage->scanLine(intJ);
for(intI=0; intI<m_intCols; intI ++)
{
intVal = (int)(*chrData);
*ptrLine = qRgb(intVal,intVal,intVal);
ptrLine ++;
chrData ++;
}
}
}
//Affichage
ptrF = QOpenGLContext::currentContext()->functions();
m_objProgram->bind();
m_objProgram->setUniformValue(m_objMatrixLoc, objQMatrix);
m_objProgram->setUniformValue(m_objTextureLoc, 0);
m_objTexture->bind();
ptrF->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_intCols, m_intRows, GL_RGBA, GL_UNSIGNED_BYTE, m_objImage->bits());
ptrF->glEnableVertexAttribArray(0); // vertex
ptrF->glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, arrVertices);
ptrF->glEnableVertexAttribArray(1); // texture coordinates
ptrF->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, arrTextureCoords);
ptrF->glDrawArrays(GL_POLYGON, 0, intNbVertices);
//cleanup
ptrF->glDisableVertexAttribArray(0);
ptrF->glDisableVertexAttribArray(1);
//*********************//
m_objTexture->release();
m_objProgram->release();
}
void GLShaderArray::ResetPixels()
{
if(m_objImage!=NULL)
{
m_objImage->fill(0);
}
}
void GLShaderArray::Cleanup()
{
m_blnInitialized=false;
m_intCols=0;
m_intRows=0;
m_objCurrentRow=NULL;
if (m_objProgram)
{
delete m_objProgram;
m_objProgram = NULL;
}
if(m_objTexture!=NULL)
{
delete m_objTexture;
m_objTexture=NULL;
}
if (m_objImage!=NULL)
{
delete m_objImage;
m_objImage = NULL;
}
}
bool GLShaderArray::SelectRow(int intLine)
{
bool blnRslt=false;
if(m_blnInitialized)
{
if((intLine<m_intRows)
&&(intLine>=0))
{
m_objCurrentRow=(QRgb *)m_objImage->scanLine(intLine);
blnRslt=true;
}
else
{
m_objCurrentRow=NULL;
}
}
return blnRslt;
}
bool GLShaderArray::SetDataColor(int intCol,QRgb objColor)
{
bool blnRslt=false;
if(m_blnInitialized)
{
if((intCol<m_intCols)
&&(intCol>=0)
&&(m_objCurrentRow!=NULL))
{
m_objCurrentRow[intCol]=objColor;
blnRslt=true;
}
}
return blnRslt;
}

View File

@ -0,0 +1,76 @@
///////////////////////////////////////////////////////////////////////////////////
// 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_GUI_GLSHADERARRAY_H_
#define INCLUDE_GUI_GLSHADERARRAY_H_
#include <QString>
#include <QOpenGLFunctions>
#include <QOpenGLFunctions_2_0>
#include <QOpenGLFunctions_2_1>
#include <QOpenGLFunctions_3_0>
#include <QOpenGLTexture>
#include <QOpenGLShaderProgram>
#include <QOpenGLContext>
#include <QMatrix4x4>
#include <QVector4D>
#include <QDebug>
#include <QColor>
#include <math.h>
class QOpenGLShaderProgram;
class QMatrix4x4;
class QVector4D;
class GLShaderArray
{
public:
GLShaderArray();
~GLShaderArray();
void InitializeGL(int intCols, int intRows);
void ResizeContainer(int intCols, int intRows);
void Cleanup();
QRgb *GetRowBuffer(int intRow);
void RenderPixels(unsigned char *chrData);
void ResetPixels();
bool SelectRow(int intLine);
bool SetDataColor(int intCol,QRgb objColor);
protected:
QOpenGLShaderProgram *m_objProgram;
int m_objMatrixLoc;
int m_objTextureLoc;
int m_objColorLoc;
static const QString m_strVertexShaderSourceArray;
static const QString m_strFragmentShaderSourceColored;
QImage *m_objImage=NULL;
QOpenGLTexture *m_objTexture=NULL;
int m_intCols;
int m_intRows;
QRgb * m_objCurrentRow;
bool m_blnInitialized;
};
#endif /* INCLUDE_GUI_GLSHADERARRAY_H_ */

View File

@ -0,0 +1,43 @@
<h1>ATV demodulator plugin</h1>
<h2>Introduction</h2>
This plugin can be used to view ATV.
<h2>Interface</h2>
![ATV Demodulator plugin GUI](../../../doc/img/ATVemod_plugin.png)
<h3>1: Frequency shift from center frequency of reception direction</h3>
The "+/-" button on the left side of the dial toggles between positive and negative shift.
<h3>2: Frequency shift from center frequency of reception value</h3>
Use the wheels to adjust the frequency shift in Hz from the center frequency of reception. Left click on a digit sets the cursor position at this digit. Right click on a digit sets all digits on the right to zero. This effectively floors value at the digit position.
<h3>3: Channel power</h3>
Average total power in dB relative to a +/- 1.0 amplitude signal received in the pass band.
<h3>4: Audio mute</h3>
Use this button to toggle audio mute for this channel. The button will light up in green if the squelch is open. This helps identifying which channels are active in a multi-channel configuration.
<h3>5: Level meter in dB</h3>
- top bar (green): average value
- bottom bar (blue green): instantaneous peak value
- tip vertical bar (bright green): peak hold value
<h3>6: RF bandwidth</h3>
This is the bandwidth in kHz of the channel signal before demodulation. It can be set continuously in 1 kHz steps from 1 to 40 kHz.
<h3>7: Volume</h3>
This is the volume of the audio signal from 0.0 (mute) to 10.0 (maximum). It can be varied continuously in 0.1 steps using the dial button.
<h3>8: Squelch threshold</h3>
This is the squelch threshold in dB. The average total power received in the signal bandwidth before demodulation is compared to this value and the squelch input is open above this value. It can be varied continuously in 0.1 dB steps from 0.0 to -100.0 dB using the dial button.