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