282 lines
6.9 KiB
Python
282 lines
6.9 KiB
Python
|
#!/usr/bin/env python
|
||
|
# -*- coding: utf-8 -*-
|
||
|
"""
|
||
|
Prints steps of Reed-Solomon decoding
|
||
|
|
||
|
(C) 2022 Louis Heredero louis.heredero@edu.vs.ch
|
||
|
"""
|
||
|
|
||
|
class GF:
|
||
|
def __init__(self, val):
|
||
|
self.val = val
|
||
|
|
||
|
def copy(self):
|
||
|
return GF(self.val)
|
||
|
|
||
|
def __add__(self, n):
|
||
|
return GF(self.val ^ n.val)
|
||
|
|
||
|
def __sub__(self, n):
|
||
|
return GF(self.val ^ n.val)
|
||
|
|
||
|
def __mul__(self, n):
|
||
|
if self.val == 0 or n.val == 0:
|
||
|
return GF(0)
|
||
|
|
||
|
return GF.EXP[GF.LOG[self.val].val + GF.LOG[n.val].val].copy()
|
||
|
|
||
|
def __truediv__(self, n):
|
||
|
if n.val == 0:
|
||
|
raise ZeroDivisionError
|
||
|
if self.val == 0:
|
||
|
return GF(0)
|
||
|
|
||
|
return GF.EXP[(GF.LOG[self.val].val + 255 - GF.LOG[n.val].val)%255].copy()
|
||
|
|
||
|
def __pow__(self, n):
|
||
|
return GF.EXP[(GF.LOG[self.val].val * n.val)%255].copy()
|
||
|
|
||
|
def __repr__(self):
|
||
|
return self.val.__repr__()
|
||
|
#return f"GF({self.val})"
|
||
|
|
||
|
def log(self):
|
||
|
return GF.LOG[self.val]
|
||
|
|
||
|
GF.EXP = [GF(0)]*512
|
||
|
GF.LOG = [GF(0)]*256
|
||
|
value = 1
|
||
|
for exponent in range(255):
|
||
|
GF.LOG[value] = GF(exponent)
|
||
|
GF.EXP[exponent] = GF(value)
|
||
|
value = ((value << 1) ^ 285) if value > 127 else value << 1
|
||
|
|
||
|
for i in range(255, 512):
|
||
|
GF.EXP[i] = GF.EXP[i-255].copy()
|
||
|
|
||
|
|
||
|
class Poly:
|
||
|
def __init__(self, coefs):
|
||
|
self.coefs = coefs.copy()
|
||
|
|
||
|
@property
|
||
|
def deg(self):
|
||
|
return len(self.coefs)
|
||
|
|
||
|
def copy(self):
|
||
|
return Poly(self.coefs)
|
||
|
|
||
|
def __add__(self, p):
|
||
|
d1, d2 = self.deg, p.deg
|
||
|
deg = max(d1,d2)
|
||
|
result = [GF(0) for i in range(deg)]
|
||
|
|
||
|
for i in range(d1):
|
||
|
result[i + deg - d1] = self.coefs[i]
|
||
|
|
||
|
for i in range(d2):
|
||
|
result[i + deg - d2] += p.coefs[i]
|
||
|
|
||
|
return Poly(result)
|
||
|
|
||
|
def __mul__(self, p):
|
||
|
result = [GF(0) for i in range(self.deg+p.deg-1)]
|
||
|
|
||
|
for i in range(p.deg):
|
||
|
for j in range(self.deg):
|
||
|
result[i+j] += self.coefs[j] * p.coefs[i]
|
||
|
|
||
|
return Poly(result)
|
||
|
|
||
|
def __truediv__(self, p):
|
||
|
dividend = self.coefs.copy()
|
||
|
dividend += [GF(0) for i in range(p.deg-1)]
|
||
|
quotient = []
|
||
|
|
||
|
for i in range(self.deg):
|
||
|
coef = dividend[i] / p.coefs[0]
|
||
|
quotient.append(coef)
|
||
|
print("sub:", p*Poly([coef]))
|
||
|
|
||
|
for j in range(p.deg):
|
||
|
dividend[i+j] -= p.coefs[j] * coef
|
||
|
|
||
|
print("rem:", dividend)
|
||
|
print()
|
||
|
|
||
|
while dividend[0].val == 0:
|
||
|
dividend.pop(0)
|
||
|
|
||
|
return [Poly(quotient), Poly(dividend)]
|
||
|
|
||
|
def __repr__(self):
|
||
|
return f"<Poly {self.coefs}>"
|
||
|
|
||
|
def eval(self, x):
|
||
|
y = GF(0)
|
||
|
|
||
|
for i in range(self.deg):
|
||
|
y += self.coefs[i] * x**GF(self.deg-i-1)
|
||
|
|
||
|
return y
|
||
|
|
||
|
def del_lead_zeros(self):
|
||
|
while len(self.coefs) > 1 and self.coefs[0].val == 0:
|
||
|
self.coefs.pop(0)
|
||
|
|
||
|
if len(self.coefs) == 0:
|
||
|
self.coefs = [GF(0)]
|
||
|
|
||
|
return self
|
||
|
|
||
|
def get_generator_poly(n):
|
||
|
poly = Poly([GF(1)])
|
||
|
|
||
|
for i in range(n):
|
||
|
poly *= Poly([GF(1), GF(2)**GF(i)])
|
||
|
|
||
|
return poly
|
||
|
|
||
|
class ReedSolomonException(Exception):
|
||
|
pass
|
||
|
|
||
|
def correct(data, ec):
|
||
|
n = len(ec)
|
||
|
|
||
|
data = Poly([GF(int(cw, 2)) for cw in data+ec])
|
||
|
##print("data", list(map(lambda c:c.val, data.coefs)))
|
||
|
print("r(x)", data)
|
||
|
|
||
|
syndrome = [0]*n
|
||
|
corrupted = False
|
||
|
for i in range(n):
|
||
|
syndrome[i] = data.eval(GF.EXP[i])
|
||
|
if syndrome[i].val != 0:
|
||
|
corrupted = True
|
||
|
|
||
|
if not corrupted:
|
||
|
print("No errors")
|
||
|
return data
|
||
|
|
||
|
syndrome = Poly(syndrome[::-1])
|
||
|
print("syndrome", syndrome)
|
||
|
|
||
|
#Find locator poly
|
||
|
sigma, omega = euclidean_algorithm(Poly([GF(1)]+[GF(0) for i in range(n)]), syndrome, n)
|
||
|
print("locator", sigma)
|
||
|
print("evaluator", omega)
|
||
|
error_loc = find_error_loc(sigma)
|
||
|
print("location", error_loc)
|
||
|
|
||
|
error_mag = find_error_mag(omega, error_loc)
|
||
|
print("mag", error_mag)
|
||
|
|
||
|
for i in range(len(error_loc)):
|
||
|
pos = GF(error_loc[i]).log()
|
||
|
pos = data.deg - pos.val - 1
|
||
|
if pos < 0:
|
||
|
raise ReedSolomonException("Bad error location")
|
||
|
|
||
|
data.coefs[pos] += GF(error_mag[i])
|
||
|
|
||
|
return data
|
||
|
|
||
|
def euclidean_algorithm(a, b, R):
|
||
|
if a.deg < b.deg:
|
||
|
a, b = b, a
|
||
|
|
||
|
r_last = a
|
||
|
r = b
|
||
|
t_last = Poly([GF(0)])
|
||
|
t = Poly([GF(1)])
|
||
|
|
||
|
while r.deg-1 >= int(R/2):
|
||
|
r_last_last = r_last
|
||
|
t_last_last = t_last
|
||
|
r_last = r
|
||
|
t_last = t
|
||
|
if r_last.coefs[0] == 0:
|
||
|
raise ReedSolomonException("r_{i-1} was zero")
|
||
|
|
||
|
r = r_last_last
|
||
|
q = Poly([GF(0)])
|
||
|
denom_lead_term = r_last.coefs[0]
|
||
|
dlt_inv = denom_lead_term ** GF(-1)
|
||
|
I = 0
|
||
|
while r.deg >= r_last.deg and r.coefs[0] != 0:
|
||
|
I += 1
|
||
|
deg_diff = r.deg - r_last.deg
|
||
|
scale = r.coefs[0] * dlt_inv
|
||
|
q += Poly([scale]+[GF(0) for i in range(deg_diff)])
|
||
|
r += r_last * Poly([scale]+[GF(0) for i in range(deg_diff)])
|
||
|
q.del_lead_zeros()
|
||
|
r.del_lead_zeros()
|
||
|
|
||
|
if I > 100:
|
||
|
raise ReedSolomonException("Too long")
|
||
|
|
||
|
t = (q * t_last).del_lead_zeros() + t_last_last
|
||
|
t.del_lead_zeros()
|
||
|
if r.deg >= r_last.deg:
|
||
|
raise ReedSolomonException("Division algorithm failed to reduce polynomial")
|
||
|
|
||
|
sigma_tilde_at_zero = t.coefs[-1]
|
||
|
if sigma_tilde_at_zero.val == 0:
|
||
|
raise ReedSolomonException("sigma_tilde(0) was zero")
|
||
|
|
||
|
inv = Poly([sigma_tilde_at_zero ** GF(-1)])
|
||
|
sigma = t * inv
|
||
|
omega = r * inv
|
||
|
|
||
|
return [sigma, omega]
|
||
|
|
||
|
def find_error_loc(error_loc):
|
||
|
num_errors = error_loc.deg-1
|
||
|
if num_errors == 1:
|
||
|
return [error_loc.coefs[-2].val]
|
||
|
|
||
|
result = [0]*num_errors
|
||
|
e = 0
|
||
|
i = 1
|
||
|
while i < 256 and e < num_errors:
|
||
|
if error_loc.eval(GF(i)).val == 0:
|
||
|
result[e] = (GF(i) ** GF(-1)).val
|
||
|
e += 1
|
||
|
|
||
|
i += 1
|
||
|
|
||
|
if e != num_errors:
|
||
|
raise ReedSolomonException("Error locator degree does not match number of roots")
|
||
|
|
||
|
return result
|
||
|
|
||
|
def find_error_mag(error_eval, error_loc):
|
||
|
s = len(error_loc)
|
||
|
result = [0]*s
|
||
|
for i in range(s):
|
||
|
xi_inv = GF(error_loc[i]) ** GF(-1)
|
||
|
denom = GF(1)
|
||
|
for j in range(s):
|
||
|
if i != j:
|
||
|
denom *= GF(1) + GF(error_loc[j]) * xi_inv
|
||
|
|
||
|
result[i] = ( error_eval.eval(xi_inv) * (denom ** GF(-1)) ).val
|
||
|
|
||
|
return result
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
m = Poly([GF(67),GF(111),GF(100),GF(101),GF(115)])
|
||
|
g = get_generator_poly(4)
|
||
|
print()
|
||
|
print(g)
|
||
|
print(m/g)
|
||
|
print()
|
||
|
|
||
|
data = [67,111,110,101,115]
|
||
|
#ec = [119,123,82]
|
||
|
ec = [50,166,245,58]
|
||
|
|
||
|
data = [f"{n:08b}" for n in data]
|
||
|
ec = [f"{n:08b}" for n in ec]
|
||
|
|
||
|
print(correct(data, ec))
|