Як демодулювати сигнал AFSK у програмному забезпеченні


14

Я намагаюся передавати двійкові дані з одного пристрою на інший по звуковому каналу (динамік / мікрофон). Я використовую AFSK (Audio Frequency Shift Keying), як у пакетному радіо, з та двома частотами f m a r k = 1200  Гц та f s p a c e = 2200  Гц . Я трохи розігрувався в Ruby, і моя перша реалізація просто імітує класичний невід'ємний демодулятор, який добре працює, поки що.1200 Бодfмаrк=1200 Гцfсpаcе=2200 Гц

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

  • Розсувний DFT (FFT)
  • Розсувний фільтр Герцеля
  • Фаза заблокована петля
  • Нульовий перехід

Яким був би шлях? Існує занадто багато варіантів на вибір. Я впевнений, що є ще більше варіантів. Можливо, існують навіть кращі рішення, ніж ті, які я назвав вище? Хтось навіть має приклади коду для мене? Мене хвилює

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

Будь-які пропозиції та підказки дуже вдячні!


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

Відповіді:


9

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

Зі сторони, я хочу запропонувати вам змінити 2200 Гц на 2400 Гц. Наївна реалізація схеми 1200/2200 Гц призведе до розривів, як це видно приблизно на дві третини на графіку внизу, де 2200 Гц переходять до 1200 Гц.

1200 Гц і 2200 Гц

Щоб мінімізувати пропускну здатність, яку ви використовуєте, і уникнути розривів, які спотворюватимуть сигнал, вам потрібно зробити фазу безперервною. Навіть якщо ви зробите фазу передавача безперервною, все одно залишатиметься проблема, що символи 2200 Гц не завжди матимуть однакову кількість нульових перетинів через різні фази. Зазвичай вони матимуть чотири нульові переходи, але іноді вони матимуть три. З іншого боку, символи 1200 Гц завжди матимуть два нульові переходи, оскільки швидкість передачі передач рівномірно поділяється на частоту FSK.

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

1200 Гц і 2400 Гц


Гей Джим, дякую за детальну відповідь! Мій модулятор насправді робить CPFSK, тому розриви не є проблемою. Я навмисно вибрав 1200 і 2200 Гц, тому що гармоніки не збігаються так само, як з кратними 1200. Або я тут помиляюся? ПЛЛ звучать чудово, але я насправді не маю уявлення про те, як їх реалізувати. Чи знаєте ви якісь хороші джерела щодо ПЛЛ програмного забезпечення?
Патрік Осіті

@Patrick Ні, ви впевнені, що 1200 і 2400 Гц будуть мати гармоніки, що перекриваються. У контексті нульового перетину, я не думаю, що гармоніка має значення. І ні, я боюсь, що не знаю хорошого інтернет-джерела про ПЛЛ.
Джим Клей

Це неправильно. 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 Гц, з дуже хорошими результатами.

гріхcos

Отримана амплітуда досить незалежна від фази сигналу, і вихід 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.