You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

ReedSolomonDecoder.cs 8.6 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. /*
  2. * Copyright 2007 ZXing authors
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. namespace ZXing.Common.ReedSolomon
  17. {
  18. /// <summary> <p>Implements Reed-Solomon decoding, as the name implies.</p>
  19. ///
  20. /// <p>The algorithm will not be explained here, but the following references were helpful
  21. /// in creating this implementation:</p>
  22. ///
  23. /// <ul>
  24. /// <li>Bruce Maggs.
  25. /// <a href="http://www.cs.cmu.edu/afs/cs.cmu.edu/project/pscico-guyb/realworld/www/rs_decode.ps">
  26. /// "Decoding Reed-Solomon Codes"</a> (see discussion of Forney's Formula)</li>
  27. /// <li>J.I. Hall. <a href="www.mth.msu.edu/~jhall/classes/codenotes/GRS.pdf">
  28. /// "Chapter 5. Generalized Reed-Solomon Codes"</a>
  29. /// (see discussion of Euclidean algorithm)</li>
  30. /// </ul>
  31. ///
  32. /// <p>Much credit is due to William Rucklidge since portions of this code are an indirect
  33. /// port of his C++ Reed-Solomon implementation.</p>
  34. ///
  35. /// </summary>
  36. /// <author>Sean Owen</author>
  37. /// <author>William Rucklidge</author>
  38. /// <author>sanfordsquires</author>
  39. public sealed class ReedSolomonDecoder
  40. {
  41. private readonly GenericGF field;
  42. public ReedSolomonDecoder(GenericGF field)
  43. {
  44. this.field = field;
  45. }
  46. /// <summary>
  47. /// <p>Decodes given set of received codewords, which include both data and error-correction
  48. /// codewords. Really, this means it uses Reed-Solomon to detect and correct errors, in-place,
  49. /// in the input.</p>
  50. /// </summary>
  51. /// <param name="received">data and error-correction codewords</param>
  52. /// <param name="twoS">number of error-correction codewords available</param>
  53. /// <returns>false: decoding fails</returns>
  54. public bool decode(int[] received, int twoS)
  55. {
  56. var poly = new GenericGFPoly(field, received);
  57. var syndromeCoefficients = new int[twoS];
  58. var noError = true;
  59. for (var i = 0; i < twoS; i++)
  60. {
  61. var eval = poly.evaluateAt(field.exp(i + field.GeneratorBase));
  62. syndromeCoefficients[syndromeCoefficients.Length - 1 - i] = eval;
  63. if (eval != 0)
  64. {
  65. noError = false;
  66. }
  67. }
  68. if (noError)
  69. {
  70. return true;
  71. }
  72. var syndrome = new GenericGFPoly(field, syndromeCoefficients);
  73. var sigmaOmega = runEuclideanAlgorithm(field.buildMonomial(twoS, 1), syndrome, twoS);
  74. if (sigmaOmega == null)
  75. return false;
  76. var sigma = sigmaOmega[0];
  77. var errorLocations = findErrorLocations(sigma);
  78. if (errorLocations == null)
  79. return false;
  80. var omega = sigmaOmega[1];
  81. var errorMagnitudes = findErrorMagnitudes(omega, errorLocations);
  82. for (var i = 0; i < errorLocations.Length; i++)
  83. {
  84. var position = received.Length - 1 - field.log(errorLocations[i]);
  85. if (position < 0)
  86. {
  87. // throw new ReedSolomonException("Bad error location");
  88. return false;
  89. }
  90. received[position] = GenericGF.addOrSubtract(received[position], errorMagnitudes[i]);
  91. }
  92. return true;
  93. }
  94. internal GenericGFPoly[] runEuclideanAlgorithm(GenericGFPoly a, GenericGFPoly b, int R)
  95. {
  96. // Assume a's degree is >= b's
  97. if (a.Degree < b.Degree)
  98. {
  99. GenericGFPoly temp = a;
  100. a = b;
  101. b = temp;
  102. }
  103. GenericGFPoly rLast = a;
  104. GenericGFPoly r = b;
  105. GenericGFPoly tLast = field.Zero;
  106. GenericGFPoly t = field.One;
  107. // Run Euclidean algorithm until r's degree is less than R/2
  108. while (r.Degree >= R / 2)
  109. {
  110. GenericGFPoly rLastLast = rLast;
  111. GenericGFPoly tLastLast = tLast;
  112. rLast = r;
  113. tLast = t;
  114. // Divide rLastLast by rLast, with quotient in q and remainder in r
  115. if (rLast.isZero)
  116. {
  117. // Oops, Euclidean algorithm already terminated?
  118. // throw new ReedSolomonException("r_{i-1} was zero");
  119. return null;
  120. }
  121. r = rLastLast;
  122. GenericGFPoly q = field.Zero;
  123. int denominatorLeadingTerm = rLast.getCoefficient(rLast.Degree);
  124. int dltInverse = field.inverse(denominatorLeadingTerm);
  125. while (r.Degree >= rLast.Degree && !r.isZero)
  126. {
  127. int degreeDiff = r.Degree - rLast.Degree;
  128. int scale = field.multiply(r.getCoefficient(r.Degree), dltInverse);
  129. q = q.addOrSubtract(field.buildMonomial(degreeDiff, scale));
  130. r = r.addOrSubtract(rLast.multiplyByMonomial(degreeDiff, scale));
  131. }
  132. t = q.multiply(tLast).addOrSubtract(tLastLast);
  133. if (r.Degree >= rLast.Degree)
  134. {
  135. // throw new IllegalStateException("Division algorithm failed to reduce polynomial?");
  136. return null;
  137. }
  138. }
  139. int sigmaTildeAtZero = t.getCoefficient(0);
  140. if (sigmaTildeAtZero == 0)
  141. {
  142. // throw new ReedSolomonException("sigmaTilde(0) was zero");
  143. return null;
  144. }
  145. int inverse = field.inverse(sigmaTildeAtZero);
  146. GenericGFPoly sigma = t.multiply(inverse);
  147. GenericGFPoly omega = r.multiply(inverse);
  148. return new GenericGFPoly[] { sigma, omega };
  149. }
  150. private int[] findErrorLocations(GenericGFPoly errorLocator)
  151. {
  152. // This is a direct application of Chien's search
  153. int numErrors = errorLocator.Degree;
  154. if (numErrors == 1)
  155. {
  156. // shortcut
  157. return new int[] { errorLocator.getCoefficient(1) };
  158. }
  159. int[] result = new int[numErrors];
  160. int e = 0;
  161. for (int i = 1; i < field.Size && e < numErrors; i++)
  162. {
  163. if (errorLocator.evaluateAt(i) == 0)
  164. {
  165. result[e] = field.inverse(i);
  166. e++;
  167. }
  168. }
  169. if (e != numErrors)
  170. {
  171. // throw new ReedSolomonException("Error locator degree does not match number of roots");
  172. return null;
  173. }
  174. return result;
  175. }
  176. private int[] findErrorMagnitudes(GenericGFPoly errorEvaluator, int[] errorLocations)
  177. {
  178. // This is directly applying Forney's Formula
  179. int s = errorLocations.Length;
  180. int[] result = new int[s];
  181. for (int i = 0; i < s; i++)
  182. {
  183. int xiInverse = field.inverse(errorLocations[i]);
  184. int denominator = 1;
  185. for (int j = 0; j < s; j++)
  186. {
  187. if (i != j)
  188. {
  189. //denominator = field.multiply(denominator,
  190. // GenericGF.addOrSubtract(1, field.multiply(errorLocations[j], xiInverse)));
  191. // Above should work but fails on some Apple and Linux JDKs due to a Hotspot bug.
  192. // Below is a funny-looking workaround from Steven Parkes
  193. int term = field.multiply(errorLocations[j], xiInverse);
  194. int termPlus1 = (term & 0x1) == 0 ? term | 1 : term & ~1;
  195. denominator = field.multiply(denominator, termPlus1);
  196. // removed in java version, not sure if this is right
  197. // denominator = field.multiply(denominator, GenericGF.addOrSubtract(1, field.multiply(errorLocations[j], xiInverse)));
  198. }
  199. }
  200. result[i] = field.multiply(errorEvaluator.evaluateAt(xiInverse), field.inverse(denominator));
  201. if (field.GeneratorBase != 0)
  202. {
  203. result[i] = field.multiply(result[i], xiInverse);
  204. }
  205. }
  206. return result;
  207. }
  208. }
  209. }