【回顾】PDU编码 —— (Unicode 转 UTF-8 )

前言

为什么要讲PDU编码呢?
因为上一遍: Linux下的串口编程(实现AT指令的收发)只是实现了再串口下发送普通AT指令,但是我们 这个项目最终实现的是短信收发的短信猫,所以还要实现在串口下发送和接收短信,而在我们的EC200U-CN中发送SMS(短消息)会有两种模式:一种是TEXT模式,另一种就是PDU模式,PDU模式既支持英文,也支持中文;而TEXT模式则只支持纯英文和数字模式,我们发短信不可能不发中文吧,所以现在知道为什么要学这个PDU编码了吧!

1. 什么是PDU编码?

PDU编码是一种用来发送和接收SMS信息(短消息)的模式,相当于一个数据包,它不仅支持中文短信,也能发送英文短信,PDU模式被所有的手机所支持,可以使用任何的字符集,这也是手机的默认编码方式。
收发短消息的方法还有Blocl Mode, Text Mode ,常用用的就是Text和PDU 模式,但是text模式只能发送英文,不能发送中文。
在这里插入图片描述

2. 什么是Unicode编码、什么是UTF-8编码以及怎么转换的?

PDU编码中后面的内容(上面所说的M)就是代表了短消息内容的Unicode编码,而我们linux下终端输入的编码默认为UTF-8编码,所以涉及到UTF-8编码转为Unicode编码,然后才是PDU编码的组成。

2.1 什么是Unicode编码呢?

Unicode编码是包含了世界上所有的语言的每一个符号,每一个符号都可以用一个独一无二的的编码来表示,这样就解决了因不同语言编码问题导致不同乱码的问题。
Unicode 的缺点:Unicode 只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储:无法区别Unicode 和ASCII:计算机无法区分三个字节表示一个符号(Unicode)还是分别表示三个符号(ASCII)。
另外,我们知道,英文字母只用一个字节表示就够了,如果unicode统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储空间来说是极大的浪费。此时UTF-8代码诞生了。

2.2 什么是UTF-8编码呢?

UTF-8 是在互联网上使用最广的一种Unicode 的实现方式。
UTF-8 是一种变长的编码方式。它可以使用1-6 个字节表示一个符号,根据不同的符号而变化字节长度。

在这里插入图片描述
后面也是根据这个规则来进行UTF-8编码转为Unicode编码的。
在这里插入图片描述
在这里插入图片描述

2.3 代码实现UTF-8编码转换为Unicode编码

/*-----------------------------------------------------------------------
|  funtion:UTF-8 code to unicode.                                                        
|                                                                        
| argument: pdu: The pdu code required parameters.                       
|                                                                        
|   return: rv: success: == 0  ;  error: < 0                             
|-----------------------------------------------------------------------*/
int utf8_to_unicode(pdu_ctx_t *pdu)
{
    int     i = 0;
    char    *temp = pdu->hex_unicode;
    int     hex_len = 0;
    char    byte1, byte2, byte3, byte4;  // High byte to low byte.

    if(!pdu)
    {
        printf("%s,Invalid input arguments\n",__func__);
        return -1;
    }

    hex_len = sizeof(pdu->hex_unicode);
    memset(temp,0,hex_len);

    pdu->hex_unicode_len = 0;

    while(pdu->send_message[i])
    {
        if((pdu->send_message[i] > 0x00) && (pdu->send_message[i] <= 0x7E))  // one byte 
        {
            *temp = 0;
            temp++;
            *temp = pdu->send_message[i];
            temp++;
            i++;
            pdu->hex_unicode_len += 2;
        }
        else if(0xC0 == (pdu->send_message[i] & 0xE0)) //two bytes: eg: 1100 1110 & 1110 0000 = 1100 0000(0xC0)
        {
            byte1 = pdu->send_message[i];
            byte2 = pdu->send_message[i+1];

            /* check the top two bits of low byte whether is '10' */
            if((byte2 & 0xC0) != 0x80)
            {
                printf("UTF-8 codes have any questions\n");
                return -2;
            }

            *temp = ((byte1 >> 2) & 0x07);
            temp++;
            *temp = ((byte1 << 6) + (byte2 & 0x3F));
            temp++;
            i += 2;
            pdu->hex_unicode_len += 2;
        }
        else if(0xE0 == (pdu->send_message[i] & 0xF0)) //three bytes: eg: 1110 0010 & 1111 0000 = 1110 0000(0xE0) 
        {
            byte1 = pdu->send_message[i];
            byte2 = pdu->send_message[i+1];
            byte3 = pdu->send_message[i+2];

            /* check the top two bits of low byte whether is '10' */
            if(((byte2 & 0xC0) != 0x80) || ((byte3 & 0xC0) != 0x80))
            {
                printf("UTF-8 codes have any questions\n");
                return -3;
            }

            *temp = ((byte1 << 4) + ((byte2 >> 2) & 0x0F));
            temp++;
            *temp = ((byte2 << 6) + (byte3 & 0x3F));
            temp++;
            i += 3;
            pdu->hex_unicode_len += 2;
        }
        else if(0xF0 == (pdu->send_message[i] & 0xF8)) //four bytes: eg: 1111 0111 & 1111 1000 = 1111 0000(0xF0)
        {
            byte1 = pdu->send_message[i];
            byte2 = pdu->send_message[i+1];
            byte3 = pdu->send_message[i+2];
            byte4 = pdu->send_message[i+3];

            /* check the top two bits of low byte whether is '10' */
            if(((byte2 & 0xC0) != 0x80) || ((byte3 & 0xC0) != 0x80) || ((byte4 & 0xC0) != 0x80))
            {
                printf("UTF-8 codes have any questions\n");
                return -4;
            }

            *temp = (((byte1 << 2) & 0x1C) + ((byte2 >> 4) & 0x03));
            temp++;
            *temp = ((byte2 << 4) + ((byte3 >> 2) & 0x0F));
            temp++;
            *temp = ((byte3 << 6) + (byte4 & 0x3F));
            temp++;
            i += 4;
            pdu->hex_unicode_len += 3;
        }
        else
        {
            printf("UTF-8 codes have any questions\n");
            return -5;
        }
    }

    return 0;
}

