├── .gitignore ├── .travis-ci.sh ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── dune-project ├── notebooks └── rs-library-tutorial.ipynb ├── reedsolomon.opam ├── src ├── codec.ml ├── codec.mli ├── dune ├── galois.ml ├── galois.mli ├── iter.ml ├── iter.mli ├── matrix.ml ├── matrix.mli ├── ops.ml ├── ops.mli ├── poly.ml └── poly.mli ├── test ├── dune └── test_iter.ml └── webdemo ├── dune └── rswebdemo.ml /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | _build 3 | .*.swp 4 | *.js 5 | *.byte 6 | .merlin 7 | *.install 8 | -------------------------------------------------------------------------------- /.travis-ci.sh: -------------------------------------------------------------------------------- 1 | # Edit this for your own project dependencies 2 | OPAM_DEPENDS="ocamlfind" 3 | 4 | # install OCaml + OPAM 5 | case "$OCAML_VERSION,$OPAM_VERSION" in 6 | 3.12.1,1.0.0) ppa=avsm/ocaml312+opam10 ;; 7 | 3.12.1,1.1.0) ppa=avsm/ocaml312+opam11 ;; 8 | 4.00.1,1.0.0) ppa=avsm/ocaml40+opam10 ;; 9 | 4.00.1,1.1.0) ppa=avsm/ocaml40+opam11 ;; 10 | 4.01.0,1.0.0) ppa=avsm/ocaml41+opam10 ;; 11 | 4.01.0,1.1.0) ppa=avsm/ocaml41+opam11 ;; 12 | *) echo Unknown $OCAML_VERSION,$OPAM_VERSION; exit 1 ;; 13 | esac 14 | 15 | echo "yes" | sudo add-apt-repository ppa:$ppa 16 | sudo apt-get update -qq 17 | sudo apt-get install -qq ocaml ocaml-native-compilers camlp4-extra opam 18 | export OPAMYES=1 19 | opam init 20 | opam install ${OPAM_DEPENDS} 21 | eval `opam config env` 22 | 23 | # Test 24 | make 25 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | script: bash -ex .travis-ci.sh 3 | env: 4 | - OCAML_VERSION=4.01.0 OPAM_VERSION=1.1.0 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 MicroJamJar Ltd. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean all install uninstall rswebdemo 2 | 3 | all: 4 | jbuilder build @install 5 | 6 | install: all 7 | jbuilder install 8 | 9 | uninstall: 10 | jbuilder uninstall 11 | 12 | rswebdemo: all 13 | jbuilder build webdemo/rswebdemo.bc.js 14 | 15 | test: all 16 | jbuilder build test/test_iter.exe 17 | ./_build/default/test/test_iter.exe 18 | 19 | clean: 20 | rm -fr _build 21 | find . -name "*~" | xargs rm 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | reedsolomon 2 | =========== 3 | 4 | [![Build Status](https://travis-ci.org/ujamjar/reedsolomon.svg?branch=master)](https://travis-ci.org/ujamjar/reedsolomon) 5 | 6 | Reed-Solomon Error Correction CODEC in OCaml. 7 | 8 | The code in the modules Poly, Matrix and Codec is pretty abstract and 9 | not very efficient, however, it implements a few different options 10 | for decoding Reed-Solomon codes (Peterson, Euclid and Berlekamp-Massey) 11 | and both error and erasure correction. 12 | 13 | A much faster, error correction only, implementation is 14 | provided in the Iter module (not blazingly fast, but not too bad). 15 | 16 | ``` 17 | open Reedsolomon.Iter 18 | 19 | (* code parameters *) 20 | let param = 21 | { 22 | m = 8; 23 | k = 239; 24 | t = 8; 25 | n = 255; 26 | b = 0; 27 | prim_poly = 285; 28 | prim_elt = 2; 29 | } 30 | 31 | (* construct rs codec *) 32 | let rs = Reedsolomon.Iter.init param 33 | 34 | (* construct parity from data 35 | 36 | Array.length data = param.k 37 | Array.length parity = param.t * 2 *) 38 | let () = rs.encode data parity 39 | 40 | (* message to send 41 | 42 | Array.length message = param.n *) 43 | let message = Array.concat [ data; parity ] 44 | 45 | (* decode the received and potentially err'd message 46 | 47 | Array.length received = param.n 48 | Array.length corrected = param.n *) 49 | let n_corrections = rs.decode received corrected 50 | ``` 51 | 52 | -------------------------------------------------------------------------------- /dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 1.2) 2 | -------------------------------------------------------------------------------- /notebooks/rs-library-tutorial.ipynb: -------------------------------------------------------------------------------- 1 | {"metadata":{"name":"","language":"ocaml"},"worksheets":[{"cells":[{"metadata":{},"cell_type":"heading","source":"Reed-solomon CODEC","level":1},{"metadata":{},"cell_type":"markdown","source":"This notebook will describe a Reed-Solomon CODEC library for OCaml."},{"metadata":{},"cell_type":"heading","source":"Building","level":2},{"metadata":{},"cell_type":"markdown","source":"```bash\n$ make\n$ make install\n```"},{"metadata":{},"cell_type":"heading","source":"Library structure","level":2},{"metadata":{},"cell_type":"markdown","source":"* `Ops` - interface for basic mathematical operations.\n* `Poly, Matrix` - polynomial and matrix operations\n* `Galois` - galois field arithmetic operations, including extension fields\n* `Rs` - high level Reed-Solomon CODEC implementation"},{"metadata":{},"cell_type":"heading","source":"Using the library","level":2},{"metadata":{},"input":"#require \"reedsolomon,iocaml.notebook\"","cell_type":"code","prompt_number":1,"outputs":[{"output_type":"stream","text":"\tCamlp4 Parsing version 4.01.0\n\n","stream":"stdout"},{"output_type":"stream","text":"/home/andyman/.opam/4.01.0-test/lib/reedsolomon: added to search path\n/home/andyman/.opam/4.01.0-test/lib/reedsolomon/reedsolomon.cma: loaded\n","stream":"stderr"},{"output_type":"stream","text":"/home/andyman/.opam/4.01.0-test/lib/iocaml: added to search path\n","stream":"stderr"}],"language":"python","collapsed":false},{"metadata":{},"cell_type":"markdown","source":"The `Rs` module defines a few pre-built standards. For now we will use `BBCTest` which describes the RS code from this excellent [whitepaper](http://www.bbc.co.uk/rd/publications/whitepaper031)."},{"metadata":{},"input":"module X = Rs.BBCTest","cell_type":"code","prompt_number":2,"outputs":[{"output_type":"pyout","prompt_number":2,"html":"
module X :\n  sig\n    module Gp : sig val pp : int val pe : int end\n    module G :\n      sig\n        type t = int\n        val zero : t\n        val one : t\n        val ( +: ) : t -> t -> t\n        val ( -: ) : t -> t -> t\n        val to_string : t -> string\n        val alpha : t\n        val n_elems : int\n        val log : t -> int\n        val antilog : int -> t\n        val ( *: ) : t -> t -> t\n        val ( /: ) : t -> t -> t\n        val ( **: ) : t -> int -> t\n        val inv : t -> t\n      end\n    module Rp : sig val k : int val t : int val b : int end\n    module R :\n      sig\n        type elt = int\n        module M :\n          sig\n            type t = elt\n            type matrix = t array array\n            val rows : matrix -> int\n            val cols : matrix -> int\n            val init : int -> int -> (int -> int -> t) -> matrix\n            val create : int -> int -> matrix\n            val copy : matrix -> matrix\n            val identity : int -> matrix\n            val transpose : matrix -> matrix\n            val map : (t -> t) -> matrix -> matrix\n            val map2 : (t -> t -> t) -> matrix -> matrix -> matrix\n            val row_vector : t array -> matrix\n            val col_vector : t array -> matrix\n            val ( >> ) : matrix -> matrix -> matrix\n            val ( ^^ ) : matrix -> matrix -> matrix\n            val sub : int -> int -> int -> int -> matrix -> matrix\n            val ( +: ) : matrix -> matrix -> matrix\n            val ( -: ) : matrix -> matrix -> matrix\n            val ( *: ) : matrix -> matrix -> matrix\n            val ( *:. ) : matrix -> t -> matrix\n            val minor : int -> int -> matrix -> matrix\n            val det : matrix -> t\n            val adjoint_inverse : matrix -> t * matrix\n            val gauss_jordan : matrix -> matrix\n            val gauss_jordan_inverse : matrix -> matrix\n            module Row :\n              sig\n                val swap : int -> int -> int -> matrix\n                val mult : int -> int -> t -> matrix\n                val madd : int -> int -> int -> t -> matrix\n              end\n          end\n        module R :\n          sig\n            type elt = elt\n            type t = elt array\n            val degree : t -> int\n            val zero : t\n            val one : t\n            val x : t\n            val to_poly : elt array -> t\n            val of_poly : t -> elt array\n            val copy : t -> t\n            type poly_format =\n              Rs.BBCTest.R.R.poly_format = {\n              coef : elt -> string;\n              indet : int -> string;\n            }\n            val poly_format : poly_format\n            val string_format : bool -> poly_format -> int -> elt -> string\n            val to_string :\n              ?down:bool -> ?str:(int -> elt -> string) -> t -> string\n            val trim : t -> t\n            val slice : t -> int -> t\n            val ( +: ) : t -> t -> t\n            val ( -: ) : t -> t -> t\n            val ( *: ) : t -> t -> t\n            val ( /: ) : t -> t -> t * t\n            val ( *:. ) : t -> elt -> t\n            val ( /:. ) : t -> elt -> t\n            val ( ^: ) : t -> int -> t\n            val ( **: ) : t -> int -> t\n            val ext_gcd : t -> t -> t * t\n            val eval : t -> elt -> elt\n          end\n        type poly = R.t\n        type loc = int\n        val root : int -> elt\n        val generator : poly\n        val xn : int -> poly\n        val x2t : poly\n        val parity : poly -> poly\n        val encode : poly -> poly\n        val horner : poly -> elt -> elt\n        val syndromes : poly -> poly\n        val key_equations : poly -> int -> M.matrix * M.matrix\n        val solve_key_equations : M.matrix * M.matrix -> M.matrix\n        val peterson : poly -> poly\n        val euclid_inner : poly * poly -> poly * poly -> poly * poly\n        val euclid : ?norm:bool -> ?lim:int -> poly -> poly * poly\n        val berlekamp_massey_iter :\n          poly -> int -> poly * poly * int -> poly * poly * int\n        val berlekamp_massey : poly -> poly\n        module Sarwate :\n          sig\n            val iBM : poly -> poly\n            val riBM : poly -> poly * poly\n            val rriBM : poly -> poly * poly\n            val forney : poly -> poly -> loc -> elt\n          end\n        val chien : poly -> loc list\n        val error_location : loc -> int\n        val error_magnitude : int -> poly -> poly -> poly\n        val deriv : poly -> poly\n        val forney : poly -> poly -> loc -> elt\n        val error : elt list -> loc list -> poly\n        val correct : poly -> poly -> poly\n        val decode_euclid : poly -> poly\n        val decode_berlekamp_massey : poly -> poly\n        val decode_peterson : poly -> poly\n        val decode : poly -> poly\n        val erasure_locator : int list -> poly\n        val zero_erasures : poly -> int list -> poly\n        val error_and_erasure :\n          elt list -> loc list -> elt list -> loc list -> poly\n        val decode_erasures_euclid : poly -> int list -> poly\n        val decode_erasures : poly -> int list -> poly\n        val decode_errors_and_erasures_euclid : poly -> int list -> poly\n        val decode_errors_and_erasures_berlekamp_massey :\n          poly -> int list -> poly\n        val decode_errors_and_erasures : poly -> int list -> poly\n      end\n  end\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"cell_type":"markdown","source":"There are 4 modules defined therein;\n\n* `Gp` - parameters for the Galois field\n* `G` - Galois field implementation\n* `Rp` - parameters for the RS CODEC\n* `R` - Reed-Solomon CODEC implemention\n\nLets look at some of those parameters."},{"metadata":{},"input":"let pp, pe = X.Gp.pp, X.Gp.pe","cell_type":"code","prompt_number":3,"outputs":[{"output_type":"pyout","prompt_number":3,"html":"
val pp : int = 19\nval pe : int = 2\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"cell_type":"markdown","source":"* pp - primtive polynomial of galois field\n* pe - primtive element of galois field"},{"metadata":{},"input":"let k, t = X.Rp.k, X.Rp.t\nlet n = k + (2*t)","cell_type":"code","prompt_number":4,"outputs":[{"output_type":"pyout","prompt_number":4,"html":"
val k : int = 11\nval t : int = 2\n
","metadata":{}},{"output_type":"pyout","prompt_number":4,"html":"
val n : int = 15\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"cell_type":"markdown","source":"* `k` - number of symbols in a message\n* `t` - error correction capability\n* `n` - code word size"},{"metadata":{},"cell_type":"heading","source":"Galois field elements","level":2},{"metadata":{},"input":"let number_of_field_elements = X.G.n_elems","cell_type":"code","prompt_number":5,"outputs":[{"output_type":"pyout","prompt_number":5,"html":"
val number_of_field_elements : int = 16\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"cell_type":"markdown","source":"This tells us something about the Galois field - specifically that it has 16 elements. They are represented by integers in the range 0 to 15.\n\nWe can do arithmetic operations over the field using the functions in `X.G`. "},{"metadata":{},"input":"(* utility function to display a html table *)\nlet table rows cols f =\n let tag x s = \"<\" ^ x ^ \">\" ^ s ^ \"\" in\n let concat x s = s |> Array.map (tag x) |> Array.to_list |> String.concat \"\" in\n let init n x f = Array.init n f |> concat x in\n Iocaml.display \"text/html\" \n (\"\" ^ (init rows \"tr\" (fun row -> init cols \"td\" (f row))) ^ \"
\")","cell_type":"code","prompt_number":6,"outputs":[{"output_type":"pyout","prompt_number":6,"html":"
val table : int -> int -> (int -> int -> string) -> unit = <fun>\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"input":"table 16 16 (fun i j -> X.G.(to_string (i +: j)))","cell_type":"code","prompt_number":7,"outputs":[{"output_type":"display_data","html":"
0123456789101112131415
1032547698111013121514
2301674510118914151213
3210765411109815141312
4567012312131415891011
5476103213121514981110
6745230114151213101189
7654321015141312111098
8910111213141501234567
9811101312151410325476
1011891415121323016745
1110981514131232107654
1213141589101145670123
1312151498111054761032
1415121310118967452301
1514131211109876543210
","metadata":{}},{"output_type":"pyout","prompt_number":7,"html":"
- : unit = ()\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"input":"table 16 16 (fun i j -> X.G.(to_string (i *: j)))","cell_type":"code","prompt_number":8,"outputs":[{"output_type":"display_data","html":"
0000000000000000
0123456789101112131415
0246810121431751191513
0365121510911813147412
0481237111562141051139
0510157213814114191236
0612101113715391514824
0714915816131034251211
0831161451312415710291
0918211310413512615714
0107131449315582111612
0115141011547122913683
0121175914210611315348
0139411285215116314107
0141511332129768410115
0151329641111412387510
","metadata":{}},{"output_type":"pyout","prompt_number":8,"html":"
- : unit = ()\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"input":"table 1 16 (fun _ i -> X.G.(to_string (inv i)))","cell_type":"code","prompt_number":9,"outputs":[{"output_type":"display_data","html":"
0191413117615212510438
","metadata":{}},{"output_type":"pyout","prompt_number":9,"html":"
- : unit = ()\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"cell_type":"heading","source":"Polynomials","level":2},{"metadata":{},"cell_type":"markdown","source":"The `Poly` module represents polynomials as arrays of coefficients. The coefficient type is generic and provided as an argument to the Poly.Make functor.\n\nThe indices of the array give the power of the indeterminate. For example"},{"metadata":{},"input":"module IPoly = Poly.Make(Ops.Int)","cell_type":"code","prompt_number":10,"outputs":[{"output_type":"pyout","prompt_number":10,"html":"
module IPoly :\n  sig\n    type elt = Ops.Int.t\n    type t = Ops.Int.t array\n    val degree : t -> int\n    val zero : t\n    val one : t\n    val x : t\n    val to_poly : elt array -> t\n    val of_poly : t -> elt array\n    val copy : t -> t\n    type poly_format =\n      Poly.Make(Ops.Int).poly_format = {\n      coef : elt -> string;\n      indet : int -> string;\n    }\n    val poly_format : poly_format\n    val string_format : bool -> poly_format -> int -> elt -> string\n    val to_string : ?down:bool -> ?str:(int -> elt -> string) -> t -> string\n    val trim : t -> t\n    val slice : t -> int -> t\n    val ( +: ) : t -> t -> t\n    val ( -: ) : t -> t -> t\n    val ( *: ) : t -> t -> t\n    val ( /: ) : t -> t -> t * t\n    val ( *:. ) : t -> elt -> t\n    val ( /:. ) : t -> elt -> t\n    val ( ^: ) : t -> int -> t\n    val ( **: ) : t -> int -> t\n    val ext_gcd : t -> t -> t * t\n    val eval : t -> elt -> elt\n  end\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"input":"(* display poly *)\nlet display_poly poly = \n (* modifiy printing a little to suit mathjax *)\n let str = X.R.R.(string_format true \n { poly_format with \n indet = function 0 -> \"\" | 1 -> \"x\" | _ as n -> \"x^{\" ^ string_of_int n ^ \"}\"; }) in\n Iocaml.display \"text/html\" (\"$$\" ^ X.R.R.to_string ~str:str poly ^ \"$$\")","cell_type":"code","prompt_number":11,"outputs":[{"output_type":"pyout","prompt_number":11,"html":"
val display_poly : X.R.R.t -> unit = <fun>\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"input":"let f,g = [| 7; 3 |], [| 4; 0; 2 |]","cell_type":"code","prompt_number":12,"outputs":[{"output_type":"pyout","prompt_number":12,"html":"
val f : int array = [|7; 3|]\nval g : int array = [|4; 0; 2|]\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"input":"display_poly f;;\ndisplay_poly g;;","cell_type":"code","prompt_number":13,"outputs":[{"output_type":"display_data","html":"$$3.x + 7$$","metadata":{}},{"output_type":"display_data","html":"$$2.x^{2} + 4$$","metadata":{}},{"output_type":"pyout","prompt_number":13,"html":"
- : unit = ()\n
","metadata":{}},{"output_type":"pyout","prompt_number":13,"html":"
- : unit = ()\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"input":"display_poly IPoly.(f *: g)","cell_type":"code","prompt_number":14,"outputs":[{"output_type":"display_data","html":"$$6.x^{3} + 14.x^{2} + 12.x + 28$$","metadata":{}},{"output_type":"pyout","prompt_number":14,"html":"
- : unit = ()\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"cell_type":"heading","source":"More on Galois fields","level":2},{"metadata":{},"cell_type":"markdown","source":"The `Galois` module is actually quite general and can implement the fields in different ways. Indeed the integer representation above is actually derived from a lower level version based on polynomials over GF2."},{"metadata":{},"input":"table 2 2 (fun i j -> Galois.Primitive.GF2.(to_string (i +: j)));;\ntable 2 2 (fun i j -> Galois.Primitive.GF2.(to_string (i *: j)));;","cell_type":"code","prompt_number":15,"outputs":[{"output_type":"display_data","html":"
01
10
","metadata":{}},{"output_type":"display_data","html":"
00
01
","metadata":{}},{"output_type":"pyout","prompt_number":15,"html":"
- : unit = ()\n
","metadata":{}},{"output_type":"pyout","prompt_number":15,"html":"
- : unit = ()\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"input":"module GFPoly = Galois.Extension.Make(struct \n module Poly = Poly.Make(Galois.Primitive.GF2)\n let pp = [| 1; 1; 0; 0; 1 |]\nend)","cell_type":"code","prompt_number":16,"outputs":[{"output_type":"pyout","prompt_number":16,"html":"
module GFPoly :\n  sig\n    type t = Galois.Primitive.GF2.t array\n    val zero : t\n    val one : t\n    val ( +: ) : t -> t -> t\n    val ( -: ) : t -> t -> t\n    val ( *: ) : t -> t -> t\n    val ( /: ) : t -> t -> t\n    val to_string : t -> string\n  end\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"cell_type":"markdown","source":"Using this representation we can see the underlying structure of $GF(2^4)$ fields as polynomials."},{"metadata":{},"input":"let x : GFPoly.t = [|0; 1; 0; 0|]","cell_type":"code","prompt_number":17,"outputs":[{"output_type":"pyout","prompt_number":17,"html":"
val x : GFPoly.t = [|0; 1; 0; 0|]\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"input":"GFPoly.to_string x","cell_type":"code","prompt_number":18,"outputs":[{"output_type":"pyout","prompt_number":18,"html":"
- : string = "x"\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"input":"GFPoly.(to_string (x *: x))","cell_type":"code","prompt_number":19,"outputs":[{"output_type":"pyout","prompt_number":19,"html":"
- : string = "x^2"\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"input":"let rec pow = GFPoly.(function\n | 0 -> one\n | 1 -> x\n | _ as n -> x *: pow (n-1))","cell_type":"code","prompt_number":20,"outputs":[{"output_type":"pyout","prompt_number":20,"html":"
val pow : int -> GFPoly.t = <fun>\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"input":"table 15 3 (fun i -> function \n | 0 -> \"$x^{\" ^ string_of_int i ^ \"}$\"\n | 1 -> \"$\" ^ GFPoly.to_string (pow i) ^ \"$\"\n | _ -> X.G.(to_string (X.Gp.pe **: i)))","cell_type":"code","prompt_number":21,"outputs":[{"output_type":"display_data","html":"
$x^{0}$$1$1
$x^{1}$$x$2
$x^{2}$$x^2$4
$x^{3}$$x^3$8
$x^{4}$$x + 1$3
$x^{5}$$x^2 + x$6
$x^{6}$$x^3 + x^2$12
$x^{7}$$x^3 + x + 1$11
$x^{8}$$x^2 + 1$5
$x^{9}$$x^3 + x$10
$x^{10}$$x^2 + x + 1$7
$x^{11}$$x^3 + x^2 + x$14
$x^{12}$$x^3 + x^2 + x + 1$15
$x^{13}$$x^3 + x^2 + 1$13
$x^{14}$$x^3 + 1$9
","metadata":{}},{"output_type":"pyout","prompt_number":21,"html":"
- : unit = ()\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"cell_type":"heading","source":"Encoding","level":2},{"metadata":{},"cell_type":"markdown","source":"The encoding process takes a **message** of **k** symbols and produces a **codeword** of **n** symbols."},{"metadata":{},"input":"let message = [| 11; 10; 9; 8; 7; 6; 5; 4; 3; 2; 1 |]\nlet () = display_poly message","cell_type":"code","prompt_number":22,"outputs":[{"output_type":"display_data","html":"$$x^{10} + 2.x^{9} + 3.x^{8} + 4.x^{7} + 5.x^{6} + 6.x^{5} + 7.x^{4} + 8.x^{3} + 9.x^{2} + 10.x + 11$$","metadata":{}},{"output_type":"pyout","prompt_number":22,"html":"
val message : int array = [|11; 10; 9; 8; 7; 6; 5; 4; 3; 2; 1|]\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"input":"let codeword = X.R.encode message\nlet () = display_poly codeword","cell_type":"code","prompt_number":23,"outputs":[{"output_type":"display_data","html":"$$x^{14} + 2.x^{13} + 3.x^{12} + 4.x^{11} + 5.x^{10} + 6.x^{9} + 7.x^{8} + 8.x^{7} + 9.x^{6} + 10.x^{5} + 11.x^{4} + 3.x^{3} + 3.x^{2} + 12.x + 12$$","metadata":{}},{"output_type":"pyout","prompt_number":23,"html":"
val codeword : X.R.poly = [|12; 12; 3; 3; 11; 10; 9; 8; 7; 6; 5; 4; 3; 2; 1|]\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"cell_type":"markdown","source":"The encoding process is rather simple \n\n$$codeword = message.x^{2t} + (message.x^{2t} \\mod generator)$$\n\nWe can implement it in a few lines of polynomial arithmetic."},{"metadata":{},"input":"let myencode message = \n let open X.R.R in\n let (%:) a b = snd (a /: b) in (* mod; the remainder of division *)\n let message = message *: (x **: (2*t)) in (* shift message *)\n message +: (message %: X.R.generator)","cell_type":"code","prompt_number":24,"outputs":[{"output_type":"pyout","prompt_number":24,"html":"
val myencode : X.R.R.t -> X.R.R.t = <fun>\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"input":"let mycodeword = myencode message\nlet () = display_poly mycodeword","cell_type":"code","prompt_number":25,"outputs":[{"output_type":"display_data","html":"$$x^{14} + 2.x^{13} + 3.x^{12} + 4.x^{11} + 5.x^{10} + 6.x^{9} + 7.x^{8} + 8.x^{7} + 9.x^{6} + 10.x^{5} + 11.x^{4} + 3.x^{3} + 3.x^{2} + 12.x + 12$$","metadata":{}},{"output_type":"pyout","prompt_number":25,"html":"
val mycodeword : X.R.R.t =\n  [|12; 12; 3; 3; 11; 10; 9; 8; 7; 6; 5; 4; 3; 2; 1|]\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"cell_type":"markdown","source":"It is worth noting that the RS encoder is systematic - that is the orginal message is included in the code word. The symbols added by the encoder are known as parity symbols."},{"metadata":{},"cell_type":"heading","source":"Errors","level":2},{"metadata":{},"cell_type":"markdown","source":"During transmission or storage the codeword may become corrupted by some errors.\n\n$$received = codeword + errors$$\n\nIt is the purpose of the Reed-Solomon decoder to calculate the error polynomial from the received polynomial then\n\n$$corrected = received - errors$$\n\nFor a codeword with $2t$ parity symbols the Reed-Solomon decoder will be able to correct upto $t$ symbol errors."},{"metadata":{},"input":"let error = [| 0; 0; 2; 0; 0; 0; 0; 0; 0; 13; 0; 0; 0; 0; 0 |]\nlet () = display_poly error","cell_type":"code","prompt_number":26,"outputs":[{"output_type":"display_data","html":"$$13.x^{9} + 2.x^{2}$$","metadata":{}},{"output_type":"pyout","prompt_number":26,"html":"
val error : int array = [|0; 0; 2; 0; 0; 0; 0; 0; 0; 13; 0; 0; 0; 0; 0|]\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"input":"let received = X.R.R.(codeword +: error)\nlet () = display_poly received","cell_type":"code","prompt_number":27,"outputs":[{"output_type":"display_data","html":"$$x^{14} + 2.x^{13} + 3.x^{12} + 4.x^{11} + 5.x^{10} + 11.x^{9} + 7.x^{8} + 8.x^{7} + 9.x^{6} + 10.x^{5} + 11.x^{4} + 3.x^{3} + x^{2} + 12.x + 12$$","metadata":{}},{"output_type":"pyout","prompt_number":27,"html":"
val received : X.R.R.t = [|12; 12; 1; 3; 11; 10; 9; 8; 7; 11; 5; 4; 3; 2; 1|]\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"cell_type":"heading","source":"Decoding","level":2},{"metadata":{},"cell_type":"markdown","source":"We start by running the decoder on our example."},{"metadata":{},"input":"let corrected = X.R.decode received\nlet () = display_poly corrected\nlet okay = corrected = codeword","cell_type":"code","prompt_number":28,"outputs":[{"output_type":"display_data","html":"$$x^{14} + 2.x^{13} + 3.x^{12} + 4.x^{11} + 5.x^{10} + 6.x^{9} + 7.x^{8} + 8.x^{7} + 9.x^{6} + 10.x^{5} + 11.x^{4} + 3.x^{3} + 3.x^{2} + 12.x + 12$$","metadata":{}},{"output_type":"pyout","prompt_number":28,"html":"
val corrected : X.R.poly =\n  [|12; 12; 3; 3; 11; 10; 9; 8; 7; 6; 5; 4; 3; 2; 1|]\n
","metadata":{}},{"output_type":"pyout","prompt_number":28,"html":"
val okay : bool = true\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"cell_type":"markdown","source":"As we see the decoder has taken the (corrupt) received poly and recovered the original codeword.\n\nAlthough I shall avoid a technical description of the maths involved in decoding (if you're interested check out that BBC white paper), I will show how the decoder is put together from the functions in the `Rs` module.\n\nThe first step is to calculate the syndrome vector. This is done by evalulating the received polynomial (here we use horners method) at each of the roots of the generator polynomial (of which there are $2t$)."},{"metadata":{},"input":"let syndromes = Array.init (2*t) X.R.(fun i -> horner received (root i))","cell_type":"code","prompt_number":29,"outputs":[{"output_type":"pyout","prompt_number":29,"html":"
val syndromes : X.R.elt array = [|15; 3; 4; 12|]\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"cell_type":"markdown","source":"Because the roots of the generator are also the roots of the codeword, if any of these values are non-zero then we know there are errors to be corrected, as is the case here.\n\nWe now have a choice on how we will solve the key equations - Petersons' method (matrix inversion), Berlekamp-Massey (black magic) or Euclids algorithm (polynomial factorisation). We'll choose the later as it's easiest to understand, though implementations of all methods are provided. From the syndromes this will produce the error locator and error evaluator polynomials."},{"metadata":{},"input":"let v, l = X.R.euclid syndromes","cell_type":"code","prompt_number":30,"outputs":[{"output_type":"pyout","prompt_number":30,"html":"
val v : X.R.poly = [|14; 3|]\nval l : X.R.poly = [|9; 7; 7|]\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"cell_type":"markdown","source":"Chien search finds the error locations."},{"metadata":{},"input":"let el = X.R.chien l\nlet el' = List.map X.R.error_location el (* compute actual locations *)","cell_type":"code","prompt_number":31,"outputs":[{"output_type":"pyout","prompt_number":31,"html":"
val el : X.R.loc list = [6; 13]\n
","metadata":{}},{"output_type":"pyout","prompt_number":31,"html":"
val el' : int list = [9; 2]\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"cell_type":"markdown","source":"Forneys algorithm solves for the error values."},{"metadata":{},"input":"let ev = List.map (X.R.forney v l) el","cell_type":"code","prompt_number":32,"outputs":[{"output_type":"pyout","prompt_number":32,"html":"
val ev : X.R.elt list = [13; 2]\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"cell_type":"markdown","source":"Using `el` and `ev` we form the error polynomial"},{"metadata":{},"input":"let e = X.R.error ev el","cell_type":"code","prompt_number":33,"outputs":[{"output_type":"pyout","prompt_number":33,"html":"
val e : X.R.poly = [|0; 0; 2; 0; 0; 0; 0; 0; 0; 13|]\n
","metadata":{}}],"language":"python","collapsed":false},{"metadata":{},"cell_type":"markdown","source":"And finally it can be added to the received polynomial to form the corrected polynomial."},{"metadata":{},"input":"let corrected = X.R.R.(received +: e)\nlet () = display_poly corrected","cell_type":"code","prompt_number":34,"outputs":[{"output_type":"display_data","html":"$$x^{14} + 2.x^{13} + 3.x^{12} + 4.x^{11} + 5.x^{10} + 6.x^{9} + 7.x^{8} + 8.x^{7} + 9.x^{6} + 10.x^{5} + 11.x^{4} + 3.x^{3} + 3.x^{2} + 12.x + 12$$","metadata":{}},{"output_type":"pyout","prompt_number":34,"html":"
val corrected : X.R.R.t = [|12; 12; 3; 3; 11; 10; 9; 8; 7; 6; 5; 4; 3; 2; 1|]\n
","metadata":{}}],"language":"python","collapsed":false}],"metadata":{}}],"nbformat":3,"nbformat_minor":0} -------------------------------------------------------------------------------- /reedsolomon.opam: -------------------------------------------------------------------------------- 1 | opam-version: "2.0" 2 | name: "reedsolomon" 3 | maintainer: "Andy Ray " 4 | authors: "Andy Ray " 5 | homepage: "https://github.com/ujamjar/reedsolomon" 6 | bug-reports: "https://github.com/ujamjar/reedsolomon/issues" 7 | dev-repo: "git+https://github.com/ujamjar/reedsolomon.git" 8 | doc: "https://github.com/ujamjar/reedsolomon" 9 | license: "MIT" 10 | synopsis: "Reed-Solomon Error Correction CODEC" 11 | description: "Reed-Solomon Error Coorection CODEC libaray." 12 | 13 | build: [ 14 | ["dune" "build" "-p" name "-j" jobs] 15 | ] 16 | 17 | depends: [ 18 | "ocaml" {>= "4.02.0"} 19 | "dune" {build} 20 | ] 21 | url { 22 | src: "https://github.com/ujamjar/reedsolomon/archive/v0.3.tar.gz" 23 | checksum: "md5=d59a4bf6c65ec034e39685bad037eb4f" 24 | } 25 | -------------------------------------------------------------------------------- /src/codec.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * reedsolomon - error correction CODEC 3 | * 4 | * (c) 2014 MicroJamJar Ltd 5 | * 6 | * Author(s): andy.ray@ujamjar.com 7 | * Description: 8 | * 9 | *) 10 | 11 | module type RsParams = 12 | sig 13 | val k : int 14 | val t : int 15 | val b : int 16 | end 17 | 18 | module type RsPoly = sig 19 | 20 | type elt 21 | 22 | module M : (Matrix.S with type t = elt) 23 | module R : (Poly.S with type t = elt array and type elt = elt) 24 | 25 | type poly = R.t 26 | type loc = int 27 | 28 | val root : int -> elt 29 | val generator : poly 30 | val xn : int -> poly 31 | val x2t : poly 32 | val parity : poly -> poly 33 | val encode : poly -> poly 34 | 35 | val horner : poly -> elt -> elt 36 | val syndromes : poly -> poly 37 | 38 | val key_equations : poly -> int -> M.matrix * M.matrix 39 | val solve_key_equations : M.matrix * M.matrix -> M.matrix 40 | val peterson : poly -> poly 41 | 42 | val euclid_inner : (poly * poly) -> (poly * poly) -> (poly * poly) 43 | val euclid : ?norm:bool -> ?lim:int -> poly -> poly * poly 44 | 45 | val berlekamp_massey_iter : 46 | poly -> int -> (poly * poly * int) -> (poly * poly * int) 47 | val berlekamp_massey : poly -> poly 48 | 49 | module Sarwate : sig 50 | val iBM : poly -> poly 51 | val riBM : poly -> poly * poly 52 | val rriBM : poly -> poly * poly 53 | val forney : poly -> poly -> (loc -> elt) 54 | end 55 | 56 | val chien : poly -> loc list 57 | val error_location : loc -> int 58 | 59 | val error_magnitude : int -> poly -> poly -> poly 60 | val deriv : poly -> poly 61 | val forney : poly -> poly -> (loc -> elt) 62 | 63 | val error : elt list -> loc list -> poly 64 | 65 | val correct : poly -> poly -> poly 66 | 67 | val decode_euclid : poly -> poly 68 | val decode_berlekamp_massey : poly -> poly 69 | val decode_peterson : poly -> poly 70 | val decode : poly -> poly 71 | 72 | val erasure_locator : int list -> poly 73 | val zero_erasures : poly -> int list -> poly 74 | val error_and_erasure : elt list -> loc list -> elt list -> loc list -> poly 75 | 76 | val decode_erasures_euclid : poly -> int list -> poly 77 | val decode_erasures : poly -> int list -> poly 78 | 79 | val decode_errors_and_erasures_euclid : poly -> int list -> poly 80 | (*val decode_errors_and_erasures_berlekamp_massey : poly -> int list -> poly*) 81 | val decode_errors_and_erasures : poly -> int list -> poly 82 | end 83 | 84 | module MakePoly(G : Galois.Table.Ops)(P : RsParams) = struct 85 | 86 | type elt = G.t 87 | 88 | module M = Matrix.Make(G) 89 | module R = Poly.Make(G) 90 | 91 | type poly = R.t 92 | type loc = int 93 | 94 | let root i = G.(alpha **: (P.b + i)) 95 | 96 | let generator = 97 | let roots = 98 | Array.init (2*P.t) 99 | (fun i -> [| root i; G.one |]) 100 | in 101 | List.fold_left (fun p' p -> R.(p' *: p)) R.one (Array.to_list roots) 102 | 103 | let xn n = R.(one ^: n) 104 | 105 | let x2t = xn (2 * P.t) 106 | 107 | let parity d = R.(snd ((d *: x2t) /: generator)) 108 | 109 | let encode d = R.(trim ((d *: x2t) +: parity d)) 110 | 111 | let horner p a = 112 | let p = List.rev (Array.to_list p) in 113 | List.fold_left G.(fun acc x -> (acc *: a) +: x) G.zero p 114 | 115 | let syndromes r = Array.init (2*P.t) (fun i -> horner r (root i)) 116 | 117 | let key_equations s v = 118 | M.init v v (fun r c -> s.(v-(c+1)+r)), 119 | M.init v 1 (fun r c -> s.(v+r)) 120 | 121 | (* K.L = S => K^-1.S = L, iff det(S) <> 0 *) 122 | let solve_key_equations (k,s) = 123 | let k' = M.gauss_jordan_inverse k in 124 | M.(k' *: s) 125 | 126 | let peterson s = 127 | let rec p v = 128 | if v = 0 then R.zero 129 | else 130 | let km, kv = key_equations s v in 131 | (* rather than check the determinant (which is VERY slow), 132 | * invert the matrix, then check it actually worked *) 133 | let km' = M.gauss_jordan_inverse km in 134 | if M.(km' *: km = identity v) then 135 | let v = M.(km' *: kv) in 136 | Array.init (M.rows v + 1) (fun i -> 137 | if i=0 then G.one else v.(i-1).(0)) 138 | else 139 | p (v-1) 140 | in 141 | p P.t 142 | 143 | let euclid_inner (r0,r1) (s0,s1) = 144 | let q,r = R.(r1 /: r0) in 145 | let s = R.(s1 -: (q *: s0)) in 146 | r, s 147 | 148 | (* calculate error locator and value polys using extended gcd *) 149 | let euclid ?(norm=false) ?(lim=P.t) s = 150 | let open R in 151 | let rec f (r0,r1) (s0,s1) = 152 | if degree r0 < lim then r0,s0 153 | else 154 | let r, s = euclid_inner (r0,r1) (s0,s1) in 155 | f (r,r0) (s,s0) 156 | in 157 | let v, l = f (R.trim s,x2t) (one,zero) in 158 | if norm then 159 | let inv' = G.( one /: l.(0) ) in 160 | Array.map G.(( *: ) inv') v, 161 | Array.map G.(( *: ) inv') l 162 | else 163 | v, l 164 | 165 | let berlekamp_massey_iter s k (n,c,l) = 166 | let get a n = if n >= Array.length a then G.zero else a.(n) in 167 | let rec e i v = 168 | if i>l then v else e (i+1) G.(v +: (get n i *: get s (k-1-i))) 169 | in 170 | let e = e 1 s.(k-1) in 171 | let n, c, l = 172 | if e = G.zero then 173 | n,c,l 174 | else 175 | let n' = R.(n +: (c *:. e)) in 176 | let c, l = 177 | if 2*l < k then R.(n /:. e), k-l else c, l 178 | in 179 | n', c, l 180 | in 181 | n, R.(c ^: 1), l 182 | 183 | (* calculate error locator poly using the berlekamp-massey algorithm *) 184 | let berlekamp_massey s = 185 | let one, x = R.(one, one ^: 1) in 186 | let rec f k (n,c,l as x) = 187 | if k > 2*P.t then n 188 | else 189 | let x = berlekamp_massey_iter s k x in 190 | f (k+1) x 191 | in 192 | f 1 (one, x, 0) 193 | 194 | let deriv l = 195 | (* set even powers to 0 *) 196 | let l = Array.init (Array.length l) (fun i -> 197 | if i mod 2 = 0 then G.zero else l.(i)) in 198 | (* divide by x *) 199 | Array.init (Array.length l - 1) (fun i -> l.(i+1)) 200 | 201 | module Sarwate = struct 202 | 203 | let t = P.t 204 | 205 | let get a i = 206 | if i < 0 then G.zero 207 | else if i >= Array.length a then G.zero 208 | else a.(i) 209 | let foldi f z n = 210 | let acc = ref z in 211 | for i=0 to n-1 do 212 | acc := f !acc i 213 | done; 214 | !acc 215 | let iteri a f = 216 | for i=0 to Array.length a - 1 do 217 | a.(i) <- f i 218 | done 219 | let copy t f = 220 | for i=0 to Array.length t - 1 do 221 | t.(i) <- f.(i) 222 | done 223 | let shiftup a b = 224 | for i=Array.length a - 1 downto 0 do 225 | a.(i) <- get a (i-1) 226 | done 227 | let shiftdown a b = 228 | for i=0 to Array.length a - 1 do 229 | a.(i) <- get b (i+1) 230 | done 231 | 232 | let iBM = 233 | let z,o = G.zero, G.one in 234 | let lambda = Array.make (t+1) z in 235 | let lambda' = Array.make (t+1) z in 236 | let b = Array.make (t+1) z in 237 | let k = ref 0 in 238 | let gamma = ref o in 239 | let delta = ref z in 240 | 241 | let init () = 242 | let f i = if i=0 then o else z in 243 | iteri lambda f; 244 | iteri b f; 245 | k := 0; 246 | gamma := o; 247 | delta := z; 248 | in 249 | 250 | let iter s = 251 | let update_delta r d i = G.(d +: (get s (r-i) *: lambda.(i))) in 252 | let update_lambda i = 253 | G.((!gamma *: lambda.(i)) -: (!delta *: get b (i-1))) 254 | in 255 | for r=0 to 2*t-1 do 256 | (* step 1 *) 257 | delta := foldi (update_delta r) z (t+1); 258 | (* step 2 *) 259 | copy lambda' lambda; 260 | iteri lambda update_lambda; 261 | (* step 3 *) 262 | if !delta <> z && !k >= 0 then begin 263 | copy b lambda'; (* previous lambda *) 264 | gamma := !delta; 265 | k := - !k - 1 266 | end else begin 267 | shiftup b b; 268 | gamma := !gamma; 269 | k := !k + 1 270 | end; 271 | done 272 | (* XXX step 4 *) 273 | in 274 | 275 | (fun s -> 276 | init (); 277 | iter s; 278 | Array.init (t+1) (fun i -> lambda.(i))) 279 | 280 | let riBM = 281 | let z,o = G.zero, G.one in 282 | let lambda = Array.make (t+1) z in 283 | let lambda' = Array.make (t+1) z in 284 | let b = Array.make (t+1) z in 285 | let delta = Array.make (2*t) z in 286 | let delta' = Array.make (2*t) z in 287 | let theta = Array.make (2*t) z in 288 | let gamma = ref o in 289 | let k = ref 0 in 290 | 291 | let init s = 292 | let f i = if i=0 then o else z in 293 | iteri lambda f; 294 | iteri b f; 295 | copy delta s; 296 | copy theta s; 297 | gamma := o; 298 | k := 0; 299 | in 300 | 301 | let iter () = 302 | let update_lambda i = 303 | G.((!gamma *: lambda.(i)) -: (delta'.(0) *: get b (i-1))) 304 | in 305 | let update_delta i = 306 | G.((!gamma *: get delta' (i+1)) -: (delta'.(0) *: theta.(i))) 307 | in 308 | for r=0 to 2*t-1 do 309 | (* step 1 *) 310 | copy lambda' lambda; 311 | copy delta' delta; 312 | iteri lambda update_lambda; 313 | iteri delta update_delta; 314 | (* step 2 *) 315 | if delta'.(0) <> z && !k >= 0 then begin 316 | copy b lambda'; (* previous lambda *) 317 | shiftdown theta delta'; 318 | gamma := delta'.(0); 319 | k := - !k - 1 320 | end else begin 321 | shiftup b b; 322 | (*copy theta theta; 323 | gamma := !gamma;*) 324 | k := !k + 1 325 | end; 326 | done 327 | in 328 | 329 | (fun s -> 330 | init s; 331 | iter (); 332 | Array.init t (fun i -> delta.(i)), 333 | Array.init (t+1) (fun i -> lambda.(i))) 334 | 335 | let rriBM = 336 | let z,o = G.zero, G.one in 337 | let delta = Array.make (3*t+1) z in 338 | let delta' = Array.make (3*t+1) z in 339 | let theta = Array.make (3*t+1) z in 340 | let gamma = ref o in 341 | let k = ref 0 in 342 | 343 | let init s = 344 | let f i = 345 | if i<2*t then s.(i) 346 | else if i=3*t then o 347 | else z 348 | in 349 | iteri delta f; 350 | copy theta delta; 351 | gamma := o; 352 | k := 0; 353 | in 354 | 355 | let iter () = 356 | let update_delta i = 357 | G.((!gamma *: get delta' (i+1)) -: (delta'.(0) *: theta.(i))) 358 | in 359 | for i=0 to 2*t-1 do 360 | (* step 1 *) 361 | copy delta' delta; 362 | iteri delta update_delta; 363 | (* step 2 *) 364 | if delta'.(0) <> z && !k >= 0 then begin 365 | shiftdown theta delta'; 366 | gamma := delta'.(0); 367 | k := - !k - 1 368 | end else begin 369 | (*copy theta theta; 370 | gamma := !gamma;*) 371 | k := !k + 1 372 | end; 373 | done 374 | in 375 | 376 | (fun s -> 377 | init s; 378 | iter (); 379 | Array.init t (fun i -> delta.(i)), 380 | Array.init (t+1) (fun i -> delta.(t+i))) 381 | 382 | let forney v l = 383 | let l' = deriv l in 384 | (fun x' -> 385 | let x' = G.antilog x' in 386 | let x = G.(x' **: (P.b+(2*P.t)-1)) in 387 | G.(x *: (horner v x' /: horner l' x'))) 388 | 389 | end 390 | 391 | let chien l = 392 | let rec f n = 393 | if n = (G.n_elems-1) then [] 394 | else 395 | if horner l (G.antilog n) = G.zero then 396 | n :: f (n+1) 397 | else 398 | f (n+1) 399 | in 400 | f 0 401 | 402 | let error_location n = G.(log (inv (antilog n))) 403 | 404 | let error_magnitude v l s = 405 | let get a n = if n >= Array.length a then G.zero else a.(n) in 406 | Array.init v (fun i -> 407 | let a = Array.init (i+1) (fun j -> G.(get s j *: get l (i-j))) in 408 | Array.fold_left G.(+:) G.zero a) 409 | 410 | let forney v l = 411 | let l' = deriv l in 412 | (fun x' -> 413 | let x' = G.antilog x' in 414 | let x = G.(x' **: (P.b-1)) in 415 | G.(x *: (horner v x' /: horner l' x'))) 416 | 417 | let error v l = 418 | let x = List.map2 (fun a b -> error_location a,b) l v in 419 | let n = P.k + P.t*2 in 420 | R.to_poly (Array.init n (fun i -> try List.assoc i x with _ -> G.zero)) 421 | 422 | let correct r e = R.(r +: e) 423 | 424 | (* error correction *) 425 | 426 | let decode_euclid r = 427 | let s = syndromes r in 428 | if Array.fold_left (fun b s -> b && s = G.zero) true s then r 429 | else 430 | let v, l = euclid s in 431 | let el = chien l in 432 | let ev = List.map (forney v l) el in 433 | let e = error ev el in 434 | correct r e 435 | 436 | let decode_berlekamp_massey r = 437 | let s = syndromes r in 438 | if Array.fold_left (fun b s -> b && s = G.zero) true s then r 439 | else 440 | let l = berlekamp_massey s in 441 | let el = chien l in 442 | let v = error_magnitude (List.length el) l s in 443 | let ev = List.map (forney v l) el in 444 | let e = error ev el in 445 | correct r e 446 | 447 | let decode_peterson r = 448 | let s = syndromes r in 449 | if Array.fold_left (fun b s -> b && s = G.zero) true s then r 450 | else 451 | let l = peterson s in 452 | let el = chien l in 453 | let v = error_magnitude (List.length el) l s in 454 | let ev = List.map (forney v l) el in 455 | let e = error ev el in 456 | correct r e 457 | 458 | let decode = decode_euclid 459 | 460 | (* erasure correction *) 461 | 462 | let erasure_locator y = 463 | let terms = List.map (fun y -> [| G.one; G.(alpha **: y) |]) y in 464 | List.fold_left (fun a x -> R.(a *: x)) R.one terms 465 | 466 | let zero_erasures r y = 467 | let r = Array.copy r in 468 | List.iter (fun y -> r.(y) <- G.zero) y; 469 | r 470 | 471 | let error_and_erasure ev el fv fl = 472 | let e = List.map2 (fun a b -> error_location a,b) el ev in 473 | let f = List.map2 (fun a b -> a,b) fl fv in 474 | let x = e @ f in 475 | let n = P.k + P.t*2 in 476 | R.to_poly (Array.init n (fun i -> try List.assoc i x with _ -> G.zero)) 477 | 478 | 479 | let decode_erasures_euclid r y = 480 | if y = [] then r 481 | else 482 | let f = List.length y in 483 | let tau = erasure_locator y in 484 | let r = zero_erasures r y in 485 | let s = syndromes r in 486 | let xi = R.(slice (tau *: s) (2*P.t-1)) in 487 | let omega, lambda = euclid ~norm:true ~lim:(P.t + (f / 2) + 0) xi in 488 | let phi = R.(tau *: lambda) in 489 | let forney = forney omega phi in 490 | let fv = List.map forney 491 | (List.map (fun y -> G.(log (inv (alpha **: y)))) y) 492 | in 493 | let e = error_and_erasure [] [] fv y in 494 | correct r e 495 | 496 | let decode_erasures = decode_erasures_euclid 497 | 498 | let decode_errors_and_erasures_euclid r y = 499 | let f = List.length y in 500 | let tau = erasure_locator y in 501 | let r = zero_erasures r y in 502 | let s = syndromes r in 503 | let xi = R.(slice (tau *: s) (2*P.t-1)) in 504 | let omega, lambda = euclid ~norm:true ~lim:(P.t + (f / 2) + 0) xi in 505 | let el = chien lambda in 506 | let phi = R.(tau *: lambda) in 507 | let forney = forney omega phi in 508 | let ev = List.map forney el in 509 | let fv = List.map forney 510 | (List.map (fun y -> G.(log (inv (alpha **: y)))) y) 511 | in 512 | let e = error_and_erasure ev el fv y in 513 | correct r e 514 | 515 | let decode_errors_and_erasures_berlekamp_massey r y = 516 | (*let f = List.length y in*) 517 | let tau = erasure_locator y in 518 | let r = zero_erasures r y in 519 | let s = syndromes r in 520 | let xi = R.(slice (tau *: s) (2*P.t-1)) in 521 | (*let omega, lambda = euclid ~norm:true ~lim:(P.t + (f / 2) + 0) xi in*) 522 | 523 | let lambda = berlekamp_massey xi in 524 | let omega = R.(slice (lambda *: xi) (2*P.t-1)) in 525 | 526 | let el = chien lambda in 527 | let phi = R.(tau *: lambda) in 528 | let forney = forney omega phi in 529 | let ev = List.map forney el in 530 | let fv = List.map forney 531 | (List.map (fun y -> G.(log (inv (alpha **: y)))) y) 532 | in 533 | let e = error_and_erasure ev el fv y in 534 | correct r e 535 | 536 | let decode_errors_and_erasures = decode_errors_and_erasures_euclid 537 | 538 | end 539 | 540 | module type Standard = sig 541 | module Gp : Galois.Table.Params 542 | module G : Galois.Table.Ops with type t = int 543 | module Rp : RsParams 544 | module R : RsPoly with type elt = int 545 | end 546 | 547 | module MakeStandard(Gp : Galois.Table.Params)(Rp : RsParams) = struct 548 | module Gp = Gp 549 | module G = Galois.Table.MakeInt(Gp) 550 | module Rp = Rp 551 | module R = MakePoly(G)(Rp) 552 | end 553 | 554 | module BBCTest = MakeStandard 555 | (struct 556 | let pp = 19 557 | let pe = 2 558 | end) 559 | (struct 560 | let t = 2 561 | let k = 15 - (2*t) 562 | let b = 0 563 | end) 564 | 565 | module CCSDS = struct 566 | 567 | let dual_of_poly = [| 568 | 0x00;0x7b;0xaf;0xd4;0x99;0xe2;0x36;0x4d; 569 | 0xfa;0x81;0x55;0x2e;0x63;0x18;0xcc;0xb7; 570 | 0x86;0xfd;0x29;0x52;0x1f;0x64;0xb0;0xcb; 571 | 0x7c;0x07;0xd3;0xa8;0xe5;0x9e;0x4a;0x31; 572 | 0xec;0x97;0x43;0x38;0x75;0x0e;0xda;0xa1; 573 | 0x16;0x6d;0xb9;0xc2;0x8f;0xf4;0x20;0x5b; 574 | 0x6a;0x11;0xc5;0xbe;0xf3;0x88;0x5c;0x27; 575 | 0x90;0xeb;0x3f;0x44;0x09;0x72;0xa6;0xdd; 576 | 0xef;0x94;0x40;0x3b;0x76;0x0d;0xd9;0xa2; 577 | 0x15;0x6e;0xba;0xc1;0x8c;0xf7;0x23;0x58; 578 | 0x69;0x12;0xc6;0xbd;0xf0;0x8b;0x5f;0x24; 579 | 0x93;0xe8;0x3c;0x47;0x0a;0x71;0xa5;0xde; 580 | 0x03;0x78;0xac;0xd7;0x9a;0xe1;0x35;0x4e; 581 | 0xf9;0x82;0x56;0x2d;0x60;0x1b;0xcf;0xb4; 582 | 0x85;0xfe;0x2a;0x51;0x1c;0x67;0xb3;0xc8; 583 | 0x7f;0x04;0xd0;0xab;0xe6;0x9d;0x49;0x32; 584 | 0x8d;0xf6;0x22;0x59;0x14;0x6f;0xbb;0xc0; 585 | 0x77;0x0c;0xd8;0xa3;0xee;0x95;0x41;0x3a; 586 | 0x0b;0x70;0xa4;0xdf;0x92;0xe9;0x3d;0x46; 587 | 0xf1;0x8a;0x5e;0x25;0x68;0x13;0xc7;0xbc; 588 | 0x61;0x1a;0xce;0xb5;0xf8;0x83;0x57;0x2c; 589 | 0x9b;0xe0;0x34;0x4f;0x02;0x79;0xad;0xd6; 590 | 0xe7;0x9c;0x48;0x33;0x7e;0x05;0xd1;0xaa; 591 | 0x1d;0x66;0xb2;0xc9;0x84;0xff;0x2b;0x50; 592 | 0x62;0x19;0xcd;0xb6;0xfb;0x80;0x54;0x2f; 593 | 0x98;0xe3;0x37;0x4c;0x01;0x7a;0xae;0xd5; 594 | 0xe4;0x9f;0x4b;0x30;0x7d;0x06;0xd2;0xa9; 595 | 0x1e;0x65;0xb1;0xca;0x87;0xfc;0x28;0x53; 596 | 0x8e;0xf5;0x21;0x5a;0x17;0x6c;0xb8;0xc3; 597 | 0x74;0x0f;0xdb;0xa0;0xed;0x96;0x42;0x39; 598 | 0x08;0x73;0xa7;0xdc;0x91;0xea;0x3e;0x45; 599 | 0xf2;0x89;0x5d;0x26;0x6b;0x10;0xc4;0xbf; 600 | |] 601 | 602 | let poly_of_dual = [| 603 | 0x00;0xcc;0xac;0x60;0x79;0xb5;0xd5;0x19; 604 | 0xf0;0x3c;0x5c;0x90;0x89;0x45;0x25;0xe9; 605 | 0xfd;0x31;0x51;0x9d;0x84;0x48;0x28;0xe4; 606 | 0x0d;0xc1;0xa1;0x6d;0x74;0xb8;0xd8;0x14; 607 | 0x2e;0xe2;0x82;0x4e;0x57;0x9b;0xfb;0x37; 608 | 0xde;0x12;0x72;0xbe;0xa7;0x6b;0x0b;0xc7; 609 | 0xd3;0x1f;0x7f;0xb3;0xaa;0x66;0x06;0xca; 610 | 0x23;0xef;0x8f;0x43;0x5a;0x96;0xf6;0x3a; 611 | 0x42;0x8e;0xee;0x22;0x3b;0xf7;0x97;0x5b; 612 | 0xb2;0x7e;0x1e;0xd2;0xcb;0x07;0x67;0xab; 613 | 0xbf;0x73;0x13;0xdf;0xc6;0x0a;0x6a;0xa6; 614 | 0x4f;0x83;0xe3;0x2f;0x36;0xfa;0x9a;0x56; 615 | 0x6c;0xa0;0xc0;0x0c;0x15;0xd9;0xb9;0x75; 616 | 0x9c;0x50;0x30;0xfc;0xe5;0x29;0x49;0x85; 617 | 0x91;0x5d;0x3d;0xf1;0xe8;0x24;0x44;0x88; 618 | 0x61;0xad;0xcd;0x01;0x18;0xd4;0xb4;0x78; 619 | 0xc5;0x09;0x69;0xa5;0xbc;0x70;0x10;0xdc; 620 | 0x35;0xf9;0x99;0x55;0x4c;0x80;0xe0;0x2c; 621 | 0x38;0xf4;0x94;0x58;0x41;0x8d;0xed;0x21; 622 | 0xc8;0x04;0x64;0xa8;0xb1;0x7d;0x1d;0xd1; 623 | 0xeb;0x27;0x47;0x8b;0x92;0x5e;0x3e;0xf2; 624 | 0x1b;0xd7;0xb7;0x7b;0x62;0xae;0xce;0x02; 625 | 0x16;0xda;0xba;0x76;0x6f;0xa3;0xc3;0x0f; 626 | 0xe6;0x2a;0x4a;0x86;0x9f;0x53;0x33;0xff; 627 | 0x87;0x4b;0x2b;0xe7;0xfe;0x32;0x52;0x9e; 628 | 0x77;0xbb;0xdb;0x17;0x0e;0xc2;0xa2;0x6e; 629 | 0x7a;0xb6;0xd6;0x1a;0x03;0xcf;0xaf;0x63; 630 | 0x8a;0x46;0x26;0xea;0xf3;0x3f;0x5f;0x93; 631 | 0xa9;0x65;0x05;0xc9;0xd0;0x1c;0x7c;0xb0; 632 | 0x59;0x95;0xf5;0x39;0x20;0xec;0x8c;0x40; 633 | 0x54;0x98;0xf8;0x34;0x2d;0xe1;0x81;0x4d; 634 | 0xa4;0x68;0x08;0xc4;0xdd;0x11;0x71;0xbd; 635 | |] 636 | 637 | module Gp = struct 638 | let pp = 391 639 | let pe = 173 640 | end 641 | 642 | module Rs16' = MakeStandard(Gp)(struct 643 | let t = 16 644 | let k = 255-(2*t) 645 | let b = 128-t 646 | end) 647 | 648 | module Rs8' = MakeStandard(Gp)(struct 649 | let t = 8 650 | let k = 255-(2*t) 651 | let b = 128-t 652 | end) 653 | 654 | let dop = Array.map (Array.get dual_of_poly) 655 | let pod = Array.map (Array.get poly_of_dual) 656 | let (>>) f g x = g (f x) 657 | 658 | module Map(S : Standard) = struct 659 | module Gp = S.Gp 660 | module G = S.G 661 | module Rp = S.Rp 662 | module R = struct 663 | include S.R 664 | let parity = pod >> parity >> dop 665 | let encode = pod >> encode >> dop 666 | let decode = pod >> decode >> dop 667 | end 668 | end 669 | 670 | module Rs16 = Map(Rs16') 671 | module Rs8 = Map(Rs8') 672 | 673 | end 674 | 675 | module DVB = MakeStandard 676 | (struct 677 | let pp = 285 678 | let pe = 2 679 | end) 680 | (struct 681 | let t = 8 682 | let k = 188 683 | let b = 0 684 | end) 685 | 686 | module ATSC = MakeStandard 687 | (struct 688 | let pp = 285 689 | let pe = 2 690 | end) 691 | (struct 692 | let t = 10 693 | let k = 187 694 | let b = 0 695 | end) 696 | 697 | module G709 = MakeStandard 698 | (struct 699 | let pp = 285 700 | let pe = 2 701 | end) 702 | (struct 703 | let t = 8 704 | let k = 239 705 | let b = 0 706 | end) 707 | 708 | -------------------------------------------------------------------------------- /src/codec.mli: -------------------------------------------------------------------------------- 1 | (* 2 | * reedsolomon - error correction CODEC 3 | * 4 | * (c) 2014 MicroJamJar Ltd 5 | * 6 | * Author(s): andy.ray@ujamjar.com 7 | * Description: 8 | * 9 | *) 10 | 11 | (** Configuration of a Reed-Solomon code *) 12 | module type RsParams = 13 | sig 14 | val k : int 15 | val t : int 16 | val b : int 17 | end 18 | 19 | (** RS encoding and decoding *) 20 | module type RsPoly = sig 21 | 22 | type elt 23 | 24 | module M : (Matrix.S with type t = elt) 25 | module R : (Poly.S with type t = elt array and type elt = elt) 26 | 27 | type poly = R.t 28 | type loc = int 29 | 30 | val root : int -> elt 31 | val generator : poly 32 | val xn : int -> poly 33 | val x2t : poly 34 | val parity : poly -> poly 35 | val encode : poly -> poly 36 | 37 | val horner : poly -> elt -> elt 38 | val syndromes : poly -> poly 39 | 40 | val key_equations : poly -> int -> M.matrix * M.matrix 41 | val solve_key_equations : M.matrix * M.matrix -> M.matrix 42 | val peterson : poly -> poly 43 | 44 | val euclid_inner : (poly * poly) -> (poly * poly) -> (poly * poly) 45 | val euclid : ?norm:bool -> ?lim:int -> poly -> poly * poly 46 | 47 | val berlekamp_massey_iter : 48 | poly -> int -> (poly * poly * int) -> (poly * poly * int) 49 | val berlekamp_massey : poly -> poly 50 | 51 | (** inversionless berlekamp massey algorithms. 52 | 53 | Based on the paper "High-speed architectures for 54 | Reed-Solomon decoders" Dilip V Sarwate, Naresh R Shanbhag *) 55 | module Sarwate : sig 56 | val iBM : poly -> poly 57 | val riBM : poly -> poly * poly 58 | val rriBM : poly -> poly * poly 59 | val forney : poly -> poly -> (loc -> elt) 60 | end 61 | 62 | val chien : poly -> loc list 63 | val error_location : loc -> int 64 | 65 | val error_magnitude : int -> poly -> poly -> poly 66 | val deriv : poly -> poly 67 | val forney : poly -> poly -> (loc -> elt) 68 | 69 | val error : elt list -> loc list -> poly 70 | 71 | val correct : poly -> poly -> poly 72 | 73 | val decode_euclid : poly -> poly 74 | val decode_berlekamp_massey : poly -> poly 75 | val decode_peterson : poly -> poly 76 | val decode : poly -> poly 77 | 78 | val erasure_locator : int list -> poly 79 | val zero_erasures : poly -> int list -> poly 80 | val error_and_erasure : elt list -> loc list -> elt list -> loc list -> poly 81 | 82 | val decode_erasures_euclid : poly -> int list -> poly 83 | val decode_erasures : poly -> int list -> poly 84 | 85 | val decode_errors_and_erasures_euclid : poly -> int list -> poly 86 | (* erasure decoding not working with berlekamp massey *) 87 | (*val decode_errors_and_erasures_berlekamp_massey : poly -> int list -> poly*) 88 | val decode_errors_and_erasures : poly -> int list -> poly 89 | end 90 | 91 | (** Create a Reed-Solomon code based on the given Galois field and code parameters *) 92 | module MakePoly(G : Galois.Table.Ops)(P : RsParams) : 93 | (RsPoly with type elt = G.t) 94 | 95 | (* some example RS CODECs in use *) 96 | 97 | module type Standard = sig 98 | module Gp : Galois.Table.Params 99 | module G : Galois.Table.Ops with type t = int 100 | module Rp : RsParams 101 | module R : RsPoly with type elt = int 102 | end 103 | 104 | module MakeStandard(Gp : Galois.Table.Params)(Rp : RsParams) : Standard 105 | 106 | (** Test code used in BBC white paper *) 107 | module BBCTest : Standard 108 | 109 | (** Consultative Committee for Space Data Systems *) 110 | module CCSDS : sig 111 | val dual_of_poly : int array 112 | val poly_of_dual : int array 113 | (** t=16 *) 114 | module Rs16 : Standard 115 | (** t=8 *) 116 | module Rs8 : Standard 117 | end 118 | 119 | (** Digital Video Broadcasting *) 120 | module DVB : Standard 121 | 122 | (** Advanced Television Systems Committee *) 123 | module ATSC : Standard 124 | 125 | (** Interfaces for the Optical Transport Network *) 126 | module G709 : Standard 127 | 128 | 129 | -------------------------------------------------------------------------------- /src/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name reedsolomon) 3 | (flags (:standard -w -27-32-33-35-39-50)) 4 | (public_name reedsolomon)) 5 | 6 | -------------------------------------------------------------------------------- /src/galois.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * reedsolomon - error correction CODEC 3 | * 4 | * (c) 2014 MicroJamJar Ltd 5 | * 6 | * Author(s): andy.ray@ujamjar.com 7 | * Description: 8 | * 9 | *) 10 | 11 | module Primitive = struct 12 | 13 | module type GF_n = sig 14 | val n : int 15 | end 16 | 17 | (* GF(n) primitive finite field *) 18 | module GFN(GF_n : GF_n) = struct 19 | open GF_n 20 | 21 | type t = int 22 | let zero = 0 23 | let one = 1 24 | let (+:) a b = (a + b) mod n 25 | let (-:) a b = (a - b + n) mod n 26 | let ( *: ) a b = (a * b) mod n 27 | 28 | (* this builds a potentially huge table to construct 29 | * inverses. we could also use the ext_gcd algorithm. 30 | * see poly.ml *) 31 | let inv = 32 | let rec find x v = 33 | if v=0 then 0 34 | else if x=n then failwith "couldn't find inverse" 35 | else if v *: x = 1 then v 36 | else find (x+1) v 37 | in 38 | Array.init n (find 0) 39 | 40 | let ( /: ) a b = a *: inv.(b) 41 | 42 | let to_string = string_of_int 43 | end 44 | 45 | (* GF(2) *) 46 | module GF2 = struct 47 | type t = int 48 | let zero = 0 49 | let one = 1 50 | let (+:) = (lxor) 51 | let (-:) = (lxor) 52 | let ( *: ) = (land) 53 | let ( /: ) a b = a 54 | let to_string = string_of_int 55 | end 56 | 57 | end 58 | 59 | (* GF extension fields built from primitive fields and polynomials *) 60 | module Extension = struct 61 | 62 | module type Generator = sig 63 | (* polynomial with primitive field coefficients *) 64 | module Poly : Poly.S 65 | (* primitive polynomial *) 66 | val pp : Poly.t 67 | end 68 | 69 | module Make(G : Generator) = struct 70 | type t = G.Poly.t 71 | let zero = G.Poly.zero 72 | let one = G.Poly.one 73 | let (+:) = G.Poly.(+:) 74 | let (-:) = G.Poly.(-:) 75 | let ( *: ) a b = snd (G.Poly.((a *: b) /: G.pp)) 76 | let ( /: ) a b = 77 | let _,b' = G.Poly.ext_gcd G.pp b in 78 | a *: b' 79 | 80 | let to_string = G.Poly.to_string ~down:true ~str:G.Poly.(string_format true poly_format) 81 | end 82 | 83 | end 84 | 85 | module GF2N = struct 86 | 87 | module Make(P : sig val pp : int array end) = Extension.Make(struct 88 | module Poly = Poly.Make(Primitive.GF2) 89 | let pp = P.pp 90 | end) 91 | 92 | (* list of primitive polynomials for various GF(2) extension fields *) 93 | let gf2_prim_polys = 94 | let mk_poly pp = 95 | Array.init (1 + (List.fold_left (fun a v -> max a v) 0 pp)) 96 | (fun i -> if List.mem i pp then 1 else 0) 97 | in 98 | Array.map mk_poly 99 | [| 100 | []; (* 0 to 2 we dont have primitive polys listed *) 101 | []; 102 | [0;1;2]; (* wasnt in the table, but appears to be correct. *) 103 | [0;1;3]; 104 | [0;1;4]; 105 | [0;2;5]; 106 | [0;1;6]; 107 | [0;3;7]; 108 | [0;2;3;4;8]; 109 | [0;4;9]; 110 | [0;3;10]; 111 | [0;2;11]; 112 | [0;1;4;6;12]; 113 | [0;1;3;4;13]; 114 | [0;1;6;10;14]; 115 | [0;1;15]; 116 | [0;1;3;12;16]; 117 | [0;3;17]; 118 | [0;7;18]; 119 | [0;1;2;5;19]; 120 | [0;3;20]; 121 | [0;2;21]; 122 | [0;1;22]; 123 | [0;5;23]; 124 | [0;1;2;7;24]; 125 | |] 126 | 127 | end 128 | 129 | module Table = struct 130 | 131 | module type Generator = sig 132 | module Ops : Ops.OpsBase 133 | (* primitive element *) 134 | val alpha : Ops.t 135 | end 136 | 137 | module type Ops = sig 138 | include Ops.OpsBase 139 | val alpha : t 140 | 141 | val n_elems : int 142 | 143 | val log : t -> int 144 | val antilog : int -> t 145 | 146 | val ( *: ) : t -> t -> t 147 | val ( /: ) : t -> t -> t 148 | val ( **: ) : t -> int -> t 149 | 150 | val inv : t -> t 151 | end 152 | 153 | module Make(G : Generator) = struct 154 | include G.Ops 155 | 156 | module PM = Map.Make(struct 157 | type t = G.Ops.t 158 | let compare = compare 159 | end) 160 | module IM = Map.Make(struct 161 | type t = int 162 | let compare = compare 163 | end) 164 | 165 | let alpha = G.alpha 166 | 167 | (* map poly to corresponding power of alpha *) 168 | let pow_map = 169 | let rec f a n map = 170 | (* element already in map, so we are done *) 171 | try let _ = PM.find a map in map 172 | with 173 | (* add power to map *) 174 | | Not_found -> f (a *: alpha) (n+1) (PM.add a n map) 175 | (* some other problem *) 176 | | _ -> failwith "log_map exception" 177 | in 178 | f alpha 1 (PM.add one 0 PM.empty) 179 | 180 | (* map power of alpha to poly *) 181 | let ipow_map = 182 | PM.fold (fun k v m -> IM.add v k m) pow_map IM.empty 183 | 184 | let n_elems = PM.cardinal pow_map + 1 185 | 186 | let log a = PM.find a pow_map 187 | let antilog a = IM.find a ipow_map 188 | 189 | let ( *: ) a b = 190 | if a=zero || b = zero then zero 191 | else 192 | let a, b = log a, log b in 193 | antilog ((a+b) mod (n_elems-1)) 194 | 195 | let (/:) a b = 196 | if a = zero || b = zero then zero 197 | (*else if b = zero then failwith "divide by zero"*) 198 | else 199 | let a, b = log a, log b in 200 | antilog ((a-b+n_elems-1) mod (n_elems-1)) 201 | 202 | let inv a = 203 | if a = zero then zero (* cant invert *) 204 | else 205 | let a = log a in 206 | antilog ((n_elems-1-a) mod (n_elems-1)) 207 | 208 | let rec ( **: ) a n = 209 | if a = zero then zero 210 | else if n < 0 then 211 | (inv a) **: (-n) 212 | else 213 | let a = log a in 214 | antilog ((a*n) mod (n_elems-1)) 215 | 216 | end 217 | 218 | module Int(Ops : (Ops with type t = int array)) = struct 219 | 220 | type t = int 221 | let zero = 0 222 | let one = 1 223 | let (+:) = (lxor) 224 | let (-:) = (lxor) 225 | let to_string = string_of_int 226 | 227 | let n_elems = Ops.n_elems 228 | 229 | let to_int x = 230 | fst (List.fold_left 231 | (fun (a,p) x -> x*p+a,p*2) (0,1) (Array.to_list x)) 232 | 233 | let to_poly x = 234 | let rec f v = 235 | if v = 0 then [] 236 | else if v land 1 = 1 then 237 | 1 :: f (v lsr 1) 238 | else 239 | 0 :: f (v lsr 1) 240 | in 241 | if x=0 then [|0|] 242 | else Array.of_list (f x) 243 | 244 | let alpha = to_int Ops.alpha 245 | 246 | let mk_table min max f = 247 | let len = max - min + 1 in 248 | let a = Array.init len (fun i -> f (i+min)) in 249 | (fun i -> a.(i - min)) 250 | 251 | let rec index i = 252 | if i < 0 then index (i+n_elems-1) 253 | else i mod (n_elems-1) 254 | 255 | let log' i = 256 | if i=0 then 0 else Ops.log (to_poly i) 257 | let log = mk_table 0 (n_elems-1) log' 258 | 259 | let antilog' i' = 260 | let i = index i' in 261 | to_int (Ops.antilog i) 262 | let antilog = mk_table (2-n_elems) (2*(n_elems-2)+1) antilog' 263 | 264 | let inv a = 265 | if a = 0 then 0 266 | else 267 | let a = log a in 268 | antilog (n_elems-1-a) 269 | 270 | let ( *: ) a b = 271 | if a=zero || b = zero then zero 272 | else 273 | let a, b = log a, log b in 274 | antilog (a+b) 275 | 276 | let (/:) a b = 277 | if a = zero || b = zero then zero 278 | (*else if b = zero then failwith "divide by zero"*) 279 | else 280 | let a, b = log a, log b in 281 | antilog (a-b) 282 | 283 | let rec ( **: ) a n = 284 | if n = 0 then one 285 | else if a = zero then zero 286 | else 287 | let n = index (log a * n) in 288 | antilog n 289 | 290 | end 291 | 292 | module type Params = sig 293 | val pp : int 294 | val pe : int 295 | end 296 | 297 | module Params(P : Params) = struct 298 | let mk_gf_param x = 299 | let rec f x = 300 | if x = 0 then [] 301 | else 302 | (if x land 1 = 0 then 0 else 1) :: f (x lsr 1) 303 | in 304 | Array.of_list (f x) 305 | module Ops = Extension.Make(struct 306 | module Poly = Poly.Make(Primitive.GF2) 307 | let pp = mk_gf_param P.pp 308 | end) 309 | let alpha = mk_gf_param P.pe 310 | end 311 | 312 | module MakeInt(P : Params) = Int(Make(Params(P))) 313 | 314 | end 315 | 316 | -------------------------------------------------------------------------------- /src/galois.mli: -------------------------------------------------------------------------------- 1 | (* 2 | * reedsolomon - error correction CODEC 3 | * 4 | * (c) 2014 MicroJamJar Ltd 5 | * 6 | * Author(s): andy.ray@ujamjar.com 7 | * Description: 8 | * 9 | *) 10 | 11 | (* primitive fields of type GF(n) where n is prime *) 12 | module Primitive : sig 13 | module type GF_n = sig 14 | val n : int 15 | end 16 | (* GF(n) primitive finite field *) 17 | module GFN(GF_n : GF_n) : (Ops.OpsBase with type t = int) 18 | (* GF(2) *) 19 | module GF2 : (Ops.OpsBase with type t = int) 20 | end 21 | 22 | (* GF(n^m) extension fields built from primitive fields and polynomials *) 23 | module Extension : sig 24 | 25 | module type Generator = sig 26 | module Poly : Poly.S 27 | (** polynomial with primitive field coefficients *) 28 | val pp : Poly.t 29 | (** primitive polynomial *) 30 | end 31 | 32 | (** make extension field *) 33 | module Make(G : Generator) : (Ops.OpsBase with type t = G.Poly.t) 34 | 35 | end 36 | 37 | (** convenience module for building GF(2^n) fields *) 38 | module GF2N : sig 39 | 40 | module Make(P : sig val pp : int array end) : 41 | (Ops.OpsBase with type t = int array) 42 | 43 | val gf2_prim_polys : int array array 44 | (* list of primitive polys for GF(2); 3..24 *) 45 | 46 | end 47 | 48 | module Table : sig 49 | 50 | module type Generator = sig 51 | module Ops : Ops.OpsBase 52 | val alpha : Ops.t 53 | (* primitive element *) 54 | end 55 | 56 | module type Ops = sig 57 | include Ops.OpsBase 58 | 59 | val alpha : t 60 | (** primitive element *) 61 | 62 | val n_elems : int 63 | (** number of elements in field *) 64 | 65 | val log : t -> int 66 | (** log x = b when alpha^b = x *) 67 | 68 | val antilog : int -> t 69 | (** inverse log *) 70 | 71 | val ( *: ) : t -> t -> t 72 | (** multiplication *) 73 | 74 | val ( /: ) : t -> t -> t 75 | (** division *) 76 | 77 | val ( **: ) : t -> int -> t 78 | (** power *) 79 | 80 | val inv : t -> t 81 | (** inverse *) 82 | end 83 | 84 | (** builds log/antilog table representation over any field representation *) 85 | module Make(G : Generator) : (Ops with type t = G.Ops.t) 86 | 87 | (** specialised representation using integers *) 88 | module Int(Ops : (Ops with type t = int array)) : (Ops with type t = int) 89 | 90 | (** simplified field specification using integers *) 91 | module type Params = sig 92 | val pp : int 93 | (** primitive polynomial (including leading power) *) 94 | 95 | val pe : int 96 | (** primitive element *) 97 | end 98 | 99 | module Params(P : Params) : (Generator with type Ops.t = int array) 100 | 101 | module MakeInt(P : Params) : (Ops with type t = int) 102 | 103 | 104 | end 105 | 106 | -------------------------------------------------------------------------------- /src/iter.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * reedsolomon - error correction CODEC 3 | * 4 | * (c) 2014 MicroJamJar Ltd 5 | * 6 | * Author(s): andy.ray@ujamjar.com 7 | * Description: 8 | * 9 | *) 10 | 11 | type rsparams = { 12 | m : int; (* bits per symbol *) 13 | k : int; (* message symbols *) 14 | t : int; (* correction capability, 2t=parity symbols *) 15 | n : int; (* codeword symbols *) 16 | b : int; (* starting root of generator *) 17 | prim_poly : int; (* primitive polynomial *) 18 | prim_elt : int; (* primitive element *) 19 | } 20 | 21 | type rspoly = int array 22 | 23 | let rspoly n = Array.make n 0 24 | 25 | let to_string = 26 | Array.fold_left 27 | (fun s x -> s ^ " " ^ string_of_int x) 28 | "" 29 | 30 | type rscodec = { 31 | params : rsparams; 32 | encode : rspoly -> rspoly -> unit; 33 | decode : rspoly -> rspoly -> int; 34 | } 35 | 36 | let init p = 37 | 38 | (* generate the reference implementation *) 39 | let module Gp = struct 40 | let pp = p.prim_poly 41 | let pe = p.prim_elt 42 | end in 43 | let module Rp = struct 44 | let t = p.t 45 | let k = p.k 46 | let b = p.b 47 | end in 48 | let module R = Codec.MakeStandard(Gp)(Rp) in 49 | let module G = R.G in 50 | 51 | (* array utilities *) 52 | let len = Array.length in 53 | let clear (a: int array) = for i=0 to len a - 1 do a.(i) <- 0 done in 54 | let get (a: int array) i = if i < 0 then G.zero else if i >= len a then G.zero else a.(i) in 55 | let iteri (a: int array) (f : int -> int) = for i=0 to len a - 1 do a.(i) <- f i done in 56 | let copy (t: int array) (f:int array) = for i=0 to len t - 1 do t.(i) <- f.(i) done in 57 | let shiftup (a: int array) (b: int array) = for i=len a - 1 downto 0 do a.(i) <- get a (i-1) done in 58 | let shiftdown (a: int array) (b: int array) = for i=0 to len a - 1 do a.(i) <- get b (i+1) done in 59 | 60 | let encode = 61 | let t2 = 2 * p.t in 62 | let generator = Array.init t2 (fun i -> R.R.generator.(t2-i-1)) in 63 | (fun message parity -> 64 | let k = len message in 65 | clear parity; 66 | for j=0 to k - 1 do 67 | let s = R.G.( message.(j) +: parity.(0) ) in 68 | for i=0 to t2 - 2 do 69 | parity.(i) <- R.G.((s *: generator.(i)) +: parity.(i+1)) 70 | done; 71 | parity.(t2-1) <- R.G.(s *: generator.(t2-1)) 72 | done 73 | ) 74 | in 75 | 76 | let decode = 77 | let t2 = 2 * p.t in 78 | 79 | (* roots of the generator polynomial *) 80 | let roots = Array.init t2 (fun i -> R.R.root i) in 81 | 82 | (* syndromes *) 83 | let syndromes = rspoly t2 in 84 | 85 | (* horners rule for polynomial evaluation *) 86 | let acc = ref G.zero in 87 | let horner a p = 88 | acc := G.zero; 89 | for i=0 to Array.length p - 1 do 90 | acc := G.( (!acc *: a) +: p.(i) ) 91 | done; 92 | !acc 93 | in 94 | 95 | let rhorner a p = 96 | acc := G.zero; 97 | for i=Array.length p-1 downto 0 do 98 | acc := G.( (!acc *: a) +: p.(i) ) 99 | done; 100 | !acc 101 | in 102 | 103 | (* inversion-less berlekamp-massy *) 104 | let riBM = 105 | let t = p.t in 106 | let z,o = G.zero, G.one in 107 | let lambda = Array.make (t+1) z in 108 | let lambda' = Array.make (t+1) z in 109 | let b = Array.make (t+1) z in 110 | let delta = Array.make (2*t) z in 111 | let delta' = Array.make (2*t) z in 112 | let theta = Array.make (2*t) z in 113 | let gamma = ref o in 114 | let k = ref 0 in 115 | 116 | let init s = 117 | let f i = if i=0 then o else z in 118 | iteri lambda f; 119 | iteri b f; 120 | copy delta s; 121 | copy theta s; 122 | gamma := o; 123 | k := 0; 124 | in 125 | 126 | let iter () = 127 | let update_lambda i = 128 | G.((!gamma *: lambda.(i)) -: (delta'.(0) *: get b (i-1))) 129 | in 130 | let update_delta i = 131 | G.((!gamma *: get delta' (i+1)) -: (delta'.(0) *: theta.(i))) 132 | in 133 | for r=0 to 2*t-1 do (* this is systolic, so much data is 0 during 134 | part of the iteration *) 135 | (* step 1 *) 136 | copy lambda' lambda; 137 | copy delta' delta; 138 | iteri lambda update_lambda; 139 | iteri delta update_delta; 140 | (* step 2 *) 141 | if delta'.(0) <> z && !k >= 0 then begin 142 | copy b lambda'; (* previous lambda *) 143 | shiftdown theta delta'; 144 | gamma := delta'.(0); 145 | k := - !k - 1 146 | end else begin 147 | shiftup b b; 148 | k := !k + 1 149 | end; 150 | done 151 | in 152 | 153 | (fun s d l -> 154 | init s; 155 | iter (); 156 | for i=0 to t-1 do d.(i) <- delta.(i) done; 157 | for i=0 to t do l.(i) <- lambda.(i) done) 158 | in 159 | 160 | let delta = rspoly p.t in 161 | let lambda = rspoly (p.t+1) in 162 | let lambda' = rspoly ((p.t+1)/2) in 163 | 164 | (* chien search *) 165 | let error_locs = rspoly p.t in 166 | let n_errors = ref 0 in 167 | let chien () = 168 | for n=0 to p.n-1 do 169 | if rhorner (G.antilog n) lambda = G.zero then begin 170 | error_locs.(!n_errors) <- n; 171 | incr n_errors; 172 | end 173 | done 174 | in 175 | 176 | let deriv () = 177 | for i=0 to ((p.t+1)/2)-1 do 178 | lambda'.(i) <- lambda.((i*2)+1) 179 | done 180 | in 181 | 182 | (* forney *) 183 | let error_magnitudes = rspoly p.t in 184 | let forney () = 185 | for i=0 to !n_errors-1 do 186 | let x' = G.antilog error_locs.(i) in 187 | let x = G.(x' **: (p.b+t2-1)) in 188 | let x2 = G.(x' **: 2) in 189 | error_magnitudes.(i) <- G.(x *: (rhorner x' delta /: rhorner x2 lambda')) 190 | done 191 | in 192 | 193 | let correct c = 194 | for i=0 to !n_errors-1 do 195 | let loc = error_locs.(i) in 196 | let loc = if loc = 0 then p.n-1 else loc-1 in 197 | let mag = error_magnitudes.(i) in 198 | c.(loc) <- G.(c.(loc) +: mag) 199 | done 200 | in 201 | 202 | (*let dump a = 203 | let open Printf in 204 | for i=0 to Array.length a - 1 do 205 | printf "%i " a.(i) 206 | done; 207 | printf "\n" 208 | in*) 209 | 210 | let ssum = ref G.zero in 211 | (fun received corrected -> 212 | n_errors := 0; 213 | ssum := G.zero; 214 | for i=0 to t2-1 do 215 | syndromes.(i) <- horner roots.(i) received; 216 | ssum := !ssum lor syndromes.(i) 217 | done; 218 | for i=0 to p.n-1 do 219 | corrected.(i) <- received.(i) 220 | done; 221 | if !ssum <> 0 then begin 222 | riBM syndromes delta lambda; 223 | chien (); 224 | deriv (); 225 | forney (); 226 | correct corrected 227 | end; 228 | !n_errors) 229 | in 230 | 231 | { params=p; encode; decode } 232 | 233 | 234 | -------------------------------------------------------------------------------- /src/iter.mli: -------------------------------------------------------------------------------- 1 | (* 2 | * reedsolomon - error correction CODEC 3 | * 4 | * (c) 2014 MicroJamJar Ltd 5 | * 6 | * Author(s): andy.ray@ujamjar.com 7 | * Description: 8 | * 9 | *) 10 | 11 | type rsparams = { 12 | m : int; (* bits per symbol *) 13 | k : int; (* message symbols *) 14 | t : int; (* correction capability, 2t=parity symbols *) 15 | n : int; (* codeword symbols *) 16 | b : int; (* starting root of generator *) 17 | prim_poly : int; (* primitive polynomial *) 18 | prim_elt : int; (* primitive element *) 19 | } 20 | 21 | type rspoly = int array 22 | 23 | val rspoly : int -> rspoly 24 | 25 | type rscodec = { 26 | params : rsparams; 27 | encode : rspoly -> rspoly -> unit; 28 | decode : rspoly -> rspoly -> int; 29 | } 30 | 31 | val init : rsparams -> rscodec 32 | 33 | -------------------------------------------------------------------------------- /src/matrix.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * reedsolomon - error correction CODEC 3 | * 4 | * (c) 2014 MicroJamJar Ltd 5 | * 6 | * Author(s): andy.ray@ujamjar.com 7 | * Description: 8 | * 9 | *) 10 | 11 | (* some simple matrix routines for addition, multiplication etc *) 12 | 13 | module type S = sig 14 | 15 | type t 16 | type matrix = t array array 17 | 18 | (* size of matrix *) 19 | val rows : matrix -> int 20 | val cols : matrix -> int 21 | 22 | (*val print : ?c:out_channel -> (t -> string) -> int -> matrix -> unit*) 23 | 24 | (* construction of various matrices *) 25 | val init : int -> int -> (int -> int -> t) -> matrix 26 | val create : int -> int -> matrix 27 | val copy : matrix -> matrix 28 | val identity : int -> matrix 29 | val transpose : matrix -> matrix 30 | val map : (t -> t) -> matrix -> matrix 31 | val map2 : (t -> t -> t) -> matrix -> matrix -> matrix 32 | 33 | val row_vector : t array -> matrix 34 | val col_vector : t array -> matrix 35 | 36 | (* concatenate matrices *) 37 | val (>>) : matrix -> matrix -> matrix 38 | val (^^) : matrix -> matrix -> matrix 39 | 40 | (* select a sub matrix *) 41 | val sub : int -> int -> int -> int -> matrix -> matrix 42 | 43 | (* arithmetic *) 44 | val (+:) : matrix -> matrix -> matrix 45 | val (-:) : matrix -> matrix -> matrix 46 | val ( *: ) : matrix -> matrix -> matrix 47 | val ( *:. ) : matrix -> t -> matrix 48 | 49 | (* functions related to inverses *) 50 | val minor : int -> int -> matrix -> matrix 51 | val det : matrix -> t 52 | val adjoint_inverse : matrix -> t * matrix 53 | 54 | (* these functions require element division *) 55 | val gauss_jordan : matrix -> matrix 56 | val gauss_jordan_inverse : matrix -> matrix 57 | 58 | module Row : sig 59 | val swap : int -> int -> int -> matrix 60 | val mult : int -> int -> t -> matrix 61 | val madd : int -> int -> int -> t -> matrix 62 | end 63 | 64 | end 65 | 66 | module Make(Ops : Ops.OpsBase) = struct 67 | 68 | type t = Ops.t 69 | 70 | type matrix = t array array 71 | 72 | let rows m = Array.length m 73 | let cols m = Array.length m.(0) 74 | 75 | (*let print ?(c=stdout) conv width m = 76 | let open Printf in 77 | for row=0 to rows m - 1 do 78 | for col=0 to cols m - 1 do 79 | fprintf c "%*s " width (conv m.(row).(col)) 80 | done; 81 | fprintf c "\n" 82 | done*) 83 | 84 | let init rows cols f = Array.init rows 85 | (fun r -> Array.init cols (fun c -> f r c)) 86 | 87 | let create rows cols = init rows cols (fun _ _ -> Ops.zero) 88 | 89 | let copy m = init (rows m) (cols m) (fun r c -> m.(r).(c)) 90 | 91 | let identity n = init n n (fun r c -> if r=c then Ops.one else Ops.zero) 92 | 93 | let transpose m = init (cols m) (rows m) (fun c r -> m.(r).(c)) 94 | 95 | let map f m = init (rows m) (cols m) (fun r c -> f m.(r).(c)) 96 | let map2 f m0 m1 = init (rows m0) (cols m1) 97 | (fun r c -> f m0.(r).(c) m1.(r).(c)) 98 | 99 | 100 | let col_vector a = init (Array.length a) 1 (fun c _ -> a.(c)) 101 | let row_vector a = init 1 (Array.length a) (fun _ r -> a.(r)) 102 | 103 | let (>>) a b = 104 | assert (rows a = rows b); 105 | let ca, cb = cols a, cols b in 106 | init (rows a) (ca + cb) (fun row col -> 107 | if col < ca then a.(row).(col) 108 | else b.(row).(col - ca)) 109 | 110 | let (^^) a b = 111 | assert (cols a = cols b); 112 | let ra, rb = rows a, rows b in 113 | init (ra + rb) (cols a) (fun row col -> 114 | if row < ra then a.(row).(col) 115 | else b.(row - ra).(col)) 116 | 117 | let sub row_off col_off rows cols m = 118 | init rows cols (fun r c -> m.(row_off + r).(col_off + c)) 119 | 120 | let (+:) a b = 121 | assert (rows a = rows b); 122 | assert (cols a = cols b); 123 | init (rows a) (cols a) (fun r c -> Ops.(a.(r).(c) +: b.(r).(c))) 124 | 125 | let (-:) a b = 126 | assert (rows a = rows b); 127 | assert (cols a = cols b); 128 | init (rows a) (cols a) (fun r c -> Ops.(a.(r).(c) -: b.(r).(c))) 129 | 130 | let ( *: ) a b = 131 | let ca, cb = cols a, cols b in 132 | let ra, rb = rows a, rows b in 133 | assert (ca = rb); 134 | let mult row col = 135 | let rec f i x = 136 | if i=ca then x 137 | else f (i+1) Ops.(x +: a.(row).(i) *: b.(i).(col)) 138 | in 139 | f 0 Ops.zero 140 | in 141 | init ra cb mult 142 | 143 | let ( *:. ) a b = 144 | init (rows a) (cols a) (fun r c -> Ops.(a.(r).(c) *: b)) 145 | 146 | let minor row col m = 147 | assert (row < rows m); 148 | assert (col < cols m); 149 | init (rows m - 1) (cols m - 1) 150 | (fun r c -> 151 | let r = if r < row then r else r+1 in 152 | let c = if c < col then c else c+1 in 153 | m.(r).(c) 154 | ) 155 | 156 | let rec det m = 157 | assert (rows m = cols m); 158 | let n = cols m in 159 | if n=1 then m.(0).(0) 160 | (*else if n=2 then det2 m*) 161 | else 162 | let rec f i = 163 | if i=n then Ops.zero 164 | else 165 | let m' = minor 0 i m in 166 | let op = if i mod 2 = 0 then Ops.(-:) else Ops.(+:) in 167 | Ops.(op (m.(0).(i) *: det m') (f (i+1))) 168 | in 169 | f 0 170 | 171 | let adjoint_inverse m = 172 | let d = det m in 173 | if d = Ops.zero then failwith "cannot invert" 174 | else 175 | let m = init (rows m) (cols m) (fun r c -> 176 | let inv = (r+c) mod 2 = 1 in 177 | let d = det (minor r c m) in 178 | if inv then Ops.(zero -: d) 179 | else d) 180 | in 181 | d, transpose m 182 | 183 | let gauss_jordan m = 184 | let open Ops in 185 | let m = copy m in 186 | (* scale row r by s *) 187 | let scale_row r s = 188 | for i=0 to Array.length r - 1 do 189 | r.(i) <- r.(i) /: s 190 | done 191 | in 192 | (* scale row y by s and subtract from x *) 193 | let sub_scale_row x y s = 194 | for i=0 to Array.length x - 1 do 195 | x.(i) <- x.(i) -: (y.(i) *: s) 196 | done 197 | in 198 | (* find the biggest element at the diagonal 199 | * position between row r and the rows below. 200 | * move the largest element to the pivot position. 201 | * at worst this will avoid 0 pivots. it should 202 | * also help numerical stability a bit *) 203 | let pivot r = 204 | let max = ref r in 205 | for i=r+1 to rows m - 1 do 206 | if m.(i).(r) > m.(!max).(r) then 207 | max := i 208 | done; 209 | let a,b = m.(r), m.(!max) in 210 | m.(r) <- b; 211 | m.(!max) <- a 212 | in 213 | for row=0 to rows m - 1 do 214 | pivot row; 215 | scale_row m.(row) m.(row).(row); 216 | for i=0 to rows m - 1 do 217 | if i <> row then 218 | sub_scale_row m.(i) m.(row) m.(i).(row) 219 | done 220 | done; 221 | m 222 | 223 | let gauss_jordan_inverse m = 224 | let n = rows m in 225 | let m = m >> identity n in 226 | let m = gauss_jordan m in 227 | sub 0 n n n m 228 | 229 | (* elementary row operations *) 230 | module Row = struct 231 | 232 | let swap n i j = 233 | init n n (fun r c -> 234 | if r=i then 235 | if c=j then Ops.one else Ops.zero 236 | else if r=j then 237 | if c=i then Ops.one else Ops.zero 238 | else if r=c then Ops.one 239 | else Ops.zero) 240 | 241 | let mult n i m = 242 | init n n (fun r c -> 243 | if r=c then 244 | if r=i then m 245 | else Ops.one 246 | else Ops.zero) 247 | 248 | let madd n i j m = 249 | init n n (fun r c -> 250 | if r=j && c=i then m 251 | else if r=c then Ops.one 252 | else Ops.zero) 253 | 254 | end 255 | 256 | end 257 | 258 | -------------------------------------------------------------------------------- /src/matrix.mli: -------------------------------------------------------------------------------- 1 | (* 2 | * reedsolomon - error correction CODEC 3 | * 4 | * (c) 2014 MicroJamJar Ltd 5 | * 6 | * Author(s): andy.ray@ujamjar.com 7 | * Description: 8 | * 9 | *) 10 | 11 | 12 | module type S = sig 13 | 14 | type t 15 | type matrix = t array array 16 | 17 | (* size of matrix *) 18 | val rows : matrix -> int 19 | val cols : matrix -> int 20 | 21 | (*val print : ?c:out_channel -> (t -> string) -> int -> matrix -> unit*) 22 | 23 | (* construction of various matrices *) 24 | val init : int -> int -> (int -> int -> t) -> matrix 25 | val create : int -> int -> matrix 26 | val copy : matrix -> matrix 27 | val identity : int -> matrix 28 | val transpose : matrix -> matrix 29 | val map : (t -> t) -> matrix -> matrix 30 | val map2 : (t -> t -> t) -> matrix -> matrix -> matrix 31 | 32 | val row_vector : t array -> matrix 33 | val col_vector : t array -> matrix 34 | 35 | (* concatenate matrices *) 36 | val (>>) : matrix -> matrix -> matrix 37 | val (^^) : matrix -> matrix -> matrix 38 | 39 | (* select a sub matrix *) 40 | val sub : int -> int -> int -> int -> matrix -> matrix 41 | 42 | (* arithmetic *) 43 | val (+:) : matrix -> matrix -> matrix 44 | val (-:) : matrix -> matrix -> matrix 45 | val ( *: ) : matrix -> matrix -> matrix 46 | val ( *:. ) : matrix -> t -> matrix 47 | 48 | (* functions related to inverses *) 49 | val minor : int -> int -> matrix -> matrix 50 | val det : matrix -> t 51 | val adjoint_inverse : matrix -> t * matrix 52 | 53 | (* these functions require element division *) 54 | val gauss_jordan : matrix -> matrix 55 | val gauss_jordan_inverse : matrix -> matrix 56 | 57 | (* elementary row operations *) 58 | module Row : sig 59 | val swap : int -> int -> int -> matrix 60 | val mult : int -> int -> t -> matrix 61 | val madd : int -> int -> int -> t -> matrix 62 | end 63 | 64 | end 65 | 66 | module Make(Ops : Ops.OpsBase) : (S with type t = Ops.t) 67 | 68 | -------------------------------------------------------------------------------- /src/ops.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * reedsolomon - error correction CODEC 3 | * 4 | * (c) 2014 MicroJamJar Ltd 5 | * 6 | * Author(s): andy.ray@ujamjar.com 7 | * Description: 8 | * 9 | *) 10 | 11 | module type OpsBase = sig 12 | type t 13 | val zero : t 14 | val one : t 15 | val (+:) : t -> t -> t 16 | val (-:) : t -> t -> t 17 | val ( *: ) : t -> t -> t 18 | val (/:) : t -> t -> t 19 | val to_string : t -> string 20 | end 21 | 22 | module type OpsFull = sig 23 | include OpsBase 24 | 25 | val (%:) : t -> t -> t 26 | val abs : t -> t 27 | 28 | val (<<:) : t -> int -> t 29 | val (>>+) : t -> int -> t 30 | val (>>:) : t -> int -> t 31 | 32 | val (&:) : t -> t -> t 33 | val (|:) : t -> t -> t 34 | val (^:) : t -> t -> t 35 | val (~:) : t -> t 36 | 37 | val of_int : int -> t 38 | val to_int : t -> int 39 | val of_int32 : int32 -> t 40 | val to_int32 : t -> int32 41 | val of_int64 : int64 -> t 42 | val to_int64 : t -> int64 43 | val of_float : float -> t 44 | val to_float : t -> float 45 | val of_string : string -> t 46 | end 47 | 48 | module Int = struct 49 | type t = int 50 | let zero = 0 51 | let one = 1 52 | let (+:) = (+) 53 | let (-:) = (-) 54 | let ( *: ) = ( * ) 55 | let (/:) = (/) 56 | let (%:) = (mod) 57 | let abs = abs 58 | let (<<:) = (lsl) 59 | let (>>+) = (asr) 60 | let (>>:) = (lsr) 61 | let (&:) = (land) 62 | let (|:) = (lor) 63 | let (^:) = (lxor) 64 | let (~:) = lnot 65 | let of_int x = x 66 | let to_int x = x 67 | let of_int32 = Int32.to_int 68 | let to_int32 = Int32.of_int 69 | let of_int64 = Int64.to_int 70 | let to_int64 = Int64.of_int 71 | let of_float x = int_of_float x 72 | let to_float x = float_of_int x 73 | let of_string x = int_of_string x 74 | let to_string x = string_of_int x 75 | end 76 | 77 | module Int32 = struct 78 | type t = int32 79 | open Int32 80 | let zero = 0l 81 | let one = 1l 82 | let (+:) = add 83 | let (-:) = sub 84 | let ( *: ) = mul 85 | let (/:) = div 86 | let (%:) = rem 87 | let abs = abs 88 | let (<<:) = shift_left 89 | let (>>+) = shift_right 90 | let (>>:) = shift_right_logical 91 | let (&:) = logand 92 | let (|:) = logor 93 | let (^:) = logxor 94 | let (~:) = lognot 95 | let of_int = of_int 96 | let to_int = to_int 97 | let of_int32 x = x 98 | let to_int32 x = x 99 | let of_int64 = Int64.to_int32 100 | let to_int64 = Int64.of_int32 101 | let of_float = of_float 102 | let to_float = to_float 103 | let of_string = of_string 104 | let to_string = to_string 105 | end 106 | 107 | module Int64 = struct 108 | type t = int64 109 | open Int64 110 | let zero = 0L 111 | let one = 1L 112 | let (+:) = add 113 | let (-:) = sub 114 | let ( *: ) = mul 115 | let (/:) = div 116 | let (%:) = rem 117 | let abs = abs 118 | let (<<:) = shift_left 119 | let (>>+) = shift_right 120 | let (>>:) = shift_right_logical 121 | let (&:) = logand 122 | let (|:) = logor 123 | let (^:) = logxor 124 | let (~:) = lognot 125 | let of_int = of_int 126 | let to_int = to_int 127 | let of_float = of_float 128 | let to_float = to_float 129 | let of_string = of_string 130 | let to_string = to_string 131 | let of_int32 = Int64.of_int32 132 | let to_int32 = Int64.to_int32 133 | let of_int64 x = x 134 | let to_int64 x = x 135 | end 136 | 137 | module Float = struct 138 | type t = float 139 | let zero = 0.0 140 | let one = 1.0 141 | let (+:) = (+.) 142 | let (-:) = (-.) 143 | let ( *: ) = ( *. ) 144 | let (/:) = ( /. ) 145 | let (%:) = mod_float 146 | let abs = abs_float 147 | let (<<:) a b = failwith "Ops.Float: <<:" 148 | let (>>+) a b = failwith "Ops.Float: >>+" 149 | let (>>:) a b = failwith "Ops.Float: >>:" 150 | let (&:) a b = failwith "Ops.Float: &:" 151 | let (|:) a b = failwith "Ops.Float: |:" 152 | let (^:) a b = failwith "Ops.Float: ^:" 153 | let (~:) a = failwith "Ops.Float: ~:" 154 | let of_int = float_of_int 155 | let to_int = int_of_float 156 | let of_float x = x 157 | let to_float x = x 158 | let of_string = float_of_string 159 | let to_string = string_of_float 160 | let of_int32 = Int32.to_float 161 | let to_int32 = Int32.of_float 162 | let of_int64 = Int64.to_float 163 | let to_int64 = Int64.of_float 164 | end 165 | 166 | 167 | -------------------------------------------------------------------------------- /src/ops.mli: -------------------------------------------------------------------------------- 1 | (* 2 | * reedsolomon - error correction CODEC 3 | * 4 | * (c) 2014 MicroJamJar Ltd 5 | * 6 | * Author(s): andy.ray@ujamjar.com 7 | * Description: 8 | * 9 | *) 10 | 11 | module type OpsBase = sig 12 | type t 13 | val zero : t 14 | val one : t 15 | val (+:) : t -> t -> t 16 | val (-:) : t -> t -> t 17 | val ( *: ) : t -> t -> t 18 | val (/:) : t -> t -> t 19 | val to_string : t -> string 20 | end 21 | 22 | module type OpsFull = sig 23 | include OpsBase 24 | 25 | (* arithmetic *) 26 | val (%:) : t -> t -> t 27 | val abs : t -> t 28 | 29 | (* shift *) 30 | 31 | val (<<:) : t -> int -> t 32 | val (>>+) : t -> int -> t 33 | val (>>:) : t -> int -> t 34 | 35 | (* bitwise *) 36 | 37 | val (&:) : t -> t -> t 38 | val (|:) : t -> t -> t 39 | val (^:) : t -> t -> t 40 | val (~:) : t -> t 41 | 42 | (* conversion *) 43 | 44 | val of_int : int -> t 45 | val to_int : t -> int 46 | val of_int32 : int32 -> t 47 | val to_int32 : t -> int32 48 | val of_int64 : int64 -> t 49 | val to_int64 : t -> int64 50 | val of_float : float -> t 51 | val to_float : t -> float 52 | val of_string : string -> t 53 | end 54 | 55 | module Int : (OpsFull with type t = int) 56 | module Int32 : (OpsFull with type t = int32) 57 | module Int64 : (OpsFull with type t = int64) 58 | module Float : (OpsFull with type t = float) 59 | 60 | 61 | -------------------------------------------------------------------------------- /src/poly.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * reedsolomon - error correction CODEC 3 | * 4 | * (c) 2014 MicroJamJar Ltd 5 | * 6 | * Author(s): andy.ray@ujamjar.com 7 | * Description: 8 | * 9 | *) 10 | 11 | (* polynomial api. powers are at array index positions *) 12 | 13 | module type S = sig 14 | 15 | type elt 16 | type t 17 | 18 | val degree : t -> int 19 | val zero : t 20 | val one : t 21 | val x : t 22 | val to_poly : elt array -> t 23 | val of_poly : t -> elt array 24 | val copy : t -> t 25 | type poly_format = 26 | { 27 | coef : elt -> string; 28 | indet : int -> string; 29 | } 30 | val poly_format : poly_format 31 | val string_format : bool -> poly_format -> int -> elt -> string 32 | val to_string : ?down:bool -> ?str:(int -> elt -> string) -> t -> string 33 | val trim : t -> t 34 | val slice : t -> int -> t 35 | 36 | val (+:) : t -> t -> t 37 | val (-:) : t -> t -> t 38 | val ( *: ) : t -> t -> t 39 | val (/:) : t -> t -> t * t 40 | val ( *:. ) : t -> elt -> t 41 | val (/:.) : t -> elt -> t 42 | val (^:) : t -> int -> t 43 | val ( **: ) : t -> int -> t 44 | 45 | val ext_gcd : t -> t -> t * t 46 | 47 | val eval : t -> elt -> elt 48 | 49 | end 50 | 51 | module Make(E : Ops.OpsBase) = struct 52 | 53 | type elt = E.t 54 | type t = elt array 55 | 56 | let string_of_elt = E.to_string 57 | 58 | let degree p = Array.length p - 1 59 | 60 | let zero = [| E.zero |] 61 | let one = [| E.one |] 62 | let x = [| E.zero;E.one |] 63 | 64 | (* remove high powers which are 0 *) 65 | let trim' p = 66 | (* look for trailing zero's *) 67 | let deg = degree p in 68 | if deg < 0 then p 69 | else 70 | let rec cnt n = 71 | if n = 0 then n 72 | else if p.(n) = E.zero then cnt (n-1) 73 | else n 74 | in 75 | let c = cnt deg in 76 | if c = deg then p 77 | else Array.init (c+1) (fun i -> p.(i)) 78 | 79 | (* ensure '0' is [|0|] *) 80 | let trim p = 81 | let p = trim' p in 82 | if degree p < 0 then zero 83 | else p 84 | 85 | let to_poly p = trim p 86 | let of_poly p = p 87 | 88 | let slice p deg = 89 | let pdeg = degree p in 90 | if deg = pdeg then p 91 | else 92 | Array.init (deg+1) (fun i -> 93 | if i <= pdeg then p.(i) 94 | else E.zero) 95 | 96 | let copy p = Array.copy p 97 | 98 | type poly_format = 99 | { 100 | coef : elt -> string; 101 | indet : int -> string; 102 | } 103 | 104 | let poly_format = 105 | { 106 | coef = string_of_elt; 107 | indet = 108 | function 109 | | 0 -> "" 110 | | 1 -> "x" 111 | | _ as n -> "x^" ^ string_of_int n; 112 | } 113 | 114 | let string_format filter fmt pow cof = 115 | let coef = fmt.coef cof in 116 | let indet = fmt.indet pow in 117 | if filter && cof=E.zero then "" 118 | else if filter && cof=E.one then 119 | match pow with 120 | | 0 -> coef 121 | | _ -> indet 122 | else 123 | match coef,indet with 124 | | "","" -> "" 125 | | coef,"" -> coef 126 | | "",indet -> indet 127 | | _ -> coef ^ "." ^ indet 128 | 129 | let to_string ?(down=true) ?(str=(string_format true poly_format)) p = 130 | let open Printf in 131 | let sep a s = 132 | if a="" then s else (if down then s ^ " + " ^ a else a ^ " + " ^ s) 133 | in 134 | let p = 135 | List.filter ((<>)"") (Array.to_list (Array.mapi str p)) 136 | in 137 | List.fold_left sep "" p 138 | (*Array.fold_left sep "" (Array.mapi str p)*) 139 | 140 | (* add polys *) 141 | let add a b = 142 | let max = max (degree a) (degree b) in 143 | let a,b = slice a max, slice b max in 144 | trim (Array.init (max+1) E.(fun i -> a.(i) +: b.(i))) 145 | 146 | let sub a b = 147 | let max = max (degree a) (degree b) in 148 | let a,b = slice a max, slice b max in 149 | trim (Array.init (max+1) E.(fun i -> a.(i) -: b.(i))) 150 | 151 | (* raise to power of x *) 152 | let pow_x a b = 153 | if b < 0 then 154 | (try trim (Array.init (degree a + b + 1) (fun i -> a.(i-b))) 155 | with _ -> zero) 156 | else if b=0 then trim a 157 | else trim (Array.concat [ Array.make b E.zero; a ]) 158 | 159 | (* scale poly *) 160 | let mul_scalar a b = trim (Array.map E.(fun a -> a *: b) a) 161 | let div_scalar a b = trim (Array.map E.(fun a -> a /: b) a) 162 | 163 | (* multiply polys *) 164 | let mul a b = 165 | let a, b = trim a, trim b in 166 | let factors = Array.mapi (fun n a -> pow_x (mul_scalar b a) n) a in 167 | trim (Array.fold_left add [| E.zero |] factors) 168 | 169 | (* polynomial divison *) 170 | let div a b = 171 | let a, b = trim a, trim b in 172 | if b = zero then failwith "poly divide by zero" 173 | else if b = one then a, zero 174 | else if degree b = 0 then div_scalar a b.(0), zero 175 | else 176 | let da = degree a in 177 | let db = degree b in 178 | let rec div a q = 179 | let da = degree a in 180 | if da < db then q,a 181 | else 182 | let n = da-db in 183 | let s = E.(a.(da) /: b.(db)) in 184 | let a' = trim (slice (sub a (mul_scalar (pow_x b n) s)) (da-1)) in 185 | div a' ((n,s)::q) 186 | in 187 | let q,r = div a [] in 188 | trim (Array.init da (fun i -> try List.assoc i q with _ -> E.zero)), r 189 | 190 | let rec ext_gcd a b = 191 | if b = zero then one, zero 192 | else 193 | let q, r = div a b in 194 | let s, t = ext_gcd b r in 195 | t, sub s (mul q t) 196 | 197 | let eval p x = 198 | let deg = degree p in 199 | let rec f n x' = 200 | if n > deg then E.zero 201 | else 202 | let a = E.(x' *: p.(n)) in 203 | E.(a +: f (n+1) (x' *: x)) 204 | in 205 | E.(p.(0) +: f 1 x) 206 | 207 | let (+:) = add 208 | let (-:) = sub 209 | let ( *: ) = mul 210 | let (/:) = div 211 | let ( *:. ) = mul_scalar 212 | let (/:.) = div_scalar 213 | let (^:) = pow_x 214 | let rec ( **: ) x = function 215 | | 0 -> one 216 | | 1 -> x 217 | | _ as n -> x *: (x **: (n-1)) 218 | 219 | end 220 | 221 | -------------------------------------------------------------------------------- /src/poly.mli: -------------------------------------------------------------------------------- 1 | (* 2 | * reedsolomon - error correction CODEC 3 | * 4 | * (c) 2014 MicroJamJar Ltd 5 | * 6 | * Author(s): andy.ray@ujamjar.com 7 | * Description: 8 | * 9 | *) 10 | 11 | (** basic polynomials of 1 variable *) 12 | module type S = sig 13 | 14 | (** polynomial coefficient type *) 15 | type elt 16 | (** an array of 'elt's representing the polynomial. powers 17 | are at index position (ie lowest first) *) 18 | type t 19 | 20 | (** the degree of the polynomial *) 21 | val degree : t -> int 22 | (** this is represented as [|E.zero|] *) 23 | val zero : t 24 | (** this is represented as [|E.one|] *) 25 | val one : t 26 | (** this is represented as [|E.zero;E.one|] *) 27 | val x : t 28 | (** convert to poly *) 29 | val to_poly : elt array -> t 30 | (** convert from poly *) 31 | val of_poly : t -> elt array 32 | (** make a copy of the poly *) 33 | val copy : t -> t 34 | 35 | (** control over print formatting *) 36 | type poly_format = 37 | { 38 | coef : elt -> string; 39 | indet : int -> string; 40 | } 41 | val poly_format : poly_format 42 | val string_format : bool -> poly_format -> int -> elt -> string 43 | 44 | (** create string of poly *) 45 | val to_string : ?down:bool -> ?str:(int -> elt -> string) -> t -> string 46 | 47 | (** legalise the poly. high order powers which are 0 are removed. *) 48 | val trim : t -> t 49 | 50 | val slice : t -> int -> t 51 | 52 | (** poly addition *) 53 | val (+:) : t -> t -> t 54 | (** poly subtraction *) 55 | val (-:) : t -> t -> t 56 | (** poly multiplication *) 57 | val ( *: ) : t -> t -> t 58 | (** poly division *) 59 | val (/:) : t -> t -> t * t 60 | (** scalar multiplication *) 61 | val ( *:. ) : t -> elt -> t 62 | (** scalar division *) 63 | val (/:.) : t -> elt -> t 64 | (** multiply poly by x^n *) 65 | val (^:) : t -> int -> t 66 | (** raise poly to power n *) 67 | val ( **: ) : t -> int -> t 68 | 69 | (** extended gcd algorithm *) 70 | val ext_gcd : t -> t -> t * t 71 | 72 | (** evaluate polynomial *) 73 | val eval : t -> elt -> elt 74 | 75 | end 76 | 77 | (** Basic polynomial representations. Coefficients are members of 78 | Ops.OpsBase (which effectively provide '+' and '*') *) 79 | module Make(E : Ops.OpsBase) : (S with type t = E.t array 80 | and type elt = E.t) 81 | 82 | -------------------------------------------------------------------------------- /test/dune: -------------------------------------------------------------------------------- 1 | (executable 2 | (name test_iter) 3 | (flags (:standard -w -27-35)) 4 | (libraries reedsolomon unix)) 5 | -------------------------------------------------------------------------------- /test/test_iter.ml: -------------------------------------------------------------------------------- 1 | open Reedsolomon.Iter 2 | 3 | module Test(X : sig val rsp : rsparams end) = struct 4 | 5 | open X 6 | 7 | let () = Printf.printf "%i bits/symbol, %i symbols/codeword, t=%i\n" 8 | rsp.m rsp.n rsp.t 9 | 10 | let rand_sort a = 11 | for i=(Array.length a - 1) downto 1 do 12 | let swap = Random.int (i+1) in 13 | let tmp = a.(i) in 14 | a.(i) <- a.(swap); 15 | a.(swap) <- tmp 16 | done 17 | 18 | (* random error vector with 'e' errors *) 19 | let error e = 20 | let e = Array.init rsp.n (fun i -> if i < e then 1 + Random.int rsp.n else 0) in 21 | rand_sort e; 22 | e 23 | 24 | let random n = Array.init n (fun i -> Random.int (rsp.n+1)) 25 | 26 | let rs = init rsp 27 | 28 | let print a = 29 | let open Printf in 30 | for i=0 to Array.length a - 1 do 31 | printf "%.2x" a.(i); 32 | done; 33 | printf "\n" 34 | 35 | (* correctness tests *) 36 | let test1 n_errors = 37 | let data = random rsp.k in 38 | let parity = rspoly (rsp.t*2) in 39 | let () = rs.encode data parity in 40 | let message = Array.concat [ data;parity ] in 41 | let err = error n_errors in 42 | let received = Array.init rsp.n (fun i -> message.(i) lxor err.(i)) in 43 | let corrected = rspoly rsp.n in 44 | let n_corrections = rs.decode received corrected in 45 | if corrected <> message || n_corrections <> n_errors then begin 46 | Printf.printf "\n\nERROR:\n\n"; 47 | print data; 48 | print message; 49 | print err; 50 | print received; 51 | print corrected; 52 | Printf.printf "n_errors=%i n_corrections=%i\n" n_errors n_corrections; 53 | end; 54 | n_corrections, corrected = message 55 | 56 | let testn nn = 57 | for ne=0 to rsp.t do 58 | for n=0 to nn-1 do 59 | Printf.printf "\r%i %i%!" ne n; 60 | let ce,ok = test1 ne in 61 | assert (ce=ne); 62 | assert ok; 63 | done; 64 | Printf.printf "\r \r%!"; 65 | done; 66 | Printf.printf "OK\n%!" 67 | 68 | let () = testn 10000 69 | 70 | (* performance tests. *) 71 | let perf n_errors n_tests = 72 | let data = random rsp.k in 73 | let parity = rspoly (rsp.t*2) in 74 | let () = rs.encode data parity in 75 | let message = Array.concat [ data;parity ] in 76 | let err = error n_errors in 77 | let received = Array.init rsp.n (fun i -> message.(i) lxor err.(i)) in 78 | let corrected = rspoly rsp.n in 79 | for i=0 to n_tests-1 do 80 | ignore (rs.decode received corrected) 81 | done 82 | 83 | let perfs n_tests = 84 | for ne=0 to rsp.t do 85 | let time = Unix.gettimeofday() in 86 | perf ne n_tests; 87 | let time = Unix.gettimeofday() -. time in 88 | Printf.printf "%i errors/cw %f secs %f Mbits/sec\n%!" 89 | ne time 90 | (float_of_int (rsp.n * rsp.m * n_tests) /. (1000000. *. time)) 91 | done 92 | 93 | let () = perfs 10000 94 | 95 | end 96 | 97 | let bbc = 98 | { 99 | m = 4; 100 | k = 11; 101 | t = 2; 102 | n = 15; 103 | b = 0; 104 | prim_poly = 19; 105 | prim_elt = 2; 106 | } 107 | 108 | let g709 = 109 | { 110 | m = 8; 111 | k = 239; 112 | t = 8; 113 | n = 255; 114 | b = 0; 115 | prim_poly = 285; 116 | prim_elt = 2; 117 | } 118 | 119 | let g16 = 120 | { 121 | m = 8; 122 | k = 255-32; 123 | t = 16; 124 | n = 255; 125 | b = 0; 126 | prim_poly = 285; 127 | prim_elt = 2; 128 | } 129 | 130 | (* 4 bits/symbol, t=2 *) 131 | module A = Test(struct let rsp = bbc end) 132 | (* 8 bits/symbol, t=8 *) 133 | module B = Test(struct let rsp = g709 end) 134 | (* 8 bits/symbol, t=16 *) 135 | module C = Test(struct let rsp = g16 end) 136 | 137 | -------------------------------------------------------------------------------- /webdemo/dune: -------------------------------------------------------------------------------- 1 | (executable 2 | (name rswebdemo) 3 | (libraries reedsolomon js_of_ocaml) 4 | (flags (:standard -w -27-32)) 5 | (js_of_ocaml (flags "+nat.js")) 6 | (preprocess (pps js_of_ocaml-ppx))) 7 | -------------------------------------------------------------------------------- /webdemo/rswebdemo.ml: -------------------------------------------------------------------------------- 1 | (* Reed-Solomon web tutorial *) 2 | open Reedsolomon 3 | module D = Dom_html 4 | 5 | let rec nbits x = 6 | if x < 0 then failwith "arg to clog2 must be >= 0"; 7 | match x with 0 | 1 -> 1 | x -> 1 + (nbits (x/2)) 8 | 9 | module B = struct 10 | (* copied from hardcaml - could be much more efficient (but it doesnt matter *) 11 | let constb v = 12 | let to_int = function '0' -> 0 | '1' -> 1 | _ -> failwith "invalid constant" in 13 | let len = String.length v in 14 | let rec const b i = 15 | if len = i then b 16 | else const (to_int v.[i] :: b) (i+1) 17 | in 18 | List.rev (const [] 0) 19 | let bstr_of_int w d = 20 | let rec b i d = 21 | if i = w then "" 22 | else b (i+1) (d asr 1) ^ (if d land 1 = 1 then "1" else "0") 23 | in 24 | b 0 d 25 | let consti l v = constb (bstr_of_int l v) 26 | let consti b v = [] 27 | let to_int x = List.fold_left (fun acc x -> (acc * 2) + x) 0 x 28 | 29 | end 30 | 31 | (***********************************************************************) 32 | 33 | let debug = true 34 | let jlog s = if debug then Firebug.console##(log s) 35 | let log s = if debug then jlog (Js.string s) 36 | 37 | let ostr = Js.to_string 38 | let jstr = Js.string 39 | let jstri x = jstr (string_of_int x) 40 | 41 | let get_element e = 42 | let d = D.document in 43 | Js.Opt.get 44 | (d##(getElementById (Js.string ("jsoo_" ^ e)))) 45 | (fun () -> 46 | log ("get_element failed: " ^ e); 47 | assert false) 48 | 49 | let rec delete_kids par = 50 | let kid = par##.firstChild in 51 | match Js.Opt.to_option kid with 52 | | Some(kid) -> begin 53 | Dom.removeChild par kid; 54 | delete_kids par 55 | end 56 | | None -> 57 | () 58 | 59 | let get_input n = 60 | match D.tagged (get_element n) with 61 | | D.Input(x) -> x 62 | | _ -> failwith ("couldn't find text element" ^ n) 63 | 64 | let mk_table border n_rows n_cols f = 65 | let d = D.document in 66 | let table = D.createTable d in 67 | let tbody = D.createTbody d in 68 | Dom.appendChild table tbody; 69 | for row=0 to n_rows-1 do 70 | let trow = D.createTr d in 71 | for col=0 to n_cols-1 do 72 | let telt = D.createTd d in 73 | if (border row col) then begin 74 | let style = telt##.style in 75 | style##.borderStyle := Js.string "solid"; 76 | style##.borderWidth := Js.string "1px"; 77 | style##.borderColor := Js.string "#d0d0d0"; 78 | end; 79 | Dom.appendChild telt (f row col); 80 | Dom.appendChild trow telt 81 | done; 82 | Dom.appendChild tbody trow 83 | done; 84 | table 85 | 86 | let mk_p ?(className="js_para") s = 87 | let d = D.document in 88 | let t = D.createP d in 89 | t##.className := Js.string className; 90 | t##.innerHTML := Js.string s; 91 | t 92 | 93 | (* XXX this is a bit yukky... 94 | * we invoke the 'old' handler then the new one to allow us to 95 | * chain them, however, the first time round there is no handler 96 | * so what happens? We catch problems with exceptions. 97 | * I dont know how to detect when its empty - the old way 98 | * of checking against 'no_handler' worked in chrome but not firefox *) 99 | let install_window_onload h = 100 | let o = D.window##.onload in 101 | D.window##.onload := D.handler (fun e -> 102 | let safe_invoke h = 103 | try ignore(D.invoke_handler h D.window e) 104 | with _ -> log "safe_invoke failed" 105 | in 106 | safe_invoke o; 107 | safe_invoke h; 108 | Js._false 109 | ) 110 | 111 | (***********************************************************************) 112 | 113 | class type jq_math = object('self) 114 | method parseMath : Dom_html.element Js.t -> unit Js.meth 115 | end 116 | let math : jq_math Js.t = (Js.Unsafe.variable "window")##._M 117 | 118 | 119 | (***********************************************************************) 120 | 121 | module type Params = sig 122 | val m : int 123 | val n : int 124 | val k : int 125 | val t : int 126 | val b : int 127 | val pp : int 128 | val pe : int 129 | val decimal : bool 130 | val ud : bool 131 | end 132 | 133 | let value name = (get_input name)##.value 134 | let int_value name = int_of_string (ostr (value name)) 135 | 136 | let get_m () = int_value "param_m" 137 | let get_t () = int_value "param_t" 138 | let get_pp () = int_value "param_pp" 139 | let get_pe () = int_value "param_pe" 140 | let get_b () = int_value "param_b" 141 | 142 | let get_n () = (1 lsl (get_m())) - 1 143 | let get_k () = get_n() - (get_t() * 2) 144 | 145 | let get_dec () = Js.to_bool (get_input "show_decimal")##.checked 146 | let get_ud () = Js.to_bool (get_input "up_down")##.checked 147 | 148 | let set_int_value name x = (get_input name)##.value := jstr (string_of_int x) 149 | 150 | let set_pp = set_int_value "param_pp" 151 | let set_pe = set_int_value "param_pe" 152 | 153 | let read_params () = 154 | get_m(), get_t(), get_pp(), get_pe(), get_b(), get_dec(), get_ud() 155 | 156 | let mk_params () = 157 | let m,t,pp,pe,b,dec,ud = read_params () in 158 | let module P = struct 159 | let m = m 160 | let n = (1 lsl m) - 1 161 | let k = n - (2*t) 162 | let t = t 163 | let b = b 164 | let pp = pp 165 | let pe = pe 166 | let decimal = dec 167 | let ud = ud 168 | end in 169 | (module P : Params) 170 | 171 | (***********************************************************************) 172 | 173 | module Gen(P : Params) = struct 174 | 175 | let prim_poly = Array.of_list (List.rev (B.consti (nbits P.pp) P.pp)) 176 | let prim_elt = Array.of_list (List.rev (B.consti (nbits P.pe) P.pe)) 177 | 178 | module G' = Galois.Table.Make(struct 179 | module Ops = Galois.Extension.Make(struct 180 | module Poly = Poly.Make(Galois.Primitive.GF2) 181 | let pp = prim_poly 182 | end) 183 | let alpha = prim_elt 184 | end) 185 | 186 | let intg n = B.to_int (List.rev (Array.to_list n)) 187 | 188 | module G = struct 189 | include G' 190 | let to_string x = string_of_int (intg x) 191 | end 192 | 193 | let constg n = G.(zero +: (Array.of_list (List.rev (B.consti P.m n)))) 194 | 195 | module Rs = Reedsolomon.Codec.MakePoly(G)(P) 196 | 197 | (***********************************************************************) 198 | 199 | let sep s l = 200 | List.fold_left (fun a x -> if a = "" then x else a ^ s ^ x) "" l 201 | 202 | let poly_split = 8 203 | 204 | let alpha = "α" 205 | let loc = "Λ" 206 | let omega = "Ω" 207 | 208 | let to_pow_string x = 209 | if x = G.zero then "0" 210 | else 211 | alpha ^ "^" ^ string_of_int (G.log x) 212 | 213 | let pow_str v x n = (* v.x^n *) 214 | match n with 215 | | 0 -> v 216 | | 1 -> if v="1" then x else v ^ x 217 | | _ -> (if v="1" then "" else v) ^ x ^ "^" ^ string_of_int n 218 | 219 | let jqmath_of_poly' ?(var="x") zero to_string p = 220 | let p = Array.to_list p in 221 | let _,p = List.fold_left (fun (p,l) x -> (p+1,(p,x)::l)) (0,[]) p in 222 | let p = List.filter (fun (_,v) -> v <> zero) p in 223 | let p = if P.ud then p else List.rev p in 224 | if p=[] then to_string zero 225 | else 226 | let rec split i l a = 227 | if i=0 then List.rev a, l 228 | else 229 | match l with 230 | | [] -> split 0 l a 231 | | h::t -> split (i-1) t (h::a) 232 | in 233 | let rec group l = 234 | let h,t = split poly_split l [] in 235 | match t with 236 | | [] -> if h=[] then [] else [h] 237 | | _ -> h :: group t 238 | in 239 | let p = group p in 240 | let f p = 241 | sep " + " 242 | (List.map (fun (p,v) -> pow_str (to_string v) var p) p) 243 | in 244 | let p = List.map f p in 245 | let s = "\\table " ^ sep " + ;" p ^ ";" in 246 | log ("rendered into " ^ string_of_int (List.length p) ^ 247 | " elements"); 248 | if List.length p > 1 then "(" ^ s ^ ")" 249 | else s 250 | 251 | let jqmath_of_gf ?(var="x") p = jqmath_of_poly' ~var 0 string_of_int p 252 | let jqmath_of_poly ?(var="x") p = jqmath_of_poly' ~var G.zero G.to_string p 253 | let jqmath_of_poly_a ?(var="x") p = jqmath_of_poly' ~var G.zero to_pow_string p 254 | 255 | let jqmath_of_poly ?(var="x") p = 256 | if P.decimal then jqmath_of_poly ~var p 257 | else jqmath_of_poly_a ~var p 258 | 259 | let jqwrap s = "$$" ^ s ^ "$$" 260 | 261 | let jqmath_elt name str = 262 | let div = get_element name in 263 | delete_kids div; 264 | Dom.appendChild div (mk_p (jqwrap str)); 265 | math##(parseMath div) 266 | 267 | (***********************************************************************) 268 | 269 | let encoder d e = 270 | let t = Rs.encode d in 271 | let r = Rs.R.(t +: e) in 272 | jqmath_elt "message_poly" (jqmath_of_poly d); 273 | jqmath_elt "generator_poly" (jqmath_of_poly Rs.generator); 274 | jqmath_elt "error_poly" (jqmath_of_poly e); 275 | jqmath_elt "codeword_poly" (jqmath_of_poly t); 276 | jqmath_elt "received_poly" (jqmath_of_poly r); 277 | t, r 278 | 279 | let string_of_syndrome s i = 280 | "R(" ^ to_pow_string (Rs.root i) ^ "), =, " ^ to_pow_string s.(i) ^ 281 | ", =, " ^ string_of_int (intg s.(i)) ^ ";" 282 | 283 | let euclid s = 284 | let rec gen r s = 285 | match r, s with 286 | | r0::r1::rt, s0::s1::st -> begin 287 | if Rs.R.degree r0 < P.t then r,s 288 | else 289 | let r', s' = Rs.euclid_inner (r0,r1) (s0,s1) in 290 | gen (r'::r) (s'::s) 291 | end 292 | | _ -> failwith "invalid" 293 | in 294 | let r, s = gen [Rs.R.trim s; Rs.x2t] [Rs.R.one; Rs.R.zero] in 295 | Array.of_list (List.rev r), Array.of_list (List.rev s) 296 | 297 | let gen_euclid_table s = 298 | let r,s = euclid s in 299 | let x = Array.init (Array.length r*2) (fun i -> 300 | if i mod 2 = 0 then r.(i/2) else s.(i/2)) 301 | in 302 | let x = Array.mapi (fun i p -> 303 | (if i mod 2 = 0 then "r_{" else "s_{") ^ 304 | string_of_int (i/2) ^ "}, =, " ^ 305 | "{" ^ jqmath_of_poly p ^ "};") x 306 | in 307 | jqmath_elt "euclid" ("\\table " ^ Array.fold_left (^) "" x) 308 | 309 | let berlekamp_massey s = 310 | let one, x = Rs.R.(one, one ^: 1) in 311 | let rec f k (n,c,l as x) = 312 | if k > 2*P.t then [] 313 | else 314 | let x = Rs.berlekamp_massey_iter s k x in 315 | x :: f (k+1) x 316 | in 317 | f 1 (one, x, 0) 318 | 319 | let gen_berlekamp_massey_table s = 320 | let x = berlekamp_massey s in 321 | let x = Array.of_list x in 322 | let x = Array.mapi (fun k (n,c,l) -> 323 | let k = string_of_int (k+1) in 324 | "L_{"^k^"}, =, " ^ string_of_int l ^ ";" ^ 325 | "Λ_{"^k^"}, =, {" ^ jqmath_of_poly n ^ "};" ^ 326 | "C_{"^k^"}, =, {" ^ jqmath_of_poly c ^ "};") x 327 | in 328 | jqmath_elt "berlekamp_massey" ("\\table " ^ Array.fold_left (^) "" x) 329 | 330 | let decoder r = 331 | (* syndromes *) 332 | let s = Rs.syndromes r in 333 | let s' = Array.init (2*P.t) (string_of_syndrome s) in 334 | jqmath_elt "syndromes" ("\\table " ^ Array.fold_left (^) "" s'); 335 | jqmath_elt "syndrome_poly" (jqmath_of_poly s); 336 | (* euclid *) 337 | let v, l = Rs.euclid ~norm:true s in 338 | gen_euclid_table s; 339 | gen_berlekamp_massey_table s; 340 | jqmath_elt "error_locator_poly" (jqmath_of_poly l); 341 | jqmath_elt "error_magnitude_poly" (jqmath_of_poly v); 342 | (* chien *) 343 | let el = Rs.chien l in 344 | let el' = Array.init P.n (fun i -> 345 | let x = Rs.horner l (G.antilog i) in 346 | loc ^ "(" ^ to_pow_string (G.antilog i) ^ "), =, " ^ 347 | string_of_int (intg x) ^ ", =, " ^ to_pow_string x ^ ";") 348 | in 349 | jqmath_elt "chien_search" ("\\table " ^ Array.fold_left (^) "" el'); 350 | let el' = Array.of_list el in 351 | let el'' = Array.init (Array.length el') (fun i -> 352 | "X_{" ^ string_of_int (i+1) ^ "}^{-1}, =, " ^ 353 | to_pow_string (G.antilog el'.(i)) ^ 354 | ", thus, log_{α} X_{" ^ string_of_int (i+1) ^ "}, =, " ^ 355 | string_of_int G.(log (inv (antilog el'.(i)))) ^ ";") 356 | in 357 | jqmath_elt "error_locations" ("\\table " ^ Array.fold_left (^) "" el''); 358 | (* forney *) 359 | let forney = Rs.forney v l in 360 | let ev = List.map forney el in 361 | let l' = Rs.deriv l in 362 | let f = "X_{j}^{"^ string_of_int (1-P.b) ^ "} {" ^ 363 | jqmath_of_poly_a ~var:"X_{j}^{-1}" v ^ "} / {" ^ 364 | jqmath_of_poly_a ~var:"X_{j}^{-1}" l' ^ "}" 365 | in 366 | jqmath_elt "forney_poly" f; 367 | let forney i el = 368 | let x' = G.antilog el in 369 | let x = G.inv x' in 370 | let f = forney el in 371 | "Y_{" ^ string_of_int (i+1) ^ "}, =, " ^ 372 | to_pow_string G.(x **: (1-P.b)) ^ 373 | "{" ^ to_pow_string (Rs.horner v x') ^ " / " ^ 374 | to_pow_string (Rs.horner l' x') ^ "}, =, " ^ 375 | to_pow_string f ^ ", =, " ^ string_of_int (intg f) ^ ";" 376 | in 377 | let f = Array.init (Array.length el') (fun i -> forney i el'.(i)) in 378 | jqmath_elt "forney_res" ("\\table " ^ Array.fold_left (^) "" f); 379 | (* correction *) 380 | let e' = Rs.error ev el in 381 | jqmath_elt "calc_error_poly" (jqmath_of_poly e'); 382 | jqmath_elt "corrected_poly" (jqmath_of_poly Rs.R.(e'+:r)) 383 | 384 | end 385 | 386 | (***********************************************************************) 387 | 388 | let hide_galois_field _ = 389 | (get_element "show_galois_field_div")##.style##.display := jstr "none"; 390 | Js._false 391 | 392 | let toggle_galois_field _ = 393 | let module X = Gen( (val (mk_params ()) : Params) ) in 394 | let open X in 395 | let box = get_element "show_galois_field_div" in 396 | if box##.style##.display = jstr "block" then begin 397 | hide_galois_field () 398 | end else begin 399 | jqmath_elt "prim_poly" (jqmath_of_gf prim_poly); 400 | jqmath_elt "prim_elt" (jqmath_of_gf prim_elt); 401 | (* table of GF(2^m) elements *) 402 | let p = 403 | Array.init G.n_elems (fun i -> 404 | let c = constg i in 405 | string_of_int i ^ ", {" ^ 406 | jqmath_of_gf c ^ "}, " ^ to_pow_string c ^ ";") 407 | in 408 | let s = "\\table " ^ Array.fold_left (^) "" p in 409 | jqmath_elt "galois_field" s; 410 | (get_element "show_galois_field_div")##.style##.display := jstr "block"; 411 | Js._false 412 | end 413 | 414 | (***********************************************************************) 415 | 416 | (* number input cell *) 417 | let mk_num_input id = 418 | let i = D.createInput ~_type:(jstr "text") D.document in 419 | i##.id :=jstr id; 420 | i##.maxLength := 4; 421 | i##.size := 4; 422 | i##.value := jstr "0"; 423 | i##.onkeypress := D.handler (fun e -> 424 | (* only allow numbers *) 425 | let cc = e##.charCode in 426 | match Js.Optdef.to_option cc with 427 | | None -> Js._true 428 | | Some(cc) -> 429 | if cc > 31 && (cc < 48 || cc > 57) then Js._false 430 | else Js._true 431 | ); 432 | i 433 | 434 | type table_data = 435 | { 436 | name : string; 437 | data : Dom_html.inputElement Js.t array ref; 438 | } 439 | 440 | let message_data = { name = "message_data"; data = ref [||]; } 441 | let error_data = { name = "error_data"; data = ref [||]; } 442 | 443 | (* construct a new table, copy data from old one where possible *) 444 | let mk_table_data n d = 445 | if Array.length !(d.data) <> n then begin 446 | d.data := Array.init n 447 | (fun i -> 448 | let inp = mk_num_input (d.name ^ string_of_int i) in 449 | inp##.value := (try !(d.data).(i)##.value with _ -> jstr "0"); 450 | inp); 451 | let table = mk_table (fun _ _ -> false) ((n+7)/8) 8 (fun r c -> 452 | let i = (r*8)+c in 453 | try ((!(d.data).(i)) :> Dom.element Js.t) 454 | with _ -> (mk_p "" :> Dom.element Js.t)) 455 | in 456 | let div = get_element d.name in 457 | delete_kids div; 458 | Dom.appendChild div table 459 | end 460 | 461 | let set_table_data table data = 462 | mk_table_data (Array.length data) table; 463 | Array.iteri (fun x i -> i##.value := jstr (string_of_int data.(x))) 464 | !(table.data) 465 | 466 | let get_table_data table = 467 | Array.map (fun i -> int_of_string (ostr (i##.value))) !(table.data) 468 | 469 | let read_data () = 470 | get_table_data message_data, get_table_data error_data 471 | 472 | let init_table table fn = 473 | let data = Array.init (Array.length !(table.data)) fn in 474 | set_table_data table data; 475 | Js._false 476 | 477 | (***********************************************************************) 478 | 479 | let run_rs _ = 480 | let module X = Gen( (val (mk_params ()) : Params) ) in 481 | let d, e = read_data () in 482 | let t, r = X.encoder (Array.map X.constg d) (Array.map X.constg e) in 483 | X.decoder r; 484 | Js._false 485 | 486 | let int_of_gf2_prim_poly m = 487 | let x = Galois.GF2N.gf2_prim_polys.(m) in 488 | let x = B.to_int (List.rev (Array.to_list x)) in 489 | x 490 | 491 | let derived_params _ = 492 | let div = get_element "derived_params" in 493 | let p = mk_p 494 | ("n = " ^ string_of_int (get_n()) ^ 495 | ", k = " ^ string_of_int (get_k ())) 496 | in 497 | delete_kids div; 498 | Dom.appendChild div p; 499 | Js._false 500 | 501 | let update_tables _ = 502 | let m, t, _, _, _, _, _ = read_params () in 503 | let n = (1 lsl m) - 1 in 504 | let k = n - 2*t in 505 | mk_table_data k message_data; 506 | mk_table_data n error_data; 507 | Js._false 508 | 509 | let select_poly _ = 510 | set_pp (int_of_gf2_prim_poly (get_m ())); 511 | set_pe 2; 512 | Js._false 513 | 514 | let init_table table fn _ = 515 | let n,t = get_n(), get_t() in 516 | let fn = fn n t in 517 | init_table table fn 518 | 519 | let onload _ = 520 | log "loading reed-solomon tutorial app"; 521 | 522 | (* example from the bbc white paper *) 523 | let d = [| 11;10;9;8;7;6;5;4;3;2;1 |] in 524 | let e = [| 0;0;2;0;0;0;0;0;0;13;0;0;0;0;0 |] in 525 | 526 | (* create input data tables *) 527 | set_table_data message_data d; 528 | set_table_data error_data e; 529 | 530 | (get_element "show_galois_field")##.onclick := 531 | D.handler toggle_galois_field; 532 | 533 | (get_element "random_message")##.onclick := 534 | D.handler (init_table message_data (fun n _ _ -> Random.int (n+1))); 535 | 536 | (get_element "random_errors")##.onclick := 537 | D.handler (init_table error_data 538 | (fun n t _ -> if Random.int n <= t then (Random.int n)+1 else 0)); 539 | 540 | (get_element "clear_message")##.onclick := 541 | D.handler (init_table message_data (fun _ _ _ -> 0)); 542 | (get_element "clear_errors")##.onclick := 543 | D.handler (init_table error_data (fun _ _ _ -> 0)); 544 | 545 | let handlers l = (* run multiple handlers *) 546 | D.handler (fun e -> List.iter (fun f -> ignore (f e)) l; Js._false) 547 | in 548 | ignore (derived_params()); 549 | 550 | (get_input "param_m")##.onchange := handlers 551 | [update_tables; hide_galois_field; select_poly; derived_params]; 552 | (get_input "param_t")##.onchange := handlers 553 | [update_tables; derived_params]; 554 | 555 | (get_input "param_pp")##.onchange := D.handler hide_galois_field; 556 | (get_input "param_pe")##.onchange := D.handler hide_galois_field; 557 | 558 | (get_element "calculate_rs")##.onclick := D.handler run_rs; 559 | 560 | ignore (run_rs()); 561 | 562 | Js._false 563 | 564 | let _ = install_window_onload (D.handler onload) 565 | 566 | --------------------------------------------------------------------------------