Complete VIDEO-001 through VIDEO-004 software video studies

This commit is contained in:
LittleSam129
2026-09-16 18:27:35 +03:00
parent af7c05f2da
commit 95f664076a
403 changed files with 392009 additions and 0 deletions

View File

@@ -0,0 +1,84 @@
"""Расчётная длительность пакета VIDEO-002; PHY и радиоприёмник не меняются."""
from __future__ import annotations
from dataclasses import asdict, dataclass
import math
from protocol.video_stream_transport import HEADER_SIZE
PROTOCOL_OVERHEAD_BYTES = 12 # Заголовок protocol.packet и CRC-32.
PREAMBLE_SYMBOLS = 1280
PHY_HEADER_SYMBOLS = 144
TRANSIENT_SYMBOLS = 42 # Два защитных интервала и хвост RRC.
FEC_TAIL_BITS = 6
RRC_ROLLOFF = 0.35
CURRENT_MAX_PACKET_BYTES = 1024
@dataclass(frozen=True)
class RadioEstimate:
modulation: str
symbols_per_second: int
phy_payload_bytes: int
gap_ms: float
video_bytes_per_packet: int
packet_bytes_before_fec: int
coded_body_bits: int
airtime_ms: float
useful_video_bps: float
overhead_fraction: float
raw_bps: int
occupied_bandwidth_hz: float
symbol_duration_us: float
within_current_packet_limit: bool
implemented_in_current_phy: bool
def as_dict(self) -> dict:
return asdict(self)
def estimate(modulation: str, symbols_per_second: int,
phy_payload_bytes: int, gap_ms: float) -> RadioEstimate:
"""QPSK: преамбула/PHY-заголовок BPSK, тело FEC по два бита на символ."""
if modulation not in ("BPSK", "QPSK") or symbols_per_second <= 0:
raise ValueError("Неизвестная модуляция или символьная скорость")
if phy_payload_bytes <= HEADER_SIZE or gap_ms < 0:
raise ValueError("Недопустимые нагрузка или пауза")
packet_bytes = phy_payload_bytes + PROTOCOL_OVERHEAD_BYTES
coded_bits = 2 * (packet_bytes * 8 + FEC_TAIL_BITS)
bits_per_symbol = 1 if modulation == "BPSK" else 2
body_symbols = math.ceil(coded_bits / bits_per_symbol)
symbols = PREAMBLE_SYMBOLS + PHY_HEADER_SYMBOLS + body_symbols + TRANSIENT_SYMBOLS
airtime_ms = 1000 * symbols / symbols_per_second
video_bytes = phy_payload_bytes - HEADER_SIZE
useful_bps = video_bytes * 8000 / (airtime_ms + gap_ms)
# Вся ёмкость интервала, кроме байтов изображения, относится к накладным расходам.
overhead = 1 - useful_bps / (symbols_per_second * bits_per_symbol)
return RadioEstimate(
modulation, symbols_per_second, phy_payload_bytes, gap_ms,
video_bytes, packet_bytes, coded_bits, airtime_ms, useful_bps,
overhead, symbols_per_second * bits_per_symbol,
symbols_per_second * (1 + RRC_ROLLOFF),
1_000_000 / symbols_per_second,
packet_bytes <= CURRENT_MAX_PACKET_BYTES,
modulation == "BPSK" and symbols_per_second == 500_000
and phy_payload_bytes == 256 and gap_ms == 5,
)
def matrix() -> list[RadioEstimate]:
return [estimate(modulation, speed, payload, gap)
for modulation, speed in (("BPSK", 500_000),
("QPSK", 250_000),
("QPSK", 500_000))
for payload in (256, 512, 1024)
for gap in (5, 2, 1, 0)]
def margin_fits(rate_bps: float, peak_bps: float, margin: float) -> bool:
"""Запас 20%: предельная скорость >= пик * 1.2."""
if not 0 <= margin <= 1:
raise ValueError("Запас вне диапазона 0–1")
return rate_bps >= peak_bps * (1 + margin)