v17rx.h

Go to the documentation of this file.
00001 /*
00002  * SpanDSP - a series of DSP components for telephony
00003  *
00004  * v17rx.h - ITU V.17 modem receive part
00005  *
00006  * Written by Steve Underwood <steveu@coppice.org>
00007  *
00008  * Copyright (C) 2003 Steve Underwood
00009  *
00010  * All rights reserved.
00011  *
00012  * This program is free software; you can redistribute it and/or modify
00013  * it under the terms of the GNU General Public License as published by
00014  * the Free Software Foundation; either version 2 of the License, or
00015  * (at your option) any later version.
00016  *
00017  * This program is distributed in the hope that it will be useful,
00018  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00019  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020  * GNU General Public License for more details.
00021  *
00022  * You should have received a copy of the GNU General Public License
00023  * along with this program; if not, write to the Free Software
00024  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00025  *
00026  * $Id: v17rx.h,v 1.22 2005/12/06 14:34:03 steveu Exp $
00027  */
00028 
00029 /*! \file */
00030 
00031 #if !defined(_V17RX_H_)
00032 #define _V17RX_H_
00033 
00034 /*! \page v17rx_page The V.17 receiver
00035 \section v17rx_page_sec_1 What does it do?
00036 The V.17 receiver implements the receive side of a V.17 modem. This can operate
00037 at data rates of 14400, 12000, 9600 and 7200 bits/second. The audio input is a stream
00038 of 16 bit samples, at 8000 samples/second. The transmit and receive side of V.17
00039 modems operate independantly. V.17 is mostly used for FAX transmission over PSTN
00040 lines, where it provides the standard 14400 bits/second rate. 
00041 
00042 \section v17rx_page_sec_2 How does it work?
00043 V.17 uses QAM modulation, and trellis coding. It specifies a training sequence at
00044 the start of transmission, which makes the design of a V.17 receiver relatively
00045 straightforward. The first stage of the training sequence consists of 256
00046 symbols, alternating between two constellation positions. The receiver monitors
00047 the signal power, to sense the possible presence of a valid carrier. When the
00048 alternating signal begins, the power rising above a minimum threshold (-26dBm0)
00049 causes the main receiver computation to begin. The initial measured power is
00050 used to quickly set the gain of the receiver. After this initial setting, the
00051 front end gain is locked, and the adaptive equalizer tracks any subsequent
00052 signal level variation. The signal is multiplied by a complex carrier, generated
00053 by a DDS, at 8000 samples/second. It is then fed at 24000 samples/second (i.e.
00054 signal, zero, zero, signal, zero, zero, ...) to a root raised cosine pulse
00055 shaping filter. This interpolates the samples, and pulse shapes at the same
00056 time. Every fifth sample is taken from the output of the filter, and fed to an
00057 adaptive equalizer. This means the adaptive equalizer receives samples at 4800
00058 samples/second, so it is a T/2 equalizer. The Gardner algorithm is used to tune
00059 the sampling, so the samples fed to the equalizer are close to the mid point and
00060 edges of each symbol. Initially the algorithm is very lightly damped, to ensure
00061 the symbol alignment pulls in quickly. Because the sampling rate will not be
00062 precisely the same as the transmitter's (the spec. says the symbol timing should
00063 be within 0.01%), the receiver constantly evaluates and corrects this sampling
00064 throughout its operation. During the symbol timing maintainence phase, the
00065 algorithm uses a heavily damped Gardner plus integrate and dump approach to
00066 updates. This heavy damping achieves several things - the Gardner algorithm is
00067 statistically based, so the statistics must be smoothed; a number of samples
00068 must be fed to the equalizer buffer before the equalizer output actually
00069 responds to a step change in the sampling; we need to prevent rapid fluctuations
00070 in the sampling position, due to the optimal position being close to a boundary.
00071 
00072 The carrier is specified as 1800Hz +- 1Hz at the transmitter, and 1800 +-7Hz at
00073 the receiver. The receive carrier would only be this inaccurate if the link
00074 includes FDM sections. These are being phased out, but the design must still
00075 allow for the worst case. Using an initial 1800Hz signal for demodulation gives
00076 a worst case rotation rate for the constellation of about one degree per symbol.
00077 Once the Gardner algorithm has been given time to lock to the symbol timing of
00078 the initial alternating pattern, the phase of the demodulated signal is recorded
00079 on two successive symbols - once for each of the constellation positions. The
00080 receiver then tracks the symbol alternations, until a large phase jump occurs.
00081 This signifies the start of the next phase of the training sequence. At this
00082 point the total phase shift between the original recorded symbol phase, and the
00083 symbol phase just before the phase jump occurred is used to provide a coarse
00084 estimation of the rotation rate of the constellation, and it current absolute
00085 angle of rotation. These are used to update the current carrier phase and phase
00086 update rate in the carrier DDS. The working data already in the pulse shaping
00087 filter and equalizer buffers is given a similar step rotation to pull it all
00088 into line. From this point on, a heavily damped integrate and dump approach,
00089 based on the angular difference between each received constellation position and
00090 its expected position, is sufficient to track the carrier, and maintain phase
00091 alignment. A fast rough approximator for the arc-tangent function is adequate
00092 for the estimation of the angular error. 
00093 
00094 The next phase of the training sequence is a scrambled sequence of two
00095 particular symbols. We train the T/2 adaptive equalizer using this sequence. The
00096 scrambling makes the signal sufficiently diverse to ensure the equalizer
00097 converges to the proper generalised solution. At the end of this sequence, the
00098 equalizer should be sufficiently well adapted that is can correctly resolve the
00099 full QAM constellation. However, the equalizer continues to adapt throughout
00100 operation of the modem, fine tuning on the more complex data patterns of the
00101 full QAM constellation. 
00102 
00103 In the last phase of the training sequence, the modem enters normal data
00104 operation, with a short defined period of all ones as data. As in most high
00105 speed modems, data in a V.17 modem passes through a scrambler, to whiten the
00106 spectrum of the signal. The transmitter should initialise its data scrambler,
00107 and pass the ones through it. At the end of the ones, real data begins to pass
00108 through the scrambler, and the transmit modem is in normal operation. The
00109 receiver tests that ones are really received, in order to verify the modem
00110 trained correctly. If all is well, the data following the ones is fed to the
00111 application, and the receive modem is up and running. Unfortunately, some
00112 transmit side of some real V.17 modems fail to initialise their scrambler before
00113 sending the ones. This means the first 23 received bits (the length of the
00114 scrambler register) cannot be trusted for the test. The receive modem,
00115 therefore, only tests that bits starting at bit 24 are really ones.
00116 
00117 The V.17 signal is trellis coded. Two bits of each symbol are convolutionally coded
00118 to form a 3 bit trellis code - the two original bits, plus an extra redundant bit. It
00119 is possible to ignore the trellis coding, and just decode the non-redundant bits.
00120 However, the noise performance of the receiver would suffer. Using a proper
00121 trellis decoder adds several dB to the noise tolerance to the receiving modem. Trellis
00122 coding seems quite complex at first sight, but is fairly straightforward once you
00123 get to grips with it.
00124 
00125 Trellis decoding tracks the data in terms of the possible states of the convolutional
00126 coder at the transmitter. There are 8 possible states of the V.17 coder. The first
00127 step in trellis decoding is to find the best candidate constellation point
00128 for each of these 8 states. One of thse will be our final answer. The constellation
00129 has been designed so groups of 8 are spread fairly evenly across it. Locating them
00130 is achieved is a reasonably fast manner, by looking up the answers in a set of space
00131 map tables. The disadvantage is the tables are potentially large enough to affect
00132 cache performance. The trellis decoder works over 16 successive symbols. The result
00133 of decoding is not known until 16 symbols after the data enters the decoder. The
00134 minimum total accumulated mismatch between each received point and the actual
00135 constellation (termed the distance) is assessed for each of the 8 states. A little
00136 analysis of the coder shows that each of the 8 current states could be arrived at
00137 from 4 different previous states, through 4 different constellation bit patterns.
00138 For each new state, the running total distance is arrived at by inspecting a previous
00139 total plus a new distance for the appropriate 4 previous states. The minimum of the 4
00140 values becomes the new distance for the state. Clearly, a mechanism is needed to stop
00141 this distance from growing indefinitely. A sliding window, and several other schemes
00142 are possible. However, a simple single pole IIR is very simple, and provides adequate
00143 results.
00144 
00145 For each new state we store the constellation bit pattern, or path, to that state, and
00146 the number of the previous state. We find the minimum distance amongst the 8 new
00147 states for each new symbol. We then trace back through the states, until we reach the
00148 one 16 states ago which leads to the current minimum distance. The bit pattern stored
00149 there is the error corrected bit pattern for that symbol.
00150 
00151 So, what does Trellis coding actually achieve? TCM is easier to understand by looking
00152 at the V.23bis modem spec. The V.32bis spec. is very similar to V.17, except that it
00153 is a full duplex modem and has non-TCM options, as well as the TCM ones in V.17.
00154 
00155 V32bis defines two options for pumping 9600 bits per second down a phone line - one
00156 with and one without TCM. Both run at 2400 baud. The non-TCM one uses simple 16 point
00157 QAM on the raw data. The other takes two out of every four raw bits, and convolutionally
00158 encodes them to 3. Now we have 5 bits per symbol, and we need 32 point QAM to send the
00159 data.
00160 
00161 The raw error rate from simple decoding of the 32 point QAM is horrible compared to
00162 decoding the 16 point QAM. If a point decoded from the 32 point QAM is wrong, the likely
00163 correct choice should be one of the adjacent ones. It is unlikely to have been one that
00164 is far away across the constellation, unless there was a huge noise spike, interference,
00165 or something equally nasty. Now, the 32 point symbols do not exist in isolation. There
00166 was a kind of temporal smearing in the convolutional coding. It created a well defined
00167 dependency between successive symbols. If we knew for sure what the last few symbols
00168 were, they would lead us to a limited group of possible values for the current symbol,
00169 constrained by the behaviour of the convolutional coder. If you look at how the symbols
00170 were mapped to constellation points, you will see the mapping tries to spread those
00171 possible symbols as far apart as possible. This will leave only one that is pretty
00172 close to the received point, which must be the correct choice. However, this assumes
00173 we know the last few symbols for sure. Since we don't, we have a bit more work to do
00174 to achieve reliable decoding.
00175 
00176 Instead of decoding to the nearest point on the constellation, we decode to a group of
00177 likely constellation points in the neighbourhood of the received point. We record the
00178 mismatch for each - that is the distance across the constellation between the received
00179 point and the group of nearby points. To avoid square roots, recording x2 + y2 can be
00180 good enough. Symbol by symbol, we record this information. After a few symbols we can
00181 stand back and look at the recorded information.
00182 
00183 For each symbol we have a set of possible symbol values and error metric pairs. The
00184 dependency between symbols, created by the convolutional coder, means some paths from
00185 symbol to symbol are possible and some are not. It we trace back through the possible
00186 symbol to symbol paths, and total up the error metric through those paths, we end up
00187 with a set of figures of merit (or more accurately figures of demerit, since
00188 larger == worse) for the likelihood of each path being the correct one. The path with
00189 the lowest total metric is the most likely, and gives us our final choice for what we
00190 think the current symbol really is.
00191 
00192 That was hard work. It takes considerable computation to do this selection and traceback,
00193 symbol by symbol. We need to get quite a lot from this. It needs to drive the error rate
00194 down so far that is compensates for the much higher error rate due to the larger
00195 constellation, and then buys us some actual benefit. Well in the example we are looking
00196 at - V.32bis at 9600bps - it works out the error rate from the TCM option is like using
00197 the non-TCM option with several dB more signal to noise ratio. That's nice. The non-TCM
00198 option is pretty reasonable on most phone lines, but a better error rate is always a
00199 good thing. However, V32bis includes a 14,400bps option. That uses 2400 baud, and 6 bit
00200 symbols. Convolutional encoding increases that to 7 bits per symbol, by taking 2 bits and
00201 encoding them to 3. This give a 128 point QAM constellation. Again, the difference between
00202 using this, and using just an uncoded 64 point constellation is equivalent to maybe 5dB of
00203 extra signal to noise ratio. However, in this case it is the difference between the modem
00204 working only on the most optimal lines, and being widely usable across most phone lines.
00205 TCM absolutely transformed the phone line modem business.
00206 */
00207 
00208 #define V17_EQUALIZER_LEN   7  /* this much to the left and this much to the right */
00209 #define V17_EQUALIZER_MASK  15 /* one less than a power of 2 >= (2*V17_EQUALIZER_LEN + 1) */
00210 
00211 #define V17RX_FILTER_STEPS  27
00212 
00213 #define V17_TRELLIS_DEPTH   16
00214 
00215 /*!
00216     V.17 modem receive side descriptor. This defines the working state for a
00217     single instance of a V.17 modem receiver.
00218 */
00219 typedef struct
00220 {
00221     /*! \brief The bit rate of the modem. Valid values are 4800, 7200 and 9600. */
00222     int bit_rate;
00223     /*! \brief The callback function used to put each bit received. */
00224     put_bit_func_t put_bit;
00225     /*! \brief A user specified opaque pointer passed to the put_but routine. */
00226     void *user_data;
00227     /*! \brief A callback function which may be enabled to report every symbol's
00228                constellation position. */
00229     qam_report_handler_t *qam_report;
00230     /*! \brief A user specified opaque pointer passed to the qam_report callback
00231                routine. */
00232     void *qam_user_data;
00233 
00234     /*! \brief The route raised cosine (RRC) pulse shaping filter buffer. */
00235     complex_t rrc_filter[2*V17RX_FILTER_STEPS];
00236     /*! \brief Current offset into the RRC pulse shaping filter buffer. */
00237     int rrc_filter_step;
00238 
00239     /*! \brief The state of the differential decoder */
00240     int diff;
00241     /*! \brief The register for the data scrambler. */
00242     unsigned int scramble_reg;
00243     /*! \brief TRUE if the short training sequence is to be used. */
00244     int short_train;
00245     int in_training;
00246     int training_count;
00247     float training_error;
00248     int carrier_present;
00249 
00250     /*! \brief The current phase of the carrier (i.e. the DDS parameter). */
00251     uint32_t carrier_phase;
00252     /*! \brief The update rate for the phase of the carrier (i.e. the DDS increment). */
00253     int32_t carrier_phase_rate;
00254     /*! \brief The carrier update rate saved for reuse when using short training. */
00255     int32_t carrier_phase_rate_save;
00256     float carrier_track_p;
00257     float carrier_track_i;
00258 
00259     /*! \brief The received signal power monitor. */
00260     power_meter_t power;
00261     int32_t carrier_on_power;
00262     int32_t carrier_off_power;
00263     float agc_scaling;
00264     float agc_scaling_save;
00265 
00266     float eq_delta;
00267     /*! \brief The adaptive equalizer coefficients */
00268     complex_t eq_coeff_save[2*V17_EQUALIZER_LEN + 1];
00269     complex_t eq_coeff[2*V17_EQUALIZER_LEN + 1];
00270     complex_t eq_buf[V17_EQUALIZER_MASK + 1];
00271     /*! \brief Current offset into equalizer buffer. */
00272     int eq_step;
00273     int eq_put_step;
00274 
00275     /*! \brief Integration variable for damping the Gardner algorithm tests. */
00276     int gardner_integrate;
00277     /*! \brief Current step size of Gardner algorithm integration. */
00278     int gardner_step;
00279     /*! \brief The total gardner timing correction, since the carrier came up.
00280                This is only for performance analysis purposes. */
00281     int gardner_total_correction;
00282     /*! \brief The current fractional phase of the baud timing. */
00283     int baud_phase;
00284 
00285     /*! \brief Starting phase angles for the coarse carrier aquisition step. */
00286     int32_t start_angles[2];
00287     /*! \brief History list of phase angles for the coarse carrier aquisition step. */
00288     int32_t angles[16];
00289     /*! \brief A pointer to the current constellation. */
00290     const complex_t *constellation;
00291     /*! \brief A pointer to the current space map. There is a space map for
00292                each trellis state. */
00293     int space_map;
00294     /*! \brief The number of bits in each symbol at the current bit rate. */
00295     int bits_per_symbol;
00296 
00297     /*! \brief Current pointer to the trellis buffers */
00298     int trellis_ptr;
00299     /*! \brief The trellis. */
00300     int full_path_to_past_state_locations[V17_TRELLIS_DEPTH][8];
00301     /*! \brief The trellis. */
00302     int past_state_locations[V17_TRELLIS_DEPTH][8];
00303     /*! \brief Euclidean distances (actually the sqaures of the distances)
00304                from the last states of the trellis. */
00305     float distances[8];
00306     /*! \brief Error and flow logging control */
00307     logging_state_t logging;
00308 } v17_rx_state_t;
00309 
00310 extern const complex_t v17_14400_constellation[128];
00311 extern const complex_t v17_12000_constellation[64];
00312 extern const complex_t v17_9600_constellation[32];
00313 extern const complex_t v17_7200_constellation[16];
00314 
00315 #ifdef __cplusplus
00316 extern "C" {
00317 #endif
00318 
00319 /*! Initialise a V.17 modem receive context.
00320     \brief Initialise a V.17 modem receive context.
00321     \param s The modem context.
00322     \param rate The bit rate of the modem. Valid values are 7200, 9600, 12000 and 14400.
00323     \param put_bit The callback routine used to put the received data.
00324     \param user_data An opaque pointer passed to the put_bit routine.
00325     \return A pointer to the modem context, or NULL if there was a problem. */
00326 v17_rx_state_t *v17_rx_init(v17_rx_state_t *s, int rate, put_bit_func_t put_bit, void *user_data);
00327 
00328 /*! Reinitialise an existing V.17 modem receive context.
00329     \brief Reinitialise an existing V.17 modem receive context.
00330     \param s The modem context.
00331     \param rate The bit rate of the modem. Valid values are 7200, 9600, 12000 and 14400.
00332     \param short_train TRUE if a short training sequence is expected.
00333     \return 0 for OK, -1 for bad parameter */
00334 int v17_rx_restart(v17_rx_state_t *s, int rate, int short_train);
00335 
00336 /*! Release a V.17 modem receive context.
00337     \brief Release a V.17 modem receive context.
00338     \param s The modem context.
00339     \return 0 for OK */
00340 int v17_rx_release(v17_rx_state_t *s);
00341 
00342 /*! Change the put_bit function associated with a V.17 modem receive context.
00343     \brief Change the put_bit function associated with a V.17 modem receive context.
00344     \param s The modem context.
00345     \param put_bit The callback routine used to handle received bits.
00346     \param user_data An opaque pointer. */
00347 void v17_rx_set_put_bit(v17_rx_state_t *s, put_bit_func_t put_bit, void *user_data);
00348 
00349 /*! Process a block of received V.17 modem audio samples.
00350     \brief Process a block of received V.17 modem audio samples.
00351     \param s The modem context.
00352     \param amp The audio sample buffer.
00353     \param len The number of samples in the buffer.
00354 */
00355 void v17_rx(v17_rx_state_t *s, const int16_t *amp, int len);
00356 
00357 /*! Get a snapshot of the current equalizer coefficients.
00358     \brief Get a snapshot of the current equalizer coefficients.
00359     \param s The modem context.
00360     \param coeffs The vector of complex coefficients.
00361     \return The number of coefficients in the vector. */
00362 int v17_rx_equalizer_state(v17_rx_state_t *s, complex_t **coeffs);
00363 
00364 /*! Get the current received carrier frequency.
00365     \param s The modem context.
00366     \return The frequency, in Hertz. */
00367 float v17_rx_carrier_frequency(v17_rx_state_t *s);
00368 
00369 /*! Get the current symbol timing correction since startup.
00370     \param s The modem context.
00371     \return The correction. */
00372 float v17_rx_symbol_timing_correction(v17_rx_state_t *s);
00373 
00374 /*! Get a current received signal power.
00375     \param s The modem context.
00376     \return The signal power, in dBm0. */
00377 float v17_rx_signal_power(v17_rx_state_t *s);
00378 
00379 /*! Set the power level at which the carrier detection will cut in
00380     \param s The modem context.
00381     \param cutoff The signal cutoff power, in dBm0. */
00382 void v17_rx_signal_cutoff(v17_rx_state_t *s, float cutoff);
00383 
00384 /*! Set a handler routine to process QAM status reports
00385     \param s The modem context.
00386     \param handler The handler routine.
00387     \param user_data An opaque pointer passed to the handler routine. */
00388 void v17_rx_set_qam_report_handler(v17_rx_state_t *s, qam_report_handler_t *handler, void *user_data);
00389 
00390 #ifdef __cplusplus
00391 }
00392 #endif
00393 
00394 #endif
00395 /*- End of file ------------------------------------------------------------*/

Generated on Fri Nov 10 09:40:24 2006 for libspandsp by  doxygen 1.5.1