РИТМ SDR USRP передача и приём сигналов через РЧ-тракт
Author
engee.package.install("Engee-Device-Manager")
engee.package.start("Engee-Device-Manager")
using EngeeDSP, Statistics, Dates, Plots
using Main.EngeeDeviceManager.Targets.RITM_SDR_API
import Main.EngeeDeviceManager.Targets.RITM_SDR_API
function plot_signal(iq_data::Vector{ComplexF64}, fs_hz::Float64, title::String="";
time_samples::Int=1000, center_freq_hz::Float64=0.0,
signal_type::String="default", rbw_hz::Union{Nothing, Float64}=nothing)
plotlyjs()
if length(iq_data) == 0
RITM_SDR_API.@logmsg "✗ Нет данных"
return
end
is_wifi_mode = signal_type == "wi-fi"
if is_wifi_mode
idx = 1:10000
p1 = plot(real.(iq_data[idx]),
label="I",
color=:red,
linewidth=1.0)
plot!(p1, imag.(iq_data[idx]),
label="Q",
color=:blue,
linewidth=1.0)
ylabel!(p1, "Амплитуда")
title!(p1, "Сигнал")
plot!(p1, legend=:best, grid=true)
else
p1 = plot(real.(iq_data[1:min(time_samples, length(iq_data))]),
label="I",
color=:red,
linewidth=1.5)
plot!(p1, imag.(iq_data[1:min(time_samples, length(iq_data))]),
label="Q",
color=:blue,
linewidth=1.5)
ylabel!(p1, "Амплитуда")
title!(p1, "Сигнал")
plot!(p1, legend=:best, grid=true)
end
RBW = rbw_hz === nothing ? fs_hz / 4096 : rbw_hz
scope = EngeeDSP.spectrumAnalyzer()
scope.SampleRate = fs_hz
scope.Method = "filter-bank"
scope.FrequencySpan = "full"
scope.PlotAsTwoSidedSpectrum = true
scope.RBWSource = "property"
scope.RBW = RBW
scope.AveragingMethod = "exponential"
scope.ForgettingFactor = 0.9
scope.SpectrumType = "power"
if is_wifi_mode
scope.SpectrumUnits = "dBFS"
ylabel_spectrum = "Мощность (dBFS)"
else
scope.SpectrumUnits = "dBm"
ylabel_spectrum = "Мощность (дБм)"
end
scope.Window = "Kaiser"
scope.SidelobeAttenuation = 60
scope.FrequencyResolutionMethod = "rbw"
scope.FrequencyScale = "linear"
scope.ShowGrid = true
scope.ShowLegend = false
scope.LineWidth = 1.5
scope.Title = "Spectrum Analyzer - Filter bank method"
scope(iq_data)
spectrumdata = EngeeDSP.getSpectrumData(scope)
frequencies = spectrumdata["frequencies"]
spec = spectrumdata["spectrum"]
if is_wifi_mode && center_freq_hz > 0
f_offset_mhz = (frequencies .- center_freq_hz) / 1e6
xlabel_text = "Смещение частоты от $(center_freq_hz/1e6) МГц (МГц)"
plot_title = "Спектр Wi-Fi сигнала"
else
f_offset_mhz = (frequencies .- (is_wifi_mode ? center_freq_hz : 0)) / 1e6
xlabel_text = "Частота (МГц)"
plot_title = "Спектр сигнала"
end
p2 = plot(f_offset_mhz, spec,
linewidth=1.5,
label="RBW = $(round(RBW/1e3, digits=2)) кГц",
xlabel=xlabel_text,
ylabel=ylabel_spectrum,
title=plot_title,
grid=true)
combined_plot = plot(p1, p2,
layout=(2,1),
size=(900, 700),
plot_title=isempty(title) ? "Анализ сигнала" : title)
timestamp = Dates.format(now(), "yyyy-mm-dd_HH-MM-SS")
filename = "signal_$(timestamp).png"
savefig(combined_plot, filename)
RITM_SDR_API.@logmsg "✓ Сохранено: $filename"
return combined_plot
end
function generate_signal(fs_hz::Float64, n_samples::Int;
signal_type::String="multitone",
f_start::Float64=10e6,
f_end::Float64=100e6,
f_low::Float64=10e6,
f_high::Float64=100e6,
num_tones::Int=30,
f1::Float64=50e6,
f2::Float64=80e6,
noise_level::Float64=0.0)
t = (0:n_samples-1) / fs_hz
signal = zeros(ComplexF64, n_samples)
if signal_type == "multitone"
freqs_pos = LinRange(f_low, f_high, num_tones)
freqs_neg = -freqs_pos
for f in freqs_pos
signal .+= cispi.(2 * f * t)
end
for f in freqs_neg
signal .+= cispi.(2 * f * t)
end
RITM_SDR_API.@logmsg "✓ Сгенерирован многочастотный сигнал: $(num_tones) тонов ±[$(round(f_low/1e6, digits=2))-$(round(f_high/1e6, digits=2))] МГц"
elseif signal_type == "chirp"
chirp_rate = (f_end - f_start) / (n_samples / fs_hz)
signal = cispi.(2 * (f_start * t + 0.5 * chirp_rate * t.^2))
RITM_SDR_API.@logmsg "✓ Сгенерирован ЛЧМ сигнал: $(round(f_start/1e6, digits=2))-$(round(f_end/1e6, digits=2)) МГц"
elseif signal_type == "two_tone"
signal = cispi.(2 * f1 * t) + cispi.(2 * f2 * t) + cispi.(-2 * f1 * t) + cispi.(-2 * f2 * t)
if noise_level > 0
noise = noise_level * ComplexF64.(randn(n_samples), randn(n_samples))
signal .+= noise
end
RITM_SDR_API.@logmsg "✓ Сгенерирован двухтоновый сигнал с симметричным спектром: ±$(round(f1/1e6, digits=2)) МГц и ±$(round(f2/1e6, digits=2)) МГц"
else
error("Неизвестный тип сигнала: $signal_type")
end
signal = signal / maximum(abs.(signal)) * 0.95
return signal
end
function configure_sdr(client::RITM_SDR_API.RITMClient, freq_hz::Int=0)
RITM_SDR_API.set_ip_core(client, "0x80030100", "0x00f")
RITM_SDR_API.set_ip_core(client, "0x80030104", "0x1")
RITM_SDR_API.set_ip_core(client, "0x80080100", "0x0")
RITM_SDR_API.set_ip_core(client, "0x8008010C", "0x0")
RITM_SDR_API.set_ip_core(client, "0x80080104", "0x0")
RITM_SDR_API.set_ip_core(client, "0x80080118", "0x0")
RITM_SDR_API.set_ip_core(client, "0x80080110", "0x0")
RITM_SDR_API.set_ip_core(client, "0x80080114", "0x0")
RITM_SDR_API.set_ip_core(client, "0x8008011C", "0x000000000")
RITM_SDR_API.set_ip_core(client, "0x80030110", "0x400")
RITM_SDR_API.set_ip_core(client, "0x80030118", "0x400")
RITM_SDR_API.set_ip_core(client, "0x80030114", "0x1")
RITM_SDR_API.set_ip_core(client, "0x8003011C", "0x1")
RITM_SDR_API.set_ip_core(client, "0x8003010C", "0x1")
RITM_SDR_API.set_ip_core(client, "0x80030108", "0x1")
RITM_SDR_API.set_ip_core(client, "0x80070124", "0x1")
RITM_SDR_API.set_ip_core(client, "0x8007012C", "0x1")
RITM_SDR_API.set_ip_core(client, "0x80070100", "0x1")
RITM_SDR_API.set_ip_core(client, "0x80070104", "0x1")
RITM_SDR_API.set_ip_core(client, "0x80070108", "0x0")
RITM_SDR_API.set_ip_core(client, "0x80070130", "0x1")
RITM_SDR_API.set_ip_core(client, "0x800F0100", "0x1")
RITM_SDR_API.set_ip_core(client, "0x80100100", "0x1")
RITM_SDR_API.set_ip_core(client, "0x80050100", "0x007")
RITM_SDR_API.set_ip_core(client, "0x80050104", "0x1")
RITM_SDR_API.set_ip_core(client, "0x80050110", "0x400")
RITM_SDR_API.set_ip_core(client, "0x8005010C", "0x400")
RITM_SDR_API.set_ip_core(client, "0x80050114", "0x1")
RITM_SDR_API.set_ip_core(client, "0x80050118", "0x1")
RITM_SDR_API.set_ip_core(client, "0x80050108", "0x1")
if freq_hz > 0
RITM_SDR_API.set_tx_carrier_freq_hz(client, freq_hz)
end
sleep(0.5)
return true
end
function receive_iq(client::RITM_SDR_API.RITMClient, samples::Int=600000)
RITM_SDR_API.@logmsg "Запуск приема ($samples отсчетов)"
RITM_SDR_API.reset_buffers(client)
RITM_SDR_API.reset_RX_DMA(client)
sleep(0.2)
if !RITM_SDR_API.receive_to_ram(client, samples)
RITM_SDR_API.@logmsg "✗ Ошибка запуска приема"
return nothing
end
RITM_SDR_API.@logmsg "Ожидание ОЗУ..."
max_attempts = 5
dma_completed = false
sleep(2)
for attempt in 1:max_attempts
if RITM_SDR_API.is_dma_transfer_end(client)
dma_completed = true
break
end
sleep(0.5)
end
rx_raw = RITM_SDR_API._get_data(client)
if rx_raw === nothing
RITM_SDR_API.@logmsg "✗ Ошибка получения данных"
return nothing
end
iq_data = RITM_SDR_API.parse_rx_data(rx_raw)
if length(iq_data) == 0
RITM_SDR_API.@logmsg "✗ Ошибка парсинга"
return nothing
end
return iq_data
end
function wifi_channel_frequency(band::String, channel::Int)
if band == "2.4GHz"
if 1 <= channel <= 13
return 2412 + (channel - 1) * 5
elseif channel == 14
return 2484
else
throw(ArgumentError("В диапазоне 2.4GHz допустимы каналы 1–14, получен $channel"))
end
elseif band == "5GHz"
channel_to_freq = Dict{Int,Int}(
36 => 5180, 40 => 5200, 44 => 5220, 48 => 5240,
52 => 5260, 56 => 5280, 60 => 5300, 64 => 5320,
100 => 5500, 104 => 5520, 108 => 5540, 112 => 5560,
116 => 5580, 120 => 5600, 124 => 5620, 128 => 5640,
132 => 5660, 136 => 5680, 140 => 5700, 144 => 5720,
149 => 5745, 153 => 5765, 157 => 5785, 161 => 5805,
165 => 5825, 169 => 5845, 173 => 5865, 177 => 5885,
)
if haskey(channel_to_freq, channel)
return channel_to_freq[channel]
else
throw(ArgumentError("Канал $channel не является известным 20‑МГц каналом в диапазоне 5GHz. " *
"Допустимые каналы: $(sort(collect(keys(channel_to_freq))))"))
end
else
throw(ArgumentError("Допустимые диапазоны: \"2.4GHz\", \"5GHz\". Получен: $band"))
end
end
RITM_SDR_API.@logmsg "✓ Библиотека вспомогательных функций RITM SDR USRP готова"