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

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