/* ACfax - Fax reception with X11-interface for amateur radio Copyright (C) 1995-1998 Andreas Czechanowski, DL4SDC This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. andreas.czechanowski@ins.uni-stuttgart.de */ /* * mod_demod.c - FM and AM modulator/demodulator for ACfax */ #include <stdio.h> #include <stdlib.h> #include <math.h> #include <unistd.h> #include "mod_demod.h" SHORT int firwide[] = { 6, 20, 7, -42, -74, -12, 159, 353, 440 }; SHORT int firmiddle[] = { 0, -18, -38, -39, 0, 83, 191, 284, 320 }; SHORT int firnarrow[] = { -7, -18, -15, 11, 56, 116, 177, 223, 240 }; /* these some more coefficient tables were for testing only. */ /* int firwide[] = { 12, 24, 7, -42, -74, -12, 159, 353, 440 }; */ /* int firnarrow[] = { -16, -21, -15, 11, 56, 116, 177, 223, 240 }; */ /* int fircheap[] = { 0, 0, 0, 0, 0, 0, 36, 99, 128 }; */ /* int firtoobig[] = { -14, -12, 18, 40, -1, -80, -71, 114, 381, 510 } */ /* int firnotbad[] = { 11, 16, 1, -29, -44, -3, 98, 208, 256 }; */ static SHORT int *fcoeff = firmiddle; static int *sqrt_lo; /* square-root lookup-table for AM demodulator */ static int *sqrt_hi; /* (splitted in 2 parts) */ static int *sintab; /* sine wave table for FM modulator */ static int *asntab; /* arcsin-table for FM-demodulator */ static int *fmphsinc; /* phase-increment-table for FM modulator */ static char *amreal; /* amplitude tables for AM modulator */ static int compval; /* comparator value for APT detecion */ /* void main(int argc, char **argv) { char indata[2048]; int outdata[1024]; int q; modem_init(); set_modem_param (FIL_MIDL, 255, 400); while ((q = fread(indata, 1, 2048, stdin)) > 0) { q &= 0xfffe; fm_demod(indata, q, outdata); } } */ /* Initialize the modulator/demodulator functions. This function allocates space for the arrays and lookup-tables */ void modem_init(void) { int i; static int inited = 0; if (inited) return; sqrt_lo = (int *)malloc(2048*sizeof(int)); sqrt_hi = (int *)malloc(2048*sizeof(int)); if (!(sqrt_lo) || !(sqrt_hi)) { perror("modem_init:sqrt_*"); exit(1); } amreal = (char *)malloc(258); if (!(amreal)) { perror("modem_init:am{real|imag}"); exit(1); } sintab = (int *)malloc(1024*sizeof(int)); if (!(sintab)) { perror("modem_init:sintab"); exit(1); } asntab = (int *)malloc(512*sizeof(int)); if (!(asntab)) { perror("modem_init:asntab"); exit(1); } fmphsinc = (int *)malloc(258*sizeof(int)); for (i=0; i<1024; i++) sintab[i] = 127.5 + 127*sin(i*PI/512.0); inited = -1; } /* set up a lookup-table for the given deviation which gives values from 0 to maxval with clipping beyond the edges for reception, initialize a lookup-table for FM transmission. */ void set_modem_param(int filter, int maxval, int devi) { int phmax; int i; /* initialize decoder first, if not already done */ modem_init(); /* do some range-checking */ if (devi < 100) devi = 100; if (devi > 1200) devi = 1200; if (maxval < 1) maxval = 1; if (maxval > 255) maxval = 255; phmax = 255.9 * sin(devi * PI / 4000.0) + 0.5; for (i=0; i<512; i++) { if (i <= 256-phmax) asntab[i] = 0; else if (i >= 256+phmax) asntab[i] = maxval; else asntab[i] = maxval * 2000 / devi * asin((i-256.0)/256.5) / PI + maxval/ 2; } for (i=0; i<2048; i++) { sqrt_lo[i] = (sqrt(i) * maxval / 256.0) + 0.5; sqrt_hi[i] = (sqrt(32*i) * maxval / 256.0) + 0.5; } for (i=0; i<=maxval; i++) fmphsinc[i] = devi / 2000.0 * 65536; for (i=0; i<= maxval; i++) amreal[i] = 127.5 + 127.5*i/maxval; compval = maxval >> 1; switch (filter) { case FIL_NARR: fcoeff = firnarrow; fprintf(stderr, "selecting narrow filter\n"); break; case FIL_MIDL: fcoeff = firmiddle; fprintf(stderr, "selecting middle filter\n"); break; case FIL_WIDE: fcoeff = firwide; fprintf(stderr, "selecting wide filter\n"); break; } } /* Demodulate the samples taken with 8kHz to a frequency sampled with 4kHz. This is done with a delay-demodulator : +----------+ +-------+ +----+ +---+ +->|*sin(2kHz)|->|FIR-LPF|--*->|z^-1|->|mul|--+ | +----------+ +-------+ | +----+ +---+ | | *-->-- / | AM^2 | | \ / |+ | | +---------+ \ / +---+ +---+ <input>--+ AM^2<--|amp^2 det| X |add|->|div|--<FM-out> | +---------+ / \ +---+ +---+ | | / \ |- | *-->-- \ | | +----------+ +-------+ | +----+ +---+ | +->|*cos(2kHz)|->|FIR-LPF|--*->|z^-1|->|mul|--+ +----------+ +-------+ +----+ +---+ The cosine-signal is simply a sequence of 1,0,-1,0 , and the sine- signal is a sequence of 0,1,0,-1 values because the frequency of these is exactly a fouth of the sample-rate. The values multiplied by zero need not be evaluated by the FIR-filter, what reduces the calculation-effort to one half. Taps of the FIR-chain which are to be multiplied with the same coefficient (possible due to the symmetry of the pulse-response) are first added or subtracted, so again reducing the number of multiplications to one half. NOTE: incnt must be a multiple of 2 to operate properly ! output range is determined by asntab, which is set up by set_modem_param. This table also performs the arcsin-correction and clips values that are beyond the given deviation. */ void fm_demod(char *smplin, int incnt, char *decout) { static SHORT int firbuf[32]; /* buffer holding the input-values */ /* static int inptr = 0; / pointer for data-entry into firbuf */ /* static int rdptr = 0; / pointer for data-output from firbuf */ static int neg = 0; /* flag changing every 2 sample-points */ static int px, py, qx, qy; /* the filtered output-results */ static int pamp, qamp, pfrq, qfrq; /* static int n = 0; int smplptr = 0; */ while (incnt >= 2) { /* shift buffer "left" by 2 : copy firbuf[2..17] to firbuf[0..15] */ memcpy(firbuf, firbuf+2, (16*sizeof(int))); /* enter 2 new samle-points into buffer */ firbuf[16] = *(unsigned char *)smplin++ - 128; firbuf[17] = *(unsigned char *)smplin++ - 128; incnt -= 2; /* do the first quarter : multiply with +0-0+0-0+0-0+0-0+ for px and 0-0+0-0+0-0+0-0+0 for py. 2 sample-periodes later, multiply with 0-0+0 for px and -0+0- for py, what is done using the neg variable as flag. In this case, the result for frequency is negated (amplitude is always positive) */ px = (firbuf[0] + firbuf[16]) * fcoeff[0]; px -= (firbuf[2] + firbuf[14]) * fcoeff[2]; px += (firbuf[4] + firbuf[12]) * fcoeff[4]; px -= (firbuf[6] + firbuf[10]) * fcoeff[6]; px += (firbuf[8]) * fcoeff[8]; px >>= 2; py = (firbuf[15] - firbuf[1]) * fcoeff[1]; py += (firbuf[3] - firbuf[13]) * fcoeff[3]; py += (firbuf[11] - firbuf[5]) * fcoeff[5]; py += (firbuf[7] - firbuf[9]) * fcoeff[7]; py >>= 2; /* dfft->src[smplptr].r = (neg) ? -px : px; dfft->src[smplptr].i = 0; smplptr++; */ /* rdptr = (rdptr+1) & 31; */ pamp = ((px*px + py*py) >> 8) + 1; pfrq = (px*qy - py*qx) / (pamp + qamp); /* do the second quarter : multiply with 0-0+0-0+0-0+0-0+0 for px, -0+0-0+0-0+0-0+0- for py */ qx = (firbuf[15+1] - firbuf[1+1]) * fcoeff[1]; qx += (firbuf[3+1] - firbuf[13+1]) * fcoeff[3]; qx += (firbuf[11+1] - firbuf[5+1]) * fcoeff[5]; qx += (firbuf[7+1] - firbuf[9+1]) * fcoeff[7]; qx >>= 2; qy = -(firbuf[0+1] + firbuf[16+1]) * fcoeff[0]; qy += (firbuf[2+1] + firbuf[14+1]) * fcoeff[2]; qy -= (firbuf[4+1] + firbuf[12+1]) * fcoeff[4]; qy += (firbuf[6+1] + firbuf[10+1]) * fcoeff[6]; qy -= (firbuf[8+1]) * fcoeff[8]; qy >>= 2; /* dfft->src[smplptr].r = (neg) ? -qx : qx; dfft->src[smplptr].i = 0; smplptr++; */ /* rdptr = (rdptr+1) & 31; */ qamp = ((qx*qx + qy*qy) >> 8) + 1; qfrq = (px*qy - py*qx) / (qamp + pamp); /* asntab gives values from minval to maxval for given deviation */ *decout = (unsigned char)asntab[(pfrq+qfrq+256) & 511]; /* printf("%3d %5d %5d\n", n, (px*qy)/1024, (py*qx)/1024); printf("%3d %5d %5d\n", n, *decout, pamp+qamp); n++; */ decout++; neg = ~neg; } /* do_dfft(dfft); for (smplptr=0; smplptr<1024; smplptr++) { dx = dfft->dest[smplptr].r; dy = dfft->dest[smplptr].i; printf("%4.1f %3.1f\n", smplptr * 3.906, 20*log10(sqrt(dx*dx + dy*dy))); } */ } /* Demodulate the samples taken with 9.6kHz to an amplitude sampled with 4.8kHz. This done with a synchronous demodulator : +------------+ +-------+ +------+ +->|*sin(2.4kHz)|->|FIR-LPF|->|square|-+ | +------------+ +-------+ +------+ | | | | | | +-----+ +-------+ <input>--+ |adder|->|sq.root|--<AM-out> | +-----+ +-------+ | | | | | +------------+ +-------+ +------+ | +->|*cos(2.4kHz)|->|FIR-LPF|->|square|-+ +------------+ +-------+ +------+ The cosine-signal is simply a sequence of 1,0,-1,0 , and the sine- signal is a sequence of 0,1,0,-1 values because the frequency of these is exactly a fouth of the sample-rate. The values multiplied by zero need not be evaluated by the FIR-filter, what reduces the calculation-effort to one half. NOTE: incnt must be a multiple of 2 to operate properly ! output range is determined by sqrt_lo and sqrt_hi, which are set up by set_modem_param. */ void am_demod(char *smplin, int incnt, char *decout) { static SHORT int firbuf[32]; /* buffer holding the input-values */ /* static int inptr = 0; / pointer for data-entry into firbuf */ /* static int rdptr = 0; / pointer for data-output from firbuf */ static int neg = 0; /* flag changing every 2 sample-points */ static int px, py, qx, qy; /* the filtered output-results */ static int pamp, qamp; /* static int n = 0; int smplptr = 0; */ while (incnt >= 2) { /* shift buffer "left" by 2 : copy firbuf[2..17] to firbuf[0..15] */ memcpy(firbuf, firbuf+2, (16*sizeof(int))); /* enter 2 new samle-points into buffer */ firbuf[16] = *(unsigned char *)smplin++ - 128; firbuf[17] = *(unsigned char *)smplin++ - 128; incnt -= 2; /* do the first quarter : multiply with +0-0+0-0+0-0+0-0+ for px and 0-0+0-0+0-0+0-0+0 for py. 2 sample-periodes later, multiply with 0-0+0 for px and -0+0- for py, what is done using the neg variable as flag. In this case, the result for frequency is negated (amplitude is always positive) */ px = (firbuf[0] + firbuf[16]) * fcoeff[0]; px -= (firbuf[2] + firbuf[14]) * fcoeff[2]; px += (firbuf[4] + firbuf[12]) * fcoeff[4]; px -= (firbuf[6] + firbuf[10]) * fcoeff[6]; px += firbuf[8] * fcoeff[8]; px >>= 2; py = (firbuf[15] - firbuf[1]) * fcoeff[1]; py += (firbuf[3] - firbuf[13]) * fcoeff[3]; py += (firbuf[11] - firbuf[5]) * fcoeff[5]; py += (firbuf[7] - firbuf[9]) * fcoeff[7]; py >>= 2; /* dfft->src[smplptr].r = (neg) ? -px : px; dfft->src[smplptr].i = 0; smplptr++; */ /* rdptr = (rdptr+1) & 31; */ pamp = (px*px + py*py); /* do the second quarter : multiply with 0-0+0-0+0-0+0-0+0 for px, -0+0-0+0-0+0-0+0- for py */ qx = (firbuf[15+1] - firbuf[1+1]) * fcoeff[1]; qx += (firbuf[3+1] - firbuf[13+1]) * fcoeff[3]; qx += (firbuf[11+1] - firbuf[5+1]) * fcoeff[5]; qx += (firbuf[7+1] - firbuf[9+1]) * fcoeff[7]; qx >>= 2; qy = -(firbuf[0+1] + firbuf[16+1]) * fcoeff[0]; qy += (firbuf[2+1] + firbuf[14+1]) * fcoeff[2]; qy -= (firbuf[4+1] + firbuf[12+1]) * fcoeff[4]; qy += (firbuf[6+1] + firbuf[10+1]) * fcoeff[6]; qy -= firbuf[8+1] * fcoeff[8]; qy >>= 2; /* dfft->src[smplptr].r = (neg) ? -qx : qx; dfft->src[smplptr].i = 0; smplptr++; */ /* rdptr = (rdptr+1) & 31; */ qamp = (qx*qx + qy*qy + pamp) / 6400; if (qamp >= 65535) *decout = sqrt_hi[2047]; else if (qamp >= 2047) *decout = (unsigned char)sqrt_hi[qamp >> 5]; else *decout = (unsigned char)sqrt_lo[qamp]; /* printf("%3d %5d %5d\n", n, (px*px)/1024, (py*py)/1024); printf("%3d %5d %5d\n", n, *decout, qamp); n++; */ decout++; neg = ~neg; } } /* Encode a frequency sampled at 4kHz to an AF-signal sampled at 8kHz. This is done by shifting the phase by the appropriate value for each sample. */ void fm_modulate(char *codin, int incnt, char *smplout) { static int phs = 0; while (incnt-- > 0) { phs += 0x10000 + fmphsinc[*(unsigned char *)codin++]; phs &= 0x3ffff; *smplout++ = (char)sintab[phs >> 8]; phs += 0x10000 + fmphsinc[*(unsigned char *)codin++]; phs &= 0x3ffff; *smplout++ = (char)sintab[phs >> 8]; } } /* Encode an amplitude sampled at 4.8kHz to an AF-signal sampled at 9.6kHz. This is done by multiplying the amplitude-values with a sampled sine- function represented by a 0,1,0,-1 sequence. */ void am_modulate(char *codin, int incnt, char *smplout) { static int neg = 0; while (incnt-- > 0) { if (neg) { *smplout++ = amreal[*(unsigned char *)codin++]; *smplout++ = 128; /*amimag[*codin++]*/ } else { *smplout++ = amreal[*(unsigned char *)codin++] ^ 0xff; *smplout++ = 128; /*amimag[*codin++] ^ 0xff*/ } neg = ~neg; } }