| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456 |
- #include <stdio.h>
- #include <stdlib.h>
- #include <math.h>
- #include <string.h>
- #include <locale.h>
- #include <float.h>
- #include "emg_util.h"
- #include <assert.h>
- // SOS coefficients (20-450 Hz bandpass, fs=1000Hz)
- // 将常量数组类型从 double 改为 float,并添加 'f' 后缀
- const float sos[2][6] = { // double -> float
- {1.0000000000f, 2.0000000000f, 1.0000000000f, 1.0f, -1.750100000f, 0.7797000000f}, // 所有数值添加 f 后缀
- {1.0000000000f, -2.0000000000f, 1.0000000000f, 1.0f, -1.9912000000f, 0.9912000000f} // 所有数值添加 f 后缀
- };
- // Notch filter parameters (50Hz notch, fs=1000Hz)
- // 将系数类型从 double 改为 float,并添加 'f' 后缀
- const float NOTCH_B[3] = {0.9955f, -1.8936f, 0.9955f}; // double -> float, 添加 f 后缀
- const float NOTCH_A[3] = {1.0000f, -1.8936f, 0.9911f}; // double -> float, 添加 f 后缀
- typedef struct {
- float b[3]; // Numerator coefficients: b0, b1, b2 (double -> float)
- float a[3]; // Denominator coefficients: a0, a1, a2 (a0 is typically normalized to 1) (double -> float)
- float x1, x2; // Input delay units: x[n-1], x[n-2] (double -> float)
- float y1, y2; // Output delay units: y[n-1], y[n-2] (double -> float)
- } NotchFilter;
- // 这些变量如果是在全局范围内使用,建议加上 extern 声明并在某个.c文件中定义。
- extern float f0; // Power line frequency (50Hz) (double -> float)
- extern float q; // Quality factor (double -> float)
- extern float low_cut; // Low cutoff frequency (double -> float)
- extern float high_cut; // High cutoff frequency (double -> float)
- extern int order; // Filter order (保持不变)
- // Butterworth filter structure (现在为单精度 float)
- typedef struct {
- float x1, x2; // double -> float
- float y1, y2; // double -> float
- } BiquadState;
- // Symlet 4 (sym4) wavelet filter coefficients (normalized)
- // 将系数类型从 double 改为 float,并添加 'f' 后缀
- static float sym4_h[8] = { // double -> float
- 0.0266700579010166f,
- 0.0607514995432352f,
- -0.0656160944756398f,
- -0.1498879107562273f,
- 0.4829629131445341f,
- 0.8365163037378608f,
- 0.2242848007341553f,
- -0.1077300958383618f
- };
- static float sym4_g[8] = { // double -> float
- -0.1077300958383618f,
- -0.2242848007341553f,
- 0.8365163037378608f,
- -0.4829629131445341f,
- -0.1498879107562273f,
- 0.0656160944756398f,
- 0.0607514995432352f,
- -0.0266700579010166f
- };
- /**
- * Reset notch filter states
- * Function: Clear the input and output history values of the filter to ensure new filtering process is not affected by previous states
- * Parameter: filter - pointer to the notch filter structure
- */
- void notchFilterReset(NotchFilter *filter) {
- // Reset input history (x1: previous input, x2: input from two steps ago)
- filter->x1 = filter->x2 = 0.0f; // 改为 0.0f
- // Reset output history (y1: previous output, y2: output from two steps ago)
- filter->y1 = filter->y2 = 0.0f; // 改为 0.0f
- }
- /**
- * Process a single sample with notch filter
- * Function: Filter a single input data sample based on IIR filter difference equation
- * Parameters:
- * filter - pointer to notch filter structure (contains coefficients and historical states)
- * input - current input sample data
- * Return: Filtered output data
- */
- float notchFilterProcess(NotchFilter *filter, float input) { // double -> float
- // Calculate current output according to IIR filter difference equation
- // Formula: y[n] = b0*x[n] + b1*x[n-1] + b2*x[n-2] - a1*y[n-1] - a2*y[n-2]
- float output = // double -> float
- filter->b[0] * input + // Current input term
- filter->b[1] * filter->x1 + // Previous input term
- filter->b[2] * filter->x2 - // Input term from two steps ago
- filter->a[1] * filter->y1 - // Previous output feedback term
- filter->a[2] * filter->y2; // Output feedback term from two steps ago
- // Update input history (shift operation,保存最新的两个输入值)
- filter->x2 = filter->x1; // Two steps ago = previous step
- filter->x1 = input; // Previous step = current input
- // Update output history (shift operation,保存最新的两个输出值)
- filter->y2 = filter->y1; // Two steps ago = previous step
- filter->y1 = output; // Previous step = current output
- return output;
- }
- /**
- * Process array data with notch filter
- * Function: Continuously filter an entire segment of input data by processing samples one by one
- * Parameters:
- * filter - pointer to notch filter structure
- * input - pointer to input data array
- * output - pointer to output data array (memory should be pre-allocated)
- * length - length of input/output data (number of samples)
- */
- void notchFilterProcessArray(NotchFilter *filter, const float *input, float *output, int length) { // double -> float
- // Iterate through input array and filter each sample
- int i;
- for (i = 0; i < length; i++) {
- output[i] = notchFilterProcess(filter, input[i]);
- }
- }
- // Apply a biquad section
- float biquad_filter(float input, const float *sos_row, BiquadState *state) { // double -> float
- float b0 = sos_row[0]; // double -> float
- float b1 = sos_row[1];
- float b2 = sos_row[2];
- float a1 = sos_row[4]; // Note index: the 5th element is a1
- float a2 = sos_row[5]; // The 6th element is a2
- float output; // double -> float
- output = b0 * input + b1 * state->x1 + b2 * state->x2
- - a1 * state->y1 - a2 * state->y2;
- state->x2 = state->x1;
- state->x1 = input;
- state->y2 = state->y1;
- state->y1 = output;
- return output;
- }
- // Reverse array
- void reverse_array(float *data, int n) { // double -> float
- int i;
- for (i = 0; i < n / 2; i++) {
- float temp = data[i]; // double -> float
- data[i] = data[n - 1 - i];
- data[n - 1 - i] = temp;
- }
- }
- /**
- * @brief Zero-phase bandpass filtering (similar to MATLAB filtfilt)
- * @param indata: Input signal array
- * @param outdata: Output signal array (filtered result)
- * @param len: Signal length
- * @param sos: 2x6 second-order sections coefficient matrix
- * @param num_stages: Number of sections (2 here)
- */
- void filtfilt_sos(const float *indata, float *outdata, int len, const float sos[2][6], int num_stages) { // double -> float
- // === All variable declarations moved to top ===
- float *temp; // double -> float
- BiquadState *states;
- int i, j;
- float sample; // double -> float
- temp = (float *)malloc(len * sizeof(float)); // double -> float
- states = (BiquadState *)calloc(num_stages, sizeof(BiquadState));
- // === Forward filtering ===
- for (i = 0; i < len; i++) {
- sample = indata[i];
- for (j = 0; j < num_stages; j++) {
- sample = biquad_filter(sample, sos[j], &states[j]);
- }
- temp[i] = sample;
- }
- memset(states, 0, num_stages * sizeof(BiquadState));
- reverse_array(temp, len);
- for (i = 0; i < len; i++) {
- sample = temp[i];
- for (j = 0; j < num_stages; j++) {
- sample = biquad_filter(sample, sos[j], &states[j]);
- }
- temp[i] = sample;
- }
- reverse_array(temp, len);
- memcpy(outdata, temp, len * sizeof(float)); // double -> float
- free(temp);
- free(states);
- }
- // Quick sort (ascending order)
- void quicksort(float *arr, int n) { // double -> float
- // All variables moved to top
- float pivot, temp; // double -> float
- int i, j;
- if (n <= 1) return;
- pivot = arr[n / 2];
- i = 0;
- j = n - 1;
- while (i <= j) {
- while (arr[i] < pivot) i++;
- while (arr[j] > pivot) j--;
- if (i <= j) {
- temp = arr[i]; arr[i] = arr[j]; arr[j] = temp;
- i++; j--;
- }
- }
- quicksort(arr, j + 1);
- if (i < n) quicksort(arr + i, n - i);
- }
- // Calculate median
- float median(float *arr, int n) { // double -> float
- // All variables moved to top
- float *copy; // double -> float
- float med; // double -> float
- int i;
- copy = (float*)malloc(n * sizeof(float)); // double -> float
- if (!copy) return 0.0f; // Memory allocation failed, 0.0 -> 0.0f
- for (i = 0; i < n; i++) {
- copy[i] = arr[i];
- }
- quicksort(copy, n);
- if (n % 2 == 1) {
- med = copy[n/2];
- } else {
- med = (copy[n/2-1] + copy[n/2]) * 0.5f; // 0.5 -> 0.5f
- }
- free(copy);
- return med;
- }
- /**
- * Symmetric index mapping function, used to simulate 'sym' mode boundary extension in MATLAB.
- *
- * @param i Currently calculated index position.
- * @param N Signal length.
- * @return Mapped index position.
- */
- int symmetric_index(int i, int N) {
- while (i < 0) { // If index is less than 0, flip to positive range
- i = -i - 1;
- }
- while (i >= N) { // If index exceeds signal length, mirror back into signal
- i = 2 * N - i - 1;
- }
- return i;
- }
- // Convolution + downsampling (symmetric boundaries)
- void dwt_conv_down(const float *input, int len_in, const float *filt, int len_filt, float *output) { // double -> float
- // All variables moved to top
- int out_len;
- int i, j, center, n, idx;
- float sum; // double -> float
- out_len = (len_in + 1) / 2; // Corrected length
- for (i = 0; i < out_len; i++) {
- center = 2 * i;
- sum = 0.0f; // 0.0 -> 0.0f
- for (j = 0; j < len_filt; j++) {
- n = center - (len_filt - 1)/2 + j;
- idx = symmetric_index(n, len_in);
- sum += input[idx] * filt[len_filt - 1 - j];
- }
- output[i] = sum;
- }
- }
- // Upsampling + convolution (reconstruction)
- void idwt_up_conv(const float *input, int len_in, const float *filt, int len_filt, float *output, int len_out) { // double -> float
- // All variables moved to top
- int i, j, pos_in, pos_out;
- int radius;
- memset(output, 0, len_out * sizeof(float)); // double -> float
- radius = (len_filt - 1) / 2; // 3 for sym4
- for (i = 0; i < len_in; i++) {
- pos_in = 2 * i;
- for (j = 0; j < len_filt; j++) {
- pos_out = pos_in + j - radius;
- if (pos_out >= 0 && pos_out < len_out) {
- output[pos_out] += input[i] * filt[len_filt - 1 - j];
- }
- }
- }
- }
- // Soft threshold function
- void soft_threshold(float *vec, int n, float thr) { // double -> float
- int i;
- for (i = 0; i < n; i++) {
- if (vec[i] > thr)
- vec[i] -= thr;
- else if (vec[i] < -thr)
- vec[i] += thr;
- else
- vec[i] = 0.0f; // 0.0 -> 0.0f
- }
- }
- void denoise_emg_wavelab(float *data, int N, float fs, float *denoised) { // double -> float
- // === All variable declarations moved to top ===
- const int levels = 6;
- float *work, *tmp_low, *tmp_high; // double -> float
- float *details[6]; // double -> float
- int det_len[6];
- int current_len;
- int lev, out_len;
- float *abs_d6; // double -> float
- float med_abs, sigma, thr; // double -> float
- int i;
- // Allocate memory
- work = (float*)malloc(N * sizeof(float)); // double -> float
- tmp_low = (float*)malloc(N * sizeof(float));
- tmp_high = (float*)malloc(N * sizeof(float));
- if (!work || !tmp_low || !tmp_high) {
- // Error handling
- if (work) free(work);
- if (tmp_low) free(tmp_low);
- if (tmp_high) free(tmp_high);
- return;
- }
- memcpy(work, data, N * sizeof(float)); // double -> float
- current_len = N;
- // === 1. Multi-level wavelet decomposition ===
- for (lev = 0; lev < levels; lev++) {
- out_len = (current_len + 1) / 2;
- dwt_conv_down(work, current_len, sym4_h, 8, tmp_low);
- dwt_conv_down(work, current_len, sym4_g, 8, tmp_high);
- details[lev] = (float*)malloc(out_len * sizeof(float)); // double -> float
- det_len[lev] = out_len;
- memcpy(details[lev], tmp_high, out_len * sizeof(float)); // double -> float
- memcpy(work, tmp_low, out_len * sizeof(float)); // double -> float
- current_len = out_len;
- }
- // === 2. Noise estimation ===
- abs_d6 = (float*)malloc(det_len[5] * sizeof(float)); // double -> float
- for (i = 0; i < det_len[5]; i++) {
- abs_d6[i] = fabsf(details[5][i]); // fabs -> fabsf for float
- }
- med_abs = median(abs_d6, det_len[5]);
- sigma = med_abs / 0.6745f/4.2; // 0.6745 -> 0.6745f
- // thr = sigma * sqrt(2.0 * log((double)N));
- thr = sigma * sqrtf(2.0f * logf((float)N)); // sqrt -> sqrtf, log -> logf, (double)N -> (float)N, 2.0 -> 2.0f
- printf("Threshold: %.15f sigma:%.15f\n", thr,sigma);
- free(abs_d6);
- // === 3. Soft thresholding ===
- for (lev = 0; lev < levels; lev++) {
- soft_threshold(details[lev], det_len[lev], thr);
- }
- // === 4. Reconstruction ===
- memcpy(tmp_low, work, current_len * sizeof(float)); // double -> float
- for (lev = levels - 1; lev >= 0; lev--) {
- out_len = (lev == 5) ? N : det_len[lev] * 2;
- idwt_up_conv(tmp_low, current_len, sym4_h, 8, work, out_len);
- idwt_up_conv(details[lev], det_len[lev], sym4_g, 8, tmp_high, out_len);
- for (i = 0; i < out_len; i++) {
- work[i] += tmp_high[i];
- }
- memcpy(tmp_low, work, out_len * sizeof(float)); // double -> float
- current_len = out_len;
- }
- memcpy(denoised, work, N * sizeof(float)); // double -> float
- free(work);
- free(tmp_low);
- free(tmp_high);
- for (i = 0; i < levels; i++) {
- if (details[i]) free(details[i]);
- }
- }
- void remove_dc_offset(const float* src, float* dest, int n) { // double -> float
- float sum, mean; // double -> float
- int i;
- if (n <= 0) return;
- sum = 0.0f; // 0.0 -> 0.0f
- for (i = 0; i < n; i++) {
- sum += src[i];
- }
- mean = sum / n;
- for (i = 0; i < n; i++) {
- dest[i] = src[i] - mean;
- }
- }
- // ================= Main processing function =================
- void emg_denoised(float *emg_raw, int length, int fs, float *emg_denoised) { // double -> float
- // === All variable declarations moved to top ===
- // float *temp1; // double -> float
- // float *temp2; // double -> float
- NotchFilter filter;
- int i;
- // Validate input validity
- if (!emg_raw || length <= 0 || !emg_denoised) {
- fprintf(stderr, "Error: Invalid input parameters\n");
- return;
- }
- // Initialize notch filter (using predefined coefficients)
- for (i = 0; i < 3; i++) {
- filter.b[i] = NOTCH_B[i]; // 确保 NOTCH_B 和 NOTCH_A 在 emg_util.h 中也是 float 类型
- filter.a[i] = NOTCH_A[i];
- }
- // Reset filter states
- notchFilterReset(&filter);
- // Remove DC offset
- remove_dc_offset(emg_raw, emg_denoised, length);
- // Notch filtering (50Hz or 60Hz)
- //notchFilterProcessArray(&filter, emg_denoised, emg_denoised, length);
- // Bidirectional second-order section filtering (zero-phase bandpass filtering)
- filtfilt_sos(emg_denoised, emg_denoised, length, sos, 2); // 确保 sos 系数数组在 emg_util.h 中也是 float 类型
- for(i=0;i<length;i++)
- {
- emg_denoised[i]=emg_denoised[i]/1000;
- }
- // Wavelet denoising (using sym4)
- //denoise_emg_wavelab(emg_denoised, length, fs, emg_denoised);
- }
|