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