Как демодулировать сигнал AFSK в программном обеспечении


14

Я пытаюсь передать двоичные данные с одного устройства на другое по аудиоканалу (динамик / микрофон). Я использую AFSK (Audio Frequency Shift Keying), как в Packet Radio, с и двумя частотами и . Я немного поиграл в Ruby, и моя первая реализация просто имитирует классический некогерентный демодулятор, который пока работает отлично.1200 БодемaрКзнак равно1200 Гцеsпaсезнак равно2200 Гц

Проблема в том, что я пытаюсь перенести это на мобильную платформу, где важна производительность, а мое текущее решение слишком медленное. Я нашел множество способов демодуляции AFSK в программном обеспечении:

  • Скользящий DFT (БПФ)
  • Скользящий фильтр Герцеля
  • Фазовая петля
  • Пересечение нуля

Каков будет путь? Есть слишком много вариантов для выбора. Я уверен, что есть еще больше вариантов. Возможно, существуют даже лучшие решения, чем те, которые я назвал выше? У кого-нибудь есть примеры кода для меня? Я обеспокоен

  • Производительность (должна работать на мобильной платформе, скажем, на устройстве iOS или Android)
  • Стабильность (должна быть в состоянии справиться с некоторым шумом)

Любые предложения и советы с благодарностью!


3
Я думаю, что вы, вероятно, недополучаете возможности мобильных устройств, на которые вы ориентируетесь. Помните, что современные устройства - это многоядерные процессоры с тактовой частотой, превышающей 1 ГГц. Обработка сигнала <10 к / с с помощью демодулятора FSK не должна представлять проблемы с производительностью. Но не должно быть никаких причин, по которым ваш существующий подход (который звучит для меня как фильтрация меток / пробелов) не сможет работать в режиме реального времени на современной мобильной платформе. Даже более сложный подход, основанный на PLL, должен удобно вписываться в вашу среду обработки. Я бы немного профилировал ваш существующий код.
Джейсон Р

Ответы:


9

Я думаю, что вы могли бы получить лучшую производительность с точки зрения частоты ошибок по битам демодулятора (BER) с фазовой автоподстройкой частоты. Вам нужно, чтобы это было быстро, хотя. Я думаю, что ваш лучший выбор для быстрого алгоритма, который все еще работает достаточно хорошо, - это пересечение нуля.

Кстати, я хотел бы предложить вам изменить 2200 Гц на 2400 Гц. Наивная реализация схемы 1200/2200 Гц привела бы к разрывам, как видно примерно на две трети на графике ниже, где 2200 Гц переходит к 1200 Гц.

1200 Гц и 2200 Гц

Чтобы минимизировать используемую полосу пропускания и избежать разрывов, которые будут искажать сигнал, вам потребуется сделать фазу непрерывной. Даже если вы сделаете фазу передатчика непрерывной, все равно будет проблема в том, что символы 2200 Гц не всегда будут иметь одинаковое количество пересечений нуля из-за разных фаз. Обычно у них будет четыре пересечения нуля, но иногда у них будет три. С другой стороны, символы 1200 Гц всегда будут иметь два пересечения нуля, поскольку скорость передачи данных равномерно делится на частоту FSK.

Вы можете решить обе эти проблемы, изменив 2200 Гц на 2400 Гц. Тогда символы всегда будут начинаться и заканчиваться при 0 градусах (таким образом, автоматически делая их непрерывными по фазе), и они всегда будут иметь одинаковое количество пересечений нуля - два и четыре.

1200 Гц и 2400 Гц


Привет, Джим, спасибо за подробный ответ! Мой модулятор на самом деле делает CPFSK, поэтому разрывы не проблема. Я намеренно выбрал 1200 и 2200 Гц, потому что гармоники не перекрываются так сильно, как с кратными 1200. Или я здесь не прав? PLLs звучат великолепно, но я действительно не представляю, как их реализовать. Вы знаете какие-нибудь хорошие источники о программных ФАПЧ?
Патрик Осци

@ Патрик Нет, вы правы, что 1200 и 2400 Гц будут иметь перекрывающиеся гармоники. В контексте пересечения нуля я не думаю, что гармоники имеют значение. И нет, боюсь, я не знаю хорошего онлайн-источника о PLL.
Джим Клэй

