Using VB.NET to Encode SMS and EMS

我发表在Codeproject上的文章,直接粘贴过来了。此文被评为Prize Winner

Online PDU Encoder

Introduction

Hi, we're back again to generate the PDU code while we have known how to decode PDU code. But at first, sorry for the late publishing of my PDU encoder as I was busy for a long time. These classes are written for my own program, but they can be easily implemented in your project. And this tiny demo program can be used to get the correct PDU code from your setting so as to convince your AT command test work. I'll show how to do this later in this article.

Background

It was last year that I did some work on SMS and EMS PDU code encoding and decoding. And I wrote my PDU Decoder to help me get the PDU strings returned from the mobile. That article has been read by more than twenty thousands people all over the world. Some one pointed out the bugs hidden in my decoder and they helped me to improve my decoder.

But my encoder, although I have done it in February this year, I have it shown to the world in the hot summer. I'm sorry again for this.

Like the decoder, this encoder also was created according to GSM 03.40. But I use 3GPP TS 23.040 V6.5.0 for reference. The link to these documents can be found at the end of this article.

OK. Here we start, I'll introduce my encoder step by step and hope it will give you a hand when designing an encoder.

The architecture of this encoder

The most important parts, public class SMS and ConcatenatedShortMessage, are under the namespace SMS and Encoder. Figure 1 shows this.

The SMS class is foundation of SMS and EMS. It has the basic elements required for SMS. Figure 2 shows you the SMS class. I'll explain how this class works in detail later.

Please note that I have set a group of protected variables prefixed with TP. These variables' names can be easily found in 3GPP or GSM documents. And there're also a set of properties, e.g. ServiceCenterNumber, TP_Status_Report_Request. When you set these properties, they prepare the correct PDU string and store them in protected variables for future use.

This class implements one function GetSMSPDUCode() to generate PDU code. This function simply joins the protected variables in a certain order to get a PDU code. Another function FirstOctet() contains the first octet data in PDU code. We can use it in some particular occasions.

We know that EMS is composed of several SMSs. Each SMS has TP_UserDataHeader set which is different from simple SMS. We can easily inherent from SMS to create the base of EMS. And please note the Concatenated Short Message is only part of EMS, which allows you to send messages containing more than 160 ASCII characters or 70 Unicode characters. You can inherent the SMS class and build another portion of EMS.

From figure 2 you can see an Integer variable named TotalMessages. It tells you how many SMS PDU Codes are generated. And note the GetEMSPDUCode(), it returns a number of SMS PDU codes. I'll explain this later.

How it really works?

In order to demonstrate and explain the details about how this encoder works, let us open my demo program in Visual Studio.

First, please find function GetPDU() in the Form and make a checkpoint. We will trace it down.

Then build and start this program. Fill the blanks according to the picture shown at the beginning of this article and then click Get PDU Code button.

The program stops at the checkpoint. Let's trace down the code. It first check the coding scheme and user data length to ensure if EMS will be used. Please note that the SMSObject is an object type. It will be blind to a specific type of SMS or EMS. Going down the code, we see it sets ServiceCenterNumber and other properties.

Please trace into ServiceCenterNumber property and you can see it prepares PDU code from your setting. The final result is stored in a protected variable SC_Number. Similar process happen to all the properties.

Then, according to the type of SMSObject, we use GetSMSPDUCode() or GetEMSPDUCode() to get PDU codes.

Function details

Let's look into the detail of the GetSMSPDUCode function:

Public Overridable Function GetSMSPDUCode() As String
    
Dim PDUCode As String
    
'Check User Data Length
    If TP_DCS = ENUM_TP_DCS.DefaultAlphabet Then
        
If TP_UD.Length > 280 Then 
            
Throw New Exception("User Data is TOO LONG for SMS")
    
End If
    
If TP_DCS = ENUM_TP_DCS.UCS2 Then
        
If TP_UD.Length > 280 Then 
            
Throw New Exception("User Data is TOO LONG for SMS")
    
End If
    
'Make PDUCode
    PDUCode = SC_Number
    PDUCode 
+= FirstOctet()
    PDUCode 
+= Format(TP_MR, "X2")
    PDUCode 
+= TP_DA
    PDUCode 
+= Format(TP_PID, "X2")
    PDUCode 
+= Format(TP_DCS, "X2")
    PDUCode 
+= Format(TP_VP, "X2")
    PDUCode 
+= Format(TP_UDL, "X2")
    PDUCode 
+= TP_UD
    
Return PDUCode
End Function

This function first checks the coding and determines if the length of user data is longer than the maximum size. Then every prepared PDU code is added to the variable PDUCode. Note the PDU codes are added by a certain order. So it is an easy task to modify the order or add some more PDU codes simply by changing the order or adding the other PDU codes to PDUCode. This gives my class flexibility and extensibility.

Here are also two core functions: Encode7Bit() and EncodeUCS2(). You can treat UCS2 as Unicode and use AscW function to get Unicode character.

But Encode7Bit is much more complex. In order to ease this encoding, I use several help functions such as BitsToHex, CharTo7Bits and so on. First I convert hex number to binary and join them together, then I split them into 8 bits. Please refer to GSM documents for detailed information about 7 bit encoding.

Let us take a look at GetEMSPDUCode():

Public Function GetEMSPDUCode() As String()
    
Select Case tp_dcs
        
Case ENUM_TP_DCS.UCS2
            TotalMessages 
