IEEE802.15.4 - стандарт для беспроводных персональных сетей с низким уровнем мощности сигнала в нелицензируемых диапазонах частот.
Он определяет физический уровень и MAC уровень доступа к среде.
В этой статье рассматривается физический уровень, версия 2003 года и OQPSK модуляция.

Сначала немного теории.
Стандарт рассчитан на устройства с низким потреблением и низкой стоимостью. Поэтому в нем выбран метод модуляции OQPSK имеющий малый пик-фактор (отношение пиковой мощности к средней) и допустима высокая нестабильность передатчика 40ppm.
Диапазон(который будем принимать) 2400-2483.5МГц.
Это нелицензируемый диапазон ISM(Industrial, Scientific, Medical) и в нем предусмотрены каналы с 11 по 26 шириной по 2МГц.
Для совместного использования спектра с другими устройствами, например Wi-Fi, и помехоустойчивости, применяется метод расширения спектра DSSS(direct-sequence spread spectrum).
Скорость передачи данных в канале составляет 250КБит/с. Биты объединены в символы по четыре и каждый символ передается псевдо шумовой последовательностью из 32х отсчетов.
Итого получаем частоту дискретизации 2МГц. Для определения начала сигнала, и нужд синхронизации, передается преамбула из восьми символов "0".

Теперь практика.
Частота дискретизации 2МГц минимальна. На практике (при приеме) её увеличивают. Это удобно сделать вдвое, т.е 4Мгц.
Для частотной, временной и фазовой синхронизации необходимо принять преамбулу. Классический метод - сравнение принятого сигнала с задержанным.
Преамбула состоит из восьми нолей и сравнивая принятый сигнал, и задержанный на время передачи символа 16мкс, можно определить рассинхронизацию.
Можно но не всю. При нестабильности в 40ppm разность фаз может превысить 180гр. Здесь не приводиться расчета, но при такой нестабильности сравнивать можно только с задержанным сигналом не более чем на 5мкс. И здесь наступает расплата за дешевизну - "low cost" дорожает.
Есть разные методы решить эту проблему. Выбранный метод - двойная кросс-корреляция.
Т.к. сравнивать можно одинаковые сигналы, а менее чем через 16мкс сигнал не повторяется, то для сравнения применим местный, синтезированный сигнал, соответствующий символу "0". В этом методе принятый сигнал делится на небольшие части менее 5мкс и сравнивается с ожидаемым локальным.
Данное сравнение даст нам отличие по частоте (угол между векторами) от местного сигнала для каждой части, но не от частоты передачи.
Но вторая кросс-корреляция уже полученных смещений между собой и будет нужной оценкой.
Вроде все долго и запутанно, но в итоге функция довольно простая:

complex ieee802_15_4::double_correlator(const complex *chip_)
{
    for(int j = 0; j < num_segment; ++j){
        int n = j * len_segment;
        seg_real[j] = 0.0f;
        seg_imag[j] = 0.0f;
        for(int i = n; i < n + len_segment; ++i){
            seg_real[j] += chip_[i].real() * local_zz_real[i] + chip_[i].imag() * local_zz_imag[i];
            seg_imag[j] += chip_[i].imag() * local_zz_real[i] - chip_[i].real() * local_zz_imag[i];
        }
    }
    float sum_real = 0.0f;
    float sum_imag = 0.0f;
    for(int i = 1; i < num_segment; ++i){
        int j = i - 1;
        sum_real += seg_real[i] * seg_real[j] + seg_imag[i] * seg_imag[j];
        sum_imag += seg_imag[i] * seg_real[j] - seg_real[i] * seg_imag[j];
    }

    return {sum_real, sum_imag};

}

и самая требовательная к производительности.

И получаем наши восемь символов:

Зеленая линия - результат корреляции. Красная порог - подсчитывающий восемь нолей.

После преамбулы ожидается два символа 0x07 и 0x0A описанные в стандарте как SDF(start-of-frame delimiter) или разделение начала кадра. К ним уже можно применить наше вычисленное смещение по частоте, а сравнив первый символ с локальным(ожидаемым), получим и отклонение по фазе:

Здесь зеленая линия - принятый сигнал, красная - ожидаемый(синтезированный или локальный). На горизонтальной оси номера отсчетов. До 64-го отсчета символ 0x07 и он синхронизирован по частоте. После 64-го отсчета символ 0x0A. Он уже синхронизирован по фазе. Хорошо видно почти идеальное совпадение, особенно с учетом того, что решение о принятом бите принимается по расположению отсчета с большей амплитудой относительно центральной оси. Напомним - работаем с передискретизацией в два раза на приеме и дискретизация в два раза при передаче. При принятии решения три отсчета отбрасываются.

Остается синхронизация по времени.
Для синхронизации по времени применён интерполятор Фарроу по схеме - 4 point, 3rrd order Hermite, x-form. Звучит угрожающе но выглядит в коде не так сложно:

complex ieee802_15_4::interpolator(const complex &data_, const float &mu_)
{
    // 4-point, 3rd-order Hermite (x-form)
    delay_data_3 = delay_data_2;
    delay_data_2 = delay_data_1;
    delay_data_1 = data_;
    complex c0 = delay_data_2;
    complex c1 = 0.5f * (delay_data_1 - delay_data_3);
    complex c2 = delay_data_3 - 2.5f * delay_data_2 + 2.0f * delay_data_1 - 0.5f * data_;
    complex c3 = 0.5f * (data_ - delay_data_3) + 1.5f * (delay_data_2 - delay_data_1);
    x1 = mu_;
    x2 = x1 * x1;
    x3 = x2 * x1;
    complex x = c3 * x3 + c2 * x2 + c1 * x1 + c0;
    complex out = {i_delay_2, x.imag()};
    i_delay_2 = i_delay_1;
    i_delay_1 = x.real();

    return out;

}

