Files
SDR-Rover/tests/lab024a_pluto_rx_capture.py

172 lines
5.4 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.
"""
Lab024A. Первый приём реальных IQ-сэмплов с Pluto+.
Схема:
антенна 40860 МГц -> RX1
Передатчики TX1 и TX2 не используются.
"""
from pathlib import Path
import json
import adi
import matplotlib.pyplot as plt
import numpy as np
# ---------------------------------------------------------------------
# Параметры приёмника
# ---------------------------------------------------------------------
PLUTO_URI = "ip:192.168.2.1"
CENTER_FREQUENCY_HZ = 100_000_000
SAMPLE_RATE_HZ = 2_400_000
RX_BANDWIDTH_HZ = 2_000_000
RX_BUFFER_SIZE = 262_144
OUTPUT_DIRECTORY = Path("data/raw")
IQ_FILE_PATH = OUTPUT_DIRECTORY / "lab024a_pluto_rx_100mhz.npy"
METADATA_FILE_PATH = OUTPUT_DIRECTORY / "lab024a_pluto_rx_100mhz.json"
SPECTRUM_FILE_PATH = OUTPUT_DIRECTORY / "lab024a_pluto_rx_100mhz_spectrum.png"
def calculate_spectrum(
samples: np.ndarray,
sample_rate_hz: float,
center_frequency_hz: float,
) -> tuple[np.ndarray, np.ndarray]:
"""
Рассчитывает спектр принятого комплексного IQ-сигнала.
Возвращает:
frequencies_hz — абсолютные радиочастоты;
power_db — относительная мощность спектра в дБ.
"""
sample_count = len(samples)
window = np.hanning(sample_count)
windowed_samples = samples * window
spectrum = np.fft.fftshift(np.fft.fft(windowed_samples))
power = np.abs(spectrum) ** 2
power_db = 10.0 * np.log10(power + 1e-12)
power_db -= np.max(power_db)
baseband_frequencies_hz = np.fft.fftshift(
np.fft.fftfreq(sample_count, d=1.0 / sample_rate_hz)
)
absolute_frequencies_hz = (
center_frequency_hz + baseband_frequencies_hz
)
return absolute_frequencies_hz, power_db
def main() -> None:
OUTPUT_DIRECTORY.mkdir(parents=True, exist_ok=True)
print("Подключение к Pluto+...")
sdr = adi.Pluto(uri=PLUTO_URI)
# Используем только первый приёмный канал RX1.
sdr.rx_enabled_channels = [0]
sdr.sample_rate = SAMPLE_RATE_HZ
sdr.rx_lo = CENTER_FREQUENCY_HZ
sdr.rx_rf_bandwidth = RX_BANDWIDTH_HZ
# Автоматическая регулировка усиления.
sdr.gain_control_mode_chan0 = "slow_attack"
sdr.rx_buffer_size = RX_BUFFER_SIZE
print()
print("Параметры приёмника:")
print(f" URI: {PLUTO_URI}")
print(f" Центральная частота: {sdr.rx_lo / 1e6:.3f} МГц")
print(f" Частота дискретизации: {sdr.sample_rate / 1e6:.3f} Мвыб/с")
print(f" Полоса RX: {sdr.rx_rf_bandwidth / 1e6:.3f} МГц")
print(f" Режим усиления: {sdr.gain_control_mode_chan0}")
print(f" Размер буфера: {sdr.rx_buffer_size} отсчётов")
print()
print("Получение IQ-сэмплов...")
# Первый буфер после перенастройки иногда содержит переходный процесс.
_ = sdr.rx()
# Второй буфер сохраняем и анализируем.
samples = np.asarray(sdr.rx(), dtype=np.complex64)
print("IQ-сэмплы получены.")
np.save(IQ_FILE_PATH, samples)
mean_value = np.mean(samples)
rms_value = np.sqrt(np.mean(np.abs(samples) ** 2))
peak_value = np.max(np.abs(samples))
metadata = {
"pluto_uri": PLUTO_URI,
"center_frequency_hz": int(sdr.rx_lo),
"sample_rate_hz": int(sdr.sample_rate),
"rx_bandwidth_hz": int(sdr.rx_rf_bandwidth),
"gain_control_mode": sdr.gain_control_mode_chan0,
"sample_count": int(len(samples)),
"sample_dtype": str(samples.dtype),
"mean_i": float(np.real(mean_value)),
"mean_q": float(np.imag(mean_value)),
"rms": float(rms_value),
"peak": float(peak_value),
}
with METADATA_FILE_PATH.open("w", encoding="utf-8") as metadata_file:
json.dump(metadata, metadata_file, ensure_ascii=False, indent=4)
frequencies_hz, power_db = calculate_spectrum(
samples=samples,
sample_rate_hz=float(sdr.sample_rate),
center_frequency_hz=float(sdr.rx_lo),
)
plt.figure(figsize=(12, 6))
plt.plot(frequencies_hz / 1e6, power_db)
plt.title("Lab024A. Спектр сигнала, принятого Pluto+")
plt.xlabel("Частота, МГц")
plt.ylabel("Относительная мощность, дБ")
plt.grid(True)
plt.ylim(-100, 5)
plt.tight_layout()
plt.savefig(SPECTRUM_FILE_PATH, dpi=150)
plt.show()
print()
print("Статистика:")
print(f" Количество сэмплов: {len(samples)}")
print(f" Тип данных: {samples.dtype}")
print(f" Среднее I: {np.real(mean_value):.3f}")
print(f" Среднее Q: {np.imag(mean_value):.3f}")
print(f" RMS: {rms_value:.3f}")
print(f" Пиковая амплитуда: {peak_value:.3f}")
print()
print("Созданы файлы:")
print(f" IQ: {IQ_FILE_PATH}")
print(f" Метаданные:{METADATA_FILE_PATH}")
print(f" Спектр: {SPECTRUM_FILE_PATH}")
print()
print("Lab024A выполнена успешно.")
if __name__ == "__main__":
main()