সরাসরি পর্বে যান
পর্ব ০৯ · ৬২-এর মধ্যে · মডিউল ২

Error detection আর correction — parity, checksum, CRC আর Hamming

Error Detection & Correction — Parity, Internet Checksum, CRC and Hamming Codes

Taught in Bangla, this lesson explains why bits flip, single-bit and burst errors, redundancy, FEC versus ARQ, Hamming distance and what dmin can detect and correct with proof, simple and two-dimensional parity, the Internet checksum, CRC by modulo-2 polynomial division with a full worked example, CRC-32 and the Ethernet FCS computed in C, and Hamming(7,4) single-error correction.

এই পর্বে যা শিখবেন

  • বিট কেন উল্টে যায়, single-bit আর burst error কী, আর error detection বনাম correction (FEC বনাম ARQ)
  • Hamming distance আর dmin — কতটি ভুল ধরা ও সারানো যায়, তার প্রমাণসহ
  • Parity, দুই মাত্রার parity আর Internet checksum হাতে হিসাব করা — আর এদের দুর্বলতা
  • CRC-র modulo-2 ভাগ ধাপে ধাপে, কোন ভুল সে নিশ্চিতভাবে ধরে তার কারণ, আর Ethernet-এর CRC-32
  • Hamming(7,4) code দিয়ে encoding আর syndrome থেকে ভুল বিট খুঁজে সারানো; stack-এর কোন স্তরে কোন যাচাই থাকে

১ · বিট কেন উল্টে যায় — noise, interference আর burst

পর্ব ০৭-এ দেখেছেন, তারের ভেতর বা বাতাসে বিট যায় সংকেত হয়ে — ভোল্টেজের ওঠানামা, আলোর ঝলক বা রেডিও তরঙ্গ। প্রাপক সংকেত মেপে ঠিক করে “এটি ০ না ১”. সমস্যা হলো, সংকেত পথে একা থাকে না। তারের ইলেকট্রনের এলোমেলো নড়াচড়া থেকে আসে তাপজনিত noise; পাশের তারের সংকেত চুইয়ে আসে (crosstalk); মোটর, মাইক্রোওয়েভ ওভেন বা বজ্রপাত থেকে আসে বৈদ্যুতিক ঝাঁকুনি; বেতারে একই চ্যানেলে অন্য Wi-Fi বা দেয়ালে প্রতিফলিত তরঙ্গ। এগুলো সংকেতের সাথে মিশে গেলে প্রাপক কখনো কখনো ১-কে ০ পড়ে, ০-কে ১।

ঢাকার ব্যস্ত মোড়ে দাঁড়িয়ে ফোনে কেউ আপনাকে একটি bKash নম্বর বলছে। বাস হর্ন দিলে একটি অঙ্ক শুনলেন ভুল — “৫”-কে “৯”. এটি single-bit error. আবার পাশ দিয়ে মিছিল গেল, পুরো তিন সেকেন্ড কিছুই বোঝা গেল না — পরপর চারটি অঙ্ক নষ্ট। এটি burst error. ভালো উপায় কী? নম্বর বলার পর শেষে “সব অঙ্কের যোগফল ৪২” বলে দেওয়া — আপনি যোগ করে মিলিয়ে দেখবেন। এই বাড়তি তথ্যটুকুই এই পর্বের মূল বিষয়।

ভুলকে দুই ভাগে দেখা হয়:

  • Single-bit error: একটি ডেটা-এককে (যেমন একটি byte বা frame) শুধু একটি বিট উল্টেছে। Noise ছোট আর ক্ষণিক হলে এমন হয়।
  • Burst error: প্রথম ভুল বিট থেকে শেষ ভুল বিট পর্যন্ত দৈর্ঘ্যকে বলে burst-এর দৈর্ঘ্য। মাঝের সব বিট ভুল হতেই হবে এমন নয়। যেমন 0100 পাঠিয়ে 1101 পেলে প্রথম আর শেষ বিট ভুল, দৈর্ঘ্য ৪। দ্রুতগতির link-এ burst-ই বেশি দেখা যায়, কারণ ১ মাইক্রোসেকেন্ডের একটি ঝাঁকুনি ১ Gbit/s link-এ প্রায় ১০০০টি বিটের সময় জুড়ে থাকে।

ভুলের হারকে মাপা হয় BER (bit error rate) দিয়ে — গড়ে কত বিটে একটি ভুল। ধরুন BER = ১০−৬ আর frame ১০০০ বিটের। ভুলগুলো স্বাধীন হলে frame-টি পুরো নির্ভুল পৌঁছানোর সম্ভাবনা (১ − ১০−৬)১০০০ ≈ ০.৯৯৯০, অর্থাৎ প্রায় প্রতি হাজারে একটি frame নষ্ট। ১২,০০০ বিটের বড় Ethernet frame হলে নষ্ট হবে প্রায় শতকরা ১.২টি। ফাইবারে BER সাধারণত এর চেয়ে বহু গুণ কম, কিন্তু বেতারে অনেক বেশি হতে পারে। তাই যত ভালো link-ই হোক, প্রাপককে জানতেই হবে কোন frame বিশ্বাসযোগ্য।

২ · Redundancy — ধরা বনাম সারানো, FEC বনাম ARQ

শুধু ডেটা পাঠালে প্রাপক কোনোভাবেই বুঝতে পারবে না ভুল হয়েছে কি না — কারণ যেকোনো বিট-নকশাই বৈধ ডেটা হতে পারে। তাই প্রেরক ডেটা থেকে হিসাব করে কিছু বাড়তি বিট জুড়ে দেয়। একে বলে redundancy (এখানে “বাড়তি তথ্য” অর্থে, পর্ব ০২-এর বাড়তি যন্ত্র বা পথ নয়)। ডেটা আর বাড়তি বিট মিলে যা পাঠানো হয়, তাকে বলে codeword. প্রাপক একই নিয়মে আবার হিসাব করে; না মিললে বোঝে কোথাও গোলমাল।

এখানে দুটি আলাদা লক্ষ্য:

Error detection

ভুল ধরা — শুধু জানা যে “এই frame-এ গোলমাল আছে”। কোন বিট, তা জানা যায় না। Frame ফেলে দেওয়া হয়, আর দরকার হলে আবার চাওয়া হয়। বাড়তি বিট লাগে কম।

Ethernet CRC, TCP checksum

Error correction

ভুল সারানো — কোন বিট উল্টেছে তা খুঁজে বের করে ঠিক করে দেওয়া, আবার পাঠানোর দরকার ছাড়াই। বাড়তি বিট লাগে বেশি।

Hamming, Reed-Solomon, LDPC

ভুল ধরা পড়লে কী করা হবে, তার দুই নীতি:

  • ARQ (Automatic Repeat reQuest): প্রাপক শুধু ভুল ধরে, ভালো frame পেলে ACK পাঠায়; ACK না পেলে প্রেরক আবার পাঠায়। কম redundancy, কিন্তু প্রতিটি ভুলের দাম অন্তত একটি round-trip সময়। পর্ব ১০-এ stop-and-wait, Go-Back-N আর Selective Repeat দেখবেন; TCP-ও (পর্ব ৩০) এই নীতিতে চলে।
  • FEC (Forward Error Correction): প্রেরক আগেভাগেই এত redundancy দেয় যে প্রাপক নিজেই ভুল সারায় — ফেরত-পথ লাগে না। যেখানে আবার পাঠানো ব্যয়বহুল বা অসম্ভব — মঙ্গল গ্রহ থেকে ছবি (একমুখী যাত্রায় কয়েক মিনিট), সরাসরি টিভি সম্প্রচার, CD/DVD, ভিডিও কল — সেখানে FEC.

Redundancy-র দাম মাপা হয় code rate দিয়ে: m বিট ডেটার সাথে r বিট বাড়তি দিলে n = m + r, আর rate = m/n. Hamming(7,4)-এর rate ৪/৭ ≈ ০.৫৭; Ethernet-এর ১৫০০ বাইট payload-এ ৪ বাইট CRC — rate প্রায় ০.৯৯৭। বাস্তবে প্রায়ই দুটি একসাথে চলে: Wi-Fi বা 4G/5G-র রেডিও FEC দিয়ে বেশিরভাগ ভুল সারায়, যা বাকি থাকে তা CRC ধরে আর ARQ আবার পাঠায় (একে hybrid ARQ বলে)।

৩ · Hamming distance আর dmin — কতটা ধরা, কতটা সারানো যায় (প্রমাণসহ)

দুটি সমান দৈর্ঘ্যের বিট-নকশার Hamming distance হলো কতগুলো অবস্থানে তারা আলাদা। হিসাবের সহজ উপায়: দুটিকে XOR করে ১-এর সংখ্যা গোনা। যেমন 10001001 আর 10110001: XOR = 00111000, তিনটি ১ — তাই d = ৩। এর মানে, একটিকে আরেকটিতে বদলাতে ঠিক তিনটি বিট উল্টাতে হবে।

একটি code মানে বৈধ codeword-এর একটি তালিকা। সেই তালিকার যেকোনো দুটির মধ্যে সবচেয়ে কম দূরত্বকে বলে minimum distance, dmin. পুরো তত্ত্বটি এই এক সংখ্যায়:

ধরা যায়: সর্বোচ্চ dmin − ১টি ভুল
সারানো যায়: সর্বোচ্চ t = ⌊(dmin − ১) / ২⌋টি ভুল

প্রমাণ (detection): প্রেরক codeword c পাঠাল, পথে sটি বিট উল্টাল, যেখানে ১ ≤ s ≤ dmin − ১। প্রাপ্ত শব্দ w-এর সাথে c-র দূরত্ব s. যেকোনো অন্য বৈধ codeword c′-এর দূরত্ব c থেকে কমপক্ষে dmin, যা s-এর চেয়ে বড়। তাই w কোনো বৈধ codeword নয় — না c (কারণ s ≥ ১), না অন্য কোনো c′. প্রাপক “অবৈধ শব্দ” দেখে ভুল ধরে ফেলে। আর এর চেয়ে ভালো করা সম্ভব নয়: দূরত্ব ঠিক dmin এমন দুটি codeword আছে, তাদের একটির ঠিক সেই dminটি বিট উল্টালে আরেকটি বৈধ codeword হয়ে যায় — প্রাপক টেরই পায় না।

প্রমাণ (correction): প্রাপক নিয়ম মানে — “প্রাপ্ত শব্দের সবচেয়ে কাছের codeword-কেই আসল ধরা হবে”। ধরা যাক ২t + ১ ≤ dmin আর ভুল হয়েছে সর্বোচ্চ t টি, অর্থাৎ d(c, w) ≤ t. Hamming distance ত্রিভুজ-অসমতা মানে, তাই যেকোনো অন্য codeword c′-এর জন্য

d(c′, w) ≥ d(c′, c) − d(c, w) ≥ dmin − t ≥ ২t + ১ − t = t + ১ > t ≥ d(c, w)

অর্থাৎ c-ই একমাত্র সবচেয়ে কাছের codeword, আর প্রাপক ঠিকটিই বেছে নেয়। ছবির ভাষায়: প্রতিটি codeword ঘিরে t ব্যাসার্ধের একটি “বল” আঁকুন; ২t + ১ ≤ dmin হলে বলগুলো একে অপরকে ছোঁয় না, তাই প্রতিটি প্রাপ্ত শব্দ বড়জোর একটি বলের ভেতরে পড়ে।

