kopia lustrzana https://github.com/f4exb/sdrangel
Sample offset is adjusted every line
rodzic
44d199cfab
commit
2e90be0044
|
@ -43,14 +43,12 @@ ATVDemodSink::ATVDemodSink() :
|
||||||
m_ampMin(-1.0f),
|
m_ampMin(-1.0f),
|
||||||
m_ampMax(1.0f),
|
m_ampMax(1.0f),
|
||||||
m_ampDelta(2.0f),
|
m_ampDelta(2.0f),
|
||||||
m_colIndex(0),
|
m_sampleOffset(0),
|
||||||
m_sampleIndex(0),
|
m_sampleOffsetFrac(0.0f),
|
||||||
m_sampleIndexDetected(0),
|
m_sampleOffsetDetected(0),
|
||||||
m_hSyncShiftSum(0.0f),
|
m_hSyncShift(0.0f),
|
||||||
m_hSyncShiftCount(0),
|
|
||||||
m_hSyncErrorCount(0),
|
m_hSyncErrorCount(0),
|
||||||
m_amSampleIndex(0),
|
m_amSampleIndex(0),
|
||||||
m_rowIndex(0),
|
|
||||||
m_lineIndex(0),
|
m_lineIndex(0),
|
||||||
m_ampAverage(4800),
|
m_ampAverage(4800),
|
||||||
m_bfoPLL(200/1000000, 100/1000000, 0.01),
|
m_bfoPLL(200/1000000, 100/1000000, 0.01),
|
||||||
|
@ -490,8 +488,6 @@ void ATVDemodSink::applyChannelSettings(int channelSampleRate, int channelFreque
|
||||||
}
|
}
|
||||||
|
|
||||||
m_fieldIndex = 0;
|
m_fieldIndex = 0;
|
||||||
m_colIndex = 0;
|
|
||||||
m_rowIndex = 0;
|
|
||||||
|
|
||||||
m_channelSampleRate = channelSampleRate;
|
m_channelSampleRate = channelSampleRate;
|
||||||
m_channelFrequencyOffset = channelFrequencyOffset;
|
m_channelFrequencyOffset = channelFrequencyOffset;
|
||||||
|
@ -584,8 +580,6 @@ void ATVDemodSink::applySettings(const ATVDemodSettings& settings, bool force)
|
||||||
}
|
}
|
||||||
|
|
||||||
m_fieldIndex = 0;
|
m_fieldIndex = 0;
|
||||||
m_colIndex = 0;
|
|
||||||
m_rowIndex = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((settings.m_topTimeFactor != m_settings.m_topTimeFactor) || force) {
|
if ((settings.m_topTimeFactor != m_settings.m_topTimeFactor) || force) {
|
||||||
|
|
|
@ -150,15 +150,14 @@ private:
|
||||||
float m_fltBufferI[6];
|
float m_fltBufferI[6];
|
||||||
float m_fltBufferQ[6];
|
float m_fltBufferQ[6];
|
||||||
|
|
||||||
int m_colIndex;
|
|
||||||
int m_sampleIndex; // assumed (averaged) sample offset from the start of horizontal sync pulse
|
|
||||||
int m_sampleIndexDetected; // detected sample offset from the start of horizontal sync pulse
|
|
||||||
int m_amSampleIndex;
|
int m_amSampleIndex;
|
||||||
int m_rowIndex;
|
|
||||||
|
int m_sampleOffset; // assumed (averaged) sample offset from the start of horizontal sync pulse
|
||||||
|
float m_sampleOffsetFrac; // sample offset, fractional part
|
||||||
|
int m_sampleOffsetDetected; // detected sample offset from the start of horizontal sync pulse
|
||||||
int m_lineIndex;
|
int m_lineIndex;
|
||||||
|
|
||||||
float m_hSyncShiftSum;
|
float m_hSyncShift;
|
||||||
int m_hSyncShiftCount;
|
|
||||||
int m_hSyncErrorCount;
|
int m_hSyncErrorCount;
|
||||||
|
|
||||||
float prevSample;
|
float prevSample;
|
||||||
|
@ -200,18 +199,18 @@ private:
|
||||||
inline void processSample(float& sample, int& sampleVideo)
|
inline void processSample(float& sample, int& sampleVideo)
|
||||||
{
|
{
|
||||||
// Filling pixel on the current line - reference index 0 at start of sync pulse
|
// Filling pixel on the current line - reference index 0 at start of sync pulse
|
||||||
m_tvScreenData->setSampleValue(m_sampleIndex - m_numberSamplesPerHSync, sampleVideo);
|
m_tvScreenData->setSampleValue(m_sampleOffset - m_numberSamplesPerHSync, sampleVideo);
|
||||||
|
|
||||||
if (m_settings.m_hSync)
|
if (m_settings.m_hSync)
|
||||||
{
|
{
|
||||||
// Horizontal Synchro detection
|
// Horizontal Synchro detection
|
||||||
if ((prevSample >= m_settings.m_levelSynchroTop &&
|
if ((prevSample >= m_settings.m_levelSynchroTop &&
|
||||||
sample < m_settings.m_levelSynchroTop) // horizontal synchro detected
|
sample < m_settings.m_levelSynchroTop) // horizontal synchro detected
|
||||||
&& (m_sampleIndexDetected > m_samplesPerLine - m_numberSamplesPerHTopNom))
|
&& (m_sampleOffsetDetected > m_samplesPerLine - m_numberSamplesPerHTopNom))
|
||||||
{
|
{
|
||||||
double sampleIndexDetectedFrac =
|
float sampleOffsetDetectedFrac =
|
||||||
(sample - m_settings.m_levelSynchroTop) / (prevSample - sample);
|
(sample - m_settings.m_levelSynchroTop) / (prevSample - sample);
|
||||||
double hSyncShift = -m_sampleIndex - sampleIndexDetectedFrac;
|
float hSyncShift = -m_sampleOffset - m_sampleOffsetFrac - sampleOffsetDetectedFrac;
|
||||||
if (hSyncShift > m_samplesPerLine / 2)
|
if (hSyncShift > m_samplesPerLine / 2)
|
||||||
hSyncShift -= m_samplesPerLine;
|
hSyncShift -= m_samplesPerLine;
|
||||||
else if (hSyncShift < -m_samplesPerLine / 2)
|
else if (hSyncShift < -m_samplesPerLine / 2)
|
||||||
|
@ -220,46 +219,50 @@ private:
|
||||||
if (fabs(hSyncShift) > m_numberSamplesPerHTopNom)
|
if (fabs(hSyncShift) > m_numberSamplesPerHTopNom)
|
||||||
{
|
{
|
||||||
m_hSyncErrorCount++;
|
m_hSyncErrorCount++;
|
||||||
if (m_hSyncErrorCount >= 8)
|
if (m_hSyncErrorCount >= 4)
|
||||||
{
|
{
|
||||||
// Fast sync: shift is too large, needs to be fixed ASAP
|
// Fast sync: shift is too large, needs to be fixed ASAP
|
||||||
m_sampleIndex = 0;
|
m_hSyncShift = hSyncShift;
|
||||||
m_hSyncShiftSum = 0.0;
|
|
||||||
m_hSyncShiftCount = 0;
|
|
||||||
m_hSyncErrorCount = 0;
|
m_hSyncErrorCount = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_hSyncShiftSum += hSyncShift;
|
// Slow sync: slight adjustment is needed
|
||||||
m_hSyncShiftCount++;
|
m_hSyncShift = hSyncShift * 0.2f;
|
||||||
m_hSyncErrorCount = 0;
|
m_hSyncErrorCount = 0;
|
||||||
}
|
}
|
||||||
m_sampleIndexDetected = 0;
|
m_sampleOffsetDetected = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_sampleIndexDetected++;
|
m_sampleOffsetDetected++;
|
||||||
}
|
}
|
||||||
else
|
m_sampleOffset++;
|
||||||
{
|
|
||||||
m_hSyncShiftSum = 0.0f;
|
|
||||||
m_hSyncShiftCount = 0;
|
|
||||||
}
|
|
||||||
m_sampleIndex++;
|
|
||||||
|
|
||||||
if (m_settings.m_vSync)
|
if (m_settings.m_vSync)
|
||||||
{
|
{
|
||||||
if (m_sampleIndex > m_fieldDetectStartPos && m_sampleIndex < m_fieldDetectEndPos)
|
if (m_sampleOffset > m_fieldDetectStartPos && m_sampleOffset < m_fieldDetectEndPos)
|
||||||
m_fieldDetectSampleCount += sample < m_settings.m_levelSynchroTop;
|
m_fieldDetectSampleCount += sample < m_settings.m_levelSynchroTop;
|
||||||
if (m_sampleIndex > m_vSyncDetectStartPos && m_sampleIndex < m_vSyncDetectEndPos)
|
if (m_sampleOffset > m_vSyncDetectStartPos && m_sampleOffset < m_vSyncDetectEndPos)
|
||||||
m_vSyncDetectSampleCount += sample < m_settings.m_levelSynchroTop;
|
m_vSyncDetectSampleCount += sample < m_settings.m_levelSynchroTop;
|
||||||
}
|
}
|
||||||
|
|
||||||
// end of line
|
// end of line
|
||||||
if (m_sampleIndex >= m_samplesPerLine)
|
if (m_sampleOffset >= m_samplesPerLine)
|
||||||
{
|
{
|
||||||
m_sampleIndex = 0;
|
if (m_settings.m_hSync)
|
||||||
|
{
|
||||||
|
float sampleOffsetFloat = m_hSyncShift + m_sampleOffsetFrac;
|
||||||
|
m_sampleOffset = sampleOffsetFloat;
|
||||||
|
m_sampleOffsetFrac = sampleOffsetFloat - m_sampleOffset;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_sampleOffset = 0;
|
||||||
|
}
|
||||||
|
m_hSyncShift = 0.0f;
|
||||||
|
|
||||||
|
m_lineIndex++;
|
||||||
if (m_settings.m_atvStd == ATVDemodSettings::ATVStdHSkip) {
|
if (m_settings.m_atvStd == ATVDemodSettings::ATVStdHSkip) {
|
||||||
processEOLHSkip();
|
processEOLHSkip();
|
||||||
} else {
|
} else {
|
||||||
|
@ -273,24 +276,9 @@ private:
|
||||||
// Standard vertical sync
|
// Standard vertical sync
|
||||||
inline void processEOLClassic()
|
inline void processEOLClassic()
|
||||||
{
|
{
|
||||||
m_lineIndex++;
|
|
||||||
|
|
||||||
if (m_lineIndex == m_numberOfVSyncLines + 3 && m_fieldIndex == 0)
|
if (m_lineIndex == m_numberOfVSyncLines + 3 && m_fieldIndex == 0)
|
||||||
{
|
{
|
||||||
float shiftSamples = 0.0f;
|
|
||||||
|
|
||||||
// Slow sync: slight adjustment is needed
|
|
||||||
if (m_hSyncShiftCount != 0 && m_settings.m_hSync)
|
|
||||||
{
|
|
||||||
shiftSamples = m_hSyncShiftSum / m_hSyncShiftCount;
|
|
||||||
m_sampleIndex = shiftSamples;
|
|
||||||
m_hSyncShiftSum = 0.0f;
|
|
||||||
m_hSyncShiftCount = 0;
|
|
||||||
m_hSyncErrorCount = 0;
|
|
||||||
}
|
|
||||||
m_registeredTVScreen->renderImage();
|
m_registeredTVScreen->renderImage();
|
||||||
//m_registeredTVScreen->renderImage(0,
|
|
||||||
// shiftSamples < -1.0f ? -1.0f : (shiftSamples > 1.0f ? 1.0f : shiftSamples));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_vSyncDetectSampleCount > m_vSyncDetectThreshold &&
|
if (m_vSyncDetectSampleCount > m_vSyncDetectThreshold &&
|
||||||
|
@ -323,47 +311,21 @@ private:
|
||||||
if (m_interleaved)
|
if (m_interleaved)
|
||||||
rowIndex = rowIndex * 2 - m_fieldIndex;
|
rowIndex = rowIndex * 2 - m_fieldIndex;
|
||||||
|
|
||||||
// TODO: CHANGE
|
m_tvScreenData->selectRow(rowIndex, m_sampleOffsetFrac);
|
||||||
float shiftSamples = 0.0f;
|
|
||||||
if (m_hSyncShiftCount != 0)
|
|
||||||
shiftSamples = m_hSyncShiftSum / m_hSyncShiftCount;
|
|
||||||
m_tvScreenData->selectRow(rowIndex,
|
|
||||||
shiftSamples < -1.0f ? -1.0f : (shiftSamples > 1.0f ? 1.0f : shiftSamples));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vertical sync is obtained by skipping horizontal sync on the line that triggers vertical sync (new frame)
|
// Vertical sync is obtained by skipping horizontal sync on the line that triggers vertical sync (new frame)
|
||||||
inline void processEOLHSkip()
|
inline void processEOLHSkip()
|
||||||
{
|
{
|
||||||
m_lineIndex++;
|
if ((m_sampleOffsetDetected > (3 * m_samplesPerLine) / 2) // Vertical sync is first horizontal sync after skip (count at least 1.5 line length)
|
||||||
m_rowIndex++;
|
|
||||||
|
|
||||||
if ((m_sampleIndexDetected > (3*m_samplesPerLine) / 2) // Vertical sync is first horizontal sync after skip (count at least 1.5 line length)
|
|
||||||
|| (!m_settings.m_vSync && (m_lineIndex >= m_settings.m_nbLines))) // Vsync ignored and reached nominal number of lines per frame
|
|| (!m_settings.m_vSync && (m_lineIndex >= m_settings.m_nbLines))) // Vsync ignored and reached nominal number of lines per frame
|
||||||
{
|
{
|
||||||
float shiftSamples = 0.0f;
|
|
||||||
|
|
||||||
// Slow sync: slight adjustment is needed
|
|
||||||
if (m_hSyncShiftCount != 0 && m_settings.m_hSync)
|
|
||||||
{
|
|
||||||
shiftSamples = m_hSyncShiftSum / m_hSyncShiftCount;
|
|
||||||
m_sampleIndex = shiftSamples;
|
|
||||||
m_hSyncShiftSum = 0.0f;
|
|
||||||
m_hSyncShiftCount = 0;
|
|
||||||
m_hSyncErrorCount = 0;
|
|
||||||
}
|
|
||||||
m_registeredTVScreen->renderImage();
|
m_registeredTVScreen->renderImage();
|
||||||
//m_registeredTVScreen->renderImage(0,
|
|
||||||
// shiftSamples < -1.0f ? -1.0f : (shiftSamples > 1.0f ? 1.0f : shiftSamples));
|
|
||||||
m_lineIndex = 0;
|
m_lineIndex = 0;
|
||||||
m_rowIndex = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: CHANGE
|
m_tvScreenData->selectRow(m_lineIndex, m_sampleOffsetFrac);
|
||||||
float shiftSamples = m_hSyncShiftSum / m_hSyncShiftCount;
|
|
||||||
m_tvScreenData->setSampleValue(m_rowIndex,
|
|
||||||
shiftSamples < -1.0f ? -1.0f : (shiftSamples > 1.0f ? 1.0f : shiftSamples));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif // INCLUDE_ATVDEMODSINK_H
|
#endif // INCLUDE_ATVDEMODSINK_H
|
|
@ -104,10 +104,34 @@ void TVScreenAnalog::initializeGL()
|
||||||
{
|
{
|
||||||
initializeOpenGLFunctions();
|
initializeOpenGLFunctions();
|
||||||
|
|
||||||
|
connect(QOpenGLContext::currentContext(), &QOpenGLContext::aboutToBeDestroyed,
|
||||||
|
this, &TVScreenAnalog::cleanup); // TODO: when migrating to QOpenGLWidget
|
||||||
|
|
||||||
m_shader = std::make_shared<QOpenGLShaderProgram>(this);
|
m_shader = std::make_shared<QOpenGLShaderProgram>(this);
|
||||||
m_shader->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
|
if (!m_shader->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource))
|
||||||
m_shader->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
|
{
|
||||||
m_shader->link();
|
qWarning()
|
||||||
|
<< "TVScreenAnalog::initializeGL: error in vertex shader:"
|
||||||
|
<< m_shader->log();
|
||||||
|
m_shader = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!m_shader->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource))
|
||||||
|
{
|
||||||
|
qWarning()
|
||||||
|
<< "TVScreenAnalog::initializeGL: error in fragment shader:"
|
||||||
|
<< m_shader->log();
|
||||||
|
m_shader = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!m_shader->link())
|
||||||
|
{
|
||||||
|
qWarning()
|
||||||
|
<< "TVScreenAnalog::initializeGL: error linking shader:"
|
||||||
|
<< m_shader->log();
|
||||||
|
m_shader = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
m_vertexAttribIndex = m_shader->attributeLocation("vertex");
|
m_vertexAttribIndex = m_shader->attributeLocation("vertex");
|
||||||
m_texCoordAttribIndex = m_shader->attributeLocation("texCoord");
|
m_texCoordAttribIndex = m_shader->attributeLocation("texCoord");
|
||||||
|
@ -117,9 +141,6 @@ void TVScreenAnalog::initializeGL()
|
||||||
m_imageHeightLoc = m_shader->uniformLocation("imh");
|
m_imageHeightLoc = m_shader->uniformLocation("imh");
|
||||||
m_texelWidthLoc = m_shader->uniformLocation("tlw");
|
m_texelWidthLoc = m_shader->uniformLocation("tlw");
|
||||||
m_texelHeightLoc = m_shader->uniformLocation("tlh");
|
m_texelHeightLoc = m_shader->uniformLocation("tlh");
|
||||||
|
|
||||||
connect(QOpenGLContext::currentContext(), &QOpenGLContext::aboutToBeDestroyed,
|
|
||||||
this, &TVScreenAnalog::cleanup); // TODO: when migrating to QOpenGLWidget
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TVScreenAnalog::initializeTextures()
|
void TVScreenAnalog::initializeTextures()
|
||||||
|
@ -162,6 +183,13 @@ void TVScreenAnalog::paintGL()
|
||||||
{
|
{
|
||||||
m_isDataChanged = false;
|
m_isDataChanged = false;
|
||||||
|
|
||||||
|
if (!m_shader)
|
||||||
|
{
|
||||||
|
glClearColor(0.2f, 0.0f, 0.0f, 1.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_imageTexture ||
|
if (!m_imageTexture ||
|
||||||
m_imageTexture->width() != m_data->getWidth() ||
|
m_imageTexture->width() != m_data->getWidth() ||
|
||||||
m_imageTexture->height() != m_data->getHeight())
|
m_imageTexture->height() != m_data->getHeight())
|
||||||
|
@ -192,7 +220,7 @@ void TVScreenAnalog::paintGL()
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
|
||||||
1, m_data->getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, m_data->getLineShiftData());
|
1, m_data->getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, m_data->getLineShiftData());
|
||||||
|
|
||||||
float rectHalfWidth = 1.0f + 4 * texelWidth;
|
float rectHalfWidth = 1.0f + 4.0f / (imageWidth - 4.0f);
|
||||||
GLfloat vertices[] =
|
GLfloat vertices[] =
|
||||||
{
|
{
|
||||||
-rectHalfWidth, -1.0f,
|
-rectHalfWidth, -1.0f,
|
||||||
|
|
Ładowanie…
Reference in New Issue