2
ACfax - Fax reception with X11-interface for amateur radio
3
Copyright (C) 1995-1998 Andreas Czechanowski, DL4SDC
5
This program is free software; you can redistribute it and/or modify
6
it under the terms of the GNU General Public License as published by
7
the Free Software Foundation; either version 2 of the License, or
8
(at your option) any later version.
10
This program is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
GNU General Public License for more details.
15
You should have received a copy of the GNU General Public License
16
along with this program; if not, write to the Free Software Foundation,
17
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
andreas.czechanowski@ins.uni-stuttgart.de
23
* mod_demod.c - FM and AM modulator/demodulator for ACfax
30
#include "mod_demod.h"
32
SHORT int firwide[] = { 6, 20, 7, -42, -74, -12, 159, 353, 440 };
33
SHORT int firmiddle[] = { 0, -18, -38, -39, 0, 83, 191, 284, 320 };
34
SHORT int firnarrow[] = { -7, -18, -15, 11, 56, 116, 177, 223, 240 };
36
/* these some more coefficient tables were for testing only. */
37
/* int firwide[] = { 12, 24, 7, -42, -74, -12, 159, 353, 440 }; */
38
/* int firnarrow[] = { -16, -21, -15, 11, 56, 116, 177, 223, 240 }; */
39
/* int fircheap[] = { 0, 0, 0, 0, 0, 0, 36, 99, 128 }; */
40
/* int firtoobig[] = { -14, -12, 18, 40, -1, -80, -71, 114, 381, 510 } */
41
/* int firnotbad[] = { 11, 16, 1, -29, -44, -3, 98, 208, 256 }; */
43
static SHORT int *fcoeff = firmiddle;
44
static int *sqrt_lo; /* square-root lookup-table for AM demodulator */
45
static int *sqrt_hi; /* (splitted in 2 parts) */
46
static int *sintab; /* sine wave table for FM modulator */
47
static int *asntab; /* arcsin-table for FM-demodulator */
48
static int *fmphsinc; /* phase-increment-table for FM modulator */
49
static char *amreal; /* amplitude tables for AM modulator */
50
static int compval; /* comparator value for APT detecion */
53
void main(int argc, char **argv)
60
set_modem_param (FIL_MIDL, 255, 400);
61
while ((q = fread(indata, 1, 2048, stdin)) > 0) {
63
fm_demod(indata, q, outdata);
68
/* Initialize the modulator/demodulator functions. This function allocates
69
space for the arrays and lookup-tables
75
static int inited = 0;
79
sqrt_lo = (int *)malloc(2048*sizeof(int));
80
sqrt_hi = (int *)malloc(2048*sizeof(int));
81
if (!(sqrt_lo) || !(sqrt_hi)) {
82
perror("modem_init:sqrt_*");
85
amreal = (char *)malloc(258);
87
perror("modem_init:am{real|imag}");
90
sintab = (int *)malloc(1024*sizeof(int));
92
perror("modem_init:sintab");
95
asntab = (int *)malloc(512*sizeof(int));
97
perror("modem_init:asntab");
100
fmphsinc = (int *)malloc(258*sizeof(int));
101
for (i=0; i<1024; i++)
102
sintab[i] = 127.5 + 127*sin(i*PI/512.0);
107
/* set up a lookup-table for the given deviation which gives values from
108
0 to maxval with clipping beyond the edges for reception, initialize
109
a lookup-table for FM transmission.
111
void set_modem_param(int filter, int maxval, int devi)
116
/* initialize decoder first, if not already done */
118
/* do some range-checking */
119
if (devi < 100) devi = 100;
120
if (devi > 1200) devi = 1200;
121
if (maxval < 1) maxval = 1;
122
if (maxval > 255) maxval = 255;
123
phmax = 255.9 * sin(devi * PI / 4000.0) + 0.5;
124
for (i=0; i<512; i++) {
127
else if (i >= 256+phmax)
130
asntab[i] = maxval * 2000 / devi * asin((i-256.0)/256.5) / PI
133
for (i=0; i<2048; i++) {
134
sqrt_lo[i] = (sqrt(i) * maxval / 256.0) + 0.5;
135
sqrt_hi[i] = (sqrt(32*i) * maxval / 256.0) + 0.5;
137
for (i=0; i<=maxval; i++)
138
fmphsinc[i] = devi / 2000.0 * 65536;
139
for (i=0; i<= maxval; i++)
140
amreal[i] = 127.5 + 127.5*i/maxval;
141
compval = maxval >> 1;
145
fprintf(stderr, "selecting narrow filter\n");
149
fprintf(stderr, "selecting middle filter\n");
153
fprintf(stderr, "selecting wide filter\n");
158
/* Demodulate the samples taken with 8kHz to a frequency sampled with 4kHz.
159
This is done with a delay-demodulator :
161
+----------+ +-------+ +----+ +---+
162
+->|*sin(2kHz)|->|FIR-LPF|--*->|z^-1|->|mul|--+
163
| +----------+ +-------+ | +----+ +---+ |
166
| +---------+ \ / +---+ +---+
167
<input>--+ AM^2<--|amp^2 det| X |add|->|div|--<FM-out>
168
| +---------+ / \ +---+ +---+
171
| +----------+ +-------+ | +----+ +---+ |
172
+->|*cos(2kHz)|->|FIR-LPF|--*->|z^-1|->|mul|--+
173
+----------+ +-------+ +----+ +---+
175
The cosine-signal is simply a sequence of 1,0,-1,0 , and the sine-
176
signal is a sequence of 0,1,0,-1 values because the frequency of
177
these is exactly a fouth of the sample-rate. The values multiplied by
178
zero need not be evaluated by the FIR-filter, what reduces the
179
calculation-effort to one half. Taps of the FIR-chain which are to
180
be multiplied with the same coefficient (possible due to the
181
symmetry of the pulse-response) are first added or subtracted,
182
so again reducing the number of multiplications to one half.
184
NOTE: incnt must be a multiple of 2 to operate properly !
185
output range is determined by asntab, which is set up by
186
set_modem_param. This table also performs the arcsin-correction
187
and clips values that are beyond the given deviation.
189
void fm_demod(char *smplin, int incnt, char *decout)
191
static SHORT int firbuf[32]; /* buffer holding the input-values */
192
/* static int inptr = 0; / pointer for data-entry into firbuf */
193
/* static int rdptr = 0; / pointer for data-output from firbuf */
194
static int neg = 0; /* flag changing every 2 sample-points */
195
static int px, py, qx, qy; /* the filtered output-results */
196
static int pamp, qamp, pfrq, qfrq;
204
/* shift buffer "left" by 2 : copy firbuf[2..17] to firbuf[0..15] */
205
memcpy(firbuf, firbuf+2, (16*sizeof(int)));
206
/* enter 2 new samle-points into buffer */
207
firbuf[16] = *(unsigned char *)smplin++ - 128;
208
firbuf[17] = *(unsigned char *)smplin++ - 128;
211
/* do the first quarter : multiply with +0-0+0-0+0-0+0-0+ for px
212
and 0-0+0-0+0-0+0-0+0 for py.
213
2 sample-periodes later, multiply with 0-0+0 for px and -0+0- for py,
214
what is done using the neg variable as flag. In this case, the result
215
for frequency is negated (amplitude is always positive) */
216
px = (firbuf[0] + firbuf[16]) * fcoeff[0];
217
px -= (firbuf[2] + firbuf[14]) * fcoeff[2];
218
px += (firbuf[4] + firbuf[12]) * fcoeff[4];
219
px -= (firbuf[6] + firbuf[10]) * fcoeff[6];
220
px += (firbuf[8]) * fcoeff[8];
222
py = (firbuf[15] - firbuf[1]) * fcoeff[1];
223
py += (firbuf[3] - firbuf[13]) * fcoeff[3];
224
py += (firbuf[11] - firbuf[5]) * fcoeff[5];
225
py += (firbuf[7] - firbuf[9]) * fcoeff[7];
228
dfft->src[smplptr].r = (neg) ? -px : px;
229
dfft->src[smplptr].i = 0;
232
/* rdptr = (rdptr+1) & 31; */
233
pamp = ((px*px + py*py) >> 8) + 1;
234
pfrq = (px*qy - py*qx) / (pamp + qamp);
236
/* do the second quarter : multiply with 0-0+0-0+0-0+0-0+0 for px,
237
-0+0-0+0-0+0-0+0- for py */
238
qx = (firbuf[15+1] - firbuf[1+1]) * fcoeff[1];
239
qx += (firbuf[3+1] - firbuf[13+1]) * fcoeff[3];
240
qx += (firbuf[11+1] - firbuf[5+1]) * fcoeff[5];
241
qx += (firbuf[7+1] - firbuf[9+1]) * fcoeff[7];
243
qy = -(firbuf[0+1] + firbuf[16+1]) * fcoeff[0];
244
qy += (firbuf[2+1] + firbuf[14+1]) * fcoeff[2];
245
qy -= (firbuf[4+1] + firbuf[12+1]) * fcoeff[4];
246
qy += (firbuf[6+1] + firbuf[10+1]) * fcoeff[6];
247
qy -= (firbuf[8+1]) * fcoeff[8];
250
dfft->src[smplptr].r = (neg) ? -qx : qx;
251
dfft->src[smplptr].i = 0;
254
/* rdptr = (rdptr+1) & 31; */
255
qamp = ((qx*qx + qy*qy) >> 8) + 1;
256
qfrq = (px*qy - py*qx) / (qamp + pamp);
258
/* asntab gives values from minval to maxval for given deviation */
259
*decout = (unsigned char)asntab[(pfrq+qfrq+256) & 511];
261
printf("%3d %5d %5d\n", n, (px*qy)/1024, (py*qx)/1024);
262
printf("%3d %5d %5d\n", n, *decout, pamp+qamp);
270
for (smplptr=0; smplptr<1024; smplptr++) {
271
dx = dfft->dest[smplptr].r;
272
dy = dfft->dest[smplptr].i;
273
printf("%4.1f %3.1f\n", smplptr * 3.906, 20*log10(sqrt(dx*dx + dy*dy)));
279
/* Demodulate the samples taken with 9.6kHz to an amplitude sampled with 4.8kHz.
280
This done with a synchronous demodulator :
282
+------------+ +-------+ +------+
283
+->|*sin(2.4kHz)|->|FIR-LPF|->|square|-+
284
| +------------+ +-------+ +------+ |
288
<input>--+ |adder|->|sq.root|--<AM-out>
292
| +------------+ +-------+ +------+ |
293
+->|*cos(2.4kHz)|->|FIR-LPF|->|square|-+
294
+------------+ +-------+ +------+
296
The cosine-signal is simply a sequence of 1,0,-1,0 , and the sine-
297
signal is a sequence of 0,1,0,-1 values because the frequency of
298
these is exactly a fouth of the sample-rate. The values multiplied by
299
zero need not be evaluated by the FIR-filter, what reduces the
300
calculation-effort to one half.
302
NOTE: incnt must be a multiple of 2 to operate properly !
303
output range is determined by sqrt_lo and sqrt_hi, which
304
are set up by set_modem_param.
306
void am_demod(char *smplin, int incnt, char *decout)
308
static SHORT int firbuf[32]; /* buffer holding the input-values */
309
/* static int inptr = 0; / pointer for data-entry into firbuf */
310
/* static int rdptr = 0; / pointer for data-output from firbuf */
311
static int neg = 0; /* flag changing every 2 sample-points */
312
static int px, py, qx, qy; /* the filtered output-results */
313
static int pamp, qamp;
321
/* shift buffer "left" by 2 : copy firbuf[2..17] to firbuf[0..15] */
322
memcpy(firbuf, firbuf+2, (16*sizeof(int)));
323
/* enter 2 new samle-points into buffer */
324
firbuf[16] = *(unsigned char *)smplin++ - 128;
325
firbuf[17] = *(unsigned char *)smplin++ - 128;
328
/* do the first quarter : multiply with +0-0+0-0+0-0+0-0+ for px
329
and 0-0+0-0+0-0+0-0+0 for py.
330
2 sample-periodes later, multiply with 0-0+0 for px and -0+0- for py,
331
what is done using the neg variable as flag. In this case, the result
332
for frequency is negated (amplitude is always positive) */
333
px = (firbuf[0] + firbuf[16]) * fcoeff[0];
334
px -= (firbuf[2] + firbuf[14]) * fcoeff[2];
335
px += (firbuf[4] + firbuf[12]) * fcoeff[4];
336
px -= (firbuf[6] + firbuf[10]) * fcoeff[6];
337
px += firbuf[8] * fcoeff[8];
339
py = (firbuf[15] - firbuf[1]) * fcoeff[1];
340
py += (firbuf[3] - firbuf[13]) * fcoeff[3];
341
py += (firbuf[11] - firbuf[5]) * fcoeff[5];
342
py += (firbuf[7] - firbuf[9]) * fcoeff[7];
345
dfft->src[smplptr].r = (neg) ? -px : px;
346
dfft->src[smplptr].i = 0;
349
/* rdptr = (rdptr+1) & 31; */
350
pamp = (px*px + py*py);
352
/* do the second quarter : multiply with 0-0+0-0+0-0+0-0+0 for px,
353
-0+0-0+0-0+0-0+0- for py */
354
qx = (firbuf[15+1] - firbuf[1+1]) * fcoeff[1];
355
qx += (firbuf[3+1] - firbuf[13+1]) * fcoeff[3];
356
qx += (firbuf[11+1] - firbuf[5+1]) * fcoeff[5];
357
qx += (firbuf[7+1] - firbuf[9+1]) * fcoeff[7];
359
qy = -(firbuf[0+1] + firbuf[16+1]) * fcoeff[0];
360
qy += (firbuf[2+1] + firbuf[14+1]) * fcoeff[2];
361
qy -= (firbuf[4+1] + firbuf[12+1]) * fcoeff[4];
362
qy += (firbuf[6+1] + firbuf[10+1]) * fcoeff[6];
363
qy -= firbuf[8+1] * fcoeff[8];
366
dfft->src[smplptr].r = (neg) ? -qx : qx;
367
dfft->src[smplptr].i = 0;
370
/* rdptr = (rdptr+1) & 31; */
371
qamp = (qx*qx + qy*qy + pamp) / 6400;
374
*decout = sqrt_hi[2047];
375
else if (qamp >= 2047)
376
*decout = (unsigned char)sqrt_hi[qamp >> 5];
378
*decout = (unsigned char)sqrt_lo[qamp];
381
printf("%3d %5d %5d\n", n, (px*px)/1024, (py*py)/1024);
382
printf("%3d %5d %5d\n", n, *decout, qamp);
390
/* Encode a frequency sampled at 4kHz to an AF-signal sampled at 8kHz.
391
This is done by shifting the phase by the appropriate value for each
394
void fm_modulate(char *codin, int incnt, char *smplout)
398
while (incnt-- > 0) {
399
phs += 0x10000 + fmphsinc[*(unsigned char *)codin++];
401
*smplout++ = (char)sintab[phs >> 8];
402
phs += 0x10000 + fmphsinc[*(unsigned char *)codin++];
404
*smplout++ = (char)sintab[phs >> 8];
408
/* Encode an amplitude sampled at 4.8kHz to an AF-signal sampled at 9.6kHz.
409
This is done by multiplying the amplitude-values with a sampled sine-
410
function represented by a 0,1,0,-1 sequence.
412
void am_modulate(char *codin, int incnt, char *smplout)
416
while (incnt-- > 0) {
418
*smplout++ = amreal[*(unsigned char *)codin++];
419
*smplout++ = 128; /*amimag[*codin++]*/
421
*smplout++ = amreal[*(unsigned char *)codin++] ^ 0xff;
422
*smplout++ = 128; /*amimag[*codin++] ^ 0xff*/