Reed-Solomon Codes——RS纠错码

1. 引言

前序博客有:

源自
上图源自 How to send a self-correcting message (Hamming codes)

Reed-Solomon Codes为基于block的纠错码,在数字通信和存储中有大量应用,在如下系统中用于纠错:

  • 存储设备(包括磁带、光盘、DVD、条形码等)
  • 无线或移动通信(包括手机、微波连接等)
  • 卫星通信
  • 数字电视/DVB
  • 高速调制解调器,如ADSL、xDSL等

典型的系统如下图所示:
在这里插入图片描述
Reed-Solomon encoder(编码器) 接收一块数字数据并添加额外的“冗余”位。数据在传输或存储过程中出现错误的原因有多种(如噪声或干扰,CD上的划痕等等)。
Reed-Solomon decoder(解码器) 会处理每个块,并试图纠正错误,恢复原始数据。

能纠正的错误类型和错误数量具体取决于Reed-Solomon码的特性。

2. Reed-Solomon码的特性

Reed-Solomon码为BCH码的子集,属于linear block码。

Reed-Solomon码定义为:
R S ( n , k ) RS(n,k) RS(n,k) with s s s-bit symbols。

也就意味着:

  • Reed-Solomon 编码器接收 k k k个data symbols,每个symbol对应有 s s s bits,然后添加parity symbols,最终获得长度为 n n n的symbol codeword。parity symbol的个数为 n − k n-k nk个,每个parity symbol对应有 s s s bits。
  • Reed-Solomon 解码器可纠正codeword中最多 t t t个symbol错误,其中 2 t = n − k 2t=n-k 2t=nk

对Reed-Solomon进行编解码所需的算力,与每个codeword内的parity symbols数量相关。更大的 t t t值以为这可纠正更多的错误,但是相比于 小 t t t 需要更多的算力。

已知symbol size为 s s s,则Reed-Solomon码的最大codeword length n n n n = 2 s − 1 n=2^s-1 n=2s1。如对于8-bit symbol( s = 8 s=8 s=8),其code最大长度为 255 255 255 bytes。

以下为典型的Reed-Solomon codeword(又名Systematic码,因为数据部分保持不变,仅附加了parity symbols):
在这里插入图片描述
比如,流行的Reed-Solomon码 R S ( 255 , 223 ) RS(255,223) RS(255,223) with 8-bit symbols,每个codeword包含了255 code word bytes,其中223 bytes为数据,32 bytes为parity,有:
n = 255 , k = 223 , s = 8 n=255,k=223,s=8 n=255,k=223,s=8
25 = 32 , t = 16 25=32, t=16 25=32,t=16

解码器可纠正code word中的任意16个symbol error,即codeword中的任意位置的最多16 bytes错误可被自动纠正。

2.1 缩短Reed-Solomon码

理论上Reed-Solomon码长度是可缩短的,通过在编码时将一定数量的data symbols设置为0,不传输这些为0的data symbols,然后在解码时重新插入为0的data symbols。
仍然以上面的 ( 255 , 223 ) (255,223) (255,223)码为例,可将其缩短为 ( 200 , 168 ) (200,168) (200,168)码,编码时的block内有168 data bytes,然后理论上增加55 zero bytes,创建出一个 ( 255 , 223 ) (255, 223) (255,223) codeword,但是实际仅需传输168 data bytes和32 parity bytes。

2.2 Symbol Errors

若单个symbol内有一个bit出错或者所有bit都错了时,则均为一个symbol error。

仍然以 R S ( 255 , 223 ) RS(255, 223) RS(255,223)为例,其可纠正 16 16 16个symbol errors,最差的情况是,有16 bit errors,每个bit错误发生在不同的symbol(byte),解码器纠正16 bit errors。最好的情况是,16个symbol内的所有bit都错了,此时解码器可纠正 16 × 8 16\times 8 16×8 bit errors。

Reed-Solomon码特别适于纠正burst errors(即所接收到的codeword内有a series of bits出错)。

2.3 解码

Reed-Solomon代数解码的过程中,可:

  • 纠错
  • 擦除:当已知错误symbol的位置时,可进行擦除。

解码器可最多纠正 t t t个symbol错误,或最多擦除 2 t 2t 2t个错误symbol。在数字通信系统中,擦除信息通常由解调器提供,即解调器会对其收到的symbols中可能 包含错误的symbol 进行“标记”。

当对codeword解码时,有以下3种可能:

  • 1)若 2 s + r < 2 t 2s+r<2t 2s+r<2t,即 s s s个error, t t t个擦除,则总是可恢复出所传输的原始code word。
  • 2)解码器发现其无法恢复原始code word,并说明了该情况。
  • 3)解码器,在无任何说明的情况下,进行了错误解码并恢复了错误的code word。

以上3种情况发生的概率取决于具体的Reed-Solomon码、error数量 以及 error分布情况。

2.4 coding gain编码增益

使用Reed-Solomon码的优势在于,解码后数据内包含错误的概率,通常远低于,未使用Reed-Solomon码的错误发生概率。通常将其称为coding gain。

如:
数字通信系统中,设计的Bit Error Ration(BER)为 1 0 − 9 10^{-9} 109,即所接收到的 1 0 9 10^9 109个bits,最多仅能有 1 1 1个bit出错。
可通过增加发射器的功率 或 添加Reed-Solomon码(或另一种 前向纠错码) 来实现。Reed-Solomon码可使得系统发射器以较低的输出功率实现目标BER。由于使用Reed-Solomon所节约的power,即称为coding gain。

3. Reed-Solomon码的编解码架构

Reed-Solomon编解码可在软件或特定用途硬件内实现。

3.1 关键概念

  • 1)Finite(Galois) Field Arithmetic:
    Reed-Solomon码基于数学上名为Galois fields或Finite fields的特殊域。finite field内具有的属性为:field elements 经(加减乘除等)运算后的结果 仍在field域内。
    Reed-Solomon编码器或解码器需要执行这些运算操作,这些运算需要特殊的硬件或实现相应的软件函数。

  • 2)Generator多项式:
    Reed-Solomon codeword采用特殊的多项式生成,所有有效的codewords都可整除该generator polynomial。
    generator多项式的通用形式为:
    g ( x ) = ( x − α 0 ) ( x − α 1 ) ⋯ ( x − α 2 t − 1 ) g(x)=(x-\alpha^0)(x-\alpha^{1})\cdots(x-\alpha^{2t-1}) g(x)=(xα0)(xα1)(xα2t1)
    codeword的构建方式为:
    c ( x ) = g ( x ) ⋅ i ( x ) c(x)=g(x)\cdot i(x) c(x)=g(x)i(x)
    其中 g ( x ) g(x) g(x)为generator多项式, i ( x ) i(x) i(x)为information block(即上面提到的data symbol), c ( x ) c(x) c(x)为一个有效codeword, α \alpha α为field F域内的一个primitive element(generator)。

所谓generator,是指 [ α 0 , α 1 , α 2 , ⋯   , α ∣ F ∣ − 2 ] [\alpha^0, \alpha^1,\alpha^2,\cdots,\alpha^{|F|-2}] [α0,α1,α2,,αF2]为不同的非零值,即generator α \alpha α的幂乘生成了field F内的所有非零元素。 ∣ F ∣ |F| F为field size,即不同元素的总数。

  • 待编码的数据长度 k k k满足 1 ≤ k < ∣ F ∣ 1\leq k< |F| 1k<F
  • 在数据之后附加的纠错码长度 m = 2 t m=2t m=2t满足 1 ≤ m < ∣ F ∣ − k 1\leq m<|F|-k 1m<Fk
  • 编码后的长度 n = k + m n=k+m n=k+m满足 2 ≤ n < ∣ F ∣ 2\leq n<|F| 2n<F

