
В этом исследовательском тексте я попробовал найти способ передать бинарные данные при помощи лазерного луча. У меня уже был текст про передачу бинарных данных при помощи звука. Теперь же попробуем передать данные при помощи света.
Постановка задачи:
Наладить беспроводную передачу бинарных данных по лазерному лучу в реальном времени. Битовая скорость 25 бит в секунду. В качестве аппаратуры трансивера задействовать электронную плату с микроконтроллером STM32F407VE.
Данные передавать BPSK модуляцией (QAM-2). Частота несущей должна быть 250 Hz. Частота дискретизации - 1kHz (Длительность семпла 1 ms). Битовая частота BPSK 25 bit/s. Длительность одного чирпа 40ms (40 семплов). Сигнал принимать фотодиодом в связке с операционным усилителем (микросхема OPT101). Все вычисления по приёму и цифровой обработке данных выполнять на уровне прошивки микроконтроллера STM32F407VE. Надо принять при помощи ADC1 семплы с выхода фотодатчика, который подключен к пину PA3. В прошивке надо декодировать BPSK модуляцию. Запрограммировать программный Costas Loop внутри прошивки. Поток принятых битов отображать в GPIO. Печатать биты в виде последовательности hex чисел.
Теория
Все модуляции (ASK, OOK, BPSK, FSK, 256-QAM, CSS, GFSK, MSK) можно отнести к интерфейсам физического уровня модели OSI-7, так как модуляции поясняют как именно передаются биты в физической среде. BPSK тоже можно рассматривать как интерфейс физического уровня.
Какие есть параметры у BPSK сигнала?
№ | Параметр | Единица измерения | Значение |
1 | несущая частота | Hz | 250 |
2 | битовая скорость | bit/s | 25 |
3 | амплитуда несущей | PCM | 2000 |
4 | длительность одного чипа | секунды | 40ms |
5* | Частота дискретизации | Hz | 1000 |
6 | порядок бит при упаковке байта внутри BPSK сигнала | младшим битом вперёд / старшим битом вперед | MSB |
7* | Разрядность одного семпла | bit | 12 |
Вот так выглядит BPSK сигнал в частотной области. Br - это битовая скорость.

При понижении Br битовой скорости BPSK вырождается в синус. Поэтому при уменьшении битовой скорости ширина сигнала сужается. При повышении битовой скорости BPSK спектр расширяется.
chip (чип) - это минимальный фрагмент BPSK сигнала, где фаза несущей частоты не меняется. Фактический в BPSK чип это один бит данных.

Гетеродин - локальный генератор. Это генератор синусоиды, который испускает локальную копию несущей частоты. Сигнал гетеродина смешивается с несущей и таким образом на выходе смесителя оказывается два сигнала: высокочастотное слагаемое и снятый с несущей полезный информационный сигнал.
Реализация
Что надо из оборудования?
Наименование | Количество | Пояснение | Цена |
Лазерный модуль KY-008 | 2 | Для отправки сигнала | 200 |
Электронная плата JZ-F407VET6 | 2 | Для обработки данных | 1200 |
OPT101 Monolithic Photodiode and Single-Supply Transimpedance Amplifier | 2 | Фотодиод с операционным усилителем для приема сигнала | 2300 |
Элемент солнечной батареи (optional) | 2 | для приема сигнала | ? |
Прежде всего Вам потребуется лазерный модуль KY-008. Надо купить сразу несколько экземпляров, так как попадаются бракованные экземпляры.

Фотоприемником может быть как отдельный Фотодиод BPW21R, 420..675нм с отдельным операционным усилителем. Так и полностью интегрированный компонент OPT101, содержащий как фотодиод, так и операционный усилитель.

