Lab045: demonstrate real RF BPSK error correction
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
86
protocol/bit_interleaver.py
Normal file
86
protocol/bit_interleaver.py
Normal file
@@ -0,0 +1,86 @@
|
||||
"""Детерминированный блочный перемежитель (запись строками, чтение столбцами).
|
||||
|
||||
Перемежитель декоррелирует пачки ошибок канала: соседние по входу элементы
|
||||
после перестановки оказываются разнесены, а пачка подряд идущих ошибок
|
||||
канала после обратной перестановки распадается на разрозненные одиночные
|
||||
ошибки, которые свёрточный код исправляет лучше всего.
|
||||
|
||||
Схема. Для длины ``length`` и числа столбцов ``columns`` элементы условно
|
||||
записываются построчно в матрицу ``rows × columns`` (``rows =
|
||||
ceil(length/columns)``) и считываются по столбцам. Если длина не кратна
|
||||
размеру матрицы, недостающие ячейки последнего ряда просто пропускаются —
|
||||
ни один элемент не теряется и не выдумывается. Результат выражен как
|
||||
перестановка индексов ``permutation`` длиной ``length``: перемежённый
|
||||
поток ``output[k] = input[permutation[k]]``. Обратная операция применяет
|
||||
обратную перестановку и работает как с битами, так и напрямую с soft-
|
||||
значениями BPSK на приёме. Перестановка зависит только от ``length`` и
|
||||
``columns`` и не зависит от передаваемых данных.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
def _validated_permutation(permutation: np.ndarray, length: int) -> np.ndarray:
|
||||
"""Проверить, что перестановка содержит каждый допустимый индекс один раз."""
|
||||
|
||||
raw_permutation = np.asarray(permutation)
|
||||
if raw_permutation.ndim != 1:
|
||||
raise ValueError("permutation должен быть одномерным")
|
||||
if len(raw_permutation) != length:
|
||||
raise ValueError("длина values не совпадает с длиной перестановки")
|
||||
if not np.issubdtype(raw_permutation.dtype, np.integer):
|
||||
raise ValueError("permutation должен содержать целые индексы")
|
||||
checked = raw_permutation.astype(np.int64, copy=False)
|
||||
if not np.array_equal(np.sort(checked), np.arange(length, dtype=np.int64)):
|
||||
raise ValueError("permutation не является полной перестановкой индексов")
|
||||
return checked
|
||||
|
||||
|
||||
def build_permutation(length: int, columns: int) -> np.ndarray:
|
||||
"""Построить перестановку чтения по столбцам для блока длиной ``length``.
|
||||
|
||||
Возвращает одномерный массив индексов длиной ``length``: ``output[k] =
|
||||
input[permutation[k]]``. Гарантируется, что каждый индекс 0..length-1
|
||||
встречается ровно один раз.
|
||||
"""
|
||||
|
||||
if not isinstance(length, (int, np.integer)) or length < 1:
|
||||
raise ValueError("length должен быть положительным целым")
|
||||
if not isinstance(columns, (int, np.integer)) or columns < 1:
|
||||
raise ValueError("columns должен быть положительным целым")
|
||||
columns = int(columns)
|
||||
length = int(length)
|
||||
rows = (length + columns - 1) // columns
|
||||
permutation = [
|
||||
row * columns + column
|
||||
for column in range(columns)
|
||||
for row in range(rows)
|
||||
if row * columns + column < length
|
||||
]
|
||||
result = np.asarray(permutation, dtype=np.int64)
|
||||
assert len(result) == length
|
||||
return result
|
||||
|
||||
|
||||
def interleave(values: np.ndarray, permutation: np.ndarray) -> np.ndarray:
|
||||
"""Применить перестановку: ``output[k] = values[permutation[k]]``."""
|
||||
|
||||
values = np.asarray(values)
|
||||
if values.ndim != 1:
|
||||
raise ValueError("values должен быть одномерным")
|
||||
checked_permutation = _validated_permutation(permutation, len(values))
|
||||
return values[checked_permutation]
|
||||
|
||||
|
||||
def deinterleave(values: np.ndarray, permutation: np.ndarray) -> np.ndarray:
|
||||
"""Обратить перестановку без потери типа (работает и с soft-значениями)."""
|
||||
|
||||
values = np.asarray(values)
|
||||
if values.ndim != 1:
|
||||
raise ValueError("values должен быть одномерным")
|
||||
checked_permutation = _validated_permutation(permutation, len(values))
|
||||
restored = np.empty_like(values)
|
||||
restored[checked_permutation] = values
|
||||
return restored
|
||||
Reference in New Issue
Block a user