Здесь нужно вычислить задержку mu_.
Сделаем это сравнивая синхронизированный символ 0x0A с локальным путем корреляции.
По точкам корреляционной кривой определим искомое mu_ методом рано-поздно:

Смметричный вид показывает совпадение частот дискретизации. Перекос - отклонение.

Можно декодировать фрейм, но только короткий. Если 5 байт типа “acknowledgment” принимались уверенно, то более длинный “data” разваливался в конце.
Начальной синхронизации оказалось недостаточно. Увеличивать точность не хотелось.
Компьютер старенький, уже 7 лет, производительность на исходе, уже и так много написанно, лень ...
Поэтому следующий метод без названия. На просторах интернета не нашел что-то подобное, но “ничто не ново под луной”.
Суть метода проста. Раз первые символы принимаются замечательно(см. картинку выше), то можно вычислить фазовую ошибку и компенсировать в следующем символе. И так до конца.
И вот созвездие OQPSK:

А здесь принят и подтвержден (в стандарте проверка целостности CRC16) довольно слабый сигнал:

Вычисление соотношения сигна - шум пока нет.

Для тестов был приобретён шлюз и самая простая кнопка Zigbee, но можно было обойтись и без них. Оказалось вокруг довольно интенсивный обмен на двух каналах. На умный дом не похоже - слишком частая передача(до нескольких кадров в секунду).
Пример приема:

Собственно весь UI целиком:

Сам проект можно посмотреть здесь: https://github.com/Oleg-Malyutin/SDR_ieee802.15.4_PHY

В дальнейших планах передатчик (и это гораздо проще).
Спасибо за внимание.

Комментарии (8)


  1. nv13
    04.06.2025 22:08

    40 ppm это стабильность хорошего кварца, а для него интервал корреляции сигнала существенно больше, чем 5 мкс. Если Вы про разброс частот передатчика и приёмника, то да, подобные рассуждения актуальны, и надо делать подстройку по частоте или подкручивать фазу постоянно. В общем тут какая то некорректность у Вас, имхо


    1. olrad Автор
      04.06.2025 22:08

      Расчет довольно простой. 16мкс - это ±31,25кГц ( 1/16(мкс)). При таком отклонении вектор сравниваемого сигнала повернется от -pi до +pi. Теперь берем ±40ppm и 2.4ГГц - получим 99,6кГц. Отклонение допустимое стандартом в этом диапазоне в три (с лишним) раза больше. Вот на эти три с лишним и делим. В итоге 5мкс.


  1. NutsUnderline
    04.06.2025 22:08

    Формулы это конечно хорошо, но неплохо бы и картинков в с железом.

    Оказалось вокруг довольно интенсивный обмен на двух каналах. 

    То же и с bluetooth. причем можно для начала даже тратиться на PlutoSDR - достаточно смартфона или адаптера для ПК. чтобы увидеть что вокруг много устройств.

    для того чтобы насладиться эфиром в IEEE802.15.4  (ну и тоже bluetooth) можно попробовать недорогую платку на nrf52840, для нее есть прошивки сниффера с хорошим разбором пакетов в wireshark


    1. olrad Автор
      04.06.2025 22:08

      Не сбивайте меня с пути истинного. Какое железо? Это SDR!


      1. NutsUnderline
        04.06.2025 22:08

        ну а PlutoSDR наверное тоже скачан с github :) Серьезно, ну почему хотя бы не добавить буквально 1 фразу "Эксперимент проводился при помощи следующего оборудования:"


        1. olrad Автор
          04.06.2025 22:08

          Не добавил потому, что эксперимент проводился и с PlutoSDR, и с AirSpy, и с SdrPlay ... C RTL-SDR не вышло, не тот диапазон. Конечно все это с githab не скачано. На githab выложен проект который развивается. Кстати WireShark ничего интересного не покажет - это MAC уровень и там шифрование. Здесь уровень PHY.


          1. NutsUnderline
            04.06.2025 22:08

            А это уже раздел статьи под названием "постановка задачи" ;) Было бы странно (и интересно) если бы поверх IEEE802.15.4 что то нестандартное шло. Есть например работа где ключи шифрования Zigbee намониторили.


            1. olrad Автор
              04.06.2025 22:08

              Постановка задачи:

              Производители устройств ZigBee(и аналогичные) утверждают дальность приема 300м (прямая видимость). Верим.

              Более серьезные производители - 30м. Еще больше верим.

              Исследователи этих устройств - 30футов. Безусловно верим.

              Это и есть постановка задачи. Добиться приема при минимальном соотношении сигнал\шум. И лучше это делать с SDR. Что касается "поверх IEEE802.15.4" - стандарт не определяет. Можете передавать что угодно, прописаны только условия MAC доступа к среде.

              И снова - это развивающийся проект. В стандарте 2020 года уже 19 видов модуляции. Возможно есть все прошивки на nrf52840 или аналогичных устройств, здесь выбрана технология SDR.