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