Mercurial > hg > audiostuff
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 ------------------------------------------------------------*/ |