VIDEO-005: add full offline QPSK IQ video receiver

This commit is contained in:
LittleSam129
2026-09-17 13:34:58 +03:00
parent 95f664076a
commit 6df5f8aa39
18 changed files with 2651 additions and 13 deletions

View File

@@ -0,0 +1,60 @@
"""Формирование комплексного IQ для программного видеотракта VIDEO-005."""
from __future__ import annotations
from functools import lru_cache
from typing import Sequence
import numpy as np
from scipy.signal import upfirdn
from protocol.bpsk_radio import root_raised_cosine_taps
from protocol.video003_modem import (
RRC_ROLLOFF,
RRC_SPAN_SYMBOLS,
SAMPLE_RATE_HZ,
OfflineFrame,
samples_per_symbol,
)
SYMBOL_RATE = 500_000
NOMINAL_CARRIER_HZ = 125_000.0
GUARD_SAMPLES = 64
@lru_cache(maxsize=2)
def rrc_taps(symbol_rate: int = SYMBOL_RATE) -> np.ndarray:
"""Возвращает неизменяемые коэффициенты уже применяемого RRC-фильтра."""
taps = root_raised_cosine_taps(
RRC_ROLLOFF, samples_per_symbol(symbol_rate), RRC_SPAN_SYMBOLS
)
taps.flags.writeable = False
return taps
def shape_packet(
frame: OfflineFrame,
symbol_rate: int = SYMBOL_RATE,
carrier_hz: float = NOMINAL_CARRIER_HZ,
) -> np.ndarray:
"""Формирует один IQ-пакет с защитными паузами, без канальных нарушений."""
if not np.isfinite(carrier_hz) or abs(carrier_hz) >= SAMPLE_RATE_HZ / 2:
raise ValueError("несущая должна лежать внутри полосы дискретизации")
if frame.modulation != "QPSK" or frame.rate != "2/3":
raise ValueError("VIDEO-005 использует QPSK и внутренний FEC 2/3")
shaped = upfirdn(rrc_taps(symbol_rate), frame.symbols, up=samples_per_symbol(symbol_rate))
envelope = np.pad(shaped, (GUARD_SAMPLES, GUARD_SAMPLES))
phase = 2 * np.pi * carrier_hz * np.arange(envelope.size) / SAMPLE_RATE_HZ
return (envelope * np.exp(1j * phase)).astype(np.complex64)
def shape_stream(
frames: Sequence[OfflineFrame],
symbol_rate: int = SYMBOL_RATE,
carrier_hz: float = NOMINAL_CARRIER_HZ,
) -> np.ndarray:
"""Соединяет IQ-пакеты; приёмнику их границы не передаются."""
if not frames:
raise ValueError("пустой поток")
return np.concatenate(tuple(shape_packet(frame, symbol_rate, carrier_hz) for frame in frames))