3.2 Reed-Solomon编码架构——Systematic编码器

基于 m m m α \alpha α的generator多项式定义为:
g ( x ) = ∏ i = 0 m − 1 ( x − α i ) = ( x − α 0 ) ( x − α 1 ) ⋯ ( x − α m − 1 ) g(x)=\prod_{i=0}^{m-1}(x-\alpha^i)=(x-\alpha^0)(x-\alpha^1)\cdots (x-\alpha^{m-1}) g(x)=i=0m1(xαi)=(xα0)(xα1)(xαm1)

原始消息为一系列 k k k个值 ( M 0 , M 1 , ⋯   , M k − 1 ) (M_0, M_1,\cdots,M_{k-1}) (M0,M1,,Mk1),简单地以这些原始消息值为系数构建message多项式:
M ( x ) = ∑ i = 0 k − 1 M i x i = M 0 x 0 + M 1 x 1 + ⋯ + M k − 1 x k − 1 M(x)=\sum_{i=0}^{k-1}M_ix^i=M_0x^0+M_1x^1+\cdots + M_{k-1}x^{k-1} M(x)=i=0k1Mixi=M0x0+M1x1++Mk1xk1

计算Reed-Solomon codeword的公式为:
s ( x ) = M ( x ) x m − [ ( M ( x ) x m ) m o d    g ( x ) ] s(x)=M(x)x^m-[(M(x)x^m)\mod g(x)] s(x)=M(x)xm[(M(x)xm)modg(x)]
其中 [ ( M ( x ) x m ) m o d    g ( x ) ] [(M(x)x^m)\mod g(x)] [(M(x)xm)modg(x)]多项式具有的degree小于等于 m − 1 m-1 m1,因此不会与 M ( x ) x m M(x)x^m M(x)xm项的结果相互影响。总的 s ( x ) s(x) s(x)的degree小于等于 n − 1 n-1 n1

s ( x ) s(x) s(x)展开为:
s ( x ) = ∑ i = 0 n − 1 s i x i = s 0 x 0 + s 1 x 1 + ⋯ s n − 1 x n − 1 s(x)=\sum_{i=0}^{n-1}s_ix^i=s_0x^0+s_1x^1+\cdots s_{n-1}x^{n-1} s(x)=i=0n1sixi=s0x0+s1x1+sn1xn1
最终发送的RS码为 n n n个值 ( s 0 , s 1 , s 2 , ⋯   , s n − 1 ) (s_0,s_1,s_2,\cdots,s_{n-1}) (s0,s1,s2,,sn1)

3.3 Reed-Solomon解码架构——Peterson-Gorenstein-Zierler解码器

假设收到的codeword为 ( r 0 , r 1 , ⋯   , r n − 1 ) (r_0,r_1,\cdots,r_{n-1}) (r0,r1,,rn1),可将received codeword多项式定义为:
r ( x ) = ∑ i = 0 n − 1 r i x i = r 0 x 0 + r 1 x 1 + ⋯ + r n − 1 x n − 1 r(x)=\sum_{i=0}^{n-1}r_ix^i=r_0x^0+r_1x^1+\cdots + r_{n-1}x^{n-1} r(x)=i=0n1rixi=r0x0+r1x1++rn1xn1

作为接收方,并不知道实际发送的值为 ( s 0 , s 1 , s 2 , ⋯   , s n − 1 ) (s_0,s_1,s_2,\cdots,s_{n-1}) (s0,s1,s2,,sn1),但会定义error值,令 e 0 = r 0 − s 0 , e 1 = r 1 − s 1 , ⋯   , e n − 1 = r n − 1 − s n − 1 e_0=r_0-s_0, e_1=r_1-s_1,\cdots,e_{n-1}=r_{n-1}-s_{n-1} e0=r0s0,e1=r1s1,,en1=rn1sn1,从而直接定义error多项式为:
e ( x ) = r ( x ) − s ( x ) = ∑ i = 0 n − 1 e i x i = e 0 x 0 + e 1 x 1 + ⋯ + e n − 1 x n − 1 e(x)=r(x)-s(x)=\sum_{i=0}^{n-1}e_ix^i=e_0x^0+e_1x^1+\cdots+e_{n-1}x^{n-1} e(x)=r(x)s(x)=i=0n1eixi=e0x0+e1x1++en1xn1

Reed-Solomon解码架构为:
在这里插入图片描述
其中:

  • r ( x ) r(x) r(x):为received codeword
  • S i S_i Si:为Syndromes
  • L ( x ) L(x) L(x):Error locator polynomial
  • X i X_i Xi:Error locations
  • Y i Y_i Yi:Error magnitudes
  • c ( x ) c(x) c(x):Recovered code word
  • ν \nu ν:number of errors

