Q PDU的核心编码方式已经清楚了,如何实现用AT命令收发短消息呢?
A 在上篇中,我们已经讨论了7-bit, 8bit和UCS2这几种PDU用户信息的编码方式,并且给出了实现代码。现在,重点描述PDU全串的编码和解码过程,以及GSM 07.05的AT命令实现方法。这些是底层的核心代码,为了保证代码的可移植性,我们尽可能不用MFC的类,必要时用ANSI C标准库函数。
首先,定义如下常量和结构:
// 用户信息编码方式 #define GSM_7BIT 0 #define GSM_8BIT 4 #define GSM_UCS2 8 // 短消息参数结构,编码/解码共用 // 其中,字符串以0结尾 typedef struct { char SCA[16]; // 短消息服务中心号码(SMSC地址) char TPA[16]; // 目标号码或回复号码(TP-DA或TP-RA) char TP_PID; // 用户信息协议标识(TP-PID) char TP_DCS; // 用户信息编码方式(TP-DCS) char TP_SCTS[16]; // 服务时间戳字符串(TP_SCTS), 接收时用到 char TP_UD[161]; // 原始用户信息(编码前或解码后的TP-UD) char index; // 短消息序号,在读取时用到 } SM_PARAM;
大家已经注意到PDU串中的号码和时间,都是两两颠倒的字符串。利用下面两个函数可进行正反变换:
// 正常顺序的字符串转换为两两颠倒的字符串,若长度为奇数,补'F'凑成偶数 // 如:"8613851872468" --> "683158812764F8" // pSrc: 源字符串指针 // pDst: 目标字符串指针 // nSrcLength: 源字符串长度 // 返回: 目标字符串长度 int gsmInvertNumbers(const char* pSrc, char* pDst, int nSrcLength) { int nDstLength; // 目标字符串长度 char ch; // 用于保存一个字符 // 复制串长度 nDstLength = nSrcLength; // 两两颠倒 for(int i=0; i<nSrcLength;i+=2) { ch = *pSrc++; // 保存先出现的字符 *pDst++ = *pSrc++; // 复制后出现的字符 *pDst++ = ch; // 复制先出现的字符 } // 源串长度是奇数吗? if(nSrcLength & 1) { *(pDst-2) = 'F'; // 补'F' nDstLength++; // 目标串长度加1 } // 输出字符串加个结束符 *pDst = '/0'; // 返回目标字符串长度 return 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; i<nSrcLength;i+=2) { ch = *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 gsmEncodePdu(const SM_PARAM* pSrc, char* pDst) { int nLength; // 内部用的串长度 int nDstLength; // 目标PDU串长度 unsigned char buf[256]; //