So demodulieren Sie ein AFSK-Signal in der Software


14

Ich versuche, Binärdaten über einen Audiokanal (Lautsprecher / Mikrofon) von einem Gerät zum anderen zu übertragen. Ich verwende AFSK (Audio Frequency Shift Keying) wie bei Packet Radio, mit und zwei Frequenzen und . Ich habe ein bisschen in Ruby herumgespielt und meine erste Implementierung imitiert einfach einen klassischen inkohärenten Demodulator, der bis jetzt gut funktioniert.1200 Baudfmeinrk=1200 Hzfspeince=2200 Hz

Das Problem ist, ich versuche, dies auf eine mobile Plattform zu portieren, bei der die Leistung ein Problem darstellt und meine aktuelle Lösung zu langsam ist. Ich habe zahlreiche Möglichkeiten gefunden, AFSK in Software zu demodulieren:

  • Gleitende DFT (FFT)
  • Schiebefilter Görtzel
  • Phasenregelkreis
  • Nullstelle

Was wäre der richtige Weg? Es stehen einfach zu viele Optionen zur Auswahl. Ich bin sicher, dass es noch mehr Optionen gibt. Vielleicht gibt es noch bessere Lösungen als die oben genannten? Hat jemand überhaupt ein Codebeispiel für mich? Ich mache mir Sorgen um

  • Leistung (sollte auf einer mobilen Plattform ausgeführt werden, z. B. einem iOS- oder Android-Gerät)
  • Stabilität (sollte etwas Lärm vertragen)

Anregungen und Hinweise werden sehr geschätzt!


3
Ich denke, Sie verkaufen wahrscheinlich die Fähigkeiten der Mobilgeräte, auf die Sie abzielen, zu kurz. Denken Sie daran, dass moderne Geräte Multicore-Prozessoren mit Taktraten über 1 GHz sind. Die Verarbeitung eines Signals mit <10 ksps mit einem FSK-Demodulator sollte kein Leistungsproblem darstellen. Es sollte jedoch keinen Grund geben, warum Ihr bestehender Ansatz (der für mich nach Mark / Space-Filterung klingt) auf einer modernen mobilen Plattform nicht in Echtzeit ausgeführt werden kann. Selbst ein ausgefeilter PLL-basierter Ansatz sollte problemlos in Ihren Verarbeitungsumschlag passen. Ich würde Ihren vorhandenen Code ein wenig profilieren.
Jason R

Antworten:


9

Ich denke, dass Sie die beste Leistung in Bezug auf die Demodulator-Bitfehlerrate (BER) mit einer Phasenregelschleife erzielen können. Sie müssen jedoch schnell sein. Ich denke, Ihre beste Wahl für einen schnellen Algorithmus, der noch einigermaßen gut funktioniert, ist der Nulldurchgang.

Als Randnotiz möchte ich vorschlagen, dass Sie die 2200 Hz in 2400 Hz ändern. Eine naive Implementierung des 1200/2200-Hz-Schemas würde Diskontinuitäten ergeben, wie etwa zwei Drittel in der Darstellung unten zeigen, wo die 2200-Hz auf 1200-Hz übergehen.

1200 Hz und 2200 Hz

Um die Bandbreite, die Sie verwenden, zu minimieren und Diskontinuitäten zu vermeiden, die das Signal verzerren, müssen Sie die Phase kontinuierlich machen. Selbst wenn Sie die Sendephase kontinuierlich machen, wird es immer noch das Problem geben, dass die 2200-Hz-Symbole aufgrund der unterschiedlichen Phasen nicht immer die gleiche Anzahl von Nulldurchgängen aufweisen. Normalerweise haben sie vier Nulldurchgänge, aber manchmal haben sie drei. Die 1200-Hz-Symbole haben dagegen immer zwei Nulldurchgänge, da sich die Baudrate gleichmäßig in die FSK-Frequenz aufteilt.

Sie können beide Probleme lösen, indem Sie 2200 Hz auf 2400 Hz ändern. Dann beginnen und enden die Symbole immer bei 0 Grad (wodurch sie automatisch phasenkontinuierlich werden) und sie haben immer die gleiche Anzahl von Nulldurchgängen - zwei und vier.

