comparison spandsp-0.0.6pre17/tests/fax_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 * fax_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: fax_tests.c,v 1.102 2009/05/30 15:23:13 steveu Exp $
26 */
27
28 /*! \page fax_tests_page FAX tests
29 \section fax_tests_page_sec_1 What does it do?
30 \section fax_tests_page_sec_2 How does it work?
31 */
32
33 #if defined(HAVE_CONFIG_H)
34 #include "config.h"
35 #endif
36
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <unistd.h>
40 #include <string.h>
41 #include <assert.h>
42 #include <sndfile.h>
43
44 //#if defined(WITH_SPANDSP_INTERNALS)
45 #define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
46 //#endif
47
48 #include "spandsp.h"
49 #include "spandsp-sim.h"
50
51 #include "fax_utils.h"
52
53 #define SAMPLES_PER_CHUNK 160
54
55 #define INPUT_TIFF_FILE_NAME "../test-data/itu/fax/itutests.tif"
56
57 #define OUTPUT_FILE_NAME_WAVE "fax_tests.wav"
58
59 #define FAX_MACHINES 2
60
61 struct machine_s
62 {
63 int chan;
64 int16_t amp[SAMPLES_PER_CHUNK];
65 int len;
66 fax_state_t *fax;
67 awgn_state_t *awgn;
68 int done;
69 int succeeded;
70 char tag[50];
71 int error_delay;
72 int total_audio_time;
73 } machines[FAX_MACHINES];
74
75 int use_receiver_not_ready = FALSE;
76 int test_local_interrupt = FALSE;
77 int t30_state_to_wreck = -1;
78
79 static int phase_b_handler(t30_state_t *s, void *user_data, int result)
80 {
81 int i;
82 char tag[20];
83
84 i = (int) (intptr_t) user_data;
85 snprintf(tag, sizeof(tag), "%c: Phase B:", i);
86 printf("%c: Phase B handler on channel %c - (0x%X) %s\n", i, i, result, t30_frametype(result));
87 log_rx_parameters(s, tag);
88 return T30_ERR_OK;
89 }
90 /*- End of function --------------------------------------------------------*/
91
92 static int phase_d_handler(t30_state_t *s, void *user_data, int result)
93 {
94 int i;
95 char tag[20];
96
97 i = (int) (intptr_t) user_data;
98 snprintf(tag, sizeof(tag), "%c: Phase D:", i);
99 printf("%c: Phase D handler on channel %c - (0x%X) %s\n", i, i, result, t30_frametype(result));
100 log_transfer_statistics(s, tag);
101 log_tx_parameters(s, tag);
102 log_rx_parameters(s, tag);
103
104 if (use_receiver_not_ready)
105 t30_set_receiver_not_ready(s, 3);
106
107 if (test_local_interrupt)
108 {
109 if (i == 0)
110 {
111 printf("%d: Initiating interrupt request\n", i);
112 t30_local_interrupt_request(s, TRUE);
113 }
114 else
115 {
116 switch (result)
117 {
118 case T30_PIP:
119 case T30_PRI_MPS:
120 case T30_PRI_EOM:
121 case T30_PRI_EOP:
122 printf("%d: Accepting interrupt request\n", i);
123 t30_local_interrupt_request(s, TRUE);
124 break;
125 case T30_PIN:
126 break;
127 }
128 }
129 }
130 return T30_ERR_OK;
131 }
132 /*- End of function --------------------------------------------------------*/
133
134 static void phase_e_handler(t30_state_t *s, void *user_data, int result)
135 {
136 int i;
137 t30_stats_t t;
138 char tag[20];
139
140 i = (intptr_t) user_data;
141 snprintf(tag, sizeof(tag), "%c: Phase E:", i);
142 printf("%c: Phase E handler on channel %c - (%d) %s\n", i, i, result, t30_completion_code_to_str(result));
143 log_transfer_statistics(s, tag);
144 log_tx_parameters(s, tag);
145 log_rx_parameters(s, tag);
146 t30_get_transfer_statistics(s, &t);
147 machines[i - 'A'].succeeded = (result == T30_ERR_OK) && (t.pages_tx == 12 || t.pages_rx == 12);
148 machines[i - 'A'].done = TRUE;
149 }
150 /*- End of function --------------------------------------------------------*/
151
152 static void real_time_frame_handler(t30_state_t *s,
153 void *user_data,
154 int direction,
155 const uint8_t *msg,
156 int len)
157 {
158 int i;
159
160 i = (intptr_t) user_data;
161 printf("%c: Real time frame handler on channel %c - %s, %s, length = %d\n",
162 i,
163 i,
164 (direction) ? "line->T.30" : "T.30->line",
165 t30_frametype(msg[2]),
166 len);
167 }
168 /*- End of function --------------------------------------------------------*/
169
170 static int document_handler(t30_state_t *s, void *user_data, int event)
171 {
172 int i;
173
174 i = (intptr_t) user_data;
175 printf("%c: Document handler on channel %c - event %d\n", i, i, event);
176 return FALSE;
177 }
178 /*- End of function --------------------------------------------------------*/
179
180 int main(int argc, char *argv[])
181 {
182 SNDFILE *wave_handle;
183 SNDFILE *input_wave_handle;
184 int i;
185 int j;
186 int k;
187 struct machine_s *mc;
188 int outframes;
189 char buf[128 + 1];
190 int16_t silence[SAMPLES_PER_CHUNK];
191 int16_t out_amp[2*SAMPLES_PER_CHUNK];
192 int alldone;
193 const char *input_tiff_file_name;
194 const char *input_audio_file_name;
195 int log_audio;
196 int use_ecm;
197 int use_tep;
198 int use_transmit_on_idle;
199 int use_line_hits;
200 int polled_mode;
201 int reverse_flow;
202 int use_page_limits;
203 int supported_modems;
204 int signal_level;
205 int noise_level;
206 float signal_scaling;
207 time_t start_time;
208 time_t end_time;
209 char *page_header_info;
210 int opt;
211 t30_state_t *t30;
212 logging_state_t *logging;
213
214 log_audio = FALSE;
215 input_tiff_file_name = INPUT_TIFF_FILE_NAME;
216 input_audio_file_name = NULL;
217 use_ecm = FALSE;
218 use_line_hits = FALSE;
219 use_tep = FALSE;
220 polled_mode = FALSE;
221 page_header_info = NULL;
222 reverse_flow = FALSE;
223 use_transmit_on_idle = TRUE;
224 use_receiver_not_ready = FALSE;
225 use_page_limits = FALSE;
226 signal_level = 0;
227 noise_level = -99;
228 supported_modems = T30_SUPPORT_V27TER | T30_SUPPORT_V29 | T30_SUPPORT_V17;
229 while ((opt = getopt(argc, argv, "ehH:i:I:lm:n:prRs:tTw:")) != -1)
230 {
231 switch (opt)
232 {
233 case 'e':
234 use_ecm = TRUE;
235 break;
236 case 'h':
237 use_line_hits = TRUE;
238 break;
239 case 'H':
240 page_header_info = optarg;
241 break;
242 case 'i':
243 input_tiff_file_name = optarg;
244 break;
245 case 'I':
246 input_audio_file_name = optarg;
247 break;
248 case 'l':
249 log_audio = TRUE;
250 break;
251 case 'm':
252 supported_modems = atoi(optarg);
253 break;
254 case 'n':
255 noise_level = atoi(optarg);
256 break;
257 case 'p':
258 polled_mode = TRUE;
259 break;
260 case 'r':
261 reverse_flow = TRUE;
262 break;
263 case 'R':
264 use_receiver_not_ready = TRUE;
265 break;
266 case 's':
267 signal_level = atoi(optarg);
268 break;
269 case 't':
270 use_tep = TRUE;
271 break;
272 case 'T':
273 use_page_limits = TRUE;
274 break;
275 case 'w':
276 t30_state_to_wreck = atoi(optarg);
277 break;
278 default:
279 //usage();
280 exit(2);
281 break;
282 }
283 }
284
285 input_wave_handle = NULL;
286 if (input_audio_file_name)
287 {
288 if ((input_wave_handle = sf_open_telephony_read(input_audio_file_name, 1)) == NULL)
289 {
290 fprintf(stderr, " Cannot open audio file '%s'\n", input_audio_file_name);
291 exit(2);
292 }
293 }
294
295 wave_handle = NULL;
296 if (log_audio)
297 {
298 if ((wave_handle = sf_open_telephony_write(OUTPUT_FILE_NAME_WAVE, 2)) == NULL)
299 {
300 fprintf(stderr, " Cannot create audio file '%s'\n", OUTPUT_FILE_NAME_WAVE);
301 exit(2);
302 }
303 }
304
305 memset(silence, 0, sizeof(silence));
306 for (j = 0; j < FAX_MACHINES; j++)
307 {
308 machines[j].chan = j;
309 mc = &machines[j];
310
311 i = mc->chan + 1;
312 sprintf(buf, "%d%d%d%d%d%d%d%d", i, i, i, i, i, i, i, i);
313 if (reverse_flow)
314 mc->fax = fax_init(NULL, (mc->chan & 1) ? TRUE : FALSE);
315 else
316 mc->fax = fax_init(NULL, (mc->chan & 1) ? FALSE : TRUE);
317 mc->awgn = NULL;
318 signal_scaling = 1.0f;
319 if (noise_level > -99)
320 {
321 mc->awgn = awgn_init_dbm0(NULL, 1234567, noise_level);
322 signal_scaling = powf(10.0f, signal_level/20.0f);
323 printf("Signal scaling %f\n", signal_scaling);
324 }
325 fax_set_transmit_on_idle(mc->fax, use_transmit_on_idle);
326 fax_set_tep_mode(mc->fax, use_tep);
327 t30 = fax_get_t30_state(mc->fax);
328 t30_set_tx_ident(t30, buf);
329 t30_set_tx_sub_address(t30, "Sub-address");
330 t30_set_tx_sender_ident(t30, "Sender ID");
331 t30_set_tx_password(t30, "Password");
332 t30_set_tx_polled_sub_address(t30, "Polled sub-address");
333 t30_set_tx_selective_polling_address(t30, "Selective polling address");
334 t30_set_tx_page_header_info(t30, page_header_info);
335 t30_set_tx_nsf(t30, (const uint8_t *) "\x50\x00\x00\x00Spandsp\x00", 12);
336 t30_set_ecm_capability(t30, use_ecm);
337 t30_set_supported_t30_features(t30,
338 T30_SUPPORT_IDENTIFICATION
339 | T30_SUPPORT_SELECTIVE_POLLING
340 | T30_SUPPORT_SUB_ADDRESSING);
341
342 if ((mc->chan & 1))
343 t30_set_minimum_scan_line_time(t30, 40);
344 t30_set_supported_image_sizes(t30,
345 T30_SUPPORT_US_LETTER_LENGTH
346 | T30_SUPPORT_US_LEGAL_LENGTH
347 | T30_SUPPORT_UNLIMITED_LENGTH
348 | T30_SUPPORT_215MM_WIDTH
349 | T30_SUPPORT_255MM_WIDTH
350 | T30_SUPPORT_303MM_WIDTH);
351 t30_set_supported_resolutions(t30,
352 T30_SUPPORT_STANDARD_RESOLUTION
353 | T30_SUPPORT_FINE_RESOLUTION
354 | T30_SUPPORT_SUPERFINE_RESOLUTION
355 | T30_SUPPORT_R8_RESOLUTION
356 | T30_SUPPORT_R16_RESOLUTION
357 | T30_SUPPORT_300_300_RESOLUTION
358 | T30_SUPPORT_400_400_RESOLUTION
359 | T30_SUPPORT_600_600_RESOLUTION
360 | T30_SUPPORT_1200_1200_RESOLUTION
361 | T30_SUPPORT_300_600_RESOLUTION
362 | T30_SUPPORT_400_800_RESOLUTION
363 | T30_SUPPORT_600_1200_RESOLUTION);
364 t30_set_supported_modems(t30, supported_modems);
365 if (use_ecm)
366 t30_set_supported_compressions(t30, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION);
367 if ((mc->chan & 1))
368 {
369 if (polled_mode)
370 {
371 if (use_page_limits)
372 t30_set_tx_file(t30, input_tiff_file_name, 3, 6);
373 else
374 t30_set_tx_file(t30, input_tiff_file_name, -1, -1);
375 }
376 else
377 {
378 sprintf(buf, "fax_tests_%d.tif", (mc->chan + 1)/2);
379 t30_set_rx_file(t30, buf, -1);
380 t30_set_rx_encoding(t30, T4_COMPRESSION_ITU_T6);
381 }
382 }
383 else
384 {
385 if (polled_mode)
386 {
387 sprintf(buf, "fax_tests_%d.tif", (mc->chan + 1)/2);
388 t30_set_rx_file(t30, buf, -1);
389 t30_set_rx_encoding(t30, T4_COMPRESSION_ITU_T6);
390 }
391 else
392 {
393 if (use_page_limits)
394 t30_set_tx_file(t30, input_tiff_file_name, 3, 6);
395 else
396 t30_set_tx_file(t30, input_tiff_file_name, -1, -1);
397 }
398 }
399 t30_set_phase_b_handler(t30, phase_b_handler, (void *) (intptr_t) mc->chan + 'A');
400 t30_set_phase_d_handler(t30, phase_d_handler, (void *) (intptr_t) mc->chan + 'A');
401 t30_set_phase_e_handler(t30, phase_e_handler, (void *) (intptr_t) mc->chan + 'A');
402 t30_set_real_time_frame_handler(t30, real_time_frame_handler, (void *) (intptr_t) mc->chan + 'A');
403 t30_set_document_handler(t30, document_handler, (void *) (intptr_t) mc->chan + 'A');
404 sprintf(mc->tag, "FAX-%d", j + 1);
405
406 logging = t30_get_logging_state(t30);
407 span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME | SPAN_LOG_FLOW);
408 span_log_set_tag(logging, mc->tag);
409 span_log_set_level(&t30->t4.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME | SPAN_LOG_FLOW);
410 span_log_set_tag(&t30->t4.logging, mc->tag);
411
412 logging = fax_get_logging_state(mc->fax);
413 span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME | SPAN_LOG_FLOW);
414 span_log_set_tag(logging, mc->tag);
415
416 memset(mc->amp, 0, sizeof(mc->amp));
417 mc->total_audio_time = 0;
418 mc->done = FALSE;
419 }
420 time(&start_time);
421 for (;;)
422 {
423 alldone = TRUE;
424 for (j = 0; j < FAX_MACHINES; j++)
425 {
426 mc = &machines[j];
427
428 if ((j & 1) == 0 && input_audio_file_name)
429 {
430 mc->len = sf_readf_short(input_wave_handle, mc->amp, SAMPLES_PER_CHUNK);
431 if (mc->len == 0)
432 break;
433 }
434 else
435 {
436 mc->len = fax_tx(mc->fax, mc->amp, SAMPLES_PER_CHUNK);
437 if (mc->awgn)
438 {
439 for (k = 0; k < mc->len; k++)
440 mc->amp[k] = ((int16_t) (mc->amp[k]*signal_scaling)) + awgn(mc->awgn);
441 }
442 }
443 mc->total_audio_time += SAMPLES_PER_CHUNK;
444 if (!use_transmit_on_idle)
445 {
446 /* The receive side always expects a full block of samples, but the
447 transmit side may not be sending any when it doesn't need to. We
448 may need to pad with some silence. */
449 if (mc->len < SAMPLES_PER_CHUNK)
450 {
451 memset(mc->amp + mc->len, 0, sizeof(int16_t)*(SAMPLES_PER_CHUNK - mc->len));
452 mc->len = SAMPLES_PER_CHUNK;
453 }
454 }
455 t30 = fax_get_t30_state(mc->fax);
456 logging = t30_get_logging_state(t30);
457 span_log_bump_samples(logging, mc->len);
458 logging = fax_get_logging_state(mc->fax);
459 span_log_bump_samples(logging, mc->len);
460
461 if (log_audio)
462 {
463 for (k = 0; k < mc->len; k++)
464 out_amp[2*k + j] = mc->amp[k];
465 }
466 if (machines[j ^ 1].len < SAMPLES_PER_CHUNK)
467 memset(machines[j ^ 1].amp + machines[j ^ 1].len, 0, sizeof(int16_t)*(SAMPLES_PER_CHUNK - machines[j ^ 1].len));
468 t30 = fax_get_t30_state(mc->fax);
469 #if defined(WITH_SPANDSP_INTERNALS)
470 if (use_line_hits)
471 {
472 /* TODO: This applies very crude line hits. improve it */
473 if (t30->state == 22)
474 {
475 if (++mc->error_delay == 100)
476 {
477 fprintf(stderr, "HIT %d!\n", j);
478 mc->error_delay = 0;
479 for (k = 0; k < 5; k++)
480 mc->amp[k] = 0;
481 }
482 }
483 }
484 if (t30->state == t30_state_to_wreck)
485 memset(machines[j ^ 1].amp, 0, sizeof(int16_t)*SAMPLES_PER_CHUNK);
486 #endif
487 if (fax_rx(mc->fax, machines[j ^ 1].amp, SAMPLES_PER_CHUNK))
488 break;
489 if (!mc->done)
490 alldone = FALSE;
491 }
492
493 if (log_audio)
494 {
495 outframes = sf_writef_short(wave_handle, out_amp, SAMPLES_PER_CHUNK);
496 if (outframes != SAMPLES_PER_CHUNK)
497 break;
498 }
499
500 if (alldone || j < FAX_MACHINES)
501 break;
502 }
503 time(&end_time);
504 for (j = 0; j < FAX_MACHINES; j++)
505 {
506 mc = &machines[j];
507 fax_release(mc->fax);
508 }
509 if (log_audio)
510 {
511 if (sf_close(wave_handle))
512 {
513 fprintf(stderr, " Cannot close audio file '%s'\n", OUTPUT_FILE_NAME_WAVE);
514 exit(2);
515 }
516 }
517 if (input_audio_file_name)
518 {
519 if (sf_close(input_wave_handle))
520 {
521 fprintf(stderr, " Cannot close audio file '%s'\n", input_audio_file_name);
522 exit(2);
523 }
524 }
525 printf("Total audio time = %ds (wall time %ds)\n", machines[0].total_audio_time/8000, (int) (end_time - start_time));
526 return 0;
527 }
528 /*- End of function --------------------------------------------------------*/
529 /*- End of file ------------------------------------------------------------*/

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