Пока весь мир развлекается с нейросетями и высокими технологиями, я решил исполнить студенческую мечту — собрать радио (а то как сапожник без сапог). Во время курса по радиосигналам (по их приему и обработке) в моей голове представлялось это всё как конструктор, и я не видел в этом ничего сложного. По крайней мере, все модели, построенные в Mathcad и MATLAB, давали обнадеживающие результаты, но хотелось это всё проверить на практике и в качестве результата не увидеть первоначально промодулированную идеальную синусоиду, а услышать незнакомый аудиосигнал. Просто приобрести готовый SDR‐USB — это не интересно, а вот реализовать что‐то подобное (Простой SDR приёмник на ПЛИС), только для FM диапазона, мне уже по душе. В дополнение ко всему, в моих руках оказалась отладочная плата DE10‐Standart с ПЛИС на борту — излишне мощная и громадная для этой задачи игрушка.  Нет смысла описывать все её возможности — кто знает тот знает, кому интересно — тот найдёт. Лучше опишу то, что пригодится в проекте мне: ПЛИС собственной персоной Параллельный внешний интерфейс GPIO на 36 контактов Аудиовыход Также, в процессе пришлось слегка использовать HPS ядро, но не залезая в него (об этом позднее). Вероятно, для кого‐то информация будет не нова и где‐то весьма не корректна, но, всё равно, поделюсь некоторыми возможностями, которые я для себя обнаружил. Цель: Познакомиться с обработкой сигналов на практике и разработать «нечто», позволяющее услышать хотя бы намек на принятую радиостанцию. Задачи: Подготовить сигнал ФМ диапазона (98‐108 МГц) для оцифровки Оцифровать сигнал для ПЛИС Произвести цифровую обработку Насладиться результатом Так как для прямой обработки сигнала мне необходима частота дискретизации от 216 МГц и выше, то сперва была мысль перенести спектр входного сигнала на нулевую частоту, потом податься в аналоговую схемотехнику, поэкспериментировать с фильтрами, смесителями, усилителями и прочей аналоговой шелухой. Но за неимением хорошего осциллографа, мне не захотелось вслепую возиться с этим. Было бы проще использовать высокоскоростной АЦП с параллельным интерфейсом, но сколько бы я не искал дешевый, доступный, не требующий какой‐то сложной топологии платы. Я такой не нашёл. Максимально удовлетворяющий мне по цене, но не по частоте дискретизации, оказался 8‐ми битный AD9057 на 80 MSPS. Немного расстроившись, я стал размышлять над двумя путями: либо работать в третьей зоне Найквиста  либо придумать метод как взять отсчеты сигнала чаще чем получается. Не знаю какие злые силы меня навели на следующую цепочку мыслей. Если АЦП берет отсчеты 80 млн раз в секунду, то почему бы не взять три АЦП, которые будут работать параллельно, но каждая запустится чуть позже. Да и, тем более, ПЛИС позволяет довольно точно настраивать тактовые сигналы. Что может пойти не так? В крайнем случае, если не получится, я всегда смогу использовать только один АЦП и работать в третьей зоне Найквиста. Довольно поздно я обнаружил, что изобрел велосипед и имя ему «time‐interleaved adc» или «АЦП с чередованием во времени». Им иногда пользуются, хотя он и имеет свои недостатки.   Таким образом, блок схема платы аналого‐цифрового преобразования выглядит до безобразия просто.    Рецепт принципиальной схемы максимально прост. На порцию для трёх АЦП режем входной сигнал с помощью разветвителя из резисторов, разделяем аналоговое и цифровое питание по вкусу, посыпаем обильным количеством конденсаторов, раскладываем цифровые интерфейсы по разъемам PBD. Смещение сигнала обеспечивается внутренним резистором АЦП. Больше здесь сказать нечего :)  Пока платы изготавливались я задумался о генерации сигнала с частотной модуляцией, который немного поможет настроить разрабатываемый приемник.   Пустить под модуляцию обычную синусоиду — скучно. Поэтому, чтобы было веселее страдать, я принял решение синтезировать мелодию. Примерный алгоритм действий: Определил перечисляемым типом ноты от «ля» малой октавы до «си» второй октавы и для каждой ноты свой делитель опорной частоты; Записал два массива: первый — с последовательностью нот в мелодии, второй — с длительностью нот; Запустил счетчик от 0 до N−1 количества нот и проходил по всем массивам; В зависимости от ноты на выход подавался сигнал с определенной частотой. Сперва была реализация «в лоб» и звуковой сигнал генерировался путем деления частоты тактового генератора. Для частотного, в данном случае, манипулирования я создал с помощью PLL два тактовых сигнала на 99,96 МГц и 100,04 МГц. Таким образом, если в звуковом сигнале логическая единица, то на выходе частота 100,04 МГц, если логический ноль — то 99,96 МГц. Этот сигнал был выведен через обычный разъем GPIO к которому подключен провод ~70 см длиной. Радио в смартфоне, настроенное на частоту 100 МГц, прекрасно принимало сигнал на расстоянии до 10 метров. После этого я приступил ко второй реализации чуть более грамотнее. В качестве генерирования чистых нот (синусоид) использовал встроенное IP‐ядро NCO, благодаря чему, у меня было от 10 бит качественного звука. Для желаемой частоты ноты мне достаточно было менять только значение приращения фазы. Прослушивание синтезируемой мелодии я выполнил через встроенный в отладочную плату разъём Jack. Для этого я взял файл из демонстрационного проекта, который работает с данным кодеком, и «прикрутил» к нему простой интерфейс Avalon‐ST Sink. Только не забываем добавить в проект модули настройки аудио кодека по I2C. Сама частотная модуляция теперь уже не выходила за границы отладочной платы и реализовывалась с помощью второго NCO ядра, настроенного на частоту 100 МГц, и дополнительной галочки Frequency Modulation Input. Ко входу значения приращения фазы добавляются дополнительные младшие биты для модулирующего (звукового) сигнала. Поделюсь информацией, которую я обнаружил в процессе разработки, и, которая помогла в настройке, но в конце исключил из реализации. Вдруг кому‐то пригодится и поможет.   После нескольких неудачных попыток собрать проект, мне стало необходимо понаблюдать что происходит с обрабатываемым сигналом после моих манипуляций, узнать его форму и спектр. Конечно, это можно было бы попробовать реализовать внутри ПЛИС, либо перенести данные в Линукс, но я пошёл по более костыльному методу, и решил его как то вытащить на компьютер Так как я уже более менее был знаком с цифровой обработкой в среде MATLAB, то начал копать в эту сторону. Да и сам MATLAB везде кричит что он самый мощный инструмент для всего. И (о пламя!) я нашёл очень, как мне кажется, полезный инструмент AXI Master Read (и Writer). Matlab позволяет синтезировать специальные IP‐ядра, которые можно подключать в проекте ПЛИС и считывать данные с памяти по интерфейсу Ethernet или JTAG. Кстати, последнему не нужно назначать сигналы в Assignment Editor, он сам всё знает и подключится без посторонней помощи. На стороне ПК в Simulink используется блок AXI Master Read, в котором нужно указать сколько и с какого адреса хочешь считать данные. Конечно, скоростей больших с JTAG не добьешься, но хоть что‐то. Вполне доступное описание есть на официальном сайте. Я протестировал его сначала на небольшом видеосигнале, и мне удалось с огромной задержкой, конечно, но получить изображение, которое можно оценить или сохранить для своих целей. С аудиосигналом попроще — мне удалось считывать данные с частотой дискретизации 200 кГц. Правда, есть нюанс с синхронизацией и данные приходят «рваные» — с этим придётся ещё повозиться. На имеющейся отладочной плате со стороны ПЛИС подключена SDRAM, работать с которой я смог на максимальной частоте лишь ~130 МГц. При работе с видеосигналом такой частоты не хватает для больших разрешений. С другой стороны, на отладочной плате присутствует довольно мощное HPS ядро с 1GB памяти DDR3, но, как я уже говорил, в Linux мне лезть не хотелось, и какова же была моя радость что мне не пришлось. Оказалось, что ПЛИС тоже может воспользоваться этой памятью. Нет,  я конечно это знал, но не понимал каким образом. По простому, для этого можно взять ядро hps процессора из демонстрационного проекта GHRD отладочной платы, выкинуть всё ненужное и использовать его как ядро памяти. Единственное, что для настройки проекта пришлось сделать следующие танцы с бубном:  я оставил сигналы reset; перед компиляцией пришлось с генерировать tcl‐скрипты hps; после компиляции файл прошивки переконвертировать в rbf (конвертер также имеется в демонстрационном проекте); закинуть на flash с образом Linux и установить MSEL на запуск HPS.  (читаем My_First_hps) В своё время мне не хватало этой информации и я не знал что всё так просто.  Глубокой теории  я расписывать не буду, так как её описывали все кому не лень, и сравнений фильтров тоже великое множество. Жестких требований к фильтрации я себе не ставил, просто было интересно их практическое применение.  Сперва я перепробовал фильтры в Simulink через Filter Designer, пытался сгенерировать из него сразу в Verilog код (мне не понравилось). Тогда, всё таки, сдался и использовал ядро от Altera FIR, разобрался как туда экспортировать коэффициенты из MATLAB. Ну вот могли бы уже добавить функцию в Matlab — создавать коэффициенты сразу через запятую, в одну строчку, как надо для ядра Altera!   Тысячу и один раз прочитал и выучил на dsplib статьи по CIC фильтрам и дециматорам. Вроде разобрался, но сомневаюсь.  Из всех манипуляций, проб и ошибок я пришел к следующей структуре приемника (его половине). Так как на входе три параллельных шины данных, то необходимо их преобразовать в последовательные данные на частоте 240 МГц, но я беспокоился что ПЛИС не будет успевать при таких частотах выполнять математические действия (поправьте если не так). Поэтому, для переноса спектра на нулевую частоту, последовательные данные с гетеродина (синус и косинус), также преобразовываются в три параллельных и умножаются с входным оцифрованным сигналом. Результаты произведения просто суммируются и частота дискретизации сигнала становится 80 МГц. Если честно, я пробовал интегрировать вместо сумматора самописный CIC фильтр с децимацией R=3, но, вероятно, его простая реализация была некорректна, так что я оставил идею его отработки до лучших времён.   Следующими каскадами идут две пары CIC фильтра с порядком N=2, задержкой D=2, и с децимацией  25 и 16, соответственно. От корректирующего фильтра решил принципиально отказаться, предполагая что сильных искажений я не получу.  В итоге, на выходе фильтров имеются данные с частотой дискретизации 200 кГц. Наверное, стоит признаться, что в конечной реализации приемника я снизил частоту дискретизации до 90 МГц, и работаю в третьей зоне Найквиста. При этом второй фильтр имеет децимацию R=6, но также используются все три АЦП на 30 МГц (вдруг мне, всё таки, бракованные приехали и не выносят высоких частот).  Это оказалось самой больной темой. Не из‐за сложности, а из‐за множества структурных схем в разных источниках. Все они одинаковые и разные одновременно. Я проверил их в Simulink и в теории они работали, но в теории всегда всё работает, а на практике…  Для реализации «Only FPGA» желательно избегать делителей и тригонометрических функций, поэтому схему демодулятора я выбрал самую простейшую. В реализации «FPGA‐PC» квадратурные составляющие принимаемого сигнала передаются в Simulink, в котором имеется готовый блок демодуляции. После него стоит фильтр низких частот чтобы уменьшить шум. Результаты более‐менее удовлетворительные.  Потешно, что тестовый сигнал может неплохо принимается и без демодуляции, но я это исправил когда уменьшил амплитуду модулирующего сигнала (вроде при этом изменилась база сигнала). С реальными сигналами всё хуже, но(!), самое главное, я смог поймать некоторые радиостанции, хоть и с плохим звуком (честно я отчаялся хоть что‐то услышать). Лучше всего различимы станции только с речью. Звук очень зашумленный, и в реализации, где данные передаются на ПК, хоть и можно поставить фильтр, подавляющий большинство шумов, но, как я и говорил, из‐за рассинхронизации передачи данных, звук дополнительно искажается. При попытке записать результат с аудио разъема Jack в аудиофайл, я снова случайно использовал Simulink, где могу принять звук с микрофона, слегка его обработать, установить фильтр для подавления высокочастотных шумов и удобно записать аудиосигнал в файл. Несмотря на шумный результат, я доволен любому словечку и звуку принятых радиостанций. Рад, что обнаружил для себя и научился новым техническим приемам, и поучаствовал в цифровой обработке реального сигнала. Интересно будет почитать комментарии и о технических ошибках, замеченных в ходе действий над проектом. Только не сильно строго, а то я брошу электронику и уйду в монастырь художником :) (Шучу, кто‐то же должен вам подпекать). Ссылка на кусочки ip‐ядер из проекта Ссылка на аудио принятых радиостанций К сожалению, записи с лучшим звуком я не могу выложить из–за их содержания. Если у вас имеются несложные Linux проекты для платы DE10‐Standart (или подобных), то поделитесь пожалуйста. Хотелось бы уметь обрабатывать данные, записанные в память, или хотя бы передавать по Ethernet на ПК (по‐любому же есть простые реализации).   Если у кого‐то завалялась в тумбочке мощная нейросеть и нормальный АЦП на частотах от 220 МГц, не хотите ли поэкспериментировать и научить нейросеть самой принимать,  фильтровать и демодулировать сигнал. Я понимаю, что её основной принцип это классифицировать, но вдруг :) Либо можете развернуто в комментариях описать почему это невозможно (но вы еще даже не пробовали). Лайонс Р. Цифровая обработка сигналов Implementation of a software defined FM mixed demodulator on FPGA November 2015 FPGA Implementation of an IF Sampled FM Receiver Демодуляция сигналов с угловой модуляцией. PM и FM демодуляторы Interleaved ADCs Through the Ages. Ken Poulton. Keysight Laboratories