Codedminধরা যায়সারানো যায়
শুধু ডেটা (কোনো বাড়তি বিট নেই)১০০
একটি parity বিট২১ (আসলে যেকোনো বিজোড় সংখ্যা)০
Hamming(7,4)৩২১
দুই মাত্রার parity, বা Hamming + একটি বাড়তি parity (SECDED)৪৩১ (সাথে ২টি ধরে)
প্রতিটি বিট তিনবার পাঠানো (0 → 000)৩২১ (সংখ্যাগরিষ্ঠের ভোটে)

একই code দিয়ে একসাথে “t টি সারানো” আর “আরও বেশি ধরা” — দুটো পুরোপুরি পাওয়া যায় না। dmin = ৩ হলে হয় ২টি পর্যন্ত ভুল ধরবেন, নয়তো ১টি সারাবেন। সারানোর নিয়ম চালু থাকলে ২টি ভুল হওয়া শব্দকে প্রাপক ভুল codeword-এ “সারিয়ে” ফেলতে পারে — অংশ ৮-এর simulator-এ ঠিক এটি দেখবেন। সাধারণ সূত্র: t টি সারিয়ে একসাথে e টি (e ≥ t) ধরতে চাইলে লাগে dmin ≥ t + e + ১।

৪ · Parity — এক বিটের পাহারা, আর দুই মাত্রার parity

সবচেয়ে সরল কৌশল: ডেটার শেষে একটি parity বিট জোড়া, যাতে মোট ১-এর সংখ্যা জোড় হয় (even parity) বা বিজোড় হয় (odd parity). ASCII-তে ‘A’ = 1000001, এতে দুটি ১ — জোড়। Even parity-তে বাড়তি বিট ০, পাঠানো হয় 10000010; odd parity-তে বাড়তি বিট ১, পাঠানো হয় 10000011. প্রাপক ১ গোনে; even parity-তে বিজোড় পেলে ভুল।

কী ধরা পড়ে না? যেকোনো একটি বিট উল্টালে ১-এর সংখ্যার জোড়-বিজোড় বদলায় — ধরা পড়ে। কিন্তু দুটি বিট উল্টালে জোড়-বিজোড় আগের মতোই থাকে — ধরা পড়ে না। সাধারণভাবে বিজোড় সংখ্যক ভুল ধরা পড়ে, জোড় সংখ্যক পড়ে না। Burst error-এ জোড় সংখ্যক বিট উল্টানোর সম্ভাবনা প্রায় অর্ধেক, তাই নেটওয়ার্কে একা parity প্রায় অচল। পুরোনো serial লাইন আর memory-তে একসময় এর ব্যবহার ছিল ব্যাপক।

দুই মাত্রার parity — কোন বিট, তাও বলা যায়

ডেটাকে সারি আর কলামের একটি ছকে সাজিয়ে প্রতিটি সারির শেষে আর প্রতিটি কলামের নিচে একটি করে parity বিট দিলে পাওয়া যায় two-dimensional parity. একটি বিট উল্টালে তার সারি আর তার কলাম — দুটির parity-ই ভুল দেখাবে, আর দুটির ছেদবিন্দুই সেই বিট। প্রাপক বিটটি উল্টে দিলেই ভুল সারানো হয়ে গেল। নিচে ৩ × ৪ = ১২ বিট ডেটা নিয়ে ধাপে ধাপে দেখুন:

প্রেরক১২ বিট ডেটা, ৩ সারি × ৪ কলাম
        c1 c2 c3 c4
row1     1  0  1  1
row2     0  1  1  0
row3     1  1  0  0
প্রেরকপ্রতিটি সারির parity (even)
        c1 c2 c3 c4 │ P
row1     1  0  1  1 │ 1   three 1s -> 1
row2     0  1  1  0 │ 0   two 1s   -> 0
row3     1  1  0  0 │ 0   two 1s   -> 0
প্রেরক → তারকলামের parity আর কোণের বিট
        c1 c2 c3 c4 │ P
row1     1  0  1  1 │ 1
row2     0  1  1  0 │ 0
row3     1  1  0  0 │ 0
────────────────────┼──
P        0  0  0  1 │ 1
প্রাপকপথে row2, c3-এর বিট উল্টে গেছে
        c1 c2 c3 c4 │ P
row1     1  0  1  1 │ 1
row2     0  1  0  0 │ 0
row3     1  1  0  0 │ 0
────────────────────┼──
P        0  0  0  1 │ 1
প্রাপকসারিগুলো আবার যাচাই
        c1 c2 c3 c4 │ P   recount
row1     1  0  1  1 │ 1   1  ok
row2     0  1  0  0 │ 0   1  FAIL
row3     1  1  0  0 │ 0   0  ok
প্রাপককলাম যাচাই, ছেদবিন্দুতে বিট উল্টানো
        c1 c2 c3 c4 │ P
row1     1  0  1  1 │ 1
row2     0  1  1  0 │ 0   <- row 2 failed
row3     1  1  0  0 │ 0
────────────────────┼──
P        0  0  0  1 │ 1
recount  0  0  1  1     <- c3 failed
fix: flip (row2, c3) 0 -> 1
সীমাএকই সারিতে দুটি ভুল হলে
        c1 c2 c3 c4 │ P   recount
row1     1  0  1  1 │ 1   1  ok
row2     1  1  0  0 │ 0   0  ok  <- row looks fine!
row3     1  1  0  0 │ 0   0  ok
P        0  0  0  1
recount  1  0  1  1     <- c1, c3 fail: detected, not fixable
  1. ডেটা ছকে সাজানো। ১২টি ডেটা-বিট ৩টি সারি আর ৪টি কলামে। এখনো কোনো পাহারা নেই — একটি বিট উল্টালে প্রাপক টেরই পাবে না।
  2. সারির parity. প্রতিটি সারির ১ গুনে even parity: row1-এ তিনটি ১, তাই parity ১; row2 আর row3-এ দুটি করে ১, তাই ০। এটুকু থাকলে কোন সারিতে ভুল, তা জানা যাবে।
  3. কলামের parity. c1 = ১ + ০ + ১ → ০, c2 = ০ + ১ + ১ → ০, c3 = ১ + ১ + ০ → ০, c4 = ১ + ০ + ০ → ১। কোণের বিট = সারি-parity-গুলোর parity (১ + ০ + ০ → ১), যা কলাম-parity-গুলোর parity-র সাথেও মেলে।
  4. পাঠানো হলো ২০ বিট। ১২ ডেটা + ৩ সারি-parity + ৪ কলাম-parity + ১ কোণ = ২০ বিট। Code rate ১২/২০ = ০.৬ — সস্তা নয়, কিন্তু এর dmin ৪: এক, দুই বা তিনটি ভুল সবসময় ধরা পড়ে।
  5. পথে একটি বিট উল্টাল। row2-এর c3-এর ১ হয়ে গেল ০। প্রাপক এখনো জানে না কোনটি; সে শুধু নিয়ম মেনে আবার গুনবে।
  6. সারি যাচাই। row2-এ এখন একটি মাত্র ১ (বিজোড়), অথচ পাঠানো parity ০ — row2 ব্যর্থ। বাকি দুটি সারি ঠিক। তাহলে ভুলটি row2-এর কোথাও।
  7. কলাম যাচাই ও সারানো। c3-এ এখন ১ + ০ + ০ = বিজোড়, অথচ parity ০ — c3 ব্যর্থ। ছেদবিন্দু (row2, c3) — সেই বিট ০ থেকে আবার ১ করে দিলে সব সারি আর কলাম মিলে যায়। ভুল সারানো হলো, আবার পাঠাতে হলো না।
  8. সীমা। একই সারিতে দুটি ভুল (c1 আর c3) হলে সারির parity মিলে যায়, কিন্তু দুটি কলাম ব্যর্থ — ভুল ধরা পড়ে, তবে কোন সারিতে তা জানা যায় না, তাই সারানো যায় না। আর একটি আয়তক্ষেত্রের চার কোণে চারটি ভুল হলে সব সারি আর কলামই মিলে যায় — ধরা পড়ে না।

৫ · Internet checksum — যোগফল দিয়ে যাচাই

