Администрирование Разное A Software-Defined Radio for the Masses, часть 2 Sun, September 27 2026  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.

A Software-Defined Radio for the Masses, часть 2 Печать
Добавил(а) microsin   

Здесь приведен полный перевод на русский язык части 2 оригинальной статьи "A Software-Defined Radio for the Masses, Part 2" от Gerald Youngblood, AC5OG. Описывается полная программно-определяемая (SDR) радиостанция на базе ПК, использующая звуковую карту и инновационную схему детектора. В объяснении математика сведена к минимуму. Перевод первой части см. в [1]. Незнакомые термины и сокращения см. в Словарике части 1 [1].

В части 1 этой серии из четырех статей дано основное описание цифровой обработки сигналов (digital signal processing, DSP) в программно-определяемых радиоустройствах (software-defined radio, SDR). Часть 1 также предоставила обзор полнофункционального радио, которое использует персональный компьютер для выполнения всех функций DSP. Данная статья начинает реализацию дизайна с полного описания программного обеспечения, обеспечивающего полнодуплексный интерфейс к стандартной звуковой карте PC.

Чтобы осуществить магию цифровой обработки сигнала, мы должны быть в состоянии преобразовать сигнал из аналогового в цифровой и обратно в аналоговый. Большинство экспериментаторов-любителей уже имеют эту возможность, и многие использовали ее для медленного сканирования телевидения или новых цифровых режимов, таких как PSK31.

PSK31 (Phase Shift Keying, 31 Baud) — это популярный цифровой режим связи для радиолюбителей, предназначенный для живого диалога с клавиатуры в реальном времени. Его ключевая особенность — высокая эффективность, позволяющая устанавливать связь по всему миру с малой мощностью (5–25 Вт) и простыми антеннами.

Как это работает и в чем его сила? Вместо изменения частоты, как в старом режиме RTTY, PSK31 кодирует информацию, меняя фазу несущего звукового тона. В программном обеспечении звуковой сигнал преобразуется в текст и обратно. На слух он похож на непрерывный свист с едва уловимой "вибрацией".

Режим имеет три ключевых преимущества:

1. Предельно узкая полоса — всего около 31 .. 100 Гц. Это позволяет работать на одной полосе частот, где едва умещается один голосовой канал (SSB). Вся энергия сигнала сконцентрирована в очень узком спектре, что резко повышает отношение сигнал/шум на приеме.
2. Эффективное кодирование — используется Varicode, где часто встречающиеся буквы (например, пробел или "E") кодируются очень короткими последовательностями, а редкие — длинными. Это ускоряет передачу обычного текста, приближая скорость к набору на клавиатуре (~50 слов в минуту).
3. Устойчивость к помехам — благодаря узкой полосе и методу модуляции PSK31 часто позволяет уверенно принимать сигналы там, где голосовая связь или другие цифровые режимы уже не работают.

Недостатки и ограничения:

• Не для файлов — он не имеет встроенной системы коррекции ошибок и слишком медленный для передачи больших файлов, только для "живого" текстового общения.
• Чувствительность к фазе — может плохо работать на трассах, проходящих через полярные области, где ионосфера вносит фазовые искажения ("полярное мерцание").

Что нужно для работы? Оборудование самое простое: любой SSB-трансивер, компьютер со звуковой картой и соединительные кабели. Программы для работы с PSK31 (например, Digipan, FLDIGI) в большинстве своем бесплатны.

На каких частотах искать? Стандартные частоты для PSK31 находятся в КВ-диапазонах. Например, на 20-метровом диапазоне — это 14.070 МГц, на 40-метровом — 7.040 МГц и выше.

Часть 1 также обсуждает силу квадратурной обработки сигнала с использованием синфазной (in-phase, I) и квадратурной (quadrature, Q) составляющих сигнала для приема ли передачи, когда используются практические методы реализации любой модуляции. К счастью, любая современная звуковая карта PC предоставляет отличный метод оцифровки сигналов I и Q. Поскольку почти все карты сегодня обеспечивают 16-битное стерео при частоте дискретизации 44.1 кГц, у нас есть именно то, что нам нужно для захвата и обработки сигналов в программном обеспечении. Рис. 1 иллюстрирует прямое подключение микшера квадратурного преобразования к звуковой карте ПК.

SDR for Masses Part2 fig01

Рис. 1. Приемник прямого преобразования с квадратурным смесителем, выходы I/Q которого напрямую подключены к звуковой карте. Используется в прототипе устройства SDR автора.

В этой статье обсуждается полный исходный код для интерфейса со звуковой картой через драйверы DirectX на языке Microsoft Visual Basic. Предполагается, что читатель обладает некоторыми фундаментальными знаниями языкового программирования высокого уровня.

[Звуковая карта и возможности PC]

