1 /*
2 * SpanDSP - a series of DSP components for telephony
3 *
4 * fsk_tests.c - Tests for the low speed FSK modem code (V.21, V.23, etc.).
5 *
6 * Written by Steve Underwood <steveu@coppice.org>
7 *
8 * Copyright (C) 2003 Steve Underwood
9 *
10 * All rights reserved.
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License version 2, as
14 * published by the Free Software Foundation.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
26 /*! \page fsk_tests_page FSK modem tests
27 \section fsk_tests_page_sec_1 What does it do?
28 These tests allow either:
29
30 - An FSK transmit modem to feed an FSK receive modem, of the same type,
31 through a telephone line model. BER testing is then used to evaluate
32 performance under various line conditions. This is effective for testing
33 the basic performance of the receive modem. It is also the only test mode
34 provided for evaluating the transmit modem.
35
36 - An FSK receive modem is used to decode FSK audio, stored in a file.
37 This is good way to evaluate performance with audio recorded from other
38 models of modem, and with real world problematic telephone lines.
39
40 \section fsk_tests_page_sec_2 How does it work?
41 */
42
43 #if defined(HAVE_CONFIG_H)
44 #include "config.h"
45 #endif
46
47 #include <stdlib.h>
48 #include <stdio.h>
49 #include <unistd.h>
50 #include <string.h>
51 #include <assert.h>
52 #include <sndfile.h>
53
54 #include "spandsp.h"
55 #include "spandsp-sim.h"
56
57 #define BLOCK_LEN 160
58
59 #define OUTPUT_FILE_NAME "fsk.wav"
60
61 char *decode_test_file = NULL;
62 both_ways_line_model_state_t *model;
63 int rx_bits = 0;
64 bool cutoff_test_carrier = false;
65
rx_status(void * user_data,int status)66 static void rx_status(void *user_data, int status)
67 {
68 printf("FSK rx status is %s (%d)\n", signal_status_to_str(status), status);
69 }
70 /*- End of function --------------------------------------------------------*/
71
tx_status(void * user_data,int status)72 static void tx_status(void *user_data, int status)
73 {
74 printf("FSK tx status is %s (%d)\n", signal_status_to_str(status), status);
75 }
76 /*- End of function --------------------------------------------------------*/
77
put_bit(void * user_data,int bit)78 static void put_bit(void *user_data, int bit)
79 {
80 if (bit < 0)
81 {
82 rx_status(user_data, bit);
83 return;
84 }
85
86 printf("Rx bit %d - %d\n", rx_bits++, bit);
87 }
88 /*- End of function --------------------------------------------------------*/
89
cutoff_test_rx_status(void * user_data,int status)90 static void cutoff_test_rx_status(void *user_data, int status)
91 {
92 printf("FSK rx status is %s (%d)\n", signal_status_to_str(status), status);
93 switch (status)
94 {
95 case SIG_STATUS_CARRIER_UP:
96 cutoff_test_carrier = true;
97 break;
98 case SIG_STATUS_CARRIER_DOWN:
99 cutoff_test_carrier = false;
100 break;
101 }
102 }
103 /*- End of function --------------------------------------------------------*/
104
cutoff_test_put_bit(void * user_data,int bit)105 static void cutoff_test_put_bit(void *user_data, int bit)
106 {
107 if (bit < 0)
108 {
109 cutoff_test_rx_status(user_data, bit);
110 return;
111 }
112 }
113 /*- End of function --------------------------------------------------------*/
114
reporter(void * user_data,int reason,bert_results_t * results)115 static void reporter(void *user_data, int reason, bert_results_t *results)
116 {
117 int channel;
118
119 channel = (int) (intptr_t) user_data;
120 switch (reason)
121 {
122 case BERT_REPORT_SYNCED:
123 fprintf(stderr, "%d: BERT report synced\n", channel);
124 break;
125 case BERT_REPORT_UNSYNCED:
126 fprintf(stderr, "%d: BERT report unsync'ed\n", channel);
127 break;
128 case BERT_REPORT_REGULAR:
129 fprintf(stderr, "%d: BERT report regular - %d bits, %d bad bits, %d resyncs\n", channel, results->total_bits, results->bad_bits, results->resyncs);
130 break;
131 case BERT_REPORT_GT_10_2:
132 fprintf(stderr, "%d: BERT report > 1 in 10^2\n", channel);
133 break;
134 case BERT_REPORT_LT_10_2:
135 fprintf(stderr, "%d: BERT report < 1 in 10^2\n", channel);
136 break;
137 case BERT_REPORT_LT_10_3:
138 fprintf(stderr, "%d: BERT report < 1 in 10^3\n", channel);
139 break;
140 case BERT_REPORT_LT_10_4:
141 fprintf(stderr, "%d: BERT report < 1 in 10^4\n", channel);
142 break;
143 case BERT_REPORT_LT_10_5:
144 fprintf(stderr, "%d: BERT report < 1 in 10^5\n", channel);
145 break;
146 case BERT_REPORT_LT_10_6:
147 fprintf(stderr, "%d: BERT report < 1 in 10^6\n", channel);
148 break;
149 case BERT_REPORT_LT_10_7:
150 fprintf(stderr, "%d: BERT report < 1 in 10^7\n", channel);
151 break;
152 default:
153 fprintf(stderr, "%d: BERT report reason %d\n", channel, reason);
154 break;
155 }
156 }
157 /*- End of function --------------------------------------------------------*/
158
main(int argc,char * argv[])159 int main(int argc, char *argv[])
160 {
161 fsk_tx_state_t *caller_tx;
162 fsk_rx_state_t *caller_rx;
163 fsk_tx_state_t *answerer_tx;
164 fsk_rx_state_t *answerer_rx;
165 bert_state_t caller_bert;
166 bert_state_t answerer_bert;
167 bert_results_t bert_results;
168 power_meter_t caller_meter;
169 power_meter_t answerer_meter;
170 int16_t caller_amp[BLOCK_LEN];
171 int16_t answerer_amp[BLOCK_LEN];
172 int16_t caller_model_amp[BLOCK_LEN];
173 int16_t answerer_model_amp[BLOCK_LEN];
174 int16_t out_amp[2*BLOCK_LEN];
175 SNDFILE *inhandle;
176 SNDFILE *outhandle;
177 int outframes;
178 int i;
179 int j;
180 int samples;
181 int test_bps;
182 int noise_level;
183 int noise_sweep;
184 int bits_per_test;
185 int line_model_no;
186 int modem_under_test_1;
187 int modem_under_test_2;
188 int modems_set;
189 int channel_codec;
190 int rbs_pattern;
191 int on_at;
192 int off_at;
193 int opt;
194 bool log_audio;
195 tone_gen_descriptor_t tone_desc;
196 tone_gen_state_t tone_tx;
197
198 channel_codec = MUNGE_CODEC_NONE;
199 rbs_pattern = 0;
200 line_model_no = 0;
201 decode_test_file = NULL;
202 noise_sweep = false;
203 modem_under_test_1 = FSK_V21CH1;
204 modem_under_test_2 = FSK_V21CH2;
205 log_audio = false;
206 modems_set = 0;
207 while ((opt = getopt(argc, argv, "c:d:lm:nr:s:")) != -1)
208 {
209 switch (opt)
210 {
211 case 'c':
212 channel_codec = atoi(optarg);
213 break;
214 case 'd':
215 decode_test_file = optarg;
216 break;
217 case 'l':
218 log_audio = true;
219 break;
220 case 'm':
221 line_model_no = atoi(optarg);
222 break;
223 case 'n':
224 noise_sweep = true;
225 break;
226 case 'r':
227 rbs_pattern = atoi(optarg);
228 break;
229 case 's':
230 switch (modems_set++)
231 {
232 case 0:
233 modem_under_test_1 = atoi(optarg);
234 break;
235 case 1:
236 modem_under_test_2 = atoi(optarg);
237 break;
238 }
239 break;
240 default:
241 //usage();
242 exit(2);
243 break;
244 }
245 }
246
247 if (modem_under_test_1 >= 0)
248 printf("Modem channel 1 is '%s'\n", preset_fsk_specs[modem_under_test_1].name);
249 if (modem_under_test_2 >= 0)
250 printf("Modem channel 2 is '%s'\n", preset_fsk_specs[modem_under_test_2].name);
251
252 outhandle = NULL;
253
254 if (log_audio)
255 {
256 if ((outhandle = sf_open_telephony_write(OUTPUT_FILE_NAME, 2)) == NULL)
257 {
258 fprintf(stderr, " Cannot create audio file '%s'\n", OUTPUT_FILE_NAME);
259 exit(2);
260 }
261 }
262 noise_level = -200;
263 bits_per_test = 0;
264 inhandle = NULL;
265
266 memset(caller_amp, 0, sizeof(*caller_amp));
267 memset(answerer_amp, 0, sizeof(*answerer_amp));
268 memset(caller_model_amp, 0, sizeof(*caller_model_amp));
269 memset(answerer_model_amp, 0, sizeof(*answerer_model_amp));
270 power_meter_init(&caller_meter, 7);
271 power_meter_init(&answerer_meter, 7);
272
273 if (decode_test_file)
274 {
275 if ((inhandle = sf_open_telephony_read(decode_test_file, 1)) == NULL)
276 {
277 fprintf(stderr, " Cannot open audio file '%s'\n", decode_test_file);
278 exit(2);
279 }
280 caller_rx = fsk_rx_init(NULL, &preset_fsk_specs[modem_under_test_1], FSK_FRAME_MODE_SYNC, put_bit, NULL);
281 fsk_rx_set_modem_status_handler(caller_rx, rx_status, (void *) &caller_rx);
282 test_bps = preset_fsk_specs[modem_under_test_1].baud_rate;
283
284 for (;;)
285 {
286 samples = sf_readf_short(inhandle, caller_model_amp, BLOCK_LEN);
287 if (samples < BLOCK_LEN)
288 break;
289 for (i = 0; i < samples; i++)
290 power_meter_update(&caller_meter, caller_model_amp[i]);
291 fsk_rx(caller_rx, caller_model_amp, samples);
292 }
293
294 if (sf_close_telephony(inhandle))
295 {
296 fprintf(stderr, " Cannot close audio file '%s'\n", decode_test_file);
297 exit(2);
298 }
299 fsk_rx_free(caller_rx);
300 }
301 else
302 {
303 printf("Test cutoff level\n");
304 caller_rx = fsk_rx_init(NULL, &preset_fsk_specs[modem_under_test_1], FSK_FRAME_MODE_SYNC, cutoff_test_put_bit, NULL);
305 fsk_rx_signal_cutoff(caller_rx, -30.0f);
306 fsk_rx_set_modem_status_handler(caller_rx, cutoff_test_rx_status, (void *) &caller_rx);
307 on_at = 0;
308 for (i = -40; i < -25; i++)
309 {
310 tone_gen_descriptor_init(&tone_desc,
311 1500,
312 i,
313 0,
314 0,
315 1,
316 0,
317 0,
318 0,
319 true);
320 tone_gen_init(&tone_tx, &tone_desc);
321 for (j = 0; j < 10; j++)
322 {
323 samples = tone_gen(&tone_tx, caller_model_amp, 160);
324 fsk_rx(caller_rx, caller_model_amp, samples);
325 }
326 if (cutoff_test_carrier)
327 break;
328 }
329 on_at = i;
330 off_at = 0;
331 for ( ; i > -40; i--)
332 {
333 tone_gen_descriptor_init(&tone_desc,
334 1500,
335 i,
336 0,
337 0,
338 1,
339 0,
340 0,
341 0,
342 true);
343 tone_gen_init(&tone_tx, &tone_desc);
344 for (j = 0; j < 10; j++)
345 {
346 samples = tone_gen(&tone_tx, caller_model_amp, 160);
347 fsk_rx(caller_rx, caller_model_amp, samples);
348 }
349 if (!cutoff_test_carrier)
350 break;
351 }
352 off_at = i;
353 printf("Carrier on at %d, off at %d\n", on_at, off_at);
354 if (on_at < -29 || on_at > -26
355 ||
356 off_at < -35 || off_at > -31)
357 {
358 printf("Tests failed.\n");
359 exit(2);
360 }
361 fsk_rx_free(caller_rx);
362
363 printf("Test with BERT\n");
364 test_bps = preset_fsk_specs[modem_under_test_1].baud_rate;
365 if (modem_under_test_1 >= 0)
366 {
367 caller_tx = fsk_tx_init(NULL, &preset_fsk_specs[modem_under_test_1], (get_bit_func_t) bert_get_bit, &caller_bert);
368 fsk_tx_set_modem_status_handler(caller_tx, tx_status, (void *) &caller_tx);
369 answerer_rx = fsk_rx_init(NULL, &preset_fsk_specs[modem_under_test_1], FSK_FRAME_MODE_SYNC, (put_bit_func_t) bert_put_bit, &answerer_bert);
370 fsk_rx_set_modem_status_handler(answerer_rx, rx_status, (void *) &answerer_rx);
371 }
372 if (modem_under_test_2 >= 0)
373 {
374 answerer_tx = fsk_tx_init(NULL, &preset_fsk_specs[modem_under_test_2], (get_bit_func_t) bert_get_bit, &answerer_bert);
375 fsk_tx_set_modem_status_handler(answerer_tx, tx_status, (void *) &answerer_tx);
376 caller_rx = fsk_rx_init(NULL, &preset_fsk_specs[modem_under_test_2], FSK_FRAME_MODE_SYNC, (put_bit_func_t) bert_put_bit, &caller_bert);
377 fsk_rx_set_modem_status_handler(caller_rx, rx_status, (void *) &caller_rx);
378 }
379 test_bps = preset_fsk_specs[modem_under_test_1].baud_rate;
380
381 bits_per_test = 500000;
382 noise_level = -24;
383
384 bert_init(&caller_bert, bits_per_test, BERT_PATTERN_ITU_O152_11, test_bps, 20);
385 bert_set_report(&caller_bert, 100000, reporter, (void *) (intptr_t) 1);
386 bert_init(&answerer_bert, bits_per_test, BERT_PATTERN_ITU_O152_11, test_bps, 20);
387 bert_set_report(&answerer_bert, 100000, reporter, (void *) (intptr_t) 2);
388 if ((model = both_ways_line_model_init(line_model_no,
389 (float) noise_level,
390 -15.0f,
391 -15.0f,
392 line_model_no,
393 (float) noise_level,
394 -15.0f,
395 -15.0f,
396 channel_codec,
397 rbs_pattern)) == NULL)
398 {
399 fprintf(stderr, " Failed to create line model\n");
400 exit(2);
401 }
402
403 for (;;)
404 {
405 samples = fsk_tx(caller_tx, caller_amp, BLOCK_LEN);
406 for (i = 0; i < samples; i++)
407 power_meter_update(&caller_meter, caller_amp[i]);
408 samples = fsk_tx(answerer_tx, answerer_amp, BLOCK_LEN);
409 for (i = 0; i < samples; i++)
410 power_meter_update(&answerer_meter, answerer_amp[i]);
411 both_ways_line_model(model,
412 caller_model_amp,
413 caller_amp,
414 answerer_model_amp,
415 answerer_amp,
416 samples);
417
418 //printf("Powers %10.5fdBm0 %10.5fdBm0\n", power_meter_current_dbm0(&caller_meter), power_meter_current_dbm0(&answerer_meter));
419
420 fsk_rx(answerer_rx, caller_model_amp, samples);
421 for (i = 0; i < samples; i++)
422 out_amp[2*i] = caller_model_amp[i];
423 for ( ; i < BLOCK_LEN; i++)
424 out_amp[2*i] = 0;
425
426 fsk_rx(caller_rx, answerer_model_amp, samples);
427 for (i = 0; i < samples; i++)
428 out_amp[2*i + 1] = answerer_model_amp[i];
429 for ( ; i < BLOCK_LEN; i++)
430 out_amp[2*i + 1] = 0;
431
432 if (log_audio)
433 {
434 outframes = sf_writef_short(outhandle, out_amp, BLOCK_LEN);
435 if (outframes != BLOCK_LEN)
436 {
437 fprintf(stderr, " Error writing audio file\n");
438 exit(2);
439 }
440 }
441
442 if (samples < BLOCK_LEN)
443 {
444 bert_result(&caller_bert, &bert_results);
445 fprintf(stderr, "%ddB AWGN, %d bits, %d bad bits, %d resyncs\n", noise_level, bert_results.total_bits, bert_results.bad_bits, bert_results.resyncs);
446 if (!noise_sweep)
447 {
448 if (bert_results.total_bits != bits_per_test - 43
449 ||
450 bert_results.bad_bits != 0
451 ||
452 bert_results.resyncs != 0)
453 {
454 printf("Tests failed.\n");
455 exit(2);
456 }
457 }
458 bert_result(&answerer_bert, &bert_results);
459 fprintf(stderr, "%ddB AWGN, %d bits, %d bad bits, %d resyncs\n", noise_level, bert_results.total_bits, bert_results.bad_bits, bert_results.resyncs);
460 if (!noise_sweep)
461 {
462 if (bert_results.total_bits != bits_per_test - 43
463 ||
464 bert_results.bad_bits != 0
465 ||
466 bert_results.resyncs != 0)
467 {
468 printf("Tests failed.\n");
469 exit(2);
470 }
471 break;
472 }
473
474 /* Put a little silence between the chunks in the file. */
475 memset(out_amp, 0, sizeof(out_amp));
476 if (log_audio)
477 {
478 for (i = 0; i < 200; i++)
479 outframes = sf_writef_short(outhandle, out_amp, BLOCK_LEN);
480 }
481 if (modem_under_test_1 >= 0)
482 {
483 caller_tx = fsk_tx_init(NULL, &preset_fsk_specs[modem_under_test_1], (get_bit_func_t) bert_get_bit, &caller_bert);
484 fsk_tx_set_modem_status_handler(caller_tx, tx_status, (void *) &caller_tx);
485 answerer_rx = fsk_rx_init(NULL, &preset_fsk_specs[modem_under_test_1], FSK_FRAME_MODE_SYNC, (put_bit_func_t) bert_put_bit, &answerer_bert);
486 fsk_rx_set_modem_status_handler(answerer_rx, rx_status, (void *) &answerer_rx);
487 }
488 if (modem_under_test_2 >= 0)
489 {
490 answerer_tx = fsk_tx_init(NULL, &preset_fsk_specs[modem_under_test_2], (get_bit_func_t) bert_get_bit, &answerer_bert);
491 fsk_tx_set_modem_status_handler(answerer_tx, tx_status, (void *) &answerer_tx);
492 caller_rx = fsk_rx_init(NULL, &preset_fsk_specs[modem_under_test_2], FSK_FRAME_MODE_SYNC, (put_bit_func_t) bert_put_bit, &caller_bert);
493 fsk_rx_set_modem_status_handler(caller_rx, rx_status, (void *) &caller_rx);
494 }
495 noise_level++;
496 both_ways_line_model_free(model);
497 if ((model = both_ways_line_model_init(line_model_no,
498 (float) noise_level,
499 line_model_no,
500 -15.0f,
501 -15.0f,
502 noise_level,
503 channel_codec,
504 -15.0f,
505 -15.0f,
506 0)) == NULL)
507 {
508 fprintf(stderr, " Failed to create line model\n");
509 exit(2);
510 }
511 bert_init(&caller_bert, bits_per_test, BERT_PATTERN_ITU_O152_11, test_bps, 20);
512 bert_set_report(&caller_bert, 100000, reporter, (void *) (intptr_t) 1);
513 bert_init(&answerer_bert, bits_per_test, BERT_PATTERN_ITU_O152_11, test_bps, 20);
514 bert_set_report(&answerer_bert, 100000, reporter, (void *) (intptr_t) 2);
515 }
516 }
517 bert_release(&caller_bert);
518 bert_release(&answerer_bert);
519 if (modem_under_test_1 >= 0)
520 {
521 fsk_tx_free(caller_tx);
522 fsk_rx_free(answerer_rx);
523 }
524 if (modem_under_test_2 >= 0)
525 {
526 fsk_tx_free(answerer_tx);
527 fsk_rx_free(caller_rx);
528 }
529 both_ways_line_model_free(model);
530 printf("Tests passed.\n");
531 }
532 if (log_audio)
533 {
534 if (sf_close_telephony(outhandle))
535 {
536 fprintf(stderr, " Cannot close audio file '%s'\n", OUTPUT_FILE_NAME);
537 exit(2);
538 }
539 }
540 return 0;
541 }
542 /*- End of function --------------------------------------------------------*/
543 /*- End of file ------------------------------------------------------------*/
544