[APRS FSK Demodulation] How do I measure frequencies accurately in one cycle?

[Intro]: So I would like to build an APRS demodulator/decoder it is an Audio FSK signal. I have the basic idea of what I need to do, and right now I am focusing on the detection of the mark and space [1, 0] respectively, essentially working on this project in “blocks”.

[Requirements]: I need to capture the frequency either 1200/2200Hz signals and report them as either 1, or 0. At 1200baud the signal will need to be read every 0.83 milliseconds. The signal coming in will be a sine wave of variable amplitude so it is my understanding that I cannot use an interrupt to signal the high/low transition of a pin especially since the signal will be centered at 2.5v to prevent the signal from going negative.

[Code Explanation & Issues]: I wrote a small bit of code which uses the ADC to trigger on zero crossings and uses the millis() function to time the interval. This works alright at frequencies <100Hz, but tends to lock on random frequencies as well and is only accurate +/- 3 Hz at low frequencies. This accuracy would be fine if we were only comparing 1200/2200Hz but it will not count that high. I searched through some frequency measurement code like FreqCount, and a few other ARPS TNC/demodulation but do not understand how to properly “read” their code and understand it.

int millisValues[3];
int increment = 0;
int i = 0;
int wait = 0;
int values = 0;
float freq = 0;

void setup() {
  pinMode(A0, INPUT);
  Serial.begin(9600);
}

void loop() {
  values = analogRead(A0);
//  Serial.println(values);
  if (values < 512 && wait == 0){ //Check for zero crossing, wait if we passed it.
    millisValues[i] = millis();
    i++;
    wait = 1;
//    Serial.println(i);
  }
  if (values >= 512){
    wait = 0; 
  }
  if (i == 2){
      i = 0;
      float freq = (millisValues[1] - millisValues[0]); //determine time between zero crossings
      Serial.print("Frequency is ");
      Serial.print(1/freq*1000); //conversion to frequency
      Serial.println(" Hz");
      delay(50); //added for readability. Would not need this in final code
  }
}

[Request]: I hope to find some suggestions on how to approach this project, and find a general intuition on how to solve these problems with my level of understanding of programming. My idea is that if I change the frequency divider of which the Arduino samples at that I could improve the accuracy of the timing since it will measure faster. This is the only idea I have at this point so any feedback would be appreciated. As I mentioned earlier I am focusing only on the conversion of the 1200/2200Hz signal into the binary equivalent.

Thank you for your time.

Can you show a screen capture of the signal. How many 1200 Hz pulses are there ? sometimes just one ?
The Arduino can measure a pulse length, either with a Arduino function ( pulseIn() - Arduino Reference ) or with a hardware timer.
What about the duty cycle ? and how does it change from 1200 Hz to 2200Hz ?

Please provide links to the APRS documentation that you are using.

[Requirements]: I need to capture the frequency either 1200/2200Hz signals and report them as either 1, or 0. At 1200baud the signal will need to be read every 0.83 milliseconds. The signal coming in will be a sine wave of variable amplitude so it is my understanding that I cannot use an interrupt to signal the high/low transition of a pin especially since the signal will be centered at 2.5v to prevent the signal from going negative.

I believe the analog comparator will suit your needs. It has an interrupt and an output that can trigger a timer/counter. Check it out in section 22 of the datasheet if you're using the ATmega328P.

Most APRS stations use the modulation scheme known as Bell 202 1200 baud AFSK. There is plenty of code around to demodulate AFSK signals, for example here is one written in C and then in assembly language for an ATtiny10.

Basically, the algorithm is a 2 point Fourier transform.

Hi,
Don’t they make IC’s ready to do this job?

The XR2211 was quite popular in AmateurRadio 1200/300 Baud Packet Radio.

Tom… :slight_smile:
Google is your friend. AFSK 1200Baud Demod IC

XR2211Av104.pdf (236 KB)

HART_DS8500.pdf (133 KB)

Yes I would use a demodulator chip for this and leave the processing power of the Arduino to do something useful.

I guess Google was hating me today. I fished around for 20 minutes and gave up, had to leave anyway. I found references to the Bell 202 spec, but I was curious about the details. Never found them. I guess it’s all dinosaur stuff now anyway.

Hi,

Google AX25, its the Amateur Radio spec.

Tom... :slight_smile:

TomGeorge:
Hi,

Google AX25, its the Amateur Radio spec.

Tom... :slight_smile:

I was there. If I looked again, I could continue, but I've lost interest.

Some time ago I built a packet modem around a 16pin DIP MX614 chip. It works very well.

The basic circuit was a much simplified version of this: http://www.cmlmicro.com/assets/614_TCM3105.pdf

The good old LM567 tone decoder is your friend here.
Use 2 for better accuracy.

Here is an arduino library Arduino APRS Library | unsigned.io for AFSK demodulation ( BELL 202). There are also designs for his ATMEGA328P based "micromodem" for APRS stuff. You can even buy a ready made modem there.
I have an open question with him about using his AFSK demodulator for telephone caller ID processing.
Analog FSK demodulator chips are getting quite rare now.

Edit:
Here is another paper on decoding Bell 202 with a discussion on possible demodulation methods: http://www.cypress.com/file/42141/download