Самые ранние звуковые карты PC показывали низкую производительность на версиях со звуком 8-bit mono. Сегодня почти все PC оборудованы картами с поддержкой 16-bit stereo достаточного качества для использования в SDR. Такие карты позволят нам демодулировать, фильтровать и отображать сигналы с полосой до приблизительно 44 кГц, если оцифровка происходит на частоте дискретизации 44.1 кГц (полоса 44 вместо 22 кГц из-за того, что используются 2 канала, что фактически удваивает частоту дискретизации). Для высокопроизводительных приложений важно выбрать звуковую карту с широким динамическим диапазоном - порядка 90 dB. Большинство стереофонических звуковых карт подойдут для начальных экспериментов, хотя могут показывать несколько худшие параметры.

Самая лучшая 16-битная карта по соотношению производительность/цена, которую я нашел, это описываемая в этой статье карта Santa Cruz 6-channel DSP Audio Accelerator от компании Turtle Beach Inc (www.tbeach.com). Она предоставляет четыре 18-bit внутренних analog-to-digital (A/D) входных каналов и шесть 20-bit digital-to-analog (D/A) выходных каналов с частотами дискретизации до 48 кГц. Производитель указывает показатель отношения сигнал/шум (signal-to-noise ratio, SNR) 96 dB и общий коэффициент гармонических искажений плюс шум (total harmonic distortion plus noise, THD+N) лучше -91 dB. Перекрестные помехи между каналами (crosstalk) заявлен на уровне –105 dB при частоте сигнала 100 Гц.

Каждый бит A/D или D/A преобразователя представляет 6 dB динамического диапазона, так что 16-битный преобразователь имеет теоретический лимит 96 dB. Для достижения этого уровня параметров требуется очень хороший преобразователь с малошумящим дизайном. Многие 16-битные звуковые карты показывают эффективный динамический диапазон не выше 12 - 14 бит. Чтобы помочь достичь более высоких параметров, карта Santa Cruz использует 18-bit A/D преобразователь, чтобы выдать в спецификации динамический диапазон 96 dB (16-bit).

SoundBlaster 64 предоставляет разумную производительность порядка 76 dB SNR, как заявлено компанией PC AV Tech на сайте www.pcavtech.com. Я использовал эту карту с хорошими результатами, однако предпочитаю карту Santa Cruz.

Вычислительная мощность, требуемая от PC, в значительной степени зависит от обработки сигнала, необходимой приложению. Поскольку я использую очень высокопроизводительные фильтры и большие преобразования с быстрым Фурье (FFT), моим приложениям требуется как минимум 400-MHz процессор Pentium II с минимальным объемом оперативной памяти 128 МБ. Если вам требуется меньшая производительность от программного обеспечения, вы можете обойтись гораздо более медленной машиной. Поскольку начальный уровень для новых ПК теперь составляет 1 ГГц, многие любители имеют достаточную вычислительную мощность.

[Microsoft DirectX в сравнении с Windows Multimedia]

Цифровая обработка сигнала с использованием звуковой карты ПК требует, чтобы мы могли захватывать блоки оцифрованных данных I и Q через стереовходы, обрабатывать эти сигналы и возвращать их на выходы звуковой карты в псевдореальном времени. Это называется full duplex, полный дуплекс. К сожалению, нет высокоуровневого программного интерфейса, который предлагает необходимые нам возможности для приложения SDR.

Microsoft теперь предоставляет два интерфейса прикладного программирования [2] (API), которые позволяют прямой доступ к звуковой карте на C++ и Visual Basic. Оригинальным интерфейсом является Windows Multimedia, использующая Waveform Audio API. Хотя моя ранняя работа была выполнена с Waveform Audio API, позже я отказался от него из-за более высокой производительности и более простого интерфейса, который предлагает DirectX. Единственное ограничение, которое я нашел в DirectX, заключается в том, что в настоящее время он не поддерживает звуковые карты с разрешением более 16 бит. Для 24-битных карт требуется Windows Multimedia. В то время как карта Santa Cruz поддерживает 18-битный внутренний интерфейс, она представляет интерфейсу только 16-битный. Сведения о том, где загрузить комплект разработчика ПО DirectX (SDK), см. в примечании [2].

[Концепция кольцевого буфера]

Типовая полнодуплексная звуковая карта ПК позволяет осуществлять одновременный захват и воспроизведение двух или более аудиоканалов (стерео). К сожалению, в Visual Basic или C++ нет высокоуровневого кода для прямой поддержки полного дуплекса, как это требуется в SDR. Поэтому нам придется написать код для прямого управления картой через DirectX API.

