3

我有一个应用程序可以解码来自磁条阅读器的数据。但是,我很难让计算出的LRC检查字节与卡片上的字节匹配。如果我要抓取 3 张卡片,每张卡片有 3 条轨道,我猜下面的算法将适用于这些卡片中 9 条轨道中的 4 条。

我使用的算法看起来像这样(C#):

private static char GetLRC(string s, int start, int end)
{
    int result = 0;
    for (int i = start; i <= end; i++)
    {
        result ^= Convert.ToByte(s[i]);
    }
    return Convert.ToChar(result);
}

这是未通过检查的磁道 3 数据示例。在这张卡上,磁道 2 匹配,但磁道 1 也失败了。

   0 1 2 3 4 5 6 7  8 9 A B C D E F
00 3 4 4 4 4 4 4 4  4 4 4 5 5 5 5 5 
10 5 5 5 5 5 6 6 6  6 6 6 6 6 6 6 7 
20 7 7 7 7 7 7 7 7  7 8 8 8 8 8 8 8 
30 8 8 8 9 9 9 9 9  9 9 9 9 9 0 0 0 
40 0 0 0 0 0 0 0 1  2 3 4 1 1 1 1 1 
50 1 1 1 1 1 2 2 2  2 2 2 2 2 2 2 3 
60 3 3 3 3 3 3 3 3 

扇区分隔符是';' 它以“?”结尾。

该磁道的 LRC 字节为 0x30。不幸的是,上面的算法根据以下计算计算出 0x00 的 LRC(为它的长度道歉。我想彻底):

00 ^ 3b = 3b ';'
3b ^ 33 = 08
08 ^ 34 = 3c
3c ^ 34 = 08
08 ^ 34 = 3c
3c ^ 34 = 08
08 ^ 34 = 3c
3c ^ 34 = 08
08 ^ 34 = 3c
3c ^ 34 = 08
08 ^ 34 = 3c
3c ^ 34 = 08
08 ^ 35 = 3d
3d ^ 35 = 08
08 ^ 35 = 3d
3d ^ 35 = 08
08 ^ 35 = 3d
3d ^ 35 = 08
08 ^ 35 = 3d
3d ^ 35 = 08
08 ^ 35 = 3d
3d ^ 35 = 08
08 ^ 36 = 3e
3e ^ 36 = 08
08 ^ 36 = 3e
3e ^ 36 = 08
08 ^ 36 = 3e
3e ^ 36 = 08
08 ^ 36 = 3e
3e ^ 36 = 08
08 ^ 36 = 3e
3e ^ 36 = 08
08 ^ 37 = 3f
3f ^ 37 = 08
08 ^ 37 = 3f
3f ^ 37 = 08
08 ^ 37 = 3f
3f ^ 37 = 08
08 ^ 37 = 3f
3f ^ 37 = 08
08 ^ 37 = 3f
3f ^ 37 = 08
08 ^ 38 = 30
30 ^ 38 = 08
08 ^ 38 = 30
30 ^ 38 = 08
08 ^ 38 = 30
30 ^ 38 = 08
08 ^ 38 = 30
30 ^ 38 = 08
08 ^ 38 = 30
30 ^ 38 = 08
08 ^ 39 = 31
31 ^ 39 = 08
08 ^ 39 = 31
31 ^ 39 = 08
08 ^ 39 = 31
31 ^ 39 = 08
08 ^ 39 = 31
31 ^ 39 = 08
08 ^ 39 = 31
31 ^ 39 = 08
08 ^ 30 = 38
38 ^ 30 = 08
08 ^ 30 = 38
38 ^ 30 = 08
08 ^ 30 = 38
38 ^ 30 = 08
08 ^ 30 = 38
38 ^ 30 = 08
08 ^ 30 = 38
38 ^ 30 = 08
08 ^ 31 = 39
39 ^ 32 = 0b
0b ^ 33 = 38
38 ^ 34 = 0c
0c ^ 31 = 3d
3d ^ 31 = 0c
0c ^ 31 = 3d
3d ^ 31 = 0c
0c ^ 31 = 3d
3d ^ 31 = 0c
0c ^ 31 = 3d
3d ^ 31 = 0c
0c ^ 31 = 3d
3d ^ 31 = 0c
0c ^ 32 = 3e
3e ^ 32 = 0c
0c ^ 32 = 3e
3e ^ 32 = 0c
0c ^ 32 = 3e
3e ^ 32 = 0c
0c ^ 32 = 3e
3e ^ 32 = 0c
0c ^ 32 = 3e
3e ^ 32 = 0c
0c ^ 33 = 3f
3f ^ 33 = 0c
0c ^ 33 = 3f
3f ^ 33 = 0c
0c ^ 33 = 3f
3f ^ 33 = 0c
0c ^ 33 = 3f
3f ^ 33 = 0c
0c ^ 33 = 3f
3f ^ 3f = 00 '?'

如果有人能指出如何修复我的算法,我将不胜感激。

谢谢,保罗


编辑:

这样您就可以查看我是否在 LRC 计算中意外遗漏了任何字节或包含了错误的字节(最后的 '.' 实际上是 '\r')。所有三个轨道的完整数据:

   0 1 2 3 4 5 6 7  8 9 A B C D E F
00 % U V W X Y Z 0  1 2 3 4 5 6 7 8 
10 9 9 A B C D E F  G H I J K L M N 
20 O P Q R S T U V  W X Y Z 1 2 3 0 
30 1 2 3 4 5 6 7 8  9 A B C D E F G 
40 H I J K L M N O  P Q R S T ? 3 ; 
50 1 2 3 4 5 6 7 1  2 3 4 5 6 7 8 9 
60 0 1 2 3 4 5 6 7  8 9 0 1 2 3 4 5 
70 6 7 8 9 0 ? 5 ;  3 4 4 4 4 4 4 4 
80 4 4 4 5 5 5 5 5  5 5 5 5 5 6 6 6 
90 6 6 6 6 6 6 6 7  7 7 7 7 7 7 7 7 
A0 7 8 8 8 8 8 8 8  8 8 8 9 9 9 9 9 
B0 9 9 9 9 9 0 0 0  0 0 0 0 0 0 0 1 
C0 2 3 4 1 1 1 1 1  1 1 1 1 1 2 2 2 
D0 2 2 2 2 2 2 2 3  3 3 3 3 3 3 3 3 
E0 ? 0 .

GetLRC()算法按照建议重新检测,仅异或出现奇数次的字节:

private static char GetLRC(string s, int start, int end)
{
    int result = 0;

    byte cur_byte = Convert.ToByte(s[start]);

    int count = 0;
    for (int i = start; i <= end; i++)
    {
        byte b = Convert.ToByte(s[i]);
        if (cur_byte != b)
        {
            if (count % 2 != 0)
            {
                result ^= cur_byte;
            }
            cur_byte = b;
            count = 0;
        }
        ++count;
    }

    if (count % 2 != 0)
    {
        result ^= cur_byte;
    }

    return Convert.ToChar(result);
}

GetLRC()新函数所采取的计算步骤:

00 ^ 3b = 3b ';'
3b ^ 33 = 08
08 ^ 31 = 39
39 ^ 32 = 0b
0b ^ 33 = 38
38 ^ 34 = 0c
0c ^ 33 = 3f
3f ^ 3f = 00 '?'

问题:LRC 字节是来自卡本身还是由读卡器固件添加?(即也许这是一个固件错误)

4

3 回答 3

1

我可以提个建议吗?将您的数据存储为运行长度,并且仅在运行长度为奇数时执行异或 - 然后仅执行一次 (runLength & 0x01) 次。这将摆脱大量无价值的工作,并使正在发生的事情更加清晰。这样做会产生:


Run Lengths:
(01,3b)(01,33)(10,34)(10,35)(10,36)(10,37)(10,38)(10,39)(10,30)
(01,31)(01,32)(01,33)(01,34)(10,31)(10,32)(09,33)(1,3f)

做偶数/奇数的事情会给出:


3b ^ 33 ^ 31 ^ 32 ^ 33 ^ 34 ^ 33 ^ 3f
        08-->39-->0B-->38-->0C-->3F-->00

看起来更简单,更干净。我的猜测是,查看您的数据,您的数据流中某处有额外的 30 个或 1 个短。添加额外的 30 可以得到您的答案:


3b ^ 33 ^ 31 ^ 32 ^ 33 ^ 34 ^ 33 ^ 30 ^ 3F
        08-->39-->0B-->38-->0C-->3F-->0F-->30

除此之外,我会继续挖掘......

您可以在输入参数中添加一些断言或其他验证吗?我不想看到越界开始/结束导致兴奋和/或空字符串。另外,是否有可能在开始结束时取消一个?包含/不包含数据范围?这可能会导致数据末尾的额外 0x030 从存储在轨道 3 末尾的 0 转换为 0x30。此外,是否有可能存在损坏的数据或损坏的 LRU?显然,这就是你的支票试图抓住的那种东西。也许它抓住了什么?

于 2010-05-20T20:58:53.280 回答
1

修正了LRC的算法,但计算LRC的数据格式可能有误。(这取决于您的 MSR 阅读器)

ANSI/ISO 定义了两种轨道格式(Alpha 和 BCD)。二进制的编码不同于ASCII。

在这种情况下,开始哨兵是';' ,所以格式应该是BCD。(Alpha 开始哨兵是 '%')

LRC 使用“Real track data”计算(不包括奇偶校验位),

Convert rule 
ASCII to BCD ->(ASCII - 0x30) 

    --Data Bits--    Parity 
    b1  b2  b3  b4   b5      Character  Function

    0   0   0   0    1        0 (0H)    Data
    1   0   0   0    0        1 (1H)      " 
    0   1   0   0    0        2 (2H)      " 
    1   1   0   0    1        3 (3H)      " 
    0   0   1   0    0        4 (4H)      " 
    1   0   1   0    1        5 (5H)      " 
    0   1   1   0    1        6 (6H)      " 
    1   1   1   0    0        7 (7H)      " 
    0   0   0   1    0        8 (8H)      " 
    1   0   0   1    1        9 (9H)      " 
    0   1   0   1    1        : (AH)    Control
    1   1   0   1    0        ; (BH)    Start Sentinel
    0   0   1   1    1        < (CH)    Control
    1   0   1   1    0        = (DH)    Field Separator
    0   1   1   1    0        > (EH)    Control
    1   1   1   1    1        ? (FH)    End Sentinel

在您的样本中,

  1. 将 ASCII 磁道数据转换为 BCD 格式。
  2. 使用 BCD 数据计算 LRC,结果为 0x00。
  3. 然后将LRC(BCD转ASCII),最终得到LRC = 0x30。

PS ASCII 转换为 Alpha

    if(bASCII >= 0x20 && bASCII <= 0x5B)
    {
        return(bASCII - 0x20);
    }
    else if(bASCII >= 0x5C && bASCII <= 0x5F)
    {
        return(bASCII - 0x1F);
    }
于 2016-06-24T06:05:30.933 回答
0

您的算法与维基百科文章中的 LRC 算法不匹配。你确定你使用的是正确的算法吗?

于 2012-10-16T21:09:59.907 回答