1 /*
2  * Copyright (c) 2007 - 2015 Joseph Gaeddert
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20  * THE SOFTWARE.
21  */
22 
23 //
24 // Windowing functions
25 //
26 // References:
27 //  [Kaiser:1980] James F. Kaiser and Ronald W. Schafer, "On
28 //      the Use of I0-Sinh Window for Spectrum Analysis,"
29 //      IEEE Transactions on Acoustics, Speech, and Signal
30 //      Processing, vol. ASSP-28, no. 1, pp. 105--107,
31 //      February, 1980.
32 //  [harris:1978] frederic j. harris, "On the Use of Windows for Harmonic
33 //      Analysis with the Discrete Fourier Transform," Proceedings of the
34 //      IEEE, vol. 66, no. 1, January, 1978.
35 //  [Nuttall:1981] Albert H. Nuttall, "Some Windows with Very Good Sidelobe
36 //      Behavior,"  IEEE Transactions on Acoustics, Speech, and Signal
37 //      Processing, vol. ASSP-29, no. 1, pp. 84-91, February, 1981.
38 
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <math.h>
43 
44 #include "liquid.internal.h"
45 
46 const char * liquid_window_str[LIQUID_WINDOW_NUM_FUNCTIONS][2] = {
47     // short name,  long name
48     {"unknown",         "unknown"                   },
49     {"hamming",         "Hamming"                   },
50     {"hann",            "Hann"                      },
51     {"blackmanharris",  "Blackman-harris (4-term)"  },
52     {"blackmanharris7", "Blackman-harris (7-term)"  },
53     {"kaiser",          "Kaiser-Bessel"             },
54     {"flattop",         "flat top"                  },
55     {"triangular",      "triangular"                },
56     {"rcostaper",       "raised-cosine taper"       },
57     {"kbd",             "Kaiser-Bessel derived"     },
58 };
59 
60 // Print compact list of existing and available windowing functions
liquid_print_windows()61 void liquid_print_windows()
62 {
63     unsigned int i;
64     unsigned int len = 10;
65 
66     // print all available MOD schemes
67     printf("          ");
68     for (i=0; i<LIQUID_WINDOW_NUM_FUNCTIONS; i++) {
69         printf("%s", liquid_window_str[i][0]);
70 
71         if (i != LIQUID_WINDOW_NUM_FUNCTIONS-1)
72             printf(", ");
73 
74         len += strlen(liquid_window_str[i][0]);
75         if (len > 48 && i != LIQUID_WINDOW_NUM_FUNCTIONS-1) {
76             len = 10;
77             printf("\n          ");
78         }
79     }
80     printf("\n");
81 }
82 
83 // returns modulation_scheme based on input string
liquid_getopt_str2window(const char * _str)84 liquid_window_type liquid_getopt_str2window(const char * _str)
85 {
86     // compare each string to short name
87     unsigned int i;
88     for (i=0; i<LIQUID_WINDOW_NUM_FUNCTIONS; i++) {
89         if (strcmp(_str,liquid_window_str[i][0])==0) {
90             return i;
91         }
92     }
93 
94     fprintf(stderr,"warning: liquid_getopt_str2window(), unknown/unsupported window scheme : %s\n", _str);
95     return LIQUID_WINDOW_UNKNOWN;
96 }
97 
98 // Kaiser-Bessel derived window
liquid_kbd(unsigned int _n,unsigned int _N,float _beta)99 float liquid_kbd(unsigned int _n,
100                  unsigned int _N,
101                  float _beta)
102 {
103     // TODO add reference
104 
105     // validate input
106     if (_n >= _N) {
107         fprintf(stderr,"error: liquid_kbd(), index exceeds maximum\n");
108         exit(1);
109     } else if (_N == 0) {
110         fprintf(stderr,"error: liquid_kbd(), window length must be greater than zero\n");
111         exit(1);
112     } else if ( _N % 2 ) {
113         fprintf(stderr,"error: liquid_kbd(), window length must be odd\n");
114         exit(1);
115     }
116 
117     unsigned int M = _N / 2;
118     if (_n >= M)
119         return liquid_kbd(_N-_n-1,_N,_beta);
120 
121     float w0 = 0.0f;
122     float w1 = 0.0f;
123     float w;
124     unsigned int i;
125     for (i=0; i<=M; i++) {
126         // compute Kaiser window
127         w = kaiser(i,M+1,_beta,0.0f);
128 
129         // accumulate window sums
130         w1 += w;
131         if (i <= _n) w0 += w;
132     }
133     //printf("%12.8f / %12.8f = %12.8f\n", w0, w1, w0/w1);
134 
135     return sqrtf(w0 / w1);
136 }
137 
138 
139 // Kaiser-Bessel derived window (full window function)
liquid_kbd_window(unsigned int _n,float _beta,float * _w)140 void liquid_kbd_window(unsigned int _n,
141                        float _beta,
142                        float * _w)
143 {
144     unsigned int i;
145     // TODO add reference
146 
147     // validate input
148     if (_n == 0) {
149         fprintf(stderr,"error: liquid_kbd_window(), window length must be greater than zero\n");
150         exit(1);
151     } else if ( _n % 2 ) {
152         fprintf(stderr,"error: liquid_kbd_window(), window length must be odd\n");
153         exit(1);
154     } else if ( _beta < 0.0f ) {
155         fprintf(stderr,"error: liquid_kbd_window(), _beta must be positive\n");
156         exit(1);
157     }
158 
159     // compute half length
160     unsigned int M = _n / 2;
161 
162     // generate regular Kaiser window, length M+1
163     float w_kaiser[M+1];
164     for (i=0; i<=M; i++)
165         w_kaiser[i] = kaiser(i,M+1,_beta,0.0f);
166 
167     // compute sum(wk[])
168     float w_sum = 0.0f;
169     for (i=0; i<=M; i++)
170         w_sum += w_kaiser[i];
171 
172     // accumulate window
173     float w_acc = 0.0f;
174     for (i=0; i<M; i++) {
175         w_acc += w_kaiser[i];
176         _w[i] = sqrtf(w_acc / w_sum);
177     }
178 
179     // window has even symmetry; flip around index M
180     for (i=0; i<M; i++)
181         _w[_n-i-1] = _w[i];
182 }
183 
184 
185 // Kaiser window [Kaiser:1980]
186 //  _n      :   sample index
187 //  _N      :   window length (samples)
188 //  _beta   :   window taper parameter
189 //  _mu     :   fractional sample offset
kaiser(unsigned int _n,unsigned int _N,float _beta,float _mu)190 float kaiser(unsigned int _n,
191              unsigned int _N,
192              float        _beta,
193              float        _mu)
194 {
195     // validate input
196     if (_n > _N) {
197         fprintf(stderr,"error: kaiser(), sample index must not exceed window length\n");
198         exit(1);
199     } else if (_beta < 0) {
200         fprintf(stderr,"error: kaiser(), beta must be greater than or equal to zero\n");
201         exit(1);
202     } else if (_mu < -0.5 || _mu > 0.5) {
203         fprintf(stderr,"error: kaiser(), fractional sample offset must be in [-0.5,0.5]\n");
204         exit(1);
205     }
206 
207     float t = (float)_n - (float)(_N-1)/2 + _mu;
208     float r = 2.0f*t/(float)(_N);
209     float a = liquid_besseli0f(_beta*sqrtf(1-r*r));
210     float b = liquid_besseli0f(_beta);
211     return a / b;
212 }
213 
214 // Hamming window [Nuttall:1981]
hamming(unsigned int _n,unsigned int _N)215 float hamming(unsigned int _n,
216               unsigned int _N)
217 {
218     // validate input
219     if (_n > _N) {
220         fprintf(stderr,"error: hamming(), sample index must not exceed window length\n");
221         exit(1);
222     }
223 
224     return 0.53836 - 0.46164*cosf( (2*M_PI*(float)_n) / ((float)(_N-1)) );
225 }
226 
227 // Hann window
hann(unsigned int _n,unsigned int _N)228 float hann(unsigned int _n,
229            unsigned int _N)
230 {
231     // validate input
232     if (_n > _N) {
233         fprintf(stderr,"error: hann(), sample index must not exceed window length\n");
234         exit(1);
235     }
236 
237     // TODO test this function
238     // TODO add reference
239     return 0.5f - 0.5f*cosf( (2*M_PI*(float)_n) / ((float)(_N-1)) );
240 }
241 
242 // Blackman-harris window [harris:1978]
blackmanharris(unsigned int _n,unsigned int _N)243 float blackmanharris(unsigned int _n,
244                      unsigned int _N)
245 {
246     // validate input
247     if (_n > _N) {
248         fprintf(stderr,"error: blackmanharris(), sample index must not exceed window length\n");
249         exit(1);
250     }
251 
252     // TODO test this function
253     // TODO add reference
254     float a0 = 0.35875f;
255     float a1 = 0.48829f;
256     float a2 = 0.14128f;
257     float a3 = 0.01168f;
258     float t = 2*M_PI*(float)_n / ((float)(_N-1));
259 
260     return a0 - a1*cosf(t) + a2*cosf(2*t) - a3*cosf(3*t);
261 }
262 
263 // 7th-order Blackman-harris window
blackmanharris7(unsigned int _n,unsigned int _N)264 float blackmanharris7(unsigned int _n, unsigned int _N)
265 {
266     // validate input
267     if (_n > _N) {
268         fprintf(stderr,"error: blackmanharris7(), sample index must not exceed window length\n");
269         exit(1);
270     }
271 
272 	float a0 = 0.27105f;
273 	float a1 = 0.43329f;
274 	float a2 = 0.21812f;
275 	float a3 = 0.06592f;
276 	float a4 = 0.01081f;
277 	float a5 = 0.00077f;
278 	float a6 = 0.00001f;
279 	float t = 2*M_PI*(float)_n / ((float)(_N-1));
280 
281 	return a0 - a1*cosf(  t) + a2*cosf(2*t) - a3*cosf(3*t)
282 			  + a4*cosf(4*t) - a5*cosf(5*t) + a6*cosf(6*t);
283 }
284 
285 // Flat-top window
flattop(unsigned int _n,unsigned int _N)286 float flattop(unsigned int _n, unsigned int _N)
287 {
288     // validate input
289     if (_n > _N) {
290         fprintf(stderr,"error: flattop(), sample index must not exceed window length\n");
291         exit(1);
292     }
293 
294 	float a0 = 1.000f;
295 	float a1 = 1.930f;
296 	float a2 = 1.290f;
297 	float a3 = 0.388f;
298 	float a4 = 0.028f;
299 	float t = 2*M_PI*(float)_n / ((float)(_N-1));
300 
301 	return a0 - a1*cosf(t) + a2*cosf(2*t) - a3*cosf(3*t) + a4*cosf(4*t);
302 }
303 
304 // Triangular window
triangular(unsigned int _n,unsigned int _N,unsigned int _L)305 float triangular(unsigned int _n,
306                  unsigned int _N,
307                  unsigned int _L)
308 {
309     // validate input
310     if (_n > _N) {
311         fprintf(stderr,"error: triangular(), sample index must not exceed window length\n");
312         exit(1);
313     } else if (_L != _N-1 && _L != _N && _L != _N+1) {
314         fprintf(stderr,"error: triangular(), sub-length must be in _N+{-1,0,1}\n");
315         exit(1);
316     } else if (_L == 0) {
317         fprintf(stderr,"error: triangular(), sub-length must be greater than zero\n");
318         exit(1);
319     }
320 
321 	float _num   = (float)_n - (float)((_N-1)/2.0f);
322 	float _denom = ((float)_L)/2.0f;
323 	return 1.0 - fabsf(_num / _denom);
324 }
325 
326 // raised-cosine tapering window
327 //  _n      :   window index
328 //  _t      :   taper length
329 //  _N      :   full window length
liquid_rcostaper_windowf(unsigned int _n,unsigned int _t,unsigned int _N)330 float liquid_rcostaper_windowf(unsigned int _n,
331                                unsigned int _t,
332                                unsigned int _N)
333 {
334     // validate input
335     if (_n > _N) {
336         fprintf(stderr,"error: liquid_rcostaper_windowf(), sample index must not exceed window length\n");
337         exit(1);
338     } else if (_t > _N/2) {
339         fprintf(stderr,"error: liquid_rcostaper_windowf(), taper length cannot exceed half window length\n");
340         exit(1);
341     }
342 
343     // reverse time for ramp-down section
344     if (_n > _N - _t - 1)
345         _n = _N - _n - 1;
346 
347     // return ramp or flat component
348     return (_n < _t) ? 0.5f - 0.5f*cosf(M_PI*((float)_n + 0.5f) / (float)_t) : 1.0f;
349 }
350 
351 
352