IP, TCP আর UDP যে checksum ব্যবহার করে, তার নিয়ম RFC 1071-এ ব্যাখ্যা করা আছে। ধারণাটি সেই bKash নম্বরের যোগফলের মতো, কিন্তু ১৬-বিট সংখ্যায়, আর একটি বিশেষ যোগে — one's complement যোগ:

  1. ডেটাকে ১৬-বিটের word-এ ভাগ করুন (বাইট বিজোড় হলে শেষে একটি শূন্য বাইট ধরে নিন)।
  2. সব word যোগ করুন। যোগফল ১৬ বিট ছাড়িয়ে গেলে বাড়তি carry-টি কেটে এনে নিচের দিকে আবার যোগ করুন — একে বলে end-around carry.
  3. শেষ যোগফলের সব বিট উল্টে দিন (one's complement). এটিই checksum, যা header-এর নির্দিষ্ট ঘরে বসে।
  4. প্রাপক checksum-সহ সব word একইভাবে যোগ করে। ভুল না থাকলে যোগফল হবে 0xFFFF (সব ১), অর্থাৎ তার complement ০।

পূর্ণ সমাধান করা উদাহরণ

প্রশ্ন: তিনটি ১৬-বিট word 0x1234, 0xF00F, 0xABCD-এর Internet checksum কত? প্রাপক কীভাবে যাচাই করবে?

  0x1234      0001 0010 0011 0100
+ 0xF00F    + 1111 0000 0000 1111
= 0x10243   1 0000 0010 0100 0011   (17 bits: carry out)
  wrap:       0000 0010 0100 0011 + 1 = 0x0244
+ 0xABCD    + 1010 1011 1100 1101
= 0xAE11      1010 1110 0001 0001   (no carry)
checksum = ~0xAE11 = 0101 0001 1110 1110 = 0x51EE

receiver: 0x1234 + 0xF00F + 0xABCD + 0x51EE
        = 0xAE11 + 0x51EE = 0xFFFF  ->  ~0xFFFF = 0x0000  OK

প্রথম যোগে ১৭তম বিটে একটি carry বেরিয়েছে; সেটি ফেলে না দিয়ে নিচে এনে যোগ করায় 0x0243 হলো 0x0244. শেষ যোগফল 0xAE11, তার সব বিট উল্টে checksum 0x51EE. প্রাপক চারটি word যোগ করে 0xFFFF পায় — সব ঠিক। পর্ব ০৩-এ (অংশ ৭) একটি আসল IPv4 header-এর checksum 0x9b1a এই নিয়মেই হিসাব করা হয়েছিল; অংশ ৭-এর C প্রোগ্রাম একই মান আবার বের করবে।

কেন এটি দুর্বল, তবু কেন টিকে আছে

  • ক্রম বদল ধরা পড়ে না: যোগের ক্রম বদলালে যোগফল বদলায় না। দুটি ১৬-বিট word-এর জায়গা অদলবদল হলে checksum একই থাকে।
  • পরস্পর কাটাকাটি: এক word-এর কোনো বিট ০ → ১ আর অন্য word-এর একই অবস্থানের বিট ১ → ০ হলে যোগফল অপরিবর্তিত।
  • মাত্র ১৬ বিট: এলোমেলো বড় গোলমালে মোটামুটি ৬৫,৫৩৬-এ ১ সম্ভাবনায় ভুল ধরা পড়ে না। আবার one's complement-এ 0x0000 আর 0xFFFF দুটিই “শূন্য” — এই দুইয়ের মধ্যে বদলও চোখ এড়ায়।

তবু ইন্টারনেট এটি রেখেছে কারণ এটি software-এ খুব দ্রুত (শুধু যোগ), byte order-নির্ভর নয় (RFC 1071 দেখায় যে big-endian বা little-endian মেশিনে একইভাবে যোগ করা যায়), আর ছোট পরিবর্তনের পর পুরোটা আবার না যোগ করে শুধু পার্থক্য দিয়ে হালনাগাদ করা যায় (RFC 1624) — router প্রতিটি packet-এ TTL কমিয়ে ঠিক এটাই করে। আসল পাহারা link স্তরের CRC-র কাছে; checksum দ্বিতীয় স্তরের নিরাপত্তা জাল।

৬ · CRC — বহুপদী আর modulo-2 ভাগ

CRC (cyclic redundancy check) যোগ নয়, ভাগ ব্যবহার করে। ভাবুন, প্রেরক আর প্রাপক আগে থেকে একটি “ভাজক” ঠিক করে রেখেছে। প্রেরক ডেটা এমনভাবে সাজায় যে পাঠানো পুরো বিট-নকশাটি ভাজক দিয়ে নিঃশেষে বিভাজ্য হয়। প্রাপক ভাগ করে দেখে — ভাগশেষ ০ হলে ঠিক, না হলে ভুল। বিভাজ্যতার পরীক্ষা যোগের চেয়ে অনেক কঠিন শর্ত, তাই ভুল লুকানো অনেক কঠিন।

বহুপদী দৃষ্টিভঙ্গি

একটি বিট-নকশাকে পড়া হয় এমন একটি বহুপদী (polynomial) হিসেবে, যার সহগ শুধু ০ বা ১। 10011 মানে ১·x৪ + ০·x৩ + ০·x২ + ১·x + ১ = x৪ + x + ১। আর হিসাব হয় modulo-2 arithmetic-এ: ১ + ১ = ০, কোনো carry নেই, কোনো borrow নেই। ফলে যোগ আর বিয়োগ একই জিনিস — দুটোই বিট-বিট XOR.

প্রেরক আর প্রাপক একটি generator polynomial G(x) ঠিক করে, যার degree r (অর্থাৎ বিট-নকশায় r + ১টি বিট)। ডেটা M(x) হলে নিয়ম:

  1. ডেটার শেষে r টি ০ জুড়ুন — অর্থাৎ xr·M(x).
  2. একে G(x) দিয়ে modulo-2 ভাগ করুন; ভাগশেষ R(x)-এ r টি বিট। এটিই CRC.
  3. জোড়া ০-গুলোর জায়গায় R বসিয়ে পাঠান: T(x) = xr·M(x) + R(x).

কেন T(x) নিঃশেষে বিভাজ্য? xr·M(x) = Q(x)·G(x) + R(x), তাই T(x) = Q(x)·G(x) + R(x) + R(x) = Q(x)·G(x), কারণ modulo-2-তে R + R = ০। প্রাপক পুরো frame ভাগ করে ভাগশেষ ০ পেলে মেনে নেয়।

এবার পরীক্ষার সবচেয়ে চেনা উদাহরণ — ডেটা 1101011011, generator 10011 (x৪ + x + ১, r = ৪):

প্রস্তুতিr = 4, তাই ৪টি শূন্য জোড়া
G(x) = x^4 + x + 1  ->  10011   (r = 4)
M    = 1101011011
x^4 M = 11010110110000
ধাপ: প্রথম XORসামনের বিট 1, তাই G দিয়ে XOR
        1
10011 ) 11010110110000
        10011
        -----
        01001
ধাপ: দ্বিতীয় XORএকটি বিট নামিয়ে আবার
        11
10011 ) 11010110110000
        10011
        -----
         10011   1001 + next bit 1
         10011
         -----
         00000
ধাপ: শূন্যগুলো পার, তৃতীয় XORসামনের বিট 0 হলে ভাগফলে 0
        1100001
10011 ) 11010110110000
        10011
        -----
         10011
         10011
         -----
         00000
              10110   quotient gets 0000
              10011
              -----
              00101
ধাপ: শেষ XORভাগশেষে ৪টি বিট
        1100001010
10011 ) 11010110110000
        10011
        -----
         10011
         10011
         -----
         00000
              10110
              10011
              -----
              00101
                10100   quotient gets 0
                10011
                -----
                00111
                  1110   remainder
প্রেরক → তারশূন্যের জায়গায় CRC
data  1101011011
CRC             1110
sent  11010110111110   (14 bits)
প্রাপকপুরো frame ÷ G
11010110111110 ÷ 10011
remainder = 0000  ->  accept
প্রাপকপথে ৪ নম্বর বিট উল্টে গেলে
sent      11010110111110
received  11000110111110
received ÷ 10011 -> remainder = 0111  ->  reject
হিসাবের খাতা
ভাগফল (এ পর্যন্ত)
এই ধাপের XOR
ভাগশেষ
  1. প্রস্তুতি। Generator 10011-এ ৫টি বিট, তাই degree r = ৪ — CRC হবে ৪ বিটের। ডেটার শেষে ৪টি ০ জুড়ে ভাজ্য হলো ১৪ বিটের 11010110110000.
  2. প্রথম XOR. ভাজ্যের প্রথম ৫টি বিট 11010, সামনের বিট ১ — তাই ভাগফলে ১ লিখে G-এর সাথে XOR: 11010 ⊕ 10011 = 01001. সাধারণ ভাগের মতো “বড় না ছোট” তুলনা নেই; সামনের বিট ১ হলেই G বিয়োগ (XOR) হয়।
  3. দ্বিতীয় XOR. সামনের ০ বাদ দিয়ে 1001, তার সাথে ভাজ্যের পরের বিট (১) নামিয়ে 10011. সামনে ১, তাই ভাগফলে আবার ১ আর XOR: ফল 00000. ভাগফল এখন 11.
  4. শূন্যগুলো পার, তৃতীয় XOR. পরের চারটি অবস্থানে সামনের বিট ০ — প্রতিবার ভাগফলে ০ লিখে একটি বিট নামানো হয় (০০০০০ দিয়ে XOR করার সমান)। পঞ্চম অবস্থানে পাওয়া গেল 10110, সামনে ১ — ভাগফলে ১, XOR করে 00101. ভাগফল 1100001.
  5. শেষ XOR আর ভাগশেষ। পরের অবস্থানে সামনে ০ (ভাগফলে ০), তারপর 10100 ⊕ 10011 = 00111 (ভাগফলে ১), শেষ অবস্থানে আবার ০। ডেটার ১০টি বিট শেষ, ভাগফল 1100001010, আর পড়ে থাকা শেষ ৪টি বিট — ভাগশেষ 1110. এটিই CRC.
  6. পাঠানো frame. জোড়া ০০০০-র জায়গায় ভাগশেষ বসে: 11010110111110, ১৪ বিট। ১০ বিট ডেটার জন্য ৪ বিট পাহারা।
  7. প্রাপকের যাচাই। প্রাপক পুরো ১৪ বিটকে একই 10011 দিয়ে ভাগ করে — এবার কোনো ০ জোড়ে না। ভাগশেষ 0000, তাই frame গৃহীত। (প্রমাণ: T = Q·G, তাই ভাগশেষ ০ হতেই হবে।)
  8. ভুল হলে। পথে ৪ নম্বর বিট ১ থেকে ০ হলো: প্রাপ্ত 11000110111110. ভাগশেষ 0111 — শূন্য নয়, তাই frame বাতিল। Ethernet-এ এমন frame নীরবে ফেলে দেওয়া হয়; দরকার হলে ওপরের স্তর (যেমন TCP) আবার পাঠায়।

হাতের ভাগ যন্ত্রে মিলিয়ে নিন। নিচের simulator প্রতিটি অবস্থানে কোন ৫ বিটের সাথে কী XOR হলো তা ছাপে, তারপর প্রাপকের দিক থেকে ঠিক আর ভুল frame যাচাই করে। data আর gen বদলে নিজের প্রশ্নও চালাতে পারেন — Run চাপুন:

// CRC by modulo-2 long division: every XOR step is printed.
function crc(data, gen) {
  const r = gen.length - 1;                      // degree of G(x)
  const bits = (data + "0".repeat(r)).split(""); // append r zeros
  let quotient = "";
  for (let i = 0; i < data.length; i++) {
    const lead = bits[i];
    const divisor = lead === "1" ? gen : "0".repeat(gen.length);
    const win = bits.slice(i, i + gen.length).join("");
    for (let j = 0; j < gen.length; j++)       // XOR = subtraction mod 2
      bits[i + j] = bits[i + j] === divisor[j] ? "0" : "1";
    quotient += lead;
    console.log(`step ${String(i + 1).padStart(2)}: ${win} xor ${divisor} = ` +
                bits.slice(i, i + gen.length).join(""));
  }
  return { quotient, rem: bits.slice(-r).join("") };
}
function remainderOf(frame, gen) {             // receiver: divide whole frame
  const b = frame.split("");
  for (let i = 0; i + gen.length <= b.length; i++)
    if (b[i] === "1") for (let j = 0; j < gen.length; j++) b[i + j] = b[i + j] === gen[j] ? "0" : "1";
  return b.slice(-(gen.length - 1)).join("");
}
const data = "1101011011", gen = "10011";        // G(x) = x^4 + x + 1
const { quotient, rem } = crc(data, gen);
console.log(`quotient = ${quotient}, CRC (remainder) = ${rem}`);
const frame = data + rem;
console.log(`sent frame     = ${frame}  check -> ${remainderOf(frame, gen)}`);
const bad = frame.slice(0, 3) + (frame[3] === "1" ? "0" : "1") + frame.slice(4);
console.log(`bit 4 flipped  = ${bad}  check -> ${remainderOf(bad, gen)}`);

আউটপুট (নির্ধারিত — প্রতিবার একই):

step  1: 11010 xor 10011 = 01001
step  2: 10011 xor 10011 = 00000
step  3: 00001 xor 00000 = 00001
step  4: 00010 xor 00000 = 00010
step  5: 00101 xor 00000 = 00101
step  6: 01011 xor 00000 = 01011
step  7: 10110 xor 10011 = 00101
step  8: 01010 xor 00000 = 01010
step  9: 10100 xor 10011 = 00111
step 10: 01110 xor 00000 = 01110
quotient = 1100001010, CRC (remainder) = 1110
sent frame     = 11010110111110  check -> 0000
bit 4 flipped  = 11000110111110  check -> 0111

দশটি ধাপ — ডেটার প্রতিটি বিটের জন্য একটি। যেখানে সামনের বিট ১ (ধাপ ১, ২, ৭, ৯), সেখানে 10011 দিয়ে XOR; বাকি ছয়টিতে 00000 দিয়ে, যা আসলে “কিছু না করে একটি বিট নামানো”। Stepper-এর চারটি XOR ঠিক এই চারটি লাইন। ভাগফল 1100001010 আর ভাগশেষ 1110 হাতের হিসাবের সাথে মেলে; প্রাপকের দিকে ঠিক frame-এ ভাগশেষ 0000, আর একটি বিট উল্টালে 0111. বাস্তবে hardware এই কাজটি করে একটি shift register আর কয়েকটি XOR gate দিয়ে, প্রতি clock-এ একটি বিট — তাই CRC হার্ডওয়্যারে প্রায় বিনামূল্যে।

CRC কোন ভুল নিশ্চিতভাবে ধরে — আর কেন

