Comment démoduler un signal AFSK dans un logiciel


14

J'essaie de transmettre des données binaires d'un appareil à un autre sur un canal audio (haut-parleur / micro). J'utilise AFSK (Audio Frequency Shift Keying) comme dans Packet Radio, avec et deux fréquences f m a r k = 1200  Hz et f s p a c e = 2200  Hz . J'ai joué un peu dans Ruby et ma première implémentation imite simplement un démodulateur incohérent classique, qui fonctionne bien jusqu'à présent.1200 BaudFmunerk=1200 HzFspunece=2200 Hz

Le problème est que j'essaie de porter cela sur une plate-forme mobile où les performances sont un problème et ma solution actuelle est trop lente. J'ai trouvé de nombreuses façons de démoduler AFSK dans le logiciel:

  • DFT coulissant (FFT)
  • Filtre Görtzel coulissant
  • Boucle verrouillée en phase
  • Passage à zéro

Quelle serait la voie à suivre? Il y a juste trop d'options à choisir. Je suis sûr qu'il y a encore plus d'options disponibles. Peut-être existe-t-il des solutions encore meilleures que celles que j'ai nommées ci-dessus? Quelqu'un a-t-il même des exemples de code pour moi? Je suis préoccupé par

  • Performance (devrait fonctionner sur une plate-forme mobile, par exemple un appareil iOS ou Android)
  • Stabilité (devrait être capable de gérer un peu de bruit)

Toutes les suggestions et astuces sont grandement appréciées!


3
Je pense que vous vendez probablement à découvert les capacités des appareils mobiles que vous ciblez. N'oubliez pas que les appareils modernes sont des processeurs multicœurs avec des vitesses d'horloge supérieures à 1 GHz. Le traitement d'un signal <10 ksps avec un démodulateur FSK ne devrait pas poser de problème de performances. Mais il ne devrait y avoir aucune raison pour que votre approche existante (qui me semble être un filtrage des marques / espaces) ne puisse pas s'exécuter en temps réel sur une plate-forme mobile moderne. Même une approche PLL plus sophistiquée devrait s'intégrer confortablement dans votre enveloppe de traitement. Je profilerais un peu votre code existant.
Jason R

Réponses:


9

Je pense que vous pourriez obtenir les meilleures performances en termes de taux d'erreur binaire du démodulateur (BER) avec une boucle à verrouillage de phase. Cependant, vous en avez besoin pour être rapide. Je pense que votre meilleur pari pour un algorithme rapide qui fonctionne encore raisonnablement bien est le passage par zéro.

Sur une note latérale, je voudrais suggérer que vous changez le 2200 Hz en 2400 Hz. Une mise en œuvre naïve du schéma 1200/2200 Hz produirait des discontinuités, comme on le voit environ aux deux tiers dans le graphique ci-dessous, où le 2200 Hz passe à 1200 Hz.

1200 Hz et 2200 Hz

Afin de minimiser la bande passante que vous utilisez et d'éviter les discontinuités qui fausseront le signal dont vous aurez besoin pour rendre la phase continue. Même si vous rendez la phase de l'émetteur continue, cependant, il y aura toujours le problème que les symboles 2200 Hz n'auront pas toujours le même nombre de passages par zéro en raison des différentes phases. Habituellement, ils auront quatre passages à zéro, mais parfois ils en auront trois. Les symboles 1200 Hz, d'autre part, auront toujours deux passages à zéro parce que le débit en bauds se divise également en fréquence FSK.

Vous pouvez résoudre ces deux problèmes en modifiant la fréquence 2200 Hz à 2400 Hz. Ensuite, les symboles commenceront et se termineront toujours à 0 degré (ce qui les rendra automatiquement en phase continue), et ils auront toujours le même nombre de passages à zéro - deux et quatre.

1200 Hz et 2400 Hz


Salut Jim, merci pour ta réponse détaillée! Mon modulateur fonctionne en fait CPFSK, donc les discontinuités ne sont pas un problème. J'ai délibérément choisi 1200 et 2200 Hz car les harmoniques ne se chevauchent pas autant qu'avec des multiples de 1200. Ou ai-je tort ici? Les PLL sonnent bien, mais je n'ai vraiment aucune idée de la façon de les implémenter. Connaissez-vous de bonnes sources de PLL logicielles?
Patrick Oscity

@Patrick Non, vous avez raison de dire que 1200 et 2400 Hz auront des harmoniques qui se chevauchent. Dans le contexte du passage par zéro, cependant, je ne pense pas que les harmoniques importent. Et non, je crains de ne pas connaître de bonne source en ligne sur les PLL.
Jim Clay

Ce n'est pas correct. AFSK 1200 suit Bell 202, et il indique que les tonalités devraient être 1200 et 2200. La discontinuité ne devrait jamais se produire du côté de l'émetteur. Découvrez les modulateurs open source AFSK 1200, la modulation se fait en gardant une trace d'un incrément de phase pour chaque ton: si ton == LOW alors last_phase + = ph_low else last_phase + = ph_high endif; next_sample = sin (last_phase);
vz0

5

J'ai fait un décodeur pour AFSK (norme Bell 202) en utilisant des récepteurs de corrélation pour 1200 Hz et 2200 Hz, avec de très bons résultats.

péché et cos indépendamment, puis en intégrant chacun, et en calculant la valeur absolue (carrée).

L'amplitude résultante est assez indépendante de la phase du signal et le SNR de sortie est très bon.


C'est exactement ce que j'ai essayé auparavant et ce que j'ai appelé «démodulateur incohérent classique». Peut-être que mon implémentation est erronée, mais je crains qu'elle souffre de débordements de tampon en raison d'un traitement lent. Merci quand même!
Patrick Oscity

0

Dans le cas de RTTY 45,45 bauds, vous aurez également des symboles qui ne sont pas un nombre entier d'échantillons, vous avez donc besoin d'une fonction qui peut être appelée chaque échantillon, puis signaler sa valeur de retour lorsque ce symbole est terminé. Et vous avez besoin d'un accumulateur de phase, qui garde un décompte courant sur la position de la phase de l'onde sinusoïdale.

Pour envoyer des symboles dont la longueur n'est pas un multiple entier de la fréquence d'échantillonnage, vous avez besoin de cette fonction ...

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

Pour l'utiliser, générez le prochain échantillon d'onde sinusoïdale et appelez cette fonction, puis vérifiez si la valeur de retour n'est PAS égale à deux. S'il n'est pas égal à deux, passez au symbole suivant et décidez si vous envoyez une marque d'espace, puis appelez à nouveau cette fonction à l'intérieur du bloc de code qui s'exécute lorsque vous découvrez que la valeur de retour n'est pas égale à deux.

Et voici l'accumulateur de phase du firmware Rockbox, avec un changement pour permettre des changements d'amplitude (le volume complet est de 32767, 180 degrés hors phase le volume complet est de -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;
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.