Mercurial > hg > audiostuff
comparison spandsp-0.0.6pre17/tests/v27ter_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 * 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.106 2009/07/09 13:52:09 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 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 v27ter_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 "v27ter.wav" | |
81 | |
82 char *decode_test_file = NULL; | |
83 int use_gui = FALSE; | |
84 | |
85 int symbol_no = 0; | |
86 | |
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_REGULAR: | |
103 fprintf(stderr, "BERT report regular - %d bits, %d bad bits, %d resyncs\n", results->total_bits, results->bad_bits, results->resyncs); | |
104 memcpy(&latest_results, results, sizeof(latest_results)); | |
105 break; | |
106 default: | |
107 fprintf(stderr, "BERT report %s\n", bert_event_to_str(reason)); | |
108 break; | |
109 } | |
110 } | |
111 /*- End of function --------------------------------------------------------*/ | |
112 | |
113 static void v27ter_rx_status(void *user_data, int status) | |
114 { | |
115 printf("V.27ter rx status is %s (%d)\n", signal_status_to_str(status), status); | |
116 } | |
117 /*- End of function --------------------------------------------------------*/ | |
118 | |
119 static void v27terputbit(void *user_data, int bit) | |
120 { | |
121 if (bit < 0) | |
122 { | |
123 v27ter_rx_status(user_data, bit); | |
124 return; | |
125 } | |
126 if (decode_test_file) | |
127 printf("Rx bit %d - %d\n", rx_bits++, bit); | |
128 else | |
129 bert_put_bit(&bert, bit); | |
130 } | |
131 /*- End of function --------------------------------------------------------*/ | |
132 | |
133 static void v27ter_tx_status(void *user_data, int status) | |
134 { | |
135 printf("V.27ter tx status is %s (%d)\n", signal_status_to_str(status), status); | |
136 } | |
137 /*- End of function --------------------------------------------------------*/ | |
138 | |
139 static int v27tergetbit(void *user_data) | |
140 { | |
141 return bert_get_bit(&bert); | |
142 } | |
143 /*- End of function --------------------------------------------------------*/ | |
144 | |
145 static void qam_report(void *user_data, const complexf_t *constel, const complexf_t *target, int symbol) | |
146 { | |
147 int i; | |
148 int len; | |
149 complexf_t *coeffs; | |
150 float fpower; | |
151 float error; | |
152 v27ter_rx_state_t *rx; | |
153 static float smooth_power = 0.0f; | |
154 #if defined(ENABLE_GUI) | |
155 static int reports = 0; | |
156 #endif | |
157 | |
158 rx = (v27ter_rx_state_t *) user_data; | |
159 if (constel) | |
160 { | |
161 fpower = (constel->re - target->re)*(constel->re - target->re) | |
162 + (constel->im - target->im)*(constel->im - target->im); | |
163 smooth_power = 0.95f*smooth_power + 0.05f*fpower; | |
164 #if defined(ENABLE_GUI) | |
165 if (use_gui) | |
166 { | |
167 qam_monitor_update_constel(qam_monitor, constel); | |
168 qam_monitor_update_carrier_tracking(qam_monitor, v27ter_rx_carrier_frequency(rx)); | |
169 qam_monitor_update_symbol_tracking(qam_monitor, v27ter_rx_symbol_timing_correction(rx)); | |
170 } | |
171 #endif | |
172 error = constel->im*target->re - constel->re*target->im; | |
173 printf("Tracking error %f %f %f %f %f %f\n", error, v27ter_rx_carrier_frequency(rx), constel->re, constel->im, target->re, target->im); | |
174 printf("%8d [%8.4f, %8.4f] [%8.4f, %8.4f] %2x %8.4f %8.4f %9.4f %7.3f %7.4f\n", | |
175 symbol_no, | |
176 constel->re, | |
177 constel->im, | |
178 target->re, | |
179 target->im, | |
180 symbol, | |
181 fpower, | |
182 smooth_power, | |
183 v27ter_rx_carrier_frequency(rx), | |
184 v27ter_rx_signal_power(rx), | |
185 v27ter_rx_symbol_timing_correction(rx)); | |
186 len = v27ter_rx_equalizer_state(rx, &coeffs); | |
187 printf("Equalizer B:\n"); | |
188 for (i = 0; i < len; i++) | |
189 printf("%3d (%15.5f, %15.5f) -> %15.5f\n", i, coeffs[i].re, coeffs[i].im, powerf(&coeffs[i])); | |
190 #if defined(WITH_SPANDSP_INTERNALS) | |
191 printf("Gardtest %d %f %d\n", symbol_no, v27ter_rx_symbol_timing_correction(rx), rx->gardner_integrate); | |
192 #endif | |
193 printf("Carcar %d %f\n", symbol_no, v27ter_rx_carrier_frequency(rx)); | |
194 #if defined(ENABLE_GUI) | |
195 if (use_gui) | |
196 { | |
197 if (++reports >= 1000) | |
198 { | |
199 qam_monitor_update_equalizer(qam_monitor, coeffs, len); | |
200 reports = 0; | |
201 } | |
202 } | |
203 #endif | |
204 symbol_no++; | |
205 } | |
206 else | |
207 { | |
208 printf("Gardner step %d\n", symbol); | |
209 len = v27ter_rx_equalizer_state(rx, &coeffs); | |
210 printf("Equalizer A:\n"); | |
211 for (i = 0; i < len; i++) | |
212 printf("%3d (%15.5f, %15.5f) -> %15.5f\n", i, coeffs[i].re, coeffs[i].im, powerf(&coeffs[i])); | |
213 #if defined(ENABLE_GUI) | |
214 if (use_gui) | |
215 qam_monitor_update_equalizer(qam_monitor, coeffs, len); | |
216 #endif | |
217 } | |
218 } | |
219 /*- End of function --------------------------------------------------------*/ | |
220 | |
221 int main(int argc, char *argv[]) | |
222 { | |
223 v27ter_rx_state_t *rx; | |
224 v27ter_tx_state_t *tx; | |
225 bert_results_t bert_results; | |
226 int16_t gen_amp[BLOCK_LEN]; | |
227 int16_t amp[BLOCK_LEN]; | |
228 SNDFILE *inhandle; | |
229 SNDFILE *outhandle; | |
230 int outframes; | |
231 int samples; | |
232 int tep; | |
233 int test_bps; | |
234 int noise_level; | |
235 int signal_level; | |
236 int bits_per_test; | |
237 int line_model_no; | |
238 int block_no; | |
239 int log_audio; | |
240 int channel_codec; | |
241 int rbs_pattern; | |
242 int opt; | |
243 logging_state_t *logging; | |
244 | |
245 channel_codec = MUNGE_CODEC_NONE; | |
246 rbs_pattern = 0; | |
247 test_bps = 4800; | |
248 tep = FALSE; | |
249 line_model_no = 0; | |
250 decode_test_file = NULL; | |
251 use_gui = FALSE; | |
252 noise_level = -70; | |
253 signal_level = -13; | |
254 bits_per_test = 50000; | |
255 log_audio = FALSE; | |
256 while ((opt = getopt(argc, argv, "b:B:c:d:glm:n:r:s:t")) != -1) | |
257 { | |
258 switch (opt) | |
259 { | |
260 case 'b': | |
261 test_bps = atoi(optarg); | |
262 if (test_bps != 4800 && test_bps != 2400) | |
263 { | |
264 fprintf(stderr, "Invalid bit rate specified\n"); | |
265 exit(2); | |
266 } | |
267 break; | |
268 case 'B': | |
269 bits_per_test = atoi(optarg); | |
270 break; | |
271 case 'c': | |
272 channel_codec = atoi(optarg); | |
273 break; | |
274 case 'd': | |
275 decode_test_file = optarg; | |
276 break; | |
277 case 'g': | |
278 #if defined(ENABLE_GUI) | |
279 use_gui = TRUE; | |
280 #else | |
281 fprintf(stderr, "Graphical monitoring not available\n"); | |
282 exit(2); | |
283 #endif | |
284 break; | |
285 case 'l': | |
286 log_audio = TRUE; | |
287 break; | |
288 case 'm': | |
289 line_model_no = atoi(optarg); | |
290 break; | |
291 case 'n': | |
292 noise_level = atoi(optarg); | |
293 break; | |
294 case 'r': | |
295 rbs_pattern = atoi(optarg); | |
296 break; | |
297 case 's': | |
298 signal_level = atoi(optarg); | |
299 break; | |
300 case 't': | |
301 tep = TRUE; | |
302 break; | |
303 default: | |
304 //usage(); | |
305 exit(2); | |
306 break; | |
307 } | |
308 } | |
309 inhandle = NULL; | |
310 outhandle = NULL; | |
311 | |
312 if (log_audio) | |
313 { | |
314 if ((outhandle = sf_open_telephony_write(OUT_FILE_NAME, 1)) == NULL) | |
315 { | |
316 fprintf(stderr, " Cannot create audio file '%s'\n", OUT_FILE_NAME); | |
317 exit(2); | |
318 } | |
319 } | |
320 | |
321 if (decode_test_file) | |
322 { | |
323 /* We will decode the audio from a file. */ | |
324 tx = NULL; | |
325 if ((inhandle = sf_open_telephony_read(decode_test_file, 1)) == NULL) | |
326 { | |
327 fprintf(stderr, " Cannot open audio file '%s'\n", decode_test_file); | |
328 exit(2); | |
329 } | |
330 } | |
331 else | |
332 { | |
333 /* We will generate V.27ter audio, and add some noise to it. */ | |
334 tx = v27ter_tx_init(NULL, test_bps, tep, v27tergetbit, NULL); | |
335 v27ter_tx_power(tx, signal_level); | |
336 v27ter_tx_set_modem_status_handler(tx, v27ter_tx_status, (void *) tx); | |
337 /* Move the carrier off a bit */ | |
338 #if defined(WITH_SPANDSP_INTERNALS) | |
339 tx->carrier_phase_rate = dds_phase_ratef(1810.0f); | |
340 #endif | |
341 bert_init(&bert, bits_per_test, BERT_PATTERN_ITU_O152_11, test_bps, 20); | |
342 bert_set_report(&bert, 10000, reporter, NULL); | |
343 | |
344 if ((line_model = one_way_line_model_init(line_model_no, (float) noise_level, channel_codec, rbs_pattern)) == NULL) | |
345 { | |
346 fprintf(stderr, " Failed to create line model\n"); | |
347 exit(2); | |
348 } | |
349 } | |
350 | |
351 rx = v27ter_rx_init(NULL, test_bps, v27terputbit, NULL); | |
352 v27ter_rx_set_modem_status_handler(rx, v27ter_rx_status, (void *) rx); | |
353 v27ter_rx_set_qam_report_handler(rx, qam_report, (void *) rx); | |
354 logging = v27ter_rx_get_logging_state(rx); | |
355 span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW); | |
356 span_log_set_tag(logging, "V.27ter-rx"); | |
357 | |
358 #if defined(ENABLE_GUI) | |
359 if (use_gui) | |
360 { | |
361 qam_monitor = qam_monitor_init(2.0f, NULL); | |
362 if (!decode_test_file) | |
363 { | |
364 start_line_model_monitor(129); | |
365 line_model_monitor_line_model_update(line_model->near_filter, line_model->near_filter_len); | |
366 } | |
367 } | |
368 #endif | |
369 | |
370 memset(&latest_results, 0, sizeof(latest_results)); | |
371 for (block_no = 0; ; block_no++) | |
372 { | |
373 if (decode_test_file) | |
374 { | |
375 samples = sf_readf_short(inhandle, amp, BLOCK_LEN); | |
376 #if defined(ENABLE_GUI) | |
377 if (use_gui) | |
378 qam_monitor_update_audio_level(qam_monitor, amp, samples); | |
379 #endif | |
380 if (samples == 0) | |
381 break; | |
382 } | |
383 else | |
384 { | |
385 samples = v27ter_tx(tx, gen_amp, BLOCK_LEN); | |
386 #if defined(ENABLE_GUI) | |
387 if (use_gui) | |
388 qam_monitor_update_audio_level(qam_monitor, gen_amp, samples); | |
389 #endif | |
390 if (samples == 0) | |
391 { | |
392 printf("Restarting on zero output\n"); | |
393 | |
394 /* Push a little silence through, to ensure all the data bits get out of the buffers */ | |
395 memset(amp, 0, BLOCK_LEN*sizeof(int16_t)); | |
396 v27ter_rx(rx, amp, BLOCK_LEN); | |
397 v27ter_rx(rx, amp, BLOCK_LEN); | |
398 v27ter_rx(rx, amp, BLOCK_LEN); | |
399 | |
400 /* Note that we might get a few bad bits as the carrier shuts down. */ | |
401 bert_result(&bert, &bert_results); | |
402 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); | |
403 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); | |
404 /* See if bit errors are appearing yet. Also check we are getting enough bits out of the receiver. The last regular report | |
405 should be error free, though the final report will generally contain bits errors as the carrier was dying. The total | |
406 number of bits out of the receiver should be at least the number we sent. Also, since BERT sync should have occurred | |
407 rapidly at the start of transmission, the last report should have occurred at not much less than the total number of | |
408 bits we sent. */ | |
409 if (bert_results.total_bits < bits_per_test | |
410 || | |
411 latest_results.total_bits < bits_per_test - 100 | |
412 || | |
413 latest_results.bad_bits != 0) | |
414 { | |
415 break; | |
416 } | |
417 memset(&latest_results, 0, sizeof(latest_results)); | |
418 signal_level--; | |
419 v27ter_tx_restart(tx, test_bps, tep); | |
420 v27ter_tx_power(tx, signal_level); | |
421 v27ter_rx_restart(rx, test_bps, FALSE); | |
422 bert_init(&bert, bits_per_test, BERT_PATTERN_ITU_O152_11, test_bps, 20); | |
423 bert_set_report(&bert, 10000, reporter, NULL); | |
424 one_way_line_model_release(line_model); | |
425 if ((line_model = one_way_line_model_init(line_model_no, (float) noise_level, channel_codec, 0)) == NULL) | |
426 { | |
427 fprintf(stderr, " Failed to create line model\n"); | |
428 exit(2); | |
429 } | |
430 } | |
431 | |
432 if (log_audio) | |
433 { | |
434 outframes = sf_writef_short(outhandle, gen_amp, samples); | |
435 if (outframes != samples) | |
436 { | |
437 fprintf(stderr, " Error writing audio file\n"); | |
438 exit(2); | |
439 } | |
440 } | |
441 one_way_line_model(line_model, amp, gen_amp, samples); | |
442 } | |
443 #if defined(ENABLE_GUI) | |
444 if (use_gui && !decode_test_file) | |
445 line_model_monitor_line_spectrum_update(amp, samples); | |
446 #endif | |
447 v27ter_rx(rx, amp, samples); | |
448 } | |
449 if (!decode_test_file) | |
450 { | |
451 bert_result(&bert, &bert_results); | |
452 fprintf(stderr, "At completion:\n"); | |
453 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); | |
454 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); | |
455 one_way_line_model_release(line_model); | |
456 if (signal_level > -43) | |
457 { | |
458 printf("Tests failed.\n"); | |
459 exit(2); | |
460 } | |
461 | |
462 printf("Tests passed.\n"); | |
463 } | |
464 #if defined(ENABLE_GUI) | |
465 if (use_gui) | |
466 qam_wait_to_end(qam_monitor); | |
467 #endif | |
468 if (decode_test_file) | |
469 { | |
470 if (sf_close(inhandle)) | |
471 { | |
472 fprintf(stderr, " Cannot close audio file '%s'\n", decode_test_file); | |
473 exit(2); | |
474 } | |
475 } | |
476 if (log_audio) | |
477 { | |
478 if (sf_close(outhandle)) | |
479 { | |
480 fprintf(stderr, " Cannot close audio file '%s'\n", OUT_FILE_NAME); | |
481 exit(2); | |
482 } | |
483 } | |
484 return 0; | |
485 } | |
486 /*- End of function --------------------------------------------------------*/ | |
487 /*- End of file ------------------------------------------------------------*/ |