Привет, Хабр.
Наверное все, хоть немного интересующиеся радиосвязью, знают что с помощью 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 показан на рисунке:

Как можно видеть, мы получаем данные с приемника, затем преобразуем непрерывный поток в набор «векторов» размером 1024, над этими блоками выполняем FFT, преобразуем значения из комплексных в вещественные, и отправляем данные по UDP. Разумеется, все это можно было бы сделать и непосредственно в Python с помощью SoapySDR и numpy, но объем кода был бы несколько больше.
Блок QT GUI Frequency Sink нужен для «отладки», с ним можно убедиться что сигналы радиостанций действительно принимаются и при необходимости настроить усиление приемника. Во время работы программы картинка должна быть примерно такой:

Если все работает, GUI Frequency Sink можно отключить, а в настройках проекта указать «No GUI», чтобы не тратить зря ресурсы. В принципе, эту программу дальше можно запускать как сервис, без какого-либо UI.
Поскольку мы передаем данные по UDP, то принимать их мы можем любым клиентом, и даже на другом ПК. Будем использовать Python, для прототипа этого вполне достаточно.
Сначала мы получаем данные по UDP:
Т.к. мы будем работать с графикой, удобно воспользоваться библиотекой pygame. Рисование 3D-спектра делается просто, мы храним данные в массиве, и выводим линии сверху вниз, от более старых к более новым.
Последнее, это вывод частот и названий станций на экран. Преобразование Фурье дает на выходе 1024 точки, соответствующие ширине полосы пропускания приемника. Мы также знаем центральную частоту, так что вычисление позиции делается элементарной школьной пропорцией.
Собственно и все, объединяем все это вместе, запускаем обе программы одновременно, и получаем на экране real-time панораму, показывающую работающие в данный момент FM-станции:

Легко можно видеть, что разные станции вещают с разным уровнем, или по ширине полосы отличить моно-вещание от стерео.
Ну а теперь обещанная панорама всего FM-диапазона. Для этого нам придется отложить RTL-SDR и взять приемник посерьезнее, например такой:

Я использую профессиональную модель от Ettus Research, но с точки зрения кода все тоже самое, достаточно поменять в GNU Radio один блок на другой. А вот так это выглядит на спектре при ширине полосы приема 24МГц:

Любопытно, насколько разные уровни сигналов имеют разные радиостанции.
Разумеется, принимать можно не только станции FM-диапазона, но и любые другие, в пределах рабочих частот SDR-приемника. Для примера, вот так выглядит авиадиапазон:

Можно видеть постоянно активные частоты (вероятно метеослужба ATIS) и периодически возникающий радиообмен. А вот так выглядит фрагмент спектра GSM, однако, его сигнал шире чем 24МГц, и целиком не помещается.

Как можно видеть, изучение радиоэфира процесс довольно занимательный, даже в 3D. Разумеется, здесь не было цели сделать «еще один спектроанализатор», это лишь прототип, сделанный для фана. Увы, отрисовка работает медленно, для вывода нескольких тысяч примитивов Python это не лучший выбор. Также алгоритм раскрашивания линий оставляет желать лучшего, в идеале, он может быть более гибким.
Для желающих поэкспериментировать самостоятельно, исходный код:
Как обычно, всем удачных экспериментов.
Наверное все, хоть немного интересующиеся радиосвязью, знают что с помощью 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 это не лучший выбор. Также алгоритм раскрашивания линий оставляет желать лучшего, в идеале, он может быть более гибким.
Для желающих поэкспериментировать самостоятельно, исходный код:
sdr_render.py
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
sdr_receiver.grc
<?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>
Как обычно, всем удачных экспериментов.