以上架构图中:

  • 1)Syndrome Calculation(计算Syndromes):
    与parity calculation的计算类似,仅取决于errors(而不取决于所传输的code word),Reed-Solomon中有 m = 2 t m=2t m=2t个syndromes。syndromes可通过向 r ( x ) r(x) r(x)提交 generator多项式 g ( x ) g(x) g(x) m = 2 t m=2t m=2t 个根来计算。
    对于 0 ≤ i < m 0\leq i <m 0i<m,计算 m m m个syndrome值的方法为——evaluating the received codeword多项式 r ( x ) r(x) r(x) at various powers of the generator:
    S i = r ( α i ) = s ( α i ) + e ( α i ) = 0 + e ( α i ) = e ( α i ) = e 0 α 0 i + e 1 α 1 i + ⋯ + e n − 1 α ( n − 1 ) i S_i=r(\alpha^i)=s(\alpha^i)+e(\alpha^i)=0+e(\alpha^i)=e(\alpha^i)=e_0\alpha^{0i}+e_1\alpha^{1i}+\cdots+e_{n-1}\alpha^{(n-1)i} Si=r(αi)=s(αi)+e(αi)=0+e(αi)=e(αi)=e0α0i+e1α1i++en1α(n1)i
    其中对于 0 ≤ i < m 0\leq i <m 0i<m,有 s ( α i ) = 0 s(\alpha^i)=0 s(αi)=0。由此可看出,syndromes值仅依赖于添加到sent codeword上的errors,与sent codeword无关,与原始消息也无关。
    若所有的syndrome值均为0,则该codeword是正确的,即无需做任何处理,工作至此完成。
    所有 m m m 个syndrome方程式为:
    { S 0 = e 0 α 0 × 0 + e 1 α 1 × 0 + ⋯ + e n − 1 α ( n − 1 ) × 0 S 1 = e 0 α 0 × 1 + e 1 α 1 × 1 + ⋯ + e n − 1 α ( n − 1 ) × 1 ⋯ S m − 1 = e 0 α 0 × ( m − 1 ) + e 1 α 1 × ( m − 1 ) + ⋯ + e n − 1 α ( n − 1 ) × ( m − 1 ) \left\{\begin{matrix} S_0=e_0\alpha^{0\times 0}+e_1\alpha^{1\times 0}+\cdots + e_{n-1}\alpha^{(n-1)\times 0}\\ S_1=e_0\alpha^{0\times 1}+e_1\alpha^{1\times 1}+\cdots + e_{n-1}\alpha^{(n-1)\times 1}\\ \cdots \\ S_{m-1}=e_0\alpha^{0\times (m-1)}+e_1\alpha^{1\times (m-1)}+\cdots + e_{n-1}\alpha^{(n-1)\times (m-1)} \end{matrix}\right. S0=e0α0×0+e1α1×0++en1α(n1)×0S1=e0α0×1+e1α1×1++en1α(n1)×1Sm1=e0α0×(m1)+e1α1×(m1)++en1α(n1)×(m1)
    以矩阵表示syndrome方程组为:
    [ α 0 × 0 α 0 × 1 ⋯ α ( n − 1 ) × 0 α 0 × 1 α 0 × 1 ⋯ α ( n − 1 ) × 1 ⋮ ⋮ ⋱ ⋮ α 0 × ( m − 1 ) α 0 × ( m − 1 ) ⋯ α ( n − 1 ) × ( m − 1 ) ] [ e 0 e 1 ⋮ e n − 1 ] = [ S 0 S 1 ⋮ S m − 1 ] \begin{bmatrix} \alpha^{0\times 0} &\alpha^{0\times 1} & \cdots & \alpha^{(n-1)\times 0} \\ \alpha^{0\times 1} &\alpha^{0\times 1} & \cdots & \alpha^{(n-1)\times 1} \\ \vdots & \vdots & \ddots & \vdots \\ \alpha^{0\times (m-1)} &\alpha^{0\times (m-1)} & \cdots & \alpha^{(n-1)\times (m-1)} \end{bmatrix}\begin{bmatrix} e_0 \\ e_1 \\ \vdots \\ e_{n-1} \end{bmatrix}=\begin{bmatrix} S_0 \\ S_1 \\ \vdots \\ S_{m-1} \end{bmatrix} α0×0α0×1α0×(m1)α0×1α0×1α0×(m1)α(n1)×0α(n1)×1α(n1)×(m1) e0e1en1 = S0S1Sm1

  • 2)寻找Symbol Error Locations:
    寻找Symbol Error Locations的过程,包含了,对包含t个未知变量simultaneous多项式的求解。有多种快速算法可实现该求解。这些算法利用了Reed-Solomon码的特殊结构,可大幅减少所需的算力。
    求解算法中通常包含2步:

    • 2.1)找到一个error locator多项式:可使用Berlekamp-Massey算法或Euclid算法。实际Euclid算法使用更广,因其更易于实现。但是Berlekamp-Massey算法的效率会更高。
    • 2.2)找到该多项式的根:通过Chien search算法实现。

    ν \nu ν来表示试图找到的errors个数,要求 1 ≤ ν ≤ ⌊ m / 2 ⌋ 1\leq \nu\leq \left \lfloor m/2 \right \rfloor 1νm/2。除非有时空限制,否则最好将 ν \nu ν设置为尽可能大,以可纠正尽可能多的错误。
    假设我们已知这 ν \nu ν个error位置为 I 0 , I 1 , ⋯   , I ν − 1 I_0,I_1,\cdots, I_{\nu-1} I0,I1,,Iν1,为 n n n个received codeword值的orderless set of unique indexes,因此,每个元素均满足 0 ≤ I i < n 0\leq I_i<n 0Ii<n。注意,此处有:indexes I i I_i Ii处的error值可能为非零,但是除 e I 0 , e I 1 , ⋯   , e I ν − 1 e_{I_0},e_{I_1},\cdots,e_{I_{\nu-1}} eI0,eI1,,eIν1之外的其它indexes处的error值必须为0。
    对于 0 ≤ i < ν 0\leq i < \nu 0i<ν,基于error location indexes I i I_i Ii,引入新变量:
    X i = α I i X_i=\alpha^{I_i} Xi=αIi
    Y i = e I i Y_i=e_{I_i} Yi=eIi
    由于所有其它indexes的 e i e_i ei值均为0,因此可将syndrome方程式组重写为:
    [ X 0 0 X 1 0 ⋯ X ν − 1 0 X 0 1 X 1 1 ⋯ X ν − 1 1 ⋮ ⋮ ⋱ ⋮ X 0 m − 1 X 1 m − 1 ⋯ X ν − 1 m − 1 ] [ Y 0 Y 1 ⋮ Y ν − 1 ] = [ S 0 S 1 ⋮ S m − 1 ] \begin{bmatrix} X_0^{0} & X_1^{0} & \cdots & X_{\nu-1}^{0} \\ X_0^{1} & X_1^{1} & \cdots & X_{\nu-1}^{1} \\ \vdots & \vdots & \ddots & \vdots \\ X_0^{m-1} & X_1^{m-1} & \cdots & X_{\nu-1}^{m-1} \end{bmatrix}\begin{bmatrix} Y_0 \\ Y_1 \\ \vdots \\ Y_{\nu-1} \end{bmatrix}=\begin{bmatrix} S_0 \\ S_1 \\ \vdots \\ S_{m-1} \end{bmatrix} X00X01X0m1X10X11X1m1Xν10Xν11Xν1m1 Y0Y1Yν1 = S0S1Sm1
    此时,对 X i , Y i X_i,Y_i Xi,Yi值仍是位置的,但是,接下来有个聪明的multi-step方法来知晓具体的 X i , Y i X_i,Y_i Xi,Yi值。
    基于未知的 X i X_i Xi变量,定义error locator多项式为:
    Λ ( x ) = ∏ i = 0 ν − 1 ( 1 − X i x ) = 1 + Λ 1 x + Λ 2 x + ⋯ + Λ ν x ν \Lambda(x)=\prod_{i=0}^{\nu-1}(1-X_ix)=1+\Lambda_1x+ \Lambda_2x+\cdots+\Lambda_{\nu}x^{\nu} Λ(x)=i=0ν1(1Xix)=1+Λ1x+Λ2x++Λνxν
    error locator多项式系数为 ( 1 , Λ 1 , Λ 2 , ⋯   , Λ ν ) (1,\Lambda_1,\Lambda_2,\cdots,\Lambda_{\nu}) (1,Λ1,Λ2,,Λν)
    对于任意的 0 ≤ i < ν 0\leq i<\nu 0i<ν,有:
    0 = Λ ( X i − 1 ) = 1 + Λ 1 X i − 1 + Λ 2 X i − 2 + ⋯ + Λ ν X i − ν 0=\Lambda(X_i^{-1})=1+\Lambda_1X_i^{-1}+\Lambda_2X_i^{-2}+\cdots + \Lambda_{\nu}X_i^{-\nu} 0=Λ(Xi1)=1+Λ1Xi1+Λ2Xi2++ΛνXiν
    以上等式为0是因为 ( 1 − X i X i − 1 ) = 1 − 1 = 0 (1-X_iX_i^{-1})=1-1=0 (1XiXi1)=11=0
    对于 0 ≤ i < ν 0\leq i<\nu 0i<ν,取任意的 j ∈ Z j\in\mathbb{Z} jZ,在以上等式左右两边都乘以 Y i X i j + ν Y_iX_i^{j+\nu} YiXij+ν,有:
    Y i X i j + ν 0 = Y i X i j + ν Λ ( X i − 1 ) = Y i X i j + ν ( 1 + Λ 1 X i − 1 + Λ 2 X i − 2 + ⋯ + Λ ν X i − ν ) Y_iX_i^{j+\nu}0=Y_iX_i^{j+\nu}\Lambda(X_i^{-1})=Y_iX_i^{j+\nu}(1+\Lambda_1X_i^{-1}+\Lambda_2X_i^{-2}+\cdots + \Lambda_{\nu}X_i^{-\nu}) YiXij+ν0=YiXij+νΛ(Xi1)=YiXij+ν(1+Λ1Xi1+Λ2Xi2++ΛνXiν)
    0 = Y i X i j + ν Λ ( X i − 1 ) = Y i X i j + ν + Λ 1 Y i X i j + ν − 1 + Λ 2 Y i X i j + ν − 2 + ⋯ + Λ ν Y i X i j 0=Y_iX_i^{j+\nu}\Lambda(X_i^{-1})=Y_iX_i^{j+\nu}+\Lambda_1Y_iX_i^{j+\nu-1}+\Lambda_2Y_iX_i^{j+\nu-2}+\cdots + \Lambda_{\nu}Y_iX_i^{j} 0=YiXij+νΛ(Xi1)=YiXij+ν+Λ1YiXij+ν1+Λ2YiXij+ν2++ΛνYiXij
    对所有的 0 ≤ i < ν 0\leq i<\nu 0i<ν方程式求和,有:
    0 = ∑ i = 0 ν − 1 Y i X i j + ν Λ ( X i − 1 ) = ( ∑ i = 0 ν − 1 Y i X i j + ν ) + Λ 1 ( ∑ i = 0 ν − 1 Y i X i j + ν − 1 ) + Λ 2 ( ∑ i = 0 ν − 1 Y i X i j + ν − 2 ) + ⋯ + Λ ν ( ∑ i = 0 ν − 1 Y i X i j ) = S j + ν + Λ 1 S j + ν − 1 + Λ 2 S j + ν − 2 + ⋯ + Λ ν S j 0=\sum_{i=0}^{\nu-1}Y_iX_i^{j+\nu}\Lambda(X_i^{-1})=(\sum_{i=0}^{\nu-1}Y_iX_i^{j+\nu})+\Lambda_1(\sum_{i=0}^{\nu-1}Y_iX_i^{j+\nu-1})+\Lambda_2(\sum_{i=0}^{\nu-1}Y_iX_i^{j+\nu-2})+\cdots + \Lambda_{\nu}(\sum_{i=0}^{\nu-1}Y_iX_i^{j}) = S_{j+\nu}+\Lambda_1S_{j+\nu-1}+\Lambda_2S_{j+\nu-2}+\cdots +\Lambda_{\nu}S_j 0=i=0ν1YiXij+νΛ(Xi1)=(i=0ν1YiXij+ν)+Λ1(i=0ν1YiXij+ν1)+Λ2(i=0ν1YiXij+ν2)++Λν(i=0ν1YiXij)=Sj+ν+Λ1Sj+ν1+Λ2Sj+ν2++ΛνSj
    将以上等式左右项重组,对于 0 ≤ j < ν 0\leq j < \nu 0j<ν,均有:
    Λ ν S j + Λ ν − 1 S j + 1 + ⋯ + Λ 1 S j + ν − 1 = − S j + ν \Lambda_{\nu}S_j + \Lambda_{\nu-1}S_{j+1} + \cdots + \Lambda_1S_{j+\nu-1}=-S_{j+\nu} ΛνSj+Λν1Sj+1++Λ1Sj+ν1=Sj+ν
    对于所有的 0 ≤ j < ν 0\leq j < \nu 0j<ν,可获得 ν \nu ν个线性等式:
    { Λ ν S 0 + Λ ν − 1 S 1 + ⋯ + Λ 1 S ν − 1 = − S ν Λ ν S 1 + Λ ν − 1 S 2 + ⋯ + Λ 1 S ν = − S ν + 1 ⋯ Λ ν S ν − 1 + Λ ν − 1 S ν + ⋯ + Λ 1 S 2 ν − 2 = − S 2 ν − 1 \left\{\begin{matrix} \Lambda_{\nu}S_0 + \Lambda_{\nu-1}S_{1} + \cdots + \Lambda_1S_{\nu-1}=-S_{\nu}\\ \Lambda_{\nu}S_1 + \Lambda_{\nu-1}S_{2} + \cdots + \Lambda_1S_{\nu}=-S_{\nu+1}\\ \cdots \\ \Lambda_{\nu}S_{\nu-1} + \Lambda_{\nu-1}S_{\nu} + \cdots + \Lambda_1S_{2\nu-2}=-S_{2\nu-1}\\ \end{matrix}\right. ΛνS0+Λν1S1++Λ1Sν1=SνΛνS1+Λν1S2++Λ1Sν=Sν+1ΛνSν1+Λν1Sν++Λ1S2ν2=S2ν1
    以矩阵形式表示为:
    [ S 0 S 1 ⋯ S ν − 1 S 1 S 2 ⋯ S ν ⋮ ⋮ ⋱ ⋮ S ν − 1 S ν ⋯ S 2 ν − 2 ] [ Λ ν Λ ν − 1 ⋮ Λ 1 ] = [ − S ν − S ν + 1 ⋮ − S 2 ν − 1 ] \begin{bmatrix} S_0 & S_1 & \cdots & S_{\nu-1} \\ S_1 & S_2 & \cdots & S_{\nu} \\ \vdots & \vdots & \ddots & \vdots \\ S_{\nu-1} & S_{\nu} & \cdots & S_{2\nu-2} \end{bmatrix}\begin{bmatrix} \Lambda_{\nu} \\ \Lambda_{\nu-1} \\ \vdots \\ \Lambda_{1} \end{bmatrix}=\begin{bmatrix} -S_{\nu} \\ -S_{\nu+1} \\ \vdots \\ -S_{2\nu-1} \end{bmatrix} S0S1Sν1S1S2SνSν1SνS2ν2 ΛνΛν1Λ1 = SνSν+1S2ν1
    以上由 ν + 1 \nu+1 ν+1个方程式组成,若以上线性方程式是consistent的,根据Gauss-Jordan消减法,可获得系数 Λ 1 , Λ 2 , ⋯   , Λ ν \Lambda_1,\Lambda_2,\cdots,\Lambda_{\nu} Λ1,Λ2,,Λν,从而获得了error locator多项式 Λ ( x ) \Lambda(x) Λ(x),对所有的 0 ≤ i < n 0\leq i <n 0i<n,取 x = α − i x=\alpha^{-i} x=αi,检查 Λ ( α − 1 ) = 0 \Lambda(\alpha^{-1})=0 Λ(α1)=0是否成立。

  • 3)寻找Symbol Error Values:仍然包含了,对包含t个未知变量simultaneous多项式的求解。广泛使用的快速算法为Forney算法。
    此时,已知了所有的error location indexes I 0 , I 1 , ⋯   , I ν − 1 I_0,I_1,\cdots,I_{\nu-1} I0,I1,,Iν1,以及对于 0 ≤ i < ν 0\leq i< \nu 0i<ν,已知所有的 X i = α I i X_i=\alpha^{I_i} Xi=αIi值。

    • 3.1)由于已知了 X i X_i Xi值和 S i S_i Si值,可求解之前方程式:
      [ X 0 0 X 1 0 ⋯ X ν − 1 0 X 0 1 X 1 1 ⋯ X ν − 1 1 ⋮ ⋮ ⋱ ⋮ X 0 m − 1 X 1 m − 1 ⋯ X ν − 1 m − 1 ] [ Y 0 Y 1 ⋮ Y ν − 1 ] = [ S 0 S 1 ⋮ S m − 1 ] \begin{bmatrix} X_0^{0} & X_1^{0} & \cdots & X_{\nu-1}^{0} \\ X_0^{1} & X_1^{1} & \cdots & X_{\nu-1}^{1} \\ \vdots & \vdots & \ddots & \vdots \\ X_0^{m-1} & X_1^{m-1} & \cdots & X_{\nu-1}^{m-1} \end{bmatrix}\begin{bmatrix} Y_0 \\ Y_1 \\ \vdots \\ Y_{\nu-1} \end{bmatrix}=\begin{bmatrix} S_0 \\ S_1 \\ \vdots \\ S_{m-1} \end{bmatrix} X00X01X0m1X10X11X1m1Xν10Xν11Xν1m1 Y0Y1Yν1 = S0S1Sm1
      的解 Y i = e I i Y_i=e_{I_i} Yi=eIi
    • 3.2)由于已知了error locations I i I_i Ii 和 error values Y i Y_i Yi,可试图fix the received codeword。
      定义repaired codeword多项式为:
      r ′ ( x ) = r 0 ′ x 0 + r 1 ′ x 1 + ⋯ + r n − 1 ′ x n − 1 r'(x)=r_0'x^0+r_1'x_1+\cdots +r_{n-1}'x^{n-1} r(x)=r0x0+r1x1++rn1xn1
      其中,对于 0 ≤ i < ν 0\leq i <\nu 0i<ν,系数 r I i ′ = r I i − Y i = r I i − e I i r_{I_i}'=r_{I_i}-Y_i=r_{I_i}-e_{I_i} rIi=rIiYi=rIieIi,而对于任意的 0 ≤ i < n 0\leq i <n 0i<n且不满足 0 ≤ i < ν 0\leq i <\nu 0i<ν i i i值,有 r i ′ = r i r_i'=r_i ri=ri。由此,the repaired codeword多项式 r ′ ( x ) r'(x) r(x)具有all-zero syndrom values。
      该解码codeword是基于received codeword的最佳猜想。

