Ondřej Hruška 6 years ago
parent
commit
26c94c9aef
6 changed files with 197 additions and 27 deletions
  1. 8 1
      Makefile
  2. 6 3
      envelope-proj.pro
  3. 31 2
      main.c
  4. 24 0
      main.d
  5. 61 13
      src/vec_match.c
  6. 67 8
      src/vec_match.h

+ 8 - 1
Makefile View File

@@ -1,7 +1,14 @@
1
+CFLAGS      += -Os -ggdb -std=gnu99 -Wfatal-errors
2
+CFLAGS      += -Wall -Wextra -Wshadow
3
+CFLAGS      += -Wwrite-strings -Wold-style-definition -Winline -Wmissing-noreturn -Wstrict-prototypes
4
+CFLAGS      += -Wredundant-decls -Wfloat-equal -Wsign-compare
5
+CFLAGS      += -Wunused-function
6
+CFLAGS    += -MD -lm
7
+
1 8
 all: main.out
2 9
 
3 10
 main.out: main.c src/vec_match.c src/vec_match.h
4
-	gcc -o main.out main.c src/vec_match.c -Isrc
11
+	gcc $(CFLAGS) -o main.out main.c src/vec_match.c -Isrc
5 12
 
6 13
 run: main.out
7 14
 	./main.out

+ 6 - 3
envelope-proj.pro View File

@@ -4,10 +4,13 @@ CONFIG -= app_bundle
4 4
 CONFIG -= qt
5 5
 
6 6
 SOURCES += main.c \
7
-    vec_match.c
7
+    vec_match.c \
8
+    src/vec_match.c
8 9
 
9 10
 DISTFILES += \
10
-    style.astylerc
11
+    style.astylerc \
12
+    Makefile
11 13
 
12 14
 HEADERS += \
13
-    vec_match.h
15
+    vec_match.h \
16
+    src/vec_match.h

+ 31 - 2
main.c View File

@@ -15,8 +15,37 @@ static float data[DATA_LEN] = {
15 15
 };
16 16
 
17 17
 