= (TP_UD.Length / 4\ 66 + ((TP_UD.Length / 4 Mod 66= 0)
        
Case ENUM_TP_DCS.DefaultAlphabet
            TotalMessages 
= (tp_ud.Length \ 266- ((tp_ud.Length Mod 266= 0)
    
End Select

    
Dim Result(TotalMessages) As String
    
Dim tmpTP_UD As String
    
Dim i As Integer
    TP_UDHI 
= 2 ^ 6
    
Dim Reference As Integer = Rnd(1* 65536
    
'16bit Reference Number 'See 3GPP Document
    For i = 0 To TotalMessages
        
Select Case tp_dcs
            
Case ENUM_TP_DCS.UCS2
                tmpTP_UD 
= Mid(TP_UD, i * 66 * 4 + 166 * 4)
                
'When TP_UDL is odd, the max length of an Unicode string 
                'in PDU code is 66 Charactor.
                'See [3GPP TS 23.040 V6.5.0 (2004-09] 9.2.3.24.1
            Case ENUM_TP_DCS.DefaultAlphabet
                tmpTP_UD 
= Mid(tp_ud, i * 133 * 2 + 1133 * 2)
        
End Select
        Result(i) 
= SC_Number
        Result(i) 
+= FirstOctet()
        Result(i) 
+= Format(TP_MR, "X2")
        Result(i) 
+= TP_DA
        Result(i) 
+= Format(TP_PID, "X2")
        Result(i) 
+= Format(TP_DCS, "X2")
        Result(i) 
+= Format(TP_VP, "X2")
        
If tp_dcs = ENUM_TP_DCS.UCS2 Then
            TP_UDL 
= tmpTP_UD.Length / 2 + 6 + 1 '6:IE
        End If
        
If tp_dcs = ENUM_TP_DCS.DefaultAlphabet Then
            TP_UDL 
= Fix((tmpTP_UD.Length + 7 * 2* 4 / 7'6:length of IE
        End If
        Result(i) 
+= Format(TP_UDL, "X2")
        Result(i) 
+= "060804" 'TP_UDHL and some of Concatenated message
        Result(i) += Format(Reference, "X4")
        Result(i) 
+= Format(TotalMessages + 1"X2")
        Result(i) 
+= Format(i + 1"X2")
        Result(i) 
+= tmpTP_UD
    
Next
Return Result

As you can see, the above code is similar to GetSMSPDUCode(). But you can also see TP_UDHI is set indicating that TP_UDH appears in TP_UD. Note TP_UDL is calculated during the loop process. As I mentioned in my source code, it also has problems treating TP_UDL while the TP_DCS is DefaultAlphabet. To get details on how EMS is created, please refer to 3GPP TS 23.040 V6.5.0.

Let us send an SMS!

I'm happy to show you that it's so easy to send an SMS to a certain number. In this demonstration, I'll show you how to get my account information using my PDU Encoder, Microsoft HyperTerminal and my PDU Decoder. My phone is Siemens M55, and note some AT commands may not work on your phone or GSM Modem.

First let's produce an SMS PDU Code. Start my program, and fill the Number, Options and User Data as shown in Figure 3. Then press "Get PDU Code" button to get PDU code and length for AT which will be used later. Press "Copy to Clipboard" to copy PDU Code to clipboard.

Start HyperTerminal program. I give this session a name "Send SMS Demo" and I choose COM3 which is the port my phone is connected to. Then follow these instructions:

<CR> equals to Enter key of your keyboard.

Step 1: Type AT<CR> and ensure the device is ready.

Step 2: Type AT+CPMS="MT","MT","MT"<CR> to set preferred memory storage. Here I set it to Mobile Terminal.

Step 3: Type AT+CMGS=12<CR>. The number 12.

Step 4: Paste PDU code to HyperTerminal and press CTRL+Z to end the input. At this time your phone will send SMS to the number you specified. Here my destination number is 1861 from which I can get my account info.

Step 5: After a while I can see my phone receive a status report and my account info.

Step 6: Type AT+CMGL=1 to list the incoming message. Here it returns:

    +CMGL: 1,1,,166
    0891683108200805F0066104818116505013612455005050136124550000FFFFFFFFFFFFFFFFFFFF
    FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
    FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
    FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
    FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
    +CMGL: 2,1,,83
    0891683108200805F00404A1811600085050136164350044672C67085DF24F7F75288BDD8D39003A
    00350031002E00340037002052694F5998845B586B3E003A00350039002E0033003600204F1860E0
    003A00370039002E00350031

Step 7: Use my PDU Decoder to decode above PDU codes. Note there're some Chinese characters.

    SMS_STATUS_REPORT
    Send time:2005-5-31 16:42:55 Receive time:2005-5-31 16:42:55 Status:Success
    SMS_RECEIVED
    From:1861 Time:2005-5-31 16:46:53
    ±¾ÔÂÒÑʹÓû°·Ñ:51.47 Ê£ÓàÔ¤´æ¿î:59.36 ÓÅ»Ý:79.51

You can see that it's an easy task to manually send and receive SMSs though my tools and HyperTerminal. Be glad to use them to ease your test work!

Reference

Note

You can use and modify my code freely. If you find some bugs or improve my code, please contact me. This will help me to fix bugs and also it will help a lot of people all over the world! Thanks for reading my article and thanks again for using my code!

Contact me

posted @ 2005-08-03 23:10  Dream world 梦想天空  阅读(1186)  评论(0编辑  收藏  举报