1 //
2 // This file is part of Dire Wolf, an amateur radio packet TNC.
3 //
4 // Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2019 John Langner, WB2OSZ
5 //
6 // This program is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 2 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18 //
19
20
21
22 /*------------------------------------------------------------------
23 *
24 * Module: demod.c
25 *
26 * Purpose: Common entry point for multiple types of demodulators.
27 *
28 * Input: Audio samples from either a file or the "sound card."
29 *
30 * Outputs: Calls hdlc_rec_bit() for each bit demodulated.
31 *
32 *---------------------------------------------------------------*/
33
34 #include "direwolf.h"
35
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <math.h>
39 #include <unistd.h>
40 #include <sys/stat.h>
41 #include <string.h>
42 #include <assert.h>
43 #include <ctype.h>
44
45 #include "audio.h"
46 #include "demod.h"
47 #include "tune.h"
48 #include "fsk_demod_state.h"
49 #include "fsk_gen_filter.h"
50 #include "hdlc_rec.h"
51 #include "textcolor.h"
52 #include "demod_9600.h"
53 #include "demod_afsk.h"
54 #include "demod_psk.h"
55
56
57
58 // Properties of the radio channels.
59
60 static struct audio_s *save_audio_config_p;
61
62
63 // TODO: temp experiment.
64
65
66 static int zerostuff = 1; // temp experiment.
67
68 // Current state of all the decoders.
69
70 static struct demodulator_state_s demodulator_state[MAX_CHANS][MAX_SUBCHANS];
71
72
73 static int sample_sum[MAX_CHANS][MAX_SUBCHANS];
74 static int sample_count[MAX_CHANS][MAX_SUBCHANS];
75
76
77 /*------------------------------------------------------------------
78 *
79 * Name: demod_init
80 *
81 * Purpose: Initialize the demodulator(s) used for reception.
82 *
83 * Inputs: pa - Pointer to audio_s structure with
84 * various parameters for the modem(s).
85 *
86 * Returns: 0 for success, -1 for failure.
87 *
88 *
89 * Bugs: This doesn't do much error checking so don't give it
90 * anything crazy.
91 *
92 *----------------------------------------------------------------*/
93
demod_init(struct audio_s * pa)94 int demod_init (struct audio_s *pa)
95 {
96 int chan; /* Loop index over number of radio channels. */
97 char profile;
98
99
100
101 /*
102 * Save audio configuration for later use.
103 */
104
105 save_audio_config_p = pa;
106
107 for (chan = 0; chan < MAX_CHANS; chan++) {
108
109 if (save_audio_config_p->achan[chan].medium == MEDIUM_RADIO) {
110
111 char *p;
112 char just_letters[16];
113 int num_letters;
114 int have_plus;
115
116 /*
117 * These are derived from config file parameters.
118 *
119 * num_subchan is number of demodulators.
120 * This can be increased by:
121 * Multiple frequencies.
122 * Multiple letters (not sure if I will continue this).
123 *
124 * num_slicers is set to max by the "+" option.
125 */
126
127 save_audio_config_p->achan[chan].num_subchan = 1;
128 save_audio_config_p->achan[chan].num_slicers = 1;
129
130 switch (save_audio_config_p->achan[chan].modem_type) {
131
132 case MODEM_OFF:
133 break;
134
135 case MODEM_AFSK:
136 case MODEM_EAS:
137
138 if (save_audio_config_p->achan[chan].modem_type == MODEM_EAS) {
139 if (save_audio_config_p->achan[chan].fix_bits != RETRY_NONE) {
140 text_color_set(DW_COLOR_INFO);
141 dw_printf ("Channel %d: FIX_BITS option has been turned off for EAS.\n", chan);
142 save_audio_config_p->achan[chan].fix_bits = RETRY_NONE;
143 }
144 if (save_audio_config_p->achan[chan].passall != 0) {
145 text_color_set(DW_COLOR_INFO);
146 dw_printf ("Channel %d: PASSALL option has been turned off for EAS.\n", chan);
147 save_audio_config_p->achan[chan].passall = 0;
148 }
149 }
150
151 /*
152 * Tear apart the profile and put it back together in a normalized form:
153 * - At least one letter, supply suitable default if necessary.
154 * - Upper case only.
155 * - Any plus will be at the end.
156 */
157 num_letters = 0;
158 just_letters[num_letters] = '\0';
159 have_plus = 0;
160 for (p = save_audio_config_p->achan[chan].profiles; *p != '\0'; p++) {
161
162 if (islower(*p)) {
163 just_letters[num_letters] = toupper(*p);
164 num_letters++;
165 just_letters[num_letters] = '\0';
166 }
167
168 else if (isupper(*p)) {
169 just_letters[num_letters] = *p;
170 num_letters++;
171 just_letters[num_letters] = '\0';
172 }
173
174 else if (*p == '+') {
175 have_plus = 1;
176 if (p[1] != '\0') {
177 text_color_set(DW_COLOR_ERROR);
178 dw_printf ("Channel %d: + option must appear at end of demodulator types \"%s\" \n",
179 chan, save_audio_config_p->achan[chan].profiles);
180 }
181 }
182
183 else if (*p == '-') {
184 have_plus = -1;
185 if (p[1] != '\0') {
186 text_color_set(DW_COLOR_ERROR);
187 dw_printf ("Channel %d: - option must appear at end of demodulator types \"%s\" \n",
188 chan, save_audio_config_p->achan[chan].profiles);
189 }
190
191 } else {
192 text_color_set(DW_COLOR_ERROR);
193 dw_printf ("Channel %d: Demodulator types \"%s\" can contain only letters and + - characters.\n",
194 chan, save_audio_config_p->achan[chan].profiles);
195 }
196 }
197
198 assert (num_letters == (int)(strlen(just_letters)));
199
200 /*
201 * Pick a good default demodulator if none specified.
202 */
203 if (num_letters == 0) {
204
205 if (save_audio_config_p->achan[chan].baud < 600) {
206
207 /* This has been optimized for 300 baud. */
208
209 strlcpy (just_letters, "D", sizeof(just_letters));
210
211 }
212 else {
213 #if __arm__
214 /* We probably don't have a lot of CPU power available. */
215 /* Previously we would use F if possible otherwise fall back to A. */
216
217 /* In version 1.2, new default is E+ /3. */
218 strlcpy (just_letters, "E", sizeof(just_letters)); // version 1.2 now E.
219 if (have_plus != -1) have_plus = 1; // Add as default for version 1.2
220 // If not explicitly turned off.
221 if (save_audio_config_p->achan[chan].decimate == 0) {
222 if (save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec > 40000) {
223 save_audio_config_p->achan[chan].decimate = 3;
224 }
225 }
226 #else
227 strlcpy (just_letters, "E", sizeof(just_letters)); // version 1.2 changed C to E.
228 if (have_plus != -1) have_plus = 1; // Add as default for version 1.2
229 // If not explicitly turned off.
230 #endif
231 }
232 num_letters = 1;
233 }
234
235
236 assert (num_letters == (int)(strlen(just_letters)));
237
238 /*
239 * Put it back together again.
240 */
241
242 /* At this point, have_plus can have 3 values: */
243 /* 1 = turned on, either explicitly or by applied default */
244 /* -1 = explicitly turned off. change to 0 here so it is false. */
245 /* 0 = off by default. */
246
247 if (have_plus == -1) have_plus = 0;
248
249 strlcpy (save_audio_config_p->achan[chan].profiles, just_letters, sizeof(save_audio_config_p->achan[chan].profiles));
250
251 assert (strlen(save_audio_config_p->achan[chan].profiles) >= 1);
252
253 if (have_plus) {
254 strlcat (save_audio_config_p->achan[chan].profiles, "+", sizeof(save_audio_config_p->achan[chan].profiles));
255 }
256
257 /* These can be increased later for the multi-frequency case. */
258
259 save_audio_config_p->achan[chan].num_subchan = num_letters;
260 save_audio_config_p->achan[chan].num_slicers = 1;
261
262 /*
263 * Some error checking - Can use only one of these:
264 *
265 * - Multiple letters.
266 * - New + multi-slicer.
267 * - Multiple frequencies.
268 */
269
270 if (have_plus && save_audio_config_p->achan[chan].num_freq > 1) {
271
272 text_color_set(DW_COLOR_ERROR);
273 dw_printf ("Channel %d: Demodulator + option can't be combined with multiple frequencies.\n", chan);
274 save_audio_config_p->achan[chan].num_subchan = 1; // Will be set higher later.
275 save_audio_config_p->achan[chan].num_freq = 1;
276 }
277
278 if (num_letters > 1 && save_audio_config_p->achan[chan].num_freq > 1) {
279
280 text_color_set(DW_COLOR_ERROR);
281 dw_printf ("Channel %d: Multiple demodulator types can't be combined with multiple frequencies.\n", chan);
282
283 save_audio_config_p->achan[chan].profiles[1] = '\0';
284 num_letters = 1;
285 }
286
287 if (save_audio_config_p->achan[chan].decimate == 0) {
288 save_audio_config_p->achan[chan].decimate = 1;
289 if (strchr (just_letters, 'D') != NULL && save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec > 40000) {
290 save_audio_config_p->achan[chan].decimate = 3;
291 }
292 }
293
294 text_color_set(DW_COLOR_DEBUG);
295 dw_printf ("Channel %d: %d baud, AFSK %d & %d Hz, %s, %d sample rate",
296 chan, save_audio_config_p->achan[chan].baud,
297 save_audio_config_p->achan[chan].mark_freq, save_audio_config_p->achan[chan].space_freq,
298 save_audio_config_p->achan[chan].profiles,
299 save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec);
300 if (save_audio_config_p->achan[chan].decimate != 1)
301 dw_printf (" / %d", save_audio_config_p->achan[chan].decimate);
302 if (save_audio_config_p->achan[chan].dtmf_decode != DTMF_DECODE_OFF)
303 dw_printf (", DTMF decoder enabled");
304 dw_printf (".\n");
305
306
307 /*
308 * Initialize the demodulator(s).
309 *
310 * We have 3 cases to consider.
311 */
312
313 // TODO1.3: revisit this logic now that it is less restrictive.
314
315 if (num_letters > 1) {
316 int d;
317
318 /*
319 * Multiple letters, usually for 1200 baud.
320 * Each one corresponds to a demodulator and subchannel.
321 *
322 * An interesting experiment but probably not too useful.
323 * Can't have multiple frequency pairs.
324 * In version 1.3 this can be combined with the + option.
325 */
326
327 save_audio_config_p->achan[chan].num_subchan = num_letters;
328
329 if (save_audio_config_p->achan[chan].num_subchan != num_letters) {
330 text_color_set(DW_COLOR_ERROR);
331 dw_printf ("INTERNAL ERROR, %s:%d, chan=%d, num_subchan(%d) != strlen(\"%s\")\n",
332 __FILE__, __LINE__, chan, save_audio_config_p->achan[chan].num_subchan, save_audio_config_p->achan[chan].profiles);
333 }
334
335 if (save_audio_config_p->achan[chan].num_freq != 1) {
336 text_color_set(DW_COLOR_ERROR);
337 dw_printf ("INTERNAL ERROR, %s:%d, chan=%d, num_freq(%d) != 1\n",
338 __FILE__, __LINE__, chan, save_audio_config_p->achan[chan].num_freq);
339 }
340
341 for (d = 0; d < save_audio_config_p->achan[chan].num_subchan; d++) {
342 int mark, space;
343 assert (d >= 0 && d < MAX_SUBCHANS);
344
345 struct demodulator_state_s *D;
346 D = &demodulator_state[chan][d];
347
348 profile = save_audio_config_p->achan[chan].profiles[d];
349 mark = save_audio_config_p->achan[chan].mark_freq;
350 space = save_audio_config_p->achan[chan].space_freq;
351
352 if (save_audio_config_p->achan[chan].num_subchan != 1) {
353 text_color_set(DW_COLOR_DEBUG);
354 dw_printf (" %d.%d: %c %d & %d\n", chan, d, profile, mark, space);
355 }
356
357 demod_afsk_init (save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec / save_audio_config_p->achan[chan].decimate,
358 save_audio_config_p->achan[chan].baud,
359 mark,
360 space,
361 profile,
362 D);
363
364 if (have_plus) {
365 /* I'm not happy about putting this hack here. */
366 /* should pass in as a parameter rather than adding on later. */
367
368 save_audio_config_p->achan[chan].num_slicers = MAX_SLICERS;
369 D->num_slicers = MAX_SLICERS;
370 }
371
372 /* For signal level reporting, we want a longer term view. */
373 // TODO: Should probably move this into the init functions.
374
375 D->quick_attack = D->agc_fast_attack * 0.2f;
376 D->sluggish_decay = D->agc_slow_decay * 0.2f;
377 }
378 }
379 else if (have_plus) {
380
381 /*
382 * PLUS - which (formerly) implies we have only one letter and one frequency pair.
383 *
384 * One demodulator feeds multiple slicers, each a subchannel.
385 */
386
387 if (num_letters != 1) {
388 text_color_set(DW_COLOR_ERROR);
389 dw_printf ("INTERNAL ERROR, %s:%d, chan=%d, strlen(\"%s\") != 1\n",
390 __FILE__, __LINE__, chan, just_letters);
391 }
392
393 if (save_audio_config_p->achan[chan].num_freq != 1) {
394 text_color_set(DW_COLOR_ERROR);
395 dw_printf ("INTERNAL ERROR, %s:%d, chan=%d, num_freq(%d) != 1\n",
396 __FILE__, __LINE__, chan, save_audio_config_p->achan[chan].num_freq);
397 }
398
399 if (save_audio_config_p->achan[chan].num_freq != save_audio_config_p->achan[chan].num_subchan) {
400 text_color_set(DW_COLOR_ERROR);
401 dw_printf ("INTERNAL ERROR, %s:%d, chan=%d, num_freq(%d) != num_subchan(%d)\n",
402 __FILE__, __LINE__, chan, save_audio_config_p->achan[chan].num_freq, save_audio_config_p->achan[chan].num_subchan);
403 }
404
405 struct demodulator_state_s *D;
406 D = &demodulator_state[chan][0];
407
408 /* I'm not happy about putting this hack here. */
409 /* This belongs in demod_afsk_init but it doesn't have access to the audio config. */
410
411 save_audio_config_p->achan[chan].num_slicers = MAX_SLICERS;
412
413 demod_afsk_init (save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec / save_audio_config_p->achan[chan].decimate,
414 save_audio_config_p->achan[chan].baud,
415 save_audio_config_p->achan[chan].mark_freq,
416 save_audio_config_p->achan[chan].space_freq,
417 save_audio_config_p->achan[chan].profiles[0],
418 D);
419
420 if (have_plus) {
421 /* I'm not happy about putting this hack here. */
422 /* should pass in as a parameter rather than adding on later. */
423
424 save_audio_config_p->achan[chan].num_slicers = MAX_SLICERS;
425 D->num_slicers = MAX_SLICERS;
426 }
427
428 /* For signal level reporting, we want a longer term view. */
429
430 D->quick_attack = D->agc_fast_attack * 0.2f;
431 D->sluggish_decay = D->agc_slow_decay * 0.2f;
432 }
433 else {
434 int d;
435 /*
436 * One letter.
437 * Can be combined with multiple frequencies.
438 */
439
440 if (num_letters != 1) {
441 text_color_set(DW_COLOR_ERROR);
442 dw_printf ("INTERNAL ERROR, %s:%d, chan=%d, strlen(\"%s\") != 1\n",
443 __FILE__, __LINE__, chan, save_audio_config_p->achan[chan].profiles);
444 }
445
446 save_audio_config_p->achan[chan].num_subchan = save_audio_config_p->achan[chan].num_freq;
447
448 for (d = 0; d < save_audio_config_p->achan[chan].num_freq; d++) {
449
450 int mark, space, k;
451 assert (d >= 0 && d < MAX_SUBCHANS);
452
453 struct demodulator_state_s *D;
454 D = &demodulator_state[chan][d];
455
456 profile = save_audio_config_p->achan[chan].profiles[0];
457
458 k = d * save_audio_config_p->achan[chan].offset - ((save_audio_config_p->achan[chan].num_freq - 1) * save_audio_config_p->achan[chan].offset) / 2;
459 mark = save_audio_config_p->achan[chan].mark_freq + k;
460 space = save_audio_config_p->achan[chan].space_freq + k;
461
462 if (save_audio_config_p->achan[chan].num_freq != 1) {
463 text_color_set(DW_COLOR_DEBUG);
464 dw_printf (" %d.%d: %c %d & %d\n", chan, d, profile, mark, space);
465 }
466
467 demod_afsk_init (save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec / save_audio_config_p->achan[chan].decimate,
468 save_audio_config_p->achan[chan].baud,
469 mark, space,
470 profile,
471 D);
472
473 if (have_plus) {
474 /* I'm not happy about putting this hack here. */
475 /* should pass in as a parameter rather than adding on later. */
476
477 save_audio_config_p->achan[chan].num_slicers = MAX_SLICERS;
478 D->num_slicers = MAX_SLICERS;
479 }
480
481 /* For signal level reporting, we want a longer term view. */
482
483 D->quick_attack = D->agc_fast_attack * 0.2f;
484 D->sluggish_decay = D->agc_slow_decay * 0.2f;
485
486 } /* for each freq pair */
487 }
488 break;
489
490 case MODEM_QPSK: // New for 1.4
491
492 // In versions 1.4 and 1.5, V.26 "Alternative A" was used.
493 // years later, I discover that the MFJ-2400 used "Alternative B."
494 // It looks like the other two manufacturers use the same but we
495 // can't be sure until we find one for compatbility testing.
496
497 // In version 1.6 we add a choice for the user.
498 // If neither one was explicitly specified, print a message and take
499 // a default. My current thinking is that we default to direwolf <= 1.5
500 // compatible for version 1.6 and MFJ compatible after that.
501
502 if (save_audio_config_p->achan[chan].v26_alternative == V26_UNSPECIFIED) {
503
504 text_color_set(DW_COLOR_ERROR);
505 dw_printf ("Two incompatible versions of 2400 bps QPSK are now available.\n");
506 dw_printf ("For compatbility with direwolf <= 1.5, use 'V26A' modem option in config file.\n");
507 dw_printf ("For compatbility MFJ-2400 use 'V26B' modem option in config file.\n");
508 dw_printf ("Command line options -j and -J can be used for channel 0.\n");
509 dw_printf ("For more information, read the Dire Wolf User Guide and\n");
510 dw_printf ("2400-4800-PSK-for-APRS-Packet-Radio.pdf.\n");
511 dw_printf ("The default is now MFJ-2400 compatibility mode.\n");
512
513 save_audio_config_p->achan[chan].v26_alternative = V26_DEFAULT;
514 }
515
516
517 // TODO: See how much CPU this takes on ARM and decide if we should have different defaults.
518
519 if (strlen(save_audio_config_p->achan[chan].profiles) == 0) {
520 //#if __arm__
521 // strlcpy (save_audio_config_p->achan[chan].profiles, "R", sizeof(save_audio_config_p->achan[chan].profiles));
522 //#else
523 strlcpy (save_audio_config_p->achan[chan].profiles, "PQRS", sizeof(save_audio_config_p->achan[chan].profiles));
524 //#endif
525 }
526 save_audio_config_p->achan[chan].num_subchan = strlen(save_audio_config_p->achan[chan].profiles);
527
528 save_audio_config_p->achan[chan].decimate = 1; // think about this later.
529 text_color_set(DW_COLOR_DEBUG);
530 dw_printf ("Channel %d: %d bps, QPSK, %s, %d sample rate",
531 chan, save_audio_config_p->achan[chan].baud,
532 save_audio_config_p->achan[chan].profiles,
533 save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec);
534 if (save_audio_config_p->achan[chan].decimate != 1)
535 dw_printf (" / %d", save_audio_config_p->achan[chan].decimate);
536
537 if (save_audio_config_p->achan[chan].v26_alternative == V26_B)
538 dw_printf (", compatible with MFJ-2400");
539 else
540 dw_printf (", compatible with earlier direwolf");
541
542 if (save_audio_config_p->achan[chan].dtmf_decode != DTMF_DECODE_OFF)
543 dw_printf (", DTMF decoder enabled");
544 dw_printf (".\n");
545
546 int d;
547 for (d = 0; d < save_audio_config_p->achan[chan].num_subchan; d++) {
548
549 assert (d >= 0 && d < MAX_SUBCHANS);
550 struct demodulator_state_s *D;
551 D = &demodulator_state[chan][d];
552 profile = save_audio_config_p->achan[chan].profiles[d];
553
554 //text_color_set(DW_COLOR_DEBUG);
555 //dw_printf ("About to call demod_psk_init for Q-PSK case, modem_type=%d, profile='%c'\n",
556 // save_audio_config_p->achan[chan].modem_type, profile);
557
558 demod_psk_init (save_audio_config_p->achan[chan].modem_type,
559 save_audio_config_p->achan[chan].v26_alternative,
560 save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec / save_audio_config_p->achan[chan].decimate,
561 save_audio_config_p->achan[chan].baud,
562 profile,
563 D);
564
565 //text_color_set(DW_COLOR_DEBUG);
566 //dw_printf ("Returned from demod_psk_init\n");
567
568 /* For signal level reporting, we want a longer term view. */
569 /* Guesses based on 9600. Maybe revisit someday. */
570
571 D->quick_attack = 0.080 * 0.2;
572 D->sluggish_decay = 0.00012 * 0.2;
573 }
574 break;
575
576 case MODEM_8PSK: // New for 1.4
577
578 // TODO: See how much CPU this takes on ARM and decide if we should have different defaults.
579
580 if (strlen(save_audio_config_p->achan[chan].profiles) == 0) {
581 //#if __arm__
582 // strlcpy (save_audio_config_p->achan[chan].profiles, "V", sizeof(save_audio_config_p->achan[chan].profiles));
583 //#else
584 strlcpy (save_audio_config_p->achan[chan].profiles, "TUVW", sizeof(save_audio_config_p->achan[chan].profiles));
585 //#endif
586 }
587 save_audio_config_p->achan[chan].num_subchan = strlen(save_audio_config_p->achan[chan].profiles);
588
589 save_audio_config_p->achan[chan].decimate = 1; // think about this later
590 text_color_set(DW_COLOR_DEBUG);
591 dw_printf ("Channel %d: %d bps, 8PSK, %s, %d sample rate",
592 chan, save_audio_config_p->achan[chan].baud,
593 save_audio_config_p->achan[chan].profiles,
594 save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec);
595 if (save_audio_config_p->achan[chan].decimate != 1)
596 dw_printf (" / %d", save_audio_config_p->achan[chan].decimate);
597 if (save_audio_config_p->achan[chan].dtmf_decode != DTMF_DECODE_OFF)
598 dw_printf (", DTMF decoder enabled");
599 dw_printf (".\n");
600
601 //int d;
602 for (d = 0; d < save_audio_config_p->achan[chan].num_subchan; d++) {
603
604 assert (d >= 0 && d < MAX_SUBCHANS);
605 struct demodulator_state_s *D;
606 D = &demodulator_state[chan][d];
607 profile = save_audio_config_p->achan[chan].profiles[d];
608
609 //text_color_set(DW_COLOR_DEBUG);
610 //dw_printf ("About to call demod_psk_init for 8-PSK case, modem_type=%d, profile='%c'\n",
611 // save_audio_config_p->achan[chan].modem_type, profile);
612
613 demod_psk_init (save_audio_config_p->achan[chan].modem_type,
614 save_audio_config_p->achan[chan].v26_alternative,
615 save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec / save_audio_config_p->achan[chan].decimate,
616 save_audio_config_p->achan[chan].baud,
617 profile,
618 D);
619
620 //text_color_set(DW_COLOR_DEBUG);
621 //dw_printf ("Returned from demod_psk_init\n");
622
623 /* For signal level reporting, we want a longer term view. */
624 /* Guesses based on 9600. Maybe revisit someday. */
625
626 D->quick_attack = 0.080 * 0.2;
627 D->sluggish_decay = 0.00012 * 0.2;
628 }
629 break;
630
631 //TODO: how about MODEM_OFF case?
632
633 case MODEM_BASEBAND:
634 case MODEM_SCRAMBLE:
635 case MODEM_AIS:
636 default: /* Not AFSK */
637 {
638
639 // For AIS we will accept only a good CRC without any fixup attempts.
640 // Even with that, there are still a lot of CRC false matches with random noise.
641
642 if (save_audio_config_p->achan[chan].modem_type == MODEM_AIS) {
643 if (save_audio_config_p->achan[chan].fix_bits != RETRY_NONE) {
644 text_color_set(DW_COLOR_INFO);
645 dw_printf ("Channel %d: FIX_BITS option has been turned off for AIS.\n", chan);
646 save_audio_config_p->achan[chan].fix_bits = RETRY_NONE;
647 }
648 if (save_audio_config_p->achan[chan].passall != 0) {
649 text_color_set(DW_COLOR_INFO);
650 dw_printf ("Channel %d: PASSALL option has been turned off for AIS.\n", chan);
651 save_audio_config_p->achan[chan].passall = 0;
652 }
653 }
654
655 if (strcmp(save_audio_config_p->achan[chan].profiles, "") == 0) {
656
657 /* Apply default if not set earlier. */
658 /* Not sure if it should be on for ARM too. */
659 /* Need to take a look at CPU usage and performance difference. */
660
661 /* Version 1.5: Remove special case for ARM. */
662 /* We want higher performance to be the default. */
663 /* "MODEM 9600 -" can be used on very slow CPU if necessary. */
664
665 strlcpy (save_audio_config_p->achan[chan].profiles, "+", sizeof(save_audio_config_p->achan[chan].profiles));
666 }
667
668
669 #ifdef TUNE_ZEROSTUFF
670 zerostuff = TUNE_ZEROSTUFF;
671 #endif
672
673
674 /*
675 * We need a minimum number of audio samples per bit time for good performance.
676 * Easier to check here because demod_9600_init might have an adjusted sample rate.
677 */
678
679 float ratio = (float)(save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec)
680 / (float)(save_audio_config_p->achan[chan].baud);
681
682 /*
683 * Set reasonable upsample ratio if user did not override.
684 */
685
686 if (save_audio_config_p->achan[chan].upsample == 0) {
687
688 if (ratio < 5) {
689
690 // example: 44100 / 9600 is 4.59
691 // Big improvement with x2.
692 // x4 seems to work the best.
693 // The other parameters are not as touchy.
694 // Might reduce on ARM if it takes too much CPU power.
695
696 save_audio_config_p->achan[chan].upsample = 4;
697 }
698 else if (ratio < 10) {
699
700 // 48000 / 9600 is 5.00
701 // Need more reasearch. Treat like above for now.
702
703 save_audio_config_p->achan[chan].upsample = 4;
704 }
705 else if (ratio < 15) {
706
707 // ...
708
709 save_audio_config_p->achan[chan].upsample = 2;
710 }
711 else { // >= 15
712 //
713 // An example of this might be .....
714 // Probably no benefit.
715
716 save_audio_config_p->achan[chan].upsample = 1;
717 }
718 }
719
720 #ifdef TUNE_UPSAMPLE
721 save_audio_config_p->achan[chan].upsample = TUNE_UPSAMPLE;
722 #endif
723
724 text_color_set(DW_COLOR_DEBUG);
725 dw_printf ("Channel %d: %d baud, %s, %s, %d sample rate x %d",
726 chan,
727 save_audio_config_p->achan[chan].baud,
728 save_audio_config_p->achan[chan].modem_type == MODEM_AIS ? "AIS" : "K9NG/G3RUH",
729 save_audio_config_p->achan[chan].profiles,
730 save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec,
731 save_audio_config_p->achan[chan].upsample);
732 if (save_audio_config_p->achan[chan].dtmf_decode != DTMF_DECODE_OFF)
733 dw_printf (", DTMF decoder enabled");
734 dw_printf (".\n");
735
736 struct demodulator_state_s *D;
737 D = &demodulator_state[chan][0]; // first subchannel
738
739
740 save_audio_config_p->achan[chan].num_subchan = 1;
741 save_audio_config_p->achan[chan].num_slicers = 1;
742
743 if (strchr(save_audio_config_p->achan[chan].profiles, '+') != NULL) {
744
745 /* I'm not happy about putting this hack here. */
746 /* This belongs in demod_9600_init but it doesn't have access to the audio config. */
747
748 save_audio_config_p->achan[chan].num_slicers = MAX_SLICERS;
749 }
750
751
752 text_color_set(DW_COLOR_INFO);
753 dw_printf ("The ratio of audio samples per sec (%d) to data rate in baud (%d) is %.1f\n",
754 save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec,
755 save_audio_config_p->achan[chan].baud,
756 (double)ratio);
757 if (ratio < 3) {
758 text_color_set(DW_COLOR_ERROR);
759 dw_printf ("There is little hope of success with such a low ratio. Use a higher sample rate.\n");
760 }
761 else if (ratio < 5) {
762 dw_printf ("This is on the low side for best performance. Can you use a higher sample rate?\n");
763 if (save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec == 44100) {
764 dw_printf ("For example, can you use 48000 rather than 44100?\n");
765 }
766 }
767 else if (ratio < 6) {
768 dw_printf ("Increasing the sample rate should improve decoder performance.\n");
769 }
770 else if (ratio > 15) {
771 dw_printf ("Sample rate is more than adequate. You might lower it if CPU load is a concern.\n");
772 }
773 else {
774 dw_printf ("This is a suitable ratio for good performance.\n");
775 }
776
777 demod_9600_init (save_audio_config_p->achan[chan].modem_type,
778 save_audio_config_p->achan[chan].upsample * save_audio_config_p->adev[ACHAN2ADEV(chan)].samples_per_sec,
779 save_audio_config_p->achan[chan].baud, D);
780
781 if (strchr(save_audio_config_p->achan[chan].profiles, '+') != NULL) {
782
783 /* I'm not happy about putting this hack here. */
784 /* should pass in as a parameter rather than adding on later. */
785
786 save_audio_config_p->achan[chan].num_slicers = MAX_SLICERS;
787 D->num_slicers = MAX_SLICERS;
788 }
789
790 /* For signal level reporting, we want a longer term view. */
791
792 D->quick_attack = D->agc_fast_attack * 0.2f;
793 D->sluggish_decay = D->agc_slow_decay * 0.2f;
794 }
795 break;
796
797 } /* switch on modulation type. */
798
799 } /* if channel number is valid */
800
801 } /* for chan ... */
802
803
804 return (0);
805
806 } /* end demod_init */
807
808
809
810 /*------------------------------------------------------------------
811 *
812 * Name: demod_get_sample
813 *
814 * Purpose: Get one audio sample fromt the specified sound input source.
815 *
816 * Inputs: a - Index for audio device. 0 = first.
817 *
818 * Returns: -32768 .. 32767 for a valid audio sample.
819 * 256*256 for end of file or other error.
820 *
821 * Global In: save_audio_config_p->adev[ACHAN2ADEV(chan)].bits_per_sample - So we know whether to
822 * read 1 or 2 bytes from audio stream.
823 *
824 * Description: Grab 1 or two btyes depending on data source.
825 *
826 * When processing stereo, the caller will call this
827 * at twice the normal rate to obtain alternating left
828 * and right samples.
829 *
830 *----------------------------------------------------------------*/
831
832 #define FSK_READ_ERR (256*256)
833
834
835 __attribute__((hot))
demod_get_sample(int a)836 int demod_get_sample (int a)
837 {
838 int x1, x2;
839 signed short sam; /* short to force sign extention. */
840
841
842 assert (save_audio_config_p->adev[a].bits_per_sample == 8 || save_audio_config_p->adev[a].bits_per_sample == 16);
843
844
845 if (save_audio_config_p->adev[a].bits_per_sample == 8) {
846
847 x1 = audio_get(a);
848 if (x1 < 0) return(FSK_READ_ERR);
849
850 assert (x1 >= 0 && x1 <= 255);
851
852 /* Scale 0..255 into -32k..+32k */
853
854 sam = (x1 - 128) * 256;
855
856 }
857 else {
858 x1 = audio_get(a); /* lower byte first */
859 if (x1 < 0) return(FSK_READ_ERR);
860
861 x2 = audio_get(a);
862 if (x2 < 0) return(FSK_READ_ERR);
863
864 assert (x1 >= 0 && x1 <= 255);
865 assert (x2 >= 0 && x2 <= 255);
866
867 sam = ( x2 << 8 ) | x1;
868 }
869
870 return (sam);
871 }
872
873
874 /*-------------------------------------------------------------------
875 *
876 * Name: demod_process_sample
877 *
878 * Purpose: (1) Demodulate the AFSK signal.
879 * (2) Recover clock and data.
880 *
881 * Inputs: chan - Audio channel. 0 for left, 1 for right.
882 * subchan - modem of the channel.
883 * sam - One sample of audio.
884 * Should be in range of -32768 .. 32767.
885 *
886 * Returns: None
887 *
888 * Descripion: We start off with two bandpass filters tuned to
889 * the given frequencies. In the case of VHF packet
890 * radio, this would be 1200 and 2200 Hz.
891 *
892 * The bandpass filter amplitudes are compared to
893 * obtain the demodulated signal.
894 *
895 * We also have a digital phase locked loop (PLL)
896 * to recover the clock and pick out data bits at
897 * the proper rate.
898 *
899 * For each recovered data bit, we call:
900 *
901 * hdlc_rec (channel, demodulated_bit);
902 *
903 * to decode HDLC frames from the stream of bits.
904 *
905 * Future: This could be generalized by passing in the name
906 * of the function to be called for each bit recovered
907 * from the demodulator. For now, it's simply hard-coded.
908 *
909 *--------------------------------------------------------------------*/
910
911
912 __attribute__((hot))
demod_process_sample(int chan,int subchan,int sam)913 void demod_process_sample (int chan, int subchan, int sam)
914 {
915 float fsam;
916 int k;
917
918
919 struct demodulator_state_s *D;
920
921 assert (chan >= 0 && chan < MAX_CHANS);
922 assert (subchan >= 0 && subchan < MAX_SUBCHANS);
923
924 D = &demodulator_state[chan][subchan];
925
926
927 /* Scale to nice number, actually -2.0 to +2.0 for extra headroom */
928
929 fsam = sam / 16384.0f;
930
931 /*
932 * Accumulate measure of the input signal level.
933 */
934
935
936 /*
937 * Version 1.2: Try new approach to capturing the amplitude.
938 * This is same as the later AGC without the normalization step.
939 * We want decay to be substantially slower to get a longer
940 * range idea of the received audio.
941 */
942
943 if (fsam >= D->alevel_rec_peak) {
944 D->alevel_rec_peak = fsam * D->quick_attack + D->alevel_rec_peak * (1.0f - D->quick_attack);
945 }
946 else {
947 D->alevel_rec_peak = fsam * D->sluggish_decay + D->alevel_rec_peak * (1.0f - D->sluggish_decay);
948 }
949
950 if (fsam <= D->alevel_rec_valley) {
951 D->alevel_rec_valley = fsam * D->quick_attack + D->alevel_rec_valley * (1.0f - D->quick_attack);
952 }
953 else {
954 D->alevel_rec_valley = fsam * D->sluggish_decay + D->alevel_rec_valley * (1.0f - D->sluggish_decay);
955 }
956
957
958 /*
959 * Select decoder based on modulation type.
960 */
961
962 switch (save_audio_config_p->achan[chan].modem_type) {
963
964 case MODEM_OFF:
965
966 // Might have channel only listening to DTMF for APRStt gateway.
967 // Don't waste CPU time running a demodulator here.
968 break;
969
970 case MODEM_AFSK:
971 case MODEM_EAS:
972
973 if (save_audio_config_p->achan[chan].decimate > 1) {
974
975 sample_sum[chan][subchan] += sam;
976 sample_count[chan][subchan]++;
977 if (sample_count[chan][subchan] >= save_audio_config_p->achan[chan].decimate) {
978 demod_afsk_process_sample (chan, subchan, sample_sum[chan][subchan] / save_audio_config_p->achan[chan].decimate, D);
979 sample_sum[chan][subchan] = 0;
980 sample_count[chan][subchan] = 0;
981 }
982 }
983 else {
984 demod_afsk_process_sample (chan, subchan, sam, D);
985 }
986 break;
987
988 case MODEM_QPSK:
989 case MODEM_8PSK:
990
991 if (save_audio_config_p->achan[chan].decimate > 1) {
992
993 text_color_set(DW_COLOR_ERROR);
994 dw_printf ("Invalid combination of options. Exiting.\n");
995 // Would probably work but haven't thought about it or tested yet.
996 exit (1);
997 }
998 else {
999 demod_psk_process_sample (chan, subchan, sam, D);
1000 }
1001 break;
1002
1003 case MODEM_BASEBAND:
1004 case MODEM_SCRAMBLE:
1005 case MODEM_AIS:
1006 default:
1007
1008 if (zerostuff) {
1009 /* Literature says this is better if followed */
1010 /* by appropriate low pass filter. */
1011 /* So far, both are same in tests with different */
1012 /* optimal low pass filter parameters. */
1013
1014 for (k=1; k<save_audio_config_p->achan[chan].upsample; k++) {
1015 demod_9600_process_sample (chan, 0, D);
1016 }
1017 demod_9600_process_sample (chan, sam * save_audio_config_p->achan[chan].upsample, D);
1018 }
1019 else {
1020
1021 /* Linear interpolation. */
1022 static int prev_sam;
1023
1024 switch (save_audio_config_p->achan[chan].upsample) {
1025 case 1:
1026 demod_9600_process_sample (chan, sam, D);
1027 break;
1028 case 2:
1029 demod_9600_process_sample (chan, (prev_sam + sam) / 2, D);
1030 demod_9600_process_sample (chan, sam, D);
1031 break;
1032 case 3:
1033 demod_9600_process_sample (chan, (2 * prev_sam + sam) / 3, D);
1034 demod_9600_process_sample (chan, (prev_sam + 2 * sam) / 3, D);
1035 demod_9600_process_sample (chan, sam, D);
1036 break;
1037 case 4:
1038 demod_9600_process_sample (chan, (3 * prev_sam + sam) / 4, D);
1039 demod_9600_process_sample (chan, (prev_sam + sam) / 2, D);
1040 demod_9600_process_sample (chan, (prev_sam + 3 * sam) / 4, D);
1041 demod_9600_process_sample (chan, sam, D);
1042 break;
1043 default:
1044 assert (0);
1045 break;
1046 }
1047 prev_sam = sam;
1048 }
1049 break;
1050
1051 } /* switch modem_type */
1052 return;
1053
1054 } /* end demod_process_sample */
1055
1056
1057
1058
1059
1060
1061 /* Doesn't seem right. Need to revisit this. */
1062 /* Resulting scale is 0 to almost 100. */
1063 /* Cranking up the input level produces no more than 97 or 98. */
1064 /* We currently produce a message when this goes over 90. */
1065
demod_get_audio_level(int chan,int subchan)1066 alevel_t demod_get_audio_level (int chan, int subchan)
1067 {
1068 struct demodulator_state_s *D;
1069 alevel_t alevel;
1070
1071 assert (chan >= 0 && chan < MAX_CHANS);
1072 assert (subchan >= 0 && subchan < MAX_SUBCHANS);
1073
1074 /* We have to consider two different cases here. */
1075 /* N demodulators, each with own slicer and HDLC decoder. */
1076 /* Single demodulator, multiple slicers each with own HDLC decoder. */
1077
1078 if (demodulator_state[chan][0].num_slicers > 1) {
1079 subchan = 0;
1080 }
1081
1082 D = &demodulator_state[chan][subchan];
1083
1084 // Take half of peak-to-peak for received audio level.
1085
1086 alevel.rec = (int) (( D->alevel_rec_peak - D->alevel_rec_valley ) * 50.0f + 0.5f);
1087
1088 if (save_audio_config_p->achan[chan].modem_type == MODEM_AFSK ||
1089 save_audio_config_p->achan[chan].modem_type == MODEM_EAS) {
1090
1091 /* For AFSK, we have mark and space amplitudes. */
1092
1093 alevel.mark = (int) ((D->alevel_mark_peak ) * 100.0f + 0.5f);
1094 alevel.space = (int) ((D->alevel_space_peak ) * 100.0f + 0.5f);
1095 }
1096 else if (save_audio_config_p->achan[chan].modem_type == MODEM_QPSK ||
1097 save_audio_config_p->achan[chan].modem_type == MODEM_8PSK) {
1098 alevel.mark = -1;
1099 alevel.space = -1;
1100 }
1101 else {
1102
1103 #if 1
1104 /* Display the + and - peaks. */
1105 /* Normally we'd expect them to be about the same. */
1106 /* However, with SDR, or other DC coupling, we could have an offset. */
1107
1108 alevel.mark = (int) ((D->alevel_mark_peak) * 200.0f + 0.5f);
1109 alevel.space = (int) ((D->alevel_space_peak) * 200.0f - 0.5f);
1110
1111
1112 #else
1113 /* Here we have + and - peaks after filtering. */
1114 /* Take half of the peak to peak. */
1115 /* The "5/6" factor worked out right for the current low pass filter. */
1116 /* Will it need to be different if the filter is tweaked? */
1117
1118 alevel.mark = (int) ((D->alevel_mark_peak - D->alevel_space_peak) * 100.0f * 5.0f/6.0f + 0.5f);
1119 alevel.space = -1; /* to print one number inside of ( ) */
1120 #endif
1121 }
1122 return (alevel);
1123 }
1124
1125
1126 /* end demod.c */
1127