DirectX внутренне управляет всеми низкоуровневыми буферами и их соответствующими интерфейсами с аппаратными средствами звуковой карты. Наш код должен будет управлять буферами высокого уровня DirectX (называемыми DirectSoundBuffer и DirectSoundCaptureBuffer), чтобы обеспечить бесперебойную работу в многозадачной системе. DirectSoundCaptureBuffer хранит оцифрованные сигналы от стереопреобразователя в кольцевом буфере и уведомляет приложение о наступлении заранее определенных событий. После захвата в буфере мы можем считывать данные, выполнять необходимые функции модуляции или демодуляции с использованием DSP и отправлять данные в DirectSoundBuffer для цифроаналогового преобразования и вывода на колонки или передатчик.

Чтобы обеспечить плавную работу в многопоточной системе с несколькими треками без прерывания звука, необходимо будет обеспечить многоуровневый буфер как для захвата, так и для воспроизведения. Возможно, вы слышали термин «двойная буферизация». Мы будем использовать двойную буферизацию в DirectSoundCaptureBuffer и четырехкратную буферизацию в DirectSoundBuffer. Я обнаружил, что на выходе требовался quad-буфер с обнаружением перезаписи для предотвращения проблем с перезаписью, когда система сильно загружена другими приложениями. На рис. 2A и рис. 2B показана концепция кольцевого двойного буфера, который используется для DirectSoundCaptureBuffer. Хотя буфер действительно является линейным массивом в памяти, как показано на рис. 2B, мы можем визуализировать его как кольцевой, как показано на рис. 2A. Это потому что DirectX управляет буфером таким образом, что как только каждый курсор достигает конца массива, драйвер сбрасывает курсор в начало буфера.

SDR for Masses Part2 fig02

Рис. 2. Кольцевые буферы DirectSoundCaptureBuffer и DirectSoundBuffer.

DirectSoundCaptureBuffer разбивается на два блока, каждый из которых равен по размеру объему данных, подлежащих захвату и обработке между каждым событием. Обратите внимание, что событие очень похоже на прерывание. В нашем случае мы будем использовать размер блока 2048 выборок звука. Поскольку мы используем стерео (двухканальную) плату с 16 битами на канал, мы будем захватывать 8192 байта на блок (2048 выборок × 2 канала × 2 байта). Поэтому DirectSoundCaptureBuffer будет вдвое больше (16384 байт).

Поскольку DirectSoundCaptureBuffer поделен на 2 блока данных, нам понадобится отправлять оповещение о событии в приложение после захвата каждого блока. Драйвер DirectX в любой момент времени поддерживает курсоры для отслеживания позиции операций захвата. Драйвер предоставляет способ установки определенных мест в буфере, которые вызывают генерацию события, сообщая тем самым приложению, что нужно забрать данные. Затем мы можем прочитать правильный блок напрямую из сегмента DirectSoundCaptureBuffer, который был завершен.

На рис. 2А эти два курсора напоминают указатели на циферблате, вращающиеся в направлении по часовой стрелке. Курсор захвата lPlay представляет точку, в которой данные в настоящее время захватываются (я знаю, что это звучит задом наперед, но именно так его определила Microsoft). Курсор чтения, lWrite, отслеживает курсор захвата и указывает точку, до которой данные могут быть безопасно прочитаны. Данные после lWrite и до и включая lPlay не обязательно являются хорошими данными из-за аппаратной буферизации. Мы можем использовать курсор lWrite для запуска события, которое говорит программному обеспечению читать каждый соответствующий блок данных, как это будет обсуждаться позже в статье. Поэтому мы получим два события на полный оборот кольцевого буфера. Основной принцип - данные звука могут быть захвачены в одну половину буфера, в то время как данные безопасно считываются из другой половины.

Рис. 2C показывает DirectSoundBuffer, который используется для вывода данных в D/A преобразователи. В этом случае мы будем использовать четырехкратный буфер, чтобы обеспечить достаточно места между текущим воспроизводимым сегментом и сегментом, который записывается. Курсор воспроизведения lPlay всегда указывает на следующий проигрываемый байт. Курсор записи lWrite указывает место, после которого мы можем безопасно записать данные в буфер. Курсоры D/A воспроизведения можно рассматривать как вращающиеся по часовой стрелке так же, как это делают курсоры захвата A/D. Мы должны отслеживать расположение курсоров перед записью в буферные ячейки между курсорами, чтобы предотвратить перезапись данных, которые уже отправлены в оборудование для воспроизведения.

