自己定的pe包收发规则:仅供参考
===========================
1.2上传一个数据的进程
规定车台上电后就开始按照预先设定好的参数不间断地向控制中心发送定位数据。压缩数据包要求确认,非压缩包不要求确认。如果车台未收到确认信息,会在规定时间内重发此包,直到次数溢出。发送的这个Pe数据包里,包含象征身份的“车台ID号码”,以及发送Pe数据包个数的“信息序列号”,中心通过车台ID号码识别这条Pe包是哪个车台发送过来的,通过信息序列号的连续性来判断Pe数据包是否有丢失。在传送重要信息的时候,同样需要中心回传确认信息。
1.3 Pe包的格式
一个Pe数据包由信息头、保留位、车台ID号码、信息序列号、协议号、内容长度、信息内容、校验和、结束标志组成,除了信息内容可以为空以外,其它各部分均不能省略,且前后位置也不可改变。此系统中所有的传输,不论传送数据还是命令,均通过Pe包来传送,这是唯一合法的可识别信息传送格式。命令和数据以及是何种命令或数据通过协议号加以区别。它们的前后位次和占用字节长度见表一。
表一
信息头 | 保留位 | 车台ID号码 | 信息序列号 | 协议号 | 内容长度 | 信息内容 | 校验和 | 结束标志 |
4个字节 | 2个字节 | 4个字节 | 4个字节 | 1个字节 | 1个字节 | ≤255个字节 | 2个字节 | 2个字节 |
例如要发送一个网络测试命令,此命令协议号为0x00,长度为0x00,信息内容为空,信息格式为:
@@Pe 0x0000 0xE91DB5FF 0x00000001 0x00 0x00 0x03F0 0x0D 0x0A
|
为了描述方便,阅读容易,信息头用了ASCII来表示,中间加入了空格,实际当中是不存在的,严谨的描述应为下面这条,以16进制表示。若以后不作特殊声明,采用上面的形式举例。
404050650000E91DB5FF00000001000003F00D0A
1.4 Pe包的解释
1.4.1信息头
占用四个字节,表明消息的开始。以@@Pe表示。只有正确接收到信息头以后,才开始接受后续数据,否则,不予理睬。即便是成功地接收到了帧头,只要其余各项有一项不正确,就按错误帧处理。不论在车台ID号码、信息序列号以及信息内容中接收到和信息头相同的数据时,均按相应格式中的数据含义处理,而不再具有帧头的含义。
1.4.2保留位
占用两个字节,留作系统以后升级使用,等于0时表示未定义。
1.4.3车台ID号码
占用四个字节,车载台的识别号码,一般为SIM卡号,是每一个车载台的身份号码。中心通过这个号码识别是谁发送过来的数据,移动端的车台通过这个号码验证命令是不是发送给自己的。SIM卡号取低10位,然后转化成十六进制数即可。例如SIM卡号为13911038463,去掉最高位的1,将数据3911038463转换成长整数0xE91DB5FF。这个长整数就是所需要的车台ID号码。因为这个号码始终是以三开头的,所以这个数值不得小于0xB2D05E00(十进制3000000000)。当接收到小于此数的ID号码后,按错误帧处理。
1.4.4信息序列号
占用四个字节,这是车台发送信息的序列号,用于接收方检测是否有信息的丢失。中心和车台各自按自己发送Pe包的个数计数,互不影响。车台在上电后此数复位等于零,如果不断电此数不复位为零,发送第一帧数据时便开始计数。
1.4.6内容长度
占用一个字节,表明信息内容的字节长度。从第一个信息字节开始计数,以1表示,到最后一个信息字节计数结束。
1.4.7信息内容
占用0至二百五十五之间的任意长度字节,不定长。待传送的信息内容长度可以等于零,即发送一个空帧,最长不得超过255个字节。
1.4.8校验和
占用两个字节,从Pe包的第一个字节开始进行校验,到校验和的前一个字节结束校验,校验以字节为单位,校验方式为和校验。
校验和的计算方法:
(1) 把校验和的值初始化为零
(2) 加Pe包的第一个字节,加Pe包的第二个字节……加到和校验的前一个字节
(3) 这个累加和就是所需要的校验和
(4) 校验和本身和结束标志不计入校验
1.4.9结束标志
占用两个字节,表明此帧信息的结束。以十六进制数0x0D、0x0A表示。
2上传数据信息
由车台发往控制中心的Pe包,叫做上传数据信息或上发数据信息。上传的压缩数据包要求确认,非压缩包不要求确认。如果车台未收到确认信息,会在规定时间内重发此包,直到次数溢出。
2.1发送非压缩的定位信息
车台在上电后,会按照中心设置的参数来发送定位信息,比如每5秒发一条定位数据给控制中心。
车台在短信方式下,设置时间间隔不小于30秒,默认时间间隔为5分钟,即300秒。车台不采用任何的压缩。命令协议号定位0x11。
车台在GPRS方式下,默认发送时间间隔为30秒。当发送时间间隔小于10秒或大于120秒时,车台发送非压缩数据包,不采用任何的压缩。命令协议号定位0x11。当发送时间间隔大于等于10秒而小于120秒时,车台发送压缩数据包或非压缩数据包(可设),即无论上传时间间隔是多少,客户也可采用非压缩方式,只传送一点的定位数据。
2.1.1信息内容的定义
传送非压缩的定位信息的时候,要把Pe包的协议号填充为0x11,内容长度填写为0x14,信息内容用表三的格式填写。
表三
日期时间 | 纬度 | 经度 | 速度 | 方向 | 状态位 | 时间间隔 |
占7字节 | 占4字节 | 占4字节 | 占1字节 | 占1字节 | 占2字节 | 占1字节 |
例如要发送一条非压缩的Pe包。省略号及后面的文字仅为注释,以下同。
@@Pe 0x0000 0xE91DB5FF 0x00000002 0x11 0x14 ………………Pe包固定部分 0x07D2 0x0C 0x0B 0x0B 0x15 0x19…………………………………日期、时间 0x088AB23C 0x18FB2BEA 0x00 0x66………………………………纬度经度速度方向 0x0001 0x07……………………………………………………………状态位、时间间隔 0x0956 0x0D 0x0A……………………………………………………校验和、结束标志 |
2.1.2信息内容的解释
下面是对定位数据格式的解释和数值计算方法的说明。
2.1.2.1时间日期
表示这条定位信息的时间。七个字节的分配如表四。
表四
年 | 月 | 日 | 时 | 分 | 秒 |
占用2字节 | 占用1节 | 占用1节 | 占用1节 | 占用1节 | 占用1节 |
例子中的日期时间是2002年12月11日11时21分25秒,只需要把这些数字转化成十六进制就可以了。
2.1.2.2纬度
占用4个字节,表示定位数据的纬度。数值范围-324,000,000至324,000,000 ,表示 -90度到+90度的范围。转化方式如下:
(1) 把OEM板输出的数据转化成以度为单位的小数
(2) 把转化后的小数乘以3,600,000(即毫秒)
(3) 再把乘过以后的数字转化成十六进制
例如把OEM板输出的数值转化成以度为单位的小数值是39.794916度,
39.794916*3600000=143261697
转化成十六进制数为88A0001
通过逆向变化可以把这个十六进制的数转化成十进制表示的度。
2.1.2.3经度
占用4个字节,表示定位数据的经度。数值范围是 -648,000,000~ +648,000,000,表示 -180度~ +180度。转化方法和纬度的转化方法完全相同。例子中代表的值是东经116.420546度。
2.1.2.4速度
占用1个字节,表示GPS天线的运行速度,表示范围0~255,单位千米/小时。例子中的值是79Km/H。转化只需要把原值去掉小数,然后转化成十六进制数即可。
2.1.2.5方向
占用1个字节,表示GPS天线的运行方向,以正北方为0度,顺时针增加,单位为2度。数值范围0~180。把OEM板输出的方向值取整除以2转化成十六进制就可以了。逆运算可以求出真实的方向值。例子中代表的实际值是204度。
2.1.2.6状态位
占用2个字节,用来传递车台的状态信息或者OEM板是否已经定位等。把这两个字节看作16个位,最低位是第0位,最高位是第15位。传送时先传送高位,后传送低位。各位代表的含义定义如表五。
状态位的两个字节高低位的分配:
高位 低位
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
表五
第0位 | 0:GPS不定位 1:GPS已定位 |
第1位 | 1:电源断电(如果有电池支持时) |
第2位 | 0:熄火状态 1:点火状态 |
第3位 | 输入口2状态 |
第4位 |
|
第5位 |
|
第6位 |
|
第7位 |
|
第8位 |
|
第9位 |
|
第10位 |
|
第11位 |
|
第12位 |
|
第13位 | 车载终端FLASH数据损坏报警 |
第14位 | 低报警 |
第15位 | 高报警(输入口1状态) |
信息头 | 保留位 | 车台ID | 序列号 | 协议号 | 长度 | 内容 | 校验和 | 结束符 |
4字节 | 2字节 | 4字节 | 4字节 | 1字节 | 1字节 | ≤255 | 2字节 | 2字节 |
@@Pe | 0x0000 | E91DB5FF | 00000001 | 0x00 | 0x00 |
| 0x03F0 | 0x0D0A |
===========================
UDP发送代码分析
void Cuuuu88Dlg::UDP_TestPocket_send()
{
SOCKET socket1;
InitWinsock();
struct sockaddr_in server;
int len =sizeof(server);
server.sin_family=AF_INET;
CString sport;
GetDlgItemText(IDC_PORT,sport);
GetDlgItemText(IDC_PORT,sport);
if(_wtoi(sport)>65535||_wtoi(sport)<1)
{
MessageBox(L"端口输入无效!");
}
server.sin_port=htons(_wtoi(sport)); ///server的监听端口
CString stime;
char addr[32];
GetDlgItemText(IDC_SERVER,stime);
if(stime.GetLength() <= 32)
{
wchar_t mWTemp[32];
char mTemp[32];
_tcscpy(mWTemp, stime);
WCharToMByte(mWTemp, mTemp, sizeof(mTemp)/sizeof(mTemp[0]));
if(INADDR_NONE != inet_addr(mTemp))
{
server.sin_addr.s_addr=inet_addr(mTemp); ///server的地址
}
else
{
MessageBox(L"IP地址无效!/r/n");
}
}
else
{
MessageBox(L"IP地址无效!/r/n");
}
//addr = stime.GetBuffer();
//WideCharToMultiByte(CP_ACP, 0, stime.GetBuffer(), -1, addr, 30, NULL, NULL);
//server.sin_addr.s_addr=inet_addr((char *)addr); ///server的地址
socket1=socket(AF_INET,SOCK_DGRAM,0);
int i=1;
while (i)
{
char buffer[21]={0};
char pe_head[7]="@@Pe";//头
char pe_id[5];
char pe_sn[5];
char pe_pr[2];
char pe_len[2];
char pe_check[3];
char pe_end[3];
char pe_poket[256]={0};
unsigned long phonenum=0;
CString temnu;
GetDlgItemText(IDC_CNID,temnu);
phonenum=_wtoi64(temnu.GetBuffer());
if(phonenum>4294967294||phonenum<3000000000)
MessageBox(L"车台号码错误!请重新输入");
memcpy(pe_id,(char *) &phonenum,4);
ChangeHL(pe_id,4);
int sn;
sn=1;
memcpy(pe_sn,(char *) &sn,4);
ChangeHL(pe_sn,4);//序列号
int pr=0x00;
memcpy(pe_pr,(char *) &pr,1);//协议版本
int length;
{
length=0;
memcpy(pe_len,(char *) &length,1);
}
int pe_count=0;
{//计算校验和
for(int it1=0;it1<6;it1++)
{
pe_count+=(BYTE)pe_head[it1];
}
for(int it2=0;it2<4;it2++)
{
pe_count+=(BYTE)pe_id[it2];
}
for(int it3=0;it3<4;it3++)
{
pe_count+=(BYTE)pe_sn[it3];
}
pe_count+=(BYTE)pe_pr[0];
pe_count+=(BYTE)pe_len[0];
}
memcpy(pe_check,(char *) &pe_count,2);//校验位
ChangeHL(pe_check,2);
sprintf(pe_end,"/r/n");//结束符
//==============拼接
memcpy(buffer,pe_head,6);
memcpy((buffer+6),pe_id,4);
memcpy((buffer+10),pe_sn,4);
memcpy((buffer+14),pe_pr,1);
memcpy((buffer+15),pe_len,1);
{//测试包,没有内容
memcpy((buffer+16),pe_check,2);
memcpy((buffer+18),pe_end,2);
}
CString udpsend;
udpsend.Empty();
char buffer2[20];
Bytes2String((BYTE *)buffer,buffer2,20);
udpsend=buffer2;
SetDlgItemText(IDC_SEND,udpsend);
//发给控件显示发送内容
if (sendto(socket1,buffer,128,0,(struct sockaddr*)&server,len)!=SOCKET_ERROR)
{
}
i--;
}
closesocket(socket1);// TODO: Add your control notification handler code here
}
========================================
相关函数
BOOL InitWinsock()
{
int Error;
WORD VersionRequested;
WSADATA WsaData;
VersionRequested=MAKEWORD(2,2);
Error=WSAStartup(VersionRequested,&WsaData); //启动WinSock2
if(Error!=0)
{
return FALSE;
}
else
{
if(LOBYTE(WsaData.wVersion)!=2||HIBYTE(WsaData.wHighVersion)!=2)
{
WSACleanup();
return FALSE;
}
}
return TRUE;
}
Cuuuu88Dlg::