পাঠ ০৮ · ৫৭-এর মধ্যে · মডিউল ২

অ্যানালগ-টু-ডিজিটাল ও ডিজিটাল-টু-অ্যানালগ কনভার্সন

Analog-to-digital & digital-to-analog conversion
৯ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

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

  • ADC-এর দুটো ধাপ (sample, quantize) এবং DAC-এর reconstruction ধাপ কীভাবে একসাথে কাজ করে
  • Zero-order hold (ZOH) reconstruction কী, এবং এটি কেন কন্টিনিউয়াস সিগন্যালের একটি সিঁড়ি-আকৃতির (staircase) আনুমানিক রূপ মাত্র
  • সত্যিকারের কোড দিয়ে সম্পূর্ণ ADC → DAC পাইপলাইন চালিয়ে প্রকৃত before/after মান পর্যবেক্ষণ করা
  • M2 (L05-L08)-এর সবগুলো ধারণা — নাইকুইস্ট থিওরেম, অ্যালিয়াসিং, কোয়ান্টাইজেশন — কীভাবে একটি বাস্তব ADC/DAC ডিভাইসে একত্রিত হয়

১ · ADC — দুটো স্বাধীন ধাপ

একটি ADCAnalog-to-Digital Converterএকটি হার্ডওয়্যার/প্রক্রিয়া যা একটি কন্টিনিউয়াস অ্যানালগ সিগন্যালকে স্যাম্পলিং ও কোয়ান্টাইজেশনের মাধ্যমে একটি ডিজিটাল সংখ্যার সিরিজে রূপান্তর করে। আসলে দুটো সম্পূর্ণ ভিন্ন, স্বাধীন সীমাবদ্ধতা একসাথে প্রয়োগ করে:

ধাপ ১ · স্যাম্পলিং
সময়ে ডিসক্রিট করা — কত ঘন ঘন মান নেওয়া হবে (L05-L06)। ভুল হলে অ্যালিয়াসিং — স্থায়ী তথ্য-ক্ষতি।
ধাপ ২ · কোয়ান্টাইজেশন
অ্যামপ্লিটিউডে ডিসক্রিট করা — কত নিখুঁতভাবে প্রতিটি মান সংরক্ষণ করা হবে (L07)। প্রতিটি মানে ছোট, নিয়ন্ত্রিত এরর যোগ হয়।

এই দুটো ধাপ সম্পূর্ণ স্বতন্ত্র — একটি ADC-এর স্যাম্পল রেট বাড়ালে বিট-গভীরতার উপর কোনো প্রভাব পড়ে না, আর উল্টোটাও সত্য। একটি ভালো ADC ডিজাইনে দুটোই আলাদাভাবে সিগন্যালের প্রয়োজন অনুযায়ী বেছে নিতে হয়।

২ · DAC — Zero-Order Hold পুনর্গঠন

বিপরীত দিকে, একটি DACDigital-to-Analog Converterএকটি হার্ডওয়্যার/প্রক্রিয়া যা ডিজিটাল স্যাম্পলের সিরিজ থেকে একটি কন্টিনিউয়াস অ্যানালগ সিগন্যাল পুনর্গঠন করে। ডিজিটাল স্যাম্পল থেকে একটি কন্টিনিউয়াস সিগন্যাল ফিরিয়ে আনে। সবচেয়ে সাধারণ, সরল পদ্ধতি হলো zero-order hold (ZOH) — প্রতিটি স্যাম্পল মান ঠিক পরবর্তী স্যাম্পল না আসা পর্যন্ত অপরিবর্তিত "ধরে রাখা" (হার্ডওয়্যারে সাধারণত একটি sample-and-hold সার্কিট দিয়ে বাস্তবায়িত হয়), ফলে আউটপুট একটি সিঁড়ি-আকৃতির (staircase) সিগন্যাল হয় — আসল কন্টিনিউয়াস সিগন্যালের একটি আনুমানিক রূপ, নিখুঁত নয়। (একটি লো-পাস ফিল্টার প্রায়ই ZOH আউটপুটকে আরও মসৃণ করতে ব্যবহৃত হয় — এই স্মুথিং ফিল্টারের গণিত M7-M8-এ কভার হবে।)

৩ · সত্যিকারের ডেমো — সম্পূর্ণ ADC → DAC পাইপলাইন

নিচের কোড সেলে একটি ৬ Hz, ০.৮ অ্যামপ্লিটিউডের টেস্ট সিগন্যাল $f_s=32$ Hz-এ স্যাম্পল করে, L07-এর কোয়ান্টাইজার দিয়ে ৪-বিটে (১৬ লেভেল) কোয়ান্টাইজ করে ADC আউটপুট তৈরি করা হচ্ছে। তারপর DAC-এর ZOH reconstruction অনুকরণ করতে, দুটো স্যাম্পলের মাঝামাঝি সময়ে "ধরে রাখা" মানকে প্রকৃত কন্টিনিউয়াস সিগন্যালের সাথে তুলনা করা হচ্ছে।