Reed-Solomon解码者试图识别并纠正最多 t t t个errors或 2 t 2t 2t个erasures。

3.4 时间复杂度

以上编码器为短而简介的,其时间复杂度为 Θ ( m k ) \Theta(mk) Θ(mk),已不太可能在简洁性或速度上有显著改进。

假设最大纠错能力为 ν = ⌊ ⌋ \nu=\left \lfloor \right \rfloor ν=,以上解码器的时间复杂度为 Θ ( m 3 + m k ) \Theta(m^3+mk) Θ(m3+mk)。其中的三次方runtime并不理想,可借助其它算法改进到二次方。Berlekamp-Massey算法find error locator多项式的时间复杂度为 Θ ( m 2 ) \Theta(m^2) Θ(m2),且Forney算法find error values的时间复杂度也为 Θ ( m 2 ) \Theta(m^2) Θ(m2)

4. Reed-Solomon编解码器实现

4.1 Reed-Solomon编解码器硬件实现

目前有一些商业硬件实现了Reed-Solomon编解码功能,很多采用了“off-the-shelf”集成电路来对Reed-Solomon码进行编解码。这些IC支持一定数量的可编程性(如 R S ( 255 , k ) RS(255,k) RS(255,k),其中 t = 1 t=1 t=1~16个symbols)。
当前的趋势是VHDL或Verilog设计(logic cores 或 intellectual property cores)。相比于标准IC,主要优势有:

  • 1)logic core可与其他VHDL或Verilog元素集成
  • 2)可与FPGA(Field Programmable Gate Array)或ASIC(Application Specific Integrated Circuit)合成

