SoDaRadio-5.0.3-master:8901fb5
ReSampler.cxx
Go to the documentation of this file.
1 /*
2  Copyright (c) 2012,2013,2014 Matthew H. Reilly (kb1vc)
3  All rights reserved.
4 
5  Redistribution and use in source and binary forms, with or without
6  modification, are permitted provided that the following conditions are
7  met:
8 
9  Redistributions of source code must retain the above copyright
10  notice, this list of conditions and the following disclaimer.
11  Redistributions in binary form must reproduce the above copyright
12  notice, this list of conditions and the following disclaimer in
13  the documentation and/or other materials provided with the
14  distribution.
15 
16  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28 
29 #include "ReSampler.hxx"
30 #include <string.h>
31 #include <math.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <iostream>
35 #include <boost/format.hpp>
36 
37 static void bindump(char * fn, std::complex<float> * buf, unsigned int num_elts, int append = 0) __attribute__ ((unused));
38 static void bindump(char * fn, std::complex<float> * buf, unsigned int num_elts, int append)
39 {
40  FILE * of;
41  if(append != 0) {
42  of = fopen(fn, "a");
43  }
44  else {
45  of = fopen(fn, "w");
46  }
47  unsigned int i;
48  for(i = 0; i < num_elts; i++) {
49  fprintf(of, "%d %g %g\n", i + (append * num_elts), buf[i].real(), buf[i].imag());
50  }
51  fclose(of);
52 }
53 
54 static void bindump_2(char * fn, std::complex<float> * buf, unsigned int num_elts, unsigned int num_cols) __attribute__ ((unused));
55 static void bindump_2(char * fn, std::complex<float> * buf, unsigned int num_elts, unsigned int num_cols)
56 {
57  FILE * of;
58  of = fopen(fn, "w");
59  unsigned int i, j;
60  for(i = 0; i < num_elts; i++) {
61  fprintf(of, "%d ", i);
62  for(j = 0; j < num_cols; j++) {
63  fprintf(of, "%g %g ", buf[i * num_cols + j].real(), buf[i * num_cols + j].imag());
64  }
65  fprintf(of, "\n");
66  }
67  fclose(of);
68 }
69 
70 static unsigned int ipow(unsigned int a, unsigned int b)
71 {
72  unsigned int ret = 1;
73  unsigned int i ;
74 
75  for(i = 0; i < b; i++) {
76  ret *= a;
77  }
78 
79  return ret;
80 }
81 
82 SoDa::ReSampler::ReSampler(unsigned int interpolate_ratio,
83  unsigned int decimate_ratio,
84  unsigned int _inlen,
85  unsigned int _filter_len)
86 {
87  // save the parameters
88  inlen = _inlen;
89  iM = interpolate_ratio;
90  dN = decimate_ratio;
91  filter_len = _filter_len;
92  Q = filter_len;
93  M = _inlen;
94 
95 
96  // give preferences to convenient powers of two.
97  // but let's search for the nearest solution that is
98  // a multiple of 2^a * 3^b * 5^c where b and c are in the range 0..3
99  // and a is in the range 1..16
100  unsigned int N_guess, N_best, E_best;
101  N_best = 2;
102  E_best = 0x80000000;
103  unsigned int i;
104  for(i = 1; i <= 0xff; i++) {
105  unsigned int a = i & 0xf;
106  unsigned int b = (i >> 4) & 0x3;
107  unsigned int c = (i >> 6) & 0x3;
108  N_guess = ipow(2, a) * ipow(3, b) * ipow(5, c);
109  if(N_guess >= (M + Q - 1)) {
110  unsigned slop = N_guess - (M + Q - 1);
111  if(slop < E_best) {
112  N_best = N_guess;
113  E_best = slop;
114  }
115  }
116  }
117  N = N_best;
118 
119 
120  // now that we have N, we can back-calculate Q.
121  Q = N - M;
122 
123  filter_len = Q;
124 
125  tail_index = M - (Q - 1);
126 
127  // create the buffers.
128  // a copy of the input sequence
129  inbuf = (std::complex<float> *) fftwf_alloc_complex(N);
130  // the transformed input
131  in_fft = (std::complex<float> *) fftwf_alloc_complex(N);
132 
133  // zero out the whole of the input buffer
134  for(i = 0; i < N; i++) inbuf[i] = std::complex<float>(0.0,0.0);
135 
136  // the filter bank is really iM interleaved filters
137  // but we can't seem to make that work right now, so we're
138  // backing off to a simpler way.
139  c_filt = new std::complex<float>*[iM];
140  for(i = 0; i < iM; i++) {
141  c_filt[i] = (std::complex<float> *) fftwf_alloc_complex(N);
142  }
143 
144  // the upsampled result
145  filt_fft = (std::complex<float> *) fftwf_alloc_complex(iM*N);
146  // the inverse transform of the upsampled result
147  interp_res = (std::complex<float> *) fftwf_alloc_complex(iM*N);
148 
149  // the first plan is pretty simple.
150  in_fft_plan = fftwf_plan_dft_1d(N,
151  (fftwf_complex *) inbuf,
152  (fftwf_complex *) in_fft,
153  FFTW_FORWARD, FFTW_ESTIMATE);
154 
155 
156  // now create the inverting plans
157  int n[1];
158  n[0] = N;
159  mid_ifft_plan = fftwf_plan_many_dft(1, n, iM,
160  (fftwf_complex *) filt_fft, NULL, iM, 1,
161  (fftwf_complex *) interp_res, NULL, iM, 1,
162  FFTW_BACKWARD, FFTW_ESTIMATE);
163 
164 
165  // now create the polyphase filter banks.
167 
168  transform_gain = 1.0 / ((float) N);
169 }
170 
172 {
173  // filter_len must be odd
174  int fN = filter_len;
175  if((fN & 1) == 0) fN--;
176 
177  unsigned int i, j, k;
178 
179  // Use the plan of fischer in "The mkfilter Digital Filter Generation Program" mkshape...
180  // This is borrowed quite heavily and with tremendous gratitude from
181  // http://www-users.cs.york.ac.uk/~fisher Thanks, Prof. Fisher...
182  float alpha, beta;
183  float cutdown = ((float) ((iM > dN) ? iM : dN));
184  beta = 1.0 / (2.2 * cutdown); // This is the transition freq -- a little shy of 0.5/cutdown
185  alpha = 0.1; // this is a shape factor
186 
187  // create a frequency domain image of the filter.
188  float f1 = (1.0 - alpha) * beta;
189  float f2 = (1.0 + alpha) * beta;
190  float tau = 0.5 / alpha;
191 
192  // now create the filter banks.
193  std::complex<float> * filt_FD = (std::complex<float>*) fftwf_alloc_complex(N);
194  std::complex<float> * filt_td = (std::complex<float>*) fftwf_alloc_complex(N);
195  std::complex<float> * filt_td2 = (std::complex<float>*) fftwf_alloc_complex(N);
196 
197  float f;
198  float f_incr = 1.0 / ((float) (N));
199  unsigned int hlim = (N);
200  float gain_corr = 1.0 / ((float) hlim);
201  // gain_corr = gain_corr * gain_corr;
202  for(i = 0, f = 0.0; i <= hlim / 2; i++, f += f_incr) {
203  if(f <= f1) {
204  filt_FD[i] = std::complex<float>(1.0, 0.0);
205  }
206  else if (f <= f2) {
207  float h = 0.5 * (1.0 + cos(M_PI * tau * (f - f1) / beta));
208  filt_FD[i] = std::complex<float>(h, 0.0);
209  }
210  else {
211  filt_FD[i] = std::complex<float>(0.0, 0.0);
212  }
213 
214  filt_FD[i] *= tau * gain_corr;
215 
216  if(i != 0) filt_FD[hlim - i] = filt_FD[i];
217  }
218 
219  // then ifft it to time domain
220  fftwf_plan filt_ifft_plan = fftwf_plan_dft_1d(hlim,
221  (fftwf_complex *) filt_FD,
222  (fftwf_complex *) filt_td,
223  FFTW_BACKWARD, FFTW_ESTIMATE);
224  fftwf_execute(filt_ifft_plan);
225 
226  float sum = 0.0;
227  for(i = 0; i < hlim; i++) sum += filt_td[i].real();
228 
229  float sgain = 1.0 / sum;
230  for(i = 0; i < hlim; i++) filt_td[i] *= sgain;
231 
232  fftwf_destroy_plan(filt_ifft_plan);
233 
234  // now truncate the filter
235  // We've got an image that is symmetric around 0.
236  for(i = 0; i < N; i++) {
237  filt_td2[i] = std::complex<float>(0.0,0.0);
238  }
239 
240  // shift it up to be symmetric around filt_len / 2
241  for(i = 0, j = N - (filter_len/2); j < N; i++, j++) {
242  filt_td2[i] = filt_td[j];
243  }
244  for(j = 0; j < filter_len / 2; j++, i++) {
245  filt_td2[i] = filt_td[j];
246  }
247 
248  // bindump("filt_td2.dat", filt_td2, N);
249 
250  // and fft back to frequency domain for each of the iM filters.
251  for(i = 0; i < iM; i++) {
252  // grab the slice of the filter that we need to transform.
253 
254  for(j = i, k = 0; j < filter_len; k++, j += iM) {
255  filt_td[k] = filt_td2[j];
256  }
257  for(; k < N; k++) filt_td[k] = std::complex<float>(0.0,0.0);
258  fftwf_plan filt_fft_plan = fftwf_plan_dft_1d(N,
259  (fftwf_complex *) filt_td,
260  (fftwf_complex *) c_filt[i],
261  FFTW_FORWARD, FFTW_ESTIMATE);
262  fftwf_execute(filt_fft_plan);
263  fftwf_destroy_plan(filt_fft_plan);
264  }
265 
266  // now free up buffers
267  fftwf_free(filt_td2);
268  fftwf_free(filt_td);
269  fftwf_free(filt_FD);
270 }
271 
272 unsigned int SoDa::ReSampler::apply(std::complex<float> * in,
273  std::complex<float> * out,
274  float gain)
275 {
276  unsigned int i, j;
277  // copy the input buffer
278  memcpy(&(inbuf[Q-1]), in, sizeof(std::complex<float>) * M);
279 
280  // transform the input
281  fftwf_execute(in_fft_plan); // , (fftwf_complex *) in, (fftwf_complex *) in_fft);
282 
283  // now multiply by the M filters
284  for(i = 0; i < N; i++) {
285  for(j = 0; j < iM; j++) {
286  filt_fft[j + i*iM] = in_fft[i] * c_filt[j][i];
287  }
288  }
289  // do M inverse transforms on the filter result ...
290  // to produce M interleaved time domain vectors.
291  fftwf_execute(mid_ifft_plan);
292 
293  // save the last bits of the input buffer to the Q-1 side of the FFT input vector
294  // Do this now incase inbuf and outbuf are the same buffers. (we may
295  // over-write the outbuf with the downsample at the bottom.... )
296  memcpy(inbuf, &(in[tail_index]), sizeof(std::complex<float>) * (Q-1));
297 
298  // now build the output buffer.
299 
300  // now downsample by a factor of dN.
301  int idx = 0;
302  unsigned int mctr = 0;
303  unsigned int out_lim = (M * iM) / dN;
304  std::complex<float> * ir_clean = &(interp_res[(Q-1)]);
305  // std::cerr << " iM, dN, M, N, out_lim = "
306  // << iM << " " << dN << " " << M << " " << N << " " << out_lim << std::endl;
307  for(j = 0; j < out_lim; j++) {
308  out[j] = ir_clean[mctr + idx * iM] * gain * transform_gain * ((float) iM);
309  mctr += dN;
310  while(mctr >= iM) {
311  mctr -= iM;
312  idx += 1;
313  }
314  }
315  // bindump("out.dat", out, j);
316 
317  return 0;
318 }
319 
320 
321 
322 unsigned int SoDa::ReSampler::apply(float * in,
323  float * out,
324  float gain)
325 {
326  unsigned int i, j;
327 
328  for(i = 0; i < M; i++) {
329  inbuf[i+(Q-1)] = std::complex<float>(in[i], 0.0);
330  }
331 
332  // transform the input
333  fftwf_execute(in_fft_plan); // , (fftwf_complex *) in, (fftwf_complex *) in_fft);
334 
335  // now multiply by the M filters
336  for(i = 0; i < N; i++) {
337  for(j = 0; j < iM; j++) {
338  filt_fft[j + i*iM] = in_fft[i] * c_filt[j][i];
339  }
340  }
341  // do M inverse transforms on the filter result ...
342  // to produce M interleaved time domain vectors.
343  fftwf_execute(mid_ifft_plan);
344 
345  // save the last bits of the input buffer to the Q-1 side of the FFT input vector
346  // Do this now incase inbuf and outbuf are the same buffers. (we may
347  // over-write the outbuf with the downsample at the bottom.... )
348  for(i = 0; i < (Q-1); i++) {
349  inbuf[i] = std::complex<float>(in[i + tail_index], 0.0);
350  }
351 
352  // now build the output buffer.
353 
354  // now downsample by a factor of dN.
355  int idx = 0;
356  unsigned int mctr = 0;
357  unsigned int out_lim = (M * iM) / dN;
358  std::complex<float> * ir_clean = &(interp_res[(Q-1)]);
359  for(j = 0; j < out_lim; j++) {
360  out[j] = ir_clean[mctr + idx * iM].real() * gain * transform_gain * ((float) iM);
361  mctr += dN;
362  while(mctr >= iM) {
363  mctr -= iM;
364  idx += 1;
365  }
366  }
367 
368  return 0;
369 }
std::complex< float > * inbuf
the fft input buffer
Definition: ReSampler.hxx:91
unsigned int M
Definition: ReSampler.hxx:86
float transform_gain
Definition: ReSampler.hxx:94
std::complex< float > ** c_filt
filter bank for interp/deci filter
Definition: ReSampler.hxx:87
static void bindump_2(char *fn, std::complex< float > *buf, unsigned int num_elts, unsigned int num_cols) __attribute__((unused))
Definition: ReSampler.cxx:55
unsigned int iM
Definition: ReSampler.hxx:85
void CreateFilter(unsigned int filter_len)
create a filter bank for a polyphase resampler.
Definition: ReSampler.cxx:171
unsigned int dN
Definition: ReSampler.hxx:85
STL namespace.
unsigned int inlen
Definition: ReSampler.hxx:86
unsigned int Q
Definition: ReSampler.hxx:85
std::complex< float > * in_fft
the result of the first fft stage
Definition: ReSampler.hxx:90
int filter_len
the length of the polyphase filter that we&#39;re using.
Definition: ReSampler.hxx:83
static unsigned int ipow(unsigned int a, unsigned int b)
Definition: ReSampler.cxx:70
unsigned int N
Definition: ReSampler.hxx:86
unsigned int tail_index
Definition: ReSampler.hxx:85
ReSampler(unsigned int interp_ratio, unsigned int decim_ratio, unsigned int _inlen, unsigned int filter_len)
Constructor.
Definition: ReSampler.cxx:82
std::complex< float > * filt_fft
the filtered image of the input stream
Definition: ReSampler.hxx:88
std::complex< float > * interp_res
the interpolation result
Definition: ReSampler.hxx:89
static void bindump(char *fn, std::complex< float > *buf, unsigned int num_elts, int append=0) __attribute__((unused))
Definition: ReSampler.cxx:38
fftwf_plan in_fft_plan
Definition: ReSampler.hxx:98
fftwf_plan mid_ifft_plan
Definition: ReSampler.hxx:99
unsigned int apply(std::complex< float > *in, std::complex< float > *out, float gain=1.0)
apply the resampler to a buffer of IQ samples.
Definition: ReSampler.cxx:272