parent
							
								
									70face0a0a
								
							
						
					
					
						commit
						33a394e3da
					
				@ -0,0 +1,4 @@ | 
				
			|||||||
 | 
					all: main.out | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					main.out: main.c vec_match.c vec_match.h | 
				
			||||||
 | 
						gcc -o main.out main.c vec_match.c -I.
 | 
				
			||||||
@ -1,161 +1,44 @@ | 
				
			|||||||
#include <stdio.h> | 
					#include <stdio.h> | 
				
			||||||
#include <stdlib.h> | 
					#include <stdlib.h> | 
				
			||||||
#include <stdint.h> | 
					 | 
				
			||||||
#include <stdbool.h> | 
					#include <stdbool.h> | 
				
			||||||
#include <string.h> | 
					 | 
				
			||||||
#include <limits.h> | 
					 | 
				
			||||||
#include <float.h> | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define DATA_LEN 11 | 
					#include "vec_match.h" | 
				
			||||||
 | 
					 | 
				
			||||||
#define SQUARE(a) ((a)*(a)) | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define DATA_LEN 11 | 
				
			||||||
 | 
					
 | 
				
			||||||
static float reference[DATA_LEN] = { | 
					static float reference[DATA_LEN] = { | 
				
			||||||
	0, 10, 20, 30, 40, 50, 40, 30, 20, 10, 0 | 
						0, 10, 20, 30, 40, 50, 40, 30, 20, 10, 0 | 
				
			||||||
}; | 
					}; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
static float data[DATA_LEN] = { | 
					static float data[DATA_LEN] = { | 
				
			||||||
	0, 10, 20, 30, 40, 50, 40, 30, 20, 10, 0 | 
						0, 10, 20, 30, 40, 50, 50, 40, 30, 30, 10 | 
				
			||||||
}; | 
					}; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Calculate fuzzy envelope | 
					 | 
				
			||||||
 * | 
					 | 
				
			||||||
 * @param data source data | 
					 | 
				
			||||||
 * @param envelope envelope (same length as source data) | 
					 | 
				
			||||||
 * @param length data length | 
					 | 
				
			||||||
 * @param drift_x horizontal offset (left/right growth) | 
					 | 
				
			||||||
 * @param drift_y vertical offset (vertical growth) | 
					 | 
				
			||||||
 */ | 
					 | 
				
			||||||
void calc_envelope(const float *data, float *envelope, uint32_t length, uint8_t drift_x, float drift_y) | 
					 | 
				
			||||||
{ | 
					 | 
				
			||||||
	int a, b, i, j; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for (i = 0; i < (int)length; i++) { | 
					 | 
				
			||||||
		float peak = FLT_MIN; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// find highest value in the surrounding drift_x points
 | 
					 | 
				
			||||||
		a = i - drift_x; | 
					 | 
				
			||||||
		b = i + drift_x; | 
					 | 
				
			||||||
		if (a < 0) a = 0; | 
					 | 
				
			||||||
		if (b >= (int)length) b = length - 1; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for (j = a; j <= b; j++) { | 
					 | 
				
			||||||
			if (peak < data[j]) peak = data[j]; | 
					 | 
				
			||||||
		} | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// apply drift_y
 | 
					 | 
				
			||||||
		peak += drift_y; | 
					 | 
				
			||||||
		envelope[i] = peak; | 
					 | 
				
			||||||
	} | 
					 | 
				
			||||||
} | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Match signal to reference, allowing for some offser and noise | 
					 | 
				
			||||||
 * | 
					 | 
				
			||||||
 * @param data matched data | 
					 | 
				
			||||||
 * @param ref reference data | 
					 | 
				
			||||||
 * @param length data length (data & ref length must be equal) | 
					 | 
				
			||||||
 * @param drift_x allowed horizontal drift (Hz drift if values are 1 Hz FFT bins) | 
					 | 
				
			||||||
 * @param offset_y allowed vertical offset (bin amplitude, positive or negative) | 
					 | 
				
			||||||
 * @param envl_match_error error metric calculated with allowed drift and offset | 
					 | 
				
			||||||
 * @param abs_match_error error metric calculated from raw data (can be used if envelope match passes) | 
					 | 
				
			||||||
 * @return envelope match status (match using drift and offset) | 
					 | 
				
			||||||
 */ | 
					 | 
				
			||||||
