Lab043: add hardware diagnostics and HackRF comparison tools
This commit is contained in:
@@ -650,6 +650,60 @@ def locate_calibration_tone_start(
|
||||
raise RuntimeError("Начало двухтоновой калибровки не найдено по фиксированному порогу 10 дБ")
|
||||
|
||||
|
||||
def locate_calibration_tone_start_spectral(
|
||||
samples: np.ndarray,
|
||||
sample_rate_hz: float,
|
||||
block_samples: int = CALIBRATION_NFFT,
|
||||
hop_samples: int = CALIBRATION_NFFT // 4,
|
||||
required_consecutive_blocks: int = 3,
|
||||
) -> int:
|
||||
"""Найти начало по двум известным тонам с тем же порогом 10 дБ.
|
||||
|
||||
Резервный метод нужен, когда узкие тоны надёжны спектрально, но не дают
|
||||
десятикратного роста полной широкополосной мощности. Дополнительно
|
||||
применяется существующее ограничение геометрии тонов: ошибка такта не
|
||||
должна превышать 1000 ppm.
|
||||
"""
|
||||
|
||||
received = np.asarray(samples, dtype=np.complex128)
|
||||
if received.ndim != 1 or len(received) < block_samples:
|
||||
raise ValueError("Захват слишком короток для спектрального поиска калибровки")
|
||||
if (
|
||||
sample_rate_hz <= 0.0
|
||||
or block_samples < 1024
|
||||
or hop_samples <= 0
|
||||
or required_consecutive_blocks <= 0
|
||||
):
|
||||
raise ValueError("Параметры спектрального поиска должны быть положительными")
|
||||
window = np.hanning(block_samples)
|
||||
frequencies = np.fft.fftshift(
|
||||
np.fft.fftfreq(block_samples, d=1.0 / sample_rate_hz)
|
||||
)
|
||||
starts = np.arange(
|
||||
0,
|
||||
len(received) - block_samples + 1,
|
||||
hop_samples,
|
||||
dtype=np.int64,
|
||||
)
|
||||
active = np.zeros(len(starts), dtype=bool)
|
||||
for index, start in enumerate(starts):
|
||||
block = received[start : start + block_samples]
|
||||
centered = block - np.mean(block)
|
||||
spectrum = np.fft.fftshift(np.fft.fft(centered * window))
|
||||
estimate = estimate_calibration_from_spectrum(
|
||||
frequencies,
|
||||
np.abs(spectrum) ** 2,
|
||||
)
|
||||
active[index] = bool(estimate.valid)
|
||||
for index in range(len(active) - required_consecutive_blocks + 1):
|
||||
if bool(np.all(active[index : index + required_consecutive_blocks])):
|
||||
return int(starts[index])
|
||||
raise RuntimeError(
|
||||
"Начало двухтоновой калибровки не найдено спектрально: "
|
||||
"два тона не прошли неизменный порог 10 дБ и проверку геометрии"
|
||||
)
|
||||
|
||||
|
||||
def split_calibration_and_bpsk(
|
||||
samples: np.ndarray,
|
||||
plan: PrbsTransmissionPlan,
|
||||
@@ -659,7 +713,15 @@ def split_calibration_and_bpsk(
|
||||
"""Выделить тоны и BPSK по одной временной шкале одного захвата."""
|
||||
|
||||
received = np.asarray(samples, dtype=np.complex128)
|
||||
tone_start = locate_calibration_tone_start(received, actual_rx_sample_rate_hz)
|
||||
try:
|
||||
tone_start = locate_calibration_tone_start(received, actual_rx_sample_rate_hz)
|
||||
tone_start_locator = "broadband_power_10db"
|
||||
except RuntimeError:
|
||||
tone_start = locate_calibration_tone_start_spectral(
|
||||
received,
|
||||
actual_rx_sample_rate_hz,
|
||||
)
|
||||
tone_start_locator = "two_tone_spectral_10db"
|
||||
tone_duration_rx = round(
|
||||
plan.calibration_sample_count / actual_tx_sample_rate_hz * actual_rx_sample_rate_hz
|
||||
)
|
||||
@@ -684,6 +746,7 @@ def split_calibration_and_bpsk(
|
||||
received[bpsk_start:bpsk_end],
|
||||
{
|
||||
"tone_start_sample": int(tone_start),
|
||||
"tone_start_locator": tone_start_locator,
|
||||
"calibration_start_sample": int(calibration_start),
|
||||
"calibration_end_sample": int(calibration_end),
|
||||
"bpsk_nominal_start_sample": int(bpsk_nominal_start),
|
||||
|
||||
Reference in New Issue
Block a user