update to spandsp-0.0.6pre12

git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@13311 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
Michael Jerris 2009-05-14 21:52:52 +00:00
parent 92955376b2
commit 5d21a6e4b2
11 changed files with 618 additions and 368 deletions

View File

@ -22,12 +22,40 @@
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: v22bis.h,v 1.9 2009/04/26 09:50:28 steveu Exp $
* $Id: v22bis.h,v 1.10 2009/04/29 12:37:45 steveu Exp $
*/
#if !defined(_SPANDSP_PRIVATE_V22BIS_H_)
#define _SPANDSP_PRIVATE_V22BIS_H_
/*! Segments of the training sequence on the receive side */
enum
{
V22BIS_RX_TRAINING_STAGE_NORMAL_OPERATION,
V22BIS_RX_TRAINING_STAGE_SYMBOL_ACQUISITION,
V22BIS_RX_TRAINING_STAGE_LOG_PHASE,
V22BIS_RX_TRAINING_STAGE_UNSCRAMBLED_ONES,
V22BIS_RX_TRAINING_STAGE_UNSCRAMBLED_ONES_SUSTAINING,
V22BIS_RX_TRAINING_STAGE_SCRAMBLED_ONES_AT_1200,
V22BIS_RX_TRAINING_STAGE_SCRAMBLED_ONES_AT_1200_SUSTAINING,
V22BIS_RX_TRAINING_STAGE_WAIT_FOR_SCRAMBLED_ONES_AT_2400,
V22BIS_RX_TRAINING_STAGE_PARKED
};
/*! Segments of the training sequence on the transmit side */
enum
{
V22BIS_TX_TRAINING_STAGE_NORMAL_OPERATION = 0,
V22BIS_TX_TRAINING_STAGE_INITIAL_TIMED_SILENCE,
V22BIS_TX_TRAINING_STAGE_INITIAL_SILENCE,
V22BIS_TX_TRAINING_STAGE_U11,
V22BIS_TX_TRAINING_STAGE_U0011,
V22BIS_TX_TRAINING_STAGE_S11,
V22BIS_TX_TRAINING_STAGE_TIMED_S11,
V22BIS_TX_TRAINING_STAGE_S1111,
V22BIS_TX_TRAINING_STAGE_PARKED
};
/*!
V.22bis modem descriptor. This defines the working state for a single instance
of a V.22bis modem.
@ -195,6 +223,8 @@ int v22bis_rx_restart(v22bis_state_t *s);
void v22bis_report_status_change(v22bis_state_t *s, int status);
void v22bis_equalizer_coefficient_reset(v22bis_state_t *s);
#if defined(__cplusplus)
}
#endif

View File

@ -22,7 +22,7 @@
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: v22bis.h,v 1.41 2009/04/25 10:18:50 steveu Exp $
* $Id: v22bis.h,v 1.42 2009/04/29 12:37:45 steveu Exp $
*/
/*! \file */
@ -150,13 +150,20 @@ SPAN_DECLARE(void) v22bis_tx_power(v22bis_state_t *s, float power);
\return 0 for OK, -1 for bad parameter. */
SPAN_DECLARE(int) v22bis_restart(v22bis_state_t *s, int bit_rate);
/*! Request a retrain for a V.22bis modem context. A rate change may also be resquested.
/*! Request a retrain for a V.22bis modem context. A rate change may also be requested.
\brief Request a retrain for a V.22bis modem context.
\param s The modem context.
\param bit_rate The bit rate of the modem. Valid values are 1200 and 2400.
\return 0 for OK, -1 for bad parameter. */
\return 0 for OK, -1 for request rejected. */
SPAN_DECLARE(int) v22bis_request_retrain(v22bis_state_t *s, int bit_rate);
/*! Request a loopback 2 for a V.22bis modem context.
\brief Request a loopback 2 for a V.22bis modem context.
\param s The modem context.
\param enable TRUE to enable loopback, or FALSE to disable it.
\return 0 for OK, -1 for request reject. */
SPAN_DECLARE(int) v22bis_remote_loopback(v22bis_state_t *s, int enable);
/*! Report the current operating bit rate of a V.22bis modem context.
\brief Report the current operating bit rate of a V.22bis modem context
\param s The modem context. */

View File

@ -30,9 +30,9 @@
/* The date and time of the version are in UTC form. */
#define SPANDSP_RELEASE_DATE 20090427
#define SPANDSP_RELEASE_TIME 151958
#define SPANDSP_RELEASE_DATETIME_STRING "20090427 151958"
#define SPANDSP_RELEASE_DATE 20090502
#define SPANDSP_RELEASE_TIME 044449
#define SPANDSP_RELEASE_DATETIME_STRING "20090502 044449"
#endif
/*- End of file ------------------------------------------------------------*/

View File

