perf(tx): таблица ранов кадра строится по границам ранов, а не по тактам несущей

buildGateRuns/buildPhysicalGateRuns крутили автомат передачи по одному такту (пинг 10 байт =
8728 тактов) — на 12 МГц это ~30 мс между вызовом send и стартом DMA, на каждой передаче
(задний ~20 мс). Обход по границам (txWalkRuns: ран = toggleCounter+1 тактов уровня state)
даёт ту же последовательность за число шагов = число ранов (225). Эквивалентность проверена на
хосте: 12480 случаев, len 1..38, множитель 2..8, логические и физические раны совпадают
бит-в-бит (len=39 — чтение за буфером в sync-фазе автомата у обеих версий, на эфире ≤31).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AeA1K5dBUKrnoXVwyoVjzq
This commit is contained in:
2026-09-07 17:33:20 +03:00
parent 86956bcf99
commit d103d2a3ae
2 changed files with 54 additions and 41 deletions

View File

@ -352,6 +352,29 @@ bool IR_Encoder::txEmitTick(TxFsmState &st, const uint8_t *sendBufferLocal, bool
return txAdvanceAfterOutput(st, sendBufferLocal);
}
// Обход кадра по границам ранов: между границами автомат выдаёт st.toggleCounter+1 тактов
// уровня st.state (txAdvanceAfterOutput считает toggleCounter до нуля, затем txAdvanceBoundary
// открывает следующий ран). Даёт ту же последовательность тактов, что потиковый обход, но за
// число шагов = число ранов (пинг: ~230 вместо ~8700 тактов — на 12 МГц это ~30 мс перед стартом DMA).
template <typename Emit>
bool IR_Encoder::txWalkRuns(TxFsmState &st, const uint8_t *sendBufferLocal, Emit emit)
{
for (;;)
{
const bool gate = st.state;
const uint32_t lenTicks = (uint32_t)st.toggleCounter + 1U;
if (!emit(gate, lenTicks))
{
return false;
}
st.toggleCounter = 0;
if (!txAdvanceBoundary(st, sendBufferLocal))
{
return true;
}
}
}
void IR_Encoder::loadTxFsmFromMembers(TxFsmState &st) const
{
st.sendLen = sendLen;
@ -476,28 +499,27 @@ size_t IR_Encoder::buildGateRuns(const uint8_t *packet, uint8_t len, IR_TxGateRu
st.currentBitSequence = bitHigh;
size_t runCount = 0;
bool isActive = true;
while (isActive)
{
bool gate = false;
isActive = txEmitTick(st, sendBufferLocal, gate);
const bool ok = txWalkRuns(st, sendBufferLocal, [&](bool gate, uint32_t lenTicks) -> bool {
if (runCount > 0 && outRuns[runCount - 1].gate == gate)
{
outRuns[runCount - 1].lenTicks = (uint16_t)(outRuns[runCount - 1].lenTicks + 1U);
}
else
{
if (runCount >= maxRuns)
const uint32_t merged = (uint32_t)outRuns[runCount - 1].lenTicks + lenTicks;
if (merged > 65535U)
{
return 0;
return false;
}
outRuns[runCount].gate = gate;
outRuns[runCount].lenTicks = 1U;
runCount++;
outRuns[runCount - 1].lenTicks = (uint16_t)merged;
return true;
}
}
return runCount;
if (runCount >= maxRuns || lenTicks > 65535U)
{
return false;
}
outRuns[runCount].gate = gate;
outRuns[runCount].lenTicks = (uint16_t)lenTicks;
runCount++;
return true;
});
return ok ? runCount : 0;
}
size_t IR_Encoder::buildPhysicalGateRuns(const uint8_t *packet, uint8_t len, IR_TxGateRun *outRuns, size_t maxRuns, uint16_t multiply)
@ -564,40 +586,29 @@ size_t IR_Encoder::buildPhysicalGateRuns(const uint8_t *packet, uint8_t len, IR_
bool currentGate = false;
uint32_t currentLogicalLen = 0;
bool havePendingRun = false;
bool isActive = true;
while (isActive)
{
bool gate = false;
isActive = txEmitTick(st, sendBufferLocal, gate);
if (!havePendingRun)
const bool ok = txWalkRuns(st, sendBufferLocal, [&](bool gate, uint32_t lenTicks) -> bool {
if (havePendingRun && currentGate == gate)
{
currentGate = gate;
currentLogicalLen = 1U;
havePendingRun = true;
continue;
currentLogicalLen += lenTicks;
return true;
}
if (currentGate == gate)
if (havePendingRun && !appendPhysicalRun(currentGate, currentLogicalLen, runCount))
{
currentLogicalLen++;
continue;
return false;
}
if (!appendPhysicalRun(currentGate, currentLogicalLen, runCount))
{
return 0;
}
currentGate = gate;
currentLogicalLen = 1U;
currentLogicalLen = lenTicks;
havePendingRun = true;
return true;
});
if (!ok)
{
return 0;
}
if (havePendingRun && !appendPhysicalRun(currentGate, currentLogicalLen, runCount))
{
return 0;
}
return runCount;
}