ATV demod: effective support of random channel sample rate

This commit is contained in:
f4exb 2020-08-18 16:23:40 +02:00
parent 51c2eaee82
commit e76e2321c1
9 changed files with 123 additions and 358 deletions

View File

@ -129,7 +129,6 @@ void ATVDemod::applySettings(const ATVDemodSettings& settings, bool force)
{
qDebug() << "ATVDemod::applySettings:"
<< "m_inputFrequencyOffset:" << settings.m_inputFrequencyOffset
<< "m_forceDecimator:" << settings.m_forceDecimator
<< "m_bfoFrequency:" << settings.m_bfoFrequency
<< "m_atvModulation:" << settings.m_atvModulation
<< "m_fmDeviation:" << settings.m_fmDeviation
@ -145,8 +144,6 @@ void ATVDemod::applySettings(const ATVDemodSettings& settings, bool force)
<< "m_halfFrames:" << settings.m_halfFrames
<< "m_levelSynchroTop:" << settings.m_levelSynchroTop
<< "m_levelBlack:" << settings.m_levelBlack
<< "m_lineTimeFactor:" << settings.m_lineTimeFactor
<< "m_topTimeFactor:" << settings.m_topTimeFactor
<< "m_rgbColor:" << settings.m_rgbColor
<< "m_title:" << settings.m_title
<< "m_udpAddress:" << settings.m_udpAddress

View File

@ -116,8 +116,6 @@ void ATVDemodGUI::displaySettings()
ui->synchLevelText->setText(QString("%1 mV").arg((int) (m_settings.m_levelSynchroTop * 1000.0f)));
ui->blackLevel->setValue((int) (m_settings.m_levelBlack * 1000.0f));
ui->blackLevelText->setText(QString("%1 mV").arg((int) (m_settings.m_levelBlack * 1000.0f)));
ui->lineTime->setValue(m_settings.m_lineTimeFactor);
ui->topTime->setValue(m_settings.m_topTimeFactor);
ui->modulation->setCurrentIndex((int) m_settings.m_atvModulation);
ui->fps->setCurrentIndex(ATVDemodSettings::getFpsIndex(m_settings.m_fps));
ui->nbLines->setCurrentIndex(ATVDemodSettings::getNumberOfLinesIndex(m_settings.m_nbLines));
@ -131,7 +129,6 @@ void ATVDemodGUI::displaySettings()
//********** RF values **********
ui->deltaFrequency->setValue(m_settings.m_inputFrequencyOffset);
ui->decimatorEnable->setChecked(m_settings.m_forceDecimator);
ui->rfFiltering->setChecked(m_settings.m_fftFiltering);
ui->bfo->setValue(m_settings.m_bfoFrequency);
ui->bfoText->setText(QString("%1").arg(m_settings.m_bfoFrequency * 1.0, 0, 'f', 0));
@ -172,11 +169,11 @@ void ATVDemodGUI::applyTVSampleRate()
{
qDebug("TVDemodGUI::applyTVSampleRate");
unsigned int nbPointsPerLine;
ATVDemodSettings::getBaseValues(m_basebandSampleRate, m_settings.m_fps*m_settings.m_nbLines, m_tvSampleRate, nbPointsPerLine);
ui->tvSampleRateText->setText(tr("%1k").arg(m_tvSampleRate/1000.0f, 0, 'f', 2));
ATVDemodSettings::getBaseValues(m_basebandSampleRate, m_settings.m_fps*m_settings.m_nbLines, nbPointsPerLine);
ui->tvSampleRateText->setText(tr("%1k").arg(m_basebandSampleRate/1000.0f, 0, 'f', 2));
ui->nbPointsPerLineText->setText(tr("%1p").arg(nbPointsPerLine));
m_scopeVis->setLiveRate(m_tvSampleRate);
setRFFiltersSlidersRange(m_tvSampleRate);
m_scopeVis->setLiveRate(m_basebandSampleRate);
setRFFiltersSlidersRange(m_basebandSampleRate);
displayRFBandwidths();
lineTimeUpdate();
topTimeUpdate();
@ -238,8 +235,7 @@ ATVDemodGUI::ATVDemodGUI(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, Base
m_channelMarker(this),
m_doApplySettings(false),
m_intTickCount(0),
m_basebandSampleRate(48000),
m_tvSampleRate(48000)
m_basebandSampleRate(48000)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose, true);
@ -341,12 +337,7 @@ void ATVDemodGUI::setChannelMarkerBandwidth()
}
else
{
if ((m_basebandSampleRate == m_tvSampleRate) && (!m_settings.m_forceDecimator)) {
m_channelMarker.setBandwidth(m_basebandSampleRate);
} else {
m_channelMarker.setBandwidth(ui->rfBW->value()*m_rfSliderDivisor);
}
m_channelMarker.setBandwidth(m_basebandSampleRate);
m_channelMarker.setSidebands(ChannelMarker::dsb);
}
@ -435,22 +426,6 @@ void ATVDemodGUI::on_blackLevel_valueChanged(int value)
applySettings();
}
void ATVDemodGUI::on_lineTime_valueChanged(int value)
{
ui->lineTime->setToolTip(QString("Line length adjustment (%1)").arg(value));
m_settings.m_lineTimeFactor = value;
lineTimeUpdate();
applySettings();
}
void ATVDemodGUI::on_topTime_valueChanged(int value)
{
ui->topTime->setToolTip(QString("Horizontal sync pulse length adjustment (%1 %)").arg(value));
m_settings.m_topTimeFactor = value;
topTimeUpdate();
applySettings();
}
void ATVDemodGUI::on_hSync_clicked()
{
m_settings.m_hSync = ui->hSync->isChecked();
@ -504,7 +479,7 @@ void ATVDemodGUI::on_reset_clicked(bool checked)
void ATVDemodGUI::on_modulation_currentIndexChanged(int index)
{
m_settings.m_atvModulation = (ATVDemodSettings::ATVModulation) index;
setRFFiltersSlidersRange(m_tvSampleRate);
setRFFiltersSlidersRange(m_basebandSampleRate);
setChannelMarkerBandwidth();
applySettings();
}
@ -528,14 +503,7 @@ void ATVDemodGUI::on_rfOppBW_valueChanged(int value)
void ATVDemodGUI::on_rfFiltering_toggled(bool checked)
{
m_settings.m_fftFiltering = checked;
setRFFiltersSlidersRange(m_tvSampleRate);
setChannelMarkerBandwidth();
applySettings();
}
void ATVDemodGUI::on_decimatorEnable_toggled(bool checked)
{
m_settings.m_forceDecimator = checked;
setRFFiltersSlidersRange(m_basebandSampleRate);
setChannelMarkerBandwidth();
applySettings();
}
@ -585,41 +553,31 @@ void ATVDemodGUI::on_screenTabWidget_currentChanged(int index)
void ATVDemodGUI::lineTimeUpdate()
{
float nominalLineTime = ATVDemodSettings::getNominalLineTime(m_settings.m_nbLines, m_settings.m_fps);
int lineTimeScaleFactor = (int) std::log10(nominalLineTime);
if (m_tvSampleRate == 0) {
m_fltLineTimeMultiplier = std::pow(10.0, lineTimeScaleFactor-3);
} else {
m_fltLineTimeMultiplier = 1.0f / m_tvSampleRate;
}
float lineTime = nominalLineTime + m_fltLineTimeMultiplier * ui->lineTime->value();
if (lineTime < 0.0)
if (nominalLineTime < 0.0)
ui->lineTimeText->setText("invalid");
else if(lineTime < 0.000001)
ui->lineTimeText->setText(tr("%1 ns").arg(lineTime * 1000000000.0, 0, 'f', 2));
else if(lineTime < 0.001)
ui->lineTimeText->setText(tr("%1 µs").arg(lineTime * 1000000.0, 0, 'f', 2));
else if(lineTime < 1.0)
ui->lineTimeText->setText(tr("%1 ms").arg(lineTime * 1000.0, 0, 'f', 2));
else if(nominalLineTime < 0.000001)
ui->lineTimeText->setText(tr("%1 ns").arg(nominalLineTime * 1000000000.0, 0, 'f', 2));
else if(nominalLineTime < 0.001)
ui->lineTimeText->setText(tr("%1 µs").arg(nominalLineTime * 1000000.0, 0, 'f', 2));
else if(nominalLineTime < 1.0)
ui->lineTimeText->setText(tr("%1 ms").arg(nominalLineTime * 1000.0, 0, 'f', 2));
else
ui->lineTimeText->setText(tr("%1 s").arg(lineTime * 1.0, 0, 'f', 2));
ui->lineTimeText->setText(tr("%1 s").arg(nominalLineTime * 1.0, 0, 'f', 2));
}
void ATVDemodGUI::topTimeUpdate()
{
float nominalTopTime = ATVDemodSettings::getNominalLineTime(m_settings.m_nbLines, m_settings.m_fps) * (4.7f / 64.0f);
float topTime = nominalTopTime * (ui->topTime->value() / 100.0f);
if (topTime < 0.0)
if (nominalTopTime < 0.0)
ui->topTimeText->setText("invalid");
else if (topTime < 0.000001)
ui->topTimeText->setText(tr("%1 ns").arg(topTime * 1000000000.0, 0, 'f', 2));
else if(topTime < 0.001)
ui->topTimeText->setText(tr("%1 µs").arg(topTime * 1000000.0, 0, 'f', 2));
else if(topTime < 1.0)
ui->topTimeText->setText(tr("%1 ms").arg(topTime * 1000.0, 0, 'f', 2));
else if (nominalTopTime < 0.000001)
ui->topTimeText->setText(tr("%1 ns").arg(nominalTopTime * 1000000000.0, 0, 'f', 2));
else if(nominalTopTime < 0.001)
ui->topTimeText->setText(tr("%1 µs").arg(nominalTopTime * 1000000.0, 0, 'f', 2));
else if(nominalTopTime < 1.0)
ui->topTimeText->setText(tr("%1 ms").arg(nominalTopTime * 1000.0, 0, 'f', 2));
else
ui->topTimeText->setText(tr("%1 s").arg(topTime * 1.0, 0, 'f', 2));
ui->topTimeText->setText(tr("%1 s").arg(nominalTopTime * 1.0, 0, 'f', 2));
}

View File

@ -76,10 +76,8 @@ private:
ScopeVis* m_scopeVis;
float m_fltLineTimeMultiplier;
int m_rfSliderDivisor;
int m_basebandSampleRate;
int m_tvSampleRate;
MessageQueue m_inputMessageQueue;
explicit ATVDemodGUI(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* objParent = 0);
@ -104,8 +102,6 @@ private slots:
void tick();
void on_synchLevel_valueChanged(int value);
void on_blackLevel_valueChanged(int value);
void on_lineTime_valueChanged(int value);
void on_topTime_valueChanged(int value);
void on_hSync_clicked();
void on_vSync_clicked();
void on_invertVideo_clicked();
@ -118,7 +114,6 @@ private slots:
void on_rfBW_valueChanged(int value);
void on_rfOppBW_valueChanged(int value);
void on_rfFiltering_toggled(bool checked);
void on_decimatorEnable_toggled(bool checked);
void on_deltaFrequency_changed(qint64 value);
void on_bfo_valueChanged(int value);
void on_fmDeviation_valueChanged(int value);

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>700</width>
<height>743</height>
<height>699</height>
</rect>
</property>
<property name="sizePolicy">
@ -114,23 +114,6 @@
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="decimatorEnable">
<property name="toolTip">
<string>Toggle decimator on/off</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/arrow_down.png</normaloff>:/arrow_down.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="tvSampleRateText">
<property name="minimumSize">
@ -592,7 +575,7 @@
<x>10</x>
<y>570</y>
<width>681</width>
<height>81</height>
<height>104</height>
</rect>
</property>
<property name="windowTitle">
@ -903,19 +886,6 @@
</property>
</widget>
</item>
<item row="0" column="6">
<widget class="QLabel" name="blackLevelText">
<property name="toolTip">
<string>Black level value</string>
</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>
</item>
<item row="0" column="2">
<widget class="QLabel" name="synchLevelText">
<property name="toolTip">
@ -936,14 +906,27 @@
</property>
</widget>
</item>
<item row="0" column="4">
<item row="0" column="3">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QLabel" name="blackLevelLabel">
<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>
</item>
<item row="0" column="5">
<item row="1" column="1">
<widget class="QSlider" name="blackLevel">
<property name="toolTip">
<string>Black level</string>
@ -965,38 +948,42 @@
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lineTimeLabel">
<item row="1" column="2">
<widget class="QLabel" name="blackLevelText">
<property name="toolTip">
<string>Black level value</string>
</property>
<property name="text">
<string>Line</string>
<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>
</item>
<item row="1" column="1">
<widget class="QSlider" name="lineTime">
<item row="0" column="6">
<widget class="QLabel" name="lineTimeText">
<property name="minimumSize">
<size>
<width>62</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Line length adjustment</string>
<string>Effective line length value</string>
</property>
<property name="minimum">
<number>-100</number>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;right&quot;&gt;&amp;mu;S&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="maximum">
<number>100</number>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="singleStep">
<number>1</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>0</number>
</property>
<property name="sliderPosition">
<number>0</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</widget>
</item>
<item row="0" column="4">
<widget class="QLabel" name="lineTimeLabel">
<property name="text">
<string>Line</string>
</property>
</widget>
</item>
@ -1007,31 +994,6 @@
</property>
</widget>
</item>
<item row="1" column="5">
<widget class="QSlider" name="topTime">
<property name="toolTip">
<string>Horizontal sync pulse length adjustment</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="singleStep">
<number>1</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>25</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="1" column="6">
<widget class="QLabel" name="topTimeText">
<property name="minimumSize">
@ -1051,38 +1013,6 @@
</property>
</widget>
</item>
<item row="0" column="3">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="2">
<widget class="QLabel" name="lineTimeText">
<property name="minimumSize">
<size>
<width>62</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Effective line length value</string>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;right&quot;&gt;&amp;mu;S&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>

View File

@ -31,7 +31,6 @@ ATVDemodSettings::ATVDemodSettings() :
void ATVDemodSettings::resetToDefaults()
{
m_inputFrequencyOffset = 0;
m_forceDecimator = false;
m_bfoFrequency = 0.0f;
m_atvModulation = ATV_FM1;
m_fmDeviation = 0.5f;
@ -49,8 +48,6 @@ void ATVDemodSettings::resetToDefaults()
m_halfFrames = false; // m_fltRatioOfRowsToDisplay = 1.0
m_levelSynchroTop = 0.15f;
m_levelBlack = 0.3f;
m_lineTimeFactor = 0;
m_topTimeFactor = 25;
m_rgbColor = QColor(255, 255, 255).rgb();
m_title = "ATV Demodulator";
m_udpAddress = "127.0.0.1";
@ -66,8 +63,6 @@ QByteArray ATVDemodSettings::serialize() const
s.writeU32(2, m_rgbColor);
s.writeS32(3, roundf(m_levelSynchroTop*1000.0)); // mV
s.writeS32(4, roundf(m_levelBlack*1000.0)); // mV
s.writeS32(5, m_lineTimeFactor);
s.writeS32(6, m_topTimeFactor);
s.writeS32(7, m_atvModulation);
s.writeS32(8, m_fps);
s.writeBool(9, m_hSync);
@ -90,7 +85,6 @@ QByteArray ATVDemodSettings::serialize() const
s.writeS32(22, m_amScalingFactor);
s.writeS32(23, m_amOffsetFactor);
s.writeBool(24, m_fftFiltering);
s.writeBool(25, m_forceDecimator);
return s.final();
}
@ -116,8 +110,6 @@ bool ATVDemodSettings::deserialize(const QByteArray& arrData)
m_levelSynchroTop = tmp / 1000.0f;
d.readS32(4, &tmp, 310);
m_levelBlack = tmp / 1000.0f;
d.readS32(5, &m_lineTimeFactor, 0);
d.readS32(6, &m_topTimeFactor, 25);
d.readS32(7, &tmp, 0);
m_atvModulation = static_cast<ATVModulation>(tmp);
d.readS32(8, &tmp, 25);
@ -141,7 +133,6 @@ bool ATVDemodSettings::deserialize(const QByteArray& arrData)
d.readS32(22, &m_amScalingFactor, 100);
d.readS32(23, &m_amOffsetFactor, 0);
d.readBool(24, &m_fftFiltering, false);
d.readBool(25, &m_forceDecimator, false);
return true;
}
@ -303,33 +294,6 @@ float ATVDemodSettings::getNominalLineTime(int nbLines, int fps)
return 1.0f / ((float) nbLines * (float) fps);
}
/**
* calculates m_fltLineTimeMultiplier
*/
void ATVDemodSettings::lineTimeUpdate(unsigned int sampleRate)
{
float nominalLineTime = getNominalLineTime(m_nbLines, m_fps);
int lineTimeScaleFactor = (int) std::log10(nominalLineTime);
if (sampleRate == 0) {
m_fltLineTimeMultiplier = std::pow(10.0, lineTimeScaleFactor-3);
} else {
m_fltLineTimeMultiplier = 1.0f / sampleRate;
}
}
float ATVDemodSettings::getLineTime(unsigned int sampleRate)
{
lineTimeUpdate(sampleRate);
float nominalLineTime = 1.0f / ((float) m_nbLines * (float) m_fps);
return nominalLineTime + m_fltLineTimeMultiplier * m_lineTimeFactor;
}
float ATVDemodSettings::getTopTime(unsigned int sampleRate)
{
return getNominalLineTime(m_nbLines, m_fps) * (4.7f / 64.0f) * (m_topTimeFactor / 100.0f);
}
int ATVDemodSettings::getRFSliderDivisor(unsigned int sampleRate)
{
int scaleFactor = (int) std::log10(sampleRate/2);
@ -352,7 +316,7 @@ float ATVDemodSettings::getRFBandwidthDivisor(ATVModulation modulation)
}
}
void ATVDemodSettings::getBaseValues(int sampleRate, int linesPerSecond, int& tvSampleRate, uint32_t& nbPointsPerLine)
void ATVDemodSettings::getBaseValues(int sampleRate, int linesPerSecond, uint32_t& nbPointsPerLine)
{
int maxPoints = sampleRate / linesPerSecond;
int i = maxPoints;
@ -365,5 +329,4 @@ void ATVDemodSettings::getBaseValues(int sampleRate, int linesPerSecond, int& tv
}
nbPointsPerLine = i == 0 ? maxPoints : i;
tvSampleRate = nbPointsPerLine * linesPerSecond;
}