Python
import math

def quantize(x, bits, xmin=-1.0, xmax=1.0):
    """L07-এর একই মিড-ট্রেড ইউনিফর্ম কোয়ান্টাইজার -- ADC-এর কোয়ান্টাইজেশন ধাপ"""
    levels = 2 ** bits
    step = (xmax - xmin) / levels
    idx = int((x - xmin) / step)
    idx = max(0, min(levels - 1, idx))
    xq = xmin + (idx + 0.5) * step
    return xq

fs = 32.0        # স্যাম্পল রেট -- ADC ধাপ ১
f = 6.0          # টেস্ট সিগন্যাল ফ্রিকোয়েন্সি
N = 12
bits = 4         # ADC ধাপ ২: কোয়ান্টাইজেশন রেজোলিউশন
A = 0.8

samples_true = [A * math.sin(2 * math.pi * f * n / fs) for n in range(N)]
samples_q = [quantize(x, bits) for x in samples_true]   # ADC ধাপ ২

print("--- ADC: sample + quantize ---")
print(f"{'n':>3} | {'t (s)':>7} | {'x(t)':>9} | {'ADC out':>9} | {'error':>9}")
for n in range(N):
    t = n / fs
    e = samples_true[n] - samples_q[n]
    print(f"{n:>3} | {t:>7.4f} | {samples_true[n]:>9.5f} | {samples_q[n]:>9.5f} | {e:>9.5f}")

signal_power = sum(x * x for x in samples_true) / N
noise_power = sum((samples_true[n] - samples_q[n]) ** 2 for n in range(N)) / N
snr = 10 * math.log10(signal_power / noise_power)
print(f"\nend-to-end ADC SNR = {snr:.3f} dB")

print("\n--- DAC: zero-order-hold reconstruction (মাঝামাঝি সময়ে) ---")
print(f"{'from':>5} | {'to':>3} | {'t_mid':>7} | {'true_mid':>9} | {'held(DAC)':>10} | {'error':>9}")
max_hold_err = 0.0
for n in range(N - 1):
    t_mid = (n + 0.5) / fs
    true_mid = A * math.sin(2 * math.pi * f * t_mid)
    held = samples_q[n]                       # DAC ধাপ: পরের স্যাম্পল না আসা পর্যন্ত ধরে রাখা
    e = true_mid - held
    max_hold_err = max(max_hold_err, abs(e))
    print(f"{n:>5} | {n+1:>3} | {t_mid:>7.4f} | {true_mid:>9.5f} | {held:>10.5f} | {e:>9.5f}")

print(f"\nসর্বোচ্চ hold error (মাঝামাঝি সময়ে) = {max_hold_err:.5f}")

    
দুটো ভিন্ন ধরনের এরর স্পষ্টভাবে আলাদা: ADC টেবিলে স্যাম্পল-মুহূর্তের এরর শুধুই কোয়ান্টাইজেশন থেকে আসছে (সর্বোচ্চ প্রায় $0.0625$, L07-এর মতোই ছোট), আর সামগ্রিক SNR $\approx \mathbf{23.395}$ dB। কিন্তু দ্বিতীয় টেবিলে — মাঝামাঝি সময়ে — এরর অনেক বড় (সর্বোচ্চ $\approx \mathbf{0.507}$), কারণ এটি এখন কোয়ান্টাইজেশন এরর এবং ZOH-এর "ধরে রাখা" আনুমানিকতা — দুটো মিলিয়ে। এটাই প্রমাণ করে DAC-এর আউটপুট শুধু স্যাম্পল বিন্দুতেই নির্ভরযোগ্য, তার মাঝের সময়ে নয় — বাস্তব DAC হার্ডওয়্যারে তাই একটি স্মুথিং (রিকনস্ট্রাকশন) ফিল্টার প্রায়ই যোগ করা হয়।
মূল কথা · Key takeaway

একটি সম্পূর্ণ ADC/DAC পাইপলাইনে M2-এর সবগুলো ধারণা একত্রিত হয় — নাইকুইস্ট থিওরেম (L05) বলে দেয় স্যাম্পল রেট কত হতে হবে, অ্যালিয়াসিং (L06) বলে দেয় কেন anti-aliasing ফিল্টার আগে দরকার, কোয়ান্টাইজেশন (L07) বলে দেয় বিট-গভীরতা কীভাবে নির্ভুলতা নির্ধারণ করে, আর এই পাঠ দেখাল কীভাবে এই সবকিছু ADC-তে "sample then quantize" আর DAC-তে "hold then (optionally) smooth" হিসেবে বাস্তবায়িত হয়। এখান থেকেই M3 শুরু হবে — কনভোলিউশন, যা এই ডিজিটাইজড সিগন্যালের উপর প্রথম বড় অপারেশন।

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

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