পথে ভুল হলে প্রাপক পায় T(x) + E(x), যেখানে E(x)-এর প্রতিটি ১ মানে একটি উল্টে যাওয়া বিট। T নিঃশেষে বিভাজ্য, তাই প্রাপকের ভাগশেষ = E(x)-এর ভাগশেষ। ভুল ধরা পড়ে না ঠিক তখনই, যখন G(x) দিয়ে E(x) নিঃশেষে বিভাজ্য। এই এক বাক্য থেকে সব ধর্ম বেরিয়ে আসে:

  • সব single-bit error: E(x) = xi. G-তে দুই বা ততোধিক পদ থাকলে (যেমন xr আর ১) G কখনো xi-কে ভাগ করতে পারে না।
  • r বা তার কম দৈর্ঘ্যের সব burst: দৈর্ঘ্য b ≤ r হলে E(x) = xi·B(x), যেখানে B-র degree b − ১ < r আর B-র শেষ পদ ১। G-তে ধ্রুবক পদ ১ থাকায় G-র সাথে x-এর কোনো সাধারণ উৎপাদক নেই; তাই G | E হলে G | B হতে হতো — কিন্তু ছোট degree-র শূন্য-নয় এমন বহুপদীকে বড় degree-র G ভাগ করতে পারে না।
  • সব বিজোড় সংখ্যক ভুল, যদি (x + ১) দিয়ে G বিভাজ্য হয়: বিজোড় সংখ্যক পদ থাকলে E(১) = ১ (modulo ২)। G | E হলে E = G·Q, আর G(১) = ০ হওয়ায় E(১) = ০ হতো — বিরোধ। তাই এমন E ধরা পড়বেই।
  • লম্বা burst: দৈর্ঘ্য r + ১ হলে না-ধরা পড়ার সম্ভাবনা ১/২r−১, আরও লম্বা হলে ১/২r (ভুলের নকশা এলোমেলো ধরে)। r = ৩২ হলে প্রায় ৪৩০ কোটিতে ১।

আমাদের 10011-এর ক্ষেত্রে G(১) = ১ + ১ + ১ = ১ (modulo ২), অর্থাৎ (x + ১) দিয়ে বিভাজ্য নয় — তাই এই G সব বিজোড় ভুলের নিশ্চয়তা দেয় না, তবে সব single-bit আর ৪ পর্যন্ত দৈর্ঘ্যের সব burst ধরে। কোন G বেছে নেওয়া হবে, সেটিই CRC নকশার আসল শিল্প; ভাবনার প্রশ্ন ৪-এ দেখবেন।

৭ · CRC-32, Ethernet-এর FCS আর C-তে হিসাব

Ethernet (IEEE 802.3) frame-এর শেষের ৪ বাইটের FCS হলো CRC-32 — generator-এর degree ৩২, hex-এ 0x04C11DB7 (x৩২-এর পদটি লেখা হয় না)। হিসাব হয় destination MAC থেকে payload-এর শেষ পর্যন্ত; preamble ধরা হয় না। বাস্তব CRC-32-এ সরল ভাগের সাথে আরও তিনটি নিয়ম যুক্ত: শুরুর মান সব ১ (0xFFFFFFFF, যাতে সামনে কয়েকটি বাড়তি ০ যোগ হলেও ধরা পড়ে), প্রতিটি বাইটের বিট উল্টো ক্রমে (“reflected”, কারণ Ethernet প্রতিটি বাইটের কম মানের বিট আগে পাঠায়), আর শেষে ফলকে আবার সব বিট উল্টানো। এত নিয়মের কারণে একই নামের CRC-ও ভিন্ন ফল দিতে পারে — তাই মানদণ্ড হিসেবে একটি test vector ব্যবহার হয়: ASCII "123456789"-এর CRC-32 হতে হবে 0xCBF43926.

নিচের C প্রোগ্রাম দুটি কাজ করে: পর্ব ০৩-এর IPv4 header-এর Internet checksum হিসাব ও যাচাই (আর দুটি word অদলবদল করে checksum-কে বোকা বানানো), তারপর bit-by-bit CRC-32 দিয়ে test vector মেলানো:

#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
/* Internet checksum (RFC 1071): one's complement of the one's complement sum */
static uint16_t inet_csum(const uint8_t *b, size_t n) {
    uint32_t s = 0;
    for (size_t k = 0; k + 1 < n; k += 2) s += (uint32_t)(b[k] << 8 | b[k + 1]);
    if (n & 1) s += (uint32_t)b[n - 1] << 8;          /* odd length: pad with 0 */
    while (s >> 16) s = (s & 0xffff) + (s >> 16);     /* end-around carry */
    return (uint16_t)~s;
}
/* CRC-32 as in Ethernet: reflected poly 0xEDB88320, init and final XOR 0xFFFFFFFF */
static uint32_t crc32(const uint8_t *b, size_t n) {
    uint32_t c = 0xFFFFFFFFu;
    for (size_t k = 0; k < n; k++) {
        c ^= b[k];
        for (int i = 0; i < 8; i++) c = (c >> 1) ^ (0xEDB88320u & (0u - (c & 1u)));
    }
    return c ^ 0xFFFFFFFFu;
}
int main(void) {
    uint8_t ip[20] = { 0x45,0x00,0x00,0x1e,0x1c,0x46,0x40,0x00,0x40,0x11,
                       0x00,0x00,0xc0,0xa8,0x01,0x0a,0xc0,0xa8,0x01,0x14 };
    uint16_t c = inet_csum(ip, sizeof ip);
    printf("IPv4 header checksum      = 0x%04x\n", c);
    ip[10] = (uint8_t)(c >> 8); ip[11] = (uint8_t)c;     /* store it */
    printf("receiver: csum of header  = 0x%04x (0 = OK)\n", inet_csum(ip, sizeof ip));
    for (int k = 2; k < 4; k++) { uint8_t t = ip[k]; ip[k] = ip[k + 2]; ip[k + 2] = t; }
    printf("after swapping two words  = 0x%04x (error NOT detected)\n", inet_csum(ip, sizeof ip));
    const uint8_t msg[] = "123456789";
    printf("CRC-32(\"123456789\")       = 0x%08x\n", crc32(msg, 9));
    printf("CRC-32(\"123456788\")       = 0x%08x\n", crc32((const uint8_t *)"123456788", 9));
    return 0;
}

আউটপুট:

IPv4 header checksum      = 0x9b1a
receiver: csum of header  = 0x0000 (0 = OK)
after swapping two words  = 0x0000 (error NOT detected)
CRC-32("123456789")       = 0xcbf43926
CRC-32("123456788")       = 0xbcf309b0

প্রথম লাইনে checksum 0x9b1a — পর্ব ০৩-এর হাতে করা হিসাবের সাথে হুবহু মেলে। প্রাপকের দিকে checksum-সহ পুরো header-এর ওপর একই ফাংশন চালালে ফল 0x0000 (যোগফল 0xFFFF-এর complement) — ঠিক আছে। এরপর total length (0x001e) আর identification (0x1c46) word দুটির জায়গা বদলানো হলো — header এখন অর্থহীন (৭,২৩৮ বাইটের packet!), অথচ checksum এখনো 0x0000 দেখাচ্ছে: ভুল ধরা পড়েনি। শেষ দুই লাইনে CRC-32 test vector 0xcbf43926 মিলেছে, তাই implementation ঠিক; আর শেষ অক্ষর ‘9’ থেকে ‘8’ করলে (মাত্র একটি বিটের পার্থক্য) CRC পুরো বদলে 0xbcf309b0. ভেতরের লুপের 0u - (c & 1u) কৌশলটি শেষ বিট ১ হলে সব-১ mask, ০ হলে ০ বানায় — “সামনের বিট ১ হলে G দিয়ে XOR” নিয়মটিই, শুধু reflected দিকে। বাস্তব লাইব্রেরি (যেমন zlib) ২৫৬ ঘরের টেবিল বা CPU-র বিশেষ instruction ব্যবহার করে একসাথে পুরো বাইট বা তার বেশি সামলায়।

৮ · Hamming code (7,4) — নিজে নিজে ভুল সারানো

Richard Hamming ১৯৫০ সালে Bell System Technical Journal-এ একটি চমৎকার নকশা প্রকাশ করেন: ৪টি ডেটা-বিটের সাথে ৩টি parity বিট, মোট ৭ বিট, যা যেকোনো একটি বিটের ভুল খুঁজে ঠিক করে দেয়। কৌশল হলো অবস্থান নম্বর ১ থেকে ৭ ধরে parity বিটগুলোকে বসানো ২-এর ঘাতের অবস্থানে — ১, ২, ৪ — আর ডেটা বসানো বাকি ৩, ৫, ৬, ৭-এ। প্রতিটি parity বিট পাহারা দেয় সেই অবস্থানগুলো, যাদের নম্বরের বাইনারিতে সংশ্লিষ্ট বিটটি ১:

Parity বিটনম্বরের কোন বিটযে অবস্থানগুলো পাহারা দেয়Even parity নিয়ম
p1 (অবস্থান ১)বিট ০ (…১)১, ৩, ৫, ৭p1 = d3 ⊕ d5 ⊕ d7
p2 (অবস্থান ২)বিট ১ (…১০)২, ৩, ৬, ৭p2 = d3 ⊕ d6 ⊕ d7
p4 (অবস্থান ৪)বিট ২ (১০০)৪, ৫, ৬, ৭p4 = d5 ⊕ d6 ⊕ d7

প্রাপক তিনটি পরীক্ষা আবার করে: প্রতিটি দলের সব বিট XOR করে পায় s1, s2, s4. এই তিন বিট মিলে syndrome (s4 s2 s1). কোনো ভুল না থাকলে syndrome ০০০। অবস্থান k-এর বিট উল্টালে ঠিক সেই পরীক্ষাগুলো ব্যর্থ হয়, যাদের দলে k আছে — অর্থাৎ k-এর বাইনারির ১-গুলো। তাই syndrome-এর বাইনারি মানই ভুল বিটের অবস্থান! ধাপে ধাপে দেখুন:

