fckeuspy-go/vendor/github.com/makiuchi-d/gozxing/common/reedsolomon/reedsolomon_decoder.go
2025-09-28 21:03:39 +02:00

205 lines
5.5 KiB
Go

package reedsolomon
import (
errors "golang.org/x/xerrors"
)
type ReedSolomonDecoder struct {
field *GenericGF
}
func NewReedSolomonDecoder(field *GenericGF) *ReedSolomonDecoder {
return &ReedSolomonDecoder{field}
}
func (this *ReedSolomonDecoder) Decode(received []int, twoS int) ReedSolomonException {
poly, e := NewGenericGFPoly(this.field, received)
if e != nil {
return WrapReedSolomonException(e)
}
syndromeCoefficients := make([]int, twoS)
noError := true
for i := 0; i < twoS; i++ {
eval := poly.EvaluateAt(this.field.Exp(i + this.field.GetGeneratorBase()))
syndromeCoefficients[len(syndromeCoefficients)-1-i] = eval
if eval != 0 {
noError = false
}
}
if noError {
return nil
}
syndrome, e := NewGenericGFPoly(this.field, syndromeCoefficients)
if e != nil {
return WrapReedSolomonException(e)
}
monomial, e := this.field.BuildMonomial(twoS, 1)
if e != nil {
return WrapReedSolomonException(e)
}
sigma, omega, e := this.runEuclideanAlgorithm(monomial, syndrome, twoS)
if e != nil {
return WrapReedSolomonException(e)
}
errorLocations, e := this.findErrorLocations(sigma)
if e != nil {
return WrapReedSolomonException(e)
}
errorMagnitudes, e := this.findErrorMagnitudes(omega, errorLocations)
if e != nil {
return WrapReedSolomonException(e)
}
for i := 0; i < len(errorLocations); i++ {
log, e := this.field.Log(errorLocations[i])
if e != nil {
return WrapReedSolomonException(e)
}
position := len(received) - 1 - log
if position < 0 {
return NewReedSolomonException("Bad error location")
}
received[position] = GenericGF_addOrSubtract(received[position], errorMagnitudes[i])
}
return nil
}
func (this *ReedSolomonDecoder) runEuclideanAlgorithm(a, b *GenericGFPoly, R int) (sigma, omega *GenericGFPoly, e error) {
// Assume a's degree is >= b's
if a.GetDegree() < b.GetDegree() {
a, b = b, a
}
rLast := a
r := b
tLast := this.field.GetZero()
t := this.field.GetOne()
// Run Euclidean algorithm until r's degree is less than R/2
for r.GetDegree() >= R/2 {
rLastLast := rLast
tLastLast := tLast
rLast = r
tLast = t
// Divide rLastLast by rLast, with quotient in q and remainder in r
if rLast.IsZero() {
// Oops, Euclidean algorithm already terminated?
return nil, nil, NewReedSolomonException("r_{i-1} was zero")
}
r = rLastLast
q := this.field.GetZero()
denominatorLeadingTerm := rLast.GetCoefficient(rLast.GetDegree())
dltInverse, e := this.field.Inverse(denominatorLeadingTerm)
if e != nil {
return nil, nil, e
}
for r.GetDegree() >= rLast.GetDegree() && !r.IsZero() {
degreeDiff := r.GetDegree() - rLast.GetDegree()
scale := this.field.Multiply(r.GetCoefficient(r.GetDegree()), dltInverse)
monomial, e := this.field.BuildMonomial(degreeDiff, scale)
if e != nil {
return nil, nil, e
}
q, e = q.AddOrSubtract(monomial)
if e != nil {
return nil, nil, e
}
polynomial, e := rLast.MultiplyByMonomial(degreeDiff, scale)
if e != nil {
return nil, nil, e
}
r, e = r.AddOrSubtract(polynomial)
if e != nil {
return nil, nil, e
}
}
q, e = q.Multiply(tLast)
if e != nil {
return nil, nil, e
}
t, e = q.AddOrSubtract(tLastLast)
if e != nil {
return nil, nil, e
}
if r.GetDegree() >= rLast.GetDegree() {
return nil, nil, errors.New("IllegalStateException: Division algorithm failed to reduce polynomial?")
}
}
sigmaTildeAtZero := t.GetCoefficient(0)
if sigmaTildeAtZero == 0 {
return nil, nil, NewReedSolomonException("sigmaTilde(0) was zero")
}
inverse, e := this.field.Inverse(sigmaTildeAtZero)
if e != nil {
return nil, nil, e
}
return t.MultiplyBy(inverse), r.MultiplyBy(inverse), nil
}
func (this *ReedSolomonDecoder) findErrorLocations(errorLocator *GenericGFPoly) ([]int, error) {
// This is a direct application of Chien's search
numErrors := errorLocator.GetDegree()
if numErrors == 1 { // shortcut
return []int{errorLocator.GetCoefficient(1)}, nil
}
result := make([]int, numErrors)
e := 0
for i := 1; i < this.field.GetSize() && e < numErrors; i++ {
if errorLocator.EvaluateAt(i) == 0 {
var err error
result[e], err = this.field.Inverse(i)
if err != nil {
return nil, err
}
e++
}
}
if e != numErrors {
return nil, NewReedSolomonException("Error locator degree does not match number of roots")
}
return result, nil
}
func (this *ReedSolomonDecoder) findErrorMagnitudes(errorEvaluator *GenericGFPoly, errorLocations []int) ([]int, error) {
// This is directly applying Forney's Formula
s := len(errorLocations)
result := make([]int, s)
for i := 0; i < s; i++ {
xiInverse, e := this.field.Inverse(errorLocations[i])
if e != nil {
return nil, e
}
denominator := 1
for j := 0; j < s; j++ {
if i != j {
//denominator = field.multiply(denominator,
// GenericGF.addOrSubtract(1, field.multiply(errorLocations[j], xiInverse)));
// Above should work but fails on some Apple and Linux JDKs due to a Hotspot bug.
// Below is a funny-looking workaround from Steven Parkes
term := this.field.Multiply(errorLocations[j], xiInverse)
var termPlus1 int
if (term & 0x1) == 0 {
termPlus1 = term | 1
} else {
termPlus1 = term & ^1
}
denominator = this.field.Multiply(denominator, termPlus1)
}
}
inverse, e := this.field.Inverse(denominator)
if e != nil {
return nil, e
}
result[i] = this.field.Multiply(errorEvaluator.EvaluateAt(xiInverse), inverse)
if this.field.GetGeneratorBase() != 0 {
result[i] = this.field.Multiply(result[i], xiInverse)
}
}
return result, nil
}