Параметры связи
Попробуем передавать 25 бит в секунду. На один бит пусть приходиться один чирп. Частота несущей пусть будет 250 Hz. По есть один чирп - это 10 периодов несущей.
Передатчик
С передачей данных по лазеру всё достаточно просто. Лазерный передатчик - это просто микроконтроллер, который генерирует PWM сигнал. Микроконтроллеры STM32 позволяет инвертировать фазу этого PWM.
bool timer_polarity_set(uint8_t num, TimerOutChannel_t channel, TimerPolarity_t polarity) { bool res = false; TimerInfo_t* Info = TimerGetInfo(num); if(Info ){ TimerRegCCER_t CCER; CCER.dword = Info->TIMx->CCER; switch(channel){ case TIMER_OUT_CHANNEL_0:{ res = false; } break; case TIMER_OUT_CHANNEL_1:{ CCER.CC1P = TimerPolarityToStmPolarity(polarity); } break; case TIMER_OUT_CHANNEL_2:{ CCER.CC2P = TimerPolarityToStmPolarity(polarity); } break; case TIMER_OUT_CHANNEL_3:{ CCER.CC3P = TimerPolarityToStmPolarity(polarity); } break; case TIMER_OUT_CHANNEL_4:{ CCER.CC4P = TimerPolarityToStmPolarity(polarity); } break; case TIMER_OUT_CHANNEL_5:{ res = false; } break; default : res = false; break; } Info->TIMx->CCER=CCER.dword ; } return res; }
Получается, что можно делать сдвиг фазы на 180 градусов. Таким образом, можно практически аппаратно и бесплатно на лету генерировать на пине цифровой BPSK сигнал. Далее подключаем к PWM пину лазерный модуль KY-008 и на выходе источается BPSK модулированный лазерный луч, передающий данные в мировое пространство.
Но есть одно но. Фазу надо переключить на границе переключения периодов несущей. Это сделает передаваемый сигнал чище. Плюс так будет проще отлаживаться на принимающей стороне.

Приемник
Прием BPSK сигнала - это большая и нетривиальная задача.
Фаза 1: Захват семплов
Если неcущая у нас 250 Hz, то частота дискретизации по теореме Котельникова должна быть как минимум 500 Hz. Я пока поставлю для надежности частоту дискретизации равную 1k Hz. Про получение потока ADC отсчетов у меня есть отдельный текст: Потоковая запись ADC семплов на STM32. Это не так просто как кажется на первый взгляд. Надо задействовать DMA, прерывания и пр.
Фаза 2: Фильтрация постоянного смещения
После измерения на ADC отсчёты всегда приходят положительные 0.....4095. Как на лету отсеивать постоянную составляющую сигнала для последующей ЦОС обработки? Для этой цели, есть вот такой цифровой фильтр.

в коде это выглядит вот так
bool dc_cut_filter_proc_sample(uint8_t num, const int32_t x_n, int32_t* const y_n) { bool res = false; DcCutFilterHandle_t *Node = DcCutFilterGetNode(num); if (Node) { if (y_n) { int32_t e_n = (Node->numerator * Node->a_n_1)/Node->denominator; int32_t a_n = x_n + e_n; *y_n = a_n - Node->a_n_1; res = true; Node->a_n_1 = a_n; } } return res; }
фильтр в самом деле работает и исключает постоянное смещение.

Фаза 3: Снятие сигнала с несущей частоты
Надо настроить гетеродин, который будет выдавать два сигнала: sin и cos на частоте несущей. В нашем случае на частоте 250 Hz. При этом, так как мы принимаем PWM, то и сигнал гетеродина надо генерировать тоже в виде PWM. Вот так.

Первое, что происходит с переменным сигналом после ADC это его перемножение через смеситель (перемножитель) на сигналы квадратурного гетеродина. Фаза этого гетеродина будет позже регулироваться алгоритмом Costas Loop.

Чтобы исходный сигнал снять с несущей его надо пропустить через квадратурный смеситель. Внутри квадратурного смесителя работает локальный генератор: гетеродин. Частота гетеродина равна частоте несущей. То есть 250 Hz. Период гетеродина соответственно 4 ms. При этом длительность семпла у нас 1ms. Получается, что на один период гетеродина приходится всего 4 семпла. Это значит, что фазу гетеродина мы сможем регулировать только в 4х позициях: 0, 90, 180, 270 градусов. Это упрощает задачу регулирования фазы гетеродина. Об этом позже.
Фаза 4: Отсев двойной частоты
После смесителя в сигнале появляется два слагаемых. Полезное слагаемое с фазой сигнала и слагаемое с удвоенной частотой. Это буквально свойство из школьных тригонометрических функций.

Надо как-то ликвидировать высокую частоту на выходе смесителя. Для этого сигнал надо пропустить через фильтр нижних частот. Это надо сделать так, чтобы было поменьше вычислений. Самым простым фильтром является оконный интегратор. По сути это однородный FIR фильтр, у которого все коэффициенты равны единице. Получается фильтр с усилением. Зато это относительно просто вычислять в условиях исполнения на микроконтроллере. Но можно еще больше упростить однородный фильтр, если на каждом шаге не суммировать все элементы, а просто добавлять первый и вычитать последний элементы из сдвигового регистра z^{-N}.