Это не правильно. AFSK 1200 следует за Bell 202 и говорит, что тоны должны быть 1200 и 2200. Прерывание никогда не должно происходить на стороне передатчика. Проверьте модуляторы AFSK 1200 с открытым исходным кодом, модуляция выполняется путем отслеживания приращения фазы для каждого тона: если тон == НИЗКИЙ, то last_phase + = ph_low else last_phase + = ph_high endif; next_sample = sin (last_phase);
vz0

5

Я сделал декодер для AFSK (стандарт Bell 202), используя корреляционные приемники для 1200 Гц и 2200 Гц, с очень хорошими результатами.

грехсоз

Результирующая амплитуда совершенно не зависит от фазы сигнала, а выходное SNR очень хорошее.


Это именно то, что я пробовал раньше, и то, что я назвал «классический некогерентный демодулятор». Может быть, моя реализация ошибочна, но я боюсь, что она страдает от переполнения буфера из-за медленной обработки. Спасибо, в любом случае!
Патрик Осцити

0

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

Чтобы отправить символы, длина которых не кратна частоте дискретизации, вам нужна эта функция ...

int millisecondTimer(double milliseconds, double samplerate, int resettime)
{

    static int fracsample=0;
    static int counter=0;
    static int retvalue=0;
    static int first=1;
    static double oldmilliseconds=1.0;
    static int whole_samples=0;
    static int samerror=32768;
    if(resettime==1)
    {
        samerror=0;
        counter=0;
        retvalue=1;
        first=1;
    }
    if(first==1 || milliseconds !=oldmilliseconds)
    {
        double samplesneeded=1;
        double wholesamples=0;
        samplesneeded=(samplerate) * (milliseconds /1000.0);
        samerror=(modf(samplesneeded, &wholesamples)) * 32768.0;
        whole_samples=wholesamples;
        first=0;
    }

    if(counter<=whole_samples)
    {
        retvalue=2;
        counter++;
    }
    else
    {
        counter-=whole_samples;
        retvalue=1;
        fracsample+=samerror;
        oldmilliseconds=milliseconds;
        if(fracsample>=32768)
        {
            fracsample-=32768;
            counter--;
        }

    }
    return retvalue;
}

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

А вот фазовый аккумулятор из прошивки Rockbox, с возможностью изменения амплитуды (полная громкость равна 32767, полная громкость 180 градусов по фазе равна -32768).

signed short lerpsin(float frequency,signed short amplitude,unsigned long samplerate)
{
    /* 128 sixteen bit sine samples + guard point */
    static unsigned long phase=0;
    unsigned int pos =0;
    unsigned short frac=0;
    static unsigned long step=0;
    static float old_frequency=0;
    signed short diff=0;
    static const signed short sinetab[129] =
    {
        0,   1607,   3211,   4807,   6392,   7961,   9511,  11038,
        12539,  14009,  15446,  16845,  18204,  19519,  20787,  22004,
        23169,  24278,  25329,  26318,  27244,  28105,  28897,  29621,
        30272,  30851,  31356,  31785,  32137,  32412,  32609,  32727,
        32767,  32727,  32609,  32412,  32137,  31785,  31356,  30851,
        30272,  29621,  28897,  28105,  27244,  26318,  25329,  24278,
        23169,  22004,  20787,  19519,  18204,  16845,  15446,  14009,
        12539,  11038,   9511,   7961,   6392,   4807,   3211,   1607,
        0,  -1607,  -3211,  -4807,  -6392,  -7961,  -9511, -11038,
        -12539, -14009, -15446, -16845, -18204, -19519, -20787, -22004,
        -23169, -24278, -25329, -26318, -27244, -28105, -28897, -29621,
        -30272, -30851, -31356, -31785, -32137, -32412, -32609, -32727,
        -32767, -32727, -32609, -32412, -32137, -31785, -31356, -30851,
        -30272, -29621, -28897, -28105, -27244, -26318, -25329, -24278,
        -23169, -22004, -20787, -19519, -18204, -16845, -15446, -14009,
        -12539, -11038, -9511,   -7961,  -6392,  -4807,  -3211,  -1607,
        0,
    };
    if(frequency!=old_frequency)
    {
        step = 0x100000000ull*frequency / samplerate;
    }
    phase+=step;
    pos = phase >> 25;
    frac = (phase & 0x01ffffff) >> 9;
    diff = sinetab[pos + 1] - sinetab[pos];
    old_frequency=frequency;
    return ((-((sinetab[pos] + (frac*diff >> 16)))) * amplitude) >> 15;
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.