Теперь рассмотрим, как данные отображаются из DirectSoundCaptureBuffer в DirectSoundBuffer. Чтобы предотвратить разрывы или хлопки в звуке из-за загрузки процессора, мы захотим заполнить весь четверной буфер перед началом цикла воспроизведения. DirectX позволяет приложению установить начальную точку для курсора lPlay и начать воспроизведение в любое время. На рис. 3 показано, как блоки данных последовательно преобразуются из буфера DirectSoundCaptureBuffer в буфер DirectSoundBuffer. Блок 0 из буфера DirectSoundCaptureBuffer передается в блок 0 буфера DirectSoundBuffer. Затем блок 1 буфера DirectSoundCaptureBuffer передается блоку 1 буфера DirectSoundBuffer и т. д. Последующие примеры исходного кода показывают, как осуществляется управление буферами.

SDR for Masses Part2 fig03

Рис. 3. Метод отображения DirectSoundCaptureBuffer на DirectSoundBuffer.

[Full Duplex, реализация по шагам]

В последующих секциях предоставляется подробное описание full-duplex реализации на DirectX. Код примера захватывает и проигрывает стереофонический звуковой сигнал, который задерживается на 4 периода захвата из-за буферизации. По мере чтения вам следует обращаться к секции документации "DirectX Audio" из руководства DirectX 8.0 Programmers Reference, которое устанавливается вместе с DirectX software developer’s kit (SDK). Код DSP будет обсуждаться в следующей статье из этой серии [], где будет рассматриваться модуляция и демодуляция квадратурных сигналов в SDR. Вот шаги, которые выполняются в создании интерфейса DirectX:

• Установка библиотек DirectX runtime и SDK.
• Добавление ссылки на DirectX8 для Visual Basic Type Library.
• Определение переменных, буферов ввода/вывода (I/O) и объектов DirectX.
• Реализация событий DirectX8 и обработчиков событий.
• Создание аудио-устройств.
• Создание событий DirectX.
• Старт и стоп буферов захвата и проигрывания.
• Обработка DirectXEvent8.
• Заполнение буферов проигрывания перед запуском проигрывания.
• Детектирование и коррекция ошибок перезаписи.
• Парсинг стереобуфера в сигналы I и Q.
• Удаление объектов и событий при выходе из программы.

Полный функциональный код для драйвера DirectX, написанный на Microsoft Visual Basic предоставлен для загрузки на сайте QEX Web [3].

Установите DirectX и зарегистрируйте его в рамках Visual Basic. Первый шаг состоит в загрузке драйвера DirectX и DirectX SDK с сайта Microsoft (см. примечание [3]). Как только драйвер и SDK установлены, вам нужно зарегистрировать DirectX8 для Visual Basic Type Library в среде разработки Visual Basic.

Если вы строите проект с нуля, то сначала создайте проект Visual Basic и назовите его "Sound". После загрузки проекта перейдите в Project Menu/References, где загрузится диалог, показанный на рис. 4. Прокрутите список "Available References", пока не найдете "DirectX8 for Visual Basic Type Library", затем поставьте туда галочку. Когда вы кликните на кнопку "OK", библиотека DirectX8 будет зарегистрирована для вашего проекта.

SDR for Masses Part2 fig04

Рис. 4. Регистрация DirectX8 для Visual Basic Type Library в Visual Basic IDE.

Определите переменные, буферы и объекты DirectX. У формы окна проекта Sound будет имя frmSound. В секции General формы frmSound вам нужно декларировать все переменные, буферы и объекты DirectX, которые будут использоваться в интерфейсе драйвера. Рис. 5 предоставляет код, который вы можете скопировать в секцию General. Все определения комментируются в коде и должны быть понятными при рассмотрении кода подпрограмм.

Option Explicit

'Определение констант
Const Fs As Long = 44100 'Частота дискретизации, Гц
Const NFFT As Long = 4096 'Количество бинов FFT
Const BLKSIZE As Long = 2048 'Размер блока для захвата/проигрывания (capture/play)
Const CAPTURESIZE As Long = 4096 'Размер буфера захвата (Capture Buffer)

'Определение объектов DirectX
Dim dx As New DirectX8 'DirectX object
Dim ds As DirectSound8 'DirectSound object
Dim dspb As DirectSoundPrimaryBuffer8 'Primary buffer object
Dim dsc As DirectSoundCapture8 'Capture object
Dim dsb As DirectSoundSecondaryBuffer8 'Output Buffer object
Dim dscb As DirectSoundCaptureBuffer8 'Capture Buffer object

'Определения типов
Dim dscbd As DSCBUFFERDESC 'Описание Capture Buffer
Dim dsbd As DSBUFFERDESC 'Описание DirectSound Buffer
Dim dspbd As WAVEFORMATEX 'Описание Primary Buffer
Dim CapCurs As DSCURSORS 'DirectSound Capture Cursor
Dim PlyCurs As DSCURSORS 'DirectSound Play Cursor'Создание буферов ввода/вывода звука (I/O Sound Buffer)
Dim inBuffer(CAPTURESIZE) As Integer 'Demodulator Input Buffer
Dim outBuffer(CAPTURESIZE) As Integer 'Demodulator Output Buffer

