The tests/ directory held 50 laboratory programs and no tests. They model channels, run hundreds of repetitions and write CSV, PNG and reports; calling that a test suite blocked introducing a real one, because any pytest run would have collected the labs and re-executed every experiment. - move all 50 lab programs to experiments/ with git mv, preserving history - rewrite the 38 cross-imports between labs from tests.labNNN to experiments.labNNN - leave tests/ empty for actual fast checks of protocol/ - point quick_gate and the hook at the new layout and add experiments/ to the syntax sweep - update the paths quoted in the Lab042 specification and the verifier agent definition This also defuses the import-time work finding without touching 41 files: the labs still create directories and write files on import, but nothing imports them now except the gate, which does so deliberately. Gate passes: syntax clean, protocol imports, 15 lab modules import, 2 functional suites run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
153 lines
4.3 KiB
Python
153 lines
4.3 KiB
Python
"""
|
||
Lab002, часть 2.
|
||
Повреждение одного бита пакета без контроля целостности.
|
||
|
||
Цель:
|
||
1. Сформировать цифровой пакет.
|
||
2. Намеренно изменить один бит полезной нагрузки.
|
||
3. Разобрать повреждённый пакет.
|
||
4. Убедиться, что без CRC повреждение не обнаруживается.
|
||
"""
|
||
|
||
import struct
|
||
|
||
|
||
# ============================================================
|
||
# Константы протокола
|
||
# ============================================================
|
||
|
||
SYNC_WORD = 0xAA55
|
||
PROTOCOL_VERSION = 1
|
||
MESSAGE_TYPE_TEXT = 1
|
||
|
||
HEADER_FORMAT = ">HBBHH"
|
||
|
||
|
||
# ============================================================
|
||
# Формирование исходного пакета
|
||
# ============================================================
|
||
|
||
message = "ПРИВЕТ SDR"
|
||
payload = message.encode("utf-8")
|
||
|
||
sequence_number = 1
|
||
message_type = MESSAGE_TYPE_TEXT
|
||
payload_length = len(payload)
|
||
|
||
header = struct.pack(
|
||
HEADER_FORMAT,
|
||
SYNC_WORD,
|
||
PROTOCOL_VERSION,
|
||
message_type,
|
||
sequence_number,
|
||
payload_length,
|
||
)
|
||
|
||
packet = header + payload
|
||
|
||
|
||
print("Исходное сообщение:")
|
||
print(message)
|
||
|
||
print("\nИсходный пакет:")
|
||
print(packet.hex(" "))
|
||
|
||
|
||
# ============================================================
|
||
# Имитация повреждения пакета
|
||
# ============================================================
|
||
|
||
# bytes нельзя изменять напрямую.
|
||
# Поэтому преобразуем пакет в изменяемый массив bytearray.
|
||
corrupted_packet = bytearray(packet)
|
||
|
||
header_size = struct.calcsize(HEADER_FORMAT)
|
||
|
||
# В строке "ПРИВЕТ SDR":
|
||
#
|
||
# "ПРИВЕТ" занимает 12 байтов UTF-8,
|
||
# пробел занимает 1 байт,
|
||
# буква S находится по индексу 13 внутри PAYLOAD.
|
||
payload_byte_index = 13
|
||
|
||
# Полный индекс внутри пакета:
|
||
packet_byte_index = header_size + payload_byte_index
|
||
|
||
original_byte = corrupted_packet[packet_byte_index]
|
||
|
||
# XOR с 0x01 изменяет младший бит:
|
||
#
|
||
# 0x53 = 01010011 = S
|
||
# 0x52 = 01010010 = R
|
||
corrupted_packet[packet_byte_index] ^= 0x01
|
||
|
||
corrupted_byte = corrupted_packet[packet_byte_index]
|
||
|
||
|
||
print("\nИзменяемый байт внутри пакета:")
|
||
|
||
print(
|
||
f"До повреждения: "
|
||
f"{original_byte:3d} "
|
||
f"= 0x{original_byte:02X} "
|
||
f"= {original_byte:08b}"
|
||
)
|
||
|
||
print(
|
||
f"После повреждения: "
|
||
f"{corrupted_byte:3d} "
|
||
f"= 0x{corrupted_byte:02X} "
|
||
f"= {corrupted_byte:08b}"
|
||
)
|
||
|
||
print("\nПовреждённый пакет:")
|
||
print(bytes(corrupted_packet).hex(" "))
|
||
|
||
|
||
# ============================================================
|
||
# Разбор повреждённого пакета
|
||
# ============================================================
|
||
|
||
received_header = corrupted_packet[:header_size]
|
||
received_payload = corrupted_packet[header_size:]
|
||
|
||
(
|
||
received_sync,
|
||
received_version,
|
||
received_type,
|
||
received_sequence,
|
||
received_length,
|
||
) = struct.unpack(HEADER_FORMAT, received_header)
|
||
|
||
|
||
# ============================================================
|
||
# Проверки структуры
|
||
# ============================================================
|
||
|
||
assert received_sync == SYNC_WORD
|
||
assert received_version == PROTOCOL_VERSION
|
||
assert received_type == MESSAGE_TYPE_TEXT
|
||
assert received_sequence == sequence_number
|
||
assert received_length == len(received_payload)
|
||
|
||
print("\nВсе проверки заголовка пройдены.")
|
||
|
||
restored_message = bytes(received_payload).decode("utf-8")
|
||
|
||
|
||
print("\nСообщение после повреждения:")
|
||
print(restored_message)
|
||
|
||
print("\nИсходное сообщение:")
|
||
print(message)
|
||
|
||
|
||
# ============================================================
|
||
# Проверка результата эксперимента
|
||
# ============================================================
|
||
|
||
assert restored_message != message
|
||
|
||
print("\nПовреждение данных произошло.")
|
||
print("Но заголовок и длина пакета остались правильными.")
|
||
print("Без CRC приёмник не смог определить ошибку.") |