@ -5,7 +5,7 @@
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2003, 2004, 2005, 2006, 2007 Steve Underwood
* Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Steve Underwood
*
* All rights reserved.
*
@ -22,7 +22,7 @@
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: t30.c,v 1.291 2009/04/23 15:40:32 steveu Exp $
* $Id: t30.c,v 1.298 2009/04/30 18:46:14 steveu Exp $
*/
/*! \file */
@ -73,8 +73,13 @@
#include "t30_local.h"
/*! The maximum number of consecutive retries allowed. */
#define MAX_MESSAGE_TRIES 3
/*! The maximum permitted number of retries of a single command allowed. */
#define MAX_COMMAND_TRIES 3
/*! The maximum permitted number of retries of a single response request allowed. This
is not specified in T.30. However, if you don't apply some limit a messed up FAX
terminal could keep you retrying all day. Its a backstop protection. */
#define MAX_RESPONSE_TRIES 6
/*! Conversion between milliseconds and audio samples. */
#define ms_to_samples(t) (((t)*SAMPLE_RATE)/1000)
@ -245,7 +250,7 @@ term it T2A. No tolerance is specified for this timer. T2A specifies the maximum
end of a frame, after the initial flag has been seen. */
#define DEFAULT_TIMER_T2A 3000
/*! If the HDLC carrier falls during reception, we need to apply a minimum time before continuing. if we
/*! If the HDLC carrier falls during reception, we need to apply a minimum time before continuing. If we
don't, there are circumstances where we could continue and reply before the incoming signals have
really finished. E.g. if a bad DCS is received in a DCS-TCF sequence, we need wait for the TCF
carrier to pass, before continuing. This timer is specified as 200ms, but no tolerance is specified.
@ -316,14 +321,16 @@ received. If the timer T8 expires, a DCN command is transmitted for call release
enum
{
TIMER_IS_T2 = 0,
TIMER_IS_T2A = 1,
TIMER_IS_T2B = 2,
TIMER_IS_T2C = 3,
TIMER_IS_T4 = 4,
TIMER_IS_T4A = 5,
TIMER_IS_T4B = 6,
TIMER_IS_T4C = 7
TIMER_IS_IDLE = 0,
TIMER_IS_T2,
TIMER_IS_T1A,
TIMER_IS_T2A,
TIMER_IS_T2B,
TIMER_IS_T2C,
TIMER_IS_T4,
TIMER_IS_T4A,
TIMER_IS_T4B,
TIMER_IS_T4C
};
/* Start points in the fallback table for different capabilities */
@ -2507,6 +2514,9 @@ static void process_rx_fcd(t30_state_t *s, const uint8_t *msg, int len)
{
unexpected_frame_length(s, msg, len);
}
/* We have received something, so any missing carrier status is out of date */
if (s->current_status == T30_ERR_RX_NOCARRIER)
s->current_status = T30_ERR_OK;
break;
default:
unexpected_non_final_frame(s, msg, len);
@ -2517,16 +2527,21 @@ static void process_rx_fcd(t30_state_t *s, const uint8_t *msg, int len)
static void process_rx_rcp(t30_state_t *s, const uint8_t *msg, int len)
{
/* Return to control for partial page. These might come through with or without the final frame tag,
so we have this routine to deal with the "no final frame tag" case. */
/* Return to control for partial page. These might come through with or without the final frame tag.
Here we deal with the "no final frame tag" case. */
switch (s->state)
{
case T30_STATE_F_DOC_ECM:
set_state(s, T30_STATE_F_POST_DOC_ECM);
queue_phase(s, T30_PHASE_D_RX);
timer_t2_start(s);
/* We have received something, so any missing carrier status is out of date */
if (s->current_status == T30_ERR_RX_NOCARRIER)
s->current_status = T30_ERR_OK;
break;
case T30_STATE_F_POST_DOC_ECM:
/* Just ignore this */
/* Just ignore this. It must be an extra RCP. Several are usually sent, to maximise the chance
of receiving a correct one. */
break;
default:
unexpected_non_final_frame(s, msg, len);
@ -2773,7 +2788,7 @@ static void process_state_d_post_tcf(t30_state_t *s, const uint8_t *msg, int len
break;
case T30_DIS:
/* It appears they didn't see what we sent - retry the TCF */
if (++s->retries >= MAX_MESSAGE_TRIES)
if (++s->retries >= MAX_COMMAND_TRIES)
{
span_log(&s->logging, SPAN_LOG_FLOW, "Too many retries. Giving up.\n");
s->current_status = T30_ERR_RETRYDCN;
@ -2827,6 +2842,10 @@ static void process_state_f_cfr(t30_state_t *s, const uint8_t *msg, int len)
/* We're waiting for a response to the CFR we sent */
switch (msg[2] & 0xFE)
{
case T30_DCS:
/* If we received another DCS, they must have missed our CFR */
process_rx_dcs(s, msg, len);
break;
case T30_CRP:
repeat_last_command(s);
break;
@ -3150,7 +3169,7 @@ static void process_state_f_post_doc_non_ecm(t30_state_t *s, const uint8_t *msg,
}
/*- End of function --------------------------------------------------------*/
static void process_state_f_doc_ecm(t30_state_t *s, const uint8_t *msg, int len)
static void process_state_f_doc_and_post_doc_ecm(t30_state_t *s, const uint8_t *msg, int len)
{
uint8_t fcf2;
@ -3164,16 +3183,22 @@ static void process_state_f_doc_ecm(t30_state_t *s, const uint8_t *msg, int len)
process_rx_dcs(s, msg, len);
break;
case T4_RCP:
/* Return to control for partial page. These might come through with or without the final frame tag.
Here we deal with the "final frame tag" case. */
if (s->state == T30_STATE_F_DOC_ECM)
{
/* Return to control for partial page */
queue_phase(s, T30_PHASE_D_RX);
set_state(s, T30_STATE_F_POST_DOC_ECM);
queue_phase(s, T30_PHASE_D_RX);
timer_t2_start(s);
/* We have received something, so any missing carrier status is out of date */
if (s->current_status == T30_ERR_RX_NOCARRIER)
s->current_status = T30_ERR_OK;
}
else
{
/* Ignore extra RCP frames. The source will usually send several to maximise the chance of
one getting through OK. */
/* Just ignore this. It must be an extra RCP. Several are usually sent, to maximise the chance
of receiving a correct one. */
}
break;
case T30_EOR:
@ -4166,6 +4191,7 @@ static void process_rx_control_msg(t30_state_t *s, const uint8_t *msg, int len)
/* Restart the command or response timer, T2 or T4 */
switch (s->timer_t2_t4_is)
{
case TIMER_IS_T1A:
case TIMER_IS_T2:
case TIMER_IS_T2A:
case TIMER_IS_T2B:
@ -4358,7 +4384,7 @@ static void process_rx_control_msg(t30_state_t *s, const uint8_t *msg, int len)
break;
case T30_STATE_F_DOC_ECM:
case T30_STATE_F_POST_DOC_ECM:
process_state_f_doc_ecm(s, msg, len);
process_state_f_doc_and_post_doc_ecm(s, msg, len);
break;
case T30_STATE_F_POST_RCP_MCF:
process_state_f_post_rcp_mcf(s, msg, len);
@ -4564,7 +4590,7 @@ static void set_state(t30_state_t *s, int state)
static void repeat_last_command(t30_state_t *s)
{
s->step = 0;
if (++s->retries >= MAX_MESSAGE_TRIES)
if (++s->retries >= MAX_COMMAND_TRIES)
{
span_log(&s->logging, SPAN_LOG_FLOW, "Too many retries. Giving up.\n");
switch (s->state)
@ -4670,13 +4696,14 @@ static void timer_t2a_start(t30_state_t *s)
{
span_log(&s->logging, SPAN_LOG_FLOW, "Start T1A\n");
s->timer_t2_t4 = ms_to_samples(DEFAULT_TIMER_T1A);
s->timer_t2_t4_is = TIMER_IS_T1A;
}
else
{
span_log(&s->logging, SPAN_LOG_FLOW, "Start T2A\n");
s->timer_t2_t4 = ms_to_samples(DEFAULT_TIMER_T2A);
s->timer_t2_t4_is = TIMER_IS_T2A;
}
s->timer_t2_t4_is = TIMER_IS_T2A;
}
/*- End of function --------------------------------------------------------*/
@ -4714,8 +4741,47 @@ static void timer_t4b_start(t30_state_t *s)
static void timer_t2_t4_stop(t30_state_t *s)
{
span_log(&s->logging, SPAN_LOG_FLOW, "Stop T2/T4\n");
const char *tag;
switch (s->timer_t2_t4_is)
{
case TIMER_IS_IDLE:
tag = "none";
break;
case TIMER_IS_T1A:
tag = "T1A";
break;
case TIMER_IS_T2:
tag = "T2";
break;
case TIMER_IS_T2A:
tag = "T2A";
break;
case TIMER_IS_T2B:
tag = "T2B";
break;
case TIMER_IS_T2C:
tag = "T2C";
break;
case TIMER_IS_T4:
tag = "T4";
break;
case TIMER_IS_T4A:
tag = "T4A";
break;
case TIMER_IS_T4B:
tag = "T4B";
break;
case TIMER_IS_T4C:
tag = "T4C";
break;
default:
tag = "T2/T4";
break;
}
span_log(&s->logging, SPAN_LOG_FLOW, "Stop %s (%d remaining)\n", tag, s->timer_t2_t4);
s->timer_t2_t4 = 0;
s->timer_t2_t4_is = TIMER_IS_IDLE;
}
/*- End of function --------------------------------------------------------*/
@ -4754,7 +4820,8 @@ static void timer_t1_expired(t30_state_t *s)
static void timer_t2_expired(t30_state_t *s)
{
span_log(&s->logging, SPAN_LOG_FLOW, "T2 expired in phase %s, state %d\n", phase_names[s->phase], s->state);
if (s->timer_t2_t4_is != TIMER_IS_T2B)
span_log(&s->logging, SPAN_LOG_FLOW, "T2 expired in phase %s, state %d\n", phase_names[s->phase], s->state);
switch (s->state)
{
case T30_STATE_III_Q_MCF:
@ -4820,6 +4887,14 @@ static void timer_t2_expired(t30_state_t *s)
}
/*- End of function --------------------------------------------------------*/
static void timer_t1a_expired(t30_state_t *s)
{
span_log(&s->logging, SPAN_LOG_FLOW, "T1A expired in phase %s, state %d. An HDLC frame lasted too long.\n", phase_names[s->phase], s->state);
s->current_status = T30_ERR_HDLC_CARRIER;
disconnect(s);
}
/*- End of function --------------------------------------------------------*/
static void timer_t2a_expired(t30_state_t *s)
{
span_log(&s->logging, SPAN_LOG_FLOW, "T2A expired in phase %s, state %d. An HDLC frame lasted too long.\n", phase_names[s->phase], s->state);
@ -5273,6 +5348,7 @@ SPAN_DECLARE(int) t30_non_ecm_get_chunk(void *user_data, uint8_t buf[], int max_
static void t30_hdlc_rx_status(void *user_data, int status)
{
t30_state_t *s;
int was_trained;
s = (t30_state_t *) user_data;
span_log(&s->logging, SPAN_LOG_FLOW, "HDLC signal status is %s (%d) in state %d\n", signal_status_to_str(status), status, s->state);
@ -5293,32 +5369,56 @@ static void t30_hdlc_rx_status(void *user_data, int status)
switch (s->timer_t2_t4_is)
{
case TIMER_IS_T2B:
s->timer_t2_t4_is = TIMER_IS_T2C;
timer_t2_t4_stop(s);
s->timer_t2_t4_is = TIMER_IS_T2C;
break;
case TIMER_IS_T4B:
s->timer_t2_t4_is = TIMER_IS_T4C;
timer_t2_t4_stop(s);
s->timer_t2_t4_is = TIMER_IS_T4C;
break;
}
break;
case SIG_STATUS_CARRIER_DOWN:
was_trained = s->rx_trained;
s->rx_signal_present = FALSE;
s->rx_trained = FALSE;
/* If a phase change has been queued to occur after the receive signal drops,
its time to change. */
if (s->state == T30_STATE_F_DOC_ECM)
{
/* We should be receiving a document right now, but we haven't seen an RCP at the end of
transmission. */
if (was_trained)
{
/* We trained OK, so we should have some kind of received page, possibly with
zero good HDLC frames. It just did'nt end cleanly with an RCP. */
span_log(&s->logging, SPAN_LOG_WARNING, "ECM signal did not end cleanly\n");
/* Fake the existance of an RCP, and proceed */
set_state(s, T30_STATE_F_POST_DOC_ECM);
queue_phase(s, T30_PHASE_D_RX);
timer_t2_start(s);
/* We at least trained, so any missing carrier status is out of date */
if (s->current_status == T30_ERR_RX_NOCARRIER)
s->current_status = T30_ERR_OK;
}
else
{
/* Either there was no image carrier, or we failed to train to it. */
span_log(&s->logging, SPAN_LOG_WARNING, "ECM carrier not found\n");
s->current_status = T30_ERR_RX_NOCARRIER;
}
}
if (s->next_phase != T30_PHASE_IDLE)
{
timer_t2_t4_stop(s);
/* The appropriate timer for the next phase should already be in progress */
set_phase(s, s->next_phase);
if (s->next_phase == T30_PHASE_C_NON_ECM_RX)
timer_t2_start(s);
s->next_phase = T30_PHASE_IDLE;
}
else
{
switch (s->timer_t2_t4_is)
{
case TIMER_IS_T1A:
case TIMER_IS_T2A:
case TIMER_IS_T2C:
timer_t2b_start(s);
@ -5344,6 +5444,7 @@ static void t30_hdlc_rx_status(void *user_data, int status)
{
switch(s->timer_t2_t4_is)
{
case TIMER_IS_T1A:
case TIMER_IS_T2:
case TIMER_IS_T2A:
timer_t2a_start(s);
@ -5416,7 +5517,7 @@ SPAN_DECLARE_NONSTD(void) t30_hdlc_accept(void *user_data, const uint8_t *msg, i
return;
}
s->rx_frame_received = TRUE;
/* Cancel the command or response timer */
/* Cancel the command or response timer (if one is running) */
timer_t2_t4_stop(s);
process_rx_control_msg(s, msg, len);
}
@ -5760,6 +5861,9 @@ SPAN_DECLARE(void) t30_timer_update(t30_state_t *s, int samples)
{
switch (s->timer_t2_t4_is)
{
case TIMER_IS_T1A:
timer_t1a_expired(s);
break;
case TIMER_IS_T2:
timer_t2_expired(s);
break;
@ -5869,6 +5973,13 @@ SPAN_DECLARE(int) t30_restart(t30_state_t *s)
s->far_dis_dtc_len = 0;
memset(&s->far_dis_dtc_frame, 0, sizeof(s->far_dis_dtc_frame));
t30_build_dis_or_dtc(s);
memset(&s->rx_info, 0, sizeof(s->rx_info));
release_resources(s);
/* The ECM page number is only reset at call establishment */
s->ecm_rx_page = 0;
s->ecm_tx_page = 0;
s->far_end_detected = FALSE;
s->timer_t0_t1 = ms_to_samples(DEFAULT_TIMER_T0);
if (s->calling_party)
{
set_state(s, T30_STATE_T);
@ -5879,13 +5990,6 @@ SPAN_DECLARE(int) t30_restart(t30_state_t *s)
set_state(s, T30_STATE_ANSWERING);
set_phase(s, T30_PHASE_A_CED);
}
memset(&s->rx_info, 0, sizeof(s->rx_info));
s->far_end_detected = FALSE;
s->timer_t0_t1 = ms_to_samples(DEFAULT_TIMER_T0);
release_resources(s);
/* The ECM page number is only reset at call establishment */
s->ecm_rx_page = 0;
s->ecm_tx_page = 0;
return 0;
}
/*- End of function --------------------------------------------------------*/

View File

@ -22,7 +22,7 @@
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: t38_terminal.c,v 1.124 2009/03/13 14:49:56 steveu Exp $
* $Id: t38_terminal.c,v 1.125 2009/05/02 04:43:48 steveu Exp $
*/
/*! \file */
@ -566,6 +566,8 @@ static int stream_non_ecm(t38_terminal_state_t *s)
/* Create a 75ms silence */
if (fe->t38.current_tx_indicator != T38_IND_NO_SIGNAL)
delay = t38_core_send_indicator(&fe->t38, T38_IND_NO_SIGNAL, fe->t38.indicator_tx_count);
else
delay = 75000;
fe->timed_step = T38_TIMED_STEP_NON_ECM_MODEM_2;
fe->next_tx_samples = fe->samples;
break;
@ -663,6 +665,8 @@ static int stream_hdlc(t38_terminal_state_t *s)
/* Create a 75ms silence */
if (fe->t38.current_tx_indicator != T38_IND_NO_SIGNAL)
delay = t38_core_send_indicator(&fe->t38, T38_IND_NO_SIGNAL, fe->t38.indicator_tx_count);
else
delay = 75000;
fe->timed_step = T38_TIMED_STEP_HDLC_MODEM_2;
fe->next_tx_samples = fe->samples;
break;

View File

@ -1668,9 +1668,14 @@ static void encode_eol(t4_state_t *s)
}
if (s->row_bits)
{
/* We may need to pad the row to a minimum length. */
if (s->row_bits + length < s->min_bits_per_row)
put_encoded_bits(s, 0, s->min_bits_per_row - (s->row_bits + length));
/* We may need to pad the row to a minimum length, unless we are in T.6 mode.
In T.6 we only come here at the end of the page to add the EOFB marker, which
is like two 1D EOLs. */
if (s->line_encoding != T4_COMPRESSION_ITU_T6)
{
if (s->row_bits + length < s->min_bits_per_row)
put_encoded_bits(s, 0, s->min_bits_per_row - (s->row_bits + length));
}
put_encoded_bits(s, code, length);
update_row_bit_info(s);
}
@ -1678,6 +1683,10 @@ static void encode_eol(t4_state_t *s)
{
/* We don't pad zero length rows. They are the consecutive EOLs which end a page. */
put_encoded_bits(s, code, length);
/* Don't do the full update row bit info, or the minimum suddenly drops to the
length of an EOL. Just clear the row bits, so we treat the next EOL as an
end of page EOL, with no padding. */
s->row_bits = 0;
}
}
/*- End of function --------------------------------------------------------*/
@ -2183,8 +2192,9 @@ SPAN_DECLARE(int) t4_tx_start_page(t4_state_t *s)
encode_eol(s);
}
/* Force any partial byte in progress to flush */
put_encoded_bits(s, 0, 7);
/* Force any partial byte in progress to flush using ones. Any post EOL padding when
sending is normally ones, so this is consistent. */
put_encoded_bits(s, 0xFF, 7);
s->bit_pos = 7;
s->bit_ptr = 0;
s->line_image_size = s->image_size*8;

View File

@ -22,12 +22,19 @@
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: v22bis_rx.c,v 1.66 2009/04/27 15:18:52 steveu Exp $
* $Id: v22bis_rx.c,v 1.67 2009/04/29 12:37:45 steveu Exp $
*/
/*! \file */
/* THIS IS A WORK IN PROGRESS - NOT YET FUNCTIONAL! */
/* THIS IS A WORK IN PROGRESS - It is basically functional, but it is not feature
complete, and doesn't reliably sync over the signal and noise level ranges it
should. There are some nasty inefficiencies too!
TODO:
Better noise performance
Retrain is incomplete
Rate change is not implemented
Remote loopback is not implemented */
#if defined(HAVE_CONFIG_H)
#include "config.h"
@ -103,33 +110,6 @@ The basic method used by the V.22bis receiver is:
Descramble and output the bits represented by the decision.
*/
enum
{
V22BIS_RX_TRAINING_STAGE_NORMAL_OPERATION,
V22BIS_RX_TRAINING_STAGE_SYMBOL_ACQUISITION,
V22BIS_RX_TRAINING_STAGE_LOG_PHASE,
V22BIS_RX_TRAINING_STAGE_UNSCRAMBLED_ONES,
V22BIS_RX_TRAINING_STAGE_UNSCRAMBLED_ONES_SUSTAINING,
V22BIS_RX_TRAINING_STAGE_SCRAMBLED_ONES_AT_1200,
V22BIS_RX_TRAINING_STAGE_SCRAMBLED_ONES_AT_1200_SUSTAINING,
V22BIS_RX_TRAINING_STAGE_WAIT_FOR_SCRAMBLED_ONES_AT_2400,
V22BIS_RX_TRAINING_STAGE_PARKED
};
/* Segments of the training sequence */
enum
{
V22BIS_TX_TRAINING_STAGE_NORMAL_OPERATION = 0,
V22BIS_TX_TRAINING_STAGE_INITIAL_TIMED_SILENCE,
V22BIS_TX_TRAINING_STAGE_INITIAL_SILENCE,
V22BIS_TX_TRAINING_STAGE_U11,
V22BIS_TX_TRAINING_STAGE_U0011,
V22BIS_TX_TRAINING_STAGE_S11,
V22BIS_TX_TRAINING_STAGE_TIMED_S11,
V22BIS_TX_TRAINING_STAGE_S1111,
V22BIS_TX_TRAINING_STAGE_PARKED
};
static const uint8_t space_map_v22bis[6][6] =
{
{11, 9, 9, 6, 6, 7},
@ -194,19 +174,28 @@ SPAN_DECLARE(int) v22bis_rx_equalizer_state(v22bis_state_t *s, complexf_t **coef
}
/*- End of function --------------------------------------------------------*/
static void equalizer_reset(v22bis_state_t *s)
void v22bis_equalizer_coefficient_reset(v22bis_state_t *s)
{
/* Start with an equalizer based on everything being perfect */
#if defined(SPANDSP_USE_FIXED_POINTx)
cvec_zeroi16(s->rx.eq_coeff, 2*V22BIS_EQUALIZER_LEN + 1);
s->rx.eq_coeff[V22BIS_EQUALIZER_LEN] = complex_seti16(3*FP_FACTOR, 0*FP_FACTOR);
cvec_zeroi16(s->rx.eq_buf, V22BIS_EQUALIZER_MASK + 1);
s->rx.eq_delta = 32768.0f*EQUALIZER_DELTA/(2*V22BIS_EQUALIZER_LEN + 1);
#else
cvec_zerof(s->rx.eq_coeff, 2*V22BIS_EQUALIZER_LEN + 1);
s->rx.eq_coeff[V22BIS_EQUALIZER_LEN] = complex_setf(3.0f, 0.0f);
cvec_zerof(s->rx.eq_buf, V22BIS_EQUALIZER_MASK + 1);
s->rx.eq_delta = EQUALIZER_DELTA/(2*V22BIS_EQUALIZER_LEN + 1);
#endif
}
/*- End of function --------------------------------------------------------*/
static void equalizer_reset(v22bis_state_t *s)
{
v22bis_equalizer_coefficient_reset(s);
#if defined(SPANDSP_USE_FIXED_POINTx)
cvec_zeroi16(s->rx.eq_buf, V22BIS_EQUALIZER_MASK + 1);
#else
cvec_zerof(s->rx.eq_buf, V22BIS_EQUALIZER_MASK + 1);
#endif
s->rx.eq_put_step = 20 - 1;
s->rx.eq_step = 0;
@ -345,35 +334,63 @@ static int decode_baudx(v22bis_state_t *s, int nearest)
}
/*- End of function --------------------------------------------------------*/
static __inline__ int find_quadrant(const complexf_t *z)
static __inline__ void symbol_sync(v22bis_state_t *s)
{
int b1;
int b2;
float p;
float q;
complexf_t zz;
complexf_t a;
complexf_t b;
complexf_t c;
/* Split the space along the two diagonals, as follows:
\ 1 /
\ /
2 X 0
/ \
/ 3 \
*/
b1 = (z->im > z->re);
b2 = (z->im < -z->re);
return (b2 << 1) | (b1 ^ b2);
/* This routine adapts the position of the half baud samples entering the equalizer. */
/* Perform a Gardner test for baud alignment on the three most recent samples. */
if (s->rx.sixteen_way_decisions)
{
p = s->rx.eq_buf[(s->rx.eq_step - 3) & V22BIS_EQUALIZER_MASK].re
- s->rx.eq_buf[(s->rx.eq_step - 1) & V22BIS_EQUALIZER_MASK].re;
p *= s->rx.eq_buf[(s->rx.eq_step - 2) & V22BIS_EQUALIZER_MASK].re;
q = s->rx.eq_buf[(s->rx.eq_step - 3) & V22BIS_EQUALIZER_MASK].im
- s->rx.eq_buf[(s->rx.eq_step - 1) & V22BIS_EQUALIZER_MASK].im;
q *= s->rx.eq_buf[(s->rx.eq_step - 2) & V22BIS_EQUALIZER_MASK].im;
}
else
{
/* Rotate the points to the 45 degree positions, to maximise the effectiveness of
the Gardner algorithm. This is particularly significant at the start of operation
to pull things in quickly. */
zz = complex_setf(0.894427, 0.44721f);
a = complex_mulf(&s->rx.eq_buf[(s->rx.eq_step - 3) & V22BIS_EQUALIZER_MASK], &zz);
b = complex_mulf(&s->rx.eq_buf[(s->rx.eq_step - 2) & V22BIS_EQUALIZER_MASK], &zz);
c = complex_mulf(&s->rx.eq_buf[(s->rx.eq_step - 1) & V22BIS_EQUALIZER_MASK], &zz);
p = (a.re - c.re)*b.re;
q = (a.im - c.im)*b.im;
}
s->rx.gardner_integrate += (p + q > 0.0f) ? s->rx.gardner_step : -s->rx.gardner_step;
if (abs(s->rx.gardner_integrate) >= 16)
{
/* This integrate and dump approach avoids rapid changes of the equalizer put step.
Rapid changes, without hysteresis, are bad. They degrade the equalizer performance
when the true symbol boundary is close to a sample boundary. */
s->rx.eq_put_step += (s->rx.gardner_integrate/16);
s->rx.total_baud_timing_correction += (s->rx.gardner_integrate/16);
//span_log(&s->logging, SPAN_LOG_FLOW, "Gardner kick %d [total %d]\n", s->rx.gardner_integrate, s->rx.total_baud_timing_correction);
if (s->rx.qam_report)
s->rx.qam_report(s->rx.qam_user_data, NULL, NULL, s->rx.gardner_integrate);
s->rx.gardner_integrate = 0;
}
}
/*- End of function --------------------------------------------------------*/
static void process_half_baud(v22bis_state_t *s, const complexf_t *sample)
{
complexf_t a;
complexf_t b;
complexf_t c;
complexf_t z;
complexf_t zz;
const complexf_t *target;
float p;
float q;
int re;
int im;
int nearest;
@ -392,56 +409,11 @@ static void process_half_baud(v22bis_state_t *s, const complexf_t *sample)
if ((s->rx.baud_phase ^= 1))
return;
/* Perform a Gardner test for baud alignment on the three most recent samples. */
#if 0
p = s->rx.eq_buf[(s->rx.eq_step - 3) & V22BIS_EQUALIZER_MASK].re
- s->rx.eq_buf[(s->rx.eq_step - 1) & V22BIS_EQUALIZER_MASK].re;
p *= s->rx.eq_buf[(s->rx.eq_step - 2) & V22BIS_EQUALIZER_MASK].re;
q = s->rx.eq_buf[(s->rx.eq_step - 3) & V22BIS_EQUALIZER_MASK].im
- s->rx.eq_buf[(s->rx.eq_step - 1) & V22BIS_EQUALIZER_MASK].im;
q *= s->rx.eq_buf[(s->rx.eq_step - 2) & V22BIS_EQUALIZER_MASK].im;
#else
if (s->rx.sixteen_way_decisions)
{
p = s->rx.eq_buf[(s->rx.eq_step - 3) & V22BIS_EQUALIZER_MASK].re
- s->rx.eq_buf[(s->rx.eq_step - 1) & V22BIS_EQUALIZER_MASK].re;
p *= s->rx.eq_buf[(s->rx.eq_step - 2) & V22BIS_EQUALIZER_MASK].re;
q = s->rx.eq_buf[(s->rx.eq_step - 3) & V22BIS_EQUALIZER_MASK].im
- s->rx.eq_buf[(s->rx.eq_step - 1) & V22BIS_EQUALIZER_MASK].im;
q *= s->rx.eq_buf[(s->rx.eq_step - 2) & V22BIS_EQUALIZER_MASK].im;
}
else
{
/* Rotate the points to the 45 degree positions, to maximise the effectiveness of the Gardner algorithm */
zz = complex_setf(cosf(26.57f*3.14159f/180.0f), sinf(26.57f*3.14159f/180.0f));
a = complex_mulf(&s->rx.eq_buf[(s->rx.eq_step - 3) & V22BIS_EQUALIZER_MASK], &zz);
b = complex_mulf(&s->rx.eq_buf[(s->rx.eq_step - 2) & V22BIS_EQUALIZER_MASK], &zz);
c = complex_mulf(&s->rx.eq_buf[(s->rx.eq_step - 1) & V22BIS_EQUALIZER_MASK], &zz);
p = (a.re - c.re)*b.re;
q = (a.im - c.im)*b.im;
}
#endif
p += q;
s->rx.gardner_integrate += ((p + q) > 0.0f) ? s->rx.gardner_step : -s->rx.gardner_step;
if (abs(s->rx.gardner_integrate) >= 16)
{
/* This integrate and dump approach avoids rapid changes of the equalizer put step.
Rapid changes, without hysteresis, are bad. They degrade the equalizer performance
when the true symbol boundary is close to a sample boundary. */
s->rx.eq_put_step += (s->rx.gardner_integrate/16);
s->rx.total_baud_timing_correction += (s->rx.gardner_integrate/16);
//span_log(&s->logging, SPAN_LOG_FLOW, "Gardner kick %d [total %d]\n", s->rx.gardner_integrate, s->rx.total_baud_timing_correction);
if (s->rx.qam_report)
s->rx.qam_report(s->rx.qam_user_data, NULL, NULL, s->rx.gardner_integrate);
s->rx.gardner_integrate = 0;
}
symbol_sync(s);
z = equalizer_get(s);
/* Find the constellation point */
if (s->rx.sixteen_way_decisions)
{
re = (int) (z.re + 3.0f);
@ -458,9 +430,17 @@ static void process_half_baud(v22bis_state_t *s, const complexf_t *sample)
}
else
{
zz = complex_setf(3.0f/3.162278f, -1.0f/3.162278f);
/* Rotate to 45 degrees, to make the slicing trivial */
zz = complex_setf(0.894427, 0.44721f);
zz = complex_mulf(&z, &zz);
nearest = (find_quadrant(&zz) << 2) | 0x01;
nearest = 0x01;
if (zz.re < 0.0f)
nearest |= 0x04;
if (zz.im < 0.0f)
{
nearest ^= 0x04;
nearest |= 0x08;
}
}
raw_bits = 0;
@ -471,6 +451,31 @@ static void process_half_baud(v22bis_state_t *s, const complexf_t *sample)
target = &v22bis_constellation[nearest];
track_carrier(s, &z, target);
tune_equalizer(s, &z, target);
raw_bits = phase_steps[((nearest >> 2) - (s->rx.constellation_state >> 2)) & 3];
/* TODO: detect unscrambled ones indicating a loopback request */
/* Search for the S1 signal that might be requesting a retrain */
if ((s->rx.last_raw_bits ^ raw_bits) == 0x3)
{
s->rx.pattern_repeats++;
}
else
{
if (s->rx.pattern_repeats >= 50 && (s->rx.last_raw_bits == 0x3 || s->rx.last_raw_bits == 0x0))
{
/* We should get a full run of 00 11 (about 60 bauds) at either modem. */
span_log(&s->logging, SPAN_LOG_FLOW, "+++ S1 detected (%d long)\n", s->rx.pattern_repeats);
span_log(&s->logging, SPAN_LOG_FLOW, "+++ Accepting a retrain request\n");
s->rx.pattern_repeats = 0;
s->rx.training_count = 0;
s->rx.training = V22BIS_RX_TRAINING_STAGE_SCRAMBLED_ONES_AT_1200;
s->tx.training_count = 0;
s->tx.training = V22BIS_TX_TRAINING_STAGE_U0011;
v22bis_equalizer_coefficient_reset(s);
v22bis_report_status_change(s, SIG_STATUS_MODEM_RETRAIN_OCCURRED);
}
s->rx.pattern_repeats = 0;
}
decode_baud(s, nearest);
break;
case V22BIS_RX_TRAINING_STAGE_SYMBOL_ACQUISITION:
@ -505,7 +510,7 @@ static void process_half_baud(v22bis_state_t *s, const complexf_t *sample)
track_carrier(s, &z, target);
raw_bits = phase_steps[((nearest >> 2) - (s->rx.constellation_state >> 2)) & 3];
s->rx.constellation_state = nearest;
if (raw_bits != s->rx.last_raw_bits)
if (raw_bits != s->rx.last_raw_bits)
s->rx.pattern_repeats = 0;
else
s->rx.pattern_repeats++;
@ -578,7 +583,7 @@ static void process_half_baud(v22bis_state_t *s, const complexf_t *sample)
{
/* We should get a full run of 00 11 (about 60 bauds) at the calling modem, but only about 20
at the answering modem, as the first 40 are TED settling time. */
span_log(&s->logging, SPAN_LOG_FLOW, "+++ S1 detected at %d\n", s->rx.pattern_repeats);
span_log(&s->logging, SPAN_LOG_FLOW, "+++ S1 detected (%d long)\n", s->rx.pattern_repeats);
if (s->bit_rate == 2400)
{
if (!s->caller)
@ -709,7 +714,8 @@ SPAN_DECLARE(int) v22bis_rx(v22bis_state_t *s, const int16_t amp[], int len)
s->rx.rrc_filter_step = 0;
/* Calculate the I filter, with an arbitrary phase step, just so we can calculate
the signal power. */
the signal power of the required carrier, with any guard tone or spillback of our
own transmitted signal suppressed. */
if (s->caller)
{
ii = rx_pulseshaper_2400_re[6][0]*s->rx.rrc_filter[s->rx.rrc_filter_step];
@ -744,7 +750,7 @@ SPAN_DECLARE(int) v22bis_rx(v22bis_state_t *s, const int16_t amp[], int len)
if (s->rx.training != V22BIS_RX_TRAINING_STAGE_PARKED)
{
/* Only spend effort processing this data if the modem is not
parked, after training failure. */
parked, after a training failure. */
z = dds_complexf(&s->rx.carrier_phase, s->rx.carrier_phase_rate);
if (s->rx.training == V22BIS_RX_TRAINING_STAGE_SYMBOL_ACQUISITION)
{

View File

@ -22,12 +22,13 @@
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: v22bis_tx.c,v 1.61 2009/04/25 10:18:50 steveu Exp $
* $Id: v22bis_tx.c,v 1.62 2009/04/29 12:37:45 steveu Exp $
*/
/*! \file */
/* THIS IS A WORK IN PROGRESS - NOT YET FUNCTIONAL! */
/* THIS IS A WORK IN PROGRESS - It is basically functional, but it is not feature
complete, and doesn't reliably sync over the signal and noise level ranges it should! */
#if defined(HAVE_CONFIG_H)
#include "config.h"
@ -240,20 +241,6 @@ Both ends should accept unscrambled binary 1 or binary 0 as the preamble.
#define ms_to_symbols(t) (((t)*600)/1000)
/* Segments of the training sequence */
enum
{
V22BIS_TX_TRAINING_STAGE_NORMAL_OPERATION = 0,
V22BIS_TX_TRAINING_STAGE_INITIAL_TIMED_SILENCE,
V22BIS_TX_TRAINING_STAGE_INITIAL_SILENCE,
V22BIS_TX_TRAINING_STAGE_U11,
V22BIS_TX_TRAINING_STAGE_U0011,
V22BIS_TX_TRAINING_STAGE_S11,
V22BIS_TX_TRAINING_STAGE_TIMED_S11,
V22BIS_TX_TRAINING_STAGE_S1111,
V22BIS_TX_TRAINING_STAGE_PARKED
};
static const int phase_steps[4] =
{
1, 0, 2, 3
@ -598,6 +585,7 @@ SPAN_DECLARE(int) v22bis_restart(v22bis_state_t *s, int bit_rate)
SPAN_DECLARE(int) v22bis_request_retrain(v22bis_state_t *s, int bit_rate)
{
/* TODO: support bit rate switching */
switch (bit_rate)
{
case 2400:
@ -606,7 +594,33 @@ SPAN_DECLARE(int) v22bis_request_retrain(v22bis_state_t *s, int bit_rate)
default:
return -1;
}
/* TODO: Implement retrain and bit rate change */
/* TODO: support bit rate changes */
/* Retrain is only valid when we are normal operation at 2400bps */
if (s->rx.training != V22BIS_RX_TRAINING_STAGE_NORMAL_OPERATION
||
s->tx.training != V22BIS_TX_TRAINING_STAGE_NORMAL_OPERATION
||
s->negotiated_bit_rate != 2400)
{
return -1;
}
/* Send things back into the training process at the appropriate point.
The far end should detect the S1 signal, and reciprocate. */
span_log(&s->logging, SPAN_LOG_FLOW, "+++ Initiating a retrain\n");
s->rx.pattern_repeats = 0;
s->rx.training_count = 0;
s->rx.training = V22BIS_RX_TRAINING_STAGE_SCRAMBLED_ONES_AT_1200;
s->tx.training_count = 0;
s->tx.training = V22BIS_TX_TRAINING_STAGE_U0011;
v22bis_equalizer_coefficient_reset(s);
v22bis_report_status_change(s, SIG_STATUS_MODEM_RETRAIN_OCCURRED);
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) v22bis_remote_loopback(v22bis_state_t *s, int enable)
{
/* TODO: */
return -1;
}
/*- End of function --------------------------------------------------------*/

View File

@ -22,7 +22,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: fax_decode.c,v 1.54 2009/02/10 13:06:47 steveu Exp $
* $Id: fax_decode.c,v 1.55 2009/04/29 12:37:45 steveu Exp $
*/
/*! \page fax_decode_page FAX decoder
@ -229,7 +229,7 @@ static void hdlc_accept(void *user_data, const uint8_t *msg, int len, int ok)
{
if (msg[0] != 0xFF || !(msg[1] == 0x03 || msg[1] == 0x13))
{
fprintf(stderr, "Bad frame header - %02x %02x", msg[0], msg[1]);
fprintf(stderr, "Bad frame header - %02x %02x\n", msg[0], msg[1]);
return;
}
print_frame("HDLC: ", msg, len);

View File

@ -137,7 +137,6 @@ static void display_page_stats(t4_state_t *s)
}
/*- End of function --------------------------------------------------------*/
#if 0
static int row_read_handler(void *user_data, uint8_t buf[], size_t len)
{
int i;
@ -189,22 +188,108 @@ static int row_write_handler(void *user_data, const uint8_t buf[], size_t len)
printf("Oops - '%c' at end of row %d\n", *s, row);
if (memcmp(buf, ref, len))
{
printf("Failed at row %d\n", row);
printf("Test failed at row %d\n", row);
exit(2);
}
return len;
}
/*- End of function --------------------------------------------------------*/
#endif
static int detect_page_end(int bit, int page_ended)
{
static int consecutive_eols;
static int max_consecutive_eols;
static int consecutive_zeros;
static int consecutive_ones;
static int eol_zeros;
static int eol_ones;
static int expected_eols;
static int end_marks;
/* Check the EOLs are added properly to the end of an image. We can't rely on the
decoder giving the right answer, as a full set of EOLs is not needed for the
decoder to work. */
if (bit == -1000000)
{
/* Reset */
consecutive_eols = 0;
max_consecutive_eols = 0;
consecutive_zeros = 0;
consecutive_ones = 0;
end_marks = 0;
eol_zeros = 11;
eol_ones = (page_ended == T4_COMPRESSION_ITU_T4_2D) ? 2 : 1;
expected_eols = (page_ended == T4_COMPRESSION_ITU_T6) ? 2 : 6;
return FALSE;
}
/* Monitor whether the EOLs are there in the correct amount */
if (bit == 0)
{
consecutive_zeros++;
consecutive_ones = 0;
}
else if (bit == 1)
{
if (++consecutive_ones == eol_ones)
{
if (consecutive_eols == 0 && consecutive_zeros >= eol_zeros)
consecutive_eols++;
else if (consecutive_zeros == eol_zeros)
consecutive_eols++;
else
consecutive_eols = 0;
consecutive_zeros = 0;
consecutive_ones = 0;
}
if (max_consecutive_eols < consecutive_eols)
max_consecutive_eols = consecutive_eols;
}
else if (bit == SIG_STATUS_END_OF_DATA)
{
if (end_marks == 0)
{
if (max_consecutive_eols != expected_eols)
{
printf("Only %d EOLs (should be %d)\n", max_consecutive_eols, expected_eols);
return 2;
}
consecutive_zeros = 0;
consecutive_eols = 0;
max_consecutive_eols = 0;
}
if (!page_ended)
{
/* We might need to push a few bits to get the receiver to report the
end of page condition (at least with T.6). */
if (++end_marks > 50)
{
printf("Receiver missed the end of page mark\n");
return 2;
}
return 0;
}
return 1;
}
return 0;
}
/*- End of function --------------------------------------------------------*/
int main(int argc, char *argv[])
{
static const int compression_sequence[] =
{
T4_COMPRESSION_ITU_T4_1D,
T4_COMPRESSION_ITU_T4_2D,
T4_COMPRESSION_ITU_T6
};
int sends;
int page_no;
int bit;
int end_of_page;
int end_marks;
int decode_test;
int res;
int compression;
int compression_step;
int add_page_headers;
@ -214,6 +299,7 @@ int main(int argc, char *argv[])
char buf[1024];
uint8_t block[1024];
const char *in_file_name;
const char *decode_file_name;
int opt;
int i;
int bit_error_rate;
@ -221,29 +307,37 @@ int main(int argc, char *argv[])
int tests_failed;
unsigned int last_pkt_no;
unsigned int pkt_no;
int page_ended;
FILE *file;
tests_failed = 0;
decode_test = FALSE;
compression = -1;
compression_step = 0;
add_page_headers = FALSE;
restart_pages = FALSE;
in_file_name = IN_FILE_NAME;
min_row_bits = 0;
decode_file_name = NULL;
/* Use a non-zero default minimum row length to ensure we test the consecutive EOLs part
properly. */
min_row_bits = 50;
block_size = 0;
bit_error_rate = 0;
dump_as_xxx = FALSE;
while ((opt = getopt(argc, argv, "126b:dehri:m:x")) != -1)
while ((opt = getopt(argc, argv, "126b:d:ehri:m:x")) != -1)
{
switch (opt)
{
case '1':
compression = T4_COMPRESSION_ITU_T4_1D;
compression_step = -1;
break;
case '2':
compression = T4_COMPRESSION_ITU_T4_2D;
compression_step = -1;
break;
case '6':
compression = T4_COMPRESSION_ITU_T6;
compression_step = -1;
break;
case 'b':
block_size = atoi(optarg);
@ -251,7 +345,7 @@ int main(int argc, char *argv[])
block_size = 1024;
break;
case 'd':
decode_test = TRUE;
decode_file_name = optarg;
break;
case 'e':
bit_error_rate = 0x3FF;
@ -282,7 +376,7 @@ int main(int argc, char *argv[])
memset(&receive_state, 0, sizeof(receive_state));
end_of_page = FALSE;
if (decode_test)
if (decode_file_name)
{
if (compression < 0)
compression = T4_COMPRESSION_ITU_T4_1D;
@ -302,7 +396,8 @@ int main(int argc, char *argv[])
page_no = 1;
t4_rx_start_page(&receive_state);
last_pkt_no = 0;
while (fgets(buf, 1024, stdin))
file = fopen(decode_file_name, "r");
while (fgets(buf, 1024, file))
{
if (sscanf(buf, "HDLC: FCD: 06 %x", &pkt_no) == 1)
{
@ -361,6 +456,7 @@ int main(int argc, char *argv[])
}
}
}
fclose(file);
if (dump_as_xxx)
dump_image_as_xxx(&receive_state);
t4_rx_end_page(&receive_state);
@ -370,165 +466,8 @@ int main(int argc, char *argv[])
else
{
#if 1
/* Send end gets TIFF from a file */
if (t4_tx_init(&send_state, in_file_name, -1, -1) == NULL)
{
printf("Failed to init T.4 send\n");
exit(2);
}
span_log_set_level(&send_state.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME | SPAN_LOG_FLOW);
t4_tx_set_min_row_bits(&send_state, min_row_bits);
t4_tx_set_local_ident(&send_state, "111 2222 3333");
/* Receive end puts TIFF to a new file. */
if (t4_rx_init(&receive_state, OUT_FILE_NAME, T4_COMPRESSION_ITU_T4_2D) == NULL)
{
printf("Failed to init T.4 rx\n");
exit(2);
}
span_log_set_level(&receive_state.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME | SPAN_LOG_FLOW);
t4_rx_set_x_resolution(&receive_state, t4_tx_get_x_resolution(&send_state));
t4_rx_set_y_resolution(&receive_state, t4_tx_get_y_resolution(&send_state));
t4_rx_set_image_width(&receive_state, t4_tx_get_image_width(&send_state));
/* Now send and receive all the pages in the source TIFF file */
page_no = 1;
sends = 0;
/* Select whether we step round the compression schemes, or use a single specified one. */
compression_step = (compression < 0) ? 0 : -1;
for (;;)
{
end_marks = 0;
/* Add a header line to alternate pages, if required */
if (add_page_headers && (sends & 2))
t4_tx_set_header_info(&send_state, "Header");
else
t4_tx_set_header_info(&send_state, NULL);
if (restart_pages && (sends & 1))
{
/* Use restart, to send the page a second time */
if (t4_tx_restart_page(&send_state))
break;
}
else
{
switch (compression_step)
{
case 0:
compression = T4_COMPRESSION_ITU_T4_1D;
compression_step++;
break;
case 1:
compression = T4_COMPRESSION_ITU_T4_2D;
compression_step++;
break;
case 2:
compression = T4_COMPRESSION_ITU_T6;
compression_step = 0;
break;
}
t4_tx_set_tx_encoding(&send_state, compression);
t4_rx_set_rx_encoding(&receive_state, compression);
if (t4_tx_start_page(&send_state))
break;
}
t4_rx_start_page(&receive_state);
if (block_size == 0)
{
do
{
bit = t4_tx_get_bit(&send_state);
if (bit == SIG_STATUS_END_OF_DATA)
{
/* T.6 data does not contain an image termination sequence.
T.4 1D and 2D do, and should locate that sequence. */
if (compression == T4_COMPRESSION_ITU_T6)
break;
if (++end_marks > 50)
{
printf("Receiver missed the end of page mark\n");
tests_failed++;
break;
}
}
if (bit_error_rate)
{
if ((rand() % bit_error_rate) == 0)
bit ^= 1;
}
end_of_page = t4_rx_put_bit(&receive_state, bit & 1);
}
while (!end_of_page);
}
else if (block_size == 1)
{
do
{
bit = t4_tx_get_byte(&send_state);
if ((bit & 0x100))
{
/* T.6 data does not contain an image termination sequence.
T.4 1D and 2D do, and should locate that sequence. */
if (compression == T4_COMPRESSION_ITU_T6)
break;
if (++end_marks > 50)
{
printf("Receiver missed the end of page mark\n");
tests_failed++;
break;
}
}
end_of_page = t4_rx_put_byte(&receive_state, bit & 0xFF);
}
while (!end_of_page);
}
else
{
do
{
bit = t4_tx_get_chunk(&send_state, block, block_size);
if (bit > 0)
end_of_page = t4_rx_put_chunk(&receive_state, block, bit);
if (bit < block_size)
{
/* T.6 data does not contain an image termination sequence.
T.4 1D and 2D do, and should locate that sequence. */
if (compression == T4_COMPRESSION_ITU_T6)
break;
if (++end_marks > 50)
{
printf("Receiver missed the end of page mark\n");
tests_failed++;
break;
}
}
}
while (!end_of_page);
}
if (dump_as_xxx)
dump_image_as_xxx(&receive_state);
display_page_stats(&receive_state);
if (!restart_pages || (sends & 1))
t4_tx_end_page(&send_state);
t4_rx_end_page(&receive_state);
sends++;
}
t4_tx_release(&send_state);
t4_rx_release(&receive_state);
/* And we should now have a matching received TIFF file. Note this will only match
at the image level. TIFF files allow a lot of ways to express the same thing,
so bit matching of the files is not the normal case. */
fflush(stdout);
sprintf(buf, "tiffcmp -t %s " OUT_FILE_NAME, in_file_name);
if (tests_failed)// || system(buf))
{
printf("Tests failed\n");
exit(2);
}
#endif
#if 0
/* Send end gets TIFF from a function */
printf("Testing image_function->compress->decompress->image_function\n");
/* Send end gets image from a function */
if (t4_tx_init(&send_state, in_file_name, -1, -1) == NULL)
{
printf("Failed to init T.4 tx\n");
@ -553,26 +492,17 @@ int main(int argc, char *argv[])
/* Now send and receive all the pages in the source TIFF file */
page_no = 1;
/* Select whether we step round the compression schemes, or use a single specified one. */
compression_step = (compression < 0) ? 0 : -1;
/* If we are stepping around the compression schemes, reset to the start of the sequence. */
if (compression_step > 0)
compression_step = 0;
for (;;)
{
end_marks = 0;
/* Add a header line to alternate pages, if required */
switch (compression_step)
if (compression_step >= 0)
{
case 0:
compression = T4_COMPRESSION_ITU_T4_1D;
compression_step++;
break;
case 1:
compression = T4_COMPRESSION_ITU_T4_2D;
compression_step++;
break;
case 2:
compression = T4_COMPRESSION_ITU_T6;
compression_step = 0;
break;
compression = compression_sequence[compression_step++];
if (compression_step > 3)
break;
}
t4_tx_set_tx_encoding(&send_state, compression);
t4_rx_set_rx_encoding(&receive_state, compression);
@ -585,10 +515,6 @@ int main(int argc, char *argv[])
bit = t4_tx_get_bit(&send_state);
if (bit == SIG_STATUS_END_OF_DATA)
{
/* T.6 data does not contain an image termination sequence.
T.4 1D and 2D do, and should locate that sequence. */
if (compression == T4_COMPRESSION_ITU_T6)
break;
if (++end_marks > 50)
{
printf("Receiver missed the end of page mark\n");
@ -601,12 +527,159 @@ int main(int argc, char *argv[])
while (!end_of_page);
t4_tx_end_page(&send_state);
t4_rx_end_page(&receive_state);
break;
if (compression_step < 0)
break;
}
t4_tx_release(&send_state);
t4_rx_release(&receive_state);
#endif
#if 1
printf("Testing TIFF->compress->decompress->TIFF cycle\n");
/* Send end gets TIFF from a file */
if (t4_tx_init(&send_state, in_file_name, -1, -1) == NULL)
{
printf("Failed to init T.4 send\n");
exit(2);
}
span_log_set_level(&send_state.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME | SPAN_LOG_FLOW);
t4_tx_set_min_row_bits(&send_state, min_row_bits);
t4_tx_set_local_ident(&send_state, "111 2222 3333");
/* Receive end puts TIFF to a new file. */
if (t4_rx_init(&receive_state, OUT_FILE_NAME, T4_COMPRESSION_ITU_T4_2D) == NULL)
{
printf("Failed to init T.4 rx for '%s'\n", OUT_FILE_NAME);
exit(2);
}
span_log_set_level(&receive_state.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_SHOW_SAMPLE_TIME | SPAN_LOG_FLOW);
t4_rx_set_x_resolution(&receive_state, t4_tx_get_x_resolution(&send_state));
t4_rx_set_y_resolution(&receive_state, t4_tx_get_y_resolution(&send_state));
t4_rx_set_image_width(&receive_state, t4_tx_get_image_width(&send_state));
/* Now send and receive all the pages in the source TIFF file */
page_no = 1;
sends = 0;
/* If we are stepping around the compression schemes, reset to the start of the sequence. */
if (compression_step > 0)
compression_step = 0;
for (;;)
{
end_marks = 0;
/* Add a header line to alternate pages, if required */
if (add_page_headers && (sends & 2))
t4_tx_set_header_info(&send_state, "Header");
else
t4_tx_set_header_info(&send_state, NULL);
if (restart_pages && (sends & 1))
{
/* Use restart, to send the page a second time */
if (t4_tx_restart_page(&send_state))
break;
}
else
{
if (compression_step >= 0)
{
compression = compression_sequence[compression_step++];
if (compression_step > 2)
compression_step = 0;
}
t4_tx_set_tx_encoding(&send_state, compression);
t4_rx_set_rx_encoding(&receive_state, compression);
if (t4_tx_start_page(&send_state))
break;
}
t4_rx_start_page(&receive_state);
detect_page_end(-1000000, compression);
page_ended = FALSE;
if (block_size == 0)
{
for (;;)
{
bit = t4_tx_get_bit(&send_state);
/* Monitor whether the EOLs are there in the correct amount */
if ((res = detect_page_end(bit, page_ended)))
{
tests_failed += (res - 1);
break;
}
if (!page_ended)
{
if (bit_error_rate)
{
if ((rand() % bit_error_rate) == 0)
bit ^= 1;
}
if (t4_rx_put_bit(&receive_state, bit & 1))
page_ended = TRUE;
}
}
/* Now throw junk at the receive context, to ensure stuff occuring
after the end of page condition has no bad effect. */
for (i = 0; i < 1000; i++)
{
t4_rx_put_bit(&receive_state, (rand() >> 10) & 1);
}
}
else if (block_size == 1)
{
do
{
bit = t4_tx_get_byte(&send_state);
if ((bit & 0x100))
{
if (++end_marks > 50)
{
printf("Receiver missed the end of page mark\n");
tests_failed++;
break;
}
}
end_of_page = t4_rx_put_byte(&receive_state, bit & 0xFF);
}
while (!end_of_page);
}
else
{
do
{
bit = t4_tx_get_chunk(&send_state, block, block_size);
if (bit > 0)
end_of_page = t4_rx_put_chunk(&receive_state, block, bit);
if (bit < block_size)
{
if (++end_marks > 50)
{
printf("Receiver missed the end of page mark\n");
tests_failed++;
break;
}
}
}
while (!end_of_page);
}
if (dump_as_xxx)
dump_image_as_xxx(&receive_state);
display_page_stats(&receive_state);
if (!restart_pages || (sends & 1))
t4_tx_end_page(&send_state);
t4_rx_end_page(&receive_state);
sends++;
}
t4_tx_release(&send_state);
t4_rx_release(&receive_state);
/* And we should now have a matching received TIFF file. Note this will only match
at the image level. TIFF files allow a lot of ways to express the same thing,
so bit matching of the files is not the normal case. */
fflush(stdout);
sprintf(buf, "tiffcmp -t %s %s", in_file_name, OUT_FILE_NAME);
if (tests_failed || system(buf))
{
printf("Tests failed\n");
exit(2);
}
#endif
printf("Tests passed\n");
}
return 0;

View File

@ -15,7 +15,7 @@
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# $Id: tsb85_tests.sh,v 1.6 2008/09/11 15:13:42 steveu Exp $
# $Id: tsb85_tests.sh,v 1.7 2009/04/30 15:04:20 steveu Exp $
#
run_tsb85_test()
@ -54,11 +54,12 @@ for TEST in OREN01 OREN02 OREN03 OREN04 OREN05 OREN06 OREN07 OREN08 OREN09 OREN1
run_tsb85_test
done
# MRGX03 is failing because the V.27ter modemsays it trained on HDLC
# MRGX05 is failing because we don't distinguish MPS immediately after MCF from MPS after
# a corrupt image signal.
#for TEST in MRGX01 MRGX02 MRGX03 MRGX04 MRGX05 MRGX06 MRGX07 MRGX08 ; do
for TEST in MRGX01 MRGX02 MRGX03 MRGX04 MRGX06 MRGX07 MRGX08 ; do
for TEST in MRGX01 MRGX02 MRGX04 MRGX06 MRGX07 MRGX08 ; do
run_tsb85_test
done
@ -94,7 +95,8 @@ for TEST in OTGC10 OTGC11 ; do
run_tsb85_test
done
for TEST in OTEN01 OTEN02 OTEN03 OTEN04 OTEN05 OTEN06 ; do
#for TEST in OTEN01 OTEN02 OTEN03 OTEN04 OTEN05 OTEN06 ; do
for TEST in OTEN01 OTEN03 OTEN04 OTEN05 OTEN06 ; do
run_tsb85_test
done