প্র ০১ উপরের ডেমোতে ADC এরর (সর্বোচ্চ ≈ ০.০৬২৫) আর DAC hold এরর (সর্বোচ্চ ≈ ০.৫০৭) — এত বড় পার্থক্য কেন?

ADC এরর শুধুমাত্র কোয়ান্টাইজেশন থেকে — একটি স্যাম্পল-মুহূর্তে প্রকৃত মান আর নিকটতম কোয়ান্টাইজেশন-লেভেলের পার্থক্য, যা সর্বোচ্চ $\Delta/2=0.0625$ (৪-বিটে) হতে পারে। কিন্তু DAC hold এরর একটি সম্পূর্ণ ভিন্ন উৎস থেকে — সিগন্যাল দুটো স্যাম্পলের মাঝে পরিবর্তিত হচ্ছে, অথচ ZOH পুরো এই সময়টায় শুধু আগের স্যাম্পলের মানই ধরে রাখছে। এই "সিগন্যাল কতটা দ্রুত পরিবর্তিত হচ্ছে" নির্ভর এররটি সাধারণত কোয়ান্টাইজেশন এররের চেয়ে অনেক বড় হয়, বিশেষত কম স্যাম্পল রেটে (এখানে $f_s=32$ Hz, সিগন্যাল ফ্রিকোয়েন্সি $6$ Hz — প্রতি চক্রে মাত্র ~৫.৩টি স্যাম্পল)।

প্র ০২ স্যাম্পল রেট $f_s$ অনেক বাড়ালে (যেমন ৩২ থেকে ৩২০ Hz) DAC hold এরর কী হবে বলে আপনার ধারণা?

অনেক কমে যাবে। উচ্চ $f_s$-এ প্রতি চক্রে অনেক বেশি স্যাম্পল পড়ে, তাই দুটো স্যাম্পলের মাঝে সিগন্যাল খুব কম পরিবর্তিত হয় — ZOH-এর "ধরে রাখা" আনুমানিকতা তখন প্রকৃত কন্টিনিউয়াস সিগন্যালের অনেক কাছাকাছি থাকে। এটাই দেখায় কেন উচ্চ স্যাম্পল রেট শুধু অ্যালিয়াসিং প্রতিরোধেই নয়, DAC reconstruction-এর মানও উন্নত করে।

প্র ০৩ একটি ADC-তে স্যাম্পল রেট বাড়ানো আর বিট-গভীরতা বাড়ানো — দুটোই "নির্ভুলতা বাড়ায়"। এই দুটো ঠিক কোন ভিন্ন ধরনের ভুল কমায়?

স্যাম্পল রেট বাড়ালে সময়-ডোমেইন নির্ভুলতা বাড়ে — সিগন্যালের দ্রুত পরিবর্তন ধরার ক্ষমতা (L05-L06-এর অ্যালিয়াসিং সমস্যা কমে, এবং এই পাঠের DAC hold এরর কমে)। বিট-গভীরতা বাড়ালে অ্যামপ্লিটিউড-ডোমেইন নির্ভুলতা বাড়ে — প্রতিটি মান কত নিখুঁতভাবে সংরক্ষিত হচ্ছে (L07-এর কোয়ান্টাইজেশন এরর কমে)। দুটো সম্পূর্ণ স্বতন্ত্র রিসোর্স — একটি বাড়ালে অন্যটির উপর সরাসরি প্রভাব পড়ে না, বরং উভয়ই আলাদাভাবে ডেটা রেট/মেমরি ব্যয় বাড়ায়।

অনুশীলন

  1. চিন্তা করুন: উপরের কোড সেলে bits = 4-কে bits = 2-এ বদলালে (লেভেল $4$টি হয়ে যাবে) সামগ্রিক ADC SNR বাড়বে না কমবে বলে আপনার ধারণা?

    কমবে — কম বিট মানে কম লেভেল, বড় স্টেপ সাইজ, বড় কোয়ান্টাইজেশন এরর, তাই নয়েজ পাওয়ার বাড়বে এবং SNR কমবে (L07-এর "বিট বাড়ালে SNR বাড়ে" সম্পর্কের ঠিক বিপরীত)।

  2. পরীক্ষা করুন: কোড সেলে bits = 4-কে bits = 2-এ পরিবর্তন করে Run চেপে প্রকৃত SNR দেখুন।

    রান করলে সামগ্রিক ADC SNR ≈ ১১.৬৭৭ dB দেখাবে — ৪-বিটের ২৩.৩৯৫ dB থেকে প্রায় অর্ধেক (দুই বিট কমায় প্রায় $2 × 6.02 \approx 12$ dB কমার প্রত্যাশার সাথে সামঞ্জস্যপূর্ণ)। এটি L07-এর বিট-বনাম-SNR সম্পর্কের সরাসরি নিশ্চিতকরণ, এবার একটি সম্পূর্ণ ADC পাইপলাইনের প্রেক্ষাপটে।

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

আগের পাঠ
কোয়ান্টাইজেশন ও কোয়ান্টাইজেশন এরর