View File

@ -47,7 +47,6 @@ struct ATVDemodSettings
// RF settings
qint64 m_inputFrequencyOffset; //!< Offset from baseband center frequency
bool m_forceDecimator; //!< Force use of rational decimator when channel sample rate matches TV sample rate
int m_bfoFrequency; //!< BFO frequency (Hz)
ATVModulation m_atvModulation; //!< RF modulation type
float m_fmDeviation; //!< Expected FM deviation
@ -67,8 +66,6 @@ struct ATVDemodSettings
bool m_halfFrames; //!< Toggle half frames processing
float m_levelSynchroTop; //!< Horizontal synchronization top level (0.0 to 1.0 scale)
float m_levelBlack; //!< Black level (0.0 to 1.0 scale)
int m_lineTimeFactor; //!< added: +/- 100 something
int m_topTimeFactor; //!< percentage of nominal horizontal top (pulse) time
// common channel settings
quint32 m_rgbColor;
@ -84,8 +81,6 @@ struct ATVDemodSettings
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
float getLineTime(unsigned int sampleRate);
float getTopTime(unsigned int sampleRate);
int getRFSliderDivisor(unsigned int sampleRate);
static int getFps(int fpsIndex);
@ -94,12 +89,9 @@ struct ATVDemodSettings
static int getNumberOfLinesIndex(int nbLines);
static float getNominalLineTime(int nbLines, int fps);
static float getRFBandwidthDivisor(ATVModulation modulation);
static void getBaseValues(int sampleRate, int linesPerSecond, int& tvSampleRate, uint32_t& nbPointsPerLine);
static void getBaseValues(int sampleRate, int linesPerSecond, uint32_t& nbPointsPerLine);
private:
void lineTimeUpdate(unsigned int sampleRate);
float m_fltLineTimeMultiplier;
int m_rfSliderDivisor;
};

