OpenUNB — продолжаем! Итак, мы сформировали сигнал OpenUNB согласно проекту стандарта (исходники — на Гитхабе). Сигнал вылетел в эфир, показав нам свои прелести на приборе. Теперь нам нужно его конвертировать в цифровую форму, отделить от других возможных сигналов и от шума и определить по частоте и по времени. В полосе ни много, ни мало, а 1000 каналов. Чудеса цифровой обработки сигналов! И вы удивитесь, на каком простом железе их можно совершить.
Напомню, OpenUNB предполагает односторонню связь. (Кстати, согласно опросу, около 40 процентов проголосовавших с таким подходом согласны.) То есть, базовая станция (БС) — простой приемник. Это сильно упрощает нам жизнь — для реализации минимального набора OpenUNB нам не нужны дорогие SDR и средства обработки. Нам достаточно грошового RTL-SDR и компьютера типа Raspberry.
Так вот, берем в руки сладкую парочку: RTL-SDR и Raspberry. Берем также голову в руки и пишем код приемника. Приемник должен обнаруживать все пакеты соответствующего формата в полосе OpenUNB, демодулировать их, декодировать и проверять на ошибки. На этом формально работа физического уровня OpenUNB заканчивается. Сегодня мы уделим внимание первым двум этапам обработки: обнаружение и демодуляция пакетов OpenUNB.
В полосе OpenUNB 1000 номинальных каналов в полосе 100 кГц. (Правда, дальше мы увидим, что на практике приходится нарезать по меньшей мере вдвое больше каналов.) Пакет излучается на одном частотном канале с модуляцией DBPSK с частотой 100 Гц. Мы не будем делать прием только одного канала, это никому не нужно. Во-первых, в стандарте заложена возможность повторов на разных частотах для улучшения чувствительности. Во-вторых, неточность опорных генераторов оконечных устройств (ОУ) в диапазоне температур их работы все равно приводит к миграции частоты передачи в пределах нескольких частотных каналов на приеме БС.
Придет время, и устройств интернета вещей должно быть много, поэтому БС нового стандарта должна проектироваться так, чтобы принимать сразу множество датчиков. Поэтому мы сразу делаем обнаружение во всей полосе. Для начала нужно разделить полосу приема 100 кГц на элементарные полосы, соответствующие полосе сигнала. Теоретически, полоса сигнала — 100 Гц. Давайте это проверим по спектру излучаемого сигнала.
Мы видим, что в реальности полоса чуть меньше.
Исходя их этих измерений, задаем требования к амплитудно-частотной характеристике (АЧХ) фильтра основной избирательности (ФОС) и рассчитываем его импульсную характеристику (ИХ). Получается 12500 коэффициентов ИХ с АЧХ следующего вида.
На самом деле, при задании требований к АЧХ ФОС в такой многоканальном системе должен разрешаться очень серьезный компромис. С одной стороны, мы не должны уменьшить энергию полезного сигнала, должны держать полосу ФОС достаточно широкой, чтобы основная часть энергии попала в фильтр. С другой, мы должны уменьшить мешающее влияние других сигналов, делая границу полосы задержания как можно меньше. Эта тема может быть предметом даже не инженерных, а научных изысканий. Стандарты на системы передачи иногда задают маску АЧХ приемника, чтобы инженеры не занимались наукой. Мы сейчас остановимся на том, что есть, оставив эту оптимизацию напотом, еще и потому, что дальше вы увидите, что эту полосу нужно будет немного увеличить по другим соображениям.
С точки зрения вычислительной эффективности (не забывайте, мы хотим уложиться в очень недорогое железо и должны экономить), расфильтровывать полосу приема нужно "быстрыми" методами. Нужно делать так называемую гребенку фильтров с помощью быстрого преобразования Фурье (БПФ). Здесь я только приведу схему обработки, а подробности вы сможете посмотреть в Интернете или в моей статье на эту тему.
Пусть мы определились с частотой отсчетов на выходе гребенки, она соответствует символьной скорости — 100 Гц. Теперь нам нужно определить сетку частот гребенки фильтров. Если бы оконечные устройства излучали сигнал с точной настройкой, то мы должны были бы делать гребенку с частотным шагом 100 Гц. Так как частота сигналов ОУ мигрирует на величину более одного канала, то мы обязаны считать, что частота на приеме будет любой. Тогда требование к сетке частот будет устанавливать следующий блок в тракте приема — обнаружитель пакетов.
Здесь следует оговориться, что вариантов обнаружителя пакетов может быть много, с разной степенью практичности для реализации в столь слабом железе. Мы готовы поддержать дискуссию и изменить алгоритм, если вы найдете другой, более экономичный вычислительно и/или более помехоустойчивый. Мало того, вы можете форкнуть код приемника на Гитхабе и реализовать и протестировать его сами (благо железо недорого и может даже оказаться у вас под рукой) и всем потом рассказать. Это будет реально круто! Только не забывайте держать код открытым, это же OpenUNB.
Рассмотрим практически приемлемый вариант обнаружителя пакетов. Исходя из опыта нескольких разработок в области UNB, экономичным методом реализации обнаружителя пакетов является обнаружитель на жестких решениях. И мы на этом этапе не будем изобретать велосипед. Цель обнаружителя: находить преамбулы одновременно во всей полосе приема. Длина преамбулы — 32 бита. Поиск преамбулы происходит по выходу относительно-фазового дискриминатора, работающего по каждому выходу гребенки фильтров. Например, если шаг гребенки у нас будет 50 Гц, а частота отсчетов с гребенки 100 Гц, то в секунду будет 200000 (двести тысяч) проверок на преамбулу. Напомню, что шаг гребенки мы еще только хотим определить, исходя из характеристик работа обнаружителя.
Прикидка показывает, что в таком варианте качество работы обнаружителя не будет сильно страдать при отстройке сигнала от центра фильтра на 25(?) Гц. Эта прикидка была проверена на практике. Теперь мы можем считать достаточной сетку гребенки 50 Гц. Только полосу пропускания ФОС нам нужно также увеличить на 25 Гц, чтобы не терять энергию сигнала при максимальных отстройках. Все, с расфильтровкой покончено.
Код умножения на ИХ выглядит так:
for (unsigned int ch=0; ch < numOfChannels; ch++) {
float iC = 0.0;
float qC = 0.0;
for (int j=0; j < BL_125K_to_100/numOfChannels; j++) {
iC += dataIn[i + ch + j*numOfChannels][0] * B_125K_to_100[ch + j*numOfChannels];
qC += dataIn[i + ch + j*numOfChannels][1] * B_125K_to_100[ch + j*numOfChannels];
}
fftw_in[ch][0] = iC;
fftw_in[ch][1] = qC;
}
БПФ используется библиотечное:
fftwf_execute(fftw_p);
Теперь нам нужно выбрать количество ошибок в преамбуле, которые мы будет считать допустимым. Это значение нужно выбирать таким, чтобы соблюсти примерное равенство помехоустойчивости преамбулы и помехоустойчивость остальной части пакета. Исходя из опыта, число допустимых ошибок в преамбуле выбрано равным трем. Считается, что при таком количестве ошибок в преамбуле отношение сигнал-шум будет таким, что количество ошибок в остальной части пакета позволит последующим стадиям обработки их исправить и выдать проверенный пакет.
Код обнаружителя:
corr1[ch] = q1[ch]*q2[ch] + i1[ch]*i2[ch];
corr[ch] = (corr[ch] >> 1) | ((corr1[ch] > 0 ? 1:0) << 31);
const uint32_t prea = 0x97157A6F;
int err1 = bitDif(corr[ch], prea);
Следует опять отметить, что все эти эмпирически принятые решения могут быть вами обоснованно оспорены. Мы будем внимательно прислушиваться и воплощать в код все самые лучшие модификации. В этом и состоит большая часть смысла открытости OpenUNB.
После обнаружения преамбулы считается, что есть некоторая вероятность, что в данное время в данном частотном канале был принят пакет. Чтобы подтвердить или опровергнуть эту гипотезу, нужный отрезок сигнала проходит процедуру демодуляции в мягкие решения, которые поступают на вход декодеру корректирующего кода и далее на блок проверки контрольной суммы пакета. Если контрольная сумма пакета сошлась, гипотеза о приеме пакета считается истинной и пакет отправляется на криптографическую обработку.
Формирование мягких решений о символах пока сделано упрощенно:
std::vector<float> data;
for (int i=1; i< pp->data.size(); i++) {
float corr;
corr = pp->data[i - 1].real() * pp->data[i].real() + pp->data[i - 1].imag() * pp->data[i].imag();
}
На этом обнаружитель пакетов и демодулятор заказчиваются.
Подведем итог. Мы вчерне прикинули и проверили на практике технические решения по первичному приему OpenUNB на простом железе. Гребенка должна быть с шагом 50 Гц, фильтр основной избирательности должен иметь полосу пропускания 100 Гц. Код обнаружителя пакетов реализован, проверен на столе в работе в том числе и с малыми SNR и находится в Гитхабе. Проверка на местности еще впереди.
Обоснование выбора корректирующего ошибки кода и описание его реализации будет приведено в следующей статье.
Хочется выразить огромную благодарность за проделанную работу нашему основному программисту — deef137. На все вопросы по коду ответит он. По обработке сигналов спрашивайте меня.
ECRV
Какая длина пакетов и преамбул? Какой точности нужны кварцы? И далеко не все Sub-1GHz чипы умеют передавать на скорости 100, какие еще есть скорости?
itsar Автор
Скорость только 100 бод.
Точности недорогого термокомпенсированного генератора хватит.
Длина преамбулы — 32 символа, длина закодированного пакета — 128 или 256 символов.