跳转到主要内容

纯Python Reed Solomon编码/解码器

项目描述

PyPI-Status PyPI-Versions PyPI-Downloads

Build-Status Coverage

Conda-Forge-Status Conda-Forge-Platforms Conda-Forge-Downloads

一个用于保护您的数据免受错误和比特腐蚀的Pythonic 通用错误和擦除Reed-Solomon编解码器。它包括一个纯Python实现和一个可选的速度优化Cython/C扩展。

这是一种突发型实现,因此它支持高于2^3的任何伽罗瓦域,但不支持二进制流。突发错误是非随机错误,在硬盘等数据存储介质上更常见,因此这个库更适合用于数据存储保护,而不是用于流噪声校正,尽管它也可以用于这个目的,但会有一些开销(因为它只与字节一起工作,而不是位)。

基于“Bobmath”和“LRQ3000”撰写的优秀的教程,请访问维基教科书。如果您是刚开始学习里德-所罗门纠错码,维基教科书文章是一个很好的入门介绍。


安装

对于最新稳定版本,请使用以下命令安装

pip install --upgrade reedsolo

对于最新开发版本(不要在生产环境中使用!),请使用以下命令

pip install --upgrade git+https://github.com/tomerfiliba/reedsolomon

如果您通过pip安装时遇到问题,也许这个命令可能会帮到您

pip install reedsolo --no-binary={reedsolo}

默认情况下,仅安装纯Python实现。如果您有Cython和C++编译器,可以可选地使用以下命令构建更快的cythonized二进制文件

pip install --upgrade reedsolo --install-option="--cythonize" --verbose

或本地使用以下命令

python setup.py install --cythonize

然后setup.py将尝试构建Cython优化的模块creedsolo.pyx(如果已安装Cython),然后可以将其导入为import creedsolo,而不是import reedsolo,这两个模块具有相同的功能。

作为替代方案,使用conda在各种平台上安装编译版本

conda install -c conda-forge reedsolo

使用方法

使用高级RSCodec类的基本用法

# Initialization
>>> from reedsolo import RSCodec, ReedSolomonError
>>> rsc = RSCodec(10)  # 10 ecc symbols

# Encoding
# just a list of numbers/symbols:
>>> rsc.encode([1,2,3,4])
b'\x01\x02\x03\x04,\x9d\x1c+=\xf8h\xfa\x98M'
# bytearrays are accepted and the output will be matched:
>>> rsc.encode(bytearray([1,2,3,4]))
bytearray(b'\x01\x02\x03\x04,\x9d\x1c+=\xf8h\xfa\x98M')
# encoding a byte string is as easy:
>>> rsc.encode(b'hello world')
b'hello world\xed%T\xc4\xfd\xfd\x89\xf3\xa8\xaa'
# Note: strings of any length, even if longer than the Galois field, will be encoded as well using transparent chunking.

# Decoding (repairing)
>>> rsc.decode(b'hello world\xed%T\xc4\xfd\xfd\x89\xf3\xa8\xaa')[0]  # original
b'hello world'
>>> rsc.decode(b'heXlo worXd\xed%T\xc4\xfdX\x89\xf3\xa8\xaa')[0]     # 3 errors
b'hello world'
>>> rsc.decode(b'hXXXo worXd\xed%T\xc4\xfdX\x89\xf3\xa8\xaa')[0]     # 5 errors
b'hello world'
>>> rsc.decode(b'hXXXo worXd\xed%T\xc4\xfdXX\xf3\xa8\xaa')[0]        # 6 errors - fail
Traceback (most recent call last):
  ...
reedsolo.ReedSolomonError: Too many (or few) errors found by Chien Search for the errata locator polynomial!

重要升级通知:对于1.0之前的用户:请注意,RSCodec.decode()返回3个变量

  1. 解码(纠正)后的消息

  2. 解码消息和纠错码(它本身也是纠正过的)

  3. 和错误和擦除的位置列表

以下是这些输出的使用方法

>>> tampered_msg = b'heXlo worXd\xed%T\xc4\xfdX\x89\xf3\xa8\xaa'
>>> decoded_msg, decoded_msgecc, errata_pos = rsc.decode(tampered_msg)
>>> print(decoded_msg)  # decoded/corrected message
bytearray(b'hello world')
>>> print(decoded_msgecc)  # decoded/corrected message and ecc symbols
bytearray(b'hello world\xed%T\xc4\xfd\xfd\x89\xf3\xa8\xaa')
>>> print(errata_pos)  # errata_pos is returned as a bytearray, hardly intelligible
bytearray(b'\x10\t\x02')
>>> print(list(errata_pos))  # convert to a list to get the errata positions as integer indices
[16, 9, 2]