'Определение указателей и счетчиков
Dim Pass As Long 'Количество проходов захвата
Dim InPtr As Long 'Указатель блока буфера захвата
Dim OutPtr As Long 'Указатель блока буфера вывода
Dim StartAddr As Long 'Стартовый адрес блока буфера
Dim EndAddr As Long 'Конечный адрес блока буфер
Dim CaptureBytes As Long 'Сколько захваченных байт готовы для чтения

'Определение переменных счетчика циклов для синхронизации цикла событий захвата
Dim TimeStart As Double 'Время старта для цикла DirectX8Event
Dim TimeEnd As Double 'Время окончания для цикла DirectX8Event
Dim AvgCtr As Long 'Подсчет количества событий для усреднения
Dim AvgTime As Double 'Хранит среднее время цикла события

'Настройка переменных Event для Capture Buffer Implements DirectXEvent8 'Разрешает DirectX Events
Dim hEvent(1) As Long 'Дескриптор (обработчик) для DirectX Event
Dim EVNT(1) As DSBPOSITIONNOTIFY 'Оповещает о позиции в массиве
Dim Receiving As Boolean 'Если true, то режим приема
Dim FirstPass As Boolean 'Обозначает первый проход от Start

Рис. 5. Декларация переменных, буферов, событий и объектов. Этот код находится в секции General модуля формы frmSound.

Создание звуковых устройств (Audio Device). Теперь мы готовы создать объекты DirectSound и настроить формат буферов захвата (capture buffer) и проигрывания (play buffer). По мере чтения дальнейшего описания обращайтесь к коду рис. 6.

'Настройка объектов DirectSound и буферов Capture и Play
Sub CreateDevices()
On Local Error Resume Next

Set ds = dx.DirectSoundCreate(vbNullString) 'DirectSound object
Set dsc = dx.DirectSoundCaptureCreate(vbNullString) 'DirectSound Capture

'Проверка, правильно ли инсталлирована звуковая карта:
If Err.Number < > 0 Then
MsgBox "Не получается запустить DirectSound. Проверьте установку звуковой карты”
End
End If

'Установите уровень кооперативности, чтобы позволить установить формат Primary Buffer:
ds.SetCooperativeLevel Me.hWnd, DSSCL_PRIORITY

'Установите формат буфера захвата (capture buffer):
With dscbd
With .fxFormat
.nFormatTag = WAVE_FORMAT_PCM
.nChannels = 2 'Stereo
.lSamplesPerSec = Fs 'Частота дискретизации, Гц
.nBitsPerSample = 16 '16 бит на выборку
.nBlockAlign = .nBitsPerSample / 8 * .nChannels
.lAvgBytesPerSec = .lSamplesPerSec * .nBlockAlign
End With
.lFlags = DSCBCAPS_DEFAULT
.lBufferBytes = (dscbd.fxFormat.nBlockAlign * CAPTURESIZE) 'Размер буфера
CaptureBytes = .lBufferBytes \ 2 'Сколько байт для 1/2 буфера захвата
End With

Set dscb = dsc.CreateCaptureBuffer(dscbd) 'Создание буфера захвата

'Настройка формата вторичного буфера проигрывания (playback buffer):
With dsbd
.fxFormat = dscbd.fxFormat
.lBufferBytes = dscbd.lBufferBytes * 2 'Проигрывание 2X размера буфера захвата
.lFlags = DSBCAPS_GLOBALFOCUS Or DSBCAPS_GETCURRENTPOSITION2
End With

dspbd = dsbd.fxFormat 'Установка формата Primary Buffer
dspb.SetFormat dspbd ' такая же, как и у Secondary Buffer

Set dsb = ds.CreateSoundBuffer(dsbd) 'Создание вторичного буфера
End Sub

Рис. 6. Создание устройств захвата и проигрывания DirectX.

Все действия по первоначальной настройке устройств выполняет подпрограмма CreateDevices. Первый шаг состоит в создании объектов DirectSound и DirectSoundCapture. Затем мы проверяем, установлена ли в системе совместимая звуковая карта. Если нет, то для пользователя отобразится сообщение об ошибке. Далее мы устанавливаем уровень кооперации DSSCL_PRIORITY, чтобы позволить настроить формат Primary Buffer таким же, как и у Secondary Buffer. Последующий код настраивает формат DirectSoundCaptureBufferDescription и создает объект DirectSoundCaptureBuffer. Формат устанавливается на 16-bit stereo на частоте дискретизации из константы Fs.

Далее DirectSoundBufferDescription устанавливается на тот же формат, что и DirectSoundCaptureBufferDescription. Затем мы устанавливает формат Primary Buffer как у Secondary Buffer перед созданием объекта DirectSoundBuffer.

