Come demodulare un segnale AFSK nel software


14

Sto cercando di trasmettere dati binari da un dispositivo all'altro tramite un canale audio (altoparlante / microfono). Uso AFSK (Audio Frequency Shift Keying) come in Packet Radio, con e due frequenze f m a r k = 1200  Hz e f s p a c e = 2200  Hz . Ho giocato un po 'su Ruby e la mia prima implementazione imita semplicemente un classico demodulatore incoerente, che finora funziona bene.1200 Baudfmark=1200 Hzfspace=2200 Hz

Il problema è che sto provando a portarlo su una piattaforma mobile in cui le prestazioni sono un problema e la mia soluzione attuale è troppo lenta. Ho trovato numerosi modi per demodulare AFSK nel software:

  • DFT scorrevole (FFT)
  • Filtro Görtzel scorrevole
  • Circuito di aggancio fase
  • Zero Crossing

Quale sarebbe la strada da percorrere? Ci sono troppe opzioni tra cui scegliere. Sono sicuro che ci sono ancora più opzioni disponibili. Forse esistono soluzioni ancora migliori di quelle che ho nominato sopra? Qualcuno ha anche un esempio di codice per me? Mi preoccupo

  • Prestazioni (dovrebbe funzionare su piattaforma mobile, diciamo un dispositivo iOS o Android)
  • Stabilità (dovrebbe essere in grado di gestire un po 'di rumore)

Eventuali suggerimenti e suggerimenti sono molto apprezzati!


3
Penso che probabilmente venderai a corto di funzionalità dei dispositivi mobili a cui ti rivolgi. Ricorda che i dispositivi moderni sono processori multicore con velocità di clock superiori a 1 GHz. L'elaborazione di un segnale <10 ksps con un demodulatore FSK non dovrebbe presentare un problema di prestazioni. Ma non dovrebbe esserci alcun motivo per cui il tuo approccio esistente (che suona per me come mark / space filtering) non dovrebbe essere in grado di funzionare in tempo reale su una moderna piattaforma mobile. Anche un approccio più sofisticato basato su PLL dovrebbe adattarsi comodamente alla tua busta di elaborazione. Profilerei un po 'il tuo codice esistente.
Jason R,

Risposte:


9

Penso che potresti ottenere le migliori prestazioni in termini di bit-error rate (BER) del demodulatore con un loop a fase bloccata. Tuttavia, è necessario che sia veloce. Penso che la tua scommessa migliore per un algoritmo veloce che funzioni ancora ragionevolmente bene sia l'attraversamento zero.

A proposito, vorrei suggerire di cambiare i 2200 Hz in 2400 Hz. Un'implementazione ingenua dello schema 1200/2200 Hz produrrebbe discontinuità, come visto circa due terzi nel diagramma sottostante, dove i 2200 Hz passano a 1200 Hz.

1200 Hz e 2200 Hz

Al fine di ridurre al minimo la larghezza di banda in uso ed evitare discontinuità che distorcono il segnale, è necessario rendere la fase continua. Anche se si rende continua la fase del trasmettitore, ci sarà comunque il problema che i simboli a 2200 Hz non avranno sempre lo stesso numero di passaggi per zero a causa delle diverse fasi. Di solito avranno quattro passaggi a zero, ma a volte ne avranno tre. D'altra parte, i simboli a 1200 Hz avranno sempre due incroci di zero perché la velocità di trasmissione si divide uniformemente nella frequenza FSK.

È possibile risolvere entrambi questi problemi modificando 2200 Hz in 2400 Hz. Quindi i simboli inizieranno e finiranno sempre a 0 gradi (rendendoli così automaticamente continui di fase) e avranno sempre lo stesso numero di passaggi per zero - due e quattro.

1200 Hz e 2400 Hz


Ehi Jim, grazie per la tua risposta dettagliata! Il mio modulatore in realtà fa CPFSK, quindi le discontinuità non sono un problema. Ho scelto deliberatamente 1200 e 2200 Hz perché le armoniche non si sovrappongono tanto quanto con i multipli di 1200. O sbaglio qui? I PLL suonano alla grande, ma non ho davvero idea di come implementarli. Conosci qualche buona fonte sui PLL del software?
Patrick Oscity,

@Patrick No, hai ragione nel dire che 1200 e 2400 Hz avranno armoniche sovrapposte. Nel contesto del passaggio per lo zero, tuttavia, non credo che le armoniche contino. E no, temo di non conoscere una buona fonte online di PLL.
Jim Clay,

Questo non è corretto AFSK 1200 segue Bell 202, e dice che i toni dovrebbero essere 1200 e 2200. La discontinuità non dovrebbe mai verificarsi sul lato del trasmettitore. Dai un'occhiata ai modulatori AFSK 1200 open source, la modulazione viene eseguita tenendo traccia di un incremento di fase per ogni tono: se tono == BASSO quindi last_phase + = ph_low else last_phase + = ph_high endif; next_sample = sin (last_phase);
vz0

5

Ho realizzato un decodificatore per AFSK (standard Bell 202) utilizzando ricevitori di correlazione per 1200 Hz e 2200 Hz, con risultati molto buoni.

peccatocos

L'ampiezza risultante è abbastanza indipendente dalla fase del segnale e l'uscita SNR è molto buona.


Questo è esattamente quello che ho provato prima e quello che ho chiamato il "classico demodulatore incoerente". Forse la mia implementazione è errata, ma temo che soffra di overflow del buffer a causa dell'elaborazione lenta. Grazie comunque!
Patrick Oscity,

0

Nel caso di RTTY 45.45 baud, avrai anche simboli che non sono un numero intero di campioni, quindi hai bisogno di una funzione che può essere chiamata ogni campione e quindi segnalare il suo valore di ritorno quando quel simbolo è terminato. E hai bisogno di un accumulatore di fase, che mantiene un conteggio corrente su dove si trova la fase dell'onda sinusoidale.

Per inviare simboli la cui lunghezza non è un multiplo intero della frequenza di campionamento è necessaria questa funzione ...

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

Per usarlo, genera il prossimo campione di onda sinusoidale e chiama questa funzione, quindi controlla se il valore di ritorno NON è uguale a due. Se non è uguale a due, passa al simbolo successivo e decidi se stai inviando un segno di spazio, quindi chiama di nuovo questa funzione all'interno del blocco di codice che viene eseguito quando scopri che il valore restituito non è uguale a due.

Ed ecco l'accumulatore di fase dal firmware Rockbox, con una modifica per consentire cambiamenti di ampiezza (il volume completo è 32767, il volume pieno sfasato di 180 gradi è -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;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.