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