পাঠ ৪৪ · ৫৭-এর মধ্যে · মডিউল ১০
Home / Courses / Digital Signal Processing / স্পেকট্রাল এস্টিমেশন

পাওয়ার স্পেকট্রাল ডেনসিটি এস্টিমেশন

Power spectral density estimation
৯ মিনিট পড়া মধ্যম-উচ্চ · Intermediate-Advanced Python কোডসহ সম্পূর্ণ বাংলায়

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

  • পাওয়ার স্পেকট্রাল ডেনসিটির সংজ্ঞা ও কেন এটি নয়েজি সিগন্যাল বিশ্লেষণে গুরুত্বপূর্ণ
  • পেরিওডোগ্রাম সূত্র ($|X[k]|^2/N$) ও তা M৫-M৬-এর DFT/FFT-এর উপর সরাসরি তৈরি
  • Python-এ হাতে-লেখা DFT দিয়ে একটি সত্যিকারের পেরিওডোগ্রাম গণনা ও পিক শনাক্তকরণ
  • পেরিওডোগ্রামের সীমাবদ্ধতা (ভ্যারিয়ান্স) এবং Welch's method-এর ধারণাগত সমাধান

১ · পাওয়ার স্পেকট্রাল ডেনসিটি কী

একটি সিগন্যালের পাওয়ার স্পেকট্রাল ডেনসিটি (PSD)Power Spectral Densityএকটি সিগন্যালের গড় পাওয়ার ফ্রিকোয়েন্সি অনুযায়ী কীভাবে বিতরণ হয়ে আছে তা বর্ণনাকারী ফাংশন। বলে দেয় সিগন্যালের মোট পাওয়ার কোন কোন ফ্রিকোয়েন্সিতে "কেন্দ্রীভূত" হয়ে আছে। M২২-এ শেখা DFT সরাসরি একটি নির্দিষ্ট-দৈর্ঘ্যের সিগন্যালের ফ্রিকোয়েন্সি-কম্পোনেন্ট দেয়, কিন্তু PSD-র মূল ব্যবহার হয় যখন সিগন্যালে র‍্যান্ডম নয়েজ থাকে (M১১-এ বিস্তারিত) — তখন প্রশ্ন হয় "গড়ে কোন ফ্রিকোয়েন্সিতে সত্যিকারের সিগন্যাল লুকিয়ে আছে, নয়েজের ভেতরে?"

সবচেয়ে সাধারণ এস্টিমেটর হলো পেরিওডোগ্রাম:

$$S_{xx}[k] = \frac{|X[k]|^2}{N}$$

যেখানে $X[k]$ হলো সিগন্যালের $N$-বিন্দুর DFT (M৫-M৬)। এটি আসলে Parseval-এর উপপাদ্যের একটি সরাসরি প্রয়োগ — সময়-ডোমেইনের এনার্জি ফ্রিকোয়েন্সি-ডোমেইনের এনার্জির সমান, আর $N$ দিয়ে ভাগ করে তা "গড় পাওয়ার প্রতি ফ্রিকোয়েন্সি-বিন" আকারে পাওয়া যায়।

সরাসরি DFT-এর উপর নির্ভরশীল
পেরিওডোগ্রাম গণনা করতে নতুন কিছু শিখতে হয় না — শুধু M৫/M৬-এর DFT/FFT নিয়ে তার ম্যাগনিটিউড স্কয়ার করে $N$ দিয়ে ভাগ করলেই হয়।
নয়েজের ভেতরে সিগন্যাল খোঁজা
সত্যিকারের সিগন্যাল ফ্রিকোয়েন্সি বিনগুলোতে পাওয়ার "কেন্দ্রীভূত" থাকে, আর র‍্যান্ডম নয়েজ প্রায় সমানভাবে সব বিনে ছড়িয়ে থাকে — এই পার্থক্যই পিক শনাক্ত করতে সাহায্য করে।
ফ্রিকোয়েন্সি রেজোলিউশন
M২৫-এ শেখা নিয়ম এখানেও প্রযোজ্য — বিন-স্পেসিং হলো $f_s/N$, আর দুটি কাছাকাছি ফ্রিকোয়েন্সি আলাদা করতে প্রকৃত সিগন্যাল-দৈর্ঘ্য (রেকর্ডিং সময়) বাড়াতে হয়, শুধু জিরো-প্যাডিং যথেষ্ট নয়।

