1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-17 13:51:47 -05:00

ATV Modulator: complete vertical sync rework. Removed 405 lines and added 819 lines system. Implements #578

This commit is contained in:
f4exb 2020-07-31 04:04:16 +02:00
parent 96078bc9b8
commit 8578b3e117
5 changed files with 522 additions and 342 deletions

View File

@ -292,42 +292,45 @@ int ATVModGUI::getNbLines()
switch(ui->nbLines->currentIndex()) switch(ui->nbLines->currentIndex())
{ {
case 0: case 0:
return 640; return 819;
break;
case 2:
return 525;
break;
case 3:
return 480;
break;
case 4:
return 405;
break;
case 5:
return 360;
break;
case 6:
return 343;
break;
case 7:
return 240;
break;
case 8:
return 180;
break;
case 9:
return 120;
break;
case 10:
return 90;
break;
case 11:
return 60;
break;
case 12:
return 32;
break; break;
case 1: case 1:
return 640;
break;
case 3:
return 525;
break;
case 4:
return 480;
break;
case 5:
return 405;
break;
case 6:
return 360;
break;
case 7:
return 343;
break;
case 8:
return 240;
break;
case 9:
return 180;
break;
case 10:
return 120;
break;
case 11:
return 90;
break;
case 12:
return 60;
break;
case 13:
return 32;
break;
case 2:
default: default:
return 625; return 625;
break; break;
@ -337,30 +340,32 @@ int ATVModGUI::getNbLines()
int ATVModGUI::getNbLinesIndex(int nbLines) int ATVModGUI::getNbLinesIndex(int nbLines)
{ {
if (nbLines < 32) { if (nbLines < 32) {
return 1;
} else if (nbLines < 60) {
return 12;
} else if (nbLines < 90) {
return 11;
} else if (nbLines < 120) {
return 10;
} else if (nbLines < 180) {
return 9;
} else if (nbLines < 240) {
return 8;
} else if (nbLines < 343) {
return 7;
} else if (nbLines < 360) {
return 6;
} else if (nbLines < 405) {
return 5;
} else if (nbLines < 480) {
return 4;
} else if (nbLines < 525) {
return 3;
} else if (nbLines < 625) {
return 2; return 2;
} else if (nbLines < 60) {
return 13;
} else if (nbLines < 90) {
return 12;
} else if (nbLines < 120) {
return 11;
} else if (nbLines < 180) {
return 10;
} else if (nbLines < 240) {
return 9;
} else if (nbLines < 343) {
return 8;
} else if (nbLines < 360) {
return 7;
} else if (nbLines < 405) {
return 6;
} else if (nbLines < 480) {
return 5;
} else if (nbLines < 525) {
return 4;
} else if (nbLines < 625) {
return 3;
} else if (nbLines < 640) { } else if (nbLines < 640) {
return 2;
} else if (nbLines < 819) {
return 1; return 1;
} else { } else {
return 0; return 0;

View File

@ -537,6 +537,11 @@
<property name="toolTip"> <property name="toolTip">
<string>Lines per full frame</string> <string>Lines per full frame</string>
</property> </property>
<item>
<property name="text">
<string>819</string>
</property>
</item>
<item> <item>
<property name="text"> <property name="text">
<string>640</string> <string>640</string>
@ -698,7 +703,7 @@
</item> </item>
<item> <item>
<property name="text"> <property name="text">
<string>PAL405</string> <string>819L</string>
</property> </property>
</item> </item>
<item> <item>

View File

@ -28,12 +28,12 @@ struct ATVModSettings
{ {
typedef enum typedef enum
{ {
ATVStdPAL625, ATVStdPAL625, //!< standard 625 lines B, D, G, H, I, K, K1 and N
ATVStdPAL525, ATVStdPAL525, //!< standard 525 lines M
ATVStd405, ATVStd819, //!< standard 819 lines F (Belgium)
ATVStdShortInterleaved, ATVStdShortInterlaced, //!< non-standard with mimimal vertical sync sequences permitted by SDR technology
ATVStdShort, ATVStdShort, //!< same as above
ATVStdHSkip, ATVStdHSkip //!< first introduced vertical sync by skipping horizontal sync to indicate start of image
} ATVStd; } ATVStd;
typedef enum typedef enum

View File

@ -39,12 +39,80 @@ const int ATVModSource::m_nbBars = 6;
const int ATVModSource::m_cameraFPSTestNbFrames = 100; const int ATVModSource::m_cameraFPSTestNbFrames = 100;
const int ATVModSource::m_ssbFftLen = 1024; const int ATVModSource::m_ssbFftLen = 1024;
const ATVModSource::LineType ATVModSource::StdPAL625_F1Start[] = {
LineBroadPulses, // 1 (index 0)
LineBroadPulses, // 2
LineBroadShortPulses, // 3
LineShortPulses, // 4
LineShortPulses // 5
};
const ATVModSource::LineType ATVModSource::StdPAL625_F2Start[] = {
LineBroadPulses, // 314 (index 313)
LineBroadPulses, // 315
LineShortPulses, // 316
LineShortPulses, // 317
LineShortBlackPulses // 318
};
const ATVModSource::LineType ATVModSource::StdPAL525_F1Start[] = {
LineShortPulses, // 1 (index 0)
LineShortPulses,
LineShortPulses,
LineBroadPulses, // 4
LineBroadPulses,
LineBroadPulses,
LineShortPulses, // 7
LineShortPulses,
LineShortPulses, // 9
};
const ATVModSource::LineType ATVModSource::StdPAL525_F2Start[] = {
LineShortPulses, // 264 (index 263)
LineShortPulses, // 265
LineShortBroadPulses, // 266
LineBroadPulses, // 267
LineBroadPulses, // 268
LineBroadShortPulses, // 269
LineShortPulses, // 270
LineShortPulses, // 271
LineShortBlackPulses // 272
};
const ATVModSource::LineType ATVModSource::Std819_F1Start[] = {
LineBroadPulses, // 1 (index 0)
LineBroadPulses, // 2
LineBroadPulses, // 3
LineBroadShortPulses, // 4
LineShortPulses, // 5
LineShortPulses, // 6
LineShortPulses, // 7
};
const ATVModSource::LineType ATVModSource::Std819_F2Start[] = {
LineBroadPulses, // 410 (index 409)
LineBroadPulses, // 411
LineBroadPulses, // 412
LineShortPulses, // 413
LineShortPulses, // 414
LineShortPulses, // 415
};
const ATVModSource::LineType ATVModSource::StdShort_F1Start[] = {
LineBroadPulses,
LineShortPulses,
};
const ATVModSource::LineType ATVModSource::StdShort_F2Start[] = {
LineBroadPulses,
LineShortPulses,
};
ATVModSource::ATVModSource() : ATVModSource::ATVModSource() :
m_channelSampleRate(1000000), m_channelSampleRate(1000000),
m_channelFrequencyOffset(0), m_channelFrequencyOffset(0),
m_modPhasor(0.0f), m_modPhasor(0.0f),
m_tvSampleRate(1000000), m_tvSampleRate(1000000),
m_evenImage(true),
m_horizontalCount(0), m_horizontalCount(0),
m_lineCount(0), m_lineCount(0),
m_imageOK(false), m_imageOK(false),
@ -61,7 +129,8 @@ ATVModSource::ATVModSource() :
m_DSBFilter(nullptr), m_DSBFilter(nullptr),
m_DSBFilterBuffer(nullptr), m_DSBFilterBuffer(nullptr),
m_DSBFilterBufferIndex(0), m_DSBFilterBufferIndex(0),
m_messageQueueToGUI(nullptr) m_messageQueueToGUI(nullptr),
m_imageLine(0)
{ {
scanCameras(); scanCameras();
@ -78,6 +147,8 @@ ATVModSource::ATVModSource() :
applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true); applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true);
applySettings(m_settings, true); // does applyStandard() too; applySettings(m_settings, true); // does applyStandard() too;
m_lineType = getLineType(m_settings.m_atvStd, m_lineCount);
} }
ATVModSource::~ATVModSource() ATVModSource::~ATVModSource()
@ -236,42 +307,44 @@ Complex& ATVModSource::modulateVestigialSSB(Real& sample)
void ATVModSource::pullVideo(Real& sample) void ATVModSource::pullVideo(Real& sample)
{ {
if ((m_settings.m_atvStd == ATVModSettings::ATVStdHSkip) && (m_lineCount == m_nbLines2)) // last line in skip mode if (m_settings.m_atvStd == ATVModSettings::ATVStdHSkip)
{ {
pullImageLine(sample, true); // pull image line without sync pullImageSample(sample, m_lineCount == m_nbLines - 1); // pull image line without sync at end of image
}
else if (m_lineCount < m_nbLines2 + 1) // even image or non interlaced
{
int iLine = m_lineCount;
if (iLine < m_nbSyncLinesHeadE + m_nbBlankLines)
{
pullVSyncLine(sample);
}
else if (iLine > m_nbLines2 - m_nbSyncLinesBottom)
{
pullVSyncLine(sample);
} }
else else
{ {
pullImageLine(sample); switch(m_lineType)
}
}
else // odd image
{ {
int iLine = m_lineCount - m_nbLines2 - 1; case LineImage:
pullImageSample(sample);
if (iLine < m_nbSyncLinesHeadO + m_nbBlankLines) break;
{ case LineImageHalf1Short:
pullVSyncLine(sample); pullImageFirstHalfShortSample(sample);
} break;
else if (iLine > m_nbLines2 - 1 - m_nbSyncLinesBottom) case LineImageHalf1Broad:
{ pullImageFirstHalfBroadSample(sample);
pullVSyncLine(sample); break;
} case LineImageHalf2:
else pullImageLastHalfSample(sample);
{ break;
pullImageLine(sample); case LineShortPulses:
pullVSyncLineEquPulsesSample(sample);
break;
case LineBroadPulses:
pullVSyncLineLongPulsesSample(sample);
break;
case LineShortBroadPulses:
pullVSyncLineEquLongPulsesSample(sample);
break;
case LineBroadShortPulses:
pullVSyncLineLongEquPulsesSample(sample);
break;
case LineShortBlackPulses:
pullVSyncLineEquBlackPulsesSample(sample);
break;
case LineBlack:
default:
pullBlackLineSample(sample);
} }
} }
@ -284,12 +357,25 @@ void ATVModSource::pullVideo(Real& sample)
if (m_lineCount < m_nbLines - 1) if (m_lineCount < m_nbLines - 1)
{ {
m_lineCount++; m_lineCount++;
if (m_lineCount > (m_nbLines/2)) m_evenImage = !m_evenImage;
if ((m_lineType == LineImage)
|| (m_lineType == LineImageHalf1Short)
|| (m_lineType == LineImageHalf1Broad)
|| (m_lineType == LineImageHalf2)) {
m_imageLine += m_interlaced ? 2 : 1;
}
if (m_lineCount == m_nbLinesField1) { // field 1 -> field 2 or first field2
m_imageLine = m_imageLineStart2; // field2 image line start index
}
m_lineType = getLineType(m_settings.m_atvStd, m_lineCount);
} }
else // new image else // new image
{ {
m_lineCount = 0; m_lineCount = 0;
m_evenImage = !m_evenImage; m_imageLine = m_imageLineStart1; // field1 image line start index
m_lineType = getLineType(m_settings.m_atvStd, m_lineCount);
if ((m_settings.m_atvModInput == ATVModSettings::ATVModInputVideo) && m_videoOK && (m_settings.m_videoPlay) && !m_videoEOF) if ((m_settings.m_atvModInput == ATVModSettings::ATVModInputVideo) && m_videoOK && (m_settings.m_videoPlay) && !m_videoEOF)
{ {
@ -519,14 +605,15 @@ void ATVModSource::applyStandard(const ATVModSettings& settings)
m_pointsPerSync = (uint32_t) ((4.7f / 64.0f) * m_pointsPerLine); m_pointsPerSync = (uint32_t) ((4.7f / 64.0f) * m_pointsPerLine);
m_pointsPerBP = (uint32_t) ((5.8f / 64.0f) * m_pointsPerLine); m_pointsPerBP = (uint32_t) ((5.8f / 64.0f) * m_pointsPerLine);
m_pointsPerFP = (uint32_t) ((1.5f / 64.0f) * m_pointsPerLine); m_pointsPerFP = (uint32_t) ((1.5f / 64.0f) * m_pointsPerLine);
m_pointsPerFSync = (uint32_t) ((2.3f / 64.0f) * m_pointsPerLine); m_pointsPerVEqu = (uint32_t) ((2.3f / 64.0f) * m_pointsPerLine); // short vertical sync pulse (equalizing)
m_pointsPerVSync = (uint32_t) (((32.0f - 4.7f) / 64.0f) * m_pointsPerLine); // broad vertical sync pulse (field sync)
m_pointsPerImgLine = m_pointsPerLine - m_pointsPerSync - m_pointsPerBP - m_pointsPerFP; m_pointsPerImgLine = m_pointsPerLine - m_pointsPerSync - m_pointsPerBP - m_pointsPerFP;
m_nbHorizPoints = m_pointsPerLine; m_nbHorizPoints = m_pointsPerLine;
m_pointsPerHBar = m_pointsPerImgLine / m_nbBars; m_pointsPerHBar = m_pointsPerImgLine / m_nbBars;
m_hBarIncrement = m_spanLevel / (float) (m_nbBars-1); m_hBarIncrement = m_spanLevel / (float) (m_nbBars - 1);
m_vBarIncrement = m_spanLevel / (float) m_nbBars; m_vBarIncrement = m_spanLevel / (float) (m_nbBars - 1);
m_nbLines = settings.m_nbLines; m_nbLines = settings.m_nbLines;
m_nbLines2 = m_nbLines / 2; m_nbLines2 = m_nbLines / 2;
@ -537,101 +624,69 @@ void ATVModSource::applyStandard(const ATVModSettings& settings)
// << " m_fps: " << m_config.m_fps // << " m_fps: " << m_config.m_fps
// << " rateUnits: " << rateUnits // << " rateUnits: " << rateUnits
// << " nbPointsPerRateUnit: " << nbPointsPerRateUnit // << " nbPointsPerRateUnit: " << nbPointsPerRateUnit
// << " m_tvSampleRate: " << m_tvSampleRate // << " m_tvSampleRate: " << m_tvSampleRate;
// << " m_pointsPerTU: " << m_pointsPerTU;
switch(settings.m_atvStd) switch(settings.m_atvStd)
{ {
case ATVModSettings::ATVStdHSkip: case ATVModSettings::ATVStdHSkip:
m_nbImageLines = m_nbLines; // lines less the total number of sync lines m_nbImageLines = m_nbLines; // lines less the total number of sync lines (0)
m_nbImageLines2 = m_nbImageLines; // force non interleaved for vbars m_nbImageLines2 = m_nbImageLines; // non interlaced (unused)
m_imageLineStart1 = 0;
m_imageLineStart2 = 0;
m_interlaced = false; m_interlaced = false;
m_nbSyncLinesHeadE = 0; // number of sync lines on the top of a frame even
m_nbSyncLinesHeadO = 0; // number of sync lines on the top of a frame odd
m_nbSyncLinesBottom = -1; // force no vsync in even block
m_nbLongSyncLines = 0;
m_nbHalfLongSync = 0;
m_nbWholeEqLines = 0;
m_singleLongSync = true;
m_nbBlankLines = 0;
m_blankLineLvel = 0.7f; m_blankLineLvel = 0.7f;
m_nbLines2 = m_nbLines - 1; m_nbLines2 = m_nbLines; // non interlaced
m_nbLinesField1 = m_nbLines; // non interlaced
break; break;
case ATVModSettings::ATVStdShort: case ATVModSettings::ATVStdShort:
m_nbImageLines = m_nbLines - 2; // lines less the total number of sync lines m_nbImageLines = m_nbLines - 2; // lines less the total number of sync lines
m_nbImageLines2 = m_nbImageLines; // force non interleaved for vbars m_nbImageLines2 = m_nbImageLines; // non interlaced (unused)
m_imageLineStart1 = 0;
m_imageLineStart2 = 0;
m_interlaced = false; m_interlaced = false;
m_nbSyncLinesHeadE = 1; // number of sync lines on the top of a frame even
m_nbSyncLinesHeadO = 1; // number of sync lines on the top of a frame odd
m_nbSyncLinesBottom = 0;
m_nbLongSyncLines = 1;
m_nbHalfLongSync = 0;
m_nbWholeEqLines = 0;
m_singleLongSync = true;
m_nbBlankLines = 1;
m_blankLineLvel = 0.7f; m_blankLineLvel = 0.7f;
m_nbLines2 = m_nbLines; // force non interleaved => treated as even for all lines m_nbLines2 = m_nbLines; // non interlaced
m_nbLinesField1 = m_nbLines; // non interlaced
break; break;
case ATVModSettings::ATVStdShortInterleaved: case ATVModSettings::ATVStdShortInterlaced:
m_nbImageLines = m_nbLines - 2; // lines less the total number of sync lines m_nbImageLines2 = m_nbLines2 - 2; // lines in half image less number of sync/service lines
m_nbImageLines2 = m_nbImageLines / 2; m_nbImageLines = 2*m_nbImageLines2;
m_imageLineStart1 = 1; //m_nbLines % 2;
m_imageLineStart2 = 0; //1 - (m_nbLines % 2);
m_interlaced = true; m_interlaced = true;
m_nbSyncLinesHeadE = 1; // number of sync lines on the top of a frame even
m_nbSyncLinesHeadO = 1; // number of sync lines on the top of a frame odd
m_nbSyncLinesBottom = 0;
m_nbLongSyncLines = 1;
m_nbHalfLongSync = 0;
m_nbWholeEqLines = 0;
m_singleLongSync = true;
m_nbBlankLines = 1;
m_blankLineLvel = 0.7f; m_blankLineLvel = 0.7f;
break; m_nbLinesField1 = m_nbLines2;
case ATVModSettings::ATVStd405: // Follows loosely the 405 lines standard
m_nbImageLines = m_nbLines - 14; // lines less the total number of sync lines
// 14 = m_nbSyncLinesHeadE + m_nbSyncLinesHeadO + m_nbSyncLinesBottom + m_nbHalfLongSync + m_nbWholeEqLines
m_nbImageLines2 = m_nbImageLines / 2;
m_interlaced = true;
m_nbSyncLinesHeadE = 4; // number of sync lines on the top of a frame even
m_nbSyncLinesHeadO = 4; // number of sync lines on the top of a frame odd
m_nbSyncLinesBottom = 3;
m_nbLongSyncLines = 2;
m_nbHalfLongSync = 1;
m_nbWholeEqLines = 2;
m_singleLongSync = false;
m_nbBlankLines = 7; // yields 376 lines (195 - 7) * 2
m_blankLineLvel = m_blackLevel;
break; break;
case ATVModSettings::ATVStdPAL525: // Follows PAL-M standard case ATVModSettings::ATVStdPAL525: // Follows PAL-M standard
m_nbImageLines = m_nbLines - 14; m_nbImageLines2 = (m_nbLines/2) - 19; // 525 -> 243 per half
m_nbImageLines2 = m_nbImageLines / 2; m_nbImageLines = 2*m_nbImageLines2;
m_imageLineStart1 = 1;
m_imageLineStart2 = 0;
m_interlaced = true; m_interlaced = true;
m_nbSyncLinesHeadE = 4;
m_nbSyncLinesHeadO = 4; // number of sync lines on the top of a frame odd
m_nbSyncLinesBottom = 3;
m_nbLongSyncLines = 2;
m_nbHalfLongSync = 1;
m_nbWholeEqLines = 2;
m_singleLongSync = false;
m_nbBlankLines = 15; // yields 480 lines (255 - 15) * 2
m_blankLineLvel = m_blackLevel; m_blankLineLvel = m_blackLevel;
m_nbLinesField1 = m_nbLines2+1;
break;
case ATVModSettings::ATVStd819: // Follows 819 F standard (belgium)
m_nbImageLines2 = (m_nbLines/2) - 28;
m_nbImageLines = 2*m_nbImageLines2;
m_imageLineStart1 = 1;
m_imageLineStart2 = 0;
m_interlaced = true;
m_blankLineLvel = m_blackLevel;
m_nbLinesField1 = m_nbLines2;
break; break;
case ATVModSettings::ATVStdPAL625: // Follows PAL-B/G/H standard case ATVModSettings::ATVStdPAL625: // Follows PAL-B/G/H standard
default: default:
m_nbImageLines = m_nbLines - 14; m_nbImageLines2 = (m_nbLines/2) - 24; // 625 -> 288 per half
m_nbImageLines2 = m_nbImageLines / 2; m_nbImageLines = 2*m_nbImageLines2;
m_imageLineStart1 = 0;
m_imageLineStart2 = 1;
m_interlaced = true; m_interlaced = true;
m_nbSyncLinesHeadE = 4;
m_nbSyncLinesHeadO = 4; // number of sync lines on the top of a frame odd
m_nbSyncLinesBottom = 3;
m_nbLongSyncLines = 2;
m_nbHalfLongSync = 1;
m_nbWholeEqLines = 2;
m_singleLongSync = false;
m_nbBlankLines = 17; // yields 576 lines (305 - 17) * 2
m_blankLineLvel = m_blackLevel; m_blankLineLvel = m_blackLevel;
m_nbLinesField1 = m_nbLines2+1;
} }
m_linesPerVBar = m_nbImageLines2 / m_nbBars; m_linesPerVBar = m_nbImageLines / m_nbBars;
if (m_imageOK) if (m_imageOK)
{ {
@ -713,7 +768,7 @@ void ATVModSource::openVideo(const QString& fileName)
void ATVModSource::resizeImage() void ATVModSource::resizeImage()
{ {
float fy = (m_nbImageLines - 2*m_nbBlankLines) / (float) m_imageOriginal.rows; float fy = (float) m_nbImageLines / (float) m_imageOriginal.rows;
float fx = m_pointsPerImgLine / (float) m_imageOriginal.cols; float fx = m_pointsPerImgLine / (float) m_imageOriginal.cols;
cv::resize(m_imageOriginal, m_image, cv::Size(), fx, fy); cv::resize(m_imageOriginal, m_image, cv::Size(), fx, fy);
qDebug("ATVModSource::resizeImage: %d x %d -> %d x %d", m_imageOriginal.cols, m_imageOriginal.rows, m_image.cols, m_image.rows); qDebug("ATVModSource::resizeImage: %d x %d -> %d x %d", m_imageOriginal.cols, m_imageOriginal.rows, m_image.cols, m_image.rows);
@ -721,7 +776,7 @@ void ATVModSource::resizeImage()
void ATVModSource::calculateVideoSizes() void ATVModSource::calculateVideoSizes()
{ {
m_videoFy = (m_nbImageLines - 2*m_nbBlankLines) / (float) m_videoHeight; m_videoFy = (float) m_nbImageLines / (float) m_videoHeight;
m_videoFx = m_pointsPerImgLine / (float) m_videoWidth; m_videoFx = m_pointsPerImgLine / (float) m_videoWidth;
m_videoFPSq = m_videoFPS / m_fps; m_videoFPSq = m_videoFPS / m_fps;
m_videoFPSCount = m_videoFPSq; m_videoFPSCount = m_videoFPSq;
@ -741,7 +796,7 @@ void ATVModSource::calculateCamerasSizes()
{ {
for (std::vector<ATVCamera>::iterator it = m_cameras.begin(); it != m_cameras.end(); ++it) for (std::vector<ATVCamera>::iterator it = m_cameras.begin(); it != m_cameras.end(); ++it)
{ {
it->m_videoFy = (m_nbImageLines - 2*m_nbBlankLines) / (float) it->m_videoHeight; it->m_videoFy = (float) m_nbImageLines / (float) it->m_videoHeight;
it->m_videoFx = m_pointsPerImgLine / (float) it->m_videoWidth; it->m_videoFx = m_pointsPerImgLine / (float) it->m_videoWidth;
it->m_videoFPSq = it->m_videoFPS / m_fps; it->m_videoFPSq = it->m_videoFPS / m_fps;
it->m_videoFPSqManual = it->m_videoFPSManual / m_fps; it->m_videoFPSqManual = it->m_videoFPSManual / m_fps;

View File

@ -128,6 +128,20 @@ private:
{} {}
}; };
enum LineType
{
LineImage, //!< Full image line
LineImageHalf1Short, //!< Half image line first then short pulse
LineImageHalf1Broad, //!< Half image line first then broad pulse
LineImageHalf2, //!< Half image line first black then image
LineShortPulses, //!< VSync short pulses a.k.a equalizing
LineBroadPulses, //!< VSync broad pulses a.k.a field sync
LineShortBroadPulses, //!< VSync short then broad pulses
LineBroadShortPulses, //!< VSync broad then short pulses
LineShortBlackPulses, //!< VSync short pulse then black
LineBlack, //!< Full black line
};
int m_channelSampleRate; int m_channelSampleRate;
int m_channelFrequencyOffset; int m_channelFrequencyOffset;
ATVModSettings m_settings; ATVModSettings m_settings;
@ -144,31 +158,27 @@ private:
int m_pointsPerBP; //!< number of line points for the back porch int m_pointsPerBP; //!< number of line points for the back porch
int m_pointsPerImgLine; //!< number of line points for the image line int m_pointsPerImgLine; //!< number of line points for the image line
uint32_t m_pointsPerFP; //!< number of line points for the front porch uint32_t m_pointsPerFP; //!< number of line points for the front porch
int m_pointsPerFSync; //!< number of line points for the field first sync - a.k.a Equalizing pulse int m_pointsPerVEqu; //!< number of line points for the equalizing (short) pulse
uint32_t m_pointsPerHBar; //!< number of line points for a bar of the bar chart int m_pointsPerVSync; //!< number of line points for the vertical sync (broad) pulse
uint32_t m_linesPerVBar; //!< number of lines for a bar of the bar chart
uint32_t m_pointsPerTU; //!< number of line points per time unit
int m_nbLines; //!< number of lines per complete frame int m_nbLines; //!< number of lines per complete frame
int m_nbLines2; //!< same number as above (non interlaced) or half the number above (interlaced) int m_nbLines2; //!< same number as above (non interlaced) or Euclidean half the number above (interlaced)
uint32_t m_nbImageLines; //!< number of image lines excluding synchronization lines int m_nbLinesField1; //!< In interlaced mode: number of lines in field1 transition included
uint32_t m_nbImageLines2; //!< same number as above (non interlaced) or half the number above (interlaced) uint32_t m_nbImageLines2; //!< half the number of effective image lines
uint32_t m_nbImageLines; //!< number of effective image lines
uint32_t m_imageLineStart1; //!< start index of line for field 1
uint32_t m_imageLineStart2; //!< start index of line for field 2
int m_nbHorizPoints; //!< number of line points per horizontal line int m_nbHorizPoints; //!< number of line points per horizontal line
int m_nbSyncLinesHeadE; //!< number of header sync lines on even frame
int m_nbSyncLinesHeadO; //!< number of header sync lines on odd frame
int m_nbSyncLinesBottom;//!< number of sync lines at bottom
int m_nbLongSyncLines; //!< number of whole long sync lines for vertical synchronization
int m_nbHalfLongSync; //!< number of half long sync / equalization lines
int m_nbWholeEqLines; //!< number of whole equalizing lines
bool m_singleLongSync; //!< single or double long sync per long sync line
int m_nbBlankLines; //!< number of lines in a frame (full or half) that are blanked (black) at the top of the image
float m_blankLineLvel; //!< video level of blank lines float m_blankLineLvel; //!< video level of blank lines
uint32_t m_pointsPerHBar; //!< number of line points for a bar of the bar chart
float m_hBarIncrement; //!< video level increment at each horizontal bar increment float m_hBarIncrement; //!< video level increment at each horizontal bar increment
uint32_t m_linesPerVBar; //!< number of lines for a bar of the bar chart
float m_vBarIncrement; //!< video level increment at each vertical bar increment float m_vBarIncrement; //!< video level increment at each vertical bar increment
bool m_interlaced; //!< true if image is interlaced (2 half frames per frame) bool m_interlaced; //!< true if image is interlaced (2 half frames per frame)
bool m_evenImage; //!< in interlaced mode true if this is an even image
int m_horizontalCount; //!< current point index on line int m_horizontalCount; //!< current point index on line
int m_lineCount; //!< current line index in frame int m_lineCount; //!< current line index in frame
int m_imageLine; //!< current line index in image
float m_fps; //!< resulting frames per second float m_fps; //!< resulting frames per second
LineType m_lineType; //!< current line type
MovingAverageUtil<double, double, 16> m_movingAverage; MovingAverageUtil<double, double, 16> m_movingAverage;
quint32 m_levelCalcCount; quint32 m_levelCalcCount;
@ -223,6 +233,15 @@ private:
static const int m_nbBars; //!< number of bars in bar or chessboard patterns static const int m_nbBars; //!< number of bars in bar or chessboard patterns
static const int m_cameraFPSTestNbFrames; //!< number of frames for camera FPS test static const int m_cameraFPSTestNbFrames; //!< number of frames for camera FPS test
static const LineType StdPAL625_F1Start[];
static const LineType StdPAL625_F2Start[];
static const LineType StdPAL525_F1Start[];
static const LineType StdPAL525_F2Start[];
static const LineType Std819_F1Start[];
static const LineType Std819_F2Start[];
static const LineType StdShort_F1Start[];
static const LineType StdShort_F2Start[];
void pullFinalize(Complex& ci, Sample& sample); void pullFinalize(Complex& ci, Sample& sample);
void pullVideo(Real& sample); void pullVideo(Real& sample);
void calculateLevel(Real& sample); void calculateLevel(Real& sample);
@ -242,7 +261,185 @@ private:
MessageQueue *getMessageQueueToGUI() { return m_messageQueueToGUI; } MessageQueue *getMessageQueueToGUI() { return m_messageQueueToGUI; }
inline void pullImageLine(Real& sample, bool noHSync = false) inline LineType getLineType(ATVModSettings::ATVStd standard, int lineNumber)
{
if (standard == ATVModSettings::ATVStdHSkip)
{
return LineImage; // all lines are image lines
}
else if (standard == ATVModSettings::ATVStdPAL625) // m_nbLines2 = 312 - fieldLine 0 = 313
{
if (lineNumber < m_nbLines2)
{
if (lineNumber < 5) { // field 1 start (0..4 in line index = line number - 1)
return StdPAL625_F1Start[lineNumber];
} else if (lineNumber < 22) { // field 1 black lines (5..21)
return LineBlack;
} else if (lineNumber == 22) { // field 1 half image line (22)
return LineImageHalf2;
} else if (lineNumber < m_nbLines2 - 2) { // field 1 full image (23..309)
return LineImage;
} else if (lineNumber < m_nbLines2) { // field 1 bottom (310..311)
return LineShortPulses;
}
}
else if (lineNumber == m_nbLines2) // field transition 1 -> 2 (312)
{
return LineShortBroadPulses;
}
else
{
int fieldLine = lineNumber - m_nbLines2 - 1;
if (fieldLine < 5) { // field 2 start (313..(313+5-1=317))
return StdPAL625_F2Start[fieldLine];
} else if (fieldLine < 22) { // field 2 black lines (318..(313+22-1=334))
return LineBlack;
} else if (fieldLine < m_nbLines2 - 3) { // field 2 full image (335..(313+309-1=621))
return LineImage;
} else if (fieldLine == m_nbLines2 - 3) { // field 2 half image line (313+309=622)
return LineImageHalf1Short;
} else { // field 2 bottom (623..624..)
return LineShortPulses;
}
}
}
else if (standard == ATVModSettings::ATVStdPAL525) // m_nbLines2 = 262 - fieldLine 0 = 263
{
if (lineNumber < m_nbLines2)
{
if (lineNumber < 9) { // field 1 start (0..8 in line index)
return StdPAL525_F1Start[lineNumber];
} else if (lineNumber < 20) { // field 1 black lines (9..19)
return LineBlack;
} else if (lineNumber < m_nbLines2) { // field 1 full image (20..261)
return LineImage;
}
}
else if (lineNumber == m_nbLines2) // field transition 1 -> 2 or field 1 half image line (262)
{
return LineImageHalf1Short;
}
else
{
int fieldLine = lineNumber - m_nbLines2 - 1;
if (fieldLine < 9) { // field 2 start (263..(263+9-1=271))
return StdPAL525_F2Start[fieldLine];
} else if (fieldLine < 19) { // field 2 black lines (272..(263+19-1=281))
return LineBlack;
} else if (fieldLine == 19) { // field 2 half image line (263+19=282)
return LineImageHalf2;
} else if (fieldLine < m_nbLines2) { // field 2 full line (283..(263+262-1=524))
return LineImage;
} else { // failsafe: should not get there - same as field 1 start first line
return LineShortPulses;
}
}
}
else if (standard == ATVModSettings::ATVStd819) // Standard F (Belgian) - m_nbLines2 = 409 - fieldLine 0 = 409
{
if (lineNumber < m_nbLines2 - 1)
{
if (lineNumber < 7) { // field 1 start (0..6 in line index)
return Std819_F1Start[lineNumber];
} else if (lineNumber < 26) { // field 1 black lines (7..25)
return LineBlack;
} else if (lineNumber == 26) { // field 1 half image line (26)
return LineImageHalf2;
} else if (lineNumber < m_nbLines2 - 4) { // field 1 full image (27..404)
return LineImage;
} else if (lineNumber < m_nbLines2 - 1) { // field 1 bottom (405..407)
return LineShortPulses;
}
}
else if (lineNumber == m_nbLines2 - 1) // transition field 1 -> 2 (408)
{
return LineShortBroadPulses;
}
else
{
int fieldLine = lineNumber - m_nbLines2;
if (fieldLine < 6) { // field 2 start (409..(409+6-1=414))
return Std819_F2Start[fieldLine];
} else if (fieldLine < 26) { // field 2 black lines (415..(409+26-1=434))
return LineBlack;
} else if (fieldLine < m_nbLines2 - 3) { // field 2 full image (435..(409+406-1=814))
return LineImage;
} else if (fieldLine == m_nbLines2 - 3) { // field 2 half image line (409+406=815)
return LineImageHalf1Short;
} else { // field 2 bottom (816..818..)
return LineShortPulses;
}
}
}
else if (standard == ATVModSettings::ATVStdShortInterlaced)
{
if (lineNumber < m_nbLines2)
{
if (lineNumber < 2) {
return StdShort_F1Start[lineNumber];
} else {
return LineImage;
}
}
else
{
int fieldLine = lineNumber - m_nbLines2;
if (fieldLine < 2) {
return StdShort_F2Start[fieldLine];
} else if (fieldLine < m_nbLines2) {
return LineImage;
} else { // failsafe - will add a black line for odd number of lines
return LineBlack;
}
}
}
else if (standard == ATVModSettings::ATVStdShort)
{
if (lineNumber < 2) {
return StdShort_F2Start[lineNumber];
} else if (lineNumber < m_nbLines) {
return LineImage;
} else {
return LineBlack;
}
}
}
inline void pullImageLastHalfSample(Real& sample)
{
if (m_horizontalCount < m_pointsPerSync + m_pointsPerBP) { // sync
pullImageSample(sample);
} else if (m_horizontalCount < (m_nbHorizPoints/2)) {
sample = m_blackLevel;
} else {
pullImageSample(sample);
}
}
inline void pullImageFirstHalfShortSample(Real& sample)
{
if (m_horizontalCount < (m_nbHorizPoints/2)) {
pullImageSample(sample);
} else {
pullVSyncLineEquPulsesSample(sample);
}
}
inline void pullImageFirstHalfBroadSample(Real& sample)
{
if (m_horizontalCount < (m_nbHorizPoints/2)) {
pullImageSample(sample);
} else {
pullVSyncLineLongPulsesSample(sample);
}
}
inline void pullImageSample(Real& sample, bool noHSync = false)
{ {
if (m_horizontalCount < m_pointsPerSync) // sync pulse if (m_horizontalCount < m_pointsPerSync) // sync pulse
{ {
@ -254,10 +451,13 @@ private:
} }
else if (m_horizontalCount < m_pointsPerSync + m_pointsPerBP + m_pointsPerImgLine) else if (m_horizontalCount < m_pointsPerSync + m_pointsPerBP + m_pointsPerImgLine)
{ {
if (m_imageLine >= m_nbImageLines) // out of image zone
{
sample = m_spanLevel * m_settings.m_uniformLevel + m_blackLevel; // uniform line
return;
}
int pointIndex = m_horizontalCount - (m_pointsPerSync + m_pointsPerBP); int pointIndex = m_horizontalCount - (m_pointsPerSync + m_pointsPerBP);
int oddity = m_lineCount < m_nbLines2 + 1 ? 0 : 1;
int iLine = oddity == 0 ? m_lineCount : m_lineCount - m_nbLines2 - 1;
int iLineImage = iLine - m_nbBlankLines - (oddity == 0 ? m_nbSyncLinesHeadE : m_nbSyncLinesHeadO);
switch(m_settings.m_atvModInput) switch(m_settings.m_atvModInput)
{ {
@ -265,58 +465,46 @@ private:
sample = (pointIndex / m_pointsPerHBar) * m_hBarIncrement + m_blackLevel; sample = (pointIndex / m_pointsPerHBar) * m_hBarIncrement + m_blackLevel;
break; break;
case ATVModSettings::ATVModInputVBars: case ATVModSettings::ATVModInputVBars:
sample = (iLine / m_linesPerVBar) * m_vBarIncrement + m_blackLevel; sample = (m_imageLine / m_linesPerVBar) * m_vBarIncrement + m_blackLevel;
break; break;
case ATVModSettings::ATVModInputChessboard: case ATVModSettings::ATVModInputChessboard:
sample = (((iLine / m_linesPerVBar)*5 + (pointIndex / m_pointsPerHBar)) % 2) * m_spanLevel * m_settings.m_uniformLevel + m_blackLevel; sample = (((m_imageLine / m_linesPerVBar)*5 + (pointIndex / m_pointsPerHBar)) % 2) * m_spanLevel * m_settings.m_uniformLevel + m_blackLevel;
break; break;
case ATVModSettings::ATVModInputHGradient: case ATVModSettings::ATVModInputHGradient:
sample = (pointIndex / (float) m_pointsPerImgLine) * m_spanLevel + m_blackLevel; sample = (pointIndex / (float) m_pointsPerImgLine) * m_spanLevel + m_blackLevel;
break; break;
case ATVModSettings::ATVModInputVGradient: case ATVModSettings::ATVModInputVGradient:
sample = ((iLine -5) / (float) m_nbImageLines2) * m_spanLevel + m_blackLevel; sample = (m_imageLine / (float) m_nbImageLines) * m_spanLevel + m_blackLevel;
break; break;
case ATVModSettings::ATVModInputDiagonal: case ATVModSettings::ATVModInputDiagonal:
sample = pointIndex < (iLine * m_pointsPerImgLine) / m_nbLines2 ? m_blackLevel : m_settings.m_uniformLevel + m_blackLevel; sample = pointIndex < (m_imageLine * m_pointsPerImgLine) / m_nbImageLines ? m_blackLevel : m_settings.m_uniformLevel + m_blackLevel;
break; break;
case ATVModSettings::ATVModInputImage: case ATVModSettings::ATVModInputImage:
if (!m_imageOK || (iLineImage < -oddity) || m_image.empty()) if (!m_imageOK || m_image.empty())
{ {
sample = m_spanLevel * m_settings.m_uniformLevel + m_blackLevel; sample = m_spanLevel * m_settings.m_uniformLevel + m_blackLevel;
} }
else else
{ {
unsigned char pixv; unsigned char pixv;
pixv = m_image.at<unsigned char>(m_imageLine, pointIndex); // row (y), col (x)
if (m_interlaced) {
pixv = m_image.at<unsigned char>(2*iLineImage + oddity, pointIndex); // row (y), col (x)
} else {
pixv = m_image.at<unsigned char>(iLineImage, pointIndex); // row (y), col (x)
}
sample = (pixv / 256.0f) * m_spanLevel + m_blackLevel; sample = (pixv / 256.0f) * m_spanLevel + m_blackLevel;
} }
break; break;
case ATVModSettings::ATVModInputVideo: case ATVModSettings::ATVModInputVideo:
if (!m_videoOK || (iLineImage < -oddity) || m_videoFrame.empty()) if (!m_videoOK || m_videoFrame.empty())
{ {
sample = m_spanLevel * m_settings.m_uniformLevel + m_blackLevel; sample = m_spanLevel * m_settings.m_uniformLevel + m_blackLevel;
} }
else else
{ {
unsigned char pixv; unsigned char pixv;
pixv = m_videoFrame.at<unsigned char>(m_imageLine, pointIndex); // row (y), col (x)
if (m_interlaced) {
pixv = m_videoFrame.at<unsigned char>(2*iLineImage + oddity, pointIndex); // row (y), col (x)
} else {
pixv = m_videoFrame.at<unsigned char>(iLineImage, pointIndex); // row (y), col (x)
}
sample = (pixv / 256.0f) * m_spanLevel + m_blackLevel; sample = (pixv / 256.0f) * m_spanLevel + m_blackLevel;
} }
break; break;
case ATVModSettings::ATVModInputCamera: case ATVModSettings::ATVModInputCamera:
if ((iLineImage < -oddity) || (m_cameraIndex < 0)) if (m_cameraIndex < 0)
{ {
sample = m_spanLevel * m_settings.m_uniformLevel + m_blackLevel; sample = m_spanLevel * m_settings.m_uniformLevel + m_blackLevel;
} }
@ -331,20 +519,14 @@ private:
else else
{ {
unsigned char pixv; unsigned char pixv;
pixv = camera.m_videoFrame.at<unsigned char>(m_imageLine, pointIndex); // row (y), col (x)
if (m_interlaced) {
pixv = camera.m_videoFrame.at<unsigned char>(2*iLineImage + oddity, pointIndex); // row (y), col (x)
} else {
pixv = camera.m_videoFrame.at<unsigned char>(iLineImage, pointIndex); // row (y), col (x)
}
sample = (pixv / 256.0f) * m_spanLevel + m_blackLevel; sample = (pixv / 256.0f) * m_spanLevel + m_blackLevel;
} }
} }
break; break;
case ATVModSettings::ATVModInputUniform: case ATVModSettings::ATVModInputUniform:
default: default:
sample = m_spanLevel * m_settings.m_uniformLevel + m_blackLevel; sample = m_spanLevel * m_settings.m_uniformLevel + m_blackLevel; // uniform line
} }
} }
else // front porch else // front porch
@ -353,149 +535,82 @@ private:
} }
} }
inline void pullVSyncLineLongPulses(Real& sample) inline void pullVSyncLineEquPulsesSample(Real& sample)
{ {
int halfIndex = m_horizontalCount % (m_nbHorizPoints/2); if (m_horizontalCount < m_pointsPerVEqu) {
sample = 0.0f; // ultra-black
if (halfIndex < (m_nbHorizPoints/2) - m_pointsPerSync) // ultra-black } else if (m_horizontalCount < (m_nbHorizPoints/2)) {
{ sample = m_blackLevel; // black
sample = 0.0f; } else if (m_horizontalCount < (m_nbHorizPoints/2) + m_pointsPerVEqu) {
} sample = 0.0f; // ultra-black
else // black
{
if (m_singleLongSync && (m_horizontalCount < m_nbHorizPoints/2)) {
sample = 0.0f;
} else { } else {
sample = m_blackLevel;
}
}
}
inline void pullVSyncLineEqualizingPulses(Real& sample)
{
if (m_horizontalCount < m_pointsPerSync)
{
sample = 0.0f; // ultra-black
}
else if (m_horizontalCount < (m_nbHorizPoints/2))
{
sample = m_blackLevel; // black
}
else if (m_horizontalCount < (m_nbHorizPoints/2) + m_pointsPerFSync)
{
sample = 0.0f; // ultra-black
}
else
{
sample = m_blackLevel; // black sample = m_blackLevel; // black
} }
} }
inline void pullVSyncLineEqualizingThenLongPulses(Real& sample) inline void pullVSyncLineLongPulsesSample(Real& sample)
{
if (m_horizontalCount < m_pointsPerSync)
{ {
if (m_horizontalCount < m_pointsPerVSync) {
sample = 0.0f; // ultra-black sample = 0.0f; // ultra-black
} } else if (m_horizontalCount < (m_nbHorizPoints/2)) {
else if (m_horizontalCount < (m_nbHorizPoints/2))
{
sample = m_blackLevel; // black sample = m_blackLevel; // black
} } else if (m_horizontalCount < (m_nbHorizPoints/2) + m_pointsPerVSync) {
else if (m_horizontalCount < m_nbHorizPoints - m_pointsPerSync)
{
sample = 0.0f; // ultra-black sample = 0.0f; // ultra-black
} } else {
else
{
sample = m_blackLevel; // black sample = m_blackLevel; // black
} }
} }
inline void pullVSyncLineLongThenEqualizingPulses(Real& sample) inline void pullVSyncLineEquLongPulsesSample(Real& sample)
{
if (m_horizontalCount < (m_nbHorizPoints/2) - m_pointsPerSync)
{ {
if (m_horizontalCount < m_pointsPerVEqu) {
sample = 0.0f; // ultra-black sample = 0.0f; // ultra-black
} } else if (m_horizontalCount < (m_nbHorizPoints/2)) {
else if (m_horizontalCount < (m_nbHorizPoints/2))
{
sample = m_blackLevel; // black sample = m_blackLevel; // black
} } else if (m_horizontalCount < (m_nbHorizPoints/2) + m_pointsPerVSync) {
else if (m_horizontalCount < (m_nbHorizPoints/2) + m_pointsPerFSync)
{
sample = 0.0f; // ultra-black sample = 0.0f; // ultra-black
} } else {
else
{
sample = m_blackLevel; // black sample = m_blackLevel; // black
} }
} }
inline void pullVSyncLine(Real& sample) inline void pullVSyncLineEquBlackPulsesSample(Real& sample)
{ {
if (m_lineCount < m_nbLines2 + 1) // even if (m_horizontalCount < m_pointsPerVEqu) {
{ sample = 0.0f; // ultra-black
int fieldLine = m_lineCount; } else {
sample = m_blackLevel; // black
}
}
if (fieldLine < m_nbLongSyncLines) // 0,1: Whole line "long" pulses inline void pullVSyncLineLongEquPulsesSample(Real& sample)
{ {
pullVSyncLineLongPulses(sample); if (m_horizontalCount < m_pointsPerVSync) {
} sample = 0.0f; // ultra-black
else if (fieldLine < m_nbLongSyncLines + m_nbHalfLongSync) // long pulse then equalizing pulse } else if (m_horizontalCount < (m_nbHorizPoints/2)) {
{ sample = m_blackLevel; // black
pullVSyncLineLongThenEqualizingPulses(sample); } else if (m_horizontalCount < (m_nbHorizPoints/2) + m_pointsPerVEqu) {
} sample = 0.0f; // ultra-black
else if (fieldLine < m_nbLongSyncLines + m_nbHalfLongSync + m_nbWholeEqLines) // Whole line equalizing pulses } else {
{ sample = m_blackLevel; // black
pullVSyncLineEqualizingPulses(sample);
}
else if (fieldLine > m_nbLines2 - m_nbHalfLongSync) // equalizing pulse then long pulse
{
pullVSyncLineEqualizingThenLongPulses(sample);
}
else if (fieldLine > m_nbLines2 - m_nbHalfLongSync - m_nbWholeEqLines) // Whole line equalizing pulses
{
pullVSyncLineEqualizingPulses(sample);
}
else // black images
{
if (m_horizontalCount < m_pointsPerSync)
{
sample = 0.0f;
}
else
{
sample = m_blankLineLvel;
} }
} }
}
else // odd
{
int fieldLine = m_lineCount - m_nbLines2 - 1;
if (fieldLine < m_nbLongSyncLines) // 0,1: Whole line "long" pulses inline void pullVSyncLineLongBlackPulsesSample(Real& sample)
{ {
pullVSyncLineLongPulses(sample); if (m_horizontalCount < m_pointsPerVSync) {
} sample = 0.0f; // ultra-black
else if (fieldLine < m_nbLongSyncLines + m_nbWholeEqLines) // Whole line equalizing pulses } else {
{ sample = m_blackLevel; // black
pullVSyncLineEqualizingPulses(sample);
}
else if (fieldLine > m_nbLines2 - 1 - m_nbWholeEqLines - m_nbHalfLongSync) // Whole line equalizing pulses
{
pullVSyncLineEqualizingPulses(sample);
}
else // black images
{
if (m_horizontalCount < m_pointsPerSync)
{
sample = 0.0f;
}
else
{
sample = m_blankLineLvel;
} }
} }
inline void pullBlackLineSample(Real& sample)
{
if (m_horizontalCount < m_pointsPerSync) {
sample = 0.0f; // ultra-black
} else {
sample = m_blackLevel; // black
} }
} }
}; };