由于我们使用具有10个纠错码(ecc)符号的编解码器解码失败,因此带有6个错误的编码,让我们尝试使用更大的编解码器,带有12个ecc符号。

>>> rsc = RSCodec(12)  # using 2 more ecc symbols (to correct max 6 errors or 12 erasures)
>>> rsc.encode(b'hello world')
b'hello world?Ay\xb2\xbc\xdc\x01q\xb9\xe3\xe2='
>>> rsc.decode(b'hello worXXXXy\xb2XX\x01q\xb9\xe3\xe2=')[0]         # 6 errors - ok, but any more would fail
b'hello world'
>>> rsc.decode(b'helXXXXXXXXXXy\xb2XX\x01q\xb9\xe3\xe2=', erase_pos=[3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 16])[0]  # 12 erasures - OK
b'hello world'

这表明我们可以解码两倍多的擦除(我们提供错误位置),比错误(位置未知)多。这是错误纠正与擦除纠正的代价。

要获得可独立纠正的最大错误数量擦除数量(即,不是同时)

>>> maxerrors, maxerasures = rsc.maxerrata(verbose=True)
This codec can correct up to 6 errors and 12 erasures independently
>>> print(maxerrors, maxerasures)
6 12

要同时纠正的最大错误数量擦除数量,您需要指定您期望的错误或擦除数量

>>> maxerrors, maxerasures = rsc.maxerrata(erasures=6, verbose=True)  # we know the number of erasures, will calculate how many errors we can afford
This codec can correct up to 3 errors and 6 erasures simultaneously
>>> print(maxerrors, maxerasures)
3 6
>>> maxerrors, maxerasures = rsc.maxerrata(errors=5, verbose=True)  # we know the number of errors, will calculate how many erasures we can afford
This codec can correct up to 5 errors and 2 erasures simultaneously
>>> print(maxerrors, maxerasures)
5 2

请注意,如果数据块中的错误和擦除数量超过由maxerrata()方法计算的单个界限,编解码器将尝试引发ReedSolomonError异常,但可能根本不会检测到任何错误(这是纠错码的理论限制)。换句话说,纠错码在检测单个界限以上的消息块是否损坏时不可靠。如果您希望有更高的可靠性在错误检测中,请使用SHA或MD5等校验和或哈希对您的消息进行校验,这些校验和或哈希可靠性更高,并且没有擦除数量的限制(唯一潜在的问题是碰撞,但概率非常低)。

注意:要捕获ReedSolomonError异常,请勿忘记首先导入它:from reedsolo import ReedSolomonError

要检查给定其纠错符号的消息是否被篡改,而不进行解码,请使用check()方法

# Checking
>> rsc.check(b'hello worXXXXy\xb2XX\x01q\xb9\xe3\xe2=')  # Tampered message will return False
[False]
>> rmes, rmesecc, errata_pos = rsc.decode(b'hello worXXXXy\xb2XX\x01q\xb9\xe3\xe2=')
>> rsc.check(rmesecc)  # Corrected or untampered message will return True
[True]
>> print('Number of detected errors and erasures: %i, their positions: %s' % (len(errata_pos), list(errata_pos)))
Number of detected errors and erasures: 6, their positions: [16, 15, 12, 11, 10, 9]

默认情况下,大多数里德-所罗门编解码器限制为可以编码在256位内的字符,长度最大为256个字符。但这个编解码器是通用的,您可以通过增加伽罗瓦域来增加或减少长度和最大字符值。

# To use longer chunks or bigger values than 255 (may be very slow)
>> rsc = RSCodec(12, nsize=4095)  # always use a power of 2 minus 1
>> rsc = RSCodec(12, c_exp=12)  # alternative way to set nsize=4095
>> mes = 'a' * (4095-12)
>> mesecc = rsc.encode(mes)
>> mesecc[2] = 1
>> mesecc[-1] = 1
>> rmes, rmesecc, errata_pos = rsc.decode(mesecc)
>> rsc.check(mesecc)
[False]
>> rsc.check(rmesecc)
[True]

请注意,RSCodec 类支持透明分块,因此您不需要增加伽罗瓦域来支持更长的消息,但字符仍然限制在 256 位(或您使用 c_exp 设置的任何域)。

通过直接访问数学函数的底层用法

如果您想要完全控制,可以跳过 API,直接使用库。下面是方法:

首先您需要初始化预计算表。

