Files
IR-protocol/IR_DecoderRaw.cpp
2026-04-02 17:25:10 +03:00

1171 lines
34 KiB
C++
Raw 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>
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;
}
};
//////////////////////////////////// isr ///////////////////////////////////////////
volatile uint32_t time_;
void IR_DecoderRaw::isr()
{
noInterrupts();
time_ = micros();
interrupts();
if (time_ < oldTime)
{
time_ += 1000;
}
oldTime = time_;
FrontStorage edge;
edge.dir = port->IDR & mask;
edge.time = time_;
#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)
{
return;
}
subBuffer.push(edge);
}
////////////////////////////////////////////////////////////////////////////////////
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;
isWrongPack = false;
isPreamb = true;
riseSyncTime = bitTime /* 1100 */;
#ifdef IRDEBUG
wrCounter = 0;
#endif
memset(dataBuffer, 0x00, dataByteSizeMax);
}
void IR_DecoderRaw::listenStart()
{
if (isReciveRaw && ((micros() - prevRise) > IR_timeout * 2))
{
#if defined(IRDEBUG_SERIAL_PACK)
packTraceOnTimeoutOrAbort(true);
#endif
isReciveRaw = false;
firstRX();
}
}
// ---- быстрая проверка конца пакета ---------------------------------
inline void IR_DecoderRaw::checkTimeout()
{
if (!isRecive) return; // уже не принимаем нечего проверять
if (micros() - lastEdgeTime > IR_timeout * 2U)
{
#if defined(IRDEBUG_SERIAL_PACK)
packTraceOnTimeoutOrAbort(false);
#endif
isRecive = false; // приём завершён
msgTypeReceive = 0;
// firstRX(); // подготовка к новому пакету
lastEdgeTime = micros(); // защита от повторного срабатывания
}
}
// ====================================================================
void IR_DecoderRaw::tick()
{
// FrontStorage *currentFrontPtr;
// noInterrupts();
// currentFrontPtr = subBuffer.pop();
// interrupts();
FrontStorage currentFront;
noInterrupts();
listenStart();
FrontStorage *currentFrontPtr;
currentFrontPtr = subBuffer.pop();
if (currentFrontPtr == nullptr)
{
isSubBufferOverflow = false;
checkTimeout(); // <--- новое место проверки
interrupts();
#if defined(IR_EDGE_TRACE)
while (edgeTraceFlushChunk(Serial, 48) > 0) {}
#endif
return;
} // Если данных нет - ничего не делаем
currentFront = *currentFrontPtr;
interrupts();
// ---------- буфер пуст: фронтов нет, проверяем тайм-аут ----------
// if (currentFrontPtr == nullptr)
// {
// isSubBufferOverflow = false;
// return;
// }
// // ---------- есть фронт: продолжаем обработку ----------
// FrontStorage currentFront = *currentFrontPtr;
lastEdgeTime = currentFront.time; // запоминаем любой фронт
////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (currentFront.dir)
{ // Если __/``` ↑
if (currentFront.time - prevRise > riseTimeMax / 4 || highCount || lowCount)
{ // комплексный фикс рваной единицы
risePeriod = currentFront.time - prevRise;
highTime = currentFront.time - prevFall;
lowTime = prevFall - prevRise;
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
// goto END; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#endif
//----------------------------------------------------------------------------------
#ifdef IRDEBUG
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 дроблёных единиц
{
goto END;
}
// определить направление фронта
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
{ // Если ```\__ ↓
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////
END:;
#if defined(IR_EDGE_TRACE)
while (edgeTraceFlushChunk(Serial, 48) > 0) {}
#endif
}
void IR_DecoderRaw::writeToBuffer(bool bit, bool packTraceInvertFix)
{
#if !defined(IRDEBUG_SERIAL_PACK)
(void)packTraceInvertFix;
#endif
if (i_dataBuffer > dataByteSizeMax * 8)
{ // проверка переполнения
isBufferOverflow = true;
#if defined(IRDEBUG_SERIAL_PACK)
if (packTraceOpen)
packTraceEmitErrorFlash(F("ERROR: buffer overflow"));
#endif
}
if (isBufferOverflow || isPreamb || isWrongPack)
{
isRecive = false;
isReciveRaw = false;
msgTypeReceive = 0;
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 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;
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
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
// 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