123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- <?php
- /*
- * Copyright 2007 ZXing authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- namespace Zxing\Common\Reedsolomon;
- /**
- * <p>Implements Reed-Solomon decoding, as the name implies.</p>
- *
- * <p>The algorithm will not be explained here, but the following references were helpful
- * in creating this implementation:</p>
- *
- * <ul>
- * <li>Bruce Maggs.
- * <a href="http://www.cs.cmu.edu/afs/cs.cmu.edu/project/pscico-guyb/realworld/www/rs_decode.ps">
- * "Decoding Reed-Solomon Codes"</a> (see discussion of Forney's Formula)</li>
- * <li>J.I. Hall. <a href="www.mth.msu.edu/~jhall/classes/codenotes/GRS.pdf">
- * "Chapter 5. Generalized Reed-Solomon Codes"</a>
- * (see discussion of Euclidean algorithm)</li>
- * </ul>
- *
- * <p>Much credit is due to William Rucklidge since portions of this code are an indirect
- * port of his C++ Reed-Solomon implementation.</p>
- *
- * @author Sean Owen
- * @author William Rucklidge
- * @author sanfordsquires
- */
- final class ReedSolomonDecoder
- {
- private $field;
- public function __construct($field)
- {
- $this->field = $field;
- }
- /**
- * <p>Decodes given set of received codewords, which include both data and error-correction
- * codewords. Really, this means it uses Reed-Solomon to detect and correct errors, in-place,
- * in the input.</p>
- *
- * @param received data and error-correction codewords
- * @param twoS number of error-correction codewords available
- *
- * @throws ReedSolomonException if decoding fails for any reason
- */
- public function decode(&$received, $twoS)
- {
- $poly = new GenericGFPoly($this->field, $received);
- $syndromeCoefficients = fill_array(0, $twoS, 0);
- $noError = true;
- for ($i = 0; $i < $twoS; $i++) {
- $eval = $poly->evaluateAt($this->field->exp($i + $this->field->getGeneratorBase()));
- $syndromeCoefficients[count($syndromeCoefficients) - 1 - $i] = $eval;
- if ($eval != 0) {
- $noError = false;
- }
- }
- if ($noError) {
- return;
- }
- $syndrome = new GenericGFPoly($this->field, $syndromeCoefficients);
- $sigmaOmega =
- $this->runEuclideanAlgorithm($this->field->buildMonomial($twoS, 1), $syndrome, $twoS);
- $sigma = $sigmaOmega[0];
- $omega = $sigmaOmega[1];
- $errorLocations = $this->findErrorLocations($sigma);
- $errorMagnitudes = $this->findErrorMagnitudes($omega, $errorLocations);
- $errorLocationsCount = count($errorLocations);
- for ($i = 0; $i < $errorLocationsCount; $i++) {
- $position = count($received) - 1 - $this->field->log($errorLocations[$i]);
- if ($position < 0) {
- throw new ReedSolomonException("Bad error location");
- }
- $received[$position] = GenericGF::addOrSubtract($received[$position], $errorMagnitudes[$i]);
- }
- }
- private function runEuclideanAlgorithm($a, $b, $R)
- {
- // Assume a's degree is >= b's
- if ($a->getDegree() < $b->getDegree()) {
- $temp = $a;
- $a = $b;
- $b = $temp;
- }
- $rLast = $a;
- $r = $b;
- $tLast = $this->field->getZero();
- $t = $this->field->getOne();
- // Run Euclidean algorithm until r's degree is less than R/2
- while ($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?
- throw new ReedSolomonException("r_{i-1} was zero");
- }
- $r = $rLastLast;
- $q = $this->field->getZero();
- $denominatorLeadingTerm = $rLast->getCoefficient($rLast->getDegree());
- $dltInverse = $this->field->inverse($denominatorLeadingTerm);
- while ($r->getDegree() >= $rLast->getDegree() && !$r->isZero()) {
- $degreeDiff = $r->getDegree() - $rLast->getDegree();
- $scale = $this->field->multiply($r->getCoefficient($r->getDegree()), $dltInverse);
- $q = $q->addOrSubtract($this->field->buildMonomial($degreeDiff, $scale));
- $r = $r->addOrSubtract($rLast->multiplyByMonomial($degreeDiff, $scale));
- }
- $t = $q->multiply($tLast)->addOrSubtract($tLastLast);
- if ($r->getDegree() >= $rLast->getDegree()) {
- throw new ReedSolomonException("Division algorithm failed to reduce polynomial?");
- }
- }
- $sigmaTildeAtZero = $t->getCoefficient(0);
- if ($sigmaTildeAtZero == 0) {
- throw new ReedSolomonException("sigmaTilde(0) was zero");
- }
- $inverse = $this->field->inverse($sigmaTildeAtZero);
- $sigma = $t->multiply($inverse);
- $omega = $r->multiply($inverse);
- return [$sigma, $omega];
- }
- private function findErrorLocations($errorLocator)
- {
- // This is a direct application of Chien's search
- $numErrors = $errorLocator->getDegree();
- if ($numErrors == 1) { // shortcut
- return [$errorLocator->getCoefficient(1)];
- }
- $result = fill_array(0, $numErrors, 0);
- $e = 0;
- for ($i = 1; $i < $this->field->getSize() && $e < $numErrors; $i++) {
- if ($errorLocator->evaluateAt($i) == 0) {
- $result[$e] = $this->field->inverse($i);
- $e++;
- }
- }
- if ($e != $numErrors) {
- throw new ReedSolomonException("Error locator degree does not match number of roots");
- }
- return $result;
- }
- private function findErrorMagnitudes($errorEvaluator, $errorLocations)
- {
- // This is directly applying Forney's Formula
- $s = count($errorLocations);
- $result = fill_array(0, $s, 0);
- for ($i = 0; $i < $s; $i++) {
- $xiInverse = $this->field->inverse($errorLocations[$i]);
- $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);
- $termPlus1 = ($term & 0x1) == 0 ? $term | 1 : $term & ~1;
- $denominator = $this->field->multiply($denominator, $termPlus1);
- }
- }
- $result[$i] = $this->field->multiply($errorEvaluator->evaluateAt($xiInverse),
- $this->field->inverse($denominator));
- if ($this->field->getGeneratorBase() != 0) {
- $result[$i] = $this->field->multiply($result[$i], $xiInverse);
- }
- }
- return $result;
- }
- }
|