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