Nick:
To decode FSK on a computer, I've recently been playing with two basic DFT approaches. One is an 8-point Fourier transform and the other is an autocorrelation approach, which works a bit better. I implemented both to decode Bell 202 1200 baud FSK (1200/2200 Hz) which is close to what you have.
I digitize off-the-air waveforms at 9600 bits per second, make .wav file and decode them offline using a C program on a PC, using the Code::Blocks compiler. I think either would be work fine for your case.
FT program (easy to change the upper frequency, I'll post the autocorrelation version separately)
//fourier transform version. Does not work quite as well as correlator (higher error rate).
//better with 16 bit coefficients than 8 bit
//from http://ubuntuforums.org/showthread.php?t=968690
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <math.h>
//for Code::Blocks
// project->build options->search directories, add (lib,include)
// linker settings: link libraries, add path to libsndfil\lib\libsndfile.lib
// copy bin\libsndfile-1.dll to source directory
// ...?? (can't get this to work) statically link by adding to linker options -static
#include <sndfile.h>
#define NPERBAUD 8
#define PI 3.141592653
// uart variables
#define IDLE 1
#define START 2
#define RUN 4
#define RXFLAG 8
#define FRAMERR 16
#define STOP 32
#define PARITY 64
int coeffloi[8],coeffloq[8],coeffhii[8],coeffhiq[8];
void initFSK(void)
{
int i;
for (i=0; i<NPERBAUD; i++)
{
coeffloi[i] = 16383*cos(2*PI*i/NPERBAUD*1200/1200);
coeffloq[i] = 16383*sin(2*PI*i/NPERBAUD*1200/1200);
coeffhii[i] = 16383*cos(2*PI*i/NPERBAUD*2200/1200);
coeffhiq[i] = 16383*sin(2*PI*i/NPERBAUD*2200/1200);
}
}
/*
https://sites.google.com/site/wayneholder/attiny-4-5-9-10-assembly-ide-and-programmer/bell-202-1200-baud-demodulator-in-an-attiny10
Sample the incoming FSK signal at 9600 samples/second (8 times the 1200 Hz frequency used in Bell 202 modulation)
Pass this through two digital filters, one tuned to 1200 Hz and the other to 2200 Hz.
The function is called for index = 0 through length(data) - 8
After stepping though at least 8 samples, the value returned from demodulate() will be >0 if it has
demodulated a 2200 Hz tone, or < 0 for a 1200 Hz tone.
*/
int demodulate (signed char data[]) {
// static int8_t coeffloi[] = { 64, 45, 0, -45, -64, -45, 0, 45};
// static int8_t coeffloq[] = { 0, 45, 64, 45, 0, -45, -64, -45};
// static int8_t coeffhii[] = { 64, 8, -62, -24, 55, 39, -45, -51};
// static int8_t coeffhiq[] = { 0, 63, 17, -59, -32, 51, 45, -39};
int outloi = 0, outloq = 0, outhii = 0, outhiq = 0;
int ii;
int sample;
for (ii = 0; ii < 8; ii++) {
sample = data[ii];
outloi += sample * coeffloi[ii];
outloq += sample * coeffloq[ii];
outhii += sample * coeffhii[ii];
outhiq += sample * coeffhiq[ii];
}
return (outhii >> 8) * (outhii >> 8) + (outhiq >> 8) * (outhiq >> 8) -
(outloi >> 8) * (outloi >> 8) - (outloq >> 8) * (outloq >> 8);
}
int main()
{
// file handling
SNDFILE *sf;
SF_INFO info;
int num_channels;
int num, num_items;
int *buf;
int f,sr,c;
int i,j;
FILE *out;
// decode and uart
static char rx,ch;
static short status = IDLE;
static int clock = 0; // counter for sample
static int bit = 0; // bit counter
static char parity=0;
signed char buf2[8]={0}; //signal samples
int k,spb=8; //samples per bit
int result,output,count=0;
initFSK();
/* Open the WAV file. */
info.format = 0;
sf = sf_open("helicopter_8_9600.wav",SFM_READ,&info);
// sf = sf_open("CID-test.wav",SFM_READ,&info);
if (sf == NULL)
{
printf("Failed to open the file.\n");
exit(-1);
}
/* Print some of the info, and figure out how much data to read. */
f = info.frames;
sr = info.samplerate;
c = info.channels;
printf("frames = %d\n",f);
printf("sample rate = %d\n",sr);
printf("channels = %d\n",c);
printf("format = %X\n",info.format);
num_items = f*c;
printf("num_items = %d\n",num_items);
/* Allocate space for the data to be read, then read it. */
buf = (int *) malloc(num_items*sizeof(int));
num = sf_read_int(sf,buf,num_items);
sf_close(sf);
printf("Read %d items\n",num);
//Write data to filedata.out.
// out = fopen("filedata.csv","w");
// fprintf(out,"%d,",(int8_t) ((unsigned int)buf[i]>>24));
// fclose(out);
k=0;
for (i = 0; i < num_items - 8; i += c)
{
for(k=0; k<7; k++) buf2[k]=buf2[k+1]; //shuffle to the left
buf2[7] = (signed char) ((unsigned int)buf[i]>>24);
result = demodulate(buf2);
if(result>0) result=0; else result=1;
// now we can build a uart
// the byte is delivered in ten bit chunks...
// in order,
// start bit (0)
// bit 0
// ...
// bit 7
// stop bit (1)
// see what our state is
if (status & IDLE) // we're idling
{
status &= ~RXFLAG;
status &= ~PARITY; //clear parity flag
if (!result) // falling edge of start bit
{
status &= ~IDLE; // so we idle no longer
status |= START; // we're started
bit = 0; // reset bit counter
clock = 0; // and the clock count
parity = 0;
}
// else we're still waiting for end of stop bit
}
else
{
if (status & START) // aha, we got the falling edge
{
if ((clock <= spb/2) && (result)) // oops, false trigger...noise perhaps
{
status &= ~START;
status |= IDLE; // so drop back to idle mode
}
else
clock++; // otherwise, one more clock
if (clock == spb/2) // or are we now in mid start-bit
{
status &= ~START;
status &= ~IDLE;
status |= RUN; // so now we're hot to trot
clock = 0; // reset counter
}
}
else
{
if (status & RUN) // we're reading data (allegedly)
{
if (clock < spb-1) // time for a sample?
clock++; // not yet
else
{
if (bit < 8) // normal read
{
clock = 0;
rx = rx>>1;
if (result) {
rx |= 0x80;
parity++; //count 1 bits
}
else rx &= 0x7f;
bit ++;
}
else
{
if (! result) // frame error?
{
status |= FRAMERR; //if stop==0
}
else
{
status &= ~FRAMERR;
}
status |= IDLE;
status |= RXFLAG;
if (parity&1) status |= PARITY; //1 if # of 1 bits is odd
status &= ~RUN;
status &= ~START;
}
}
}
}
}
if (status & RXFLAG) { //added for debug, sjr
ch=rx&0x7f; //remove parity bit
if(ch == 0x0d) printf("\n");
if(ch<32 || ch>126) ch='.';
printf("%c",ch);
count++;
if(count>35) {printf("\n"); count=0;}
} // end if (status & RXFLAG)
output = (status<<8)+rx;
}
return(output);
}