(八)Linux搭载4G模块——PDU短信格式的获取与解码

一、前言

在上一篇:(七)Linux搭载4G模块——AT指令实现短信包的获取和删除,我们知道了怎么用AT指令取获取存储器中的短信包,而且短信包的格式分为两种,分别是Text格式和PDU格式。PDU格式可以解码出中英文,所以后面获取短信我们都用PDU格式。

二、PDU短信格式的获取与解码

2.1拆分PDU包

下面是我收到某个号码的PDU包,短信内容是"你好"。
PDU包的构成=中心号码长度+中心号码+TPDU头字节+源号码长度+源号码+协议标识+数据编码方案+日期时间+数据长度+数据
在这里插入图片描述
PDU包解码和我们发送信息时打包的规则都差不多,逆着来就好。不过纯英文PDU包和中文PDU包的数据部分解码是很大不同的,英文需要用到7bit解码方法,中文的先从字符流转字节流,再将字节流的Unicde格式转成UTF8格式即可。具体原理可看此文章:中英文短信解码

2.2 对PDU的数据部分操作

(1)字符流转字节流

具体原理可看此篇文章:字符流与字节流的相互转换原理及代码(Linux C)

int str_to_byte(char *source, char *dest, int source_len)
{
    char    highbyte;//高字节
    char    lowbyte;//低字节
    int     i = 0;

    //在所接收的的信息字符串中,只有数字和字母,所以只对数字和字母作处理即可
    for (i; 2*i < source_len; i ++)
    {
        highbyte = toupper(source[i*2]);//将所有小写字母转成大写
        lowbyte = toupper(source[i*2 +1]);

        if(highbyte <= 0x39)
            highbyte -= 0x30;
        else
            highbyte -= 0x37;

        if(lowbyte <= 0x39)
            lowbyte -= 0x30;
        else
            lowbyte -= 0x37;

        dest[i] = (highbyte << 4) | lowbyte;
        //printf("dest[%d]=%02x\n",i, dest[i]);
    } 

    return 0;
}

(2)中文解码——Unicode编码转UTF8编码

这里Unicode编码转UTF8编码使用了Linux系统下的iconv系列的函数,需要了解它们的使用方法。
在这里插入图片描述

#include <iconv.h>
iconv_t iconv_open(const char* tcode, const char* fromecode);

iconv_open()函数申请一个转换描述符,转换字符序列从编码fromcode到编码tcode
转换描述符包含转换状态,调用icvon_open()以后,转换处于初始状态;调用icvon()以后,改变装换描述符的转换状态。注意iconv_open()返回的是一个iconv_t类型的变量,然后供iconv()和iconv_close()使用。
返回值:iconv_open()函数返回一个新申请的返回描述符。 出错时返回( iconv_t)-1

size_t iconv(iconv_t cd,
                    char **inbuf, size_t *inbytesleft,
                    char **outbuf, size_t *outbytesleft);

iconv()函数实现编码转换,此函数从inbuf中读取字符,转换后输出到outbuf中,inbytesleft用以记录还未转换的字符数,outbytesleft用以记录输出缓冲的剩余空间。注意:inbuf和outbuf都必须是有存储空间的不能定义为常量。

int iconv_close(iconv_t cd);

iconv_close()函数用于关闭转换句柄,释放资源。

int unicode_to_utf8(char *source, size_t source_len, char *dest, size_t dest_len)
{
    int     i = 0;
    char    temp;
    int     retval = -1;
    iconv_t cd;
    char    unicode_buf[512] = {0};
    

    if (!source)
    {
        printf("Invalid argument!\n");
        return -1;
    }

    str_to_byte(source, unicode_buf, strlen(source));
	//注意这里一定不能使用strlen来求长度,因为字节序中有英文的话遇上0x00就会停止计算长度,求出来的长度比实际长度小。
    for (i; i < sizeof(unicode_buf); i +=2)
    {
        temp = unicode_buf[i];
        unicode_buf[i] = unicode_buf[i+1];
        unicode_buf[i+1] = temp;
    }

    cd = iconv_open("UTF-8","UNICODE");
    if (cd < 0)
    {
        printf("iconv_open() failure!\n");
        return -2;
    }

    char    *source_ptr = unicode_buf;
    

    retval =  iconv(cd, &source_ptr, &source_len, &dest, &dest_len);
    if(retval < 0)
    {
        printf("Unicode to utf-8 failure:%s\n", strerror(errno));
        iconv_close(cd);
        return -3;
    }

    //printf("Unicode to utf-8 successfully:%s\n", dest);

    iconv_close(cd);
    return 0;
}

(3)英文数据解码——7bit编码

GSM默认采用7bit编码,实际上,7位编码是种压缩算法,因为,ASCII码(不包括扩展ASCII),其值小于0x80,最高位bit8是0,被忽略了;而7bit编码就利用了这一位来存储数据;其编码时,依次将下一7位编码的后几位逐次移至前面,形成新的8位编码。
在这里插入图片描述