View File

@ -30,8 +30,8 @@ const int ATVDemodSink::m_ssbFftLen = 1024;
ATVDemodSink::ATVDemodSink() :
m_channelSampleRate(1000000),
m_channelFrequencyOffset(0),
m_tvSampleRate(1000000),
m_samplesPerLine(100),
m_samplesPerLineFrac(0.0f),
m_videoTabIndex(0),
m_scopeSink(nullptr),
m_registeredTVScreen(nullptr),
@ -53,19 +53,16 @@ ATVDemodSink::ATVDemodSink() :
m_ampAverage(4800),
m_bfoPLL(200/1000000, 100/1000000, 0.01),
m_bfoFilter(200.0, 1000000.0, 0.9),
m_interpolatorDistance(1.0f),
m_interpolatorDistanceRemain(0.0f),
m_DSBFilter(nullptr),
m_DSBFilterBuffer(nullptr),
m_DSBFilterBufferIndex(0)
{
qDebug("ATVDemodSink::ATVDemodSink");
//*************** ATV PARAMETERS ***************
//m_intNumberSamplePerLine=0;
m_synchroSamples=0;
m_interleaved = true;
m_DSBFilter = new fftfilt(m_settings.m_fftBandwidth / (float) m_tvSampleRate, 2*m_ssbFftLen); // arbitrary cutoff
m_DSBFilter = new fftfilt(m_settings.m_fftBandwidth / (float) m_channelSampleRate, 2*m_ssbFftLen); // arbitrary cutoff
m_DSBFilterBuffer = new Complex[m_ssbFftLen];
std::fill(m_DSBFilterBuffer, m_DSBFilterBuffer + m_ssbFftLen, Complex{0.0, 0.0});
std::fill(m_fltBufferI, m_fltBufferI+6, 0.0f);
@ -97,19 +94,7 @@ void ATVDemodSink::feed(const SampleVector::const_iterator& begin, const SampleV
c *= m_nco.nextIQ();
}
if ((m_tvSampleRate == m_channelSampleRate) && (!m_settings.m_forceDecimator)) // no decimation
{
demod(c);
}
else
{
Complex ci;
if (m_interpolator.decimate(&m_interpolatorDistanceRemain, c, &ci))
{
demod(ci);
m_interpolatorDistanceRemain += m_interpolatorDistance;
}
}
demod(c);
}
if ((m_videoTabIndex == 1) && (m_scopeSink)) // do only if scope tab is selected and scope is available
@ -327,9 +312,9 @@ void ATVDemodSink::demod(Complex& c)
}
}
void ATVDemodSink::applyStandard(int sampleRate, const ATVDemodSettings& settings, float lineDuration)
void ATVDemodSink::applyStandard(int sampleRate, ATVDemodSettings::ATVStd atvStd, float lineDuration)
{
switch(settings.m_atvStd)
switch(atvStd)
{
case ATVDemodSettings::ATVStdHSkip:
// what is left in a line for the image
@ -385,7 +370,7 @@ void ATVDemodSink::applyStandard(int sampleRate, const ATVDemodSettings& setting
// Table 2. Details of line synchronizing signals
m_numberSamplesPerLineSignals = (int)(lineDuration * sampleRate * 12.0 / 64.0); // "a", Line-blanking interval
m_numberSamplesPerHSync = (int)(lineDuration * sampleRate * 10.5 / 64.0); // "b", Interval between time datum and back edge of line-blanking pulse
m_numberSamplesPerHTopNom = (int)(lineDuration * sampleRate * 4.7 / 64.0); // "d", Duration of synchronizing pulse
m_numberSamplesPerHTop = (int)(lineDuration * sampleRate * 4.7 / 64.0); // "d", Duration of synchronizing pulse
// Table 3. Details of field synchronizing signals
float hl = 32.0f; // half of the line
@ -406,8 +391,6 @@ void ATVDemodSink::applyStandard(int sampleRate, const ATVDemodSettings& setting
float vSyncDetectPercent = 0.5f;
m_vSyncDetectThreshold = (int)(detectTotalLen * vSyncDetectPercent);
m_numberSamplesPerHTop = m_numberSamplesPerHTopNom * (settings.m_topTimeFactor / 100.0f); // adjust the value used in the system
}
bool ATVDemodSink::getBFOLocked()
@ -440,43 +423,31 @@ void ATVDemodSink::applyChannelSettings(int channelSampleRate, int channelFreque
if ((channelSampleRate != m_channelSampleRate) || force)
{
unsigned int samplesPerLineNom;
ATVDemodSettings::getBaseValues(channelSampleRate, m_settings.m_nbLines * m_settings.m_fps, m_tvSampleRate, samplesPerLineNom);
m_samplesPerLine = samplesPerLineNom + m_settings.m_lineTimeFactor;
ATVDemodSettings::getBaseValues(channelSampleRate, m_settings.m_nbLines * m_settings.m_fps, samplesPerLineNom);
m_samplesPerLine = samplesPerLineNom;
m_samplesPerLineFrac = (float)channelSampleRate / (m_settings.m_nbLines * m_settings.m_fps) - m_samplesPerLine;
qDebug() << "ATVDemodSink::applyChannelSettings:"
<< " m_tvSampleRate: " << m_tvSampleRate
<< " m_channelSampleRate: " << m_channelSampleRate
<< " m_fftBandwidth: " << m_settings.m_fftBandwidth
<< " m_fftOppBandwidth:" << m_settings.m_fftOppBandwidth
<< " m_bfoFrequency: " << m_settings.m_bfoFrequency;
if (m_tvSampleRate > 0)
{
m_interpolatorDistanceRemain = 0;
m_interpolatorDistance = (Real) m_tvSampleRate / (Real) channelSampleRate;
m_interpolator.create(24,
m_tvSampleRate,
m_settings.m_fftBandwidth / ATVDemodSettings::getRFBandwidthDivisor(m_settings.m_atvModulation),
3.0
);
}
else
{
m_tvSampleRate = channelSampleRate;
}
m_channelSampleRate = channelSampleRate;
m_DSBFilter->create_asym_filter(
m_settings.m_fftOppBandwidth / (float) m_tvSampleRate,
m_settings.m_fftBandwidth / (float) m_tvSampleRate
m_settings.m_fftOppBandwidth / (float) m_channelSampleRate,
m_settings.m_fftBandwidth / (float) m_channelSampleRate
);
std::fill(m_DSBFilterBuffer, m_DSBFilterBuffer + m_ssbFftLen, Complex{0.0, 0.0});
m_DSBFilterBufferIndex = 0;
m_bfoPLL.configure((float) m_settings.m_bfoFrequency / (float) m_tvSampleRate,
100.0 / m_tvSampleRate,
m_bfoPLL.configure((float) m_settings.m_bfoFrequency / (float) m_channelSampleRate,
100.0 / m_channelSampleRate,
0.01);
m_bfoFilter.setFrequencies(m_tvSampleRate, m_settings.m_bfoFrequency);
m_bfoFilter.setFrequencies(m_channelSampleRate, m_settings.m_bfoFrequency);
}
applyStandard(m_tvSampleRate, m_settings, ATVDemodSettings::getNominalLineTime(m_settings.m_nbLines, m_settings.m_fps));
applyStandard(m_channelSampleRate, m_settings.m_atvStd, ATVDemodSettings::getNominalLineTime(m_settings.m_nbLines, m_settings.m_fps));
if (m_registeredTVScreen)
{
@ -497,7 +468,6 @@ void ATVDemodSink::applySettings(const ATVDemodSettings& settings, bool force)
{
qDebug() << "ATVDemodSink::applySettings:"
<< "m_inputFrequencyOffset:" << settings.m_inputFrequencyOffset
<< "m_forceDecimator:" << settings.m_forceDecimator
<< "m_bfoFrequency:" << settings.m_bfoFrequency
<< "m_atvModulation:" << settings.m_atvModulation
<< "m_fmDeviation:" << settings.m_fmDeviation
@ -513,62 +483,47 @@ void ATVDemodSink::applySettings(const ATVDemodSettings& settings, bool force)
<< "m_halfFrames:" << settings.m_halfFrames
<< "m_levelSynchroTop:" << settings.m_levelSynchroTop
<< "m_levelBlack:" << settings.m_levelBlack
<< "m_lineTimeFactor:" << settings.m_lineTimeFactor
<< "m_topTimeFactor:" << settings.m_topTimeFactor
<< "m_rgbColor:" << settings.m_rgbColor
<< "m_title:" << settings.m_title
<< "m_udpAddress:" << settings.m_udpAddress
<< "m_udpPort:" << settings.m_udpPort
<< "force:" << force;
if ((settings.m_nbLines != m_settings.m_nbLines)
|| (settings.m_fps != m_settings.m_fps)
|| (settings.m_atvStd != m_settings.m_atvStd)
|| (settings.m_atvModulation != m_settings.m_atvModulation)
|| (settings.m_fftBandwidth != m_settings.m_fftBandwidth)
|| (settings.m_fftOppBandwidth != m_settings.m_fftOppBandwidth)
|| (settings.m_atvStd != m_settings.m_atvStd)
|| (settings.m_lineTimeFactor != m_settings.m_lineTimeFactor) || force)
if ((settings.m_fftBandwidth != m_settings.m_fftBandwidth)
|| (settings.m_fftOppBandwidth != m_settings.m_fftOppBandwidth) || force)
{
unsigned int samplesPerLineNom;
ATVDemodSettings::getBaseValues(m_channelSampleRate, settings.m_nbLines * settings.m_fps, m_tvSampleRate, samplesPerLineNom);
m_samplesPerLine = samplesPerLineNom + settings.m_lineTimeFactor;
m_ampAverage.resize(m_samplesPerLine * m_settings.m_nbLines * 2); // AGC average in two full images
qDebug() << "ATVDemodSink::applySettings:"
<< " m_tvSampleRate: " << m_tvSampleRate
<< " m_fftBandwidth: " << settings.m_fftBandwidth
<< " m_fftOppBandwidth:" << settings.m_fftOppBandwidth
<< " m_bfoFrequency: " << settings.m_bfoFrequency;
if (m_tvSampleRate > 0)
{
m_interpolatorDistanceRemain = 0;
m_interpolatorDistance = (Real) m_tvSampleRate / (Real) m_channelSampleRate;
m_interpolator.create(24,
m_tvSampleRate,
settings.m_fftBandwidth / ATVDemodSettings::getRFBandwidthDivisor(settings.m_atvModulation),
3.0
);
}
else
{
m_tvSampleRate = m_channelSampleRate;
}
m_DSBFilter->create_asym_filter(
settings.m_fftOppBandwidth / (float) m_tvSampleRate,
settings.m_fftBandwidth / (float) m_tvSampleRate
settings.m_fftOppBandwidth / (float) m_channelSampleRate,
settings.m_fftBandwidth / (float) m_channelSampleRate
);
std::fill(m_DSBFilterBuffer, m_DSBFilterBuffer + m_ssbFftLen, Complex{0.0, 0.0});
m_DSBFilterBufferIndex = 0;
}
m_bfoPLL.configure((float) settings.m_bfoFrequency / (float) m_tvSampleRate,
100.0 / m_tvSampleRate,
if ((settings.m_bfoFrequency != m_settings.m_bfoFrequency) || force)
{
m_bfoPLL.configure((float) settings.m_bfoFrequency / (float) m_channelSampleRate,
100.0 / m_channelSampleRate,
0.01);
m_bfoFilter.setFrequencies(m_tvSampleRate, settings.m_bfoFrequency);
m_bfoFilter.setFrequencies(m_channelSampleRate, settings.m_bfoFrequency);
}
applyStandard(m_tvSampleRate, settings, ATVDemodSettings::getNominalLineTime(settings.m_nbLines, settings.m_fps));
if ((settings.m_nbLines != m_settings.m_nbLines)
|| (settings.m_fps != m_settings.m_fps)
|| (settings.m_atvStd != m_settings.m_atvStd) || force)
{
unsigned int samplesPerLineNom;
ATVDemodSettings::getBaseValues(m_channelSampleRate, settings.m_nbLines * settings.m_fps, samplesPerLineNom);
m_samplesPerLine = samplesPerLineNom;
m_samplesPerLineFrac = (float)m_channelSampleRate / (m_settings.m_nbLines * m_settings.m_fps) - m_samplesPerLine;
m_ampAverage.resize(m_samplesPerLine * m_settings.m_nbLines * 2); // AGC average in two full images
qDebug() << "ATVDemodSink::applySettings:"
<< " m_channelSampleRate: " << m_channelSampleRate
<< " m_samplesPerLine:" << m_samplesPerLine
<< " m_samplesPerLineFrac:" << m_samplesPerLineFrac;
applyStandard(m_channelSampleRate, settings.m_atvStd, ATVDemodSettings::getNominalLineTime(settings.m_nbLines, settings.m_fps));
if (m_registeredTVScreen)
{
@ -582,10 +537,6 @@ void ATVDemodSink::applySettings(const ATVDemodSettings& settings, bool force)
m_fieldIndex = 0;
}
if ((settings.m_topTimeFactor != m_settings.m_topTimeFactor) || force) {
m_numberSamplesPerHTop = m_numberSamplesPerHTopNom * (settings.m_topTimeFactor / 100.0f);
}
if ((settings.m_fmDeviation != m_settings.m_fmDeviation) || force) {
m_objPhaseDiscri.setFMScaling(1.0f / settings.m_fmDeviation);
}

View File

@ -97,9 +97,9 @@ private:
int m_channelSampleRate;
int m_channelFrequencyOffset;
int m_tvSampleRate;
int m_samplesPerLine; //!< number of samples per complete line (includes sync signals) - adusted value
ATVDemodSettings m_settings;
int m_samplesPerLine; //!< number of samples per complete line (includes sync signals) - adusted value
float m_samplesPerLineFrac; //!< number of samples per complete line (includes sync signals), fractional part
ATVDemodSettings m_settings;
int m_videoTabIndex;
//*************** SCOPE ***************
@ -112,8 +112,7 @@ private:
std::shared_ptr<TVScreenAnalogBuffer> m_tvScreenBuffer;
//int m_intNumberSamplePerLine;
int m_numberSamplesPerHTopNom; //!< number of samples per horizontal synchronization pulse (pulse in ultra-black) - nominal value
int m_numberSamplesPerHTop; //!< number of samples per horizontal synchronization pulse (pulse in ultra-black) - adusted value
int m_numberSamplesPerHTop; //!< number of samples per horizontal synchronization pulse (pulse in ultra-black) - integer value
int m_numberOfBlackLines; //!< this is the total number of lines not part of the image and is used for vertical screen size
int m_firstVisibleLine;
@ -161,7 +160,6 @@ private:
int m_hSyncErrorCount;
float prevSample;
int m_avgColIndex;
SampleVector m_sampleBuffer;
@ -177,11 +175,6 @@ private:
SimplePhaseLock m_bfoPLL;
SecondOrderRecursiveFilter m_bfoFilter;
// Interpolator group for decimation and/or double sideband RF filtering
Interpolator m_interpolator;
Real m_interpolatorDistance;
Real m_interpolatorDistanceRemain;
// Used for vestigial SSB with asymmetrical filtering (needs double sideband scheme)
fftfilt* m_DSBFilter;
Complex* m_DSBFilterBuffer;
@ -191,10 +184,8 @@ private:
// Used for FM
PhaseDiscriminators m_objPhaseDiscri;
//QElapsedTimer m_objTimer;
void demod(Complex& c);
void applyStandard(int sampleRate, const ATVDemodSettings& settings, float lineDuration);
void applyStandard(int sampleRate, ATVDemodSettings::ATVStd atvStd, float lineDuration);
inline void processSample(float& sample, int& sampleVideo)
{
@ -206,7 +197,7 @@ private:
// Horizontal Synchro detection
if ((prevSample >= m_settings.m_levelSynchroTop &&
sample < m_settings.m_levelSynchroTop) // horizontal synchro detected
&& (m_sampleOffsetDetected > m_samplesPerLine - m_numberSamplesPerHTopNom))
&& (m_sampleOffsetDetected > m_samplesPerLine - m_numberSamplesPerHTop))
{
float sampleOffsetDetectedFrac =
(sample - m_settings.m_levelSynchroTop) / (prevSample - sample);
@ -216,7 +207,7 @@ private:
else if (hSyncShift < -m_samplesPerLine / 2)
hSyncShift += m_samplesPerLine;
if (fabs(hSyncShift) > m_numberSamplesPerHTopNom)
if (fabs(hSyncShift) > m_numberSamplesPerHTop)
{
m_hSyncErrorCount++;
if (m_hSyncErrorCount >= 4)
@ -252,7 +243,7 @@ private:
{
if (m_settings.m_hSync)
{
float sampleOffsetFloat = m_hSyncShift + m_sampleOffsetFrac;
float sampleOffsetFloat = m_hSyncShift + m_sampleOffsetFrac - m_samplesPerLineFrac;
m_sampleOffset = sampleOffsetFloat;
m_sampleOffsetFrac = sampleOffsetFloat - m_sampleOffset;
}

View File

@ -51,7 +51,6 @@ void ATVDemodWebAPIAdapter::webapiFormatChannelSettings(
SWGSDRangel::SWGChannelSettings& response,
const ATVDemodSettings& settings)
{
response.getAtvDemodSettings()->setBlndecimatorEnable(settings.m_forceDecimator ? 1 : 0);
response.getAtvDemodSettings()->setBlnFftFiltering(settings.m_fftFiltering ? 1 : 0);
response.getAtvDemodSettings()->setBlnHSync(settings.m_hSync ? 1 : 0);
response.getAtvDemodSettings()->setBlnInvertVideo(settings.m_invertVideo ? 1 : 0);
@ -71,11 +70,9 @@ void ATVDemodWebAPIAdapter::webapiFormatChannelSettings(
response.getAtvDemodSettings()->setHalfImage(settings.m_halfFrames ? 1 : 0);
response.getAtvDemodSettings()->setIntFrequencyOffset(settings.m_inputFrequencyOffset);
response.getAtvDemodSettings()->setIntNumberOfLines(settings.m_nbLines);
response.getAtvDemodSettings()->setLineTimeFactor(settings.m_lineTimeFactor);
response.getAtvDemodSettings()->setNbLinesIndex(ATVDemodSettings::getNumberOfLinesIndex(settings.m_nbLines));
response.getAtvDemodSettings()->setRgbColor(settings.m_rgbColor);
response.getAtvDemodSettings()->setTitle(new QString(settings.m_title));
response.getAtvDemodSettings()->setTopTimeFactor(settings.m_topTimeFactor);
response.getAtvDemodSettings()->setUdpAddress(new QString(settings.m_udpAddress));
response.getAtvDemodSettings()->setUdpPort(settings.m_udpPort);
}
@ -85,9 +82,6 @@ void ATVDemodWebAPIAdapter::webapiUpdateChannelSettings(
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response)
{
if (channelSettingsKeys.contains("blndecimatorEnable")) {
settings.m_forceDecimator = response.getAtvDemodSettings()->getBlndecimatorEnable() != 0;
}
if (channelSettingsKeys.contains("blnFFTFiltering")) {
settings.m_fftFiltering = response.getAtvDemodSettings()->getBlnFftFiltering() != 0;
}
@ -142,18 +136,12 @@ void ATVDemodWebAPIAdapter::webapiUpdateChannelSettings(
if (channelSettingsKeys.contains("intNumberOfLines")) {
settings.m_nbLines = response.getAtvDemodSettings()->getIntNumberOfLines();
}
if (channelSettingsKeys.contains("lineTimeFactor")) {
settings.m_lineTimeFactor = response.getAtvDemodSettings()->getLineTimeFactor();
}
if (channelSettingsKeys.contains("rgbColor")) {
settings.m_rgbColor = response.getAtvDemodSettings()->getRgbColor();
}
if (channelSettingsKeys.contains("title")) {
settings.m_title = *response.getAtvDemodSettings()->getTitle();
}
if (channelSettingsKeys.contains("topTimeFactor")) {
settings.m_topTimeFactor = response.getAtvDemodSettings()->getTopTimeFactor();
}
if (channelSettingsKeys.contains("udpAddress")) {
settings.m_udpAddress = *response.getAtvDemodSettings()->getUdpAddress();
}