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