763 lines
30 KiB
C
763 lines
30 KiB
C
/*
|
|
* SpanDSP - a series of DSP components for telephony
|
|
*
|
|
* v22bis_tx.c - ITU V.22bis modem transmit part
|
|
*
|
|
* Written by Steve Underwood <steveu@coppice.org>
|
|
*
|
|
* Copyright (C) 2004 Steve Underwood
|
|
*
|
|
* All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License version 2.1,
|
|
* as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
/*! \file */
|
|
|
|
/* 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"
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <inttypes.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#if defined(HAVE_TGMATH_H)
|
|
#include <tgmath.h>
|
|
#endif
|
|
#if defined(HAVE_MATH_H)
|
|
#include <math.h>
|
|
#endif
|
|
#if defined(HAVE_STDBOOL_H)
|
|
#include <stdbool.h>
|
|
#else
|
|
#include "spandsp/stdbool.h"
|
|
#endif
|
|
#include "floating_fudge.h"
|
|
|
|
#include "spandsp/telephony.h"
|
|
#include "spandsp/alloc.h"
|
|
#include "spandsp/fast_convert.h"
|
|
#include "spandsp/logging.h"
|
|
#include "spandsp/complex.h"
|
|
#include "spandsp/vector_float.h"
|
|
#include "spandsp/complex_vector_float.h"
|
|
#include "spandsp/vector_int.h"
|
|
#include "spandsp/complex_vector_int.h"
|
|
#include "spandsp/async.h"
|
|
#include "spandsp/dds.h"
|
|
#include "spandsp/power_meter.h"
|
|
|
|
#include "spandsp/v29rx.h"
|
|
#include "spandsp/v22bis.h"
|
|
|
|
#include "spandsp/private/logging.h"
|
|
#include "spandsp/private/power_meter.h"
|
|
#include "spandsp/private/v22bis.h"
|
|
|
|
#if defined(SPANDSP_USE_FIXED_POINT)
|
|
#define FP_SCALE(x) FP_Q6_10(x)
|
|
#else
|
|
#define FP_SCALE(x) (x)
|
|
#endif
|
|
|
|
#include "v22bis_tx_rrc.h"
|
|
|
|
/* Quoting from the V.22bis spec.
|
|
|
|
6.3.1.1 Interworking at 2400 bit/s
|
|
|
|
6.3.1.1.1 Calling modem
|
|
|
|
a) On connection to line the calling modem shall be conditioned to receive signals
|
|
in the high channel at 1200 bit/s and transmit signals in the low channel at 1200 bit/s
|
|
in accordance with section 2.5.2.2. It shall apply an ON condition to circuit 107 in accordance
|
|
with Recommendation V.25. The modem shall initially remain silent.
|
|
|
|
b) After 155 +-10 ms of unscrambled binary 1 has been detected, the modem shall remain silent
|
|
for a further 456 +-10 ms then transmit an unscrambled repetitive double dibit pattern of 00
|
|
and 11 at 1200 bit/s for 100 +-3 ms. Following this signal the modem shall transmit scrambled
|
|
binary 1 at 1200 bit/s.
|
|
|
|
c) If the modem detects scrambled binary 1 in the high channel at 1200 bit/s for 270 +-40 ms,
|
|
the handshake shall continue in accordance with section 6.3.1.2.1 c) and d). However, if unscrambled
|
|
repetitive double dibit 00 and 11 at 1200 bit/s is detected in the high channel, then at the
|
|
end of receipt of this signal the modem shall apply an ON condition to circuit 112.
|
|
|
|
d) 600 +-10 ms after circuit 112 has been turned ON the modem shall begin transmitting scrambled
|
|
binary 1 at 2400 bit/s, and 450 +-10 ms after circuit 112 has been turned ON the receiver may
|
|
begin making 16-way decisions.
|
|
|
|
e) Following transmission of scrambled binary 1 at 2400 bit/s for 200 +-10 ms, circuit 106 shall
|
|
be conditioned to respond to circuit 105 and the modem shall be ready to transmit data at
|
|
2400 bit/s.
|
|
|
|
f) When 32 consecutive bits of scrambled binary 1 at 2400 bit/s have been detected in the high
|
|
channel the modem shall be ready to receive data at 2400 bit/s and shall apply an ON condition
|
|
to circuit 109.
|
|
|
|
6.3.1.1.2 Answering modem
|
|
|
|
a) On connection to line the answering modem shall be conditioned to transmit signals in the high
|
|
channel at 1200 bit/s in accordance with section 2.5.2.2 and receive signals in the low channel at
|
|
1200 bit/s. Following transmission of the answer sequence in accordance with Recommendation
|
|
V.25, the modem shall apply an ON condition to circuit 107 and then transmit unscrambled
|
|
binary 1 at 1200 bit/s.
|
|
|
|
b) If the modem detects scrambled binary 1 or 0 in the low channel at 1200 bit/s for 270 +-40 ms,
|
|
the handshake shall continue in accordance with section 6.3.1.2.2 b) and c). However, if unscrambled
|
|
repetitive double dibit 00 and 11 at 1200 bit/s is detected in the low channel, at the end of
|
|
receipt of this signal the modem shall apply an ON condition to circuit 112 and then transmit
|
|
an unscrambled repetitive double dibit pattern of 00 and 11 at 1200 bit/s for 100 +-3 ms.
|
|
Following these signals the modem shall transmit scrambled binary 1 at 1200 bit/s.
|
|
|
|
c) 600 +-10 ms after circuit 112 has been turned ON the modem shall begin transmitting scrambled
|
|
binary 1 at 2400 bit/s, and 450 +-10 ms after circuit 112 has been turned ON the receiver may
|
|
begin making 16-way decisions.
|
|
|
|
d) Following transmission of scrambled binary 1 at 2400 bit/s for 200 +-10 ms, circuit 106 shall
|
|
be conditioned to respond to circuit 105 and the modem shall be ready to transmit data at
|
|
2400 bit/s.
|
|
|
|
e) When 32 consecutive bits of scrambled binary 1 at 2400 bit/s have been detected in the low
|
|
channel the modem shall be ready to receive data at 2400 bit/s and shall apply an ON
|
|
condition to circuit 109.
|
|
|
|
6.3.1.2 Interworking at 1200 bit/s
|
|
|
|
The following handshake is identical to the Recommendation V.22 alternative A and B handshake.
|
|
|
|
6.3.1.2.1 Calling modem
|
|
|
|
a) On connection to line the calling modem shall be conditioned to receive signals in the high
|
|
channel at 1200 bit/s and transmit signals in the low channel at 1200 bit/s in accordance
|
|
with section 2.5.2.2. It shall apply an ON condition to circuit 107 in accordance with
|
|
Recommendation V.25. The modem shall initially remain silent.
|
|
|
|
b) After 155 +-10 ms of unscrambled binary 1 has been detected, the modem shall remain silent
|
|
for a further 456 +-10 ms then transmit scrambled binary 1 at 1200 bit/s (a preceding V.22 bis
|
|
signal, as shown in Figure 7/V.22 bis, would not affect the operation of a V.22 answer modem).
|
|
|
|
c) On detection of scrambled binary 1 in the high channel at 1200 bit/s for 270 +-40 ms the modem
|
|
shall be ready to receive data at 1200 bit/s and shall apply an ON condition to circuit 109 and
|
|
an OFF condition to circuit 112.
|
|
|
|
d) 765 +-10 ms after circuit 109 has been turned ON, circuit 106 shall be conditioned to respond
|
|
to circuit 105 and the modem shall be ready to transmit data at 1200 bit/s.
|
|
|
|
6.3.1.2.2 Answering modem
|
|
|
|
a) On connection to line the answering modem shall be conditioned to transmit signals in the high
|
|
channel at 1200 bit/s in accordance with section 2.5.2.2 and receive signals in the low channel at
|
|
1200 bit/s.
|
|
|
|
Following transmission of the answer sequence in accordance with V.25 the modem shall apply
|
|
an ON condition to circuit 107 and then transmit unscrambled binary 1 at 1200 bit/s.
|
|
|
|
b) On detection of scrambled binary 1 or 0 in the low channel at 1200 bit/s for 270 +-40 ms the
|
|
modem shall apply an OFF condition to circuit 112 and shall then transmit scrambled binary 1
|
|
at 1200 bit/s.
|
|
|
|
c) After scrambled binary 1 has been transmitted at 1200 bit/s for 765 +-10 ms the modem shall be
|
|
ready to transmit and receive data at 1200 bit/s, shall condition circuit 106 to respond to
|
|
circuit 105 and shall apply an ON condition to circuit 109.
|
|
|
|
Note - Manufacturers may wish to note that in certain countries, for national purposes, modems are
|
|
in service which emit an answering tone of 2225 Hz instead of unscrambled binary 1.
|
|
|
|
|
|
V.22bis to V.22bis
|
|
------------------
|
|
Calling party
|
|
S1 scrambled 1's scrambled 1's data
|
|
at 1200bps at 2400bps
|
|
|---------------------------------------------------------|XXXXXXXX|XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX|XXXXXXXXXXXXXX|XXXXXXXXXXXXX
|
|
|<155+-10>|<456+-10>|<100+-3>| |<------600+-10------>|<---200+-10-->|
|
|
^ | ^<----450+-100---->|[16 way decisions begin]
|
|
| | |
|
|
| v |
|
|
| |<------450+-100----->|[16 way decisions begin]
|
|
| |<----------600+-10-------->|
|
|
|<2150+-350>|<--3300+-700->|<75+-20>| |<100+-3>| |<---200+-10-->
|
|
|-----------|XXXXXXXXXXXXXX|--------|XXXXXXXXXXXXXXXXXXXXXXXXXXXX|XXXXXXXX|XXXXXXXXXXXXXXXXXX|XXXXXXXXXXXXXX|XXXXXXXXXXXXX
|
|
silence 2100Hz unscrambled 1's S1 scrambled 1's scrambled 1's data
|
|
at 1200bps at 1200bps at 2400bps
|
|
Answering party
|
|
|
|
S1 = Unscrambled double dibit 00 and 11 at 1200bps
|
|
When the 2400bps section starts, both sides should look for 32 bits of continuous ones, as a test of integrity.
|
|
|
|
|
|
|
|
|
|
V.22 to V.22bis
|
|
---------------
|
|
Calling party
|
|
scrambled 1's data
|
|
at 1200bps
|
|
|---------------------------------------------------------|XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX|XXXXXXXXXXXXX
|
|
|<155+-10>|<456+-10>| |<270+-40>|<--------765+-10-------->|
|
|
^ | ^
|
|
| | |
|
|
| | |
|
|
| | |
|
|
| v |
|
|
|<2150+-350>|<--3300+-700->|<75+-20>| |<270+-40>|<---------765+-10-------->|
|
|
|-----------|XXXXXXXXXXXXXX|--------|XXXXXXXXXXXXXXXXXXXXXXXXXXXXX|XXXXXXXXXXXXXXXXXXXXXXXXXX|XXXXXXXXXXXXX
|
|
silence 2100Hz unscrambled 1's scrambled 1's data
|
|
at 1200bps at 1200bps
|
|
Answering party
|
|
|
|
Both ends should accept unscrambled binary 1 or binary 0 as the preamble.
|
|
|
|
|
|
|
|
|
|
V.22bis to V.22
|
|
---------------
|
|
Calling party
|
|
S1 scrambled 1's data
|
|
at 1200bps
|
|
|---------------------------------------------------------|XXXXXXXX|XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX|XXXXXXXXXXXXX
|
|
|<155+-10>|<456+-10>|<100+-3>| |<-270+-40-><------765+-10------>|
|
|
^ | ^
|
|
| | |
|
|
| v |
|
|
| |
|
|
| |
|
|
|<2150+-350>|<--3300+-700->|<75+-20>| |<-270+-40->|<------765+-10----->|
|
|
|-----------|XXXXXXXXXXXXXX|--------|XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX|XXXXXXXXXXXXXXXXXXXX|XXXXXXXXXXXXX
|
|
silence 2100Hz unscrambled 1's scrambled 1's data
|
|
at 1200bps at 1200bps
|
|
Answering party
|
|
|
|
Both ends should accept unscrambled binary 1 or binary 0 as the preamble.
|
|
*/
|
|
|
|
#define ms_to_symbols(t) (((t)*600)/1000)
|
|
|
|
static const int phase_steps[4] =
|
|
{
|
|
1, 0, 2, 3
|
|
};
|
|
|
|
#if defined(SPANDSP_USE_FIXED_POINT)
|
|
const complexi16_t v22bis_constellation[16] =
|
|
#else
|
|
const complexf_t v22bis_constellation[16] =
|
|
#endif
|
|
{
|
|
{FP_SCALE( 1.0f), FP_SCALE( 1.0f)},
|
|
{FP_SCALE( 3.0f), FP_SCALE( 1.0f)}, /* 1200bps 00 */
|
|
{FP_SCALE( 1.0f), FP_SCALE( 3.0f)},
|
|
{FP_SCALE( 3.0f), FP_SCALE( 3.0f)},
|
|
{FP_SCALE(-1.0f), FP_SCALE( 1.0f)},
|
|
{FP_SCALE(-1.0f), FP_SCALE( 3.0f)}, /* 1200bps 01 */
|
|
{FP_SCALE(-3.0f), FP_SCALE( 1.0f)},
|
|
{FP_SCALE(-3.0f), FP_SCALE( 3.0f)},
|
|
{FP_SCALE(-1.0f), FP_SCALE(-1.0f)},
|
|
{FP_SCALE(-3.0f), FP_SCALE(-1.0f)}, /* 1200bps 10 */
|
|
{FP_SCALE(-1.0f), FP_SCALE(-3.0f)},
|
|
{FP_SCALE(-3.0f), FP_SCALE(-3.0f)},
|
|
{FP_SCALE( 1.0f), FP_SCALE(-1.0f)},
|
|
{FP_SCALE( 1.0f), FP_SCALE(-3.0f)}, /* 1200bps 11 */
|
|
{FP_SCALE( 3.0f), FP_SCALE(-1.0f)},
|
|
{FP_SCALE( 3.0f), FP_SCALE(-3.0f)}
|
|
};
|
|
|
|
static int fake_get_bit(void *user_data)
|
|
{
|
|
return 1;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
static __inline__ int scramble(v22bis_state_t *s, int bit)
|
|
{
|
|
int out_bit;
|
|
|
|
if (s->tx.scrambler_pattern_count >= 64)
|
|
{
|
|
bit ^= 1;
|
|
s->tx.scrambler_pattern_count = 0;
|
|
}
|
|
out_bit = (bit ^ (s->tx.scramble_reg >> 13) ^ (s->tx.scramble_reg >> 16)) & 1;
|
|
s->tx.scramble_reg = (s->tx.scramble_reg << 1) | out_bit;
|
|
|
|
if (out_bit == 1)
|
|
s->tx.scrambler_pattern_count++;
|
|
else
|
|
s->tx.scrambler_pattern_count = 0;
|
|
return out_bit;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
static __inline__ int get_scrambled_bit(v22bis_state_t *s)
|
|
{
|
|
int bit;
|
|
|
|
if ((bit = s->tx.current_get_bit(s->get_bit_user_data)) == SIG_STATUS_END_OF_DATA)
|
|
{
|
|
/* Fill out this symbol with ones, and prepare to send
|
|
the rest of the shutdown sequence. */
|
|
s->tx.current_get_bit = fake_get_bit;
|
|
s->tx.shutdown = 1;
|
|
bit = 1;
|
|
}
|
|
return scramble(s, bit);
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
#if defined(SPANDSP_USE_FIXED_POINT)
|
|
static complexi16_t training_get(v22bis_state_t *s)
|
|
#else
|
|
static complexf_t training_get(v22bis_state_t *s)
|
|
#endif
|
|
{
|
|
#if defined(SPANDSP_USE_FIXED_POINT)
|
|
static const complexi16_t zero = {0, 0};
|
|
#else
|
|
static const complexf_t zero = {0.0f, 0.0f};
|
|
#endif
|
|
int bits;
|
|
|
|
/* V.22bis training sequence */
|
|
switch (s->tx.training)
|
|
{
|
|
case V22BIS_TX_TRAINING_STAGE_INITIAL_TIMED_SILENCE:
|
|
/* The answerer waits 75ms, then sends unscrambled ones */
|
|
if (++s->tx.training_count >= ms_to_symbols(75))
|
|
{
|
|
/* Initial 75ms of silence is over */
|
|
span_log(&s->logging, SPAN_LOG_FLOW, "+++ starting U11 1200\n");
|
|
s->tx.training_count = 0;
|
|
s->tx.training = V22BIS_TX_TRAINING_STAGE_U11;
|
|
}
|
|
/* Fall through */
|
|
case V22BIS_TX_TRAINING_STAGE_INITIAL_SILENCE:
|
|
/* Silence */
|
|
s->tx.constellation_state = 0;
|
|
return zero;
|
|
case V22BIS_TX_TRAINING_STAGE_U11:
|
|
/* Send continuous unscrambled ones at 1200bps (i.e. 270 degree phase steps). */
|
|
/* Only the answering modem sends unscrambled ones. It is the first thing exchanged between the modems. */
|
|
s->tx.constellation_state = (s->tx.constellation_state + phase_steps[3]) & 3;
|
|
return v22bis_constellation[(s->tx.constellation_state << 2) | 0x01];
|
|
case V22BIS_TX_TRAINING_STAGE_U0011:
|
|
/* Continuous unscrambled double dibit 00 11 at 1200bps. This is termed the S1 segment in
|
|
the V.22bis spec. It is only sent to request or accept 2400bps mode, and lasts 100+-3ms. After this
|
|
timed burst, we unconditionally change to sending scrambled ones at 1200bps. */
|
|
s->tx.constellation_state = (s->tx.constellation_state + phase_steps[3*(s->tx.training_count & 1)]) & 3;
|
|
if (++s->tx.training_count >= ms_to_symbols(100))
|
|
{
|
|
span_log(&s->logging, SPAN_LOG_FLOW, "+++ starting S11 after U0011\n");
|
|
if (s->calling_party)
|
|
{
|
|
s->tx.training_count = 0;
|
|
s->tx.training = V22BIS_TX_TRAINING_STAGE_S11;
|
|
}
|
|
else
|
|
{
|
|
s->tx.training_count = ms_to_symbols(756 - (600 - 100));
|
|
s->tx.training = V22BIS_TX_TRAINING_STAGE_TIMED_S11;
|
|
}
|
|
}
|
|
return v22bis_constellation[(s->tx.constellation_state << 2) | 0x01];
|
|
case V22BIS_TX_TRAINING_STAGE_TIMED_S11:
|
|
/* A timed period of scrambled ones at 1200bps. */
|
|
if (++s->tx.training_count >= ms_to_symbols(756))
|
|
{
|
|
if (s->negotiated_bit_rate == 2400)
|
|
{
|
|
span_log(&s->logging, SPAN_LOG_FLOW, "+++ starting S1111 (C)\n");
|
|
s->tx.training_count = 0;
|
|
s->tx.training = V22BIS_TX_TRAINING_STAGE_S1111;
|
|
}
|
|
else
|
|
{
|
|
span_log(&s->logging, SPAN_LOG_FLOW, "+++ Tx normal operation (1200)\n");
|
|
s->tx.training_count = 0;
|
|
s->tx.training = V22BIS_TX_TRAINING_STAGE_NORMAL_OPERATION;
|
|
v22bis_report_status_change(s, SIG_STATUS_TRAINING_SUCCEEDED);
|
|
s->tx.current_get_bit = s->get_bit;
|
|
}
|
|
}
|
|
/* Fall through */
|
|
case V22BIS_TX_TRAINING_STAGE_S11:
|
|
/* Scrambled ones at 1200bps. */
|
|
bits = scramble(s, 1);
|
|
bits = (bits << 1) | scramble(s, 1);
|
|
s->tx.constellation_state = (s->tx.constellation_state + phase_steps[bits]) & 3;
|
|
return v22bis_constellation[(s->tx.constellation_state << 2) | 0x01];
|
|
case V22BIS_TX_TRAINING_STAGE_S1111:
|
|
/* Scrambled ones at 2400bps. We send a timed 200ms burst, and switch to normal operation at 2400bps */
|
|
bits = scramble(s, 1);
|
|
bits = (bits << 1) | scramble(s, 1);
|
|
s->tx.constellation_state = (s->tx.constellation_state + phase_steps[bits]) & 3;
|
|
bits = scramble(s, 1);
|
|
bits = (bits << 1) | scramble(s, 1);
|
|
if (++s->tx.training_count >= ms_to_symbols(200))
|
|
{
|
|
/* We have completed training. Now handle some real work. */
|
|
span_log(&s->logging, SPAN_LOG_FLOW, "+++ Tx normal operation (2400)\n");
|
|
s->tx.training_count = 0;
|
|
s->tx.training = V22BIS_TX_TRAINING_STAGE_NORMAL_OPERATION;
|
|
v22bis_report_status_change(s, SIG_STATUS_TRAINING_SUCCEEDED);
|
|
s->tx.current_get_bit = s->get_bit;
|
|
}
|
|
return v22bis_constellation[(s->tx.constellation_state << 2) | bits];
|
|
}
|
|
return zero;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
#if defined(SPANDSP_USE_FIXED_POINT)
|
|
static complexi16_t getbaud(v22bis_state_t *s)
|
|
#else
|
|
static complexf_t getbaud(v22bis_state_t *s)
|
|
#endif
|
|
{
|
|
#if defined(SPANDSP_USE_FIXED_POINT)
|
|
static const complexi16_t zero = {0, 0};
|
|
#else
|
|
static const complexf_t zero = {0.0f, 0.0f};
|
|
#endif
|
|
int bits;
|
|
|
|
if (s->tx.training)
|
|
{
|
|
/* Send the training sequence */
|
|
return training_get(s);
|
|
}
|
|
|
|
/* There is no graceful shutdown procedure defined for V.22bis. Just
|
|
send some ones, to ensure we get the real data bits through, even
|
|
with bad ISI. */
|
|
if (s->tx.shutdown)
|
|
{
|
|
if (++s->tx.shutdown > 10)
|
|
return zero;
|
|
}
|
|
/* The first two bits define the quadrant */
|
|
bits = get_scrambled_bit(s);
|
|
bits = (bits << 1) | get_scrambled_bit(s);
|
|
s->tx.constellation_state = (s->tx.constellation_state + phase_steps[bits]) & 3;
|
|
if (s->negotiated_bit_rate == 1200)
|
|
{
|
|
bits = 0x01;
|
|
}
|
|
else
|
|
{
|
|
/* The other two bits define the position within the quadrant */
|
|
bits = get_scrambled_bit(s);
|
|
bits = (bits << 1) | get_scrambled_bit(s);
|
|
}
|
|
return v22bis_constellation[(s->tx.constellation_state << 2) | bits];
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE_NONSTD(int) v22bis_tx(v22bis_state_t *s, int16_t amp[], int len)
|
|
{
|
|
#if defined(SPANDSP_USE_FIXED_POINT)
|
|
complexi16_t v;
|
|
complexi32_t x;
|
|
complexi32_t z;
|
|
int16_t iamp;
|
|
#else
|
|
complexf_t v;
|
|
complexf_t x;
|
|
complexf_t z;
|
|
float famp;
|
|
#endif
|
|
int sample;
|
|
|
|
if (s->tx.shutdown > 10)
|
|
return 0;
|
|
for (sample = 0; sample < len; sample++)
|
|
{
|
|
if ((s->tx.baud_phase += 3) >= 40)
|
|
{
|
|
s->tx.baud_phase -= 40;
|
|
v = getbaud(s);
|
|
s->tx.rrc_filter_re[s->tx.rrc_filter_step] = v.re;
|
|
s->tx.rrc_filter_im[s->tx.rrc_filter_step] = v.im;
|
|
if (++s->tx.rrc_filter_step >= V22BIS_TX_FILTER_STEPS)
|
|
s->tx.rrc_filter_step = 0;
|
|
}
|
|
#if defined(SPANDSP_USE_FIXED_POINT)
|
|
/* Root raised cosine pulse shaping at baseband */
|
|
x.re = vec_circular_dot_prodi16(s->tx.rrc_filter_re, tx_pulseshaper[TX_PULSESHAPER_COEFF_SETS - 1 - s->tx.baud_phase], V22BIS_TX_FILTER_STEPS, s->tx.rrc_filter_step) >> 14;
|
|
x.im = vec_circular_dot_prodi16(s->tx.rrc_filter_im, tx_pulseshaper[TX_PULSESHAPER_COEFF_SETS - 1 - s->tx.baud_phase], V22BIS_TX_FILTER_STEPS, s->tx.rrc_filter_step) >> 14;
|
|
/* Now create and modulate the carrier */
|
|
z = dds_complexi32(&s->tx.carrier_phase, s->tx.carrier_phase_rate);
|
|
iamp = (x.re*z.re - x.im*z.im) >> 15;
|
|
iamp = (int16_t) (((int32_t) iamp*s->tx.gain) >> 11);
|
|
if (s->tx.guard_phase_rate && (s->tx.rrc_filter_re[s->tx.rrc_filter_step] != 0 || s->tx.rrc_filter_im[s->tx.rrc_filter_step] != 0))
|
|
{
|
|
/* Add the guard tone */
|
|
iamp += dds_mod(&s->tx.guard_phase, s->tx.guard_phase_rate, s->tx.guard_tone_gain, 0);
|
|
}
|
|
/* Don't bother saturating. We should never clip. */
|
|
amp[sample] = iamp;
|
|
#else
|
|
/* Root raised cosine pulse shaping at baseband */
|
|
x.re = vec_circular_dot_prodf(s->tx.rrc_filter_re, tx_pulseshaper[TX_PULSESHAPER_COEFF_SETS - 1 - s->tx.baud_phase], V22BIS_TX_FILTER_STEPS, s->tx.rrc_filter_step);
|
|
x.im = vec_circular_dot_prodf(s->tx.rrc_filter_im, tx_pulseshaper[TX_PULSESHAPER_COEFF_SETS - 1 - s->tx.baud_phase], V22BIS_TX_FILTER_STEPS, s->tx.rrc_filter_step);
|
|
/* Now create and modulate the carrier */
|
|
z = dds_complexf(&s->tx.carrier_phase, s->tx.carrier_phase_rate);
|
|
famp = (x.re*z.re - x.im*z.im)*s->tx.gain;
|
|
if (s->tx.guard_phase_rate && (s->tx.rrc_filter_re[s->tx.rrc_filter_step] != 0.0f || s->tx.rrc_filter_im[s->tx.rrc_filter_step] != 0.0f))
|
|
{
|
|
/* Add the guard tone */
|
|
famp += dds_modf(&s->tx.guard_phase, s->tx.guard_phase_rate, s->tx.guard_tone_gain, 0);
|
|
}
|
|
/* Don't bother saturating. We should never clip. */
|
|
amp[sample] = (int16_t) lfastrintf(famp);
|
|
#endif
|
|
}
|
|
return sample;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(void) v22bis_tx_power(v22bis_state_t *s, float power)
|
|
{
|
|
float sig_power;
|
|
float guard_tone_power;
|
|
float sig_gain;
|
|
float guard_tone_gain;
|
|
|
|
/* If is there is a guard tone we need to scale down the signal power a bit, so the aggregate of the signal
|
|
and guard tone power is the specified power. */
|
|
if (s->tx.guard_phase_rate == dds_phase_ratef(550.0f))
|
|
{
|
|
sig_power = power - 1.0f;
|
|
guard_tone_power = sig_power - 3.0f;
|
|
}
|
|
else if(s->tx.guard_phase_rate == dds_phase_ratef(1800.0f))
|
|
{
|
|
sig_power = power - 0.55f;
|
|
guard_tone_power = sig_power - 6.0f;
|
|
}
|
|
else
|
|
{
|
|
sig_power = power;
|
|
guard_tone_power = -9999.0f;
|
|
}
|
|
sig_gain = 0.4490f*powf(10.0f, (sig_power - DBM0_MAX_POWER)/20.0f)*32768.0f/TX_PULSESHAPER_GAIN;
|
|
guard_tone_gain = powf(10.0f, (guard_tone_power - DBM0_MAX_POWER)/20.0f)*32768.0f;
|
|
#if defined(SPANDSP_USE_FIXED_POINT)
|
|
s->tx.gain = (int16_t) sig_gain;
|
|
s->tx.guard_tone_gain = (int16_t) guard_tone_gain;
|
|
#else
|
|
s->tx.gain = sig_gain;
|
|
s->tx.guard_tone_gain = guard_tone_gain;
|
|
#endif
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
static int v22bis_tx_restart(v22bis_state_t *s)
|
|
{
|
|
#if defined(SPANDSP_USE_FIXED_POINT)
|
|
vec_zeroi16(s->tx.rrc_filter_re, sizeof(s->tx.rrc_filter_re)/sizeof(s->tx.rrc_filter_re[0]));
|
|
vec_zeroi16(s->tx.rrc_filter_im, sizeof(s->tx.rrc_filter_im)/sizeof(s->tx.rrc_filter_im[0]));
|
|
#else
|
|
vec_zerof(s->tx.rrc_filter_re, sizeof(s->tx.rrc_filter_re)/sizeof(s->tx.rrc_filter_re[0]));
|
|
vec_zerof(s->tx.rrc_filter_im, sizeof(s->tx.rrc_filter_im)/sizeof(s->tx.rrc_filter_im[0]));
|
|
#endif
|
|
s->tx.rrc_filter_step = 0;
|
|
s->tx.scramble_reg = 0;
|
|
s->tx.scrambler_pattern_count = 0;
|
|
if (s->calling_party)
|
|
s->tx.training = V22BIS_TX_TRAINING_STAGE_INITIAL_SILENCE;
|
|
else
|
|
s->tx.training = V22BIS_TX_TRAINING_STAGE_INITIAL_TIMED_SILENCE;
|
|
s->tx.training_count = 0;
|
|
s->tx.carrier_phase = 0;
|
|
s->tx.guard_phase = 0;
|
|
s->tx.baud_phase = 0;
|
|
s->tx.constellation_state = 0;
|
|
s->tx.current_get_bit = fake_get_bit;
|
|
s->tx.shutdown = 0;
|
|
return 0;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(void) v22bis_set_get_bit(v22bis_state_t *s, get_bit_func_t get_bit, void *user_data)
|
|
{
|
|
s->get_bit = get_bit;
|
|
s->get_bit_user_data = user_data;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(void) v22bis_set_put_bit(v22bis_state_t *s, put_bit_func_t put_bit, void *user_data)
|
|
{
|
|
s->put_bit = put_bit;
|
|
s->put_bit_user_data = user_data;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(void) v22bis_set_modem_status_handler(v22bis_state_t *s, modem_status_func_t handler, void *user_data)
|
|
{
|
|
s->status_handler = handler;
|
|
s->status_user_data = user_data;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(logging_state_t *) v22bis_get_logging_state(v22bis_state_t *s)
|
|
{
|
|
return &s->logging;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(int) v22bis_restart(v22bis_state_t *s, int bit_rate)
|
|
{
|
|
switch (bit_rate)
|
|
{
|
|
case 2400:
|
|
case 1200:
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
s->bit_rate = bit_rate;
|
|
s->negotiated_bit_rate = 1200;
|
|
if (v22bis_tx_restart(s))
|
|
return -1;
|
|
return v22bis_rx_restart(s);
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(int) v22bis_request_retrain(v22bis_state_t *s, int bit_rate)
|
|
{
|
|
/* TODO: support bit rate switching */
|
|
switch (bit_rate)
|
|
{
|
|
case 2400:
|
|
case 1200:
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
/* 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, bool enable)
|
|
{
|
|
/* TODO: */
|
|
return -1;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(int) v22bis_get_current_bit_rate(v22bis_state_t *s)
|
|
{
|
|
return s->negotiated_bit_rate;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(v22bis_state_t *) v22bis_init(v22bis_state_t *s,
|
|
int bit_rate,
|
|
int guard,
|
|
bool calling_party,
|
|
get_bit_func_t get_bit,
|
|
void *get_bit_user_data,
|
|
put_bit_func_t put_bit,
|
|
void *put_bit_user_data)
|
|
{
|
|
switch (bit_rate)
|
|
{
|
|
case 2400:
|
|
case 1200:
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
if (s == NULL)
|
|
{
|
|
if ((s = (v22bis_state_t *) span_alloc(sizeof(*s))) == NULL)
|
|
return NULL;
|
|
}
|
|
memset(s, 0, sizeof(*s));
|
|
span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
|
|
span_log_set_protocol(&s->logging, "V.22bis");
|
|
s->bit_rate = bit_rate;
|
|
s->calling_party = calling_party;
|
|
|
|
s->get_bit = get_bit;
|
|
s->get_bit_user_data = get_bit_user_data;
|
|
s->put_bit = put_bit;
|
|
s->put_bit_user_data = put_bit_user_data;
|
|
|
|
if (s->calling_party)
|
|
{
|
|
s->tx.carrier_phase_rate = dds_phase_ratef(1200.0f);
|
|
}
|
|
else
|
|
{
|
|
s->tx.carrier_phase_rate = dds_phase_ratef(2400.0f);
|
|
switch (guard)
|
|
{
|
|
case V22BIS_GUARD_TONE_550HZ:
|
|
s->tx.guard_phase_rate = dds_phase_ratef(550.0f);
|
|
break;
|
|
case V22BIS_GUARD_TONE_1800HZ:
|
|
s->tx.guard_phase_rate = dds_phase_ratef(1800.0f);
|
|
break;
|
|
default:
|
|
s->tx.guard_phase_rate = 0;
|
|
break;
|
|
}
|
|
}
|
|
v22bis_tx_power(s, -14.0f);
|
|
v22bis_restart(s, s->bit_rate);
|
|
return s;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(int) v22bis_release(v22bis_state_t *s)
|
|
{
|
|
return 0;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
|
|
SPAN_DECLARE(int) v22bis_free(v22bis_state_t *s)
|
|
{
|
|
span_free(s);
|
|
return 0;
|
|
}
|
|
/*- End of function --------------------------------------------------------*/
|
|
/*- End of file ------------------------------------------------------------*/
|