root/SigProc/trunk/SigProc/SigProc.h @ 4019

Revision 4019, 19.1 KB (checked in by jgaeddert, 6 years ago)

moving from const short to #define constants; updating Doxyfile

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