পাঠ ৩১ · ৫৭-এর মধ্যে · মডিউল ৭
Home / Courses / Digital Signal Processing / FIR ফিল্টার ডিজাইন

FIR ফিল্টার — লিনিয়ার ফেজ ও উইন্ডোয়িং দিয়ে ডিজাইন

FIR filters — linear phase & design by windowing
১৪ মিনিট পড়া উচ্চ · Advanced Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • কেন একটি আদর্শ লো-পাস ফিল্টারের ইমপালস রেসপন্স সিংক ফাংশন, এবং তা হাতে গণনা করা
  • উইন্ডোয়িং পদ্ধতি ব্যবহার করে একটি বাস্তবায়নযোগ্য, সসীম-দৈর্ঘ্যের FIR ফিল্টার ডিজাইন করা
  • কেন সিমেট্রিক কোয়েফিসিয়েন্ট ঠিক লিনিয়ার ফেজ নিশ্চিত করে
  • ডিজাইন করা ফিল্টারকে কনভোলিউশনের মাধ্যমে একটি সত্যিকারের সিগন্যালে প্রয়োগ করা
  • DFT ম্যাগনিটিউড দিয়ে ফিল্টারের ফলাফল যাচাই করা — একটি সম্পূর্ণ ডিজাইন-থেকে-ভেরিফিকেশন পাইপলাইন

১ · আদর্শ লো-পাস ফিল্টার ও সিংক ফাংশন

একটি আদর্শ (ideal) লো-পাস ফিল্টারের ফ্রিকোয়েন্সি রেসপন্স একটি "ব্রিক-ওয়াল" আকৃতির — কাটঅফ ফ্রিকোয়েন্সি fc-এর নিচে গেইন ঠিক ১, উপরে ঠিক ০। এই আদর্শ রেসপন্সের ইনভার্স DTFT (M5/L20-এর ধারণা) নিলে যে ইমপালস রেসপন্স পাওয়া যায় তা একটি সুপরিচিত, স্ট্যান্ডার্ড ফলাফল — একটি সিংক (sinc) ফাংশন:

$$h_{ideal}[n] = \frac{\sin(2\pi f_c (n-M_0))}{\pi (n-M_0)}, \qquad h_{ideal}[M_0] = 2f_c$$

