收藏 分享(赏)

消息编码与解码.doc

上传人:11xg27ws 文档编号:5786808 上传时间:2019-03-17 格式:DOC 页数:16 大小:88KB
下载 相关 举报
消息编码与解码.doc_第1页
第1页 / 共16页
消息编码与解码.doc_第2页
第2页 / 共16页
消息编码与解码.doc_第3页
第3页 / 共16页
消息编码与解码.doc_第4页
第4页 / 共16页
消息编码与解码.doc_第5页
第5页 / 共16页
点击查看更多>>
资源描述

1、1.消息编码与解码用 C 实现 7-bit 编码和解码的算法如下:/ 7-bit 编码/ pSrc: 源字符串指针/ pDst: 目标编码串指针/ nSrcLength: 源字符串长度/ 返回: 目标编码串长度int gsmEncode7bit(const char* pSrc, unsigned char* pDst, int nSrcLength)int nSrc; / 源字符串的计数值int nDst; / 目标编码串的计数值int nChar; / 当前正在处理的组内字符字节的序号,范围是 0-7unsigned char nLeft; / 上一字节残余的数据/ 计数值初始化nSrc

2、= 0;nDst = 0;/ 将源串每 8 个字节分为一组,压缩成 7 个字节/ 循环该处理过程,直至源串被处理完/ 如果分组不到 8 字节,也能正确处理while(nSrc/ 取源字符串的计数值的最低 3 位nChar = nSrc / 处理源串的每个字节if(nChar = 0)/ 组内第一个字节,只是保存起来,待处理下一个字节时使用nLeft = *pSrc;else/ 组内其它字节,将其右边部分与残余数据相加,得到一个目标编码字节*pDst = (*pSrc nChar;/ 修改目标串的指针和计数值 pDst+;nDst+; / 修改源串的指针和计数值pSrc+; nSrc+;/ 返回

3、目标串长度return nDst; / 7-bit 解码/ pSrc: 源编码串指针/ pDst: 目标字符串指针/ nSrcLength: 源编码串长度/ 返回: 目标字符串长度int gsmDecode7bit(const unsigned char* pSrc, char* pDst, int nSrcLength)int nSrc; / 源字符串的计数值int nDst; / 目标解码串的计数值int nByte; / 当前正在处理的组内字节的序号,范围是 0-6unsigned char nLeft; / 上一字节残余的数据/ 计数值初始化nSrc = 0;nDst = 0;/ 组内

4、字节序号和残余数据初始化nByte = 0;nLeft = 0;/ 将源数据每 7 个字节分为一组,解压缩成 8 个字节/ 循环该处理过程,直至源数据被处理完/ 如果分组不到 7 字节,也能正确处理while(nSrc/ 将源字节右边部分与残余数据相加,去掉最高位,得到一个目标解码字节*pDst = (*pSrc (7-nByte);/ 修改目标串的指针和计数值pDst+;nDst+;/ 修改字节计数值nByte+;/ 到了一组的最后一个字节if(nByte = 7)/ 额外得到一个目标解码字节*pDst = nLeft;/ 修改目标串的指针和计数值pDst+;nDst+;/ 组内字节序号和残

5、余数据初始化nByte = 0;nLeft = 0;/ 修改源串的指针和计数值pSrc+;nSrc+;*pDst = 0;/ 返回目标串长度return nDst;需要指出的是,7-bit 的字符集与 ANSI 标准字符集不完全一致,在 0x20 以下也排布了一些可打印字符,但英文字母、阿拉伯数字和常用符号的位 置两者是一样的。用上面介绍的算法收发纯英文短消息,一般情况应该是够用了。如果是法语、德语、西班牙语等,含有 “?”、“”这一类字符,则要按上面编码的输出去查表,请参阅 GSM 03.38 的规定。8-bit 编码其实没有规定什么具体的算法,不需要介绍。UCS2 编码是将每个字符(1-2

6、 个字节)按照 ISO/IEC10646 的规定,转变为 16 位的 Unicode宽字符。在 Windows 系统中,特 别是在 2000/XP 中,可以简单地调用 API 函数实现编码和解码。如果没有系统的支持,比如用单片机控制手机模块收发短消息,只好用查表法解决了。Windows 环境下,用 C 实现 UCS2 编码和解码的算法如下:/ UCS2 编码/ pSrc: 源字符串指针/ pDst: 目标编码串指针/ nSrcLength: 源字符串长度/ 返回: 目标编码串长度int gsmEncodeUcs2(const char* pSrc, unsigned char* pDst, i

7、nt nSrcLength)int nDstLength; / UNICODE 宽字符数目WCHAR wchar128; / UNICODE 串缓冲区/ 字符串UNICODE 串nDstLength = :MultiByteToWideChar(CP_ACP, 0, pSrc, nSrcLength, wchar, 128);/ 高低字节对调,输出for(int i=0; i/ 先输出高位字节*pDst+ = wchari 8;/ 后输出低位字节*pDst+ = wchari / 返回目标编码串长度return nDstLength * 2;/ UCS2 解码/ pSrc: 源编码串指针/ p

8、Dst: 目标字符串指针/ nSrcLength: 源编码串长度/ 返回: 目标字符串长度int gsmDecodeUcs2(const unsigned char* pSrc, char* pDst, int nSrcLength)int nDstLength; / UNICODE 宽字符数目WCHAR wchar128; / UNICODE 串缓冲区/ 高低字节对调,拼成 UNICODEfor(int i=0; i/ 先高位字节wchari = *pSrc+ 字符串nDstLength = :WideCharToMultiByte(CP_ACP, 0, wchar, nSrcLength/

9、2, pDst, 160, NULL, NULL);/ 输出字符串加个结束符 pDstnDstLength = 0; / 返回目标字符串长度return nDstLength;用以上编码和解码模块,还不能将短消息字符串编码为 PDU 串需要的格式,也不能直接将PDU 串中的用户信息解码为短消息字符串,因为还差一个在可打 印字符串和字节数据之间相互转换的环节。可以循环调用 sscanf 和 sprintf 函数实现这种变换。下面提供不用这些函数的算法,它们也适用于单片机、 DSP 编程环境。/ 可打印字符串转换为字节数据/ 如:“C8329BFD0E01“ 0xC8, 0x32, 0x9B, 0

10、xFD, 0x0E, 0x01/ pSrc: 源字符串指针/ pDst: 目标数据指针/ nSrcLength: 源字符串长度/ 返回: 目标数据长度int gsmString2Bytes(const char* pSrc, unsigned char* pDst, int nSrcLength)for(int i=0; i/ 输出高 4 位if(*pSrc=0 / 0x0-0xf 的字符查找表for(int i=0; i/ 输出低 4 位*pDst+ = tab*pSrc 4;/ 输出高 4 位*pDst+ = tab*pSrc pSrc+;/ 输出字符串加个结束符*pDst = 0;/ 返

11、回目标字符串长度return nSrcLength * 2;2,消息发送/ 用户信息编码方式#define GSM_7BIT 0#define GSM_8BIT 4#define GSM_UCS2 8/ 短消息参数结构,编码/解码共用/ 其中,字符串以 0 结尾typedef struct char SCA16; / 短消息服务中心号码(SMSC 地址)char TPA16; / 目标号码或回复号码(TP-DA 或 TP-RA)char TP_PID; / 用户信息协议标识(TP-PID)char TP_DCS; / 用户信息编码方式(TP-DCS)char TP_SCTS16; / 服务时间

12、戳字符串(TP_SCTS), 接收时用到char TP_UD161; / 原始用户信息( 编码前或解码后的 TP-UD)char index; / 短消息序号,在读取时用到 SM_PARAM;大家已经注意到 PDU 串中的号码和时间,都是两两颠倒的字符串。利用下面两个函数可进行正反变换:/ 正常顺序的字符串转换为两两颠倒的字符串,若长度为奇数,补 F凑成偶数/ 如:“8613851872468“ “683158812764F8“/ pSrc: 源字符串指针/ pDst: 目标字符串指针/ nSrcLength: 源字符串长度/ 返回: 目标字符串长度int gsmInvertNumbers(c

13、onst char* pSrc, char* pDst, int nSrcLength)int nDstLength; / 目标字符串长度char ch; / 用于保存一个字符/ 复制串长度nDstLength = nSrcLength;/ 两两颠倒for(int i=0; ich = *pSrc+; / 保存先出现的字符*pDst+ = *pSrc+; / 复制后出现的字符*pDst+ = ch; / 复制先出现的字符/ 源串长度是奇数吗?if(nSrcLength / 补FnDstLength+; / 目标串长度加 1/ 输出字符串加个结束符*pDst = 0;/ 返回目标字符串长度ret

14、urn nDstLength;/ 两两颠倒的字符串转换为正常顺序的字符串/ 如:“683158812764F8“ “8613851872468“/ pSrc: 源字符串指针/ pDst: 目标字符串指针/ nSrcLength: 源字符串长度/ 返回: 目标字符串长度int gsmSerializeNumbers(const char* pSrc, char* pDst, int nSrcLength)int nDstLength; / 目标字符串长度char ch; / 用于保存一个字符/ 复制串长度nDstLength = nSrcLength;/ 两两颠倒for(int i=0; ich

15、 = *pSrc+; / 保存先出现的字符*pDst+ = *pSrc+; / 复制后出现的字符*pDst+ = ch; / 复制先出现的字符/ 最后的字符是F吗?if(*(pDst-1) = F)pDst-;nDstLength-; / 目标字符串长度减 1/ 输出字符串加个结束符*pDst = 0;/ 返回目标字符串长度return nDstLength;以下是 PDU 全串的编解码模块。为简化编程,有些字段用了固定值。/ PDU 编码,用于编制、发送短消息/ pSrc: 源 PDU 参数指针/ pDst: 目标 PDU 串指针/ 返回: 目标 PDU 串长度int gsmEncodePd

16、u(const SM_PARAM* pSrc, char* pDst)int nLength; / 内部用的串长度int nDstLength; / 目标 PDU 串长度unsigned char buf256; / 内部用的缓冲区/ SMSC 地址信息段nLength = strlen(pSrc-SCA); / SMSC 地址字符串的长度 buf0 = (char)(nLength / SMSC 地址信息长度buf1 = 0x91; / 固定: 用国际格式号码nDstLength = gsmBytes2String(buf, pDst, 2); / 转换 2 个字节到目标 PDU 串nDst

17、Length += gsmInvertNumbers(pSrc-SCA, / 转换SMSC 到目标 PDU 串/ TPDU 段基本参数、目标地址等nLength = strlen(pSrc-TPA); / TP-DA 地址字符串的长度buf0 = 0x11; / 是发送短信(TP-MTI=01),TP-VP 用相对格式(TP-VPF=10)buf1 = 0; / TP-MR=0buf2 = (char)nLength; / 目标地址数字个数(TP-DA 地址字符串真实长度)buf3 = 0x91; / 固定: 用国际格式号码nDstLength += gsmBytes2String(buf,

18、/转换 4 个字节到目标PDU串nDstLength += gsmInvertNumbers(pSrc-TPA, / 转换 TP-DA 到目标 PDU 串/ TPDU 段协议标识、编码方式、用户信息等nLength = strlen(pSrc-TP_UD); / 用户信息字符串的长度buf0 = pSrc-TP_PID; / 协议标识(TP-PID)buf1 = pSrc-TP_DCS; / 用户信息编码方式 (TP-DCS)buf2 = 0; / 有效期(TP-VP)为 5 分钟if(pSrc-TP_DCS = GSM_7BIT) / 7-bit 编码方式buf3 = nLength; /

19、编码前长度nLength = gsmEncode7bit(pSrc-TP_UD, / 转换 TP-DA 到目标 PDU 串else if(pSrc-TP_DCS = GSM_UCS2)/ UCS2 编码方式buf3 = gsmEncodeUcs2(pSrc-TP_UD, / 转换 TP-DA 到目标 PDU 串nLength = buf3 + 4; / nLength 等于该段数据长度else/ 8-bit 编码方式buf3 = gsmEncode8bit(pSrc-TP_UD, / 转换 TP-DA 到目标 PDU 串nLength = buf3 + 4; / nLength 等于该段数据长

20、度nDstLength += gsmBytes2String(buf, / 转换该段数据到目标 PDU 串/ 返回目标字符串长度return nDstLength;/ PDU 解码,用于接收、阅读短消息/ pSrc: 源 PDU 串指针/ pDst: 目标 PDU 参数指针/ 返回: 用户信息串长度int gsmDecodePdu(const char* pSrc, SM_PARAM* pDst)int nDstLength; / 目标 PDU 串长度unsigned char tmp; / 内部用的临时字节变量unsigned char buf256; / 内部用的缓冲区/ SMSC 地址信

21、息段gsmString2Bytes(pSrc, / 取长度tmp = (tmp - 1) * 2; / SMSC 号码串长度pSrc += 4; / 指针后移gsmSerializeNumbers(pSrc, pDst-SCA, tmp); / 转换 SMSC 号码到目标 PDU 串pSrc += tmp; / 指针后移/ TPDU 段基本参数、回复地址等gsmString2Bytes(pSrc, / 取基本参数pSrc += 2; / 指针后移if(tmp / 取长度if(tmp / 调整奇偶性pSrc += 4; / 指针后移gsmSerializeNumbers(pSrc, pDst-T

22、PA, tmp); / 取 TP-RA 号码pSrc += tmp; / 指针后移/ TPDU 段协议标识、编码方式、用户信息等gsmString2Bytes(pSrc, (unsigned char*) / 取协议标识(TP-PID)pSrc += 2; / 指针后移gsmString2Bytes(pSrc, (unsigned char*) / 取编码方式(TP-DCS)pSrc += 2; / 指针后移gsmSerializeNumbers(pSrc, pDst-TP_SCTS, 14); / 服务时间戳字符串(TP_SCTS) pSrc += 14; / 指针后移gsmString2B

23、ytes(pSrc, / 用户信息长度 (TP-UDL)pSrc += 2; / 指针后移if(pDst-TP_DCS = GSM_7BIT) / 7-bit 解码nDstLength = gsmString2Bytes(pSrc, buf, tmp / 格式转换gsmDecode7bit(buf, pDst-TP_UD, nDstLength); / 转换到 TP-DUnDstLength = tmp;else if(pDst-TP_DCS = GSM_UCS2)/ UCS2 解码nDstLength = gsmString2Bytes(pSrc, buf, tmp * 2); / 格式转换

24、nDstLength = gsmDecodeUcs2(buf, pDst-TP_UD, nDstLength); / 转换到 TP-DUelse/ 8-bit 解码nDstLength = gsmString2Bytes(pSrc, buf, tmp * 2); / 格式转换nDstLength = gsmDecode8bit(buf, pDst-TP_UD, nDstLength); / 转换到 TP-DU/ 返回目标字符串长度return nDstLength;依照 GSM 07.05,发送短消息用 AT+CMGS 命令,阅读短消息用 AT+CMGR 命令,列出短消息用 AT+CMGL 命

25、令,删除短消息用 AT+CMGD 命令。但 AT+CMGL 命令能够读出所有的短消息,所以我们用它实现阅读短消息功能,而没用 AT+CMGR。下面是发送、读取和删除短消息的实现代码:/ 发送短消息/ pSrc: 源 PDU 参数指针BOOL gsmSendMessage(const SM_PARAM* pSrc)int nPduLength; / PDU 串长度unsigned char nSmscLength; / SMSC 串长度int nLength; / 串口收到的数据长度char cmd16; / 命令串char pdu512; / PDU 串char ans128; / 应答串nP

26、duLength = gsmEncodePdu(pSrc, pdu); / 根据 PDU 参数,编码 PDU 串strcat(pdu, “x01a“); / 以 Ctrl-Z 结束gsmString2Bytes(pdu, / 取 PDU 串中的 SMSC 信息长度nSmscLength+; / 加上长度字节本身/ 命令中的长度,不包括 SMSC 信息长度,以数据字节计sprintf(cmd, “AT+CMGS=%dr“, nPduLength / 2 - nSmscLength); / 生成命令WriteComm(cmd, strlen(cmd); / 先输出命令串nLength = Read

27、Comm(ans, 128); / 读应答数据/ 根据能否找到“rn “ 决定成功与否if(nLength = 4 / 得到肯定回答,继续输出 PDU 串nLength = ReadComm(ans, 128); / 读应答数据/ 根据能否找到“+CMS ERROR“决定成功与否if(nLength 0 return FALSE;/ 读取短消息/ 用+CMGL 代替 +CMGR,可一次性读出全部短消息/ pMsg: 短消息缓冲区,必须足够大/ 返回: 短消息条数int gsmReadMessage(SM_PARAM* pMsg)int nLength; / 串口收到的数据长度int nMsg;

28、 / 短消息计数值char* ptr; / 内部用的数据指针char cmd16; / 命令串char ans1024; / 应答串nMsg = 0;ptr = ans;sprintf(cmd, “AT+CMGLr“); / 生成命令WriteComm(cmd, strlen(cmd); / 输出命令串nLength = ReadComm(ans, 1024); / 读应答数据/ 根据能否找到“+CMS ERROR“决定成功与否if(nLength 0 / 跳过“+CMGL:“sscanf(ptr, “%d“, / 读取序号TRACE(“ index=%dn“,pMsg-index);ptr

29、= strstr(ptr, “rn“); / 找下一行ptr += 2; / 跳过“rn“gsmDecodePdu(ptr, pMsg); / PDU 串解码pMsg+; / 准备读下一条短消息nMsg+; / 短消息计数加 1return nMsg;/ 删除短消息/ index: 短消息序号,从 1 开始BOOL gsmDeleteMessage(const int index)int nLength; / 串口收到的数据长度char cmd16; / 命令串char ans128; / 应答串sprintf(cmd, “AT+CMGD=%dr“, index); / 生成命令/ 输出命令串

30、WriteComm(cmd, strlen(cmd);/ 读应答数据nLength = ReadComm(ans, 128);/ 根据能否找到“+CMS ERROR“决定成功与否if(nLength 0 return FALSE;以上发送 AT 命令过程中用到了 WriteComm 和 ReadComm 函数,它们是用来读写串口的,依赖于具体的操作系统。在 Windows 环境下, 除了用 MSComm 控件,以及某些现成的串口通信类之外,也可以简单地调用一些 Windows API 用实现。以下是利用 API 实现的主要代码,注意我们用的是超时控制的同步(阻塞) 模式。/ 串口设备句柄HAN

31、DLE hComm;/ 打开串口/ pPort: 串口名称或设备路径,可用“COM1“或“.COM1“两种方式,建议用后者/ nBaudRate: 波特率/ nParity: 奇偶校验/ nByteSize: 数据字节宽度/ nStopBits: 停止位BOOL OpenComm(const char* pPort, int nBaudRate, int nParity, int nByteSize, int nStopBits)DCB dcb; / 串口控制块COMMTIMEOUTS timeouts = / 串口超时控制参数100, / 读字符间隔超时时间: 100 ms1, / 读操作时

32、每字符的时间: 1 ms (n 个字符总共为 n ms)500, / 基本的(额外的) 读超时时间: 500 ms1, / 写操作时每字符的时间: 1 ms (n 个字符总共为 n ms)100; / 基本的(额外的) 写超时时间: 100 mshComm = CreateFile(pPort, / 串口名称或设备路径GENERIC_READ | GENERIC_WRITE, / 读写方式0, / 共享方式:独占NULL, / 默认的安全描述符OPEN_EXISTING, / 创建方式0, / 不需设置文件属性NULL); / 不需参照模板文件if(hComm = INVALID_HANDLE

33、_VALUE) return FALSE; / 打开串口失败GetCommState(hComm, / 取 DCBdcb.BaudRate = nBaudRate;dcb.ByteSize = nByteSize;dcb.Parity = nParity;dcb.StopBits = nStopBits;SetCommState(hComm, / 设置 DCBSetupComm(hComm, 4096, 1024); / 设置输入输出缓冲区大小SetCommTimeouts(hComm, / 设置超时return TRUE;/ 关闭串口BOOL CloseComm()return CloseH

34、andle(hComm);/ 写串口/ pData: 待写的数据缓冲区指针/ nLength: 待写的数据长度void WriteComm(void* pData, int nLength)DWORD dwNumWrite; / 串口发出的数据长度WriteFile(hComm, pData, (DWORD)nLength, / 读串口/ pData: 待读的数据缓冲区指针/ nLength: 待读的最大数据长度/ 返回: 实际读入的数据长度int ReadComm(void* pData, int nLength)DWORD dwNumRead; / 串口收到的数据长度ReadFile(hComm, pData, (DWORD)nLength, return (int)dwNumRead;

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 生活休闲 > 社会民生

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报