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