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

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