২ · পেরিওডোগ্রাম — একটি সত্যিকারের Python ডেমো

নিচের কোডে $N=32$ স্যাম্পলের একটি সিগন্যাল তৈরি করা হয়েছে — $f_s=32$ Hz-এ স্যাম্পল করা একটি $f_0=6$ Hz সাইন-তরঙ্গ (যা ঠিক bin ৬-এ পড়ে, কারণ বিন-স্পেসিং $f_s/N = 1$ Hz), যার সাথে যোগ করা হয়েছে random.gauss(0, 0.4) নয়েজ (fixed seed = 7, পুনরুৎপাদনযোগ্যতার জন্য)। M৫-এর সূত্র দিয়ে হাতে-লেখা DFT ব্যবহার করে পেরিওডোগ্রাম গণনা করা হয়েছে — কোনো NumPy/SciPy নেই।

Python
import math, cmath, random

def dft(x):
    N = len(x)
    X = []
    for k in range(N):
        s = 0 + 0j
        for n in range(N):
            s += x[n] * cmath.exp(-2j * math.pi * k * n / N)
        X.append(s)
    return X

random.seed(7)
N = 32
fs = 32.0
f0 = 6.0          # প্রকৃত ফ্রিকোয়েন্সি -- ঠিক bin 6-এ পড়ে (fs/N = 1 Hz প্রতি বিন)
noise_std = 0.4

x = [math.sin(2 * math.pi * f0 * n / fs) + random.gauss(0, noise_std) for n in range(N)]

X = dft(x)
psd = [(abs(X[k]) ** 2) / N for k in range(N)]   # পেরিওডোগ্রাম: |X[k]|^2 / N

