Files
SDR-Rover/experiments/lab006_stop_and_wait_arq.py
LittleSam129 c486039053 Split experiments from tests
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>
2026-08-10 14:34:58 +03:00

325 lines
8.1 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Lab006. Надёжная передача пакетов методом Stop-and-Wait ARQ.
Передатчик:
1. Отправляет пакет.
2. Ожидает подтверждение ACK.
3. При отсутствии правильного ACK повторяет передачу.
4. После исчерпания попыток считает сообщение недоставленным.
Приёмник:
1. Проверяет пакет.
2. Отбрасывает повреждённые пакеты.
3. Отправляет ACK для правильного пакета.
4. Не передаёт приложению повторные копии одного сообщения.
"""
import random
from protocol.packet import (
CRCError,
MESSAGE_TYPE_ACK,
MESSAGE_TYPE_TEXT,
PacketError,
build_packet,
parse_packet,
)
from tools.virtual_channel import transmit
# ============================================================
# Настройки эксперимента
# ============================================================
MESSAGE_COUNT = 100
# Максимальное количество передач одного сообщения.
MAX_ATTEMPTS = 5
# Параметры канала от передатчика к приёмнику.
DATA_DROP_PROBABILITY = 0.10
DATA_BIT_ERROR_PROBABILITY = 0.002
# Параметры обратного канала для ACK.
ACK_DROP_PROBABILITY = 0.10
ACK_BIT_ERROR_PROBABILITY = 0.002
# Фиксированное число делает эксперимент повторяемым.
random_generator = random.Random(2026)
# ============================================================
# Счётчики
# ============================================================
delivered_messages = 0
failed_messages = 0
data_transmissions = 0
ack_transmissions = 0
duplicate_packets = 0
invalid_data_packets = 0
invalid_ack_packets = 0
dropped_data_packets = 0
dropped_ack_packets = 0
# Номера пакетов, уже переданных приложению приёмника.
received_sequences = set()
# ============================================================
# Передача сообщений
# ============================================================
for sequence_number in range(MESSAGE_COUNT):
message = f"ПАКЕТ {sequence_number}"
payload = message.encode("utf-8")
data_packet = build_packet(
payload=payload,
message_type=MESSAGE_TYPE_TEXT,
sequence_number=sequence_number,
)
message_confirmed = False
# Повторяем передачу до MAX_ATTEMPTS раз.
for attempt_number in range(1, MAX_ATTEMPTS + 1):
data_transmissions += 1
received_data_packet, _ = transmit(
packet=data_packet,
drop_probability=DATA_DROP_PROBABILITY,
bit_error_probability=DATA_BIT_ERROR_PROBABILITY,
random_generator=random_generator,
)
# Пакет полностью потерялся.
if received_data_packet is None:
dropped_data_packets += 1
continue
# Приёмник проверяет пакет.
try:
parsed_data = parse_packet(received_data_packet)
except (CRCError, PacketError):
invalid_data_packets += 1
continue
# Проверка типа сообщения.
if parsed_data.message_type != MESSAGE_TYPE_TEXT:
invalid_data_packets += 1
continue
# Проверяем, не получали ли мы этот пакет ранее.
if parsed_data.sequence_number in received_sequences:
duplicate_packets += 1
else:
restored_message = parsed_data.payload.decode("utf-8")
assert restored_message == message
received_sequences.add(
parsed_data.sequence_number
)
# Формируем подтверждение.
#
# ACK имеет тот же sequence_number,
# что и подтверждаемый пакет.
ack_packet = build_packet(
payload=b"",
message_type=MESSAGE_TYPE_ACK,
sequence_number=parsed_data.sequence_number,
)
ack_transmissions += 1
received_ack_packet, _ = transmit(
packet=ack_packet,
drop_probability=ACK_DROP_PROBABILITY,
bit_error_probability=ACK_BIT_ERROR_PROBABILITY,
random_generator=random_generator,
)
# ACK полностью потерялся.
if received_ack_packet is None:
dropped_ack_packets += 1
continue
# Передатчик проверяет ACK.
try:
parsed_ack = parse_packet(received_ack_packet)
except (CRCError, PacketError):
invalid_ack_packets += 1
continue
# ACK должен иметь правильный тип и номер.
if (
parsed_ack.message_type != MESSAGE_TYPE_ACK
or parsed_ack.sequence_number != sequence_number
):
invalid_ack_packets += 1
continue
# Подтверждение получено.
message_confirmed = True
break
if message_confirmed:
delivered_messages += 1
else:
failed_messages += 1
# ============================================================
# Расчёт статистики
# ============================================================
delivery_rate = (
delivered_messages
/ MESSAGE_COUNT
* 100
)
failure_rate = (
failed_messages
/ MESSAGE_COUNT
* 100
)
average_data_transmissions = (
data_transmissions
/ MESSAGE_COUNT
)
# ============================================================
# Вывод результата
# ============================================================
print("=== Lab006. Stop-and-Wait ARQ ===")
print("\nНастройки:")
print("Количество сообщений:", MESSAGE_COUNT)
print("Максимум попыток:", MAX_ATTEMPTS)
print(
"Вероятность потери DATA:",
DATA_DROP_PROBABILITY,
)
print(
"Вероятность битовой ошибки DATA:",
DATA_BIT_ERROR_PROBABILITY,
)
print(
"Вероятность потери ACK:",
ACK_DROP_PROBABILITY,
)
print(
"Вероятность битовой ошибки ACK:",
ACK_BIT_ERROR_PROBABILITY,
)
print("\nОсновные результаты:")
print(
"Успешно подтверждено сообщений:",
delivered_messages,
)
print(
"Не подтверждено сообщений:",
failed_messages,
)
print(
f"Доля успешной доставки: "
f"{delivery_rate:.1f} %"
)
print(
f"Доля недоставленных сообщений: "
f"{failure_rate:.1f} %"
)
print("\nПередачи:")
print(
"Всего передач DATA:",
data_transmissions,
)
print(
"Всего передач ACK:",
ack_transmissions,
)
print(
f"Среднее число передач DATA "
f"на одно сообщение: "
f"{average_data_transmissions:.2f}"
)
print("\nОшибки канала:")
print(
"Полностью потеряно DATA:",
dropped_data_packets,
)
print(
"Повреждено DATA:",
invalid_data_packets,
)
print(
"Полностью потеряно ACK:",
dropped_ack_packets,
)
print(
"Повреждено ACK:",
invalid_ack_packets,
)
print(
"Получено повторных DATA-пакетов:",
duplicate_packets,
)
# ============================================================
# Автоматические проверки
# ============================================================
assert (
delivered_messages
+ failed_messages
== MESSAGE_COUNT
)
assert delivered_messages <= len(received_sequences)
assert data_transmissions >= MESSAGE_COUNT
print(
"\nПроверка пройдена: "
"все сообщения учтены."
)