int decode7bit(const unsigned char* source, char* dest, int source_len)
{
        int nsrc = 0;  
        int ndst = 0;
        int nbyte = 0;
        unsigned char nleft = 0;

        if (!source||!dest||(source_len<=0))
        {
            printf("[%s]Invalid parameter\n",__func__);
            return -1;
        }
        
        while (nsrc < source_len)
        {
            *dest = ((*source << nbyte) | nleft) & 0x7f;
            nleft = *source >> (7-nbyte);
            dest++;
            ndst++;
            nbyte++;
            if(nbyte == 7)
            {
                *dest = nleft;
                dest++;
                ndst++;
                nbyte = 0;
                nleft = 0;
            }
            source++;
            nsrc++;
        }
        *dest = 0;

        return ndst;
}

int english_decod(char *source, char *dest, int source_len)
{
    char temp_buf[512] = {0};

    if (!source|| !dest)
    {
        printf("Invalid argument.\n");
        return -1;
    }

    str_to_byte(source, temp_buf, source_len);
    decode7bit(temp_buf, dest, strlen(temp_buf));

    return 0;
}

2.3获取并解析所有短信——get_all_sms()

我们知道在整个PDU包中,包括了中心号码长度,源号码长度,数据长度。但我们获取到它们的时候是16进制的字符串的形式,这就需要我们进行计算再使用它们了,下面是我写的一个算法,把它们计算转成了整数形式,这样用的时候就方便多了。

int calculate_len(char *ptr)
{
    int len;
    int highbyte;
    int lowbyte;

    if (*ptr < 0x39)
        highbyte = (*ptr - 0x30)*16;
    else
        highbyte = (*ptr-0x37)*16;

    //printf("highbyte:%d\n", highbyte);

    ptr++;
    if (*ptr < 0x39)
        lowbyte = *ptr - 0x30;
    else
        lowbyte = *ptr - 0x37;

    //printf("lowbyte:%d\n", lowbyte);
    len = highbyte + lowbyte;
    //printf("len:%d\n", len);

    return len;
}

int get_all_sms(ttyusb_ctx_t *ttyusb_ctx, char *sms_buf, int sms_buf_len)
{
    int     retval = -1;
    char    temp_buf[1024] = {0};
    char    *start_ptr = NULL;
    int     center_number_len = 0;
    char    phone_number[32] = {0};
    int     phone_len = 0;
    int     phone_flag = 0;
    int     encoding = 0;//编码
    int     data_len = 0;
    char    send_buf[128] = {0};
    char    time_buf[13] = {0};
    int     index = 0;
    int     j = 0;
    char    temp;
    char    data_buf[1024] = {0};

    if (!ttyusb_ctx)
    {
        log_error("[%s]Invalid argument.\n", __func__);
        return -1;
    }

    retval = send_at_cmd(ttyusb_ctx, "AT+CSDH=1\r", "OK", NULL, 0);
    if(retval < 0)
    {
        printf("Send command AT+CSDH=1 failure!\n");
        return -2;
    }


    while (1)
    { 
        memset(send_buf, 0 , sizeof(send_buf));
        sprintf(send_buf, "AT+CMGR=%d\r", index);
        memset(temp_buf, 0, sizeof(temp_buf));
        retval = send_at_cmd(ttyusb_ctx, send_buf, "+CMGR", temp_buf, sizeof(temp_buf));
        if(retval < 0)
        {
            printf("There is no index:%d\n", index);
            break;
            return -3;
        }
        start_ptr = strstr(temp_buf, "+CMGR");
        
        
        start_ptr = strstr(start_ptr, "\r\n");
        start_ptr += 2;//让指针指向PDU首部
        
        center_number_len = calculate_len(start_ptr);//计算中心号码长度
        start_ptr = start_ptr + 2 + center_number_len*2 + 2;
        //printf("电话长度:%s\n",start_ptr);

        phone_len = calculate_len(start_ptr);//计算发送人号码长度
        //printf("phone_len:%d\n", phone_len);
        //如果号码是奇数位,补上一个F计算
        if(phone_len%2 != 0)
        {
            phone_len++;
            phone_flag = 1;
        }
        //获取发送人号码

        strncpy(phone_number, start_ptr+2+2, phone_len);
        for(j = 0; j < phone_len; j +=2)
        {
            temp = phone_number[j];
            phone_number[j] = phone_number[j+1];
            if(phone_flag && ((phone_len -2) == j))
            {
                phone_number[j+1] ='\0';
            }
            else
            {
                phone_number[j+1] = temp;
            }
        }

        printf("PDU package:%s\n", temp_buf);
        printf("SMS Index:%d\n", index);
        printf("Sender:%s\n", phone_number);


        //log_debug("target_number:%s\n", target_number);
        //printf("phone_number:%s\n", phone_number);
        start_ptr = start_ptr + 2 + 2 + phone_len  + 2;
        //printf("数据编码开始:%s\n",start_ptr);
        encoding = calculate_len(start_ptr);//获取编码格式
        start_ptr = start_ptr + 2 ;

        memset(time_buf, 0 ,sizeof(time_buf));
        snprintf(time_buf, 12+1, "%s", start_ptr);

        printf("Receive time:");
        for(j = 0; j < 12; j +=2)
        {
            temp = time_buf[j];
            time_buf[j] = time_buf[j+1];
            time_buf[j+1] = temp;
            printf("-%c%c", time_buf[j], time_buf[j+1]);
        }
        printf("\n");
        
        
        start_ptr = start_ptr + 12 + 2;
        
        data_len = calculate_len(start_ptr);
        data_len = data_len*2;
        start_ptr = start_ptr +2;
        memset(data_buf, 0, data_len);
        snprintf(data_buf, data_len+1, "%s", start_ptr);

        if(encoding)
        {
            memset(sms_buf, 0, sms_buf_len);
            retval = unicode_to_utf8(data_buf, sizeof(data_buf), sms_buf, sms_buf_len);
        }
        else
        {
            //log_debug("This is the English code\n");
            english_decod(start_ptr, sms_buf, data_len);
            
        }

        printf("SMS:%s\n", sms_buf);
        start_ptr = NULL;
        index++;

        printf("-----------------------------------------------------------------------------------------------------\n");
            
    }

}