Фаза 5: Определение смещения фазы гетеродина
На выходе фильтров IQ получается два целых числа, которые образуют комплексное число. Надо вычислить аргумент этого комплексного числа и его минимальное отклонение от оси X. Это можно определить при помощи функции арктангенса. Если на выходе I/Q после фильтра нижних частот получился комплексный ноль, то как определить ошибку по фазе? Ведь нулевой вектор может быть направлен в любую сторону. В этом случае можно просто считать, что ошибка по фазе равна нулю.
/* Q ^ --------|++++++++ --------|++++++++ __--------|++++++++_______\I ++++++++|-------- / ++++++++|-----*-- ++++++++|-------- */ inline float calc_carrier_phase_err_rad(float i_val, float q_val) { float carr_phase_err_rad = 0.0f; if(0.0f < i_val) { carr_phase_err_rad = atan2f(q_val, i_val); // -pi/2...... pi/2 } else { carr_phase_err_rad = atan2f(-q_val, -i_val);// -pi/2...... pi/2 } #ifdef HAS_BPSK_DEBUG LOG_PARN(SDR, "Phase,%f+j(%f),Err:%7.6f Rad",i_val,q_val, carr_phase_err_rad); #endif return carr_phase_err_rad; }
Удивительно то, что когда я заменил atan2f на извлечение значений фаз из предварительно рассчитанных LookUp таблиц, то обработка стала только медленнее.
float calc_carrier_phase_err_lut_rad(const int32_t i_val, const int32_t q_val) { float phase_err_rad = 0.0f; if(0<=i_val) { if(0<=q_val) { phase_err_rad = phaseErrRadLUT_iqPos[i_val][q_val]; }else{ phase_err_rad = phaseErrRadLUT_iPosQNeg[i_val][-q_val]; } }else{ if(0<=q_val){ phase_err_rad = phaseErrRadLUT_iNegQpos[-i_val][q_val]; }else{ phase_err_rad = phaseErrRadLUT_iNegQneg[-i_val][-q_val]; } } #ifdef HAS_BPSK_DEBUG LOG_PARN(QUAD_MIX_4FS, "Phase,%f+j(%f),Err:%7.6f Rad",i_val,q_val, phase_err_rad); #endif return phase_err_rad; }
Фаза 6 Регулирование фазы гетеродина.
Фаза гетеродина величина инертная. Меняется только при изменении расстояния между передатчиком и приемником. Поэтому фаза не может меняться мгновенно. Менять фазу надо непрерывно и медленно. Отлаживать алгоритм можно вообще на статической прохардкоженной фазе. Фазу гетеродина можно регулировать PID регулятором. PID регуляторы это не только про регулирование температуры нагревателя или частоты вращения мотора. PID регулятор вполне может применятся в SDR обработке сигналов. Интегральная часть регулятора завязана на размер сдвигового регистра в фильтре нижних частот. Надо назначать интегральный коэффициент равным 1/M, где M-длинна сдвигового регистра в скользящем сумматоре, который образует ФНЧ.
Фаза 7 Прореживание сигнала
Как только PID регулятор выйдет на корректную фазу, начнется уверенный прием битов. Данные будут в I компоненте комплексного числа. Q будет равна нулю. Однако эти биты по-прежнему на частоте дискретизации (1kHz). Чтобы выделать логические биты (25 bit/s) надо пропустить сигнал I_Filt через компаратор и дециматор. Дециматор прореживает поток семплов. Из 40 семплов одного знака оставляет только один. Из битов далее формируются байты. Дециматор легко реализовать на основе конечного автомата из трёх состояний.

Принятые биты можно отображать в GPIO и формировать как мозаику байты данных. Для определенности можно считать, что данный принимаются побайтово, а сами биты принимаются старшим битом вперед.
Фаза 8 Прием битов информации.
Принятые биты можно отображать на отдельном GPIO пине. Сами биты лучше кодировать самосинхронизирующимся кодом. Например манчестерским кодом.

Поэтому на принимающей стороне надо декодировать манчестерский код. Найти первую последовательность 11 или 00 и начать декодировать реальные биты .

Достоинства BPSK модуляции
++Относительная простота декодирования. Достаточно настроить Costas Loop и можно смело принимать поток битов.
Недостатки BPSK модуляции
--BPSK плох тем, что перед приемом битов ему надо синхронизоваться с фазой несущей частоты. Поэтому первые биты данных будут непременно потеряны. Как только гетеродин подстроит фазу под несущую, так сразу начнется уверенный прием самих битов. Поэтому надо в протоколе перед каждым пакетом добавлять какую-н преамбулу, которая поможет принимающей стороне синхронизироваться.
--Прием может быть в инвертированных битах. Надо как-то распознать инвертированные биты.

--BPSK модуляция очень не любит принимать длинные последовательности нулей (или единиц). BPSK приемники может легко принять лишний бит (ноль или единицу). Поэтому надо передавать не просто биты, а самосинхронизирующиеся коды, в которых не бывает больше трех нулей подряд. Условно надо делать битстаффинг после каждых трех бит, или применять манчестерский код.
--BPSK модуляция не показывается начало и конец пакета с данными. Не показывает начало и конец отдельных байтов. Всё, что мы получаем это поток нулей и единиц. Пакетная синхронизация это задача более высоких уровней обработки данных. Можно конечно добавить отдельный пин для индикации того факта, что Costas loop вышел в режим стабилизации, однако это потребует дополнительных вычислений, которые негативно повлияют на производительность прошивки.
Какие параметры в BPSK приемнике?
При написании программы обработки BPSK модулированного сигнала вам придется конфигурировать множество параметров. В своей установке я выбрал вот такие параметры.
Параметр | Какое значение ставить | Единицы измерения |
Несущая частота | 250 | Hz |
Частота дискретизации ADC | 1000 | Hz |
Форма сигнала гетеродина | PWM | форма сигнала |
Битовая скорость | 25 | bit/s |
Параметр ФВЧ для отсева DC смещения (0 ....1) | 0.95 | -- |
Порядок FIR фильтра | 26 | семплов |
Конфиги PID регулятора | I=1/FIRordef (0.001) | безразмерный |
Как отлаживать прием данных по BPSK?
1) IQ отображать на DAC. Таким образом можно анализировать сигнальное созвездие буквально осциллографом в режиме реального времени отрисовывая фигуры Лисажу.
Название провода | Что отображать | GPIO | MCU pin |
DAC_OUT1 | I | PA4 | 29 |
DAC_OUT2 | Q | PA5 | 30 |
2) Весть тракт декодирования, всю SDR обработку можно отладить на LapTop PC внутри консольного Win приложения, написанного на том же самом языке программирования, что и микроконтроллерная прошивка, то есть на языке программирования Си.
3) Прием битов можно просматривать при помощи логического анализатора. Принятые биты показывать на GPIO. Буквально двумя пинами: RxData и RxClock.

