ডিসক্রিট ফুরিয়ার ট্রান্সফর্ম (DFT)
এই পাঠে যা শিখবেন
- DFT-এর সংজ্ঞা, DTFT থেকে এর উৎপত্তি, এবং $O(N^2)$ সরাসরি সামেশন সূত্র
- Python-এ সম্পূর্ণ হাতে-লেখা একটি DFT ফাংশন বাস্তবায়ন করা (স্ট্যান্ডার্ড লাইব্রেরি ছাড়া)
- DFT বিন ইনডেক্স $k$-কে প্রকৃত ফ্রিকোয়েন্সি (Hz)-এ রূপান্তর করা ($f_k = k \cdot f_s / N$)
- একটি সিগন্যালের জানা ফ্রিকোয়েন্সি DFT-এর পিক ম্যাগনিটিউড বিন থেকে সত্যিকারের গণনা দিয়ে পুনরুদ্ধার করা
১ · DTFT থেকে DFT — কেন এই পরিবর্তন দরকার
L২০-এ দেখেছি DTFT $X(e^{j\omega})$ একটি কন্টিনিউয়াস ফাংশন — $\omega$ যেকোনো বাস্তব মান নিতে পারে। কম্পিউটারে এটি সরাসরি সংরক্ষণ করা অসম্ভব (অসীম সংখ্যক মান)। সমাধান: $\omega$-কে ঠিক $N$টি সমান-ব্যবধানের মানে স্যাম্পল করা, $\omega_k = \frac{2\pi k}{N}$, যেখানে $k = 0, 1, \ldots, N-1$। এই স্যাম্পল-করা DTFT-ই DFTDiscrete Fourier Transformএকটি সসীম-দৈর্ঘ্যের সিগন্যালকে ঠিক N-টি সমান-ব্যবধানের ফ্রিকোয়েন্সি-বিনে রূপান্তরকারী সসীম সামেশন সূত্র — সম্পূর্ণভাবে কম্পিউটারে গণনাযোগ্য।:
$$X[k] = \sum_{n=0}^{N-1} x[n]\, e^{-j\frac{2\pi}{N}kn}, \qquad k = 0, 1, \ldots, N-1$$প্রতিটি $k$-কে একটি ফ্রিকোয়েন্সি বিন বলা হয়, যা প্রকৃত ফ্রিকোয়েন্সি $f_k = \dfrac{k \cdot f_s}{N}$ Hz-এর সাথে সংশ্লিষ্ট (যেখানে $f_s$ হলো স্যাম্পল রেট, M২-তে সংজ্ঞায়িত)। বিন-থেকে-বিনের ব্যবধান, অর্থাৎ ফ্রিকোয়েন্সি রেজোলিউশন, হলো $f_s/N$ Hz — এটি L২৫-এ গুরুত্বপূর্ণ হয়ে উঠবে।
$N$টি বিনের প্রতিটির জন্য $N$টি পদ যোগ করতে হয় — মোট $N \times N = N^2$ কমপ্লেক্স গুণন। ছোট $N$-এ ঠিক আছে, বড় $N$-এ ধীর — M৬-এ FFT এই সমস্যার সমাধান।
ইনপুট $N$টি সংখ্যা, আউটপুটও $N$টি (কমপ্লেক্স) সংখ্যা — কোনো অসীম সামেশন নেই, তাই সরাসরি কোডে বাস্তবায়নযোগ্য।
$f_k = k \cdot f_s/N$ — এই রূপান্তর সূত্রটি DFT ব্যবহারের সবচেয়ে গুরুত্বপূর্ণ ব্যবহারিক পদক্ষেপ, ভুল করলে ফ্রিকোয়েন্সি রিডিং ভুল হয়ে যায়।
২ · হাতে-লেখা DFT বাস্তবায়ন
নিচে সরাসরি সূত্র অনুযায়ী একটি DFT ফাংশন লেখা হয়েছে — শুধু cmath.exp ও নেস্টেড লুপ, কোনো
NumPy/SciPy নেই। এটি এই কোর্সের বাকি অংশে বারবার পুনরায় ব্যবহৃত হবে, তাই এখানে সঠিকভাবে যাচাই করা গুরুত্বপূর্ণ।
import cmath, math
def dft(x):
"""সরাসরি O(N^2) সামেশন সূত্র দিয়ে DFT -- এই কোর্সে বারবার পুনর্ব্যবহৃত হবে।"""
N = len(x)
X = []
for k in range(N):
s = sum(x[n] * cmath.exp(-2j * math.pi * k * n / N) for n in range(N))
X.append(s)
return X
fs = 64.0 # স্যাম্পল রেট (Hz)
N = 32 # স্যাম্পল সংখ্যা
f1, f2 = 8.0, 20.0 # প্রকৃত (জানা) ফ্রিকোয়েন্সি, Hz
a1, a2 = 1.0, 0.5 # অ্যামপ্লিটিউড
x = [a1 * math.sin(2 * math.pi * f1 * n / fs) + a2 * math.sin(2 * math.pi * f2 * n / fs)
for n in range(N)]
X = dft(x)
mags = [abs(v) for v in X]
print(f"fs={fs} Hz, N={N}, বিন রেজোলিউশন fs/N = {fs/N} Hz")
print(f"{'k':>3} | {'freq (Hz)':>9} | {'|X[k]|':>8}")
for k in range(N // 2 + 1):
print(f"{k:3d} | {k*fs/N:9.2f} | {mags[k]:8.3f}")
৩ · পিক-বিন খুঁজে ফ্রিকোয়েন্সি পুনরুদ্ধার
বাস্তব ব্যবহারে আমরা কোন বিনে পিক আছে তা "চোখে দেখে" নয়, বরং সর্বোচ্চ ম্যাগনিটিউডের বিন প্রোগ্রামগতভাবে
খুঁজে বের করি। নিচের কোডে (উপরের x, X, mags ব্যবহার করে) সেই
অনুসন্ধান দেখানো হলো।
# উপরের কোষের x, X, mags, fs, N ব্যবহার করে --
# k=0 (DC) বাদ দিয়ে, k=1..N/2 রেঞ্জে সর্বোচ্চ দুটো ম্যাগনিটিউডের বিন খোঁজা
sorted_bins = sorted(range(1, N // 2 + 1), key=lambda k: -mags[k])
top2 = sorted_bins[:2]
print("সর্বোচ্চ ম্যাগনিটিউডের বিন (k=1..N/2):", top2)
for k in top2:
f_recovered = k * fs / N
print(f" k={k} -> পুনরুদ্ধারকৃত ফ্রিকোয়েন্সি = {f_recovered} Hz, |X[k]| = {mags[k]:.4f}")
print()
print(f"প্রকৃত ফ্রিকোয়েন্সি ছিল: {f1} Hz ও {f2} Hz")
[4, 10], যা থেকে পুনরুদ্ধারকৃত ফ্রিকোয়েন্সি $8.0$ Hz ও $20.0$ Hz —
প্রকৃত ফ্রিকোয়েন্সি $8.0$ Hz ও $20.0$ Hz-এর সাথে হুবহু নির্ভুলভাবে মিলে যায় (কারণ এই
উদাহরণে উভয় ফ্রিকোয়েন্সি ঠিক পূর্ণসংখ্যা বিনে পড়েছে)। এটাই DFT-এর সবচেয়ে ব্যবহারিক প্রয়োগ — একটি অজানা
সিগন্যালে কোন কোন ফ্রিকোয়েন্সি লুকিয়ে আছে তা সত্যিকারের গণনা দিয়ে বের করা।
এই পাঠের dft() ফাংশন আর "বিন-থেকে-Hz" রূপান্তর সূত্র ($f_k = k \cdot f_s/N$) হলো এই কোর্সের
সবচেয়ে গুরুত্বপূর্ণ পুনর্ব্যবহারযোগ্য বিল্ডিং ব্লক — L২৩-এ এর বৈশিষ্ট্য যাচাই, L২৪-এ উইন্ডোয়িং, L২৫-এ
জিরো-প্যাডিং, M৬-এ FFT-এর সাথে ক্রস-চেক, এবং M৭-M১০-এ ফিল্টার ও স্পেকট্রাল-এস্টিমেশন যাচাইয়ে বারবার এই
একই প্যাটার্ন ফিরে আসবে। তবে মনে রাখবেন — এখানে ফ্রিকোয়েন্সিগুলো ঠিক পূর্ণসংখ্যা বিনে পড়েছিল বলেই
ফলাফল এত পরিষ্কার হয়েছে; L২৪-L২৫-এ দেখা যাবে বাস্তব সিগন্যাল সবসময় এত সৌভাগ্যবান হয় না।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ উপরের ডেমোতে $N=32$, $f_s=64$ Hz দিয়ে বিন রেজোলিউশন $2$ Hz পাওয়া গেল। যদি $N=64$ করা হতো (একই $f_s$), রেজোলিউশন কী হতো, আর এটি কি ভালো নাকি খারাপ?
$N=64$-এ রেজোলিউশন $f_s/N = 64/64 = 1$ Hz হতো — অর্থাৎ আগের চেয়ে দ্বিগুণ ভালো (সূক্ষ্ম) রেজোলিউশন, কারণ বিন-থেকে-বিনের ব্যবধান কমে গেছে। কিন্তু বেশি $N$ মানে বেশি real স্যাম্পল দরকার (দীর্ঘ সময় ধরে সিগন্যাল রেকর্ড করা), এবং $O(N^2)$ গণনাও বেড়ে যায় — L২৫-এ দেখা যাবে শুধু জিরো-প্যাড করে (নকল স্যাম্পল যোগ করে) এই একই সুবিধা পাওয়া যায় না।
প্র ০২ যদি প্রকৃত ফ্রিকোয়েন্সি $f_1=8$ Hz-এর বদলে $f_1=9$ Hz হতো (একই $f_s=64$, $N=32$), তাহলে পিক কি একটি একক বিনে ঠিক পড়ত?
না। $9$ Hz-কে বিন ইনডেক্সে রূপান্তর করলে $k = f/(f_s/N) = 9/2 = 4.5$ — একটি ভগ্নাংশ, কোনো পূর্ণসংখ্যা বিন নয়। ফলে শক্তি $k=4$ ও $k=5$-এর মধ্যে "ছড়িয়ে" পড়বে, এবং আশেপাশের বিনগুলোতেও কিছুটা শক্তি "লিক" করবে — এটাই স্পেকট্রাল লিকেজ, যা L২৪-এ বিস্তারিত আলোচনা করা হবে।
প্র ০৩
উপরের কোডে sorted_bins খোঁজার সময় $k=0$ (DC বিন) বাদ দেওয়া হয়েছে কেন?
$k=0$ সবসময় সিগন্যালের গড় মান (DC কম্পোনেন্ট) প্রতিনিধিত্ব করে, দোদুল্যমান (oscillating) কোনো ফ্রিকোয়েন্সি নয়। আমাদের টেস্ট সিগন্যাল দুটো সাইন-তরঙ্গের যোগফল, যাদের গড় মান শূন্য, তাই এখানে $X[0]\approx 0$ হবে — কিন্তু সাধারণভাবে যদি সিগন্যালে কোনো ধ্রুবক অফসেট (DC bias) থাকে, তাহলে $X[0]$ বড় হতে পারে এবং প্রকৃত দোদুল্যমান ফ্রিকোয়েন্সি খোঁজার সময় এটিকে ভুলবশত "পিক" ধরে ফেলা এড়াতে সবসময় $k \geq 1$ থেকে খোঁজা ভালো অভ্যাস।
অনুশীলন
-
চিন্তা করুন: যদি
fs = 64.0,N = 32অপরিবর্তিত রেখেf1 = 8.0-কেf1 = 16.0-এ বদলানো হয়, ফ্রিকোয়েন্সি বিন-ইনডেক্স $k$ কত হবে বলে আপনার ধারণা ($k = f \cdot N / f_s$ সূত্র ব্যবহার করে)?$k = f_1 \cdot N / f_s = 16 \times 32 / 64 = 8$। অর্থাৎ $k=8$ বিনে পিক পড়বে, যা $f = 8 \times 64/32 = 16$ Hz-এর সাথে মেলে — একটি পূর্ণসংখ্যা বিন, তাই এখানেও পরিষ্কার একক-বিন পিক পাওয়া উচিত।
-
পরীক্ষা করুন: উপরের প্রথম কোড সেলে
f1 = 8.0-কেf1 = 16.0-এ বদলে Run চাপুন এবং দ্বিতীয় কোড সেলটিও (একইxব্যবহার করে, যেহেতু কোষগুলো একই সেশনে চলে) রান করে পিক-বিন খুঁজে যাচাই করুন আপনার হিসেব ঠিক ছিল কিনা।রান করলে প্রথম কোষে $k=8$-এ $|X[8]| = 16.000$ দেখাবে (আগের মতোই $16$, কারণ অ্যামপ্লিটিউড $a_1=1.0$ অপরিবর্তিত), আর $k=10$-এ $f_2=20$ Hz-এর পিক ($|X[10]|=8.000$) আগের মতোই থাকবে। দ্বিতীয় কোষে
top2হবে[8, 10], পুনরুদ্ধারকৃত ফ্রিকোয়েন্সি $16.0$ Hz ও $20.0$ Hz — হিসেবের সাথে হুবহু মিলে যায়।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
-
DFT-এর বৈশিষ্ট্য — সিমেট্রি, পিরিয়ডিসিটি, লিকেজ পরবর্তী পাঠ
এই পাঠের
dft()ফাংশন পুনরায় ব্যবহার করে কনজুগেট সিমেট্রি ও পিরিয়ডিসিটি সংখ্যাগতভাবে যাচাই করা হবে। - কেন FFT — DFT-এর কম্পিউটেশনাল কমপ্লেক্সিটি M৬ এই পাঠের $O(N^2)$ সরাসরি DFT-কে $O(N\log N)$-এ কমানোর অ্যালগরিদম, একই ফলাফল দিয়ে ক্রস-চেক করা হবে।
- Math for AI & ML কোর্স সহোদর কোর্স কমপ্লেক্স এক্সপোনেনশিয়াল ও ট্রিগোনোমেট্রিক আইডেন্টিটির গাণিতিক ভিত্তি।