Como desmodular um sinal AFSK no software

14

Estou tentando transmitir dados binários de um dispositivo para outro através de um canal de áudio (alto-falante / microfone). Eu uso AFSK (Audio Frequency Shift Keying) como em Packet Radio, com e duas freqüências e . Eu brinquei um pouco no Ruby e minha primeira implementação simplesmente imita um demodulador incoerente clássico, que funciona bem até agora.1200 Baudfmark=1200 Hzfspace=2200 Hz

O problema é que estou tentando portar isso para uma plataforma móvel em que o desempenho é uma preocupação e minha solução atual é muito lenta. Encontrei várias maneiras de desmodular o AFSK no software:

  • DFT deslizante (FFT)
  • Filtro de Görtzel deslizante
  • Loop bloqueado de fase
  • Zero Crossing

Qual seria o caminho a seguir? Existem muitas opções para escolher. Tenho certeza de que existem ainda mais opções disponíveis. Talvez existam soluções ainda melhores do que as que eu citei acima? Alguém tem algum exemplo de código para mim? Estou preocupado com

  • Desempenho (deve ser executado na plataforma móvel, por exemplo, um dispositivo iOS ou Android)
  • Estabilidade (deve ser capaz de lidar com algum ruído)

Todas as sugestões e sugestões são muito apreciadas!

Patrick Oscity
fonte
3
Acho que você provavelmente está vendendo a descoberto os recursos dos dispositivos móveis que está segmentando. Lembre-se de que os dispositivos modernos são processadores multicore com velocidades de clock superiores a 1 GHz. O processamento de um sinal <10 ksps com um demodulador FSK não deve apresentar um problema de desempenho. Mas não deve haver motivo para que sua abordagem existente (que me parece uma filtragem de marca / espaço) não possa ser executada em tempo real em uma plataforma móvel moderna. Mesmo uma abordagem mais sofisticada baseada em PLL deve caber confortavelmente no seu envelope de processamento. Eu analisaria seu código existente um pouco.
Jason R

Respostas:

9

Eu acho que você poderia obter o melhor desempenho em termos de taxa de erro de bits do demodulador (BER) com um loop de fase bloqueada. Você precisa ser rápido, no entanto. Eu acho que sua melhor aposta para um algoritmo rápido que ainda funciona razoavelmente bem é o cruzamento de zero.

Em uma nota lateral, eu gostaria de sugerir que você altere os 2200 Hz para 2400 Hz. Uma implementação ingênua do esquema de 1200/2200 Hz produziria descontinuidades, como visto cerca de dois terços no gráfico abaixo, onde os 2200 Hz passam para 1200 Hz.

1200 Hz e 2200 Hz

Para minimizar a largura de banda usada e evitar descontinuidades que distorcem o sinal, você precisará tornar a fase contínua. Mesmo se você tornar a fase do transmissor contínua, ainda haverá o problema de que os símbolos de 2200 Hz nem sempre terão o mesmo número de cruzamentos de zero devido às diferentes fases. Geralmente eles terão quatro cruzamentos nulos, mas às vezes eles terão três. Os símbolos de 1200 Hz, por outro lado, sempre terão duas passagens de zero, porque a taxa de transmissão se divide uniformemente na frequência FSK.

Você pode resolver esses dois problemas alterando os 2200 Hz para 2400 Hz. Então os símbolos sempre começam e terminam em 0 graus (tornando-os automaticamente contínuos de fase) e sempre terão o mesmo número de cruzamentos zero - dois e quatro.

1200 Hz e 2400 Hz

Jim Clay
fonte
Olá Jim, obrigado pela sua resposta detalhada! Meu modulador realmente faz o CPFSK, portanto, as descontinuidades não são um problema. Escolhi deliberadamente 1200 e 2200 Hz porque os harmônicos não se sobrepõem tanto quanto os múltiplos de 1200. Ou estou errado aqui? Os PLLs parecem ótimos, mas realmente não tenho idéia de como implementá-los. Você conhece boas fontes sobre PLLs de software?
Patrick Oscity
@ Patrick Não, você está certo de que 1200 e 2400 Hz terão harmônicos sobrepostos. No contexto do cruzamento de zero, porém, não acho que os harmônicos sejam importantes. E não, receio não ter uma boa fonte on-line sobre PLLs.
21712 Jim Clay
Isso não está correto. O AFSK 1200 segue a Bell 202 e diz que os tons devem ser 1200 e 2200. A descontinuidade nunca deve ocorrer no lado do transmissor. Confira os moduladores AFSK 1200 de código aberto, a modulação é feita acompanhando um incremento de fase para cada tom: se o tom == BAIXO, last_phase + = ph_low else last_phase + = ph_high endif; next_sample = sin (última_fase);
vz0
5

Fiz um decodificador para AFSK (padrão Bell 202) usando receptores de correlação para 1200 Hz e 2200 Hz, com resultados muito bons.

pecadoporque

A amplitude resultante é bastante independente da fase do sinal e o SNR de saída é muito bom.

Juancho
fonte
Isto é exatamente o que eu tentei antes e o que chamei de "demodulador incoerente clássico". Talvez minha implementação seja incorreta, mas receio que ela sofra estouros de buffer devido ao processamento lento. Obrigado mesmo assim!
Patrick Oscity
0

No caso de RTTY 45.45 baud, você também terá símbolos que não são um número inteiro de amostras, portanto, você precisa de uma função que possa ser chamada de cada amostra e, em seguida, sinalizar seu valor de retorno quando esse símbolo terminar. E você precisa de um acumulador de fase, que mantenha um registro constante de onde está a fase da onda senoidal.

Para enviar símbolos cujo comprimento não seja um múltiplo inteiro da taxa de amostragem, você precisa desta função ...

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 usá-lo, gere a próxima amostra de onda senoidal e chame essa função e verifique se o valor de retorno NÃO é igual a dois. Se não for igual a dois, avance para o próximo símbolo e decida se está enviando uma marca de espaço, depois chame essa função novamente dentro do bloco de código que será executado quando você descobrir que o valor de retorno não é igual a dois.

E aqui está o acumulador de fase do firmware Rockbox, com uma alteração para permitir alterações na amplitude (o volume total é 32767, o volume total fora de fase de 180 graus é -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
fonte