Настройка событий (DirectX Events). Для этого предназначена подпрограмма SetEvents. Как обсуждалось ранее, DirectSoundCaptureBuffer делится на 2 блока, так что мы можем читать из одного блока, пока звук захватывается и записывается в другой блок. Чтобы это реализовать, мы должны знать, когда DirectX завершит запись в блок. Это осуществляется с помощью DirectXEvent8. Рис. 7 предоставляет код, необходимый для настройки двух событий, которые произойдут, когда курсор lWrite достигнет 50% и 100% DirectSoundCaptureBuffer.

'Настройка событий для оповещения заполнения буфера захвата на позициях 0 и 1/2
Sub SetEvents()
hEvent(0) = dx.CreateEvent(Me) 'Установка обработчика для первой половины буфера
hEvent(1) = dx.CreateEvent(Me) 'Установка обработчика для второй половины буфера

'Buffer Event 0 устанавливает Write 50% буфера
EVNT(0).hEventNotify = hEvent(0)
EVNT(0).lOffset = (dscbd.lBufferBytes \ 2) - 1 'Событие для первой половины буфера захвата

'Buffer Event 1 устанавливает Write 100% буфера
EVNT(1).hEventNotify = hEvent(1)
EVNT(1).lOffset = dscbd.lBufferBytes - 1 'Событие для второй половины буфера захвата

dscb.SetNotificationPositions 2, EVNT() 'Установка количества позиций оповещения на 2
End Sub

Рис. 7. Создание событий DirectX.

Мы начнем с создания двух обработчиков событий hEvent(0) и hEvent(1). Следующий код созает обработчик для каждого соответствующего события, и настроит их для срабатывания при заполнения каждой половины DirectSoundCaptureBuffer. И в завершении мы установим количество позиций оповещения на два, и передадим имя массива EVNT() обработчиков событий в DirectX.

Подпрограммы CreateDevices и SetEvents должны вызваться из подпрограммы загрузки формы Form_Load(). Подпрограмма выгрузки формы Form_Unload должна остановить захват и проигрывание, а также должна уничтожить все объекты DirectX перед закрытием приложения. Код загрузки и выгрузки показан на рис. 8.

'Создание устройств DirectSound и установка событий DirectX8Events
Private Sub Form_Load()
CreateDevices 'Создание устройств DirectSound
SetEvents 'Настройка событий DirectX
End Sub

'Выключение всего и закрытие приложения
Private Sub Form_Unload(Cancel As Integer)
If Receiving = True Then
dsb.Stop 'Stop Playback
dscb.Stop 'Stop Capture
End If

Dim i As Integer
For i = 0 To UBound(hEvent) 'Kill DirectX Events
DoEvents
If hEvent(i) Then dx.DestroyEvent hEvent(i)
Next

Set dx = Nothing 'Уничтожение объектов DirectX
Set ds = Nothing
Set dsc = Nothing
Set dsb = Nothing
Set dscb = Nothing

Unload Me
End Sub

Рис. 8. Создание и уничтожение устройств и событий DirectSound Devices.

Старт и остановка Capture/Playback. Рис. 9 иллюстрирует, как запустить и остановить работу DirectSoundCaptureBuffer. Команда dscb.Start DSCBSTART_LOOPING запускает DirectSoundCaptureBuffer в непрерывном кольцевом цикле. Когда заполнится первая половина буфера, запустится подпрограмма DirectX Event8, и данные могут быть прочитаны, обработаны и отправлены в DirectSoundBuffer. Обратите внимание, что DirectSoundBuffer пока не запущен, так как мы будем в четыре раза буферизировать вывод, чтобы предотвратить загрузку процессора от возникновения пробелов в выводе. Флаг FirstPass указывает событию начать заполнение DirectSoundBuffer в первый раз перед началом зацикливания буфера.

'Включение Capture/Playback
Private Sub cmdOn_Click()
dscb.Start DSCBSTART_LOOPING 'Start Capture Looping
Receiving = True 'Установка флага в режим приема
FirstPass = True 'Это первый проход после Start
OutPtr = 0 'Начинаем запись с первого буфера
End Sub

'Выключение Capture/Playback
Private Sub cmdOff_Click()
Receiving = False 'Сброс флага приема
FirstPass = False 'Сброс флага первого прохода
dscb.Stop 'Stop Capture Loop
dsb.Stop 'Stop Playback Loop
End Sub

Рис. 9. Старт и остановка захвата/воспроизведения (capture/playback) буферов.

