Files
IR-protocol/IR_DecoderRaw.cpp

1746 lines
52 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "IR_DecoderRaw.h"
#include "IR_Encoder.h"
#include <cstdio>
#include <cstring>
#if IR_GLITCH_REJECT_PHASE_NUDGE
/** Подтяжка опоры фазы после отброса шипа (как irfoxGlitchPhaseNudgeUs в плагине). */
static inline void irGlitchPhaseNudge(uint32_t edgeTime, uint16_t riseSync, volatile uint32_t &prevRise)
{
if (edgeTime > riseSync)
{
const uint32_t nudged = edgeTime - riseSync;
if (nudged > prevRise && nudged < edgeTime)
prevRise = nudged;
}
}
#endif
static inline uint8_t irPulseFilterHoldbackEdges()
{
uint8_t hb = (uint8_t)IR_INPUT_FILTER_HOLDBACK_EDGES;
if (hb < 2U)
hb = 2U;
if (hb > 4U)
hb = 4U;
return hb;
}
static inline uint16_t irClampU16(uint32_t v)
{
return (v > 0xFFFFU) ? 0xFFFFU : (uint16_t)v;
}
IR_DecoderRaw::IR_DecoderRaw(const uint8_t pin, uint16_t addr, IR_Encoder *encPair) : encoder(encPair)
{
setPin(pin);
id = addr;
prevRise = prevFall = prevPrevFall = prevPrevRise = 0;
if (encPair != nullptr)
{
encPair->decPair = this;
}
#ifdef IRDEBUG
pinMode(wrHigh, OUTPUT);
pinMode(wrLow, OUTPUT);
pinMode(writeOp, OUTPUT);
pinMode(errOut, OUTPUT);
pinMode(up, OUTPUT);
pinMode(down, OUTPUT);
#endif
}
bool IR_DecoderRaw::isSubOverflow()
{
noInterrupts();
volatile bool ret = isSubBufferOverflow;
interrupts();
return ret;
}
bool IR_DecoderRaw::availableRaw()
{
if (isAvailable)
{
isAvailable = false;
return true;
}
else
{
return false;
}
};
void IR_DecoderRaw::pulseFilterResetStats()
{
pulseFilterDropHoldOverflow = 0;
pulseFilterDropGlitchPairs = 0;
}
bool IR_DecoderRaw::registerPairMuteEncoder(IR_Encoder *enc)
{
if (enc == nullptr)
return false;
for (uint8_t i = 0; i < pairMuteEncoderCount; ++i)
{
if (pairMuteEncoders[i] == enc)
return true;
}
if (pairMuteEncoderCount >= IR_PAIR_MUTE_MAX_ENCODERS)
return false;
pairMuteEncoders[pairMuteEncoderCount++] = enc;
return true;
}
void IR_DecoderRaw::refreshPairMuteState()
{
uint16_t active = 0;
for (uint8_t i = 0; i < pairMuteEncoderCount; ++i)
{
IR_Encoder *enc = pairMuteEncoders[i];
if (enc != nullptr && enc->isBusy())
++active;
}
const uint32_t nowUs = micros();
noInterrupts();
const bool wasActive = (isPairSending != 0);
isPairSending = active;
#if IR_RX_BRIEF_LOG
if (!wasActive && active != 0)
{
rxBriefMuteBlockedEdges = 0;
rxBriefMuteBeginPending = true;
rxBriefMuteBeginUs = nowUs;
}
else if (wasActive && active == 0)
{
rxBriefMuteEndPending = true;
rxBriefMuteEndUs = nowUs;
rxBriefMuteEndCount = rxBriefMuteBlockedEdges;
rxBriefMuteBlockedEdges = 0;
}
#endif
interrupts();
}
#if IR_RX_BRIEF_LOG
const __FlashStringHelper *IR_DecoderRaw::rxBriefReasonTag(RxBriefReason reason)
{
switch (reason)
{
case RxBriefReason::MuteBegin: return F("MUTE_BEGIN");
case RxBriefReason::MuteEnd: return F("MUTE_END");
case RxBriefReason::RawOverflow: return F("QRAW");
case RxBriefReason::FilterOverflow: return F("QFLT");
case RxBriefReason::HoldOverflow: return F("HOLD");
case RxBriefReason::Glitch: return F("GLITCH");
case RxBriefReason::Timing: return F("TIME");
case RxBriefReason::Preamble: return F("PREAMB");
case RxBriefReason::Sync: return F("SYNC");
case RxBriefReason::BufferOverflow: return F("BUF");
case RxBriefReason::Timeout: return F("TIMEOUT");
case RxBriefReason::Crc: return F("CRC");
case RxBriefReason::Ok: return F("OK");
default: return F("UNK");
}
}
void IR_DecoderRaw::rxBriefLog(RxBriefReason reason, uint16_t a, uint16_t b, uint32_t tUs)
{
#if IR_RX_BRIEF_LOG_REJECT_ONLY
if (reason == RxBriefReason::Ok || reason == RxBriefReason::Preamble)
return;
#endif
if (tUs == 0U)
tUs = micros();
Serial.print(F("IRRX t="));
Serial.print((unsigned long)tUs);
Serial.print(F(" rsn="));
Serial.print(rxBriefReasonTag(reason));
switch (reason)
{
case RxBriefReason::MuteBegin:
break;
case RxBriefReason::MuteEnd:
case RxBriefReason::RawOverflow:
Serial.print(F(" cnt="));
Serial.print(a);
break;
case RxBriefReason::FilterOverflow:
case RxBriefReason::HoldOverflow:
case RxBriefReason::Glitch:
Serial.print(F(" total="));
Serial.print(a);
break;
case RxBriefReason::Timing:
Serial.print(F(" rp="));
Serial.print(a);
if (b)
{
Serial.print(F(" hp="));
Serial.print(b);
}
break;
case RxBriefReason::Preamble:
Serial.print(F(" good="));
Serial.print(a);
if (b)
{
Serial.print(F(" per="));
Serial.print(b);
}
break;
case RxBriefReason::Sync:
Serial.print(F(" err="));
Serial.print(a);
break;
case RxBriefReason::BufferOverflow:
Serial.print(F(" bits="));
Serial.print(a);
break;
case RxBriefReason::Timeout:
Serial.print(F(" bits="));
Serial.print(a);
if (b)
{
Serial.print(F(" exp="));
Serial.print(b);
}
break;
case RxBriefReason::Crc:
case RxBriefReason::Ok:
Serial.print(F(" len="));
Serial.print(a);
if (b)
{
Serial.print(F(" err="));
Serial.print(b);
}
break;
}
Serial.println();
}
void IR_DecoderRaw::rxBriefNoteMuteBlockedIsr(uint32_t tUs)
{
(void)tUs;
if (rxBriefMuteBlockedEdges != UINT16_MAX)
++rxBriefMuteBlockedEdges;
}
void IR_DecoderRaw::rxBriefNoteRawOverflowIsr(uint32_t tUs)
{
if (rxBriefRawOverflowDrops != UINT16_MAX)
++rxBriefRawOverflowDrops;
rxBriefRawOverflowLastUs = tUs;
}
void IR_DecoderRaw::rxBriefFlushDeferredIsrLogs()
{
bool muteBeginPending = false;
uint32_t muteBeginUs = 0;
bool muteEndPending = false;
uint32_t muteEndUs = 0;
uint16_t muteEndCnt = 0;
uint16_t rawCnt = 0;
uint32_t rawLastUs = 0;
noInterrupts();
muteBeginPending = rxBriefMuteBeginPending;
muteBeginUs = rxBriefMuteBeginUs;
rxBriefMuteBeginPending = false;
rxBriefMuteBeginUs = 0;
muteEndPending = rxBriefMuteEndPending;
muteEndUs = rxBriefMuteEndUs;
muteEndCnt = rxBriefMuteEndCount;
rxBriefMuteEndPending = false;
rxBriefMuteEndUs = 0;
rxBriefMuteEndCount = 0;
rawCnt = rxBriefRawOverflowDrops;
rawLastUs = rxBriefRawOverflowLastUs;
rxBriefRawOverflowDrops = 0;
rxBriefRawOverflowLastUs = 0;
interrupts();
if (muteBeginPending)
rxBriefLog(RxBriefReason::MuteBegin, 0, 0, muteBeginUs);
if (muteEndPending)
rxBriefLog(RxBriefReason::MuteEnd, muteEndCnt, 0, muteEndUs);
if (rawCnt)
rxBriefLog(RxBriefReason::RawOverflow, rawCnt, 0, rawLastUs);
}
#endif
//////////////////////////////////// isr ///////////////////////////////////////////
void IR_DecoderRaw::isr()
{
// Интервалы между соседними фронтами считаются как (uint32_t)(t1 - t0) — корректно при
// паузе < ~35 мин между фронтами; условие «тишина > longSilence» в preambleProcessEdge
// переписано без front.time > prevRise (оно ломается при wrap).
uint32_t t;
noInterrupts();
t = micros();
interrupts();
FrontStorage edge;
edge.dir = port->IDR & mask;
edge.time = t;
#if defined(IR_EDGE_TRACE)
edgeTracePush(edge.time, edge.dir ? 1u : 0u,
isPairSending ? (uint8_t)IR_EDGE_TRACE_F_SKIP_DECODE : 0u);
#endif
if (isPairSending)
{
#if IR_RX_BRIEF_LOG
rxBriefNoteMuteBlockedIsr(edge.time);
#endif
return;
}
if (!subBuffer.push(edge))
{
isSubBufferOverflow = true;
#if IR_RX_BRIEF_LOG
rxBriefNoteRawOverflowIsr(edge.time);
#endif
}
}
////////////////////////////////////////////////////////////////////////////////////
void IR_DecoderRaw::firstRX()
{
#if defined(IRDEBUG_SERIAL_PACK)
packTraceResetFrame();
#endif
errors.reset();
packSize = 0;
isBufferOverflow = false;
isAvailable = false;
bufBitPos = 0;
isData = true;
i_dataBuffer = 0;
nextControlBit = bitPerByte;
i_syncBit = 0;
err_syncBit = 0;
isWrongPack = false;
isPreamb = true;
riseSyncTime = bitTime /* 1100 */;
#ifdef IRDEBUG
wrCounter = 0;
#endif
memset(dataBuffer, 0x00, dataByteSizeMax);
pulseFilterReset();
preambleResetToIdle();
}
bool IR_DecoderRaw::rxTimeoutPipelineBusy() const
{
if (pulseFilterHoldCount != 0U)
return true;
noInterrupts();
const bool busy = !subBuffer.isEmpty();
interrupts();
return busy;
}
void IR_DecoderRaw::listenStart()
{
if (rxTimeoutPipelineBusy())
return;
if (isReciveRaw && ((micros() - lastEdgeTime) > IR_timeout * 2U))
{
#if defined(IRDEBUG_SERIAL_PACK)
packTraceOnTimeoutOrAbort(true);
#endif
isReciveRaw = false;
firstRX();
}
}
// ---- быстрая проверка конца пакета ---------------------------------
inline void IR_DecoderRaw::checkTimeout()
{
if (!isRecive) return; // уже не принимаем нечего проверять
if (rxTimeoutPipelineBusy())
return;
if (micros() - lastEdgeTime > IR_timeout * 2U)
{
#if defined(IRDEBUG_SERIAL_PACK)
packTraceOnTimeoutOrAbort(false);
#endif
#if IR_RX_BRIEF_LOG
const uint16_t expected = (i_dataBuffer >= 8U) ? uint16_t(dataBuffer[0] & IR_MASK_MSG_INFO) : 0U;
rxBriefLog(RxBriefReason::Timeout, i_dataBuffer, expected, micros());
#endif
isRecive = false; // приём завершён
msgTypeReceive = 0;
// Как после listenStart(): без сброса isReciveRaw + firstRX() декодер остаётся
// с «сырым» флагом приёма / Locked и не может заново поймать преамбулу до очень
// длинной тишины (см. preambleProcessEdge Idle и listenStart).
isReciveRaw = false;
firstRX();
// Не подставлять lastEdgeTime = micros(): при хвосте в subBuffer следующий фронт
// имеет edge.time < micros() — откат lastEdgeTime даёт ложный TIMEOUT на следующем tick().
// Повторного checkTimeout при тишине нет: isRecive уже false.
}
}
// ====================================================================
void IR_DecoderRaw::tick()
{
#if IR_RX_BRIEF_LOG
rxBriefFlushDeferredIsrLogs();
#endif
// listenStart/checkTimeout: в конце tick (END) и при отсутствии фронта (ниже).
// Не в начале до pop: иначе после checkTimeout lastEdgeTime vs micros() расходятся
// с метками ISR из очереди → ложные TIMEOUT (bits=0) каждый пакет.
FrontStorage rawFront;
bool hasRawFront = false;
bool processedFront = false;
noInterrupts();
FrontStorage *rawPtr = subBuffer.pop();
if (rawPtr != nullptr)
{
rawFront = *rawPtr;
hasRawFront = true;
}
interrupts();
if (IR_INPUT_MIN_PULSE_US > 0U)
{
if (hasRawFront)
{
pulseFilterPushRaw(rawFront);
FrontStorage confirmedFront;
while (pulseFilterTryTakeConfirmed(confirmedFront))
{
processDecodedFront(confirmedFront);
processedFront = true;
}
}
else
{
const uint32_t nowUs = micros();
FrontStorage confirmedFront;
while (pulseFilterTryFlushOne(nowUs, confirmedFront))
{
processDecodedFront(confirmedFront);
processedFront = true;
}
}
}
else if (hasRawFront)
{
processDecodedFront(rawFront);
processedFront = true;
}
if (!processedFront)
{
isSubBufferOverflow = false;
listenStart();
checkTimeout();
#if defined(IR_EDGE_TRACE)
while (edgeTraceFlushChunk(Serial, 48) > 0) {}
#endif
return;
} // Если данных нет - ничего не делаем
listenStart();
checkTimeout();
#if IR_RX_BRIEF_LOG
rxBriefFlushDeferredIsrLogs();
#endif
#if defined(IR_EDGE_TRACE)
while (edgeTraceFlushChunk(Serial, 48) > 0) {}
#endif
}
void IR_DecoderRaw::processDecodedFront(const FrontStorage &currentFront)
{
if (preambleProcessEdge(currentFront))
{
lastEdgeTime = currentFront.time;
return;
}
lastEdgeTime = currentFront.time; // запоминаем любой фронт
////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (currentFront.dir)
{ // Если __/``` ↑
const uint32_t candRp = currentFront.time - prevRise;
const uint32_t candHt = currentFront.time - prevFall;
const uint32_t candLt = prevFall - prevRise;
#if IR_SHORT_LOW_GLITCH_REJECT
const bool short_low_glitch =
isRecive && !isPreamb && candHt < (riseTimeMin / 8U) && candLt >= riseTimeMin &&
candRp >= riseTimeMin && candRp <= IR_timeout;
if (short_low_glitch)
{
errors.other++;
#if IR_RX_BRIEF_LOG
rxBriefLog(RxBriefReason::Glitch, 1, 0, currentFront.time);
#endif
#if IR_GLITCH_REJECT_PHASE_NUDGE
irGlitchPhaseNudge(currentFront.time, riseSyncTime, prevRise);
#endif
return;
}
#endif
#if IR_MICRO_GAP_RISE_REJECT
const bool micro_gap_cand_lt_ok =
(candLt >= riseTimeMin) || (candLt >= (riseTimeMin / 4U) && candLt < riseTimeMin);
const bool micro_gap_rise = isRecive && !isPreamb && candHt < (riseTimeMin / 8U) && micro_gap_cand_lt_ok &&
candRp >= (riseTimeMin / 4U) && candRp < riseTimeMin && candRp <= IR_timeout;
if (micro_gap_rise)
{
errors.other++;
#if IR_RX_BRIEF_LOG
rxBriefLog(RxBriefReason::Glitch, 1, 0, currentFront.time);
#endif
#if IR_GLITCH_REJECT_PHASE_NUDGE
irGlitchPhaseNudge(currentFront.time, riseSyncTime, prevRise);
#endif
return;
}
#endif
if (candRp <= riseTimeMax / 4U && !highCount && !lowCount)
{
errors.other++;
#if IR_RX_BRIEF_LOG
rxBriefLog(RxBriefReason::Timing, irClampU16(candRp), 0, currentFront.time);
#endif
return;
}
if (candRp > riseTimeMax / 4 || highCount || lowCount)
{ // комплексный фикс рваной единицы
risePeriod = candRp;
highTime = candHt;
lowTime = candLt;
prevRise = currentFront.time;
if (
risePeriod > UINT32_MAX - IR_timeout * 10 ||
highTime > UINT32_MAX - IR_timeout * 10 ||
lowTime > UINT32_MAX - IR_timeout * 10 ||
prevRise > UINT32_MAX - IR_timeout * 10)
{
#ifdef IRDEBUG
errPulse(down, 50);
// Serial.print("\n");
// Serial.print("risePeriod: ");
// Serial.println(risePeriod);
// Serial.print("highTime: ");
// Serial.println(highTime);
// Serial.print("lowTime: ");
// Serial.println(lowTime);
// Serial.print("prevRise: ");
// Serial.println(prevRise);
#endif
}
}
else
{
errors.other++;
}
}
else
{ // Если ```\__ ↓
if (currentFront.time - prevFall > riseTimeMin / 4)
{
prevFall = currentFront.time;
}
else
{
errors.other++;
}
}
#ifdef IRDEBUG
// return; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#endif
//----------------------------------------------------------------------------------
#ifdef IRDEBUG
digitalWrite(errOut, currentFront.dir);
#endif
if (risePeriod > IR_timeout || isBufferOverflow || risePeriod < riseTimeMin || isWrongPack)
// ~Мы в пределах таймаута и буффер не переполнен и fix дроблёных единиц
{
#if IR_RX_BRIEF_LOG
if (!isBufferOverflow && !isWrongPack)
rxBriefLog(RxBriefReason::Timing, irClampU16((uint32_t)risePeriod),
irClampU16((uint32_t)highTime), currentFront.time);
#endif
return;
}
// определить направление фронта
if (currentFront.dir)
{ // Если __/``` ↑
highCount = 0;
lowCount = 0;
allCount = 0;
bool invertErr = false;
// #ifdef IRDEBUG
// Serial.print("\n");
// Serial.print("wrCounter: ");
// Serial.println(wrCounter++);
// Serial.print("risePeriod: ");
// Serial.println(risePeriod);
// Serial.print("highTime: ");
// Serial.println(highTime);
// Serial.print("lowTime: ");
// Serial.println(lowTime);
// #endif
if (aroundRise(risePeriod))
{ // тактирование есть, сигнал хороший - без ошибок(?)
if (highTime > lowTime)
{ // 1
#ifdef IRDEBUG
errPulse(wrHigh, 1);
#endif
writeToBuffer(HIGH);
}
else
{ // 0
#ifdef IRDEBUG
errPulse(wrLow, 1);
#endif
writeToBuffer(LOW);
}
}
else
{ // пропущены такты! сигнал средний // ошибка пропуска
highCount = ceil_div(highTime, riseTime); // предполагаемое колличество HIGH битов
lowCount = ceil_div(lowTime, riseTime); // предполагаемое колличество LOW битов
allCount = ceil_div(risePeriod, riseTime); // предполагаемое колличество всего битов
if (highCount == 0 && highTime > riseTime / 3)
{ // fix короткой единицы (?)после пропуска нулей(?)
highCount++;
errors.other++;
#ifdef IRDEBUG
errPulse(up, 50);
#endif
}
if (lowCount + highCount > allCount)
{ // fix ошибочных сдвигов
if (lowCount > highCount)
{ // Лишние нули
lowCount = allCount - highCount;
errors.lowSignal += lowCount;
#ifdef IRDEBUG
// errPulse(errOut, 3);
errPulse(down, 40);
errPulse(up, 10);
errPulse(down, 40);
#endif
}
else if (lowCount < highCount)
{ // Лишние единицы
highCount = allCount - lowCount;
errors.highSignal += highCount;
#ifdef IRDEBUG
errPulse(down, 10);
errPulse(up, 40);
errPulse(down, 10);
// errPulse(errOut, 4);
#endif
// неизвестный случай Инверсит след бит или соседние
// Очень редко
// TODO: Отловить проверить
}
else if (lowCount == highCount)
{
#ifdef IRDEBUG
errPulse(down, 40);
errPulse(up, 40);
errPulse(down, 40);
#endif
invertErr = true;
// Serial.print("...");
errors.other += allCount;
}
// errorCounter += allCount;
}
// errorCounter += allCount;
// errors.other+=allCount;
if (lowCount < highCount)
{
errors.highSignal += highCount;
}
else
{
errors.lowSignal += lowCount;
}
// errPulse(errOut, 1);
for (int8_t i = 0; i < lowCount && 8 - i; i++)
{ // отправка LOW битов, если есть
if (i == lowCount - 1 && invertErr)
{
invertErr = false;
writeToBuffer(HIGH, true);
#ifdef IRDEBUG
errPulse(wrHigh, 1);
#endif
}
else
{
writeToBuffer(LOW);
#ifdef IRDEBUG
errPulse(wrLow, 1);
#endif
}
}
for (int8_t i = 0; i < highCount && 8 - i; i++)
{ // отправка HIGH битов, если есть
if (i == highCount - 1 && invertErr)
{
invertErr = false;
writeToBuffer(LOW, true);
#ifdef IRDEBUG
errPulse(wrLow, 1);
#endif
}
else
{
writeToBuffer(HIGH);
#ifdef IRDEBUG
errPulse(wrHigh, 1);
#endif
}
}
}
}
else
{ // Если ```\__ ↓
}
}
void IR_DecoderRaw::writeToBuffer(bool bit, bool packTraceInvertFix)
{
#if !defined(IRDEBUG_SERIAL_PACK)
(void)packTraceInvertFix;
#endif
if (i_dataBuffer > dataByteSizeMax * 8)
{ // проверка переполнения
isBufferOverflow = true;
#if IR_RX_BRIEF_LOG
rxBriefLog(RxBriefReason::BufferOverflow, i_dataBuffer, 0, micros());
#endif
#if defined(IRDEBUG_SERIAL_PACK)
if (packTraceOpen)
packTraceEmitErrorFlash(F("ERROR: buffer overflow"));
#endif
}
if (isBufferOverflow || isPreamb || isWrongPack)
{
// Как checkTimeout/listenStart: firstRX() сбрасывает буфер битов, преамбулу и
// pulseFilterReset() — при IR_INPUT_MIN_PULSE_US > 0 иначе остаётся «хвост» в hold/filtered.
isRecive = false;
isReciveRaw = false;
msgTypeReceive = 0;
firstRX();
return;
}
// Переключение флага, data или syncBit
if (bufBitPos == nextControlBit)
{
nextControlBit += (isData ? syncBits : bitPerByte); // маркер следующего переключения
isData = !isData;
i_syncBit = 0; // сброс счетчика битов синхронизации
err_syncBit = 0; // сброс счетчика ошибок синхронизации
}
if (isData)
{ // Запись битов в dataBuffer
dataBuffer[(i_dataBuffer / 8)] |= bit << (7 - i_dataBuffer % 8); // Запись в буффер
i_dataBuffer++;
bufBitPos++;
#if defined(IRDEBUG_SERIAL_PACK)
if (packTraceInvertFix)
{
packTracePushChar('`');
packTracePushChar(bit ? '1' : '0');
packTracePushChar('`');
}
else
packTracePushBit(bit);
#endif
}
else
{
//********************************* Проверка контрольных sync битов *******************************//
////////////////////// Исправление лишнего нуля ///////////////////////
if (i_syncBit == 0)
{ // Первый бит синхронизации
if (bit != (dataBuffer[((i_dataBuffer - 1) / 8)] >> (7 - (i_dataBuffer - 1) % 8) & 1))
{
bufBitPos++;
i_syncBit++;
#if defined(IRDEBUG_SERIAL_PACK)
packTracePushBit(bit);
#endif
}
else
{
i_syncBit = 0;
errors.other++;
err_syncBit++;
const bool fatalSync = (err_syncBit >= syncBits);
if (fatalSync)
{
#if defined(IRDEBUG_SERIAL_PACK) && defined(IRDEBUG_SERIAL_SOFT_REJECT)
if (packTraceSoftReject())
{
packTraceHadWrongSync = true;
packTraceForceEndSyncPhase();
err_syncBit = 0;
i_syncBit = 0;
isWrongPack = false;
}
else
#endif
{
isWrongPack = true;
#if IR_RX_BRIEF_LOG
rxBriefLog(RxBriefReason::Sync, err_syncBit, 0, micros());
#endif
#if defined(IRDEBUG_SERIAL_PACK)
packTraceEmitErrorFlash(F("ERROR: Wrong sync bit"));
#endif
}
}
}
}
else
{ // Последующие биты синхронизации
bufBitPos++;
i_syncBit++;
#if defined(IRDEBUG_SERIAL_PACK)
packTracePushBit(bit);
#endif
}
isWrongPack = (err_syncBit >= syncBits);
} //**************************************************************************************************//
#ifdef IRDEBUG
bit ? infoPulse(writeOp, 2) : infoPulse(writeOp, 1);
#endif
if (!isAvailable && isData && !isWrongPack)
{
if (i_dataBuffer == 8 * msgBytes)
{ // Ппервый байт
packSize = dataBuffer[0] & IR_MASK_MSG_INFO;
}
// Тип приёма (для isReceive): выставляем сразу после первого байта, ДО проверки «Конец».
// Иначе при packSize==1 один и тот же шаг i_dataBuffer==8 одновременно «закрывает» кадр (msgTypeReceive=0)
// и снова выставляет msgTypeReceive ниже — флаг залипает, пока не придёт ошибка/другой кадр.
if (packSize && (i_dataBuffer == 8))
{
msgTypeReceive = (dataBuffer[0] >> 5) | 0b11111000;
}
if (packSize && (i_dataBuffer == packSize * bitPerByte))
{ // Конец
packInfo.buffer = dataBuffer;
packInfo.crc = crcValue;
packInfo.err = errors;
packInfo.packSize = packSize;
packInfo.rTime = riseSyncTime;
isRecive = false;
isReciveRaw = false;
preambleResetToIdle();
msgTypeReceive = 0;
isAvailable = crcCheck(packSize - crcBytes, crcValue);
#ifdef BRUTEFORCE_CHECK
{
uint16_t packTraceBfByte = 0;
uint8_t packTraceBfBit = 0;
bool packTraceBfMark = false;
if (!isAvailable) // Исправление первого бита // Очень большая затычка...
for (size_t i = 0; i < min(uint16_t(packSize - crcBytes * 2U), uint16_t(dataByteSizeMax)); ++i)
{
for (int j = 0; j < 8; ++j)
{
// инвертируем бит
dataBuffer[i] ^= 1 << j;
isAvailable =
crcCheck(min(uint16_t(packSize - crcBytes), uint16_t(dataByteSizeMax - 1U)), crcValue);
// обратно инвертируем бит в исходное состояние
if (isAvailable)
{
packTraceBfByte = static_cast<uint16_t>(i);
packTraceBfBit = static_cast<uint8_t>(j);
packTraceBfMark = true;
goto OUT_BRUTEFORCE;
}
else
{
dataBuffer[i] ^= 1 << j;
}
}
}
OUT_BRUTEFORCE:
#if defined(IRDEBUG_SERIAL_PACK)
if (packTraceBfMark)
packTraceWrapDataBitInBackticks(packTraceBfByte, packTraceBfBit);
#endif
}
#endif
#if defined(IRDEBUG_SERIAL_PACK)
if (isAvailable)
packTraceEmitEndOk(static_cast<uint8_t>(packSize));
else
packTraceEmitEndBadCrc(static_cast<uint8_t>(packSize));
#endif
const uint16_t errSum =
uint16_t(errors.lowSignal) + uint16_t(errors.highSignal) + uint16_t(errors.other);
#if IR_RX_BRIEF_LOG
if (isAvailable)
rxBriefLog(RxBriefReason::Ok, packSize, errSum, micros());
else
rxBriefLog(RxBriefReason::Crc, packSize, errSum, micros());
#endif
if (!isAvailable && packSize > 0 && packSize <= dataByteSizeMax) {
memcpy(rejectBuffer, dataBuffer, packSize);
rejectPackSize = static_cast<uint8_t>(packSize);
isRejectAvailable = true;
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}
bool IR_DecoderRaw::crcCheck(uint8_t len, crc_t &crc)
{
bool crcOK = false;
crc = 0;
crc = (crc8(dataBuffer, 0, len, poly1) << 8) & ~((crc_t)0xFF);
crc |= crc8(dataBuffer, 0, len + 1, poly2) & (crc_t)0xFF;
if (dataBuffer[len] == (crc >> 8) & 0xFF &&
dataBuffer[len + 1] == (crc & 0xFF))
{
crcOK = true;
}
else
{
crcOK = false;
}
return crcOK;
}
bool IR_DecoderRaw::availableReject()
{
if (!isRejectAvailable)
return false;
isRejectAvailable = false;
return true;
}
uint16_t IR_DecoderRaw::ceil_div(uint16_t val, uint16_t divider)
{
int ret = val / divider;
if ((val << 4) / divider - (ret << 4) >= 8)
ret++;
return ret;
}
#if defined(IR_EDGE_TRACE)
void IR_DecoderRaw::edgeTracePush(uint32_t t_us, uint8_t level, uint8_t flags)
{
const uint16_t cap = static_cast<uint16_t>(IR_EDGE_TRACE_CAPACITY);
noInterrupts();
const uint16_t w = edgeTrace_w;
const uint16_t r = edgeTrace_r;
const uint16_t next = static_cast<uint16_t>((w + 1u) % cap);
if (next == r)
{
edgeTrace_overflow = true;
interrupts();
return;
}
edgeTrace_buf[w].t_us = t_us;
edgeTrace_buf[w].level = level ? 1u : 0u;
edgeTrace_buf[w].flags = flags;
edgeTrace_w = next;
interrupts();
}
void IR_DecoderRaw::edgeTraceClear()
{
noInterrupts();
edgeTrace_w = 0;
edgeTrace_r = 0;
edgeTrace_overflow = false;
interrupts();
}
uint16_t IR_DecoderRaw::edgeTracePendingCount() const
{
noInterrupts();
const uint16_t w = edgeTrace_w;
const uint16_t r = edgeTrace_r;
interrupts();
const uint16_t cap = static_cast<uint16_t>(IR_EDGE_TRACE_CAPACITY);
if (w >= r)
return static_cast<uint16_t>(w - r);
return static_cast<uint16_t>(cap - r + w);
}
uint16_t IR_DecoderRaw::edgeTraceFlushChunk(Print &out, uint16_t maxRec)
{
if (maxRec == 0)
maxRec = 48;
constexpr uint16_t kStackCap = 64;
if (maxRec > kStackCap)
maxRec = kStackCap;
const uint16_t cap = static_cast<uint16_t>(IR_EDGE_TRACE_CAPACITY);
noInterrupts();
const uint16_t w = edgeTrace_w;
const uint16_t r = edgeTrace_r;
uint16_t avail = (w >= r) ? static_cast<uint16_t>(w - r) : static_cast<uint16_t>(cap - r + w);
uint16_t toCopy = (avail > maxRec) ? maxRec : avail;
const bool truncated = (avail > toCopy);
if (toCopy == 0)
{
interrupts();
return 0;
}
uint8_t tmp[kStackCap * 6];
for (uint16_t i = 0; i < toCopy; ++i)
{
const uint16_t idx = static_cast<uint16_t>((r + i) % cap);
memcpy(tmp + i * 6u, &edgeTrace_buf[idx], 6u);
}
edgeTrace_r = static_cast<uint16_t>((r + toCopy) % cap);
const bool ovf = edgeTrace_overflow;
interrupts();
uint8_t meta = 0;
if (ovf)
meta |= 0x01u;
if (truncated)
meta |= 0x02u;
uint8_t line[3 + kStackCap * 6];
line[0] = meta;
line[1] = static_cast<uint8_t>(toCopy & 0xFFu);
line[2] = static_cast<uint8_t>((toCopy >> 8) & 0xFFu);
memcpy(line + 3, tmp, toCopy * 6u);
out.print(F("\n@IRF1v1:"));
static const char hd[] = "0123456789abcdef";
const uint16_t lineLen = static_cast<uint16_t>(3u + toCopy * 6u);
for (uint16_t i = 0; i < lineLen; ++i)
{
const uint8_t b = line[i];
out.write(hd[b >> 4]);
out.write(hd[b & 0x0Fu]);
}
out.write('\n');
return toCopy;
}
#endif // IR_EDGE_TRACE
#if defined(IRDEBUG_SERIAL_PACK)
struct IrPackTraceSeg
{
uint8_t isSync;
uint8_t nbits; // число логических бит (данные) или символов синхры
uint8_t nchars; // сырых символов в b (данные: 8…24 из‑за `0`/`1`)
char b[24];
};
namespace {
void ptPrintHexU8(uint8_t v)
{
static const char hd[] = "0123456789ABCDEF";
Serial.print(hd[v >> 4]);
Serial.print(hd[v & 0x0Fu]);
}
/** Тройной пробел перед началом блока: msg→from, from→to, to→data, data→CRC. */
static bool ptRawLeadTriple(uint8_t byteIndex, uint8_t framePs)
{
if (byteIndex == 1 || byteIndex == 3)
return true;
if (framePs > 7 && byteIndex == 5)
return true;
if (framePs >= 7 && byteIndex == static_cast<uint8_t>(framePs - 2))
return true;
return false;
}
/** В IR raw: только 0/1 из flex-сегмента (без `), takeBits логических бит после skipLogical. */
static void ptEmitRawFlexSliceSerial(const char *s, uint8_t nbytes, uint8_t skipLogical, uint8_t takeBits)
{
size_t i = 0;
uint8_t logical = 0;
uint8_t emitted = 0;
while (i < nbytes && emitted < takeBits)
{
if (s[i] == '`' && i + 2u < nbytes && (s[i + 1] == '0' || s[i + 1] == '1') && s[i + 2] == '`')
{
if (logical >= skipLogical)
{
Serial.print(s[i + 1]);
++emitted;
}
i += 3;
++logical;
}
else if (s[i] == '0' || s[i] == '1')
{
if (logical >= skipLogical)
{
Serial.print(s[i]);
++emitted;
}
++i;
++logical;
}
else
break;
}
}
static bool ptTryConsumeFlexDataBit(const char *buf, uint16_t len, uint16_t &pos, IrPackTraceSeg &d)
{
if (pos + 2 < len && buf[pos] == '`' && (buf[pos + 1] == '0' || buf[pos + 1] == '1') && buf[pos + 2] == '`')
{
if (d.nchars + 3u > sizeof(d.b))
return false;
d.b[d.nchars++] = '`';
d.b[d.nchars++] = buf[pos + 1];
d.b[d.nchars++] = '`';
pos = static_cast<uint16_t>(pos + 3u);
return true;
}
if (pos < len && (buf[pos] == '0' || buf[pos] == '1'))
{
if (d.nchars + 1u > sizeof(d.b))
return false;
d.b[d.nchars++] = buf[pos++];
return true;
}
return false;
}
} // namespace
void IR_DecoderRaw::packTraceResetFrame()
{
packTraceOpen = false;
packTraceHadWrongSync = false;
packTraceLen = 0;
packTraceBitBuf[0] = '\0';
}
void IR_DecoderRaw::packTracePushChar(char c)
{
if (packTraceLen + 1u < kPackTraceBufCap)
{
packTraceBitBuf[packTraceLen++] = c;
packTraceBitBuf[packTraceLen] = '\0';
}
}
void IR_DecoderRaw::packTracePushBit(bool bit) { packTracePushChar(bit ? '1' : '0'); }
void IR_DecoderRaw::packTraceWrapDataBitInBackticks(uint16_t byteIndex, uint8_t bitInByte)
{
const uint32_t target = uint32_t(byteIndex) * 8u + bitInByte;
uint32_t dbit = 0;
uint16_t pos = 0;
bool inData = true;
uint16_t len = packTraceLen;
if (len >= kPackTraceBufCap)
len = kPackTraceBufCap - 1u;
while (pos < len)
{
if (inData)
{
uint8_t bitLen = 0;
if (pos + 2 < len && packTraceBitBuf[pos] == '`' && packTraceBitBuf[pos + 2] == '`' &&
(packTraceBitBuf[pos + 1] == '0' || packTraceBitBuf[pos + 1] == '1'))
bitLen = 3;
else if (packTraceBitBuf[pos] == '0' || packTraceBitBuf[pos] == '1')
bitLen = 1;
else
return;
if (dbit == target)
{
if (bitLen == 3)
return;
if (packTraceLen + 2u >= kPackTraceBufCap)
return;
const uint8_t finalBit =
static_cast<uint8_t>((dataBuffer[byteIndex] >> (7u - bitInByte)) & 1u);
const char ch = finalBit ? '1' : '0';
memmove(packTraceBitBuf + pos + 2, packTraceBitBuf + pos, packTraceLen - pos);
packTraceBitBuf[pos] = '`';
packTraceBitBuf[pos + 1] = ch;
packTraceBitBuf[pos + 2] = '`';
packTraceLen += 2;
if (packTraceLen < kPackTraceBufCap)
packTraceBitBuf[packTraceLen] = '\0';
return;
}
++dbit;
pos = static_cast<uint16_t>(pos + bitLen);
if ((dbit % 8u) == 0u)
inData = false;
}
else
{
uint8_t sc = 0;
while (pos < len && sc < syncBits)
{
const char c = packTraceBitBuf[pos];
if (c == '0' || c == '1' || c == '?')
{
++pos;
++sc;
}
else
break;
}
inData = true;
}
}
}
bool IR_DecoderRaw::packTraceSoftReject() const
{
#if defined(IRDEBUG_SERIAL_SOFT_REJECT)
return true;
#else
return false;
#endif
}
void IR_DecoderRaw::packTraceForceEndSyncPhase()
{
for (uint8_t i = 0; i < syncBits; i++)
packTracePushChar('?');
const uint8_t cycLen = bitPerByte + syncBits;
const uint16_t cyc = uint16_t(bufBitPos / cycLen);
bufBitPos = uint16_t((cyc + 1u) * cycLen);
if (bufBitPos == nextControlBit)
{
nextControlBit += (isData ? syncBits : bitPerByte);
isData = !isData;
i_syncBit = 0;
err_syncBit = 0;
}
}
void IR_DecoderRaw::packTraceEmitHex(uint8_t byteCount) const
{
Serial.print(F("IR hex:"));
for (uint8_t i = 0; i < byteCount && i < dataByteSizeMax; i++)
{
Serial.print(' ');
ptPrintHexU8(dataBuffer[i]);
}
Serial.println();
}
void IR_DecoderRaw::packTraceEmitRawBitsLine(bool endWithNewline) const
{
Serial.print(F("IR raw: "));
uint16_t len = packTraceLen;
if (len >= kPackTraceBufCap)
len = kPackTraceBufCap - 1u;
const char *buf = packTraceBitBuf;
uint16_t pos = 0;
uint8_t byteIndex = 0;
bool firstSeg = true;
const uint8_t framePs =
(i_dataBuffer >= 8) ? static_cast<uint8_t>(dataBuffer[0] & IR_MASK_MSG_INFO) : 0;
while (pos < len)
{
IrPackTraceSeg d{};
d.isSync = 0;
while (pos < len && d.nbits < 8)
{
const uint16_t posBefore = pos;
if (!ptTryConsumeFlexDataBit(buf, len, pos, d))
break;
if (pos == posBefore)
break;
++d.nbits;
}
if (!d.nbits)
break;
if (!firstSeg)
{
if (ptRawLeadTriple(byteIndex, framePs))
Serial.print(F(" "));
else
Serial.print(' ');
}
firstSeg = false;
if (d.nbits < 8)
{
ptEmitRawFlexSliceSerial(d.b, d.nchars, 0, d.nbits);
break;
}
if (byteIndex == 0u)
{
ptEmitRawFlexSliceSerial(d.b, d.nchars, 0, 3);
Serial.print(' ');
ptEmitRawFlexSliceSerial(d.b, d.nchars, 3, 5);
}
else
ptEmitRawFlexSliceSerial(d.b, d.nchars, 0, 8);
++byteIndex;
if (pos >= len)
break;
IrPackTraceSeg s{};
s.isSync = 1;
while (pos < len && s.nbits < syncBits)
{
const char c = buf[pos];
if (c != '0' && c != '1' && c != '?')
break;
if (s.nchars >= sizeof(s.b))
break;
s.b[s.nchars++] = c;
++s.nbits;
++pos;
}
if (s.nbits)
{
Serial.print(' ');
for (uint8_t i = 0; i < s.nbits; ++i)
Serial.print(s.b[i]);
}
}
if (endWithNewline)
Serial.println();
}
void IR_DecoderRaw::packTraceEmitErrorFlash(const __FlashStringHelper *msg)
{
Serial.println();
packTraceEmitRawBitsLine(false);
Serial.print(F(" => "));
Serial.println(msg);
{
uint16_t nb = i_dataBuffer / 8u;
if (nb > dataByteSizeMax)
nb = dataByteSizeMax;
packTraceEmitHex(static_cast<uint8_t>(nb));
}
packTraceResetFrame();
}
void IR_DecoderRaw::packTraceEmitEndOk(uint8_t packSize)
{
Serial.println();
packTraceEmitRawBitsLine(false);
Serial.print(F(" => OK: "));
irPackTracePrintOkCommand(dataBuffer, packSize);
Serial.println();
packTraceEmitHex(packSize);
packTraceResetFrame();
}
void IR_DecoderRaw::packTraceEmitEndBadCrc(uint8_t packSize)
{
Serial.println();
packTraceEmitRawBitsLine(false);
Serial.println(F(" => ERROR: Wrong CRC"));
packTraceEmitHex(packSize);
packTraceResetFrame();
}
void IR_DecoderRaw::packTraceOnTimeoutOrAbort(bool fromListenStart)
{
(void)fromListenStart;
if (!packTraceOpen)
return;
const uint16_t expected = (i_dataBuffer >= 8) ? uint16_t(dataBuffer[0] & IR_MASK_MSG_INFO) : 0;
uint16_t gotBytes = i_dataBuffer / 8;
if (gotBytes > dataByteSizeMax)
gotBytes = dataByteSizeMax;
Serial.println();
packTraceEmitRawBitsLine(false);
Serial.print(F(" => ERROR: TIMEOUT, rx_data_size = "));
Serial.print(expected);
Serial.print(F(", but only "));
Serial.print(gotBytes);
Serial.println(F(" bytes received"));
packTraceEmitHex(static_cast<uint8_t>(gotBytes));
packTraceResetFrame();
}
__attribute__((weak)) void irPackTracePrintOkCommand(const uint8_t *buf, uint8_t packSize)
{
(void)buf;
(void)packSize;
}
#endif // IRDEBUG_SERIAL_PACK
uint32_t IR_DecoderRaw::absDiffU32(uint32_t a, uint32_t b)
{
return (a > b) ? (a - b) : (b - a);
}
void IR_DecoderRaw::pulseFilterShiftLeft(uint8_t n)
{
if (n == 0 || pulseFilterHoldCount == 0)
return;
if (n >= pulseFilterHoldCount)
{
pulseFilterHoldCount = 0;
return;
}
const uint8_t newCount = static_cast<uint8_t>(pulseFilterHoldCount - n);
for (uint8_t i = 0; i < newCount; ++i)
pulseFilterHoldEdges[i] = pulseFilterHoldEdges[static_cast<uint8_t>(i + n)];
pulseFilterHoldCount = newCount;
}
void IR_DecoderRaw::pulseFilterReset()
{
pulseFilterHoldCount = 0;
pulseFilterLastRawValid = false;
pulseFilterLastRawTime = 0;
}
void IR_DecoderRaw::pulseFilterPushRaw(const FrontStorage &e)
{
const uint32_t minUs = IR_INPUT_MIN_PULSE_US;
if (minUs == 0U)
{
return;
}
pulseFilterLastRawTime = e.time;
pulseFilterLastRawValid = true;
if (pulseFilterHoldCount >= kPulseFilterHoldCap)
{
pulseFilterDropHoldOverflow++;
#if IR_RX_BRIEF_LOG
rxBriefLog(RxBriefReason::HoldOverflow, irClampU16(pulseFilterDropHoldOverflow), 0, e.time);
#endif
pulseFilterShiftLeft(1);
}
pulseFilterHoldEdges[pulseFilterHoldCount++] = e;
}
bool IR_DecoderRaw::pulseFilterTryTakeConfirmed(FrontStorage &out, uint32_t logTime)
{
const uint32_t minUs = IR_INPUT_MIN_PULSE_US;
if (minUs == 0U)
return false;
const uint8_t holdback = irPulseFilterHoldbackEdges();
if (logTime == 0U)
logTime = pulseFilterLastRawTime;
for (;;)
{
if (pulseFilterHoldCount < 2)
return false;
const uint32_t dt = pulseFilterHoldEdges[1].time - pulseFilterHoldEdges[0].time;
if (dt < minUs)
{
pulseFilterDropGlitchPairs++;
#if IR_RX_BRIEF_LOG
rxBriefLog(RxBriefReason::Glitch, irClampU16(pulseFilterDropGlitchPairs), 0, logTime);
#endif
pulseFilterShiftLeft(2);
continue;
}
if (pulseFilterHoldCount <= holdback)
return false;
out = pulseFilterHoldEdges[0];
pulseFilterShiftLeft(1);
return true;
}
}
bool IR_DecoderRaw::pulseFilterTryFlushOne(uint32_t nowUs, FrontStorage &out)
{
if (IR_INPUT_MIN_PULSE_US == 0U || !pulseFilterLastRawValid || pulseFilterHoldCount == 0)
return false;
const uint32_t waitUs = IR_INPUT_MIN_PULSE_US * (uint32_t)IR_INPUT_FILTER_TIMEOUT_MULT;
if ((uint32_t)(nowUs - pulseFilterLastRawTime) < waitUs)
return false;
while (pulseFilterHoldCount > 0)
{
if (pulseFilterHoldCount >= 2)
{
const uint32_t dt = pulseFilterHoldEdges[1].time - pulseFilterHoldEdges[0].time;
if (dt < IR_INPUT_MIN_PULSE_US)
{
pulseFilterDropGlitchPairs++;
#if IR_RX_BRIEF_LOG
rxBriefLog(RxBriefReason::Glitch, irClampU16(pulseFilterDropGlitchPairs), 0, nowUs);
#endif
pulseFilterShiftLeft(2);
continue;
}
}
out = pulseFilterHoldEdges[0];
pulseFilterShiftLeft(1);
return true;
}
return false;
}
uint32_t IR_DecoderRaw::preambleJitterTolUs(uint32_t baselineUs) const
{
const uint32_t pct = (baselineUs * (uint32_t)IR_PREAMBLE_JITTER_PCT) / 100U;
return (pct > (uint32_t)IR_PREAMBLE_JITTER_US_MIN) ? pct : (uint32_t)IR_PREAMBLE_JITTER_US_MIN;
}
bool IR_DecoderRaw::preambleRisePeriodCoarseOk(uint32_t periodUs) const
{
const uint32_t base = (uint32_t)bitTime;
const uint32_t minP = (base * (uint32_t)IR_PREAMBLE_PERIOD_MIN_FACTOR_PCT) / 100U;
const uint32_t maxP = (base * (uint32_t)IR_PREAMBLE_PERIOD_MAX_FACTOR_PCT) / 100U;
return periodUs >= minP && periodUs <= maxP;
}
void IR_DecoderRaw::preambleResetToIdle()
{
preambleState = PreambleState::Idle;
preambleGoodPeriods = 0;
preambleMeanPeriod = 0;
preambleCandidateLastEdgeTime = 0;
preambleCandidateFirstRiseTime = 0;
preambleCandidateFirstRiseValid = false;
preambFrontCounter = 0;
isPreamb = false;
isWrongPack = false;
isBufferOverflow = false;
}
void IR_DecoderRaw::preambleStartCandidate(const FrontStorage &front)
{
preambleState = PreambleState::Candidate;
preambleGoodPeriods = 0;
preambleMeanPeriod = 0;
preambleCandidateLastEdgeTime = front.time;
preambleCandidateFirstRiseTime = front.time;
preambleCandidateFirstRiseValid = front.dir;
preambFrontCounter = 0;
isPreamb = true;
isRecive = false;
isReciveRaw = false;
}
bool IR_DecoderRaw::preambleProcessEdge(const FrontStorage &front)
{
const uint32_t longSilence = IR_timeout * 2U;
const uint32_t candTimeout = IR_timeout * (uint32_t)IR_PREAMBLE_CANDIDATE_TIMEOUT_MULT;
if (preambleState == PreambleState::Idle)
{
if (isReciveRaw)
return false;
// Не использовать front.time > prevRise: после переполнения micros (~2^32 µs) новое t
// меньше prevRise в unsigned-смысле — преамбула никогда не стартует («залипание» RX).
// prevRise == 0: как раньше — «длинная тишина» только по абсолютному front.time > longSilence.
if (!isReciveRaw && front.dir &&
((prevRise == 0U && front.time > longSilence) ||
(prevRise != 0U && (uint32_t)(front.time - prevRise) > longSilence)))
preambleStartCandidate(front);
}
if (preambleState == PreambleState::Candidate)
{
if ((uint32_t)(front.time - preambleCandidateLastEdgeTime) > candTimeout)
{
#if IR_RX_BRIEF_LOG
rxBriefLog(RxBriefReason::Preamble, preambleGoodPeriods, 0, front.time);
#endif
preambleStartCandidate(front);
}
preambleCandidateLastEdgeTime = front.time;
if (!front.dir)
return true;
if (!preambleCandidateFirstRiseValid)
{
preambleCandidateFirstRiseValid = true;
preambleCandidateFirstRiseTime = front.time;
return true;
}
const uint32_t period = front.time - preambleCandidateFirstRiseTime;
preambleCandidateFirstRiseTime = front.time;
if (!preambleRisePeriodCoarseOk(period))
{
preambleGoodPeriods = 0;
preambleMeanPeriod = 0;
#if IR_RX_BRIEF_LOG
rxBriefLog(RxBriefReason::Preamble, 0, irClampU16(period), front.time);
#endif
return true;
}
if (preambleGoodPeriods == 0)
{
preambleGoodPeriods = 1;
preambleMeanPeriod = (uint16_t)period;
}
else
{
const uint32_t tol = preambleJitterTolUs(preambleMeanPeriod);
if (absDiffU32(period, preambleMeanPeriod) <= tol)
{
if (preambleGoodPeriods < 255U)
++preambleGoodPeriods;
preambleMeanPeriod =
(uint16_t)(((uint32_t)preambleMeanPeriod * 3U + period) / 4U);
}
else
{
#if IR_RX_BRIEF_LOG
rxBriefLog(RxBriefReason::Preamble, preambleGoodPeriods, irClampU16(period), front.time);
#endif
preambleGoodPeriods = 1;
preambleMeanPeriod = (uint16_t)period;
}
}
if (freeFrec)
riseSyncTime = (riseSyncTime + period / 2U) / 2U;
if (preambleGoodPeriods >= kPreambleLockNeed)
{
// Новый кадр обязан стартовать с чистого state, иначе возможны CRC/sync срывы
// при lock без промежуточного listenStart()->firstRX().
errors.reset();
packSize = 0;
isBufferOverflow = false;
isAvailable = false;
bufBitPos = 0;
isData = true;
i_dataBuffer = 0;
nextControlBit = bitPerByte;
i_syncBit = 0;
err_syncBit = 0;
isWrongPack = false;
msgTypeReceive = 0;
memset(dataBuffer, 0x00, dataByteSizeMax);
preambleState = PreambleState::Locked;
isPreamb = false;
isRecive = true;
isReciveRaw = true;
risePeriod = preambleMeanPeriod;
#if defined(IRDEBUG_SERIAL_PACK)
packTraceResetFrame();
packTraceOpen = true;
#endif
#ifdef IRDEBUG
errPulse(up, 50);
errPulse(down, 50);
#endif
prevRise = front.time + preambleMeanPeriod / 2U;
return true;
}
return true;
}
if (preambleState == PreambleState::Locked)
{
if (!isReciveRaw)
preambleResetToIdle();
return false;
}
return !isReciveRaw;
}
// IRDEBUG FUNC
#ifdef IRDEBUG
inline void IR_DecoderRaw::errPulse(uint8_t pin, uint8_t count)
{
for (size_t i = 0; i < count; i++)
{
digitalWrite(pin, 1);
digitalWrite(pin, 0);
}
digitalWrite(pin, 0);
}
inline void IR_DecoderRaw::infoPulse(uint8_t pin, uint8_t count)
{
for (size_t i = 0; i < count; i++)
{
digitalWrite(pin, 1);
digitalWrite(pin, 0);
}
}
#endif