>> import reedsolo as rs
>> rs.init_tables(0x11d)

小贴士:如果您收到错误:ValueError: byte 必须在范围(0, 256)内,请检查您的素多项式是否正确适用于您的域。小贴士2:默认情况下,您只能编码最大长度和最大符号值 = 256 的消息。如果您想编码更大的消息,请使用以下内容(其中 c_exp 是您的伽罗瓦域的指数,例如,12 = 最大长度 2^12 = 4096):

>> prim = rs.find_prime_polys(c_exp=12, fast_primes=True, single=True)
>> rs.init_tables(c_exp=12, prim=prim)

让我们定义我们的 RS 消息和 ecc 大小。

>> n = 255  # length of total message+ecc
>> nsym = 12  # length of ecc
>> mes = "a" * (n-nsym)  # generate a sample message

为了优化,您可以预先计算生成多项式。

>> gen = rs.rs_generator_poly_all(n)

然后进行编码。

>> mesecc = rs.rs_encode_msg(mes, nsym, gen=gen[nsym])

让我们篡改我们的消息。

>> mesecc[1] = 0

进行解码。

>> rmes, recc, errata_pos = rs.rs_correct_msg(mesecc, nsym, erase_pos=erase_pos)

请注意,消息和 ecc 都已纠正(如果可能的话)。小贴士:如果您知道一些擦除位置,您可以在列表 erase_pos 中指定它们以加倍修复能力。但您也可以只指定一个空列表。

您可以检查纠正了多少个错误和/或擦除,这对于设计自适应比特率算法可能很有用。

>> print('A total of %i errata were corrected over all chunks of this message.' % len(errata_pos))

如果解码失败,它通常将自动检查并引发 ReedSolomonError 异常,您可以处理它。但是,如果您想手动检查修复的消息是否正确,您可以这样做。

>> rs.rs_check(rmes + recc, nsym)

注意:如果您想使用具有不同参数的多个 reedsolomon,您需要在调用 reedsolo 函数之前备份全局变量并恢复它们。

>> rs.init_tables()
>> global gf_log, gf_exp, field_charac
>> bak_gf_log, bak_gf_exp, bak_field_charac = gf_log, gf_exp, field_charac

然后您可以在任何时候做:

>> global gf_log, gf_exp, field_charac
>> gf_log, gf_exp, field_charac = bak_gf_log, bak_gf_exp, bak_field_charac
>> mesecc = rs.rs_encode_msg(mes, nsym)
>> rmes, recc, errata_pos = rs.rs_correct_msg(mesecc, nsym)

如果您使用 RSCodec,全局备份不是必需的,因为它将自动管理。

阅读源代码的注释以获取有关其工作方式的更多信息,以及您可以根据需要设置的各种参数,如果您需要与其他 RS 编解码器接口。

扩展描述

该代码的 wikiversity 已整合到一个很好的 API 中,具有异常处理。该算法可以纠正最多 2*e+v <= nsym,其中 e 是错误数,v 是擦除数,nsym = n-k = ecc(错误纠正码)符号数。这意味着您可以纠正 floor(nsym/2) 个错误,或 nsym 个擦除(已知位置的错误),或两者结合。这称为 Singleton 界限,是任何错误纠正算法可以纠正的最大/最优理论擦除和错误数量(尽管有实验性的方法可以走得更远,称为列表解码,这里未实现,但欢迎提交拉取请求!)。该代码应在大多数合理的 Python 版本(2.4-3.7)上运行,但我在 2.7 和 3.7 上进行了测试。Python 3.8 应该可以运行,但 Cython 目前与此版本不兼容。

如果使用 PyPy 在纯 Python 实现(reedsolo.py)上,或者编译 Cython 扩展 creedsolo.pyx(它比 PyPy 快约 2 倍),该编解码器的性能相当合理。您可以期望编码速率达到几个 MB/s。

此库也经过彻底的单元测试,因此几乎任何编码/解码情况都应该得到覆盖。

该编解码器是通用的,这意味着它可以解码由另一个 RS 编解码器编码的任何消息,只要您提供正确的参数。请注意,然而,如果您使用更高的域(即更大的 c_exp),算法将变慢,首先是因为我们无法再使用优化的 bytearray() 结构,而只能使用 array.array('i', ...),其次是因为 Reed-Solomon 的复杂度是二次的(在编码和解码中),这意味着您的消息越长,编码/解码所需的时间就越长(二次增长)!

