소프트웨어에서 AFSK 신호를 복조하는 방법


14

오디오 채널 (스피커 / 마이크)을 통해 한 장치에서 다른 장치로 이진 데이터를 전송하려고합니다. 와 2 개의 주파수 f m a r k = 1200  Hzf s p a c e = 2200  Hz 와 함께 Packet Radio에서와 같이 AFSK (Audio Frequency Shift Keying)를 사용 합니다. 나는 루비에서 조금 놀았고 첫 번째 구현은 고전적인 코 히어 런트 복조기를 모방하여 지금까지 잘 작동합니다.1200 보오드에프미디엄아르 자형케이=1200 Hz에프에스이자형=2200 Hz

문제는 성능이 문제가되고 현재 솔루션이 너무 느린 모바일 플랫폼으로 이것을 이식하려고한다는 것입니다. 소프트웨어에서 AFSK를 복조하는 많은 방법을 찾았습니다.

  • 슬라이딩 DFT (FFT)
  • 슬라이딩 괴테 필터
  • 위상 고정 루프
  • 제로 크로싱

가는 방법은 무엇입니까? 선택할 수있는 옵션이 너무 많습니다. 더 많은 옵션을 사용할 수 있다고 확신합니다. 아마도 위에서 지정한 솔루션보다 더 나은 솔루션이 있습니까? 누군가 나에게 코드 예제를 가지고 있습니까? 나는 걱정이다

  • 성능 (iOS 또는 Android 장치와 같은 모바일 플랫폼에서 실행해야 함)
  • 안정성 (일부 소음을 처리 할 수 ​​있어야 함)

어떤 제안이나 힌트라도 대단히 감사합니다!


3
타겟팅하려는 휴대 기기의 기능을 짧게 판매 할 것으로 보입니다. 최신 장치는 클럭 속도가 1GHz를 초과하는 멀티 코어 프로세서입니다. FSK 복조기로 <10 ksps 신호를 처리하면 성능 문제가 발생하지 않아야합니다. 그러나 기존의 접근 방식 (마크 / 공간 필터링과 같은 소리)이 최신 모바일 플랫폼에서 실시간으로 실행될 수없는 이유는 없습니다. 보다 정교한 PLL 기반 접근 방식도 처리 범위에 편안하게 맞아야합니다. 기존 코드를 약간 프로파일 링했습니다.
Jason R

답변:


9

위상 고정 루프가있는 복조기 비트 오류율 (BER) 측면에서 최고의 성능을 얻을 수 있다고 생각합니다. 그래도 빠를 필요가 있습니다. 나는 여전히 합리적으로 잘 수행하는 빠른 알고리즘에 대한 최선의 방법은 제로 크로싱이라고 생각합니다.

참고로 2200Hz를 2400Hz로 변경하는 것이 좋습니다. 1200/2200 Hz 체계의 순진한 구현은 아래 그림에서 약 3 분의 2로 볼 수 있듯이 2200 Hz가 1200 Hz로 전이되는 불연속을 생성합니다.

1200Hz 및 2200Hz

사용중인 대역폭을 최소화하고 신호를 왜곡하는 불연속을 피하려면 위상을 연속적으로 만들어야합니다. 그러나 송신기 위상을 연속으로 설정하더라도 위상이 다르기 때문에 2200Hz 기호가 항상 같은 수의 제로 크로싱을 갖지는 않을 수 있습니다. 보통 4 개의 교차점을 가지지 만 때로는 3 개의 교차점을 갖습니다. 반면에 1200Hz 기호는 보오율이 FSK 주파수로 균등하게 분할되므로 항상 두 개의 제로 크로싱을 갖습니다.

2200Hz를 2400Hz로 변경하면이 두 가지 문제를 모두 해결할 수 있습니다. 그런 다음 기호는 항상 0도에서 시작하고 끝납니다 (따라서 자동으로 위상을 연속적으로 설정 함).

1200Hz 및 2400Hz


Jim, 자세한 답변 감사합니다! 내 변조기는 실제로 CPFSK를 수행하므로 불연속성은 문제가되지 않습니다. 고조파가 1200의 배수만큼 겹치지 않기 때문에 고의적으로 1200과 2200Hz를 선택했습니다. PLL은 훌륭하게 들리지만 실제로 구현 방법을 모릅니다. 소프트웨어 PLL에 대한 좋은 소스를 알고 있습니까?
Patrick Oscity

@Patrick 아니요, 1200 및 2400Hz에 겹치는 고조파가있을 것입니다. 그러나 제로 크로싱의 맥락에서 나는 고조파가 중요하다고 생각하지 않습니다. 그리고 아니, 나는 PLL에 대한 좋은 온라인 소스를 모른다는 것을 두려워한다.
Jim Clay

이것은 정확하지 않습니다. AFSK 1200은 Bell 202를 따르며 톤은 1200과 2200이어야합니다. 불연속성은 송신기 측에서 발생하지 않아야합니다. 오픈 소스 AFSK 1200 변조기를 확인하십시오. 변조는 모든 톤에 대한 위상 증분을 추적하여 수행됩니다. tone == LOW이면 last_phase + = ph_low else last_phase + = ph_high endif; next_sample = sin (last_phase);
vz0

5

1200Hz 및 2200Hz의 상관 수신기를 사용하여 AFSK (Bell 202 표준) 용 디코더를 만들었으며 매우 좋은 결과를 얻었습니다.

코사인 독립적으로 다음 각각을 적분하고 절대 값 (제곱) 값을 계산하는 합니다.

결과적인 진폭은 신호 위상과는 상당히 독립적이며 출력 SNR은 매우 좋습니다.


이것이 바로 이전에 시도한 것과 "클래식 인코 히어 런트 복조기"라고하는 것입니다. 어쩌면 내 구현이 잘못되었을 수 있지만 느린 처리로 인해 버퍼 오버플로가 발생 할까 걱정됩니다. 어쨌든 고마워!
Patrick Oscity

0

RTTY 45.45 보드의 경우 정수 샘플이 아닌 심볼도 있으므로 각 샘플을 호출 한 다음 해당 심볼이 종료되면 리턴 값으로 신호를 보낼 수있는 함수가 필요합니다. 그리고 사인파의 위상이 어디인지 확인하는 위상 누산기가 필요합니다.

길이가 샘플 속도의 정수 배수가 아닌 기호를 보내려면이 함수가 필요합니다 ...

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

이를 사용하려면 다음 사인파 샘플을 생성하고이 함수를 호출 한 다음 반환 값이 2가 아닌지 확인하십시오. 2가 아닌 경우 다음 기호로 이동하여 공간 표시를 보내는 지 여부를 결정한 다음 리턴 값이 2가 아닌 것을 발견하면 실행되는 코드 블록 내에서이 함수를 다시 호출하십시오.

그리고 여기에는 진폭 변경을 허용하는 변경 사항이 포함 된 Rockbox 펌웨어의 위상 누산기가 있습니다 (전체 볼륨은 32767, 180도 전체 볼륨은 -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;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.