mirror of
https://github.com/Show-maket/IR-protocol.git
synced 2026-09-21 20:39:35 +00:00
Make IR timing and RX terminal state explicit
This commit is contained in:
@ -374,15 +374,45 @@ bool IR_DecoderRaw::rxTimeoutPipelineBusy() const
|
||||
return busy;
|
||||
}
|
||||
|
||||
bool IR_DecoderRaw::rxPipelineActive() const
|
||||
{
|
||||
return rxLineActive() || rxTimeoutPipelineBusy();
|
||||
}
|
||||
|
||||
uint8_t IR_DecoderRaw::currentRxMsgType() const
|
||||
{
|
||||
if (i_dataBuffer < static_cast<uint16_t>(msgBytes) * bitPerByte)
|
||||
return 0xFFU;
|
||||
return static_cast<uint8_t>((dataBuffer[0] >> 5U) & IR_MASK_MSG_TYPE);
|
||||
}
|
||||
|
||||
void IR_DecoderRaw::noteRxTerminal(IR_RxTerminalReason reason, uint8_t msgType, bool hadLock)
|
||||
{
|
||||
++rxTerminalInfo.seq;
|
||||
rxTerminalInfo.reason = reason;
|
||||
rxTerminalInfo.msgType = msgType;
|
||||
rxTerminalInfo.hadLock = hadLock;
|
||||
}
|
||||
|
||||
void IR_DecoderRaw::listenStart()
|
||||
{
|
||||
if (rxTimeoutPipelineBusy())
|
||||
return;
|
||||
if (isReciveRaw && ((micros() - lastEdgeTime) > IR_timeout * 2U))
|
||||
const uint32_t nowUs = micros();
|
||||
if (isReciveRaw && ((nowUs - lastEdgeTime) > IR_timeout * 2U))
|
||||
{
|
||||
#if defined(IRDEBUG_SERIAL_PACK)
|
||||
packTraceOnTimeoutOrAbort(true);
|
||||
#endif
|
||||
if (isRecive)
|
||||
{
|
||||
const uint16_t expected =
|
||||
(i_dataBuffer >= 8U) ? uint16_t(dataBuffer[0] & IR_MASK_MSG_INFO) : 0U;
|
||||
rxBriefLog(RxBriefReason::Timeout, i_dataBuffer, expected, nowUs);
|
||||
noteRxTerminal(IR_RxTerminalReason::LockedTimeout, currentRxMsgType(), true);
|
||||
isRecive = false;
|
||||
msgTypeReceive = 0;
|
||||
}
|
||||
isReciveRaw = false;
|
||||
firstRX();
|
||||
}
|
||||
@ -403,6 +433,7 @@ inline void IR_DecoderRaw::checkTimeout()
|
||||
#endif
|
||||
const uint16_t expected = (i_dataBuffer >= 8U) ? uint16_t(dataBuffer[0] & IR_MASK_MSG_INFO) : 0U;
|
||||
rxBriefLog(RxBriefReason::Timeout, i_dataBuffer, expected, micros());
|
||||
noteRxTerminal(IR_RxTerminalReason::LockedTimeout, currentRxMsgType(), true);
|
||||
isRecive = false; // приём завершён
|
||||
msgTypeReceive = 0;
|
||||
// Как после listenStart(): без сброса isReciveRaw + firstRX() декодер остаётся
|
||||
@ -473,6 +504,7 @@ void IR_DecoderRaw::tick()
|
||||
isSubBufferOverflow = false;
|
||||
listenStart();
|
||||
checkTimeout();
|
||||
expirePreambleCandidateIfIdle(micros());
|
||||
#if defined(IR_EDGE_TRACE)
|
||||
while (edgeTraceFlushChunk(Serial, 48) > 0) {}
|
||||
#endif
|
||||
@ -480,6 +512,7 @@ void IR_DecoderRaw::tick()
|
||||
} // Если данных нет - ничего не делаем
|
||||
listenStart();
|
||||
checkTimeout();
|
||||
expirePreambleCandidateIfIdle(micros());
|
||||
#if IR_RX_BRIEF_LOG
|
||||
rxBriefFlushDeferredIsrLogs();
|
||||
#endif
|
||||
@ -778,6 +811,13 @@ void IR_DecoderRaw::writeToBuffer(bool bit, bool packTraceInvertFix)
|
||||
}
|
||||
if (isBufferOverflow || isPreamb || isWrongPack)
|
||||
{
|
||||
const bool hadLock =
|
||||
isRecive || isReciveRaw || preambleState == PreambleState::Locked;
|
||||
const bool wasObservable =
|
||||
hadLock ||
|
||||
(preambleState == PreambleState::Candidate && preambleWasObservable);
|
||||
if (wasObservable)
|
||||
noteRxTerminal(IR_RxTerminalReason::DecodeAbort, currentRxMsgType(), hadLock);
|
||||
// Как checkTimeout/listenStart: firstRX() сбрасывает буфер битов, преамбулу и
|
||||
// pulseFilterReset() — при IR_INPUT_MIN_PULSE_US > 0 иначе остаётся «хвост» в hold/filtered.
|
||||
isRecive = false;
|
||||
@ -941,6 +981,9 @@ void IR_DecoderRaw::writeToBuffer(bool bit, bool packTraceInvertFix)
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
noteRxTerminal(isAvailable ? IR_RxTerminalReason::FrameOk
|
||||
: IR_RxTerminalReason::FrameCrcError,
|
||||
currentRxMsgType(), true);
|
||||
#if defined(IRDEBUG_SERIAL_PACK)
|
||||
if (isAvailable)
|
||||
packTraceEmitEndOk(static_cast<uint8_t>(packSize));
|
||||
@ -1583,6 +1626,7 @@ void IR_DecoderRaw::preambleResetToIdle()
|
||||
{
|
||||
preambleState = PreambleState::Idle;
|
||||
preambleGoodPeriods = 0;
|
||||
preambleWasObservable = false;
|
||||
preambleMeanPeriod = 0;
|
||||
preambleCandidateLastEdgeTime = 0;
|
||||
preambleCandidateFirstRiseTime = 0;
|
||||
@ -1597,6 +1641,10 @@ void IR_DecoderRaw::preambleStartCandidate(const FrontStorage &front)
|
||||
{
|
||||
preambleState = PreambleState::Candidate;
|
||||
preambleGoodPeriods = 0;
|
||||
// The first post-silence rise already opens a potential frame epoch.
|
||||
// Keep the line busy until that epoch locks or expires after real silence:
|
||||
// even a badly distorted response may contain no coarse-valid rise period.
|
||||
preambleWasObservable = true;
|
||||
preambleMeanPeriod = 0;
|
||||
preambleCandidateLastEdgeTime = front.time;
|
||||
preambleCandidateFirstRiseTime = front.time;
|
||||
@ -1607,6 +1655,24 @@ void IR_DecoderRaw::preambleStartCandidate(const FrontStorage &front)
|
||||
isReciveRaw = false;
|
||||
}
|
||||
|
||||
void IR_DecoderRaw::expirePreambleCandidateIfIdle(uint32_t nowUs)
|
||||
{
|
||||
if (preambleState != PreambleState::Candidate || rxTimeoutPipelineBusy())
|
||||
return;
|
||||
|
||||
const uint32_t candTimeout =
|
||||
IR_timeout * static_cast<uint32_t>(IR_PREAMBLE_CANDIDATE_TIMEOUT_MULT);
|
||||
if ((uint32_t)(nowUs - preambleCandidateLastEdgeTime) <= candTimeout)
|
||||
return;
|
||||
|
||||
const uint8_t goodPeriods = preambleGoodPeriods;
|
||||
const bool wasObservable = preambleWasObservable;
|
||||
rxBriefLog(RxBriefReason::Preamble, goodPeriods, 0, nowUs);
|
||||
preambleResetToIdle();
|
||||
if (wasObservable)
|
||||
noteRxTerminal(IR_RxTerminalReason::CandidateTimeout, 0xFFU, false);
|
||||
}
|
||||
|
||||
bool IR_DecoderRaw::preambleProcessEdge(const FrontStorage &front)
|
||||
{
|
||||
const uint32_t longSilence = IR_timeout * 2U;
|
||||
@ -1622,7 +1688,10 @@ bool IR_DecoderRaw::preambleProcessEdge(const FrontStorage &front)
|
||||
if (!isReciveRaw && front.dir &&
|
||||
((prevRise == 0U && front.time > longSilence) ||
|
||||
(prevRise != 0U && (uint32_t)(front.time - prevRise) > longSilence)))
|
||||
{
|
||||
preambleStartCandidate(front);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (preambleState == PreambleState::Candidate)
|
||||
@ -1630,7 +1699,10 @@ bool IR_DecoderRaw::preambleProcessEdge(const FrontStorage &front)
|
||||
if ((uint32_t)(front.time - preambleCandidateLastEdgeTime) > candTimeout)
|
||||
{
|
||||
rxBriefLog(RxBriefReason::Preamble, preambleGoodPeriods, 0, front.time);
|
||||
if (preambleWasObservable)
|
||||
noteRxTerminal(IR_RxTerminalReason::CandidateTimeout, 0xFFU, false);
|
||||
preambleStartCandidate(front);
|
||||
return true;
|
||||
}
|
||||
|
||||
preambleCandidateLastEdgeTime = front.time;
|
||||
@ -1648,15 +1720,20 @@ bool IR_DecoderRaw::preambleProcessEdge(const FrontStorage &front)
|
||||
preambleCandidateFirstRiseTime = front.time;
|
||||
if (!preambleRisePeriodCoarseOk(period))
|
||||
{
|
||||
rxBriefLog(RxBriefReason::Preamble, preambleGoodPeriods,
|
||||
irClampU16(period), front.time);
|
||||
preambleGoodPeriods = 0;
|
||||
preambleMeanPeriod = 0;
|
||||
rxBriefLog(RxBriefReason::Preamble, 0, irClampU16(period), front.time);
|
||||
// Keep preambleWasObservable sticky: this edge proves the medium is
|
||||
// still active, but not that a possible physical frame has ended.
|
||||
// Only silence timeout or a real locked terminal releases it.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (preambleGoodPeriods == 0)
|
||||
{
|
||||
preambleGoodPeriods = 1;
|
||||
preambleWasObservable = true;
|
||||
preambleMeanPeriod = (uint16_t)period;
|
||||
}
|
||||
else
|
||||
@ -1673,6 +1750,7 @@ bool IR_DecoderRaw::preambleProcessEdge(const FrontStorage &front)
|
||||
{
|
||||
rxBriefLog(RxBriefReason::Preamble, preambleGoodPeriods, irClampU16(period), front.time);
|
||||
preambleGoodPeriods = 1;
|
||||
preambleWasObservable = true;
|
||||
preambleMeanPeriod = (uint16_t)period;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user