Blogs

uninInitiveation的线性反馈移位寄存器,第XVI部分:Reed-Solomon纠错

杰森萨赫斯2018年6月19日

上次,我们谈到了 纠错和检测,覆盖汉明距离,CRC和汉明代码等一些基础知识。如果您对此主题的新功能,我将强烈建议返回阅读该文章。

这次我们要覆盖 芦苇所罗门码。 (我意味着在XV部分覆盖这个话题,但这篇文章已经太久了,所以我’ve大致分为一半。)这些是纠错的工作研讨会之一,它们用于压倒性的丰富,以纠正错误的短暂爆发。我们’请参阅使用Reed Solomon编码消息相当简单,因此可以轻松地在嵌入式系统上进行C。

We’RE也将讨论纠错码的一些性能指标。

芦苇所罗门码

与许多其他纠错码一样,簧片索蒙代码是参数化的。 reed-solomon \((n,k)\)代码,其中\(n = 2 ^ m-1 \)和\(k = n-2t \)可以纠正\(t \)错误。它们不是二元代码;每个符号必须具有\(2 ^ m \)可能的值,通常是\(gf(2 ^ m)\)的元素。

具有\(n = 255 \)的簧片所罗门代码非常常见,因为然后每个符号是\(gf(2 ^ 8)\)的元素,并且可以表示为单个字节。 a \((255,247)\)RS代码将能够纠正4个错误,并且A \((255,223)\)RS代码将能够纠正16个错误,每个错误是一个传输符号中的任意错误。

您可能已经注意到在上一篇文章中讨论的某些编码技术中的一种模式:

  • 如果我们计算一串位字符串的奇偶校验并连接奇偶校验位,则结果消息具有零模数2的奇偶校验
  • 如果我们计算一串字节字符串的校验和,并串联否定校验和,则结果消息具有零模2256的校验和
  • 如果我们计算消息的CRC(没有初始/最终比特翻转)并连接CRC,则结果消息具有余零Modulo的剩余CRC多项式
  • 如果我们计算汉明代码的奇偶校验位,并将它们连接到数据位,则产生的消息具有初始模块的余量零点多项式

在所有情况下,我们都有这种魔法技术,使用划分算法来计算剩余部分,并连接其余的剩余部分,使得结果消息具有零余数。如果我们收到一条确实具有非零余数的消息,我们可以检测到这一点,在某些情况下,在某些情况下使用非零余量来确定错误发生并纠正它们的位置。

奇偶校验和校验和是较差的方法,因为它们无法检测到换位。它’很容易取消平价翻转;它’在其他地方不小心翻转另一个稍微逐步抵消CRC误差,更难得更难。

如果有一个与校验和相似的编码算法,它是什么,因为它以字节逐字节为基础运行,但对交换字节具有鲁棒性? Reed-Solomon是一种这样的算法。它相当于我们的所有其他线性编码技术做同样的事情’谈到编码消息:

  • 采取数据位
  • 使用多项式划分算法来计算剩余部分
  • 通过连接数据位和余数位来创建码字
  • 有效的码字现在具有零余数

在这篇文章中,我将描述芦苇所罗门编码,但是 不是 典型的解码方法,这些方法有点复杂,包括像Berlekamp-Massey算法这样的花哨声音步骤(是的,这是我们在中看到的相同的步骤 第六部分,只有它’完成\(gf(2 ^ m)\)而不是二进制\(gf(2)\)版本), Chian Search.,而且 福尼算法。如果您想了解更多,我建议在列表中读取BBC白皮书WHP031 参考,它确实进入了正确的细节水平,并且有很多例子。

除了更复杂的理解之外,解码也更加计算密集。 Reed-Solomon编码器很简单地在硬件中实现— which we’ll see a bit later —并且很简单地在嵌入式系统中实现软件。解码器是棘手的,你’D可能想要的东西比8位微控制器更强大,以执行它们,或者它们’ll非常慢。在空间通信中首次使用Reed-Solomon在航天任务上,包括没有确定的编码器的编码器可以在地球上解码接收的信号。来自A. 威廉A. Geisel的Reed-Solomon错误校正编码NASA教程:

芦苇所罗门(RS)代码一直在寻找广泛的 自1977年自行车以来的应用程序’s deep space 通信系统。在旅行者时’s launch, efficient 编码器存在,但准确的解码方法甚至不是 可用的!喷射推进实验室(JPL)科学家及 工程师赌博的是,随着航行到天王星的时候 1986年,解码算法和设备都是可用的 完善。他们是正确的!航行员’S通信系统是 能够从每秒获得21,600位的数据速率 20亿英里远,收到的信号能量1000亿 比普通腕表电池弱的时间!

该声明“准确的解码方法甚至不可用”1977年有点可疑,虽然这一概念对即可以建造的信仰的概念 一些 其他 账户:

Lahmeyer是该项目的唯一工程师,以及他的技术人员和建筑商的团队不知疲倦地努力让旅行者到达天王星的解码器。否则,它们必须减慢收到的数据速率降低噪声干扰。“每个芯片必须有线,” Lahmeyer says. “谢天谢地,我们按时工作。”

(杰斐逊市杂志,2015年6月)

为了使天王星和海王星成为可能的扩展任务, 必须设计一些新的和急外的技术 用于使用硬件的航天器和新颖的新技术 最满意必须验证使用。一个值得注意的要求是需要 为了通过对所有先前任务用于所有使用的非常低效的方法来改进数据流的纠错编码(以及 木星和土星的盗贼),最大值低得多 来自天王星和海王星(由于距离)的数据速率会很大 损失了可能已经存在的图片数量和其他数据 获得。一个新的和高效的数据编码方法 芦苇所罗门(RS)编码计划在天王星的航行员中,以及 编码装置集成到航天器设计中。问题 是,虽然RS编码易于做,并且编码设备是 比较简单,在接地上收到后解码数据 是如此在数学上密集,地球上没有电脑 在Voyager设计时处理解码任务– or even 推出。使用RS编码器的数据吞吐量的增加是关于 旧的标准高尔编码的三个倍数,所以包括 能力被认为是非常值得的。假设可以通过在天王星中的航行时间来开发解码器。这样的 是这种情况,今天RS编码是常规用于CD玩家的 许多其他普通家用电子设备。

(天文学家,2017年8月)

Geisel可能意味着“efficient” rather than “accurate”;解码算法在a之前是众所周知的 1978年的美国宇航局报告 which states

首先描述这些代码 由I. I. Reed和G. Solomon于1960年。一个系统解码算法不是 发现直到1968年Berlekamp。因为他们的复杂性,卢比 除非没有其他代码合适时,代码没有广泛使用。

Reed-Solomon编码的细节

At any rate, with Reed-Solomon, we have a polynomial, but instead of the coefficients being 0 or 1 like our usual polynomials in \( GF(2)[x] \), this time the coefficients are elements of \( GF(2^m) \). A byte-oriented Reed-Solomon encoding uses \( GF(2^8) \). The polynomial used in Reed-Solomon is usually written as \( g(x) \), the generator polynomial, with

$$ g(x)=(x + \ lambda ^ b)(x + \ lambda ^ {b + 1})(x + \ lambda ^ {b + 2})\ ldots(x + \ lambda ^ {b + nk-1} )$$

其中\(\ lambda \)是\(gf(2 ^ m)\)和\(b \)的原始元素是某种常量,通常为0或1或\(-t \)。这意味着多项式’S根是\(\ lambda \)的连续权力,这使得数学效果很好。

我们所做的就是将消息视为多项式\(m(x)\),计算剩余部分\(r(x)= x ^ {nk} m(x)\ bmod g(x)\),然后连接它到了消息;得到的码字\(c(x)= x ^ {n-k} m(x)+ r(x)\)保证是发电机多项式\(g(x)\)的倍数。在接收端,我们计算\(c(x)\ bmod g(x)\),如果它’s zero, everything’太棒了;否则我们必须做一些血腥数学,但这允许我们找到并纠正\(t \)符号的错误。

这可能听起来很抽象,所以让’通过一个例子来工作。

在Python的里德萨洛蒙

We’重新编码一些邮件,使用芦苇于芦苇于两种方式,首先使用 unireedsolomon library, and then from scratch using libgf2. If you’重新使用库,你’ll需要真的小心;图书馆应该是 普遍的,意味着您可以编码或解码 任何 芦苇所罗门的特殊味道—与CRC一样,有许多可能的变体,并且您需要能够指定每个符号的Galois字段的多项式,以及发电元素\(\ lambda \)和初始功率\(b \)。如果有人告诉你他们’re using Reed-Solomon \((255,223)\)代码,他们’吸食有趣的东西,因为那里’s多于一个,您需要知道哪个特定的变种以便正确沟通。

BBC白皮书中有两个例子:

  • 一个使用\(rs(15,11)\)4位符号,符号字段\(gf(2)[y] /(y ^ 4 + y + 1)\);对于生成器,\(\ lambda = y = {\ tt 2} \)和\(b = 0 \),形成发电机多项式

$$ \ begin {align} g(x) &=(x + 1)(x + \ lambda)(x + \ lambda ^ 2)(x + \ lambda ^ 3)\ cr &= x ^ 4 + \ lambda ^ {12} x ^ 3 + \ lambda ^ {4} x ^ 2 + x + \ lambda ^ 6 \ cr &= x ^ 4 + 15x ^ 3 + 3x ^ 2 + x + 12 \ cr &= x ^ 4 + {\ tt f} x ^ 3 + {\ tt 3} x ^ 2 + {\ tt 1} x + {\ tt 12} \end{align}$$

  • 另一个使用这一点 DVB-T. 规格 that uses \( RS(255,239) \) for 8-bit symbols, with symbol field \( GF(2)[y]/(y^8 + y^4 + y^3 + y^2 + 1) \) (hex representation 11d) and for the generator, \( \lambda = y = {\tt 2} \) and \( b=0 \), forming the generator polynomial

$$ \ begin {align} g(x) &=(x + 1)(x + \ lambda)(x + \ lambda ^ 2)(x + \ lambda ^ 3)\ ldots(x + \ lambda ^ {15})\ cr &= x ^ {16} + \ lambda ^ {12} x ^ 3 + \ lambda ^ {4} x ^ 2 + x + \ lambda ^ 6 \ cr &= x ^ {16} + 59x ^ {15} + 13x ^ {14} + 104x ^ {13} + 189x ^ {12} + 68x ^ {11} + 209x ^ {10} + 30x ^ 9 + 8x ^ 8 \ Cr. &\ qquad + 163x ^ 7 + 65x ^ 6 + 41x ^ 5 + 229x ^ 4 + 98x ^ 3 + 50x ^ 2 + 36x + 59 \ cr &= x ^ {16} + {\ tt 3b} x ^ {15} + {\ tt 0d} x ^ {14} + {\ tt 68} x ^ {13} + {\ tt bd} x ^ {12} + {\ tt 44} x ^ {11} + {\ tt d1} x ^ {10} + {\ tt 1e} x ^ 9 + {\ tt 08} x ^ 8 \ cr &\ qquad + {\ tt a3} x ^ 7 + {\ tt 41} x ^ 6 + {\ tt 29} x ^ 5 + {\ tt e5} x ^ 4 + {\ tt 62} x ^ 3 + {\ TT 32} x ^ 2 + {\ tt 24} x + {\ tt 3b} \end{align}$$

In unireedsolomon, the RSCoder class 使用了这些参数:

  • c_exp —这是我们两个示例的每个符号的位数,4和8
  • prim —这是我们两个示例的符号字段多项式,0x13和0x11d
  • generator —这是符号字段中的发电元素,两个示例为0x2
  • fcr — this is the “首先连续根”\(b \),两个例子的\(b = 0 \)

您可以通过编码消息\(m(x)= 1 \)来验证发电机多项式,所有零符号在末尾的尾随一个符号;这将创建一个码字\(c(x)= x ^ {n-k} + r(x)= g(x)\)。

让’s do it!

import unireedsolomon as rs

def codeword_symbols(msg):
    return [ord(c) for c in msg]

def find_generator(rscoder):
    """ Returns the generator polynomial of an RSCoder class """
    msg = [0]*(rscoder.k - 1) + [1]
    degree = rscoder.n - rscoder.k
    codeword = rscoder.encode(msg)
    # look at the trailing elements of the codeword
    return codeword_symbols(codeword[(-degree-1):])

rs4 = rs.RSCoder(15,11,generator=2,prim=0x13,fcr=0,c_exp=4)
print "g4(x) = %s" % find_generator(rs4)

rs8 = rs.RSCoder(255,239,generator=2,prim=0x11d,fcr=0,c_exp=8)
print "g8(x) = %s" % find_generator(rs8)
g4(x) = [1, 15, 3, 1, 12]
g8(x) = [1, 59, 13, 104, 189, 68, 209, 30, 8, 163, 65, 41, 229, 98, 50, 36, 59]

易如反掌!

Encoding messages is just a matter of using either a string (byte array) or a list of message symbols, and we get a string back by default. (You can use encode(msg, return_string=False) but then you get a bunch of GF2Int objects back rather than integer coefficients.)

(警告:Uniredsolomon包使用全球单例管理Galois Field算术, so if you want to have multiple RSCoder objects out there, they’ll only work if they share the same field generator polynomial. This is why each time I want to switch back between the 4-bit and the 8-bit RSCoder, I have to create a new object, so that it reconfigures the way Galois field arithmetic works.)

BBC白皮书仅用于4位\(RS(15,11)\)示例,使用\([1,2,3,4,5,6,7,9,9,10, 11] \)产生余数\([3,3,12,12] \):

rs4 = rs.RSCoder(15,11,generator=2,prim=0x13,fcr=0,c_exp=4)
encmsg15 = codeword_symbols(rs4.encode([1,2,3,4,5,6,7,8,9,10,11]))
print "RS(15,11) encoded example message from BBC WHP031:\n", encmsg15
RS(15,11) encoded example message from BBC WHP031:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 3, 3, 12, 12]

The real fun begins when we can start encoding actual messages, like Ernie, you have a banana in your ear! Since this has length 37, let’使用代码的缩短版本,\(RS(53,37)\)。

妈妈’小宝贝喜欢shortnin’, Shortnin’, Mama’小宝贝喜欢shortnin’ Bread

哦,我们避风港’T谈到缩短的代码—这是我们刚刚省略码字中的初始符号,并且发送器和接收器都同意将它们视为隐含的零。八位\(RS(53,37)\)发射机编码为度为52的码字多项式,接收器刚延伸到254度(在开始时添加了202个暗示的零字节)以便解码的目的就像任何其他\(rs(255,239)\)代码。

