diff --git a/IR_DecoderRaw.cpp b/IR_DecoderRaw.cpp index a230806..325d52a 100644 --- a/IR_DecoderRaw.cpp +++ b/IR_DecoderRaw.cpp @@ -145,9 +145,19 @@ const __FlashStringHelper *IR_DecoderRaw::rxBriefReasonTag(RxBriefReason reason) 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) { + 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 (reason == RxBriefReason::Ok || reason == RxBriefReason::Preamble) return; @@ -220,8 +230,26 @@ void IR_DecoderRaw::rxBriefLog(RxBriefReason reason, uint16_t a, uint16_t b, uin break; } 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)tUs; @@ -373,10 +401,8 @@ inline void IR_DecoderRaw::checkTimeout() #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() декодер остаётся @@ -487,9 +513,7 @@ void IR_DecoderRaw::processDecodedFront(const FrontStorage ¤tFront) 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 @@ -504,9 +528,7 @@ void IR_DecoderRaw::processDecodedFront(const FrontStorage ¤tFront) 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 @@ -516,9 +538,7 @@ void IR_DecoderRaw::processDecodedFront(const FrontStorage ¤tFront) if (candRp <= riseTimeMax / 4U && !highCount && !lowCount) { errors.other++; -#if IR_RX_BRIEF_LOG rxBriefLog(RxBriefReason::Timing, irClampU16(candRp), 0, currentFront.time); -#endif return; } @@ -582,11 +602,9 @@ void IR_DecoderRaw::processDecodedFront(const FrontStorage ¤tFront) 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; } @@ -752,9 +770,7 @@ void IR_DecoderRaw::writeToBuffer(bool bit, bool packTraceInvertFix) if (i_dataBuffer >= dataByteSizeMax * 8) { // проверка переполнения (>=: иначе при i_dataBuffer==dataByteSizeMax*8 запись dataBuffer[38] за границей массива — B3) 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")); @@ -831,9 +847,7 @@ void IR_DecoderRaw::writeToBuffer(bool bit, bool packTraceInvertFix) #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 @@ -935,12 +949,10 @@ void IR_DecoderRaw::writeToBuffer(bool bit, bool packTraceInvertFix) #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(packSize); @@ -1486,9 +1498,7 @@ void IR_DecoderRaw::pulseFilterPushRaw(const FrontStorage &e) if (pulseFilterHoldCount >= kPulseFilterHoldCap) { pulseFilterDropHoldOverflow++; -#if IR_RX_BRIEF_LOG rxBriefLog(RxBriefReason::HoldOverflow, irClampU16(pulseFilterDropHoldOverflow), 0, e.time); -#endif pulseFilterShiftLeft(1); } @@ -1513,9 +1523,7 @@ bool IR_DecoderRaw::pulseFilterTryTakeConfirmed(FrontStorage &out, uint32_t logT if (dt < minUs) { pulseFilterDropGlitchPairs++; -#if IR_RX_BRIEF_LOG rxBriefLog(RxBriefReason::Glitch, irClampU16(pulseFilterDropGlitchPairs), 0, logTime); -#endif pulseFilterShiftLeft(2); continue; } @@ -1545,9 +1553,7 @@ bool IR_DecoderRaw::pulseFilterTryFlushOne(uint32_t nowUs, FrontStorage &out) if (dt < IR_INPUT_MIN_PULSE_US) { pulseFilterDropGlitchPairs++; -#if IR_RX_BRIEF_LOG rxBriefLog(RxBriefReason::Glitch, irClampU16(pulseFilterDropGlitchPairs), 0, nowUs); -#endif pulseFilterShiftLeft(2); continue; } @@ -1623,9 +1629,7 @@ bool IR_DecoderRaw::preambleProcessEdge(const FrontStorage &front) { if ((uint32_t)(front.time - preambleCandidateLastEdgeTime) > candTimeout) { -#if IR_RX_BRIEF_LOG rxBriefLog(RxBriefReason::Preamble, preambleGoodPeriods, 0, front.time); -#endif preambleStartCandidate(front); } @@ -1646,9 +1650,7 @@ bool IR_DecoderRaw::preambleProcessEdge(const FrontStorage &front) { preambleGoodPeriods = 0; preambleMeanPeriod = 0; -#if IR_RX_BRIEF_LOG rxBriefLog(RxBriefReason::Preamble, 0, irClampU16(period), front.time); -#endif return true; } @@ -1669,9 +1671,7 @@ bool IR_DecoderRaw::preambleProcessEdge(const FrontStorage &front) } else { -#if IR_RX_BRIEF_LOG rxBriefLog(RxBriefReason::Preamble, preambleGoodPeriods, irClampU16(period), front.time); -#endif preambleGoodPeriods = 1; preambleMeanPeriod = (uint16_t)period; } diff --git a/IR_DecoderRaw.h b/IR_DecoderRaw.h index 6cc3dee..a574934 100644 --- a/IR_DecoderRaw.h +++ b/IR_DecoderRaw.h @@ -65,6 +65,14 @@ public: uint16_t edgeTraceFlushChunk(Print &out, uint16_t maxRec = 48); #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 не сошёлся — один раз можно прочитать копию сырых байтов. bool availableReject(); uint8_t getRejectSize() const { return rejectPackSize; } @@ -235,9 +243,13 @@ bool isReciveRaw = false; /// @return Результат 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 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 rxBriefNoteRawOverflowIsr(uint32_t tUs); void rxBriefFlushDeferredIsrLogs();