RealDftNormalization.h
Go to the documentation of this file.
1 // RealDftNormalization.h
2 //
3 // David Adams
4 // April 2019, December 2020
5 //
6 // This class provides an interface to specify and interpret the normalization
7 // of one dimension of a DFT (discrete Fourier transform) of real data.
8 //
9 // The 1D DFT for N samples x has N complex entries aka frequency components. With standard
10 // normalization (see below) the kth component of the DFT is
11 // X_k = SUM_m x_m exp(-2*pi*i*m*k/N)
12 // Here, the 2N values that specify the real and imaginary components of X are called
13 // the long representation of the DFT because only N values are needed in the case of
14 // interest here where the original data x is purely real.
15 //
16 // The redundancy in the these 2*N values is reflected in the mirror (or alias) relation
17 // X[N-i] = X*[i]
18 // where X* denotes complex conjugation.
19 //
20 // We define a compact representation that omits the later aliased entries. There are two
21 // shorter vectors with one holding amplitudes and the second holding phases.
22 // For N even, the amplitude vector has length N/2+1 and the phase vector is length N/2.
23 // For N odd, both vectors have the same length: N/2+1 = (N+1)/2.
24 // Equivalently for any N, the respective lengths are N/2+1 and (N+1)/2.
25 // Thus the length of the original data may be deduced from the lengths of the
26 // amplitude and phase vectors.
27 // The first phase is always zero or pi (the first term is real) leaving N significant values
28 // for N even or odd.
29 //
30 // There is ambiguity in the sign of the amplitude as flipping the sign is equivalent to
31 // adding pi to the correponding phase. Thus, for most of the entries, then amplitude may
32 // be chosen positive. The one exception is the last entry for N even. There is no corresponding
33 // phase and so that amplitude may be forced to be negative.
34 //
35 // The values in the long representation may be obtained from those in the compact.
36 // Denoting the real and imaginary vectors by real and imag and the amplitude and phase
37 // vectors by amp and pha with respective lengths Namp and Npha:
38 // real[0] = amp[0]*cos(pha[0]), imag[0] = 0
39 // real[i] = K*amp[i]*cos(pha[i]), imag[i] = K*amp[i]*sin(pha[i]) i < Npha
40 // real[i] = amp[i]*cos(pha[i]), imag[i] = 0 i = Npha = N/2, N even
41 // real[i] = K*amp[N-i]*cos(pha[N-i]), imag[i] = -K*amp[N-i]*sin(pha[N-i]) i > Npha
42 //
43 // We support two term normalizations in the above expressions:
44 // termNormalization:
45 // 1 - Unit: K = 1
46 // 2 - Power: K = 1/sqrt(2)
47 //
48 // In the former case, the amplitude for any frequency component has the same magnitude as the
49 // corresponding term in the long representation. Convolution may be accomplished by directly
50 // multiplying terms in the compact representation. Evaluation of the total power requires a sum
51 // over all terms in the long representation or a double counting of aliased terms in the
52 // compact representation.
53 //
54 // In the latter case, the amplitude for a component includes the power contribution for its
55 // alias, if present, i.e. the amplitude is increased by sqrt(2) for terms that have aliases.
56 // The total power may be obtained directly by summing the (squares of the) amplitudes in the
57 // compact representation. Aliased terms must be corrected for double counting if both DFTs
58 // have power normalization.
59 //
60 // There are also a few natural conventions for global normalization of the transformed data:
61 // globalNormalization:
62 // 1 - Standard ==> integrated raw power of transform is N times larger than input.
63 // Normalization of 1/N is applied on inverse to recover original waveform.
64 // 2 - Consistent ==> Integrated power of transform or inverse is the same as its input.
65 // I.e. normalization of 1/sqrt(N) is applied both forward and backward.
66 // 3 - Bin ==> Integrated power of transform is 1/N times input.
67 // Normalization of 1/N on forward transform and one on inverse.
68 //
69 // Unlike the term normalization, this normalization is relevant to both both the long
70 // and compact representations.
71 //
72 // Complete specification of the normalization requires both of these values, here denoted
73 // globalNormalization-termNormalization, e.g. standard-unit for standard global normalization
74 // and unit term normalization.
75 //
76 // Convolution of two DFTs is simple multiplication of their terms and the result has the same
77 // normalization as the first if the second has standard-unit normalization aka convolution
78 // normalization. Corrections to aliased or all terms are required for other normalizations.
79 //
80 // We anticipate data transforms will use consistent-power normalization and that convolution
81 // transforms (response, filter, smearing, etc) will use the standard-uni normalization.
82 // This way power can be obtained directly from data transforms and the (de)covolution of data
83 // with a convolution function is obtained by direct multiplication (division) of the compact
84 // entries.
85 
86 #ifndef RealDftNormalization_H
87 #define RealDftNormalization_H
88 
89 #include <string>
90 #include <vector>
91 #include <cmath>
92 
94 
95 public:
96 
97  using Index = unsigned int;
98 
101 
102  // Normalization conversion from an index.
104  if ( iarg == 1 ) return Standard;
105  if ( iarg == 2 ) return Consistent;
106  if ( iarg == 3 ) return Bin;
108  }
110  if ( iarg == 1 ) return Unit;
111  if ( iarg == 2 ) return Power;
113  }
114 
115  // Ctors from the data that specifies the two normalizations.
116  // The data can be the two enums, the corresponding int or a single packed int:
117  // 10*term + global.
119  : m_global(gnorm), m_term(tnorm) { }
122  // Conversion from a single index: 10*term + global.
123  explicit RealDftNormalization(Index inorm) : RealDftNormalization(inorm%10, inorm/10) { }
124 
125  // Return the global normalization.
127  bool isStandard() const { return globalNormalization() == Standard; }
128  bool isConsistent() const { return globalNormalization() == Consistent; }
129  bool isBin() const { return globalNormalization() == Bin; }
131  if ( isStandard() ) return "standard";
132  if ( isConsistent() ) return "consistent";
133  if ( isBin() ) return "bin";
134  return "invalid";
135  }
136 
137  // Return the term normalization.
139  bool isUnit() const { return termNormalization() == Unit; }
140  bool isPower() const { return termNormalization() == Power; }
142  if ( isUnit() ) return "unit";
143  if ( isPower() ) return "power";
144  return "invalid";
145  }
146 
147  // Check normalization is valid.
148  bool hasValidGlobalNormalization() const { return isStandard() || isConsistent() || isBin(); }
149  bool hasValidTermNormalization() const { return isUnit() || isPower(); }
151 
152  // Equality.
153  bool operator==(const RealDftNormalization& rhs) {
154  if ( ! isValid() ) return false;
155  if ( ! rhs.isValid() ) return false;
156  if ( globalNormalization() != rhs.globalNormalization() ) return false;
157  if ( termNormalization() != rhs.termNormalization() ) return false;
158  return true;
159  }
160 
161  // Normalization to use for convolution.
163 
164 private: // data
165 
168 
169 };
170 
171 //**********************************************************************
172 
173 #endif
TermNormalization termNormalization() const
GlobalNormalization m_global
std::string string
Definition: nybbler.cc:12
bool hasValidTermNormalization() const
std::string termName() const
RealDftNormalization(GlobalNormalization gnorm, TermNormalization tnorm)
static RealDftNormalization convolutionNormalization()
static TermNormalization getTermNormalization(Index iarg)
TermNormalization m_term
bool hasValidGlobalNormalization() const
static GlobalNormalization getGlobalNormalization(Index iarg)
RealDftNormalization(Index ignorm, Index itnorm)
GlobalNormalization globalNormalization() const
std::string globalName() const
bool operator==(const RealDftNormalization &rhs)