Files
SDR-Rover/protocol/punctured_fec.py

82 lines
3.6 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.
"""Программные варианты R=2/3 и 3/4 на базе штатного кода R=1/2.
Прокалывание удаляет известные позиции кодового слова. При декодировании
эти позиции имеют нулевую мягкую метрику (стирание), после чего работает
штатный терминированный декодер Витерби.
"""
from __future__ import annotations
from functools import lru_cache
import numpy as np
from protocol import convolutional_fec
PUNCTURE_PATTERNS: dict[str, tuple[int, ...]] = {
"1/2": (1, 1),
"2/3": (1, 1, 1, 0),
"3/4": (1, 1, 1, 0, 0, 1),
}
@lru_cache(maxsize=32)
def _mask(mother_bit_count: int, rate: str) -> np.ndarray:
if rate not in PUNCTURE_PATTERNS:
raise ValueError("неизвестная скорость кода")
if mother_bit_count < 0:
raise ValueError("отрицательная длина кодового слова")
pattern = np.asarray(PUNCTURE_PATTERNS[rate], dtype=bool)
mask = np.resize(pattern, mother_bit_count)
mask.flags.writeable = False
return mask
def coded_bit_count(information_bit_count: int, rate: str) -> int:
"""Точное число передаваемых битов, включая шесть хвостовых битов кода."""
mother_count = convolutional_fec.encoded_bit_count(information_bit_count)
if rate not in PUNCTURE_PATTERNS:
raise ValueError("неизвестная скорость кода")
pattern = PUNCTURE_PATTERNS[rate]
periods, remaining = divmod(mother_count, len(pattern))
return periods * sum(pattern) + sum(pattern[:remaining])
def effective_rate(information_bit_count: int, rate: str) -> float:
"""Отношение полезных битов к реально передаваемым кодовым битам."""
if information_bit_count <= 0:
raise ValueError("число полезных битов должно быть положительным")
return information_bit_count / coded_bit_count(information_bit_count, rate)
def encode(information_bits: np.ndarray, rate: str) -> np.ndarray:
"""Сначала применить штатный код, затем удалить позиции по маске."""
mother_bits = convolutional_fec.encode(information_bits)
return mother_bits[_mask(len(mother_bits), rate)]
def depuncture_soft(
soft_values: np.ndarray, information_bit_count: int, rate: str
) -> np.ndarray:
"""Вернуть стёртые позиции с нулевой мягкой метрикой."""
values = np.asarray(soft_values, dtype=np.float64)
if values.ndim != 1 or not np.all(np.isfinite(values)):
raise ValueError("мягкие значения должны быть конечным одномерным массивом")
mother_count = convolutional_fec.encoded_bit_count(information_bit_count)
mask = _mask(mother_count, rate)
if len(values) != int(np.count_nonzero(mask)):
raise ValueError("неверная длина проколотого кодового слова")
restored = np.zeros(mother_count, dtype=np.float64)
restored[mask] = values
return restored
def decode_soft(
soft_values: np.ndarray, information_bit_count: int, rate: str
) -> np.ndarray:
"""Декодировать через существующий 64-состояний декодер Витерби."""
return convolutional_fec.viterbi_decode_soft(
depuncture_soft(soft_values, information_bit_count, rate),
information_bit_count,
)