mirror of
https://github.com/Show-maket/IR-protocol.git
synced 2026-09-18 19:13:58 +00:00
feat(rx): always-on счётчики причин RX-событий (rxReasonCnt) + RXSTAT-сводка
rxBriefLog теперь безусловен: ВСЕГДА инкрементирует rxReasonCnt[reason] (наблюдаемость по контракту живучести — работает в проде без печати), печать события — только при IR_RX_BRIEF_LOG. 14 точек вызова развёрнуты из-под #if (Glitch/Timing/Preamble/Sync/BufOverflow/Timeout/Crc/Ok + pulse-filter пути); ISR-агрегатные MuteBegin/End/RawOverflow остаются только при логе (их флаш живёт в brief-механике). Публичное API: rxReasonCounters() / rxReasonCountersClear() / printRxReasonStats(Print&) -> 'RXSTAT,GLITCH=..,TIME=..,...,OK=..'. Компил-чек: LaserTestCheck (G4, флаг выкл) и Plan_B (F4, флаг вкл) — чисто. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -145,9 +145,19 @@ const __FlashStringHelper *IR_DecoderRaw::rxBriefReasonTag(RxBriefReason reason)
|
|||||||
default: return F("UNK");
|
default: return F("UNK");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif // IR_RX_BRIEF_LOG (rxBriefReasonTag)
|
||||||
|
|
||||||
|
// Безусловна: always-on счёт причин RX-событий (наблюдаемость по контракту
|
||||||
|
// живучести — счётчики живут и в проде без печати); печать — только под флагом.
|
||||||
void IR_DecoderRaw::rxBriefLog(RxBriefReason reason, uint16_t a, uint16_t b, uint32_t tUs)
|
void IR_DecoderRaw::rxBriefLog(RxBriefReason reason, uint16_t a, uint16_t b, uint32_t tUs)
|
||||||
{
|
{
|
||||||
|
const uint8_t ri = (uint8_t)reason;
|
||||||
|
if (ri < 14U)
|
||||||
|
rxReasonCnt[ri]++;
|
||||||
|
#if !IR_RX_BRIEF_LOG
|
||||||
|
(void)a; (void)b; (void)tUs;
|
||||||
|
return;
|
||||||
|
#else
|
||||||
#if IR_RX_BRIEF_LOG_REJECT_ONLY
|
#if IR_RX_BRIEF_LOG_REJECT_ONLY
|
||||||
if (reason == RxBriefReason::Ok || reason == RxBriefReason::Preamble)
|
if (reason == RxBriefReason::Ok || reason == RxBriefReason::Preamble)
|
||||||
return;
|
return;
|
||||||
@ -220,8 +230,26 @@ void IR_DecoderRaw::rxBriefLog(RxBriefReason reason, uint16_t a, uint16_t b, uin
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Serial.println();
|
Serial.println();
|
||||||
|
#endif // IR_RX_BRIEF_LOG (печать)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IR_DecoderRaw::printRxReasonStats(Print &out) const
|
||||||
|
{
|
||||||
|
static const char *const kTags[14] = {"?", "MUTEB", "MUTEE", "QRAW", "QFLT", "HOLD",
|
||||||
|
"GLITCH", "TIME", "PREAMB", "SYNC", "BUF",
|
||||||
|
"TIMEOUT", "CRC", "OK"};
|
||||||
|
out.print(F("RXSTAT"));
|
||||||
|
for (uint8_t i = 1; i < 14U; i++)
|
||||||
|
{
|
||||||
|
out.print(',');
|
||||||
|
out.print(kTags[i]);
|
||||||
|
out.print('=');
|
||||||
|
out.print(rxReasonCnt[i]);
|
||||||
|
}
|
||||||
|
out.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if IR_RX_BRIEF_LOG
|
||||||
void IR_DecoderRaw::rxBriefNoteMuteBlockedIsr(uint32_t tUs)
|
void IR_DecoderRaw::rxBriefNoteMuteBlockedIsr(uint32_t tUs)
|
||||||
{
|
{
|
||||||
(void)tUs;
|
(void)tUs;
|
||||||
@ -373,10 +401,8 @@ inline void IR_DecoderRaw::checkTimeout()
|
|||||||
#if defined(IRDEBUG_SERIAL_PACK)
|
#if defined(IRDEBUG_SERIAL_PACK)
|
||||||
packTraceOnTimeoutOrAbort(false);
|
packTraceOnTimeoutOrAbort(false);
|
||||||
#endif
|
#endif
|
||||||
#if IR_RX_BRIEF_LOG
|
|
||||||
const uint16_t expected = (i_dataBuffer >= 8U) ? uint16_t(dataBuffer[0] & IR_MASK_MSG_INFO) : 0U;
|
const uint16_t expected = (i_dataBuffer >= 8U) ? uint16_t(dataBuffer[0] & IR_MASK_MSG_INFO) : 0U;
|
||||||
rxBriefLog(RxBriefReason::Timeout, i_dataBuffer, expected, micros());
|
rxBriefLog(RxBriefReason::Timeout, i_dataBuffer, expected, micros());
|
||||||
#endif
|
|
||||||
isRecive = false; // приём завершён
|
isRecive = false; // приём завершён
|
||||||
msgTypeReceive = 0;
|
msgTypeReceive = 0;
|
||||||
// Как после listenStart(): без сброса isReciveRaw + firstRX() декодер остаётся
|
// Как после listenStart(): без сброса isReciveRaw + firstRX() декодер остаётся
|
||||||
@ -487,9 +513,7 @@ void IR_DecoderRaw::processDecodedFront(const FrontStorage ¤tFront)
|
|||||||
if (short_low_glitch)
|
if (short_low_glitch)
|
||||||
{
|
{
|
||||||
errors.other++;
|
errors.other++;
|
||||||
#if IR_RX_BRIEF_LOG
|
|
||||||
rxBriefLog(RxBriefReason::Glitch, 1, 0, currentFront.time);
|
rxBriefLog(RxBriefReason::Glitch, 1, 0, currentFront.time);
|
||||||
#endif
|
|
||||||
#if IR_GLITCH_REJECT_PHASE_NUDGE
|
#if IR_GLITCH_REJECT_PHASE_NUDGE
|
||||||
irGlitchPhaseNudge(currentFront.time, riseSyncTime, prevRise);
|
irGlitchPhaseNudge(currentFront.time, riseSyncTime, prevRise);
|
||||||
#endif
|
#endif
|
||||||
@ -504,9 +528,7 @@ void IR_DecoderRaw::processDecodedFront(const FrontStorage ¤tFront)
|
|||||||
if (micro_gap_rise)
|
if (micro_gap_rise)
|
||||||
{
|
{
|
||||||
errors.other++;
|
errors.other++;
|
||||||
#if IR_RX_BRIEF_LOG
|
|
||||||
rxBriefLog(RxBriefReason::Glitch, 1, 0, currentFront.time);
|
rxBriefLog(RxBriefReason::Glitch, 1, 0, currentFront.time);
|
||||||
#endif
|
|
||||||
#if IR_GLITCH_REJECT_PHASE_NUDGE
|
#if IR_GLITCH_REJECT_PHASE_NUDGE
|
||||||
irGlitchPhaseNudge(currentFront.time, riseSyncTime, prevRise);
|
irGlitchPhaseNudge(currentFront.time, riseSyncTime, prevRise);
|
||||||
#endif
|
#endif
|
||||||
@ -516,9 +538,7 @@ void IR_DecoderRaw::processDecodedFront(const FrontStorage ¤tFront)
|
|||||||
if (candRp <= riseTimeMax / 4U && !highCount && !lowCount)
|
if (candRp <= riseTimeMax / 4U && !highCount && !lowCount)
|
||||||
{
|
{
|
||||||
errors.other++;
|
errors.other++;
|
||||||
#if IR_RX_BRIEF_LOG
|
|
||||||
rxBriefLog(RxBriefReason::Timing, irClampU16(candRp), 0, currentFront.time);
|
rxBriefLog(RxBriefReason::Timing, irClampU16(candRp), 0, currentFront.time);
|
||||||
#endif
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -582,11 +602,9 @@ void IR_DecoderRaw::processDecodedFront(const FrontStorage ¤tFront)
|
|||||||
if (risePeriod > IR_timeout || isBufferOverflow || risePeriod < riseTimeMin || isWrongPack)
|
if (risePeriod > IR_timeout || isBufferOverflow || risePeriod < riseTimeMin || isWrongPack)
|
||||||
// ~Мы в пределах таймаута и буффер не переполнен и fix дроблёных единиц
|
// ~Мы в пределах таймаута и буффер не переполнен и fix дроблёных единиц
|
||||||
{
|
{
|
||||||
#if IR_RX_BRIEF_LOG
|
|
||||||
if (!isBufferOverflow && !isWrongPack)
|
if (!isBufferOverflow && !isWrongPack)
|
||||||
rxBriefLog(RxBriefReason::Timing, irClampU16((uint32_t)risePeriod),
|
rxBriefLog(RxBriefReason::Timing, irClampU16((uint32_t)risePeriod),
|
||||||
irClampU16((uint32_t)highTime), currentFront.time);
|
irClampU16((uint32_t)highTime), currentFront.time);
|
||||||
#endif
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -752,9 +770,7 @@ void IR_DecoderRaw::writeToBuffer(bool bit, bool packTraceInvertFix)
|
|||||||
if (i_dataBuffer >= dataByteSizeMax * 8)
|
if (i_dataBuffer >= dataByteSizeMax * 8)
|
||||||
{ // проверка переполнения (>=: иначе при i_dataBuffer==dataByteSizeMax*8 запись dataBuffer[38] за границей массива — B3)
|
{ // проверка переполнения (>=: иначе при i_dataBuffer==dataByteSizeMax*8 запись dataBuffer[38] за границей массива — B3)
|
||||||
isBufferOverflow = true;
|
isBufferOverflow = true;
|
||||||
#if IR_RX_BRIEF_LOG
|
|
||||||
rxBriefLog(RxBriefReason::BufferOverflow, i_dataBuffer, 0, micros());
|
rxBriefLog(RxBriefReason::BufferOverflow, i_dataBuffer, 0, micros());
|
||||||
#endif
|
|
||||||
#if defined(IRDEBUG_SERIAL_PACK)
|
#if defined(IRDEBUG_SERIAL_PACK)
|
||||||
if (packTraceOpen)
|
if (packTraceOpen)
|
||||||
packTraceEmitErrorFlash(F("ERROR: buffer overflow"));
|
packTraceEmitErrorFlash(F("ERROR: buffer overflow"));
|
||||||
@ -831,9 +847,7 @@ void IR_DecoderRaw::writeToBuffer(bool bit, bool packTraceInvertFix)
|
|||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
isWrongPack = true;
|
isWrongPack = true;
|
||||||
#if IR_RX_BRIEF_LOG
|
|
||||||
rxBriefLog(RxBriefReason::Sync, err_syncBit, 0, micros());
|
rxBriefLog(RxBriefReason::Sync, err_syncBit, 0, micros());
|
||||||
#endif
|
|
||||||
#if defined(IRDEBUG_SERIAL_PACK)
|
#if defined(IRDEBUG_SERIAL_PACK)
|
||||||
packTraceEmitErrorFlash(F("ERROR: Wrong sync bit"));
|
packTraceEmitErrorFlash(F("ERROR: Wrong sync bit"));
|
||||||
#endif
|
#endif
|
||||||
@ -935,12 +949,10 @@ void IR_DecoderRaw::writeToBuffer(bool bit, bool packTraceInvertFix)
|
|||||||
#endif
|
#endif
|
||||||
const uint16_t errSum =
|
const uint16_t errSum =
|
||||||
uint16_t(errors.lowSignal) + uint16_t(errors.highSignal) + uint16_t(errors.other);
|
uint16_t(errors.lowSignal) + uint16_t(errors.highSignal) + uint16_t(errors.other);
|
||||||
#if IR_RX_BRIEF_LOG
|
|
||||||
if (isAvailable)
|
if (isAvailable)
|
||||||
rxBriefLog(RxBriefReason::Ok, packSize, errSum, micros());
|
rxBriefLog(RxBriefReason::Ok, packSize, errSum, micros());
|
||||||
else
|
else
|
||||||
rxBriefLog(RxBriefReason::Crc, packSize, errSum, micros());
|
rxBriefLog(RxBriefReason::Crc, packSize, errSum, micros());
|
||||||
#endif
|
|
||||||
if (!isAvailable && packSize > 0 && packSize <= dataByteSizeMax) {
|
if (!isAvailable && packSize > 0 && packSize <= dataByteSizeMax) {
|
||||||
memcpy(rejectBuffer, dataBuffer, packSize);
|
memcpy(rejectBuffer, dataBuffer, packSize);
|
||||||
rejectPackSize = static_cast<uint8_t>(packSize);
|
rejectPackSize = static_cast<uint8_t>(packSize);
|
||||||
@ -1486,9 +1498,7 @@ void IR_DecoderRaw::pulseFilterPushRaw(const FrontStorage &e)
|
|||||||
if (pulseFilterHoldCount >= kPulseFilterHoldCap)
|
if (pulseFilterHoldCount >= kPulseFilterHoldCap)
|
||||||
{
|
{
|
||||||
pulseFilterDropHoldOverflow++;
|
pulseFilterDropHoldOverflow++;
|
||||||
#if IR_RX_BRIEF_LOG
|
|
||||||
rxBriefLog(RxBriefReason::HoldOverflow, irClampU16(pulseFilterDropHoldOverflow), 0, e.time);
|
rxBriefLog(RxBriefReason::HoldOverflow, irClampU16(pulseFilterDropHoldOverflow), 0, e.time);
|
||||||
#endif
|
|
||||||
pulseFilterShiftLeft(1);
|
pulseFilterShiftLeft(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1513,9 +1523,7 @@ bool IR_DecoderRaw::pulseFilterTryTakeConfirmed(FrontStorage &out, uint32_t logT
|
|||||||
if (dt < minUs)
|
if (dt < minUs)
|
||||||
{
|
{
|
||||||
pulseFilterDropGlitchPairs++;
|
pulseFilterDropGlitchPairs++;
|
||||||
#if IR_RX_BRIEF_LOG
|
|
||||||
rxBriefLog(RxBriefReason::Glitch, irClampU16(pulseFilterDropGlitchPairs), 0, logTime);
|
rxBriefLog(RxBriefReason::Glitch, irClampU16(pulseFilterDropGlitchPairs), 0, logTime);
|
||||||
#endif
|
|
||||||
pulseFilterShiftLeft(2);
|
pulseFilterShiftLeft(2);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -1545,9 +1553,7 @@ bool IR_DecoderRaw::pulseFilterTryFlushOne(uint32_t nowUs, FrontStorage &out)
|
|||||||
if (dt < IR_INPUT_MIN_PULSE_US)
|
if (dt < IR_INPUT_MIN_PULSE_US)
|
||||||
{
|
{
|
||||||
pulseFilterDropGlitchPairs++;
|
pulseFilterDropGlitchPairs++;
|
||||||
#if IR_RX_BRIEF_LOG
|
|
||||||
rxBriefLog(RxBriefReason::Glitch, irClampU16(pulseFilterDropGlitchPairs), 0, nowUs);
|
rxBriefLog(RxBriefReason::Glitch, irClampU16(pulseFilterDropGlitchPairs), 0, nowUs);
|
||||||
#endif
|
|
||||||
pulseFilterShiftLeft(2);
|
pulseFilterShiftLeft(2);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -1623,9 +1629,7 @@ bool IR_DecoderRaw::preambleProcessEdge(const FrontStorage &front)
|
|||||||
{
|
{
|
||||||
if ((uint32_t)(front.time - preambleCandidateLastEdgeTime) > candTimeout)
|
if ((uint32_t)(front.time - preambleCandidateLastEdgeTime) > candTimeout)
|
||||||
{
|
{
|
||||||
#if IR_RX_BRIEF_LOG
|
|
||||||
rxBriefLog(RxBriefReason::Preamble, preambleGoodPeriods, 0, front.time);
|
rxBriefLog(RxBriefReason::Preamble, preambleGoodPeriods, 0, front.time);
|
||||||
#endif
|
|
||||||
preambleStartCandidate(front);
|
preambleStartCandidate(front);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1646,9 +1650,7 @@ bool IR_DecoderRaw::preambleProcessEdge(const FrontStorage &front)
|
|||||||
{
|
{
|
||||||
preambleGoodPeriods = 0;
|
preambleGoodPeriods = 0;
|
||||||
preambleMeanPeriod = 0;
|
preambleMeanPeriod = 0;
|
||||||
#if IR_RX_BRIEF_LOG
|
|
||||||
rxBriefLog(RxBriefReason::Preamble, 0, irClampU16(period), front.time);
|
rxBriefLog(RxBriefReason::Preamble, 0, irClampU16(period), front.time);
|
||||||
#endif
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1669,9 +1671,7 @@ bool IR_DecoderRaw::preambleProcessEdge(const FrontStorage &front)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
#if IR_RX_BRIEF_LOG
|
|
||||||
rxBriefLog(RxBriefReason::Preamble, preambleGoodPeriods, irClampU16(period), front.time);
|
rxBriefLog(RxBriefReason::Preamble, preambleGoodPeriods, irClampU16(period), front.time);
|
||||||
#endif
|
|
||||||
preambleGoodPeriods = 1;
|
preambleGoodPeriods = 1;
|
||||||
preambleMeanPeriod = (uint16_t)period;
|
preambleMeanPeriod = (uint16_t)period;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,6 +65,14 @@ public:
|
|||||||
uint16_t edgeTraceFlushChunk(Print &out, uint16_t maxRec = 48);
|
uint16_t edgeTraceFlushChunk(Print &out, uint16_t maxRec = 48);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/// Always-on счётчики RX-событий по причинам (см. RxBriefReason: 6=Glitch,
|
||||||
|
/// 7=Timing, 8=Preamble, 9=Sync, 10=BufOverflow, 11=Timeout, 12=Crc, 13=Ok).
|
||||||
|
/// MuteBegin/End и RawOverflow(1..3) тикают только при IR_RX_BRIEF_LOG (ISR-агрегат).
|
||||||
|
const uint16_t *rxReasonCounters() const { return rxReasonCnt; }
|
||||||
|
void rxReasonCountersClear() { for (uint8_t i = 0; i < 14; i++) rxReasonCnt[i] = 0; }
|
||||||
|
/// Однострочная сводка: "RXSTAT,GLITCH=..,TIME=..,PREAMB=..,SYNC=..,BUF=..,TIMEOUT=..,CRC=..,OK=.."
|
||||||
|
void printRxReasonStats(Print &out) const;
|
||||||
|
|
||||||
/// Кадр собран по длине из заголовка, но CRC не сошёлся — один раз можно прочитать копию сырых байтов.
|
/// Кадр собран по длине из заголовка, но CRC не сошёлся — один раз можно прочитать копию сырых байтов.
|
||||||
bool availableReject();
|
bool availableReject();
|
||||||
uint8_t getRejectSize() const { return rejectPackSize; }
|
uint8_t getRejectSize() const { return rejectPackSize; }
|
||||||
@ -235,9 +243,13 @@ bool isReciveRaw = false;
|
|||||||
/// @return Результат
|
/// @return Результат
|
||||||
uint16_t ceil_div(uint16_t val, uint16_t divider);
|
uint16_t ceil_div(uint16_t val, uint16_t divider);
|
||||||
|
|
||||||
|
// rxBriefLog безусловен: ВСЕГДА инкрементирует rxReasonCnt[reason]
|
||||||
|
// (always-on наблюдаемость по контракту живучести), печать события —
|
||||||
|
// только при IR_RX_BRIEF_LOG. Вызовы в местах отказов тоже безусловны.
|
||||||
|
void rxBriefLog(RxBriefReason reason, uint16_t a = 0, uint16_t b = 0, uint32_t tUs = 0);
|
||||||
|
uint16_t rxReasonCnt[14] = {}; // индекс = (uint8_t)RxBriefReason, 1..13
|
||||||
#if IR_RX_BRIEF_LOG
|
#if IR_RX_BRIEF_LOG
|
||||||
static const __FlashStringHelper *rxBriefReasonTag(RxBriefReason reason);
|
static const __FlashStringHelper *rxBriefReasonTag(RxBriefReason reason);
|
||||||
void rxBriefLog(RxBriefReason reason, uint16_t a = 0, uint16_t b = 0, uint32_t tUs = 0);
|
|
||||||
void rxBriefNoteMuteBlockedIsr(uint32_t tUs);
|
void rxBriefNoteMuteBlockedIsr(uint32_t tUs);
|
||||||
void rxBriefNoteRawOverflowIsr(uint32_t tUs);
|
void rxBriefNoteRawOverflowIsr(uint32_t tUs);
|
||||||
void rxBriefFlushDeferredIsrLogs();
|
void rxBriefFlushDeferredIsrLogs();
|
||||||
|
|||||||
Reference in New Issue
Block a user