AIS的信息内容是经过压缩的,压缩的方法比较特殊,因为要求压缩的结果是可见字符。本文针对压缩以及解压缩进行描述。
对于VDM消息中的压缩码,编码格式是根据以下对照表来进行的。制定这种编码格式的目的一是为了压缩信息内容,二是要求压缩以后的信息能够以ASCII码显示,以便使用文本方式传输(如果直接压缩,可能会产生不可见字符,这就是一般压缩文件以二进制方式存储的原因)。
ASCII
HEX = binary
|
Valid Character
|
Binary Field represented
|
ASCII
HEX = binary
|
Valid Character
|
Binary Field represented
|
30=00110000
|
0
|
000000
|
50=01010000
|
P
|
100000
|
31=00110001
|
1
|
000001
|
51=01010001
|
Q
|
100001
|
32=00110010
|
2
|
000010
|
52=01010010
|
R
|
100010
|
33=00110011
|
3
|
000011
|
53=01010011
|
S
|
100011
|
34=00110100
|
4
|
000100
|
54=01010100
|
T
|
100100
|
35=00110101
|
5
|
000101
|
55=01010101
|
U
|
100101
|
36=00110110
|
6
|
000110
|
56=01010110
|
V
|
100110
|
37=00110111
|
7
|
000111
|
57=01010111
|
W
|
100111
|
38=00111000
|
8
|
001000
|
60=01100000
|
‘
|
101000
|
39=00111001
|
9
|
001001
|
61=01100001
|
a
|
101001
|
3A
=00111010
|
:
|
001010
|
62=01100010
|
b
|
101010
|
3B=00111011
|
;
|
001011
|
63=01100011
|
c
|
101011
|
3C
=00111100
|
<
|
001100
|
64=01100100
|
d
|
101100
|
3D=00111101
|
=
|
001101
|
65=01100101
|
e
|
101101
|
3E=00111110
|
>
|
001110
|
66=01100110
|
f
|
101110
|
3F
=00111111
|
?
|
001111
|
67=01100111
|
g
|
101111
|
40=01000000
|
@
|
010000
|
68=01101000
|
h
|
110000
|
41=01000001
|
A
|
010001
|
69=01101001
|
i
|
110001
|
42=01000010
|
B
|
010010
|
6A
=01101010
|
j
|
110010
|
43=01000011
|
C
|
010011
|
6B=01101011
|
k
|
110011
|
44=01000100
|
D
|
010100
|
6C
=01101100
|
l
|
110100
|
45=01000101
|
E
|
010101
|
6D=01101101
|
m
|
110101
|
46=01000110
|
F
|
010110
|
6E=01101110
|
n
|
110110
|
47=01000111
|
G
|
010111
|
6F
=01101111
|
o
|
110111
|
48=01001000
|
H
|
011000
|
70=01110000
|
p
|
111000
|
49=01001001
|
I
|
011001
|
71=01110001
|
q
|
111001
|
4A
=01001010
|
J
|
011010
|
72=01110010
|
r
|
111010
|
4B=01001011
|
K
|
011011
|
73=01110011
|
s
|
111011
|
4C
=01001100
|
L
|
011100
|
74=01110100
|
t
|
111100
|
4D=01001101
|
M
|
011101
|
75=01110101
|
u
|
111101
|
4E=01001110
|
N
|
011110
|
76=01110110
|
v
|
111110
|
4F
=01001111
|
O
|
011111
|
77=01110111
|
w
|
111111
|
目前我们只需要考虑使用以上编码格式进行解码就可以了。以下是解码流程。
1.Convert ASCII-code to 6-bit binary field(将ASCII码转换成6位二进制值)
算法图例:
第一个判断是因为经过编码的文本字符只可能为0x30到0x77间的可显示字符。
第二个判断与第三个判断是由于在编码表中,我们可以看到,0x57以后并不是0x58,而是0x60,所以在0x58到0x5F间的字符是无效字符。
以上三个判断是对需要解码的字符有效性的判断。
其下的流程是真正的解码流程。至于为什么用这样的流程,大家可以推敲,目前我们只需安照该流程执行,即可将VDM消息中的压缩信息转为6比特的信息了。
解码算法代码:
//转换单个字符
bool EightByteToSix(BYTE inEight,BYTE &outSix)
{
//以下两个判断用于检测所输入的ASCII码是否有效
if(inEight < 0x30 || inEight > 0x77)
return false;
if(inEight > 0x57 && inEight < 0x60)
return false;
//检查结束
outSix = inEight + 0x28; //加上101000
if(outSix > 0x80) //如果SUM>10000000
outSix += 0x20; //加上100000
else
outSix += 0x28; //加上101000
outSix = outSix<<2; //右移两位,获取LSB
return true ;
}
//转换整个压缩信息
2. Convert ASCII-code String to 6-bit binary field Array
(将ASCII
码字符串转换成6
位二进制值表示的字符数组)
说明:将每个ASCII
码转换成6bits
码后,因为数据是以字节为单位保存的,因此需要将这些6bits
码放置到字节中去。比如ASCII
码串”A2L9”
,转换成6bits
二进制是这样的:010001 000010 011100 001001
。用字节表示就是 01000100 00100111 00001001
即0x44,0x27,0x09
这三个ASCII
码。
算法代码:
//返回参数用LPBYTE而不用CString,是因为转换的数据中可能出现0x00。这在字符串中会作为串结束符看待,因此将无法得到串的真实长度
bool EightStrToSix(CString inEight, LPBYTE outSix)
{
BYTE len = inEight.GetLength();
BYTE nowBt = 0x00;//用于记录当前未用完的记录6Bits码的字节
BYTE midBt;//中间转换记录字节
BYTE outLen = 0;
for(int i=0;i<len;i++)//处理所有ASCII码
{
BYTE bt = inEight.GetAt(i);
BYTE six;
If(EightByteToSix(bt,six)==false)//将当前ASCII码转换成6 bits码
return false;
int res = i%4;//因为4个ASCII码转换成3个字节的6bits码,因此将是每4个ASCII码的转换成为一个循环过程
switch(res)
{
//当是第一个ASCII码时,不能直接完成一个6bits码的字节转换,因为还有两个bits没有填入数据。用nowBt暂先保存6bits码正在记录数据的字节
case 0:
nowBt = six;
break;
//当处理第二个ASCII码时,显然,加上第一个ASCII码的转换,2个6bits码将是12bits,因此可以完成一个字节的数据,另外余下的4bits记录到nowBt中,等待下一个ASCII码的处理。
case 1:
midBt = six >>6; //将最高的两位移到末尾,以便将高二位保存到nowBt的低二位中
nowBt = nowBt | midBt;//完成6bits码的第一个字节
outSix[outLen] = nowBt;//保存到输出的字节数组中
outLen ++;
//以下两步移位是将当前6bits码的中间4位移动到高四位中。并记录到nowBt中。
nowBt = six >>2;
nowBt = nowBt <<4;
break;
//当处理第三个ASCII码时,nowBt中的有效位是高四位,因此需要将新的6bits码的高四位放到nowBt的低四位中,然后保存nowBt到输出数组,再将新的6bits码的第5,6位移到高二位后记录到nowBt中
case 2:
midBt = six >>4;//将高四位移到低四位
nowBt = nowBt | midBt;//完成6bits码的第二个字节
outSix[outLen] = nowBt;//保存到输出的字节数组
outLen ++;
//将新的6bits码的第5,6位移到高二位后记录到nowBt中
nowBt = six >>2;
nowBt = nowBt <<6;
break;
//当处理第四个ASCII码时,nowBt中的有效位是高二位,因此将新的6bits码的高6位移到nowBt的低6位,正好完成一个循环
case 3:
midBt = six >>2;//将高六位移到低六位
nowBt = nowBt | midBt;//完成6bits码的第三个字节
outSix[outLen] = nowBt;//保存到输出的字节数组
outLen ++;
nowBt = 0x00;//nowBt复位
break;
}
default:
{
break;
}
}
}
return true;
}
信息获取代码
压缩信息解码完成后,就可以从解码信息中读取指定的内容了。
根据读取的信息的比特长度不同,读取的方法也有所变化。
3.
从解码数据中获取指定类型的数据内容
3.1获取1bits 到 8bits的整数数据
//lpInfo—调用EightStrToSix获取的解码数据
//dwBitStart –需要获取的数据的起始位. dwBitStart 从0开始
//byLen –数据所占位数
BYTE GetByteValFromInfo(const LPBYTE lpInfo,DWORD dwBitStart,BYTE byLen)
{
BYTE byStartByte = dwBitStart/8;//获取起始字节序号
BYTE byStartBit = dwBitStart%8;//获取起始字节中的起始位数
BYTE byInfo = 0x00;
if(8-byStartBit < byLen)//要获取的数据跨两个字节
{
BYTE by1 = *(lpInfo + byStartByte);//获取第一个字节数据
BYTE by2 = *(lpInfo + byStartByte + 1);//获取第二个字节数据
//完成两个字节中位的拼接
by1 = by1 << byStartBit;
by1 = by1 << 8 - byLen; //byLen - (8 - byStartBit)
by2 = by2 >> 16 - byLen - byStartBit; //8 - (byLen - (8 - byStartBit))
byInfo = by1 | by2;
}
else
{
byInfo = *(lpInfo + byStartByte);
byInfo = byInfo << byStartBit;
byInfo = byInfo >> 8 - byLen;
}
return byInfo;
}
3.2获取9bits 到 16bits的整数数据
WORD GetWordValFromInfo(const LPBYTE lpInfo,DWORD dwBitStart,WORD wLen)
{
BYTE byStartByte = dwBitStart/8;
BYTE byStartBit = dwBitStart%8;
WORD wInfo = 0x0000;
if(16 - byStartBit < wLen)//要获取的数据跨三个字节
{
wInfo = *(WORD*)(lpInfo + byStartByte);
wInfo = wInfo << byStartBit;
wInfo = wInfo >> 16 - wLen;
BYTE by1 = *(lpInfo + byStartByte + 2);
by1 = by1 >> 24- wLen - byStartBit;
wInfo = wInfo | by1;
}
else
{
wInfo = *(WORD*)(lpInfo + byStartByte);
wInfo = ntohs(wInfo);
wInfo = wInfo << byStartBit;
wInfo = wInfo >> 16 - wLen;
}
return wInfo;
}
3.3获取17bits 到 32bits的整数数据
DWORD GetDWordValFromInfo(const LPBYTE lpInfo,DWORD dwBitStart,DWORD dwLen)
{
BYTE byStartByte = dwBitStart/8;
BYTE byStartBit = dwBitStart%8;
DWORD dwInfo = 0x00000000;
if(dwLen <= 24)//占三个或者四个字节
{
if(24 - byStartBit < dwLen)//占四字节
{
dwInfo = *(DWORD*)(lpInfo + byStartByte);
dwInfo = ntohl(dwInfo);
dwInfo = dwInfo << byStartBit;
dwInfo - dwInfo >> 32 - dwLen;
}
else //占三字节
{
DWORD dwInfo = 0x00FFFFFF;
dwInfo = dwInfo & *(DWORD*)(lpInfo + byStartByte - 1);
dwInfo = ntohl(dwInfo);
dwInfo = dwInfo << byStartBit;
dwInfo = dwInfo >> 24 - dwLen;
}
}
else//占四个或者五个字节
{
if( (32 - byStartBit) < dwLen)//占五个字节
{
dwInfo = *(DWORD*)(lpInfo + byStartByte);
dwInfo = ntohl(dwInfo);
dwInfo = dwInfo << byStartBit;
dwInfo = dwInfo >> 32 - dwLen;
BYTE by1 = *(lpInfo + byStartByte + 4);
by1 = by1 >> 40 - dwLen - byStartBit;
dwInfo = dwInfo | by1;
}
else//占四个字节
{
dwInfo = *(DWORD*)(lpInfo + byStartByte);
dwInfo = ntohl(dwInfo);
dwInfo = dwInfo << byStartBit;
dwInfo = dwInfo >> 32 - dwLen;
}
}
return dwInfo;
}
3.4获取字符串类型数据
CString GetStringValFromInfo(const LPBYTE lpInfo, DWORD dwBitStart, DWORD dwLen)
{
WORD wChars = dwLen/6;
BYTE byStartByte = dwBitStart/8;
BYTE byStartBit = dwBitStart%8;
CString sRtnStr;
for(WORD wChar = 0; wChar < wChars; wChar++)
{
BYTE byChar = *(lpInfo + byStartByte);
byChar = byChar << byStartBit;
byChar = byChar >> 2;
if(byStartBit > 2)//表示该BYTE数据跨字节了
{
BYTE by1 = *(lpInfo + byStartByte + 1);
by1 = by1 >> 16 - 6 - byStartBit;
byChar = byChar | by1;
byStartByte ++;
byStartBit = byStartBit - 2;
}
else
{
byStartBit += 6;
if(byStartBit == 8)
{
byStartBit = 0;
byStartByte ++;
}
}
//字符串解码,如果小于0x20,需要加上0x40
//具体规则参考ITU-R M-1371-1文件第41,42页
if(byChar < 0x20)
byChar += 0x40;
sRtnStr += byChar;
}
return sRtnStr;
}