এখানে fc normalized কাটঅফ ফ্রিকোয়েন্সি (cycles/sample, অর্থাৎ fc_Hz / fs), আর M0 ফিল্টারের কেন্দ্র (center tap)। n = M0-এ সরাসরি সূত্রটি 0/0 হয়ে যায় বলে সেখানে লিমিট মান 2·fc ব্যবহার করতে হয় — এটি একটি স্ট্যান্ডার্ড, সুপরিচিত গাণিতিক ফলাফল (L'Hôpital-এর নিয়ম দিয়ে প্রমাণযোগ্য)। সমস্যা হলো — এই সিংক ফাংশন উভয় দিকে অসীম পর্যন্ত বিস্তৃত, যা বাস্তবায়ন করা অসম্ভব।

২ · উইন্ডোয়িং পদ্ধতি — একটি বাস্তবায়নযোগ্য ফিল্টার

সমাধান হলো — সিংক ফাংশনকে একটি সসীম দৈর্ঘ্যে কেটে নেওয়া এবং L24-এ শেখা একটি উইন্ডো ফাংশন (এখানে হ্যামিং উইন্ডো) দিয়ে গুণ করা, যা কাটার ফলে হওয়া তীক্ষ্ণ ধার (abrupt edge) মসৃণ করে এবং L24-এর স্পেকট্রাল লিকেজ কমায়:

$$w[n] = 0.54 - 0.46\cos\!\left(\frac{2\pi n}{M-1}\right), \qquad h[n] = h_{ideal}[n]\cdot w[n]$$

চূড়ান্ত ফিল্টার h[n] — n=0 থেকে n=M-1 পর্যন্ত সসীম দৈর্ঘ্যের — এখন একটি বাস্তবায়নযোগ্য FIR ফিল্টার। যেহেতু h_ideal[n] এবং w[n] উভয়ই কেন্দ্র M0=(M-1)/2-এর চারপাশে সিমেট্রিক (h[n] = h[M-1-n]), তাই চূড়ান্ত h[n]-ও সিমেট্রিক — এটাই ঠিক লিনিয়ার ফেজের শর্ত (Type I লিনিয়ার-ফেজ FIR, যখন M বিজোড়)। L32-এ ভিন্ন একটি ডিজাইন পদ্ধতি (ফ্রিকোয়েন্সি-স্যাম্পলিং) দেখা যাবে যেখানে একই সিমেট্রি-শর্ত সরাসরি কাজে লাগে।

সহোদর ধারণার সাথে সম্পর্ক

M3/L10-এর কনভোলিউশন ফাংশনই এখানে ফিল্টার প্রয়োগ করবে (L30-এ দেখা হয়েছে FIR প্রয়োগ = কনভোলিউশন), L24-এর হ্যামিং উইন্ডো সূত্র সিংক কাটার জন্য ব্যবহৃত হচ্ছে, আর M5/M6-এর DFT ফলাফল যাচাইয়ের জন্য ব্যবহৃত হবে — এই একটি পাইপলাইনে কোর্সের তিনটি বড় মডিউল একসাথে কাজ করছে।

৩ · সম্পূর্ণ পাইপলাইন — ডিজাইন করুন, প্রয়োগ করুন, যাচাই করুন

নিচে ধাপে ধাপে সম্পূর্ণ পাইপলাইন চালানো হয়েছে: (ক) fs=200 Hz স্যাম্পল রেটে একটি টেস্ট সিগন্যাল তৈরি — একটি ১০ Hz (নিম্ন) ও একটি ৬০ Hz (উচ্চ) কম্পোনেন্টের যোগফল; (খ) fc=30 Hz কাটঅফ, M=31 ট্যাপের একটি উইন্ডোড-সিংক লো-পাস ফিল্টার ডিজাইন; (গ) কনভোলিউশনের মাধ্যমে ফিল্টার প্রয়োগ (ফিল্টারের গ্রুপ ডিলে — (M-1)/2 = 15 নমুনা — এর জন্য আউটপুট ট্রিম করে ইনপুটের সাথে টাইম-অ্যালাইন করা); (ঘ) একটি ছোট ইনলাইন DFT-ম্যাগনিটিউড ফাংশন দিয়ে ফিল্টারিং-এর আগে ও পরে উভয় ফ্রিকোয়েন্সির ম্যাগনিটিউড পরিমাপ।

Python
import math

fs = 200.0     # স্যাম্পল রেট (Hz)
f_lo = 10.0    # নিম্ন-ফ্রিকোয়েন্সি কম্পোনেন্ট (Hz)
f_hi = 60.0    # উচ্চ-ফ্রিকোয়েন্সি কম্পোনেন্ট (Hz)
N = 60         # টেস্ট সিগন্যালের দৈর্ঘ্য

x = [math.sin(2*math.pi*f_lo*n/fs) + math.sin(2*math.pi*f_hi*n/fs) for n in range(N)]

# ---- (খ) উইন্ডোড-সিংক লো-পাস ফিল্টার ডিজাইন ----
fc = 30.0          # কাটঅফ ফ্রিকোয়েন্সি (Hz)
fc_norm = fc / fs  # normalized কাটঅফ
M = 31             # ট্যাপ সংখ্যা (বিজোড়)
center = (M - 1) // 2

def sinc_lp(d, fc_norm):
    if d == 0:
        return 2 * fc_norm
    return math.sin(2*math.pi*fc_norm*d) / (math.pi*d)

h_ideal = [sinc_lp(n - center, fc_norm) for n in range(M)]

def hamming(M):
    return [0.54 - 0.46*math.cos(2*math.pi*n/(M-1)) for n in range(M)]

w = hamming(M)
h = [h_ideal[n]*w[n] for n in range(M)]

print(f"{'n':>3} | {'h[n]':>10}")
for n in range(M):
    print(f"{n:>3} | {h[n]:>10.6f}")
print(f"\nsum(h) (DC gain) = {sum(h):.6f}")

# ---- (গ) কনভোলিউশনের মাধ্যমে ফিল্টার প্রয়োগ ----
def convolve(a, b):
    ny = len(a) + len(b) - 1
    y = [0.0]*ny
    for n in range(ny):
        s = 0.0
        for k in range(len(b)):
            if 0 <= n-k < len(a):
                s += b[k]*a[n-k]
        y[n] = s
    return y

y_full = convolve(x, h)
y = y_full[center:center+N]   # গ্রুপ ডিলে (center নমুনা) বাদ দিয়ে ইনপুটের সাথে টাইম-অ্যালাইন

# ---- (ঘ) DFT-ম্যাগনিটিউড দিয়ে যাচাই ----
def dft_mag_at(sig, f, fs):
    Ns = len(sig)
    total = sum(sig[n]*complex(math.cos(-2*math.pi*f*n/fs), math.sin(-2*math.pi*f*n/fs)) for n in range(Ns))
    return abs(total) / Ns

mag_in_lo, mag_in_hi = dft_mag_at(x, f_lo, fs), dft_mag_at(x, f_hi, fs)
mag_out_lo, mag_out_hi = dft_mag_at(y, f_lo, fs), dft_mag_at(y, f_hi, fs)

print()
print(f"{'component':>14} | {'|X(f)| আগে':>12} | {'|X(f)| পরে':>12} | {'attenuation':>12}")
for label, mi, mo in [("10 Hz (low)", mag_in_lo, mag_out_lo), ("60 Hz (high)", mag_in_hi, mag_out_hi)]:
    atten_db = 20*math.log10(mo/mi)
    print(f"{label:>14} | {mi:>12.6f} | {mo:>12.6f} | {atten_db:>10.2f} dB")

    
প্রকৃত আউটপুট নিশ্চিত করে ফিল্টার কাজ করছে ঠিক ডিজাইন অনুযায়ী। কোয়েফিসিয়েন্ট টেবিলে লক্ষ্য করুন h[n] == h[30-n] — যেমন h[0]=h[30]=0.001698, h[14]=h[16]=0.254930, কেন্দ্রে h[15]=0.3 — এই সিমেট্রি-ই লিনিয়ার ফেজের প্রমাণ (২য় অংশে আলোচিত)। sum(h) = 1.001645 — প্রায় ১, অর্থাৎ ফিল্টারের DC (0 Hz) গেইন প্রায় অপরিবর্তিত, যেমনটা একটি লো-পাস ফিল্টারের কাছে প্রত্যাশিত। ফলাফল টেবিলে — ১০ Hz উপাদানের ম্যাগনিটিউড 0.500000 থেকে 0.499992-এ (মাত্র −০.০০ dB) প্রায় অপরিবর্তিত থাকে, কিন্তু ৬০ Hz উপাদান 0.500000 থেকে 0.004318-এ (−৪১.২৭ dB) ধ্বসে পড়ে — সত্যিকারের, কম্পিউটেড প্রমাণ যে ফিল্টারটি প্রকৃতপক্ষে উচ্চ ফ্রিকোয়েন্সি নিম্ন ফ্রিকোয়েন্সির চেয়ে অনেক বেশি দমন করছে।
মূল কথা · Key takeaway

উইন্ডোয়িং পদ্ধতি একটি সরল, নির্ভরযোগ্য উপায়ে FIR ফিল্টার ডিজাইন করার কৌশল — একটি আদর্শ সিংক ইমপালস রেসপন্স নিয়ে, তা কেটে একটি উইন্ডো দিয়ে মসৃণ করে বাস্তবায়নযোগ্য করা হয়। ফলাফল সবসময় সিমেট্রিক (ঠিক লিনিয়ার ফেজ), এবং কনভোলিউশন দিয়ে প্রয়োগ করার পর DFT দিয়ে যাচাই করলে দেখা যায় ফিল্টার প্রকৃতপক্ষে তার ডিজাইন-করা কাজ করছে। L34-এ দেখা যাবে ট্যাপ সংখ্যা (M) বাড়ালে এটেনুয়েশন আরও শার্প হয়।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ কোড সেলে y_full[center:center+N] নিয়ে "গ্রুপ ডিলে" বাদ দেওয়া হয়েছে কেন? এটা না করলে কী সমস্যা হতো?

কনভোলিউশনের ফলাফল y_full-এর দৈর্ঘ্য ইনপুটের চেয়ে বেশি (60+31-1=90), এবং একটি সিমেট্রিক FIR ফিল্টার সবসময় সিগন্যালকে center=(M-1)/2=15 নমুনা বিলম্বিত করে (এটাই লিনিয়ার ফেজের সরাসরি ফলাফল — প্রতিটি ফ্রিকোয়েন্সি একই পরিমাণ সময় দেরি করে)। এই বিলম্ব বাদ না দিলে, y_full[n]-এর সাথে x[n]-এর তুলনা করা মানে ভুল সময়ের নমুনা তুলনা করা — ফলে DFT ম্যাগনিটিউড হিসাব বিকৃত হতো এবং এটেনুয়েশনের সংখ্যা সঠিক হতো না।

প্র ০২ ১০ Hz উপাদানের এটেনুয়েশন ঠিক ০ dB নয়, বরং −০.০০ dB (অর্থাৎ সামান্য নেতিবাচক)। এটা কি ডিজাইনে কোনো ভুলের লক্ষণ?

না — এটি প্রত্যাশিত ও স্বাভাবিক। একটি বাস্তবায়নযোগ্য (সসীম-দৈর্ঘ্যের, উইন্ডোড) ফিল্টার কখনোই আদর্শ ব্রিক-ওয়াল রেসপন্স হুবহু অর্জন করতে পারে না — প্যাসব্যান্ডে সামান্য "রিপল" (ripple) থাকা স্বাভাবিক, যা উইন্ডো ফাংশনের সাইড-লোবের কারণে হয় (L24-এ বিস্তারিত)। এখানে পার্থক্যটি এত ছোট (−0.0001 dB-এর কাছাকাছি) যে এটি ব্যবহারিকভাবে সম্পূর্ণ উপেক্ষণীয় — মূল কথা হলো এটি ৬০ Hz-এর −৪১.২৭ dB এটেনুয়েশনের তুলনায় নগণ্য, যা প্রমাণ করে ফিল্টার সঠিকভাবে কাজ করছে।

প্র ০৩ যদি টেস্ট সিগন্যালে একটি তৃতীয় কম্পোনেন্ট থাকত, ঠিক কাটঅফ ফ্রিকোয়েন্সিতে (৩০ Hz), তাহলে এটেনুয়েশন কেমন হতো বলে আপনার ধারণা?

ঠিক কাটঅফ ফ্রিকোয়েন্সিতে, একটি সাধারণ লো-পাস ফিল্টার ডিজাইনে গেইন সাধারণত প্যাসব্যান্ড ও স্টপব্যান্ডের মাঝামাঝি — কার্যত প্রায় অর্ধেক শক্তি (প্রায় −৬ dB ম্যাগনিটিউডে, যা "−৩ dB পয়েন্ট" নামে পরিচিত একটি প্রচলিত ফিল্টার-ডিজাইন কনভেনশন)। এটি ১০ Hz-এর প্রায়-শূন্য এটেনুয়েশন আর ৬০ Hz-এর −৪১ dB এটেনুয়েশনের মাঝামাঝি একটি ট্রানজিশন অঞ্চল — L34-এ এই ট্রানজিশন অঞ্চলের প্রকৃত শার্পনেস মাপা হবে।

অনুশীলন

  1. চিন্তা করুন: উপরের কোড সেলে fc = 30.0-কে fc = 20.0-এ পরিবর্তন করলে (fc নিচে নামানো হলে), ১০ Hz আর ৬০ Hz — দুটোই এখন কাটঅফের উপরে বা নিচে কোথায় পড়বে, এবং এটেনুয়েশন প্যাটার্নে কী পরিবর্তন হবে বলে আপনার ধারণা?

    fc=20 Hz হলে ১০ Hz (প্যাসব্যান্ডে, ২০-এর নিচে) এখনও প্রায় অপরিবর্তিত থাকবে, কিন্তু ৬০ Hz এখনও স্টপব্যান্ডে থাকবে (২০-এর অনেক উপরে) — তাই গুণগতভাবে প্যাটার্ন একই থাকবে (নিম্ন অপরিবর্তিত, উচ্চ দমিত)। তবে কাটঅফ কমে যাওয়ায় ট্রানজিশন ব্যান্ডও সরে যাবে ২০ Hz-এর কাছাকাছি।

  2. পরীক্ষা করুন: কোড সেলে fc = 30.0-কে fc = 20.0-এ পরিবর্তন করে Run চেপে আপনার হিসেব যাচাই করুন — এটেনুয়েশন টেবিলের প্রকৃত মান দেখুন।

    রান করলে ১০ Hz-এর এটেনুয়েশন এখনও প্রায় ০ dB-এর কাছাকাছি থাকবে (প্যাসব্যান্ডে), আর ৬০ Hz-এর এটেনুয়েশন আরও গভীর হবে (কাটঅফ থেকে আরও দূরে সরে যাওয়ায়, সাধারণত −৪১ dB-এর চেয়েও বেশি নেতিবাচক) — এটি নিশ্চিত করে যে ফিল্টারের আচরণ কাটঅফ ফ্রিকোয়েন্সির সাপেক্ষে সামঞ্জস্যপূর্ণভাবে পরিবর্তিত হয়, ঠিক তত্ত্ব অনুযায়ী।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

আগের পাঠ
ডিজিটাল ফিল্টার কী — FIR বনাম IIR