comparison spandsp-0.0.3/spandsp-0.0.3/tests/v17_tests.c @ 5:f762bf195c4b

import spandsp-0.0.3
author Peter Meerwald <pmeerw@cosy.sbg.ac.at>
date Fri, 25 Jun 2010 16:00:21 +0200
parents
children
comparison
equal deleted inserted replaced
4:26cd8f1ef0b1 5:f762bf195c4b
1 #define ENABLE_V17
2 /*
3 * SpanDSP - a series of DSP components for telephony
4 *
5 * v17_tests.c
6 *
7 * Written by Steve Underwood <steveu@coppice.org>
8 *
9 * Copyright (C) 2004 Steve Underwood
10 *
11 * All rights reserved.
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License version 2, as
15 * published by the Free Software Foundation.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 *
26 * $Id: v17_tests.c,v 1.60 2006/11/19 14:07:27 steveu Exp $
27 */
28
29 /*! \page v17_tests_page V.17 modem tests
30 \section v17_tests_page_sec_1 What does it do?
31 These tests test one way paths, as V.17 is a half-duplex modem. They allow either:
32
33 - A V.17 transmit modem to feed a V.17 receive modem through a telephone line
34 model. BER testing is then used to evaluate performance under various line
35 conditions. This is effective for testing the basic performance of the
36 receive modem. It is also the only test mode provided for evaluating the
37 transmit modem.
38
39 - A V.17 receive modem is used to decode V.17 audio, stored in a wave file.
40 This is good way to evaluate performance with audio recorded from other
41 models of modem, and with real world problematic telephone lines.
42
43 If the appropriate GUI environment exists, the tests are built such that a visual
44 display of modem status is maintained.
45
46 \section v17_tests_page_sec_2 How is it used?
47 */
48
49 #ifdef HAVE_CONFIG_H
50 #include "config.h"
51 #endif
52
53 #if defined(HAVE_FL_FL_H) && defined(HAVE_FL_FL_CARTESIAN_H) && defined(HAVE_FL_FL_AUDIO_METER_H)
54 #define ENABLE_GUI
55 #endif
56
57 #include <inttypes.h>
58 #include <stdlib.h>
59 #include <stdio.h>
60 #include <fcntl.h>
61 #include <string.h>
62 #if defined(HAVE_TGMATH_H)
63 #include <tgmath.h>
64 #endif
65 #if defined(HAVE_MATH_H)
66 #include <math.h>
67 #endif
68 #include <assert.h>
69 #include <audiofile.h>
70 #include <tiffio.h>
71
72 #include "spandsp.h"
73 #include "test_utils.h"
74 #include "line_model.h"
75 #if defined(ENABLE_GUI)
76 #include "modem_monitor.h"
77 #include "line_model_monitor.h"
78 #endif
79
80 #define BLOCK_LEN 160
81
82 #define OUT_FILE_NAME "v17.wav"
83
84 char *decode_test_file = NULL;
85 int use_gui = FALSE;
86
87 int symbol_no = 0;
88
89 int rx_bits = 0;
90 int tx_bits = 0;
91
92 int test_bps;
93
94 bert_state_t bert;
95 one_way_line_model_state_t *line_model;
96
97 #if defined(ENABLE_GUI)
98 qam_monitor_t *qam_monitor;
99 #endif
100
101 bert_results_t latest_results;
102
103 static void reporter(void *user_data, int reason, bert_results_t *results)
104 {
105 switch (reason)
106 {
107 case BERT_REPORT_SYNCED:
108 printf("BERT report synced\n");
109 break;
110 case BERT_REPORT_UNSYNCED:
111 printf("BERT report unsync'ed\n");
112 break;
113 case BERT_REPORT_REGULAR:
114 printf("BERT report regular - %d bits, %d bad bits, %d resyncs\n", results->total_bits, results->bad_bits, results->resyncs);
115 memcpy(&latest_results, results, sizeof(latest_results));
116 break;
117 case BERT_REPORT_GT_10_2:
118 printf("BERT report > 1 in 10^2\n");
119 break;
120 case BERT_REPORT_LT_10_2:
121 printf("BERT report < 1 in 10^2\n");
122 break;
123 case BERT_REPORT_LT_10_3:
124 printf("BERT report < 1 in 10^3\n");
125 break;
126 case BERT_REPORT_LT_10_4:
127 printf("BERT report < 1 in 10^4\n");
128 break;
129 case BERT_REPORT_LT_10_5:
130 printf("BERT report < 1 in 10^5\n");
131 break;
132 case BERT_REPORT_LT_10_6:
133 printf("BERT report < 1 in 10^6\n");
134 break;
135 case BERT_REPORT_LT_10_7:
136 printf("BERT report < 1 in 10^7\n");
137 break;
138 default:
139 printf("BERT report reason %d\n", reason);
140 break;
141 }
142 }
143 /*- End of function --------------------------------------------------------*/
144
145 static void v17putbit(void *user_data, int bit)
146 {
147 v17_rx_state_t *rx;
148 int i;
149 int len;
150 complexf_t *coeffs;
151
152 rx = (v17_rx_state_t *) user_data;
153 if (bit < 0)
154 {
155 /* Special conditions */
156 switch (bit)
157 {
158 case PUTBIT_TRAINING_FAILED:
159 printf("Training failed\n");
160 break;
161 case PUTBIT_TRAINING_SUCCEEDED:
162 printf("Training succeeded\n");
163 len = v17_rx_equalizer_state(rx, &coeffs);
164 printf("Equalizer:\n");
165 for (i = 0; i < len; i++)
166 printf("%3d (%15.5f, %15.5f) -> %15.5f\n", i, coeffs[i].re, coeffs[i].im, powerf(&coeffs[i]));
167 break;
168 case PUTBIT_CARRIER_UP:
169 printf("Carrier up\n");
170 break;
171 case PUTBIT_CARRIER_DOWN:
172 printf("Carrier down\n");
173 break;
174 default:
175 printf("Eh!\n");
176 break;
177 }
178 return;
179 }
180
181 #if 0
182 printf("Rx bit %d - %d - %d\n", rx_bits++, bit, tx_bits);
183 #else
184 if (decode_test_file)
185 printf("Rx bit %d - %d\n", rx_bits++, bit);
186 else
187 bert_put_bit(&bert, bit);
188 #endif
189 }
190 /*- End of function --------------------------------------------------------*/
191
192 static int v17getbit(void *user_data)
193 {
194 #if 0
195 if (++tx_bits >= 50000)
196 {
197 tx_bits = 0;
198 return PUTBIT_END_OF_DATA;
199 }
200 // if (tx_bits < 200)
201 // return (tx_bits & 1);
202 if (tx_bits == 1)
203 return 0;
204 return 1;
205 #else
206 return bert_get_bit(&bert);
207 #endif
208 }
209 /*- End of function --------------------------------------------------------*/
210
211 static void qam_report(void *user_data, const complexf_t *constel, const complexf_t *target, int symbol)
212 {
213 int i;
214 int len;
215 complexf_t *coeffs;
216 float fpower;
217 v17_rx_state_t *rx;
218 static float smooth_power = 0.0;
219 static int update_interval = 100;
220
221 rx = (v17_rx_state_t *) user_data;
222 if (constel)
223 {
224 #if defined(ENABLE_GUI)
225 if (use_gui)
226 {
227 qam_monitor_update_constel(qam_monitor, constel);
228 qam_monitor_update_carrier_tracking(qam_monitor, v17_rx_carrier_frequency(rx));
229 qam_monitor_update_symbol_tracking(qam_monitor, v17_rx_symbol_timing_correction(rx));
230 }
231 #endif
232 fpower = (constel->re - target->re)*(constel->re - target->re)
233 + (constel->im - target->im)*(constel->im - target->im);
234 smooth_power = 0.95*smooth_power + 0.05*fpower;
235 printf("%8d [%8.4f, %8.4f] [%8.4f, %8.4f] %2x %8.4f %8.4f %9.4f %7.3f\n",
236 symbol_no,
237 constel->re,
238 constel->im,
239 target->re,
240 target->im,
241 symbol,
242 fpower,
243 smooth_power,
244 v17_rx_carrier_frequency(rx),
245 v17_rx_signal_power(rx));
246 printf("Carrier %d %f %f\n", symbol_no, v17_rx_carrier_frequency(rx), v17_rx_symbol_timing_correction(rx));
247 symbol_no++;
248 if (--update_interval <= 0)
249 {
250 len = v17_rx_equalizer_state(rx, &coeffs);
251 printf("Equalizer A:\n");
252 for (i = 0; i < len; i++)
253 printf("%3d (%15.5f, %15.5f) -> %15.5f\n", i, coeffs[i].re, coeffs[i].im, powerf(&coeffs[i]));
254 #if defined(ENABLE_GUI)
255 if (use_gui)
256 qam_monitor_update_equalizer(qam_monitor, coeffs, len);
257 #endif
258 update_interval = 100;
259 }
260 }
261 }
262 /*- End of function --------------------------------------------------------*/
263
264 int main(int argc, char *argv[])
265 {
266 v17_rx_state_t rx;
267 v17_tx_state_t tx;
268 bert_results_t bert_results;
269 int16_t gen_amp[BLOCK_LEN];
270 int16_t amp[BLOCK_LEN];
271 AFfilehandle inhandle;
272 AFfilehandle outhandle;
273 AFfilesetup filesetup;
274 int outframes;
275 int samples;
276 int i;
277 int tep;
278 int block_no;
279 int noise_level;
280 int signal_level;
281 int bits_per_test;
282 int line_model_no;
283 int log_audio;
284 int channel_codec;
285
286 channel_codec = MUNGE_CODEC_NONE;
287 test_bps = 14400;
288 tep = FALSE;
289 line_model_no = 0;
290 decode_test_file = NULL;
291 noise_level = -70;
292 signal_level = -13;
293 bits_per_test = 50000;
294 log_audio = FALSE;
295 for (i = 1; i < argc; i++)
296 {
297 if (strcmp(argv[i], "-b") == 0)
298 {
299 bits_per_test = atoi(argv[++i]);
300 continue;
301 }
302 if (strcmp(argv[i], "-c") == 0)
303 {
304 channel_codec = atoi(argv[++i]);
305 continue;
306 }
307 if (strcmp(argv[i], "-d") == 0)
308 {
309 decode_test_file = argv[++i];
310 continue;
311 }
312 if (strcmp(argv[i], "-t") == 0)
313 {
314 tep = TRUE;
315 continue;
316 }
317 if (strcmp(argv[i], "-g") == 0)
318 {
319 use_gui = TRUE;
320 continue;
321 }
322 if (strcmp(argv[i], "-l") == 0)
323 {
324 log_audio = TRUE;
325 continue;
326 }
327 if (strcmp(argv[i], "-m") == 0)
328 {
329 line_model_no = atoi(argv[++i]);
330 continue;
331 }
332 if (strcmp(argv[i], "-n") == 0)
333 {
334 noise_level = atoi(argv[++i]);
335 continue;
336 }
337 if (strcmp(argv[i], "-s") == 0)
338 {
339 signal_level = atoi(argv[++i]);
340 continue;
341 }
342 if (strcmp(argv[i], "14400") == 0)
343 test_bps = 14400;
344 else if (strcmp(argv[i], "12000") == 0)
345 test_bps = 12000;
346 else if (strcmp(argv[i], "9600") == 0)
347 test_bps = 9600;
348 else if (strcmp(argv[i], "7200") == 0)
349 test_bps = 7200;
350 else
351 {
352 fprintf(stderr, "Invalid bit rate\n");
353 exit(2);
354 }
355 }
356 inhandle = NULL;
357 outhandle = NULL;
358
359 filesetup = AF_NULL_FILESETUP;
360 if (log_audio)
361 {
362 if ((filesetup = afNewFileSetup()) == AF_NULL_FILESETUP)
363 {
364 fprintf(stderr, " Failed to create file setup\n");
365 exit(2);
366 }
367 afInitSampleFormat(filesetup, AF_DEFAULT_TRACK, AF_SAMPFMT_TWOSCOMP, 16);
368 afInitRate(filesetup, AF_DEFAULT_TRACK, (float) SAMPLE_RATE);
369 afInitFileFormat(filesetup, AF_FILE_WAVE);
370 afInitChannels(filesetup, AF_DEFAULT_TRACK, 1);
371 if ((outhandle = afOpenFile(OUT_FILE_NAME, "w", filesetup)) == AF_NULL_FILEHANDLE)
372 {
373 fprintf(stderr, " Cannot create wave file '%s'\n", OUT_FILE_NAME);
374 exit(2);
375 }
376 }
377
378 if (decode_test_file)
379 {
380 /* We will decode the audio from a wave file. */
381 if ((inhandle = afOpenFile(decode_test_file, "r", NULL)) == AF_NULL_FILEHANDLE)
382 {
383 fprintf(stderr, " Cannot open wave file '%s'\n", decode_test_file);
384 exit(2);
385 }
386 }
387 else
388 {
389 /* We will generate V.17 audio, and add some noise to it. */
390 v17_tx_init(&tx, test_bps, tep, v17getbit, NULL);
391 v17_tx_power(&tx, signal_level);
392 /* Move the carrier off a bit */
393 tx.carrier_phase_rate = dds_phase_ratef(1792.0);
394 tx.carrier_phase = 0x40000000;
395
396 bert_init(&bert, bits_per_test, BERT_PATTERN_ITU_O152_11, test_bps, 20);
397 bert_set_report(&bert, 10000, reporter, NULL);
398
399 if ((line_model = one_way_line_model_init(line_model_no, (float) noise_level, channel_codec)) == NULL)
400 {
401 fprintf(stderr, " Failed to create line model\n");
402 exit(2);
403 }
404 }
405
406 v17_rx_init(&rx, test_bps, v17putbit, &rx);
407 v17_rx_set_qam_report_handler(&rx, qam_report, (void *) &rx);
408 span_log_set_level(&rx.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
409 span_log_set_tag(&rx.logging, "V.17 rx");
410
411 #if defined(ENABLE_GUI)
412 if (use_gui)
413 {
414 qam_monitor = qam_monitor_init(10.0, NULL);
415 if (!decode_test_file)
416 {
417 start_line_model_monitor(129);
418 line_model_monitor_line_model_update(line_model->near_filter, line_model->near_filter_len);
419 }
420 }
421 #endif
422
423 memset(&latest_results, 0, sizeof(latest_results));
424 for (block_no = 0; block_no < 100000000; block_no++)
425 {
426 if (decode_test_file)
427 {
428 samples = afReadFrames(inhandle,
429 AF_DEFAULT_TRACK,
430 amp,
431 BLOCK_LEN);
432 if (samples == 0)
433 break;
434 #if defined(ENABLE_GUI)
435 if (use_gui)
436 qam_monitor_update_audio_level(qam_monitor, amp, samples);
437 #endif
438 }
439 else
440 {
441 samples = v17_tx(&tx, gen_amp, BLOCK_LEN);
442 #if defined(ENABLE_GUI)
443 if (use_gui)
444 qam_monitor_update_audio_level(qam_monitor, gen_amp, samples);
445 #endif
446 if (samples == 0)
447 {
448 printf("Restarting on zero output\n");
449
450 /* Push a little silence through, to ensure all the data bits get out of the buffers */
451 memset(amp, 0, BLOCK_LEN*sizeof(int16_t));
452 v17_rx(&rx, amp, BLOCK_LEN);
453
454 /* Note that we might get a few bad bits as the carrier shuts down. */
455 bert_result(&bert, &bert_results);
456 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);
457 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);
458 /* See if bit errors are appearing yet. Also check we are getting enough bits out of the receiver. The last regular report
459 should be error free, though the final report will generally contain bits errors as the carrier was dying. The total
460 number of bits out of the receiver should be at least the number we sent. Also, since BERT sync should have occurred
461 rapidly at the start of transmission, the last report should have occurred at not much less than the total number of
462 bits we sent. */
463 #if 0
464 if (bert_results.total_bits < bits_per_test
465 ||
466 latest_results.total_bits < bits_per_test - 100
467 ||
468 latest_results.bad_bits != 0)
469 {
470 break;
471 }
472 #endif
473 memset(&latest_results, 0, sizeof(latest_results));
474 v17_tx_restart(&tx, test_bps, tep, TRUE);
475 v17_tx_power(&tx, signal_level);
476 v17_rx_restart(&rx, test_bps, TRUE);
477 rx.eq_put_step = rand()%(192*10/3);
478 bert_init(&bert, bits_per_test, BERT_PATTERN_ITU_O152_11, test_bps, 20);
479 bert_set_report(&bert, 10000, reporter, NULL);
480 }
481 if (log_audio)
482 {
483 outframes = afWriteFrames(outhandle,
484 AF_DEFAULT_TRACK,
485 gen_amp,
486 samples);
487 if (outframes != samples)
488 {
489 fprintf(stderr, " Error writing wave file\n");
490 exit(2);
491 }
492 }
493 one_way_line_model(line_model, amp, gen_amp, samples);
494 }
495 #if defined(ENABLE_GUI)
496 if (use_gui && !decode_test_file)
497 line_model_monitor_line_spectrum_update(amp, samples);
498 #endif
499 v17_rx(&rx, amp, samples);
500 }
501 if (decode_test_file)
502 {
503 #if defined(ENABLE_GUI)
504 if (use_gui)
505 qam_wait_to_end(qam_monitor);
506 #endif
507 }
508 else
509 {
510 bert_result(&bert, &bert_results);
511 fprintf(stderr, "At completion:\n");
512 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);
513 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);
514 one_way_line_model_release(line_model);
515 }
516 if (log_audio)
517 {
518 if (afCloseFile(outhandle))
519 {
520 fprintf(stderr, " Cannot close wave file '%s'\n", OUT_FILE_NAME);
521 exit(2);
522 }
523 afFreeFileSetup(filesetup);
524 }
525 return 0;
526 }
527 /*- End of function --------------------------------------------------------*/
528 /*- End of file ------------------------------------------------------------*/

Repositories maintained by Peter Meerwald, pmeerw@pmeerw.net.