ডাউনস্যাম্পলিং (ডেসিমেশন)
এই পাঠে যা শিখবেন
- ডেসিমেশনের সংজ্ঞা, নতুন স্যাম্পল রেট, এবং কেন এটি লিস্ট স্লাইসিং-এর মতোই সহজ দেখতে হলেও বিপজ্জনক
- কেন প্রতি
M-তম স্যাম্পল রাখলে কার্যকর নাইকুইস্ট সীমাfs/(2M)-এ নেমে আসে - DFT ম্যাগনিচিউড দিয়ে সত্যিকারের অ্যালিয়াসিং আর্টিফ্যাক্ট প্রত্যক্ষভাবে দেখা এবং পরিমাপ করা
- অ্যান্টি-অ্যালিয়াস ফিল্টার + ডেসিমেশনের সঠিক পাইপলাইন বাস্তবায়ন এবং তার প্রকৃত উন্নতি যাচাই করা
১ · ডেসিমেশন কী — কার্যকর স্যাম্পল রেট কমে যাওয়া
ডেসিমেশনDecimationএকটি ডিসক্রিট সিগন্যাল থেকে প্রতি M-তম স্যাম্পল রেখে বাকি সবগুলো বাদ দিয়ে স্যাম্পল রেট M গুণ কমানোর প্রক্রিয়া।
হলো একটি সিগন্যালকে M ফ্যাক্টরে ডাউনস্যাম্পল করা — গাণিতিকভাবে:
$$y[n] = x[nM]$$
Python-এ এটি নিছক লিস্ট স্লাইসিং x[::M]। যদি মূল সিগন্যাল fs Hz-এ স্যাম্পল করা
হয়ে থাকে, ডেসিমেটেড সিগন্যালের কার্যকর স্যাম্পল রেট হয় fs' = fs / M। এর মানে নতুন নাইকুইস্ট
সীমাও M গুণ কমে যায়:
$$f_{\text{Nyquist,new}} = \frac{fs'}{2} = \frac{fs}{2M}$$
সমস্যাটা এখানেই — মূল সিগন্যালে এমন ফ্রিকোয়েন্সি উপাদান থাকতে পারে যা পুরনো নাইকুইস্ট সীমার নিচে (নিরাপদ ছিল), কিন্তু নতুন, ছোট নাইকুইস্ট সীমার উপরে (M5/M6-এ শেখা নাইকুইস্ট-শ্যানন থিওরেম অনুযায়ী)। এমন কোনো উপাদান থাকলে সেটি ডেসিমেশনের সময় অ্যালিয়াস করবে — L06-এ দেখা অ্যালিয়াসিং-এর একই ঘটনা, শুধু এখানে এটি "প্রসেসিং-এর ভেতরে" ঘটছে, রেকর্ডিং-এর সময় নয়।
x[::M] — কোডে যতটা সহজ দেখতে, ততটাই সহজে অ্যালিয়াসিং তৈরি করতে পারে যদি সতর্ক না থাকা হয়।fs/2 থেকে fs/(2M)-এ নেমে যায় — মূল সিগন্যালে "নিরাপদ" থাকা উপাদানও নতুন সীমার উপরে পড়ে যেতে পারে।
ডেসিমেশনের আগে একটি লো-পাস অ্যান্টি-অ্যালিয়াস ফিল্টার প্রয়োগ করে বিপজ্জনক উচ্চ-ফ্রিকোয়েন্সি উপাদান সরিয়ে ফেলা।
২ · অ্যান্টি-অ্যালিয়াস ফিল্টার ছাড়া ডেসিমেশন — সত্যিকারের অ্যালিয়াসিং
একটি টেস্ট সিগন্যাল তৈরি করা যাক — fs = 48 Hz-এ স্যাম্পল করা, একটি ধীর ৩ Hz
আর একটি দ্রুত ৮ Hz (অ্যামপ্লিটিউড ০.৮) উপাদানের যোগফল। M = 4 দিয়ে নিছক
x[::4] করলে নতুন রেট fs' = 12 Hz, নতুন নাইকুইস্ট সীমা ৬ Hz। ৩ Hz
নিরাপদ, কিন্তু ৮ Hz নতুন সীমার উপরে — M5-এর হাতে-লেখা DFT দিয়ে দেখা যাক এর পরিণতি কী হয়।
import math
def dft_magnitude(x, fs):
"""হাতে-লেখা DFT (M5) — ফ্রিকোয়েন্সি বিন k=0..N/2 পর্যন্ত ম্যাগনিচিউড ফেরত দেয়"""
N = len(x)
mags = []
for k in range(N // 2 + 1):
re = sum(x[n] * math.cos(2 * math.pi * k * n / N) for n in range(N))
im = -sum(x[n] * math.sin(2 * math.pi * k * n / N) for n in range(N))
mag = math.sqrt(re**2 + im**2)
mags.append((k * fs / N, mag))
return mags
fs = 48.0
N = 48
f_low, f_high = 3.0, 8.0 # দুটি উপাদান: ধীর ৩ Hz, দ্রুত ৮ Hz (অ্যামপ্লিটিউড ০.৮)
x = [math.sin(2*math.pi*f_low*n/fs) + 0.8*math.sin(2*math.pi*f_high*n/fs) for n in range(N)]
M = 4
fs_new = fs / M # ১২ Hz -- নতুন নাইকুইস্ট সীমা ৬ Hz
x_naive = x[::M] # নিছক প্রতি চতুর্থ স্যাম্পল রাখা -- কোনো ফিল্টার নেই!
print(f"মূল রেট fs = {fs} Hz, N = {N} | ডেসিমেটেড রেট fs' = {fs_new} Hz, N' = {len(x_naive)}")
print(f"নতুন নাইকুইস্ট সীমা: {fs_new/2} Hz -- ৮ Hz উপাদানটি এই সীমার উপরে!")
# ৮ Hz উপাদানটি নতুন রেটে কোন ফ্রিকোয়েন্সিতে "ফোল্ড" (alias) করবে (L01/L06-এর সূত্র)
k = round(f_high / fs_new)
f_alias = f_high - k * fs_new
print(f"\nপ্রত্যাশিত অ্যালিয়াসড ফ্রিকোয়েন্সি: {f_high} Hz -> {f_alias} Hz (মান হিসেবে {abs(f_alias)} Hz)")
print(f"\n{'freq (Hz)':>10} | {'naive |X[k]|':>13}")
for f, m in dft_magnitude(x_naive, fs_new):
flag = " -- ভুয়া শক্তি (৮ Hz-এর অ্যালিয়াস)" if abs(f - abs(f_alias)) < 1e-9 and m > 1 else ""
print(f"{f:>10.2f} | {m:>13.3f}{flag}")
|f_alias| = 4 Hz-এ। ডেসিমেটেড ডেটা শুধু দেখে বলার কোনো উপায় নেই এই
৪ Hz শক্তি আসলে একটি প্রকৃত ৪ Hz সিগন্যাল, নাকি একটি ৮ Hz সিগন্যালের ভুতুড়ে ছায়া — এটাই অ্যালিয়াসিং-এর
বিপদ, এখন ডেসিমেশনের প্রেক্ষাপটে।
৩ · সঠিক পদ্ধতি — প্রথমে ফিল্টার, তারপর ডেসিমেট
সমাধান হলো ডেসিমেশনের আগে একটি অ্যান্টি-অ্যালিয়াস লো-পাস ফিল্টার প্রয়োগ করা, যার কাটঅফ
নতুন নাইকুইস্ট সীমায় (fs_new/2 = 6 Hz) বসানো — M7 (L31)-এর windowed-sinc পদ্ধতি পুনর্ব্যবহার
করে। এতে ৮ Hz উপাদানটি ডেসিমেশনের আগেই বহুলাংশে কমে যায়, ফলে ভাঁজ (fold) হওয়ার মতো তেমন কোনো শক্তি অবশিষ্ট
থাকে না।
def sinc_lowpass(numtaps, fc):
"""windowed-sinc লো-পাস ফিল্টার (M7/L31-এর পদ্ধতি) -- fc = কাটঅফ, fs-এর ভগ্নাংশ হিসেবে (0..0.5)"""
M = (numtaps - 1) / 2.0
h = []
for n in range(numtaps):
d = n - M
if abs(d) < 1e-9:
hn = 2 * fc # কেন্দ্রে সীমা: sin(x)/x -> 2fc যখন x->0
else:
hn = math.sin(2 * math.pi * fc * d) / (math.pi * d)
w = 0.54 - 0.46 * math.cos(2 * math.pi * n / (numtaps - 1)) # Hamming window
h.append(hn * w)
return h
def fir_apply(x, h):
"""ডিসক্রিট কনভোলিউশন (M3/L10) -- ফুল-লেংথ আউটপুট"""
nx, nh = len(x), len(h)
y = [0.0] * (nx + nh - 1)
for n in range(nx):
for k in range(nh):
y[n + k] += x[n] * h[k]
return y
numtaps = 25
fc = (fs_new / 2) / fs # কাটঅফ = নতুন নাইকুইস্ট (৬ Hz), fs=48-এর ভগ্নাংশ হিসেবে
h = sinc_lowpass(numtaps, fc)
y_full = fir_apply(x, h)
delay = (numtaps - 1) // 2 # লিনিয়ার-ফেজ FIR-এর গ্রুপ ডিলে (M7)
y_aligned = y_full[delay: delay + N]
x_filtered_dec = y_aligned[::M] # -- এবার ফিল্টার-করা সিগন্যাল থেকে ডেসিমেট
print(f"অ্যান্টি-অ্যালিয়াস ফিল্টার: {numtaps} ট্যাপ, কাটঅফ = {fc*fs} Hz")
print(f"\n{'freq (Hz)':>10} | {'naive |X[k]|':>13} | {'filtered |X[k]|':>16}")
mag_naive = dft_magnitude(x_naive, fs_new)
mag_filt = dft_magnitude(x_filtered_dec, fs_new)
for (f, mn), (_, mf) in zip(mag_naive, mag_filt):
print(f"{f:>10.2f} | {mn:>13.3f} | {mf:>16.3f}")
reduction_pct = 100 * mag_filt[4][1] / mag_naive[4][1]
preserved_pct = 100 * mag_filt[3][1] / mag_naive[3][1]
print(f"\n৪ Hz ভুয়া শক্তি: naive {mag_naive[4][1]:.3f} -> filtered {mag_filt[4][1]:.3f} ({reduction_pct:.1f}% অবশিষ্ট)")
print(f"৩ Hz প্রকৃত সিগন্যাল: naive {mag_naive[3][1]:.3f} -> filtered {mag_filt[3][1]:.3f} ({preserved_pct:.1f}% সংরক্ষিত)")
ডেসিমেশন মানে শুধু "কম স্যাম্পল রাখা" নয় — এটি কার্যকরভাবে সিগন্যালটিকে একটি নতুন, ছোট নাইকুইস্ট সীমার নিচে পুনরায় স্যাম্পল করা। M7-এর FIR লো-পাস ফিল্টারকে অ্যান্টি-অ্যালিয়াস ফিল্টার হিসেবে ব্যবহার না করলে, উচ্চ-ফ্রিকোয়েন্সি শক্তি নতুন, ভুল ফ্রিকোয়েন্সিতে ভাঁজ হয়ে ডেটাকে স্থায়ীভাবে দূষিত করে — ঠিক L06-এর অ্যালিয়াসিং-এর মতোই, কিন্তু এবার রেকর্ডিং-এর সময় নয়, প্রসেসিং পাইপলাইনের ভেতরে। L41-এ আমরা বিপরীত দিকটি দেখব — স্যাম্পল রেট বাড়ানো (আপস্যাম্পলিং), আর L42-এ দেখব কীভাবে ফিল্টার-তারপর-ডেসিমেট পদ্ধতিকে আরও কার্যকরভাবে বাস্তবায়ন করা যায় (পলিফেজ স্ট্রাকচার)।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১
যদি মূল সিগন্যালে ৮ Hz-এর বদলে শুধু ৩ Hz উপাদান থাকত (কোনো উচ্চ-ফ্রিকোয়েন্সি উপাদান ছাড়া), তাহলে
M = 4 দিয়ে নিছক x[::4] করা কি নিরাপদ হতো?
হ্যাঁ। অ্যালিয়াসিং তখনই ঘটে যখন সিগন্যালে নতুন, ছোট নাইকুইস্ট সীমা (এখানে ৬ Hz)-এর উপরে কোনো ফ্রিকোয়েন্সি উপাদান থাকে। ৩ Hz সেই সীমার অনেক নিচে, তাই এককভাবে থাকলে ফিল্টার ছাড়াই নিরাপদে ডেসিমেট করা যেত। বাস্তব সিগন্যালে সমস্যা হয় কারণ প্রায়ই অজানা বা অনিচ্ছাকৃত উচ্চ-ফ্রিকোয়েন্সি উপাদান (নয়েজ, হারমোনিক্স) উপস্থিত থাকে — তাই অ্যান্টি-অ্যালিয়াস ফিল্টার একটি নিরাপত্তামূলক অভ্যাস হিসেবে প্রায় সবসময় প্রযোজ্য হওয়া উচিত।
প্র ০২ ফিল্টার করার পরেও ৩ Hz সিগন্যালের ম্যাগনিচিউড পুরো ১০০% নয়, বরং ৯৭% কেন?
windowed-sinc ফিল্টার (M7/L31) একটি বাস্তবসম্মত, সসীম-দৈর্ঘ্যের আনুমানিকতা — এর একটি ট্রানজিশন ব্যান্ড আছে (একদম খাড়া কাটঅফ নয়), তাই কাটঅফের কাছাকাছি ফ্রিকোয়েন্সি উপাদান সামান্য কমে যায়, এবং প্যাসব্যান্ডেও সামান্য রিপল থাকতে পারে। M7/L34-এ দেখানো হয়েছে ট্যাপ-সংখ্যা বাড়ালে (দীর্ঘ ফিল্টার) এই ট্রানজিশন আরও খাড়া ও প্যাসব্যান্ড আরও সমতল হয় — এই ট্রেড-অফ প্রতিটি বাস্তব ফিল্টার ডিজাইনে উপস্থিত।
প্র ০৩
M-এর মান বাড়ালে (যেমন M=4 থেকে M=8) অ্যান্টি-অ্যালিয়াস ফিল্টারের কাটঅফ ফ্রিকোয়েন্সির
কী হবে বলে আপনার ধারণা?
কাটঅফ আরও কমে যাবে। কাটঅফ সবসময় নতুন নাইকুইস্ট সীমা fs_new/2 = fs/(2M)-এ বসাতে হয় — M যত
বড়, নতুন স্যাম্পল রেট তত ছোট, নাইকুইস্ট সীমাও তত ছোট। উদাহরণস্বরূপ এই ডেমোতে M=8 নিলে কাটঅফ হতো
48/16 = 3 Hz — যা আসলে প্রকৃত ৩ Hz সিগন্যালকেও আংশিকভাবে ছেঁটে ফেলতে শুরু করবে, দেখাচ্ছে
কেন M-এর মান বাড়ানোর একটি বাস্তব খরচ আছে (M43-এ ব্যবহারিক ট্রেড-অফ হিসেবে আলোচিত)।
অনুশীলন
-
চিন্তা করুন: উপরের কোডে
f_high = 8.0-কেf_high = 10.0-এ বদলালে (fs=48, M=4, fs_new=12 অপরিবর্তিত রেখে) নতুন অ্যালিয়াসড ফ্রিকোয়েন্সি কত হবে? সূত্রk = round(f_high/fs_new),f_alias = f_high - k*fs_newব্যবহার করে হিসেব করুন।k = round(10/12) = round(0.833) = 1, তাইf_alias = 10 - 1*12 = -2Hz — অর্থাৎ মান হিসেবে ২ Hz-এ ভুয়া শক্তি দেখা যাবে। -
পরীক্ষা করুন: দ্বিতীয় কোড সেলে
f_high = 8.0-কে10.0-এ বদলে (প্রথম সেলেও একই পরিবর্তন করে) Run চেপে naive ও filtered স্পেকট্রাম তুলনা করে দেখুন ভুয়া শক্তি এবার কোন বিনে দেখা যায় এবং ফিল্টারিং তা কতটা কমায়।রান করলে naive স্পেকট্রামে ২ Hz বিনে একটি ভুয়া স্পাইক দেখা যাবে (ঠিক হিসেব করা
f_aliasমিলে যায়), এবং অ্যান্টি-অ্যালিয়াস ফিল্টারিং-এর পরে সেই স্পাইকের ম্যাগনিচিউড উল্লেখযোগ্যভাবে কমে যাবে — ঠিক যেমন মূল ডেমোতে ৪ Hz বিনে দেখা গিয়েছিল, শুধু ফ্রিকোয়েন্সি ভিন্ন।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পরবর্তী পাঠ — আপস্যাম্পলিং (ইন্টারপোলেশন) L41 ডেসিমেশনের বিপরীত দিক — স্যাম্পল রেট বাড়ানো, জিরো-ইনসার্শন ও লো-পাস ফিল্টারিং দিয়ে অনুপস্থিত স্যাম্পলগুলো আনুমানিকভাবে পূরণ করা।
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৭টি পাঠ স্যাম্পলিং, কনভোলিউশন, Z-ট্রান্সফর্ম, DTFT/DFT, FFT, FIR/IIR ফিল্টার ডিজাইন, মাল্টিরেট প্রসেসিং, স্পেকট্রাল এস্টিমেশন, র্যান্ডম সিগন্যাল প্রসেসিং, বাস্তব প্রয়োগ ও ক্যাপস্টোন।
- Numerical Methods কোর্স সহোদর কোর্স সাধারণ সংখ্যাগত অ্যালগরিদম, রুট-ফাইন্ডিং, ইন্টিগ্রেশন ও ODE সলভিং — এই কোর্সের "হাতে-লেখা" দর্শনের একই ভিত্তি।