C#关于RS编码的开发

发布时间 2023-08-09 16:56:45作者: wzwyc

什么是RS编码?

里德-所罗门码(Reed-solomon codes,简称RS编码)是一种前向错误更正的信道编码,对由校正过采样数据所产生的有效多项式。编码过程首先在多个点上对这些多项式求冗余,然后将其传输或者存储。对多项式的这种超出必要值得采样使得多项式超定(过限定)。当接收器正确的收到足够的点后,它就可以恢复原来的多项式,即使接收到的多项式上有很多点被噪声干扰有损。
RS码是一个[n,k,n-k+1]码,是一种定义在有限域F上的长度为n,信息长度为k,最短汉明距离为n-k+1的线性分组码。由于这种编码满足Singleton界,因此它是一种最大距离可分码。由于码长为n信息长度为k的码的最大汉明距离为n-k+1,所以在这种意义下RS码是一种最优的编码方法。
RS码的纠错能力由最短汉明距离决定,为n-k+1。如果预先并不知道错误的位置,RS码最多可以纠正(n-k)/2个错误。而在某些情况下,我们可以预知错误的位置(比如BEC信道),此时RS码最多可以纠正n-k个错误。如果我们可以预先知道S个错误位置,而此外还有E个未知的错误位置,那么在满足2E+S<n-k的情况下,我们可以完全纠正这些错误。
在实际应用中,RS码经常使用大小为2m的有限域。在这种情况下,每个符号都包含有m比特的信息。发送者将编码后的分组发送给接受者,每个分组通常含有2m-1个符号。
RS码的这种性质使得它非常适合纠正传输系统中的突发错误。这是由于不论一个符号中有多少个比特发生错误,都只发生了一个符号错误。而对于不发生突发错误的传输系统,RS码的性能通常不如普通的二元码。
在最常用的(255, 223)RS码中,223个RS输入符号(每个符号有8个比特)被编码成255个输出符号。
大多数里所错误校正编码流程是成体系的。这意味着输出的码字中有一部分包含着输入数据的原始形式。
符号大小为8位的RS码迫使码长(编码长度)最长为255个符号。
标准的(255,223)RS码可以在每个码字中校正最多16个里所符号的错误。由于每个符号事实上是8个比特,这意味着这个码可以校正最多16个短爆发性错误。

C#关于RS编码的开发

Nuget包安装:

Install-Package STH1123.ReedSolomon

源码地址:https://github.com/Sonic-The-Hedgehog-LNK1123/ReedSolomon

Github上有相关的示例代码,可以通过上面的网址查看。

我用官方的示例代码测试了一下:

using STH1123.ReedSolomon;
using System;
using System.Text;

namespace ReedSolomonDemo
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            GenericGF field = new GenericGF(285, 256, 0);
            ReedSolomonEncoder rse = new ReedSolomonEncoder(field);
            int[] data = new int[] { 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
            rse.Encode(data, 9);

            Output(data);

            data = new int[] { 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x40, 0x86, 0x08, 0xD5, 0x2C, 0xAE, 0xB5, 0x8F, 0x83 };
            int[] erasures = new int[] { 0, 1, 2 };
            ReedSolomonDecoder rsd = new ReedSolomonDecoder(field);
            if (rsd.Decode(data, 9, erasures))
            {
                // Data corrected.
                Output(data);
            }
            else
            {
                // Too many errors/erasures to correct.
                Console.WriteLine("Too many errors/erasures to correct.");
            }

            Console.WriteLine("执行结束");
            Console.ReadKey();
        }

        private static void Output(int[] data)
        {
            StringBuilder builder = new StringBuilder();

            foreach (var item in data)
            {
                builder.Append(Convert.ToString(item, 16));
                builder.Append(" ");
            }

            Console.WriteLine(builder.ToString());
            Console.ReadKey();
        }
    }
}

ReedSolomonCore

上面的开源库有个问题啊,好像接口是int数组的,我们实际项目中很少用int数组来处理数据。

Nuget包安装:

Install-Package ReedSolomonCore

源码网站:https://github.com/ijat/ReedSolomonCore

示例代码:

using ReedSolomonCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ReedSolomonDemo
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            new Program().Run();
        }

        private List<int> changePositions = new List<int>();

        private void Run()
        {
            byte[] originalMessage = Encoding.UTF8.GetBytes("Nervously I loaded the twin ducks aboard the revolving platform.");

            var ecc = ReedSolomonAlgorithm.Encode(originalMessage, 32);

            Console.WriteLine($"Original message: {Encoding.UTF8.GetString(originalMessage)}");
            Console.WriteLine($"Original ecc:     {ByteArrayToString(ecc)}");
            Console.WriteLine();

            ByteError(0x35, 3, originalMessage);
            ByteError(0x51, 8, ecc);

            ByteErasure(17, originalMessage);
            ByteErasure(21, originalMessage);
            ByteErasure(16, ecc);
            ByteErasure(18, ecc);
            Console.WriteLine();

            Console.WriteLine($"Damaged message:  {Encoding.UTF8.GetString(originalMessage)}");
            Console.WriteLine($"Damaged ecc:      {ByteArrayToString(ecc)}");
            Console.WriteLine();

            //var newEcc = new byte[32];
            //Array.Copy(ecc, newEcc, 32);

            byte[] decodedMessage = ReedSolomonAlgorithm.Decode(originalMessage, ecc);

            Console.WriteLine($"Decoded message:  {Encoding.UTF8.GetString(decodedMessage)}");

            Console.ReadKey();
        }

        /* Introduce a byte error at LOC */

        private void ByteError(byte err, int loc, byte[] dst)
        {
            Console.WriteLine($"Adding Error at loc {loc}, data {dst[loc - 1]:X2}");
            dst[loc - 1] ^= err;
        }

        /* Pass in location of error (first byte position is
           labeled starting at 1, not 0), and the codeword.
        */

        private void ByteErasure(int loc, byte[] dst)
        {
            Console.WriteLine($"Erasure at loc {loc}, data {dst[loc - 1]:X2}");
            dst[loc - 1] = 0;
        }

        private string ByteArrayToString(byte[] array)
        {
            return string.Join(", ", array.Select(x => $"{x:x2}"));
        }
    }
}