print(f"{'k':>3} | {'freq(Hz)':>9} | {'|X[k]|^2/N':>12}")
for k in range(N // 2 + 1):
    freq = k * fs / N
    print(f"{k:>3} | {freq:>9.2f} | {psd[k]:>12.4f}")

peak_k = max(range(N // 2 + 1), key=lambda k: psd[k])
noise_bins = [psd[k] for k in range(1, N // 2 + 1) if k != peak_k]

print()
print(f"পিক bin: {peak_k}  (ফ্রিকোয়েন্সি {peak_k*fs/N} Hz),  পিক মান: {psd[peak_k]:.4f}")
print(f"গড় নয়েজ ফ্লোর (পিক ও DC বাদে): {sum(noise_bins)/len(noise_bins):.4f}")
print(f"সর্বোচ্চ নয়েজ-বিন মান: {max(noise_bins):.4f}")

    
রান করলে bin ৬ (৬.০০ Hz)-এ পেরিওডোগ্রাম মান ৮.১৫৭৬ — বাকি সব বিনের তুলনায় স্পষ্টভাবে সবচেয়ে উঁচু। গড় নয়েজ ফ্লোর (পিক আর DC বাদে বাকি ১৫টি বিনের গড়) মাত্র ০.১১৯৭, আর সবচেয়ে উঁচু নয়েজ-বিনও (bin ৫, ৫ Hz) মাত্র ০.৪৪৪২ — অর্থাৎ প্রকৃত পিক নয়েজ ফ্লোরের প্রায় ১৮ গুণ উপরে, স্পষ্টভাবে শনাক্তযোগ্য। এটাই দেখায় কেন পেরিওডোগ্রাম নয়েজের ভেতরে লুকানো একটি সাইন-তরঙ্গ খুঁজে বের করার জন্য কার্যকর — যদিও নয়েজ বিনগুলো একদম সমান নয় (এলোমেলো ওঠানামা করে), তবু পিকটি স্পষ্টভাবে আলাদা।

৩ · পেরিওডোগ্রামের সীমাবদ্ধতা — ভ্যারিয়ান্স ও Welch's method

উপরের ডেমোতে নয়েজ-বিনগুলোর মান লক্ষ্য করুন — ০.০০৪৭ থেকে ০.৪৪৪২ পর্যন্ত, অনেক বিস্তৃত। এটাই পেরিওডোগ্রামের পরিচিত দুর্বলতা: এটি PSD-র একটি উচ্চ-ভ্যারিয়ান্স এস্টিমেটর — একই সত্যিকারের PSD থেকে ভিন্ন ভিন্ন নয়েজ-নমুনায় পেরিওডোগ্রাম আউটপুট যথেষ্ট নড়াচড়া করে, এবং $N$ বাড়ালেও এই ভ্যারিয়ান্স কমে না (শুধু ফ্রিকোয়েন্সি-রেজোলিউশন বাড়ে, নির্ভুলতা নয়)।

বাস্তবে এই সমস্যা সমাধানের জন্য Welch's method-এর মতো কৌশল ব্যবহার হয়: সিগন্যালকে ছোট ছোট (সম্ভবত ওভারল্যাপিং) অংশে ভাগ করে প্রতিটি অংশের পেরিওডোগ্রাম আলাদাভাবে গণনা করে, তারপর সবগুলোর গড় নেওয়া হয় — একাধিক স্বতন্ত্র নমুনার গড় নিলে ভ্যারিয়ান্স কমে (ঠিক যেমন পরিসংখ্যানে একাধিক পরিমাপের গড় একক পরিমাপের চেয়ে বেশি নির্ভরযোগ্য)। এই কৌশলটি পরের পাঠের STFT-এর সাথে গঠনগতভাবে সম্পর্কিত — উভয়ই সিগন্যালকে উইন্ডো-করা ছোট অংশে ভাগ করে আলাদাভাবে DFT নেয়, শুধু উদ্দেশ্য ভিন্ন (Welch গড় নেয় একটি একক, আরও নির্ভরযোগ্য স্পেকট্রামের জন্য, STFT প্রতিটি ফ্রেম আলাদা রাখে সময়ের সাথে পরিবর্তন দেখতে)।

মূল কথা · Key takeaway

পেরিওডোগ্রাম ($|X[k]|^2/N$) হলো PSD এস্টিমেশনের সবচেয়ে সহজ পদ্ধতি, এবং M৫-M৬-এর DFT/FFT-এর উপর সরাসরি তৈরি — উপরের ডেমো দেখাল এটি নয়েজের ভেতরে একটি সত্যিকারের সাইন-তরঙ্গের ফ্রিকোয়েন্সি স্পষ্টভাবে শনাক্ত করতে পারে (পিক নয়েজ ফ্লোরের প্রায় ১৮ গুণ উপরে)। কিন্তু এর উচ্চ ভ্যারিয়ান্সের কারণে বাস্তব অ্যাপ্লিকেশনে Welch's method-এর মতো গড়-করা কৌশল ব্যবহার হয়। এই মডিউলের পরের পাঠে (L৪৫) আমরা DFT-কে সময়ের সাথে সাথে চালিয়ে STFT তৈরি করব — যেখানে সিগন্যাল স্থির নয়, সময়ের সাথে বদলায়।

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

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

প্র ০১ উপরের ডেমোতে পিক ঠিক bin ৬-এ পড়েছে, কোনো "ফাঁকে" (in-between) নয়। এটি কি কাকতালীয়, নাকি ডিজাইন করা হয়েছিল?

ডিজাইন করা হয়েছিল। বিন-স্পেসিং সবসময় $f_s/N$ — এখানে $32/32 = 1$ Hz প্রতি বিন। যেহেতু সিগন্যালের প্রকৃত ফ্রিকোয়েন্সি $f_0=6$ Hz ঠিক একটি বিনের উপর পড়ে ($6 = 6 \times 1$ Hz), তাই পুরো এনার্জি একটি একক বিনে কেন্দ্রীভূত হয়। যদি $f_0$ দুটি বিনের মাঝামাঝি হতো (যেমন ৬.৫ Hz), তাহলে M২৩/M২৪-এ শেখা স্পেকট্রাল লিকেজের কারণে এনার্জি পার্শ্ববর্তী বিনগুলোতেও ছড়িয়ে পড়ত, আর পিক কম তীক্ষ্ণ দেখাত।

প্র ০২ নয়েজ-বিনগুলোর মান (০.০০৪৭ থেকে ০.৪৪৪২) কেন এত বিস্তৃত, যদিও নয়েজ তাত্ত্বিকভাবে সব ফ্রিকোয়েন্সিতে "সমান শক্তি" রাখার কথা?

random.gauss দিয়ে তৈরি নয়েজের প্রত্যাশিত (expected) PSD সব বিনে সমান, কিন্তু একটি একক, সসীম-দৈর্ঘ্যের ($N=32$) বাস্তবায়নে এই প্রত্যাশা থেকে যথেষ্ট এলোমেলো বিচ্যুতি হবেই — এটাই ৩ নং অংশে আলোচিত পেরিওডোগ্রামের উচ্চ ভ্যারিয়ান্স সমস্যা। যদি একই পরীক্ষা বহুবার ভিন্ন random.seed() দিয়ে চালানো হয় এবং ফলাফলগুলোর গড় নেওয়া হয় (Welch's method-এর মূল ধারণা), তখন নয়েজ-বিনগুলোর গড় মান একটি সমতল রেখার কাছাকাছি স্থিতিশীল হয়ে আসবে।

প্র ০৩ noise_std-কে ০.৪ থেকে বাড়িয়ে ২.০ করলে পিক শনাক্ত করা কি সহজ হবে না কঠিন?

কঠিন হবে। পিকের মান নির্ভর করে সিগন্যাল-অংশের পাওয়ারের উপর, যা নয়েজ থেকে স্বাধীন, কিন্তু নয়েজ ফ্লোরের গড় মান নয়েজ-ভ্যারিয়ান্সের সাথে সরাসরি সমানুপাতিক। noise_std বাড়ালে নয়েজ ফ্লোর উঁচু হবে, আর পিক-থেকে-নয়েজ-ফ্লোর অনুপাত (এটাই মূলত M৪৯-এর SNR ধারণার সাথে সম্পর্কিত) কমে যাবে — চরম ক্ষেত্রে পিকটি নয়েজের মধ্যে হারিয়ে যেতে পারে।

অনুশীলন

  1. চিন্তা করুন: উপরের কোডে f0 = 6.0-কে f0 = 10.0-এ বদলালে (fs=32, N=32 অপরিবর্তিত), পিক কোন bin-এ পড়বে বলে আপনার ধারণা? বিন-স্পেসিং $f_s/N=1$ Hz ব্যবহার করে হিসেব করুন।

    বিন-স্পেসিং ১ Hz বলে $f_0=10$ Hz ঠিক bin ১০-এ পড়বে ($10/1 = 10$)। কোড রান করলে bin ১০-এ একটি স্পষ্ট পিক দেখা যাবে, ঠিক যেমন মূল ডেমোতে $f_0=6$ Hz bin ৬-এ পড়েছিল।

  2. পরীক্ষা করুন: কোড সেলে f0-কে ১০.০-এ পরিবর্তন করে Run চেপে আপনার হিসেব যাচাই করুন, এবং লক্ষ্য করুন নতুন পিক মান আগের ৮.১৫৭৬-এর কাছাকাছি কি না।

    রান করলে bin ১০-এ পিক দেখাবে, আর মান আগের পিকের কাছাকাছি থাকবে (সিগন্যালের অ্যামপ্লিটিউড ও নয়েজ পরিসংখ্যান একই রাখা হয়েছে, শুধু ফ্রিকোয়েন্সি বদলেছে) — তবে random.gauss-এর কল-সংখ্যা ও ক্রম অপরিবর্তিত থাকায় নয়েজ-নমুনাগুলো একদম অভিন্ন থাকবে, শুধু সিগন্যাল-অংশটি ভিন্ন bin-এ যোগ হবে।

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

আগের পাঠ
স্যাম্পল রেট কনভার্সন — বাস্তব প্রয়োগ