Files
IR-protocol/docs_analysis/crc_shift_findings.md
2026-07-01 15:33:49 +03:00

4.5 KiB
Raw Blame History

CRC при сдвиге битов — симуляция (воспроизведение и оценка сжатия)

Скрипт: crc_shift_sim.py (рядом). crc8 точно как в IR_config.cpp (MSB-first, init=0xFF, без final-xor). poly1=0x31, poly2=0x8C (битовое зеркало 0x31). Модель ошибки: сдвиг битов в СЕРЕДИНЕ данных (бит-слип / циклический сдвиг), длина кадра сохраняется; «чек» сверяется как check(искажённые данные) == check(оригинал). 200000 попыток на модель, данные 3/5/8 байт.

1. Твой исторический баг ВОСПРОИЗВЕДён

Одиночный CRC8 (один полином) реально пропускает сдвиг в середине пакета. Примеры (данные → сдвиг → искажённые):

  • 26 0b 0526 16 0a: crc1=0x78 совпал → single ПРОШЁЛ; двойная схема: crc2 0xd8≠0x5cпоймала.
  • cc4fb8f633cc5f71ec66: crc1=0x20 совпал → single прошёл; double поймала (crc2 0xfc≠0x90).
  • b983315b416440c3b983315e82c88186: crc1=0x3a совпал → single прошёл; double поймала.

2. Частота пропуска (доля НЕобнаруженных сдвигов)

Схема slip-delete rotate-all rotate-mid-window
single (1×CRC8, 8 бит) 0.290.41 % ~0.78 % 0.550.78 %
double (2×CRC8, 16 бит) — текущая 0.0070.015 % 0.0250.03 % 0.0080.026 %
nibble-fold (4+4 бит) 0.760.80 % ~1.58 % 1.181.55 %
nibble-trunc (4+4 бит) 1.371.57 % ~1.53 % 1.221.57 %

Вывод: double примерно в 2040× надёжнее single и ловит практически все сдвиги, которые single пропускает. Это подтверждает, зачем добавляли второй полином (0x8C = зеркало 0x31 ловит «зеркальные» сдвиговые ошибки, слепые для 0x31).

3. Идея «склеить 2 байта в 1» (полбайта на полином) — ХУЖЕ, чем есть

  • nibble-fold (свернуть каждый CRC8 в 4 бита xor'ом) и nibble-trunc (взять по 4 бита) дают 0.61.6 % пропусков — это ХУЖЕ даже одиночного полного CRC8 и в ~50150× хуже текущей двойной схемы.
  • Burst-ошибки (подряд искажённые биты — типичный IR-сбой: бит-слипы/всплески). Гарантия обнаружения = ширине контроля:
    • single 8 бит и double — надёжны на коротких burst (≥8 бит), эмпирически в выборке не пропускали и длиннее;
    • nibble-fold — гарантия ~4 бита (эмпирически до 7);
    • nibble-truncпропускает даже ОДИНОЧНЫЙ бит (усечение теряет старший ниббл) → так делать нельзя.

4. Рекомендация

  • CRC — не то место, где стоит экономить байт. Сжатие контроля до 1 байта повышает пропуск сдвигов/burst в ~50150× (с ~0.01 % до ~1 %) — прямо в том классе ошибок, ради которого второй полином и вводили.
  • Если байт очень нужен — забирать его не из CRC, а из уже выявленного резерва: свободные msgType (3,5), «пустая» зона длины (компактные кадры), или переупаковка адресов/полей. 16-битный контроль (2×CRC8) сохранить.
  • Если всё же сжимать CRC до 1 байта — только fold (xor нибблов), никогда не truncate; и принять ~1 % пропуска на сдвигах (в ~50100× хуже текущего). Как отдельный компромисс — обсуждать вместе с обратной совместимостью.

(Обратную совместимость версий протокола обсудим отдельно — по запросу Даши.)