Cómo demodular una señal AFSK en software

14

Estoy tratando de transmitir datos binarios de un dispositivo a otro a través de un canal de audio (altavoz / micrófono). Utilizo AFSK (Audio Frequency Shift Keying) como en Packet Radio, con y dos frecuencias f m a r k = 1200  Hz y f s p a c e = 2200  Hz . Jugué un poco en Ruby y mi primera implementación simplemente imita un demodulador incoherente clásico, que funciona bien, hasta ahora.1200 BaudiosFmetrounrk=1200 HzFspagunCmi=2200 Hz

El problema es que estoy intentando portar esto a una plataforma móvil donde el rendimiento es una preocupación y mi solución actual es demasiado lenta. He encontrado numerosas formas de demodular AFSK en software:

  • DFT deslizante (FFT)
  • Filtro deslizante de Görtzel
  • Fase de bucle bloqueado
  • No hay paso

¿Cuál sería el camino a seguir? Hay demasiadas opciones para elegir. Estoy seguro de que hay aún más opciones disponibles. ¿Quizás existen soluciones aún mejores que las que mencioné anteriormente? ¿Alguien tiene un código de ejemplos para mí? Estoy preocupado con

  • Rendimiento (debe ejecutarse en la plataforma móvil, por ejemplo, un dispositivo iOS o Android)
  • Estabilidad (debería poder manejar algo de ruido)

Cualquier sugerencia y sugerencias son muy apreciadas!

Patrick Oscity
fuente
3
Creo que es probable que esté vendiendo en corto las capacidades de los dispositivos móviles a los que se dirige. Recuerde que los dispositivos modernos son procesadores multinúcleo con velocidades de reloj superiores a 1 GHz. Procesar una señal de <10 ksps con un demodulador FSK no debería presentar un problema de rendimiento. Pero no debería haber ninguna razón por la cual su enfoque existente (que me parece un filtro de marca / espacio) no debería poder ejecutarse en tiempo real en una plataforma móvil moderna. Incluso un enfoque más sofisticado basado en PLL debería caber cómodamente en su sobre de procesamiento. Perfilizaría un poco su código existente.
Jason R
1
Eche un vistazo a la mía y al DSP de enseñanza a través del estudio práctico de un módem FSK .
Alexey Frunze

Respuestas:

9

Creo que podría obtener el mejor rendimiento en términos de tasa de error de bits del demodulador (BER) con un bucle de fase bloqueada. Sin embargo, necesitas que sea rápido. Creo que su mejor apuesta para un algoritmo rápido que aún funcione razonablemente bien es el cruce por cero.

En una nota al margen, me gustaría sugerir que cambie los 2200 Hz a 2400 Hz. Una implementación ingenua del esquema de 1200/2200 Hz produciría discontinuidades, como se ve alrededor de dos tercios en el gráfico a continuación, donde las transiciones de 2200 Hz a 1200 Hz.

1200 Hz y 2200 Hz

Para minimizar el ancho de banda que está utilizando y evitar discontinuidades que distorsionarán la señal que necesitará para que la fase sea continua. Sin embargo, incluso si hace que la fase del transmisor sea continua, seguirá existiendo el problema de que los símbolos de 2200 Hz no siempre tendrán el mismo número de cruces por cero debido a las diferentes fases. Por lo general, tendrán cuatro cruces por cero, pero a veces tendrán tres. Los símbolos de 1200 Hz, por otro lado, siempre tendrán dos cruces por cero porque la velocidad en baudios se divide uniformemente en la frecuencia FSK.

Puede resolver ambos problemas cambiando los 2200 Hz a 2400 Hz. Entonces, los símbolos siempre comenzarán y terminarán a 0 grados (lo que los convierte automáticamente en fase continua), y siempre tendrán el mismo número de cruces por cero: dos y cuatro.

1200 Hz y 2400 Hz

Jim Clay
fuente
Hola Jim, gracias por tu respuesta detallada. Mi modulador realmente hace CPFSK, por lo tanto, las discontinuidades no son un problema. Deliberadamente elegí 1200 y 2200 Hz porque los armónicos no se superponen tanto como con múltiplos de 1200. ¿O me equivoco aquí? Los PLL suenan muy bien, pero realmente no tengo idea de cómo implementarlos. ¿Conoces alguna buena fuente sobre PLL de software?
Patrick Oscity el
@Patrick No, tiene razón en que 1200 y 2400 Hz tendrán armónicos superpuestos. Sin embargo, en el contexto del cruce por cero, no creo que los armónicos importen. Y no, me temo que no conozco una buena fuente en línea sobre PLL.
Jim Clay
Esto no es correcto. AFSK 1200 sigue a Bell 202, y dice que los tonos deberían ser 1200 y 2200. La discontinuidad nunca debería ocurrir en el lado del transmisor. Echa un vistazo a los moduladores de código abierto AFSK 1200, la modulación se realiza manteniendo un incremento de fase para cada tono: si tono == BAJO, entonces last_phase + = ph_low else last_phase + = ph_high endif; next_sample = sin (última_fase);
vz0
5

Hice un decodificador para AFSK (estándar Bell 202) usando receptores de correlación para 1200 Hz y 2200 Hz, con muy buenos resultados.

pecadocos

La amplitud resultante es bastante independiente de la fase de señal, y la SNR de salida es muy buena.

Juancho
fuente
Esto es exactamente lo que he probado antes y lo que he llamado "demodulador incoherente clásico". Tal vez mi implementación es errónea, pero me temo que sufre desbordamientos de búfer debido al lento procesamiento. ¡Gracias de cualquier manera!
Patrick Oscity el
0

En el caso de RTTY 45.45 baudios, también tendrá símbolos que no son un número entero de muestras, por lo que necesita una función que pueda llamarse cada muestra y luego indicar su valor de retorno cuando ese símbolo haya finalizado. Y necesita un acumulador de fase, que mantiene una cuenta corriente de dónde está la fase de la onda sinusoidal.

Para enviar símbolos cuya longitud no sea un múltiplo entero de la frecuencia de muestreo, necesita esta función ...

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

Para usarlo, genere la siguiente muestra de onda sinusoidal y llame a esta función, luego verifique si el valor de retorno NO es igual a dos. Si no es igual a dos, avance al siguiente símbolo y decida si está enviando una marca de espacio, luego llame a esta función nuevamente dentro del bloque de código que se ejecuta cuando descubrió que el valor de retorno no es igual a dos.

Y aquí está el acumulador de fase del firmware de Rockbox, con un cambio para permitir cambios en la amplitud (el volumen total es 32767, el volumen completo desfasado 180 grados es -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;
}
Brent Fisher
fuente