这种“System on Chip”设计,支持将多个模块合并在单一IC内。取决于生产规模,相比于标准IC,logic cores可大幅降低系统开销,设计者可避免潜在的“周期性购买”Reed-Solomon IC。

4.2 Reed-Solomon编解码器软件实现

编码的速度要快于解码,所需的算力也更少。

相关软件代码实现见:
Reed-Solomon error-correcting code decoder

RS纠错码Java实现为:

/* 
 * Reed-Solomon error-correcting code decoder (Java)
 * 
 * Copyright (c) 2017 Project Nayuki
 * All rights reserved. Contact Nayuki for licensing.
 * https://www.nayuki.io/page/reed-solomon-error-correcting-code-decoder
 */

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Objects;


/**
 * Performs Reed-Solomon encoding and decoding. This object can encode a message into a codeword.
 * The codeword can have some values modified by external code. Then this object can try
 * to decode the codeword, and under some circumstances can reproduce the original message.
 * <p>This class is immutable and thread-safe, but the argument arrays passed into methods are not thread-safe.</p>
 */
public final class ReedSolomon<E> {
	
	/*---- Fields ----*/
	
	/** The number of values in each message. Always at least 1. */
	public final int messageLen;
	
	/** The number of error correction values to expand the message by. Always at least 1. */
	public final int eccLen;
	
	/** The number of values in each codeword, equal to messageLen + eccLen. Always at least 2. */
	public final int codewordLen;
	
	
	// The field for message and codeword values, and for performing arithmetic operations on values. Not null.
	private final Field<E> f;
	
	// An element of the field whose powers generate all the non-zero elements of the field. Not null.
	private final E generator;
	
	// The class object for the actual type parameter E, which is used in newArray(). Not null.
	private Class<E> elementType;
	
	
	
	/*---- Constructor ----*/
	
	/**
	 * Constructs a Reed-Solomon encoder-decoder with the specified field, lengths, and other parameters.
	 * <p>Note: The class argument is used like this:
	 * {@code ReedSolomon<Integer> rs = new ReedSolomon<>(f, gen, Integer.class, msgLen, eccLen);}</p>
	 * @param f the field for all values and operations (not {@code null})
	 * @param gen a generator of the field {@code f} (not {@code null})
	 * @param elemType the class object for the type parameter {@code E} (not {@code null})
	 * @param msgLen the length of message arrays, which must be positive
	 * @param eccLen the number of values to expand each message by, which must be positive
	 * @throws NullPointerException if any of the object arguments is null
	 * @throws IllegalArgumentException if msgLen &le; 0, eccLen &le; 0, or mlgLen + eccLen > Integer.MAX_VALUE
	 */
	public ReedSolomon(Field<E> f, E gen, Class<E> elemType, int msgLen, int eccLen) {
		// Check arguments
		Objects.requireNonNull(f);
		Objects.requireNonNull(gen);
		Objects.requireNonNull(elemType);
		if (msgLen <= 0 || eccLen <= 0 || Integer.MAX_VALUE - msgLen < eccLen)
			throw new IllegalArgumentException("Invalid message or ECC length");
		
		// Assign fields
		this.f = f;
		this.generator = gen;
		this.elementType = elemType;
		this.messageLen = msgLen;
		this.eccLen = eccLen;
		this.codewordLen = msgLen + eccLen;
	}
	
	
	
	/*---- Encoder methods ----*/
	
