Здесь приведен полный перевод на русский язык части 3 оригинальной статьи "A Software-Defined Radio for the Masses, Part 3" By Gerald Youngblood, AC5OG. Описывается полная программно-определяемая (SDR) радиостанция на базе ПК, использующая звуковую карту и инновационную схему детектора. В объяснении математика сведена к минимуму. Перевод первой части см. в [18]. Незнакомые термины и сокращения см. в Словарике части 1 [18].
В части 1 этой серии статей [18] было предоставлено общее описание цифровой обработки сигналов (digital signal processing, DSP), как она используется в программно-опредеяемых радиоустройствах (software-defined radios, SDRs), включая обзор полнофункционального трансивера, который основан на PC. Все функции DSP и функции управления реализованы в программе на языке Visual Basic. Часть 2 описывает исходный код Visual Basic, в котором реализован полнодуплексный квадратурный интерфейс (full-duplex quadrature interface) со звуковой картой на PC.
Как было описано ранее in-phase (I) сигнал и quadrature (Q) сигнал дают возможность виртуально модулировать или демодулировать сигналы любого типа. Детектор Tayloe, описанный в части 1 [18], представляет собой простой метод преобразования модулированного RF-сигнала квадратурную форму базового диапазона частот звуковой карты, так что сигналы I и Q можно подать на левый и правый каналы stereo звуковой карты PC для дальнейшей программной обработки. Full-duplex интерфейс DirectX8, описанный в части 2 [19], осуществляет ввод и вывод оцифрованных квадратурных сигналов через звуковую карту. Интерфейс звуковой карты обеспечивает входной буферный массив inBuffer() и выходной буферный массив outBuffer(), через который код DSP принимает захваченный сигнал и затем выводит обработанные данные сигнала.
Эта статья расширяет программный интерфейс звуковой карты для демонстрации реализации приемника SDR. Для этого в программном обеспечении реализованы следующие функции:
• Разделение буферов стереозвука на каналы I и Q. • Преобразование сигнала из домена времени (time domain) в домен частот с помощью быстрого преобразования Фурье (FFT). • Декартово-полярное (cartesian-to-polar) преобразование векторов сигнала. • Преобразование частоты из ПЧ основной полосы со смещением 11.25 кГц в 0 Гц. • Выбор боковой полосы сигнала. • Генерация коэффициента полосового фильтра. • Быстрая сверточная фильтрация FFT (fast-convolution filtering). • Преобразование сигнала обратно в time domain с помощью обратного быстрого преобразования Фурье (IFFT). • Цифровая автоматическая подстройка усиления (automatic gain control, AGC) с переменным временем реакции. • Передача обработанного сигнала в выходной буфер для операций радиопередачи или радиоприема.
Демонстрационный исходный код можно загрузить с сайта ARRLWeb [3]. Программа требует динамически линкуемой библиотеки (dynamic link library, DLL) из Intel Signal Processing Library [4], размещенной в рабочей директории проекта и скомпилированной программы. Эти файлы включены в демонстрационную программу.
[Архитектура программного обеспечения]
На рис. 1 приведена блок-схема архитектуры программного обеспечения DSP. Эта архитектура одинаково хорошо работает как для радиоприема, так и для радиопередачи, причем для переключения между этими режимами требуется изменить всего лишь несколько строк кода. Хотя на блок-схеме показаны модули коррекции амплитуды и фазы (Amplitude and Phase Correction) и адаптивного цифрового фильтра для уменьшения шума и режекции нежелательных сигналов (LMS Noise & Notch Filter), обсуждение этих фич выходит за рамки статьи.
LMS Noise and Notch Filter это адаптивный цифровой фильтр, который используется в программно-определяемых радио (SDR) для автоматического подавления нежелательных сигналов. Он основан на алгоритме наименьших квадратов (LMS).
LMS в контексте цифровой обработки сигналов и фильтрации расшифровывается как "наименьшие средние квадраты".
Этот фильтр выполняет две основные функции:
1. LMS Noise Reduction (шумоподавление): убирает шум, выделяя и сохраняя когерентные (повторяющиеся, похожие на сигнал) компоненты, а всё, что им не соответствует (шум) — ослабляет.
2. Automatic Notch Filter (автоматический режекторный фильтр, фильтр-пробка): вырезает узкую полосу частот, в которой находится мешающий сигнал, например, постоянный тон или несущую частоту другой станции. При этом он автоматически подстраивается под частоту помехи, в отличие от обычного режекторного фильтра с фиксированной настройкой.
Проще говоря, это интеллектуальный фильтр, который обучается на входящем сигнале и сам решает, что является полезным сигналом, а что — помехой, которую нужно подавить. Алгоритм адаптивной фильтрации работает по следующему принципу:
• Он вычисляет ошибку между желаемым сигналом и реальным выходом фильтра. • Затем он минимизирует средний квадрат этой ошибки (отсюда и название). • Для этого алгоритм постоянно корректирует свои весовые коэффициенты, подстраиваясь под изменяющиеся условия приема.
Именно из-за использования этого математического алгоритма фильтр и называется LMS Noise and Notch Filter — то есть фильтр, работающий по методу наименьших средних квадратов для подавления шума и узкополосных помех.
Коррекция амплитуды и фазы позволяет корректировать несовершенства в фазовом и амплитудном дисбалансе, созданном в аналоговой схеме, в частотной области. LMS noise and notch filters [5] являются адаптивной формой фильтрации с конечной импульсной характеристикой (FIR), которая обеспечивает снижение шума во временной области. Существуют другие методы подавления шума, которые могут быть выполнены в частотной области, такие как спектральное вычитание [6], корреляция [7 [и усреднение FFT [8].
[Парсинг входных буферов для получения векторов сигналов I и Q]
В статье (и в цифровой обработке сигналов в целом) массивы I и Q называют векторами по двум основным причинам: математической и практической.
1. Математическая причина (строгое определение)
В математике вектор — это упорядоченный набор чисел (кортеж). Массив отсчетов I (синфазный) и массив Q (квадратурный) — это именно последовательности чисел, следующих во времени. Поэтому с формальной точки зрения это самые настоящие векторы в пространстве сигналов.
2. Практическая причина (геометрическое представление)
Это главная причина. В SDR сигнал рассматривается не как два раздельных потока чисел (I и Q), а как одно комплексное число, где I — это реальная часть (Real), а Q — это мнимая часть (Imag):
S = I + jQ
Такое комплексное число принято изображать на комплексной плоскости (диаграмма Арганда), где:
- Ось X — это I (действительная часть). - Ось Y — это Q (мнимая часть).
Каждый отдельный отсчет сигнала в такой плоскости выглядит как точка, а если вы берете массив таких отсчетов, то каждый из них задает положение точки. Но чаще всего в обработке рассматривают не точки, а радиус-вектор — направленный отрезок от начала координат (0,0) до этой точки.
Что это дает на практике:
- Амплитуда сигнала в данный момент — это длина (модуль) этого вектора: квадратный корень от (I² + Q²). - Фаза сигнала в данный момент — это угол (аргумент) этого вектора: atan2(Q, I).
Когда вы вращаете этот вектор на комплексной плоскости, вы получаете сдвиг фазы сигнала. Когда вы меняете его длину — вы меняете амплитуду.
Почему удобно применять термин вектор в статье:
Название «вектор» подразумевает, что автор используют векторную обработку (SIMD-инструкции процессора) или матричные операции для фильтрации (как в том самом LMS-фильтре). LMS-алгоритм внутри себя постоянно умножает векторы отсчетов на весовые коэффициенты, складывает их и корректирует — это чистая линейная алгебра, где все оперируют векторами и матрицами.
Краткий итог: массивы I/Q называют векторами, потому что каждая пара (I, Q) задает направленный отрезок на комплексной плоскости, а весь массив — это последовательность таких отрезков, которую удобно обрабатывать методами векторной алгебры.
Рис. 2 предоставляет декларации переменных и констант демонстрационного кода.
PublicConstFsAsLong=44100'Частота дискретизации, выборок в секунду PublicConstNFFTAsLong=4096'Количество бинов FFT PublicConstBLKSIZEAsLong=2048'Количество выборок в блоке захвата/проигрывания (capture/play) PublicConstCAPTURESIZEAsLong=4096'Количество выборок в Capture Buffer PublicConstFILTERTAPSAsLong=2048'Количество коэффициентов (taps) в полосовом фильтре PrivateBinSizeAsSingle'Размер бинов FFT в Гц, т. е. разрешающая способность FFT
PrivateorderAsLong'Calculate Order power of 2 from NFFT PrivatefilterM(NFFT)AsDouble'Polar Magnitude of filter freq resp PrivatefilterP(NFFT)AsDouble'Polar Phase of filter freq resp PrivateRealIn(NFFT)AsDouble'Буферы FFT PrivateRealOut(NFFT)AsDouble PrivateImagIn(NFFT)AsDouble PrivateImagOut(NFFT)AsDouble
PublicFHighAsLong'Верхняя частота среза в Гц PublicFLowAsLong'Нижняя частота среза в Гц PublicFlAsDouble'Нижняя частота среза, выраженная как доля (коэффициент) ' от частоты дискретизации Fs. PublicFhAsDouble'Верхняя частота среза, выраженная как доля (коэффициент) ' от частоты дискретизации Fs. PublicSSBAsBoolean'True для режимов однополосной модуляции PublicUSBAsBoolean'Переменная для выбора полосы SSB PublicTXAsBoolean'True для выбора режима передачи PublicIFShiftAsBoolean'True для ПЧ 11.025 кГц
PublicAGCAsBoolean'True, если АРУ разрешена PublicAGCHangAsLong'AGC AGCHang time factor PublicAGCModeAsLong'Сохраняет выбор режима AGC PublicRXHangAsLong'Сохраняет установку времени RX Hang PublicAGCLoopAsLong'Счетчик буфера времени AGC AGCHang PrivateVpkAsDouble'Пик отфильтрованного выходного сигнала PrivateG(24)AsDouble'Буфер времени усиления AGCHang PrivateGainAsDouble'Установка состояния усиления для AGC PrivatePrevGainAsDouble'Усиление AGC во время предыдущего входного блока PrivateGainStepAsDouble'Шаги времени атаки AGC PrivateGainDBAsDouble'Усиление AGC в dB PrivateTempOut(BLKSIZE)AsDouble'Временный буфер для вычисления усиления PublicMaxGainAsLong'Максимальный коэффициент усиления AGC
PrivateFFTBinsAsLong'Количество бинов FFT для отображения PrivateM(NFFT)AsDouble'Полярная магнитуда двойной точности PrivateP(NFFT)AsDouble'Угол фазы двойной точности PrivateSAsLong'Счетчик циклов для выборок
"Размер бинов FFT в Гц" (Bin Size) — это частотное разрешение вашего преобразования Фурье. Простыми словами, это шаг частотной сетки, с которой анализатор «смотрит» на сигнал.
Когда вы подаете сигнал на вход БПФ (FFT), алгоритм раскладывает его на множество синусоид. Весь доступный диапазон частот (от 0 до частоты дискретизации) разбивается на равные интервалы. Один такой интервал — это один «бин» (корзина или карман).
Вот три ключевых момента, чтобы понять суть:
1. Что означает число в Гц?
Если BinSize = 10 Гц, то это значит, что FFT не может различить частоты, отличающиеся меньше чем на 10 Гц.
Например:
- Сигнал частотой 1000 Гц попадет в бин №100 (1000 / 10 = 100). - Сигнал частотой 1005 Гц попадет в тот же самый бин №100, потому что разница меньше 10 Гц. Обе частоты будут выглядеть на экране как одна и та же частота.
2. Как это зависит от количества точек FFT?
Размер бина вычисляется по простой формуле:
BinSize = Частота дискретизации (Fs) / Количество точек FFT (N)
Пример:
- Если Fs = 48 000 Гц, а N = 4096 точек, то BinSize = 48000 / 4096 ≈ 11.7 Гц. - Если взять N = 65536 точек, то BinSize = 48000 / 65536 ≈ 0.73 Гц (разрешение станет гораздо точнее).
3. Как это влияет на работу SDR (и на ваш код)?
- Маленький BinSize (например, < 1 Гц) → высокое разрешение. Вы сможете разглядеть на спектре две очень близкие радиостанции (например, на 14100000 Гц и 14100005 Гц). Но за это придется платить: фильтр работает медленнее, требует больше памяти и вычислений (больше точек FFT). - Большой BinSize (например, > 100 Гц) → низкое разрешение. Спектр выглядит сглаженным, близкие сигналы сливаются в один горб. Плюс: работает быстро и требует мало ресурсов.
Переменная BinSize в коде, скорее всего, используется для того, чтобы пересчитывать номера бинов в реальные частоты. То есть, если вы кликнули мышкой на бин №1500, программа умножит 1500 на BinSize и покажет вам, что это за частота в Герцах.
PrivateorderAsLong'Calculate Order power of 2 from NFFT
Фраза "Calculate Order power of 2 from NFFT" в контексте программирования FFT означает: "Вычислить показатель степени (порядок) числа 2, которым является NFFT".
Поскольку FFT требует, чтобы количество точек было строго равно степени двойки (например, 1024, 2048, 4096), переменная order хранит не само число точек, а показатель этой степени.
Как это работает (математика):
Если NFFT = 2048, то:
- 2048 = 2¹¹ - Следовательно, order = 11
Если NFFT = 4096, то:
- 4096 = 2¹² - Следовательно, order = 12
Зачем это нужно в программе (3 практические причины):
1. Битовое инвертирование (Bit-Reversal): классический алгоритм FFT (Кули-Тьюки) работает циклами, количество которых равно order. Ему нужно знать, сколько раз нужно делить массив пополам и переставлять отсчеты местами.
2. Вычисление «бабочек» (Butterflies): на каждом этапе алгоритма количество групп для вычислений меняется как степени двойки. Зная order, программа точно знает, сколько этапов обработки ей нужно выполнить.
3. Выделение памяти и оптимизация: зная порядок, программа может использовать сдвиги битов (операции типа `1 << order` в языках C/C++/VB) вместо умножения, что работает значительно быстрее, чем вычисление степени через циклы.
Проще говоря:
Вместо того чтобы хранить громоздкое число 4096, переменная order хранит компактное число 12. Это как сказать: «Возведи 2 в степень 12», чтобы получить 4096. Такой подход ускоряет работу программы, потому что оперировать показателем степени в циклах для процессора легче, чем большими числами с плавающей запятой.
PrivatefilterM(NFFT)AsDouble'Polar Magnitude of filter freq resp PrivatefilterP(NFFT)AsDouble'Polar Phase of filter freq resp
"Polar Magnitude of filter freq resp" переводится как "Полярная амплитуда (модуль) частотной характеристики фильтра".
Это означает, что массив filterM хранит амплитуду (величину, уровень сигнала), которую фильтр пропускает или подавляет на каждой конкретной частоте из сетки БПФ, но представленную не в линейной шкале, а в полярной системе координат.
Вот три ключевых момента для понимания:
1. Что такое «частотная характеристика фильтра» (freq resp)?
Это график, показывающий, как фильтр влияет на сигналы разных частот:
- На одних частотах он пропускает сигнал без изменений (коэффициент = 1 или 0 дБ). - На других — подавляет (коэффициент = 0 или -∞ дБ). - На промежуточных — ослабляет частично.
Массив filterM содержит эти коэффициенты для каждого бина частоты (от 0 до Fs/2).
2. Почему именно «полярная» (Polar)?
В цифровой обработке частотную характеристику можно представить двумя способами:
- Прямоугольная форма: `H = Re + jIm` (действительная + мнимая части, т.е. I и Q). - Полярная форма: `H = |H| · e^(j·φ)` (амплитуда + фаза).
Здесь filterM хранит именно амплитудную часть (модуль `|H|`) в полярной форме. Фаза (`φ`), хранится в другой переменной filterP (или не учитывается, если фильтр имеет линейную фазовую характеристику, что типично для FIR-фильтров).
3. Зачем это нужно в коде?
Массив filterM используется для умножения спектра сигнала на частотную характеристику фильтра в частотной области (метод быстрой свертки):
1. Вы берете FFT от входного сигнала → получаете спектр (комплексные числа).
2. Умножаете каждое комплексное число спектра на соответствующее значение из filterM (модуль).
3. Делаете обратное FFT (IFFT) → получаете отфильтрованный сигнал во времени.
Поскольку filterM — это модуль (вещественное число, а не комплексное), умножение на него изменяет только амплитуду каждой частотной составляющей, не сдвигая её по фазе. Это упрощает вычисления и ускоряет код.
Краткий итог: `filterM(NFFT)` — это массив вещественных чисел размером NFFT, где каждое число показывает, во сколько раз фильтр усиливает (или ослабляет) сигнал на соответствующей частоте. И хранится это в виде модуля (полярная форма), а не как пара I/Q.
Соответственно "Polar Phase of filter freq resp" переводится как "Полярная фаза (угол) частотной характеристики фильтра".
Эта переменная напрямую связана с тем, как работает фильтрация через FFT в реальном времени.
«Overlap prev FFT/IFFT» переводится как «Перекрытие (наложение) предыдущего TTF/IFFT».
Простыми словами: это буфер памяти, в котором хранится «хвост» предыдущего обработанного блока данных, чтобы добавить его к началу следующего блока.
Вот почему это нужно и как работает:
1. Проблема: фильтр длиннее, чем блок данных
У вас есть:
- FILTERTAPS = 2048 (фильтр «помнит» 2048 отсчетов). - NFFT (размер FFT) — скорее всего тоже 2048 или больше.
Но в SDR сигнал идет непрерывным потоком. Вы не можете ждать, пока наберется 2048 отсчетов, обработать их, и выкинуть — на стыках блоков будут разрывы и щелчки.
Метод «Overlap-Add» (перекрытие с суммированием):
- Вы берете блок из, скажем, 1024 новых отсчетов. - Добавляете к ним предыдущие 1024 отсчета (которые уже были обработаны ранее) — это и есть overlap (перекрытие). - Получаете блок длиной 2048 (NFFT), делаете FFT, фильтруете, делаете IFFT. - Первые 1024 отсчета результата — это готовый выходной сигнал. - Вторые 1024 отсчета вы сохраняете в буфер IOverlap и QOverlap, чтобы на следующем шаге добавить их к новым 1024 отсчетам.
2. Почему размер буфера именно NFFT - FILTERTAPS - 1?
Формула `NFFT - FILTERTAPS - 1` определяет, сколько отсчетов нужно сохранить для перекрытия.
Если `NFFT = 2048`, а `FILTERTAPS = 2048`, то размер буфера = `2048 - 2048 - 1 = -1`? Это странно...
Скорее всего, на самом деле NFFT > FILTERTAPS (например, NFFT = 4096, FILTERTAPS = 2048). Тогда:
4096 - 2048 - 1 = 2047 — размер буфера для хранения «хвоста».
Это означает, что фильтр обрабатывает блок длиной 4096 точек, из которых только часть (первые 2048) выдается на выход, а остальные сохраняются для следующего цикла.
- Шаг 1: берём отсчёты 0..7, фильтруем. Первые 4 отсчёта (0..3) отправляем на выход. Отсчёты 4..7 сохраняем в `IOverlap`. - Шаг 2: берём новые отсчёты 8..11. Добавляем к ним сохранённые отсчёты 4..7 → получаем блок 4..11 (длина 8). Фильтруем. Отсчёты 4..7 отправляем на выход. Отсчёты 8..11 сохраняем для следующего шага. - И так далее.
Таким образом, сигнал на выходе получается непрерывным и гладким, без щелчков на границах блоков.
Краткий итог: IOverlap и QOverlap — это буферы, в которых хранятся «остатки» от предыдущего обработанного блока FFT, чтобы при объединении с новым блоком создать плавное, непрерывное перекрытие (overlap) сигнала при фильтрации через метод Overlap-Add. Это стандартный приём в DSP для обработки бесконечных потоков данных конечными фильтрами (FIR) через FFT.
«Fast Convolution Filter buffers» переводится как «Буферы фильтра быстрой свертки».
Это массивы, которые используются для реализации фильтрации через быстрое преобразование Фурье (FFT) — метода, который позволяет обрабатывать сигнал значительно быстрее, чем традиционная фильтрация во временной области (особенно когда количество коэффициентов фильтра FILTERTAPS велико, например, 2048).
Вот как это работает и для чего нужны именно эти 4 буфера:
1. Что такое «быстрая свертка» (Fast Convolution)?
Обычный FIR-фильтр работает во временной области: каждый выходной отсчет вычисляется как сумма произведений 2048 входных отсчетов на 2048 коэффициентов. Это очень медленно.
Быстрая свертка делает то же самое, но через частотную область:
- Берете блок сигнала (NFFT отсчетов). - Делаете FFT → получаете спектр (комплексные числа: I + jQ). - Умножаете спектр на частотную характеристику фильтра (массив filterM). - Делаете обратное FFT (IFFT) → получаете отфильтрованный сигнал во времени.
Это дает огромный выигрыш в скорости при длинных фильтрах.
2. Почему нужны 4 буфера (а не 2)?
В SDR сигнал комплексный (I и Q). После FFT спектр тоже комплексный. Для фильтрации нужно хранить:
- Реальную часть (I) спектра до фильтрации и после. - Мнимую часть (Q) спектра до фильтрации и после.
Но почему в коде RealOut_1, RealOut_2, ImagOut_1, ImagOut_2 — 4 буфера?
Скорее всего, это организовано так:
Буфер
Назначение
RealOut_1 / ImagOut_1
Входной буфер — спектр сигнала до умножения на фильтр (результат прямого FFT).
RealOut_2 / ImagOut_2
Выходной буфер — спектр сигнала после умножения на фильтр (перед обратным FFT).
Либо это два независимых канала (например, левый и правый стерео, или два разных радиоканала), обрабатываемых параллельно.
3. Зачем нужна буферизация?
Поскольку данные поступают непрерывным потоком, а FFT работает с блоками фиксированной длины (NFFT), вам нужно:
- Накопить достаточное количество отсчетов для одного FFT. - Пока один буфер заполняется и обрабатывается, второй уже выдается на выход (или наоборот).
Это классический прием двойной буферизации (double buffering):
- Пока процессор обрабатывает данные из буфера №1, АЦП заполняет буфер №2 новыми отсчетами. - По окончании обработки буферы меняются ролями.
Так достигается непрерывная обработка в реальном времени без пропусков и задержек.
4. Связь с предыдущими переменными (общая картина):
Вся цепочка в коде выглядит так:
- Приходят новые отсчеты I/Q. - Они накапливаются с учетом перекрытия (буферы IOverlap / QOverlap). - Формируется блок длиной NFFT. - Выполняется прямое БПФ → спектр записывается в RealOut_1 / ImagOut_1. - Спектр умножается на filterM (частотную характеристику) → результат помещается в RealOut_2 / ImagOut_2. - Выполняется обратное FFT → получаем отфильтрованный сигнал во временной области. - Часть результата выдается на выход, часть сохраняется в Overlap для следующего цикла. - Цикл повторяется.
Краткий итог: RealOut_1, RealOut_2, ImagOut_1, ImagOut_2 — это рабочие буферы для хранения комплексных спектров сигнала на разных этапах быстрой свертки (до и после умножения на частотную характеристику фильтра). Они нужны для организации непрерывной конвейерной обработки и двойной буферизации в реальном времени.
«AGC AGCHang time factor» — это фактор времени «зависания» (удержания) автоматической регулировки усиления (AGC).
Простыми словами: это параметр, который определяет, как долго AGC будет удерживать усиление на пониженном уровне после того, как мощный сигнал перестал действовать.
Вот три ключевых момента для понимания:
1. Что делает AGC (Automatic Gain Control, АРУ)?
AGC автоматически подстраивает усиление приемника так, чтобы:
- Слабые сигналы — усиливались сильнее. - Сильные сигналы — усиливались слабее (чтобы не перегружать тракт и не вызывать искажений).
Это работает как «умная ручка громкости», которая сама подстраивается под уровень принимаемого сигнала.
2. Что такое «Hang» (зависание) в AGC?
В классической AGC есть два основных этапа:
Режим
Действие
Attack (атака)
Быстрое уменьшение усиления при появлении сильного сигнала (чтобы не перегрузиться).
Decay (спад/восстановление)
Постепенное увеличение усиления после того, как сильный сигнал исчез.
Hang — это задержка перед началом Decay.
То есть:
- Появился мощный сигнал → усиление резко упало (Attack). - Сигнал исчез. - Усиление зависает (Hang) на низком уровне в течение времени AGCHang. - Только после этого начинается медленное восстановление (Decay) до нормального уровня.
3. Зачем это нужно (практический смысл)?
Представьте, что вы слушаете SSB-связь (телефонный режим в радиолюбительстве). Голос имеет паузы между словами:
- Без Hang: в паузах между словами усиление начнет расти, а когда снова появится голос — резко упадет (Attack). Это создаст эффект «дыхания» (pumping) — уровень громкости будет постоянно «плавать», и голос будет звучать неестественно. - Применение Hang: усиление остается низким в течение всей паузы (благодаря фактору AGCHang), и когда голос возобновляется, уровень уже настроен правильно. Паузы остаются тихими, голос — ровным и чистым.
Это особенно важно для приема:
- Телеграфа (CW) — чтобы не было колебаний громкости между точками и тире. - Телефонных режимов (SSB/AM) — для естественного звучания речи. - Слабых сигналов в шумах — чтобы шум не «раскачивал» усиление.
4. Как это связано с «time factor»?
Название «time factor» подразумевает, что AGCHang хранится не в секундах или миллисекундах, а в безразмерных единицах (например, количество отсчетов или тактов процессора), которые затем умножаются на длительность одного отсчета (1/Fs), чтобы получить реальное время задержки. Либо это просто условный коэффициент, регулирующий длительность Hang в алгоритме.
Чем больше значение AGCHang:
- Дольше усиление остается низким после ухода сигнала. - Плавнее и естественнее звук на паузах. - Но при слишком большом значении — вы можете пропустить начало нового слабого сигнала.
Чем меньше значение:
- AGC быстрее восстанавливается. - Но может появиться эффект «дыхания» на быстрой речи или при работе с ключом.
Краткий итог: AGCHang — это параметр, который управляет временем задержки восстановления усиления в AGC. Он нужен, чтобы усиление не начинало расти в паузах между словами или символами, предотвращая неприятный эффект «дыхания» и делая прием более комфортным. Чем больше AGCHang — тем дольше «зависает» усиление на низком уровне после исчезновения сильного сигнала.
Код парсинга входного буфера inBuffer() показан на рис. 3. Левый и правый сигнальные входы должны быть разобраны на I и Q сигнальные каналы, прежде чем они будут представлены на входе FFT. 16-битовые целочисленные выборки левого и правого каналов во входном буфере перемежаются, поэтому код, показанный на рис. 3, должен использоваться для разделения сигналов. Массивы RealIn() и RealOut() используются для хранения векторов I-сигнала, а массивы ImagIn() и ImagOut() используются для хранения векторов Q-сигнала. Это соответствует номенклатуре, используемой в сложном алгоритме FFT. Не критично, какой из I и Q каналов идет на какой вход, потому что можно просто переставить массивы в коде рис. 3, если боковые полосы инвертированы.
EraseRealIn,ImagIn
ForS=0ToCAPTURESIZE-1Step2'Копирование I в RealIn и Q в ImagIn RealIn(S\2)=inBuffer(S+1) ImagIn(S\2)=inBuffer(S) NextS
Рис. 3. Парсинг входных буферов для получения векторов сигналов I и Q.
[FFT: преобразование в домен частот (спектр)]
В части 1 [18] этой серии статей обсуждается, как FFT используется для преобразования дискретных по времени сигналов из области времени (time domain) в область частот (frequency domain, или спектр). Реализация FFT "в лоб" довольно сложна математически и несколько утомительна для кодирования. К счастью, Intel предоставила код с оптимизированной производительностью в виде DLL, который можно вызвать из одной строки кода для этой и других важных функций DSP (см. примечание [4]).
FFT фактически состоит из ряда очень узких полосовых фильтров, выходные сигналы которых называются бинами, как показано на рис. 4. Каждый бин имеет значение магнитуды и фазы, представляющие содержание дискретизированного входного сигнала на центральной частоте соответствующего бина. Перекрытие соседних бинов напоминает выход гребенчатого фильтра, как описано в части 1.
Рис. 4. Выходные "бины" FFT.
PC SDR использует 4096-bin FFT. При частоте дискретизации 44100 Гц полоса пропускания каждого элемента дискретизации составляет 10.7666 Гц (44100/4096), а центральная частота каждого элемента дискретизации равна числу элементов дискретизации, умноженному на полосу пропускания. Отметим на рис. 4, что относительно центральной частоты дискретизированного квадратурного сигнала верхняя боковая полоса расположена в бинах 1 .. 2047, а нижняя боковая полоса расположена в бинах 2048 .. 4095. Ячейка 0 содержит несущую, преобразованную в 0 Гц. FFT, выполняемый на аналитическом сигнале I + jQ, позволяет анализировать положительные и отрицательные частоты раздельно.
Я использовал звуковую карту Turtle Beach Santa Cruz, у которой АЧХ гладкая с точностью 3 dB в диапазоне частот от 10 Гц до 20 кГц (замечание: даташит указывает верхнюю частоту среза 120 кГц, что скорее всего типографическая ошибка, так как максимальная частота дискретизации у этой карты 48 кГц). Поскольку мы оцифровываем RF в квадратурной форме, частота дискретизации фактически удваивается (если 44100 Гц умножить на 2 канала, получается 88200 Гц эффективная частота выборок). Это означает, что выходной спектр FFT будет в два раза больше, чем у одного оцифрованного канала. В нашем случае общая выходная полоса частот FFT составит 10.7666 Гц умножить на 4096 или 44100 Гц. Поскольку большинство звуковых карт дают спад АЧХ 20 кГц, мы вероятно, ограничены общей полосой пропускания примерно 40 кГц.
Рис. 5 показывает вызовы DLL библиотеки Intel для FFT и последующего преобразования векторов сигнала из картезианской (декартовой) системы координат в полярную систему координат. Подпрограмма nspzrFftNip принимает на входе вектора домена времени RealIn() и ImagIn(), и преобразует их в вектора домена частот RealOut() и ImagOut(). Порядок (order) FFT вычисляется в подпрограмме, которая формирует коэффициенты фильтра, что будет обсуждаться далее. NSP_Forw это константа, которая задает для подпрограммы выполнить прямое быстрое преобразование Фурье (FFT).
Рис. 5. Преобразование сигнала time domain в сигнал frequency domain с помощью FFT.
В декартовой (Cartesian) системе координат сигнал представлен величинами двух векторов: одного на вещественной плоскости или плоскости x, и одного на воображаемой плоскости или плоскости y. Эти векторы могут быть преобразованы в один вектор с величиной (М) и фазовым углом (Р) в полярной системе координат. В зависимости от конкретного алгоритма DSP, который мы хотим выполнить, одна система координат или другая может быть более эффективной. Я использую полярную систему координат для большей части обработки сигнала в этом примере. Подпрограмма nspdbrCartToPolar преобразует выходные данные FFT в полярный вектор, состоящий из значений магнитуды M() и значений фаз P(). Эта функция одновременно выполняет вычисления по формулам Выражения3 и Выражения4 в части 1 [18] этой серии статей.
[Смещение преобразования основной ПЧ к 0 Гц]
Моя оригинальная программа центрировала частоту несущей RF в бине 0 (0 Гц). С помощью этой реализации можно отображать (и слышать) весь спектр 44 кГц в режиме реального времени. Одна из проблем, встречающихся в приемниках с прямым преобразованием или с нулевым ПЧ, заключается в том, что шум увеличивается по существу около 0 Гц. Это вызвано несколькими механизмами: шумом 1/f в активных компонентах, шумом 60/120 Гц (для США, или 50/100 Гц для России) от линий электропередач переменного тока, микрофонным шумом, вызванным механической вибрацией и фазовым шумом гетеродина. Это может быть проблемой для работы со слабым сигналом, потому что большинство людей настраивают сигналы CW для тона 700-1000 Гц. К счастью, большая часть этого шума исчезает выше 1 кГц.
Учитывая, что у нас есть 44 кГц спектра для работы, мы можем сдвинуть цифровую ПЧ на любую частоту в пределах выходного диапазона FFT. Это просто вопрос принятия решения о том, какой бин БПФ обозначить в качестве несущей частоты, а затем сместить гетеродин на соответствующую величину. Затем выполняется копирование соответствующих ячеек для требуемой боковой полосы так, чтобы они располагались на 0 Гц для последующей обработки. В PC SDR я выбрал использовать смещение ПЧ (IF) 11025 Гц, что составляет одну четвертую частоты дискретизации, как показано на рис. 6.
Рис. 6. Диаграмма смещения ПЧ основного диапазона. Частота гетеродина смещается на 11025 кГц, так что требуемая несущая частота сигнала центрирована со смещением 11025 Гц на выходе FFT. Чтобы сдвинуть сигнал для последующей фильтрации, желательные элементы дискретизации просто копируются для центрирования несущей частоты fc на 0 Гц.
На рис. 7 показан исходный код для смещения ПЧ (IF) на 0 Гц. Частота несущей 11025 Гц сдвигается к бину 0, а верхняя полоса частот смещается на бины от 1 до 1023. Нижняя полоса частот смещается на бины от 3072 до 4094. Код позволяет разрешить или запретить смещение IF, как это требуется для передачи.
IFShift=True'Принудительно True для demo
IfIFShift=TrueThen'Смещение боковых полос от 11025 кГц ПЧ ForS=0To1023 IfUSBThen M(S)=M(S+1024)'Перемещение верхней полосы на 0 Гц P(S)=P(S+1024) Else M(S+3072)=M(S+1)'Перемещение нижней полосы на 0 Гц P(S+3072)=P(S+1) EndIf Next EndIf
Рис. 7. Код для преобразования с понижением частоты со смещенной ПЧ основной полосы до 0 Гц.
[Выбор боковой полосы]
Итак, как мы выбираем боковую полосу? Мы храним нули в бинах, которые не хотим слышать. Насколько это просто? Если бы можно было иметь идеальную аналоговую амплитуду и фазовый баланс на дискретизированных входных сигналах I и Q, у нас было бы бесконечное подавление боковой полосы. Поскольку это невозможно, любой дисбаланс будет проявляться в виде изображения в полосе пропускания приемника. К счастью, эти дисбалансы могут быть исправлены с помощью кода DSP либо во временной области до FFT, либо в частотной области после FFT. Эти методы выходят за рамки этого обсуждения, но я могу рассказать о них в следующей статье. Мой прототип, использующий инструментальные усилители INA103, достигает примерно 40 дБ подавления противоположной боковой полосы без коррекции в программном обеспечении.
Код для обнуления противоположной полосы частот предоставлен на рис. 8. Нижняя полоса частот находится в бинах с большими номерами, а верхняя полоса частот находится в бинах с меньшими номерами. Для экономии времени обработки я только обнулил количество бинов, содержащееся в переменной FFTBins.
[Магия фильтрации FFT Fast-Convolution]
Каждый текст DSP, который я прочитал по модуляции и демодуляции одной боковой полосы, описывает подход оцифровки ПЧ (IF). По этому методу A/D-преобразователь оцифровывает сигнал на такой IF, как 40 кГц. Затем сигнал квадратурно преобразуется с понижением частоты на программном обеспечении в основную полосу частот и фильтруется с использованием фильтров с характеристикой finite impulse response (FIR, по-русски этот термин называется фильтр с конечной импульсной характеристикой КИХ) [9]. Такие системы описал Doug Smith QEX в статье "Signals, Samples, and Stuff: A DSP Tutorial (Part 1)" [10]. При таком подходе вся обработка выполняется во временной области (time domain).
Для PC SDR я решил использовать совершенно другой подход, называемый фильтрацией быстрой свертки FFT, FFT fast-convolution filtering (также называемой FFT convolution, или сверткой FFT), который выполняет все функции фильтрации в частотной области [11]. FIR-фильтр выполняет свертку входного сигнала с импульсной характеристикой фильтра во временной области. Свертка - это математическое средство объединения двух сигналов (например, входного сигнала и импульсной характеристики фильтра) для формирования третьего сигнала (отфильтрованного выходного сигнала) [12]. Подход временной области очень хорошо работает для небольшого количества отводов фильтра. Что делать, если мы хотим построить очень высокопроизводительный фильтр с 1024 или более ответвлениями? Время на обработку данных FIR-фильтра может оказаться чрезмерно большим. Оказывается, что важным свойством преобразования Фурье является то, что свертка во временной области равна умножению в частотной области. Вместо прямой свертки входного сигнала с импульсной характеристикой фильтра с оконной структурой, как в случае FIR-фильтра, мы берем соответствующие FFT входного сигнала и импульсной характеристики фильтра и просто перемножаем их, как показано на рис. 9. Чтобы вернуться к временной области, мы выполняем IFFT продукта. Свертка FFT часто быстрее, чем прямая свертка для ядер фильтров длиной более 64 ответвлений, и дает точно такой же результат.
Рис. 9. Блок-схема фильтрации FFT fast-convolution. Коэффициенты импульсной характеристики фильтра сначала преобразуются в частотную область с использованием FFT и сохраняются для повторного использования подпрограммой фильтра. Каждый сигнальный блок преобразуется с помощью FFT и затем умножается на амплитуды частотной характеристики фильтра. Полученный отфильтрованный сигнал преобразуется обратно во временную область с использованием обратного БПФ (IFFT). Подпрограмма наложения/сложения (Overlap/Add) корректирует сигнал для круговой свертки.
Для меня свертку FFT (FFT convolution) легче понять, чем прямую свертку (direct convolution), потому что я мысленно визуализирую фильтры в частотной области. Как описано в части 1 [18] этой серии статей, выходной сигнал комплексного FFT можно рассматривать как длинный набор узких полосовых фильтров, выровненных вокруг несущей частоты (бин 0), что показано на рис. 4. Рис. 10 иллюстрирует процесс свертки FFT преобразованной импульсной характеристики фильтра с преобразованным входным сигналом. Как только сигнал преобразуется обратно во временную область через IFFT, мы должны выполнить процесс, называемый методом перекрытия/добавления (overlap/add). Это происходит потому, что процесс свертки производит выходной сигнал, который равен по длине сумме входных выборок плюс отводов фильтра минус один. Я не буду пытаться объяснить концепцию здесь, потому что она лучше всего описана в ссылках [13].
Рис. 10. Выход фильтрации FFT fast convolution. Когда коэффициенты filter-magnitude умножаются на значения signal-бин, результирующие выходные бины содержат значения только в пределах полосового фильтра.
Рис. 11 предоставляет исходный код для формирования коэффициентов полосового фильтра частотной области. Подпрограмма CalcFilter принимает в качестве параметров нижнюю частоту среза FLow и верхнюю частоту среза FHigh, чтобы сформировать АЧХ фильтра. Затем частоты среза преобразуются в их соответствующие доли частоты дискретизации для использования подпрограммой формирования фильтра nspdFirBandpass. Порядок FFT также определяется этой подпрограммой, основываясь на размере FFT, NFFT. Подпрограмма nspdFirBandpass вычисляет импульсную характеристику полосового фильтра от Fl() до Fh() и длины FILTERTAPS. Затем результат помещается в переменную массива Rh(). NSP_WinBlackmanOpt вызывает формирование окна импульсной характеристики с помощью оконной функции Blackman. Что такое оконная функция, см. DSP Guide [14]. Значение 1, которое передается подпрограмме, приводит к нормализации результата.
PublicStaticSubCalcFilter(FLowAsLong,FHighAsLong)
StaticRh(NFFT)AsDouble'Impulse response for bandpass filter
StaticIh(NFFT)AsDouble'Imaginary set to zero
StaticreH(NFFT)AsDouble'Real part of filter response
StaticimH(NFFT)AsDouble'Imaginary part of filter response
EraseIh
Fh=FHigh/Fs'Вычисление верхней и нижней частот среза
Fl=FLow/Fs' как доли частоты дискретизации Fs
BinSize=Fs/NFFT'Вычисление размера бина FFT в Гц
FFTBins=(FHigh/BinSize)+50'Количество бинов FFT в полосе фильтра
order=NFFT'Вычисление порядка NFFT как степени 2 DimOAsLong
ForO=1To16'Вычисление порядка фильтра order=order\2 Iforder=1Then order=O ExitFor EndIf Next
'Вычисление коэффициентов полосового IIR-фильтра ' с оконной функцией Blackman:
nspdFirBandpassFl,Fh,Rh,FILTERTAPS,NSP_WinBlackmanOpt,1
'Вычисление комплексных значений полосового фильтра в частотной области:
nspzrFftNipRh,Ih,reH,imH,order,NSP_Forw
nspdbrCartToPolarreH,imH,filterM,filterP,NFFT
EndSub
Рис. 11. Код для генерации коэффициентов полосового фильтра в частотной области (frequency domain).
Затем импульсная характеристика преобразуется в частотную область с помощью nspzrFftNip. Входными параметрами являются Rh() вещественная часть импульсной характеристики, и Ih() мнимая часть, которая была установлена на ноль. NSP_Forw предписывает подпрограмме выполнить прямое FFT. Затем мы преобразуем результат частотной области FFT, reH() и imH() в полярную форму с помощью подпрограммы nspdbrCartToPolar. Массивы магнитуд фильтра filterM() и фаз фильтра filterP() сохраняются для использования в фильтре быстрой свертки FFT. За исключением случаев, когда мы вручную изменяем выбор полосового фильтра, отклик фильтра не изменяется. Это означает, что мы должны рассчитать отклик фильтра только один раз, когда фильтр впервые выбран пользователем.
На рис. 12 представлен код для фильтра быстрой свертки БПФ (FFT fast-convolution filter). Используя подпрограмму nspdbMpy2, двоичные коды амплитуды спектра сигнала M() умножаются на двоичные коды амплитуды частотного отклика фильтра filterM() для генерирования результирующих отфильтрованных по месту двоичных кодов амплитуды отклика M(). Затем мы используем nspdbAdd2 для добавления двоичных кодов фазы сигнала P() к двоичным кодам фазы фильтра filterP(), при этом результат сохраняется по месту в отфильтрованных двоичных кодах фазы P().
Рис. 12. Код фильтрации быстрой свертки БПФ (FFT fast convolution filtering) с использованием полярных векторов.
Обратите внимание, что свертка БПФ также может быть выполнена в декартовых (cartesian) координатах с использованием метода, показанного на рис. 13, хотя этот метод требует больше вычислительных ресурсов. Другие применения значений амплитуды частотной области включают в себя усреднение FFT, цифровое подавление (digital squelch) и отображение спектра.
Рис. 13. Альтернативный код фильтрации быстрой свертки БПФ с использованием декартовых (cartesian) векторов.
На рис. 14 показан действительный спектральный выходной сигнал 500-Гц фильтра, использующего широкополосный входной шум и усреднение FFT сигнала в течение нескольких секунд. Это обеспечивает хорошую картину частотной характеристики и формы фильтра. Коэффициент формы 2048-отводного фильтра составляет 1.05 от 3-dB до 60-dB точек (большинство производителей измеряют от 6 дБ до 60 дБ, это более мягкая спецификация). Обратите внимание, что затухание в полосе частот подавления превышает 120 дБ примерно на 250 Гц от точек 3-dB. Это очень высококачественный фильтр!
Рис. 14. Вид АЧХ фактического CW-фильтра на 500-Гц. Фильтрация быстрой свертки БПФ (FFT fast-convolution filtering) используется с 2048 отводами фильтра для получения коэффициента формы характеристики 1.05 от 3 dB до 60 dB вниз и более 120 dB затухания в полосе подавления частот всего 250 Гц за пределами точек 3 dB.
Интересным фактом этого способа является то, что окно применяется к импульсной характеристике фильтра, а не к входному сигналу. Отклик фильтра нормализуется, так что сигналы в полосе пропускания не ослабляются в частотной области. Я полагаю, что эта нормализация отклика фильтра устраняет обычное ослабление, связанное с оконной обработкой сигнала перед выполнением FFT. Чтобы преодолеть такое затухание окна, типично применять перекрытие 50-75% в процессе выборки во временной области и усреднять FFT в частотной области. Буду признателен за комментарии знающих читателей по этой гипотезе.
[IFFT и Overlap/Add - преобразование обратно в Time Domain]
Перед возвратом во временную область (time domain) мы должны сначала преобразовать данные обратно в декартовы (Cartesian) координаты с помощью подпрограммы nspdbrPolarToCart, как показано на рис. 15. Затем путем установки флага NSP_Inv выполняется обратная БПФ (IFFT) подпрограммой nspzrFftNip, которая помещает time-domain выходные данные в массивы RealOut() и ImagOut(). Как уже обсуждалось ранее, мы должны снова реализовать перекрытие с добавлением (overlap and add) порции сигнала из предыдущего цикла захвата, как это описывается в DSP Guide (см. примечание [13]). Ioverlap() и Qoverlap() сохраняют синфазный (in-phase) и квадратурный (quadrature) сигналы с перекрытием от последнего прохода для добавления к новому блоку сигнала с помощью подпрограммы nspdbAdd3.
'Преобразование из полярных значений в декартовы (polar to cartesian) nspdbrPolarToCartM,P,RealIn,ImagIn,NFFT
'IFFT для обратного преобразование во временную область (time domain) nspzrFftNipRealIn,ImagIn,RealOut,ImagOut,order,NSP_Inv
'Overlap и Add от последнего FFT/IFFT: RealOut(s) = RealOut(s) + Overlap(s) nspdbAdd3RealOut,IOverlap,RealOut,FILTERTAPS-2 nspdbAdd3ImagOut,QOverlap,ImagOut,FILTERTAPS-2
'Сохранение Overlap для следующего прохода ForS=BLKSIZEToNFFT-1 IOverlap(S-BLKSIZE)=RealOut(S) QOverlap(S-BLKSIZE)=ImagOut(S) Next
Рис. 15. Код обратного БПФ (Inverse FFT, IFFT) и перекрытия/добавления (overlap/add).
[Цифровая АРУ с переменным временем удержания]
Код цифровой АРУ (AGC) на рис. 16 предоставляет управление усилением с быстрой атакой (fast-attack, быстрое нарастание усиления) и задержкой затухания усиления (decay) и переменным временем удержания усиления (Variable Hang Time). Как атака усиления, так и затухание усиления обе происходят за приблизительно 1 мс, однако время удержания затухания усиления (hang time) может быть установлено в любое желаемое значение с шагом 46 мс. Я решил реализовать атаку/затухание с линейной функцией нарастания, вместо экспоненциальной функции, как описано в коммуникационных текстах DSP [15]. Она работает очень хорошо, и интуитивно понятна для программирования. Блок-схема на рис. 17 описывает логику, используемую в алгоритме АРУ.
IfAGC=TrueThen 'Если true, то инкремент счетчика AGCLoop, иначе сброс его в 0 AGCLoop=IIf(AGCLoop<AGCHang-1,AGCLoop+1,0) nspdbrCartToPolarRealOut,ImagOut,M,P,BLKSIZE'Огибающая полярной магнитуды ' (Envelope Polar Magnitude) Vpk=nspdMax(M,BLKSIZE)'Получение пика магнитуды
IfVpk<>0Then'Проверка для устранения деления на 0 G(AGCLoop)=16384/Vpk'Коэффициент усиления AGC с запасом 6 dB Gain=nspdMin(G,AGCHang)'Поиск снижения пикового усиления EndIf
IfGain>MaxGainThenGain=MaxGain'Ограничение усиления до MaxGain
IfGain<PrevGainThen'Усиление AGC уменьшается GainStep=(PrevGain-Gain)/44'44 Sample ramp = 1 ms attack time ForS=0To43'Скат усиления с периодом 1 мс M(S)=M(S)*(PrevGain-((S+1)*GainStep)) Next ForS=44ToBLKSIZE-1'Умножение остальной огибающей на усиление M(S)=M(S)*Gain Next Else IfGain>PrevGainThen'Усиление AGC увеличивается GainStep=(Gain-PrevGain)/44'44 Sample ramp = 1 ms decay time ForS=0To43'Нарастание усиления с периодом 1 мс M(S)=M(S)*(PrevGain+((S+1)*GainStep)) Next ForS=44ToBLKSIZE-1'Умножение остальной огибающей на усиление M(S)=M(S)*Gain Next Else nspdbMpy1Gain,M,BLKSIZE'Умножение огибающей на усиление AGC EndIf EndIf
PrevGain=Gain'Сохранение усиления для следующего цикла nspdbThresh1M,BLKSIZE,32760,NSP_GT'Жесткий ограничитель для предотвращения ' переполнения EndIf
Для последующего описания давайте рассмотрим код на рис. 16 и его алгоритм на рис. 17. Сначала мы проверяем, что АРУ включена. Если это так, то мы инкрементируем AGCLoop, счетчик циклов времени задержки АРУ (AGC hang-time loops). Каждый проход по этому коду соответствует времени задержки 46 мс. PC SDR предоставляет настройки времени hang-time 3 (быстрое АРУ, восстановление 132 мс), 5 (среднее время восстановления, 230 мс), 7 (медленное время восстановления, 322 мс) и 22 (долгое время восстановления, 1.01 секунды). Настройка hang-time сохранена в переменной AGCHang. Как только счетчик hang-time сбрасывается, затухание происходит с линейным наклоном 1 мс.
Чтобы определить требование к усилению АРУ, мы должны детектировать огибающую демодулированного сигнала. Это просто осуществить преобразованием координат из декартовых (Cartesian) в полярные (polar). Значение M() это огибающая, или магнитуда сигнала. Вектор фазы в контексте АРУ может быть проигнорирован. Однако нам потребуется сохранить значения фаз для последующего преобразования в декартовы координаты. Как только у нас есть величины, хранящиеся в M(), становится простым нахождение пиковой величины и сохранение ее в Vpk с функцией nspdMax. После проверки, чтобы предотвратить ошибку деления на ноль, мы вычисляем коэффициент усиления относительно 50% от значения полной шкалы. Это обеспечивает запас в 6 дБ от пика сигнала до полной шкалы выходного значения DAC (ЦАП). При каждом проходе коэффициент усиления сохраняется в массиве G(), чтобы мы могли найти снижение пикового усиления в течение периода зависания с помощью функции nspdMin. Затем коэффициент уменьшения пикового усиления сохраняется в переменной Gain. Обратите внимание, что Gain сохраняется как коэффициент, а не в децибелах, так что преобразование log/antilog не требуется.
Следующим шагом является ограничение Gain значением MaxGain, которое может быть установлено пользователем. Эта система функционирует так же, как управление усилением IF, позволяющее Gain изменяться от отрицательных значений до настройки MaxGain. Хотя такое не предусмотрено в примере кода, это простая задача, чтобы создать элемент управления передней панели в Visual Basic, чтобы вручную установить значение MaxGain.
Далее определяем, нужно ли увеличивать, уменьшать или оставлять без изменений усиление. Если Gain меньше, чем PrevGain (это настройка Gain из блока сигналов, сохраненного на последнем проходе через код), мы линейно уменьшаем усиление по 44 выборкам. Это дает время атаки приблизительно 1 мс при частоте дискретизации 44100 Гц. GainStep - это наклон линейного изменения за время выборки, рассчитанный по значениям PrevGain и Gain. Затем мы постепенно уменьшаем первые 44 выборки на значение GainStep. После перехода к новому значению Gain оставшиеся выборки умножаются на фиксированное значение Gain.
Если Gain увеличивается от значения PrevGain, процесс просто реверсируется. Если коэффициент усиления не изменился, все выборки умножаются на текущую настройку коэффициента усиления. После обработки сигнального блока Gain сохраняется в PrevGain для следующего сигнального блока. Наконец, nspdbThresh1 реализует жесткий ограничитель примерно на максимальном выходном уровне DAC, чтобы предотвратить переполнение выходных буферов с целочисленной переменной.
[Отправка демодулированного или модулированного сигнала в выходной буфер]
Последним шагом является форматирование обработанного сигнала для вывода в ЦАП. При приеме сигнал RealOut() копируется выборка за выборкой, как в левый, так и в правый каналы. Для передачи RealOut() копируется в правый канал, а ImagOut () копируется в левый канал DAC. Если желателен бинауральный прием, сигнал I и Q может быть необязательно послан в правый и левый каналы соответственно, так же как в режиме передачи.
[Управление демонстрационным кодом]
Демонстрационный код SDR (см. примечание [3]) имеет несколько выбранных кнопок для установки времени задержки AGC (АРУ), выбора фильтра и выбора боковой полосы. Код для этих функций показан на рис. 18. Код понятен и легко модифицируется для дополнительных фильтров, разного времени задержки АРУ и других режимов работы. Не стесняйтесь экспериментировать.
PrivateSubcmdAGC_Click(IndexAsInteger) MaxGain=1000'Maximum digital gain = 60dB SelectCaseIndex Case0 AGC=True AGCHang=3'3 x 0.04644 sec = 139 ms Case1 AGC=True AGCHang=7'7 x 0.04644 sec = 325 ms Case2 AGC=False'AGC Off EndSelect EndSub
PrivateSubcmdMode_Click(IndexAsInteger) SelectCaseIndex Case0'Изменение режима на USB SSB=True USB=True Case1'Изменение режима на LSB SSB=True USB=False EndSelect EndSub
Рис. 18. Код управления для демонстрационной передней панели.
[Полнофункциональная программа SDR-1000]
В SDR-1000, моей реализации PC SDR, содержится значительное количество кода, не проиллюстрированного здесь. Я решил сфокусировать эту статью на основном коде DSP, необходимом для модуляции и демодуляции в частотной области. Как позволит время, я надеюсь написать будущие статьи, которые углубятся в другие интересные аспекты дизайна программного обеспечения.
Рис. 19 показывает завершенную переднюю панель SDR-1000. Большинство функций пользовательского интерфейса интуитивно понятны. Вот несколько интересных возможностей SDR-1000:
• Отображение спектра сигнала в реальном времени, с настройкой частоты одним щелчком мыши. • Сдвоенные независимые VFO с считыванием базы данных о выделении полосы частот. Пользователь может легко получить доступ к базе данных планирования полосы частот и изменить ее. • Настройка колесом мыши с возможностью изменения скорости настройки щелчком на колесе. • Многофункциональный цифровой и аналоговый измеритель мгновенного и среднего уровня сигнала, усиления АРУ, входного сигнала АЦП и уровней выходного сигнала ЦАП. • Обширное управление VFO, диапазоном и режимом. Кнопки переключения диапазонов также обеспечивают многоуровневую память в том же диапазоне. Это означает, что при многократном нажатии на кнопку данной полосы она будет циклически проходить через последние три частоты, посещенные в этом диапазоне. • Практически неограниченные возможности памяти предоставляются через интерфейс базы данных Microsoft Access. Память включает в себя все ключевые настройки радиостанции по частоте. Частоты также могут быть сгруппированы для сканирования. • На передней панели предусмотрено десять стандартных настроек фильтра, а также независимые непрерывно изменяемые фильтры как для CW, так и для SSB. • Локальные и UTC часы реального времени.
Учитывая возможности Visual Basic, возможность расширения пользовательского интерфейса практически безгранична.
Рис. 19. Передняя панель программы SDR-1000.
В коде DSP можно сделать гораздо больше для настройки PC SDR данного приложения. Например, Leif Åsbrink SM5BSZ делает под Linux интересную обработку слабого сигнала, отражаемого от поверхности Луны [16].
Кроме того, Bob Larkin W7PUA использует DSP-10, который он впервые описал в сентябрьских, октябрьских и ноябрьских выпусках QST за 1999 год, для экспериментов со слабым сигналом, принимаемым за горизонтом микроволнового распространения [17].
[Что будет в последней статье цикла]
В заключительной статье я планирую описать текущую разработку аппаратного обеспечения SDR-1000. Будет включен анализ компромисса между распределением усиления, показателем шума и динамическим диапазоном. Я также рассмотрю различные подходы к аналоговому AGC и рассмотрю управление частотой с использованием AD9854 квадратурной DDS.
Многие читатели интересовались заменой библиотеки Intel для обработки сигналов (Signal Processing Library, SPL) на Intel Performance Primatives (IPP). SPL была бесплатным дистрибутивом, но на веб-сайте Intel говорится, что IPP требует оплаты в размере 199 долларов после 30-дневного пробного периода. Полнофункциональную пробную версию IPP можно загрузить с сайта Intel по адресу www.intel.com/software/products/global/eval.htm. Автор подтвердил в Intel Product Management, что для любительских экспериментов с использованием IPP лицензионный сбор не требуется, и период оценки для такого использования не ограничен. На самом деле Intel поощряет этот тип экспериментального использования. Оплата лицензионного сбора требуется тогда и только тогда, когда существует коммерческое распространение кода DLL.
[Ссылки и примечания]
1. G. Youngblood, AC5OG, "A Software Defined Radio for the Masses, Part 1", QEX, Jul/Aug 2002, pp 13-21. 2. G. Youngblood, AC5OG, "A Software Defined Radio for the Masses, Part 2", QEX, Sep/Oct 2002, pp 10-18. 3. Демонстрационный код этого проекта можно загрузить с ARRLWeb по ссылке www.arrl.org/qexfiles/. Ищите файл архива 1102Youngblood.zip. 4. Функции библиотеки Intel Signal Processing Library предоставлены в пакете Intel Performance Primitives (IPP, Version 3.0, beta) для процессоров Pentium и архитектур Itanium. Оценочная копия IPP доступна бесплатно для загрузки по ссылке developer.intel.com/software/products/ipp/ipp30/index.htm. Коммерческое использование IPP требует полной лицензии. Не используйте IPP с демонстрационным кодом, поскольку он был протестирован только на предыдущей библиотеке обработки сигналов. 5. D. Hershberger, W9GR, and Dr S. Reyer, WA9VNJ, “Using The LMS Algorithm For QRM and QRN Reduction,” QEX, Sep 1992, pp 3-8. 6. D. Hall, KF4KL, “Spectral Subtraction for Eliminating Noise from Speech”, QEX, Apr 1996, pp 17-19. 7. J. Bloom, KE3Z, “Correlation of Sampled Signals”, QEX , Feb 1996, pp 24-28. 8. R. Lyons, Understanding Digital Signal Processing (Reading, Massachusetts: Addison-Wesley, 1997) pp 133, 330-340, 429-430. 9. D. Smith, KF6DX, Digital Signal Processing Technology (Newington, Connecticut: ARRL, 2001; ISBN: 0-87259-819-5; Order #8195) pp 4-1 through 4-15. 10. D. Smith, KF6DX, “Signals, Samples and Stuff: A DSP Tutorial (Part 1)”, QEX (Mar/Apr 1998), pp 5-6. 11. Информацию по свертке БПФ (FFT convolution) можно найти по следующим ссылкам: R. Lyons, Understanding Digital Signal Processing, (Addison-Wesley, 1997) pp 435-436; M. Frerking, Digital Signal Processing in Communication Systems (Boston, Massachusetts: Kluwer Academic Publishers) pp 202-209; and S. Smith, The Scientist and Engineer’s Guide to Digital Signal Processing (San Diego, California: California Technical Publishing) pp 311-318. 12. S. Smith, The Scientist and Engineer’s Guide to Digital Signal Processing (California Technical Publishing) pp 107-122. Это доступно для свободной загрузки на сайте www.DSPGuide.com. 13. Overlap/add method: Ibid, Chapter 18, pp 311-318; M. Freirking, pp 202-209. 14. S. Smith, Chapter 9, pp 174-177. 15. M. Frerking, Digital Signal Processing in Communication Systems, (Kluwer Academic Publishers) pp 237, 292-297, 328, 339-342, 348. 16. See Leif Åsbrink’s, SM5BSZ, веб-сайт ham.te.hik.se/homepage/sm5bsz/. 17. See Bob Larkin’s, W7PUA, домашняя страничка www.proaxis.com/~boblark/dsp10.htm. 18. A Software-Defined Radio for the Masses, часть 1. 19. A Software-Defined Radio for the Masses, часть 2. 20. A Software-Defined Radio for the Masses, часть 4.