Обработка DirectXEvent8. Как только мы запустили цикличное заполнение DirectSoundCaptureBuffer, завершение заполнения каждого блока приведет к запуску кода обработчика события DirectX Event8, показанного на рис. 10. Как уже упоминалось, события будут возникать, когда 50% и 100% буфера будут заполнены данными звука. Поскольку буфер кольцевой, он будет снова начинать заполнение с позиции 0, когда вторая половина заполнится, чтобы циклы заполнения продолжались бесконечно. При установленной частоте оцифровки 44100 Гц и 2048 выборок на блок захвата частота заполнения блоков составит 44100/2048 = 21.53 блоков в секунду, или один блок каждые 46.4 мс. Поскольку счетверенный буфер заполняется перед запуском воспроизведения, общая задержка между входом и выходом звука составит 4 × 46.4 мс = 185.6 мс.

'Обработка событий Capture, вызов подпрограмм DSP и вывод в Secondary Play Buffer
Private Sub DirectXEvent8_DXCallback (ByVal eventid As Long)
StartTimer 'Сохранение времени начала цикла

Select Case eventid 'Определение, какой из Capture Block готов к чтению
Case hEvent(0)
InPtr = 0 'Первая половина Capture Buffer
Case hEvent(1)
InPtr = 1 'Вторая половина Capture Buffer
End Select

'Начальный адрес буфера захвата:
StartAddr = InPtr * CaptureBytes

'Чтение из кольцевого DirectX-буфера Capture Buffer в inBuffer:
dscb.ReadBuffer StartAddr, CaptureBytes, inBuffer(0), DSCBLOCK_DEFAULT

'DSP Modulation/Demodulation - ЗАМЕЧАНИЕ: ЗДЕСЬ ВЫЗЫВАЕТСЯ КОД DSP
'DSP inBuffer, outBuffer
StartAddr = OutPtr * CaptureBytes 'Начальный адрес буфера проигрывания
EndAddr = OutPtr + CaptureBytes - 1 'Конечный адрес буфера проигрывания

With dsb 'Ссылка на DirectSoundBuffer
.GetCurrentPosition PlyCurs 'Получение текущей позиции проигрывания

'Если true, то произошло перекрытие по записи курсора lWrite из-за
' чрезмерной нагрузки на процессор:
If PlyCurs.lWrite >= StartAddr _
And PlyCurs.lWrite <= EndAddr Then
FirstPass = True 'Перезапуск буфера проигрывания
OutPtr = 0
StartAddr = 0
End If 'Если true, то произошло перекрытие по записи курсора lPlay из-за
' чрезмерной нагрузки на процессор:
If PlyCurs.lPlay >= StartAddr _
And PlyCurs.lPlay <= EndAddr Then
FirstPass = True 'Перезапуск буфера проигрывания
OutPtr = 0
StartAddr = 0
End If

'Запись outBuffer в DirectX кольцевой Secondary Buffer. ЗАМЕЧАНИЕ: запись
' inBuffer вызывает сквозную передачу. Замените ниже outBuffer на корректные
' данные, когда используете подпрограмму DSP для модуляции/демодуляции.
.WriteBuffer StartAddr, CaptureBytes, inBuffer(0), DSBLOCK_DEFAULT

OutPtr = IIf(OutPtr >= 3, 0, OutPtr + 1) 'Считает от 0 до 3

If FirstPass = True Then 'На первом проходе ждет значения 4 счетчика перед запуском
Pass = Pass + 1 'На 0 прокрутка буфера Secondary Play
If Pass = 3 Then 'Это помещает буфер воспроизведения в три цикла захвата
FirstPass = False ' после текущего
Pass = 0 'Сброс счетчика проходов
.SetCurrentPosition 0 'Установка позиции воспроизведения в 0
.Play DSBPLAY_LOOPING 'Запуск циклов проигрывания
End If
End If
End With StopTimer 'Отображение в окне среднего времени цикла
End Sub

Рис. 10. Обработка события DirectXEvent8. Обратите внимание, что пример кода передает inBuffer() непосредственно в DirectSoundBuffer без обработки. Вызов подпрограммы DSP для этого был закомментирован, так что аудиовход к звуковой карте пока будет передан непосредственно к аудиовыходу с задержкой 185 мс. На выходе объекты и события уничтожаются.

DirectX-событие Event8_DXCallback передает eventid как переменную. Оператор case в начале кода определяет по eventid, какая половина DirectSoundCaptureBuffer была заполнена. На основе этой информации мы можем вычислить начальный адрес для чтения каждого блока из DirectSoundCaptureBuffer в массив inBuffer() командой dscb.ReadBuffer. Затем мы просто передаем inBuffer() во внешнюю подпрограмму DSP, которая возвращает обработанные данные в массиве outBuffer().