৭ বিটের codewordp = parity, d = ডেটা
position :  1   2   3   4   5   6   7
role     :  p1  p2  d3  p4  d5  d6  d7
অবস্থান ১…৭-এর বিট আর হিসাব
বিট (১ → ৭)
এই ধাপের হিসাব
Syndrome (s4 s2 s1)
অবস্থা
  1. ডেটা বসানো। ডেটা 1011-এর চারটি বিট যায় অবস্থান ৩, ৫, ৬, ৭-এ: d3 = ১, d5 = ০, d6 = ১, d7 = ১। অবস্থান ১, ২, ৪ এখনো ফাঁকা — সেখানে parity বসবে।
  2. p1 হিসাব। p1 পাহারা দেয় ১, ৩, ৫, ৭। Even parity চাই, তাই p1 = d3 ⊕ d5 ⊕ d7 = ১ ⊕ ০ ⊕ ১ = ০.
  3. p2 হিসাব। p2 পাহারা দেয় ২, ৩, ৬, ৭: p2 = d3 ⊕ d6 ⊕ d7 = ১ ⊕ ১ ⊕ ১ = ১.
  4. p4 হিসাব — codeword তৈরি। p4 পাহারা দেয় ৪, ৫, ৬, ৭: p4 = ০ ⊕ ১ ⊕ ১ = ০. পাঠানো codeword 0110011.
  5. পথে ভুল। অবস্থান ৬-এর বিট ১ থেকে ০ হয়ে গেল। প্রাপক পেল 0110001 — সে জানে না কোথায় গোলমাল।
  6. প্রথম পরীক্ষা s1. অবস্থান ১, ৩, ৫, ৭-এর বিট: ০ ⊕ ১ ⊕ ০ ⊕ ১ = ০ — এই দল ঠিক আছে। মানে ভুল বিটের অবস্থান-নম্বরের শেষ বিট ০ (অর্থাৎ নম্বরটি জোড়)।
  7. দ্বিতীয় পরীক্ষা s2. অবস্থান ২, ৩, ৬, ৭: ১ ⊕ ১ ⊕ ০ ⊕ ১ = ১ — এই দল ব্যর্থ। ভুলটি ২, ৩, ৬ বা ৭-এর কোনো একটিতে।
  8. তৃতীয় পরীক্ষা s4. অবস্থান ৪, ৫, ৬, ৭: ০ ⊕ ০ ⊕ ০ ⊕ ১ = ১ — ব্যর্থ। Syndrome s4 s2 s1 = 110 = ৬. তিন দলের তথ্য মিলিয়ে একটিমাত্র অবস্থান বাকি থাকে: ৬।
  9. সারানো। অবস্থান ৬-এর বিট উল্টে দেওয়া হলো, codeword আবার 0110011, syndrome ০০০। অবস্থান ৩, ৫, ৬, ৭ থেকে ডেটা 1011 — যা পাঠানো হয়েছিল, ঠিক তাই। আবার পাঠানোর অনুরোধ লাগেনি।

একটি সুন্দর সংক্ষেপ: syndrome = যেসব অবস্থানে ১ আছে, তাদের নম্বরগুলোর XOR. বৈধ codeword 0110011-এ ১ আছে ২, ৩, ৬, ৭-এ, আর ২ ⊕ ৩ ⊕ ৬ ⊕ ৭ = ০। কোনো একটি অবস্থান k উল্টালে ফল হয় ঠিক k. নিচের simulator এই কৌশলেই কাজ করে, আর কয়েকটি ভুলের নকশা চালিয়ে দেখায় — দুটি ভুলে কী হয়, সেটিও:

// Hamming(7,4): parity bits at positions 1, 2, 4 (even parity).
function encode(d) {                         // d = "d3 d5 d6 d7", e.g. "1011"
  const c = [0, 0, 0, +d[0], 0, +d[1], +d[2], +d[3]]; // index 1..7 used
  for (const p of [1, 2, 4]) {               // p covers positions whose number has bit p
    let x = 0;
    for (let k = 1; k <= 7; k++) if (k & p && k !== p) x ^= c[k];
    c[p] = x;
  }
  return c.slice(1).join("");
}
function syndrome(w) {                       // XOR of positions holding a 1
  let s = 0;
  for (let k = 1; k <= 7; k++) if (w[k - 1] === "1") s ^= k;
  return s;                                  // 0 = no error, else = error position
}
const flip = (w, pos) => w.slice(0, pos - 1) + (w[pos - 1] === "1" ? "0" : "1") + w.slice(pos);
const dataOf = (w) => w[2] + w[4] + w[5] + w[6];

const sent = encode("1011");
console.log(`data 1011 -> codeword ${sent} (positions 1..7)`);
for (const errs of [[], [6], [1], [2, 6]]) {
  let recv = sent;
  for (const p of errs) recv = flip(recv, p);
  const s = syndrome(recv);
  const fixed = s ? flip(recv, s) : recv;
  console.log(`errors at [${errs}] recv ${recv} syndrome ${s.toString(2).padStart(3, "0")}` +
              ` -> fixed ${fixed} data ${dataOf(fixed)}`);
}
for (const m of [4, 8, 11, 32, 64]) {        // smallest r with 2^r >= m + r + 1
  let r = 1;
  while (2 ** r < m + r + 1) r++;
  console.log(`m=${m} data bits need r=${r} check bits -> (${m + r},${m}) code`);
}

আউটপুট:

data 1011 -> codeword 0110011 (positions 1..7)
errors at [] recv 0110011 syndrome 000 -> fixed 0110011 data 1011
errors at [6] recv 0110001 syndrome 110 -> fixed 0110011 data 1011
errors at [1] recv 1110011 syndrome 001 -> fixed 0110011 data 1011
errors at [2,6] recv 0010001 syndrome 100 -> fixed 0011001 data 1001
m=4 data bits need r=3 check bits -> (7,4) code
m=8 data bits need r=4 check bits -> (12,8) code
m=11 data bits need r=4 check bits -> (15,11) code
m=32 data bits need r=6 check bits -> (38,32) code
m=64 data bits need r=7 check bits -> (71,64) code

ভুল নেই → syndrome 000; অবস্থান ৬-এ ভুল → 110 = ৬, stepper-এর মতোই সারানো; parity বিট নিজে (অবস্থান ১) উল্টালেও syndrome 001 তাকেই দেখায় — parity বিটও সুরক্ষিত। কিন্তু চতুর্থ লাইনটি সতর্কবার্তা: অবস্থান ২ আর ৬-এ দুটি ভুল হলে syndrome ২ ⊕ ৬ = ৪ — প্রাপক নির্দোষ অবস্থান ৪ “সারিয়ে” ভুল ডেটা 1001 নিয়ে খুশি থাকে। dmin = ৩ হওয়ায় এক সাথে একটি সারানো আর দুটি নিরাপদে ধরা সম্ভব নয় — অংশ ৩-এর সতর্কবার্তা হাতেনাতে। শেষ পাঁচ লাইন পরের সূত্রের হিসাব।

সাধারণ নিয়ম: 2r ≥ m + r + ১

m টি ডেটা-বিটের সাথে কয়টি parity বিট r লাগবে, যাতে যেকোনো একটি ভুল সারানো যায়? Codeword-এর দৈর্ঘ্য n = m + r. প্রাপককে n + ১টি আলাদা অবস্থা চিনতে হবে — “কোনো ভুল নেই” আর “অবস্থান ১…n-এর কোনো একটিতে ভুল”। r বিটের syndrome সর্বোচ্চ 2rটি আলাদা মান নিতে পারে। তাই লাগবে 2r ≥ m + r + ১। m = ৪ হলে r = ৩ (৮ ≥ ৮) — ঠিক সমান, তাই Hamming(7,4)-কে বলে “perfect” code: একটি syndrome-ও অপচয় হয় না। m = ৮ (এক বাইট) হলে r = ৩-এ ৮ ≥ ১২ মেলে না, r = ৪-এ ১৬ ≥ ১৩ — তাই (12,8). ৬৪ বিট ডেটায় r = ৭; এর সাথে আরও একটি সামগ্রিক parity বিট যোগ করে (72,64) code হয়, যা server-এর ECC memory-তে ব্যবহৃত SECDED (single error correction, double error detection)-এর পরিচিত রূপ।

৯ · আধুনিক FEC আর stack-এর কোন তলায় কোন পাহারা

Hamming code এক বিট সারায়, কিন্তু বেতার link-এ দরকার অনেক বেশি। Claude Shannon ১৯৪৮ সালে দেখিয়েছিলেন, প্রতিটি noisy channel-এর একটি capacity আছে (পর্ব ০৭), আর তার চেয়ে কম হারে পাঠালে যথেষ্ট ভালো code দিয়ে ভুলের সম্ভাবনা যত খুশি কমানো সম্ভব — কিন্তু সেই code কীভাবে বানাতে হবে, তা বলেননি। পরের কয়েক দশক সেই খোঁজ:

  • Reed-Solomon (১৯৬০): বিট নয়, পুরো symbol (যেমন বাইট) ধরে কাজ করে, তাই burst-এ খুব শক্তিশালী — একটি বাইটের ৮টি বিট নষ্ট হলেও সেটি একটিমাত্র symbol-এর ভুল। CD-DVD, QR code (দাগ লাগলেও পড়া যায়), ডিজিটাল টিভি আর উচ্চগতির Ethernet-এ ব্যবহৃত।
  • LDPC (low-density parity-check): Robert Gallager ১৯৬০-এর দশকের শুরুতে উদ্ভাবন করেন, কিন্তু তখনকার কম্পিউটারে decode করা কঠিন ছিল বলে প্রায় ভুলে যাওয়া হয়; ১৯৯০-এর দশকে আবার আবিষ্কৃত হয়। খুব বড় কিন্তু “পাতলা” parity-check ছক, আর বারবার আনুমানিক হিসাব (iterative decoding) দিয়ে Shannon-এর সীমার খুব কাছে পৌঁছায়। আধুনিক Wi-Fi (802.11n থেকে ঐচ্ছিক, পরে বহুল ব্যবহৃত), 10GBASE-T Ethernet, স্যাটেলাইট টিভি (DVB-S2) আর 5G-র ডেটা চ্যানেলে ব্যবহৃত; 5G-র নিয়ন্ত্রণ চ্যানেলে আবার polar code.

এবার পুরো stack একসাথে দেখুন। একটি ফাইল ডাউনলোডের পথে একাধিক পাহারা:

স্তরযাচাইকী ঢাকেব্যর্থ হলে
PhysicalFEC (LDPC, Reed-Solomon)রেডিও বা তারের বিটনিজেই সারায়
LinkCRC-32 (Ethernet ও Wi-Fi-র FCS)পুরো frame, একটি hopFrame নীরবে বাদ; Wi-Fi-তে link-স্তরেই আবার পাঠানো
NetworkIPv4 header checksum (IPv6-এ নেই)শুধু IP headerPacket বাদ
TransportTCP checksum (বাধ্যতামূলক), UDP checksum (IPv4-এ ঐচ্ছিক, IPv6-এ সাধারণত বাধ্যতামূলক)Header + ডেটা + IP ঠিকানার “pseudo-header”, দুই প্রান্তের মধ্যেSegment বাদ; TCP পরে আবার পাঠায়
ApplicationSHA-256-এর মতো cryptographic hash, TLS-এর অখণ্ডতা যাচাইপুরো ফাইল বা বার্তা, ইচ্ছাকৃত বদলসহফাইল বাতিল, আবার ডাউনলোড

এত স্তর কেন? পর্ব ০৩-এ end-to-end principle দেখেছেন। এখানে তার সবচেয়ে বিখ্যাত উদাহরণ — Saltzer, Reed আর Clark-এর “careful file transfer”. Link CRC শুধু তারের ওপরের যাত্রা পাহারা দেয়। কিন্তু router-এর ভেতরে frame যাচাইয়ের পরে আর নতুন CRC বসানোর আগে packet থাকে memory-তে আর bus-এ; সেখানে কোনো বিট উল্টালে পরের link-এর CRC সেই ভুল ডেটার ওপরেই হিসাব হবে, আর “সঠিক” বলে পার হয়ে যাবে। Disk থেকে পড়ার ভুল বা software bug-ও কোনো link CRC ধরবে না। তাই সত্যিকারের নিশ্চয়তা দিতে পারে শুধু দুই প্রান্তের যাচাই — TCP checksum, আর তার চেয়েও শক্ত application-এর hash. নিচের স্তরের পাহারা তবু দরকারি, কিন্তু দক্ষতার জন্য: প্রতিটি hop-এ ভুল আগেভাগে ধরলে নষ্ট ডেটা পুরো পথ ঘুরে প্রাপক পর্যন্ত যায় না।