1200 Hz und 2400 Hz


Hey Jim, danke für deine ausführliche Antwort! Mein Modulator führt tatsächlich CPFSK durch, daher sind Diskontinuitäten kein Problem. Ich habe absichtlich 1200 und 2200 Hz gewählt, weil sich die Harmonischen nicht so sehr überlappen wie bei Vielfachen von 1200. Oder irre ich mich hier? PLLs klingen toll, aber ich habe wirklich keine Ahnung, wie man sie implementiert. Kennen Sie zufällig gute Quellen zu Software-PLLs?
Patrick Oscity

@Patrick Nein, Sie haben Recht, dass 1200 und 2400 Hz überlappende Harmonische haben. Im Zusammenhang mit dem Nulldurchgang denke ich jedoch nicht, dass die Harmonischen eine Rolle spielen. Und nein, ich fürchte, ich kenne keine gute Online-Quelle für PLLs.
Jim Clay

Das ist nicht richtig. AFSK 1200 folgt Bell 202, und es heißt, die Töne sollten 1200 und 2200 sein. Die Diskontinuität sollte auf der Senderseite niemals auftreten. Schauen Sie sich Open-Source-AFSK 1200-Modulatoren an, die Modulation erfolgt durch Verfolgen eines Phaseninkrements für jeden Ton: Wenn Ton == LOW, dann last_phase + = ph_low, sonst last_phase + = ph_high endif; next_sample = sin (last_phase);
vz0

5

Ich habe einen Decoder für AFSK (Bell 202 Standard) mit Korrelationsempfängern für 1200 Hz und 2200 Hz hergestellt, mit sehr guten Ergebnissen.

Da die Phase des Signals während eines Symbols unbekannt ist, besteht eine Lösung darin, im komplexen Bereich zu arbeiten: anstatt mit reellen Sinuskurven zu multiplizieren, multiplizieren Sie mit komplexen Exponentialen. Dies bedeutet, dass Sie unabhängig mit und multiplizieren , diese dann integrieren und den absoluten (quadratischen) Wert berechnen.Sündecos

Die resultierende Amplitude ist völlig unabhängig von der Signalphase und das Ausgangs-SNR ist sehr gut.


Dies ist genau das, was ich zuvor ausprobiert und als „klassischer inkohärenter Demodulator“ bezeichnet habe. Vielleicht ist meine Implementierung fehlerhaft, aber ich befürchte, dass es aufgrund der langsamen Verarbeitung zu Pufferüberläufen kommt. Danke trotzdem!
Patrick Oscity

0

Im Fall von RTTY 45.45 Baud haben Sie auch Symbole, die keine ganzzahlige Anzahl von Samples sind. Sie benötigen daher eine Funktion, die jedes Sample aufrufen und dann in ihrem Rückgabewert signalisieren kann, wenn dieses Symbol beendet ist. Und Sie brauchen einen Phasenakkumulator, der die Phase der Sinuswelle genau festhält.

Um Symbole zu senden, deren Länge kein ganzzahliges Vielfaches der Abtastrate ist, benötigen Sie diese Funktion ...

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

Um es zu verwenden, generieren Sie das nächste Sample der Sinuswelle und rufen Sie diese Funktion auf. Überprüfen Sie dann, ob der Rückgabewert NICHT gleich zwei ist. Wenn es nicht gleich zwei ist, fahren Sie mit dem nächsten Symbol fort und entscheiden Sie, ob Sie eine Leerzeichenmarke senden. Rufen Sie dann diese Funktion innerhalb des Codeblocks erneut auf, der ausgeführt wird, wenn Sie festgestellt haben, dass der Rückgabewert ungleich zwei ist.

Und hier ist der Phasenspeicher aus der Rockbox-Firmware mit einer Änderung, um Änderungen in der Amplitude zu ermöglichen (die volle Lautstärke beträgt 32767, die volle Lautstärke ist um 180 Grad phasenverschoben -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;
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.