В этой статье я продолжу рассказ об использовании GnuRadio. Начнём с лабораторных работ по исследования генераторов и фильтров, амплитудной и частотной модуляции. Это поможет глубже понять возможности GnuRadio. Дальше я покажу, как с помощью GnuRadio можно сделать несложный диктофон, способный записывать звук в формате wav.
После этого займемся распознаванием речи — сделаем свой собственный блок для GnuRadio на базе нейросети Whisper.cpp. Добавим этот блок в диктофон, а также в FM-приёмник, о котором я рассказывал в предыдущей статье “Как собрать собственный SDR-приёмник в GnuRadio без паяльника”.
Диктофон и приёмник будут записывать звук в wav-файлы, а результаты распознавания речи — в текстовые файлы.
Наверное, почти у каждого радиолюбителя, конструирующего радиоаппаратуру, есть дома генератор и осциллограф. Но даже если нет, то с помощью GnuRadio можно собрать виртуальный стенд для экспериментов с этими устройствами.
Для начала создадим простейшую установку из генератора звуковой частоты и осциллографа. В этом нам помогут блоки Signal Source и QT GUI Time Sink, выступающие в качестве генератора и осциллографа, соответственно (рис. 1).
Установите параметры блока Signal Source как это показано на рис. 2.
Здесь мы задали рабочую частоту генератора Frequency, равную 1 КГц, и синусоидальную форму сигнала Waveform. Частота дискретизации Sample Rate задана в переменной samp_rate и равна 32 КГц.
После запуска программы на экране появится окно осциллографа, на котором вы увидите сразу два сигнала (рис. 3).
Но почему здесь видно два сигнала, а не один?
Дело в том, что источник Signal Source по умолчанию работает в режиме Output Type, заданном как Complex. При этом генерируется сигнал с двумя составляющими — реальной (обозначается как I) и мнимой (обозначается как Q).
Когда тип установлен как complex, блок QT GUI Time Sink показывает эти сигналы в виде двух отдельных графиков. Вы можете отключить ненужный график, щелкнув ссылку Signal 1 или Signal 2 (рис. 4).
Масштаб по вертикали можно регулировать колёсиком мыши. Если же надо растянуть или сжать осциллограмму, то это лучше всего сделать, меняя значение параметра Number of Points блока QT GUI Time Sink. На рис. 5 показана осциллограмма при установке для этого параметра значения, равного 256.
Настраивая параметр Waveform блока Signal Source, можно задать различную форму сигнала: синус, косинус, прямоугольный, треугольный и пилообразный сигнал (рис. 6).
В поле Offset можно задать комплексное значение:
complex(-1, 0.2)
Здесь график реальной составляющей сигнала будет сдвинут на -1, а мнимой — на 0.2 (рис. 7).
Генератор Signal Source в состоянии выдавать не только комплексный сигнал, но и сигнал float, int, short и byte. Проведите эксперименты с этими типами сигналов самостоятельно, устанавливая их в поле Type блока QT GUI Time Sink.
Файл generator.grc диаграммы для этого раздела вы найдёте в моём репозитории на GitHub. Файлы из этого репозитория были созданы в программе GNU Radio Companion версии 3.10.12.0 (Python 3.12.9), запущенной на Microsoft Windows 11.
Для изучения спектра сигнала подключим к источнику Signal Source блок анализатора спектра QT GUI Time Sink (рис. 8).
Обратите внимание, что источник выдаёт синусоидальный сигнал. После запуска программы мы увидим спектр, состоящий из одного главного пика (рис. 9).
Если внимательно посмотреть на рис. 9, то можно увидеть, что слева и справа от основного пика находятся очень небольшие боковые лепестки. Их появление связано с дискретной обработкой сигнала.
Для получения спектра блок QT GUI Time Sink использует быстрое преобразование Фурье (Fast Fourier Transform, FFT). Оно позволяет определить, какие частоты содержит сигнал.
Перед выполнением спектрального анализа для уменьшения искажений спектра выполняется оконная функция, которую можно задать в параметре Window Type блока QT GUI Time Sink.
Получите спектры сигналов различной формы, задавая форму сигнала в параметре Waveform блока Signal Source. На рис. 10 показан спектр меандра.
Спектр треугольного сигнала содержит меньше пиков (рис. 11).
Проведите самостоятельно эксперименты с сигналами различной формы, доступными в блоке Signal Source.
Файл fft.grc с анализатором спектра доступен в репозитории Github.
Для визуализации спектра сигнала в современных приёмниках SDR часто применяется так называемый водопад (waterfall). Это графическое отображение изменений спектра сигнала во времени.
На водопаде интенсивность сигнала показывается цветом. Слои спектра постоянно сдвигаются вниз аналогично эффекту падающей воды, отсюда и название.
С помощью водопада можно отлеживать появление и исчезновение станций, наблюдать шумы и слабые сигналы, оценивать вид модуляции и ширину полосы передачи.
Добавим в нашу диаграмму модуль QT GUI Waterfall Sink, отображающий водопад (рис. 12).
На рис. 13 показаны результаты добавления водопада в окно программы.
При добавлении водопада указана центральная частота Center Frequency, равная частоте генератора Signal Source, а именно 1 КГц. Как видите, полосы на водопаде соответствуют пикам спектрограммы, а их цвет зависит от амплитуды этих пиков.
Для экспериментов с водопадом вы можете загрузить файл waterfall.grc из репозитория.
С помощью блоков GnuRadio можно собрать генератор качающейся частоты. Сделаем такой генератор для звукового диапазона.
С помощью блока Signal Source в этом генераторе будет вырабатываться пилообразный сигнал частотой 1 Гц. Он будет управлять частотой сигнала, создаваемого генератором VCO (Voltage controlled oscillator), частота которого зависит от управляющего напряжения. Для контроля сигнал от VCO будет подаваться на блок осциллографа QT GUI Time Sink, а также на звуковую плату компьютера через блок Audio Sink (рис. 14).
Обратите внимание, что в переменной samp_rate записана частота дискретизации, равная 44.1 КГц, подходящая для работы с звуковыми сигналами.
Настройка параметров блока Signal Source, создающего пилообразный сигнал, показаны на рис. 15.
Пилообразная форма сигнала задана значением Saw Tooth в поле Waveform. При этом частота сигнала указана равной 1 Гц в поле Frequency.
На выходе генератора формируются данные в формате float с амплитудой, равной единице. За это отвечают поля Output Type и Amplitude, соответственно. Смещение Offset указано равным нулю.
Параметры генератора VCO, управляемого напряжением, показаны на рис. 16.
Параметр Sample Rate задаёт частоту дискретизации. Мы указываем здесь переменную samp_rate со значением 44.1 КГц.
Параметр Sensitivity задаёт коэффициент преобразования входного сигнала в отклонение частоты генератора. Иными словами, он определяет, на сколько изменится мгновенная частота VCO при изменении входного значения на 1.
В нашем случае с пилообразного генератора поступает сигнал в диапазоне от -1 до 1. Сумматор Add добавляет значение 1.5. В итоге на вход VCO поступает сигнал в диапазоне от 0.5 до 2.5.
В блоке VCO используется формула:
Параметр Sensitivity задается в радианах в секунду на единицу входного сигнала. Чтобы получить отклонение частоты в герцах, нужно разделить значение этого параметра на
В нашем случае частота VCO плавно изменяется примерно от 64 Гц до 318 Гц. Для примера рассчитаю Sensitivity для VCO так, чтобы частота изменялась от 300 до 3000 Гц.
У нас на выходе генератора пилы напряжение изменяется в диапазоне от -1 до 1, значит полный размах входа:
Воспользуемся формулой:
При изменении частоты от 300 Гц до 3000 Гц полная ширина диапазона составит:
Чтобы при изменении входа на 2 частота изменилась на 2700 Гц должно выполняться:
Отсюда:
Теперь вычислим смещение. Средняя частота диапазона вычисляется так:
Соответствующее среднее значение входа:
Для диапазона 300–3000 Гц при сигнале входе от -1 до 1 нужно смещение:
Таким образом, для диапазона частот от 300 Гц до 3000 Гц установите в блоке VCO параметр Sensitivity, равный 8500, а в блоке Constant Source параметр Constant равным 1.22.
Файл диаграммы sweep.grc можно скачать из репозитория на Github.
Теперь, когда у нас есть генератор качающейся частоты, подключим к нему полосовой фильтр (рис. 17).
Для установки диапазона изменения частоты от 200 Гц до 5 КГц в блоке VCO устанавливаем параметр Sensitivity равным 15080, а значение константы Constant в блоке Constant Source равным 1.083.
К выходу блока VCO подключен полосовой фильтр Band Pass Filter, для которого установлена полоса пропускания от 400 Гц до 3400 Гц в параметрах Low Cutoff Freq и High Cutoff Freq, соответственно.
Файл диаграммы sweep-filter.grc можно скачать в репозитории на Github.
Сигнал до и после полосового фильтра попадает на блоки водопада QT GUI Waterfall Sink, а также на аудио вход через блок Audio Sink. На водопадах хорошо заметна работа полосового фильтра, вырезающего из входного сигнала полосу (рис. 18).
Изменяя параметры полосового фильтра, вы можете пропускать нужные вам частоты и задерживать остальные.
Для передачи сигналов по радиоканалам используются различные виды модуляции несущего сигнала. С помощью GnuRadio вы можете сгенерировать модулированные сигналы, изучить их осциллограммы и спектр.
При амплитудной модуляции полезный сигнал (например, звуковой) изменяет амплитуду несущего сигнала. Сигнал амплитудной модуляции с несущей (AM, DSB-LC, Double Sideband, Large Carrier) формируется следующим образом:
В этой формуле k задаёт коэффициент модуляции. Единица прибавляется для сохранения несущей, а сама несущая задается как . Выражение
задаёт модулирующий сигнал, а
— частоту несущей.
Для генерации сигнала АМ я собрал в GnuRadio диаграмму, показанную на рис. 19.
Модулирующий сигнал частотой 1000 Гц и амплитудой, равной единице, генерируется с помощью первого блока Signal Source. Второй такой блок создаёт сигнал несущей с частотой 50 КГц.
Блок Multiply Const совместно с блоком QT GUI Range позволяет регулировать глубину модуляции, изменяя амплитуду модулирующего сигнала. Далее к этому сигналу с помощью блока Add Const добавляется единица (несущая составляющая).
Блок Multiply получает на один из своих входов модулирующий сигнал, а на другой — сигнал несущей частоты. К его выходу подключен блок Throttle, ограничивающий скорость потока данных и нагрузку на процессор.
Готовый сигнал АМ с выхода блока Throttle поступает для контроля на осциллограф, водопад и анализатор спектра (рис. 20).
Регулируя глубину модуляции при помощи одноимённого движка, можно получить сигнал со слабой модуляцией или перемодулированный сигнал (рис. 21).
На спектрограмме и водопаде видны боковые полосы, отстоящие от несущей на 1 КГц, то есть на частоту модуляции.
Для эксперимента попробуйте в качестве модуляции использовать не синусоидальный сигнал, а меандр, выбрав в блоке модулирующего сигнала Signal Source параметр Waveform как Suare. Как и следовало ожидать, в сигнале появится множество боковых полос (рис. 22).
Создавая реальные радиопередающие устройства, очень важно следить за спектром излучаемого сигнала, чтобы не было помех.
Файл диаграммы am.grc доступен в репозитории на Github.
При частотной модуляции частота несущей изменяется модулирующим сигналом. Выходной сигнал с частотной модуляцией определяется формулой:
Для генерации сигнала FM в GnuRadio имеется блок WBFM Transmit. Используем его в генераторе (рис. 23).
Блок Multiply Const совместно с блоком QT GUI Range изменяет амплитуду модулирующего сигнала, влияя таким образом на девиацию частоты.
Здесь для модуляции используется генератор частотой 600 Гц.
В схеме блок WBFM Transmit формирует FM-сигнал в комплексной базовой полосе с центральной частотой 0 Гц. Частота 600 Гц, заданная в блоке Signal Source, является частотой модулирующего сигнала. Реальная радиочастотная несущая в данной схеме не формируется (рис. 24).
Файл диаграммы fm.grc можно скачать из моего репозитория в Github.
Для формирования несущей используем другую схему (рис.25).
Здесь добавлен генератор несущей на базе блока Signal Source с частотой 100 КГц. Сигнал несущей перемножается с частотно-модулированным сигналом от блока WBFM Transmit при помощи блока Multiply. Результат показан на рис. 26.
Проведите эксперимент по изменению девиации с помощью слайдера. Вы увидите, как с увеличением девиации будет увеличиваться количество боковых полос и ширина полосы спектра сигнала (рис. 27).
Файл диаграммы fm-1.grc доступен в репозитории.
Теперь я расскажу, как с помощью GnuRadio можно сделать диктофон, способный не только записывать звук в wav-файл, но и распознавать во входном потоке речь. Результаты распознавания будут сохранены в текстовый файл.
Для распознавания речи мы будем использовать Whisper.cpp — высокопроизводительную реализацию модели распознавания речи, запуская её локально, без обращения к внешним сервисам.
Проект Whisper.cpp использует модели Whisper — систему автоматического распознавания речи, обученную на 680 000 часах многоязычных и многозадачных размеченных данных, собранных из открытых веб-источников.
Прежде чем мы займёмся распознаванием речи, сделаем простой вариант диктофона, записывающего звук в wav-файл. Для записи и воспроизведения звука я использовал микрофон от Web-камеры Logitech, подключенный к порту USB, и головные телефоны, соединенные с выходом звуковой платы компьютера.
Схема первого варианта диктофона показана на рис. 28.
Звуковой сигнал от микрофона поступает от блока Audio Source. Параметры этого блока показаны на рис. 29.
Обратите внимание, что здесь в параметре Device Name я указал имя драйвера микрофона как «Микрофон (Logi C270 HD WebCam)». Этот параметр позволяет задать, с какого микрофона из числа подключенных к компьютеру нужно получать звуковой сигнал.
Вы можете оставить этот параметр пустым, и тогда будет использовано системное устройство, или указать точное имя устройства, как оно отображается в диспетчере устройств Windows (рис. 30).
Также список всех аудиоустройств, у которых есть хотя бы один входной канал, можно получить в консоли с помощью программы drivers_mic.py:
import sounddevice as sd devices = sd.query_devices() for i, dev in enumerate(devices): if dev['max_input_channels'] > 0: print(f"{i}: {dev['name']} - {dev['max_input_channels']} input channels")
Перед запуском программы установите из командной строки модуль sounddevice:
pip install sounddevice
Программа покажет на консоли список всех аудиоустройств с входными каналами:
$ python drivers_mic.py 0: Переназначение звуковых устр. - Input - 2 input channels 1: Микрофон (Logi C270 HD WebCam) - 1 input channels 2: Microphone (High Definition Aud - 2 input channels 6: Первичный драйвер записи звука - 2 input channels 7: Микрофон (Logi C270 HD WebCam) - 1 input channels 8: Microphone (High Definition Audio Device) - 2 input channels 14: Микрофон (Logi C270 HD WebCam) - 1 input channels 15: Microphone (High Definition Audio Device) - 2 input channels 18: Микрофон (HD Audio Microphone) - 2 input channels 19: Микрофон (Logi C270 HD WebCam) - 1 input channels
Вы можете скопировать нужное имя устройства в параметр Device Name блока Audio Source.
Сигнал от микрофона поступает на два фильтра High Pass Filter и Band Pass Filter. Первый из них отфильтровывает паразитную частоту 50 Гц, а второй пропускает сигналы с частотами от 200 Гц до 3400 Гц (соответствуют речевому диапазону). В качестве эксперимента попробуйте ограничить полосу пропускания сверху значением 2400 Гц, а также расширить её до 5 КГц.
Сигнал после полосового фильтра контролируется с помощью водопада и осциллографа (рис. 31), а также подаётся на выход звуковой платы компьютера.
Чтобы записать данные в файл, используется блок Wav File Sink. Параметры этого блока показаны на рис. 32.
Обратите внимание на параметр File, задающий полный путь к файлу, в который будут записаны звуковые данные. Для прослушивания записанного файла можно использовать, например, стандартное приложение Медиаплеер из состава Microsoft Windows или другую подходящую программу.
Диаграмма dictaphone.grc находится в моём репозитории.
Диктофон с распознаванием речи может пригодится, например, для записи и расшифровки совещания в аудитории или в других аналогичных случаях.
Диаграмма диктофона с распознаванием речи не выглядит очень сложно (рис. 33).
Как видно из этой диаграммы, после полосового фильтра Band Pass Filter, как и в предыдущей версии диктофона, сигнал поступает на блок записи wav-файла Wav File Sink, на осциллограф, анализатор спектра и водопад, а также на блок Whisper.cpp Recognition.
Рассматривая диаграмму диктофона, нетрудно догадаться, что распознавание речи «спряталось» в блоке Whisper.cpp Recognition. Он создан с использованием блока Embedded Python Block, который входит в состав GnuRadio. На базе этого блока можно сделать обработчик сигнала, если добавить в него код обработки на Python. Этот код можно написать таким образом, чтобы он решал нужные вам задачи, например, распознавание речи.
Создайте в Gnu Radio Companion новую диаграмму, добавив в нее блок Python Block, а затем откройте параметры этого блока (рис. 34).
Затем щелкните кнопку Open in Editor. Откроется редактор кода, заданный в программе Gnu Radio Companion по умолчанию. В качестве такого редактора я задал Sublime Text (рис. 35).
Для установки редактора по умолчанию в ОС Microsoft Windows 11 я прописал путь к исполняемому файлу редактору в параметре editor секции [grc] файла C:\Users\[имя_пользователя]\AppData\Roaming\.config\gnuradio\config.conf:
[grc] canvas_default_size = 1280, 1024 canvas_font_size = 8 default_flow_graph = editor = C:\Program Files\Sublime Text 3\sublime_text.exe
После редактирования файла перезапустите Gnu Radio Companion.
По умолчанию для добавленного блока Python Block создаётся программа умножения входного сигнала на константу:
import numpy as np from gnuradio import gr class blk(gr.sync_block): def __init__(self, example_param=1.0): gr.sync_block.__init__( self, name='Embedded Python Block', in_sig=[np.complex64], out_sig=[np.complex64] ) self.example_param = example_param def work(self, input_items, output_items): output_items[0][:] = input_items[0] * self.example_param return len(output_items[0])
В этой программе используются две библиотеки — numpy для работы с массивами данных и gr из gnuradio — базовый модуль GNU Radio для создания пользовательских блоков.
Класс blk наследуется от gr.sync_block и представляет собой синхронный блок. В нём на каждый входной элемент формируется один выходной элемент.
Конструктор init определяет параметры блока, которые будут доступны в интерфейсе GRC. В данном случае параметр example_param — это вещественное число, используемое как коэффициент усиления (или ослабления) сигнала.
Вызов базового конструктора gr.sync_block.__init__ задаёт имя блока name, отображаемое в GRC. Также задаётся тип входного сигнала in_sig и выходного out_sig сигналов как комплексных чисел np.complex64.
Метод work представляет собой вычислительный цикл блока, который вызывается планировщиком GNU Radio каждый раз, когда на входе появляются новые данные. В данном случае метод умножает весь входной буфер на заданную константу:
output_items[0][:] = input_items[0] * self.example_param
Операция выполняется как векторная с высокой производительностью за счёт использования NumPy.
Метод work возвращает значение, сообщая GNU Radio, сколько элементов было обработано и записано в выходной буфер:
return len(output_items[0])
Вернёмся к описанию блока Whisper.cpp Recognition, выполняющего распознавание речи в нашем диктофоне. Рассмотрим основные моменты в коде распознавания речи.
В конструкторе указано, что тип входных данных in_sig не комплексный, как в примере выше, а с плавающей запятой np.float32. Что касается выходных данных, то их нет, поэтому out_sig задан как None:
gr.sync_block.__init__( self, name="Whisper.cpp Recognition", in_sig=[np.float32], out_sig=None )
Метод work принимает входные данные в формате float32, преобразует их в 16-битные и накапливает данные во внутреннем буфере. При достижении заданной длительности буфера копия данных передаётся в очередь для распознавания. При завершении этот метод возвращает количество обработанных сэмплов:
def work(self, input_items, output_items): audio = (input_items[0] * 32767).astype(np.int16) self.buffer.extend(audio) if len(self.buffer) >= self.sample_rate * self.buffer_seconds: self.queue.put(self.buffer.copy()) self.buffer.clear() return len(input_items[0])
Метод worker_thread обрабатывает аудиоданные в отдельном потоке, извлекая буферы с данными из очереди. Извлеченные буферы передаются на распознавание методу process_audio:
def worker_thread(self): while True: try: item = self.queue.get(timeout=1) if item is None: break self.process_audio(item) except queue.Empty: if self.stop_event.is_set(): break
Метод process_audio распознаёт один фрагмент аудио, сохраняя текст в файл. Сначала этот метод сохраняет буфер аудио во временный wav-файл и запускает whisper.cpp через subprocess с заданной моделью и языком. Далее метод извлекает распознанный текст из потока стандартного вывода и записывает результат в почасовой текстовый файл с немедленной синхронизацией на диск.
После обработки временный wav-файл удаляется. Ошибки записываются в отдельный файл. Код метода process_audio показан ниже:
def process_audio(self, buffer_data): with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as f: wav_path = f.name try: with wave.open(wav_path, "wb") as wf: wf.setnchannels(1) wf.setsampwidth(2) wf.setframerate(self.sample_rate) wf.writeframes( np.array(buffer_data, dtype=np.int16).tobytes() ) cmd = [ self.whisper_path, "-m", self.model_path, "-f", wav_path, "-l", "ru", "--no-timestamps" ] si = subprocess.STARTUPINFO() si.dwFlags |= subprocess.STARTF_USESHOWWINDOW result = subprocess.run( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, startupinfo=si, text=True, encoding="utf-8", timeout=120 ) text = result.stdout.strip() if text: hour_file = self.get_hour_file_path() if hour_file != self.current_hour_file: self.current_hour_file = hour_file with open(self.current_hour_file, "a", encoding="utf-8") as f: f.write(text + "\n") f.flush() os.fsync(f.fileno()) except Exception as e: with open(self.error_log, "a", encoding="utf-8") as f: f.write(str(e) + "\n") finally: if os.path.exists(wav_path): os.remove(wav_path)
Обратите внимание на этот фрагмент, создающий список аргументов cmd для запуска внешней программы whisper.cpp:
cmd = [ self.whisper_path, "-m", self.model_path, "-f", wav_path, "-l", "ru", "--no-timestamps" ]
Здесь параметры self.whisper_path и self.model_path указывают пути к исполняемому файлу whisper-cli.exe и к файлу модели для распознавания речи. Параметр wav_path задаёт путь к временно созданному wav-файлу с данными аудио.
Чтобы улучшить качество распознавания речи на русском языке, в параметре -l "ru" задан русский язык распознавания.
Параметр --no-timestamps отключает вывод временных меток в результатах, оставляя только текст.
Следующий фрагмент кода выполняет распознавание речи из временного wav-файла и возвращает результат в переменную result:
si = subprocess.STARTUPINFO() si.dwFlags |= subprocess.STARTF_USESHOWWINDOW result = subprocess.run( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, startupinfo=si, text=True, encoding="utf-8", timeout=120 )
После завершения процесса текст берётся из result.stdout для записи в файл
Перед распознаванием конфигурируются параметры запуска внешнего процесса:
si = subprocess.STARTUPINFO() si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
Здесь subprocess.STARTUPINFO() создаёт объект с настройками запуска процесса, а si.dwFlags |= subprocess.STARTF_USESHOWWINDOW устанавливает флаг для отмены показа консольного окна при запуске процесса на Windows.
Обратите внимание на настройку блока Whisper.cpp Recognition (рис. 36).
Здесь указаны пути к Whisper.cpp, к модели и каталогу, в который будут записаны wav-файл и текстовые файлы с результатами распознавания.
Сначала установите Whisper.cpp. Все доступные дистрибутивы есть на странице https://github.com/ggml-org/whisper.cpp/releases.
Для Windows 11 я загрузил файл whisper-bin-x64.zip. Распакуйте архив в каталог c:/whisper. Чтобы проверить правильность распаковки убедитесь, что в этом каталоге есть файл whisper-cli.exe.
Далее нужно скачать модель с этой страницы. Я выбрал модель ggml-large-v3.bin, но есть и модели меньшего размера ggml-medium.bin и ggml-small.bin, которые вы тоже можете попробовать в работе. Файлы моделей запишите в каталог c:\whisper\models.
Вы можете запустить диктофон из программы Gnu Radio Companion обычным способом или через ярлык на рабочем столе Windows. В окне программы вы увидите водопад, осциллограмму с спектрограмму входного сигнала (рис. 37).
Произнесите в микрофон какие-нибудь слова и цифры, а потом закройте окно программы. Звук будет записан в файл C:\gnuradio_files\records.wav, а результаты его распознавания — в файл с именем вида recognized_2025-12-28_12.txt в том же каталоге.
Конечно, результат распознавания с моделью ggml-small.bin не идеален, но цифры распознаются очень точно. Вот пример:
1, 2, 3, 4, 5, 6, 7, 8, 9, 10. Раз, два, три, четыре, пять, вышел зайчик погулять. Друг охотникова бегает, прямо в зайчик и стреляет. 5-4-3-2-1 Стоп. [музыка] [музыка] Смешка
Вы можете также попробовать другие модели или отрегулировать усиление микрофона, чтобы амплитуда на осциллографе не выходила за единицу и не появились искажения.
Чтобы запустить программу с рабочего стола, создайте ярлык, указав в поле Объект путь вида:
C:\ProgramData\radioconda\pythonw.exe "C:\\GnuRadioLab\recognizer.py"
При этом рабочая папка должна быть такой: C:\ProgramData\radioconda. Для запуска программы этим методом просто щёлкните созданный ярлык.
Полный код программы для распознавания речи есть в моём репозитории. Там же вы найдете файл диаграммы recognizer.grc.
Только что я показал, как можно сделать диктофон с распознаванием речи. Теперь расскажу, как с помощью той же самой технологии можно сделать и FM-приёмник, который записывает передачу в wav-файл, а результаты распознавания речи в передаче — в текстовый файл (рис. 38).
За основу я взял приёмник, описанный в разделе «Добавляем регулировки и настройки» из статьи «Как собрать собственный SDR-приёмник в GnuRadio без паяльника».
После блока Multiply Const я добавил здесь блок Wav File Sink для сохранения звуковой записи принятой передачи в wav-файл и блок распознавания речи Whisper.cpp Recognition, взятый от только что описанного диктофона.
В блоке QT GUI Range, предназначенном для перестройки приёмника по частоте, я указал начальную частоту 95.6 МГц. В моём районе на этой частоте вещает радиостанция, передающая много текста.
Настраивая блок Wav File Sink, я указал путь к сохранению wav-файла как C:\gnuradio_files\fm-records.wav.
Настройки блока Whisper.cpp Recognition в точности такие же, как и в диктофоне с распознаванием речи. И программа распознавания, составленная на языке Python, точно такая же.
На рис. 39 показано окно приёмника с распознаванием речи в работе.
На рис. 40 вы можете увидеть содержимое текстового файла с результатами распознавания речи принятой FM-радиостанции.
Как видите, можно очень легко скопировать функционал распознавания речи из диктофона в приёмник FM простым добавлением блоков.
В моём репозитории на Github вы найдете программу распознавания, а также файл диаграммы FM-Radio-Regulator-Recognition.grc.
В этой статье я продолжил рассказ о мощном инструменте GnuRadio. Сначала я показал, как с его помощью можно проводить эксперименты с генератором и осциллографом, не имея под рукой этих приборов в виде физического оборудования. Далее вы узнали, как довольно быстро собрать диктофон и FM-приёмник без паяльника, но с записью звука и распознаванием речи.
Возможность распознавания речи на собственном оборудовании представляется мне довольно интересной. Например, можно расшифровывать файлы записи переговоров между клиентами и службой поддержки, а потом передать расшифровку нейросети, например, чтобы определить общую тональность беседы и создания выжимку. Что касается распознавания речи в потоке, то оно тоже возможно, однако требует мощного оборудования с GPU.
Пишите в комментариях, какие темы вам интересны в области GnuRadio или распознавания речи своими силами, без привлечения коммерческих сервисов.
Автор @AlexandreFrolov
НЛО прилетело и оставило здесь промокод для читателей нашего блога:
-15% на заказ любого VDS (кроме тарифа Прогрев) — HABRFIRSTVDS.
Источник


