| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <math.h> |
| |
| #include "convolve.h" |
| |
| |
| // --------------------------------------------------------------------------- |
| // Misc utils |
| // --------------------------------------------------------------------------- |
| |
| /* Generate some random values for testing */ |
| static unsigned long rand_state = 0; |
| |
| static void |
| rand_reset(void) |
| { |
| rand_state = 0; |
| } |
| |
| static unsigned long |
| rand_int(void) |
| { |
| rand_state = (1103515245UL * rand_state + 12345UL) & 0x7fffffffUL; |
| return rand_state; |
| } |
| |
| static float |
| rand_float(void) |
| { |
| union { |
| uint32_t u; |
| float f; |
| } r; |
| uint32_t u = rand_int(); |
| int e = 112 + ((u ^ (u>>8)) & 15); |
| |
| r.u = u & 0x007fffffUL; // Mantissa |
| r.u |= (u & 0x00800000UL) << 8; // Sign |
| r.u |= (e & 0xffUL) << 23; // Exponent |
| |
| return r.f; |
| } |
| |
| static void |
| gen_floats(float *vect, int len) |
| { |
| int i; |
| for (i = 0; i < len; i++) |
| vect[i] = rand_float(); |
| } |
| |
| /* Show float vector data cut and paste friendly */ |
| static void |
| dump_floats(float *vect, int len, char *name) |
| { |
| int i; |
| |
| printf("static const float %s[] = {\n\t", name); |
| for(i = 0; i < len; i++) { |
| char *end; |
| if (i == len-1) |
| end = "\n"; |
| else if ((i&3) == 3) |
| end = ",\n\t"; |
| else |
| end = ", "; |
| printf("%14.7ef%s", vect[i], end); |
| } |
| printf("};\n"); |
| } |
| |
| /* Compare float with tolerance of delta (absolute) and epsilon (relative) */ |
| static int |
| compare_floats(const float *v0, const float *v1, int len, float delta, float epsilon) |
| { |
| int i; |
| |
| for (i=0; i<len; i++) |
| { |
| float a = v0[i]; |
| float b = v1[i]; |
| |
| if (fabsf(a - b) < delta) |
| continue; |
| |
| if (fabsf(1.0f - (a/b)) < epsilon) |
| continue; |
| |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| |
| // --------------------------------------------------------------------------- |
| // Golden reference results |
| // --------------------------------------------------------------------------- |
| |
| #include "convolve_test_golden.h" |
| |
| enum test_type { |
| CONV_REAL_BASE = 0, |
| CONV_REAL_OPT = 1, |
| CONV_COMPLEX_BASE = 2, |
| CONV_COMPLEX_OPT = 3 |
| }; |
| |
| struct test_data { |
| enum test_type type; |
| int h_len; |
| const float *y_ref; |
| }; |
| |
| static const char *type_name[] = { |
| "real_base", "real_opt", "complex_base", "complex_opt", |
| }; |
| |
| static const struct test_data tests[] = { |
| { CONV_REAL_BASE, 4, y_ref_real_base_4 }, |
| { CONV_REAL_BASE, 8, y_ref_real_base_8 }, |
| { CONV_REAL_BASE, 12, y_ref_real_base_12 }, |
| { CONV_REAL_BASE, 16, y_ref_real_base_16 }, |
| { CONV_REAL_BASE, 20, y_ref_real_base_20 }, |
| { CONV_REAL_BASE, 24, y_ref_real_base_24 }, |
| { CONV_COMPLEX_BASE, 4, y_ref_complex_base_4 }, |
| { CONV_COMPLEX_BASE, 8, y_ref_complex_base_8 }, |
| { CONV_COMPLEX_BASE, 12, y_ref_complex_base_12 }, |
| { CONV_COMPLEX_BASE, 16, y_ref_complex_base_16 }, |
| { CONV_COMPLEX_BASE, 20, y_ref_complex_base_20 }, |
| { CONV_COMPLEX_BASE, 24, y_ref_complex_base_24 }, |
| { 0, 0, NULL }, |
| }; |
| |
| |
| // --------------------------------------------------------------------------- |
| // Main testing logic |
| // --------------------------------------------------------------------------- |
| |
| struct test_vec |
| { |
| float *x; |
| float *h; |
| float *y; |
| |
| int x_len; /* These are in # of _floats_ ! */ |
| int h_len; /* These are in # of _floats_ ! */ |
| int y_len; /* These are in # of _floats_ ! */ |
| }; |
| |
| /* Reset test vectors */ |
| static void |
| test_vec_reset(struct test_vec *tv, int seed) |
| { |
| rand_reset(); |
| |
| memset(tv->x, 0, tv->x_len * sizeof(float)); |
| memset(tv->h, 0, tv->h_len * sizeof(float)); |
| memset(tv->y, 0, tv->y_len * sizeof(float)); |
| |
| gen_floats(tv->x, tv->x_len); |
| gen_floats(tv->h, tv->h_len); |
| } |
| |
| /* Allocate test vectors */ |
| static struct test_vec * |
| test_vec_alloc(int x_len, int h_len) |
| { |
| struct test_vec *tv; |
| |
| tv = calloc(1, sizeof(struct test_vec)); |
| if (!tv) |
| return NULL; |
| |
| tv->x_len = x_len; |
| tv->h_len = h_len; |
| tv->y_len = x_len; /* Results can never be longer than x */ |
| |
| tv->x = convolve_h_alloc(x_len); |
| tv->h = convolve_h_alloc(h_len); |
| tv->y = convolve_h_alloc(tv->y_len); |
| |
| test_vec_reset(tv, 0); |
| |
| return tv; |
| } |
| |
| /* Release test vectors */ |
| static void |
| test_vec_release(struct test_vec *tv) |
| { |
| if (!tv) |
| return; |
| |
| free(tv->x); |
| free(tv->h); |
| free(tv->y); |
| |
| free(tv); |
| } |
| |
| /* Run convolution */ |
| static int |
| run_convolve(struct test_vec *tv, int h_len, enum test_type type) |
| { |
| int x_len; |
| int start, len; |
| |
| test_vec_reset(tv, 0); |
| |
| /* Compute params that fit within our test vectors */ |
| x_len = tv->x_len / 2; /* float vs complex */ |
| start = h_len - 1; |
| len = x_len - start; |
| |
| /* Run implementation */ |
| switch (type) { |
| case CONV_REAL_BASE: |
| base_convolve_real( |
| tv->x, x_len, |
| tv->h, h_len, |
| tv->y, tv->y_len, |
| start, len |
| ); |
| break; |
| |
| case CONV_REAL_OPT: |
| convolve_real( |
| tv->x, x_len, |
| tv->h, h_len, |
| tv->y, tv->y_len, |
| start, len |
| ); |
| break; |
| |
| case CONV_COMPLEX_BASE: |
| base_convolve_complex( |
| tv->x, x_len, |
| tv->h, h_len, |
| tv->y, tv->y_len, |
| start, len |
| ); |
| break; |
| |
| case CONV_COMPLEX_OPT: |
| convolve_complex( |
| tv->x, x_len, |
| tv->h, h_len, |
| tv->y, tv->y_len, |
| start, len |
| ); |
| break; |
| } |
| |
| return len * 2; |
| } |
| |
| |
| int main(int argc, char *argv[]) |
| { |
| struct test_vec *tv; |
| int gen_ref_mode = 0; |
| char name[80]; |
| int i, j, len; |
| |
| convolve_init(); |
| |
| /* Mode */ |
| gen_ref_mode = (argc == 2) && !strcmp("genref", argv[1]); |
| |
| /* Alloc test vectors */ |
| /* All *2 is to account for the facts all vectors are actually |
| * complex and need two floats */ |
| tv = test_vec_alloc(100*2, 25*2); |
| |
| /* Dump all input data to make sure we work off the same input data */ |
| if (!gen_ref_mode) { |
| printf("==== TEST INPUT DATA ====\n"); |
| dump_floats(tv->x, tv->x_len, "x"); |
| dump_floats(tv->h, tv->h_len, "h"); |
| printf("\n"); |
| printf("\n"); |
| } |
| |
| /* Run through all the tests */ |
| if (!gen_ref_mode) |
| printf("==== TEST ====\n"); |
| |
| for (i=0; tests[i].h_len; i++) |
| { |
| for (j=0; j<(gen_ref_mode ? 1 : 2); j++) |
| { |
| len = run_convolve(tv, tests[i].h_len, tests[i].type + j); |
| |
| snprintf(name, sizeof(name)-1, "y_ref_%s_%d", type_name[tests[i].type + j], tests[i].h_len); |
| |
| if (gen_ref_mode) |
| { |
| /* If in generate mode, output data */ |
| dump_floats(tv->y, len, name); |
| } else { |
| /* If in test mode, compare with data */ |
| printf("%s: %s\n", |
| name, |
| compare_floats(tests[i].y_ref, tv->y, len, 1e-5f, 1e-5f) ? "FAIL" : "PASS" |
| ); |
| } |
| } |
| } |
| |
| if (!gen_ref_mode) { |
| printf("\n"); |
| printf("\n"); |
| } |
| |
| /* All done ! */ |
| test_vec_release(tv); |
| |
| return 0; |
| } |