Files
SDR-Rover/protocol/video005_iq_waveform.py
2026-09-17 13:34:58 +03:00

61 lines
2.2 KiB
Python
Raw 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.
"""Формирование комплексного 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))