diff --git a/ChangeLog b/ChangeLog index 8313b29..b51e2b6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,14 @@ Changelog: ---------- +99-07-02 [becka]: + * Changes to fsk_demod: + - First parameter at creation now gives samplerate. Allows for + 7200 samples/second. + - Changed demodulator to read signed 16 encoded data. Hope that + works. + - Heavily commented it. + 99-07-01 [morten]: * Added new framework for State-machines, see misc/statemachines.c and G3/fsm.c diff --git a/modules/fsk_demod.c b/modules/fsk_demod.c index 41622e4..0140cb7 100644 --- a/modules/fsk_demod.c +++ b/modules/fsk_demod.c @@ -2,10 +2,18 @@ ****************************************************************************** Fax program for ISDN. - FSK demodulator. Takes alaw data and demodulates it to a stream of - 0/1,conf pairs. + FSK demodulator. Takes signed 16 bit data and demodulates it to a + stream of bytes containing 0/1,conf pairs. + conf is a "confidence value" ranging from 0-100% giving you an idea + about how sure the demodulator is about the result. Extended periods + of low confidence usually indicate carrier loss. Short periods are + normal when the transmitted symbol changes. This behaviour can be used + to re-lock PLLs. + + We do a s16->alaw conversion here to allow for table lookup. + This is suboptimal as we then do alaw->s16->alaw. - Copyright (C) 1998 Andreas Beck [becka@ggi-project.org] + Copyright (C) 1999 Andreas Beck [becka@ggi-project.org] Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -34,33 +42,50 @@ #include #include #include +#include /* Turn on to generate a big bunch of debugging code. */ #undef BIGDEBUG +/* Data structures for the fourier analysis. + */ typedef struct four_help { - int depth,depthsquare,currpos; - int currreal,currimag; - char *histreal,*histimag; - int stpos,ctpos,stdepth; - char *fasttable; + int depth, /* width of the fourier window */ + depthsquare, /* square of it. Used for normalizing */ + currpos; /* sliding pointer into the history */ + int currreal,currimag; /* The current real and imaginary parts of + the fourier transform. */ + char *histreal,*histimag;/* History over the window to be able to + remove the coefficients when they + slide out of the window */ + int stpos,ctpos,stdepth;/* Offsets for the current sine-position, + the cosine position (shifted by pi/4) + and the depth of the table. */ + char *fasttable; /* Lookup table for the multiplication + in the DFT. Directly gives + sin(omega*t)*i(t) */ } four_help; - +/* private data of a fsk_demod module. + */ typedef struct { - four_help freq1,freq2; - int f1,f2; - int baud; + four_help freq1,freq2; /* fourier data for both channels */ + int f1,f2; /* frequencies for 1 and 0 */ + int baud; /* baudrate - gives fourier window */ + int sps; /* samples per second */ } fskdemod_private; +/* Calculate the greatest common divisor. This is used to see when a waveform + * repeats. This optimizes the length of the waveform table. + * Euclidian algorithm. + */ int gcd(int d1,int d2) { - /* FIXME ! Is this correct ? Pretty long ago that I did that last time ... */ while(d1!=d2) { if (d1>d2) d1-=d2; @@ -73,36 +98,58 @@ int gcd(int d1,int d2) /* The following do the DFT "on the fly" with a sliding window. */ -static void init_four_help(four_help *hlp,int depth,int freq) +/* Set up all the tables. + */ +static void init_four_help(fskdemod_private *priv,four_help *hlp,int depth,int freq) { int x,y; char *ftptr; double sinval; - hlp->depth=depth; - hlp->depthsquare=depth*depth; - hlp->currpos=0; - hlp->currreal=hlp->currimag=0; + hlp->depth=depth; /* window size */ + hlp->depthsquare=depth*depth; /* normalizing helper variable */ + hlp->currpos=0; /* window initial position. */ + hlp->currreal=hlp->currimag=0; /* there is no energy in a zero window */ + + /* Allocate the history windows and erase them. */ hlp->histreal=malloc(sizeof(hlp->histreal[0])*depth); hlp->histimag=malloc(sizeof(hlp->histimag[0])*depth); while(depth--) hlp->histreal[depth]= hlp->histimag[depth]=0; - x=gcd(freq,SAMPLES_PER_SECOND);x=SAMPLES_PER_SECOND/x; + /* Calculate size of the sine lookup tables. At worst this is + sps, if they have no common divisor (we have no fractional + rates, so after 1 second we must have a match again). + As this is pretty large like 8000sps*256values=2MB, we try to + optimize by finding the GCD which gives the fraction of seconds + after which the frequency pattern repeats at the samplerate. + */ + x=gcd(freq,priv->sps);x=priv->sps/x; + + /* We derive the cosine part by shifting by PI/4. If that doesn't + * compute, we have a slight error. Warn about that. All V.21 + * signals at 8000 or 7200 sps will not trigger that case. + */ if (x%4) { fprintf(stderr,"fsk_demod: ERROR: sinetable size not divideable by 4.\n" "Cosine shift is slighty incorrect.\n"); } + /* The table is 256 entries per sample */ hlp->stdepth=256*x; hlp->stpos=0; hlp->ctpos=256*(x/4); /* The bracket _IS_ important. Do not optimize. */ + /* Allocate and fill the table. The table contains sin(omega*t)*i(t) + * and is accessed by t*256+i(t), where i(t) is assumed to be alaw- + * encoded for better dynamics. Any encoding would do. Just make sure + * you choose the same encoding in add_samp. + */ hlp->fasttable=malloc(sizeof(hlp->fasttable[0])*hlp->stdepth); ftptr=hlp->fasttable; for(x=0;xstdepth/256;x++) { - sinval=sin(x*2.0*M_PI*(double)freq/(double)SAMPLES_PER_SECOND); + sinval=sin(x*2.0*M_PI*(double)freq/(double)priv->sps); for(y=0;y<256;y++) *ftptr++=((int)(sinval*alaw2int(y))) >> ((sizeof(int)-sizeof(char))*8); @@ -118,21 +165,39 @@ static void destroy_four_help(four_help *hlp) free(hlp->fasttable);hlp->fasttable=NULL; } -inline void add_samp(four_help *hlp,char sample) +/* Slide the DFT window by one sample. + */ +inline void add_samp(four_help *hlp,ifax_sint16 sample) { + unsigned char mysample; + + /* convert sample to alaw for table lookup. + */ + mysample=sint2wala[((unsigned)sample>>4)&0xFFF]; + + /* remove old sample that slides out of the window. + * We could optimize a little further here by directly storing + * the power instead of the individual values, but I prefer to + * keep the information. We could easily modify this system to + * demodulate a PSK stream this way. + */ hlp->currreal-=hlp->histreal[hlp->currpos]; hlp->currimag-=hlp->histimag[hlp->currpos]; + /* Add and store new samples using the lookup-tables. */ hlp->currreal+= - (hlp->histreal[hlp->currpos]=hlp->fasttable[hlp->stpos+(unsigned char)sample]); + (hlp->histreal[hlp->currpos]=hlp->fasttable[hlp->stpos+(unsigned char)mysample]); hlp->currimag+= - (hlp->histimag[hlp->currpos]=hlp->fasttable[hlp->ctpos+(unsigned char)sample]); + (hlp->histimag[hlp->currpos]=hlp->fasttable[hlp->ctpos+(unsigned char)mysample]); + /* Slide on the table pointers. */ hlp->currpos++ ;if (hlp->currpos>=hlp->depth ) hlp->currpos=0; hlp->stpos+=256;if (hlp->stpos >=hlp->stdepth) hlp->stpos=0; hlp->ctpos+=256;if (hlp->ctpos >=hlp->stdepth) hlp->ctpos=0; } +/* Destroy the private data. + */ void fskdemod_destroy(ifax_modp self) { fskdemod_private *priv=(fskdemod_private *)self->private; @@ -144,15 +209,20 @@ void fskdemod_destroy(ifax_modp self) return; } +/* Module has no commands. + */ int fskdemod_command(ifax_modp self,int cmd,va_list cmds) { return 0; /* Not yet used. */ } +/* Handler - go through the data, slide it into the DFT, give back a 0/1 + * decision and a confidence value. + */ int fskdemod_handle(ifax_modp self, void *data, size_t length) { unsigned char dat[2]; - char *input=data; + ifax_sint16 *input=data; int a1,a2,conf,pwr; int handled=0; @@ -192,6 +262,11 @@ int fskdemod_handle(ifax_modp self, void *data, size_t length) return handled; } +/* Constructor. Arguments: + * int samplerate; - rate at which samples come in + * int f1,f2; - frequencies that encode 1 and 0 + * int baud; - the baudrate of the transmission. Influences DFT window. + */ int fskdemod_construct(ifax_modp self,va_list args) { fskdemod_private *priv; @@ -203,14 +278,15 @@ int fskdemod_construct(ifax_modp self,va_list args) self->handle_input =fskdemod_handle; self->command =fskdemod_command; + priv->sps =va_arg(args,int); priv->f1 =va_arg(args,int); priv->f2 =va_arg(args,int); priv->baud=va_arg(args,int); - sampbaud=(SAMPLES_PER_SECOND+priv->baud)/priv->baud; + sampbaud=(priv->sps+priv->baud)/priv->baud; - init_four_help(&priv->freq1,sampbaud,priv->f1); - init_four_help(&priv->freq2,sampbaud,priv->f2); + init_four_help(priv,&priv->freq1,sampbaud,priv->f1); + init_four_help(priv,&priv->freq2,sampbaud,priv->f2); return 0; }