Sylvain Munaut | 3733ed5 | 2018-12-21 16:38:31 +0100 | [diff] [blame] | 1 | #include <stdint.h> |
Philipp Maier | dfe0aef | 2017-03-16 18:43:33 +0100 | [diff] [blame] | 2 | #include <stdio.h> |
Pau Espin Pedrol | 4ebb289 | 2018-01-10 11:54:34 +0100 | [diff] [blame] | 3 | #include <stdlib.h> |
Sylvain Munaut | 3733ed5 | 2018-12-21 16:38:31 +0100 | [diff] [blame] | 4 | #include <string.h> |
| 5 | #include <math.h> |
| 6 | |
Pau Espin Pedrol | 43fedb6 | 2018-04-24 15:22:57 +0200 | [diff] [blame] | 7 | #include "convolve.h" |
Philipp Maier | dfe0aef | 2017-03-16 18:43:33 +0100 | [diff] [blame] | 8 | |
Philipp Maier | dfe0aef | 2017-03-16 18:43:33 +0100 | [diff] [blame] | 9 | |
Sylvain Munaut | 3733ed5 | 2018-12-21 16:38:31 +0100 | [diff] [blame] | 10 | // --------------------------------------------------------------------------- |
| 11 | // Misc utils |
| 12 | // --------------------------------------------------------------------------- |
Philipp Maier | dfe0aef | 2017-03-16 18:43:33 +0100 | [diff] [blame] | 13 | |
| 14 | /* Generate some random values for testing */ |
Sylvain Munaut | 3733ed5 | 2018-12-21 16:38:31 +0100 | [diff] [blame] | 15 | static unsigned long rand_state = 0; |
| 16 | |
| 17 | static void |
| 18 | rand_reset(void) |
Philipp Maier | dfe0aef | 2017-03-16 18:43:33 +0100 | [diff] [blame] | 19 | { |
Sylvain Munaut | 3733ed5 | 2018-12-21 16:38:31 +0100 | [diff] [blame] | 20 | rand_state = 0; |
Philipp Maier | dfe0aef | 2017-03-16 18:43:33 +0100 | [diff] [blame] | 21 | } |
| 22 | |
Sylvain Munaut | 3733ed5 | 2018-12-21 16:38:31 +0100 | [diff] [blame] | 23 | static unsigned long |
| 24 | rand_int(void) |
Philipp Maier | dfe0aef | 2017-03-16 18:43:33 +0100 | [diff] [blame] | 25 | { |
Sylvain Munaut | 3733ed5 | 2018-12-21 16:38:31 +0100 | [diff] [blame] | 26 | rand_state = (1103515245UL * rand_state + 12345UL) & 0x7fffffffUL; |
| 27 | return rand_state; |
| 28 | } |
Philipp Maier | dfe0aef | 2017-03-16 18:43:33 +0100 | [diff] [blame] | 29 | |
Sylvain Munaut | 3733ed5 | 2018-12-21 16:38:31 +0100 | [diff] [blame] | 30 | static float |
| 31 | rand_float(void) |
| 32 | { |
| 33 | union { |
| 34 | uint32_t u; |
| 35 | float f; |
| 36 | } r; |
| 37 | uint32_t u = rand_int(); |
| 38 | int e = 112 + ((u ^ (u>>8)) & 15); |
Philipp Maier | dfe0aef | 2017-03-16 18:43:33 +0100 | [diff] [blame] | 39 | |
Sylvain Munaut | 3733ed5 | 2018-12-21 16:38:31 +0100 | [diff] [blame] | 40 | r.u = u & 0x007fffffUL; // Mantissa |
| 41 | r.u |= (u & 0x00800000UL) << 8; // Sign |
| 42 | r.u |= (e & 0xffUL) << 23; // Exponent |
| 43 | |
| 44 | return r.f; |
| 45 | } |
| 46 | |
| 47 | static void |
| 48 | gen_floats(float *vect, int len) |
| 49 | { |
| 50 | int i; |
| 51 | for (i = 0; i < len; i++) |
| 52 | vect[i] = rand_float(); |
Philipp Maier | dfe0aef | 2017-03-16 18:43:33 +0100 | [diff] [blame] | 53 | } |
| 54 | |
| 55 | /* Show float vector data cut and paste friendly */ |
Sylvain Munaut | 3733ed5 | 2018-12-21 16:38:31 +0100 | [diff] [blame] | 56 | static void |
| 57 | dump_floats(float *vect, int len, char *name) |
Philipp Maier | dfe0aef | 2017-03-16 18:43:33 +0100 | [diff] [blame] | 58 | { |
| 59 | int i; |
| 60 | |
Sylvain Munaut | 3733ed5 | 2018-12-21 16:38:31 +0100 | [diff] [blame] | 61 | printf("static const float %s[] = {\n\t", name); |
Pau Espin Pedrol | 4d179ab | 2018-09-10 10:36:04 +0200 | [diff] [blame] | 62 | for(i = 0; i < len; i++) { |
Sylvain Munaut | 3733ed5 | 2018-12-21 16:38:31 +0100 | [diff] [blame] | 63 | char *end; |
| 64 | if (i == len-1) |
| 65 | end = "\n"; |
| 66 | else if ((i&3) == 3) |
| 67 | end = ",\n\t"; |
| 68 | else |
| 69 | end = ", "; |
| 70 | printf("%14.7ef%s", vect[i], end); |
Philipp Maier | dfe0aef | 2017-03-16 18:43:33 +0100 | [diff] [blame] | 71 | } |
Sylvain Munaut | 3733ed5 | 2018-12-21 16:38:31 +0100 | [diff] [blame] | 72 | printf("};\n"); |
Philipp Maier | dfe0aef | 2017-03-16 18:43:33 +0100 | [diff] [blame] | 73 | } |
| 74 | |
Sylvain Munaut | 3733ed5 | 2018-12-21 16:38:31 +0100 | [diff] [blame] | 75 | /* Compare float with tolerance of delta (absolute) and epsilon (relative) */ |
| 76 | static int |
| 77 | compare_floats(const float *v0, const float *v1, int len, float delta, float epsilon) |
| 78 | { |
| 79 | int i; |
| 80 | |
| 81 | for (i=0; i<len; i++) |
| 82 | { |
| 83 | float a = v0[i]; |
| 84 | float b = v1[i]; |
| 85 | |
| 86 | if (fabsf(a - b) < delta) |
| 87 | continue; |
| 88 | |
| 89 | if (fabsf(1.0f - (a/b)) < epsilon) |
| 90 | continue; |
| 91 | |
| 92 | return 1; |
| 93 | } |
| 94 | |
| 95 | return 0; |
| 96 | } |
| 97 | |
| 98 | |
| 99 | // --------------------------------------------------------------------------- |
| 100 | // Golden reference results |
| 101 | // --------------------------------------------------------------------------- |
| 102 | |
| 103 | #include "convolve_test_golden.h" |
| 104 | |
| 105 | enum test_type { |
| 106 | CONV_REAL_BASE = 0, |
| 107 | CONV_REAL_OPT = 1, |
| 108 | CONV_COMPLEX_BASE = 2, |
| 109 | CONV_COMPLEX_OPT = 3 |
| 110 | }; |
| 111 | |
| 112 | struct test_data { |
| 113 | enum test_type type; |
| 114 | int h_len; |
| 115 | const float *y_ref; |
| 116 | }; |
| 117 | |
| 118 | static const char *type_name[] = { |
| 119 | "real_base", "real_opt", "complex_base", "complex_opt", |
| 120 | }; |
| 121 | |
| 122 | static const struct test_data tests[] = { |
| 123 | { CONV_REAL_BASE, 4, y_ref_real_base_4 }, |
| 124 | { CONV_REAL_BASE, 8, y_ref_real_base_8 }, |
| 125 | { CONV_REAL_BASE, 12, y_ref_real_base_12 }, |
| 126 | { CONV_REAL_BASE, 16, y_ref_real_base_16 }, |
| 127 | { CONV_REAL_BASE, 20, y_ref_real_base_20 }, |
| 128 | { CONV_REAL_BASE, 24, y_ref_real_base_24 }, |
| 129 | { CONV_COMPLEX_BASE, 4, y_ref_complex_base_4 }, |
| 130 | { CONV_COMPLEX_BASE, 8, y_ref_complex_base_8 }, |
| 131 | { CONV_COMPLEX_BASE, 12, y_ref_complex_base_12 }, |
| 132 | { CONV_COMPLEX_BASE, 16, y_ref_complex_base_16 }, |
| 133 | { CONV_COMPLEX_BASE, 20, y_ref_complex_base_20 }, |
| 134 | { CONV_COMPLEX_BASE, 24, y_ref_complex_base_24 }, |
| 135 | { 0, 0, NULL }, |
| 136 | }; |
| 137 | |
| 138 | |
| 139 | // --------------------------------------------------------------------------- |
| 140 | // Main testing logic |
| 141 | // --------------------------------------------------------------------------- |
| 142 | |
| 143 | struct test_vec |
| 144 | { |
| 145 | float *x; |
| 146 | float *h; |
| 147 | float *y; |
| 148 | |
Martin Hauke | 066fd04 | 2019-10-13 19:08:00 +0200 | [diff] [blame] | 149 | int x_len; /* These are in # of _floats_ ! */ |
| 150 | int h_len; /* These are in # of _floats_ ! */ |
| 151 | int y_len; /* These are in # of _floats_ ! */ |
Sylvain Munaut | 3733ed5 | 2018-12-21 16:38:31 +0100 | [diff] [blame] | 152 | }; |
| 153 | |
| 154 | /* Reset test vectors */ |
| 155 | static void |
| 156 | test_vec_reset(struct test_vec *tv, int seed) |
| 157 | { |
| 158 | rand_reset(); |
| 159 | |
| 160 | memset(tv->x, 0, tv->x_len * sizeof(float)); |
| 161 | memset(tv->h, 0, tv->h_len * sizeof(float)); |
| 162 | memset(tv->y, 0, tv->y_len * sizeof(float)); |
| 163 | |
| 164 | gen_floats(tv->x, tv->x_len); |
| 165 | gen_floats(tv->h, tv->h_len); |
| 166 | } |
| 167 | |
| 168 | /* Allocate test vectors */ |
| 169 | static struct test_vec * |
| 170 | test_vec_alloc(int x_len, int h_len) |
| 171 | { |
| 172 | struct test_vec *tv; |
| 173 | |
| 174 | tv = calloc(1, sizeof(struct test_vec)); |
| 175 | if (!tv) |
| 176 | return NULL; |
| 177 | |
| 178 | tv->x_len = x_len; |
| 179 | tv->h_len = h_len; |
| 180 | tv->y_len = x_len; /* Results can never be longer than x */ |
| 181 | |
| 182 | tv->x = convolve_h_alloc(x_len); |
| 183 | tv->h = convolve_h_alloc(h_len); |
| 184 | tv->y = convolve_h_alloc(tv->y_len); |
| 185 | |
| 186 | test_vec_reset(tv, 0); |
| 187 | |
| 188 | return tv; |
| 189 | } |
| 190 | |
| 191 | /* Release test vectors */ |
| 192 | static void |
| 193 | test_vec_release(struct test_vec *tv) |
| 194 | { |
| 195 | if (!tv) |
| 196 | return; |
| 197 | |
| 198 | free(tv->x); |
| 199 | free(tv->h); |
| 200 | free(tv->y); |
| 201 | |
| 202 | free(tv); |
| 203 | } |
| 204 | |
| 205 | /* Run convolution */ |
| 206 | static int |
| 207 | run_convolve(struct test_vec *tv, int h_len, enum test_type type) |
Philipp Maier | dfe0aef | 2017-03-16 18:43:33 +0100 | [diff] [blame] | 208 | { |
| 209 | int x_len; |
Sylvain Munaut | 3733ed5 | 2018-12-21 16:38:31 +0100 | [diff] [blame] | 210 | int start, len; |
Philipp Maier | dfe0aef | 2017-03-16 18:43:33 +0100 | [diff] [blame] | 211 | |
Sylvain Munaut | 3733ed5 | 2018-12-21 16:38:31 +0100 | [diff] [blame] | 212 | test_vec_reset(tv, 0); |
| 213 | |
| 214 | /* Compute params that fit within our test vectors */ |
| 215 | x_len = tv->x_len / 2; /* float vs complex */ |
| 216 | start = h_len - 1; |
| 217 | len = x_len - start; |
| 218 | |
| 219 | /* Run implementation */ |
| 220 | switch (type) { |
| 221 | case CONV_REAL_BASE: |
| 222 | base_convolve_real( |
| 223 | tv->x, x_len, |
| 224 | tv->h, h_len, |
| 225 | tv->y, tv->y_len, |
| 226 | start, len |
| 227 | ); |
| 228 | break; |
| 229 | |
| 230 | case CONV_REAL_OPT: |
| 231 | convolve_real( |
| 232 | tv->x, x_len, |
| 233 | tv->h, h_len, |
| 234 | tv->y, tv->y_len, |
| 235 | start, len |
| 236 | ); |
| 237 | break; |
| 238 | |
| 239 | case CONV_COMPLEX_BASE: |
| 240 | base_convolve_complex( |
| 241 | tv->x, x_len, |
| 242 | tv->h, h_len, |
| 243 | tv->y, tv->y_len, |
| 244 | start, len |
| 245 | ); |
| 246 | break; |
| 247 | |
| 248 | case CONV_COMPLEX_OPT: |
| 249 | convolve_complex( |
| 250 | tv->x, x_len, |
| 251 | tv->h, h_len, |
| 252 | tv->y, tv->y_len, |
| 253 | start, len |
| 254 | ); |
| 255 | break; |
| 256 | } |
| 257 | |
| 258 | return len * 2; |
Philipp Maier | dfe0aef | 2017-03-16 18:43:33 +0100 | [diff] [blame] | 259 | } |
| 260 | |
Philipp Maier | dfe0aef | 2017-03-16 18:43:33 +0100 | [diff] [blame] | 261 | |
Sylvain Munaut | 3733ed5 | 2018-12-21 16:38:31 +0100 | [diff] [blame] | 262 | int main(int argc, char *argv[]) |
Philipp Maier | dfe0aef | 2017-03-16 18:43:33 +0100 | [diff] [blame] | 263 | { |
Sylvain Munaut | 3733ed5 | 2018-12-21 16:38:31 +0100 | [diff] [blame] | 264 | struct test_vec *tv; |
| 265 | int gen_ref_mode = 0; |
| 266 | char name[80]; |
| 267 | int i, j, len; |
| 268 | |
Philipp Maier | dfe0aef | 2017-03-16 18:43:33 +0100 | [diff] [blame] | 269 | convolve_init(); |
Philipp Maier | dfe0aef | 2017-03-16 18:43:33 +0100 | [diff] [blame] | 270 | |
Sylvain Munaut | 3733ed5 | 2018-12-21 16:38:31 +0100 | [diff] [blame] | 271 | /* Mode */ |
| 272 | gen_ref_mode = (argc == 2) && !strcmp("genref", argv[1]); |
Philipp Maier | dfe0aef | 2017-03-16 18:43:33 +0100 | [diff] [blame] | 273 | |
Sylvain Munaut | 3733ed5 | 2018-12-21 16:38:31 +0100 | [diff] [blame] | 274 | /* Alloc test vectors */ |
| 275 | /* All *2 is to account for the facts all vectors are actually |
| 276 | * complex and need two floats */ |
| 277 | tv = test_vec_alloc(100*2, 25*2); |
Philipp Maier | dfe0aef | 2017-03-16 18:43:33 +0100 | [diff] [blame] | 278 | |
Sylvain Munaut | 3733ed5 | 2018-12-21 16:38:31 +0100 | [diff] [blame] | 279 | /* Dump all input data to make sure we work off the same input data */ |
| 280 | if (!gen_ref_mode) { |
| 281 | printf("==== TEST INPUT DATA ====\n"); |
| 282 | dump_floats(tv->x, tv->x_len, "x"); |
| 283 | dump_floats(tv->h, tv->h_len, "h"); |
| 284 | printf("\n"); |
| 285 | printf("\n"); |
| 286 | } |
Philipp Maier | dfe0aef | 2017-03-16 18:43:33 +0100 | [diff] [blame] | 287 | |
Sylvain Munaut | 3733ed5 | 2018-12-21 16:38:31 +0100 | [diff] [blame] | 288 | /* Run through all the tests */ |
| 289 | if (!gen_ref_mode) |
| 290 | printf("==== TEST ====\n"); |
Philipp Maier | dfe0aef | 2017-03-16 18:43:33 +0100 | [diff] [blame] | 291 | |
Sylvain Munaut | 3733ed5 | 2018-12-21 16:38:31 +0100 | [diff] [blame] | 292 | for (i=0; tests[i].h_len; i++) |
| 293 | { |
| 294 | for (j=0; j<(gen_ref_mode ? 1 : 2); j++) |
| 295 | { |
| 296 | len = run_convolve(tv, tests[i].h_len, tests[i].type + j); |
Philipp Maier | dfe0aef | 2017-03-16 18:43:33 +0100 | [diff] [blame] | 297 | |
Sylvain Munaut | 3733ed5 | 2018-12-21 16:38:31 +0100 | [diff] [blame] | 298 | snprintf(name, sizeof(name)-1, "y_ref_%s_%d", type_name[tests[i].type + j], tests[i].h_len); |
Philipp Maier | dfe0aef | 2017-03-16 18:43:33 +0100 | [diff] [blame] | 299 | |
Sylvain Munaut | 3733ed5 | 2018-12-21 16:38:31 +0100 | [diff] [blame] | 300 | if (gen_ref_mode) |
| 301 | { |
| 302 | /* If in generate mode, output data */ |
| 303 | dump_floats(tv->y, len, name); |
| 304 | } else { |
| 305 | /* If in test mode, compare with data */ |
| 306 | printf("%s: %s\n", |
| 307 | name, |
| 308 | compare_floats(tests[i].y_ref, tv->y, len, 1e-5f, 1e-5f) ? "FAIL" : "PASS" |
| 309 | ); |
| 310 | } |
| 311 | } |
| 312 | } |
Philipp Maier | dfe0aef | 2017-03-16 18:43:33 +0100 | [diff] [blame] | 313 | |
Sylvain Munaut | 3733ed5 | 2018-12-21 16:38:31 +0100 | [diff] [blame] | 314 | if (!gen_ref_mode) { |
| 315 | printf("\n"); |
| 316 | printf("\n"); |
| 317 | } |
Philipp Maier | dfe0aef | 2017-03-16 18:43:33 +0100 | [diff] [blame] | 318 | |
Sylvain Munaut | 3733ed5 | 2018-12-21 16:38:31 +0100 | [diff] [blame] | 319 | /* All done ! */ |
| 320 | test_vec_release(tv); |
Philipp Maier | dfe0aef | 2017-03-16 18:43:33 +0100 | [diff] [blame] | 321 | |
| 322 | return 0; |
| 323 | } |