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 ------------------------------------------------------------*/