[編者按]傳感器和信號處理僅一線之隔,信號的前后端合理搭配,是我們更準確地感知這個世界的一種基本態度和方式。
FIR頻域重疊相加法
還記得我們(此處有重復之嫌)之前的發文《FIR連續采樣分段卷積時域重疊相加法》?不過那是在時域處理的模擬和仿真。這次我們的內容是用C++在頻域實現的濾波卷積法,仍然是重疊相加法,屆時大家可以比較一下兩種方式的差異。
基本是通過兩個處理過程。
(1)頻域的重疊相加法示意圖再拿來用一下。如下圖所示[1]。
(2)再借用一下的時域卷積經傅里葉FFT變換后,在頻域成為對應的相乘;然后再通過IFFT將中間結果轉換回時域時序結果。
讓我們直接跳進話題,先看模擬測試結果,后看C++源碼。
模擬情節設定
50Hz選頻濾波,信號中混有110Hz和210H在的干擾信號和幅值為1的直流DC。
模擬信號及其頻譜的輸出請查看我們前面的文章。這里的代碼只提供將模擬信號進行了頻域重疊相加處理,生成的濾波前后模擬信號和被濾波處理后的數據波形的比較(見下圖)。
還記得我們(此處重復)之前用C++來模擬時域處理的濾波模擬程序嗎?
你又猜對了,又是那個濾波器,又被用上了!但,是不同的實現處理方式。
濾波處理之前的波形和頻譜圖
濾波之后,直流和其他頻率的信號已經不見,只留下50Hz的正弦波(見下圖)。
頻域重疊相加濾波前后的波形比較
圖由csv文件處理后生成。又見此圖,是不是有熟悉的感覺?
頻域連續濾波模擬和驗證C++源碼
/* Project Name: Demonstration & Validation for signal filtering via the "Overlap-Add" method implemented through FFT/IFFT based on Fast Fourier Transform (FFT) based on the Cooley-Tukey algorithm. Copyright(c)2024AmphenolSensors Author: L.L. This software is provided 'as-is', without any express or implied warrantyandforsimulation/demostrationpurpose.Innoeventwilltheauthorsbeheldliableforanydamages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. */ #include#include #include #include #include #include #include #include const double PI = 3.1415926535897932384; const double SAMPLE_RATE = 1000.0; // 1000Hz const int N = 1024; // 假設采樣分段數為1024 using namespace std; // 聲明使用std命名空間 #define SEL_FFT 0 #define SEL_IFFT 1 #define SEL_FFTIFFT 2 #define SIM_CYCLE_CNT 3.0 // 模擬信號函數 vector > generateSignal(double sampleRate, double seconds) { size_t signal_len = (size_t)(sampleRate * seconds); vector > signal(signal_len); // 定義模擬信號的數組長度 for (size_t i = 0; i < signal_len; ++i) { // 包含50Hz和110Hz,210Hz信號,DC signal[i].real(1+ sin((2 * PI * (double)i * 50) / sampleRate) + sin((2 * PI * (double)i * 210) / sampleRate) + sin((2 * PI * (double)i * 110) / sampleRate)); signal[i].imag(0); } return signal; } // 基于Cooley-Tukey算法的FFT void fft2(vector > &x) { size_t n = x.size(); if (n <= 1) return; // 把序列分為奇偶兩部分 vector > even(n / 2), odd(n / 2); for (size_t i = 0; 2 * i < n; i++) { even[i] = x[i * 2]; odd[i] = x[i * 2 + 1]; } // 遞歸解決每一個序列 fft2(even); fft2(odd); // 結合步驟 for (size_t i = 0; 2 * i < n; i++) { complex t = polar(1.0, -2 * PI * (double)i / (double)n) * odd[i]; x[i] = even[i] + t; x[i + n / 2] = even[i] - t; } } // 基于Cooley-Tukey算法的IFFT void ifft2(vector > &x) { size_t n = x.size(); if (n <= 1) return; vector > even(n / 2), odd(n / 2); for (size_t i = 0; 2 * i < n; i++) { even[i] = x[i * 2]; odd[i] = x[i * 2 + 1]; } ifft2(even); ifft2(odd); for (size_t i = 0; 2 * i < n; i++) { complex t = polar(1.0, 2 * PI * (double)i /(double) n) * odd[i]; x[i] = even[i] + t; x[i + n / 2] = even[i] - t; } // 最后除以n if (n > 1) { for (size_t i = 0; i < n; i++) { x[i] /= 2; } } } // 將數據寫入文件 void writeToFile(const vector > &Original_signal, const vector > &Proceeded_Signal, const string &filename, int Type_sel) { ofstream file(filename); if (Type_sel==SEL_FFTIFFT) { file << "time(s)" << ", " << "Original Signal"<< ", " << "Filtered Signal" << " "; for (size_t i = 0; i < Original_signal.size(); i++) { file << (double)i / SAMPLE_RATE << ", " < b{0.0010175493084400998, 0.0010954624020866333, 0.001080635650435545, 0.0009293052645812359, 0.0005868808563577278, -8.138309855847798e-19, -0.0008644147524968251, -0.0019966389877814107, -0.003323586744207458, -0.004696461345361978, -0.005892320462621699, -0.006633249964255378, -0.006623614506478284, -0.005601944833604465, -0.0034001773970723163, -7.334366341273803e-18, 0.004425290874832446, 0.00949426225087417, 0.014634010415364655, 0.019132982942933127, 0.022226796444847933, 0.023207550009729024, 0.021541722692400025, 0.01697833945185371, 0.009628503914736117, -6.755395515820625e-18, -0.01102370844120733, -0.02226281209657117, -0.032372473621654914, -0.04001099412924139, -0.04402269970024527, -0.043609484958132556, -0.03846490807520255, -0.028848803480728435, -0.015588116829396594, -9.10410551538968e-18, 0.016255406162706088, 0.031374390998733945, 0.04363491329762711, 0.051616779739690075, 0.05438594145724075, 0.051616779739690075, 0.04363491329762711, 0.031374390998733945, 0.016255406162706088, -9.10410551538968e-18, -0.015588116829396594, -0.028848803480728435, -0.03846490807520255, -0.043609484958132556, -0.04402269970024527, -0.0400109941292414, -0.032372473621654914, -0.022262812096571168, -0.01102370844120733, -6.755395515820627e-18, 0.009628503914736117, 0.016978339451853702, 0.021541722692400025, 0.023207550009729034, 0.022226796444847933, 0.01913298294293312, 0.014634010415364655, 0.009494262250874175, 0.004425290874832446, -7.3343663412738e-18, -0.0034001773970723163, -0.005601944833604469, -0.006623614506478284, -0.006633249964255374, -0.005892320462621699, -0.00469646134536198, -0.003323586744207458, -0.001996638987781409, -0.0008644147524968251, -8.138309855847805e-19, 0.0005868808563577278, 0.0009293052645812359, 0.001080635650435545, 0.0010954624020866333, 0.0010175493084400998}; // (1) Resize filter coefficients vector > H(b.size()); for(size_t i=0; i< b.size(); i++) { H[i] = complex (b[i],0); } H.resize(N, complex (0.0, 0.0)); // (2)Generate simulation data sequences size_t DataSeg_len_L = N - b.size() + 1; // Data segmeng Length = L double daq_duration = (double)DataSeg_len_L * SIM_CYCLE_CNT / SAMPLE_RATE; vector >Original_signal = generateSignal(SAMPLE_RATE, daq_duration); // Generate data sequence, L * 3 // (3-1) Define a 2-D vector,3 columns, to simulate DAQ and filtering process for 3 rounds vector >> seg_Dates(3); // (3-2) Initialize data segment for (size_t i = 0; i < seg_Dates.size(); i++) { seg_Dates[i].resize(DataSeg_len_L,complex (0.0, 0.0)); // devide Original_signal into 3 parts to simulate consequent DAQ for 3 cycles for(size_t j=0; j (0.0, 0.0)); // resize each data segment to N = L + M - 1 } // (4) Start to FFT/IFFT and generate involution result vector > Filtered_signal(DataSeg_len_L * (size_t)(SIM_CYCLE_CNT) + b.size() -1 ); //L * 3 + (M - 1) fft2(H); //(4-1)Simulate3cyclesofinvolution:L*3 for(size_t i=0; i< seg_Dates.size(); i++) { fft2(seg_Dates[i]); for(size_t j = 0; j< seg_Dates[i].size(); j++) { seg_Dates[i][j] = seg_Dates[i][j] * H[j]; } ifft2(seg_Dates[i]); for(size_t k = 0; k< seg_Dates[i].size(); k++) { Filtered_signal[i*DataSeg_len_L + k] += seg_Dates[i][k]; } } ????//?(5)?Save?Origianl?signal & result?(data?after?filtering)?into?csv?file writeToFile(Original_signal, Filtered_signal,"output_Filtered2.csv", SEL_FFTIFFT); return 0; }
時間倉促,有些功能和細節并沒有考慮太多,這里功能驗證是第一。
FFT/IFFT是基于庫里-圖基的算法,各位可以選用其他的來優化替代;
有些參數可以單獨變,有些參數卻是關聯的。
-
傳感器
+關注
關注
2548文章
50677瀏覽量
751993 -
FIR
+關注
關注
4文章
146瀏覽量
33112 -
頻域
+關注
關注
1文章
87瀏覽量
26274
原文標題:數字濾波器(6)—FIR頻域連續濾波“重疊相加法”C++源碼
文章出處:【微信號:安費諾傳感器學堂,微信公眾號:安費諾傳感器學堂】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論