bool match_envelope(const float *data, | 
					 | 
				
			||||||
					const float *ref, | 
					 | 
				
			||||||
					uint32_t length, | 
					 | 
				
			||||||
					uint8_t drift_x, | 
					 | 
				
			||||||
					float offset_y, | 
					 | 
				
			||||||
					float *envl_match_error, | 
					 | 
				
			||||||
					float *abs_match_error) | 
					 | 
				
			||||||
{ | 
					 | 
				
			||||||
	int a, b; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int err_cnt = 0; | 
					 | 
				
			||||||
	float env_err = 0; | 
					 | 
				
			||||||
	float abs_err = 0; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for (int i = 0; i < (int)length; i++) { | 
					 | 
				
			||||||
		float peak = FLT_MIN; | 
					 | 
				
			||||||
		float base = FLT_MAX; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// find highest value in the surrounding drift_x points
 | 
					 | 
				
			||||||
		a = i - drift_x; | 
					 | 
				
			||||||
		b = i + drift_x; | 
					 | 
				
			||||||
		if (a < 0) a = 0; | 
					 | 
				
			||||||
		if (b >= (int)length) b = length - 1; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for (int j = a; j <= b; j++) { | 
					 | 
				
			||||||
			if (peak < ref[j]) peak = ref[j]; | 
					 | 
				
			||||||
			if (base > ref[j]) base = ref[j]; | 
					 | 
				
			||||||
		} | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// apply drift_y
 | 
					 | 
				
			||||||
		peak += offset_y; | 
					 | 
				
			||||||
		base -= offset_y; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		abs_err += SQUARE(ref[i] - data[i]); | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (data[i] >= base && data[i] <= peak) { | 
					 | 
				
			||||||
			// within limits
 | 
					 | 
				
			||||||
			continue; | 
					 | 
				
			||||||
		} else { | 
					 | 
				
			||||||
			printf("data[%d] out of range: %f, [%f ; %f]\n", i, data[i], base, peak); | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (data[i] < base) env_err += SQUARE(base - data[i]); | 
					 | 
				
			||||||
			if (data[i] > peak) env_err += SQUARE(data[i] - peak); | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			err_cnt++; | 
					 | 
				
			||||||
		} | 
					 | 
				
			||||||
	} | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// write error values to provided fields
 | 
					 | 
				
			||||||
	if (envl_match_error != NULL) *envl_match_error = env_err; | 
					 | 
				
			||||||
	if (abs_match_error != NULL) *abs_match_error = abs_err; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return err_cnt == 0; | 
					 | 
				
			||||||
} | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int main() | 
					int main() | 
				
			||||||
{ | 
					{ | 
				
			||||||
 | 
						// example
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	printf("REF:  "); | 
						printf("REF:  "); | 
				
			||||||
	for (int i = 0; i < DATA_LEN; i++) { | 
						for (int i = 0; i < DATA_LEN; i++) { | 
				
			||||||
		printf("%.0f, ", reference[i]); | 
							printf("%.0f, ", reference[i]); | 
				
			||||||
	} | 
						} | 
				
			||||||
	printf("\n"); | 
						printf("\n"); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	printf("MEAS: "); | 
						printf("MEAS: "); | 
				
			||||||
	for (int i = 0; i < DATA_LEN; i++) { | 
						for (int i = 0; i < DATA_LEN; i++) { | 
				
			||||||
		printf("%.0f, ", data[i]); | 
							printf("%.0f, ", data[i]); | 
				
			||||||
	} | 
						} | 
				
			||||||
	printf("\n"); | 
						printf("\n"); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// error metric fields
 | 
				
			||||||
	float env_e, abs_e; | 
						float env_e, abs_e; | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool ok = match_envelope(data, reference, DATA_LEN, 1, 5, &env_e, &abs_e); | 
						bool ok = vec_match(data, reference, DATA_LEN, 1, 5, &env_e, &abs_e); | 
				
			||||||
	printf("%s", ok ? "MATCH OK" : "MATCH FAILED"); | 
						printf("%s", ok ? "MATCH OK" : "MATCH FAILED"); | 
				
			||||||
	printf("\n"); | 
						printf("\n"); | 
				
			||||||
 | 
					
 | 
				
			||||||
	printf("Error rate: ENV %.2f, ABS %.2f\n", env_e, abs_e); | 
						printf("Error rate: ENV %.2f, ABS %.2f\n", env_e, abs_e); | 
				
			||||||
 | 
					 | 
				
			||||||
	/*
 | 
					 | 
				
			||||||
	calc_envelope(data, envelope, 20, 1, 5); | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for (int i = 0; i < 20; i++) { | 
					 | 
				
			||||||
		printf("%.0f, ", envelope[i]); | 
					 | 
				
			||||||
	} | 
					 | 
				
			||||||
	printf("\n"); | 
					 | 
				
			||||||
	*/ | 
					 | 
				
			||||||
} | 
					} | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,64 @@ | 
				
			|||||||
 | 
					#include <stdint.h> | 
				
			||||||
 | 
					#include <stdbool.h> | 
				
			||||||
 | 
					#include <stdlib.h> | 
				
			||||||
 | 
					#include <float.h> | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "vec_match.h" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define SQUARE(a) ((a)*(a)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool vec_match(const float *data, | 
				
			||||||
 | 
										const float *ref, | 
				
			||||||
 | 
										uint32_t length, | 
				
			||||||
 | 
										uint8_t drift_x, | 
				
			||||||
 | 
										float offset_y, | 
				
			||||||
 | 
										float *envl_match_error, | 
				
			||||||
 | 
										float *abs_match_error) | 
				
			||||||
 | 
					{ | 
				
			||||||
 | 
						int a, b; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int err_cnt = 0; | 
				
			||||||
 | 
						float env_err = 0; | 
				
			||||||
 | 
						float abs_err = 0; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (int i = 0; i < (int)length; i++) { | 
				
			||||||
 | 
							float peak = FLT_MIN; | 
				
			||||||
 | 
							float base = FLT_MAX; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// find highest value in the surrounding drift_x points
 | 
				
			||||||
 | 
							a = i - drift_x; | 
				
			||||||
 | 
							b = i + drift_x; | 
				
			||||||
 | 
							if (a < 0) a = 0; | 
				
			||||||
 | 
							if (b >= (int)length) b = length - 1; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (int j = a; j <= b; j++) { | 
				
			||||||
 | 
								if (peak < ref[j]) peak = ref[j]; | 
				
			||||||
 | 
								if (base > ref[j]) base = ref[j]; | 
				
			||||||
 | 
							} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// apply drift_y
 | 
				
			||||||
 | 
							peak += offset_y; | 
				
			||||||
 | 
							base -= offset_y; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							abs_err += SQUARE(ref[i] - data[i]); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (data[i] >= base && data[i] <= peak) { | 
				
			||||||
 | 
								// within limits
 | 
				
			||||||
 | 
								continue; | 
				
			||||||
 | 
							} else { | 
				
			||||||
 | 
								//printf("data[%d] out of range: %f, [%f ; %f]\n", i, data[i], base, peak);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (data[i] < base) env_err += SQUARE(base - data[i]); | 
				
			||||||
 | 
								if (data[i] > peak) env_err += SQUARE(data[i] - peak); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								err_cnt++; | 
				
			||||||
 | 
							} | 
				
			||||||
 | 
						} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// write error values to provided fields
 | 
				
			||||||
 | 
						if (envl_match_error != NULL) *envl_match_error = env_err; | 
				
			||||||
 | 
						if (abs_match_error != NULL) *abs_match_error = abs_err; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return err_cnt == 0; | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,25 @@ | 
				
			|||||||
 | 
					#pragma once | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdint.h> | 
				
			||||||
 | 
					#include <stdbool.h> | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Match signal to reference, allowing for some offser and noise | 
				
			||||||
 | 
					 * | 
				
			||||||
 | 
					 * @param data matched data | 
				
			||||||
 | 
					 * @param ref reference data | 
				
			||||||
 | 
					 * @param length data length (data & ref length must be equal) | 
				
			||||||
 | 
					 * @param drift_x allowed horizontal drift (Hz drift if values are 1 Hz FFT bins) | 
				
			||||||
 | 
					 * @param offset_y allowed vertical offset (bin amplitude, positive or negative) | 
				
			||||||
 | 
					 * @param envl_match_error error metric calculated with allowed drift and offset | 
				
			||||||
 | 
					 * @param abs_match_error error metric calculated from raw data (can be used if envelope match passes) | 
				
			||||||
 | 
					 * @return envelope match status (match using drift and offset) | 
				
			||||||
 | 
					 */ | 
				
			||||||
 | 
					bool vec_match(const float *data, | 
				
			||||||
 | 
								   const float *ref, | 
				
			||||||
 | 
								   uint32_t length, | 
				
			||||||
 | 
								   uint8_t drift_x, | 
				
			||||||
 | 
								   float offset_y, | 
				
			||||||
 | 
								   float *envl_match_error, | 
				
			||||||
 | 
								   float *abs_match_error); | 
				
			||||||
					Loading…
					
					
				
		Reference in new issue