SoDaRadio-5.0.3-master:8901fb5
ReSampler_Test.cxx
Go to the documentation of this file.
1 /*
2 Copyright (c) 2012, 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 #define SAMPLE_RATE 48000
29 #include <complex>
30 #include <iostream>
31 #include <fstream>
32 #include <stdlib.h>
33 #include "ReSampler.hxx"
34 #include "ReSamplers625x48.hxx"
35 #include <time.h>
36 #include <fftw3.h>
37 #include <math.h>
38 #include <string.h>
39 #include <stdio.h>
40 #include <fcntl.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <stdio.h>
44 
45 #include <sys/time.h>
46 
47  // test program to checkout the antialiasing capability of the
48  // resamplers.
49 
50 using namespace std;
51 
52 double curtime()
53 {
54  double ret;
55  struct timeval tp;
56  gettimeofday(&tp, NULL);
57  ret = ((double) tp.tv_sec) + 1e-6*((double)tp.tv_usec);
58  return ret;
59 }
60 
61 static void bindump(char * fn, std::complex<float> * buf, unsigned int num_elts) __attribute__ ((unused));
62 static void bindump(char * fn, std::complex<float> * buf, unsigned int num_elts)
63 {
64  FILE * of;
65  of = fopen(fn, "w");
66  unsigned int i;
67  for(i = 0; i < num_elts; i++) {
68  fprintf(of, "%d %g %g\n", i, buf[i].real(), buf[i].imag());
69  }
70  fclose(of);
71 }
72 
73 void checkResult(FILE * ofile,
74  float anginc, unsigned int iM, unsigned int dN,
75  unsigned int inlen,
76  std::complex<float> * invec, std::complex<float> * outvec, int idx = 0)
77 {
78  (void) invec;
79  // sweep through the output.
80 
81  // how long is the output?
82  unsigned int outlen = inlen * iM;
83  outlen = outlen / dN;
84 
85  // what is the scale factor for the angle increment?
86  float oanginc = anginc * ((float) dN) / ((float) iM);
87 
88  unsigned int i;
89  float ang = 0.0;
90  float err_sq_sum[2] = { 0.0, 0.0 };
91  for(i = 0; i < outlen; i++) {
92  // test the output.
93  fprintf(ofile, "%d\t%g\t%g\t%g\t%g\t%g\t", i + idx*outlen, ang, outvec[i].real(), outvec[i].imag(), cos(ang), sin(ang));
94  float err = outvec[i].real() - cos(ang);
95  err_sq_sum[0] += err * err;
96  fprintf(ofile, "%g\t", err);
97  err = outvec[i].imag() - sin(ang);
98  err_sq_sum[1] += err * err;
99  fprintf(ofile, "%g\n", err);
100  ang += oanginc;
101  if(ang > M_PI) ang = ang - (2.0 * M_PI);
102  }
103 
104  fprintf(ofile, "%d\t%g\t%g\t%g\t%g\t%g\t%g\t%g\n", i + idx*outlen, ang+0.5*anginc, 5.0, 5.0, cos(ang), sin(ang), 1.0, 1.0);
105  float fn = (float) outlen;
106  printf("RMS error for M/N = %d / %d : %g %g\n",
107  iM, dN, sqrt(err_sq_sum[0]/fn), sqrt(err_sq_sum[1]/fn));
108 }
109 
110 float loadVector(float ang, float ang_inc, std::complex<float> * vec, int len)
111 {
112  int i;
113  for(i = 0; i < len; i++) {
114  vec[i] = std::complex<float>(cos(ang), sin(ang));
115  ang += ang_inc;
116  int ii = i / 100;
117  if((ii == 0) || (ii == 5) || (ii == 7) || (ii == 10)) ang += ang_inc;
118  if(ang > M_PI) ang -= (2.0 * M_PI);
119  }
120  return ang;
121 }
122 
124 {
125  SoDa::ReSample625to48 rsf(30000);
126  SoDa::ReSample625to48 rsc(30000);
127  float in[60000], out[2304];
128  std::complex<float> cin[60000], cout[2304];
129  int i;
130  float ang = 0.0;
131 
132  for(i = 0; i < 60000; i++) {
133  in[i] = sin(ang);
134  cin[i] = std::complex<float>(sin(ang), cos(ang));
135  ang += 0.01;
136  }
137 
138  // now resample
139  rsf.apply(in, out);
140  rsf.apply(in + 30000, out);
141  rsc.apply(cin, cout);
142  rsc.apply(cin + 30000, cout);
143 
144  for(i = 0; i < 30000; i++) {
145  int j = i + 30000;
146  std::cout << boost::format("%d %f %f %f ") % i % in[j] % cin[j].real() % cin[j].imag();
147  if(i < 2304) {
148  std::cout << boost::format("%f %f %f\n") % out[i] % cout[i].real() % cout[i].imag();
149  }
150  else {
151  std::cout << std::endl;
152  }
153  }
154 }
155 
157 {
158 }
159 
161 {
162  // create a downsampler from 30K samples to 2304
163  SoDa::ReSample625to48 rsf(30000);
164  std::complex<float> cin[30000], cout[2304];
165 
166  ofstream to("ReSampler625to48_Test.dat");
167 
168  // pretend that our input sample rate was 625KS/sec.
169  float samp_freq = 625000.0;
170  float test_freq;
171  float angle = 0.0;
172  for(test_freq = -312500.0; test_freq < 312500.0; ) {
173  int i;
174  float phase_advance_per_tic = 2.0 * M_PI * test_freq / samp_freq;
175  float energy = 0.0;
176  for(i = 0; i < 100; i++) {
177  int j;
178  for(j = 0; j < 30000; j++) {
179  cin[j] = std::complex<float>(cos(angle), sin(angle));
180  angle += phase_advance_per_tic;
181  if(angle > M_PI) angle -= (2.0 * M_PI);
182  if(angle < -M_PI) angle += (2.0 * M_PI);
183  }
184  rsf.apply(cin, cout);
185  if(i != 0) {
186  for(j = 0; j < 2304; j++) {
187  float ip = cout[j].real();
188  float qu = cout[j].imag();
189  energy += ip * ip + qu * qu;
190  }
191  }
192  }
193  to << boost::format("%f %f\n") % test_freq % (10.0 * log10(energy));
194  std::cout << boost::format("%f %f\n") % test_freq % (10.0 * log10(energy));
195  if(test_freq < 0.0) {
196  if(test_freq > -10.0) test_freq = 10.0;
197  else test_freq *= (1.0 / 1.05);
198  }
199  else {
200  test_freq *= 1.05;
201  }
202  }
203 
204  to.close();
205 }
206 
207 int main(int argc, char * argv[])
208 {
209  (void) argc; (void) argv;
210  // create various ReSampler ratios, especially the ones I care
211  // about.
212  unsigned int resampler_ratio[][2] = { { 5, 1 },
213  { 5, 4 },
214  { 5, 3 },
215  { 5, 2 },
216  { 0, 0} };
217  // create the input and output buffers.
218  // max size of outvec/invec = 5:1.
219  std::complex<float> invec[30000];
220  std::complex<float> outvec[30000];
221  std::complex<float> out2vec[30000];
222 
223  aliasingPlot();
224 
225  exit(0);
226 
227  test625x48();
228  test48x625();
229 
230  int i, j;
231  // fill the input vector
232  float ang = 0.0;
233  float ang_inc = 2.0 * M_PI * 315.26 / 6000.0;
234  ang = loadVector(ang, ang_inc, invec, 6000);
235 
236 
237  SoDa::ReSampler * rs;
238  for(j = 0; j < 2; j++) {
239  for(i = 0; resampler_ratio[i][0] > 0; i++) {
240  unsigned int iM, dN;
241  iM = resampler_ratio[i][j];
242  dN = resampler_ratio[i][1-j];
243 
244  char buf[1024];
245  sprintf(buf, "resample_%d_%d.dat", iM, dN);
246  FILE * ofile = fopen(buf, "w");
247 
248  // create a ReSampler.
249  rs = new SoDa::ReSampler(iM, dN, 6000, 255);
250  ang = loadVector(ang, ang_inc, invec, 6000);
251  rs->apply(invec, outvec);
252  ang = loadVector(ang, ang_inc, invec, 6000);
253  double startt = curtime();
254  rs->apply(invec, outvec);
255  double endt = curtime();
256  // now check the vector
257  checkResult(ofile, ang_inc, iM, dN, 6000, invec, outvec, 0);
258  ang = loadVector(ang, ang_inc, invec, 6000);
259  rs->apply(invec, outvec);
260  checkResult(ofile, ang_inc, iM, dN, 6000, invec, outvec, 1);
261  ang = loadVector(ang, ang_inc, invec, 6000);
262  rs->apply(invec, outvec);
263  checkResult(ofile, ang_inc, iM, dN, 6000, invec, outvec, 2);
264  ang = loadVector(ang, ang_inc, invec, 6000);
265  rs->apply(invec, outvec);
266  checkResult(ofile, ang_inc, iM, dN, 6000, invec, outvec, 3);
267  std::cout << "TIM: Resample took " << (endt - startt) << " seconds" << std::endl;
268  delete rs;
269  fclose(ofile);
270  }
271  }
272 
273  // now a time test.
274  // create a 5/4 resampler and a 4/5 resampler.
275  SoDa::ReSampler * up, * down;
276  up = new SoDa::ReSampler(5, 4, 6000, 255);
277  down = new SoDa::ReSampler(4, 5, 7500, 255);
278  double ttstart = curtime();
279  for(i = 0; i < 4000; i++) {
280  up->apply(invec, outvec);
281  down->apply(outvec, invec);
282  }
283  double ttend = curtime();
284 
285  std::cout << "TIM: average time per up/down resampling pair: "
286  << (ttend - ttstart) / ((double) i) << std::endl;
287 
288  // Now try a 2304 buffer upsampled by 625/48
289  SoDa::ReSampler rs54a(5, 4, 2304, 255);
290  SoDa::ReSampler rs54b(5, 4, 2880, 255);
291  SoDa::ReSampler rs53(5, 3, 3600, 255);
292  SoDa::ReSampler rs51(5, 1, 6000, 255);
293 
294  ang = 0.0;
295  ang_inc = 2.0 * M_PI / 53.7;
296  float ang2 = 0.0;
297  float ang2_inc = 2.0 * M_PI * 3.0 / 4.0;
298  for(i = 0; i < 30000; i++) {
299  // invec[i] = std::complex<float>(0.5 * cos(ang) + cos(ang2), 0.5 * sin(ang) + sin(ang2));
300  invec[i] = std::complex<float>(cos(ang), sin(ang));
301  ang += ang_inc;
302  if(ang > M_PI) ang -= 2.0 * M_PI;
303  ang2 += ang2_inc;
304  if(ang2 > M_PI) ang2 -= 2.0 * M_PI;
305  }
306 
307  int uf = creat("up_1khz_625k.dat", 0666);
308 
309  ttstart = curtime();
310  rs54a.apply(invec, outvec);
311  rs54b.apply(outvec, out2vec);
312  rs53.apply(out2vec, outvec);
313  rs51.apply(outvec, out2vec);
314  ttend = curtime();
315  std::cout << "Upsampled from 2304 to 30000 in 5/4 5/4 5/3 5/1 in "
316  << (ttend - ttstart) << " seconds" << std::endl;
317  int stat = write(uf, out2vec, sizeof(std::complex<float>) * 30000);
318  (void) stat;
319  rs54a.apply(invec, outvec);
320  rs54b.apply(outvec, out2vec);
321  rs53.apply(out2vec, outvec);
322  rs51.apply(outvec, out2vec);
323  stat = write(uf, out2vec, sizeof(std::complex<float>) * 30000);
324  rs54a.apply(invec, outvec);
325  rs54b.apply(outvec, out2vec);
326  rs53.apply(out2vec, outvec);
327  rs51.apply(outvec, out2vec);
328  stat = write(uf, out2vec, sizeof(std::complex<float>) * 30000);
329  rs54a.apply(invec, outvec);
330  rs54b.apply(outvec, out2vec);
331  rs53.apply(out2vec, outvec);
332  rs51.apply(outvec, out2vec);
333  stat = write(uf, out2vec, sizeof(std::complex<float>) * 30000);
334 
335  close(uf);
336 
337  SoDa::ReSampler rs53b(5, 3, 2880, 255);
338  SoDa::ReSampler rs52a(5, 2, 4800, 255);
339  SoDa::ReSampler rs52b(5, 2, 12000, 255);
340  ttstart = curtime();
341  rs54a.apply(invec, outvec);
342  rs53b.apply(invec, outvec);
343  rs52a.apply(outvec, invec);
344  rs52b.apply(outvec, invec);
345  ttend = curtime();
346  std::cout << "Upsampled from 2304 to 30000 in 5/4 5/3 5/2 5/2 in "
347  << (ttend - ttstart) << " seconds" << std::endl;
348 
349 
350 
351  // Now try a 2304 buffer downsampled by 48/625
352  SoDa::ReSampler rs15(1, 5, 30000, 255);
353  SoDa::ReSampler rs35(3, 5, 6000, 255);
354  SoDa::ReSampler rs45a(4, 5, 4800, 255);
355  SoDa::ReSampler rs45b(4, 5, 2880, 255);
356 
357 
358  ang = 0.0;
359  ang_inc = 2.0 * M_PI / 50.0;
360  for(i = 0; i < 30000; i++) {
361  invec[i] = std::complex<float>(cos(ang), sin(ang));
362  ang += ang_inc;
363  if(ang > M_PI) ang -= 2.0 * M_PI;
364  }
365  ttstart = curtime();
366  rs15.apply(invec, outvec);
367  rs35.apply(outvec, out2vec);
368  rs45a.apply(out2vec, outvec);
369  rs45b.apply(outvec, out2vec);
370  ttend = curtime();
371  std::cout << "Downsampled from 30000 to 2304 in 1/5 3/5 4/5 4/5 in "
372  << (ttend - ttstart) << " seconds" << std::endl;
373 
374  int df = creat("down_1khz_625k.dat", 0666);
375  stat = write(df, out2vec, sizeof(std::complex<float>) * 2304);
376  rs15.apply(invec, outvec);
377  rs35.apply(outvec, out2vec);
378  rs45a.apply(out2vec, outvec);
379  rs45b.apply(outvec, out2vec);
380  stat = write(df, out2vec, sizeof(std::complex<float>) * 2304);
381 
382  rs15.apply(invec, outvec);
383  rs35.apply(outvec, out2vec);
384  rs45a.apply(out2vec, outvec);
385  rs45b.apply(outvec, out2vec);
386  stat = write(df, out2vec, sizeof(std::complex<float>) * 2304);
387 
388  close(df);
389 
390  SoDa::ReSampler rs25a(2, 5, 30000, 255);
391  SoDa::ReSampler rs25b(2, 5, 12000, 255);
392  SoDa::ReSampler rs35b(3, 5, 4800, 255);
393  ttstart = curtime();
394  rs25b.apply(outvec, invec);
395  rs25a.apply(outvec, invec);
396  rs35b.apply(invec, outvec);
397  rs45b.apply(invec, outvec);
398 
399 
400  ttend = curtime();
401  std::cout << "Downsampled from 30000 to 2304 in 2/5 2/5 3/5 1/5 in "
402  << (ttend - ttstart) << " seconds" << std::endl;
403 
404 
405 }
Rational Resampler.
Definition: ReSampler.hxx:41
Resampler for 625KHz to 48KHz data stream, built on rational ReSampler class.
void checkResult(FILE *ofile, float anginc, unsigned int iM, unsigned int dN, unsigned int inlen, std::complex< float > *invec, std::complex< float > *outvec, int idx=0)
void test48x625()
STL namespace.
int main(int argc, char *argv[])
void apply(std::complex< float > *in, std::complex< float > *out)
Perform the resampling on a complex float buffer.
double curtime()
void aliasingPlot()
static void bindump(char *fn, std::complex< float > *buf, unsigned int num_elts) __attribute__((unused))
float loadVector(float ang, float ang_inc, std::complex< float > *vec, int len)
void test625x48()
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