2.4 获取并解析指定的短信——get_specified_sms()

int get_specified_sms(ttyusb_ctx_t *ttyusb_ctx, char *sms_buf, int sms_buf_len, char *target_number)
{
    int     retval = -1;
    char    temp_buf[1024] = {0};
    char    *start_ptr = NULL;
    int     center_number_len = 0;
    char    phone_number[32] = {0};
    int     phone_len = 0;
    int     phone_flag = 0;
    int     encoding = 0;//编码
    int     data_len = 0;
    char    send_buf[128] = {0};
    char    time_buf[13] = {0};
    int     index = 0;
    int     j = 0;
    char    temp;
    char    data_buf[1024] = {0};
    int     sms_flag = 1;

    if (!ttyusb_ctx)
    {
        log_error("[%s]Invalid argument!\n", __func__);
        return -1;
    }

    retval = send_at_cmd(ttyusb_ctx, "AT+CSDH=1\r", "OK", NULL, 0);
    if(retval < 0)
    {
        printf("Send command AT+CSDH=1 failure!\n");
        return -2;
    }
    

    while (1)
    {
        memset(send_buf, 0 , sizeof(send_buf));
        sprintf(send_buf, "AT+CMGR=%d\r", index);
        memset(temp_buf, 0, sizeof(temp_buf));

        retval = send_at_cmd(ttyusb_ctx, send_buf, "OK", temp_buf, sizeof(temp_buf));
        if(retval < 0)
        {
            printf("There is no message %d\n", index);
            return -3;
        }

        start_ptr = strstr(temp_buf, "+CMGR");
        if(NULL == start_ptr)
        {
            printf("There is no message %d\n", index);
            break;
        }

        //printf("PDU包:%s\n", start_ptr);
        start_ptr = strstr(start_ptr, "\r\n");
        start_ptr += 2;//让指针指向PDU首部
        
        center_number_len = calculate_len(start_ptr);//计算中心号码长度
        start_ptr = start_ptr + 2 + center_number_len*2 + 2;
        //printf("电话长度:%s\n",start_ptr);

        phone_len = calculate_len(start_ptr);//计算发送人号码长度
        //printf("phone_len:%d\n", phone_len);
        //如果号码是奇数位,补上一个F计算
        if(phone_len%2 != 0)
        {
            phone_len++;
            phone_flag = 1;
        }
        //获取发送人号码

        strncpy(phone_number, start_ptr+2+2, phone_len);
        for(j = 0; j < phone_len; j +=2)
        {
            temp = phone_number[j];
            phone_number[j] = phone_number[j+1];
            if(phone_flag && ((phone_len -2) == j))
            {
                phone_number[j+1] ='\0';
            }
            else
            {
                phone_number[j+1] = temp;
            }
        }

        if(strncmp(target_number, phone_number, strlen(phone_number)))
        {
            
            start_ptr = NULL;
            index++;
            continue;
        }

        printf("PDU package:%s\n", temp_buf);
        printf("SMS Index:%d\n", index);
        printf("Sender:%s\n", phone_number);


        //log_debug("target_number:%s\n", target_number);
        //printf("phone_number:%s\n", phone_number);
        start_ptr = start_ptr + 2 + 2 + phone_len  + 2;
        //printf("数据编码开始:%s\n",start_ptr);
        encoding = calculate_len(start_ptr);//获取编码格式
        start_ptr = start_ptr + 2 ; 
        snprintf(time_buf, 12+1, "%s", start_ptr);

        printf("Receive time:");
        for(j = 0; j < 12; j +=2)
        {
            temp = time_buf[j];
            time_buf[j] = time_buf[j+1];
            time_buf[j+1] = temp;
            printf("-%c%c", time_buf[j], time_buf[j+1]);
        }
        printf("\n");
        
        start_ptr = start_ptr + 12 + 2;
        
        data_len = calculate_len(start_ptr);
        data_len = data_len*2;
        start_ptr = start_ptr +2;
        //log_debug("start_ptr3:%s\n",start_ptr);
        
        memset(data_buf, 0, data_len);
        snprintf(data_buf, data_len+1, "%s", start_ptr);

        //printf("The data PDU is %s\n", data_buf);
        memset(sms_buf, 0, sms_buf_len);
        if(encoding)
        {
            retval = unicode_to_utf8(data_buf, sizeof(data_buf), sms_buf, sms_buf_len);
        }
        else
        {
            //log_debug("This is the English code\n");
            english_decod(start_ptr, sms_buf, data_len);
            
        }

        printf("SMS:%s\n", sms_buf);
        start_ptr = NULL;
        index++;
        
        printf("-----------------------------------------------------------------------------------------------------\n");
            
    }

}