	/**
	 * Returns a new array representing the codeword produced by encoding the specified message.
	 * If the message has the correct length and all its values are
	 * valid in the field, then this method is guaranteed to succeed.
	 * @param message the message to encode, whose length must equal {@code this.messageLen}
	 * @return a new array representing the codeword values
	 * @throws NullPointerException if the message array or any of its elements are {@code null}
	 * @throws IllegalArgumentException if the message array has the wrong length
	 */
	public E[] encode(E[] message) {
		// Check arguments
		Objects.requireNonNull(message);
		if (message.length != messageLen)
			throw new IllegalArgumentException("Invalid message length");
		
		// Make the generator polynomial (this doesn't depend on the message)
		E[] genPoly = makeGeneratorPolynomial();
		
		// Compute the remainder ((message(x) * x^eccLen) mod genPoly(x)) by performing polynomial division.
		// Process message bytes (polynomial coefficients) from the highest monomial power to the lowest power
		E[] eccPoly = newArray(eccLen);
		Arrays.fill(eccPoly, f.zero());
		for (int i = messageLen - 1; i >= 0; i--) {
			E factor = f.add(message[i], eccPoly[eccLen - 1]);
			System.arraycopy(eccPoly, 0, eccPoly, 1, eccLen - 1);
			eccPoly[0] = f.zero();
			for (int j = 0; j < eccLen; j++)
				eccPoly[j] = f.subtract(eccPoly[j], f.multiply(genPoly[j], factor));
		}
		
		// Negate the remainder
		for (int i = 0; i < eccPoly.length; i++)
			eccPoly[i] = f.negate(eccPoly[i]);
		
		// Concatenate the message and ECC polynomials
		E[] result = newArray(codewordLen);
		System.arraycopy(eccPoly, 0, result, 0, eccLen);
		System.arraycopy(message, 0, result, eccLen, messageLen);
		return result;
	}
	
	
	// Computes the generator polynomial by multiplying powers of the generator value:
	// genPoly(x) = (x - gen^0) * (x - gen^1) * ... * (x - gen^(eccLen-1)).
	// The resulting array of coefficients is in little endian, i.e. from lowest to highest power, except
	// that the very highest power (the coefficient for the x^eccLen term) is omitted because it's always 1.
	// The result of this method can be pre-computed because it doesn't depend on the message to be encoded.
	private E[] makeGeneratorPolynomial() {
		// Start with the polynomial of 1*x^0, which is the multiplicative identity
		E[] result = newArray(eccLen);
		Arrays.fill(result, f.zero());
		result[0] = f.one();
		
		E genPow = f.one();
		for (int i = 0; i < eccLen; i++) {
			// At this point, genPow == generator^i.
			// Multiply the current genPoly by (x - generator^i)
			for (int j = eccLen - 1; j >= 0; j--) {
				result[j] = f.multiply(f.negate(genPow), result[j]);
				if (j >= 1)
					result[j] = f.add(result[j - 1], result[j]);
			}
			genPow = f.multiply(generator, genPow);
		}
		return result;
	}
	
	
	
	/*---- Decoder methods ----*/
	
