1 /*
2 * SpanDSP - a series of DSP components for telephony
3 *
4 * v22bis_tests.c
5 *
6 * Written by Steve Underwood <steveu@coppice.org>
7 *
8 * Copyright (C) 2004 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: v22bis_tests.c,v 1.50 2008/05/13 13:17:26 steveu Exp $
26 */
27
28 /*! \page v22bis_tests_page V.22bis modem tests
29 \section v22bis_tests_page_sec_1 What does it do?
30 These tests connect two V.22bis modems back to back, through a telephone line
31 model. BER testing is then used to evaluate performance under various line
32 conditions.
33
34 If the appropriate GUI environment exists, the tests are built such that a visual
35 display of modem status is maintained.
36
37 \section v22bis_tests_page_sec_2 How is it used?
38 */
39
40 #if defined(HAVE_CONFIG_H)
41 #include "config.h"
42 #endif
43
44 #if defined(HAVE_FL_FL_H) && defined(HAVE_FL_FL_CARTESIAN_H) && defined(HAVE_FL_FL_AUDIO_METER_H)
45 #define ENABLE_GUI
46 #endif
47
48 #include <stdlib.h>
49 #include <stdio.h>
50 #include <fcntl.h>
51 #include <unistd.h>
52 #include <string.h>
53 #include <audiofile.h>
54
55 #include "spandsp.h"
56 #include "spandsp-sim.h"
57
58 #if defined(ENABLE_GUI)
59 #include "modem_monitor.h"
60 #endif
61
62 #define BLOCK_LEN 160
63
64 #define IN_FILE_NAME "v22bis_samp.wav"
65 #define OUT_FILE_NAME "v22bis.wav"
66
67 int in_bit = 0;
68 int out_bit = 0;
69
70 int in_bit_no = 0;
71 int out_bit_no = 0;
72
73 uint8_t tx_buf[1000];
74 int rx_ptr = 0;
75 int tx_ptr = 0;
76
77 int rx_bits = 0;
78 int rx_bad_bits = 0;
79
80 int use_gui = FALSE;
81
82 both_ways_line_model_state_t *model;
83
84 v22bis_state_t caller;
85 v22bis_state_t answerer;
86
87 struct qam_report_control_s
88 {
89 v22bis_state_t *s;
90 #if defined(ENABLE_GUI)
91 qam_monitor_t *qam_monitor;
92 #endif
93 float smooth_power;
94 int symbol_no;
95 };
96
97 struct qam_report_control_s qam_caller;
98 struct qam_report_control_s qam_answerer;
99
v22bis_putbit(void * user_data,int bit)100 static void v22bis_putbit(void *user_data, int bit)
101 {
102 v22bis_state_t *s;
103 int i;
104 int len;
105 complexf_t *coeffs;
106
107 s = (v22bis_state_t *) user_data;
108 if (bit < 0)
109 {
110 /* Special conditions */
111 switch (bit)
112 {
113 case PUTBIT_TRAINING_FAILED:
114 printf("Training failed\n");
115 break;
116 case PUTBIT_TRAINING_IN_PROGRESS:
117 printf("Training in progress\n");
118 break;
119 case PUTBIT_TRAINING_SUCCEEDED:
120 printf("Training succeeded\n");
121 len = v22bis_equalizer_state(s, &coeffs);
122 printf("Equalizer:\n");
123 for (i = 0; i < len; i++)
124 printf("%3d (%15.5f, %15.5f) -> %15.5f\n", i, coeffs[i].re, coeffs[i].im, powerf(&coeffs[i]));
125 break;
126 case PUTBIT_CARRIER_UP:
127 printf("Carrier up\n");
128 break;
129 case PUTBIT_CARRIER_DOWN:
130 printf("Carrier down\n");
131 break;
132 default:
133 printf("Eh! - %d\n", bit);
134 break;
135 }
136 return;
137 }
138
139 if (bit != tx_buf[rx_ptr])
140 {
141 printf("Rx bit %d - %d\n", rx_bits, bit);
142 rx_bad_bits++;
143 }
144 rx_ptr++;
145 if (rx_ptr > 1000)
146 rx_ptr = 0;
147 rx_bits++;
148 if ((rx_bits % 100000) == 0)
149 {
150 printf("%d bits received, %d bad bits\r", rx_bits, rx_bad_bits);
151 fflush(stdout);
152 }
153 }
154 /*- End of function --------------------------------------------------------*/
155
v22bis_getbit(void * user_data)156 static int v22bis_getbit(void *user_data)
157 {
158 int bit;
159 static int tx_bits = 0;
160
161 bit = rand() & 1;
162 tx_buf[tx_ptr++] = bit;
163 if (tx_ptr > 1000)
164 tx_ptr = 0;
165 //printf("Tx bit %d\n", bit);
166 if (++tx_bits > 100000)
167 {
168 tx_bits = 0;
169 bit = 2;
170 }
171 return bit;
172 }
173 /*- End of function --------------------------------------------------------*/
174
qam_report(void * user_data,const complexf_t * constel,const complexf_t * target,int symbol)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 complexf_t *coeffs;
180 float fpower;
181 struct qam_report_control_s *s;
182
183 s = (struct qam_report_control_s *) user_data;
184 if (constel)
185 {
186 #if defined(ENABLE_GUI)
187 if (use_gui)
188 {
189 qam_monitor_update_constel(s->qam_monitor, constel);
190 qam_monitor_update_carrier_tracking(s->qam_monitor, v22bis_rx_carrier_frequency(s->s));
191 qam_monitor_update_symbol_tracking(s->qam_monitor, v22bis_symbol_timing_correction(s->s));
192 }
193 #endif
194 fpower = (constel->re - target->re)*(constel->re - target->re)
195 + (constel->im - target->im)*(constel->im - target->im);
196 s->smooth_power = 0.95f*s->smooth_power + 0.05f*fpower;
197 printf("%8d [%8.4f, %8.4f] [%8.4f, %8.4f] %2x %8.4f %8.4f\n",
198 s->symbol_no,
199 constel->re,
200 constel->im,
201 target->re,
202 target->im,
203 symbol,
204 fpower,
205 s->smooth_power);
206 s->symbol_no++;
207 }
208 else
209 {
210 printf("Gardner step %d\n", symbol);
211 len = v22bis_equalizer_state(s->s, &coeffs);
212 printf("Equalizer A:\n");
213 for (i = 0; i < len; i++)
214 printf("%3d (%15.5f, %15.5f) -> %15.5f\n", i, coeffs[i].re, coeffs[i].im, powerf(&coeffs[i]));
215 #if defined(ENABLE_GUI)
216 if (use_gui)
217 qam_monitor_update_equalizer(s->qam_monitor, coeffs, len);
218 #endif
219 }
220 }
221 /*- End of function --------------------------------------------------------*/
222
main(int argc,char * argv[])223 int main(int argc, char *argv[])
224 {
225 int16_t caller_amp[BLOCK_LEN];
226 int16_t answerer_amp[BLOCK_LEN];
227 int16_t caller_model_amp[BLOCK_LEN];
228 int16_t answerer_model_amp[BLOCK_LEN];
229 int16_t out_amp[2*BLOCK_LEN];
230 AFfilehandle outhandle;
231 AFfilesetup filesetup;
232 int outframes;
233 int samples;
234 int i;
235 int test_bps;
236 int line_model_no;
237 int bits_per_test;
238 int noise_level;
239 int signal_level;
240 int log_audio;
241 int channel_codec;
242 int opt;
243
244 channel_codec = MUNGE_CODEC_NONE;
245 test_bps = 2400;
246 line_model_no = 0;
247 noise_level = -70;
248 signal_level = -13;
249 bits_per_test = 50000;
250 log_audio = FALSE;
251 while ((opt = getopt(argc, argv, "b:c:glm:n:s:")) != -1)
252 {
253 switch (opt)
254 {
255 case 'b':
256 bits_per_test = atoi(optarg);
257 break;
258 case 'c':
259 channel_codec = atoi(optarg);
260 break;
261 case 'g':
262 #if defined(ENABLE_GUI)
263 use_gui = TRUE;
264 #else
265 fprintf(stderr, "Graphical monitoring not available\n");
266 exit(2);
267 #endif
268 break;
269 case 'l':
270 log_audio = TRUE;
271 break;
272 case 'm':
273 line_model_no = atoi(optarg);
274 break;
275 case 'n':
276 noise_level = atoi(optarg);
277 break;
278 case 's':
279 signal_level = atoi(optarg);
280 break;
281 default:
282 //usage();
283 exit(2);
284 break;
285 }
286 }
287 argc -= optind;
288 argv += optind;
289 if (argc > 0)
290 {
291 if (strcmp(argv[0], "2400") == 0)
292 test_bps = 2400;
293 else if (strcmp(argv[0], "1200") == 0)
294 test_bps = 1200;
295 else
296 {
297 fprintf(stderr, "Invalid bit rate\n");
298 exit(2);
299 }
300 }
301 filesetup = AF_NULL_FILESETUP;
302 outhandle = AF_NULL_FILEHANDLE;
303 if (log_audio)
304 {
305 if ((filesetup = afNewFileSetup()) == AF_NULL_FILESETUP)
306 {
307 fprintf(stderr, " Failed to create file setup\n");
308 exit(2);
309 }
310 afInitSampleFormat(filesetup, AF_DEFAULT_TRACK, AF_SAMPFMT_TWOSCOMP, 16);
311 afInitRate(filesetup, AF_DEFAULT_TRACK, (float) SAMPLE_RATE);
312 afInitFileFormat(filesetup, AF_FILE_WAVE);
313 afInitChannels(filesetup, AF_DEFAULT_TRACK, 2);
314
315 if ((outhandle = afOpenFile(OUT_FILE_NAME, "w", filesetup)) == AF_NULL_FILEHANDLE)
316 {
317 fprintf(stderr, " Cannot create wave file '%s'\n", OUT_FILE_NAME);
318 exit(2);
319 }
320 }
321 v22bis_init(&caller, test_bps, 2, TRUE, v22bis_getbit, v22bis_putbit, &caller);
322 v22bis_tx_power(&caller, signal_level);
323 /* Move the carrier off a bit */
324 caller.tx.carrier_phase_rate = dds_phase_ratef(1207.0f);
325 v22bis_init(&answerer, test_bps, 2, FALSE, v22bis_getbit, v22bis_putbit, &answerer);
326 v22bis_tx_power(&answerer, signal_level);
327 answerer.tx.carrier_phase_rate = dds_phase_ratef(2407.0f);
328 v22bis_set_qam_report_handler(&caller, qam_report, (void *) &qam_caller);
329 v22bis_set_qam_report_handler(&answerer, qam_report, (void *) &qam_answerer);
330 span_log_set_level(&caller.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_FLOW);
331 span_log_set_tag(&caller.logging, "caller");
332 span_log_set_level(&answerer.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_FLOW);
333 span_log_set_tag(&answerer.logging, "answerer");
334
335 qam_caller.s = &caller;
336 qam_caller.smooth_power = 0.0f;
337 qam_caller.symbol_no = 0;
338
339 qam_answerer.s = &answerer;
340 qam_answerer.smooth_power = 0.0f;
341 qam_answerer.symbol_no = 0;
342
343 #if defined(ENABLE_GUI)
344 if (use_gui)
345 {
346 qam_caller.qam_monitor = qam_monitor_init(6.0f, "Calling modem");
347 qam_answerer.qam_monitor = qam_monitor_init(6.0f, "Answering modem");
348 }
349 #endif
350
351 if ((model = both_ways_line_model_init(line_model_no, (float) noise_level, line_model_no, (float) noise_level, channel_codec, 0)) == NULL)
352 {
353 fprintf(stderr, " Failed to create line model\n");
354 exit(2);
355 }
356 for (;;)
357 {
358 samples = v22bis_tx(&caller, caller_amp, BLOCK_LEN);
359 #if defined(ENABLE_GUI)
360 if (use_gui)
361 qam_monitor_update_audio_level(qam_caller.qam_monitor, caller_amp, samples);
362 #endif
363 if (samples == 0)
364 {
365 printf("Restarting on zero output\n");
366 v22bis_restart(&caller, test_bps);
367 rx_ptr = 0;
368 tx_ptr = 0;
369 }
370
371 samples = v22bis_tx(&answerer, answerer_amp, BLOCK_LEN);
372 #if defined(ENABLE_GUI)
373 if (use_gui)
374 qam_monitor_update_audio_level(qam_answerer.qam_monitor, answerer_amp, samples);
375 #endif
376 if (samples == 0)
377 {
378 printf("Restarting on zero output\n");
379 v22bis_restart(&answerer, test_bps);
380 rx_ptr = 0;
381 tx_ptr = 0;
382 }
383
384 both_ways_line_model(model,
385 caller_model_amp,
386 caller_amp,
387 answerer_model_amp,
388 answerer_amp,
389 samples);
390
391 v22bis_rx(&answerer, caller_model_amp, samples);
392 for (i = 0; i < samples; i++)
393 out_amp[2*i] = caller_model_amp[i];
394 for ( ; i < BLOCK_LEN; i++)
395 out_amp[2*i] = 0;
396
397 v22bis_rx(&caller, answerer_model_amp, samples);
398 for (i = 0; i < samples; i++)
399 out_amp[2*i + 1] = answerer_model_amp[i];
400 for ( ; i < BLOCK_LEN; i++)
401 out_amp[2*i + 1] = 0;
402
403 if (log_audio)
404 {
405 outframes = afWriteFrames(outhandle,
406 AF_DEFAULT_TRACK,
407 out_amp,
408 BLOCK_LEN);
409 if (outframes != BLOCK_LEN)
410 {
411 fprintf(stderr, " Error writing wave file\n");
412 exit(2);
413 }
414 }
415 }
416 if (log_audio)
417 {
418 if (afCloseFile(outhandle) != 0)
419 {
420 fprintf(stderr, " Cannot close wave file '%s'\n", OUT_FILE_NAME);
421 exit(2);
422 }
423 afFreeFileSetup(filesetup);
424 }
425 return 0;
426 }
427 /*- End of function --------------------------------------------------------*/
428 /*- End of file ------------------------------------------------------------*/
429