rs8s = rs.RSCoder(53,37,generator=2,prim=0x11d,fcr=0,c_exp=8)
msg = 'Ernie, you have a banana in your ear!'
codeword = rs8s.encode(msg)
print ('codeword=%r' % codeword)
remainder = codeword[-16:]
print ('remainder(hex) = %s' % remainder.encode('hex'))
codeword='Ernie, you have a banana in your ear!U,\xa3\xb4d\x00:R\xc4P\x11\xf4n\x0f\xea\x9b'
remainder(hex) = 552ca3b464003a52c45011f46e0fea9b

消息后的二进制文件是剩下的。现在我们可以用错误解码各种消息:

for init_msg in [
    'Ernie, you have a banana in your ear!',
    'Billy! You have a banana in your ear!',
    'Arnie! You have a potato in your ear!',
    'Eddie? You hate a banana in your car?',
    '01234567ou have a banana in your ear!',
    '012345678u have a banana in your ear!',
]:
    decoded_msg, decoded_remainder = rs8s.decode(init_msg+remainder)
    print "%s -> %s" % (init_msg, decoded_msg)
Ernie, you have a banana in your ear! -> Ernie, you have a banana in your ear!
Billy! You have a banana in your ear! -> Ernie, you have a banana in your ear!
Arnie! You have a potato in your ear! -> Ernie, you have a banana in your ear!
Eddie? You hate a banana in your car? -> Ernie, you have a banana in your ear!
01234567ou have a banana in your ear! -> Ernie, you have a banana in your ear!
---------------------------------------------------------------------------
RSCodecError                              Traceback (most recent call last)
 in ()
      7     '012345678u have a banana in your ear!',
      8 ]:
----> 9     decoded_msg, decoded_remainder = rs8s.decode(init_msg+remainder)
     10     print "%s -> %s" % (init_msg, decoded_msg)

/Users/jmsachs/anaconda/lib/python2.7/site-packages/unireedsolomon/rs.pyc in decode(self, r, nostrip, k, erasures_pos, only_erasures, return_string)
    321         # being the rightmost byte
    322         # X is a corresponding array of GF(2^8) values where X_i = alpha^(j_i)
--> 323         X, j = self._chien_search(sigma)
    324 
    325         # Sanity check: Cannot guarantee correct decoding of more than n-k errata (Singleton Bound, n-k being the minimum distance), and we cannot even check if it's correct (the syndrome will always be all 0 if we try to decode above the bound), thus it's better to just return the input as-is.

/Users/jmsachs/anaconda/lib/python2.7/site-packages/unireedsolomon/rs.pyc in _chien_search(self, sigma)
    822         errs_nb = len(sigma) - 1 # compute the exact number of errors/errata that this error locator should find
    823         if len(j) != errs_nb:
--> 824             raise RSCodecError("Too many (or few) errors found by Chien Search for the errata locator polynomial!")
    825 
    826         return X, j

RSCodecError: Too many (or few) errors found by Chien Search for the errata locator polynomial!

最后一条消息无法解码,因为有9个错误和\(rs(53,37)\)或\(rs(255,239)\)只能纠正8个错误。

但如果有足够的错误很少,芦苇所罗门将把它们全部解决。

为什么它有效?

芦苇所罗门编码和解码工程的原因有点难以掌握。有很多好解释 如何 编码和解码工作,我最喜欢的是BBC白皮书,但他们都没有真正居住 为什么 有用。我们可以传递冗余和在整个剩余位均匀地传播的有限场的冗余和神秘度,或者可以将发电机多项式的根部的奇偶校验矩阵的光谱解释被视为“frequencies”, but I don’T有一个很好的解释。这里’s the best I can do:

让’S表示我们有一些已知位置的错误。这被称为 擦除,与真正的错误相比,该位置未提前知道的位置。想想一堆纸上的符号,有些白痴抹去其中一个或溢出蔬菜汤,你’ve lost one of the symbols, but you know where it is. The message received could be 45 72 6e 69 65 2c 20 ?? 6f 75 where the ?? represents an erasure. Of course, binary computers don’t have a way to compute arithmetic on ??, so what we would do instead is just pick an arbitrary pattern like 00 or 42 to replace the erasure, and mark its position (in this case, byte 7) for later processing.

无论如何,在芦苇所罗门代码中, 误差值是线性的。这真的是这里的关键。我们可以有最多可以\(2t \)擦除(而不是\(t \)真实错误),并且仍然保持这种线性独立性。线性独立意味着对于一个方程式,只有一个解决方案。例如,字节3和28的错误可以’t误以为字节9和14的错误。一旦超过最大的错误或擦除次数,线性独立属性分崩离析,并且有许多可能的方法来纠正所接收的错误,所以我们可以’t确保哪个是正确的。

为了获得更多技术:我们有一个传输的编码消息\(c_t(x)= x ^ {nk} m(x)+ r(x)\)其中\(m(x)\)是未终止的消息和\(r(x)\)是剩下的modulo发电机多项式\(g(x)\)和收到的消息\(c_r(x)= c_t(x)+ e(x)\),其中误差多项式\(e(x)\)表示发送和接收消息之间的差异。通过计算余数\(C_R(x)\ bmod g(x)\),接收器可以计算\(e(x)\ bmod g(x)\)。如果没有错误,那么\(e(x)\ bmod g(x)= 0 \)。通过擦除,我们知道错误的位置。让’s say we had \( n=255 \), \( 2t=16 \), and we had three errors at known locations \( i_1, i_2, \) and \( i_3, \) in which case \( e(x) = e_{i_1}x^{i_1} + e_{i_2}x^{i_2} + e_{i_3}x^{i_3} \). These terms \( e_{i_1}x^{i_1}, e_{i_2}x^{i_2}, \) etc. are the error values that form a linear independent set. For example, if the original message had 42 in byte 13, but we received 59 instead, then the error coefficient in that location would be 42 + 59 = 1B, so \( i_1 = 13 \) and the error value would be \( {\tt 1B}x^{13} \).

有限田地的代数保证我们,如果我们有适当的发电机多项式选择(G(x))的程度\(2t \)—用于芦苇所罗门的那个,\(g(x)= \ prod \ limits_ {i = 0} ^ {2t-1}(x-\ lambda ^ {b + i})\),是合适的—然后从\(x ^ 0 \)到\(x ^ {n-1})的任何选择\(x \)的不同权力是线性的。例如,如果\(2t = 16 \),那么我们无法写入\(x ^ {15} = ax ^ {13} + bx ^ {8} + c \)对于某种选择,对于某种选择(a,b,c \)—否则这意味着\(x ^ {15},x ^ {13},x ^ 8,\)和\(1 \)不形成线性独立的集合。这意味着如果我们在已知位置擦除\(2T \)擦除,那么我们可以弄清楚每个位置的系数,并找到未知值。

在我们的3擦除示例中,如果\(2t = 16,i_1 = 13,i_2 = 8,i_3 = 0 \),那么我们有一个非常容易的案例。所有错误都处于剩余时间以下的位置,这意味着所有错误都在其余部分中,消息符号完好无损。在这种情况下,误差多项式看起来像\(e(x)= e_ {13} x ^ {13} x ^ {13} + e_8x ^ 8 + e_0 \)对于某些错误系数\(e_ {13},e_8,e_0 \) (gf(256)\)。收到的剩余部分\(e(x)\ bmod g(x)= e(x)\),我们可以直接找到错误系数。

另一方面,让’S说,我们知道错误位于位置\(i_1 = 200,i_2 = 180,i_3 = 105 \)。在这种情况下,我们可以计算\(x ^ {200} \ bmod g(x)\),\(x ^ {180} \ bmod g(x)\),\(x ^ {105} \ bmod g( X) \)。这些每个来自0到15的系数,但我们知道接收的剩余部分\(e(x)\ bmod g(x)\)必须是三个多项式的线性组合,\(e_ {200} x ^ {200} + e_ {180} x ^ {180} + e_ {105} x ^ {105} x ^ {105} \ bmod g(x)\)我们可以解决\(e_ {200} \),\(e_ {180} \),使用线性代数和\(e_ {105})。

如果我们有真正的错误,那么类似的情况适用 大学教师’t 提前了解他们的立场。在这种情况下,我们可以纠正\(t \)错误,但代数工作,以便我们可以编写\(2t \)方程,其中\(2t \)未知(每个\每个错误位置和值(t) \)错误)并解决它们。

我们所需要的只是让某人证明这一线性独立性。我赢了那部分’能够直观地解释。如果您查看典型的解码器算法,它们将建设性地显示如何找到唯一的解决方案,但不是它为什么工作。 Reed-Solomon解码器利用所谓的“key equation”;对于欧几里德算法解码器(与Berlekamp-Massey解码器相反),关键方程是\(\ Omega(x)= s(x)\ lambda(x)\ bmod x ^ {2t} \)和您 ’应该知道这意味着什么以及如何使用它…叹。有时盲目的信仰有时会有所帮助。

这就是我赢了的原因’在解码中有更多的话。阅读BBC白皮书。

芦苇所罗门编码 libgf2

我们不’t have to use the unireedsolomon library as a black box. We can do the encoding step in libgf2 using libgf2.lfsr.PolyRingOverField. This is a helper class that the undecimate() function uses. I talked a little bit about this in 第十一部分以及一些细节“two-variable”数学的东西,在有限田地上的多项式环。让’s forget about the abstract math for now, and just start computing. The PolyRingOverField just defines a particular arithmetic of polynomials, represented as lists of coefficients in ascending order (so \( x^2 + 2 \) is represented as [2, 0, 1]):

from libgf2.gf2 import GF2QuotientRing, GF2Polynomial
from libgf2.lfsr import PolyRingOverField
import numpy as np

f16 = GF2QuotientRing(0x13)
f256 = GF2QuotientRing(0x11d)
rsprof16  = PolyRingOverField(f16)
rsprof256 = PolyRingOverField(f256)

p1 = [1,4]
p2 = [2,8]
rsprof256.mul(p1, p2)
[2, 0, 32]
def compute_generator_polynomial(rsprof, elementfield, k):
    el = 1
    g = [1]
    n = (1 << elementfield.degree) - 1 
    for _ in xrange(n-k):
        g = rsprof.mul(g, [el, 1])
        el = elementfield.mulraw(el, 2)
    return g

g16 = compute_generator_polynomial(rsprof16, f16, 11)
g256 = compute_generator_polynomial(rsprof256, f256, 239)
print "generator polynomials (in ascending order of coefficients)"
print "RS(15,11):    g16=", g16
print "RS(255,239): g256=", g256
generator polynomials (in ascending order of coefficients)
RS(15,11):    g16= [12, 1, 3, 15, 1]
RS(255,239): g256= [59, 36, 50, 98, 229, 41, 65, 163, 8, 30, 209, 68, 189, 104, 13, 59, 1]

现在,要对消息进行编码,我们只需将二进制数据转换为多项式,然后拍摄剩余的\(\ bmod g(x)\):

print "BBC WHP031 example for RS(15,11):"
msg_bbc = [1,2,3,4,5,6,7,8,9,10,11]
msgpoly = [0]*4 + [c for c in reversed(msg_bbc)]
print "message=%r" % msg_bbc
q,r = rsprof16.divmod(msgpoly, g16)
print "remainder=", r

print "\nReal-world example for RS(255,239):"
print "message=%r" % msg
msgpoly = [0]*16 + [ord(c) for c in reversed(msg)]
q,r = rsprof256.divmod(msgpoly, g256)
print "remainder=", r
print "in hex: ", ''.join('%02x' % b for b in reversed(r))
print "remainder calculated earlier:\n         %s" % remainder.encode('hex')
BBC WHP031 example for RS(15,11):
message=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
remainder= [12, 12, 3, 3]

Real-world example for RS(255,239):
message='Ernie, you have a banana in your ear!'
remainder= [155, 234, 15, 110, 244, 17, 80, 196, 82, 58, 0, 100, 180, 163, 44, 85]
in hex:  552ca3b464003a52c45011f46e0fea9b
remainder calculated earlier:
         552ca3b464003a52c45011f46e0fea9b

看看是多么容易?

出色地… you probably didn’t, because PolyRingOverField hides the implementation of the abstract algebra. We can make the encoding step even easier to understand if we take the perspective of an LFSR.

巧克力草莓桃子香蕉香蕉开心果薄荷柠檬橙色肥皂

我们可以使用LFSR处理软件或硬件(以及至少剩余计算的部分的剩余计算部分)中的芦苇所罗门编码。现在,我们必须放松我们的意思是LFSR。到目前为止,所有LFSR都直接处理二进制位,每个移位单元都有一个水龙头,或者没有’T,取决于与LFSR相关的多项式的系数。对于Reed-Solomon,计算单位是\(GF(256)\)的元素,表示为特定程度-8原始多项式的8位字节。因此,移位单元代替包含位,而不是包含字节,并且抽头将从简单的抽头改变为乘法器越过\(gf(256)\)。 (这在硬件中有点毛茸茸,但它’并不那么糟糕。)实现Reed-Solomon编码的10字节LFSR(GF(256)\)看起来像这样的东西:

我们将在右侧的消息中提供字节\(b [k] \);字节\(r_0 \)通过\(r_9 \)表示余数,并且通过\(p_9 \)表示系数\(p_0 \)表示RS代码的非领先系数’发电机多项式。在这种情况下,我们将使用具有前导系数1的10度发生器多项式。

对于每个新的字节,我们做了两件事:

  • 将所有单元格换档,在消息的一个字节中转换
  • 取出字节移出,并将其乘以发电机多项式,然后使用移位寄存器的内容添加(XOR)。

我们还必须记住此另一个\(n-k \)次(移位寄存器的长度)以覆盖\(x ^ {n-k} \)因子。 (记住,我们’Re尝试计算\(r(x)= x ^ {n-k} m(x)\)。)

f256 = GF2QuotientRing(0x11d)

def rsencode_lfsr1(field, message, gtaps, remainder_only=False):
    """ encode a message in Reed-Solomon using LFSR
    field: symbol field
    message: string of bytes
    gtaps: generator polynomial coefficients, in ascending order
    remainder_only: whether to return the remainder only
    """
    glength = len(gtaps)
    remainder = np.zeros(glength, np.int)
    for char in message + '\0'*glength:
        remainder = np.roll(remainder, 1)
        out = remainder[0]
        remainder[0] = ord(char)
        for k, c in enumerate(gtaps):
            remainder[k] ^= field.mulraw(c, out)
    remainder = ''.join(chr(b) for b in reversed(remainder))
    return remainder if remainder_only else message+remainder

print rsencode_lfsr1(f256, msg, g[:-1], remainder_only=True).encode('hex')
print "calculated earlier:"
print remainder.encode('hex')
552ca3b464003a52c45011f46e0fea9b
calculated earlier:
552ca3b464003a52c45011f46e0fea9b

