comparison spandsp-0.0.6pre17/tests/v17_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 //#define ADD_MAINS_INTERFERENCE
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.104 2009/07/09 13:52:09 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 audio 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 /* Enable the following definition to enable direct probing into the FAX structures */
50 #define WITH_SPANDSP_INTERNALS
51
52 #if defined(HAVE_CONFIG_H)
53 #include "config.h"
54 #endif
55
56 #if defined(HAVE_FL_FL_H) && defined(HAVE_FL_FL_CARTESIAN_H) && defined(HAVE_FL_FL_AUDIO_METER_H)
57 #define ENABLE_GUI
58 #endif
59
60 #include <stdlib.h>
61 #include <stdio.h>
62 #include <fcntl.h>
63 #include <unistd.h>
64 #include <string.h>
65 #include <sndfile.h>
66
67 //#if defined(WITH_SPANDSP_INTERNALS)
68 #define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
69 //#endif
70
71 #include "spandsp.h"
72 #include "spandsp-sim.h"
73
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 "v17.wav"
82
83 char *decode_test_file = NULL;
84 int use_gui = FALSE;
85
86 int symbol_no = 0;
87
88 int rx_bits = 0;
89 int tx_bits = 0;
90
91 int test_bps;
92
93 bert_state_t bert;
94 one_way_line_model_state_t *line_model;
95
96 #if defined(ENABLE_GUI)
97 qam_monitor_t *qam_monitor;
98 #endif
99
100 bert_results_t latest_results;
101
102 static void reporter(void *user_data, int reason, bert_results_t *results)
103 {
104 switch (reason)
105 {
106 case BERT_REPORT_REGULAR:
107 fprintf(stderr, "BERT report regular - %d bits, %d bad bits, %d resyncs\n", results->total_bits, results->bad_bits, results->resyncs);
108 memcpy(&latest_results, results, sizeof(latest_results));
109 break;
110 default:
111 fprintf(stderr, "BERT report %s\n", bert_event_to_str(reason));
112 break;
113 }
114 }
115 /*- End of function --------------------------------------------------------*/
116
117 static void v17_rx_status(void *user_data, int status)
118 {
119 v17_rx_state_t *rx;
120 int i;
121 int len;
122 complexf_t *coeffs;
123
124 printf("V.17 rx status is %s (%d)\n", signal_status_to_str(status), status);
125 rx = (v17_rx_state_t *) user_data;
126 switch (status)
127 {
128 case SIG_STATUS_TRAINING_SUCCEEDED:
129 len = v17_rx_equalizer_state(rx, &coeffs);
130 printf("Equalizer:\n");
131 for (i = 0; i < len; i++)
132 printf("%3d (%15.5f, %15.5f) -> %15.5f\n", i, coeffs[i].re, coeffs[i].im, powerf(&coeffs[i]));
133 break;
134 }
135 }
136 /*- End of function --------------------------------------------------------*/
137
138 static void v17putbit(void *user_data, int bit)
139 {
140 v17_rx_state_t *rx;
141
142 if (bit < 0)
143 {
144 v17_rx_status(user_data, bit);
145 return;
146 }
147
148 rx = (v17_rx_state_t *) user_data;
149 if (decode_test_file)
150 printf("Rx bit %d - %d\n", rx_bits++, bit);
151 else
152 bert_put_bit(&bert, bit);
153 }
154 /*- End of function --------------------------------------------------------*/
155
156 static void v17_tx_status(void *user_data, int status)
157 {
158 printf("V.17 tx status is %s (%d)\n", signal_status_to_str(status), status);
159 }
160 /*- End of function --------------------------------------------------------*/
161
162 static int v17getbit(void *user_data)
163 {
164 return bert_get_bit(&bert);
165 }
166 /*- End of function --------------------------------------------------------*/
167
168 static void qam_report(void *user_data, const complexf_t *constel, const complexf_t *target, int symbol)
169 {
170 int i;
171 int len;
172 complexf_t *coeffs;
173 float fpower;
174 v17_rx_state_t *rx;
175 static float smooth_power = 0.0f;
176 static int update_interval = 100;
177
178 rx = (v17_rx_state_t *) user_data;
179 if (constel)
180 {
181 #if defined(ENABLE_GUI)
182 if (use_gui)
183 {
184 qam_monitor_update_constel(qam_monitor, constel);
185 qam_monitor_update_carrier_tracking(qam_monitor, v17_rx_carrier_frequency(rx));
186 qam_monitor_update_symbol_tracking(qam_monitor, v17_rx_symbol_timing_correction(rx));
187 }
188 #endif
189 fpower = (constel->re - target->re)*(constel->re - target->re)
190 + (constel->im - target->im)*(constel->im - target->im);
191 smooth_power = 0.95f*smooth_power + 0.05f*fpower;
192 printf("%8d [%8.4f, %8.4f] [%8.4f, %8.4f] %2x %8.4f %8.4f %9.4f %7.3f %7.4f\n",
193 symbol_no,
194 constel->re,
195 constel->im,
196 target->re,
197 target->im,
198 symbol,
199 fpower,
200 smooth_power,
201 v17_rx_carrier_frequency(rx),
202 v17_rx_signal_power(rx),
203 v17_rx_symbol_timing_correction(rx));
204 printf("Carrier %d %f %f\n", symbol_no, v17_rx_carrier_frequency(rx), v17_rx_symbol_timing_correction(rx));
205 symbol_no++;
206 if (--update_interval <= 0)
207 {
208 len = v17_rx_equalizer_state(rx, &coeffs);
209 printf("Equalizer A:\n");
210 for (i = 0; i < len; i++)
211 printf("%3d (%15.5f, %15.5f) -> %15.5f\n", i, coeffs[i].re, coeffs[i].im, powerf(&coeffs[i]));
212 #if defined(ENABLE_GUI)
213 if (use_gui)
214 qam_monitor_update_equalizer(qam_monitor, coeffs, len);
215 #endif
216 update_interval = 100;
217 }
218 }
219 }
220 /*- End of function --------------------------------------------------------*/
221
222 int main(int argc, char *argv[])
223 {
224 v17_rx_state_t *rx;
225 v17_tx_state_t *tx;
226 bert_results_t bert_results;
227 int16_t gen_amp[BLOCK_LEN];
228 int16_t amp[BLOCK_LEN];
229 SNDFILE *inhandle;
230 SNDFILE *outhandle;
231 int outframes;
232 int samples;
233 int tep;
234 int block_no;
235 int noise_level;
236 int signal_level;
237 int bits_per_test;
238 int line_model_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 = 14400;
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 != 14400 && test_bps != 12000 && test_bps != 9600 && test_bps != 7200)
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.17 audio, and add some noise to it. */
334 tx = v17_tx_init(NULL, test_bps, tep, v17getbit, NULL);
335 logging = v17_tx_get_logging_state(tx);
336 span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
337 span_log_set_tag(logging, "V.17 tx");
338 v17_tx_power(tx, signal_level);
339 v17_tx_set_modem_status_handler(tx, v17_tx_status, (void *) tx);
340 #if defined(WITH_SPANDSP_INTERNALS)
341 /* Move the carrier off a bit */
342 tx->carrier_phase_rate = dds_phase_ratef(1792.0f);
343 tx->carrier_phase = 0x40000000;
344 #endif
345
346 bert_init(&bert, bits_per_test, BERT_PATTERN_ITU_O152_11, test_bps, 20);
347 bert_set_report(&bert, 10000, reporter, NULL);
348
349 if ((line_model = one_way_line_model_init(line_model_no, (float) noise_level, channel_codec, rbs_pattern)) == NULL)
350 {
351 fprintf(stderr, " Failed to create line model\n");
352 exit(2);
353 }
354 one_way_line_model_set_dc(line_model, 0.0f);
355 #if defined(ADD_MAINS_INTERFERENCE)
356 one_way_line_model_set_mains_pickup(line_model, 50, -40.0f);
357 #endif
358 }
359
360 rx = v17_rx_init(NULL, test_bps, v17putbit, NULL);
361 v17_rx_set_modem_status_handler(rx, v17_rx_status, (void *) rx);
362 v17_rx_set_qam_report_handler(rx, qam_report, (void *) rx);
363 logging = v17_rx_get_logging_state(rx);
364 span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
365 span_log_set_tag(logging, "V.17 rx");
366
367 #if defined(ENABLE_GUI)
368 if (use_gui)
369 {
370 qam_monitor = qam_monitor_init(10.0f, NULL);
371 if (!decode_test_file)
372 {
373 start_line_model_monitor(129);
374 line_model_monitor_line_model_update(line_model->near_filter, line_model->near_filter_len);
375 }
376 }
377 #endif
378
379 memset(&latest_results, 0, sizeof(latest_results));
380 for (block_no = 0; block_no < 100000000; block_no++)
381 {
382 if (decode_test_file)
383 {
384 samples = sf_readf_short(inhandle, amp, BLOCK_LEN);
385 #if defined(ENABLE_GUI)
386 if (use_gui)
387 qam_monitor_update_audio_level(qam_monitor, amp, samples);
388 #endif
389 if (samples == 0)
390 break;
391 }
392 else
393 {
394 samples = v17_tx(tx, gen_amp, BLOCK_LEN);
395 #if defined(ENABLE_GUI)
396 if (use_gui)
397 qam_monitor_update_audio_level(qam_monitor, gen_amp, samples);
398 #endif
399 if (samples == 0)
400 {
401 printf("Restarting on zero output\n");
402
403 /* Push a little silence through, to ensure all the data bits get out of the buffers */
404 memset(amp, 0, BLOCK_LEN*sizeof(int16_t));
405 v17_rx(rx, amp, BLOCK_LEN);
406
407 /* Note that we might get a few bad bits as the carrier shuts down. */
408 bert_result(&bert, &bert_results);
409 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);
410 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);
411 /* See if bit errors are appearing yet. Also check we are getting enough bits out of the receiver. The last regular report
412 should be error free, though the final report will generally contain bits errors as the carrier was dying. The total
413 number of bits out of the receiver should be at least the number we sent. Also, since BERT sync should have occurred
414 rapidly at the start of transmission, the last report should have occurred at not much less than the total number of
415 bits we sent. */
416 if (bert_results.total_bits < bits_per_test
417 ||
418 latest_results.total_bits < bits_per_test - 100
419 ||
420 latest_results.bad_bits != 0)
421 {
422 break;
423 }
424 memset(&latest_results, 0, sizeof(latest_results));
425 #if defined(WITH_SPANDSP_INTERNALS)
426 signal_level--;
427 /* Bump the receiver AGC gain by 1dB, to compensate for the above */
428 rx->agc_scaling_save *= 1.122f;
429 #endif
430 v17_tx_restart(tx, test_bps, tep, TRUE);
431 v17_tx_power(tx, signal_level);
432 v17_rx_restart(rx, test_bps, TRUE);
433 //rx.eq_put_step = rand()%(192*10/3);
434 bert_init(&bert, bits_per_test, BERT_PATTERN_ITU_O152_11, test_bps, 20);
435 bert_set_report(&bert, 10000, reporter, NULL);
436 one_way_line_model_release(line_model);
437 if ((line_model = one_way_line_model_init(line_model_no, (float) noise_level, channel_codec, 0)) == NULL)
438 {
439 fprintf(stderr, " Failed to create line model\n");
440 exit(2);
441 }
442 }
443 if (log_audio)
444 {
445 outframes = sf_writef_short(outhandle, gen_amp, samples);
446 if (outframes != samples)
447 {
448 fprintf(stderr, " Error writing audio file\n");
449 exit(2);
450 }
451 }
452 one_way_line_model(line_model, amp, gen_amp, samples);
453 }
454 #if defined(ENABLE_GUI)
455 if (use_gui && !decode_test_file)
456 line_model_monitor_line_spectrum_update(amp, samples);
457 #endif
458 v17_rx(rx, amp, samples);
459 }
460 if (!decode_test_file)
461 {
462 bert_result(&bert, &bert_results);
463 fprintf(stderr, "At completion:\n");
464 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);
465 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);
466 one_way_line_model_release(line_model);
467
468 if (signal_level > -43)
469 {
470 printf("Tests failed.\n");
471 exit(2);
472 }
473
474 printf("Tests passed.\n");
475 }
476 #if defined(ENABLE_GUI)
477 if (use_gui)
478 qam_wait_to_end(qam_monitor);
479 #endif
480 if (decode_test_file)
481 {
482 if (sf_close(inhandle))
483 {
484 fprintf(stderr, " Cannot close audio file '%s'\n", decode_test_file);
485 exit(2);
486 }
487 }
488 if (log_audio)
489 {
490 if (sf_close(outhandle))
491 {
492 fprintf(stderr, " Cannot close audio file '%s'\n", OUT_FILE_NAME);
493 exit(2);
494 }
495 }
496 return 0;
497 }
498 /*- End of function --------------------------------------------------------*/
499 /*- End of file ------------------------------------------------------------*/

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