Parity, checksum বা CRC কোনোটিই নিরাপত্তা নয়। এগুলো এলোমেলো noise-এর বিরুদ্ধে, ইচ্ছাকৃত বদলের বিরুদ্ধে নয়: কেউ ডেটা বদলালে নতুন CRC হিসাব করে বসিয়ে দিতে পারে, কারণ নিয়মটি সবার জানা আর কোনো গোপন চাবি নেই। ইচ্ছাকৃত বদল ঠেকাতে লাগে চাবিসহ MAC বা digital signature — পর্ব ৪৭ আর ৪৮-এ।

আরও গভীরে: আধুনিক NIC সাধারণত Ethernet CRC নিজেই হিসাব ও যাচাই করে, আর Linux-এ “checksum offload” চালু থাকলে TCP/UDP checksum-ও NIC-এ চলে যায় — তাই নিজের মেশিনে tcpdump-এ বেরিয়ে যাওয়া packet-এর checksum “ভুল” দেখালে ঘাবড়াবেন না, capture হয়েছে NIC checksum বসানোর আগে (ethtool -k দিয়ে offload দেখা যায়)। iSCSI আর SCTP-র মতো protocol CRC-32-এর বদলে Castagnoli-র CRC-32C ব্যবহার করে, যার জন্য x86-এর SSE4.2-তে আলাদা crc32 instruction আছে। আর ECC memory-র SECDED, যা অংশ ৮-এর Hamming code-এরই সম্প্রসারণ, server-এ প্রতিদিন বহু বিটের ভুল নিঃশব্দে সারায়।

১০ · AI-এর যুগে CRC আর Hamming

CRC আর Hamming-এর অঙ্ক AI-কে দিলে উত্তর আসে নিমেষে, ধাপসহ, আত্মবিশ্বাসী ভঙ্গিতে — আর প্রায়ই কোথাও একটি বিট ভুল। একটি XOR ভুল হলে পরের সব ধাপ ভুল হয়, অথচ লেখা দেখতে নিখুঁত। এই বিষয়ে AI-এর সাধারণ ভুল:

AI-এর ভুলউদাহরণকীভাবে যাচাই করবেন
CRC ভাগে নিয়ম ভাঙাr টি ০ জুড়তে ভুলে যাওয়া, সাধারণ বিয়োগে borrow নেওয়া, ভাগশেষে r-এর কম বা বেশি বিটঅংশ ৬-এর simulator চালান; প্রাপকের দিকে ভাগশেষ ০ আসে কি না দেখুন
Hamming-এর অবস্থান গুলিয়ে ফেলা০ থেকে গোনা, parity বিট শেষে বসানো, odd/even parity মেশানোSyndrome-এর নিয়মে উল্টো যাচাই: বৈধ codeword-এ ১-এর অবস্থানগুলোর XOR ০ কি না
অতিরঞ্জিত দাবি“CRC সব ভুল ধরে”, “checksum ডেটা সুরক্ষিত রাখে”অংশ ৬-এর ধর্মগুলো মিলিয়ে নিন; নিরাপত্তার জন্য MAC বা signature লাগে
CRC-র ধরন গুলিয়ে ফেলাCRC-32 আর CRC-32C এক বলা, reflected বা শেষের XOR বাদ দেওয়াTest vector: "123456789" → CRC-32 0xCBF43926, CRC-32C 0xE3069283
ভুল টুল বা বানানো RFC“cksum দিয়ে Ethernet CRC দেখুন” (POSIX cksum-এর CRC আলাদা), ভুল RFC নম্বরman cksum; Python-এর zlib.crc32; rfc-editor.org-এ RFC 1071 মিলিয়ে দেখুন

AI-কে দিয়ে CRC বা Hamming-এর অঙ্ক মেলাতে চাইলে তাকে প্রতিটি ধাপ দেখাতে আর নিজেই যাচাই করতে বলুন:

আমি error detection শিখছি। ডেটা 1101011011, generator 10011. ১. generator-এর degree কত আর কয়টি শূন্য জুড়তে হবে, আগে বলো। ২. modulo-2 লম্বা ভাগ করো — প্রতিটি ধাপে কোন ৫টি বিটের সাথে কী XOR হলো আর ফল কী, আলাদা লাইনে লেখো; কোনো ধাপ বাদ দিও না। ৩. শেষে পাঠানো frame-কে আবার একই generator দিয়ে ভাগ করে দেখাও যে ভাগশেষ শূন্য। ৪. এই generator কোন ধরনের ভুল নিশ্চিতভাবে ধরে আর কোনগুলো ধরে না, কারণসহ বলো; (x+1) দিয়ে এটি বিভাজ্য কি না হিসাব করে দেখাও। কোনো RFC বা standard উল্লেখ করলে শুধু তখনই করো, যখন নম্বর সম্পর্কে নিশ্চিত।

AI-এর দেওয়া error-detection উত্তর গ্রহণের আগে — চেকলিস্ট

পরীক্ষা ও ইন্টারভিউতে এই বিষয়ে প্রায়ই যা জিজ্ঞেস করা হয়:

  • ডেটা 1101011011, generator 10011 — CRC কত? — ৪টি ০ জুড়ে modulo-2 ভাগ; ভাগশেষ 1110, পাঠানো frame 11010110111110.
  • dmin = ৫ হলে কয়টি ভুল ধরা ও সারানো যায়? — ধরা ৪টি, সারানো ⌊৪/২⌋ = ২টি।
  • ৮ বিট ডেটার জন্য Hamming code-এ কয়টি parity বিট? — 2r ≥ ৮ + r + ১ থেকে r = ৪, অর্থাৎ (12,8).
  • Parity কোন ভুল ধরে না? — যেকোনো জোড় সংখ্যক বিটের ভুল, যেমন দুটি বিট একসাথে উল্টালে।
  • CRC আর checksum-এর পার্থক্য? — Checksum যোগফল, software-এ দ্রুত কিন্তু ক্রম বদল ও কাটাকাটি ধরে না; CRC বহুপদীর ভাগশেষ, hardware-এ সস্তা আর r পর্যন্ত দৈর্ঘ্যের সব burst ধরে।

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

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

প্র ০১Richard Hamming কেন error-correcting code আবিষ্কার করলেন, আর ভুল “ধরে আবার পাঠানো” না করে “নিজে সারানো” কখন ভালো?

Hamming নিজে বহুবার যে গল্প বলেছেন বলে জানা যায়, তা প্রকৌশলের বিরক্তি থেকে জন্ম নেওয়া উদ্ভাবনের একটি ক্লাসিক উদাহরণ। ১৯৪০-এর দশকের শেষে Bell Labs-এ Hamming relay-ভিত্তিক কম্পিউটারে কাজ করতেন, যেগুলো punched card থেকে প্রোগ্রাম পড়ত। মেশিনগুলো parity-র মতো কৌশলে ভুল ধরতে পারত — ধরা পড়লে কাজ থামিয়ে দিত। সপ্তাহের দিনে অপারেটর এসে আবার চালু করতেন, কিন্তু সপ্তাহান্তে কেউ থাকতেন না; মেশিন ভুল ধরে পরের কাজে চলে যেত, আর Hamming সোমবার এসে দেখতেন তাঁর পুরো হিসাব বাতিল। তাঁর বিখ্যাত প্রশ্ন ছিল মোটামুটি এমন: মেশিন যদি ভুল ধরতে পারে, তাহলে কোথায় ভুল তা খুঁজে সারাতে পারে না কেন? সেই চিন্তার ফল ১৯৫০ সালের প্রবন্ধ “Error Detecting and Error Correcting Codes”, যেখানে Hamming distance আর Hamming code দুটোই আসে।

কখন সারানো (FEC) আর কখন আবার পাঠানো (ARQ)? সিদ্ধান্ত তিনটি জিনিসের ওপর:

  • ফেরত-পথের দাম: ARQ-তে প্রতিটি ভুলের দাম অন্তত এক round-trip. পৃথিবী থেকে মঙ্গলে আলো যেতে কয়েক মিনিট লাগে; Voyager-এর মতো দূরের যানে কয়েক ঘণ্টা। CD-র মতো সংরক্ষিত মাধ্যমে “আবার পাঠাও” বলার কেউই নেই। সম্প্রচারে (টিভি) লাখো দর্শক থাকলে প্রত্যেকের অনুরোধ শোনা অসম্ভব।
  • সময়ের সীমা: ভিডিও কল বা অনলাইন গেমে ২০০ মিলিসেকেন্ড পরে আসা সঠিক ডেটা অর্থহীন — তখন ছোট FEC বা ভুল সহ্য করাই ভালো।
  • Channel-এর ভুলের হার: ফাইবারে ভুল এত কম যে প্রতিটি frame-এ বাড়তি FEC বিট পাঠানো অপচয়; শুধু CRC আর কদাচিৎ আবার পাঠানোই সস্তা। বেতারে ভুল এত বেশি যে শুধু ARQ হলে প্রায় সব frame বারবার যেত — তাই সেখানে FEC আবশ্যক।

আজকের Wi-Fi আর 4G/5G দুটোই মেশায়: LDPC বা turbo-জাতীয় code বেশিরভাগ ভুল সারায়, CRC বাকিটা ধরে, আর hybrid ARQ আবার পাঠায় — এমনকি নষ্ট পুরোনো কপি ফেলে না দিয়ে নতুন কপির সাথে মিলিয়ে decode করে।

মূল উপলব্ধি: Detection আর correction প্রতিদ্বন্দ্বী নয়, একই redundancy-র দুই ব্যবহার। কোনটি বেছে নেবেন, তা ঠিক করে ফেরত-পথের দাম, সময়ের সীমা আর channel কতটা খারাপ।

প্র ০২Internet checksum এত দুর্বল হয়েও কেন TCP/IP-তে টিকে আছে? বাস্তবে কি এর কারণে ভুল ডেটা পৌঁছায়?

১৯৭০-এর দশকের শেষে TCP/IP যখন নকশা হচ্ছিল, তখন checksum হিসাব করত সাধারণ CPU, software-এ, প্রতিটি packet-এ। CRC-র bit-by-bit ভাগ তখনকার ধীর মেশিনে ব্যয়বহুল ছিল; যোগ ছিল সবচেয়ে সস্তা কাজ। One's complement যোগের আরও কিছু সুবিধা ছিল: যেকোনো byte order-এ একই ফল (RFC 1071 এটি দেখায়), ৩২ বা ৬৪ বিটে একসাথে যোগ করে শেষে ভাঁজ করা যায়, আর একটি ঘর বদলালে পুরোটা আবার না যোগ করে শুধু পার্থক্য দিয়ে হালনাগাদ করা যায় (RFC 1624) — যা router-এর TTL কমানো আর NAT-এর port বদলের জন্য আদর্শ।

দুর্বলতাগুলো বাস্তব। Jonathan Stone আর Craig Partridge ২০০০ সালের SIGCOMM-এ প্রকাশিত “When the CRC and TCP Checksum Disagree” প্রবন্ধে বাস্তব নেটওয়ার্কের ট্রেস বিশ্লেষণ করে দেখান, link স্তরের CRC পার হওয়ার পরও উল্লেখযোগ্য সংখ্যক packet TCP checksum-এ ব্যর্থ হয় — তাঁদের সারাংশ অনুযায়ী বিভিন্ন ট্রেসে মোটামুটি ১,১০০ থেকে ৩২,০০০ packet-এ একটি। এর উৎস তারের noise নয়: router বা host-এর memory, bus, ভুলভাবে লেখা driver আর software bug — অর্থাৎ ঠিক সেই জায়গা, যা link CRC পাহারা দেয় না। তাঁরা আরও যুক্তি দেন যে এর একটি ছোট অংশ TCP checksum-কেও ফাঁকি দেয়, কারণ বাস্তব ডেটা এলোমেলো নয় (অনেক ০, পুনরাবৃত্ত নকশা) আর এমন গোলমাল সহজে কাটাকাটি করে।