Одним пином я показываю биты (data), а вторым пином я показываю границу битов (clock). На каждом перепаде тактирования clock выдвигается новый бит принятых данных в data (режим DDR).

4) Можно соединить передатчик и приемник проводной перемычкой, полностью минуя оптическую часть. Буквально от PWM к ADC. Это позволить отлаживать приём отбрасывая возможные проблемы в среде передачи данных.

Объединив заземление передатчика и приемника, можно на логическом анализаторе DS Logic сверять переданные и отправленные биты. Как можно заметить, данные в самом деле передаются и корректно принимаются. Можно заметить корреляцию между переданными битами и принятыми битами.

Чтобы сильно не нагружать прошивку я отображаю принятые биты прямо на GPIO. Буквально двумя пинами: data и clock. Одним пином я показываю биты, а вторым пином я показываю границу битов. На каждом перепаде тактирования clock выдвигается новый бит данных в data.
Оценка производительности
SDR обработка BPSK семплов это весьма трудоемкая процедура для микроконтроллера. Эксперименты проводил на микроконтроллере с ядром ARM Cortex-M4 (STM32F407VET6). Модульный тест показывает, что в среднем на обработку одного ADC семпла уходит 732 us.

Это значит, что на ARM Cortex-M4 168MHz частота дискретизации обработки ADC семплов не может превышать 1366 Hz. Иначе прошивка просто захлебнётся от наплыва необработанных отсчетов от АЦП.
Эксперимент с реальным лазерным лучом.
Трансляцию данных я произвел на основе двух плат JZ-F407VET6. Одна была передатчиком, вторая приемником.

Я светил лазером внутри подарочной картонной коробки. Там же внутри был фотодиод. Для чистоты эксперимента я накрыл коробку крышкой.

Сам фотодиод OPT101 был установлен на макетной плате.

По лазеру я циклически передавал байт 0xF0 в манчестерском коде. То есть в BPSK модулятор передатчика периодически поступал 2х байтовый массив 0xaa55. На принимающей стороне мне удалось принять 4 байта подряд!