	/**
	 * Attempts to decode the specified codeword with the maximum error-correcting
	 * capability allowed, returning either a best-guess message or {@code null}.
	 * <p>If the number of erroneous values in the codeword is less than or equal to floor(eccLen / 2),
	 * then decoding is guaranteed to succeed. Otherwise an explicit failure ({@code null} answer)
	 * is most likely, but wrong answer and right answer are also possible too.</p>
	 * @param codeword the codeword to decode, whose length must equal {@code this.codewordLen}
	 * @return a new array representing the decoded message, or {@code null} to indicate failure
	 * @throws NullPointerException if the codeword is {@code null}
	 * @throws IllegalArgumentException if the codeword array has the wrong length
	 */
	public E[] decode(E[] codeword) {
		return decode(codeword, eccLen / 2);
	}
	
	
	/**
	 * Attempts to decode the specified codeword with the specified level of
	 * error-correcting capability, returning either a best-guess message or {@code null}.
	 * <p>If the number of erroneous values in the codeword is less than or equal to numErrorsToCorrect,
	 * then decoding is guaranteed to succeed. Otherwise an explicit failure ({@code null} answer)
	 * is most likely, but wrong answer and right answer are also possible too.</p>
	 * @param codeword the codeword to decode, whose length must equal {@code this.codewordLen}
	 * @param numErrorsToCorrect the number of errors in the codeword to try to fix,
	 * which must be between 0 to floor(eccLen / 2), inclusive
	 * @return a new array representing the decoded message, or {@code null} to indicate failure
	 * @throws NullPointerException if the codeword is {@code null}
	 * @throws IllegalArgumentException if the codeword array has the wrong length,
	 * or numErrorsToCorrect is out of range
	 */
	public E[] decode(E[] codeword, int numErrorsToCorrect) {
		// Check arguments
		Objects.requireNonNull(codeword);
		if (codeword.length != codewordLen)
			throw new IllegalArgumentException("Invalid codeword length");
		if (numErrorsToCorrect < 0 || numErrorsToCorrect > eccLen / 2)
			throw new IllegalArgumentException("Number of errors to correct is out of range");
		
		// Calculate and check syndromes
		E[] syndromes = calculateSyndromes(codeword);
		if (!areAllZero(syndromes)) {
			// At this point, we know the codeword must have some errors
			if (numErrorsToCorrect == 0)
				return null;  // Only detect but not fix errors
			
			// Try to solve for the error locator polynomial
			E[] errLocPoly = calculateErrorLocatorPolynomial(syndromes, numErrorsToCorrect);
			if (errLocPoly == null)
				return null;
			
			// Try to find the codeword indexes where errors might have occurred
			int[] errLocs = findErrorLocations(errLocPoly, numErrorsToCorrect);
			if (errLocs == null || errLocs.length == 0)
				return null;
			
			// Try to find the error values at these indexes
			E[] errVals = calculateErrorValues(errLocs, syndromes);
			if (errVals == null)
				return null;
			
			// Perform repairs to the codeword with the information just derived
			E[] newCodeword = fixErrors(codeword, errLocs, errVals);
			
			// Final sanity check by recomputing syndromes
			E[] newSyndromes = calculateSyndromes(newCodeword);
			if (!areAllZero(newSyndromes))
				throw new AssertionError();
			codeword = newCodeword;
		}
		
		// At this point, all syndromes are zero.
		// Extract the message part of the codeword
		return Arrays.copyOfRange(codeword, eccLen, codeword.length);
	}
	
	
	// Returns a new array representing the sequence of syndrome values for the given codeword.
	// To summarize the math, syndrome[i] = codeword(generator^i).
	private E[] calculateSyndromes(E[] codeword) {
		// Check arguments
		Objects.requireNonNull(codeword);
		if (codeword.length != codewordLen)
			throw new IllegalArgumentException();
		
		// Evaluate the codeword polynomial at generator powers
		E[] result = newArray(eccLen);
		E genPow = f.one();
		for (int i = 0; i < result.length; i++) {
			result[i] = evaluatePolynomial(codeword, genPow);
			genPow = f.multiply(generator, genPow);
		}
		return result;
	}
	
	
	// Returns a new array representing the coefficients of the error locator polynomial
	// in little endian, or null if the syndrome values imply too many errors to handle.
	private E[] calculateErrorLocatorPolynomial(E[] syndromes, int numErrorsToCorrect) {
		// Check arguments
		Objects.requireNonNull(syndromes);
		if (syndromes.length != eccLen || numErrorsToCorrect <= 0 || numErrorsToCorrect > syndromes.length / 2)
			throw new IllegalArgumentException();
		
		// Copy syndrome values into augmented matrix
		Matrix<E> matrix = new Matrix<>(numErrorsToCorrect, numErrorsToCorrect + 1, f);
		for (int r = 0; r < matrix.rowCount(); r++) {
			for (int c = 0; c < matrix.columnCount(); c++) {
				E val = syndromes[r + c];
				if (c == matrix.columnCount() - 1)
					val = f.negate(val);
				matrix.set(r, c, val);
			}
		}
		
		// Solve the system of linear equations
		matrix.reducedRowEchelonForm();
		
		// Create result vector filled with zeros. Note that columns without a pivot
		// will yield variables that stay at the default value of zero
		E[] result = newArray(numErrorsToCorrect + 1);
		Arrays.fill(result, f.zero());
		result[0] = f.one();  // Constant term is always 1, regardless of the matrix
		
		// Find the column of the pivot in each row, and set the
		// appropriate output variable's value based on the column index
		outer:
		for (int r = 0, c = 0; r < matrix.rowCount(); r++) {
			// Advance the column index until a pivot is found, but handle specially if
			// the rightmost column is identified as a pivot or if no column is a pivot
			while (true) {
				if (c == matrix.columnCount())
					break outer;
				else if (f.equals(matrix.get(r, c), f.zero()))
					c++;
				else if (c == matrix.columnCount() - 1)
					return null;  // Linear system is inconsistent
				else
					break;
			}
			
			// Copy the value in the rightmost column to the result vector
			result[numErrorsToCorrect - c] = matrix.get(r, numErrorsToCorrect);
		}
		return result;
	}
	
	
	// Returns a new array that represents indexes into the codeword array where the value
	// might be erroneous, or null if it is discovered that the decoding process is impossible.
	// This method tries to find roots of the error locator polynomial by brute force.
	private int[] findErrorLocations(E[] errLocPoly, int maxSolutions) {
		// Check arguments
		Objects.requireNonNull(errLocPoly);
		if (maxSolutions <= 0 || maxSolutions > codewordLen)
			throw new IllegalArgumentException();
		
		// Create temporary buffer for roots found
		int[] indexesFound = new int[maxSolutions];
		int numFound = 0;
		
		// Evaluate errLocPoly(generator^-i) for 0 <= i < codewordLen
		E genRec = f.reciprocal(generator);
		E genRecPow = f.one();
		for (int i = 0; i < codewordLen; i++) {
			// At this point, genRecPow == generator^-i
			E polyVal = evaluatePolynomial(errLocPoly, genRecPow);
			if (f.equals(polyVal, f.zero())) {
				if (numFound >= indexesFound.length)
					return null;  // Too many solutions
				indexesFound[numFound] = i;
				numFound++;
			}
			genRecPow = f.multiply(genRec, genRecPow);
		}
		return Arrays.copyOf(indexesFound, numFound);
	}
	
	
	// Returns a new array representing the error values/magnitudes at the given error locations,
	// or null if the information given is inconsistent (thus decoding is impossible).
	// If the result of this method is not null, then after fixing the codeword it is guaranteed
	// to have all zero syndromes (but it could be the wrong answer, unequal to the original message).
	private E[] calculateErrorValues(int[] errLocs, E[] syndromes) {
		// Check arguments
		Objects.requireNonNull(errLocs);
		Objects.requireNonNull(syndromes);
		if (syndromes.length != eccLen)
			throw new IllegalArgumentException();
		
		// Calculate and copy values into matrix
		Matrix<E> matrix = new Matrix<>(syndromes.length, errLocs.length + 1, f);
		for (int c = 0; c < matrix.columnCount() - 1; c++) {
			E genPow = pow(generator, errLocs[c]);
			E genPowPow = f.one();
			for (int r = 0; r < matrix.rowCount(); r++) {
				matrix.set(r, c, genPowPow);
				genPowPow = f.multiply(genPow, genPowPow);
			}
		}
		for (int r = 0; r < matrix.rowCount(); r++)
			matrix.set(r, matrix.columnCount() - 1, syndromes[r]);
		
		// Solve matrix and check basic consistency
		matrix.reducedRowEchelonForm();
		if (!f.equals(matrix.get(matrix.columnCount() - 1, matrix.columnCount() - 1), f.zero()))
			return null;  // System of linear equations is inconsistent
		
		// Check that the top left side equals an identity matrix,
		// and extract the rightmost column as result vector
		E[] result = newArray(errLocs.length);
		for (int i = 0; i < result.length; i++) {
			if (!f.equals(matrix.get(i, i), f.one()))
				return null;  // Linear system is under-determined; no unique solution
			result[i] = matrix.get(i, matrix.columnCount() - 1);
		}
		return result;
	}
	
	
	// Returns a new codeword representing the given codeword with the given errors subtracted.
	// Always succeeds, as long as the array values are well-formed.
	private E[] fixErrors(E[] codeword, int[] errLocs, E[] errVals) {
		// Check arguments
		Objects.requireNonNull(codeword);
		Objects.requireNonNull(errLocs);
		Objects.requireNonNull(errVals);
		if (codeword.length != codewordLen || errLocs.length != errVals.length)
			throw new IllegalArgumentException();
		
		// Clone the codeword and change values at specific indexes
		E[] result = codeword.clone();
		for (int i = 0; i < errLocs.length; i++)
			result[errLocs[i]] = f.subtract(result[errLocs[i]], errVals[i]);
		return result;
	}
	
	
	
	/*---- Simple utility methods ----*/
	
	// Returns a new array of the given length with E as the actual element type.
	// This method exists so that unchecked generic operations are confined in one place here.
	@SuppressWarnings("unchecked")
	private E[] newArray(int len) {
		if (len < 0)
			throw new NegativeArraySizeException();
		return (E[])Array.newInstance(elementType, len);
	}
	
	
	// Returns the value of the given polynomial at the given point. The polynomial is represented
	// in little endian. In other words, this method evaluates result = polynomial(point)
	// = polynomial[0]*point^0 + polynomial[1]*point^1 + ... + ponylomial[len-1]*point^(len-1).
	private E evaluatePolynomial(E[] polynomial, E point) {
		Objects.requireNonNull(polynomial);
		Objects.requireNonNull(point);
		
		// Horner's method
		E result = f.zero();
		for (int i = polynomial.length - 1; i >= 0; i--) {
			result = f.multiply(point, result);
			result = f.add(polynomial[i], result);
		}
		return result;
	}
	
	
	// Tests whether all elements of the given array are equal to the field's zero element.
	private boolean areAllZero(E[] array) {
		Objects.requireNonNull(array);
		for (E val : array) {
			if (!f.equals(val, f.zero()))
				return false;
		}
		return true;
	}
	
	
	// Returns the given field element raised to the given power. The power must be non-negative.
	private E pow(E base, int exp) {
		Objects.requireNonNull(base);
		if (exp < 0)
			throw new UnsupportedOperationException();
		E result = f.one();
		for (int i = 0; i < exp; i++)
			result = f.multiply(base, result);
		return result;
	}
	
}

相应的demo为:

/* 
 * Reed-Solomon error-correcting code decoder demo (Java)
 * 
 * Copyright (c) 2019 Project Nayuki
 * All rights reserved. Contact Nayuki for licensing.
 * https://www.nayuki.io/page/reed-solomon-error-correcting-code-decoder
 */

import java.util.Arrays;
import java.util.Random;


public final class ReedSolomonDemo {
	
