Jak demodulować sygnał AFSK w oprogramowaniu


14

Próbuję przesyłać dane binarne z jednego urządzenia do drugiego za pośrednictwem kanału audio (głośnik / mikrofon). Używam AFSK (Audio Frequency Shift Keying) jak w pakiecie Radio, z i dwiema częstotliwościami f m a r k = 1200  Hz i f s p a c e = 2200  Hz . Grałem trochę w Rubim, a moja pierwsza implementacja po prostu imituje klasyczny niespójny demodulator, który do tej pory działa dobrze.1200 Baudfamzark=1200 Hzfaspzadomi=2200 Hz

Problem polega na tym, że próbuję przenieść to na platformę mobilną, na której wydajność jest problemem, a moje obecne rozwiązanie jest zbyt wolne. Znalazłem wiele sposobów demodulacji AFSK w oprogramowaniu:

  • Przesuwne DFT (FFT)
  • Przesuwny filtr Görtzel
  • Pętla synchronizacji fazowej
  • Zero Crossing

Jaka byłaby droga? Jest po prostu zbyt wiele opcji do wyboru. Jestem pewien, że dostępnych jest jeszcze więcej opcji. Być może istnieją jeszcze lepsze rozwiązania niż te, które wymieniłem powyżej? Czy ktoś ma dla mnie przykłady kodu? Jestem zaniepokojony

  • Wydajność (powinna działać na platformie mobilnej, powiedzmy na urządzeniu z systemem iOS lub Android)
  • Stabilność (powinna być w stanie obsłużyć trochę hałasu)

Wszelkie sugestie i wskazówki są mile widziane!


3
Myślę, że prawdopodobnie sprzedajesz mało możliwości urządzeń mobilnych, na które celujesz. Pamiętaj, że nowoczesne urządzenia to procesory wielordzeniowe o taktowaniu przekraczającym 1 GHz. Przetwarzanie sygnału <10 ksps za pomocą demodulatora FSK nie powinno stanowić problemu z wydajnością. Ale nie powinno być żadnego powodu, dla którego twoje obecne podejście (które wydaje mi się podobne do filtrowania znaków / przestrzeni) nie powinno być możliwe w czasie rzeczywistym na nowoczesnej platformie mobilnej. Nawet bardziej wyrafinowane podejście oparte na PLL powinno wygodnie mieścić się w twojej kopercie przetwarzania. Profilowałbym trochę twój istniejący kod.
Jason R

Odpowiedzi:


9

Myślę, że można uzyskać najlepszą wydajność pod względem wskaźnika błędów bitów demodulatora (BER) z pętlą fazową. Ale musisz być szybki. Myślę, że najlepszym rozwiązaniem dla szybkiego algorytmu, który nadal działa dość dobrze, jest przejście przez zero.

Na marginesie, chciałbym zasugerować zmianę 2200 Hz na 2400 Hz. Naiwna implementacja schematu 1200/2200 Hz przyniosłaby nieciągłości, jak widać około dwóch trzecich na wykresie poniżej, gdzie 2200 Hz przechodzi w 1200 Hz.

1200 Hz i 2200 Hz

Aby zminimalizować używane pasmo i uniknąć nieciągłości, które zniekształcają sygnał, konieczne jest, aby faza była ciągła. Nawet jeśli sprawisz, że faza nadajnika będzie ciągła, nadal będzie problem, że symbole 2200 Hz nie zawsze będą miały taką samą liczbę przejść przez zero z powodu różnych faz. Zwykle będą miały cztery przejścia przez zero, ale czasami będą miały trzy. Z drugiej strony symbole 1200 Hz zawsze będą miały dwa przejścia przez zero, ponieważ szybkość transmisji dzieli się równomiernie na częstotliwość FSK.

Możesz rozwiązać oba te problemy, zmieniając 2200 Hz na 2400 Hz. Wówczas symbole zawsze zaczynają się i kończą na 0 stopniach (co automatycznie powoduje, że faza jest ciągła) i zawsze będą miały tę samą liczbę przejść przez zero - dwa i cztery.

1200 Hz i 2400 Hz


Hej Jim, dzięki za szczegółową odpowiedź! Mój modulator faktycznie robi CPFSK, dlatego nieciągłości nie stanowią problemu. Celowo wybrałem 1200 i 2200 Hz, ponieważ harmoniczne nie pokrywają się tak bardzo, jak w przypadku wielokrotności 1200. A może się tu mylę? PLL brzmią świetnie, ale tak naprawdę nie mam pojęcia, jak je zaimplementować. Czy znasz jakieś dobre źródła o oprogramowaniu PLL?
Patrick Oscity

@ Patryk Nie, masz rację, że 1200 i 2400 Hz będą miały nakładające się harmoniczne. Jednak w kontekście przejścia przez zero nie sądzę, aby harmoniczne miały znaczenie. I nie, obawiam się, że nie znam dobrego źródła internetowego o PLL.
Jim Clay,

To nie jest poprawne. AFSK 1200 podąża za Bell 202 i mówi, że tony powinny wynosić 1200 i 2200. Nieciągłość nigdy nie powinna mieć miejsca po stronie nadajnika. Sprawdź modulatory AFSK 1200 typu open source, modulacja odbywa się poprzez śledzenie przyrostu fazy dla każdego tonu: jeśli ton == NISKI, to ostatnia faza + = ph_low inne last_phase + = ph_high endif; next_sample = sin (last_phase);
vz0,

5

Zrobiłem dekoder dla AFSK (standard Bell 202), używając odbiorników korelacyjnych dla 1200 Hz i 2200 Hz, z bardzo dobrymi wynikami.

grzechsałata

Otrzymana amplituda jest całkowicie niezależna od fazy sygnału, a wyjściowy SNR jest bardzo dobry.


Właśnie tego wcześniej próbowałem i to, co nazwałem „klasycznym niespójnym demodulatorem”. Być może moja implementacja jest błędna, ale obawiam się, że cierpi z powodu przepełnienia bufora z powodu wolnego przetwarzania. W każdym razie dzięki!
Patrick Oscity

0

W przypadku RTTY 45.45 bodów, będziesz mieć również symbole, które nie są liczbami całkowitymi próbek, więc potrzebujesz funkcji, którą można wywołać dla każdej próbki, a następnie zasygnalizować jej wartość zwracaną, gdy ten symbol się skończy. Potrzebujesz akumulatora fazowego, który utrzymuje bieżącą sumę w miejscu, w którym znajduje się faza fali sinusoidalnej.

Aby wysłać symbole, których długość nie jest całkowitą wielokrotnością częstotliwości próbkowania, potrzebujesz tej funkcji ...

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;
}

Aby go użyć, wygeneruj następną próbkę fali sinusoidalnej i wywołaj tę funkcję, a następnie sprawdź, czy wartość zwracana NIE jest równa dwa. Jeśli nie jest równy dwa, przejdź do następnego symbolu i zdecyduj, czy wysyłasz znak spacji, a następnie wywołaj tę funkcję ponownie w bloku kodu, który jest wykonywany, gdy dowiesz się, że zwracana wartość nie jest równa dwa.

A oto akumulator fazowy z oprogramowania Rockbox, ze zmianą pozwalającą na zmiany amplitudy (pełna objętość to 32767, 180 stopni poza fazą to pełna głośność -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;
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.