mirror of
https://github.com/Show-maket/IR-protocol.git
synced 2026-04-28 03:08:08 +00:00
Filters and stable
This commit is contained in:
@ -3,6 +3,29 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
IR_DecoderRaw::IR_DecoderRaw(const uint8_t pin, uint16_t addr, IR_Encoder *encPair) : encoder(encPair)
|
||||
{
|
||||
setPin(pin);
|
||||
@ -44,6 +67,13 @@ bool IR_DecoderRaw::availableRaw()
|
||||
}
|
||||
};
|
||||
|
||||
void IR_DecoderRaw::pulseFilterResetStats()
|
||||
{
|
||||
pulseFilterDropFilteredOverflow = 0;
|
||||
pulseFilterDropHoldOverflow = 0;
|
||||
pulseFilterDropGlitchPairs = 0;
|
||||
}
|
||||
|
||||
//////////////////////////////////// isr ///////////////////////////////////////////
|
||||
volatile uint32_t time_;
|
||||
|
||||
@ -101,11 +131,13 @@ void IR_DecoderRaw::firstRX()
|
||||
wrCounter = 0;
|
||||
#endif
|
||||
memset(dataBuffer, 0x00, dataByteSizeMax);
|
||||
pulseFilterReset();
|
||||
preambleResetToIdle();
|
||||
}
|
||||
|
||||
void IR_DecoderRaw::listenStart()
|
||||
{
|
||||
if (isReciveRaw && ((micros() - prevRise) > IR_timeout * 2))
|
||||
if (isReciveRaw && ((micros() - lastEdgeTime) > IR_timeout * 2U))
|
||||
{
|
||||
#if defined(IRDEBUG_SERIAL_PACK)
|
||||
packTraceOnTimeoutOrAbort(true);
|
||||
@ -136,49 +168,119 @@ inline void IR_DecoderRaw::checkTimeout()
|
||||
|
||||
void IR_DecoderRaw::tick()
|
||||
{
|
||||
// FrontStorage *currentFrontPtr;
|
||||
// noInterrupts();
|
||||
// currentFrontPtr = subBuffer.pop();
|
||||
// interrupts();
|
||||
|
||||
FrontStorage currentFront;
|
||||
bool hasCurrentFront = false;
|
||||
FrontStorage rawFront;
|
||||
bool hasRawFront = false;
|
||||
noInterrupts();
|
||||
listenStart();
|
||||
FrontStorage *currentFrontPtr;
|
||||
currentFrontPtr = subBuffer.pop();
|
||||
if (currentFrontPtr == nullptr)
|
||||
FrontStorage *rawPtr = subBuffer.pop();
|
||||
if (rawPtr != nullptr)
|
||||
{
|
||||
rawFront = *rawPtr;
|
||||
hasRawFront = true;
|
||||
}
|
||||
interrupts();
|
||||
|
||||
if (IR_INPUT_MIN_PULSE_US > 0U)
|
||||
{
|
||||
if (hasRawFront)
|
||||
pulseFilterFeedOneRaw(rawFront);
|
||||
else
|
||||
pulseFilterFlushTimeout(micros());
|
||||
|
||||
noInterrupts();
|
||||
FrontStorage *flt = filteredSubBuffer.pop();
|
||||
if (flt != nullptr)
|
||||
{
|
||||
currentFront = *flt;
|
||||
hasCurrentFront = true;
|
||||
}
|
||||
interrupts();
|
||||
}
|
||||
else if (hasRawFront)
|
||||
{
|
||||
currentFront = rawFront;
|
||||
hasCurrentFront = true;
|
||||
}
|
||||
|
||||
if (!hasCurrentFront)
|
||||
{
|
||||
isSubBufferOverflow = false;
|
||||
checkTimeout(); // <--- новое место проверки
|
||||
bool rawQueueHasPending = false;
|
||||
bool filteredQueueHasPending = false;
|
||||
noInterrupts();
|
||||
rawQueueHasPending = !subBuffer.isEmpty();
|
||||
if (IR_INPUT_MIN_PULSE_US > 0U)
|
||||
filteredQueueHasPending = !filteredSubBuffer.isEmpty();
|
||||
interrupts();
|
||||
const bool filterHoldHasPending = (IR_INPUT_MIN_PULSE_US > 0U) && (pulseFilterHoldCount > 0U);
|
||||
const bool hasPendingEdges = hasRawFront || rawQueueHasPending || filteredQueueHasPending || filterHoldHasPending;
|
||||
|
||||
if (!hasPendingEdges)
|
||||
{
|
||||
listenStart();
|
||||
checkTimeout();
|
||||
}
|
||||
#if defined(IR_EDGE_TRACE)
|
||||
while (edgeTraceFlushChunk(Serial, 48) > 0) {}
|
||||
#endif
|
||||
return;
|
||||
} // Если данных нет - ничего не делаем
|
||||
currentFront = *currentFrontPtr;
|
||||
interrupts();
|
||||
|
||||
// ---------- буфер пуст: фронтов нет, проверяем тайм-аут ----------
|
||||
// if (currentFrontPtr == nullptr)
|
||||
// {
|
||||
// isSubBufferOverflow = false;
|
||||
// return;
|
||||
// }
|
||||
if (preambleProcessEdge(currentFront))
|
||||
{
|
||||
lastEdgeTime = currentFront.time;
|
||||
goto END;
|
||||
}
|
||||
|
||||
// // ---------- есть фронт: продолжаем обработку ----------
|
||||
// FrontStorage currentFront = *currentFrontPtr;
|
||||
lastEdgeTime = currentFront.time; // запоминаем любой фронт
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
if (currentFront.dir)
|
||||
{ // Если __/``` ↑
|
||||
if (currentFront.time - prevRise > riseTimeMax / 4 || highCount || lowCount)
|
||||
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_GLITCH_REJECT_PHASE_NUDGE
|
||||
irGlitchPhaseNudge(currentFront.time, riseSyncTime, prevRise);
|
||||
#endif
|
||||
goto END;
|
||||
}
|
||||
#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_GLITCH_REJECT_PHASE_NUDGE
|
||||
irGlitchPhaseNudge(currentFront.time, riseSyncTime, prevRise);
|
||||
#endif
|
||||
goto END;
|
||||
}
|
||||
#endif
|
||||
if (candRp <= riseTimeMax / 4U && !highCount && !lowCount)
|
||||
{
|
||||
errors.other++;
|
||||
goto END;
|
||||
}
|
||||
|
||||
if (candRp > riseTimeMax / 4 || highCount || lowCount)
|
||||
{ // комплексный фикс рваной единицы
|
||||
risePeriod = currentFront.time - prevRise;
|
||||
highTime = currentFront.time - prevFall;
|
||||
lowTime = prevFall - prevRise;
|
||||
risePeriod = candRp;
|
||||
highTime = candHt;
|
||||
lowTime = candLt;
|
||||
prevRise = currentFront.time;
|
||||
|
||||
if (
|
||||
@ -231,79 +333,6 @@ void IR_DecoderRaw::tick()
|
||||
digitalWrite(errOut, currentFront.dir);
|
||||
#endif
|
||||
|
||||
if (currentFront.time > prevRise && currentFront.time - prevRise > IR_timeout * 2 && !isReciveRaw)
|
||||
{ // первый
|
||||
#ifdef IRDEBUG
|
||||
errPulse(up, 50);
|
||||
errPulse(down, 50);
|
||||
errPulse(up, 150);
|
||||
errPulse(down, 150);
|
||||
#endif
|
||||
preambFrontCounter = preambFronts - 1U;
|
||||
isPreamb = true;
|
||||
|
||||
isRecive = true;
|
||||
isReciveRaw = true;
|
||||
isWrongPack = false;
|
||||
#if defined(IRDEBUG_SERIAL_PACK)
|
||||
packTraceResetFrame();
|
||||
packTraceOpen = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------
|
||||
|
||||
if (preambFrontCounter)
|
||||
{ // в преамбуле
|
||||
#ifdef IRDEBUG
|
||||
// Serial.print("risePeriod: ");
|
||||
// Serial.println(risePeriod);
|
||||
#endif
|
||||
if (currentFront.dir && risePeriod < IR_timeout)
|
||||
{ // __/``` ↑ и мы в внутри пакета
|
||||
|
||||
if (risePeriod < riseTimeMin / 2)
|
||||
{ // fix рваной единицы
|
||||
preambFrontCounter += 2;
|
||||
errors.other++;
|
||||
#ifdef IRDEBUG
|
||||
errPulse(down, 350);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
if (freeFrec)
|
||||
{
|
||||
riseSyncTime = (riseSyncTime + risePeriod / 2) / 2;
|
||||
} // tuner
|
||||
}
|
||||
}
|
||||
else
|
||||
{ /* riseSyncTime = bitTime; */
|
||||
} // сброс тюнера
|
||||
preambFrontCounter--;
|
||||
// Serial.print("preambFrontCounter: "); Serial.println(preambFrontCounter);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isPreamb)
|
||||
{ // первый фронт после
|
||||
// gotTune.set(riseSyncTime);
|
||||
isPreamb = false;
|
||||
#ifdef IRDEBUG
|
||||
errPulse(up, 50);
|
||||
errPulse(down, 50);
|
||||
#endif
|
||||
prevRise += risePeriod / 2;
|
||||
// prevRise = currentFront.time + riseTime;
|
||||
goto END;
|
||||
}
|
||||
}
|
||||
|
||||
if (isPreamb)
|
||||
{
|
||||
goto END;
|
||||
}
|
||||
if (risePeriod > IR_timeout || isBufferOverflow || risePeriod < riseTimeMin || isWrongPack)
|
||||
// ~Мы в пределах таймаута и буффер не переполнен и fix дроблёных единиц
|
||||
{
|
||||
@ -487,6 +516,7 @@ void IR_DecoderRaw::writeToBuffer(bool bit, bool packTraceInvertFix)
|
||||
{
|
||||
isRecive = false;
|
||||
isReciveRaw = false;
|
||||
preambleResetToIdle();
|
||||
msgTypeReceive = 0;
|
||||
return;
|
||||
}
|
||||
@ -598,6 +628,7 @@ void IR_DecoderRaw::writeToBuffer(bool bit, bool packTraceInvertFix)
|
||||
|
||||
isRecive = false;
|
||||
isReciveRaw = false;
|
||||
preambleResetToIdle();
|
||||
msgTypeReceive = 0;
|
||||
isAvailable = crcCheck(packSize - crcBytes, crcValue);
|
||||
|
||||
@ -1147,6 +1178,265 @@ __attribute__((weak)) void irPackTracePrintOkCommand(const uint8_t *buf, uint8_t
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
bool IR_DecoderRaw::pulseFilterEmit(const FrontStorage &e)
|
||||
{
|
||||
if (filteredSubBuffer.isFull())
|
||||
{
|
||||
pulseFilterDropFilteredOverflow++;
|
||||
return false;
|
||||
}
|
||||
filteredSubBuffer.push(e);
|
||||
return true;
|
||||
}
|
||||
|
||||
void IR_DecoderRaw::pulseFilterReset()
|
||||
{
|
||||
pulseFilterHoldCount = 0;
|
||||
pulseFilterLastRawValid = false;
|
||||
pulseFilterLastRawTime = 0;
|
||||
while (filteredSubBuffer.pop() != nullptr) {}
|
||||
}
|
||||
|
||||
void IR_DecoderRaw::pulseFilterFeedOneRaw(const FrontStorage &e)
|
||||
{
|
||||
const uint32_t minUs = IR_INPUT_MIN_PULSE_US;
|
||||
if (minUs == 0U)
|
||||
{
|
||||
pulseFilterEmit(e);
|
||||
return;
|
||||
}
|
||||
|
||||
pulseFilterLastRawTime = e.time;
|
||||
pulseFilterLastRawValid = true;
|
||||
|
||||
if (pulseFilterHoldCount >= kPulseFilterHoldCap)
|
||||
{
|
||||
pulseFilterDropHoldOverflow++;
|
||||
pulseFilterEmit(pulseFilterHoldEdges[0]);
|
||||
pulseFilterShiftLeft(1);
|
||||
}
|
||||
|
||||
pulseFilterHoldEdges[pulseFilterHoldCount++] = e;
|
||||
const uint8_t holdback = irPulseFilterHoldbackEdges();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (pulseFilterHoldCount < 2)
|
||||
return;
|
||||
|
||||
const uint32_t dt = pulseFilterHoldEdges[1].time - pulseFilterHoldEdges[0].time;
|
||||
if (dt < minUs)
|
||||
{
|
||||
pulseFilterDropGlitchPairs++;
|
||||
pulseFilterShiftLeft(2);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pulseFilterHoldCount <= holdback)
|
||||
return;
|
||||
|
||||
pulseFilterEmit(pulseFilterHoldEdges[0]);
|
||||
pulseFilterShiftLeft(1);
|
||||
}
|
||||
}
|
||||
|
||||
void IR_DecoderRaw::pulseFilterFlushTimeout(uint32_t nowUs)
|
||||
{
|
||||
if (IR_INPUT_MIN_PULSE_US == 0U || !pulseFilterLastRawValid || pulseFilterHoldCount == 0)
|
||||
return;
|
||||
const uint32_t waitUs = IR_INPUT_MIN_PULSE_US * (uint32_t)IR_INPUT_FILTER_TIMEOUT_MULT;
|
||||
if ((uint32_t)(nowUs - pulseFilterLastRawTime) < waitUs)
|
||||
return;
|
||||
|
||||
while (pulseFilterHoldCount > 0)
|
||||
{
|
||||
if (pulseFilterHoldCount >= 2)
|
||||
{
|
||||
const uint32_t dt = pulseFilterHoldEdges[1].time - pulseFilterHoldEdges[0].time;
|
||||
if (dt < IR_INPUT_MIN_PULSE_US)
|
||||
{
|
||||
pulseFilterDropGlitchPairs++;
|
||||
pulseFilterShiftLeft(2);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
pulseFilterEmit(pulseFilterHoldEdges[0]);
|
||||
pulseFilterShiftLeft(1);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
if (!isReciveRaw && front.dir && front.time > prevRise && (front.time - prevRise) > longSilence)
|
||||
preambleStartCandidate(front);
|
||||
}
|
||||
|
||||
if (preambleState == PreambleState::Candidate)
|
||||
{
|
||||
if ((uint32_t)(front.time - preambleCandidateLastEdgeTime) > candTimeout)
|
||||
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;
|
||||
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
|
||||
{
|
||||
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)
|
||||
|
||||
@ -52,6 +52,10 @@ public:
|
||||
inline bool isOverflow() { return isBufferOverflow; }; // Буффер переполнился
|
||||
bool isSubOverflow();
|
||||
volatile inline bool isReciving() { return isRecive; }; // Возвращает true, если происходит приём пакета
|
||||
uint32_t pulseFilterDroppedByFilteredOverflow() const { return pulseFilterDropFilteredOverflow; }
|
||||
uint32_t pulseFilterDroppedByHoldOverflow() const { return pulseFilterDropHoldOverflow; }
|
||||
uint32_t pulseFilterDroppedGlitchPairs() const { return pulseFilterDropGlitchPairs; }
|
||||
void pulseFilterResetStats();
|
||||
|
||||
#if defined(IR_EDGE_TRACE)
|
||||
void edgeTraceClear();
|
||||
@ -101,6 +105,29 @@ private:
|
||||
// volatile FrontStorage subBuffer[subBufferSize]; // вспомогательный буфер для хранения необработанных фронтов/спадов
|
||||
|
||||
RingBuffer<FrontStorage, subBufferSize> subBuffer;
|
||||
/** Очередь фронтов после потокового анти-глитча; tick() читает из неё при включённом фильтре. */
|
||||
RingBuffer<FrontStorage, subBufferSize> filteredSubBuffer;
|
||||
static constexpr uint8_t kPulseFilterHoldCap = 6;
|
||||
FrontStorage pulseFilterHoldEdges[kPulseFilterHoldCap]{};
|
||||
uint8_t pulseFilterHoldCount = 0;
|
||||
bool pulseFilterLastRawValid = false;
|
||||
uint32_t pulseFilterLastRawTime = 0;
|
||||
uint32_t pulseFilterDropFilteredOverflow = 0;
|
||||
uint32_t pulseFilterDropHoldOverflow = 0;
|
||||
uint32_t pulseFilterDropGlitchPairs = 0;
|
||||
static constexpr uint8_t kPreambleLockNeed = (uint8_t)IR_PREAMBLE_LOCK_RISE_PERIODS;
|
||||
enum class PreambleState : uint8_t
|
||||
{
|
||||
Idle = 0,
|
||||
Candidate = 1,
|
||||
Locked = 2
|
||||
};
|
||||
PreambleState preambleState = PreambleState::Idle;
|
||||
uint8_t preambleGoodPeriods = 0;
|
||||
uint16_t preambleMeanPeriod = 0;
|
||||
uint32_t preambleCandidateLastEdgeTime = 0;
|
||||
uint32_t preambleCandidateFirstRiseTime = 0;
|
||||
bool preambleCandidateFirstRiseValid = false;
|
||||
|
||||
#if defined(IR_EDGE_TRACE)
|
||||
struct IrEdgeTraceRec
|
||||
@ -139,6 +166,18 @@ private:
|
||||
bool isReciveRaw;
|
||||
void listenStart();
|
||||
void checkTimeout(); //
|
||||
/** Один сырой фронт из subBuffer -> потоковый holdback-антиглитч. */
|
||||
void pulseFilterFeedOneRaw(const FrontStorage &e);
|
||||
void pulseFilterFlushTimeout(uint32_t nowUs);
|
||||
bool pulseFilterEmit(const FrontStorage &e);
|
||||
void pulseFilterShiftLeft(uint8_t n);
|
||||
void pulseFilterReset();
|
||||
static uint32_t absDiffU32(uint32_t a, uint32_t b);
|
||||
uint32_t preambleJitterTolUs(uint32_t baselineUs) const;
|
||||
bool preambleRisePeriodCoarseOk(uint32_t periodUs) const;
|
||||
void preambleResetToIdle();
|
||||
void preambleStartCandidate(const FrontStorage &front);
|
||||
bool preambleProcessEdge(const FrontStorage &front);
|
||||
|
||||
/// @brief Проверка CRC. Проверяет len байт со значением crc, пришедшим в пакете
|
||||
/// @param len Длина в байтах проверяемых данных
|
||||
|
||||
48
IR_config.h
48
IR_config.h
@ -154,6 +154,54 @@ typedef uint16_t crc_t;
|
||||
#define subBufferSize 250 // Буфер для складирования фронтов, пока их не обработают (передатчик)
|
||||
#endif
|
||||
|
||||
/** Минимальная длительность удержания уровня (мкс): короче — импульс/пара фронтов выкидывается до tick()
|
||||
* (иголки на плато, дребезг). 0 — фильтр выключен, фронты идут в декодер как с ISR. */
|
||||
#ifndef IR_INPUT_MIN_PULSE_US
|
||||
#define IR_INPUT_MIN_PULSE_US 0
|
||||
#endif
|
||||
/** Сколько подтверждённых фронтов держать перед выпуском в декодер (потоковая задержка). */
|
||||
#ifndef IR_INPUT_FILTER_HOLDBACK_EDGES
|
||||
#define IR_INPUT_FILTER_HOLDBACK_EDGES 3U
|
||||
#endif
|
||||
/** Если новых фронтов нет, через minPulse*mult держатель принудительно сбрасывается в декодер. */
|
||||
#ifndef IR_INPUT_FILTER_TIMEOUT_MULT
|
||||
#define IR_INPUT_FILTER_TIMEOUT_MULT 5U
|
||||
#endif
|
||||
|
||||
/** Синхронно с IrFoxProtocolConstants.h / IrFoxDecoder (плагин Saleae). */
|
||||
#ifndef IR_SHORT_LOW_GLITCH_REJECT
|
||||
#define IR_SHORT_LOW_GLITCH_REJECT 1
|
||||
#endif
|
||||
#ifndef IR_GLITCH_REJECT_PHASE_NUDGE
|
||||
#define IR_GLITCH_REJECT_PHASE_NUDGE 1
|
||||
#endif
|
||||
#ifndef IR_MICRO_GAP_RISE_REJECT
|
||||
#define IR_MICRO_GAP_RISE_REJECT 1
|
||||
#endif
|
||||
/** Лок преамбулы: сколько одинаковых подряд периодов подъёма нужно для старта кадра. */
|
||||
#ifndef IR_PREAMBLE_LOCK_RISE_PERIODS
|
||||
#define IR_PREAMBLE_LOCK_RISE_PERIODS 2U
|
||||
#endif
|
||||
/** Допуск одинаковости периода преамбулы (проценты) + минимальная абсолютная полка в мкс. */
|
||||
#ifndef IR_PREAMBLE_JITTER_PCT
|
||||
#define IR_PREAMBLE_JITTER_PCT 18U
|
||||
#endif
|
||||
#ifndef IR_PREAMBLE_JITTER_US_MIN
|
||||
#define IR_PREAMBLE_JITTER_US_MIN 80U
|
||||
#endif
|
||||
/** Грубое окно валидности периода преамбулы RISE->RISE (в процентах от bitTime).
|
||||
* Для текущего протокола преамбула заметно длиннее обычного битового периода. */
|
||||
#ifndef IR_PREAMBLE_PERIOD_MIN_FACTOR_PCT
|
||||
#define IR_PREAMBLE_PERIOD_MIN_FACTOR_PCT 220U
|
||||
#endif
|
||||
#ifndef IR_PREAMBLE_PERIOD_MAX_FACTOR_PCT
|
||||
#define IR_PREAMBLE_PERIOD_MAX_FACTOR_PCT 340U
|
||||
#endif
|
||||
/** Таймаут окна кандидата преамбулы: IR_timeout * mult. */
|
||||
#ifndef IR_PREAMBLE_CANDIDATE_TIMEOUT_MULT
|
||||
#define IR_PREAMBLE_CANDIDATE_TIMEOUT_MULT 3U
|
||||
#endif
|
||||
|
||||
#define preambPulse 3
|
||||
|
||||
#define disablePairDec false // Отключать парный приёмник, возможны баги, используйте setBlindDecoders()
|
||||
|
||||
Reference in New Issue
Block a user