18
-int main()
18
+static float data_cprs[16] = {
19
+	0, 15.7, 0, 0, 0.1, 0.2, 0.1, 10, 24.242, 0, 0, 0.1, 0.2, 0.4, 0.5, 0
20
+};
21
+
22
+static float data_cprs2[16];
23
+
24
+int main(void)
19 25
 {
26
+	for (int i = 0; i < 16; i++) {
27
+		printf("%.1f, ", data_cprs[i]);
28
+	}
29
+	printf("\n");
30
+
31
+	int len = vec_pack(data_cprs2, 16, data_cprs, 16, 2);
32
+	printf("compressed len = %d\n", len);
33
+
34
+	for (int i = 0; i < len; i++) {
35
+		printf("%.1f, ", data_cprs2[i]);
36
+	}
37
+	printf("\n");
38
+
39
+
40
+	return 0;
41
+
42
+	vec_match_cfg_t cfg = {
43
+		.length = DATA_LEN,
44
+		.drift_x = 1,
45
+		.offset_y = 5,
46
+		.abs_threshold = 0.1
47
+	};
48
+
20 49
 	// example
21 50
 
22 51
 	printf("REF:  ");
@@ -36,7 +65,7 @@ int main()
36 65
 	// error metric fields
37 66
 	float env_e, abs_e;
38 67
 
39
-	bool ok = vec_match(data, reference, DATA_LEN, 1, 5, &env_e, &abs_e);
68
+	bool ok = vec_match(data, reference, &cfg, &env_e, &abs_e);
40 69
 	printf("%s", ok ? "MATCH OK" : "MATCH FAILED");
41 70
 	printf("\n");
42 71
 

+ 24 - 0
main.d View File

@@ -0,0 +1,24 @@
1
+main.out: src/vec_match.c /usr/include/stdc-predef.h \
2
+ /usr/lib/gcc/x86_64-unknown-linux-gnu/5.3.0/include/stdint.h \
3
+ /usr/include/stdint.h /usr/include/features.h /usr/include/sys/cdefs.h \
4
+ /usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
5
+ /usr/include/gnu/stubs-64.h /usr/include/bits/wchar.h \
6
+ /usr/lib/gcc/x86_64-unknown-linux-gnu/5.3.0/include/stdbool.h \
7
+ /usr/include/stdlib.h \
8
+ /usr/lib/gcc/x86_64-unknown-linux-gnu/5.3.0/include/stddef.h \
9
+ /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \
10
+ /usr/include/endian.h /usr/include/bits/endian.h \
11
+ /usr/include/bits/byteswap.h /usr/include/bits/types.h \
12
+ /usr/include/bits/typesizes.h /usr/include/bits/byteswap-16.h \
13
+ /usr/include/sys/types.h /usr/include/time.h /usr/include/sys/select.h \
14
+ /usr/include/bits/select.h /usr/include/bits/sigset.h \
15
+ /usr/include/bits/time.h /usr/include/sys/sysmacros.h \
16
+ /usr/include/bits/pthreadtypes.h /usr/include/alloca.h \
17
+ /usr/include/bits/stdlib-float.h \
18
+ /usr/lib/gcc/x86_64-unknown-linux-gnu/5.3.0/include/float.h \
19
+ /usr/include/math.h /usr/include/bits/math-vector.h \
20
+ /usr/include/bits/libm-simd-decl-stubs.h /usr/include/bits/huge_val.h \
21
+ /usr/include/bits/huge_valf.h /usr/include/bits/huge_vall.h \
22
+ /usr/include/bits/inf.h /usr/include/bits/nan.h \
23
+ /usr/include/bits/mathdef.h /usr/include/bits/mathcalls.h \
24
+ src/vec_match.h

+ 61 - 13
src/vec_match.c View File

@@ -2,6 +2,7 @@
2 2
 #include <stdbool.h>
3 3
 #include <stdlib.h>
4 4
 #include <float.h>
5
+#include <math.h>
5 6
 
6 7
 #include "vec_match.h"
7 8
 
@@ -9,10 +10,8 @@
9 10
 
10 11
 bool vec_match(const float *data,
11 12
 			   const float *ref,
12
-			   uint32_t length,
13
-			   uint8_t drift_x,
14
-			   float offset_y,
15
-			   float *envl_match_error,
13
+			   const vec_match_cfg_t *cfg,
14
+			   float *fuzzy_match_error,
16 15
 			   float *abs_match_error)
17 16
 {
18 17
 	int a, b;
@@ -21,15 +20,15 @@ bool vec_match(const float *data,
21 20
 	float env_err = 0;
22 21
 	float abs_err = 0;
23 22
 
24
-	for (int i = 0; i < (int)length; i++) {
23
+	for (int i = 0; i < (int)cfg->length; i++) {
25 24
 		float peak = FLT_MIN;
26 25
 		float base = FLT_MAX;
27 26
 
28 27
 		// find highest value in the surrounding drift_x points
29
-		a = i - drift_x;
30
-		b = i + drift_x;
28
+		a = i - cfg->drift_x;
29
+		b = i + cfg->drift_x;
31 30
 		if (a < 0) a = 0;
32
-		if (b >= (int)length) b = length - 1;
31
+		if (b >= (int)cfg->length) b = cfg->length - 1;
33 32
 
34 33
 		for (int j = a; j <= b; j++) {
35 34
 			if (peak < ref[j]) peak = ref[j];
@@ -37,13 +36,16 @@ bool vec_match(const float *data,
37 36
 		}
38 37
 
39 38
 		// apply drift_y
40
-		peak += offset_y;
41
-		base -= offset_y;
39
+		peak += cfg->offset_y; // add abs threshold on top
40
+		base -= cfg->offset_y;
42 41
 
43
-		abs_err += SQUARE(ref[i] - data[i]);
42
+		// ignore abs threshold difference (float precision error)
43
+		if (fabs(ref[i] - data[i]) > cfg->abs_threshold) {
44
+			abs_err += SQUARE(ref[i] - data[i]);
45
+		}
44 46
 
45 47
 
46
-		if (data[i] >= base && data[i] <= peak) {
48
+		if (data[i] >= (base - cfg->abs_threshold) && data[i] <= (peak + cfg->abs_threshold)) {
47 49
 			// within limits
48 50
 			continue;
49 51
 		} else {
@@ -57,8 +59,54 @@ bool vec_match(const float *data,
57 59
 	}
58 60
 
59 61
 	// write error values to provided fields
60
-	if (envl_match_error != NULL) *envl_match_error = env_err;
62
+	if (fuzzy_match_error != NULL) *fuzzy_match_error = env_err;
61 63
 	if (abs_match_error != NULL) *abs_match_error = abs_err;
62 64
 
63 65
 	return err_cnt == 0;
64 66
 }
67
+
68
+
69
+
70
+
71
+uint32_t vec_pack(float *result, uint32_t result_capacity, const float *data, uint32_t data_length, float threshold)
72
+{
73
+	uint32_t result_len = 0;
74
+	uint32_t zeroes = 0;
75
+
76
+	for (uint32_t i = 0; i < data_length; i++) {
77
+		if (data[i] < threshold) {
78
+			zeroes++;
79
+		} else {
80
+			// write zero marker to result
81
+			if (zeroes) {
82
+				if (result_len < result_capacity) {
83
+					result[result_len] = 0.0f - zeroes; // float and negative
84
+				}
85
+
86
+				zeroes = 0;
87
+				result_len++; //length is increased even if buffer full
88
+			}
89
+
90
+			if (result_len < result_capacity) {
91
+				result[result_len] = data[i];
92
+			}
93
+
94
+			result_len++;
95
+		}
96
+	}
97
+
98
+	// handle trailing zeroes
99
+	if (zeroes) {
100
+		if (result_len < result_capacity) {
101
+			result[result_len] = 0.0f - zeroes;
102
+		}
103
+
104
+		result_len++;
105
+	}
106
+
107
+	return result_len;
108
+}
109
+
110
+
111
+
112
+

+ 67 - 8
src/vec_match.h View File

@@ -3,23 +3,82 @@
3 3
 #include <stdint.h>
4 4
 #include <stdbool.h>
5 5
 
6
+/* Example: drift_x 1, offset_y 10, abs_threshold 0.1 */
7
+typedef struct {
8
+	uint32_t length; // data length (data & ref length must be equal)
9
+	uint32_t drift_x; // allowed horizontal drift (Hz drift if values are 1 Hz FFT bins)
10
+	float offset_y;  // allowed vertical offset (bin amplitude, positive or negative)
11
+	float abs_threshold; // absolute threshold (to fix preccision errors, also added to offset_y)
12
+} vec_match_cfg_t;
13
+
6 14
 
7 15
 /**
8 16
  * Match signal to reference, allowing for some offser and noise
9 17
  *
10 18
  * @param data matched data
11 19
  * @param ref reference data
12
- * @param length data length (data & ref length must be equal)
13
- * @param drift_x allowed horizontal drift (Hz drift if values are 1 Hz FFT bins)
14
- * @param offset_y allowed vertical offset (bin amplitude, positive or negative)
15
- * @param envl_match_error error metric calculated with allowed drift and offset
20
+ * @param cfg config struct
21
+ * @param fuzzy_match_error error metric calculated with allowed drift and offset
16 22
  * @param abs_match_error error metric calculated from raw data (can be used if envelope match passes)
17 23
  * @return envelope match status (match using drift and offset)
18 24
  */
19 25
 bool vec_match(const float *data,
20 26
 			   const float *ref,
21
-			   uint32_t length,
22
-			   uint8_t drift_x,
23
-			   float offset_y,
24
-			   float *envl_match_error,
27
+			   const vec_match_cfg_t *cfg,
28
+			   float *fuzzy_match_error,
25 29
 			   float *abs_match_error);
30
+
31
+
32
+/**
33
+ * Match vectors of positive numbers.
34
+ * Negative number indicates how many consecutive elements are zero (hence the compression).
35
+ *
36
+ * 1024-long vector [12, 0, ...] would be [12, -1023]
37
+ *
38
+ * Params otherwise the same as vec_match()
39
+ */
40
+bool vec_match_packed(const float *data,
41
+					const float *ref,
42
+					const vec_match_cfg_t *cfg,
43
+					float *fuzzy_match_error,
44
+					float *abs_match_error);
45
+
46
+
47
+/**
48
+ * Compress a vector by replacing sequence of zeroes with a negative value indicating their count.
49
+ *
50
+ * Returned length may exceed result_capacity, but the buffer is never overrun.
51
+ * That can be used to gradually increase the threshold until the compressed data fits in the result buffer.
52
+ *
53
+ * The compression is by definition lossy.
54
+ *
55
+ * @param result result vector (can be the same as data vector for in-place operation)
56
+ * @param result_capacity size of result buffer.
57
+ * @param data data vector
58
+ * @param length data legth
59
+ * @param threshold max value to be considered zero in the compression
60
+ * @return length of result vector
61
+ */
62
+uint32_t vec_pack(float *result,
63
+					  uint32_t result_capacity,
64
+					  const float *data,
65
+					  uint32_t length,
66
+					  float threshold);
67
+
68
+
69
+/**
70
+ * Unpack a vector compressed with vec_pack().
71
+ *
72
+ * If returned length exceeds provided buffer capacity, it's an indication that you need to enlarge your buffer.
73
+ * The buffer is never overrun, though.
74
+ *
75
+ * @param result result buffer
76
+ * @param result_capacity result buffer size
77
+ * @param compr_data compressed data vector
78
+ * @param compr_length compressed data vector length
79
+ * @return
80
+ */
81
+uint32_t vec_unpack(float *result,
82
+					uint32_t result_capacity,
83
+					const float *compr_data,
84
+					uint32_t compr_length);