আপস্যাম্পলিং (ইন্টারপোলেশন)
এই পাঠে যা শিখবেন
- জিরো-ইনসার্শনের মাধ্যমে সিগন্যালকে
Lগুণ লম্বা করার প্রক্রিয়া - কেন জিরো-স্টাফড সিগন্যাল সরাসরি ব্যবহারযোগ্য নয়, এবং লো-পাস ফিল্টারিং কীভাবে এটিকে প্রকৃত ইন্টারপোলেটেড সিগন্যালে রূপান্তর করে
- গেইন সংশোধন (
×L) কেন দরকার তা গণনা দিয়ে দেখা - ইন্টারপোলেটেড আউটপুট সরাসরি উচ্চ-রেটে স্যাম্পল করা প্রকৃত সিগন্যালের সাথে সংখ্যাগতভাবে যাচাই করা, এবং ফিল্টারের প্রান্তে (edge) কেন সামান্য বেশি ত্রুটি দেখা যায় তা বোঝা
১ · জিরো-ইনসার্শন — ফাঁকা জায়গা তৈরি করা
আপস্যাম্পলিংUpsamplingএকটি ডিসক্রিট সিগন্যালের স্যাম্পল সংখ্যা L গুণ বাড়ানোর প্রথম ধাপ, যেখানে প্রতিটি মূল স্যাম্পলের পর (L-1)টি শূন্য বসানো হয়।
-এর প্রথম ধাপ হলো প্রতিটি স্যাম্পলের পর L-1টি শূন্য বসানো:
$$x_{\text{zero}}[n] = \begin{cases} x[n/L] & n \bmod L = 0 \\ 0 & \text{অন্যথায়} \end{cases}$$
এতে সিগন্যালের দৈর্ঘ্য L গুণ বেড়ে যায়, কিন্তু নতুন যোগ হওয়া মানগুলো নিছক শূন্য — কোনো
অর্থবহ তথ্য নয়। এই জিরো-স্টাফড সিগন্যালকে "সরাসরি" উচ্চ রেটের সিগন্যাল হিসেবে ব্যবহার করলে ফলাফল হবে
ঝাঁকুনিপূর্ণ (choppy) — মূল স্যাম্পলের মধ্যে হঠাৎ শূন্যে নেমে যাওয়া। দরকার একটি ফিল্টার যা এই শূন্যগুলোকে
প্রতিবেশী স্যাম্পল থেকে আনুমানিক মসৃণ মান দিয়ে প্রতিস্থাপন করবে।
প্রতি মূল স্যাম্পলের পর L-1টি শূন্য — দৈর্ঘ্য L গুণ, কিন্তু কোনো নতুন তথ্য নেই।
M7-এর একই windowed-sinc লো-পাস ফিল্টার এখানে শূন্যগুলো পূরণ করার কাজ করে — এটাই "ইন্টারপোলেশন ফিল্টার"।
জিরো-স্টাফিং গড় শক্তি কমিয়ে দেয় বলে ফিল্টারের গেইন L দিয়ে গুণ করে অ্যামপ্লিটিউড ঠিক রাখতে হয়।
২ · সত্যিকারের ডেমো — জিরো-স্টাফিং + লো-পাস ফিল্টার = ইন্টারপোলেশন
L40-এ আমরা একটি ৪৮ Hz সিগন্যালকে ১২ Hz-এ ডেসিমেট করেছিলাম। এবার ধরা যাক আমাদের কাছে শুধু একটি নিম্ন-রেট
সিগন্যাল আছে — fs_low = 12 Hz-এ স্যাম্পল করা একটি ৩ Hz সাইন — আর আমরা এটিকে
L = 4 দিয়ে আবার fs_new = 48 Hz-এ আপস্যাম্পল করতে চাই।
import math
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
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
fs_low = 12.0
f0 = 3.0
N_low = 12
x_low = [math.sin(2*math.pi*f0*n/fs_low) for n in range(N_low)]
L = 4
fs_new = fs_low * L # ৪৮ Hz
# ধাপ ১ -- জিরো-ইনসার্শন
x_zeros = []
for v in x_low:
x_zeros.append(v)
x_zeros.extend([0.0] * (L - 1))
print(f"মূল সিগন্যাল ({fs_low} Hz, N={N_low}):", [round(v, 4) for v in x_low[:4]], "...")
print(f"জিরো-স্টাফড সিগন্যাল ({fs_new} Hz, N={len(x_zeros)}), প্রথম ১২টি মান:")
print([round(v, 4) for v in x_zeros[:12]])
# ধাপ ২ -- লো-পাস ইন্টারপোলেশন ফিল্টার, গেইন ×L দিয়ে স্কেল করা
numtaps = 25
fc = 0.5 / L # কাটঅফ = নতুন নাইকুইস্ট-এর ভগ্নাংশ হিসেবে মূল ব্যান্ডউইথ
h = sinc_lowpass(numtaps, fc)
h_scaled = [L * v for v in h]
y_full = fir_apply(x_zeros, h_scaled)
delay = (numtaps - 1) // 2
y_interp = y_full[delay: delay + len(x_zeros)]
# তুলনার জন্য -- একই সাইন সরাসরি ৪৮ Hz-এ স্যাম্পল করা "প্রকৃত" মান
x_true = [math.sin(2*math.pi*f0*n/fs_new) for n in range(len(x_zeros))]
print(f"\n{'n':>3} | {'zero-stuffed':>13} | {'interpolated':>13} | {'true (48Hz)':>12} | {'diff':>10}")
max_err = 0.0
for n in range(24):
diff = abs(y_interp[n] - x_true[n])
max_err = max(max_err, diff)
print(f"{n:>3} | {x_zeros[n]:>13.4f} | {y_interp[n]:>13.4f} | {x_true[n]:>12.4f} | {diff:>10.2e}")
steady_state_err = max(abs(y_interp[n]-x_true[n]) for n in range(4, 24))
print(f"\nসর্বোচ্চ ত্রুটি (n=0..23, ফিল্টার-প্রান্ত সহ): {max_err:.4f}")
print(f"সর্বোচ্চ ত্রুটি (n=4..23, স্টেডি-স্টেট): {steady_state_err:.4f}")
0, 0, 0, 0) কোনো তথ্য নেই — শুধু n=4-এ আসল মূল স্যাম্পল
(1.0)। কিন্তু ফিল্টার করার পর interpolated কলামে n=1,2,3-এ যথাক্রমে
০.২৫২, ০.৫৭৯, ০.৮৬৫-এর মতো প্রকৃত, গণনা-করা মান দেখা
যাচ্ছে — নিছক শূন্য নয়! n=0..3 এই প্রথম চক্রে ত্রুটি একটু বেশি (সর্বোচ্চ ০.১৩-এর কাছাকাছি)
কারণ ফিল্টারের বাঁ পাশে কোনো "ইতিহাস" (আগের স্যাম্পল) নেই — একটি স্বাভাবিক ফিল্টার-প্রান্ত ট্রানজিয়েন্ট।
কিন্তু n=4 থেকে শুরু করে স্টেডি-স্টেটে (যেখানে ফিল্টারের দুই পাশেই যথেষ্ট প্রতিবেশী স্যাম্পল আছে) ত্রুটি
নেমে আসে মাত্র ০.০২-এর নিচে — অর্থাৎ ইন্টারপোলেটেড মান সরাসরি ৪৮ Hz-এ স্যাম্পল করা প্রকৃত
মানের সাথে প্রায় হুবহু মিলে যায়।
ইন্টারপোলেশন মানে "ফাঁকা জায়গায় শূন্য বসিয়ে রাখা" নয় — এটি একটি দুই-ধাপের প্রক্রিয়া: প্রথমে জিরো-ইনসার্শন দিয়ে জায়গা তৈরি করা, তারপর M7-এর একই লো-পাস ফিল্টারকে (গেইন সংশোধন সহ) একটি "ইন্টারপোলেটর" হিসেবে ব্যবহার করে সেই জায়গা প্রতিবেশী স্যাম্পল থেকে আনুমানিক মসৃণ মান দিয়ে পূরণ করা। L40-এর ডেসিমেশন ফিল্টার আর এখানকার ইন্টারপোলেশন ফিল্টার — দুটোই মূলত একই লো-পাস FIR ধারণা, শুধু কাটঅফ আর গেইন ভিন্ন প্রেক্ষাপটে বসানো হয়েছে। L42-এ আমরা দেখব কীভাবে এই দুই অপারেশনকে (ফিল্টার + রেট পরিবর্তন) পলিফেজ স্ট্রাকচার দিয়ে আরও কার্যকরভাবে বাস্তবায়ন করা যায়।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১
উপরের কোডে ফিল্টারের গেইন L দিয়ে স্কেল না করলে (অর্থাৎ h_scaled = h
ব্যবহার করলে) ইন্টারপোলেটেড আউটপুটের অ্যামপ্লিটিউডের কী হতো?
অ্যামপ্লিটিউড প্রায় L গুণ ছোট হয়ে যেত। জিরো-স্টাফিং প্রতি Lটি স্যাম্পলের মধ্যে
মাত্র একটিতে প্রকৃত শক্তি রাখে (বাকি সব শূন্য), তাই ফিল্টার যদি "গড়" আচরণ করে (গেইন ১), আউটপুট মূল
সিগন্যালের তুলনায় ম্লান হয়ে যাবে। গেইন ×L এই ক্ষয় পূরণ করে দেয় যাতে ইন্টারপোলেটেড
সিগন্যালের অ্যামপ্লিটিউড মূল সিগন্যালের সাথে সামঞ্জস্যপূর্ণ থাকে।
প্র ০২
এই ইন্টারপোলেশন ফিল্টারের কাটঅফ fc = 0.5/L (নতুন রেটের ভগ্নাংশ হিসেবে) বেছে নেওয়া
হয়েছে কেন — L40-এর অ্যান্টি-অ্যালিয়াস ফিল্টারের সাথে এর সম্পর্ক কী?
fc = 0.5/L মানে নতুন রেটে কাটঅফ fs_new × 0.5/L = fs_low/2 — অর্থাৎ ঠিক মূল,
নিম্ন-রেট সিগন্যালের নাইকুইস্ট সীমায়। এর বাইরে যা কিছু ফ্রিকোয়েন্সি জিরো-স্টাফিং তৈরি করেছে (এগুলোকে
"ইমেজ" বলা হয়, মূল স্পেকট্রামের পুনরাবৃত্তি) তা এই ফিল্টার সরিয়ে ফেলে। এটি L40-এর অ্যান্টি-অ্যালিয়াস
ফিল্টারের ধারণাগত আয়না — সেখানে ফিল্টার ডেসিমেশনের আগে অবাঞ্ছিত উচ্চ-ফ্রিকোয়েন্সি শক্তি সরায়, এখানে
ফিল্টার আপস্যাম্পলিং-এর পরে জিরো-ইনসার্শনের তৈরি করা অবাঞ্ছিত "ইমেজ" শক্তি সরায়।
প্র ০৩ n=0..3-এর প্রথম চক্রে ত্রুটি n=4 থেকে পরের চক্রগুলোর তুলনায় বেশি কেন — এটি কি একটি বাগ?
বাগ নয় — এটি একটি স্বাভাবিক FIR ফিল্টার-প্রান্ত (edge/transient) প্রভাব। ফিল্টারটি প্রতিটি আউটপুট
গণনায় তার আশেপাশের ২৫টি ইনপুট স্যাম্পল ব্যবহার করে (২৫-ট্যাপ ফিল্টার), কিন্তু সিগন্যালের একদম শুরুতে
(n=0..3) বাঁ দিকে যথেষ্ট "ইতিহাস" নেই — কোডে fir_apply ফাংশন সেই অনুপস্থিত মানগুলোকে
implicitly শূন্য ধরে নেয়। n যত বাড়ে, ফিল্টার তত বেশি প্রকৃত প্রতিবেশী স্যাম্পল পায়, তাই ত্রুটি দ্রুত
কমে যায় — এটি প্রতিটি বাস্তব FIR ফিল্টারিং অপারেশনের একটি সাধারণ, প্রত্যাশিত আচরণ (M7-এ বিস্তারিত)।
অনুশীলন
-
চিন্তা করুন: উপরের কোডে
L = 4-কেL = 2-এ বদলালে (fs_low=12 Hz অপরিবর্তিত রেখে) নতুনfs_newকত হবে, আর জিরো-স্টাফড সিগন্যালে প্রতি মূল স্যাম্পলের পর কতগুলো শূন্য বসবে?fs_new = fs_low × L = 12 × 2 = 24Hz, এবং প্রতি মূল স্যাম্পলের পরL-1 = 1টি শূন্য বসবে (অর্থাৎ প্রতি দুটি আউটপুট স্যাম্পলের একটি মূল, একটি শূন্য-থেকে-ফিল্টার-করা)। -
পরীক্ষা করুন: কোডে
L = 4-কেL = 2-এ পরিবর্তন করে (এবংfc = 0.5 / Lস্বয়ংক্রিয়ভাবে আপডেট হবে) Run চেপে দেখুন স্টেডি-স্টেট ত্রুটি (n বড় হলে) আগের মতোই ছোট থাকে কি না।রান করলে দেখা যাবে স্টেডি-স্টেট ত্রুটি এখনও ছোট থাকে (একই ফিল্টার-ডিজাইন নীতি প্রযোজ্য, শুধু
fcএখন0.5/2 = 0.25), যদিও দৈর্ঘ্য আর ইন্টারপোলেটেড মানের সংখ্যা বদলে যাবে (মোটN_low × L = 24স্যাম্পল, আগের ৪৮-এর বদলে) — যাচাই করুন প্রিন্ট হওয়া টেবিলের সংখ্যা দিয়ে।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পরবর্তী পাঠ — পলিফেজ ফিল্টার ও এফিসিয়েন্ট মাল্টিরেট স্ট্রাকচার L42 L40-L41-এর ফিল্টার-তারপর-রেট-পরিবর্তন পদ্ধতিকে কীভাবে অনেক কম গুণন-অপারেশনে বাস্তবায়ন করা যায় — একটি সত্যিকারের অপারেশন-কাউন্ট তুলনাসহ।
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৭টি পাঠ স্যাম্পলিং, কনভোলিউশন, Z-ট্রান্সফর্ম, DTFT/DFT, FFT, FIR/IIR ফিল্টার ডিজাইন, মাল্টিরেট প্রসেসিং, স্পেকট্রাল এস্টিমেশন, র্যান্ডম সিগন্যাল প্রসেসিং, বাস্তব প্রয়োগ ও ক্যাপস্টোন।
- Math for AI & ML কোর্স সহোদর কোর্স কমপ্লেক্স সংখ্যা, ট্রিগোনোমেট্রি ও লিনিয়ার অ্যালজেব্রার ভিত্তি — এই কোর্সের সিগন্যাল-প্রসেসিং-নির্দিষ্ট গণিতের পটভূমি।