	// Runs a bunch of demos and tests, printing information to standard error.
	public static void main(String[] args) {
		showBinaryExample();
		showPrimeExample();
		testCorrectness();
	}
	
	
	// Shows an example of encoding a binary message, and decoding a codeword containing errors.
	private static void showBinaryExample() {
		// Configurable parameters
		BinaryField field = new BinaryField(0x11D);
		Integer generator = 0x02;
		int msgLen = 8;
		int eccLen = 5;
		ReedSolomon<Integer> rs = new ReedSolomon<>(field, generator, Integer.class, msgLen, eccLen);
		
		// Generate random message
		Integer[] message = new Integer[msgLen];
		for (int i = 0; i < message.length; i++)
			message[i] = rand.nextInt(field.size);
		System.err.println("Original message: " + Arrays.toString(message));
		
		// Encode message to produce codeword
		Integer[] codeword = rs.encode(message);
		System.err.println("Encoded codeword: " + Arrays.toString(codeword));
		
		// Perturb some values in the codeword
		double probability = (double)(eccLen / 2) / (msgLen + eccLen);
		int perturbed = 0;
		for (int i = 0; i < codeword.length; i++) {
			if (rand.nextDouble() < probability) {
				codeword[i] = field.add(codeword[i], rand.nextInt(field.size - 1) + 1);
				perturbed++;
			}
		}
		System.err.println("Number of values perturbed: " + perturbed);
		System.err.println("Perturbed codeword: " + Arrays.toString(codeword));
		
		// Try to decode the codeword
		Integer[] decoded = rs.decode(codeword);
		System.err.println("Decoded message: " + (decoded != null ? Arrays.toString(decoded) : "Failure"));
		System.err.println();
	}
	
	
	// Shows an example of encoding a prime message, and decoding a codeword containing errors.
	private static void showPrimeExample() {
		// Configurable parameters
		PrimeField field = new PrimeField(97);
		Integer generator = 5;
		int msgLen = 9;
		int eccLen = 4;
		ReedSolomon<Integer> rs = new ReedSolomon<>(field, generator, Integer.class, msgLen, eccLen);
		
		// Generate random message
		Integer[] message = new Integer[msgLen];
		for (int i = 0; i < message.length; i++)
			message[i] = rand.nextInt(field.modulus);
		System.err.println("Original message: " + Arrays.toString(message));
		
		// Encode message to produce codeword
		Integer[] codeword = rs.encode(message);
		System.err.println("Encoded codeword: " + Arrays.toString(codeword));
		
		// Perturb some values in the codeword
		double probability = (double)(eccLen / 2) / (msgLen + eccLen);
		int perturbed = 0;
		for (int i = 0; i < codeword.length; i++) {
			if (rand.nextDouble() < probability) {
				codeword[i] = field.add(codeword[i], rand.nextInt(field.modulus - 1) + 1);
				perturbed++;
			}
		}
		System.err.println("Number of values perturbed: " + perturbed);
		System.err.println("Perturbed codeword: " + Arrays.toString(codeword));
		
		// Try to decode the codeword
		Integer[] decoded = rs.decode(codeword);
		System.err.println("Decoded message: " + (decoded != null ? Arrays.toString(decoded) : "Failure"));
		System.err.println();
	}
	
	
	// Tests the Reed-Solomon encoding and decoding logic under many parameters with many repetitions.
	// This prints the results of each test round, and loops infinitely unless
	// stopped by an exception (which should not happen if correctly designed).
	// - Whenever numErrors <= floor(eccLen / 2), the decoding will always succeed,
	//   otherwise the implementation is faulty.
	// - Whenever numErrors > floor(eccLen / 2), failures and wrong answers are perfectly normal,
	//   and success is generally not expected (but still possible).
	private static void testCorrectness() {
		// Field parameters
		BinaryField field = new BinaryField(0x11D);
		Integer generator = 0x02;
		
		// Run forever unless an exception is thrown or unexpected behavior is encountered
		int testDuration = 3000;  // In milliseconds
		while (true) {
			// Choose random Reed-Solomon parameters
			int msgLen = rand.nextInt(field.size) + 1;
			int eccLen = rand.nextInt(field.size) + 1;
			int codewordLen = msgLen + eccLen;
			if (codewordLen > field.size - 1)
				continue;
			int numErrors = rand.nextInt(codewordLen + 1);
			ReedSolomon<Integer> rs = new ReedSolomon<>(field, generator, Integer.class, msgLen, eccLen);
			
			// Do as many trials as possible in a fixed amount of time
			long numSuccess = 0;
			long numWrong   = 0;
			long numFailure = 0;
			long startTime = System.currentTimeMillis();
			while (System.currentTimeMillis() - startTime < testDuration) {
				
				// Generate random message
				Integer[] message = new Integer[msgLen];
				for (int i = 0; i < message.length; i++)
					message[i] = rand.nextInt(field.size);
				
				// Encode message to codeword
				Integer[] codeword = rs.encode(message);
				
				// Perturb values in the codeword
				int[] indexes = new int[codewordLen];
				for (int i = 0; i < indexes.length; i++)
					indexes[i] = i;
				for (int i = 0; i < numErrors; i++) {
					// Partial Durstenfeld shuffle
					int j = rand.nextInt(indexes.length - i) + i;
					int temp = indexes[i];
					indexes[i] = indexes[j];
					indexes[j] = temp;
					// Actual perturbation
					codeword[indexes[i]] ^= rand.nextInt(field.size - 1) + 1;
				}
				
				// Try to decode the codeword, and evaluate result
				Integer[] decoded = rs.decode(codeword);
				if (Arrays.equals(decoded, message))
					numSuccess++;
				else if (numErrors <= eccLen / 2)
					throw new AssertionError("Decoding should have succeeded");
				else if (decoded != null)
					numWrong++;
				else
					numFailure++;
			}
			
			// Print parameters and statistics for this round
			System.err.printf("msgLen=%d, eccLen=%d, codewordLen=%d, numErrors=%d;  numTrials=%d, numSuccess=%d, numWrong=%d, numFailure=%d%n",
				msgLen, eccLen, codewordLen, numErrors, numSuccess + numWrong + numFailure, numSuccess, numWrong, numFailure);
		}
	}
	
	
	private static final Random rand = new Random();
	
}

参考资料

[1] Reed-Solomon Codes (An introduction to Reed-Solomon codes: principles, architecture and implementation)
[2] Error Correction Coding
[3] Reed-Solomon error-correcting code decoder

  • 3
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Multisim是一种功能强大的电路设计和仿真软件,用于帮助工程师和学生设计、分析和验证电路的性能。它提供了直观易用的界面和丰富的电子元件库,使用户能够快速建立电路图并进行仿真。 Multisim范例是软件中提供的一系列电路设计示例,旨在帮助用户更好地理解和掌握各种电路的基本原理和特性。这些范例涵盖了不同的电子领域,如模拟电路、数字电路、通信电路等,从简单到复杂,从基础到高级,涵盖了各种常见的电路设计。 通过Multisim范例,用户可以学习和掌握各种电路的工作原理,了解各个元件的使用方法和特性。同时,范例还提供了仿真功能,用户可以在软件中模拟电路的运行情况,观察电压、电流、功耗等参数的变化,从而更好地理解电路的性能和特点。 通过Multisim范例的学习和实践,用户可以提升自己的电路设计和分析能力。他们可以理解电路中各个元件的相互作用,掌握如何通过合理的设计和参数选择来实现所需的电路功能。同时,范例还为用户提供了解决电路设计中常见问题的思路和方法,帮助他们在实际应用中更好地解决各种电路设计难题。 总之,Multisim范例是一种很好的学习工具,通过它,用户可以学习电路设计的方法和技巧,提高自己的电路设计能力,并在实际应用中解决电路设计问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值