public void RegisterSuccessfulTimestamp(int frameNo, IotaVtiTimeStamp oddFieldOSD, IotaVtiTimeStamp evenFieldOSD, DateTime oddFieldTimestamp, DateTime evenFieldTimestamp) { m_PrevFrameNo = frameNo; m_PrevOddTicks = oddFieldTimestamp.Ticks; m_PrevEvenTicks = evenFieldTimestamp.Ticks; m_PrevOddFieldNo = oddFieldOSD.FrameNumber; m_PrevEvenFieldNo = evenFieldOSD.FrameNumber; m_PrevOddFieldOSD = new IotaVtiTimeStamp(oddFieldOSD); m_PrevEvenFieldOSD = new IotaVtiTimeStamp(evenFieldOSD); }
public void TestIotaVtiTimeStamp_FrameNo(string frameNo, int expectedFrameNo) { var str = new IotaVtiTimeStampStrings() { NumSat = ' ', HH = "", MM = "", SS = "", FFFF1 = "", FFFF2 = "", FRAMENO = frameNo }; var ts = new IotaVtiTimeStamp(str); Assert.AreEqual(expectedFrameNo, ts.FrameNumber); }
public void TestIotaVtiTimeStamp_Seconds(string ss, int expectedSeconds) { var str = new IotaVtiTimeStampStrings() { NumSat = ' ', HH = "", MM = "", SS = ss, FFFF1 = "", FFFF2 = "", FRAMENO = "" }; var ts = new IotaVtiTimeStamp(str); Assert.AreEqual(expectedSeconds, ts.Seconds); }
public void TestIotaVtiTimeStamp_Minutes(string mm, int expectedMinutes) { var str = new IotaVtiTimeStampStrings() { NumSat = ' ', HH = "", MM = mm, SS = "", FFFF1 = "", FFFF2 = "", FRAMENO = "" }; var ts = new IotaVtiTimeStamp(str); Assert.AreEqual(expectedMinutes, ts.Minutes); }
public void TestIotaVtiTimeStamp_Hours(string hh, int expectedHours) { var str = new IotaVtiTimeStampStrings() { NumSat = ' ', HH = hh, MM = "", SS = "", FFFF1 = "", FFFF2 = "", FRAMENO = "" }; var ts = new IotaVtiTimeStamp(str); Assert.AreEqual(expectedHours, ts.Hours); }
public void TestIotaVtiTimeStamp_FFF2(string fff2, int expectedFFF) { var str = new IotaVtiTimeStampStrings() { NumSat = ' ', HH = "", MM = "", SS = "", FFFF1 = "", FFFF2 = fff2, FRAMENO = "" }; var ts = new IotaVtiTimeStamp(str); Assert.AreEqual(expectedFFF, ts.Milliseconds10); }
private bool RecognizedTimestampsConsistent(IotaVtiOcrProcessor stateManager, List<CalibratedBlockPosition> normalizedPositions) { var allTimeStamps = new List<IotaVtiTimeStamp>(); int index = 0; int totalTimestamps = normalizedPositions.Count; for (; ; ) { if (index == totalTimestamps - 1) break; IotaVtiTimeStampStrings timeStampStrings = IotaVtiOcrCalibratedState.OcrField(normalizedPositions[index].Image, stateManager, normalizedPositions[index].IsOddField); if (!timeStampStrings.AllCharsPresent()) { return false; } var timeStamp = new IotaVtiTimeStamp(timeStampStrings); if (stateManager.SwapFieldsOrder) { if (index + 1 == totalTimestamps - 1) break; IotaVtiTimeStampStrings timeStampStrings2 = IotaVtiOcrCalibratedState.OcrField(normalizedPositions[index + 1].Image, stateManager, normalizedPositions[index + 1].IsOddField); if (!timeStampStrings2.AllCharsPresent()) { return false; } var timeStamp2 = new IotaVtiTimeStamp(timeStampStrings2); allTimeStamps.Add(timeStamp2); index++; } allTimeStamps.Add(timeStamp); index++; } float fieldDurationMS = 0; for (int i = 0; i < allTimeStamps.Count - 1; i++) { if (allTimeStamps[i].FrameNumber != allTimeStamps[i + 1].FrameNumber - 1 && allTimeStamps[i].FrameNumber != allTimeStamps[i + 1].FrameNumber + 1) return false; int totalMillisecondsThis = (allTimeStamps[i].Hours * 3600 + allTimeStamps[i].Minutes * 60 + allTimeStamps[i].Seconds) * 10000 + allTimeStamps[i].Milliseconds10; int totalMillisecondsNext = (allTimeStamps[i + 1].Hours * 3600 + allTimeStamps[i + 1].Minutes * 60 + allTimeStamps[i + 1].Seconds) * 10000 + allTimeStamps[i + 1].Milliseconds10; fieldDurationMS = Math.Abs((totalMillisecondsNext - totalMillisecondsThis) / 10f); if (Math.Abs(fieldDurationMS - IotaVtiOcrProcessor.FIELD_DURATION_PAL) > 0.15 && Math.Abs(fieldDurationMS - IotaVtiOcrProcessor.FIELD_DURATION_NTSC) > 0.15) return false; } if (Math.Abs(fieldDurationMS - IotaVtiOcrProcessor.FIELD_DURATION_PAL) < 0.15) stateManager.VideoFormat = VideoFormat.PAL; else if (Math.Abs(fieldDurationMS - IotaVtiOcrProcessor.FIELD_DURATION_NTSC) < 0.15) stateManager.VideoFormat = VideoFormat.NTSC; else stateManager.VideoFormat = null; return true; }
internal DateTime ExtractDateTime(int frameNo, int frameStep, IotaVtiTimeStamp oddFieldOSD, IotaVtiTimeStamp evenFieldOSD) { bool failedValidation = false; string failedReason = null; if (oddFieldOSD == null || evenFieldOSD == null) return DateTime.MinValue; if (frameStep != m_FrameStep) { m_FrameStep = frameStep; m_Corrector.Reset(m_Processor.VideoFormat); } if (oddFieldOSD.FrameNumber != evenFieldOSD.FrameNumber - 1 && oddFieldOSD.FrameNumber != evenFieldOSD.FrameNumber + 1) { // Video fields are not consequtive failedValidation = true; failedReason = "Video fields are not consequtive"; } try { DateTime oddFieldTimestamp = new DateTime(1, 1, 1, oddFieldOSD.Hours, oddFieldOSD.Minutes, oddFieldOSD.Seconds).AddMilliseconds(Math.Min(10000, oddFieldOSD.Milliseconds10) / 10.0f); DateTime evenFieldTimestamp = new DateTime(1, 1, 1, evenFieldOSD.Hours, evenFieldOSD.Minutes, evenFieldOSD.Seconds).AddMilliseconds(Math.Min(10000, evenFieldOSD.Milliseconds10) / 10.0f); double fieldDuration = Math.Abs(new TimeSpan(oddFieldTimestamp.Ticks - evenFieldTimestamp.Ticks).TotalMilliseconds); if (m_Processor.VideoFormat.Value == VideoFormat.PAL && (Math.Abs(fieldDuration - IotaVtiOcrProcessor.FIELD_DURATION_PAL) > 1.0)) { // PAL field is not 20 ms failedValidation = true; failedReason = string.Format("PAL field is not 20 ms. It is {0} ms", fieldDuration); } if (m_Processor.VideoFormat.Value == VideoFormat.NTSC && (Math.Abs(fieldDuration - IotaVtiOcrProcessor.FIELD_DURATION_NTSC) > 1.0)) { // NTSC field is not 16.68 ms failedValidation = true; failedReason = string.Format("NTSC field is not 16.68 ms. It is {0} ms", fieldDuration); } int oddEvenFieldDirection = oddFieldTimestamp.Ticks - evenFieldTimestamp.Ticks < 0 ? -1 : 1; if (m_Corrector.OddEvenFieldDirectionIsKnown() && oddEvenFieldDirection != m_Corrector.GetOddEvenFieldDirection()) { // Field timestamps are wrong have changed order (did they swap)? failedValidation = true; failedReason = "Field timestamps are wrong have changed order (did they swap)?"; } if (failedValidation) { string correctionInfo; failedValidation = !m_Corrector.TryToCorrect(frameNo, frameStep, null, oddFieldOSD, evenFieldOSD, m_Processor.EvenBeforeOdd, ref oddFieldTimestamp, ref evenFieldTimestamp, out correctionInfo); failedReason += ". " + correctionInfo; } if (failedValidation) { string errorText = string.Format("OCR ERR: FrameNo: {0}, Odd Timestamp: {1}:{2}:{3}.{4} {5}, Even Timestamp: {6}:{7}:{8}.{9} {10}, {11}", frameNo, oddFieldOSD.Hours, oddFieldOSD.Minutes, oddFieldOSD.Seconds, oddFieldOSD.Milliseconds10, oddFieldOSD.FrameNumber, evenFieldOSD.Hours, evenFieldOSD.Minutes, evenFieldOSD.Seconds, evenFieldOSD.Milliseconds10, evenFieldOSD.FrameNumber, failedReason); Trace.WriteLine(errorText); if (m_CalibrationErrors.Count < 16) { var copy = new List<uint>(); copy.AddRange(m_Processor.CurrentImage); m_CalibrationImages.Add(string.Format("ocrerr_{0}.bmp", frameNo), copy.ToArray()); m_CalibrationErrors.Add(errorText); } if (m_VideoController != null) m_VideoController.RegisterOcrError(); } else m_Corrector.RegisterSuccessfulTimestamp(frameNo, oddFieldOSD, evenFieldOSD, oddFieldTimestamp, evenFieldTimestamp); if (oddFieldOSD.FrameNumber == evenFieldOSD.FrameNumber - 1) { return failedValidation ? DateTime.MinValue : oddFieldTimestamp; } else { return failedValidation ? DateTime.MinValue : evenFieldTimestamp; } } catch(Exception ex) { Trace.WriteLine(ex.GetFullStackTrace()); return DateTime.MinValue; } }
internal DateTime ExtractAAVDateTime(int frameNo, int frameStep, IotaVtiTimeStamp oddFieldOSD, IotaVtiTimeStamp evenFieldOSD) { bool failedValidation = false; string failedReason = null; if (oddFieldOSD == null || evenFieldOSD == null) return DateTime.MinValue; int integratedAavFields = 2 * m_InitializationData.IntegratedAAVFrames; if (oddFieldOSD.FrameNumber != evenFieldOSD.FrameNumber - integratedAavFields + 1 && oddFieldOSD.FrameNumber != evenFieldOSD.FrameNumber + integratedAavFields - 1) { // Video fields are not consequtive failedValidation = true; failedReason = "Integration interval is incomplete"; } try { // | e e | | o o| // |.e.o|.e.o|.e.o|.e.o| |.e.o|.e.o|.e.o|.e.o| DateTime oddFieldTimestamp = new DateTime(1, 1, 1, oddFieldOSD.Hours, oddFieldOSD.Minutes, oddFieldOSD.Seconds).AddMilliseconds(Math.Min(10000, oddFieldOSD.Milliseconds10) / 10.0f); DateTime evenFieldTimestamp = new DateTime(1, 1, 1, evenFieldOSD.Hours, evenFieldOSD.Minutes, evenFieldOSD.Seconds).AddMilliseconds(Math.Min(10000, evenFieldOSD.Milliseconds10) / 10.0f); double integrationPeriodDuration = Math.Abs(new TimeSpan(oddFieldTimestamp.Ticks - evenFieldTimestamp.Ticks).TotalMilliseconds); if (m_Processor.VideoFormat != null) { if (m_Processor.VideoFormat.Value == VideoFormat.PAL) { var calcDuration = (integrationPeriodDuration + IotaVtiOcrProcessor.FIELD_DURATION_PAL) / integratedAavFields; if ((Math.Abs(calcDuration - IotaVtiOcrProcessor.FIELD_DURATION_PAL) > 1.0)) { // PAL field is not 20 ms failedValidation = true; failedReason = string.Format("PAL field is not 20 ms. It is {0} ms", calcDuration); } } if (m_Processor.VideoFormat.Value == VideoFormat.NTSC) { var calcDuration = (integrationPeriodDuration + IotaVtiOcrProcessor.FIELD_DURATION_NTSC) / integratedAavFields; if (Math.Abs(calcDuration - IotaVtiOcrProcessor.FIELD_DURATION_NTSC) > 1.0) { // NTSC field is not 16.68 ms failedValidation = true; failedReason = string.Format("NTSC field is not 16.68 ms. It is {0} ms", calcDuration); } } } if (failedValidation) { string correctionInfo; failedValidation = !m_Corrector.TryToCorrect(frameNo, frameStep, integratedAavFields, oddFieldOSD, evenFieldOSD, m_Processor.EvenBeforeOdd, ref oddFieldTimestamp, ref evenFieldTimestamp, out correctionInfo); failedReason += ". " + correctionInfo; } if (failedValidation) { string errorText = string.Format("OCR ERR: FrameNo: {0}, Odd Timestamp: {1}:{2}:{3}.{4} {5}, Even Timestamp: {6}:{7}:{8}.{9} {10}, {11}", frameNo, oddFieldOSD.Hours, oddFieldOSD.Minutes, oddFieldOSD.Seconds, oddFieldOSD.Milliseconds10, oddFieldOSD.FrameNumber, evenFieldOSD.Hours, evenFieldOSD.Minutes, evenFieldOSD.Seconds, evenFieldOSD.Milliseconds10, evenFieldOSD.FrameNumber, failedReason); Trace.WriteLine(errorText); if (m_CalibrationErrors.Count < 16) { var copy = new List<uint>(); copy.AddRange(m_Processor.CurrentImage); m_CalibrationImages.Add(string.Format("ocrerr_{0}.bmp", frameNo), copy.ToArray()); m_CalibrationErrors.Add(errorText); } if (m_VideoController != null) m_VideoController.RegisterOcrError(); } else m_Corrector.RegisterSuccessfulTimestamp(frameNo, oddFieldOSD, evenFieldOSD, oddFieldTimestamp, evenFieldTimestamp); if (oddFieldOSD.FrameNumber == evenFieldOSD.FrameNumber - integratedAavFields + 1) { return failedValidation ? DateTime.MinValue : oddFieldTimestamp; } else { return failedValidation ? DateTime.MinValue : evenFieldTimestamp; } } catch (Exception ex) { Trace.WriteLine(ex.GetFullStackTrace()); return DateTime.MinValue; } }
public bool TryToCorrect(int frameNo, int frameStep, int? aavIntegratedFields, IotaVtiTimeStamp oddFieldOSD, IotaVtiTimeStamp evenFieldOSD, bool evenBeforeOdd, ref DateTime oddFieldTimestamp, ref DateTime evenFieldTimestamp, out string correctionDebugInfo) { if (m_PrevFrameNo == -1 || m_PrevOddTicks == -1 || m_PrevEvenTicks == -1) { correctionDebugInfo = "Cannot correct. m_PrevFrameNo == -1 || m_PrevOddTicks == -1 || m_PrevEvenTicks == -1"; return false; } correctionDebugInfo = string.Format("IOTA-VTI Correction Attempt for Frame {0}. {1:D2}:{2:D2}:{3:D2}.{4:D4} ({5}) - {6:D2}:{7:D2}:{8:D2}.{9:D4} ({10}). FrameStep: {11}", frameNo, oddFieldOSD.Hours, oddFieldOSD.Minutes, oddFieldOSD.Seconds, oddFieldOSD.Milliseconds10, oddFieldOSD.FrameNumber, evenFieldOSD.Hours, evenFieldOSD.Minutes, evenFieldOSD.Seconds, evenFieldOSD.Milliseconds10, evenFieldOSD.FrameNumber, frameStep); float knownFrameDuration = m_VideoFormat == VideoFormat.PAL ? 2 * IotaVtiOcrProcessor.FIELD_DURATION_PAL : 2 * IotaVtiOcrProcessor.FIELD_DURATION_NTSC; double fieldDuration; if (!evenBeforeOdd) { DateTime oddTimestampToCheck = oddFieldTimestamp; fieldDuration = Math.Abs(new TimeSpan(oddFieldTimestamp.Ticks - m_PrevEvenTicks).TotalMilliseconds) - (frameStep - 1) * knownFrameDuration; if (!IsFieldDurationOkay(fieldDuration)) { if (!TryCorrectTimestamp(m_PrevEvenTicks, oddFieldTimestamp, oddFieldOSD, frameStep, null, aavIntegratedFields != null)) { Trace.WriteLine(correctionDebugInfo); Trace.WriteLine("IOTA-VTI Correction Failed: Cannot correct field duration PrevEven -> CurrOdd."); return false; } else oddTimestampToCheck = new DateTime(1, 1, 1, oddFieldOSD.Hours, oddFieldOSD.Minutes, oddFieldOSD.Seconds).AddMilliseconds(Math.Min(10000, oddFieldOSD.Milliseconds10) / 10.0f); } fieldDuration = Math.Abs(new TimeSpan(oddTimestampToCheck.Ticks - evenFieldTimestamp.Ticks).TotalMilliseconds); if (aavIntegratedFields != null) fieldDuration = fieldDuration / (aavIntegratedFields.Value - 1); if (!IsFieldDurationOkay(fieldDuration)) { if (!TryCorrectTimestamp(oddFieldTimestamp.Ticks, evenFieldTimestamp, evenFieldOSD, 1, aavIntegratedFields, aavIntegratedFields != null)) { Trace.WriteLine(correctionDebugInfo); Trace.WriteLine("IOTA-VTI Correction Failed: Cannot correct field duration CurrOdd -> CurrEven."); return false; } } } else { DateTime evenTimestampToCheck = evenFieldTimestamp; fieldDuration = Math.Abs(new TimeSpan(evenFieldTimestamp.Ticks - m_PrevOddTicks).TotalMilliseconds) - (frameStep - 1) * knownFrameDuration; if (!IsFieldDurationOkay(fieldDuration)) { if (!TryCorrectTimestamp(m_PrevOddTicks, evenFieldTimestamp, evenFieldOSD, frameStep, null, aavIntegratedFields != null)) { Trace.WriteLine(correctionDebugInfo); Trace.WriteLine("IOTA-VTI Correction Failed: Cannot correct field duration PrevOdd -> CurrEven."); return false; } else evenTimestampToCheck = new DateTime(1, 1, 1, evenFieldOSD.Hours, evenFieldOSD.Minutes, evenFieldOSD.Seconds).AddMilliseconds(Math.Min(10000, evenFieldOSD.Milliseconds10) / 10.0f); } fieldDuration = Math.Abs(new TimeSpan(evenTimestampToCheck.Ticks - oddFieldTimestamp.Ticks).TotalMilliseconds); if (aavIntegratedFields != null) fieldDuration = fieldDuration / (aavIntegratedFields.Value - 1); if (!IsFieldDurationOkay(fieldDuration)) { if (!TryCorrectTimestamp(evenFieldTimestamp.Ticks, oddFieldTimestamp, oddFieldOSD, 1, aavIntegratedFields, aavIntegratedFields != null)) { Trace.WriteLine(correctionDebugInfo); Trace.WriteLine("IOTA-VTI Correction Failed: Cannot correct field duration CurrEven -> CurrOdd."); return false; } } } if (!evenBeforeOdd) { if (m_PrevEvenFieldNo + 1 != oddFieldOSD.FrameNumber) { // We don't correct Field numbers as they are not used anywhere for now, we just ignore them as no errors } if (oddFieldOSD.FrameNumber + 1 != evenFieldOSD.FrameNumber) { // We don't correct Field numbers as they are not used anywhere for now, we just ignore them as no errors } } else { if (m_PrevEvenFieldNo + 1 != oddFieldOSD.FrameNumber) { // We don't correct Field numbers as they are not used anywhere for now, we just ignore them as no errors } if (oddFieldOSD.FrameNumber + 1 != evenFieldOSD.FrameNumber) { // We don't correct Field numbers as they are not used anywhere for now, we just ignore them as no errors } } oddFieldTimestamp = new DateTime(1, 1, 1, oddFieldOSD.Hours, oddFieldOSD.Minutes, oddFieldOSD.Seconds).AddMilliseconds(Math.Min(10000, oddFieldOSD.Milliseconds10) / 10.0f); evenFieldTimestamp = new DateTime(1, 1, 1, evenFieldOSD.Hours, evenFieldOSD.Minutes, evenFieldOSD.Seconds).AddMilliseconds(Math.Min(10000, evenFieldOSD.Milliseconds10) / 10.0f); return true; }
private bool TryCorrectTimestamp(long prevFieldTimestamp, DateTime fieldToCorrectTimestamp, IotaVtiTimeStamp fieldToCorrect, int frameStep, int? aavIntegratedFields, bool isAav) { double stepCorrection = frameStep > 1 ? ((20000 * (frameStep - 1) * (m_VideoFormat == VideoFormat.PAL ? IotaVtiOcrProcessor.FIELD_DURATION_PAL : IotaVtiOcrProcessor.FIELD_DURATION_NTSC))) : 0; double fieldDistance = (10000 * (m_VideoFormat == VideoFormat.PAL ? IotaVtiOcrProcessor.FIELD_DURATION_PAL : IotaVtiOcrProcessor.FIELD_DURATION_NTSC)); double aavCorrection = aavIntegratedFields.HasValue ? (10000 * (aavIntegratedFields.Value - 2) * (m_VideoFormat == VideoFormat.PAL ? IotaVtiOcrProcessor.FIELD_DURATION_PAL : IotaVtiOcrProcessor.FIELD_DURATION_NTSC)) : 0; long expectedTimestamp = prevFieldTimestamp + (long)Math.Round(stepCorrection + fieldDistance + aavCorrection); // NOTE: We ignore the last digit from the milliseconds when comparing this timestamps. While this allows for incorectly read 10th of milliseconds to be passed // unactioned, it doesn't create any timing or measurement issues DateTime expectedDateTime = new DateTime(expectedTimestamp); string expectedTimestampString = expectedDateTime.ToString("HH:mm:ss.fff"); string expectedTimestampStringM1 = expectedDateTime.AddMilliseconds(-1).ToString("HH:mm:ss.fff"); string expectedTimestampStringP1 = expectedDateTime.AddMilliseconds(1).ToString("HH:mm:ss.fff"); string actualTimestampString = fieldToCorrectTimestamp.ToString("HH:mm:ss.fff"); string difference = XorStrings(expectedTimestampString, actualTimestampString); long numberDifferences = difference.ToCharArray().Count(c => c != '\0'); string differenceM1 = XorStrings(expectedTimestampStringM1, actualTimestampString); long numberDifferencesM1 = differenceM1.ToCharArray().Count(c => c != '\0'); string differenceP1 = XorStrings(expectedTimestampStringP1, actualTimestampString); long numberDifferencesP1 = differenceP1.ToCharArray().Count(c => c != '\0'); if (numberDifferences < numberDifferencesM1 && numberDifferences < numberDifferencesP1) { // Already correct } else if (numberDifferencesM1 < numberDifferences && numberDifferencesM1 < numberDifferencesP1) { numberDifferences = numberDifferencesM1; expectedDateTime = expectedDateTime.AddMilliseconds(-1); } else if (numberDifferencesP1 < numberDifferences && numberDifferencesP1 < numberDifferencesM1) { numberDifferences = numberDifferencesP1; expectedDateTime = expectedDateTime.AddMilliseconds(1); } if (numberDifferences <= TangraConfig.Settings.Generic.OcrMaxNumberErrorsToAutoCorrect || (isAav && fieldToCorrect.Milliseconds10 == 0) /* Duplicated video field in AAV */) { // We can correct the one or two offending characters fieldToCorrect.Hours = expectedDateTime.Hour; fieldToCorrect.Minutes = expectedDateTime.Minute; fieldToCorrect.Seconds = expectedDateTime.Second; fieldToCorrect.Milliseconds10 = (int)Math.Round((expectedDateTime.Ticks % 10000000) / 1000.0); return true; } else { // Cannot correct more than one differences. Why not?? return false; } }