emg_util.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <math.h>
  4. #include <string.h>
  5. #include <locale.h>
  6. #include <float.h>
  7. #include "emg_util.h"
  8. #include <assert.h>
  9. // SOS coefficients (20-450 Hz bandpass, fs=1000Hz)
  10. // 将常量数组类型从 double 改为 float,并添加 'f' 后缀
  11. const float sos[2][6] = { // double -> float
  12. {1.0000000000f, 2.0000000000f, 1.0000000000f, 1.0f, -1.750100000f, 0.7797000000f}, // 所有数值添加 f 后缀
  13. {1.0000000000f, -2.0000000000f, 1.0000000000f, 1.0f, -1.9912000000f, 0.9912000000f} // 所有数值添加 f 后缀
  14. };
  15. // Notch filter parameters (50Hz notch, fs=1000Hz)
  16. // 将系数类型从 double 改为 float,并添加 'f' 后缀
  17. const float NOTCH_B[3] = {0.9955f, -1.8936f, 0.9955f}; // double -> float, 添加 f 后缀
  18. const float NOTCH_A[3] = {1.0000f, -1.8936f, 0.9911f}; // double -> float, 添加 f 后缀
  19. typedef struct {
  20. float b[3]; // Numerator coefficients: b0, b1, b2 (double -> float)
  21. float a[3]; // Denominator coefficients: a0, a1, a2 (a0 is typically normalized to 1) (double -> float)
  22. float x1, x2; // Input delay units: x[n-1], x[n-2] (double -> float)
  23. float y1, y2; // Output delay units: y[n-1], y[n-2] (double -> float)
  24. } NotchFilter;
  25. // 这些变量如果是在全局范围内使用,建议加上 extern 声明并在某个.c文件中定义。
  26. extern float f0; // Power line frequency (50Hz) (double -> float)
  27. extern float q; // Quality factor (double -> float)
  28. extern float low_cut; // Low cutoff frequency (double -> float)
  29. extern float high_cut; // High cutoff frequency (double -> float)
  30. extern int order; // Filter order (保持不变)
  31. // Butterworth filter structure (现在为单精度 float)
  32. typedef struct {
  33. float x1, x2; // double -> float
  34. float y1, y2; // double -> float
  35. } BiquadState;
  36. // Symlet 4 (sym4) wavelet filter coefficients (normalized)
  37. // 将系数类型从 double 改为 float,并添加 'f' 后缀
  38. static float sym4_h[8] = { // double -> float
  39. 0.0266700579010166f,
  40. 0.0607514995432352f,
  41. -0.0656160944756398f,
  42. -0.1498879107562273f,
  43. 0.4829629131445341f,
  44. 0.8365163037378608f,
  45. 0.2242848007341553f,
  46. -0.1077300958383618f
  47. };
  48. static float sym4_g[8] = { // double -> float
  49. -0.1077300958383618f,
  50. -0.2242848007341553f,
  51. 0.8365163037378608f,
  52. -0.4829629131445341f,
  53. -0.1498879107562273f,
  54. 0.0656160944756398f,
  55. 0.0607514995432352f,
  56. -0.0266700579010166f
  57. };
  58. /**
  59. * Reset notch filter states
  60. * Function: Clear the input and output history values of the filter to ensure new filtering process is not affected by previous states
  61. * Parameter: filter - pointer to the notch filter structure
  62. */
  63. void notchFilterReset(NotchFilter *filter) {
  64. // Reset input history (x1: previous input, x2: input from two steps ago)
  65. filter->x1 = filter->x2 = 0.0f; // 改为 0.0f
  66. // Reset output history (y1: previous output, y2: output from two steps ago)
  67. filter->y1 = filter->y2 = 0.0f; // 改为 0.0f
  68. }
  69. /**
  70. * Process a single sample with notch filter
  71. * Function: Filter a single input data sample based on IIR filter difference equation
  72. * Parameters:
  73. * filter - pointer to notch filter structure (contains coefficients and historical states)
  74. * input - current input sample data
  75. * Return: Filtered output data
  76. */
  77. float notchFilterProcess(NotchFilter *filter, float input) { // double -> float
  78. // Calculate current output according to IIR filter difference equation
  79. // Formula: y[n] = b0*x[n] + b1*x[n-1] + b2*x[n-2] - a1*y[n-1] - a2*y[n-2]
  80. float output = // double -> float
  81. filter->b[0] * input + // Current input term
  82. filter->b[1] * filter->x1 + // Previous input term
  83. filter->b[2] * filter->x2 - // Input term from two steps ago
  84. filter->a[1] * filter->y1 - // Previous output feedback term
  85. filter->a[2] * filter->y2; // Output feedback term from two steps ago
  86. // Update input history (shift operation,保存最新的两个输入值)
  87. filter->x2 = filter->x1; // Two steps ago = previous step
  88. filter->x1 = input; // Previous step = current input
  89. // Update output history (shift operation,保存最新的两个输出值)
  90. filter->y2 = filter->y1; // Two steps ago = previous step
  91. filter->y1 = output; // Previous step = current output
  92. return output;
  93. }
  94. /**
  95. * Process array data with notch filter
  96. * Function: Continuously filter an entire segment of input data by processing samples one by one
  97. * Parameters:
  98. * filter - pointer to notch filter structure
  99. * input - pointer to input data array
  100. * output - pointer to output data array (memory should be pre-allocated)
  101. * length - length of input/output data (number of samples)
  102. */
  103. void notchFilterProcessArray(NotchFilter *filter, const float *input, float *output, int length) { // double -> float
  104. // Iterate through input array and filter each sample
  105. int i;
  106. for (i = 0; i < length; i++) {
  107. output[i] = notchFilterProcess(filter, input[i]);
  108. }
  109. }
  110. // Apply a biquad section
  111. float biquad_filter(float input, const float *sos_row, BiquadState *state) { // double -> float
  112. float b0 = sos_row[0]; // double -> float
  113. float b1 = sos_row[1];
  114. float b2 = sos_row[2];
  115. float a1 = sos_row[4]; // Note index: the 5th element is a1
  116. float a2 = sos_row[5]; // The 6th element is a2
  117. float output; // double -> float
  118. output = b0 * input + b1 * state->x1 + b2 * state->x2
  119. - a1 * state->y1 - a2 * state->y2;
  120. state->x2 = state->x1;
  121. state->x1 = input;
  122. state->y2 = state->y1;
  123. state->y1 = output;
  124. return output;
  125. }
  126. // Reverse array
  127. void reverse_array(float *data, int n) { // double -> float
  128. int i;
  129. for (i = 0; i < n / 2; i++) {
  130. float temp = data[i]; // double -> float
  131. data[i] = data[n - 1 - i];
  132. data[n - 1 - i] = temp;
  133. }
  134. }
  135. /**
  136. * @brief Zero-phase bandpass filtering (similar to MATLAB filtfilt)
  137. * @param indata: Input signal array
  138. * @param outdata: Output signal array (filtered result)
  139. * @param len: Signal length
  140. * @param sos: 2x6 second-order sections coefficient matrix
  141. * @param num_stages: Number of sections (2 here)
  142. */
  143. void filtfilt_sos(const float *indata, float *outdata, int len, const float sos[2][6], int num_stages) { // double -> float
  144. // === All variable declarations moved to top ===
  145. float *temp; // double -> float
  146. BiquadState *states;
  147. int i, j;
  148. float sample; // double -> float
  149. temp = (float *)malloc(len * sizeof(float)); // double -> float
  150. states = (BiquadState *)calloc(num_stages, sizeof(BiquadState));
  151. // === Forward filtering ===
  152. for (i = 0; i < len; i++) {
  153. sample = indata[i];
  154. for (j = 0; j < num_stages; j++) {
  155. sample = biquad_filter(sample, sos[j], &states[j]);
  156. }
  157. temp[i] = sample;
  158. }
  159. memset(states, 0, num_stages * sizeof(BiquadState));
  160. reverse_array(temp, len);
  161. for (i = 0; i < len; i++) {
  162. sample = temp[i];
  163. for (j = 0; j < num_stages; j++) {
  164. sample = biquad_filter(sample, sos[j], &states[j]);
  165. }
  166. temp[i] = sample;
  167. }
  168. reverse_array(temp, len);
  169. memcpy(outdata, temp, len * sizeof(float)); // double -> float
  170. free(temp);
  171. free(states);
  172. }
  173. // Quick sort (ascending order)
  174. void quicksort(float *arr, int n) { // double -> float
  175. // All variables moved to top
  176. float pivot, temp; // double -> float
  177. int i, j;
  178. if (n <= 1) return;
  179. pivot = arr[n / 2];
  180. i = 0;
  181. j = n - 1;
  182. while (i <= j) {
  183. while (arr[i] < pivot) i++;
  184. while (arr[j] > pivot) j--;
  185. if (i <= j) {
  186. temp = arr[i]; arr[i] = arr[j]; arr[j] = temp;
  187. i++; j--;
  188. }
  189. }
  190. quicksort(arr, j + 1);
  191. if (i < n) quicksort(arr + i, n - i);
  192. }
  193. // Calculate median
  194. float median(float *arr, int n) { // double -> float
  195. // All variables moved to top
  196. float *copy; // double -> float
  197. float med; // double -> float
  198. int i;
  199. copy = (float*)malloc(n * sizeof(float)); // double -> float
  200. if (!copy) return 0.0f; // Memory allocation failed, 0.0 -> 0.0f
  201. for (i = 0; i < n; i++) {
  202. copy[i] = arr[i];
  203. }
  204. quicksort(copy, n);
  205. if (n % 2 == 1) {
  206. med = copy[n/2];
  207. } else {
  208. med = (copy[n/2-1] + copy[n/2]) * 0.5f; // 0.5 -> 0.5f
  209. }
  210. free(copy);
  211. return med;
  212. }
  213. /**
  214. * Symmetric index mapping function, used to simulate 'sym' mode boundary extension in MATLAB.
  215. *
  216. * @param i Currently calculated index position.
  217. * @param N Signal length.
  218. * @return Mapped index position.
  219. */
  220. int symmetric_index(int i, int N) {
  221. while (i < 0) { // If index is less than 0, flip to positive range
  222. i = -i - 1;
  223. }
  224. while (i >= N) { // If index exceeds signal length, mirror back into signal
  225. i = 2 * N - i - 1;
  226. }
  227. return i;
  228. }
  229. // Convolution + downsampling (symmetric boundaries)
  230. void dwt_conv_down(const float *input, int len_in, const float *filt, int len_filt, float *output) { // double -> float
  231. // All variables moved to top
  232. int out_len;
  233. int i, j, center, n, idx;
  234. float sum; // double -> float
  235. out_len = (len_in + 1) / 2; // Corrected length
  236. for (i = 0; i < out_len; i++) {
  237. center = 2 * i;
  238. sum = 0.0f; // 0.0 -> 0.0f
  239. for (j = 0; j < len_filt; j++) {
  240. n = center - (len_filt - 1)/2 + j;
  241. idx = symmetric_index(n, len_in);
  242. sum += input[idx] * filt[len_filt - 1 - j];
  243. }
  244. output[i] = sum;
  245. }
  246. }
  247. // Upsampling + convolution (reconstruction)
  248. void idwt_up_conv(const float *input, int len_in, const float *filt, int len_filt, float *output, int len_out) { // double -> float
  249. // All variables moved to top
  250. int i, j, pos_in, pos_out;
  251. int radius;
  252. memset(output, 0, len_out * sizeof(float)); // double -> float
  253. radius = (len_filt - 1) / 2; // 3 for sym4
  254. for (i = 0; i < len_in; i++) {
  255. pos_in = 2 * i;
  256. for (j = 0; j < len_filt; j++) {
  257. pos_out = pos_in + j - radius;
  258. if (pos_out >= 0 && pos_out < len_out) {
  259. output[pos_out] += input[i] * filt[len_filt - 1 - j];
  260. }
  261. }
  262. }
  263. }
  264. // Soft threshold function
  265. void soft_threshold(float *vec, int n, float thr) { // double -> float
  266. int i;
  267. for (i = 0; i < n; i++) {
  268. if (vec[i] > thr)
  269. vec[i] -= thr;
  270. else if (vec[i] < -thr)
  271. vec[i] += thr;
  272. else
  273. vec[i] = 0.0f; // 0.0 -> 0.0f
  274. }
  275. }
  276. void denoise_emg_wavelab(float *data, int N, float fs, float *denoised) { // double -> float
  277. // === All variable declarations moved to top ===
  278. const int levels = 6;
  279. float *work, *tmp_low, *tmp_high; // double -> float
  280. float *details[6]; // double -> float
  281. int det_len[6];
  282. int current_len;
  283. int lev, out_len;
  284. float *abs_d6; // double -> float
  285. float med_abs, sigma, thr; // double -> float
  286. int i;
  287. // Allocate memory
  288. work = (float*)malloc(N * sizeof(float)); // double -> float
  289. tmp_low = (float*)malloc(N * sizeof(float));
  290. tmp_high = (float*)malloc(N * sizeof(float));
  291. if (!work || !tmp_low || !tmp_high) {
  292. // Error handling
  293. if (work) free(work);
  294. if (tmp_low) free(tmp_low);
  295. if (tmp_high) free(tmp_high);
  296. return;
  297. }
  298. memcpy(work, data, N * sizeof(float)); // double -> float
  299. current_len = N;
  300. // === 1. Multi-level wavelet decomposition ===
  301. for (lev = 0; lev < levels; lev++) {
  302. out_len = (current_len + 1) / 2;
  303. dwt_conv_down(work, current_len, sym4_h, 8, tmp_low);
  304. dwt_conv_down(work, current_len, sym4_g, 8, tmp_high);
  305. details[lev] = (float*)malloc(out_len * sizeof(float)); // double -> float
  306. det_len[lev] = out_len;
  307. memcpy(details[lev], tmp_high, out_len * sizeof(float)); // double -> float
  308. memcpy(work, tmp_low, out_len * sizeof(float)); // double -> float
  309. current_len = out_len;
  310. }
  311. // === 2. Noise estimation ===
  312. abs_d6 = (float*)malloc(det_len[5] * sizeof(float)); // double -> float
  313. for (i = 0; i < det_len[5]; i++) {
  314. abs_d6[i] = fabsf(details[5][i]); // fabs -> fabsf for float
  315. }
  316. med_abs = median(abs_d6, det_len[5]);
  317. sigma = med_abs / 0.6745f/4.2; // 0.6745 -> 0.6745f
  318. // thr = sigma * sqrt(2.0 * log((double)N));
  319. thr = sigma * sqrtf(2.0f * logf((float)N)); // sqrt -> sqrtf, log -> logf, (double)N -> (float)N, 2.0 -> 2.0f
  320. printf("Threshold: %.15f sigma:%.15f\n", thr,sigma);
  321. free(abs_d6);
  322. // === 3. Soft thresholding ===
  323. for (lev = 0; lev < levels; lev++) {
  324. soft_threshold(details[lev], det_len[lev], thr);
  325. }
  326. // === 4. Reconstruction ===
  327. memcpy(tmp_low, work, current_len * sizeof(float)); // double -> float
  328. for (lev = levels - 1; lev >= 0; lev--) {
  329. out_len = (lev == 5) ? N : det_len[lev] * 2;
  330. idwt_up_conv(tmp_low, current_len, sym4_h, 8, work, out_len);
  331. idwt_up_conv(details[lev], det_len[lev], sym4_g, 8, tmp_high, out_len);
  332. for (i = 0; i < out_len; i++) {
  333. work[i] += tmp_high[i];
  334. }
  335. memcpy(tmp_low, work, out_len * sizeof(float)); // double -> float
  336. current_len = out_len;
  337. }
  338. memcpy(denoised, work, N * sizeof(float)); // double -> float
  339. free(work);
  340. free(tmp_low);
  341. free(tmp_high);
  342. for (i = 0; i < levels; i++) {
  343. if (details[i]) free(details[i]);
  344. }
  345. }
  346. void remove_dc_offset(const float* src, float* dest, int n) { // double -> float
  347. float sum, mean; // double -> float
  348. int i;
  349. if (n <= 0) return;
  350. sum = 0.0f; // 0.0 -> 0.0f
  351. for (i = 0; i < n; i++) {
  352. sum += src[i];
  353. }
  354. mean = sum / n;
  355. for (i = 0; i < n; i++) {
  356. dest[i] = src[i] - mean;
  357. }
  358. }
  359. // ================= Main processing function =================
  360. void emg_denoised(float *emg_raw, int length, int fs, float *emg_denoised) { // double -> float
  361. // === All variable declarations moved to top ===
  362. // float *temp1; // double -> float
  363. // float *temp2; // double -> float
  364. NotchFilter filter;
  365. int i;
  366. // Validate input validity
  367. if (!emg_raw || length <= 0 || !emg_denoised) {
  368. fprintf(stderr, "Error: Invalid input parameters\n");
  369. return;
  370. }
  371. // Initialize notch filter (using predefined coefficients)
  372. for (i = 0; i < 3; i++) {
  373. filter.b[i] = NOTCH_B[i]; // 确保 NOTCH_B 和 NOTCH_A 在 emg_util.h 中也是 float 类型
  374. filter.a[i] = NOTCH_A[i];
  375. }
  376. // Reset filter states
  377. notchFilterReset(&filter);
  378. // Remove DC offset
  379. remove_dc_offset(emg_raw, emg_denoised, length);
  380. // Notch filtering (50Hz or 60Hz)
  381. //notchFilterProcessArray(&filter, emg_denoised, emg_denoised, length);
  382. // Bidirectional second-order section filtering (zero-phase bandpass filtering)
  383. filtfilt_sos(emg_denoised, emg_denoised, length, sos, 2); // 确保 sos 系数数组在 emg_util.h 中也是 float 类型
  384. for(i=0;i<length;i++)
  385. {
  386. emg_denoised[i]=emg_denoised[i]/1000;
  387. }
  388. // Wavelet denoising (using sym4)
  389. //denoise_emg_wavelab(emg_denoised, length, fs, emg_denoised);
  390. }