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 ------------------------------------------------------------*/

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