root/SigProc/trunk/SigProc/SigProc.h @ 4146

Revision 4146, 20.4 KB (checked in by jgaeddert, 6 years ago)

Cleaning up documentation, adding Gaussian filter design (needs testing)

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