Наверное все, хоть немного интересующиеся радиосвязью, знают что с помощью SDR-приемника возможно принимать и обрабатывать широкую полосу спектра радиодиапазона. Собственно, отображением спектра в таких программах как HDSDR или SDR# никого не удивить. Я покажу как построить псевдо-3D спектр принимаемых станций с помощью RTL-SDR, GNU Radio и примерно 100 строк кода на языке Python.
Также мы возьмем приемник посерьезнее, и посмотрим на весь FM спектр 88-108МГц.
Продолжение под катом.
Технически, задача довольно проста. SDR-приемник оцифровывает входящий радиосигнал с помощью довольно быстрого АЦП. На выходе мы получаем широкополосный IQ-сигнал в виде массива чисел, приходящих с АЦП с частотой, соответствующей частоте дискретизации. Эта же частота определяет максимальную ширину полосы, принимаемую приемником. Все тоже самое, что и в звуковой карте, только в секунду мы имеем не 22050, а 2000000 или даже 10000000 значений. Для того, чтобы вывести спектр на экран, мы должны выполнить над массивом данных преобразование Фурье. Дальше остается вывести все это на экран, и задача решена. Остается лишь вопрос, как обойтись минимумом кода, для чего нам поможет GNU Radio.
Для тестов нам также понадобится RTL-SDR приемник, цена которого составляет порядка 30$. Он позволяет принимать сигналы в диапазоне частот примерно от 70 до 1700МГц и шириной полосы до 2МГц:
Если кто захочет повторить тесты с RTL-SDR самостоятельно, приемник нужен именно такой как на фото. Есть более дешевые клоны, но они хуже качеством.
Ну а мы приступим.
GNU Radio
Граф связей в GNU Radio показан на рисунке:
Как можно видеть, мы получаем данные с приемника, затем преобразуем непрерывный поток в набор «векторов» размером 1024, над этими блоками выполняем FFT, преобразуем значения из комплексных в вещественные, и отправляем данные по UDP. Разумеется, все это можно было бы сделать и непосредственно в Python с помощью SoapySDR и numpy, но объем кода был бы несколько больше.
Блок QT GUI Frequency Sink нужен для «отладки», с ним можно убедиться что сигналы радиостанций действительно принимаются и при необходимости настроить усиление приемника. Во время работы программы картинка должна быть примерно такой:
Если все работает, GUI Frequency Sink можно отключить, а в настройках проекта указать «No GUI», чтобы не тратить зря ресурсы. В принципе, эту программу дальше можно запускать как сервис, без какого-либо UI.
Отрисовка
Поскольку мы передаем данные по UDP, то принимать их мы можем любым клиентом, и даже на другом ПК. Будем использовать Python, для прототипа этого вполне достаточно.
Сначала мы получаем данные по UDP:
fft_size = 1024
udp_data = None
UDP_IP = "127.0.0.1"
UDP_PORT = 40868
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((UDP_IP, UDP_PORT))
sock.settimeout(0.5)
try:
data, addr = sock.recvfrom(fft_size * 4)
if len(data) == 4096:
udp_data = np.frombuffer(data, dtype=np.float32)
return True
except socket.timeout:
pass
Т.к. мы будем работать с графикой, удобно воспользоваться библиотекой pygame. Рисование 3D-спектра делается просто, мы храним данные в массиве, и выводим линии сверху вниз, от более старых к более новым.
fft_size = 1024
depth = 255
fft_data = np.zeros([depth, fft_size])
def draw_image(screen, font):
x_left, x_right, y_bottom = 0, img_size[0], img_size[1] - 5
# Draw spectrum in pseudo-3d
for d in reversed(range(depth)):
for x in range(fft_size - 1):
d_x1, d_x2, d_y1, d_y2 = x + d, x + d + 1, y_bottom - int(y_ampl*fft_data[d][x]) - y_shift - d, y_bottom - int(y_ampl*fft_data[d][x+1]) - y_shift - d
if d_y1 > y_bottom - 34: d_y1 = y_bottom - 34
if d_y2 > y_bottom - 34: d_y2 = y_bottom - 34
dim = 1 - 0.8*(d/depth)
color = int(dim*data_2_color(fft_data[d][x]))
pygame.draw.line(screen, (color//2,color,0) if d > 0 else (0, 250, 0), (d_x1, d_y1), (d_x2, d_y2), (2 if d == 0 else 1))
Последнее, это вывод частот и названий станций на экран. Преобразование Фурье дает на выходе 1024 точки, соответствующие ширине полосы пропускания приемника. Мы также знаем центральную частоту, так что вычисление позиции делается элементарной школьной пропорцией.
stations = [("101.8", 101.8), ("102.1", 102.1), ("102.4", 102.4), ... ]
for st_name, freq in stations:
x_pos = fft_size*(freq - center_freq)*1000000//sample_rate
textsurface = font.render(st_name, False, (255, 255, 0))
screen.blit(textsurface, (img_size[0]//2 + x_pos - textsurface.get_width()//2, y_bottom - 22))
Собственно и все, объединяем все это вместе, запускаем обе программы одновременно, и получаем на экране real-time панораму, показывающую работающие в данный момент FM-станции:
Легко можно видеть, что разные станции вещают с разным уровнем, или по ширине полосы отличить моно-вещание от стерео.
Ну а теперь обещанная панорама всего FM-диапазона. Для этого нам придется отложить RTL-SDR и взять приемник посерьезнее, например такой:
Я использую профессиональную модель от Ettus Research, но с точки зрения кода все тоже самое, достаточно поменять в GNU Radio один блок на другой. А вот так это выглядит на спектре при ширине полосы приема 24МГц:
Любопытно, насколько разные уровни сигналов имеют разные радиостанции.
Разумеется, принимать можно не только станции FM-диапазона, но и любые другие, в пределах рабочих частот SDR-приемника. Для примера, вот так выглядит авиадиапазон:
Можно видеть постоянно активные частоты (вероятно метеослужба ATIS) и периодически возникающий радиообмен. А вот так выглядит фрагмент спектра GSM, однако, его сигнал шире чем 24МГц, и целиком не помещается.
Заключение
Как можно видеть, изучение радиоэфира процесс довольно занимательный, даже в 3D. Разумеется, здесь не было цели сделать «еще один спектроанализатор», это лишь прототип, сделанный для фана. Увы, отрисовка работает медленно, для вывода нескольких тысяч примитивов Python это не лучший выбор. Также алгоритм раскрашивания линий оставляет желать лучшего, в идеале, он может быть более гибким.
Для желающих поэкспериментировать самостоятельно, исходный код:
import numpy as np
from matplotlib import pyplot as plt
from PIL import Image, ImageDraw
import sys
import pygame
from pygame.locals import *
from threading import Thread
import io
import cv2
import time
import socket
# FFT
receiver_name = "RTL-SDR"
center_freq = 102.5
sample_rate = 1800000
stations = [("101.8", 101.8), ("102.1", 102.1), ("102.4", 102.4), ("102.7", 102.7), ("103.0", 103.0), ("103.2", 103.2)]
# Load data from UDP
UDP_IP = "127.0.0.1"
UDP_PORT = 40868
udp_data = None
sock = None
# Panorama history
fft_size = 1024
depth = 255
fft_data = np.zeros([depth, fft_size])
# Canvas and draw
img_size = (fft_size, fft_size*9//16)
y_ampl = 90
color_ampl = 70
y_shift = 250
def udp_prepare():
global sock
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((UDP_IP, UDP_PORT))
sock.settimeout(0.5)
def udp_getdata():
global sock, udp_data
try:
data, addr = sock.recvfrom(fft_size * 4)
if len(data) == 4096:
udp_data = np.frombuffer(data, dtype=np.float32)
return True
except socket.timeout:
pass
return False
def clear_data():
for y in range(depth):
fft_data[y, :] = np.full((fft_size,), -1024)
def add_new_line():
global udp_data, fft_data
# Shift old data up
for y in reversed(range(depth - 1)):
fft_data[y + 1, :] = fft_data[y, :]
# Put new data at the bottom line
if udp_data is not None:
fft_data[0, :] = udp_data
def data_2_color(data):
c = -data + 2 # TODO: detect noise floor of the spectrum
color = 150 - int(color_ampl * c)
if color < 20:
color = 20
if color > 150:
color = 150
return color
def draw_image(screen, font):
x_left, x_right, y_bottom = 0, img_size[0], img_size[1] - 5
# Draw spectrum in pseudo-3d
for d in reversed(range(depth)):
for x in range(fft_size - 1):
d_x1, d_x2, d_y1, d_y2 = x + d, x + d + 1, y_bottom - int(y_ampl*fft_data[d][x]) - y_shift - d, y_bottom - int(y_ampl*fft_data[d][x+1]) - y_shift - d
if d_y1 > y_bottom - 34: d_y1 = y_bottom - 34
if d_y2 > y_bottom - 34: d_y2 = y_bottom - 34
dim = 1 - 0.8*(d/depth)
color = int(dim*data_2_color(fft_data[d][x]))
pygame.draw.line(screen, (color//2,color,0) if d > 0 else (0, 250, 0), (d_x1, d_y1), (d_x2, d_y2), (2 if d == 0 else 1))
# Bottom line
pygame.draw.line(screen, (0,100,0), (x_left, y_bottom - 30), (x_right, y_bottom - 30), 2)
# Station names
for st_name, freq in stations:
x_pos = fft_size*(freq - center_freq)*1000000//sample_rate
textsurface = font.render(st_name, False, (255, 255, 0))
screen.blit(textsurface, (img_size[0]//2 + x_pos - textsurface.get_width()//2, y_bottom - 22))
text_mhz = font.render("MHz", False, (255, 255, 0))
screen.blit(text_mhz, (img_size[0] - 5 - text_mhz.get_width(), y_bottom - 22))
if __name__ == "__main__":
# UI init
screen = pygame.display.set_mode(img_size)
pygame.display.set_caption(receiver_name)
pygame.font.init()
font = pygame.font.SysFont('Arial Bold', 30)
# Subscribe to UDP
clear_data()
udp_prepare()
# Main loop
is_active = True
while is_active:
# Get new data
if udp_getdata():
add_new_line()
# Update screen
screen.fill((0, 0, 0))
draw_image(screen, font)
pygame.display.flip()
# Check sys events
for events in pygame.event.get():
if events.type == QUIT:
is_active = False
<?xml version='1.0' encoding='utf-8'?>
<?grc format='1' created='3.7.13'?>
<flow_graph>
<timestamp>Sun Jun 7 13:17:58 2020</timestamp>
<block>
<key>options</key>
<param>
<key>author</key>
<value></value>
</param>
<param>
<key>window_size</key>
<value></value>
</param>
<param>
<key>category</key>
<value>[GRC Hier Blocks]</value>
</param>
<param>
<key>comment</key>
<value></value>
</param>
<param>
<key>description</key>
<value></value>
</param>
<param>
<key>_enabled</key>
<value>True</value>
</param>
<param>
<key>_coordinate</key>
<value>(8, 8)</value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
</param>
<param>
<key>generate_options</key>
<value>no_gui</value>
</param>
<param>
<key>hier_block_src_path</key>
<value>.:</value>
</param>
<param>
<key>id</key>
<value>top_block</value>
</param>
<param>
<key>max_nouts</key>
<value>0</value>
</param>
<param>
<key>qt_qss_theme</key>
<value></value>
</param>
<param>
<key>realtime_scheduling</key>
<value></value>
</param>
<param>
<key>run_command</key>
<value>{python} -u {filename}</value>
</param>
<param>
<key>run_options</key>
<value>prompt</value>
</param>
<param>
<key>run</key>
<value>True</value>
</param>
<param>
<key>sizing_mode</key>
<value>fixed</value>
</param>
<param>
<key>thread_safe_setters</key>
<value></value>
</param>
<param>
<key>title</key>
<value></value>
</param>
<param>
<key>placement</key>
<value>(0,0)</value>
</param>
</block>
<block>
<key>variable</key>
<param>
<key>comment</key>
<value></value>
</param>
<param>
<key>_enabled</key>
<value>True</value>
</param>
<param>
<key>_coordinate</key>
<value>(168, 12)</value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
</param>
<param>
<key>id</key>
<value>fft_size</value>
</param>
<param>
<key>value</key>
<value>1024</value>
</param>
</block>
<block>
<key>variable</key>
<param>
<key>comment</key>
<value></value>
</param>
<param>
<key>_enabled</key>
<value>True</value>
</param>
<param>
<key>_coordinate</key>
<value>(256, 12)</value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
</param>
<param>
<key>id</key>
<value>samp_rate</value>
</param>
<param>
<key>value</key>
<value>1800000</value>
</param>
</block>
<block>
<key>blocks_complex_to_mag_squared</key>
<param>
<key>alias</key>
<value></value>
</param>
<param>
<key>comment</key>
<value></value>
</param>
<param>
<key>affinity</key>
<value></value>
</param>
<param>
<key>_enabled</key>
<value>True</value>
</param>
<param>
<key>_coordinate</key>
<value>(648, 108)</value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
</param>
<param>
<key>id</key>
<value>blocks_complex_to_mag_squared_0</value>
</param>
<param>
<key>maxoutbuf</key>
<value>0</value>
</param>
<param>
<key>minoutbuf</key>
<value>0</value>
</param>
<param>
<key>vlen</key>
<value>fft_size</value>
</param>
</block>
<block>
<key>blocks_nlog10_ff</key>
<param>
<key>alias</key>
<value></value>
</param>
<param>
<key>comment</key>
<value></value>
</param>
<param>
<key>affinity</key>
<value></value>
</param>
<param>
<key>_enabled</key>
<value>True</value>
</param>
<param>
<key>_coordinate</key>
<value>(832, 100)</value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
</param>
<param>
<key>id</key>
<value>blocks_nlog10_ff_0</value>
</param>
<param>
<key>maxoutbuf</key>
<value>0</value>
</param>
<param>
<key>minoutbuf</key>
<value>0</value>
</param>
<param>
<key>vlen</key>
<value>fft_size</value>
</param>
<param>
<key>k</key>
<value>0</value>
</param>
<param>
<key>n</key>
<value>1</value>
</param>
</block>
<block>
<key>blocks_stream_to_vector</key>
<param>
<key>alias</key>
<value></value>
</param>
<param>
<key>comment</key>
<value></value>
</param>
<param>
<key>affinity</key>
<value></value>
</param>
<param>
<key>_enabled</key>
<value>True</value>
</param>
<param>
<key>_coordinate</key>
<value>(256, 108)</value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
</param>
<param>
<key>id</key>
<value>blocks_stream_to_vector_0</value>
</param>
<param>
<key>type</key>
<value>complex</value>
</param>
<param>
<key>maxoutbuf</key>
<value>0</value>
</param>
<param>
<key>minoutbuf</key>
<value>0</value>
</param>
<param>
<key>num_items</key>
<value>fft_size</value>
</param>
<param>
<key>vlen</key>
<value>1</value>
</param>
</block>
<block>
<key>blocks_udp_sink</key>
<param>
<key>alias</key>
<value></value>
</param>
<param>
<key>comment</key>
<value></value>
</param>
<param>
<key>affinity</key>
<value></value>
</param>
<param>
<key>ipaddr</key>
<value>127.0.0.1</value>
</param>
<param>
<key>port</key>
<value>40868</value>
</param>
<param>
<key>_enabled</key>
<value>True</value>
</param>
<param>
<key>_coordinate</key>
<value>(1000, 76)</value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
</param>
<param>
<key>id</key>
<value>blocks_udp_sink_0</value>
</param>
<param>
<key>type</key>
<value>float</value>
</param>
<param>
<key>psize</key>
<value>fft_size*4</value>
</param>
<param>
<key>eof</key>
<value>True</value>
</param>
<param>
<key>vlen</key>
<value>fft_size</value>
</param>
</block>
<block>
<key>fft_vxx</key>
<param>
<key>alias</key>
<value></value>
</param>
<param>
<key>comment</key>
<value></value>
</param>
<param>
<key>affinity</key>
<value></value>
</param>
<param>
<key>_enabled</key>
<value>True</value>
</param>
<param>
<key>fft_size</key>
<value>fft_size</value>
</param>
<param>
<key>forward</key>
<value>True</value>
</param>
<param>
<key>_coordinate</key>
<value>(424, 76)</value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
</param>
<param>
<key>id</key>
<value>fft_vxx_0</value>
</param>
<param>
<key>type</key>
<value>complex</value>
</param>
<param>
<key>maxoutbuf</key>
<value>0</value>
</param>
<param>
<key>minoutbuf</key>
<value>0</value>
</param>
<param>
<key>nthreads</key>
<value>1</value>
</param>
<param>
<key>shift</key>
<value>True</value>
</param>
<param>
<key>window</key>
<value>window.blackmanharris(fft_size)</value>
</param>
</block>
<block>
<key>qtgui_freq_sink_x</key>
<param>
<key>autoscale</key>
<value>False</value>
</param>
<param>
<key>average</key>
<value>1.0</value>
</param>
<param>
<key>axislabels</key>
<value>True</value>
</param>
<param>
<key>bw</key>
<value>samp_rate</value>
</param>
<param>
<key>alias</key>
<value></value>
</param>
<param>
<key>fc</key>
<value>102.5e6</value>
</param>
<param>
<key>comment</key>
<value></value>
</param>
<param>
<key>ctrlpanel</key>
<value>False</value>
</param>
<param>
<key>affinity</key>
<value></value>
</param>
<param>
<key>_enabled</key>
<value>0</value>
</param>
<param>
<key>fftsize</key>
<value>1024</value>
</param>
<param>
<key>_coordinate</key>
<value>(256, 188)</value>
</param>
<param>
<key>gui_hint</key>
<value></value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
</param>
<param>
<key>grid</key>
<value>False</value>
</param>
<param>
<key>id</key>
<value>qtgui_freq_sink_x_0</value>
</param>
<param>
<key>legend</key>
<value>True</value>
</param>
<param>
<key>alpha1</key>
<value>1.0</value>
</param>
<param>
<key>color1</key>
<value>"blue"</value>
</param>
<param>
<key>label1</key>
<value></value>
</param>
<param>
<key>width1</key>
<value>1</value>
</param>
<param>
<key>alpha10</key>
<value>1.0</value>
</param>
<param>
<key>color10</key>
<value>"dark blue"</value>
</param>
<param>
<key>label10</key>
<value></value>
</param>
<param>
<key>width10</key>
<value>1</value>
</param>
<param>
<key>alpha2</key>
<value>1.0</value>
</param>
<param>
<key>color2</key>
<value>"red"</value>
</param>
<param>
<key>label2</key>
<value></value>
</param>
<param>
<key>width2</key>
<value>1</value>
</param>
<param>
<key>alpha3</key>
<value>1.0</value>
</param>
<param>
<key>color3</key>
<value>"green"</value>
</param>
<param>
<key>label3</key>
<value></value>
</param>
<param>
<key>width3</key>
<value>1</value>
</param>
<param>
<key>alpha4</key>
<value>1.0</value>
</param>
<param>
<key>color4</key>
<value>"black"</value>
</param>
<param>
<key>label4</key>
<value></value>
</param>
<param>
<key>width4</key>
<value>1</value>
</param>
<param>
<key>alpha5</key>
<value>1.0</value>
</param>
<param>
<key>color5</key>
<value>"cyan"</value>
</param>
<param>
<key>label5</key>
<value></value>
</param>
<param>
<key>width5</key>
<value>1</value>
</param>
<param>
<key>alpha6</key>
<value>1.0</value>
</param>
<param>
<key>color6</key>
<value>"magenta"</value>
</param>
<param>
<key>label6</key>
<value></value>
</param>
<param>
<key>width6</key>
<value>1</value>
</param>
<param>
<key>alpha7</key>
<value>1.0</value>
</param>
<param>
<key>color7</key>
<value>"yellow"</value>
</param>
<param>
<key>label7</key>
<value></value>
</param>
<param>
<key>width7</key>
<value>1</value>
</param>
<param>
<key>alpha8</key>
<value>1.0</value>
</param>
<param>
<key>color8</key>
<value>"dark red"</value>
</param>
<param>
<key>label8</key>
<value></value>
</param>
<param>
<key>width8</key>
<value>1</value>
</param>
<param>
<key>alpha9</key>
<value>1.0</value>
</param>
<param>
<key>color9</key>
<value>"dark green"</value>
</param>
<param>
<key>label9</key>
<value></value>
</param>
<param>
<key>width9</key>
<value>1</value>
</param>
<param>
<key>maxoutbuf</key>
<value>0</value>
</param>
<param>
<key>minoutbuf</key>
<value>0</value>
</param>
<param>
<key>name</key>
<value>""</value>
</param>
<param>
<key>nconnections</key>
<value>1</value>
</param>
<param>
<key>showports</key>
<value>True</value>
</param>
<param>
<key>freqhalf</key>
<value>True</value>
</param>
<param>
<key>tr_chan</key>
<value>0</value>
</param>
<param>
<key>tr_level</key>
<value>0.0</value>
</param>
<param>
<key>tr_mode</key>
<value>qtgui.TRIG_MODE_FREE</value>
</param>
<param>
<key>tr_tag</key>
<value>""</value>
</param>
<param>
<key>type</key>
<value>complex</value>
</param>
<param>
<key>update_time</key>
<value>0.10</value>
</param>
<param>
<key>wintype</key>
<value>firdes.WIN_BLACKMAN_hARRIS</value>
</param>
<param>
<key>label</key>
<value>Relative Gain</value>
</param>
<param>
<key>ymax</key>
<value>10</value>
</param>
<param>
<key>ymin</key>
<value>-140</value>
</param>
<param>
<key>units</key>
<value>dB</value>
</param>
</block>
<block>
<key>rtlsdr_source</key>
<param>
<key>alias</key>
<value></value>
</param>
<param>
<key>ant0</key>
<value></value>
</param>
<param>
<key>bb_gain0</key>
<value>20</value>
</param>
<param>
<key>bw0</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode0</key>
<value>0</value>
</param>
<param>
<key>corr0</key>
<value>0</value>
</param>
<param>
<key>freq0</key>
<value>102.5e6</value>
</param>
<param>
<key>gain_mode0</key>
<value>False</value>
</param>
<param>
<key>if_gain0</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode0</key>
<value>0</value>
</param>
<param>
<key>gain0</key>
<value>20</value>
</param>
<param>
<key>ant10</key>
<value></value>
</param>
<param>
<key>bb_gain10</key>
<value>20</value>
</param>
<param>
<key>bw10</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode10</key>
<value>0</value>
</param>
<param>
<key>corr10</key>
<value>0</value>
</param>
<param>
<key>freq10</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode10</key>
<value>False</value>
</param>
<param>
<key>if_gain10</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode10</key>
<value>0</value>
</param>
<param>
<key>gain10</key>
<value>10</value>
</param>
<param>
<key>ant11</key>
<value></value>
</param>
<param>
<key>bb_gain11</key>
<value>20</value>
</param>
<param>
<key>bw11</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode11</key>
<value>0</value>
</param>
<param>
<key>corr11</key>
<value>0</value>
</param>
<param>
<key>freq11</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode11</key>
<value>False</value>
</param>
<param>
<key>if_gain11</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode11</key>
<value>0</value>
</param>
<param>
<key>gain11</key>
<value>10</value>
</param>
<param>
<key>ant12</key>
<value></value>
</param>
<param>
<key>bb_gain12</key>
<value>20</value>
</param>
<param>
<key>bw12</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode12</key>
<value>0</value>
</param>
<param>
<key>corr12</key>
<value>0</value>
</param>
<param>
<key>freq12</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode12</key>
<value>False</value>
</param>
<param>
<key>if_gain12</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode12</key>
<value>0</value>
</param>
<param>
<key>gain12</key>
<value>10</value>
</param>
<param>
<key>ant13</key>
<value></value>
</param>
<param>
<key>bb_gain13</key>
<value>20</value>
</param>
<param>
<key>bw13</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode13</key>
<value>0</value>
</param>
<param>
<key>corr13</key>
<value>0</value>
</param>
<param>
<key>freq13</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode13</key>
<value>False</value>
</param>
<param>
<key>if_gain13</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode13</key>
<value>0</value>
</param>
<param>
<key>gain13</key>
<value>10</value>
</param>
<param>
<key>ant14</key>
<value></value>
</param>
<param>
<key>bb_gain14</key>
<value>20</value>
</param>
<param>
<key>bw14</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode14</key>
<value>0</value>
</param>
<param>
<key>corr14</key>
<value>0</value>
</param>
<param>
<key>freq14</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode14</key>
<value>False</value>
</param>
<param>
<key>if_gain14</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode14</key>
<value>0</value>
</param>
<param>
<key>gain14</key>
<value>10</value>
</param>
<param>
<key>ant15</key>
<value></value>
</param>
<param>
<key>bb_gain15</key>
<value>20</value>
</param>
<param>
<key>bw15</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode15</key>
<value>0</value>
</param>
<param>
<key>corr15</key>
<value>0</value>
</param>
<param>
<key>freq15</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode15</key>
<value>False</value>
</param>
<param>
<key>if_gain15</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode15</key>
<value>0</value>
</param>
<param>
<key>gain15</key>
<value>10</value>
</param>
<param>
<key>ant16</key>
<value></value>
</param>
<param>
<key>bb_gain16</key>
<value>20</value>
</param>
<param>
<key>bw16</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode16</key>
<value>0</value>
</param>
<param>
<key>corr16</key>
<value>0</value>
</param>
<param>
<key>freq16</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode16</key>
<value>False</value>
</param>
<param>
<key>if_gain16</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode16</key>
<value>0</value>
</param>
<param>
<key>gain16</key>
<value>10</value>
</param>
<param>
<key>ant17</key>
<value></value>
</param>
<param>
<key>bb_gain17</key>
<value>20</value>
</param>
<param>
<key>bw17</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode17</key>
<value>0</value>
</param>
<param>
<key>corr17</key>
<value>0</value>
</param>
<param>
<key>freq17</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode17</key>
<value>False</value>
</param>
<param>
<key>if_gain17</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode17</key>
<value>0</value>
</param>
<param>
<key>gain17</key>
<value>10</value>
</param>
<param>
<key>ant18</key>
<value></value>
</param>
<param>
<key>bb_gain18</key>
<value>20</value>
</param>
<param>
<key>bw18</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode18</key>
<value>0</value>
</param>
<param>
<key>corr18</key>
<value>0</value>
</param>
<param>
<key>freq18</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode18</key>
<value>False</value>
</param>
<param>
<key>if_gain18</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode18</key>
<value>0</value>
</param>
<param>
<key>gain18</key>
<value>10</value>
</param>
<param>
<key>ant19</key>
<value></value>
</param>
<param>
<key>bb_gain19</key>
<value>20</value>
</param>
<param>
<key>bw19</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode19</key>
<value>0</value>
</param>
<param>
<key>corr19</key>
<value>0</value>
</param>
<param>
<key>freq19</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode19</key>
<value>False</value>
</param>
<param>
<key>if_gain19</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode19</key>
<value>0</value>
</param>
<param>
<key>gain19</key>
<value>10</value>
</param>
<param>
<key>ant1</key>
<value></value>
</param>
<param>
<key>bb_gain1</key>
<value>20</value>
</param>
<param>
<key>bw1</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode1</key>
<value>0</value>
</param>
<param>
<key>corr1</key>
<value>0</value>
</param>
<param>
<key>freq1</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode1</key>
<value>False</value>
</param>
<param>
<key>if_gain1</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode1</key>
<value>0</value>
</param>
<param>
<key>gain1</key>
<value>10</value>
</param>
<param>
<key>ant20</key>
<value></value>
</param>
<param>
<key>bb_gain20</key>
<value>20</value>
</param>
<param>
<key>bw20</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode20</key>
<value>0</value>
</param>
<param>
<key>corr20</key>
<value>0</value>
</param>
<param>
<key>freq20</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode20</key>
<value>False</value>
</param>
<param>
<key>if_gain20</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode20</key>
<value>0</value>
</param>
<param>
<key>gain20</key>
<value>10</value>
</param>
<param>
<key>ant21</key>
<value></value>
</param>
<param>
<key>bb_gain21</key>
<value>20</value>
</param>
<param>
<key>bw21</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode21</key>
<value>0</value>
</param>
<param>
<key>corr21</key>
<value>0</value>
</param>
<param>
<key>freq21</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode21</key>
<value>False</value>
</param>
<param>
<key>if_gain21</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode21</key>
<value>0</value>
</param>
<param>
<key>gain21</key>
<value>10</value>
</param>
<param>
<key>ant22</key>
<value></value>
</param>
<param>
<key>bb_gain22</key>
<value>20</value>
</param>
<param>
<key>bw22</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode22</key>
<value>0</value>
</param>
<param>
<key>corr22</key>
<value>0</value>
</param>
<param>
<key>freq22</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode22</key>
<value>False</value>
</param>
<param>
<key>if_gain22</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode22</key>
<value>0</value>
</param>
<param>
<key>gain22</key>
<value>10</value>
</param>
<param>
<key>ant23</key>
<value></value>
</param>
<param>
<key>bb_gain23</key>
<value>20</value>
</param>
<param>
<key>bw23</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode23</key>
<value>0</value>
</param>
<param>
<key>corr23</key>
<value>0</value>
</param>
<param>
<key>freq23</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode23</key>
<value>False</value>
</param>
<param>
<key>if_gain23</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode23</key>
<value>0</value>
</param>
<param>
<key>gain23</key>
<value>10</value>
</param>
<param>
<key>ant24</key>
<value></value>
</param>
<param>
<key>bb_gain24</key>
<value>20</value>
</param>
<param>
<key>bw24</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode24</key>
<value>0</value>
</param>
<param>
<key>corr24</key>
<value>0</value>
</param>
<param>
<key>freq24</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode24</key>
<value>False</value>
</param>
<param>
<key>if_gain24</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode24</key>
<value>0</value>
</param>
<param>
<key>gain24</key>
<value>10</value>
</param>
<param>
<key>ant25</key>
<value></value>
</param>
<param>
<key>bb_gain25</key>
<value>20</value>
</param>
<param>
<key>bw25</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode25</key>
<value>0</value>
</param>
<param>
<key>corr25</key>
<value>0</value>
</param>
<param>
<key>freq25</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode25</key>
<value>False</value>
</param>
<param>
<key>if_gain25</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode25</key>
<value>0</value>
</param>
<param>
<key>gain25</key>
<value>10</value>
</param>
<param>
<key>ant26</key>
<value></value>
</param>
<param>
<key>bb_gain26</key>
<value>20</value>
</param>
<param>
<key>bw26</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode26</key>
<value>0</value>
</param>
<param>
<key>corr26</key>
<value>0</value>
</param>
<param>
<key>freq26</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode26</key>
<value>False</value>
</param>
<param>
<key>if_gain26</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode26</key>
<value>0</value>
</param>
<param>
<key>gain26</key>
<value>10</value>
</param>
<param>
<key>ant27</key>
<value></value>
</param>
<param>
<key>bb_gain27</key>
<value>20</value>
</param>
<param>
<key>bw27</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode27</key>
<value>0</value>
</param>
<param>
<key>corr27</key>
<value>0</value>
</param>
<param>
<key>freq27</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode27</key>
<value>False</value>
</param>
<param>
<key>if_gain27</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode27</key>
<value>0</value>
</param>
<param>
<key>gain27</key>
<value>10</value>
</param>
<param>
<key>ant28</key>
<value></value>
</param>
<param>
<key>bb_gain28</key>
<value>20</value>
</param>
<param>
<key>bw28</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode28</key>
<value>0</value>
</param>
<param>
<key>corr28</key>
<value>0</value>
</param>
<param>
<key>freq28</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode28</key>
<value>False</value>
</param>
<param>
<key>if_gain28</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode28</key>
<value>0</value>
</param>
<param>
<key>gain28</key>
<value>10</value>
</param>
<param>
<key>ant29</key>
<value></value>
</param>
<param>
<key>bb_gain29</key>
<value>20</value>
</param>
<param>
<key>bw29</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode29</key>
<value>0</value>
</param>
<param>
<key>corr29</key>
<value>0</value>
</param>
<param>
<key>freq29</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode29</key>
<value>False</value>
</param>
<param>
<key>if_gain29</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode29</key>
<value>0</value>
</param>
<param>
<key>gain29</key>
<value>10</value>
</param>
<param>
<key>ant2</key>
<value></value>
</param>
<param>
<key>bb_gain2</key>
<value>20</value>
</param>
<param>
<key>bw2</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode2</key>
<value>0</value>
</param>
<param>
<key>corr2</key>
<value>0</value>
</param>
<param>
<key>freq2</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode2</key>
<value>False</value>
</param>
<param>
<key>if_gain2</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode2</key>
<value>0</value>
</param>
<param>
<key>gain2</key>
<value>10</value>
</param>
<param>
<key>ant30</key>
<value></value>
</param>
<param>
<key>bb_gain30</key>
<value>20</value>
</param>
<param>
<key>bw30</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode30</key>
<value>0</value>
</param>
<param>
<key>corr30</key>
<value>0</value>
</param>
<param>
<key>freq30</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode30</key>
<value>False</value>
</param>
<param>
<key>if_gain30</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode30</key>
<value>0</value>
</param>
<param>
<key>gain30</key>
<value>10</value>
</param>
<param>
<key>ant31</key>
<value></value>
</param>
<param>
<key>bb_gain31</key>
<value>20</value>
</param>
<param>
<key>bw31</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode31</key>
<value>0</value>
</param>
<param>
<key>corr31</key>
<value>0</value>
</param>
<param>
<key>freq31</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode31</key>
<value>False</value>
</param>
<param>
<key>if_gain31</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode31</key>
<value>0</value>
</param>
<param>
<key>gain31</key>
<value>10</value>
</param>
<param>
<key>ant3</key>
<value></value>
</param>
<param>
<key>bb_gain3</key>
<value>20</value>
</param>
<param>
<key>bw3</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode3</key>
<value>0</value>
</param>
<param>
<key>corr3</key>
<value>0</value>
</param>
<param>
<key>freq3</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode3</key>
<value>False</value>
</param>
<param>
<key>if_gain3</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode3</key>
<value>0</value>
</param>
<param>
<key>gain3</key>
<value>10</value>
</param>
<param>
<key>ant4</key>
<value></value>
</param>
<param>
<key>bb_gain4</key>
<value>20</value>
</param>
<param>
<key>bw4</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode4</key>
<value>0</value>
</param>
<param>
<key>corr4</key>
<value>0</value>
</param>
<param>
<key>freq4</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode4</key>
<value>False</value>
</param>
<param>
<key>if_gain4</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode4</key>
<value>0</value>
</param>
<param>
<key>gain4</key>
<value>10</value>
</param>
<param>
<key>ant5</key>
<value></value>
</param>
<param>
<key>bb_gain5</key>
<value>20</value>
</param>
<param>
<key>bw5</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode5</key>
<value>0</value>
</param>
<param>
<key>corr5</key>
<value>0</value>
</param>
<param>
<key>freq5</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode5</key>
<value>False</value>
</param>
<param>
<key>if_gain5</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode5</key>
<value>0</value>
</param>
<param>
<key>gain5</key>
<value>10</value>
</param>
<param>
<key>ant6</key>
<value></value>
</param>
<param>
<key>bb_gain6</key>
<value>20</value>
</param>
<param>
<key>bw6</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode6</key>
<value>0</value>
</param>
<param>
<key>corr6</key>
<value>0</value>
</param>
<param>
<key>freq6</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode6</key>
<value>False</value>
</param>
<param>
<key>if_gain6</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode6</key>
<value>0</value>
</param>
<param>
<key>gain6</key>
<value>10</value>
</param>
<param>
<key>ant7</key>
<value></value>
</param>
<param>
<key>bb_gain7</key>
<value>20</value>
</param>
<param>
<key>bw7</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode7</key>
<value>0</value>
</param>
<param>
<key>corr7</key>
<value>0</value>
</param>
<param>
<key>freq7</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode7</key>
<value>False</value>
</param>
<param>
<key>if_gain7</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode7</key>
<value>0</value>
</param>
<param>
<key>gain7</key>
<value>10</value>
</param>
<param>
<key>ant8</key>
<value></value>
</param>
<param>
<key>bb_gain8</key>
<value>20</value>
</param>
<param>
<key>bw8</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode8</key>
<value>0</value>
</param>
<param>
<key>corr8</key>
<value>0</value>
</param>
<param>
<key>freq8</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode8</key>
<value>False</value>
</param>
<param>
<key>if_gain8</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode8</key>
<value>0</value>
</param>
<param>
<key>gain8</key>
<value>10</value>
</param>
<param>
<key>ant9</key>
<value></value>
</param>
<param>
<key>bb_gain9</key>
<value>20</value>
</param>
<param>
<key>bw9</key>
<value>0</value>
</param>
<param>
<key>dc_offset_mode9</key>
<value>0</value>
</param>
<param>
<key>corr9</key>
<value>0</value>
</param>
<param>
<key>freq9</key>
<value>100e6</value>
</param>
<param>
<key>gain_mode9</key>
<value>False</value>
</param>
<param>
<key>if_gain9</key>
<value>20</value>
</param>
<param>
<key>iq_balance_mode9</key>
<value>0</value>
</param>
<param>
<key>gain9</key>
<value>10</value>
</param>
<param>
<key>comment</key>
<value></value>
</param>
<param>
<key>affinity</key>
<value></value>
</param>
<param>
<key>args</key>
<value></value>
</param>
<param>
<key>_enabled</key>
<value>1</value>
</param>
<param>
<key>_coordinate</key>
<value>(16, 96)</value>
</param>
<param>
<key>_rotation</key>
<value>0</value>
</param>
<param>
<key>id</key>
<value>rtlsdr_source_0</value>
</param>
<param>
<key>maxoutbuf</key>
<value>0</value>
</param>
<param>
<key>clock_source0</key>
<value></value>
</param>
<param>
<key>time_source0</key>
<value></value>
</param>
<param>
<key>clock_source1</key>
<value></value>
</param>
<param>
<key>time_source1</key>
<value></value>
</param>
<param>
<key>clock_source2</key>
<value></value>
</param>
<param>
<key>time_source2</key>
<value></value>
</param>
<param>
<key>clock_source3</key>
<value></value>
</param>
<param>
<key>time_source3</key>
<value></value>
</param>
<param>
<key>clock_source4</key>
<value></value>
</param>
<param>
<key>time_source4</key>
<value></value>
</param>
<param>
<key>clock_source5</key>
<value></value>
</param>
<param>
<key>time_source5</key>
<value></value>
</param>
<param>
<key>clock_source6</key>
<value></value>
</param>
<param>
<key>time_source6</key>
<value></value>
</param>
<param>
<key>clock_source7</key>
<value></value>
</param>
<param>
<key>time_source7</key>
<value></value>
</param>
<param>
<key>minoutbuf</key>
<value>0</value>
</param>
<param>
<key>nchan</key>
<value>1</value>
</param>
<param>
<key>num_mboards</key>
<value>1</value>
</param>
<param>
<key>type</key>
<value>fc32</value>
</param>
<param>
<key>sample_rate</key>
<value>samp_rate</value>
</param>
<param>
<key>sync</key>
<value></value>
</param>
</block>
<connection>
<source_block_id>blocks_complex_to_mag_squared_0</source_block_id>
<sink_block_id>blocks_nlog10_ff_0</sink_block_id>
<source_key>0</source_key>
<sink_key>0</sink_key>
</connection>
<connection>
<source_block_id>blocks_nlog10_ff_0</source_block_id>
<sink_block_id>blocks_udp_sink_0</sink_block_id>
<source_key>0</source_key>
<sink_key>0</sink_key>
</connection>
<connection>
<source_block_id>blocks_stream_to_vector_0</source_block_id>
<sink_block_id>fft_vxx_0</sink_block_id>
<source_key>0</source_key>
<sink_key>0</sink_key>
</connection>
<connection>
<source_block_id>fft_vxx_0</source_block_id>
<sink_block_id>blocks_complex_to_mag_squared_0</sink_block_id>
<source_key>0</source_key>
<sink_key>0</sink_key>
</connection>
<connection>
<source_block_id>rtlsdr_source_0</source_block_id>
<sink_block_id>blocks_stream_to_vector_0</sink_block_id>
<source_key>0</source_key>
<sink_key>0</sink_key>
</connection>
<connection>
<source_block_id>rtlsdr_source_0</source_block_id>
<sink_block_id>qtgui_freq_sink_x_0</sink_block_id>
<source_key>0</source_key>
<sink_key>0</sink_key>
</connection>
</flow_graph>
Как обычно, всем удачных экспериментов.
rogenFogen
30 баксов потратить на приемник ради статьи по фану, так себе занятие во время самоизоляции. Однако статья клевая!
DmitrySpb79 Автор
Да, к сожалению Хабр никак не оптимизирован под обзоры железа, а спонсоров кто бы его оплачивал, нет. Но приемник уже был, так что ничего покупать не пришлось :)