nearlink_sdr.phy.pilot 源代码
"""导频符号插入与提取 -- TXS-10002-2025 标准 6.2.1.2 节。"""
__all__ = [
"EVEN_ROTATION_DEG",
"PILOT_PHASE_DEG",
"insert_pilots",
"pilot_symbol",
"remove_pilots",
]
import numpy as np
# 各调制方式的导频参考相位(单位:度)
# 标准定义: BPSK导频90°, QPSK导频45°, 8PSK导频22.5°
PILOT_PHASE_DEG = {
"BPSK": 90.0,
"QPSK": 45.0,
"8PSK": 22.5,
}
# 各调制方式偶数符号的相位旋转量(顺时针,单位:度)
EVEN_ROTATION_DEG = {
"BPSK": -90.0,
"QPSK": -45.0,
"8PSK": -22.5,
}
[文档]
def pilot_symbol(mod_type: str, symbol_index: int) -> complex:
"""生成带适当相位旋转的单个导频符号。
:param mod_type: 调制方式,取 "BPSK"、"QPSK" 或 "8PSK"。
:param symbol_index: 帧内以 0 为基的符号索引(决定奇偶位置)。
标准规定第一个符号处于奇数位置。
:returns: 复数导频符号。
"""
phase_deg = PILOT_PHASE_DEG[mod_type]
# "无线帧的第一个符号认为处于奇数位" → index 0 is odd, index 1 is even, ...
is_even = (symbol_index % 2) == 1
if is_even:
phase_deg += EVEN_ROTATION_DEG[mod_type]
return np.exp(1j * np.deg2rad(phase_deg))
[文档]
def insert_pilots(
data_symbols: np.ndarray,
pilot_interval: int,
mod_type: str,
start_symbol_index: int = 0,
omit_last_pilot: bool = True,
) -> tuple[np.ndarray, int]:
"""向数据符号流中插入导频符号。
每隔 `pilot_interval` 个数据符号后插入一个导频符号。
按标准规定,帧中最后一个导频应省略。
:param data_symbols: 复数数据符号数组。
:param pilot_interval: 标准中的 N 值(4、8 或 16)。
:param mod_type: 导频相位所用的调制方式。
:param start_symbol_index: 帧内第一个数据符号的绝对符号索引。
:param omit_last_pilot: 是否省略最后一个导频符号。
:returns: (output_symbols, total_symbol_count) — 插入导频后的符号数组及总符号数。
"""
if pilot_interval <= 0:
return data_symbols.copy(), len(data_symbols)
result = []
sym_idx = start_symbol_index
n_data = len(data_symbols)
data_pos = 0
pilot_positions = []
while data_pos < n_data:
# 取最多 pilot_interval 个数据符号
chunk_end = min(data_pos + pilot_interval, n_data)
chunk = data_symbols[data_pos:chunk_end]
result.append(chunk)
sym_idx += len(chunk)
data_pos = chunk_end
# 若当前块已满则在其后插入导频
if len(chunk) == pilot_interval and data_pos <= n_data:
pilot_positions.append(len(result))
p = pilot_symbol(mod_type, sym_idx)
result.append(np.array([p]))
sym_idx += 1
output = np.concatenate(result) if result else np.array([], dtype=complex)
# 按需省略最后一个导频
if omit_last_pilot and pilot_positions and len(output) > 0:
# 检查最后一个元素是否为导频
# 最后一个导频位置:找到最后插入的导频
# 检查输出的最后一个符号是否为导频
total_data_inserted = n_data
n_pilots = len(pilot_positions)
if n_pilots > 0 and total_data_inserted % pilot_interval == 0:
# 最后一个符号是导频 → 移除
output = output[:-1]
return output, len(output)
[文档]
def remove_pilots(
symbols: np.ndarray,
pilot_interval: int,
last_pilot_omitted: bool = True,
) -> np.ndarray:
"""从接收符号流中移除导频符号。
:param symbols: 含导频的接收符号。
:param pilot_interval: N 值(4、8 或 16)。
:param last_pilot_omitted: 发送侧是否已省略最后一个导频。
:returns: 移除导频后的数据符号。
"""
if pilot_interval <= 0:
return symbols.copy()
stride = pilot_interval + 1 # data + pilot
result = []
pos = 0
while pos < len(symbols):
remaining = len(symbols) - pos
if remaining >= stride:
# 完整组:取数据,跳过导频
result.append(symbols[pos : pos + pilot_interval])
pos += stride
else:
# 末尾不完整组(无导频或导频已省略)
result.append(symbols[pos:])
pos = len(symbols)
return np.concatenate(result) if result else np.array([], dtype=complex)