comparison data/model/test/TestFFTModel.h @ 1744:b92bdcd4954b by-id

Update FFT model to ById
author Chris Cannam
date Tue, 02 Jul 2019 11:49:28 +0100
parents 0e9840a381b5
children 6d09d68165a4
comparison
equal deleted inserted replaced
1743:7001b9570e37 1744:b92bdcd4954b
33 class TestFFTModel : public QObject 33 class TestFFTModel : public QObject
34 { 34 {
35 Q_OBJECT 35 Q_OBJECT
36 36
37 private: 37 private:
38 void test(DenseTimeValueModel *model, 38 void test(ModelId model, // a DenseTimeValueModel
39 WindowType window, int windowSize, int windowIncrement, int fftSize, 39 WindowType window, int windowSize, int windowIncrement, int fftSize,
40 int columnNo, vector<vector<complex<float>>> expectedValues, 40 int columnNo, vector<vector<complex<float>>> expectedValues,
41 int expectedWidth) { 41 int expectedWidth) {
42 for (int ch = 0; in_range_for(expectedValues, ch); ++ch) { 42 for (int ch = 0; in_range_for(expectedValues, ch); ++ch) {
43 FFTModel fftm(model, ch, window, windowSize, windowIncrement, fftSize); 43 FFTModel fftm(model, ch, window, windowSize, windowIncrement, fftSize);
86 QCOMPARE(imags[hs1], 999.f); 86 QCOMPARE(imags[hs1], 999.f);
87 } 87 }
88 } 88 }
89 } 89 }
90 90
91 ModelId makeMock(std::vector<Sort> sorts, int length, int pad) {
92 auto mwm = std::make_shared<MockWaveModel>(sorts, length, pad);
93 ModelById::add(mwm);
94 return mwm->getId();
95 }
96
97 void releaseMock(ModelId id) {
98 ModelById::release(id);
99 }
100
91 private slots: 101 private slots:
92 102
93 // NB. FFTModel columns are centred on the sample frame, and in 103 // NB. FFTModel columns are centred on the sample frame, and in
94 // particular this means column 0 is centred at sample 0 (i.e. it 104 // particular this means column 0 is centred at sample 0 (i.e. it
95 // contains only half the window-size worth of real samples, the 105 // contains only half the window-size worth of real samples, the
99 // (rather than something with a step in it that is harder to 109 // (rather than something with a step in it that is harder to
100 // reason about the FFT of) and the results for subsequent columns 110 // reason about the FFT of) and the results for subsequent columns
101 // are those of our expected signal. 111 // are those of our expected signal.
102 112
103 void dc_simple_rect() { 113 void dc_simple_rect() {
104 MockWaveModel mwm({ DC }, 16, 4); 114 auto mwm = makeMock({ DC }, 16, 4);
105 test(&mwm, RectangularWindow, 8, 8, 8, 0, 115 test(mwm, RectangularWindow, 8, 8, 8, 0,
106 { { {}, {}, {}, {}, {} } }, 4); 116 { { {}, {}, {}, {}, {} } }, 4);
107 test(&mwm, RectangularWindow, 8, 8, 8, 1, 117 test(mwm, RectangularWindow, 8, 8, 8, 1,
108 { { { 4.f, 0.f }, {}, {}, {}, {} } }, 4); 118 { { { 4.f, 0.f }, {}, {}, {}, {} } }, 4);
109 test(&mwm, RectangularWindow, 8, 8, 8, 2, 119 test(mwm, RectangularWindow, 8, 8, 8, 2,
110 { { { 4.f, 0.f }, {}, {}, {}, {} } }, 4); 120 { { { 4.f, 0.f }, {}, {}, {}, {} } }, 4);
111 test(&mwm, RectangularWindow, 8, 8, 8, 3, 121 test(mwm, RectangularWindow, 8, 8, 8, 3,
112 { { {}, {}, {}, {}, {} } }, 4); 122 { { {}, {}, {}, {}, {} } }, 4);
123 releaseMock(mwm);
113 } 124 }
114 125
115 void dc_simple_hann() { 126 void dc_simple_hann() {
116 // The Hann window function is a simple sinusoid with period 127 // The Hann window function is a simple sinusoid with period
117 // equal to twice the window size, and it halves the DC energy 128 // equal to twice the window size, and it halves the DC energy
118 MockWaveModel mwm({ DC }, 16, 4); 129 auto mwm = makeMock({ DC }, 16, 4);
119 test(&mwm, HanningWindow, 8, 8, 8, 0, 130 test(mwm, HanningWindow, 8, 8, 8, 0,
120 { { {}, {}, {}, {}, {} } }, 4); 131 { { {}, {}, {}, {}, {} } }, 4);
121 test(&mwm, HanningWindow, 8, 8, 8, 1, 132 test(mwm, HanningWindow, 8, 8, 8, 1,
122 { { { 4.f, 0.f }, { 2.f, 0.f }, {}, {}, {} } }, 4); 133 { { { 4.f, 0.f }, { 2.f, 0.f }, {}, {}, {} } }, 4);
123 test(&mwm, HanningWindow, 8, 8, 8, 2, 134 test(mwm, HanningWindow, 8, 8, 8, 2,
124 { { { 4.f, 0.f }, { 2.f, 0.f }, {}, {}, {} } }, 4); 135 { { { 4.f, 0.f }, { 2.f, 0.f }, {}, {}, {} } }, 4);
125 test(&mwm, HanningWindow, 8, 8, 8, 3, 136 test(mwm, HanningWindow, 8, 8, 8, 3,
126 { { {}, {}, {}, {}, {} } }, 4); 137 { { {}, {}, {}, {}, {} } }, 4);
138 releaseMock(mwm);
127 } 139 }
128 140
129 void dc_simple_hann_halfoverlap() { 141 void dc_simple_hann_halfoverlap() {
130 MockWaveModel mwm({ DC }, 16, 4); 142 auto mwm = makeMock({ DC }, 16, 4);
131 test(&mwm, HanningWindow, 8, 4, 8, 0, 143 test(mwm, HanningWindow, 8, 4, 8, 0,
132 { { {}, {}, {}, {}, {} } }, 7); 144 { { {}, {}, {}, {}, {} } }, 7);
133 test(&mwm, HanningWindow, 8, 4, 8, 2, 145 test(mwm, HanningWindow, 8, 4, 8, 2,
134 { { { 4.f, 0.f }, { 2.f, 0.f }, {}, {}, {} } }, 7); 146 { { { 4.f, 0.f }, { 2.f, 0.f }, {}, {}, {} } }, 7);
135 test(&mwm, HanningWindow, 8, 4, 8, 3, 147 test(mwm, HanningWindow, 8, 4, 8, 3,
136 { { { 4.f, 0.f }, { 2.f, 0.f }, {}, {}, {} } }, 7); 148 { { { 4.f, 0.f }, { 2.f, 0.f }, {}, {}, {} } }, 7);
137 test(&mwm, HanningWindow, 8, 4, 8, 6, 149 test(mwm, HanningWindow, 8, 4, 8, 6,
138 { { {}, {}, {}, {}, {} } }, 7); 150 { { {}, {}, {}, {}, {} } }, 7);
151 releaseMock(mwm);
139 } 152 }
140 153
141 void sine_simple_rect() { 154 void sine_simple_rect() {
142 MockWaveModel mwm({ Sine }, 16, 4); 155 auto mwm = makeMock({ Sine }, 16, 4);
143 // Sine: output is purely imaginary. Note the sign is flipped 156 // Sine: output is purely imaginary. Note the sign is flipped
144 // (normally the first half of the output would have negative 157 // (normally the first half of the output would have negative
145 // sign for a sine starting at 0) because the model does an 158 // sign for a sine starting at 0) because the model does an
146 // FFT shift to centre the phase 159 // FFT shift to centre the phase
147 test(&mwm, RectangularWindow, 8, 8, 8, 0, 160 test(mwm, RectangularWindow, 8, 8, 8, 0,
148 { { {}, {}, {}, {}, {} } }, 4); 161 { { {}, {}, {}, {}, {} } }, 4);
149 test(&mwm, RectangularWindow, 8, 8, 8, 1, 162 test(mwm, RectangularWindow, 8, 8, 8, 1,
150 { { {}, { 0.f, 2.f }, {}, {}, {} } }, 4); 163 { { {}, { 0.f, 2.f }, {}, {}, {} } }, 4);
151 test(&mwm, RectangularWindow, 8, 8, 8, 2, 164 test(mwm, RectangularWindow, 8, 8, 8, 2,
152 { { {}, { 0.f, 2.f }, {}, {}, {} } }, 4); 165 { { {}, { 0.f, 2.f }, {}, {}, {} } }, 4);
153 test(&mwm, RectangularWindow, 8, 8, 8, 3, 166 test(mwm, RectangularWindow, 8, 8, 8, 3,
154 { { {}, {}, {}, {}, {} } }, 4); 167 { { {}, {}, {}, {}, {} } }, 4);
168 releaseMock(mwm);
155 } 169 }
156 170
157 void cosine_simple_rect() { 171 void cosine_simple_rect() {
158 MockWaveModel mwm({ Cosine }, 16, 4); 172 auto mwm = makeMock({ Cosine }, 16, 4);
159 // Cosine: output is purely real. Note the sign is flipped 173 // Cosine: output is purely real. Note the sign is flipped
160 // because the model does an FFT shift to centre the phase 174 // because the model does an FFT shift to centre the phase
161 test(&mwm, RectangularWindow, 8, 8, 8, 0, 175 test(mwm, RectangularWindow, 8, 8, 8, 0,
162 { { {}, {}, {}, {}, {} } }, 4); 176 { { {}, {}, {}, {}, {} } }, 4);
163 test(&mwm, RectangularWindow, 8, 8, 8, 1, 177 test(mwm, RectangularWindow, 8, 8, 8, 1,
164 { { {}, { -2.f, 0.f }, {}, {}, {} } }, 4); 178 { { {}, { -2.f, 0.f }, {}, {}, {} } }, 4);
165 test(&mwm, RectangularWindow, 8, 8, 8, 2, 179 test(mwm, RectangularWindow, 8, 8, 8, 2,
166 { { {}, { -2.f, 0.f }, {}, {}, {} } }, 4); 180 { { {}, { -2.f, 0.f }, {}, {}, {} } }, 4);
167 test(&mwm, RectangularWindow, 8, 8, 8, 3, 181 test(mwm, RectangularWindow, 8, 8, 8, 3,
168 { { {}, {}, {}, {}, {} } }, 4); 182 { { {}, {}, {}, {}, {} } }, 4);
183 releaseMock(mwm);
169 } 184 }
170 185
171 void twochan_simple_rect() { 186 void twochan_simple_rect() {
172 MockWaveModel mwm({ Sine, Cosine }, 16, 4); 187 auto mwm = makeMock({ Sine, Cosine }, 16, 4);
173 // Test that the two channels are read and converted separately 188 // Test that the two channels are read and converted separately
174 test(&mwm, RectangularWindow, 8, 8, 8, 0, 189 test(mwm, RectangularWindow, 8, 8, 8, 0,
175 { 190 {
176 { {}, {}, {}, {}, {} }, 191 { {}, {}, {}, {}, {} },
177 { {}, {}, {}, {}, {} } 192 { {}, {}, {}, {}, {} }
178 }, 4); 193 }, 4);
179 test(&mwm, RectangularWindow, 8, 8, 8, 1, 194 test(mwm, RectangularWindow, 8, 8, 8, 1,
180 { 195 {
181 { {}, { 0.f, 2.f }, {}, {}, {} }, 196 { {}, { 0.f, 2.f }, {}, {}, {} },
182 { {}, { -2.f, 0.f }, {}, {}, {} } 197 { {}, { -2.f, 0.f }, {}, {}, {} }
183 }, 4); 198 }, 4);
184 test(&mwm, RectangularWindow, 8, 8, 8, 2, 199 test(mwm, RectangularWindow, 8, 8, 8, 2,
185 { 200 {
186 { {}, { 0.f, 2.f }, {}, {}, {} }, 201 { {}, { 0.f, 2.f }, {}, {}, {} },
187 { {}, { -2.f, 0.f }, {}, {}, {} } 202 { {}, { -2.f, 0.f }, {}, {}, {} }
188 }, 4); 203 }, 4);
189 test(&mwm, RectangularWindow, 8, 8, 8, 3, 204 test(mwm, RectangularWindow, 8, 8, 8, 3,
190 { 205 {
191 { {}, {}, {}, {}, {} }, 206 { {}, {}, {}, {}, {} },
192 { {}, {}, {}, {}, {} } 207 { {}, {}, {}, {}, {} }
193 }, 4); 208 }, 4);
209 releaseMock(mwm);
194 } 210 }
195 211
196 void nyquist_simple_rect() { 212 void nyquist_simple_rect() {
197 MockWaveModel mwm({ Nyquist }, 16, 4); 213 auto mwm = makeMock({ Nyquist }, 16, 4);
198 // Again, the sign is flipped. This has the same amount of 214 // Again, the sign is flipped. This has the same amount of
199 // energy as the DC example 215 // energy as the DC example
200 test(&mwm, RectangularWindow, 8, 8, 8, 0, 216 test(mwm, RectangularWindow, 8, 8, 8, 0,
201 { { {}, {}, {}, {}, {} } }, 4); 217 { { {}, {}, {}, {}, {} } }, 4);
202 test(&mwm, RectangularWindow, 8, 8, 8, 1, 218 test(mwm, RectangularWindow, 8, 8, 8, 1,
203 { { {}, {}, {}, {}, { -4.f, 0.f } } }, 4); 219 { { {}, {}, {}, {}, { -4.f, 0.f } } }, 4);
204 test(&mwm, RectangularWindow, 8, 8, 8, 2, 220 test(mwm, RectangularWindow, 8, 8, 8, 2,
205 { { {}, {}, {}, {}, { -4.f, 0.f } } }, 4); 221 { { {}, {}, {}, {}, { -4.f, 0.f } } }, 4);
206 test(&mwm, RectangularWindow, 8, 8, 8, 3, 222 test(mwm, RectangularWindow, 8, 8, 8, 3,
207 { { {}, {}, {}, {}, {} } }, 4); 223 { { {}, {}, {}, {}, {} } }, 4);
224 releaseMock(mwm);
208 } 225 }
209 226
210 void dirac_simple_rect() { 227 void dirac_simple_rect() {
211 MockWaveModel mwm({ Dirac }, 16, 4); 228 auto mwm = makeMock({ Dirac }, 16, 4);
212 // The window scales by 0.5 and some signs are flipped. Only 229 // The window scales by 0.5 and some signs are flipped. Only
213 // column 1 has any data (the single impulse). 230 // column 1 has any data (the single impulse).
214 test(&mwm, RectangularWindow, 8, 8, 8, 0, 231 test(mwm, RectangularWindow, 8, 8, 8, 0,
215 { { {}, {}, {}, {}, {} } }, 4); 232 { { {}, {}, {}, {}, {} } }, 4);
216 test(&mwm, RectangularWindow, 8, 8, 8, 1, 233 test(mwm, RectangularWindow, 8, 8, 8, 1,
217 { { { 0.5f, 0.f }, { -0.5f, 0.f }, { 0.5f, 0.f }, { -0.5f, 0.f }, { 0.5f, 0.f } } }, 4); 234 { { { 0.5f, 0.f }, { -0.5f, 0.f }, { 0.5f, 0.f }, { -0.5f, 0.f }, { 0.5f, 0.f } } }, 4);
218 test(&mwm, RectangularWindow, 8, 8, 8, 2, 235 test(mwm, RectangularWindow, 8, 8, 8, 2,
219 { { {}, {}, {}, {}, {} } }, 4); 236 { { {}, {}, {}, {}, {} } }, 4);
220 test(&mwm, RectangularWindow, 8, 8, 8, 3, 237 test(mwm, RectangularWindow, 8, 8, 8, 3,
221 { { {}, {}, {}, {}, {} } }, 4); 238 { { {}, {}, {}, {}, {} } }, 4);
239 releaseMock(mwm);
222 } 240 }
223 241
224 void dirac_simple_rect_2() { 242 void dirac_simple_rect_2() {
225 MockWaveModel mwm({ Dirac }, 16, 8); 243 auto mwm = makeMock({ Dirac }, 16, 8);
226 // With 8 samples padding, the FFT shift places the first 244 // With 8 samples padding, the FFT shift places the first
227 // Dirac impulse at the start of column 1, thus giving all 245 // Dirac impulse at the start of column 1, thus giving all
228 // positive values 246 // positive values
229 test(&mwm, RectangularWindow, 8, 8, 8, 0, 247 test(mwm, RectangularWindow, 8, 8, 8, 0,
230 { { {}, {}, {}, {}, {} } }, 5); 248 { { {}, {}, {}, {}, {} } }, 5);
231 test(&mwm, RectangularWindow, 8, 8, 8, 1, 249 test(mwm, RectangularWindow, 8, 8, 8, 1,
232 { { { 0.5f, 0.f }, { 0.5f, 0.f }, { 0.5f, 0.f }, { 0.5f, 0.f }, { 0.5f, 0.f } } }, 5); 250 { { { 0.5f, 0.f }, { 0.5f, 0.f }, { 0.5f, 0.f }, { 0.5f, 0.f }, { 0.5f, 0.f } } }, 5);
233 test(&mwm, RectangularWindow, 8, 8, 8, 2, 251 test(mwm, RectangularWindow, 8, 8, 8, 2,
234 { { {}, {}, {}, {}, {} } }, 5); 252 { { {}, {}, {}, {}, {} } }, 5);
235 test(&mwm, RectangularWindow, 8, 8, 8, 3, 253 test(mwm, RectangularWindow, 8, 8, 8, 3,
236 { { {}, {}, {}, {}, {} } }, 5); 254 { { {}, {}, {}, {}, {} } }, 5);
237 test(&mwm, RectangularWindow, 8, 8, 8, 4, 255 test(mwm, RectangularWindow, 8, 8, 8, 4,
238 { { {}, {}, {}, {}, {} } }, 5); 256 { { {}, {}, {}, {}, {} } }, 5);
257 releaseMock(mwm);
239 } 258 }
240 259
241 void dirac_simple_rect_halfoverlap() { 260 void dirac_simple_rect_halfoverlap() {
242 MockWaveModel mwm({ Dirac }, 16, 4); 261 auto mwm = makeMock({ Dirac }, 16, 4);
243 test(&mwm, RectangularWindow, 8, 4, 8, 0, 262 test(mwm, RectangularWindow, 8, 4, 8, 0,
244 { { {}, {}, {}, {}, {} } }, 7); 263 { { {}, {}, {}, {}, {} } }, 7);
245 test(&mwm, RectangularWindow, 8, 4, 8, 1, 264 test(mwm, RectangularWindow, 8, 4, 8, 1,
246 { { { 0.5f, 0.f }, { 0.5f, 0.f }, { 0.5f, 0.f }, { 0.5f, 0.f }, { 0.5f, 0.f } } }, 7); 265 { { { 0.5f, 0.f }, { 0.5f, 0.f }, { 0.5f, 0.f }, { 0.5f, 0.f }, { 0.5f, 0.f } } }, 7);
247 test(&mwm, RectangularWindow, 8, 4, 8, 2, 266 test(mwm, RectangularWindow, 8, 4, 8, 2,
248 { { { 0.5f, 0.f }, { -0.5f, 0.f }, { 0.5f, 0.f }, { -0.5f, 0.f }, { 0.5f, 0.f } } }, 7); 267 { { { 0.5f, 0.f }, { -0.5f, 0.f }, { 0.5f, 0.f }, { -0.5f, 0.f }, { 0.5f, 0.f } } }, 7);
249 test(&mwm, RectangularWindow, 8, 4, 8, 3, 268 test(mwm, RectangularWindow, 8, 4, 8, 3,
250 { { {}, {}, {}, {}, {} } }, 7); 269 { { {}, {}, {}, {}, {} } }, 7);
270 releaseMock(mwm);
251 } 271 }
252 272
253 }; 273 };
254 274
255 #endif 275 #endif