root/SigProc/branches/SigProc-0.6.2/SigProc/SigProc.h @ 5718

Revision 5718, 22.7 KB (checked in by jgaeddert, 6 years ago)

cleaning up modem functions

  • Property svn:eol-style set to native
Line 
1/****************************************************************************
2
3Copyright 2007 Virginia Polytechnic Institute and State University
4
5This file is part of the OSSIE Signal Processing Library.
6
7OSSIE Signal Processing Library is free software; you can redistribute it
8and/or modify it under the terms of the GNU General Public License as
9published by the Free Software Foundation; either version 2 of the License,
10or (at your option) any later version.
11
12OSSIE Signal Processing Library is distributed in the hope that it will be
13useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License along
18with OSSIE Signal Processing Library; if not, write to the Free Software
19Foundation, 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
39namespace SigProc {
40
41//-----------------------------------------------------------------------------
42//
43// Design root raised-cosine filter
44//
45//-----------------------------------------------------------------------------
46void DesignRRCFilter(
47  unsigned int k,      // samples per symbol
48  unsigned int m,      // delay
49  float beta,          // rolloff factor ( 0 < beta <= 1 )
50  float * h            // pointer to filter coefficients
51);
52
53
54//-----------------------------------------------------------------------------
55//
56// Circular buffer
57//
58//-----------------------------------------------------------------------------
59/** \brief Circlar buffer, template class
60 *
61 * \section CB_basic_description Basic Description
62 * The circular buffer template class implementation minimizes memory copies
63 * by wrapping the array around to its beginning.  Elements can be added
64 * and removed by invoking the Push() and Pop() methods, respectively.
65 *
66 * \section CB_creating Creating Buffers
67 * There are three ways to create a buffer...
68 * \code
69 * // 1. generate an empty buffer
70 * CircularBuffer <short> v1(100);
71 *
72 * // 2. wrap an existing array
73 * short * x = new short[100];
74 * for (unsigned int i=0; i<100; i++)
75 *     x[i] = i;
76 * CircularBuffer <short> v2(x, 100);
77 *
78 * // 3. copy from another CircularBuffer
79 * CircularBuffer <short> v3(v2);
80 * \endcode
81 *
82 * \section CB_resizing_buffers Resizing Buffers
83 * CircularBuffer supports dynamic memory allocation as well; if an instance
84 * of CircularBuffer is created of a particular size and then later it is
85 * determined that the size is too small, invoking SetBufferSize() can be used
86 * to increase the length without loss of data.  However, decreasing the buffer
87 * size beyond the number of elements in the buffer truncates the data.
88 *
89 * \section CB_wrapping Wrapping
90 * When the buffer is full and another element is pushed, the new element
91 * overwrites the last element in the buffer without warning.  Status of the
92 * buffer can be checked with the GetBufferSize() and GetNumElements()
93 * methods.
94 *
95 */
96template <class T>
97class CircularBuffer {
98  public:
99    /// Default constructor
100    CircularBuffer();
101
102    /// Initializing constructor (empty)
103    CircularBuffer(unsigned int _bufferSize);
104
105    /// Initializing constructor (array)
106    CircularBuffer(T * _v, unsigned int _bufferSize);
107
108    /// Copy constructor
109    CircularBuffer(CircularBuffer &);
110
111    /// destructor
112    ~CircularBuffer() { delete [] headPtr; }
113
114    /// \brief Overload the [] operator (indexing)
115    ///
116    /// Returns the value at the appropriate index as if the buffer were a
117    /// linear array
118    T operator[] (unsigned int i) {
119        return headPtr[(i_read + i) % bufferSize ];
120    }
121
122    /// Push value into the beginning of the buffer, overwrite existing element
123    /// if buffer is full
124    void Push(T _value) {
125
126        // OK to push value
127        headPtr[i_head++] = _value;
128
129        // Ensure head index does not equal or exceed bufferSize (wrap)
130        i_head = i_head % bufferSize;
131
132        // Check to see if buffer is full
133        if ( numElements < bufferSize )
134            numElements++;  // buffer not yet full
135        else
136            i_read++;       // overflow
137    }
138
139    /// Remove element from the end of the buffer
140    T Pop() {
141        if ( numElements == 0 ) {
142            std::cerr << "ERROR: SigProc::CircularBuffer::Pop() : buffer is empty!"
143                      << std::endl;
144            throw 0;
145        }
146
147        // read value
148        T retval = headPtr[i_read++];
149
150        // Ensure read index does not equal or exceed bufferSize (wrap)
151        i_read = i_read % bufferSize;
152
153        // Decrement number of elements
154        numElements--;
155
156        // RETURN value
157        return retval;
158    }
159
160    /// Releases entire buffer (resets values in buffer to zero)
161    void Release() {
162        i_head = 0;
163        i_read = 0;
164        numElements = 0;
165        memset(headPtr, 0, bufferSize*sizeof(T));
166    }
167
168    /// Releases _n elements from buffer
169    void Release( unsigned int _n ) {
170        if ( _n >= numElements ) {
171            Release();
172        } else {
173            numElements -= _n;
174            i_read = (i_read + _n) % bufferSize;
175        }
176    }
177
178    /// Return the number of memory slots allocated to the buffer
179    unsigned int GetBufferSize() { return bufferSize; }
180
181    /// Set the buffer size dynamically
182    void SetBufferSize(unsigned int _bufferSize);
183
184    /// Return the number of elements inside the buffer
185    unsigned int GetNumElements() { return numElements; }
186
187    /// \brief Get a pointer to the buffer
188    ///
189    /// This method actually shifts the elements inside the buffer so that instead
190    /// of being cyclical they are linear.
191    T * GetHeadPtr() {
192        Linearize();
193        return headPtr;
194    }
195
196    /// Prints buffer to screen
197    void Print() {
198        std::cout << " b : ";
199        for (unsigned int i=0; i<numElements; i++)
200            std::cout << " " << headPtr[(i_read+i) % bufferSize];
201        std::cout << std::endl;
202    }
203
204  protected:
205    /// Pointer to the beginning of the buffer
206    T * headPtr;
207
208    /// Head index
209    unsigned int i_head;
210
211    /// Read index
212    unsigned int i_read;
213
214    /// Memory slots allocated to the buffer
215    unsigned int bufferSize;
216
217    /// Number of elements currently in the buffer
218    unsigned int numElements;
219
220    /// \brief Linearize buffer array
221    ///
222    /// Shifts the elements in the buffer so that they are organized linearly
223    /// rather than circularly.  If the buffer is not empty, Linearize creates
224    /// a new array and copies the old values.
225    void Linearize();
226
227};
228
229//-----------------------------------------------------------------------------
230//
231// Automatic Gain Control class
232//
233//-----------------------------------------------------------------------------
234/** \brief Automatic gain control signal processor
235 *
236 * \cite  R. G. Lyons, Understanding Digital Signal Processing, 2nd ed. New Jersey:
237 * Prentice Hall, 2004.
238 */
239class AutomaticGainControl
240{
241  public:
242    /// default constructor
243    AutomaticGainControl();
244
245    /// Destructor
246    ~AutomaticGainControl();
247
248    /// Set signal processing values
249    void SetValues(
250            float _elo,
251            float _ehi,
252            float _ka,
253            float _kr,
254            float _gmin,
255            float _gmax);
256
257    /// Get signal processing values
258    void GetValues(
259            float & _elo,
260            float & _ehi,
261            float & _ka,
262            float & _kr,
263            float & _gmin,
264            float & _gmax);
265
266    /// Get status
267    void GetStatus(float & _gain, float & _energy);
268
269    /// track signal energy and apply gain (real)
270    void ApplyGain(short & I);
271
272    /// track signal energy and apply gain (complex)
273    void ApplyGain(short & I, short & Q);
274
275  private:
276    /// disallow copy constructor
277    AutomaticGainControl(AutomaticGainControl &);
278
279    /// compute necessary gain value from measured energy
280    void ComputeGain();
281
282    /// low energy threshold
283    float energy_lo;
284
285    /// high energy threshold
286    float energy_hi;
287
288    /// attack time constant
289    float ka;
290
291    /// release time constant
292    float kr;
293
294    /// minimum gain value
295    float gmin;
296
297    /// maximum gain value
298    float gmax;
299
300    /// actual tracking gain value
301    float gain;
302
303    /// actual tracking average energy value
304    float energy;
305
306    /// low-pass filter coefficient for estimating average energy
307    float zeta;
308
309    /// average energy threshold for smoother tracking
310    float energy_av;
311
312};
313
314class phase_detect {
315
316 public:
317    phase_detect(float scale_factor);
318
319    void do_work(short I_in, short Q_in, short I_nco, short Q_nco, short &out);
320
321 private:
322    phase_detect(const phase_detect &);
323
324    float scale_factor;
325};
326
327
328class nco {
329  public:
330    nco();
331    nco(unsigned int max_out);
332
333    void do_work(short control_voltage, short &sine, short &cosine);
334
335  private:
336    nco(const nco &);
337
338    int freq_index;
339    int max_out;
340};
341
342class gain {
343  public:
344    gain();
345
346    void do_work(float gain, short data_in, short &out);
347
348  private:
349    gain(const gain &);
350
351};
352
353class iir_filter {
354  public:
355    iir_filter(float a[], unsigned int len_a, float b[], unsigned int len_b);
356
357    void do_work(short x, short &y);
358
359    void ResetBuffer();
360
361  private:
362    iir_filter(const iir_filter &);
363
364    float *A;
365    float *B;
366    unsigned int len_A, len_B;
367
368    float *v;
369    unsigned int len_v;
370    unsigned int next_v;
371};
372
373
374//-----------------------------------------------------------------------------
375//
376// FIR polyphase filter bank
377//
378//-----------------------------------------------------------------------------
379/** \brief Finite impulse response (FIR) polyphase filter bank
380 *
381 * This class implementes a finite impulse response (FIR) polyphase filter
382 * bank useful for decimators that need to interpolate samples in digital
383 * receivers.
384 *
385 * The filter bank can automatically calculate filter coefficients for
386 * prototypes commonly used in communications systems. Currently, such
387 * supported filter prototypes are
388 *   - root raised-cosine
389 *
390 * Filter prototypes that will eventually be supported are
391 *   - raised-cosine
392 *   - gaussian
393 *   - triangular
394 *   - hamming
395 *
396 * The user can also load filter coefficients that have been calculated
397 * externally.
398 *
399 * \image latex polyphase_rcos_filter_k2_N4.eps "Example filter bank (1)"
400 * \image html polyphase_rcos_filter_k2_N4.png  "Example filter bank (1)"
401 *
402 * \image latex polyphase_rcos_filter_k4_N2.eps "Example filter bank (2)"
403 * \image html polyphase_rcos_filter_k4_N2.png  "Example filter bank (2)"
404 *
405 * \cite M. Rice and fred harris, "Polyphase Filterbanks for Symbol Timing
406 * Synchronization in Sampled Data Receivers," in MILCOMM Proceedings, vol.
407 * 2, October 2002, pp. 982--986.
408 *
409 */
410class FIRPolyphaseFilterBank {
411  public:
412    /// \brief Initializing constructor
413    ///
414    /// This constructor calculates the filter coefficients for several
415    /// different filter types using just a few parameters.  The filters
416    /// currently supported are:
417    ///   - 'rrcos'  : square-root raised-cosine (RRC)
418    ///   - 'drrcos' : derivative RRC
419    ///
420    FIRPolyphaseFilterBank(
421            char * _type,       // type of filter
422            unsigned int _k,    // samples per symbol
423            unsigned int _m,    // delay
424            float _beta,        // excess bandwidth factor
425            unsigned int _Npfb  // number of filters
426            );
427
428    /// \brief Initializing constructor
429    ///
430    /// This constructor loads filter bank coefficients which have
431    /// been generated externally.  The coefficients are copied from
432    /// the input array to a new buffer.
433    FIRPolyphaseFilterBank(
434            float * _H,         // filter bank coefficients
435            unsigned int _h_len,// length of each filter
436            unsigned int _Npfb  // number of filters
437            );
438
439    /// destructor
440    ~FIRPolyphaseFilterBank();
441
442    /// Push input value into buffer
443    void PushInput(short _x);
444
445    /// Compute filter output from current buffer state using specific
446    /// filter from filter bank matrix
447    void ComputeOutput(
448            short &y,           // output sample
449            unsigned int _b     // filter bank index
450            );
451
452    /// Compute filter output from current buffer state using specific
453    /// filter from filter bank matrix and an external memory buffer
454    void ComputeOutput(
455            short &y,           // output sample
456            unsigned int _b,    // filter bank index
457            short *_v           // external memory buffer array
458            );
459
460    /// Reset filter buffer
461    void ResetBuffer();
462
463    /// Print filter buffer
464    void PrintBuffer();
465
466    /// Prints filter bank coefficients to the screen
467    void PrintFilterBankCoefficients();
468
469    /// Get the length of each filter
470    unsigned int GetFilterLength() { return h_len; }
471
472    /// Get the number of filters in the bank
473    unsigned int GetNumFilters() { return Npfb; }
474
475    /// Return a pointer to the filter bank coefficients; this is intended
476    /// for debugging
477    float * GetFilterBankCoefficients() { return H; }
478
479  protected:
480
481    /// type of filter; can be one of the following
482    ///   - 'rrcos'
483    ///   - 'gaussian'
484    char * type;
485
486    /// samples per symbol
487    unsigned int k;
488
489    /// symbol delay
490    unsigned int m;
491
492    /// excess bandwidth factor
493    float beta;
494
495    /// number of filters in bank
496    unsigned int Npfb;
497
498    /// \brief filter bank coefficients matrix
499    ///
500    /// The coefficients are stored in a one-dimensional array which
501    /// is realized as a two-dimensional matrix.  The array is of
502    /// length Npfb*h_len (the number of filters in the bank times
503    /// the length of each filter).
504    float *H;
505
506    /// length of each filter
507    unsigned int h_len;
508
509    /// circular input buffer
510    CircularBuffer <short> v;
511
512    /// transpose filter bank coefficient matrix
513    void TransposeCoefficientMatrix();
514
515    // ----- calculate filter bank coefficients -----
516
517    /// Calculate root raised-cosine coefficients
518    void CalculateRRCFilterCoefficients();
519
520    /// Calculate Gaussian filter coefficients
521    void CalculateGaussianFilterCoefficients();
522
523    /// \brief Calculate derivative filter coefficients
524    ///
525    /// Approximates the derivative of the template filter
526    /// \f[ \dot{h}(nT) = \frac{\partial h(nT)}{\partial t} \f]
527    ///
528    /// using discrete samples, viz.
529    /// \f[ \dot{h}_m(nT)     = h_{m+1}(nT) - h_{m-1}(nT), \ \ m=1,2,\ldots,...M-2 \f]
530    /// \f[ \dot{h}_0(nT)     = h_{1}(nT) - h_{M-1}(nT)\f]
531    /// \f[ \dot{h}_{M-1}(nT) = h_{M-2}(nT) - h_{0}(nT)\f]
532    ///
533    void CalculateDerivativeFilterCoefficients();
534
535  private:
536
537    /// disallow copy constructor
538    FIRPolyphaseFilterBank(const FIRPolyphaseFilterBank&);
539
540};
541
542
543//-----------------------------------------------------------------------------
544//
545// Dot product definitions
546//
547//-----------------------------------------------------------------------------
548void dot_product(short *x, short *y, unsigned int N, short &z);
549void dot_product(float *x, short *y, unsigned int N, short &z);
550
551//-----------------------------------------------------------------------------
552//
553// Trigonometric functions
554//
555//-----------------------------------------------------------------------------
556
557/// Rotates a complex signal counter-clockwise by \f[\theta\f] radians
558/// \f[ \bar{y} = \bar{x} e^{j\theta} \f]
559void rotate(short I_in, short Q_in, float theta, short *I_out, short *Q_out);
560
561//-----------------------------------------------------------------------------
562//
563// Random number generators
564//
565//-----------------------------------------------------------------------------
566
567/// Uniform random number generator, (0,1]
568float randf();
569
570/// Gaussian random number generator, N(0,1)
571void randnf(float * i, float * q);
572
573
574
575
576class fir_filter {
577  public:
578    fir_filter(float a[], unsigned int len_a);
579
580    void do_work(bool run_filter, short in_sample, short &out_sample);
581    void reset();
582
583  private:
584    fir_filter(const fir_filter &);
585
586#ifdef FPM
587    mad_fixed_t *A;
588    mad_fixed_t *v;
589#else
590    float *A;
591    short *v;
592#endif
593    unsigned int len_A;
594
595    unsigned int len_v;
596    unsigned int next_v;
597};
598
599class dump_data {
600 public:
601  dump_data(const char *filename, long start_sample, long number_of_samples);
602  ~dump_data();
603
604  void write_data(float data, const char *msg = "");
605  void write_data(float a, float b, const char *msg = "");
606
607 private:
608  dump_data();
609  dump_data(const dump_data &);
610
611  std::ofstream *out_file;
612
613  long start_sample, stop_sample;
614  long current_sample;
615
616};
617
618class dc_block {
619  public:
620    dc_block(const float forget_factor);
621    ~dc_block();
622
623    void do_work(short in, short &out);
624
625  private:
626    dc_block();
627    dc_block(const dc_block &);
628
629    float forget_factor;
630    int prev_input, prev_output;
631
632};
633
634//-----------------------------------------------------------------------------
635//
636// Circular buffer definitions
637//
638//-----------------------------------------------------------------------------
639
640// Initializing constructor (empty)
641template <class T>
642CircularBuffer<T>::CircularBuffer()
643{
644    bufferSize = 1;
645    numElements = 0;
646    i_head = 0;
647    i_read = 0;
648    headPtr = new T[bufferSize];
649}
650
651// Initializing constructor (empty)
652template <class T>
653CircularBuffer<T>::CircularBuffer(unsigned int _bufferSize) {
654    bufferSize = _bufferSize;
655    numElements = 0;
656    i_head = 0;
657    i_read = 0;
658    headPtr = new T[bufferSize];
659}
660
661// Initializing constructor (array)
662template <class T>
663CircularBuffer<T>::CircularBuffer(T * _v, unsigned int _bufferSize) {
664    bufferSize = _bufferSize;
665    numElements = 0;
666    i_head = 0;
667    i_read = 0;
668    headPtr = new T[bufferSize];
669    for (unsigned int i=0; i<bufferSize; i++)
670        Push( _v[i] );
671}
672
673// Copy constructor
674template <class T>
675CircularBuffer<T>::CircularBuffer(CircularBuffer & _cb) {
676    bufferSize = _cb.bufferSize;
677    numElements = _cb.numElements;
678    i_head = _cb.i_head;
679    i_read = _cb.i_read;
680    headPtr = new T[bufferSize];
681    for (unsigned int i=0; i<bufferSize; i++)
682        headPtr[i] = _cb.headPtr[i];
683}
684
685// Set the buffer size dynamically
686template <class T>
687void CircularBuffer<T>::SetBufferSize(unsigned int _bufferSize) {
688    if ( _bufferSize < 1 ) {
689        std::cerr << "ERROR: SigProc::CircularBuffer::SetBufferSize()" << std::endl
690                  << "  => minimum buffer size is 1" << std::endl;
691        throw 0;
692    }
693
694    if ( _bufferSize == bufferSize ) {
695        // Nothing to do
696        return;
697    } else if ( _bufferSize < bufferSize && numElements > _bufferSize ) {
698        // New buffer is too small: copy only newest elements, discard oldest
699        i_read = ( i_read + numElements - _bufferSize ) % bufferSize;
700        numElements = _bufferSize;
701    } else {
702        // New buffer is sufficiently large: copy everything
703    }
704
705    // allocate new buffer memory
706    T * tmpHeadPtr = new T[_bufferSize];
707
708    for (unsigned int i=0; i<numElements; i++)
709        tmpHeadPtr[i] = headPtr[i_read++ % bufferSize];
710
711    // delete old buffer
712    delete [] headPtr;
713
714    headPtr = tmpHeadPtr;
715    i_head = numElements % bufferSize;
716    i_read = 0;
717
718    bufferSize = _bufferSize;
719}
720
721// Linearize buffer
722template <class T>
723void CircularBuffer<T>::Linearize() {
724    if ( numElements == 0 )
725        return;
726
727    T * tmpHeadPtr = new T[bufferSize];
728
729    for (unsigned int i=0; i<numElements; i++)
730        tmpHeadPtr[i] = headPtr[i_read++ % bufferSize];
731
732    delete [] headPtr;
733    headPtr = tmpHeadPtr;
734    i_head = numElements % bufferSize;
735    i_read = 0;
736}
737
738
739///
740enum ModulationScheme {
741        UNKNOWN,                                // Unknown modulation scheme
742        BPSK,   QPSK,   PSK8,   PSK16,          // Phase shift keying
743        DBPSK,  DQPSK,  DPSK8,  DPSK16,         // Differential PSK
744        PAM4,   PAM8,   PAM16,  PAM32,          // Pulse amplitude modulation
745        BFSK,   FSK4,   FSK8,   FSK16,          // Frequency shift keying
746        QAM4, QAM16,  QAM32,  QAM64,  QAM128, QAM256  // Quadrature amplitude modulation
747        };
748
749#define BPSK_LEVEL      10000   ///< BPSK amplitude (RMS=10000)
750#define QPSK_LEVEL      10000   ///< QPSK amplitude (RMS=10000)
751#define QAM4_LEVEL      7071    ///< QAM4 amplitude (RMS=10000)
752#define PSK8_LEVEL_1    7071    ///< Low 8-PSK amplitude (RMS=10000)
753#define PSK8_LEVEL_2    10000   ///< High 8-PSK amplitude (RMS=10000)
754#define QAM16_LEVEL_1   3162    ///< Low 16-QAM amplitude (RMS=10000)
755#define QAM16_LEVEL_2   9487    ///< High 16-QAM amplitude (RMS=10000)
756#define PAM4_LEVEL_1    4472    ///< Low 4-PAM amplitude (RMS=10000)
757#define PAM4_LEVEL_2    13416   ///< High 4-PAM amplitude (RMS=10000)
758
759/// Modulates a symbol into an I/Q pair for binary phase shift keying
760///
761/// \image latex ConstellationBPSK.eps "BPSK constellation"
762/// \image html ConstellationBPSK.png "BPSK constellation"
763void ModulateBPSK(short symbol_in, short &I_out, short &Q_out);
764
765/// Modulates a symbol into an I/Q pair for quadrature phase shift keying
766///
767// \image latex ConstellationQPSK.eps "QPSK constellation"
768// \image html ConstellationQPSK.png "QPSK constellation"
769void ModulateQPSK(short symbol_in, short &I_out, short &Q_out);
770
771/// Modulates a symbol into an I/Q pair for quadrature amplitude shift keying
772///
773/// \image latex ConstellationQAM4.eps "QAM4 constellation"
774/// \image html ConstellationQAM4.png "QAM4 constellation"
775void ModulateQAM4(short symbol_in, short &I_out, short &Q_out);
776
777/// Modulates a symbol into an I/Q pair for 8-ary phase shift keying
778///
779/// \image latex Constellation8PSK.eps "8-PSK constellation"
780/// \image html Constellation8PSK.png "8-PSK constellation"
781void Modulate8PSK(short symbol_in, short &I_out, short &Q_out);
782
783/// Modulates a symbol into an I/Q pair for 16-point quadrature
784/// amplitude modulation
785///
786/// \image latex Constellation16QAM.eps "16-QAM constellation"
787/// \image html Constellation16QAM.png "16-QAM constellation"
788void Modulate16QAM(short symbol_in, short &I_out, short &Q_out);
789
790/// Modulates a symbol into an I/Q pair for 4-ary pulse amplitude
791/// modulation
792///
793/// \image latex Constellation4PAM.eps "4-PAM constellation"
794/// \image html Constellation4PAM.png "4-PAM constellation"
795void Modulate4PAM(short symbol_in, short &I_out, short &Q_out);
796
797
798#define QAM16_THRESHOLD 6324    ///< 16-QAM threshold for RMS=10000 signal
799#define PAM4_THRESHOLD  8944    ///< 4-PAM threshold for RMS=10000 signal
800
801///
802void DemodulateBPSK(short I_in, short Q_in, short &symbol_out);
803
804///
805void DemodulateQPSK(short I_in, short Q_in, short &symbol_out);
806
807///
808void DemodulateQAM4(short I_in, short Q_in, short &symbol_out);
809
810///
811void Demodulate8PSK(short I_in, short Q_in, short &symbol_out);
812
813///
814void Demodulate16QAM(short I_in, short Q_in, short &symbol_out);
815
816///
817void Demodulate4PAM(short I_in, short Q_in, short &symbol_out);
818
819}
820#endif
821
822
Note: See TracBrowser for help on using the browser.