Достоинства передачи данных по лазерному лучу
++Это акустически бесшумный способ передачи информации.
++Отсутствуют сигнал в радио спектре. Трудно подслушать.
++При неподвижном передатчике и приемнике фаза особо не меняется. И передатчик и приемник статичные.
++BPSK сигнал легко аппаратно модулировать обыкновенным PWM на любом микроконтроллере, никак при этом не нагружая микроконтроллер.
Идеи проектов лазерной связи
--Передача данных между космическими спутниками.
--Военно-полевая связь. Можно сделать бинокль с передачей речи по лазерному лучу.
--Интернет между домами. Вместо провисающих проводов.
--Датчики качества воздуха. По нарушению связи можно делать выводы по взвешенным частицам пыли в объёмах воздуха.
--Можно сделать ГСН, которая будет ориентироваться только на фото сигналы строго определенного кода, который передается по BPSK лазерным лучом.
--Передача и прием данных через оптоволокно. Лазером можно светить в оптоволокно. Тогда можно прокладывать данные по извилистым маршрутам.
--Каждый светодиодный светильник уличные фонари можно сделать передатчиками бинарных данных. То есть помимо свечения, светильник может транслировать на высокой несущей ещё и точное время, свою геолокацию, номер этажа, почтовый индекс, адрес дома и прочее. Мобильные телефоны (или автомобили) могут принимать эти данные фронтальным фотодатчиком и анализировать метаданные.
Итоги и результаты
Удалось научиться декодировать BPSK сигнал буквально из потока ADC семплов в реальном времени.
По лазерному лучу мне удалось передать и принять подряд 32 бит информации на битовой скорости 25 bps на расстояние 5 см!

В качестве дальнейшего развития этого способа надо делать квадратурный смеситель полностью аппаратным. Либо перекладывать задачу снятия сигнала с несущей на FPGA, либо искать специализированный ASIC для декодирования BPSK сигнала.
Надеюсь этот текст вдохновит кого-нибудь сделать лазерный BPSK приёмник на более производительных микроконтроллерах или даже на FPGA и добиться более высоких битовых скоростей приема данных.
Словарь
Сокращение | Расшифровка |
PID | Proportional-Integral-Derivative |
ADC | Analog-to-Digital Converter |
ФВЧ | Фильтр Высоких Частот |
ФНЧ | Фильтр Нижних Частот |
ГСН | Головка СамоНаведения |
SDR | Software-Defined Radio |
NCO | Numerically controlled oscillator |
PWM | Pulse Width Modulation |
BPSK | Binary Phase Shift Keying |
Ссылки
Название | URL |
Управление фазой аппаратного PWM сигнала на STM32 | |
Потоковая запись ADC семплов на STM32 | |
Цифровая обработка сигналов, Ричард Лайонс, 2006, изд. Бином. | |
BPW21R, Фотодиод 420..675нм | |
Самодельный фазовый лазерный дальномер @iliasam | |
Оптический реверс-инжиниринг лазерного дальномера @AndreyWinter | |
Устройство слежения за движущимся источником света @avrfun | |
Декодирование BPSK Модуляции из Звука (или передача данных по воздуху) | |
KY-008 3pin 650nm Red Laser Transmitter Dot Diode Copper Head Module GOHJMY |
Вопросы
1--На какой предельной частоте дискретизации можно записывать семплы на ADC микроконтроллера STM32F407VE? 2 Msps при этом надо работать в режиме DMA.
2--Существуют ли ASIC микросхемы, которые делают квадратурный смеситель полностью аппаратно? Чтобы внутри был управляемый гетеродин NCO фазу (0.....360 градусов) и частоту (0 до 1MHz) которого можно менять по SPI в широком диапазоне. Или вовсе задавать несущую частоту извне подавая на пин. Чтобы на входе был ADC для подключения микрофона или фотодиода, а на выходе аналоговые значения I и Q. Желательно чтобы там еще внутри был Costas-loop для снятия с несущей BPSK модулированных сигнал.
Что-н типа AD9684,AD6674, SI4703 только подешевле и для акустических частот, а не для RF диапазона.
3--Если на выходе I/Q после фильтра нижних частот получился комплексный ноль, то как определить ошибку по фазе? Ведь нулевой вектор может быть направлен в любую сторону.
4--Нужно ли в PID регуляторе обнулять интегральную часть, если ошибка на входе равна нулю? В данном случае нет.
5--Как называется последовательный двухпроводной цифровой интерфейс, где биты захватываются по любому перепаду на проводе тактирования?