3. 短信中心号的处理

PDU编码中包括处理过的短信中心号,所以我们要所以它是怎么处理的(使用AT+CSCA?获取原始的短信中心号码)。
规则:
在这里插入图片描述

代码实现

获取原始的短信中心号:

/*-----------------------------------------------------------------------
|  funtion:Gain center number(AT+CSCA?).                                                  
|                                                                        
| argument: 1、receive_phone: receiver phone number.                     
|           2、serial: serial port property struct.                               
|                                                                       
|   return: rv: success: == 0  ;  error: < 0                             
|-----------------------------------------------------------------------*/
int gain_center_number(char *center_number,serial_ctx_t *serial)
{
    int             rv = 0;
    atcmd_ctx_t     atcmd;
    char            *ptr = NULL;

    if(!center_number || !serial)
    {
        printf("%s,Invalid input arguments\n",__func__);
        return -1;
    }

    memset(&atcmd,0,sizeof(atcmd_ctx_t));

    strncpy(atcmd.send_at_buf,"AT+CSCA?\r",sizeof(atcmd.send_at_buf));
    strncpy(atcmd.succe_at_buf,"CSCA",sizeof(atcmd.succe_at_buf));
    atcmd.use_len = sizeof(atcmd.use_buf);
    atcmd.resp_timeout = 2;

    rv = send_recv_atcmd(serial,&atcmd);
    if(rv < 0) 
    {
        printf("send_resp_serial() of %s failure and rv: %d\n",__func__,rv);
        return -2;
    }

    if(!atcmd.use_buf)
    {
        printf("%s not copy AT response to use_buf\n",__func__);
        return -3;
    }

    ptr = strstr(atcmd.use_buf,"CSCA"); // return the first address of CSCA 
    ptr += 7;  // address >> 7 bytes get the first address of "+86***********" 

    strncpy(center_number,ptr,CENTER_NUMBER_LEN);

    return 0;
}

处理:

/*-----------------------------------------------------------------------
|  funtion:Handle center number to unicode.                                              
|                                                                        
| argument: pdu: The pdu code required parameters.                       
|                                                                        
|   return: rv: success: == 0  ;  error: < 0                             
|-----------------------------------------------------------------------*/
int handle_center_number(pdu_ctx_t *pdu)
{   
    int     i;
    char    temp;
    char    transfer[54] = {0};

    if(!pdu)
    {
        printf("%s,Invalid input arguments\n",__func__);
        return -1;
    }

    /* Remove the first '+' and add 'F' to end  */
    memset(transfer,0,sizeof(transfer));
    snprintf(transfer,sizeof(transfer),"%s%s",&pdu->center_number[1],"F");

    /* Parity bit swap */
    for(i=0; i < strlen(transfer); i+=2)
    {
        temp = transfer[i];
        transfer[i] = transfer[i+1];
        transfer[i+1] = temp; 
    }

    /* add head length divided by 2 hex(08) and '91' */
    memset(pdu->pdu_center_number,0,sizeof(pdu->pdu_center_number));
    snprintf(pdu->pdu_center_number,sizeof(pdu->pdu_center_number),"0891%s",transfer);
    
    return 0;
}

4. 收件人号码处理

规则:
在这里插入图片描述

代码实现