Затем вычисляем StartAddr и EndAddr для следующего места записи в DirectSoundBuffer. Перед записью в буфер мы сначала проверяем, чтобы убедиться, что мы не пишем между курсорами lWrite и lPlay, что приведет к перезаписи частей буфера, которые уже были зафиксированы на выходе. Это приведет к шуму и искажению аудиовыхода. Если возникает ошибка, флаг FirstPass устанавливается в true, а указатели сбрасываются в ноль, чтобы мы очистили DirectSoundBuffer и начали все сначала. Это эффективно выполняет автоматический сброс при перегрузке процессора, что как правило происходит из-за приложений с интенсивным использованием графики, работающих вместе с приложением SDR.

Если ошибок перезаписи нет, в DirectSoundBuffer мы записываем массив outBuffer(), который был возвращен из подпрограммы DSP в память от StartAddr до EndAddr. Важное замечание: в этом примере кода вызов подпрограммы DSP закомментированы, и массив inBuffer() передается непосредственно в DirectSoundBuffer просто для тестирования. Когда флаг FirstPass имеет значение True, мы захватываем и записываем четыре блока данных перед началом цикла воспроизведения с помощью команд .SetCurrentPosition 0 и .Play DSBPLAY_LOOPING.

Вызовы подпрограмм StartTimer и StopTimer позволяют отображать среднее время вычисления цикла событий в окне приложения. Это полезно для измерения эффективности кода подпрограммы DSP, который вызывается из события. При нормальной работе эти вызовы подпрограмм должны быть закомментированы.

Парсинг Stereo Buffer в сигналы I и Q. Еще один шаг требуется для использования захваченного сигнала в подпрограмме DSP, и он заключается в разделении или разборе данных левого и правого каналов на сигналы I и Q соответственно. Это может быть выполнено с использованием кода на рис. 11. В 16-битном стерео левый и правый каналы чередуются в inBuffer() и outBuffer(). Код просто копирует чередующиеся 16-битные целочисленные значения в буферы RealIn() (то же самое, что I) и ImagIn() (то же самое, что Q) соответственно. Теперь мы готовы выполнить магию цифровой обработки сигналов, о которой мы расскажем в следующей статье [].

Erase RealIn, ImagIn
For S = 0 To CAPTURESIZE - 1 Step 2 'Копирование I в RealIn и Q в ImagIn
RealIn(S \ 2) = inBuffer(S)
ImagIn(S \ 2) = inBuffer(S + 1)
Next S

Рис. 11. Код для парсинга stereo-буфера inBuffer() в синфазный (in-phase) и квадратурный (quadrature) сигналы. Этот код должен быть встроен в подпрограмму DSP.

[Тестирование драйвера]

Для тестирования драйвера подключите генератор звука — или любое другое звуковое устройство, такое как радиоприемник — к линейному входу звуковой карты. Убедитесь, что заглушен для воспроизведения на компьютере линейный вход (mute line-in) на панели управления микшера звука Windows, чтобы звук не воспроизводился операционной системой напрямую. Микшер можно открыть двойным кликом на иконку динамика в правом нижнем углу рабочего стола Windows. Также микшер доступен через Control Panel системы.

Теперь запустите приложение Sound и кликните на кнопку On. Вы должны услышать звук, проигрываемый через драйвер. Этот проигрываемый звук будет задержан на 185 мс относительно входного звука - из-за учетверенной буферизации на выводе. Вы можете теперь несколько раз включить и выключить воспроизведение звука на линейном входе в микшере, чтобы услышать эту задержку как эффект эха. Если вы такое наблюдаете, значит все работает корректно.

[Что дальше]

В следующей статье [] мы будем рассматривать подробно код DSP, который осуществляет модуляцию и демодуляцию сигналов SSB. Будет продемонстрирован исходный код для реализации сверхвысокой производительности переменной полосовой фильтрации в частотной области, обработки ПЧ основной полосы со смещением и цифровой АРУ.

[Ссылки и примечания]

1. A Software-Defined Radio for the Masses, часть 1.
2. Информацию по программированию как для DirectX, так и для Windows Multimedia можно получить на сайте Microsoft Developer Network (MSDN) www.msdn.microsoft.com/library. Для загрузки DirectX Software Development Kit перейдите по ссылке msdn.microsoft.com/downloads/ и кликните на "Graphics and Multimedia" в левой части окна навигации. Затем кликните на "DirectX" и затем выберите "DirectX 8.12 (или более свежую версию). Драйвер DirectX runtime можно загрузить по ссылке www.microsoft.com/windows/directx/downloads/default.asp.
3. Вы можете загрузить этот пакет с сайта ARRL Web www.arrl.org/qexfiles/. Ищите файл 0902Youngblood.zip.
4. A Software-Defined Radio for the Masses, часть 3.
5. A Software-Defined Radio for the Masses, часть 4.

 

Добавить комментарий


Защитный код
Обновить

Top of Page