তাহলে আমাদের ফাইল নষ্ট হয় না কেন? কয়েকটি কারণে: বেশিরভাগ ভুল তারেই হয় আর CRC-32 তা ধরে; বাকির অধিকাংশ TCP checksum ধরে; আর আজ অধিকাংশ ট্রাফিক TLS-এ যায়, যার প্রতিটি record cryptographic অখণ্ডতা-যাচাই বহন করে — একটি বিট উল্টালেও record বাতিল। গুরুত্বপূর্ণ ডাউনলোডে SHA-256 মিলিয়ে দেখা হয়। এই ভাবনা থেকেই IPv6 header checksum বাদ দিয়েছে (link CRC আর transport checksum থাকায় প্রতিটি hop-এ আলাদা কাজ অপ্রয়োজনীয় মনে করা হয়েছে), কিন্তু UDP checksum-কে IPv6-এ সাধারণভাবে বাধ্যতামূলক করেছে।

মূল উপলব্ধি: কোনো একটি যাচাই নিখুঁত নয়; নিরাপত্তা আসে স্তরে স্তরে আলাদা ধরনের যাচাই থেকে — আর শেষ ভরসা দুই প্রান্তের শক্তিশালী যাচাই। end-to-end argument এখানে তত্ত্ব নয়, মাপা সত্য।

প্র ০৩Shannon বলেছিলেন প্রায় নির্ভুল যোগাযোগ সম্ভব — তাহলে সেই সীমার কাছে পৌঁছাতে প্রায় অর্ধশতাব্দী লাগল কেন, আর আজকের Wi-Fi আর 5G কীভাবে পারে?

১৯৪৮ সালে Claude Shannon প্রমাণ করেন: প্রতিটি noisy channel-এর একটি capacity C আছে, আর C-এর চেয়ে কম হারে পাঠালে এমন code আছে যা দিয়ে ভুলের সম্ভাবনা যত খুশি ছোট করা যায়। প্রমাণটি ছিল অস্তিত্বের — তিনি দেখান যে এলোমেলোভাবে বেছে নেওয়া খুব লম্বা code গড়ে ভালো কাজ করে, কিন্তু এমন code-কে কার্যকরভাবে decode করার উপায় দেননি। এলোমেলো code decode করতে হলে কার্যত সব codeword-এর সাথে তুলনা করতে হয় — যার সংখ্যা দৈর্ঘ্যের সাথে সূচকীয় হারে বাড়ে।

তাই কয়েক দশক ধরে প্রকৌশলীরা গঠনযুক্ত code খুঁজেছেন, যা গাণিতিক নিয়মের কারণে দ্রুত decode হয়: Hamming (১৯৫০), Reed-Solomon (১৯৬০), convolutional code আর Viterbi decoding (১৯৬০-এর দশকের শেষে)। মহাকাশ অভিযানে এগুলোকে একসাথে জুড়ে (concatenated code) ব্যবহার হয় — Voyager-এর মতো যান convolutional code-এর সাথে Reed-Solomon মিলিয়ে দূরের গ্রহ থেকে ছবি পাঠিয়েছিল বলে জানা যায়। তবু Shannon-এর সীমা থেকে কয়েক dB দূরত্ব রয়ে যাচ্ছিল।

বড় লাফ আসে ১৯৯৩ সালে, যখন Claude Berrou ও সহকর্মীরা turbo code উপস্থাপন করেন — দুটি সরল encoder আর এমন decoder, যা বারবার নিজেদের মধ্যে “মতামত” বিনিময় করে (iterative decoding). ফল এত ভালো ছিল যে প্রথমে অনেকে বিশ্বাস করতে চাননি। এর কিছু পরে David MacKay প্রমুখ গবেষক দেখান যে Robert Gallager-এর ১৯৬০-এর দশকের প্রায় ভুলে যাওয়া LDPC code-ও একই ধরনের iterative decoding-এ সীমার খুব কাছে যায় — Gallager-এর সময়ের কম্পিউটারে যা চালানো কঠিন ছিল, এখন সস্তা চিপে সম্ভব।

আজ 3G/4G-তে turbo code, Wi-Fi আর 10GBASE-T-তে LDPC, আর 5G-র ডেটা চ্যানেলে LDPC ও নিয়ন্ত্রণ চ্যানেলে polar code (Erdal Arıkan-এর ২০০৯ সালের কাজ, যা গাণিতিকভাবে capacity অর্জন প্রমাণ করে) ব্যবহৃত হয়। মোবাইলের সিগন্যাল দুর্বল হলে ফোন কম হারের (বেশি redundancy) code আর সরল modulation বেছে নেয়, সিগন্যাল ভালো হলে উল্টো — এটিই adaptive modulation and coding, যার কারণে টাওয়ারের কাছে গতি বেশি।

মূল উপলব্ধি: কী সম্ভব (তত্ত্ব) আর কী কার্যকরভাবে করা যায় (গণনা) — দুটির ফাঁক পূরণ করতে কখনো কয়েক দশক লাগে; আর অনেক সময় সমাধান লুকিয়ে থাকে পুরোনো, সময়ের আগে আসা কোনো ধারণায়।

প্র ০৪CRC-র generator polynomial কীভাবে বাছা হয়? একই ৩২ বিটের দুটি CRC কি সমান শক্তিশালী?

না — আর এটিই CRC-র সবচেয়ে কম আলোচিত কিন্তু গুরুত্বপূর্ণ দিক। অংশ ৬-এর ধর্মগুলো (সব single-bit, r পর্যন্ত সব burst) যেকোনো যুক্তিসঙ্গত degree-r generator-এর জন্যই সত্য। কিন্তু দুই, তিন বা চারটি ছড়ানো বিটের ভুল কতটা ধরা পড়বে — তা নির্ভর করে generator-এর গঠন আর ডেটার দৈর্ঘ্যের ওপর। এর মাপ হলো নির্দিষ্ট দৈর্ঘ্যে code-এর Hamming distance (HD): HD = ৪ মানে ওই দৈর্ঘ্যে যেকোনো ৩টি ভুল নিশ্চিতভাবে ধরা পড়ে।

  • দুটি ভুল: E(x) = xi(xk + ১)। G যদি k-এর কোনো ছোট মানে xk + ১-কে ভাগ করে, তাহলে k দূরত্বে থাকা দুটি ভুল ধরা পড়বে না। তাই ভালো G-এর ক্ষেত্রে এই k যত বড় সম্ভব হওয়া চাই — frame-এর দৈর্ঘ্যের চেয়ে বড়।
  • বিজোড় ভুল: (x + ১) উৎপাদক থাকলে সব বিজোড় ভুল ধরা পড়ে, কিন্তু এতে একটি degree “খরচ” হয়, যা অন্য ধর্মকে দুর্বল করতে পারে। Ethernet-এর CRC-32-তে পদের সংখ্যা ১৫টি (বিজোড়), তাই (x + ১) উৎপাদক নয়।

১৯৭০-এর দশকে বাছা Ethernet-এর CRC-32 ভালো, কিন্তু সর্বোত্তম নয়। Guy Castagnoli ১৯৯৩ সালে আর Philip Koopman ২০০০-এর দশকের শুরুতে কম্পিউটার দিয়ে বহু generator পরীক্ষা করে দেখান, একই ৩২ বিটে কিছু generator বিভিন্ন দৈর্ঘ্যের ডেটায় বেশি HD দেয়। এর ফলেই iSCSI আর SCTP-র মতো নতুন protocol CRC-32C (Castagnoli) বেছে নিয়েছে, আর Intel পরে CPU-তে এর জন্য আলাদা instruction যোগ করেছে। Koopman-এর বিশ্লেষণ অনুযায়ী Ethernet-এর CRC-32 সর্বোচ্চ আকারের frame পর্যন্ত HD ৪ বজায় রাখে — অর্থাৎ যেকোনো ৩টি বিটের ভুল ধরে।

আরও একটি সূক্ষ্মতা: CRC-র ক্ষমতা শুধু generator নয়, বাকি নিয়মেও নির্ভর করে। শুরুর মান ০ হলে frame-এর সামনে বাড়তি ০ জুড়লে CRC বদলায় না — তাই শুরুর মান সব ১। ছোট embedded সিস্টেমে প্রকৌশলীরা অনেক সময় “পরিচিত” CRC-16 বসিয়ে দেন, তাঁদের ডেটার দৈর্ঘ্যে তা কতটা কাজ করে না দেখে; Koopman-এর প্রকাশিত তালিকা এই সমস্যা এড়াতে সাহায্য করে।

মূল উপলব্ধি: “৩২ বিটের CRC” একটি পরিবারের নাম, একক জিনিস নয়। Generator, ডেটার দৈর্ঘ্য আর শুরু ও শেষের নিয়ম মিলে ঠিক হয় কোন ভুল ধরা পড়বে — আর এগুলো প্রকৌশলীর সচেতন সিদ্ধান্ত হওয়া উচিত, অভ্যাস নয়।

প্র ০৫CRC তো বেশ শক্তিশালী — তাহলে নিরাপত্তার কাজে (ইচ্ছাকৃত বদল ঠেকাতে) একে ব্যবহার করা কেন বিপজ্জনক? Wi-Fi-র ইতিহাস কী শেখায়?

CRC-র শক্তি এলোমেলো noise-এর বিরুদ্ধে। কিন্তু এর গঠনই — যা একে এত সস্তা আর বিশ্লেষণযোগ্য করে — আক্রমণকারীর কাছে সুবিধা। CRC রৈখিক (linear): দুটি বার্তার XOR-এর CRC (শুরু ও শেষের ধ্রুবক বাদে) তাদের CRC-র XOR-এর সমান। তাই কেউ ডেটার নির্দিষ্ট কিছু বিট উল্টাতে চাইলে আগেই হিসাব করে বলতে পারে CRC-র কোন বিটগুলো উল্টাতে হবে, যাতে নতুন বার্তাও “সঠিক” দেখায় — মূল ডেটা না জেনেও। আর ডেটা খোলা থাকলে তো আরও সহজ: নতুন ডেটার CRC নতুন করে হিসাব করে বসালেই হলো, কারণ কোনো গোপন চাবি নেই।

এর সবচেয়ে বিখ্যাত শিক্ষা Wi-Fi-র প্রথম নিরাপত্তা ব্যবস্থা WEP (১৯৯৯)। WEP ডেটা আর তার অখণ্ডতা-যাচাই — CRC-32-ভিত্তিক একটি “ICV” — RC4 stream cipher দিয়ে encrypt করত। Stream cipher-এ ciphertext-এর একটি বিট উল্টালে plaintext-এর ঠিক সেই বিট উল্টায়; CRC রৈখিক হওয়ায় আক্রমণকারী encrypt করা ICV-কেও মানানসইভাবে বদলাতে পারত। ২০০১ সালের দিকে Borisov, Goldberg আর Wagner-এর বিশ্লেষণ সহ একাধিক গবেষণা এই দুর্বলতা (আর RC4 চাবি ব্যবহারের অন্যান্য সমস্যা) প্রকাশ করে, আর WEP দ্রুত অনিরাপদ বলে গণ্য হয়।

