mirror of
https://github.com/Show-maket/IR-protocol.git
synced 2026-09-18 19:13:58 +00:00
104 lines
5.5 KiB
Python
104 lines
5.5 KiB
Python
# -*- coding: utf-8 -*-
|
||
# Симуляция CRC-схем IR-protocol на "сдвиговых" ошибках.
|
||
# crc8 воспроизводит IR_config.cpp: MSB-first, init=0xFF, без final-xor.
|
||
import random
|
||
random.seed(1)
|
||
POLY1 = 0x31
|
||
POLY2 = 0x8C # битовое зеркало 0x31
|
||
|
||
def crc8(data, poly):
|
||
crc = 0xFF
|
||
for b in data:
|
||
crc ^= b
|
||
for _ in range(8):
|
||
crc = ((crc << 1) ^ poly) & 0xFF if (crc & 0x80) else (crc << 1) & 0xFF
|
||
return crc
|
||
|
||
def fold4(x): # свернуть 8-бит CRC в 4 бита (xor старший^младший ниббл)
|
||
return (x ^ (x >> 4)) & 0x0F
|
||
|
||
# --- схемы контроля: считают "чек" от данных и сверяют ---
|
||
def single_chk(d): return (crc8(d, POLY1),) # 8 бит, 1 полином
|
||
def double_chk(d): c1 = crc8(d, POLY1); return (c1, crc8(d + [c1], POLY2)) # текущая: 16 бит
|
||
def nib_fold_chk(d):return ((fold4(crc8(d, POLY1)) << 4) | fold4(crc8(d, POLY2)),) # предложенная: 4+4 бит
|
||
def nib_trunc_chk(d):return (((crc8(d, POLY1) & 0x0F) << 4) | (crc8(d, POLY2) & 0x0F),)
|
||
|
||
def bits_of(data):
|
||
return [(b >> (7 - j)) & 1 for b in data for j in range(8)]
|
||
def bytes_of(bits):
|
||
return [sum(bits[i + j] << (7 - j) for j in range(8)) for i in range(0, len(bits), 8)]
|
||
|
||
# --- модели "сдвига" в середине пакета (над битами данных, длина сохраняется) ---
|
||
def slip_delete(data, p): # выкинуть бит p, сдвинуть хвост влево, добить 0 в конец
|
||
b = bits_of(data); b = b[:p] + b[p+1:] + [0]; return bytes_of(b)
|
||
def rotate_all_right(data, _): # циклический сдвиг ВСЕХ бит вправо на 1
|
||
b = bits_of(data); b = [b[-1]] + b[:-1]; return bytes_of(b)
|
||
def rotate_mid_window(data, p): # циклический сдвиг окна [p, p+16) на 1 (локальный сдвиг в середине)
|
||
b = bits_of(data); q = min(p+16, len(b))
|
||
w = b[p:q]; w = [w[-1]] + w[:-1]; b = b[:p] + w + b[q:]; return bytes_of(b)
|
||
|
||
MODELS = [("slip-delete (бит-слип)", slip_delete),
|
||
("rotate-all (сдвиг всего на 1)", rotate_all_right),
|
||
("rotate-mid-window (сдвиг окна в середине)", rotate_mid_window)]
|
||
SCHEMES = [("single (1×CRC8, 8бит)", single_chk),
|
||
("double (2×CRC8, 16бит) — текущая", double_chk),
|
||
("nibble-fold (4+4бит)", nib_fold_chk),
|
||
("nibble-trunc (4+4бит)", nib_trunc_chk)]
|
||
|
||
def run(datalen, trials):
|
||
print(f"\n===== данные {datalen} байт, {trials} попыток на модель =====")
|
||
for mname, model in MODELS:
|
||
# для каждой схемы: сколько ошибок НЕ обнаружено (чек совпал, а данные изменились)
|
||
undetected = {s[0]: 0 for s in SCHEMES}
|
||
changed = 0
|
||
first_single_miss = None
|
||
for _ in range(trials):
|
||
d = [random.randint(0, 255) for _ in range(datalen)]
|
||
p = random.randint(datalen*8//4, datalen*8*3//4) # позиция в середине
|
||
c = model(d, p)
|
||
if c == d:
|
||
continue
|
||
changed += 1
|
||
for sname, chk in SCHEMES:
|
||
if chk(c) == chk(d):
|
||
undetected[sname] += 1
|
||
if sname.startswith("single") and first_single_miss is None:
|
||
first_single_miss = (d[:], c[:], p)
|
||
print(f" модель: {mname} (изменённых кадров: {changed})")
|
||
for sname, _ in SCHEMES:
|
||
u = undetected[sname]
|
||
rate = u / changed if changed else 0
|
||
print(f" {sname:38s}: пропущено {u:6d} ({rate*100:.4f}%)")
|
||
if first_single_miss:
|
||
d, c, p = first_single_miss
|
||
print(f" ПРИМЕР пропуска single (poly1): data={bytes(d).hex()} -> shift@{p} -> {bytes(c).hex()}")
|
||
print(f" single: crc1(orig)={crc8(d,POLY1):02x} == crc1(shift)={crc8(c,POLY1):02x} -> ПРОШЁЛ")
|
||
print(f" double: crc2(orig)={double_chk(d)[1]:02x} vs crc2(shift)={double_chk(c)[1]:02x} -> {'ПРОШЁЛ (не поймал!)' if double_chk(d)==double_chk(c) else 'ПОЙМАЛ'}")
|
||
|
||
for L in (3, 5, 8):
|
||
run(L, 200000)
|
||
|
||
# Гарантированное покрытие burst-ошибок (подряд идущие искажённые биты) — ключ для IR (бит-слипы/всплески)
|
||
print("\n===== Гарантия обнаружения BURST-ошибок (100% всех burst данной длины?) =====")
|
||
def burst_guaranteed(chk, datalen, blen):
|
||
# перебор всех burst длины blen по всем позициям на нескольких случайных базах; ищем ХОТЬ ОДИН пропуск
|
||
for _ in range(300):
|
||
d = [random.randint(0,255) for _ in range(datalen)]
|
||
base = bits_of(d)
|
||
for start in range(len(base)-blen+1):
|
||
b2 = base[:]
|
||
for k in range(blen):
|
||
b2[start+k] ^= 1
|
||
c = bytes_of(b2)
|
||
if c != d and chk(c) == chk(d):
|
||
return False # найден необнаруженный burst
|
||
return True
|
||
for sname, chk in SCHEMES:
|
||
maxb = 0
|
||
for blen in range(1, 25):
|
||
if burst_guaranteed(chk, 5, blen):
|
||
maxb = blen
|
||
else:
|
||
break
|
||
print(f" {sname:38s}: 100%% ловит burst до {maxb} бит подряд")
|