mirror of
https://github.com/Show-maket/IR-protocol.git
synced 2026-04-28 03:08:08 +00:00
175 lines
6.2 KiB
Python
175 lines
6.2 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Симуляция логики IR_Encoder::buildGateRuns (IR-protocol) и утилиты CRC8.
|
||
Запуск из корня репозитория: python docs/scripts/ir_protocol_gate_runs_sim.py
|
||
Или: python ir_protocol_gate_runs_sim.py из каталога scripts/
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import sys
|
||
|
||
# --- IR_config.h (фрагмент) ---
|
||
bitPauseTakts = 12
|
||
bitActiveTakts = 25
|
||
preambPulse = 3
|
||
syncBits = 3
|
||
bitPerByte = 8
|
||
preambToggle = ((bitPauseTakts * 2 + bitActiveTakts) * 2 - 1)
|
||
bitHigh = [(bitPauseTakts) * 2 - 1, (bitActiveTakts) * 2 - 1]
|
||
bitLow = [(bitPauseTakts // 2 + bitActiveTakts) * 2 - 1, (bitPauseTakts) - 1]
|
||
|
||
preamb, data, sync, noSignal = 0, 1, 2, 3
|
||
HIGH = True
|
||
|
||
|
||
def crc8(data: bytes, start: int, end: int, poly: int) -> int:
|
||
"""Как IR_FOX::crc8 в IR_config.cpp: [start, end)."""
|
||
crc = 0xFF
|
||
for i in range(start, end):
|
||
crc ^= data[i]
|
||
for _ in range(8):
|
||
if (crc & 0x80) != 0:
|
||
crc = ((crc << 1) ^ poly) & 0xFF
|
||
else:
|
||
crc = (crc << 1) & 0xFF
|
||
return crc
|
||
|
||
|
||
def crc_pair_over_wire(packet: bytes) -> tuple[int, int]:
|
||
"""Два байта CRC как в IR_Encoder::sendDataFULL (poly1 старший, poly2 младший)."""
|
||
ps = len(packet)
|
||
if ps < 2:
|
||
return 0, 0
|
||
b1 = crc8(packet, 0, ps - 2, 0x31) & 0xFF
|
||
b2 = crc8(packet, 0, ps - 1, 0x8C) & 0xFF
|
||
return b1, b2
|
||
|
||
|
||
# Как dataByteSizeMax в IR_config.h (msg+addr+addr+bytePerPack+crc)
|
||
DATA_BYTE_SIZE_MAX = 1 + 2 + 2 + 31 + 2
|
||
|
||
|
||
def build_gate_runs(packet: bytes):
|
||
"""
|
||
Повторяет IR_Encoder::buildGateRuns: список (gate: bool, lenTicks: int), сумма lenTicks = totalTicks DMA.
|
||
Буфер дополняется нулями до dataByteSizeMax, как sendBufferLocal[dataByteSizeMax] в C++.
|
||
"""
|
||
send_len = len(packet)
|
||
send_buf = bytearray(packet) + bytes(max(0, DATA_BYTE_SIZE_MAX - len(packet)))
|
||
|
||
toggle = preambToggle
|
||
data_bit = bitPerByte - 1
|
||
data_byte = 0
|
||
preamb_front = preambPulse * 2 - 1
|
||
data_seq = bitPerByte * 2
|
||
sync_seq = syncBits * 2
|
||
sync_last = False
|
||
sig = preamb
|
||
state = HIGH
|
||
cur_seq = bitHigh
|
||
|
||
runs: list[tuple[bool, int]] = []
|
||
outer_steps = 0
|
||
|
||
while True:
|
||
outer_steps += 1
|
||
gate = state
|
||
run_len = toggle + 1 # как в C++: (uint16_t)toggleCounterLocal + 1U
|
||
|
||
if runs and runs[-1][0] == gate:
|
||
g, ln = runs[-1]
|
||
runs[-1] = (g, ln + run_len)
|
||
else:
|
||
runs.append((gate, run_len))
|
||
|
||
while True:
|
||
if sig == noSignal:
|
||
return runs, outer_steps
|
||
|
||
if sig == preamb:
|
||
if preamb_front:
|
||
preamb_front -= 1
|
||
toggle = preambToggle
|
||
break
|
||
sig = data
|
||
state = not False
|
||
continue
|
||
|
||
if sig == data:
|
||
if data_seq:
|
||
if not (data_seq & 1):
|
||
cur_seq = bitHigh if ((send_buf[data_byte] >> data_bit) & 1) else bitLow
|
||
data_bit -= 1
|
||
toggle = cur_seq[not state]
|
||
data_seq -= 1
|
||
break
|
||
sync_last = send_buf[data_byte] & 1
|
||
data_byte += 1
|
||
data_bit = bitPerByte - 1
|
||
data_seq = bitPerByte * 2
|
||
sig = sync
|
||
continue
|
||
|
||
if sig == sync:
|
||
if sync_seq:
|
||
if not (sync_seq & 1):
|
||
if sync_seq == 2:
|
||
cur_seq = bitLow if (send_buf[data_byte] & 0x80) else bitHigh
|
||
else:
|
||
cur_seq = bitLow if sync_last else bitHigh
|
||
sync_last = not sync_last
|
||
toggle = cur_seq[not state]
|
||
sync_seq -= 1
|
||
break
|
||
sig = data
|
||
sync_seq = syncBits * 2
|
||
if data_byte >= send_len:
|
||
sig = noSignal
|
||
continue
|
||
|
||
return [], 0
|
||
|
||
state = not state
|
||
|
||
|
||
def main() -> int:
|
||
print("IR-protocol: preambToggle =", preambToggle)
|
||
print()
|
||
|
||
# Пример из лога: 8-байтный эхо-пакет Version_Query (CRC OK на приёме)
|
||
echo = bytes.fromhex("C8 FA 2A FD E8 5D AA B4")
|
||
c1, c2 = crc_pair_over_wire(echo)
|
||
print("8 байт (эхо): CRC вычисленный:", f"{c1:02X}", f"{c2:02X}", "| на проводе:", f"{echo[6]:02X}", f"{echo[7]:02X}")
|
||
|
||
runs8, steps8 = build_gate_runs(echo)
|
||
total8 = sum(r[1] for r in runs8)
|
||
print(" buildGateRuns: внешних шагов FSM =", steps8, ", totalTicks =", total8, ", число run-сегментов =", len(runs8))
|
||
print()
|
||
|
||
# 31 байт из лога Frame reject (пример)
|
||
reject = bytes.fromhex(
|
||
"DF 00 00 FA 2A 5E 43 61 72 5F 76 34 2E 33 2E 38 5F 5B 31 32 4D 68 7A 5D 6B ED 1D 9A 53 96 62"
|
||
)
|
||
if len(reject) == 31:
|
||
c1, c2 = crc_pair_over_wire(reject)
|
||
print("31 байт (reject): CRC по телу 0..28 должен быть:", f"{c1:02X}", f"{c2:02X}", "| байты [29:31]:", f"{reject[29]:02X}", f"{reject[30]:02X}")
|
||
print(" Совпадение с формулой:", c1 == reject[29] and c2 == reject[30])
|
||
|
||
runs31, steps31 = build_gate_runs(reject)
|
||
total31 = sum(r[1] for r in runs31)
|
||
print(" buildGateRuns: внешних шагов =", steps31, ", totalTicks =", total31, ", run-сегментов =", len(runs31))
|
||
print()
|
||
|
||
# Связь totalTicks с моделью «N тиков на сегмент до шага FSM»
|
||
# total_build = sum(toggle_i + 1); если бы было sum(toggle_i), разница = steps
|
||
theoretical_isr_ticks = total31 - steps31
|
||
print("Для 31-байт пакета: totalTicks (buildGateRuns) =", total31)
|
||
print(" Если каждый внешний шаг даёт +1 к длине сегмента относительно ISR (runLen = toggle+1 vs toggle),")
|
||
print(" оценка «ISR-тиков» как totalTicks - outer_steps =", theoretical_isr_ticks)
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|