প্রতিরক্ষা হিসেবে পরে WPA2 আসে, যার CCMP অংশ AES ভিত্তিক MIC (message integrity code) ব্যবহার করে — একটি চাবি-নির্ভর যাচাই, যা চাবি ছাড়া কেউ হিসাব করতে পারে না। আজকের TLS-ও AES-GCM বা ChaCha20-Poly1305-এর মতো AEAD ব্যবহার করে, যেখানে encryption আর অখণ্ডতা একসাথে আসে। নিজের ব্যবস্থার জন্য করণীয় সরল: ঘরের বা অফিসের Wi-Fi-তে WEP বা পুরোনো TKIP নয়, WPA2-AES বা WPA3 ব্যবহার করুন; ডাউনলোড যাচাইয়ে প্রকাশকের দেওয়া SHA-256 মিলিয়ে দেখুন — আর সেই hash নিজেই HTTPS-এ বা signature-সহ এসেছে কি না দেখুন।

মনে রাখুন, cryptographic hash (SHA-256) চাবিহীন — এটি এলোমেলো ভুল আর ইচ্ছাকৃত বদলের পার্থক্য করা কঠিন করে, কিন্তু কেউ ডেটা আর hash দুটোই বদলাতে পারলে কাজ হয় না। তাই hash-কে বিশ্বস্ত পথে পেতে হয়, বা চাবিসহ MAC আর signature লাগে (পর্ব ৪৭)।

মূল উপলব্ধি: ভুল ধরার code প্রকৃতির বিরুদ্ধে, cryptography মানুষের বিরুদ্ধে। দুটির লক্ষ্য আলাদা, তাই একটিকে আরেকটির জায়গায় বসানো নকশার ভুল।

অনুশীলন

খাতা-কলম নিয়ে বসুন — হিসাবগুলো নিজে করে তারপর উত্তর মেলান।

  1. হাতে হিসাব (CRC): ডেটা 101110, generator 1001 (x৩ + ১)। CRC কত, পাঠানো frame কী? প্রাপকের দিকে যাচাই করুন।

    Degree ৩, তাই তিনটি ০ জুড়ে ভাজ্য 101110000.

    1011 xor 1001 = 0010      quotient 1
     0101 (lead 0)            quotient 10
      1010 xor 1001 = 0011    quotient 101
       0110 (lead 0)          quotient 1010
        1100 xor 1001 = 0101  quotient 10101
         1010 xor 1001 = 0011 quotient 101011
    remainder = 011

    CRC = 011, ভাগফল 101011, পাঠানো frame 101110011. প্রাপক 101110011-কে 1001 দিয়ে ভাগ করে ভাগশেষ 000 পায়। অংশ ৬-এর simulator-এ data আর gen বদলে মিলিয়ে নিন। লক্ষ করুন, x৩ + ১ = (x + ১)(x২ + x + ১), তাই এই generator সব বিজোড় সংখ্যক ভুল ধরে।

  2. Hamming syndrome: Hamming(7,4) (even parity, parity বিট ১, ২, ৪-এ) দিয়ে পাঠানো একটি codeword প্রাপক পেল 1010111 (অবস্থান ১ থেকে ৭)। এক বিটের ভুল ধরে নিয়ে কোন বিট ভুল, সঠিক codeword আর ডেটা কী?

    ১ আছে অবস্থান ১, ৩, ৫, ৬, ৭-এ। s1 = b1 ⊕ b3 ⊕ b5 ⊕ b7 = ১ ⊕ ১ ⊕ ১ ⊕ ১ = ০; s2 = b2 ⊕ b3 ⊕ b6 ⊕ b7 = ০ ⊕ ১ ⊕ ১ ⊕ ১ = ১; s4 = b4 ⊕ b5 ⊕ b6 ⊕ b7 = ০ ⊕ ১ ⊕ ১ ⊕ ১ = ১। Syndrome 110 = ৬ (সংক্ষেপে: ১ ⊕ ৩ ⊕ ৫ ⊕ ৬ ⊕ ৭ = ৬)। অবস্থান ৬ উল্টে সঠিক codeword 1010101, আর অবস্থান ৩, ৫, ৬, ৭ থেকে ডেটা 1101. যাচাই: 1010101-এ ১-এর অবস্থান ১, ৩, ৫, ৭ আর ১ ⊕ ৩ ⊕ ৫ ⊕ ৭ = ০ — বৈধ।

  3. আউটপুট অনুমান করুন: অংশ ৮-এর Hamming simulator-এ ভুলের তালিকায় [3, 5] যোগ করলে (অর্থাৎ অবস্থান ৩ আর ৫-এ দুটি ভুল) সেই লাইনে কী ছাপা হবে? প্রাপক কি ঠিক ডেটা পাবে?

    পাঠানো 0110011; অবস্থান ৩ (১ → ০) আর ৫ (০ → ১) উল্টে প্রাপ্ত 0100111. Syndrome = ৩ ⊕ ৫ = ৬ = 110, তাই প্রাপক অবস্থান ৬ উল্টায়:

    errors at [3,5] recv 0100111 syndrome 110 -> fixed 0100101 data 0101

    ফল আরও খারাপ: আসল দুটি ভুলের সাথে তৃতীয় একটি যোগ হলো, আর ডেটা 1011-এর বদলে 0101 — চারটির মধ্যে তিনটি বিট ভুল। দুটি ভুলের syndrome সবসময় শূন্য-নয় (কারণ দুটি আলাদা সংখ্যার XOR কখনো ০ নয়), তাই এটি ধরা যায়; কিন্তু “সারানো” চালু থাকলে প্রাপক একে একটি ভুল ভেবে নেয়। এজন্যই memory-তে বাড়তি একটি সামগ্রিক parity বিট দিয়ে SECDED বানানো হয়, যা দুটি ভুল আলাদা করে চেনে।

  4. হাতে হিসাব (Internet checksum): তিনটি ১৬-বিট word 0x8F1A, 0x9B47, 0x0C33-এর Internet checksum বের করুন। যদি পথে প্রথম দুটি word-এর জায়গা বদলে যায়, প্রাপক কি ধরতে পারবে?
    0x8F1A + 0x9B47 = 0x12A61 -> wrap: 0x2A61 + 0x1 = 0x2A62
    0x2A62 + 0x0C33 = 0x3695
    checksum = ~0x3695 = 0xC96A
    check: 0x3695 + 0xC96A = 0xFFFF

    Checksum 0xC96A. প্রথম যোগে ১৭তম বিটে carry এসেছে, তাই end-around carry. দুটি word-এর জায়গা বদলালে যোগফল একই থাকে (যোগ বিনিময়যোগ্য), তাই প্রাপক ধরতে পারবে না — Internet checksum-এর পরিচিত দুর্বলতা। CRC-তে প্রতিটি বিটের অবস্থান গুরুত্বপূর্ণ, তাই এমন অদলবদল সাধারণত ধরা পড়ে।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমCRC দিয়ে একটি frame পাঠানো আর যাচাইয়ের ধাপগুলো সাজান (▲▼ চেপে সরান)

  1. Generator-এর degree r দেখে ডেটার শেষে r টি ০ জোড়া
  2. সেই বিট-নকশাকে generator দিয়ে modulo-2 ভাগ করা
  3. r বিটের ভাগশেষ দিয়ে জোড়া ০-গুলো প্রতিস্থাপন করে পাঠানো
  4. প্রাপকের পুরো frame-কে একই generator দিয়ে ভাগ করা
  5. ভাগশেষ ০ হলে গ্রহণ, না হলে frame বাতিল

একটি code-এর minimum Hamming distance (dmin) ৫। এটি দিয়ে সর্বোচ্চ কয়টি বিটের ভুল নিশ্চিতভাবে ধরা যায় আর কয়টি সারানো যায়?

  • ধরা ৫টি, সারানো ২টি
  • ধরা ৪টি, সারানো ৪টি
  • ধরা ৪টি, সারানো ২টি
  • ধরা ২টি, সারানো ১টি

ডেটা 1101011011 আর generator 10011 (x4 + x + 1) দিয়ে CRC হিসাব করলে ভাগশেষ (CRC) কত?

  • 0000
  • 1110
  • 10011
  • 0111

৮টি ডেটা-বিটের জন্য যেকোনো একটি বিটের ভুল সারানো Hamming code বানাতে সর্বনিম্ন কয়টি parity বিট লাগবে?

  • ২টি
  • ৩টি
  • ৫টি
  • ৪টি

Even parity ব্যবহার করে ৭ বিটের ডেটা 1011001 পাঠানো হলো। Parity বিট কত হবে, আর কোন ভুল এই পদ্ধতি ধরতে পারবে না?

  • Parity বিট ০; যেকোনো দুটি বিট একসাথে উল্টালে ধরা পড়বে না
  • Parity বিট ১; একটি বিটের ভুল ধরা পড়বে না
  • Parity বিট ০; একটি বিটের ভুল ধরা পড়বে না
  • Parity বিট ১; তিনটি বিটের ভুল ধরা পড়বে না

দুটি ১৬-বিট word 0xFFF0 আর 0x0020-এর Internet checksum (one's complement sum-এর one's complement) কত?

  • 0xFFEF
  • 0x0011
  • 0xFFEE
  • 0x10010

একটি link-এ ফেরত-পথ নেই বা খুব ধীর (যেমন মহাকাশযান থেকে পৃথিবীতে ছবি পাঠানো, বা টিভি সম্প্রচার)। ভুল সামলানোর কোন কৌশল এখানে সবচেয়ে উপযুক্ত?

  • শুধু CRC আর ARQ (ভুল হলে আবার পাঠানো)
  • Forward error correction (FEC), যেমন Reed-Solomon বা LDPC
  • প্রতি বাইটে একটি parity বিট
  • IPv4 header checksum

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. Noise আর interference বিট উল্টায় — single-bit বা burst; redundancy ছাড়া প্রাপক ভুল টেরই পায় না।
  2. dmin সব ঠিক করে: ধরা যায় dmin − ১টি, সারানো যায় ⌊(dmin − ১)/২⌋টি ভুল; ভুল ধরে আবার পাঠানো ARQ, নিজে সারানো FEC.
  3. Parity জোড় সংখ্যক ভুল ধরে না; দুই মাত্রার parity একটি ভুল সারায়; Internet checksum ১৬-বিট one's complement যোগ — দ্রুত, কিন্তু অদলবদল ধরে না।
  4. CRC: r টি ০ জুড়ে G দিয়ে modulo-2 ভাগ, ভাগশেষ পাঠানো; সব single-bit আর r পর্যন্ত সব burst ধরে। ক্লাসিক: 1101011011 ÷ 10011 → 1110; Ethernet FCS = CRC-32, test vector 0xCBF43926.
  5. Hamming(7,4): parity ১, ২, ৪-এ; syndrome = ভুল বিটের অবস্থান; নিয়ম 2r ≥ m + r + ১। আর stack-এর প্রতিটি তলার পাহারা থাকা সত্ত্বেও শেষ ভরসা end-to-end যাচাই।

পরের পর্বে: আজ প্রাপক ভুল ধরতে শিখল। কিন্তু ধরার পর কী? প্রেরক জানবে কীভাবে, কতক্ষণ অপেক্ষা করবে, আর একসাথে কয়টি frame পথে রাখবে? Framing আর ARQ — stop-and-wait, Go-Back-N আর Selective Repeat, efficiency-র হিসাবসহ।

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

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান