1 /*
2  * SpanDSP - a series of DSP components for telephony
3  *
4  * v27ter_tests.c
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  * $Id: v27ter_tests.c,v 1.88 2008/07/16 17:01:49 steveu Exp $
26  */
27 
28 /*! \page v27ter_tests_page V.27ter modem tests
29 \section v27ter_tests_page_sec_1 What does it do?
30 These tests test one way paths, as V.27ter is a half-duplex modem. They allow either:
31 
32  - A V.27ter transmit modem to feed a V.27ter receive modem through a telephone line
33    model. BER testing is then used to evaluate performance under various line
34    conditions. This is effective for testing the basic performance of the
35    receive modem. It is also the only test mode provided for evaluating the
36    transmit modem.
37 
38  - A V.27ter receive modem is used to decode V.27ter audio, stored in a wave file.
39    This is good way to evaluate performance with audio recorded from other
40    models of modem, and with real world problematic telephone lines.
41 
42 If the appropriate GUI environment exists, the tests are built such that a visual
43 display of modem status is maintained.
44 
45 \section v27ter_tests_page_sec_2 How is it used?
46 */
47 
48 #if defined(HAVE_CONFIG_H)
49 #include "config.h"
50 #endif
51 
52 #if defined(HAVE_FL_FL_H)  &&  defined(HAVE_FL_FL_CARTESIAN_H)  &&  defined(HAVE_FL_FL_AUDIO_METER_H)
53 #define ENABLE_GUI
54 #endif
55 
56 #include <stdlib.h>
57 #include <stdio.h>
58 #include <fcntl.h>
59 #include <unistd.h>
60 #include <string.h>
61 #include <audiofile.h>
62 
63 #include "spandsp.h"
64 #include "spandsp-sim.h"
65 
66 #if defined(ENABLE_GUI)
67 #include "modem_monitor.h"
68 #include "line_model_monitor.h"
69 #endif
70 
71 #define BLOCK_LEN       160
72 
73 #define OUT_FILE_NAME   "v27ter.wav"
74 
75 char *decode_test_file = NULL;
76 int use_gui = FALSE;
77 
78 int symbol_no = 0;
79 
80 int rx_bits = 0;
81 
82 bert_state_t bert;
83 one_way_line_model_state_t *line_model;
84 
85 #if defined(ENABLE_GUI)
86 qam_monitor_t *qam_monitor;
87 #endif
88 
89 bert_results_t latest_results;
90 
reporter(void * user_data,int reason,bert_results_t * results)91 static void reporter(void *user_data, int reason, bert_results_t *results)
92 {
93     switch (reason)
94     {
95     case BERT_REPORT_SYNCED:
96         printf("BERT report synced\n");
97         break;
98     case BERT_REPORT_UNSYNCED:
99         printf("BERT report unsync'ed\n");
100         break;
101     case BERT_REPORT_REGULAR:
102         printf("BERT report regular - %d bits, %d bad bits, %d resyncs\n", results->total_bits, results->bad_bits, results->resyncs);
103         memcpy(&latest_results, results, sizeof(latest_results));
104         break;
105     case BERT_REPORT_GT_10_2:
106         printf("BERT report > 1 in 10^2\n");
107         break;
108     case BERT_REPORT_LT_10_2:
109         printf("BERT report < 1 in 10^2\n");
110         break;
111     case BERT_REPORT_LT_10_3:
112         printf("BERT report < 1 in 10^3\n");
113         break;
114     case BERT_REPORT_LT_10_4:
115         printf("BERT report < 1 in 10^4\n");
116         break;
117     case BERT_REPORT_LT_10_5:
118         printf("BERT report < 1 in 10^5\n");
119         break;
120     case BERT_REPORT_LT_10_6:
121         printf("BERT report < 1 in 10^6\n");
122         break;
123     case BERT_REPORT_LT_10_7:
124         printf("BERT report < 1 in 10^7\n");
125         break;
126     default:
127         printf("BERT report reason %d\n", reason);
128         break;
129     }
130 }
131 /*- End of function --------------------------------------------------------*/
132 
v27ter_rx_status(void * user_data,int status)133 static int v27ter_rx_status(void *user_data, int status)
134 {
135     printf("V.27ter rx status is %d\n", status);
136     switch (status)
137     {
138     case PUTBIT_TRAINING_FAILED:
139         printf("Training failed\n");
140         break;
141     case PUTBIT_TRAINING_IN_PROGRESS:
142         printf("Training in progress\n");
143         break;
144     case PUTBIT_TRAINING_SUCCEEDED:
145         printf("Training succeeded\n");
146         break;
147     case PUTBIT_CARRIER_UP:
148         printf("Carrier up\n");
149         break;
150     case PUTBIT_CARRIER_DOWN:
151         printf("Carrier down\n");
152         break;
153     default:
154         printf("Eh! - %d\n", status);
155         break;
156     }
157     return 0;
158 }
159 /*- End of function --------------------------------------------------------*/
160 
v27terputbit(void * user_data,int bit)161 static void v27terputbit(void *user_data, int bit)
162 {
163     if (bit < 0)
164     {
165         v27ter_rx_status(user_data, bit);
166         return;
167     }
168     if (decode_test_file)
169         printf("Rx bit %d - %d\n", rx_bits++, bit);
170     else
171         bert_put_bit(&bert, bit);
172 }
173 /*- End of function --------------------------------------------------------*/
174 
v27ter_tx_status(void * user_data,int status)175 static int v27ter_tx_status(void *user_data, int status)
176 {
177     switch (status)
178     {
179     case MODEM_TX_STATUS_DATA_EXHAUSTED:
180         printf("V.27ter tx data exhausted\n");
181         break;
182     case MODEM_TX_STATUS_SHUTDOWN_COMPLETE:
183         printf("V.27ter tx shutdown complete\n");
184         break;
185     default:
186         printf("V.27ter tx status is %d\n", status);
187         break;
188     }
189     return 0;
190 }
191 /*- End of function --------------------------------------------------------*/
192 
v27tergetbit(void * user_data)193 static int v27tergetbit(void *user_data)
194 {
195     return bert_get_bit(&bert);
196 }
197 /*- End of function --------------------------------------------------------*/
198 
qam_report(void * user_data,const complexf_t * constel,const complexf_t * target,int symbol)199 static void qam_report(void *user_data, const complexf_t *constel, const complexf_t *target, int symbol)
200 {
201     int i;
202     int len;
203     complexf_t *coeffs;
204     float fpower;
205     float error;
206     v27ter_rx_state_t *rx;
207     static float smooth_power = 0.0f;
208 #if defined(ENABLE_GUI)
209     static int reports = 0;
210 #endif
211 
212     rx = (v27ter_rx_state_t *) user_data;
213     if (constel)
214     {
215         fpower = (constel->re - target->re)*(constel->re - target->re)
216                + (constel->im - target->im)*(constel->im - target->im);
217         smooth_power = 0.95f*smooth_power + 0.05f*fpower;
218 #if defined(ENABLE_GUI)
219         if (use_gui)
220         {
221             qam_monitor_update_constel(qam_monitor, constel);
222             qam_monitor_update_carrier_tracking(qam_monitor, v27ter_rx_carrier_frequency(rx));
223             qam_monitor_update_symbol_tracking(qam_monitor, v27ter_rx_symbol_timing_correction(rx));
224         }
225 #endif
226         error = constel->im*target->re - constel->re*target->im;
227         printf("Tracking error %f %f %f %f %f %f\n", error, v27ter_rx_carrier_frequency(rx), constel->re, constel->im, target->re, target->im);
228         printf("%8d [%8.4f, %8.4f] [%8.4f, %8.4f] %2x %8.4f %8.4f %9.4f %7.3f %7.2f\n",
229                symbol_no,
230                constel->re,
231                constel->im,
232                target->re,
233                target->im,
234                symbol,
235                fpower,
236                smooth_power,
237                v27ter_rx_carrier_frequency(rx),
238                v27ter_rx_signal_power(rx),
239                v27ter_rx_symbol_timing_correction(rx));
240         len = v27ter_rx_equalizer_state(rx, &coeffs);
241         printf("Equalizer B:\n");
242         for (i = 0;  i < len;  i++)
243             printf("%3d (%15.5f, %15.5f) -> %15.5f\n", i, coeffs[i].re, coeffs[i].im, powerf(&coeffs[i]));
244         printf("Gardtest %d %f %d\n", symbol_no, v27ter_rx_symbol_timing_correction(rx), rx->gardner_integrate);
245         printf("Carcar %d %f\n", symbol_no, v27ter_rx_carrier_frequency(rx));
246 #if defined(ENABLE_GUI)
247         if (use_gui)
248         {
249             if (++reports >= 1000)
250             {
251                 qam_monitor_update_equalizer(qam_monitor, coeffs, len);
252                 reports = 0;
253             }
254         }
255 #endif
256         symbol_no++;
257     }
258     else
259     {
260         printf("Gardner step %d\n", symbol);
261         len = v27ter_rx_equalizer_state(rx, &coeffs);
262         printf("Equalizer A:\n");
263         for (i = 0;  i < len;  i++)
264             printf("%3d (%15.5f, %15.5f) -> %15.5f\n", i, coeffs[i].re, coeffs[i].im, powerf(&coeffs[i]));
265 #if defined(ENABLE_GUI)
266         if (use_gui)
267             qam_monitor_update_equalizer(qam_monitor, coeffs, len);
268 #endif
269     }
270 }
271 /*- End of function --------------------------------------------------------*/
272 
main(int argc,char * argv[])273 int main(int argc, char *argv[])
274 {
275     v27ter_rx_state_t rx;
276     v27ter_tx_state_t tx;
277     bert_results_t bert_results;
278     int16_t gen_amp[BLOCK_LEN];
279     int16_t amp[BLOCK_LEN];
280     AFfilehandle inhandle;
281     AFfilehandle outhandle;
282     AFfilesetup filesetup;
283     int outframes;
284     int samples;
285     int tep;
286     int test_bps;
287     int noise_level;
288     int signal_level;
289     int bits_per_test;
290     int line_model_no;
291     int block;
292     int log_audio;
293     int channel_codec;
294     int rbs_pattern;
295     float x;
296     int opt;
297 
298     channel_codec = MUNGE_CODEC_NONE;
299     rbs_pattern = 0;
300     test_bps = 4800;
301     tep = FALSE;
302     line_model_no = 0;
303     decode_test_file = NULL;
304     use_gui = FALSE;
305     noise_level = -70;
306     signal_level = -13;
307     bits_per_test = 50000;
308     log_audio = FALSE;
309     while ((opt = getopt(argc, argv, "b:c:d:glm:n:r:s:t")) != -1)
310     {
311         switch (opt)
312         {
313         case 'b':
314             bits_per_test = atoi(optarg);
315             break;
316         case 'c':
317             channel_codec = atoi(optarg);
318             break;
319         case 'd':
320             decode_test_file = optarg;
321             break;
322         case 'g':
323 #if defined(ENABLE_GUI)
324             use_gui = TRUE;
325 #else
326             fprintf(stderr, "Graphical monitoring not available\n");
327             exit(2);
328 #endif
329             break;
330         case 'l':
331             log_audio = TRUE;
332             break;
333         case 'm':
334             line_model_no = atoi(optarg);
335             break;
336         case 'n':
337             noise_level = atoi(optarg);
338             break;
339         case 'r':
340             rbs_pattern = atoi(optarg);
341             break;
342         case 's':
343             signal_level = atoi(optarg);
344             break;
345         case 't':
346             tep = TRUE;
347             break;
348         default:
349             //usage();
350             exit(2);
351             break;
352         }
353     }
354     argc -= optind;
355     argv += optind;
356     if (argc > 0)
357     {
358         if (strcmp(argv[0], "4800") == 0)
359             test_bps = 4800;
360         else if (strcmp(argv[0], "2400") == 0)
361             test_bps = 2400;
362         else
363         {
364             fprintf(stderr, "Invalid bit rate\n");
365             exit(2);
366         }
367     }
368 
369     inhandle = NULL;
370     outhandle = NULL;
371 
372     filesetup = AF_NULL_FILESETUP;
373     if (log_audio)
374     {
375         if ((filesetup = afNewFileSetup()) == AF_NULL_FILESETUP)
376         {
377             fprintf(stderr, "    Failed to create file setup\n");
378             exit(2);
379         }
380         afInitSampleFormat(filesetup, AF_DEFAULT_TRACK, AF_SAMPFMT_TWOSCOMP, 16);
381         afInitRate(filesetup, AF_DEFAULT_TRACK, (float) SAMPLE_RATE);
382         afInitFileFormat(filesetup, AF_FILE_WAVE);
383         afInitChannels(filesetup, AF_DEFAULT_TRACK, 1);
384         if ((outhandle = afOpenFile(OUT_FILE_NAME, "w", filesetup)) == AF_NULL_FILEHANDLE)
385         {
386             fprintf(stderr, "    Cannot create wave file '%s'\n", OUT_FILE_NAME);
387             exit(2);
388         }
389     }
390 
391     if (decode_test_file)
392     {
393         /* We will decode the audio from a wave file. */
394         if ((inhandle = afOpenFile(decode_test_file, "r", NULL)) == AF_NULL_FILEHANDLE)
395         {
396             fprintf(stderr, "    Cannot open wave file '%s'\n", decode_test_file);
397             exit(2);
398         }
399         if ((x = afGetFrameSize(inhandle, AF_DEFAULT_TRACK, 1)) != 2.0f)
400         {
401             printf("    Unexpected frame size in speech file '%s' (%f)\n", decode_test_file, x);
402             exit(2);
403         }
404         if ((x = afGetRate(inhandle, AF_DEFAULT_TRACK)) != (float) SAMPLE_RATE)
405         {
406             printf("    Unexpected sample rate in speech file '%s' (%f)\n", decode_test_file, x);
407             exit(2);
408         }
409         if ((x = afGetChannels(inhandle, AF_DEFAULT_TRACK)) != 1.0f)
410         {
411             printf("    Unexpected number of channels in speech file '%s' (%f)\n", decode_test_file, x);
412             exit(2);
413         }
414     }
415     else
416     {
417         /* We will generate V.27ter audio, and add some noise to it. */
418         v27ter_tx_init(&tx, test_bps, tep, v27tergetbit, NULL);
419         v27ter_tx_power(&tx, signal_level);
420         v27ter_tx_set_modem_status_handler(&tx, v27ter_tx_status, (void *) &tx);
421         /* Move the carrier off a bit */
422         tx.carrier_phase_rate = dds_phase_ratef(1810.0f);
423 
424         bert_init(&bert, bits_per_test, BERT_PATTERN_ITU_O152_11, test_bps, 20);
425         bert_set_report(&bert, 10000, reporter, NULL);
426 
427         if ((line_model = one_way_line_model_init(line_model_no, (float) noise_level, channel_codec, rbs_pattern)) == NULL)
428         {
429             fprintf(stderr, "    Failed to create line model\n");
430             exit(2);
431         }
432     }
433 
434     v27ter_rx_init(&rx, test_bps, v27terputbit, NULL);
435     v27ter_rx_set_modem_status_handler(&rx, v27ter_rx_status, (void *) &rx);
436     v27ter_rx_set_qam_report_handler(&rx, qam_report, (void *) &rx);
437     span_log_set_level(&rx.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
438     span_log_set_tag(&rx.logging, "V.27ter-rx");
439 
440 #if defined(ENABLE_GUI)
441     if (use_gui)
442     {
443         qam_monitor = qam_monitor_init(2.0f, NULL);
444         if (!decode_test_file)
445         {
446             start_line_model_monitor(129);
447             line_model_monitor_line_model_update(line_model->near_filter, line_model->near_filter_len);
448         }
449     }
450 #endif
451 
452     memset(&latest_results, 0, sizeof(latest_results));
453     for (block = 0;  ;  block++)
454     {
455         if (decode_test_file)
456         {
457             samples = afReadFrames(inhandle,
458                                    AF_DEFAULT_TRACK,
459                                    amp,
460                                    BLOCK_LEN);
461 #if defined(ENABLE_GUI)
462             if (use_gui)
463                 qam_monitor_update_audio_level(qam_monitor, amp, samples);
464 #endif
465             if (samples == 0)
466                 break;
467         }
468         else
469         {
470             samples = v27ter_tx(&tx, gen_amp, BLOCK_LEN);
471 #if defined(ENABLE_GUI)
472             if (use_gui)
473                 qam_monitor_update_audio_level(qam_monitor, gen_amp, samples);
474 #endif
475             if (samples == 0)
476             {
477                 printf("Restarting on zero output\n");
478 
479                 /* Push a little silence through, to ensure all the data bits get out of the buffers */
480                 memset(amp, 0, BLOCK_LEN*sizeof(int16_t));
481                 v27ter_rx(&rx, amp, BLOCK_LEN);
482                 v27ter_rx(&rx, amp, BLOCK_LEN);
483                 v27ter_rx(&rx, amp, BLOCK_LEN);
484 
485                 /* Note that we might get a few bad bits as the carrier shuts down. */
486                 bert_result(&bert, &bert_results);
487                 fprintf(stderr, "Final result %ddBm0, %d bits, %d bad bits, %d resyncs\n", signal_level, bert_results.total_bits, bert_results.bad_bits, bert_results.resyncs);
488                 fprintf(stderr, "Last report  %ddBm0, %d bits, %d bad bits, %d resyncs\n", signal_level, latest_results.total_bits, latest_results.bad_bits, latest_results.resyncs);
489                 /* See if bit errors are appearing yet. Also check we are getting enough bits out of the receiver. The last regular report
490                    should be error free, though the final report will generally contain bits errors as the carrier was dying. The total
491                    number of bits out of the receiver should be at least the number we sent. Also, since BERT sync should have occurred
492                    rapidly at the start of transmission, the last report should have occurred at not much less than the total number of
493                    bits we sent. */
494                 if (bert_results.total_bits < bits_per_test
495                     ||
496                     latest_results.total_bits < bits_per_test - 100
497                     ||
498                     latest_results.bad_bits != 0)
499                 {
500                     break;
501                 }
502                 memset(&latest_results, 0, sizeof(latest_results));
503                 signal_level--;
504                 v27ter_tx_restart(&tx, test_bps, tep);
505                 v27ter_tx_power(&tx, signal_level);
506                 v27ter_rx_restart(&rx, test_bps, FALSE);
507                 bert_init(&bert, bits_per_test, BERT_PATTERN_ITU_O152_11, test_bps, 20);
508                 bert_set_report(&bert, 10000, reporter, NULL);
509                 one_way_line_model_release(line_model);
510                 if ((line_model = one_way_line_model_init(line_model_no, (float) noise_level, channel_codec, 0)) == NULL)
511                 {
512                     fprintf(stderr, "    Failed to create line model\n");
513                     exit(2);
514                 }
515             }
516 
517             if (log_audio)
518             {
519                 outframes = afWriteFrames(outhandle,
520                                           AF_DEFAULT_TRACK,
521                                           gen_amp,
522                                           samples);
523                 if (outframes != samples)
524                 {
525                     fprintf(stderr, "    Error writing wave file\n");
526                     exit(2);
527                 }
528             }
529             one_way_line_model(line_model, amp, gen_amp, samples);
530         }
531 #if defined(ENABLE_GUI)
532         if (use_gui  &&  !decode_test_file)
533             line_model_monitor_line_spectrum_update(amp, samples);
534 #endif
535         v27ter_rx(&rx, amp, samples);
536         if (decode_test_file == NULL  &&  block%500 == 0)
537             printf("Noise level is %d\n", noise_level);
538     }
539     if (!decode_test_file)
540     {
541         bert_result(&bert, &bert_results);
542         fprintf(stderr, "At completion:\n");
543         fprintf(stderr, "Final result %ddBm0, %d bits, %d bad bits, %d resyncs\n", signal_level, bert_results.total_bits, bert_results.bad_bits, bert_results.resyncs);
544         fprintf(stderr, "Last report  %ddBm0, %d bits, %d bad bits, %d resyncs\n", signal_level, latest_results.total_bits, latest_results.bad_bits, latest_results.resyncs);
545         one_way_line_model_release(line_model);
546         if (signal_level > -43)
547         {
548             printf("Tests failed.\n");
549             exit(2);
550         }
551 
552         printf("Tests passed.\n");
553     }
554 #if defined(ENABLE_GUI)
555     if (use_gui)
556         qam_wait_to_end(qam_monitor);
557 #endif
558     if (log_audio)
559     {
560         if (afCloseFile(outhandle))
561         {
562             fprintf(stderr, "    Cannot close wave file '%s'\n", OUT_FILE_NAME);
563             exit(2);
564         }
565         afFreeFileSetup(filesetup);
566     }
567     return  0;
568 }
569 /*- End of function --------------------------------------------------------*/
570 /*- End of file ------------------------------------------------------------*/
571