理论:
4字节检错码EDC:
CD-ROM扇区中,有一个4字节共32位的EDC字域,它就是用来存放CRC码。
CD-ROM的CRC校验码生成多项式是32阶的,P(x) = (x16+x15+x2+1)(x16+x2+x+1)。
计算CRC码时用的数据块是从扇区的开头到用户数据区结束为止的数据字节,以Mode1为例,
即字节0—2063共2064个字节。将数据加上4个字节0,然后除多项式,得到的余数为校验码。
在EDC中存放的CRC码的次序如下:
EDC: | x24-x31 | x16-x23 | x8-x15 | x0-x7 |
字节号: | 2064 | 2065 | 2066 | 2067 |
276字节纠错码ECC:
CD-ROM中的数据、地址、校验码等都可以看成是属于GF(2m) = GF(28)中的元素或称符号。
GF(28)表示域中有256个元素,除0,1之外的254个元素由本原多项式P(x)生成。
CD-ROM用来构造GF(28)域的P(x)是:P(x)=X8+X4+X3+X2+1。
而GF(28)域中的本原元素为:α = (0 0 0 0 0 0 1 0)。
按ISO/IEC10149的规定,CD-ROM扇区中的ECC码采用GF(28)域上的RSPC码产生172个字节
的P校验符号和104个字节的Q校验符号。在每个扇区中,#12字节到#2075字节和ECC域中
#2076字节到#2351字节共2340个字节组成1170个字(word)。每个字s(n)由两个字节B组成,
一个称为最高有效位字节MSB,另一个叫做最低有效位字节LSB。第n个字由下面的字节组成,
s(n) = MSB[B(2n+13)]+LSB[B(2n+12)],其中n = 0,1,2,…,1169。
从#12字节开始到#2075字节共2064个字节组成的数据块排列成24×43的矩阵,如下所示:
NP | ||||||||||
0 | 1 | 2 | 3 | 41 | 42 | |||||
0 | 000 | 0001 | 0002 | … | … | … | 0041 | 0042 | ||
1 | 0043 | 0044 | 0045 | … | … | … | 0084 | 0085 | HEADER | |
2 | 0086 | 0087 | 0088 | … | … | … | 0127 | 0128 | + | |
P | Q | 用户数据 | ||||||||
+ | ||||||||||
MP | 22 | 0946 | 0947 | 0948 | … | … | … | 0987 | 0988 | 部分辅助数据 |
23 | 0989 | 0990 | 0991 | … | … | … | 1030 | 1031 | ||
24 | 1032 | 1033 | 1034 | 1073 | 1074 | P-校验 | ||||
25 | 1075 | 1076 | 1077 | … | … | … | 1116 | 1117 | ||
26 | 1118 | 1119 | 1120 | … | 1143 | Q-校验 | ||||
27 | 1144 | 1145 | 1146 | … | 1169 | |||||
0 | 1 | 2 | … | 25 | (ISO /IEC1049) |
矩阵中的元素是字。这个矩阵要把它想象成两个独立的矩阵才比较好理解和分析,
一个是由MSB字节组成的24×43矩阵,另一个是由LSB字节组成的24×43矩阵。
(1) P校验符号用(26,24)RS码产生
43列的每一列用矢量表示,记为Vp。每列有24个字节的数据再加2个字节的P校验字节,用下式表示:
Vp= | s(43*0+Np) |
s(43*1+Np) | |
s(43*2+Np) | |
s(....) | |
s(43*Mp+Np) | |
s(....) | |
s(43*22+Np) | |
s(43*23+Np) | |
s(43*24+Np) | |
s(43*25+Np) |
其中:Np = 0,1,2,……,42; Mp = 0,1,2,……,25
s(43*24+Np)和s(43*25+Np)是P校验字节。对这列字节计算得到的是两个P校验字节,称为P校验符号。
两个P校验字节加到24行和25行的对应列上,这样构成了一个26×43的矩阵,并且满足方程HP×Vp=0。
其中HP校验矩阵为
HP= | 1 | 1 | ...... | 1 | 1 | 1 |
a25 | a24 | ...... | a2 | a1 | 1 |
(2) Q校验符号用(45,43)RS码产生
增加P校验字节之后得到了一个26×43矩阵,该矩阵按对角线元素重新排列后得到一个新的矩阵:
MQ | |||||||||||
0 | 1 | 2 | 40 | 41 | 42 | Q0 | Q1 | ||||
0 | 0000 | 0044 | 0088 | … | … | 0642 | 0686 | 0730 | 1118 | 1144 | |
1 | 0043 | 0087 | 0131 | … | … | 0685 | 0729 | 0773 | 1119 | 1145 | |
2 | 0086 | 0130 | 0147 | … | … | 0728 | 0772 | 0816 | 1120 | 1146 | |
3 | 0129 | 0137 | 0217 | … | … | 0771 | 0815 | 0859 | 1121 | 1147 | |
4 | 0172 | 0216 | 0260 | … | … | 0814 | 0858 | 0902 | 1122 | 1148 | |
22 | 0946 | 0990 | 1034 | … | … | 0470 | 0514 | 0558 | 1140 | 1166 | |
NQ | 23 | 0989 | 1033 | 1077 | … | … | 0513 | 0557 | 0601 | 1141 | 1167 |
24 | 1032 | 1076 | 0002 | … | … | 0556 | 0600 | 0644 | 1142 | 1168 | |
25 | 1075 | 0001 | 0045 | … | … | 0599 | 0643 | 0687 | 1143 | 1169 |
每条对角线上的43个MSB字节和LSB字节组成的矢量记为VQ。VQ在26×43矩阵中变成行矢量。
第NQ行上的VQ矢量包含的字节如下:
VQ = | s(44*0+43*NQ) |
s(44*1+43*NQ) | |
s(44*2+43*NQ) | |
s(....) | |
s(44*MQ+43*NQ) | |
s(....) | |
s(44*41+43*NQ) | |
s(44*42+43*NQ) | |
s(43*26+NQ) | |
s(44*26+NQ) |
其中:NQ = 0,1,2,…,25;MQ = 0,1,2,…,42
s(43*26+NQ)和s(44*26+NQ)是Q校验字节。VQ中的(44*MQ+43*NQ)字节号运算结果要做mod(1118)运算。
用(45,43)RS码产生的两个Q校验字节放到对应VQ矢量的末端,并且满足下面的方程:HQ×VQ=0
其中HQ校验矩阵为
HQ= | 1 | 1 | ...... | 1 | 1 | 1 |
a44 | a43 | ...... | a2 | a1 | 1 |
(26,24)RS码和(45,43)RS码可以纠正出现在任何一行和任何一列上的一个错误,并且能相当可靠地
检测出行、列中的多重错误。如果在一个阵列中出现多重错误,Reference Technology公司提供有一
种名叫Layered ECC的算法,它可以取消多重错误。它的核心思想是交替执行行纠错和列纠错。
ECC算法首先计算MSB矩阵和LSB矩阵中每一行的校正子Sri(i = 0,1,…,25),以及每一列的校正
子Scj(j = 0,1,…,44)。因为用(45,43)RS码,所以每一个Sri和每一个Scj都有两个校正子分量。
如果Sri = 0,则说明第i行无错;如Scj = 0,说明第j行无错。
ECC算法首先纠正只有一个的错误的行。这些错误取消后就纠正只有一个的错误的列。经过一次行列交
替纠错后,只剩下很少错误。再进行一次行列交替纠错后,就可以消除全部错误。
因为RS码纠错算法本身包含找错误的位置和错误值,而错误位置已经由校正子Sr(i-k)、Sri、Sr(i+m)
和Scj、Sc(j+l)确定,所以只剩下求错误值的问题。这个问题在讨论RS码时已经解决。
对CD-ROM存储器的数据,经CIRC校正后可以使以字节做单位的误码率小于10-9,再经RSPC进行纠错后,
字节误码率可以小于10-13,这样就满足了计算机要求误码率小于10-12的要求。
对于Mode1 EDC计算范围是从#0字节开始到#2063字节共2064字节。
对于Mode2 form1 EDC计算范围是从#16字节开始到#2071字节共2056字节。
对于Mode2 form2 EDC计算范围是从#16字节开始到第2347字节共2332字节。
ECC计算范围都是从#12字节开始到#2075字节共2064字节。
/* LUTs used for computing ECC/EDC */ static BYTE ecc_f_lut[256]; static BYTE ecc_b_lut[256]; static DWORD edc_lut[256]; /* Init routine */ static void eccedc_init(void) { DWORD i, j, edc; for(i = 0; i < 256; i++) { j = (i << 1) ^ (i & 0x80 ? 0x11D : 0); ecc_f_lut[i] = j; ecc_b_lut[i ^ j] = i; edc = i; for(j = 0; j < 8; j++) edc = (edc >> 1) ^ (edc & 1 ? 0xD8018001 : 0); edc_lut[i] = edc; } } /***************************************************************************/ // Compute EDC for a block void edc_computeblock( const BYTE *src, WORD size, DWORD *dest ) { DWORD edc=0x00000000; while(size--) edc = (edc >> 8) ^ edc_lut[(edc ^ (*src++)) & 0xFF]; dest[0] = (edc >> 0) & 0xFF; dest[1] = (edc >> 8) & 0xFF; dest[2] = (edc >> 16) & 0xFF; dest[3] = (edc >> 24) & 0xFF; } /***************************************************************************/ // Compute ECC for a block (can do either P or Q) static void ecc_computeblock( BYTE *src, DWORD major_count, DWORD minor_count, DWORD major_mult, DWORD minor_inc, BYTE *dest) { DWORD size = major_count * minor_count; DWORD major, minor; for(major = 0; major < major_count; major++) { DWORD index = (major >> 1) * major_mult + (major & 1); BYTE ecc_a = 0; BYTE ecc_b = 0; for(minor = 0; minor < minor_count; minor++) { BYTE temp = src[index]; index += minor_inc; if(index >= size) index -= size; ecc_a ^= temp; ecc_b ^= temp; ecc_a = ecc_f_lut[ecc_a]; } ecc_a = ecc_b_lut[ecc_f_lut[ecc_a] ^ ecc_b]; dest[major ] = ecc_a; dest[major + major_count] = ecc_a ^ ecc_b; } } // Generate ECC P and Q codes for a block static void ecc_generate( BYTE *sector, int zeroaddress) { BYTE address[4], i; /* Save the address and zero it out */ if(zeroaddress) for(i = 0; i < 4; i++) { address[i] = sector[12 + i]; sector[12 + i] = 0; } /* Compute ECC P code */ ecc_computeblock(sector + 0xC, 86, 24, 2, 86, sector + 0x81C); /* Compute ECC Q code */ ecc_computeblock(sector + 0xC, 52, 43, 86, 88, sector + 0x8C8); /* Restore the address */ if(zeroaddress) for(i = 0; i < 4; i++) sector[12 + i] = address[i]; } /***************************************************************************/ // Generate ECC/EDC information for a sector (must be 2352 = 0x930 bytes), Returns 0 on success void eccedc_generate(BYTE *sector, int type) { DWORD i; switch(type) { case 1: /* Mode 1 */ edc_computeblock(sector + 0x00, 0x810, sector + 0x810); /* Write out zero bytes */ for(i = 0; i < 8; i++) sector[0x814 + i] = 0; ecc_generate(sector, 0); break; case 2: /* Mode 2 form 1 */ edc_computeblock(sector + 0x10, 0x808, sector + 0x818); ecc_generate(sector, 1); break; case 3: /* Mode 2 form 2 */ edc_computeblock(sector + 0x10, 0x91C, sector + 0x92C); break; } }