该算法本身可以处理每条消息(或数据块)长度最多为(2^c_exp)-1个符号,包括ECC符号,每个符号的值最多为(2^c_exp)-1(实际上,消息长度和单个字符的最大值都受到相同的数学限制)。默认情况下,我们使用GF(2^8)域,这意味着你的值限制在0到255之间(非常适合在计算机上表示单个十六进制符号,因此可以编码任何二进制流),并且限制消息+ecc的最大长度为255。然而,你可以“分块”较长的消息,以适应消息长度限制。《RSCodec》类将自动应用分块,通过将较长的消息分割成块并分别编码/解码;从API角度(即,从你的角度来看)来看,这不会造成任何区别。

要使用Cython实现,你需要pip install cython并安装一个C++编译器(Windows上的Microsoft Visual C++ 14.x和Python 3.10+),阅读官方维基上的最新说明官方wiki。然后你可以简单地切换到creedsolo.pyx所在的文件夹根目录,并输入python setup.py build_ext --inplace --cythonize。或者,你可以通过输入cython -3 creedsolo.pyx来仅生成C++代码。当构建可分发egg或从源安装模块时,如果已安装Cython和C编译器,并且向setup.py提供了--cythonize标志,则Cython模块可以转换和编译,否则默认情况下只包括纯Python实现和.cython源代码,但不会包含二进制文件。

然后,使用import RSCodec from creedsolo代替从reedsolo模块导入,最后只向RSCodec对象提供bytearray()对象。仅使用bytearray是creedsolo比reedsolo更快的其中一个原因。你可以通过指定编码将任何字符串转换为bytearray:bytearray(“Hello World”, “UTF-8”)

请注意,C实现存在一个固有的限制,即无法使用比8(=最大值为255的字符)更高的伽罗瓦域,因为C实现仅与bytearray一起工作,而bytearray仅支持字符值高达255。如果你想使用比8更高的伽罗瓦域,你需要使用纯Python版本,它包括一个假的_bytearray函数,在伽罗瓦域大于8时重载标准bytearray以调用init_tables(),或者重新编写C实现以使用列表而不是bytearray(这将慢得多,所以这违背了初衷,你最好在PyPy下简单地使用纯Python版本——C实现的旧版本就是这样做的,而且没有使用bytearray,所有的性能提升都丢失了,这就是为什么尽管有限制,bytearray仍然被保留的原因)。

边缘情况

尽管在可能且资源消耗不太大的情况下实现了健全性检查,但仍有一些情况,如果不抛出异常,则无法正确解码消息。

  • 如果提供了错误的擦除位置,解码算法将仅信任提供的位置并创建一个错误的校验子,导致解码消息不正确。如果可靠性至关重要,请在解码后始终使用check()方法来检查解码没有出错。

  • 里德-索罗门算法受到单例界限的限制,这不仅限制了它相对于纠错符号数量的纠错和擦除能力,还限制了它检查消息是否可以解码的能力。事实上,如果错误和擦除的数量超过单例界限,解码器就无法数学上肯定地知道是否存在错误,它可能是一个非常有效的消息(尽管不是你期望的消息,但从数学上来说是有效的)。因此,当消息被篡改超过单例界限时,解码器可能会引发异常,但它也可能返回一个数学上有效但仍然被篡改的消息。使用check()方法也无法解决这个问题。为了解决这个问题,一种解决方案是在里德-索罗门编解码器并行使用奇偶校验或哈希函数:使用里德-索罗门编解码器修复消息,使用奇偶校验或哈希函数检查是否存在错误。由于奇偶校验和哈希函数的工作方式,它们产生假阴性的可能性比里德-索罗门算法小得多。这是一个普遍的规则:纠错码在纠正消息方面效率很高,但在检测错误方面则不是,哈希和奇偶校验函数是完成此目的的合适工具。

作者

本模块由Tomer Filiba于2012年构思和开发。

自2015年以来,Stephen Karl Larroque进一步扩展并维护了它。

还有几位其他贡献者帮助改进并使其更健壮

Contributors

有关所有贡献者的列表,请参阅GitHub贡献者图提交历史

许可证

本软件根据您选择的无许可协议或MIT-0(MIT无署名)许可协议发布。这两个许可协议都是公有领域等效许可协议,这是原始作者Tomer Filiba的意图。

项目详情


发布历史 发布通知 | RSS源

下载文件

下载适合您平台的文件。如果您不确定该选择哪一个,请了解更多关于安装包的信息。

源分发

reedsolo-1.7.0.tar.gz (59.7 kB 查看哈希值)

上传时间

构建分发

reedsolo-1.7.0-py3-none-any.whl (32.4 kB 查看哈希值)

上传时间 Python 3

由...