/*-----------------------------------------------------------------------
|  funtion:Handle receiver number to unicode.                                             
|                                                                        
| argument: pdu: The pdu code required parameters.                       
|                                                                        
|   return: rv: success: == 0  ;  error: < 0                             
|-----------------------------------------------------------------------*/
int handle_recv_phone(pdu_ctx_t *pdu)
{
    int     i;
    char    temp;
    char    transfer[56] = {0};
    char    *head = "11000D91";
    char    *tail = "000800";

    if(!pdu)
    {
        printf("%s,Invalid input arguments\n",__func__);
        return -1;
    }

    /* Remove the first '+' and add 'F' to end  */
    memset(transfer,0,sizeof(transfer));
    snprintf(transfer,sizeof(transfer),"%s%s",&pdu->recv_phone[1],"F");

    /* Parity bit swap */
    for(i=0; i < strlen(transfer); i+=2)
    {
        temp = transfer[i];
        transfer[i] = transfer[i+1];
        transfer[i+1] = temp; 
    }

    /* add head and tail */
    memset(pdu->pdu_recv_phone,0,sizeof(pdu->pdu_recv_phone));
    snprintf(pdu->pdu_recv_phone,sizeof(pdu->pdu_recv_phone),"%s%s%s",head,transfer,tail);

    return 0;
}

5. PDU编码流程图

在这里插入图片描述

6. 字节流转换为字符流

为什么要这样呢?因为发送给串口的buf,必须是字符串类型的,也就是所谓的字符流,而目前,我们获取到的Unicode属于字节流的数据,是不能直接发送给串口的,就算发送了也不会成功,所以必须将字节流的数据转为字符流,才可以进行接下来的其他拼接与发送,下面是代码:

/*-----------------------------------------------------------------------
|  funtion:A byte stream is a character stream(hex to str). eg: 0x1A --> ‘1’ ,‘A’                             
|                                                                        
| argument: pdu: The pdu code required parameters.                       
|                                                                        
|   return: rv: success: == 0  ;  error: < 0                             
|-----------------------------------------------------------------------*/
int hex_to_str(pdu_ctx_t *pdu)
{
    int                 i;
    int                 source_len;         
    unsigned char       HighByte;  // high bytes
    unsigned char       LowByte;   // low bytes

    if(!pdu)
    {
        printf("%s,Invalid input arguments\n",__func__);
        return -1;
    }

    source_len = pdu->hex_unicode_len;

    for(i = 0; i < source_len; i++)
    {
        HighByte = pdu->hex_unicode[i] >> 4;   
        LowByte = pdu->hex_unicode[i] & 0x0f;   

        HighByte += 0x30;            
        if(HighByte <= 0x39)        
            pdu->str_unicode[i*2] = HighByte;

        else                          
            pdu->str_unicode[i*2] = HighByte + 0x07;   


        LowByte += 0x30;
        if(LowByte <= 0x39)
            pdu->str_unicode[i*2+1] = LowByte;

        else
            pdu->str_unicode[i*2+1] = LowByte + 0x07;

    }
    //printf("str: %s\n",pdu->str_unicode);
    
    return 0;
}

7. 封装组合(得到PDU编码)

/*-----------------------------------------------------------------------
|  funtion:Packet data to PDU code.                                                       
|                                                                        
| argument: pdu: The pdu code required parameters.                       
|                                                                        
|   return: rv: success: == 0  ;  error: < 0                             
|-----------------------------------------------------------------------*/
int pdu_packet(pdu_ctx_t *pdu)
{
    int     rv = 0;
    char    temp[512] = {0};

    if(!pdu)
    {
        printf("%s,Invalid input arguments\n",__func__);
        return -1;
    }

    if(handle_center_number(pdu) < 0)
    {
        printf("handle center number failure\n");
        return -2;
    }

    if(handle_recv_phone(pdu) < 0)
    {
        printf("handle center number failure\n");
        return -3;
    }

    rv = utf8_to_unicode(pdu);
    if(rv < 0)
    {
        printf("utf8 convert to unicode failure and rv: %d\n",rv);
        return -4;
    }
    //printf("hex_unicode: %x\n",pdu->hex_unicode);

    if(hex_to_str(pdu) < 0)
    {
        printf("hex convert str failure\n");
        return -5;
    }

    /* strlen(pdu->str_unicode)/2 add head of str unicode  */
    memset(temp,0,sizeof(temp));
    snprintf(temp,sizeof(temp),"%02X%s",strlen(pdu->str_unicode)/2,pdu->str_unicode);

    /* receive phone number connect to new str_unicode(temp) */
    strcat(pdu->pdu_recv_phone,temp);

    pdu->at_cmgs = (int)(strlen(pdu->pdu_recv_phone)/2); // use to AT command AT+CMGS = pdu->at_cmgs

    /* center number connect to receive phone and save pdu buf */
    memset(pdu->pdu_buf,0,sizeof(pdu->pdu_buf));
    snprintf(pdu->pdu_buf,sizeof(pdu->pdu_buf),"%s%s",pdu->pdu_center_number,pdu->pdu_recv_phone);

    //printf("pdu_buf: %s\n",pdu->pdu_buf);

    return 0;   
}

总结

提示:因为我是完成了这个项目才写的博客,所以这段代码时从代码中提取出来的。看看你有些变量看不懂,但是逻辑就是这个逻辑,可以继续关注我的下一篇博客就可以理解了

  • 转码的的过程中只要是要理解他们之间转换的规则,一个位都会影响到最后的结果;
  • 主要位运算的过程有没有出错,特别是字节流转字符流的时候,我当时就在那出错了,然后又折腾了好久。
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值