那样,但我们仍然必须在符号字段中处理乘法,这对嵌入式处理器来说是令人讨厌和繁琐的。有两种表驱动方法使这一步更容易,将有限的字段内容缩小为仅限初始化步骤,或者如果您的设计时间可以在设计时完成的东西’愿意在源代码中生成一个表。 (例如,使用Python在C中生成表系数。)

Also we have that call to np.roll(), which is too high-level for a language like C, so let’从这种低级表驱动方法看一些东西。

一个表方法是采用发电机多项式并将其乘以256个可能的系数中的每一个,因此我们得到256的表× \( n-k \) bytes.

def rsencode_lfsr2_table(field, gtaps):
    """
    Create a table for Reed-Solomon encoding using a field
    This time the taps are in *descending* order
    """
    return [[field.mulraw(b,c) for c in gtaps] for b in xrange(256)]

def rsencode_lfsr2(table, message, remainder_only=False):
    """ encode a message in Reed-Solomon using LFSR
    table: table[i][j] is the multiplication of 
             generator coefficient of x^{n-k-1-j} by incoming byte i
    message: string of bytes
    remainder_only: whether to return the remainder only
    """
    glength = len(table[0])
    remainder = [0]*glength
    for char in message + '\0'*glength:
        out = remainder[0]
        table_row = table[out]
        for k in xrange(glength-1):
            remainder[k] = remainder[k+1] ^ table_row[k]
        remainder[glength-1] = ord(char) ^ table_row[glength-1]
    remainder = ''.join(chr(b) for b in remainder)
    return remainder if remainder_only else message+remainder

table = rsencode_lfsr2_table(f256, g[-2::-1])
print "table:"
for k, row in enumerate(table):
    print "%02x: %s" % (k, ' '.join('%02x' % c for c in row))
