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

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