2.5 删除指定号码短信——delete_sms()

int delete_sms(ttyusb_ctx_t *ttyusb_ctx, char *target_number)
{
    int     retval = -1;
    char    temp_buf[1024] = {0};
    char    *start_ptr = NULL;
    int     center_number_len = 0;
    char    phone_number[32] = {0};
    int     phone_len = 0;
    int     phone_flag = 0;
    char    send_buf[128] = {0};
    char    time_buf[13] = {0};
    int     index = 0;
    int     j = 0;
    char    temp;
    char    data_buf[1024] = {0};
    int     sms_flag = 1;

    if (!ttyusb_ctx)
    {
        printf("[%s]Invalid argument!\n", __func__);
        return -1;
    }

    retval = send_at_cmd(ttyusb_ctx, "AT+CSDH=1\r", "OK", NULL, 0);
    if(retval < 0)
    {
        printf("Send command AT+CSDH=1 failure!\n");
        return -2;
    }
    

    while (1)
    {
        memset(send_buf, 0 , sizeof(send_buf));
        sprintf(send_buf, "AT+CMGR=%d\r", index);
        memset(temp_buf, 0, sizeof(temp_buf));

        retval = send_at_cmd(ttyusb_ctx, send_buf, "OK", temp_buf, sizeof(temp_buf));
        if(retval < 0)
        {
            printf("There is no message %d\n", index);
            return -3;
        }

        start_ptr = strstr(temp_buf, "+CMGR");
        if(NULL == start_ptr)
        {
            printf("There is no message %d\n", index);
            break;
        }

        //printf("数据开始:%s\n", start_ptr);
        start_ptr = strstr(start_ptr, "\r\n");
        start_ptr += 2;//让指针指向PDU首部
        
        center_number_len = calculate_len(start_ptr);//计算中心号码长度
        start_ptr = start_ptr + 2 + center_number_len*2 + 2;
        //printf("电话长度:%s\n",start_ptr);

        phone_len = calculate_len(start_ptr);//计算发送人号码长度
        //printf("phone_len:%d\n", phone_len);
        //如果号码是奇数位,补上一个F计算
        if(phone_len%2 != 0)
        {
            phone_len++;
            phone_flag = 1;
        }
        //获取发送人号码

        strncpy(phone_number, start_ptr+2+2, phone_len);
        for(j = 0; j < phone_len; j +=2)
        {
            temp = phone_number[j];
            phone_number[j] = phone_number[j+1];
            if(phone_flag && ((phone_len -2) == j))
            {
                phone_number[j+1] ='\0';
            }
            else
            {
                phone_number[j+1] = temp;
            }
        }

        if(strncmp(target_number, phone_number, strlen(phone_number)))
        {
            
            start_ptr = NULL;
            index++;
            continue;
        }

        memset(send_buf, 0 , sizeof(send_buf));
        sprintf(send_buf,"AT+CMGD=%d\r",index);
        retval = send_at_cmd(ttyusb_ctx, send_buf, "OK", NULL, 0);
        if(retval < 0)
        {
            printf("[%s]Failed to delte the information\n", __func__);
            return -3;
        }
        printf("Delete message %d ok!\n",index);

        start_ptr = NULL;
        index++;
        
        printf("-----------------------------------------------------------------------------------------------------\n");
            
    }

}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值