table:
00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01: 3b 0d 68 bd 44 d1 1e 08 a3 41 29 e5 62 32 24 3b
02: 76 1a d0 67 88 bf 3c 10 5b 82 52 d7 c4 64 48 76
03: 4d 17 b8 da cc 6e 22 18 f8 c3 7b 32 a6 56 6c 4d
04: ec 34 bd ce 0d 63 78 20 b6 19 a4 b3 95 c8 90 ec
05: d7 39 d5 73 49 b2 66 28 15 58 8d 56 f7 fa b4 d7
06: 9a 2e 6d a9 85 dc 44 30 ed 9b f6 64 51 ac d8 9a
07: a1 23 05 14 c1 0d 5a 38 4e da df 81 33 9e fc a1
08: c5 68 67 81 1a c6 f0 40 71 32 55 7b 37 8d 3d c5
09: fe 65 0f 3c 5e 17 ee 48 d2 73 7c 9e 55 bf 19 fe
0a: b3 72 b7 e6 92 79 cc 50 2a b0 07 ac f3 e9 75 b3
0b: 88 7f df 5b d6 a8 d2 58 89 f1 2e 49 91 db 51 88
0c: 29 5c da 4f 17 a5 88 60 c7 2b f1 c8 a2 45 ad 29
0d: 12 51 b2 f2 53 74 96 68 64 6a d8 2d c0 77 89 12
0e: 5f 46 0a 28 9f 1a b4 70 9c a9 a3 1f 66 21 e5 5f
0f: 64 4b 62 95 db cb aa 78 3f e8 8a fa 04 13 c1 64
10: 97 d0 ce 1f 34 91 fd 80 e2 64 aa f6 6e 07 7a 97
11: ac dd a6 a2 70 40 e3 88 41 25 83 13 0c 35 5e ac
12: e1 ca 1e 78 bc 2e c1 90 b9 e6 f8 21 aa 63 32 e1
13: da c7 76 c5 f8 ff df 98 1a a7 d1 c4 c8 51 16 da
14: 7b e4 73 d1 39 f2 85 a0 54 7d 0e 45 fb cf ea 7b
15: 40 e9 1b 6c 7d 23 9b a8 f7 3c 27 a0 99 fd ce 40
16: 0d fe a3 b6 b1 4d b9 b0 0f ff 5c 92 3f ab a2 0d
17: 36 f3 cb 0b f5 9c a7 b8 ac be 75 77 5d 99 86 36
18: 52 b8 a9 9e 2e 57 0d c0 93 56 ff 8d 59 8a 47 52
19: 69 b5 c1 23 6a 86 13 c8 30 17 d6 68 3b b8 63 69
1a: 24 a2 79 f9 a6 e8 31 d0 c8 d4 ad 5a 9d ee 0f 24
1b: 1f af 11 44 e2 39 2f d8 6b 95 84 bf ff dc 2b 1f
1c: be 8c 14 50 23 34 75 e0 25 4f 5b 3e cc 42 d7 be
1d: 85 81 7c ed 67 e5 6b e8 86 0e 72 db ae 70 f3 85
1e: c8 96 c4 37 ab 8b 49 f0 7e cd 09 e9 08 26 9f c8
1f: f3 9b ac 8a ef 5a 57 f8 dd 8c 20 0c 6a 14 bb f3
20: 33 bd 81 3e 68 3f e7 1d d9 c8 49 f1 dc 0e f4 33
21: 08 b0 e9 83 2c ee f9 15 7a 89 60 14 be 3c d0 08
22: 45 a7 51 59 e0 80 db 0d 82 4a 1b 26 18 6a bc 45
23: 7e aa 39 e4 a4 51 c5 05 21 0b 32 c3 7a 58 98 7e
24: df 89 3c f0 65 5c 9f 3d 6f d1 ed 42 49 c6 64 df
25: e4 84 54 4d 21 8d 81 35 cc 90 c4 a7 2b f4 40 e4
26: a9 93 ec 97 ed e3 a3 2d 34 53 bf 95 8d a2 2c a9
27: 92 9e 84 2a a9 32 bd 25 97 12 96 70 ef 90 08 92
28: f6 d5 e6 bf 72 f9 17 5d a8 fa 1c 8a eb 83 c9 f6
29: cd d8 8e 02 36 28 09 55 0b bb 35 6f 89 b1 ed cd
2a: 80 cf 36 d8 fa 46 2b 4d f3 78 4e 5d 2f e7 81 80
2b: bb c2 5e 65 be 97 35 45 50 39 67 b8 4d d5 a5 bb
2c: 1a e1 5b 71 7f 9a 6f 7d 1e e3 b8 39 7e 4b 59 1a
2d: 21 ec 33 cc 3b 4b 71 75 bd a2 91 dc 1c 79 7d 21
2e: 6c fb 8b 16 f7 25 53 6d 45 61 ea ee ba 2f 11 6c
2f: 57 f6 e3 ab b3 f4 4d 65 e6 20 c3 0b d8 1d 35 57
30: a4 6d 4f 21 5c ae 1a 9d 3b ac e3 07 b2 09 8e a4
31: 9f 60 27 9c 18 7f 04 95 98 ed ca e2 d0 3b aa 9f
32: d2 77 9f 46 d4 11 26 8d 60 2e b1 d0 76 6d c6 d2
33: e9 7a f7 fb 90 c0 38 85 c3 6f 98 35 14 5f e2 e9
34: 48 59 f2 ef 51 cd 62 bd 8d b5 47 b4 27 c1 1e 48
35: 73 54 9a 52 15 1c 7c b5 2e f4 6e 51 45 f3 3a 73
36: 3e 43 22 88 d9 72 5e ad d6 37 15 63 e3 a5 56 3e
37: 05 4e 4a 35 9d a3 40 a5 75 76 3c 86 81 97 72 05
38: 61 05 28 a0 46 68 ea dd 4a 9e b6 7c 85 84 b3 61
39: 5a 08 40 1d 02 b9 f4 d5 e9 df 9f 99 e7 b6 97 5a
3a: 17 1f f8 c7 ce d7 d6 cd 11 1c e4 ab 41 e0 fb 17
3b: 2c 12 90 7a 8a 06 c8 c5 b2 5d cd 4e 23 d2 df 2c
3c: 8d 31 95 6e 4b 0b 92 fd fc 87 12 cf 10 4c 23 8d
3d: b6 3c fd d3 0f da 8c f5 5f c6 3b 2a 72 7e 07 b6
3e: fb 2b 45 09 c3 b4 ae ed a7 05 40 18 d4 28 6b fb
3f: c0 26 2d b4 87 65 b0 e5 04 44 69 fd b6 1a 4f c0
40: 66 67 1f 7c d0 7e d3 3a af 8d 92 ff a5 1c f5 66
41: 5d 6a 77 c1 94 af cd 32 0c cc bb 1a c7 2e d1 5d
42: 10 7d cf 1b 58 c1 ef 2a f4 0f c0 28 61 78 bd 10
43: 2b 70 a7 a6 1c 10 f1 22 57 4e e9 cd 03 4a 99 2b
44: 8a 53 a2 b2 dd 1d ab 1a 19 94 36 4c 30 d4 65 8a
45: b1 5e ca 0f 99 cc b5 12 ba d5 1f a9 52 e6 41 b1
46: fc 49 72 d5 55 a2 97 0a 42 16 64 9b f4 b0 2d fc
47: c7 44 1a 68 11 73 89 02 e1 57 4d 7e 96 82 09 c7
48: a3 0f 78 fd ca b8 23 7a de bf c7 84 92 91 c8 a3
49: 98 02 10 40 8e 69 3d 72 7d fe ee 61 f0 a3 ec 98
4a: d5 15 a8 9a 42 07 1f 6a 85 3d 95 53 56 f5 80 d5
4b: ee 18 c0 27 06 d6 01 62 26 7c bc b6 34 c7 a4 ee
4c: 4f 3b c5 33 c7 db 5b 5a 68 a6 63 37 07 59 58 4f
4d: 74 36 ad 8e 83 0a 45 52 cb e7 4a d2 65 6b 7c 74
4e: 39 21 15 54 4f 64 67 4a 33 24 31 e0 c3 3d 10 39
4f: 02 2c 7d e9 0b b5 79 42 90 65 18 05 a1 0f 34 02
50: f1 b7 d1 63 e4 ef 2e ba 4d e9 38 09 cb 1b 8f f1
51: ca ba b9 de a0 3e 30 b2 ee a8 11 ec a9 29 ab ca
52: 87 ad 01 04 6c 50 12 aa 16 6b 6a de 0f 7f c7 87
53: bc a0 69 b9 28 81 0c a2 b5 2a 43 3b 6d 4d e3 bc
54: 1d 83 6c ad e9 8c 56 9a fb f0 9c ba 5e d3 1f 1d
55: 26 8e 04 10 ad 5d 48 92 58 b1 b5 5f 3c e1 3b 26
56: 6b 99 bc ca 61 33 6a 8a a0 72 ce 6d 9a b7 57 6b
57: 50 94 d4 77 25 e2 74 82 03 33 e7 88 f8 85 73 50
58: 34 df b6 e2 fe 29 de fa 3c db 6d 72 fc 96 b2 34
59: 0f d2 de 5f ba f8 c0 f2 9f 9a 44 97 9e a4 96 0f
5a: 42 c5 66 85 76 96 e2 ea 67 59 3f a5 38 f2 fa 42
5b: 79 c8 0e 38 32 47 fc e2 c4 18 16 40 5a c0 de 79
5c: d8 eb 0b 2c f3 4a a6 da 8a c2 c9 c1 69 5e 22 d8
5d: e3 e6 63 91 b7 9b b8 d2 29 83 e0 24 0b 6c 06 e3
5e: ae f1 db 4b 7b f5 9a ca d1 40 9b 16 ad 3a 6a ae
5f: 95 fc b3 f6 3f 24 84 c2 72 01 b2 f3 cf 08 4e 95
60: 55 da 9e 42 b8 41 34 27 76 45 db 0e 79 12 01 55
61: 6e d7 f6 ff fc 90 2a 2f d5 04 f2 eb 1b 20 25 6e
62: 23 c0 4e 25 30 fe 08 37 2d c7 89 d9 bd 76 49 23
63: 18 cd 26 98 74 2f 16 3f 8e 86 a0 3c df 44 6d 18
64: b9 ee 23 8c b5 22 4c 07 c0 5c 7f bd ec da 91 b9
65: 82 e3 4b 31 f1 f3 52 0f 63 1d 56 58 8e e8 b5 82
66: cf f4 f3 eb 3d 9d 70 17 9b de 2d 6a 28 be d9 cf
67: f4 f9 9b 56 79 4c 6e 1f 38 9f 04 8f 4a 8c fd f4
68: 90 b2 f9 c3 a2 87 c4 67 07 77 8e 75 4e 9f 3c 90
69: ab bf 91 7e e6 56 da 6f a4 36 a7 90 2c ad 18 ab
6a: e6 a8 29 a4 2a 38 f8 77 5c f5 dc a2 8a fb 74 e6
6b: dd a5 41 19 6e e9 e6 7f ff b4 f5 47 e8 c9 50 dd
6c: 7c 86 44 0d af e4 bc 47 b1 6e 2a c6 db 57 ac 7c
6d: 47 8b 2c b0 eb 35 a2 4f 12 2f 03 23 b9 65 88 47
6e: 0a 9c 94 6a 27 5b 80 57 ea ec 78 11 1f 33 e4 0a
6f: 31 91 fc d7 63 8a 9e 5f 49 ad 51 f4 7d 01 c0 31
70: c2 0a 50 5d 8c d0 c9 a7 94 21 71 f8 17 15 7b c2
71: f9 07 38 e0 c8 01 d7 af 37 60 58 1d 75 27 5f f9
72: b4 10 80 3a 04 6f f5 b7 cf a3 23 2f d3 71 33 b4
73: 8f 1d e8 87 40 be eb bf 6c e2 0a ca b1 43 17 8f
74: 2e 3e ed 93 81 b3 b1 87 22 38 d5 4b 82 dd eb 2e
75: 15 33 85 2e c5 62 af 8f 81 79 fc ae e0 ef cf 15
76: 58 24 3d f4 09 0c 8d 97 79 ba 87 9c 46 b9 a3 58
77: 63 29 55 49 4d dd 93 9f da fb ae 79 24 8b 87 63
78: 07 62 37 dc 96 16 39 e7 e5 13 24 83 20 98 46 07
79: 3c 6f 5f 61 d2 c7 27 ef 46 52 0d 66 42 aa 62 3c
7a: 71 78 e7 bb 1e a9 05 f7 be 91 76 54 e4 fc 0e 71
7b: 4a 75 8f 06 5a 78 1b ff 1d d0 5f b1 86 ce 2a 4a
7c: eb 56 8a 12 9b 75 41 c7 53 0a 80 30 b5 50 d6 eb
7d: d0 5b e2 af df a4 5f cf f0 4b a9 d5 d7 62 f2 d0
7e: 9d 4c 5a 75 13 ca 7d d7 08 88 d2 e7 71 34 9e 9d
7f: a6 41 32 c8 57 1b 63 df ab c9 fb 02 13 06 ba a6
80: cc ce 3e f8 bd fc bb 74 43 07 39 e3 57 38 f7 cc
81: f7 c3 56 45 f9 2d a5 7c e0 46 10 06 35 0a d3 f7
82: ba d4 ee 9f 35 43 87 64 18 85 6b 34 93 5c bf ba
83: 81 d9 86 22 71 92 99 6c bb c4 42 d1 f1 6e 9b 81
84: 20 fa 83 36 b0 9f c3 54 f5 1e 9d 50 c2 f0 67 20
85: 1b f7 eb 8b f4 4e dd 5c 56 5f b4 b5 a0 c2 43 1b
86: 56 e0 53 51 38 20 ff 44 ae 9c cf 87 06 94 2f 56
87: 6d ed 3b ec 7c f1 e1 4c 0d dd e6 62 64 a6 0b 6d
88: 09 a6 59 79 a7 3a 4b 34 32 35 6c 98 60 b5 ca 09
89: 32 ab 31 c4 e3 eb 55 3c 91 74 45 7d 02 87 ee 32
8a: 7f bc 89 1e 2f 85 77 24 69 b7 3e 4f a4 d1 82 7f
8b: 44 b1 e1 a3 6b 54 69 2c ca f6 17 aa c6 e3 a6 44
8c: e5 92 e4 b7 aa 59 33 14 84 2c c8 2b f5 7d 5a e5
8d: de 9f 8c 0a ee 88 2d 1c 27 6d e1 ce 97 4f 7e de
8e: 93 88 34 d0 22 e6 0f 04 df ae 9a fc 31 19 12 93
8f: a8 85 5c 6d 66 37 11 0c 7c ef b3 19 53 2b 36 a8
90: 5b 1e f0 e7 89 6d 46 f4 a1 63 93 15 39 3f 8d 5b
91: 60 13 98 5a cd bc 58 fc 02 22 ba f0 5b 0d a9 60
92: 2d 04 20 80 01 d2 7a e4 fa e1 c1 c2 fd 5b c5 2d
93: 16 09 48 3d 45 03 64 ec 59 a0 e8 27 9f 69 e1 16
94: b7 2a 4d 29 84 0e 3e d4 17 7a 37 a6 ac f7 1d b7
95: 8c 27 25 94 c0 df 20 dc b4 3b 1e 43 ce c5 39 8c
96: c1 30 9d 4e 0c b1 02 c4 4c f8 65 71 68 93 55 c1
97: fa 3d f5 f3 48 60 1c cc ef b9 4c 94 0a a1 71 fa
98: 9e 76 97 66 93 ab b6 b4 d0 51 c6 6e 0e b2 b0 9e
99: a5 7b ff db d7 7a a8 bc 73 10 ef 8b 6c 80 94 a5
9a: e8 6c 47 01 1b 14 8a a4 8b d3 94 b9 ca d6 f8 e8
9b: d3 61 2f bc 5f c5 94 ac 28 92 bd 5c a8 e4 dc d3
9c: 72 42 2a a8 9e c8 ce 94 66 48 62 dd 9b 7a 20 72
9d: 49 4f 42 15 da 19 d0 9c c5 09 4b 38 f9 48 04 49
9e: 04 58 fa cf 16 77 f2 84 3d ca 30 0a 5f 1e 68 04
9f: 3f 55 92 72 52 a6 ec 8c 9e 8b 19 ef 3d 2c 4c 3f
a0: ff 73 bf c6 d5 c3 5c 69 9a cf 70 12 8b 36 03 ff
a1: c4 7e d7 7b 91 12 42 61 39 8e 59 f7 e9 04 27 c4
a2: 89 69 6f a1 5d 7c 60 79 c1 4d 22 c5 4f 52 4b 89
a3: b2 64 07 1c 19 ad 7e 71 62 0c 0b 20 2d 60 6f b2
a4: 13 47 02 08 d8 a0 24 49 2c d6 d4 a1 1e fe 93 13
a5: 28 4a 6a b5 9c 71 3a 41 8f 97 fd 44 7c cc b7 28
a6: 65 5d d2 6f 50 1f 18 59 77 54 86 76 da 9a db 65
a7: 5e 50 ba d2 14 ce 06 51 d4 15 af 93 b8 a8 ff 5e
a8: 3a 1b d8 47 cf 05 ac 29 eb fd 25 69 bc bb 3e 3a
a9: 01 16 b0 fa 8b d4 b2 21 48 bc 0c 8c de 89 1a 01
aa: 4c 01 08 20 47 ba 90 39 b0 7f 77 be 78 df 76 4c
ab: 77 0c 60 9d 03 6b 8e 31 13 3e 5e 5b 1a ed 52 77
ac: d6 2f 65 89 c2 66 d4 09 5d e4 81 da 29 73 ae d6
ad: ed 22 0d 34 86 b7 ca 01 fe a5 a8 3f 4b 41 8a ed
ae: a0 35 b5 ee 4a d9 e8 19 06 66 d3 0d ed 17 e6 a0
af: 9b 38 dd 53 0e 08 f6 11 a5 27 fa e8 8f 25 c2 9b
b0: 68 a3 71 d9 e1 52 a1 e9 78 ab da e4 e5 31 79 68
b1: 53 ae 19 64 a5 83 bf e1 db ea f3 01 87 03 5d 53
b2: 1e b9 a1 be 69 ed 9d f9 23 29 88 33 21 55 31 1e
b3: 25 b4 c9 03 2d 3c 83 f1 80 68 a1 d6 43 67 15 25
b4: 84 97 cc 17 ec 31 d9 c9 ce b2 7e 57 70 f9 e9 84
b5: bf 9a a4 aa a8 e0 c7 c1 6d f3 57 b2 12 cb cd bf
b6: f2 8d 1c 70 64 8e e5 d9 95 30 2c 80 b4 9d a1 f2
b7: c9 80 74 cd 20 5f fb d1 36 71 05 65 d6 af 85 c9
b8: ad cb 16 58 fb 94 51 a9 09 99 8f 9f d2 bc 44 ad
b9: 96 c6 7e e5 bf 45 4f a1 aa d8 a6 7a b0 8e 60 96
ba: db d1 c6 3f 73 2b 6d b9 52 1b dd 48 16 d8 0c db
bb: e0 dc ae 82 37 fa 73 b1 f1 5a f4 ad 74 ea 28 e0
bc: 41 ff ab 96 f6 f7 29 89 bf 80 2b 2c 47 74 d4 41
bd: 7a f2 c3 2b b2 26 37 81 1c c1 02 c9 25 46 f0 7a
be: 37 e5 7b f1 7e 48 15 99 e4 02 79 fb 83 10 9c 37
bf: 0c e8 13 4c 3a 99 0b 91 47 43 50 1e e1 22 b8 0c
c0: aa a9 21 84 6d 82 68 4e ec 8a ab 1c f2 24 02 aa
c1: 91 a4 49 39 29 53 76 46 4f cb 82 f9 90 16 26 91
c2: dc b3 f1 e3 e5 3d 54 5e b7 08 f9 cb 36 40 4a dc
c3: e7 be 99 5e a1 ec 4a 56 14 49 d0 2e 54 72 6e e7
c4: 46 9d 9c 4a 60 e1 10 6e 5a 93 0f af 67 ec 92 46
c5: 7d 90 f4 f7 24 30 0e 66 f9 d2 26 4a 05 de b6 7d
c6: 30 87 4c 2d e8 5e 2c 7e 01 11 5d 78 a3 88 da 30
c7: 0b 8a 24 90 ac 8f 32 76 a2 50 74 9d c1 ba fe 0b
c8: 6f c1 46 05 77 44 98 0e 9d b8 fe 67 c5 a9 3f 6f
c9: 54 cc 2e b8 33 95 86 06 3e f9 d7 82 a7 9b 1b 54
ca: 19 db 96 62 ff fb a4 1e c6 3a ac b0 01 cd 77 19
cb: 22 d6 fe df bb 2a ba 16 65 7b 85 55 63 ff 53 22
cc: 83 f5 fb cb 7a 27 e0 2e 2b a1 5a d4 50 61 af 83
cd: b8 f8 93 76 3e f6 fe 26 88 e0 73 31 32 53 8b b8
ce: f5 ef 2b ac f2 98 dc 3e 70 23 08 03 94 05 e7 f5
cf: ce e2 43 11 b6 49 c2 36 d3 62 21 e6 f6 37 c3 ce
d0: 3d 79 ef 9b 59 13 95 ce 0e ee 01 ea 9c 23 78 3d
d1: 06 74 87 26 1d c2 8b c6 ad af 28 0f fe 11 5c 06
d2: 4b 63 3f fc d1 ac a9 de 55 6c 53 3d 58 47 30 4b
d3: 70 6e 57 41 95 7d b7 d6 f6 2d 7a d8 3a 75 14 70
d4: d1 4d 52 55 54 70 ed ee b8 f7 a5 59 09 eb e8 d1
d5: ea 40 3a e8 10 a1 f3 e6 1b b6 8c bc 6b d9 cc ea
d6: a7 57 82 32 dc cf d1 fe e3 75 f7 8e cd 8f a0 a7
d7: 9c 5a ea 8f 98 1e cf f6 40 34 de 6b af bd 84 9c
d8: f8 11 88 1a 43 d5 65 8e 7f dc 54 91 ab ae 45 f8
d9: c3 1c e0 a7 07 04 7b 86 dc 9d 7d 74 c9 9c 61 c3
da: 8e 0b 58 7d cb 6a 59 9e 24 5e 06 46 6f ca 0d 8e
db: b5 06 30 c0 8f bb 47 96 87 1f 2f a3 0d f8 29 b5
dc: 14 25 35 d4 4e b6 1d ae c9 c5 f0 22 3e 66 d5 14
dd: 2f 28 5d 69 0a 67 03 a6 6a 84 d9 c7 5c 54 f1 2f
de: 62 3f e5 b3 c6 09 21 be 92 47 a2 f5 fa 02 9d 62
df: 59 32 8d 0e 82 d8 3f b6 31 06 8b 10 98 30 b9 59
e0: 99 14 a0 ba 05 bd 8f 53 35 42 e2 ed 2e 2a f6 99
e1: a2 19 c8 07 41 6c 91 5b 96 03 cb 08 4c 18 d2 a2
e2: ef 0e 70 dd 8d 02 b3 43 6e c0 b0 3a ea 4e be ef
e3: d4 03 18 60 c9 d3 ad 4b cd 81 99 df 88 7c 9a d4
e4: 75 20 1d 74 08 de f7 73 83 5b 46 5e bb e2 66 75
e5: 4e 2d 75 c9 4c 0f e9 7b 20 1a 6f bb d9 d0 42 4e
e6: 03 3a cd 13 80 61 cb 63 d8 d9 14 89 7f 86 2e 03
e7: 38 37 a5 ae c4 b0 d5 6b 7b 98 3d 6c 1d b4 0a 38
e8: 5c 7c c7 3b 1f 7b 7f 13 44 70 b7 96 19 a7 cb 5c
e9: 67 71 af 86 5b aa 61 1b e7 31 9e 73 7b 95 ef 67
ea: 2a 66 17 5c 97 c4 43 03 1f f2 e5 41 dd c3 83 2a
eb: 11 6b 7f e1 d3 15 5d 0b bc b3 cc a4 bf f1 a7 11
ec: b0 48 7a f5 12 18 07 33 f2 69 13 25 8c 6f 5b b0
ed: 8b 45 12 48 56 c9 19 3b 51 28 3a c0 ee 5d 7f 8b
ee: c6 52 aa 92 9a a7 3b 23 a9 eb 41 f2 48 0b 13 c6
ef: fd 5f c2 2f de 76 25 2b 0a aa 68 17 2a 39 37 fd
f0: 0e c4 6e a5 31 2c 72 d3 d7 26 48 1b 40 2d 8c 0e
f1: 35 c9 06 18 75 fd 6c db 74 67 61 fe 22 1f a8 35
f2: 78 de be c2 b9 93 4e c3 8c a4 1a cc 84 49 c4 78
f3: 43 d3 d6 7f fd 42 50 cb 2f e5 33 29 e6 7b e0 43
f4: e2 f0 d3 6b 3c 4f 0a f3 61 3f ec a8 d5 e5 1c e2
f5: d9 fd bb d6 78 9e 14 fb c2 7e c5 4d b7 d7 38 d9
f6: 94 ea 03 0c b4 f0 36 e3 3a bd be 7f 11 81 54 94
f7: af e7 6b b1 f0 21 28 eb 99 fc 97 9a 73 b3 70 af
f8: cb ac 09 24 2b ea 82 93 a6 14 1d 60 77 a0 b1 cb
f9: f0 a1 61 99 6f 3b 9c 9b 05 55 34 85 15 92 95 f0
fa: bd b6 d9 43 a3 55 be 83 fd 96 4f b7 b3 c4 f9 bd
fb: 86 bb b1 fe e7 84 a0 8b 5e d7 66 52 d1 f6 dd 86
fc: 27 98 b4 ea 26 89 fa b3 10 0d b9 d3 e2 68 21 27
fd: 1c 95 dc 57 62 58 e4 bb b3 4c 90 36 80 5a 05 1c
fe: 51 82 64 8d ae 36 c6 a3 4b 8f eb 04 26 0c 69 51
ff: 6a 8f 0c 30 ea e7 d8 ab e8 ce c2 e1 44 3e 4d 6a
print rsencode_lfsr2(table, msg, remainder_only=True).encode('hex')
print "calculated earlier:"
print remainder.encode('hex')
552ca3b464003a52c45011f46e0fea9b
calculated earlier:
552ca3b464003a52c45011f46e0fea9b

或者,如果没有生成多项式’S系数为零,我们可以将这些系数代表为字段的生成元素的权力,然后在符号字段中存储\(x ^ n \)的日志和反曲线表。这些都是较小的长度表256表(具有一个伪入口),并且它们在符号字段中执行算术时删除所有依赖项。

def rsencode_lfsr3_table(field_polynomial):
    """
    Create log and antilog tables for Reed-Solomon encoding using a field
    This just uses powers of x^n.
    """
    log = [0] * 256
    antilog = [0] * 256
    c = 1
    for k in xrange(255):
        antilog[k] = c
        log[c] = k
        c <<= 1
        cxor = c ^ field_polynomial
        if cxor < c:
            c = cxor
    return log, antilog

def rsencode_lfsr3(log, antilog, gpowers, message, remainder_only=False):
    """ encode a message in Reed-Solomon using LFSR
    log, antilog: tables for log and antilog: log[x^i] = i, antilog[i] = x^i
    gpowers: generator polynomial coefficients x^i, represented as i, in descending order
    message: string of bytes
    remainder_only: whether to return the remainder only
    """
    glength = len(gpowers)
    remainder = [0]*glength
    for char in message + '\0'*glength:
        out = remainder[0]
        if out == 0:
            # No multiplication. Just shift.
            for k in xrange(glength-1):
                remainder[k] = remainder[k+1]
            remainder[glength-1] = ord(char)
        else:
            outp = log[out]
            for k in xrange(glength-1):
                j = gpowers[k] + outp
                if j >= 255:
                    j -= 255
                remainder[k] = remainder[k+1] ^ antilog[j]
            j = gpowers[glength-1] + outp
            if j >= 255:
                j -= 255
            remainder[glength-1] = ord(char) ^ antilog[j]
    remainder = ''.join(chr(b) for b in remainder)
    return remainder if remainder_only else message+remainder

log, antilog = rsencode_lfsr3_table(f256.coeffs)
for (name, table) in [('log',log),('antilog',antilog)]:
    print "%s:" % name
    for k in xrange(0,256,16):
        print "%03d-%03d: %s" % (k,k+15,
                ' '.join('%02x' % c for c in table[k:k+16]))
log:
000-015: 00 00 01 19 02 32 1a c6 03 df 33 ee 1b 68 c7 4b
016-031: 04 64 e0 0e 34 8d ef 81 1c c1 69 f8 c8 08 4c 71
032-047: 05 8a 65 2f e1 24 0f 21 35 93 8e da f0 12 82 45
048-063: 1d b5 c2 7d 6a 27 f9 b9 c9 9a 09 78 4d e4 72 a6
064-079: 06 bf 8b 62 66 dd 30 fd e2 98 25 b3 10 91 22 88
080-095: 36 d0 94 ce 8f 96 db bd f1 d2 13 5c 83 38 46 40
096-111: 1e 42 b6 a3 c3 48 7e 6e 6b 3a 28 54 fa 85 ba 3d
112-127: ca 5e 9b 9f 0a 15 79 2b 4e d4 e5 ac 73 f3 a7 57
128-143: 07 70 c0 f7 8c 80 63 0d 67 4a de ed 31 c5 fe 18
144-159: e3 a5 99 77 26 b8 b4 7c 11 44 92 d9 23 20 89 2e
160-175: 37 3f d1 5b 95 bc cf cd 90 87 97 b2 dc fc be 61
176-191: f2 56 d3 ab 14 2a 5d 9e 84 3c 39 53 47 6d 41 a2
192-207: 1f 2d 43 d8 b7 7b a4 76 c4 17 49 ec 7f 0c 6f f6
208-223: 6c a1 3b 52 29 9d 55 aa fb 60 86 b1 bb cc 3e 5a
224-239: cb 59 5f b0 9c a9 a0 51 0b f5 16 eb 7a 75 2c d7
240-255: 4f ae d5 e9 e6 e7 ad e8 74 d6 f4 ea a8 50 58 af
antilog:
000-015: 01 02 04 08 10 20 40 80 1d 3a 74 e8 cd 87 13 26
016-031: 4c 98 2d 5a b4 75 ea c9 8f 03 06 0c 18 30 60 c0
032-047: 9d 27 4e 9c 25 4a 94 35 6a d4 b5 77 ee c1 9f 23
048-063: 46 8c 05 0a 14 28 50 a0 5d ba 69 d2 b9 6f de a1
064-079: 5f be 61 c2 99 2f 5e bc 65 ca 89 0f 1e 3c 78 f0
080-095: fd e7 d3 bb 6b d6 b1 7f fe e1 df a3 5b b6 71 e2
096-111: d9 af 43 86 11 22 44 88 0d 1a 34 68 d0 bd 67 ce
112-127: 81 1f 3e 7c f8 ed c7 93 3b 76 ec c5 97 33 66 cc
128-143: 85 17 2e 5c b8 6d da a9 4f 9e 21 42 84 15 2a 54
144-159: a8 4d 9a 29 52 a4 55 aa 49 92 39 72 e4 d5 b7 73
160-175: e6 d1 bf 63 c6 91 3f 7e fc e5 d7 b3 7b f6 f1 ff
176-191: e3 db ab 4b 96 31 62 c4 95 37 6e dc a5 57 ae 41
192-207: 82 19 32 64 c8 8d 07 0e 1c 38 70 e0 dd a7 53 a6
208-223: 51 a2 59 b2 79 f2 f9 ef c3 9b 2b 56 ac 45 8a 09
224-239: 12 24 48 90 3d 7a f4 f5 f7 f3 fb eb cb 8b 0b 16
240-255: 2c 58 b0 7d fa e9 cf 83 1b 36 6c d8 ad 47 8e 00
gpowers = [log[c] for c in g[-2::-1]]
print "generator polynomial coefficient indices: ", gpowers
print "remainder:"
print rsencode_lfsr3(log, antilog, gpowers, msg, remainder_only=True).encode('hex')
print "calculated earlier:"
print remainder.encode('hex')
generator polynomial coefficient indices:  [120, 104, 107, 109, 102, 161, 76, 3, 91, 191, 147, 169, 182, 194, 225, 120]
remainder:
552ca3b464003a52c45011f46e0fea9b
calculated earlier:
552ca3b464003a52c45011f46e0fea9b

那里。它’非常简单;这里最难的事情是确保您以正确的顺序获得系数。你会’想让那些错误的方式。

C实施

我使用XC16和MPLAB X调试器测试了C实现:

""" C code for reed-solomon.c:
/*
 *
 * Copyright 2018 Jason M. Sachs
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdint.h>
#include <stddef.h>
#include <string.h>

/**
 * Reed-Solomon log-table-based implementation
 */
typedef struct {
    uint8_t log[256];
    uint8_t antilog[256];
    uint8_t symbol_polynomial;
    const uint8_t *gpowers;
    uint8_t generator_degree;
} rslogtable_t;


/**
 * Initialize log table
 * 
 * @param ptables - points to log tables
 * @param symbol_polynomial: lower 8 bits of symbol polynomial
 *         (for example, 0x1d for 0x11d = x^8 + x^4 + x^3 + x^2 + 1
 * @param gpowers - points to generator polynomial coefficients,
 *         except for the leading monomial,
 *         in descending order, expressed as powers of x in the symbol field.
 *         This requires all generator polynomial coefficients to be nonzero.
 * @param generator_degree - degree of the generator polynomial,
 *         which is also the length of the gpowers array.
 */
void rslogtable_init(
    rslogtable_t *ptables, 
    uint8_t symbol_polynomial,
    const uint8_t *gpowers,
    uint8_t generator_degree
)
{
    ptables->symbol_polynomial = symbol_polynomial;
    ptables->gpowers = gpowers;
    ptables->generator_degree = generator_degree;
    
    int k;
    uint8_t c = 1;
    for (k = 0; k < 255; ++k)
    {
        ptables->antilog[k] = c;
        ptables->log[c] = k;
        if (c >> 7)
        {
            c <<= 1;
            c ^= symbol_polynomial;
        }
        else
        {
            c <<= 1;
        }
    }
    ptables->log[0] = 0;
    ptables->antilog[255] = 0;
}

/**
 * Encode a message
 * 
 * @param ptables - pointer to table-based lookup info
 * @param pmessage - message to be encoded 
 * @param message_length - length of the message
 * @param premainder - buffer which will be filled in with the remainder.
 *     This must have a size equal to ptables->generator_degree
 */
void rslogtable_encode(
    const rslogtable_t *ptables,
    const uint8_t *pmessage,
    size_t message_length,
    uint8_t *premainder 
)
{
    int i, j, k, n;
    int highbyte;
    const int t2 = ptables->generator_degree;
    const int lastindex = t2-1;
    
    // Zero out the remainder
    for (i = 0; i < t2; ++i)
    {
        premainder[i] = 0;
    }
    
    n = message_length + t2;
    for (i = 0; i < n; ++i)
    {
        uint8_t c = (i < message_length) ? pmessage[i] : 0;
        highbyte = premainder[0];
        if (highbyte == 0)
        {
            // No multiplication, just a simple shift
            for (k = 0; k < lastindex; ++k)
            {
                premainder[k] = premainder[k+1];
            }
            premainder[lastindex] = c;
        }
        else
        {
            uint8_t log_highbyte = ptables->log[highbyte];
            for (k = 0; k < lastindex; ++k)
            {
                j = ptables->gpowers[k] + (int)log_highbyte;
                if (j >= 255)
                    j -= 255;
                premainder[k] = premainder[k+1] ^ ptables->antilog[j];
            }
            j = ptables->gpowers[lastindex] + (int)log_highbyte;
            if (j >= 255)
                j -= 255;
            premainder[lastindex] = c ^ ptables->antilog[j];
        }
    }
}

uint8_t rsremainder[16];
void rslogtable_test(void)
{
    const uint8_t gpowers[] = {
        120, 104, 107, 109, 
        102, 161, 76, 3, 
        91, 191, 147, 169,
        182, 194, 225, 120
    };
    rslogtable_t rsinfo;
    rslogtable_init(&rsinfo, 0x1d, gpowers, 16);
    
    const char message[] = "Ernie, you have a banana in your ear!";
    rslogtable_encode(&rsinfo, (const uint8_t *)message, strlen(message), rsremainder);
    __builtin_nop();
}
""";

它按预期工作:

同样,这只是编码器,但记住这里的不对称:

  • 编码器=便宜的资源有限处理器
  • 解码器= Desktop PC CPU

我们仍然可以进行错误纠正!

这值得么?

编码值得努力吗?

等待。我们一直通过第XV部分并进入这篇文章,你说,和他’s asking 这值得么?!

就是那个’问题。我们得到了一个好处,即能够检测到有时甚至是正确的错误,但它具有成本。我们必须发送额外的信息,并使用部分可用的通信带宽。我们不得不增加一些复杂性。它是净利润吗?

至少有两种方法可以查看这种成本效益的权衡,这取决于约束是什么。

分析成本福利的第一种方法是考虑我们的情况’给定发射机/接收器对,我们控制的所有都是处理发送的位的系统的数字部分。错误纠正代码如芦苇所罗门为我们提供了降低错误率的能力— and we’ll在一分钟内计算出这一点 —仅略微降低传输速率。 A \((255,239)\)Reed-Solomon代码可让我们在每个块中校正高达8个错误,每个块为255个字节,占未编码通道的传输容量的93.7%。编码复杂性成本最小,并且解码成本足够小,以便在高端嵌入式系统或台式计算机中使用Reed-Solomon解码器。

分析成本效益的第二种方式是考虑我们对发射机能量的控制,以及数字编码/解码机制的情况。我们可以通过使用纠错码来降低错误率,或者我们可以增加发射机能量以提高信噪比。这 Shannon-Hartley定理,它说信道容量\(C \ LEQ B \ log_2(1+ \ FRAC {s} {n}),可以帮助我们定量比较这些可能性。标准度量标准是 \(e_b / n_0 \) in decibels, so let’试图在它方面框架。首先,我们必须了解它是什么。

通信中的一个常见假设是一个 添加剂高斯白噪声(AWGN)频道,每个单位带宽具有一定量的噪声\(n_0 \)。这就是示波器让你限制带宽的原因;如果你不’需要它,它会降低噪音水平。在任何情况下,如果我们有一个集成和转储接收器,我们在其中集成了我们的接收信号,每个\(t \)秒我们对集成器值进行采样,逐个划分为\(t \)来计算平均值间隔,并重置积分器,然后接收的噪声可以被建模为高斯随机变量,零均值和方差\(\ sigma_n ^ 2 = \ frac {n_0} {2t} \)。 (这可以从\(\ sigma_n ^ 2 = \ frac {n_0} {2} \ int _ { - \ infty} ^ {\ infty} \ left | h(t)\ light | ^ 2 \,dt \)与\(h(t)\)的集成和转储过滤器是持续时间\(t \)和高度\(1 / t \)的单位矩形脉冲。)

The signal waveform has some energy \( E_b \) per bit. Consider a raw binary pulse signal \( x(t) = \pm A \) for a duration of \( T \) seconds for each bit, with \( +A \) representing a binary 1和\( -A \) representing a binary 0. If we compute \( E_b = \int_{-\infty}^{\infty}\left|x(t)\right|^2\, dt \) we get \( E_b = A^2T \). The integrate-and-dump receiver outputs a 1 if its output sample is positive, and 0 if its output sample is negative. We get a bit flip if the received noise is less than \( -A \) for a transmitted 1 bit, or greater than \( A \) for a transmitted 0 bit. This probability is just the Q函数 在\(q(a / \ sigma_n)= q(\ sqrt {a ^ 2 / \ sigma_n {} ^ 2})= q(\ sqrt {(e_b / t)/(n_0 / 2t)})= q (\ sqrt {2e_b / n_0})。\)

换句话说,如果我们知道比特是如何表示为信号的话,我们可以通过知道\(e_b / n_0 \)来确定比特错误的概率。

让’s graph it!

位错误率计算是我存在的祸根

只需一个快速放在一边:显示位错误率图表的部分是一个可以管理的野兽。大学教师’担心Python代码,或者计算如何工作,除非您要输入此Quagire。否则,只是看看漂亮的照片,以及我对如何解释它们的描述。

正如我所说,让’s graph it!

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
import scipy.stats

def Q(x):
    return scipy.stats.norm.cdf(-x)

def Qinv(x):
    return -scipy.stats.norm.ppf(x)

ebno_db = np.arange(-6,12,0.01)
ebno = 10**(ebno_db/10)
# Decibels are often confusing.
# dB = 10 log10 x  if x is measured in energy
# dB = 20 log10 x  if x is measured in amplitude. 

plt.semilogy(ebno_db, Q(np.sqrt(2*ebno)))
plt.ylabel('Bit error rate',fontsize=12)
plt.xlabel('$E_b/N_0$ (dB)',fontsize=12)
plt.grid('on')
plt.title('Bit error rate for raw binary pulses in AWGN channel\n'
          +'with integrate-and-dump receiver');

如果我们具有12dB的良好健康噪声裕度,那么误码率是\(9.0 \ times 10 ^ {-9} \),或者在1.1亿中的一个错误。同样,这只是使用集成和转储接收器的原始二进制脉冲。更复杂的信号整形技术喜欢 正交幅度调制 可以更好地利用可用频谱。但是’别人为别人写的文章。

具有1的\(e_b / n_0 \)比率(即’S 0DB),误码概率约为0.079,或12英寸浪费。你想要较低的错误率吗?将更多能量放入信号中,或确保在那里’噪音的能量较少。

好的,现在假设我们使用汉明(7,4)代码。让’例如,S说原始误码率为0.01,其发生在大约\(e_b / n_0 \约4.3 \)db处发生。通过汉明(7,4)代码,我们可以纠正一个错误,因此有一些案例需要考虑一个保守的上限,并假设一些最坏情况的条件:

  • 没有收到的位错误。这是\((1-0.01)^ 7 \约0.932065。\)接收器将输出4个正确的输出位,并且解码后没有错误
  • 七个码字位之一中有一个接收的位错误。这是\(7 \ CDOT 0.01 \ CDOT(1-0.01)^ 6 \约0.065904。\)接收器将输出4位正确的输出位,并且解码后没有错误。
  • 有两个或多个接收的位错误。概率是\(\约0.002031)。我们不’T知道接收器会产生多少个正确的输出位;真正的分析很复杂。让’我们只是假设,作为最糟糕的估计,我们’LL不会收到正确的输出位,因此解码后有4个错误。

在这种情况下,输出位中的预期误差数(最差情况)为4×0.002031,每个输出位0.002031。

我们从比特错误率0.01下降到约0.002031,这是好的,对吧?唯一的缺点是发送数据4比特我们必须传输7位,因此每位的传输能量为7/4更高。

如果我们查看128个可能的错误模式的每个组合的每个组合,并且计算所接收的错误计数,我们可以更确切地说是更准确的错误数量。“weighted count”W作为所接收的错误计数的总和,由导致原始和接收错误组合的模式的模式加权:

# stuff from Part XV
def hamming74_decode(codeword, return_syndrome=False):
    qr = GF2QuotientRing(0b1011)
    beta = 0b111
    syndrome = 0
    bitpos = 1 << 7
    # clock in the codeword, then three zero bits
    for k in xrange(10):
        syndrome = qr.lshiftraw1(syndrome)
        if codeword & bitpos:
            syndrome ^= 1
        bitpos >>= 1
    data = codeword & 0xf
    if syndrome == 0:
        error_pos=None
    else:
        # there are easier ways of computing a correction
        # to the data bits (I'm just not familiar with them)
        error_pos=qr.log(qr.mulraw(syndrome,beta))
        if error_pos < 4:
            data ^= (1<<error_pos)
    if return_syndrome:
        return data, syndrome, error_pos
    else:
        return data
    
# Find syndromes and error positions for basis vectors (all 1)
# and construct syndromes for all 128 possible values using linearity

syndrome_to_correction_mask = np.zeros(8, dtype=int)
patterns = np.arange(128)
syndromes = np.zeros(128, dtype=int)
bit_counts = np.zeros(128, dtype=int)
for i in xrange(7):
    error_pattern = 1<<i
    data, syndrome, error_pos = hamming74_decode(error_pattern, return_syndrome=True)
    assert error_pos == i
    syndrome_to_correction_mask[syndrome] = error_pattern
    bitmatch = (patterns & error_pattern) != 0
    syndromes[bitmatch] ^= syndrome
    bit_counts[bitmatch] += 1
    
correction_mask = syndrome_to_correction_mask[syndromes[patterns]]
corrected_patterns = patterns ^ correction_mask
received_error_count = bit_counts[corrected_patterns & 0xF] 
# determine the number of errors in the corrected data bits

H74_table = pd.DataFrame(dict(pattern=patterns,
                       raw_error_count=bit_counts,
                       correction_mask=correction_mask,
                       corrected_pattern=corrected_patterns,
                       received_error_count=received_error_count
                       ),
                  columns=['pattern',
                        'correction_mask','corrected_pattern',
                        'raw_error_count','received_error_count'])
H74_stats = (H74_table
          .groupby(['raw_error_count','received_error_count'])
          .agg({'pattern': 'count'})
          .rename(columns={'pattern':'count'}))

H74_stats
数数
raw_error_count. 收到_error_count.
0 0 1
1 0 7
2 1 9
2 9
3 3
3 1 7
2 15
3 13
4 1 13
2 15
3 7
5 1 3
2 9
3 9
6 4 7
7 4 1
H74a = H74_stats.reset_index()
H74b_data = [
    ('raw_error_count',H74a['raw_error_count']),
    ('weighted_count',)
]
H74b = ((H74a['received_error_count']*H74a['count'])
        .groupby(H74a['raw_error_count'])
        .agg({'weighted_count':'sum'}))
H74b
加权_count.
raw_error_count.
0 0
1 0
2 36
3 76
4 64
5 48
6 28
7 4

这然后让我们计算净错误率作为\((w_0(1-p)^ 7 + w_1 p(1-p)^ 6 + w_2 p ^ 2(1-p)^ 5 + \ ldots + w_6p ^ 6 (1-P)+ w_7p ^ 7)/ 4 \):

W = H74b['weighted_count']
p = 0.01
r = sum(W[i]*(1-p)**(7-i)*p**i for i in xrange(8))/4
r
0.00087429879999999997

更好!

现在让步’S再次查看该误码率图,这次使用未编码数据的曲线,以及表示使用汉明(7,4)代码的另一曲线。

# Helper functions for analyzing small Hamming codes (7,4) and (15,11)
# and Reed-Solomon(255,k) codes

import scipy.misc
import scipy.stats

class HammingAnalyzer(object):
    def __init__(self, poly):
        self.qr = GF2QuotientRing(poly)
        self.nparity = self.qr.degree
        self.n = (1 << self.nparity) - 1
        self.k = self.n - self.nparity
        self.beta = self._calculate_beta()
        assert self.decode(1<<self.k) == 0
        self.W = None
    def _calculate_beta(self):
        syndrome_msb = self.calculate_syndrome(1<<self.k)
        msb_pattern = self.qr.lshiftraw(1, self.k)
        beta = self.qr.divraw(msb_pattern, syndrome_msb)
        # Beta is calculated such that beta * syndrome(M) = M
        # for M = a codeword of all zeros with bit k = 1
        # (all data bits are zero)
        return beta
    def calculate_syndrome(self, codeword):
        syndrome = 0
        bitpos = 1 << self.n
        # clock in the codeword, then enough bits to cover the parity bits
        for _ in xrange(self.n + self.nparity):
            syndrome = self.qr.lshiftraw1(syndrome)
            if codeword & bitpos:
                syndrome ^= 1
            bitpos >>= 1
        return syndrome
    def decode(self, codeword, return_syndrome=False):
        syndrome = self.calculate_syndrome(codeword)
        data = codeword & ((1<<self.k) - 1)
        if syndrome == 0:
            error_pos=None
        else:
            # there are easier ways of computing a correction
            # to the data bits (I'm just not familiar with them)
            error_pos=self.qr.log(self.qr.mulraw(syndrome,self.beta))
            if error_pos < self.k:
                data ^= (1<<error_pos)
        if return_syndrome:
            return data, syndrome, error_pos
        else:
            return data    
    def analyze_patterns(self):
        data_mask = (1<<self.k) - 1
        syndrome_to_correction_mask = np.zeros(1<<self.nparity, dtype=int)
        npatterns = (1<<self.n)
        patterns = np.arange(npatterns)
        syndromes = np.zeros(npatterns, dtype=int)
        bit_counts = np.zeros(npatterns, dtype=int)
        for i in xrange(self.n):
            error_pattern = 1<<i
            data, syndrome, error_pos = self.decode(error_pattern, return_syndrome=True)
            assert error_pos == i
            syndrome_to_correction_mask[syndrome] = error_pattern
            bitmatch = (patterns & error_pattern) != 0
            syndromes[bitmatch] ^= syndrome
            bit_counts[bitmatch] += 1

        correction_mask = syndrome_to_correction_mask[syndromes[patterns]]
        corrected_patterns = patterns ^ correction_mask
        received_error_count = bit_counts[corrected_patterns & data_mask] 
        # determine the number of errors in the corrected data bits

        return pd.DataFrame(dict(pattern=patterns,
                           raw_error_count=bit_counts,
                           correction_mask=correction_mask,
                           corrected_pattern=corrected_patterns,
                           received_error_count=received_error_count
                           ),
                      columns=['pattern',
                            'correction_mask','corrected_pattern',
                            'raw_error_count','received_error_count'])
    def analyze_stats(self):
        stats = (self.analyze_patterns()
                  .groupby(['raw_error_count','received_error_count'])
                  .agg({'pattern': 'count'})
                  .rename(columns={'pattern':'count'}))
        Ha = stats.reset_index()
        Hb = ((Ha['received_error_count']*Ha['count'])
        .groupby(Ha['raw_error_count'])
        .agg({'weighted_count':'sum'}))
        return Hb
    def bit_error_rate(self, p, **args):
        """ calculate data error rate given raw bit error rate p """
        if self.W is None:
            self.W = self.analyze_stats()['weighted_count']
        return sum(self.W[i]*(1-p)**(self.n-i)*p**i for i in xrange(self.n+1))/self.k
    @property
    def codename(self):
        return 'Hamming (%d,%d)' % (self.n, self.k)
    @property
    def rate(self):
        return 1.0*self.k/self.n
    
class ReedSolomonAnalyzer(object):
    def __init__(self, n, k):
        self.n = n
        self.k = k
        self.t = (n-k)//2
    def bit_error_rate(self, p, **args):
        r = 0
        n = self.n
        ptotal = 0
        # Probability of byte errors
        # = 1-(1-p)^8
        # but we need to calculate it more robustly
        # for small values of p
        q = -np.expm1(8*np.log1p(-p))
        qlist = [1.0]
        for v in xrange(n):
            qlist_new = [0]*(v+2)
            qlist_new[0] = qlist[0]*(1-q)
            qlist_new[v+1] = qlist[v]*q
            for i in xrange(1, v+1):
                qlist_new[i] = qlist[i]*(1-q) + qlist[i-1]*q
            qlist = qlist_new
        for v in xrange(n+1):
            # v errors
            w = self.output_error_count(v)
            pv = qlist[v]
            ptotal += pv
            r += w*pv
        # r is the number expected number of data bit errors
        # (can't be worse than 0.5)
        return np.minimum(0.5, r*1.0/self.k/8)
    @property
    def rate(self):
        return 1.0*self.k/self.n
    
class ReedSolomonWorstCaseAnalyzer(ReedSolomonAnalyzer):
    @property
    def codename(self):
        return 'RS (%d,%d) worstcase' % (self.n, self.k)
    def output_error_count(self, v):
        if v <= self.t:
            return 0
        # Pessimistic: if we have over t errors,
        # we correct the wrong t ones.
        # Oh, and it messes up the whole byte.
        return 8*min(self.k, v + self.t)

class ReedSolomonBestCaseAnalyzer(ReedSolomonAnalyzer):
    @property
    def codename(self):
        return 'RS (%d,%d) bestcase' % (self.n, self.k)
    def output_error_count(self, v):
        # Optimistic: We always correct up to t errors.
        # And each of the symbol errors affect only 1 bit.
        return max(0, v-self.t)
    
class ReedSolomonMonteCarloAnalyzer(ReedSolomonAnalyzer):
    def __init__(self, decoder):
        super(ReedSolomonMonteCarloAnalyzer, self).__init__(decoder.n, decoder.k)
        self.decoder = decoder
    @property
    def codename(self):
        return 'RS (%d,%d) Monte Carlo' % (self.n, self.k)
    def _sim(self, v, nsim, audit=False):
        """ 
        Run random samples of decoding v bit errors from the zero codeword 
        
        v:         number of bit errors
        nsim:      number of trials
        audit:     whether to double-check that the 'where' array is correct
                   (this is a temporary variable used to locate errors)
                   
        Returns
        
        success_histogram:  histogram of corrected codewords
        failure_histogram:  histogram of uncorrected codewords
        
        For each trial we determine the number of bit errors ('count') in the
        corrected (or uncorrected) data.
        If the Reed-Solomon decoding succeeds, we add a sample to the success
        histogram, otherwise we add it to the failure histogram.
        
        Example: [4,0,0,5,1,6] indicates 
          4 samples with no bit errors,
          5 samples with 3 bit errors,
          1 sample with 4 bit errors,
          6 samples with 5 bit errors.
        """
        decoder = self.decoder
        n = decoder.n
        k = decoder.k
        success_histogram = np.array([0], int)
        failure_histogram = np.array([0], int)
        failures = 0
        for _ in xrange(nsim):
            # Construct received message
            error_bitpos = np.random.choice(n*8, v, replace=False)
            e = np.zeros((v,n), int)
            e[np.arange(v), error_bitpos//8] = 1 << (error_bitpos & 7)
            e = e.sum(axis=0)

            # Decode received message
            try:
                msg, r = decoder.decode(e, nostrip=True, return_string=False)
                d = np.array(msg, dtype=np.uint8)
    #            d = np.frombuffer(msg, dtype=np.uint8)
                success = True
            except:
                # Decoding error. Just use raw received message.
                d = e[:k]
                success = False
            where = np.array([(d>>j) & 1 for j in xrange(8)])
            count = int(where.sum())
            if success:
                if count >= len(success_histogram):
                    success_histogram.resize(count+1)
                success_histogram[count] += 1
            else:
                # yes this is essentially repeated code
                # but resize() is much easier
                # if we only have 1 object reference
                if count >= len(failure_histogram):
                    failure_histogram.resize(count+1)
                failure_histogram[count] += 1
            if audit:
                d2 = d.copy()
                bitlist,bytelist=where.nonzero()
                for bit,i in zip(bitlist,bytelist):
                    d2[i] ^= 1<<bit
                assert not d2.any()
        return success_histogram, failure_histogram
    def _simulate_errors(self, nsimlist, verbose=False):
        decoder = self.decoder
        t = (decoder.n - decoder.k)//2
        results = []
        for i,nsim in enumerate(nsimlist):
            v = t+i
            if verbose:
                print "RS(%d,%d), %d errors (%d samples)" % (
                    decoder.n, decoder.k, v, nsim)
            results.append(self._sim(v, nsim))
        return results    
    def set_simulation_sample_counts(self, nsimlist):
        self.nsimlist = nsimlist
        return self
    def simulate(self, nsimlist=None, verbose=False):
        if nsimlist is None:
            nsimlist = self.nsimlist
        self.simulation_results = self._simulate_errors(nsimlist, verbose)
        return self
    def bit_error_rate(self, p, pbinom=None, **args):
        # Redone in terms of bit errors
        r = 0
        ptotal = 0
        p = np.atleast_1d(p)
        if pbinom is None:
            ncheck = 500
            pbinom = scipy.stats.binom.pmf(np.atleast_2d(np.arange(ncheck)).T,self.n*8,p)
        for v in xrange(ncheck):
            # v errors
            w = self.output_error_count(v)
            pv = pbinom[v,:]
            ptotal += pv
            r += w*pv
        # r is the number expected number of data bit errors
        # (can't be worse than 0.5)
        return np.minimum(0.5, r*1.0/self.k/8)            
            
    def output_error_count(self, v):
        # Estimate the number of data bit errors for v transmission bit errors.
        t = self.t
        if v <= t:
            return 0
        simr = self.simulation_results
        if v-t < len(simr):
            sh, fh = simr[v-t]
            ns = sum(sh)
            ls = len(sh)
            nf = sum(fh)
            lf = len(fh)
            wt = ((sh*np.arange(ls)).sum()
                 +(fh*np.arange(lf)).sum()) * 1.0 /(ns+nf)
            return wt
        else:
            # dumb estimate: same number of input errors
            return v
ebno_db = np.arange(-6,15,0.01)
ebno = 10**(ebno_db/10)
# Decibels are often confusing.
# dB = 10 log10 x  if x is measured in energy
# dB = 20 log10 x  if x is measured in amplitude. 

def dBE(x):
    return 10*np.log10(x)

# Raw bit error probability
p = Q(np.sqrt(2*ebno))
# Probability of at least 1 error:
# This would be 1 - (1-p)^7
# but it's numerically problematic for low values of p.
pH74_any_error = -np.expm1(7*np.log1p(-p))
pH74_upper_bound = pH74_any_error - 7*p*(1-p)**6
ha74 = HammingAnalyzer(0b1011)
pH74 = ha74.bit_error_rate(p)

plt.semilogy(dBE(ebno), p, label='uncoded')
plt.semilogy(dBE(ebno*7/4), pH74_upper_bound, '--', label='Hamming (7,4) upper bound')
plt.semilogy(dBE(ebno*7/4), pH74, label='Hamming (7,4)')
plt.ylabel('Bit error rate',fontsize=12)
plt.xlabel('$E_b/N_0$ (dB)',fontsize=12)
plt.xlim(-6,14)
plt.xticks(np.arange(-6,15,2))
plt.ylim(1e-12,1)
plt.legend(loc='lower left',fontsize=10)
plt.grid('on')
plt.title('Bit error rate for raw binary pulses in AWGN channel\n'
          +'with integrate-and-dump receiver');

确切的计算和上限都相当接近。

我们可以做一些类似于Reed-Solomon错误率的事情。对于\((n,k)=(255,255-2t)\)reed-solomon代码,具有传输位误差概率\(p \),每个字节的误差概率是\(q = 1 - (1-p) ^ 8 \),并且有几个情况,给定每255字节块的字节错误\(v \):

  • \(v \ Le T \):Reed-Solomon将正确解码它们,解码的字节错误的数量为零。
  • \(V.>t \):超过\(t \)字节错误:这是棘手的部分。
    • 最佳案例估计:我们很幸运,解码器实际上纠正了\(t \)错误,导致\(v-t \)错误。 这是幽默乐观的,但它确实代表了误码率的下限。
    • 最糟糕的估计:我们不幸的是,解码器改变了\(t \)值,这是正确的, 导致\(v + t \)错误。这是幽默悲观的,但它确实代表了一个上限 在误码率。
    • 经验估计:我们实际上可以生成一些涵盖各种错误的随机样本,并看看解码器实际上是什么,然后使用结果推断数据误码率。这将更接近预期的结果,但它可以采用相当多的计算来生成足够的随机样本并运行解码器算法。

好的,现在我们’重新将所有这些联系在一起并显示四个图表:

  • 在传输期间解码数据(纠错后纠错之后)的误码率与原始误码率。这告诉我们错误率降低了多少。
    • 一个图表将显示大规模的错误率。
    • 另一个将以更仔细的详细信息显示相同的数据。
  • 解码数据与\(e_b / n_0 \)的误码率。这让我们通过他们所需的传输能量来比较误码率来实现该错误率。
  • 在分贝中的有效增益,分贝,与解码数据的误码率。
# Codes under analysis:
class RawAnalyzer(object):
    def bit_error_rate(self, p):
        return p
    @property
    def codename(self):
        return 'uncoded'
    @property
    def rate(self):
        return 1

ha74 = HammingAnalyzer(0b1011)
ha1511 = HammingAnalyzer(0b10011)
analyzers = [RawAnalyzer(), ha74, ha1511,
            ReedSolomonWorstCaseAnalyzer(255,253),
            ReedSolomonBestCaseAnalyzer(255,253),
            ReedSolomonMonteCarloAnalyzer(
              rs.RSCoder(255,253,generator=2,prim=0x11d,fcr=0,c_exp=8))
                 .set_simulation_sample_counts([10]
                    +[10000,5000,2000,1000,1000,1000,1000,1000]
                    +[100]*10),
            ReedSolomonWorstCaseAnalyzer(255,251),
            ReedSolomonBestCaseAnalyzer(255,251),
            ReedSolomonMonteCarloAnalyzer(
              rs.RSCoder(255,251,generator=2,prim=0x11d,fcr=0,c_exp=8))
                 .set_simulation_sample_counts([10]
                    +[10000,5000,2000,1000,1000,1000,1000,1000]
                    +[100]*5),
            ReedSolomonWorstCaseAnalyzer(255,247),
            ReedSolomonBestCaseAnalyzer(255,247),
            ReedSolomonMonteCarloAnalyzer(
              rs.RSCoder(255,247,generator=2,prim=0x11d,fcr=0,c_exp=8))
                 .set_simulation_sample_counts([10]
                    +[10000,5000,2000,1000,1000,1000,1000,1000]
                    +[100]*5),
            ReedSolomonWorstCaseAnalyzer(255,239),
            ReedSolomonBestCaseAnalyzer(255,239),
            ReedSolomonMonteCarloAnalyzer(
              rs.RSCoder(255,239,generator=2,prim=0x11d,fcr=0,c_exp=8))
                 .set_simulation_sample_counts([10]
                    +[10000,5000,2000,1000,1000,1000,1000,1000]),
            ReedSolomonWorstCaseAnalyzer(255,223),
            ReedSolomonBestCaseAnalyzer(255,223),
            ReedSolomonMonteCarloAnalyzer(
              rs.RSCoder(255,223,generator=2,prim=0x11d,fcr=0,c_exp=8))
                 .set_simulation_sample_counts([10]
                    +[5000,2000,1000,500,500,500,500,500]),
            ReedSolomonWorstCaseAnalyzer(255,191),
            ReedSolomonBestCaseAnalyzer(255,191),
            ReedSolomonMonteCarloAnalyzer(
              rs.RSCoder(255,191,generator=2,prim=0x11d,fcr=0,c_exp=8))
                 .set_simulation_sample_counts([10]
                    +[2000,1000,500,500,500,500,500,500]),
            ]
styles = ['k','r','g','b--','b--','b','c--','c--','c',
          'm--','m--','m',
          '#88ff00--','#88ff00--','#88ff00',
          '#ff8800--','#ff8800--','#ff8800',
          '#888888--','#888888--','#888888']
# Sigh. This takes a while.
import time

for analyzer in analyzers:
    if hasattr(analyzer, 'simulate'):
        print "RS(%d,%d)" % (analyzer.n, analyzer.k)
        np.random.seed(123)  
        # just to make the Monte Carlo analysis repeatable
        t0 = time.time()
        analyzer.simulate()
        t1 = time.time()
        print "%.2f seconds" % (t1-t0)
RS(255,253)
42.47 seconds
RS(255,251)
44.90 seconds
RS(255,247)
62.66 seconds
RS(255,239)
151.81 seconds
RS(255,223)
197.57 seconds
RS(255,191)
470.05 seconds
ebno_db = np.arange(-6,18,0.02)
ebno = 10**(ebno_db/10)
# Decibels are often confusing.
# dB = 10 log10 x  if x is measured in energy
# dB = 20 log10 x  if x is measured in amplitude. 

# Raw bit error probability
p = Q(np.sqrt(2*ebno))
# Binomial distribution -- this is used by the RS Monte Carlo analyzers
pbinom = scipy.stats.binom.pmf(np.atleast_2d(np.arange(800)).T,255*8,p)

error_rates = [analyzer.bit_error_rate(p) for analyzer in analyzers]
colors = [s[:-2] if s.endswith('--') else s for s in styles]
linestyles = ['--' if s.endswith('--') else '-' for s in styles]
def label_for(analyzer):
    codename = analyzer.codename
    if codename.endswith(' worstcase'):
        return None
    elif codename.endswith(' bestcase'):
        return codename[:-8] + 'bounds'
    else:
        return codename

# 1a and 1b. Raw vs. derived probability

for xscale,yscale in [(1e-12,1e-20),(1e-5,1e-8)]:
    fig = plt.figure(figsize=(7,6))
    ax = fig.add_subplot(1,1,1)
    for i, analyzer in enumerate(analyzers):
        p2 = error_rates[i]
        ax.loglog(p, p2, 
                  color=colors[i], linestyle=linestyles[i], label=label_for(analyzer))
    ax.set_xlabel('Raw bit error rate')
    ax.set_ylabel('Data bit error rate')
    ax.legend(loc='upper left',fontsize=8, labelspacing=0, handlelength=3)
    ax.grid('on')
    ax.set_title('Code effect on data bit error rate')
    ax.set_xlim(xscale,0.5)
    ax.set_ylim(yscale,1)

# 2. Data error rate vs. Eb/N0
fig = plt.figure(figsize=(7,6))
ax = fig.add_subplot(1,1,1)
for i, analyzer in enumerate(analyzers):
    p2 = error_rates[i]
    
    ax.semilogy(dBE(ebno/analyzer.rate), p2, 
                color=colors[i], linestyle=linestyles[i], label=label_for(analyzer))
ax.set_ylabel('Data bit error rate',fontsize=12)
ax.set_xlabel('$E_b/N_0$ (dB)',fontsize=12)
ax.set_xlim(-6,14)
ax.set_xticks(np.arange(-6,15,2))
ax.set_ylim(1e-12,1)
ax.legend(loc='lower left',fontsize=8, labelspacing=0, handlelength=3)
ax.grid('on')
ax.set_title('Data bit error rate for raw binary pulses in AWGN channel\n'
          +'with integrate-and-dump receiver');

# 3. Eb/No gain vs. data error rate
fig = plt.figure(figsize=(7,6))
ax = fig.add_subplot(1,1,1)
for i, analyzer in enumerate(analyzers):
    p2 = error_rates[i]
    ebno_equiv = Qinv(p2)**2/2
    ax.semilogx(p2, dBE(ebno_equiv*analyzer.rate/ebno),
                    color=colors[i], linestyle=linestyles[i], label=label_for(analyzer))
ax.set_xlim(1e-16,0.5)
ax.set_ylim(-8,10)
ax.set_yticks(np.arange(-8,10.1,2))
ax.legend(loc='lower left',fontsize=8, labelspacing=0, handlelength=3)
ax.set_xlabel('Data bit error rate', fontsize=12)
ax.set_ylabel('Gain (dB)', fontsize=12)
ax.grid('on')
ax.set_title('Effective $E_b/N_0$ gain vs data bit error rate');

好的,那我们在这里看看什么?

前两个图表向我们展示了一些事情:

  • 在低通道误码率(=“raw bit error rate”),可以使有效的数据比特错误率降低。例如,在A \(10 ^ {-4} \)通道误码率,汉明(7,4)代码可以将有效的数据钻头错误率降至约\(10 ^ { - 7}), RS(255,239)代码可以将有效的数据误码率降至约\(10 ^ { - 14} \)。要低得多,我们不’不得不牺牲很多带宽来做; Rs(255,239)的码率为239/255≈ 0.9373.

  • 另一方面,在高通道比特误差速率下,有效的数据比特错误率可能不会更好,并且甚至可能比信道数据误码率略高。它平均看起来可能是大约2倍,在低强度簧片簧片的情况下,如Rs(255,253)和Rs(255,251)。

  • 的意思“low” and “high”频道位误差率取决于所使用的编码技术。汉明码很短,所以你仍然看到它们以高达0.2的频道误码率从它们的好处,而RS(255,251)则不得不’T帮助减少错误率,除非您的频道位误差率低于0.0005,而RS(255,223)和RS(255,191)等高稳健性的REED-SOLOMON码工作良好达到约0.01的误差率。

底部两图显示了我们如何在\(e_b / n_0 \)惩罚中对\(e_b / n_0 \)惩罚的减少,我们使用的部分频率位为奇偶校验位的冗余。 (记住,每个比特时间的更长的平均传输时间意味着我们需要每个编码数据位的较大\(e_b \)。)

  • 与未编码的数据相比,汉明代码可以获得0.5-1.5dB的\(e_b / n_0 \),以获得相同的有效数据误码率。

  • Reed-Solomon代码可以在与未编码数据相比的2-10dB的\(e_b / n_0 \)之间获得,以获得相同的有效数据位错误率。

  • 此增益在低通道误码率下工作更好;与未编码的数据相比的优点显示,如果数据位错误率在0.0001至0.01范围以下。芦苇所罗门代码在低于大约0.005个数据钻头错误率的情况下击败汉明码,至少适用于高稳健性代码;德德萨洛蒙代码’t增加了很多冗余,如卢比(255,253),唐’除非数据误码率低于\(10 ^ { - 7} \),否则T击败汉明码。

  • 在高有效的比特错误速率下,编码消息可以具有 更差 在\(e_b / n_0 \)方面的性能对于相同的有效数据位错误率。

  • 无论如何,我们通常需要有效的有效位错误率; \(10 ^ {-6} \)中的东西到\(10 ^ { - 12} \)范围可能是合理的,具体取决于应用程序以及它如何受错误的影响。那些具有可以在检测到的错误的情况下处理重试的其他更高级别层的应用程序通常可以以\(10 ^ {-6} \)甚至更高的比特误差速率消失。另一方面,存储应用程序需要将错误保持低,因为那里’没有释放的可能机制;处理它的唯一方法是添加另一层纠错—这是某些应用中使用的东西。

为什么不使用Reed-Solomon而不是CRC?

所以在这里’一个可能想到的问题。考虑以下两对案例:

  • RS(255,253)与253字节数据包,后跟一个16位CRC
  • RS(255,251)与A 251字节数据包,后跟32位CRC

在每对中,如果使用了查找表的编码器,则存在相同的开销,并且大致相同的编码复杂性和内存要求。检查是否存在传输错误的解码器与编码器具有基本相同的复杂性;它’■只有需要更多CPU马力的错误校正步骤。

CRC可用于检测错误,但不能纠正它们。 REED所罗门代码可用于检测错误,在RS(255,253)的情况下,最多1个字节错误和RS(255,251)的两个字节误差。

那么为什么不应该’我们正在使用芦苇 - 所罗门而不是CRC?

出色地, CRCs are meant for detecting a small number of 少量 保证确定性的错误。 Reed-Solomon码设计用于处理 字节 错误,这意味着根据代码参数,意味着字节或两个突发。因此,所接收的噪音的性质可能对哪个方面具有很大的差异。噪声模型如awgn,如果任何单独的误码的可能性都独立于任何其他,有利于误差检测技术;多点突发常见的噪声模型将有利于芦荟。

但最大的影响是,通过允许纠正纠正,我们放弃了我们的一些检测错误的能力。我以XV的末尾提到了这一点。让’s look at it again.

干扰净nrp nmy

让’我看看我们的虚构 dry imp net 再次编码方案。三个码字— dry, imp, and net —每个单词的三个符号。

这种编码方案具有3的汉明距离,这意味着我们可以检测到最多2个错误或校正最多1个错误。

如果我们不’T做任何纠错,27个可能的消息中只有3个码字,因此我们检测无效消息的能力非常好。我们唯一的方式’re going to misinterpret dry as imp or net is if we happen to get the right kind of error to switch from one valid codeword to another, which needs three simultaneous errors.

另一方面,如果我们决定使用纠错,那么我们突然一直有21条可接受的消息。经过“acceptable” I mean a message that is either correct or correctable. If we get a message like dmp that was originally dry, sorry, we’re going to turn it into imp, because that’s the closest codeword. The only unacceptable messages are nrp, nmy, iey, irt, dmt, and dep, which have a minimum distance of 2 to any codeword.

有一些有限的代码—汉明代码是其中之一—这是有的财产 不可接受的消息。这些都被称为 完美的代码,这意味着任何接收的消息要么是正确的,要么可以纠正到最接近的码字。在这里,我们必须假设错误是不常见的,如果我们确实获得了更多的错误,那么我们就会产生错误的纠正。那好吧。

我看了芦苇所罗门代码一点点。 for \(rs(255,255-2t)\),它看起来像\(t = 1 \)接近完美,\(t = 2 \)isn’要么太远,但在开始添加更多冗余符号时,不可判断的消息的数量比可纠正消息的数量快得多,以及每个码字周围的这些边界—叫汉明球,即使他们’更像n立维超机—覆盖较少和更少的消息空间。这是良好的,至少用于错误检测,因为它意味着我们’即使存在超过\(t \),也可能能够检测到错误。

以下是我用于计算误码率的蒙特卡罗模拟的一些数据 —这次呈现有点不同,显示在存在超过\(t \)错误时会发生的表格。 \(v \)位错误的每个随机误差模式(并且注意到这可能会产生少于\(v \)字节错误,如果在同一字节中发生多个错误)可以有三个结果:

  • CORRECT:错误模式正确解码
  • FAIL:解码步骤失败,所以我们不’t更改收到的消息,收到的错误数保持不变
  • WORSEN:错误模式错误地解码,到不同的码字

The table shows the number of samples nsample. in each case, and the fraction of those samples that fall into the CORRECT, FAIL, and WORSEN outcomes.

rsanalyzers = [analyzer for analyzer in analyzers
               if isinstance(analyzer, ReedSolomonMonteCarloAnalyzer)]
def summarize_simulation_results(sr):
    shist, fhist = sr
    nfail = sum(fhist)
    nsample = nfail + sum(shist)
    ncorrect = shist[0]
    nworsen = sum(shist[1:])
    return (nsample,
            ncorrect*1.0/nsample, 
            nfail*1.0/nsample,
            nworsen*1.0/nsample)
data = [(analyzer.n, analyzer.k, analyzer.t, i+analyzer.t)
       +summarize_simulation_results(sr)
    for analyzer in rsanalyzers
    for i, sr in enumerate(analyzer.simulation_results)
    if i > 0 and i <= 8]
df = pd.DataFrame(data,
            columns='n k t v nsample ncorrect nfail nworsen'.split())
df.set_index(['n','k','t','v'])
nsample. ncorrect. NFail. nworsen.
n k t v
255 253 1 2 10000 0.0029 0.1228 0.8743
3 5000 0.0000 0.0000 1.0000
4 2000 0.0000 0.0490 0.9510
5 1000 0.0000 0.0000 1.0000
6 1000 0.0000 0.0130 0.9870
7 1000 0.0000 0.0000 1.0000
8 1000 0.0000 0.0140 0.9860
9 1000 0.0000 0.0000 1.0000
251 2 3 10000 0.0091 0.4998 0.4911
4 5000 0.0000 0.5036 0.4964
5 2000 0.0000 0.5115 0.4885
6 1000 0.0000 0.4850 0.5150
7 1000 0.0000 0.5110 0.4890
8 1000 0.0000 0.4940 0.5060
9 1000 0.0000 0.5190 0.4810
10 1000 0.0000 0.5070 0.4930
247 4 5 10000 0.0342 0.9288 0.0370
6 5000 0.0004 0.9628 0.0368
7 2000 0.0000 0.9490 0.0510
8 1000 0.0000 0.9650 0.0350
9 1000 0.0000 0.9710 0.0290
10 1000 0.0000 0.9650 0.0350
11 1000 0.0000 0.9710 0.0290
12 1000 0.0000 0.9610 0.0390
239 8 9 10000 0.1213 0.8787 0.0000
10 5000 0.0102 0.9898 0.0000
11 2000 0.0000 1.0000 0.0000
12 1000 0.0000 1.0000 0.0000
13 1000 0.0000 1.0000 0.0000
14 1000 0.0000 1.0000 0.0000
15 1000 0.0000 1.0000 0.0000
16 1000 0.0000 1.0000 0.0000
223 16 17 5000 0.3796 0.6204 0.0000
18 2000 0.0930 0.9070 0.0000
19 1000 0.0200 0.9800 0.0000
20 500 0.0020 0.9980 0.0000
21 500 0.0000 1.0000 0.0000
22 500 0.0000 1.0000 0.0000
23 500 0.0000 1.0000 0.0000
24 500 0.0000 1.0000 0.0000
191 32 33 2000 0.8490 0.1510 0.0000
34 1000 0.5580 0.4420 0.0000
35 500 0.3160 0.6840 0.0000
36 500 0.1700 0.8300 0.0000
37 500 0.0720 0.9280 0.0000
38 500 0.0220 0.9780 0.0000
39 500 0.0080 0.9920 0.0000
40 500 0.0020 0.9980 0.0000

这里有趣的趋势是

  • For RS(255,253), with more than 1 error, the outcome is usually WORSEN. With \( v=2 \) errors we occasionally get a successful correction. Compared to a 16-bit CRC, we risk making errors worse by trying to correct them, so unless we have a system with very low probability of 2 or more errors, we are better off using a 16-bit CRC and not trying to correct errors.
  • For RS(255,251), with more than 2 errors, the outcome is about a 50-50 split between FAILWORSEN. With \( v=3 \) errors we occasionally get a successful correction. Compared to a 32-bit CRC, we risk making errors worse by trying to correct them. It’没有像Rs(255,253)一样糟糕,但是如果有三个或更多次错误,可以让事情变得更糟的50%,使得使用32位CRC更具吸引力。
  • For RS(255,247), with more than 4 errors, the outcome is usually FAIL with a small probability of WORSEN. With \( v=4 \) and \( v=5 \) errors we occasionally get a successful correction. Compared to a 64-bit CRC, now the use of Reed-Solomon becomes much more attractive, at least for packets of length 255 or less.
  • For RS(255,239) and other decoders with higher \( t \) values, the output almost never is WORSEN. Probability of CORRECT becomes greater even though we are beyond the Hamming bound of at most \( t \) correctible errors. Reed-Solomon is very attractive, but the encoding cost starts to get higher.

These probabilities of a WORSEN outcome are fairly close to their theoretical values, which are just the fraction of all possible messages which are within a Hamming distance of \( t \) of a valid codeword.

例如,如果我们有Rs(255,253),那么有效码字的最后两个字节都完全依赖于前面的字节,因此有效码字的分数是\(1/256 ^ 2 = 1/65536 \),以及在那些有效的码字中的1的距离内的所有可能消息的分数是\(\ FRAC {1 + 255 \ times 255} {256 ^ 2} \约0.9922 \)。对于每个码字,我们有一个带有距离0的消息(码字本身)和\(255 \ times 255 \)消息,其中距离1:255个消息字节中的每一个都可以用255个其他值损坏。

或者,如果我们有Rs(255,251),则该分数变为\(\ FRAC {1 + 255 \ times 255 + \ FRAC {255 \ times 254} {2} \ times 255 ^ 2} {256 ^ 4} \ \ \大约0.4903 \ )。

该分数的通式是

$$ \ rho(t)= 256 ^ { - 2t} \ sum \ limits_ {j = 0} ^ t {255 \选择j} 255 ^ j $$

我们可以图表:

def rscoverage(t):
    y = 0
    p = 1.0/256
    x = 1.0
    for k in xrange(t+1):
        u = x
        for j in xrange(2*t-k):
            if j < k:
                u *= 255.0/256
            else:
                u *= p
        y += u
        x *= (255-k)/256.0
        x /= k+1
    return y

tlist = np.arange(1,21)
fig=plt.figure()
ax=fig.add_subplot(1,1,1)
ax.semilogy(tlist,[rscoverage(t) for t in tlist],'.')
ax.set_title('Fraction of messages in $RS(255,255-2t)$\n within distance $t$ of a valid codeword')
ax.set_xlabel('$t$',fontsize=12)
ax.set_ylabel('$\\rho$',fontsize=12)
ax.grid('on')

低端嵌入式系统的甜点可能是使用Reed-Solomon(255,247),其中\(t = 4 \)。可能(255,239),其中\(t = 8 \)。 \(t \)的较小值允许基于表的实现,其中包含最小的CPU时间和内存; \(t \)的较大值降低了错误校正的概率。

但最重要的是真正了解系统中错误的行为:

  • 错误的可能性–运行一些测试,并尝试弄清楚错误的速率而不尝试纠错。每个\ /(10 ^ {6} \)字节的平均误差率为1个错误率的系统是与每个1000字节每1000字节的平均误差率的平均误差率的系统不同的系统。

  • 错误的影响– if you’重新使用音频数据,错误可能听起来很简单“pop”在声音中,这可能不是很大的事。如果您有传感器收集和传输来自一终生实验的数据,并且丢失数据可以进行或打破结果,则纠错编码是相对便宜的保险。

Reed-Solomon代码的真实应用

Reed-Solomon代码现在广泛使用。

我们已经谈到了从天王星和海王星发出的Voyager Space探针中使用Reed-Solomon代码。他们已在最近的NASA任务中取出,例如火星重新协调编码器 Turbo代码,它具有更高的\(e_b / n_0 \)增益。

光盘和DVD使用 交叉交错的芦苇所罗门编码 即使它们显示为影响大量位数的划痕,增加鲁棒性。

宽带数据传输系统如 VDSL2 使用Reed-Solomon代码来降低给定发射器能量的错误率。

我发现最有趣的应用程序是QR码,其中簧片 - 所罗门代码与聪明的方案一起使用,以在广泛的区域上空地分布空间的数据位。这使得它们对严重的数据丢失误差具有强大。

通常,您可以看到像这样的QR码图像:

但即使部分信息已损坏,它们仍然可以被解码,如下:

或这个:

甚至这个:

我无法’T找到一个QR码应用程序,该应用程序在解码之前输出原始二进制位,但看到这些图像有多少错误或擦除是有趣的。这 QR码,L,M,Q和H中的四个级别纠错,通过要求更多位来编码给定消息,允许多达30%的错误。上面的代码是H级代码,并且在解码器无法处理图像之前,我保持更大且较大的干扰;这些略低于失败点,因此它们可能在25-30%的范围内。

包起来

好的!我们’围绕着今天芦苇所代码的主题徘徊,涵盖了一些重要观点:

  • Reed-Solomon代码代表具有\(gf(2 ^ m)\)中系数的多项式的消息,通常表示为\(rs(n,k)\)
  • Reed-Solomon \((255,255-2T)\)代码是一个常见的子集,表示码字作为255字节,每个字节\(gf(2 ^ 8)\)元素。这允许校正最多\(t \)错误或\(2t \)擦除。
  • 要指定特定的reed-solomon代码,除了了解\(n \)和\(k \)之外,您需要符号字段的特征多项式—对于\(GF(2 ^ 8)\),这是8度的多项式—和发电机多项式,它是形式的\(g(x)=(x + \ lambda ^ b)(x + \ lambda ^ {b + 1})(x + \ lambda ^ {b + 2})\ ldots(x + \ lambda ^ {b + 2t-1})\),其中\(b \)是一个整数,\(\ lambda \)是任何生成元素in \(gf(2 ^ 8)\)—意味着所有非零元素的\(gf(2 ^ 8)\)对于0到254之间的某些整数\(i \)表示为\(\ lambda ^ i \)。\(g(x)的重要属性\)是它的根源是\(2t \)连续的\(\ lambda \)的权力。
  • 代数,编码消息涉及将其表达为多项式\(m(x)\),计算\(r(x)= x ^ {2t} m(x)\ bmod g(x)\)和构造码字\ (c_t(x)= x ^ {2t} m(x)+ r(x)\)
  • 实际上,我们可以使用LFSR编码消息,其中移位单元包含\(GF(2 ^ 8)\)元素,LFSR抽头是\(GF(2 ^ 8)\)中的乘数系数,每个都有对应于发电机多项式中的相应系数。
  • 可以使用查找表来实现此LFSR方法来处理有限字段数学
  • 解码需要更多的处理能力,我们没有’t涵盖了本文中的详细信息。
  • 通过\((n,k)\)代码并使用它作为\((nl,kl)\)代码来使用缩短的芦苇级代码,其中发送器和接收器都假设有\(l \)消息开头的隐式零。
  • 计算误码率是疼痛。但是,如果您正确执行,则最终将以数据比特错误率与\(e_b / n_0 \)结束,例如此图:

  • \(t \)的较高值稍微减少了若干代码率,但允许对于相同的数据误码率,允许更大的收益\(e_b / n_0 \)。 (或者替代地,每个单位噪声每位的信号能量量的数据比特误差率大得多。)
  • 使用纠错将鲁棒性降低到错误检测,尤其是在\(t \)的低值下,但是对于\(t>4 \)oth,错误地纠正了错误的码字超过\(t \)错误的概率非常小。绝大多数时间如果存在超过\(t \)错误,解码器刚刚失败,错误的数量保持不变。有多于\(t \)错误的情况,其中解码器会对错误的码字产生校正,但除非故意引入错误,否则它具有低概率。

  • Reed-Solomon具有许多应用来减少错误的概率:空间传输,存储格式,如CD和DVD,QR码和DSL等。

那’s all for today!

下次我们将解决与CRCS有关的有趣问题。

参考

纠错代码:

Reed-Solomon / BCH代码:

AWGN频道:

QR代码:


©2018年杰森M. Sachs,保留所有权利。


要发布回复评论,请单击连接到每个注释的“回复”按钮。发布新的评论(不是回复评论),请在评论的顶部查看“写评论”选项卡。

注册将允许您参加所有相关网站的论坛,并为您提供所有PDF下载。

注册

我同意 使用条款隐私政策.

尝试我们偶尔但流行的时事通讯。非常容易取消订阅。
或登录