Last active
May 16, 2021 02:55
-
-
Save joeladdison/5244877 to your computer and use it in GitHub Desktop.
Python functions for Hamming encoding and decoding, as used in CSSE3010 Prac 4 and Project 1.
Manchester encoding is also included as a reference.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Hamming and Manchester Encoding example | |
Author: Joel Addison | |
Date: March 2013 | |
Functions to do (7,4) hamming encoding and decoding, including error detection | |
and correction. | |
Manchester encoding and decoding is also included, and by default will use | |
least bit ordering for the byte that is to be included in the array. | |
""" | |
# List of syndrome positions. SYNDROME_CHECK[pos] will give the | |
# bit in the provided encoded byte that needs to be fixed | |
# Note: bit order used is 7 6 5 4 3 2 1 0 | |
SYNDROME_CHECK = [-1, 6, 5, 0, 4, 1, 2, 3] | |
def extract_bit(byte, pos): | |
""" | |
Extract a bit from a given byte using MS ordering. | |
ie. B7 B6 B5 B4 B3 B2 B1 B0 | |
""" | |
return (byte >> pos) & 0x01 | |
def hamming_encode_nibble(data): | |
""" | |
Encode a nibble using Hamming encoding. | |
Nibble is provided in form 0b0000DDDD == 0 0 0 0 D3 D2 D1 D0 | |
Encoded byte is in form P H2 H1 H0 D3 D2 D1 D0 | |
""" | |
# Get data bits | |
d = [0, 0, 0, 0] | |
d[0] = extract_bit(data, 0) | |
d[1] = extract_bit(data, 1) | |
d[2] = extract_bit(data, 2) | |
d[3] = extract_bit(data, 3) | |
# Calculate hamming bits | |
h = [0, 0, 0] | |
h[0] = (d[1] + d[2] + d[3]) % 2 | |
h[1] = (d[0] + d[2] + d[3]) % 2 | |
h[2] = (d[0] + d[1] + d[3]) % 2 | |
# Calculate parity bit, using even parity | |
p = 0 ^ d[0] ^ d[1] ^ d[2] ^ d[3] ^ h[0] ^ h[1] ^ h[2] | |
# Encode byte | |
encoded = (data & 0x0f) | |
encoded |= (p << 7) | (h[2] << 6) | (h[1] << 5) | (h[0] << 4) | |
return encoded | |
def hamming_decode_byte(byte): | |
""" | |
Decode a single hamming encoded byte, and return a decoded nibble. | |
Input is in form P H2 H1 H0 D3 D2 D1 D0 | |
Decoded nibble is in form 0b0000DDDD == 0 0 0 0 D3 D2 D1 D0 | |
""" | |
error = 0 | |
corrected = 0 | |
# Calculate syndrome | |
s = [0, 0, 0] | |
# D1 + D2 + D3 + H0 | |
s[0] = (extract_bit(byte, 1) + extract_bit(byte, 2) + | |
extract_bit(byte, 3) + extract_bit(byte, 4)) % 2 | |
# D0 + D2 + D3 + H1 | |
s[1] = (extract_bit(byte, 0) + extract_bit(byte, 2) + | |
extract_bit(byte, 3) + extract_bit(byte, 5)) % 2 | |
# D0 + D1 + D3 + H2 | |
s[2] = (extract_bit(byte, 0) + extract_bit(byte, 1) + | |
extract_bit(byte, 3) + extract_bit(byte, 6)) % 2 | |
syndrome = (s[0] << 2) | (s[1] << 1) | s[2] | |
if syndrome: | |
# Syndrome is not 0, so correct and log the error | |
error += 1 | |
byte ^= (1 << SYNDROME_CHECK[syndrome]) | |
corrected += 1 | |
# Check parity | |
p = 0 | |
for i in range(0, 7): | |
p ^= extract_bit(byte, i) | |
if p != extract_bit(byte, 7): | |
# Parity bit is wrong, so log error | |
if syndrome: | |
# Parity is wrong and syndrome was also bad, so error is not corrected | |
corrected -= 1 | |
else: | |
# Parity is wrong and syndrome is fine, so corrected parity bit | |
error += 1 | |
corrected += 1 | |
return ((byte & 0x0f), error, corrected) | |
def manchester_encode(byte): | |
""" | |
Encode a byte using Manchester encoding. Returns an array of bits. | |
Adds two start bits (1, 1) and one stop bit (0) to the array. | |
""" | |
# Add start bits (encoded 1, 1) | |
manchester_encoded = [0, 1, 0, 1] | |
# Encode byte | |
for i in range(7, -1, -1): | |
if extract_bit(byte, i): | |
manchester_encoded.append(0) | |
manchester_encoded.append(1) | |
else: | |
manchester_encoded.append(1) | |
manchester_encoded.append(0) | |
# Add stop bit (encoded 0) | |
manchester_encoded.append(1) | |
manchester_encoded.append(0) | |
return manchester_encoded | |
def manchester_decode(manchester_array): | |
""" | |
Decode a Manchester array to a single data byte. | |
""" | |
decoded = 0 | |
for i in range(0, 8): | |
bit = 7 - i | |
# Use the second value of each encoded bit, as that is the bit value | |
# eg. 1 is encoded to [0, 1], so retrieve the second bit (1) | |
decoded |= manchester_array[4 + (i * 2) + 1] << (bit) | |
return decoded | |
def reorder_byte(byte): | |
""" | |
Changes a byte in most significant bit ordering into least significant | |
bit ordering, or vice versa. | |
""" | |
new_byte = 0 | |
for i in range(0, 8, 1): | |
new_byte |= extract_bit(byte, i) << (7 - i) | |
return new_byte | |
def encode_byte(byte, ls_order=True): | |
""" | |
Encodes the given byte using Hamming encoding, followed by Manchester | |
encoding. | |
Uses least bit ordering for the Manchester encoding by default. | |
""" | |
ls_nibble = byte & 0x0f | |
ms_nibble = (byte & 0xf0) >> 4 | |
ls_hamming = hamming_encode_nibble(ls_nibble) | |
ms_hamming = hamming_encode_nibble(ms_nibble) | |
if ls_order: | |
ls_hamming = reorder_byte(ls_hamming) | |
ms_hamming = reorder_byte(ms_hamming) | |
ls_manchester = manchester_encode(ls_hamming) | |
ms_manchester = manchester_encode(ms_hamming) | |
return ls_manchester, ms_manchester | |
def decode_byte(ls_manchester, ms_manchester, ls_order=True): | |
""" | |
Decodes two manchester arrays containing hamming encoded nibbles | |
of a single byte. The arrays are first decoded to a hamming byte, then | |
decoded from the hamming byte to a single nibble. The nibbles are joined | |
to get the final byte. | |
By default, it is assumed the manchester array contains a byte using | |
least bit ordering. | |
""" | |
ls_hamming = manchester_decode(ls_manchester) | |
ms_hamming = manchester_decode(ms_manchester) | |
if ls_order: | |
ls_hamming = reorder_byte(ls_hamming) | |
ms_hamming = reorder_byte(ms_hamming) | |
ls_nibble, ls_error, ls_corrected = hamming_decode_byte(ls_hamming) | |
ms_nibble, ms_error, ms_corrected = hamming_decode_byte(ms_hamming) | |
return ls_nibble | (ms_nibble << 4) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment