Files
SDR-Rover/protocol/bit_interleaver.py
LittleSam129 62a376c811 Lab045: demonstrate real RF BPSK error correction
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-01 10:49:00 +03:00

87 lines
4.8 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.
"""Детерминированный блочный перемежитель (запись строками, чтение столбцами).
Перемежитель декоррелирует пачки ошибок канала: соседние по входу элементы
после перестановки оказываются разнесены, а пачка подряд идущих ошибок
канала после обратной перестановки распадается на разрозненные одиночные
ошибки, которые свёрточный код исправляет лучше всего.
Схема. Для длины ``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