| 1 | /**************************************************************************** |
|---|
| 2 | |
|---|
| 3 | Copyright 2005,2006 Virginia Polytechnic Institute and State University |
|---|
| 4 | |
|---|
| 5 | This file is part of the OSSIE Signal Processing Library. |
|---|
| 6 | |
|---|
| 7 | OSSIE Signal Processing Library is free software; you can redistribute it |
|---|
| 8 | and/or modify it under the terms of the GNU General Public License as |
|---|
| 9 | published by the Free Software Foundation; either version 2 of the License, |
|---|
| 10 | or (at your option) any later version. |
|---|
| 11 | |
|---|
| 12 | OSSIE Signal Processing Library is distributed in the hope that it will be |
|---|
| 13 | useful, but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 15 | GNU General Public License for more details. |
|---|
| 16 | |
|---|
| 17 | You should have received a copy of the GNU General Public License along |
|---|
| 18 | with OSSIE Signal Processing Library; if not, write to the Free Software |
|---|
| 19 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|---|
| 20 | |
|---|
| 21 | |
|---|
| 22 | ****************************************************************************/ |
|---|
| 23 | |
|---|
| 24 | #ifndef SIG_PROC_H |
|---|
| 25 | #define SIG_PROC_H |
|---|
| 26 | |
|---|
| 27 | #include <iostream> |
|---|
| 28 | #include <fstream> |
|---|
| 29 | #include <string> |
|---|
| 30 | #include <math.h> |
|---|
| 31 | |
|---|
| 32 | #ifdef FPM |
|---|
| 33 | #include "fixed.h" |
|---|
| 34 | #endif |
|---|
| 35 | |
|---|
| 36 | #define PI 3.14159265358979f |
|---|
| 37 | #define TWO_PI 6.28318530717959f |
|---|
| 38 | |
|---|
| 39 | namespace SigProc { |
|---|
| 40 | //----------------------------------------------------------------------------- |
|---|
| 41 | // |
|---|
| 42 | // Convolutional coding definitions |
|---|
| 43 | // |
|---|
| 44 | //----------------------------------------------------------------------------- |
|---|
| 45 | |
|---|
| 46 | /** \brief Defines the basic entry for building the trellis at the decoder |
|---|
| 47 | */ |
|---|
| 48 | class TrellisEntry{ |
|---|
| 49 | public: |
|---|
| 50 | ///Contructor |
|---|
| 51 | TrellisEntry(); |
|---|
| 52 | ///Destructor |
|---|
| 53 | ~TrellisEntry(); |
|---|
| 54 | unsigned int previousState; ///< The state prior to the current state, used for tracing back the trellis |
|---|
| 55 | unsigned short int symbolNo; ///< The symbol that caused the change of state to the currrent state |
|---|
| 56 | signed int distance; ///< The total distance of the current state |
|---|
| 57 | }; |
|---|
| 58 | |
|---|
| 59 | /** \brief Includes all the necessary information for encoding and decoding the symbols |
|---|
| 60 | * |
|---|
| 61 | * The contructor has to be ALWAYS be used. The generator polynomials for each code rate have to be specified in DECIMAL, not OCTAL format |
|---|
| 62 | * |
|---|
| 63 | * For more information for the meaning of the symbols used and for generator polynomials look at |
|---|
| 64 | * Proakis Digital Communications book 4th Edition, pages 471 & 492 respectively. |
|---|
| 65 | */ |
|---|
| 66 | class trellisTable{ |
|---|
| 67 | public: |
|---|
| 68 | /// The contructor has to be always used |
|---|
| 69 | trellisTable(unsigned int * generatorPolynomials, unsigned short int k, unsigned short int n, unsigned short K); |
|---|
| 70 | |
|---|
| 71 | /// =The destructor |
|---|
| 72 | ~trellisTable(); |
|---|
| 73 | |
|---|
| 74 | unsigned short int k;///<the input bits at a time |
|---|
| 75 | unsigned short int n;///< the output bits at a time |
|---|
| 76 | unsigned short int K;///<the contraint length |
|---|
| 77 | unsigned short int numberOfInputStates;///< Based on k, how many different input states exist |
|---|
| 78 | unsigned int numberOfTrellisStates;///<Number of different trellis states, defined by k & K |
|---|
| 79 | |
|---|
| 80 | unsigned int **output; ///< The rows are the trellis states and the columns the symbols, gives the output given the current state and symbol. |
|---|
| 81 | unsigned int **nextState;///< The sane as output, gives the the next state given the current state and synbol |
|---|
| 82 | |
|---|
| 83 | protected: |
|---|
| 84 | ///Generates the trellis table given the generator polynomials in DECIMAL not OCTAL |
|---|
| 85 | void GenerateTrellisTable(unsigned int * generatorPolynomials); |
|---|
| 86 | |
|---|
| 87 | ///Performs modulo 2 addition to the bits of the input number (symbol) |
|---|
| 88 | unsigned short int Modulo2BitWiseAdd(unsigned short int inputNumber); |
|---|
| 89 | }; |
|---|
| 90 | |
|---|
| 91 | /** \brief Defines the basic functionality for the convolution encoding/decoding |
|---|
| 92 | */ |
|---|
| 93 | |
|---|
| 94 | class fec_conv{ |
|---|
| 95 | public: |
|---|
| 96 | /// It accepts the pointer to the trellis Table |
|---|
| 97 | void SetTrellisTable(trellisTable *theTrellisTableIn); |
|---|
| 98 | |
|---|
| 99 | protected: |
|---|
| 100 | /// It converts a decimal numnber (symbol) into a vector of bits |
|---|
| 101 | void Dec2Bin(unsigned int decNumber,unsigned short int * outputData,unsigned short int numberOfBits); |
|---|
| 102 | |
|---|
| 103 | trellisTable * theTrellisTable;///< It holds the trellis table used for encoding/decoding |
|---|
| 104 | }; |
|---|
| 105 | |
|---|
| 106 | /** \brief It defines the encoding functionality |
|---|
| 107 | * |
|---|
| 108 | * To use this class, first pass the trellis table pointer |
|---|
| 109 | * |
|---|
| 110 | * To encode: reset the state, feed data & get encoded data, repeat for the next packet. |
|---|
| 111 | * |
|---|
| 112 | * To make the encoder to return at the zero state during encoding, feed K 0 symbols after the encoding of the data is done. |
|---|
| 113 | */ |
|---|
| 114 | class fec_conv_encoder : public fec_conv{ |
|---|
| 115 | public: |
|---|
| 116 | ///Default constructor |
|---|
| 117 | fec_conv_encoder(); |
|---|
| 118 | |
|---|
| 119 | ///Default destructor |
|---|
| 120 | ~fec_conv_encoder(); |
|---|
| 121 | |
|---|
| 122 | /// Resets the state of the encoder to the zero state |
|---|
| 123 | void ResetState(); |
|---|
| 124 | |
|---|
| 125 | ///It gets the current state of the encoder |
|---|
| 126 | unsigned int GetState(); |
|---|
| 127 | |
|---|
| 128 | ///It accepts a vector of k bits and returns a vector of n bits |
|---|
| 129 | void Encode(unsigned short int * inputData,unsigned short int * outputData); |
|---|
| 130 | |
|---|
| 131 | protected: |
|---|
| 132 | unsigned int currentState;///<The current state of the encoder |
|---|
| 133 | }; |
|---|
| 134 | |
|---|
| 135 | /** \brief It defines the decoding functionality |
|---|
| 136 | * |
|---|
| 137 | * The following cycle should be followed to use this class: |
|---|
| 138 | * |
|---|
| 139 | * 1. Set the trellis table |
|---|
| 140 | * |
|---|
| 141 | * 2. Set the traceback length |
|---|
| 142 | * |
|---|
| 143 | * 3. Set mode |
|---|
| 144 | * |
|---|
| 145 | * 4. Feed symbols into the decoder |
|---|
| 146 | * |
|---|
| 147 | * 5. Trace back trellis |
|---|
| 148 | * |
|---|
| 149 | * 6. Get the decoded symbols |
|---|
| 150 | * |
|---|
| 151 | * 7. Reset and repeat from 4 for the next packet. If the decoding parameters change, might need to repeat from 1. |
|---|
| 152 | */ |
|---|
| 153 | class fec_conv_decoder:public fec_conv { |
|---|
| 154 | public: |
|---|
| 155 | ///Default contructor |
|---|
| 156 | fec_conv_decoder(); |
|---|
| 157 | |
|---|
| 158 | ///Default destructor |
|---|
| 159 | ~fec_conv_decoder(); |
|---|
| 160 | |
|---|
| 161 | /// Set the numbers of the input symbols to be decoded, this equals to the total bits on the output of the encoder divided by k |
|---|
| 162 | void SetNoOfSymbols2TraceBack(unsigned int tracebackLength); |
|---|
| 163 | |
|---|
| 164 | /// Feeds a symbol (in a vector of n bits) at time for decoding |
|---|
| 165 | void Symbol2Decode(unsigned short int * inputData); |
|---|
| 166 | |
|---|
| 167 | /// After all symbols were fed in the decoder, it will tracesback the trellis to the begining. |
|---|
| 168 | void TraceBackTrellis(); |
|---|
| 169 | |
|---|
| 170 | ///After the trellis was traced back, it returns a decoded symbol, starting from the beggining each time is called. |
|---|
| 171 | void GetDecodedSymbol(unsigned short int * outputData); |
|---|
| 172 | |
|---|
| 173 | ///It resets the state of the decoder in order to be able to start a new decoding session |
|---|
| 174 | void Reset(); |
|---|
| 175 | |
|---|
| 176 | ///Sets the mode of the decoder 0: the encoded data start from zero state 1: the encoded data start from the the zero state and end at the zero state. |
|---|
| 177 | void SetMode(unsigned short int mode); |
|---|
| 178 | |
|---|
| 179 | protected: |
|---|
| 180 | ///Calculates the distance between the input bits (symbol) to the current trellis symbol |
|---|
| 181 | signed int CalculateDistance(unsigned short int inBits, unsigned short int symbol); |
|---|
| 182 | |
|---|
| 183 | unsigned int currentTrellisIndex;///<The current trellis stage used when building and tracing back the trellis. |
|---|
| 184 | unsigned int decodedSymbolIndex;///<The next symbol to be returned when the GetDecodedSymbol is called. |
|---|
| 185 | unsigned int noOfSymbols2TraceBack;///<Number of symbols to trace back, should be equal to the symbols used in the encoding process. |
|---|
| 186 | unsigned int mode;///<The mode of the encoder |
|---|
| 187 | |
|---|
| 188 | unsigned int *tracedBackSymbols;///<A vector array with the symbols traced back |
|---|
| 189 | TrellisEntry **theTrellis; ///<A two dimensional array making the trellis, states are rows, and columns are each trellis stage. |
|---|
| 190 | }; |
|---|
| 191 | |
|---|
| 192 | //----------------------------------------------------------------------------- |
|---|
| 193 | // |
|---|
| 194 | // Design root raised-cosine filter |
|---|
| 195 | // |
|---|
| 196 | //----------------------------------------------------------------------------- |
|---|
| 197 | void DesignRRCFilter( |
|---|
| 198 | unsigned int k, // samples per symbol |
|---|
| 199 | unsigned int m, // delay |
|---|
| 200 | float beta, // rolloff factor ( 0 < beta <= 1 ) |
|---|
| 201 | float * h // pointer to filter coefficients |
|---|
| 202 | ); |
|---|
| 203 | |
|---|
| 204 | //----------------------------------------------------------------------------- |
|---|
| 205 | // |
|---|
| 206 | // Design Gaussian filter |
|---|
| 207 | // |
|---|
| 208 | //----------------------------------------------------------------------------- |
|---|
| 209 | void DesignGaussianFilter( |
|---|
| 210 | unsigned int k, // samples per symbol |
|---|
| 211 | unsigned int m, // delay |
|---|
| 212 | float beta, // rolloff factor ( 0 < beta <= 1 ) |
|---|
| 213 | float *& h, // pointer to filter coefficients |
|---|
| 214 | unsigned int & h_len // length of filter (len = 2*m*k+1) |
|---|
| 215 | ); |
|---|
| 216 | |
|---|
| 217 | //----------------------------------------------------------------------------- |
|---|
| 218 | // |
|---|
| 219 | // Circular buffer |
|---|
| 220 | // |
|---|
| 221 | //----------------------------------------------------------------------------- |
|---|
| 222 | /** \brief Circlar buffer, template class |
|---|
| 223 | * |
|---|
| 224 | * \section CB_basic_description Basic Description |
|---|
| 225 | * The circular buffer template class implementation minimizes memory copies |
|---|
| 226 | * by wrapping the array around to its beginning. Elements can be added |
|---|
| 227 | * and removed by invoking the Push() and Pop() methods, respectively. |
|---|
| 228 | * |
|---|
| 229 | * \section CB_creating Creating Buffers |
|---|
| 230 | * There are three ways to create a buffer... |
|---|
| 231 | * \code |
|---|
| 232 | * // 1. generate an empty buffer |
|---|
| 233 | * CircularBuffer <short> v1(100); |
|---|
| 234 | * |
|---|
| 235 | * // 2. wrap an existing array |
|---|
| 236 | * short * x = new short[100]; |
|---|
| 237 | * for (unsigned int i=0; i<100; i++) |
|---|
| 238 | * x[i] = i; |
|---|
| 239 | * CircularBuffer <short> v2(x, 100); |
|---|
| 240 | * |
|---|
| 241 | * // 3. copy from another CircularBuffer |
|---|
| 242 | * CircularBuffer <short> v3(v2); |
|---|
| 243 | * \endcode |
|---|
| 244 | * |
|---|
| 245 | * \section CB_resizing_buffers Resizing Buffers |
|---|
| 246 | * CircularBuffer supports dynamic memory allocation as well; if an instance |
|---|
| 247 | * of CircularBuffer is created of a particular size and then later it is |
|---|
| 248 | * determined that the size is too small, invoking SetBufferSize() can be used |
|---|
| 249 | * to increase the length without loss of data. However, decreasing the buffer |
|---|
| 250 | * size beyond the number of elements in the buffer truncates the data. |
|---|
| 251 | * |
|---|
| 252 | * \section CB_wrapping Wrapping |
|---|
| 253 | * When the buffer is full and another element is pushed, the new element |
|---|
| 254 | * overwrites the last element in the buffer without warning. Status of the |
|---|
| 255 | * buffer can be checked with the GetBufferSize() and GetNumElements() |
|---|
| 256 | * methods. |
|---|
| 257 | * |
|---|
| 258 | */ |
|---|
| 259 | template <class T> |
|---|
| 260 | class CircularBuffer { |
|---|
| 261 | public: |
|---|
| 262 | /// Default constructor |
|---|
| 263 | CircularBuffer(); |
|---|
| 264 | |
|---|
| 265 | /// Initializing constructor (empty) |
|---|
| 266 | CircularBuffer(unsigned int _bufferSize); |
|---|
| 267 | |
|---|
| 268 | /// Initializing constructor (array) |
|---|
| 269 | CircularBuffer(T * _v, unsigned int _bufferSize); |
|---|
| 270 | |
|---|
| 271 | /// Copy constructor |
|---|
| 272 | CircularBuffer(CircularBuffer &); |
|---|
| 273 | |
|---|
| 274 | /// destructor |
|---|
| 275 | ~CircularBuffer() { delete [] headPtr; } |
|---|
| 276 | |
|---|
| 277 | /// \brief Overload the [] operator (indexing) |
|---|
| 278 | /// |
|---|
| 279 | /// Returns the value at the appropriate index as if the buffer were a |
|---|
| 280 | /// linear array |
|---|
| 281 | T operator[] (unsigned int i) { |
|---|
| 282 | return headPtr[(i_read + i) % bufferSize ]; |
|---|
| 283 | } |
|---|
| 284 | |
|---|
| 285 | /// Push value into the beginning of the buffer, overwrite existing element |
|---|
| 286 | /// if buffer is full |
|---|
| 287 | void Push(T _value) { |
|---|
| 288 | |
|---|
| 289 | // OK to push value |
|---|
| 290 | headPtr[i_head++] = _value; |
|---|
| 291 | |
|---|
| 292 | // Ensure head index does not equal or exceed bufferSize (wrap) |
|---|
| 293 | i_head = i_head % bufferSize; |
|---|
| 294 | |
|---|
| 295 | // Check to see if buffer is full |
|---|
| 296 | if ( numElements < bufferSize ) |
|---|
| 297 | numElements++; // buffer not yet full |
|---|
| 298 | else |
|---|
| 299 | i_read++; // overflow |
|---|
| 300 | } |
|---|
| 301 | |
|---|
| 302 | /// Remove element from the end of the buffer |
|---|
| 303 | T Pop() { |
|---|
| 304 | if ( numElements == 0 ) { |
|---|
| 305 | std::cerr << "ERROR: SigProc::CircularBuffer::Pop() : buffer is empty!" |
|---|
| 306 | << std::endl; |
|---|
| 307 | throw 0; |
|---|
| 308 | } |
|---|
| 309 | |
|---|
| 310 | // read value |
|---|
| 311 | T retval = headPtr[i_read++]; |
|---|
| 312 | |
|---|
| 313 | // Ensure read index does not equal or exceed bufferSize (wrap) |
|---|
| 314 | i_read = i_read % bufferSize; |
|---|
| 315 | |
|---|
| 316 | // Decrement number of elements |
|---|
| 317 | numElements--; |
|---|
| 318 | |
|---|
| 319 | // RETURN value |
|---|
| 320 | return retval; |
|---|
| 321 | } |
|---|
| 322 | |
|---|
| 323 | /// Releases entire buffer (resets values in buffer to zero) |
|---|
| 324 | void Release() { |
|---|
| 325 | i_head = 0; |
|---|
| 326 | i_read = 0; |
|---|
| 327 | numElements = 0; |
|---|
| 328 | memset(headPtr, 0, bufferSize*sizeof(T)); |
|---|
| 329 | } |
|---|
| 330 | |
|---|
| 331 | /// Releases _n elements from buffer |
|---|
| 332 | void Release( unsigned int _n ) { |
|---|
| 333 | if ( _n >= numElements ) { |
|---|
| 334 | Release(); |
|---|
| 335 | } else { |
|---|
| 336 | numElements -= _n; |
|---|
| 337 | i_read = (i_read + _n) % bufferSize; |
|---|
| 338 | } |
|---|
| 339 | } |
|---|
| 340 | |
|---|
| 341 | /// Return the number of memory slots allocated to the buffer |
|---|
| 342 | unsigned int GetBufferSize() { return bufferSize; } |
|---|
| 343 | |
|---|
| 344 | /// Set the buffer size dynamically |
|---|
| 345 | void SetBufferSize(unsigned int _bufferSize); |
|---|
| 346 | |
|---|
| 347 | /// Return the number of elements inside the buffer |
|---|
| 348 | unsigned int GetNumElements() { return numElements; } |
|---|
| 349 | |
|---|
| 350 | /// \brief Get a pointer to the buffer |
|---|
| 351 | /// |
|---|
| 352 | /// This method actually shifts the elements inside the buffer so that instead |
|---|
| 353 | /// of being cyclical they are linear. |
|---|
| 354 | T * GetHeadPtr() { |
|---|
| 355 | Linearize(); |
|---|
| 356 | return headPtr; |
|---|
| 357 | } |
|---|
| 358 | |
|---|
| 359 | /// Prints buffer to screen |
|---|
| 360 | void Print() { |
|---|
| 361 | std::cout << " b : "; |
|---|
| 362 | for (unsigned int i=0; i<numElements; i++) |
|---|
| 363 | std::cout << " " << headPtr[(i_read+i) % bufferSize]; |
|---|
| 364 | std::cout << std::endl; |
|---|
| 365 | } |
|---|
| 366 | |
|---|
| 367 | protected: |
|---|
| 368 | /// Pointer to the beginning of the buffer |
|---|
| 369 | T * headPtr; |
|---|
| 370 | |
|---|
| 371 | /// Head index |
|---|
| 372 | unsigned int i_head; |
|---|
| 373 | |
|---|
| 374 | /// Read index |
|---|
| 375 | unsigned int i_read; |
|---|
| 376 | |
|---|
| 377 | /// Memory slots allocated to the buffer |
|---|
| 378 | unsigned int bufferSize; |
|---|
| 379 | |
|---|
| 380 | /// Number of elements currently in the buffer |
|---|
| 381 | unsigned int numElements; |
|---|
| 382 | |
|---|
| 383 | /// \brief Linearize buffer array |
|---|
| 384 | /// |
|---|
| 385 | /// Shifts the elements in the buffer so that they are organized linearly |
|---|
| 386 | /// rather than circularly. If the buffer is not empty, Linearize creates |
|---|
| 387 | /// a new array and copies the old values. |
|---|
| 388 | void Linearize(); |
|---|
| 389 | |
|---|
| 390 | }; |
|---|
| 391 | |
|---|
| 392 | //----------------------------------------------------------------------------- |
|---|
| 393 | // |
|---|
| 394 | // P/N Sequence |
|---|
| 395 | // |
|---|
| 396 | //----------------------------------------------------------------------------- |
|---|
| 397 | |
|---|
| 398 | /// P/N Sequence |
|---|
| 399 | class PNSequence |
|---|
| 400 | { |
|---|
| 401 | public: |
|---|
| 402 | /// initializing constructor |
|---|
| 403 | PNSequence(unsigned int _g, unsigned int _a); |
|---|
| 404 | |
|---|
| 405 | /// destructor |
|---|
| 406 | ~PNSequence(); |
|---|
| 407 | |
|---|
| 408 | unsigned long m; ///< shift register length |
|---|
| 409 | unsigned long n; ///< output sequence length, \f$ n=2^m-1 \f$ |
|---|
| 410 | char * g; ///< generator polynomial |
|---|
| 411 | char * a; ///< initial polynomial state |
|---|
| 412 | char * s; ///< output sequence |
|---|
| 413 | }; |
|---|
| 414 | |
|---|
| 415 | //----------------------------------------------------------------------------- |
|---|
| 416 | // |
|---|
| 417 | // Automatic Gain Control class |
|---|
| 418 | // |
|---|
| 419 | //----------------------------------------------------------------------------- |
|---|
| 420 | /** \brief Automatic gain control signal processor |
|---|
| 421 | * |
|---|
| 422 | * \cite R. G. Lyons, Understanding Digital Signal Processing, 2nd ed. New Jersey: |
|---|
| 423 | * Prentice Hall, 2004. |
|---|
| 424 | */ |
|---|
| 425 | class AutomaticGainControl |
|---|
| 426 | { |
|---|
| 427 | public: |
|---|
| 428 | /// default constructor |
|---|
| 429 | AutomaticGainControl(); |
|---|
| 430 | |
|---|
| 431 | /// Destructor |
|---|
| 432 | ~AutomaticGainControl(); |
|---|
| 433 | |
|---|
| 434 | /// Set signal processing values |
|---|
| 435 | void SetValues( |
|---|
| 436 | float _elo, |
|---|
| 437 | float _ehi, |
|---|
| 438 | float _ka, |
|---|
| 439 | float _kr, |
|---|
| 440 | float _gmin, |
|---|
| 441 | float _gmax); |
|---|
| 442 | |
|---|
| 443 | /// Get signal processing values |
|---|
| 444 | void GetValues( |
|---|
| 445 | float & _elo, |
|---|
| 446 | float & _ehi, |
|---|
| 447 | float & _ka, |
|---|
| 448 | float & _kr, |
|---|
| 449 | float & _gmin, |
|---|
| 450 | float & _gmax); |
|---|
| 451 | |
|---|
| 452 | /// Get status |
|---|
| 453 | void GetStatus(float & _gain, float & _energy); |
|---|
| 454 | |
|---|
| 455 | /// track signal energy and apply gain (real) |
|---|
| 456 | void ApplyGain(short & I); |
|---|
| 457 | |
|---|
| 458 | /// track signal energy and apply gain (complex) |
|---|
| 459 | void ApplyGain(short & I, short & Q); |
|---|
| 460 | |
|---|
| 461 | private: |
|---|
| 462 | /// disallow copy constructor |
|---|
| 463 | AutomaticGainControl(AutomaticGainControl &); |
|---|
| 464 | |
|---|
| 465 | /// compute necessary gain value from measured energy |
|---|
| 466 | void ComputeGain(); |
|---|
| 467 | |
|---|
| 468 | /// low energy threshold |
|---|
| 469 | float energy_lo; |
|---|
| 470 | |
|---|
| 471 | /// high energy threshold |
|---|
| 472 | float energy_hi; |
|---|
| 473 | |
|---|
| 474 | /// attack time constant |
|---|
| 475 | float ka; |
|---|
| 476 | |
|---|
| 477 | /// release time constant |
|---|
| 478 | float kr; |
|---|
| 479 | |
|---|
| 480 | /// minimum gain value |
|---|
| 481 | float gmin; |
|---|
| 482 | |
|---|
| 483 | /// maximum gain value |
|---|
| 484 | float gmax; |
|---|
| 485 | |
|---|
| 486 | /// actual tracking gain value |
|---|
| 487 | float gain; |
|---|
| 488 | |
|---|
| 489 | /// actual tracking average energy value |
|---|
| 490 | float energy; |
|---|
| 491 | |
|---|
| 492 | /// low-pass filter coefficient for estimating average energy |
|---|
| 493 | float zeta; |
|---|
| 494 | |
|---|
| 495 | /// average energy threshold for smoother tracking |
|---|
| 496 | float energy_av; |
|---|
| 497 | |
|---|
| 498 | }; |
|---|
| 499 | |
|---|
| 500 | class phase_detect { |
|---|
| 501 | |
|---|
| 502 | public: |
|---|
| 503 | phase_detect(float scale_factor); |
|---|
| 504 | |
|---|
| 505 | void do_work(short I_in, short Q_in, short I_nco, short Q_nco, short &out); |
|---|
| 506 | |
|---|
| 507 | private: |
|---|
| 508 | phase_detect(const phase_detect &); |
|---|
| 509 | |
|---|
| 510 | float scale_factor; |
|---|
| 511 | }; |
|---|
| 512 | |
|---|
| 513 | |
|---|
| 514 | class nco { |
|---|
| 515 | public: |
|---|
| 516 | nco(); |
|---|
| 517 | nco(unsigned int max_out); |
|---|
| 518 | |
|---|
| 519 | void do_work(short control_voltage, short &sine, short &cosine); |
|---|
| 520 | |
|---|
| 521 | private: |
|---|
| 522 | nco(const nco &); |
|---|
| 523 | |
|---|
| 524 | int freq_index; |
|---|
| 525 | int max_out; |
|---|
| 526 | }; |
|---|
| 527 | |
|---|
| 528 | class gain { |
|---|
| 529 | public: |
|---|
| 530 | gain(); |
|---|
| 531 | |
|---|
| 532 | void do_work(float gain, short data_in, short &out); |
|---|
| 533 | |
|---|
| 534 | private: |
|---|
| 535 | gain(const gain &); |
|---|
| 536 | |
|---|
| 537 | }; |
|---|
| 538 | |
|---|
| 539 | class iir_filter { |
|---|
| 540 | public: |
|---|
| 541 | iir_filter(float a[], unsigned int len_a, float b[], unsigned int len_b); |
|---|
| 542 | |
|---|
| 543 | void do_work(short x, short &y); |
|---|
| 544 | |
|---|
| 545 | void ResetBuffer(); |
|---|
| 546 | |
|---|
| 547 | private: |
|---|
| 548 | iir_filter(const iir_filter &); |
|---|
| 549 | |
|---|
| 550 | float *A; |
|---|
| 551 | float *B; |
|---|
| 552 | unsigned int len_A, len_B; |
|---|
| 553 | |
|---|
| 554 | float *v; |
|---|
| 555 | unsigned int len_v; |
|---|
| 556 | unsigned int next_v; |
|---|
| 557 | }; |
|---|
| 558 | |
|---|
| 559 | |
|---|
| 560 | //----------------------------------------------------------------------------- |
|---|
| 561 | // |
|---|
| 562 | // FIR polyphase filter bank |
|---|
| 563 | // |
|---|
| 564 | //----------------------------------------------------------------------------- |
|---|
| 565 | /** \brief Finite impulse response (FIR) polyphase filter bank |
|---|
| 566 | * |
|---|
| 567 | * This class implementes a finite impulse response (FIR) polyphase filter |
|---|
| 568 | * bank useful for decimators that need to interpolate samples in digital |
|---|
| 569 | * receivers. |
|---|
| 570 | * |
|---|
| 571 | * The filter bank can automatically calculate filter coefficients for |
|---|
| 572 | * prototypes commonly used in communications systems. Currently, such |
|---|
| 573 | * supported filter prototypes are |
|---|
| 574 | * - root raised-cosine |
|---|
| 575 | * |
|---|
| 576 | * Filter prototypes that will eventually be supported are |
|---|
| 577 | * - raised-cosine |
|---|
| 578 | * - gaussian |
|---|
| 579 | * - triangular |
|---|
| 580 | * - hamming |
|---|
| 581 | * |
|---|
| 582 | * The user can also load filter coefficients that have been calculated |
|---|
| 583 | * externally. |
|---|
| 584 | * |
|---|
| 585 | * \image latex polyphase_rcos_filter_k2_N4.eps "Example filter bank (1)" |
|---|
| 586 | * \image html polyphase_rcos_filter_k2_N4.png "Example filter bank (1)" |
|---|
| 587 | * |
|---|
| 588 | * \image latex polyphase_rcos_filter_k4_N2.eps "Example filter bank (2)" |
|---|
| 589 | * \image html polyphase_rcos_filter_k4_N2.png "Example filter bank (2)" |
|---|
| 590 | * |
|---|
| 591 | * \cite M. Rice and fred harris, "Polyphase Filterbanks for Symbol Timing |
|---|
| 592 | * Synchronization in Sampled Data Receivers," in MILCOMM Proceedings, vol. |
|---|
| 593 | * 2, October 2002, pp. 982--986. |
|---|
| 594 | * |
|---|
| 595 | */ |
|---|
| 596 | class FIRPolyphaseFilterBank { |
|---|
| 597 | public: |
|---|
| 598 | /// \brief Initializing constructor |
|---|
| 599 | /// |
|---|
| 600 | /// This constructor calculates the filter coefficients for several |
|---|
| 601 | /// different filter types using just a few parameters. The filters |
|---|
| 602 | /// currently supported are: |
|---|
| 603 | /// - 'rrcos' : square-root raised-cosine (RRC) |
|---|
| 604 | /// - 'drrcos' : derivative RRC |
|---|
| 605 | /// |
|---|
| 606 | FIRPolyphaseFilterBank( |
|---|
| 607 | char * _type, // type of filter |
|---|
| 608 | unsigned int _k, // samples per symbol |
|---|
| 609 | unsigned int _m, // delay |
|---|
| 610 | float _beta, // excess bandwidth factor |
|---|
| 611 | unsigned int _Npfb // number of filters |
|---|
| 612 | ); |
|---|
| 613 | |
|---|
| 614 | /// \brief Initializing constructor |
|---|
| 615 | /// |
|---|
| 616 | /// This constructor loads filter bank coefficients which have |
|---|
| 617 | /// been generated externally. The coefficients are copied from |
|---|
| 618 | /// the input array to a new buffer. |
|---|
| 619 | FIRPolyphaseFilterBank( |
|---|
| 620 | float * _H, // filter bank coefficients |
|---|
| 621 | unsigned int _h_len,// length of each filter |
|---|
| 622 | unsigned int _Npfb // number of filters |
|---|
| 623 | ); |
|---|
| 624 | |
|---|
| 625 | /// destructor |
|---|
| 626 | ~FIRPolyphaseFilterBank(); |
|---|
| 627 | |
|---|
| 628 | /// Push input value into buffer |
|---|
| 629 | void PushInput(short _x); |
|---|
| 630 | |
|---|
| 631 | /// Compute filter output from current buffer state using specific |
|---|
| 632 | /// filter from filter bank matrix |
|---|
| 633 | void ComputeOutput( |
|---|
| 634 | short &y, // output sample |
|---|
| 635 | unsigned int _b // filter bank index |
|---|
| 636 | ); |
|---|
| 637 | |
|---|
| 638 | /// Compute filter output from current buffer state using specific |
|---|
| 639 | /// filter from filter bank matrix and an external memory buffer |
|---|
| 640 | void ComputeOutput( |
|---|
| 641 | short &y, // output sample |
|---|
| 642 | unsigned int _b, // filter bank index |
|---|
| 643 | short *_v // external memory buffer array |
|---|
| 644 | ); |
|---|
| 645 | |
|---|
| 646 | /// Reset filter buffer |
|---|
| 647 | void ResetBuffer(); |
|---|
| 648 | |
|---|
| 649 | /// Print filter buffer |
|---|
| 650 | void PrintBuffer(); |
|---|
| 651 | |
|---|
| 652 | /// Prints filter bank coefficients to the screen |
|---|
| 653 | void PrintFilterBankCoefficients(); |
|---|
| 654 | |
|---|
| 655 | /// Get the length of each filter |
|---|
| 656 | unsigned int GetFilterLength() { return h_len; } |
|---|
| 657 | |
|---|
| 658 | /// Get the number of filters in the bank |
|---|
| 659 | unsigned int GetNumFilters() { return Npfb; } |
|---|
| 660 | |
|---|
| 661 | /// Return a pointer to the filter bank coefficients; this is intended |
|---|
| 662 | /// for debugging |
|---|
| 663 | float * GetFilterBankCoefficients() { return H; } |
|---|
| 664 | |
|---|
| 665 | protected: |
|---|
| 666 | |
|---|
| 667 | /// type of filter; can be one of the following |
|---|
| 668 | /// - 'rrcos' |
|---|
| 669 | /// - 'gaussian' |
|---|
| 670 | char * type; |
|---|
| 671 | |
|---|
| 672 | /// samples per symbol |
|---|
| 673 | unsigned int k; |
|---|
| 674 | |
|---|
| 675 | /// symbol delay |
|---|
| 676 | unsigned int m; |
|---|
| 677 | |
|---|
| 678 | /// excess bandwidth factor |
|---|
| 679 | float beta; |
|---|
| 680 | |
|---|
| 681 | /// number of filters in bank |
|---|
| 682 | unsigned int Npfb; |
|---|
| 683 | |
|---|
| 684 | /// \brief filter bank coefficients matrix |
|---|
| 685 | /// |
|---|
| 686 | /// The coefficients are stored in a one-dimensional array which |
|---|
| 687 | /// is realized as a two-dimensional matrix. The array is of |
|---|
| 688 | /// length Npfb*h_len (the number of filters in the bank times |
|---|
| 689 | /// the length of each filter). |
|---|
| 690 | float *H; |
|---|
| 691 | |
|---|
| 692 | /// length of each filter |
|---|
| 693 | unsigned int h_len; |
|---|
| 694 | |
|---|
| 695 | /// circular input buffer |
|---|
| 696 | CircularBuffer <short> v; |
|---|
| 697 | |
|---|
| 698 | /// transpose filter bank coefficient matrix |
|---|
| 699 | void TransposeCoefficientMatrix(); |
|---|
| 700 | |
|---|
| 701 | // ----- calculate filter bank coefficients ----- |
|---|
| 702 | |
|---|
| 703 | /// Calculate root raised-cosine coefficients |
|---|
| 704 | void CalculateRRCFilterCoefficients(); |
|---|
| 705 | |
|---|
| 706 | /// Calculate Gaussian filter coefficients |
|---|
| 707 | void CalculateGaussianFilterCoefficients(); |
|---|
| 708 | |
|---|
| 709 | /// \brief Calculate derivative filter coefficients |
|---|
| 710 | /// |
|---|
| 711 | /// Approximates the derivative of the template filter |
|---|
| 712 | /// \f[ \dot{h}(nT) = \frac{\partial h(nT)}{\partial t} \f] |
|---|
| 713 | /// |
|---|
| 714 | /// using discrete samples, viz. |
|---|
| 715 | /// \f[ \dot{h}_m(nT) = h_{m+1}(nT) - h_{m-1}(nT), \ \ m=1,2,\ldots,...M-2 \f] |
|---|
| 716 | /// \f[ \dot{h}_0(nT) = h_{1}(nT) - h_{M-1}(nT)\f] |
|---|
| 717 | /// \f[ \dot{h}_{M-1}(nT) = h_{M-2}(nT) - h_{0}(nT)\f] |
|---|
| 718 | /// |
|---|
| 719 | void CalculateDerivativeFilterCoefficients(); |
|---|
| 720 | |
|---|
| 721 | private: |
|---|
| 722 | |
|---|
| 723 | /// disallow copy constructor |
|---|
| 724 | FIRPolyphaseFilterBank(const FIRPolyphaseFilterBank&); |
|---|
| 725 | |
|---|
| 726 | }; |
|---|
| 727 | |
|---|
| 728 | |
|---|
| 729 | //----------------------------------------------------------------------------- |
|---|
| 730 | // |
|---|
| 731 | // Dot product definitions |
|---|
| 732 | // |
|---|
| 733 | //----------------------------------------------------------------------------- |
|---|
| 734 | void dot_product(float *x, float *y, unsigned int N, float &z); |
|---|
| 735 | void dot_product(short *x, short *y, unsigned int N, short &z); |
|---|
| 736 | void dot_product(float *x, short *y, unsigned int N, short &z); |
|---|
| 737 | void dot_product(float *x, short *y, unsigned int N, float &z); |
|---|
| 738 | |
|---|
| 739 | //----------------------------------------------------------------------------- |
|---|
| 740 | // |
|---|
| 741 | // Trigonometric functions |
|---|
| 742 | // |
|---|
| 743 | //----------------------------------------------------------------------------- |
|---|
| 744 | void arctan(float &x, float &y, float &theta); |
|---|
| 745 | |
|---|
| 746 | /// Rotates a complex signal counter-clockwise by \f[\theta\f] radians |
|---|
| 747 | /// \f[ \bar{y} = \bar{x} e^{j\theta} \f] |
|---|
| 748 | void rotate(short I_in, short Q_in, float theta, short *I_out, short *Q_out); |
|---|
| 749 | |
|---|
| 750 | //----------------------------------------------------------------------------- |
|---|
| 751 | // |
|---|
| 752 | // Random number generators |
|---|
| 753 | // |
|---|
| 754 | //----------------------------------------------------------------------------- |
|---|
| 755 | |
|---|
| 756 | /// Uniform random number generator, (0,1] |
|---|
| 757 | float randf(); |
|---|
| 758 | |
|---|
| 759 | /// Gaussian random number generator, N(0,1) |
|---|
| 760 | void randnf(float * i, float * q); |
|---|
| 761 | |
|---|
| 762 | //----------------------------------------------------------------------------- |
|---|
| 763 | // |
|---|
| 764 | // Byte packing functions |
|---|
| 765 | // |
|---|
| 766 | //----------------------------------------------------------------------------- |
|---|
| 767 | void pack_bytes( |
|---|
| 768 | char * input, |
|---|
| 769 | unsigned int input_length, |
|---|
| 770 | char * output, |
|---|
| 771 | unsigned int output_length, |
|---|
| 772 | unsigned int *num_written); |
|---|
| 773 | |
|---|
| 774 | void unpack_bytes( |
|---|
| 775 | char * input, |
|---|
| 776 | unsigned int input_length, |
|---|
| 777 | char * output, |
|---|
| 778 | unsigned int output_length, |
|---|
| 779 | unsigned int *num_written); |
|---|
| 780 | |
|---|
| 781 | void repack_bytes( |
|---|
| 782 | char * input, |
|---|
| 783 | unsigned int input_sym_size, |
|---|
| 784 | unsigned int input_length, |
|---|
| 785 | char * output, |
|---|
| 786 | unsigned int output_sym_size, |
|---|
| 787 | unsigned int output_length, |
|---|
| 788 | unsigned int *num_written); |
|---|
| 789 | |
|---|
| 790 | |
|---|
| 791 | |
|---|
| 792 | |
|---|
| 793 | class fir_filter { |
|---|
| 794 | public: |
|---|
| 795 | fir_filter(float a[], unsigned int len_a); |
|---|
| 796 | |
|---|
| 797 | void do_work(bool run_filter, short in_sample, short &out_sample); |
|---|
| 798 | void reset(); |
|---|
| 799 | |
|---|
| 800 | private: |
|---|
| 801 | fir_filter(const fir_filter &); |
|---|
| 802 | |
|---|
| 803 | #ifdef FPM |
|---|
| 804 | mad_fixed_t *A; |
|---|
| 805 | mad_fixed_t *v; |
|---|
| 806 | #else |
|---|
| 807 | float *A; |
|---|
| 808 | short *v; |
|---|
| 809 | #endif |
|---|
| 810 | unsigned int len_A; |
|---|
| 811 | |
|---|
| 812 | unsigned int len_v; |
|---|
| 813 | unsigned int next_v; |
|---|
| 814 | }; |
|---|
| 815 | |
|---|
| 816 | class dump_data { |
|---|
| 817 | public: |
|---|
| 818 | dump_data(const char *filename, long start_sample, long number_of_samples); |
|---|
| 819 | ~dump_data(); |
|---|
| 820 | |
|---|
| 821 | void write_data(float data, const char *msg = ""); |
|---|
| 822 | void write_data(float a, float b, const char *msg = ""); |
|---|
| 823 | |
|---|
| 824 | private: |
|---|
| 825 | dump_data(); |
|---|
| 826 | dump_data(const dump_data &); |
|---|
| 827 | |
|---|
| 828 | std::ofstream *out_file; |
|---|
| 829 | |
|---|
| 830 | long start_sample, stop_sample; |
|---|
| 831 | long current_sample; |
|---|
| 832 | |
|---|
| 833 | }; |
|---|
| 834 | |
|---|
| 835 | class dc_block { |
|---|
| 836 | public: |
|---|
| 837 | dc_block(const float forget_factor); |
|---|
| 838 | ~dc_block(); |
|---|
| 839 | |
|---|
| 840 | void do_work(short in, short &out); |
|---|
| 841 | |
|---|
| 842 | private: |
|---|
| 843 | dc_block(); |
|---|
| 844 | dc_block(const dc_block &); |
|---|
| 845 | |
|---|
| 846 | float forget_factor; |
|---|
| 847 | int prev_input, prev_output; |
|---|
| 848 | |
|---|
| 849 | }; |
|---|
| 850 | |
|---|
| 851 | //----------------------------------------------------------------------------- |
|---|
| 852 | // |
|---|
| 853 | // Circular buffer definitions |
|---|
| 854 | // |
|---|
| 855 | //----------------------------------------------------------------------------- |
|---|
| 856 | |
|---|
| 857 | // Initializing constructor (empty) |
|---|
| 858 | template <class T> |
|---|
| 859 | CircularBuffer<T>::CircularBuffer() |
|---|
| 860 | { |
|---|
| 861 | bufferSize = 1; |
|---|
| 862 | numElements = 0; |
|---|
| 863 | i_head = 0; |
|---|
| 864 | i_read = 0; |
|---|
| 865 | headPtr = new T[bufferSize]; |
|---|
| 866 | } |
|---|
| 867 | |
|---|
| 868 | // Initializing constructor (empty) |
|---|
| 869 | template <class T> |
|---|
| 870 | CircularBuffer<T>::CircularBuffer(unsigned int _bufferSize) { |
|---|
| 871 | bufferSize = _bufferSize; |
|---|
| 872 | numElements = 0; |
|---|
| 873 | i_head = 0; |
|---|
| 874 | i_read = 0; |
|---|
| 875 | headPtr = new T[bufferSize]; |
|---|
| 876 | } |
|---|
| 877 | |
|---|
| 878 | // Initializing constructor (array) |
|---|
| 879 | template <class T> |
|---|
| 880 | CircularBuffer<T>::CircularBuffer(T * _v, unsigned int _bufferSize) { |
|---|
| 881 | bufferSize = _bufferSize; |
|---|
| 882 | numElements = 0; |
|---|
| 883 | i_head = 0; |
|---|
| 884 | i_read = 0; |
|---|
| 885 | headPtr = new T[bufferSize]; |
|---|
| 886 | for (unsigned int i=0; i<bufferSize; i++) |
|---|
| 887 | Push( _v[i] ); |
|---|
| 888 | } |
|---|
| 889 | |
|---|
| 890 | // Copy constructor |
|---|
| 891 | template <class T> |
|---|
| 892 | CircularBuffer<T>::CircularBuffer(CircularBuffer & _cb) { |
|---|
| 893 | bufferSize = _cb.bufferSize; |
|---|
| 894 | numElements = _cb.numElements; |
|---|
| 895 | i_head = _cb.i_head; |
|---|
| 896 | i_read = _cb.i_read; |
|---|
| 897 | headPtr = new T[bufferSize]; |
|---|
| 898 | for (unsigned int i=0; i<bufferSize; i++) |
|---|
| 899 | headPtr[i] = _cb.headPtr[i]; |
|---|
| 900 | } |
|---|
| 901 | |
|---|
| 902 | // Set the buffer size dynamically |
|---|
| 903 | template <class T> |
|---|
| 904 | void CircularBuffer<T>::SetBufferSize(unsigned int _bufferSize) { |
|---|
| 905 | if ( _bufferSize < 1 ) { |
|---|
| 906 | std::cerr << "ERROR: SigProc::CircularBuffer::SetBufferSize()" << std::endl |
|---|
| 907 | << " => minimum buffer size is 1" << std::endl; |
|---|
| 908 | throw 0; |
|---|
| 909 | } |
|---|
| 910 | |
|---|
| 911 | if ( _bufferSize == bufferSize ) { |
|---|
| 912 | // Nothing to do |
|---|
| 913 | return; |
|---|
| 914 | } else if ( _bufferSize < bufferSize && numElements > _bufferSize ) { |
|---|
| 915 | // New buffer is too small: copy only newest elements, discard oldest |
|---|
| 916 | i_read = ( i_read + numElements - _bufferSize ) % bufferSize; |
|---|
| 917 | numElements = _bufferSize; |
|---|
| 918 | } else { |
|---|
| 919 | // New buffer is sufficiently large: copy everything |
|---|
| 920 | } |
|---|
| 921 | |
|---|
| 922 | // allocate new buffer memory |
|---|
| 923 | T * tmpHeadPtr = new T[_bufferSize]; |
|---|
| 924 | |
|---|
| 925 | for (unsigned int i=0; i<numElements; i++) |
|---|
| 926 | tmpHeadPtr[i] = headPtr[i_read++ % bufferSize]; |
|---|
| 927 | |
|---|
| 928 | // delete old buffer |
|---|
| 929 | delete [] headPtr; |
|---|
| 930 | |
|---|
| 931 | headPtr = tmpHeadPtr; |
|---|
| 932 | i_head = numElements % bufferSize; |
|---|
| 933 | i_read = 0; |
|---|
| 934 | |
|---|
| 935 | bufferSize = _bufferSize; |
|---|
| 936 | } |
|---|
| 937 | |
|---|
| 938 | // Linearize buffer |
|---|
| 939 | template <class T> |
|---|
| 940 | void CircularBuffer<T>::Linearize() { |
|---|
| 941 | if ( numElements == 0 ) |
|---|
| 942 | return; |
|---|
| 943 | |
|---|
| 944 | T * tmpHeadPtr = new T[bufferSize]; |
|---|
| 945 | |
|---|
| 946 | for (unsigned int i=0; i<numElements; i++) |
|---|
| 947 | tmpHeadPtr[i] = headPtr[i_read++ % bufferSize]; |
|---|
| 948 | |
|---|
| 949 | delete [] headPtr; |
|---|
| 950 | headPtr = tmpHeadPtr; |
|---|
| 951 | i_head = numElements % bufferSize; |
|---|
| 952 | i_read = 0; |
|---|
| 953 | } |
|---|
| 954 | |
|---|
| 955 | // enum DemodScheme { |
|---|
| 956 | // HARD = 0, |
|---|
| 957 | // SOFT_TRUE = 1, |
|---|
| 958 | // SOFT_STANDARD = 2, |
|---|
| 959 | // SOFT_HIGHSNR = 3 |
|---|
| 960 | // }; |
|---|
| 961 | |
|---|
| 962 | // void DemodQAM(unsigned int M, signed short X, signed short Y, DemodScheme scheme, signed char *bitsOut); |
|---|
| 963 | |
|---|
| 964 | // void DemodPSK(unsigned int M, signed short X, signed short Y, DemodScheme scheme, signed char *bitsOut); |
|---|
| 965 | |
|---|
| 966 | |
|---|
| 967 | /// |
|---|
| 968 | enum ModulationScheme { |
|---|
| 969 | UNKNOWN, // Unknown modulation scheme |
|---|
| 970 | BPSK, QPSK, PSK8, PSK16, // Phase shift keying |
|---|
| 971 | DBPSK, DQPSK, DPSK8, DPSK16, // Differential PSK |
|---|
| 972 | PAM4, PAM8, PAM16, PAM32, // Pulse amplitude modulation |
|---|
| 973 | BFSK, FSK4, FSK8, FSK16, // Frequency shift keying |
|---|
| 974 | QAM4, QAM16, QAM32, QAM64, QAM128, QAM256 // Quadrature amplitude modulation |
|---|
| 975 | }; |
|---|
| 976 | |
|---|
| 977 | #define BPSK_LEVEL 10000 ///< BPSK amplitude (RMS=10000) |
|---|
| 978 | #define QPSK_LEVEL 10000 ///< QPSK amplitude (RMS=10000) |
|---|
| 979 | #define QAM4_LEVEL 7071 ///< QAM4 amplitude (RMS=10000) |
|---|
| 980 | #define PSK8_LEVEL_1 7071 ///< Low 8-PSK amplitude (RMS=10000) |
|---|
| 981 | #define PSK8_LEVEL_2 10000 ///< High 8-PSK amplitude (RMS=10000) |
|---|
| 982 | #define QAM16_LEVEL_1 3162 ///< Low 16-QAM amplitude (RMS=10000) |
|---|
| 983 | #define QAM16_LEVEL_2 9487 ///< High 16-QAM amplitude (RMS=10000) |
|---|
| 984 | #define PAM4_LEVEL_1 4472 ///< Low 4-PAM amplitude (RMS=10000) |
|---|
| 985 | #define PAM4_LEVEL_2 13416 ///< High 4-PAM amplitude (RMS=10000) |
|---|
| 986 | |
|---|
| 987 | /// Modulates a symbol into an I/Q pair for binary phase shift keying |
|---|
| 988 | /// |
|---|
| 989 | /// \image latex ConstellationBPSK.eps "BPSK constellation" |
|---|
| 990 | /// \image html ConstellationBPSK.png "BPSK constellation" |
|---|
| 991 | void ModulateBPSK(short symbol_in, short &I_out, short &Q_out); |
|---|
| 992 | |
|---|
| 993 | /// Modulates a symbol into an I/Q pair for quadrature phase shift keying |
|---|
| 994 | /// |
|---|
| 995 | // \image latex ConstellationQPSK.eps "QPSK constellation" |
|---|
| 996 | // \image html ConstellationQPSK.png "QPSK constellation" |
|---|
| 997 | void ModulateQPSK(short symbol_in, short &I_out, short &Q_out); |
|---|
| 998 | |
|---|
| 999 | /// Modulates a symbol into an I/Q pair for quadrature amplitude shift keying |
|---|
| 1000 | /// |
|---|
| 1001 | /// \image latex ConstellationQAM4.eps "QAM4 constellation" |
|---|
| 1002 | /// \image html ConstellationQAM4.png "QAM4 constellation" |
|---|
| 1003 | void ModulateQAM4(short symbol_in, short &I_out, short &Q_out); |
|---|
| 1004 | |
|---|
| 1005 | /// Modulates a symbol into an I/Q pair for 8-ary phase shift keying |
|---|
| 1006 | /// |
|---|
| 1007 | /// \image latex Constellation8PSK.eps "8-PSK constellation" |
|---|
| 1008 | /// \image html Constellation8PSK.png "8-PSK constellation" |
|---|
| 1009 | void Modulate8PSK(short symbol_in, short &I_out, short &Q_out); |
|---|
| 1010 | |
|---|
| 1011 | /// Modulates a symbol into an I/Q pair for 16-point quadrature |
|---|
| 1012 | /// amplitude modulation |
|---|
| 1013 | /// |
|---|
| 1014 | /// \image latex Constellation16QAM.eps "16-QAM constellation" |
|---|
| 1015 | /// \image html Constellation16QAM.png "16-QAM constellation" |
|---|
| 1016 | void Modulate16QAM(short symbol_in, short &I_out, short &Q_out); |
|---|
| 1017 | |
|---|
| 1018 | /// Modulates a symbol into an I/Q pair for 4-ary pulse amplitude |
|---|
| 1019 | /// modulation |
|---|
| 1020 | /// |
|---|
| 1021 | /// \image latex Constellation4PAM.eps "4-PAM constellation" |
|---|
| 1022 | /// \image html Constellation4PAM.png "4-PAM constellation" |
|---|
| 1023 | void Modulate4PAM(short symbol_in, short &I_out, short &Q_out); |
|---|
| 1024 | |
|---|
| 1025 | |
|---|
| 1026 | #define DEMOD_COS_22_5 0.923879532511287 |
|---|
| 1027 | #define DEMOD_SIN_22_5 0.382683432365090 |
|---|
| 1028 | |
|---|
| 1029 | #define QAM16_THRESHOLD 6324 ///< 16-QAM threshold for RMS=10000 signal |
|---|
| 1030 | #define PAM4_THRESHOLD 8944 ///< 4-PAM threshold for RMS=10000 signal |
|---|
| 1031 | |
|---|
| 1032 | /// |
|---|
| 1033 | void DemodulateBPSK(short I_in, short Q_in, short &symbol_out); |
|---|
| 1034 | |
|---|
| 1035 | /// |
|---|
| 1036 | void DemodulateQPSK(short I_in, short Q_in, short &symbol_out); |
|---|
| 1037 | |
|---|
| 1038 | /// |
|---|
| 1039 | void DemodulateQAM4(short I_in, short Q_in, short &symbol_out); |
|---|
| 1040 | |
|---|
| 1041 | /// |
|---|
| 1042 | void Demodulate8PSK(short I_in, short Q_in, short &symbol_out); |
|---|
| 1043 | |
|---|
| 1044 | /// |
|---|
| 1045 | void Demodulate16QAM(short I_in, short Q_in, short &symbol_out); |
|---|
| 1046 | |
|---|
| 1047 | /// |
|---|
| 1048 | void Demodulate4PAM(short I_in, short Q_in, short &symbol_out); |
|---|
| 1049 | |
|---|
| 1050 | } |
|---|
| 1051 | #endif |
|---|
| 1052 | |
|---|
| 1053 | |
|---|