当我对SMS PDU(GSM 7 Bit)用户数据进行编码/解码时,是否需要先加入UDH? [英] When I encode/decode SMS PDU (GSM 7 Bit) user data, do I need prepend the UDH first?

查看:1144
本文介绍了当我对SMS PDU(GSM 7 Bit)用户数据进行编码/解码时,是否需要先加入UDH?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

虽然当UDH 存在时,我可以成功地对SMS消息的用户数据部分进行编码和解码,但当UDH 目前(在这种情况下,用于连接的短信)。



当我对用户数据进行解码或编码时,在这样做之前,是否需要在文本前面加上UDH? / p>

本文提供了一个编码例程,用于补偿具有填充位的UDH(我仍然不完全理解),但不提供数据的示例传递给例程,所以我没有一个明确的用例(我在网站上找不到解码示例):
http://mobiletidings.com/2009/07/06/how-to-pack-gsm7-into-septets/



到目前为止,如果在解码之前将UDH添加到用户数据中,我可以得到一些结果,但我怀疑这只是巧合。 / p>

作为示例(使用 https:// en .wikipedia.org / wiki / Concatenated_SMS ):

  UDH:='050003000302'; 
ENCODED_USER_DATA_PART:='D06536FB0DBABFE56C32'; // with padding,显然是
DecodedUserData:= Decode7Bit(UDH + ENCODED_USER_DATA_PART);
Writeln(DecodedUserData);

输出:ß@ø¿Æ@hello world

  EncodedUserData:= Encode7Bit(DecodedUserData); 
DecodedUserData:= Decode7Bit(EncodedEncodedUserData);
Writeln(DecodedUserData);

相同输出:ß@ø¿Æ@hello world



没有前缀UDH我得到垃圾:

  DecodedUserData:= Decode7Bit (ENCODED_USER_DATA_PART); 
Writeln(DecodedUserData);

输出:PKYY§An§eI



正确的处理方式是什么?



在编码用户数据时,我应该将UDH与文本包含在一起吗?



我应该在解码后剥离垃圾字符,还是我(我怀疑)完全脱离这个假设?



虽然这里的解码算法似乎在没有UDH的情况下工作,但似乎没有考虑任何UDH信息:
寻找GSM 7bit编码/解码算法



我将永远感激如果有人能以正确的方式让我直率。任何明确的示例/代码示例将非常感谢。 ; - )



我还将提供一个小样本应用程序,包括算法,如果有人觉得这将有助于解决谜语。



编辑1:



我使用的是Delphi XE2 Update 4修补程序1



编辑2:



感谢@whosrdaddy的帮助,我能够成功地获取我的编码/解码例程。



作为旁注,当UDH未被编码时,我很好奇为什么用户数据需要在7位边界上,但是由@whosrdaddy引用的ETSI规范中的段落中的最后一句回答说:


如果使用7位数据,并且TP -UD-Header没有在七分位符边界上完成,然后在最后一个
信息元素数据八位位组之后插入填充位,以便整个TP-UD标题有一个整数个
七位位组。 这是为了确保SM
本身在八位字节边界上开始,以便早期阶段的移动
将能够显示SM本身,尽管TP-UD头
TP-UD字段可能不被理解


我的代码部分基于以下示例资源:



查看对于GSM 7bit编码/解码算法



https://en.wikipedia.org/wiki/Concatenated_SMS



http://mobiletidings.com/2009/02/18/combining-sms-messages/



http://mobiletidings.com/ 2009/07/06 / how-to-pack-gsm7-in-sept ets /



http://mobileforensics.files.wordpress.com/2007/06/understanding_sms.pdf



http://www.dreamfabric.com/sms/



http://www.mediaburst.co.uk/blog/concatenated-sms/



以下是有关SMS编码/解码问题的其他人的代码。我相信它可以被简化/优化(和欢迎评论),但我已经测试了几个不同的排列和UDH标题长度成功。我希望它有帮助。

  unit SmsUtils; 

界面

使用Windows,Classes,Math;

函数Encode7Bit(const AText:string; AUdhLen:Byte;
out ATextLen:Byte):string;

函数Decode7Bit(const APduData:string; AUdhLen:Integer):string;

实现

var
g7BitToAsciiTable:字节数组[0 .. 127]
gAsciiTo7BitTable:Byte的数组[0 .. 255]

procedure InitializeTables;
var
AsciiValue:Integer;
i:整数;
begin
//创建7位到ascii表
g7BitToAsciiTable [0]:= 64; // @
g7BitToAsciiTable [1]:= 163;
g7BitToAsciiTable [2] = = 36;
g7BitToAsciiTable [3]:= 165;
g7BitToAsciiTable [4]:= 232;
g7BitToAsciiTable [5]:= 223;
g7BitToAsciiTable [6]:= 249;
g7BitToAsciiTable [7]:= 236;
g7BitToAsciiTable [8]:= 242;
g7BitToAsciiTable [9]:= 199;
g7BitToAsciiTable [10]:= 10;
g7BitToAsciiTable [11]:= 216;
g7BitToAsciiTable [12]:= 248;
g7BitToAsciiTable [13]:= 13;
g7BitToAsciiTable [14]:= 197;
g7BitToAsciiTable [15]:= 229;
g7BitToAsciiTable [16]:= 0;
g7BitToAsciiTable [17]:= 95;
g7BitToAsciiTable [18]:= 0;
g7BitToAsciiTable [19]:= 0;
g7BitToAsciiTable [20]:= 0;
g7BitToAsciiTable [21]:= 0;
g7BitToAsciiTable [22]:= 0;
g7BitToAsciiTable [23]:= 0;
g7BitToAsciiTable [24]:= 0;
g7BitToAsciiTable [25]:= 0;
g7BitToAsciiTable [26]:= 0;
g7BitToAsciiTable [27]:= 0;
g7BitToAsciiTable [28]:= 198;
g7BitToAsciiTable [29]:= 230;
g7BitToAsciiTable [30]:= 223;
g7BitToAsciiTable [31]:= 201;
g7BitToAsciiTable [32]:= 32;
g7BitToAsciiTable [33]:= 33;
g7BitToAsciiTable [34]:= 34;
g7BitToAsciiTable [35]:= 35;
g7BitToAsciiTable [36]:= 164;
g7BitToAsciiTable [37]:= 37;
g7BitToAsciiTable [38]:= 38;
g7BitToAsciiTable [39]:= 39;
g7BitToAsciiTable [40]:= 40;
g7BitToAsciiTable [41]:= 41;
g7BitToAsciiTable [42]:= 42;
g7BitToAsciiTable [43]:= 43;
g7BitToAsciiTable [44]:= 44;
g7BitToAsciiTable [45]:= 45;
g7BitToAsciiTable [46]:= 46;
g7BitToAsciiTable [47]:= 47;
g7BitToAsciiTable [48]:= 48;
g7BitToAsciiTable [49]:= 49;
g7BitToAsciiTable [50]:= 50;
g7BitToAsciiTable [51]:= 51;
g7BitToAsciiTable [52]:= 52;
g7BitToAsciiTable [53]:= 53;
g7BitToAsciiTable [54]:= 54;
g7BitToAsciiTable [55]:= 55;
g7BitToAsciiTable [56]:= 56;
g7BitToAsciiTable [57]:= 57;
g7BitToAsciiTable [58]:= 58;
g7BitToAsciiTable [59]:= 59;
g7BitToAsciiTable [60]:= 60;
g7BitToAsciiTable [61]:= 61;
g7BitToAsciiTable [62]:= 62;
g7BitToAsciiTable [63]:= 63;
g7BitToAsciiTable [64]:= 161;
g7BitToAsciiTable [65]:= 65;
g7BitToAsciiTable [66]:= 66;
g7BitToAsciiTable [67]:= 67;
g7BitToAsciiTable [68]:= 68;
g7BitToAsciiTable [69]:= 69;
g7BitToAsciiTable [70]:= 70;
g7BitToAsciiTable [71]:= 71;
g7BitToAsciiTable [72]:= 72;
g7BitToAsciiTable [73]:= 73;
g7BitToAsciiTable [74]:= 74;
g7BitToAsciiTable [75]:= 75;
g7BitToAsciiTable [76]:= 76;
g7BitToAsciiTable [77]:= 77;
g7BitToAsciiTable [78]:= 78;
g7BitToAsciiTable [79]:= 79;
g7BitToAsciiTable [80]:= 80;
g7BitToAsciiTable [81]:= 81;
g7BitToAsciiTable [82]:= 82;
g7BitToAsciiTable [83]:= 83;
g7BitToAsciiTable [84]:= 84;
g7BitToAsciiTable [85]:= 85;
g7BitToAsciiTable [86]:= 86;
g7BitToAsciiTable [87]:= 87;
g7BitToAsciiTable [88]:= 88;
g7BitToAsciiTable [89]:= 89;
g7BitToAsciiTable [90]:= 90;
g7BitToAsciiTable [91]:= 196;
g7BitToAsciiTable [92]:= 204;
g7BitToAsciiTable [93]:= 209;
g7BitToAsciiTable [94]:= 220;
g7BitToAsciiTable [95]:= 167;
g7BitToAsciiTable [96]:= 191;
g7BitToAsciiTable [97]:= 97;
g7BitToAsciiTable [98]:= 98;
g7BitToAsciiTable [99]:= 99;
g7BitToAsciiTable [100]:= 100;
g7BitToAsciiTable [101]:= 101;
g7BitToAsciiTable [102]:= 102;
g7BitToAsciiTable [103]:= 103;
g7BitToAsciiTable [104]:= 104;
g7BitToAsciiTable [105]:= 105;
g7BitToAsciiTable [106]:= 106;
g7BitToAsciiTable [107]:= 107;
g7BitToAsciiTable [108]:= 108;
g7BitToAsciiTable [109]:= 109;
g7BitToAsciiTable [110]:= 110;
g7BitToAsciiTable [111]:= 111;
g7BitToAsciiTable [112]:= 112;
g7BitToAsciiTable [113]:= 113;
g7BitToAsciiTable [114]:= 114;
g7BitToAsciiTable [115]:= 115;
g7BitToAsciiTable [116]:= 116;
g7BitToAsciiTable [117]:= 117;
g7BitToAsciiTable [118]:= 118;
g7BitToAsciiTable [119]:= 119;
g7BitToAsciiTable [120]:= 120;
g7BitToAsciiTable [121]:= 121;
g7BitToAsciiTable [122]:= 122;
g7BitToAsciiTable [123]:= 228;
g7BitToAsciiTable [124]:= 246;
g7BitToAsciiTable [125]:= 241;
g7BitToAsciiTable [126]:= 252;
g7BitToAsciiTable [127]:= 224;

//创建ascii到7位表
ZeroMemory(@ gAsciiTo7BitTable,SizeOf(gAsciiTo7BitTable));
for i:= 0 to High(g7BitToAsciiTable)do
begin
AsciiValue:= g7BitToAsciiTable [i];
gAsciiTo7BitTable [AsciiValue]:= i;
结束
结束

函数ConvertAsciiTo7Bit(const AText:string; AUdhLen:Byte):AnsiString;
const
ESC =#27;
ESCAPED_ASCII_CODES = [#94,#123,#125,#92,#91,#126,#93,#124,#164]
var
Septet:Byte;
Ch:AnsiChar;
i:整数;
begin
for i:= 1 to Length(AText)do
begin
Ch:= AnsiChar(AText [i]);
如果没有(在ESCAPED_ASCII_CODES中的Ch)然后
Septet:= gAsciiTo7BitTable [Byte(Ch)]
else
begin
结果:=结果+ ESC;
case(Ch)
#12:Septet:= 10;
#94:Septet:= 20;
#123:Septet:= 40;
#125:Septet:= 41;
#92:Septet:= 47;
#91:Septet:= 60;
#126:Septet:= 61;
#93:Septet:= 62;
#124:Septet:= 64;
#164:Septet:= 101;
else Septet:= 0;
结束
结束
结果:= Result + AnsiChar(Septet);
结束
结束

函数Convert7BitToAscii(con​​st AText:AnsiString):string;
const
ESC =#27;
var
TextLen:Integer;
Ch:Char;
i:整数;
begin
结果:='';
TextLen:= Length(AText);
i:= 1;
while(i< = TextLen)do
begin
Ch:= Char(AText [i]);
if(Ch - ESC)then
Result:= Result + Char(g7BitToAsciiTable [Ord(Ch)])
else
begin
Inc(i ); //跳过ESC
if(i <= TextLen)then
begin
Ch:= Char(AText [i]);
(ch)of
#10:Ch:=#12;
#20:Ch:=#94;
#40:Ch:=#123;
#41:Ch:=#125;
#47:Ch:=#92;
#60:Ch:=#91;
#61:Ch:=#126;
#62:Ch:=#93;
#64:Ch:=#124;
#101:Ch:=#164;
结束
结果:= Result + Ch;
结束
结束
Inc(i);
结束
结束

函数StrToHex(const AText:AnsiString):AnsiString;超载;
var
TextLen:Integer;
begin
//设置文本缓冲区大小
TextLen:= Length(AText);
//将结果的长度设置为双倍的字符串长度
SetLength(Result,TextLen * 2);
//将字符串转换为十六进制
BinToHex(PAnsiChar(AText),PAnsiChar(Result),TextLen);
结束

函数StrToHex(const AText:string):string;超载;
begin
结果:= string(StrToHex(AnsiString(AText)));
结束

函数HexToStr(const AText:AnsiString):AnsiString;超载;
var
ResultLen:Integer;
begin
//将结果的长度设置为文本长度的一半
ResultLen:= Length(AText)div 2;
SetLength(Result,ResultLen);
//将十六进制转换为字符串
if(HexToBin(PAnsiChar(AText),PAnsiChar(Result),ResultLen)ResultLen)then
Result:='十六进制字符串:'+ AText;
结束

函数HexToStr(const AText:string):string;超载;
begin
结果:= string(HexToStr(AnsiString(AText)));
结束

函数Encode7Bit(const AText:string; AUdhLen:Byte;
out ATextLen:Byte):string;
// AText:Ascii text
// AUdhLen:UDH的长度,包括UDH Len字节(例如'050003CC0101'= 6个字节)
// ATextLen:返回编码的文本长度。这可以是不同的
//比长度(AText)由于转义字符
//返回文本作为编码PDU十六进制字符串
var
Text7Bit:AnsiString;
Pdu:AnsiString;
PduIdx:整数;
PduLen:Byte;
PaddingBits:Byte;
BitsToMove:Byte;
七重奏:字节;
Octet:Byte;
PrevOctet:Byte;
ShiftedOctet:Byte;
i:整数;
begin
结果:='';
Text7Bit:= ConvertAsciiTo7Bit(AText,AUdhLen);
ATextLen:=长度(Text7Bit);
BitsToMove:= 0;
//根据UDH
确定需要多少填充位,如果(AUdhLen> 0)则
PaddingBits:= 7 - ((AUdhLen * 8)mod 7)
else
PaddingBits:= 0;
//计算要存储7位文本
//所需的字节数以及所需的任何填充位
PduLen:= Ceil(((ATextLen * 7)+ PaddingBits )/ 8);
//为PDU字节保留空间
Pdu:= AnsiString(StringOfChar(#0,PduLen));
PduIdx:= 1;
for i:= 1 to ATextLen do
begin
if(BitsToMove = 7)then
BitsToMove:= 0
else
begin
//将当前字符转换为七位字节(7位),并为
腾出空间
Septet:=(Byte(Text7Bit [i])shr BitsToMove);
if(i = ATextLen)then
Octet:= Septet
else
begin
//将下一个字符转换为七位字节,然后从中复制
//到八位字节(PDU字节)
Octet:= Septet或
字节((字节(Text7Bit [i + 1])shl字节(7 - BitsToMove)))
结束
字节(Pdu [PduIdx]):= Octet;
Inc(PduIdx);
Inc(BitsToMove);
结束
结束
//以下代码通过将其移动到* left *
// by& PaddingBits>将* p上的pdu进行填充。它通过使用与
//上述7位压缩例程相同的位存储约定,通过采用最重要的
//< PaddingBits>从每个PDU字节移动它们到下一个PDU字节的最低有效
//位。如果在去掉的前一个字节的
//高位的最后一个PDU字节中没有空格,那么这些位为
//放置在为此目的保留的附加字节中。
//注意:< PduLen>如果
//是必需的,已经设置为保留保留字节。
if(PaddingBits> 0)then
begin
SetLength(Result,(PduLen * 2));
PrevOctet:= 0;
PduIdx:= 1到PduLen do
begin
Octet:=字节(Pdu [PduIdx]);
if(PduIdx = 1)then
ShiftedOctet:= Byte(Octet shl PaddingBits)
else
ShiftedOctet:= Byte(Octet shl PaddingBits)或
Byte(PrevOctet shr(8 - PaddingBits));
字节(Pdu [PduIdx]):= ShiftedOctet;
PrevOctet:= Octet;
结束
结束
结果:= string(StrToHex(Pdu));
结束

函数Decode7Bit(const APduData:string; AUdhLen:Integer):string;
// APduData:PDU数据的十六进制字符串表示
// AUdhLen:UDH的长度,包括UDH Len(例如'050003CC0101'= 6个字节)
//返回解码的Ascii文本
var
Pdu:AnsiString;
NumSeptets:Byte;
Septets:AnsiString;
PduIdx:整数;
PduLen:整数;
by:Byte;
currBy:Byte;
left:Byte;
掩码:字节;
nextBy:Byte;
Octet:Byte;
NextOctet:Byte;
PaddingBits:Byte;
ShiftedOctet:Byte;
i:整数;
begin
结果:='';
PaddingBits:= 0;
//将十六进制字符串转换为字节
Pdu:= AnsiString(HexToStr(APduData));
PduLen:=长度(Pdu);
//以下代码通过将
// * right *替换为& PaddingBits>将PDU的末尾删除填充。它通过获取最不重要的
//< PaddingBits>从以下PDU字节移动到最多
//显示当前的PDU字节。
if(AUdhLen> 0)then
begin
PaddingBits:= 7 - ((AUdhLen * 8)mod 7);
PduIdx:= 1到PduLen do
begin
Octet:=字节(Pdu [PduIdx]);
if(PduIdx = PduLen)then
ShiftedOctet:= Byte(Octet shr PaddingBits)
else
begin
NextOctet:=字节(Pdu [PduIdx + 1]) ;
ShiftedOctet:=字节(Octet shr PaddingBits)或
字节(NextOctet shl(8 - PaddingBits));
结束
字节(Pdu [PduIdx]):= ShiftedOctet;
结束
结束
// decode
//排除填充位后,PDU中的子集数量
NumSeptets:=((PduLen * 8) - PaddingBits)div 7;
Septets:= AnsiString(StringOfChar(#0,NumSeptets));
left = = 7;
mask:= $ 7F;
nextBy:= 0;
PduIdx:= 1;
for i:= 1 to NumSeptets do
begin
如果mask = 0,则
begin
Septets [i]:= AnsiChar(nextBy);
left = = 7;
mask:= $ 7F;
nextBy:= 0;
end
else
begin
if(PduIdx> PduLen)then
Break;
by:= Byte(Pdu [PduIdx]);
Inc(PduIdx);
currBy:=((通过AND掩码)SHL(7 - 左))OR nextBy;
nextBy:=(通过AND(NOT mask))SHR左;
Septets [i]:= AnsiChar(currBy);
mask:= mask SHR 1;
left = = left - 1;
结束
结束// for
//删除最后一个字符,如果未使用
//这是一个黑客,但坦率地说,我不知道如何补偿
//。
if(Septets [NumSeptets] =#0)then
SetLength(Septets,NumSeptets - 1);
//将7位字母转换为ascii
结果:= Convert7BitToAscii(Septets);
结束

初始化
InitializeTables;
结束。


解决方案

没有你在编码时不包括UDH部分,但如果您阅读了 GSM阶段2规范第57页,他们提到这个事实:如果使用7位数据,TP-UD-Header在七分位符边界上没有完成,那么在最后一个信息元素数据字节之后,填充位将被插入
,以便在是整个
TP-UD标题的整数倍数。当你包含一个UDH部分时,情况并非如此,所以你需要做的就是计算偏移量(=填充位数)



计算偏移量代码假定UDHPart是一个AnsiString:

  Len:= Length(UDHPart)shr 1; 
偏移:= 7 - ((Len * 8)mod 7); //填充位

现在编码7位数据时,您正常进行,但最后,您将数据偏移位向左移动,此代码具有可变结果(ansistring)中的编码数据:

  //填充位
如果偏移> 0然后
begin
v:= Result;
Len:=长度(v);
BytesRemain:= ceil(((Len * 7)+ Offset)/ 8);
结果:= StringOfChar(#0,BytesRemain);
InPos:= 1 to BytesRemain do
begin
if InPos = 1 then
Byte(Result [InPos]):= Byte(v [InPos])shl offset
else
Byte(Result [InPos]):=(Byte(v [InPos])shl offset)或(Byte(v [InPos-1])shr(8 - offset));
结束
结束

解码是一回事,您首先在解码之前将7位数据偏移位向右移位。 ..



我希望这会让你进入正确的轨道...


While I can successfully encode and decode the user data part of an SMS message when a UDH is not present, I'm having trouble doing so when a UDH is present (in this case, for concatenated SMS).

When I decode or encode the user data, do I need to prepend the UDH to the text before doing so?

This article provides an encoding routine sample that compensates for the UDH with padding bits (which I still don't completely understand) but it doesn't give an example of data being passed to the routine so I don't have a clear use case (and I could not find a decoding sample on the site): http://mobiletidings.com/2009/07/06/how-to-pack-gsm7-into-septets/.

So far, I have been able to get some results if I prepend the UDH to the user data before decoding it, but I suspect this is just a coincidence.

As an example (using values from https://en.wikipedia.org/wiki/Concatenated_SMS):

UDH := '050003000302';
ENCODED_USER_DATA_PART := 'D06536FB0DBABFE56C32'; // with padding, evidently
DecodedUserData := Decode7Bit(UDH + ENCODED_USER_DATA_PART);
Writeln(DecodedUserData);

Output: "ß@ø¿Æ @hello world"

EncodedUserData := Encode7Bit(DecodedUserData);
DecodedUserData := Decode7Bit(EncodedEncodedUserData);
Writeln(DecodedUserData);

Same Output: "ß@ø¿Æ @hello world"

Without prepending the UDH I get garbage:

DecodedUserData := Decode7Bit(ENCODED_USER_DATA_PART);
Writeln(DecodedUserData);

Output: "PKYY§An§eYI"

What is correct way of handling this?

Am I supposed to include the UDH with the text when encoding the user data?

Am I supposed to strip off the garbage characters after decoding, or am I (as I suspect) completely off base with this assumption?

While the decoding algorithm here seems to work without a UDH it doesn't seem to take any UDH information into account: Looking for GSM 7bit encode/decode algorithm.

I would be eternally grateful if someone could set me straight on the correct way to proceed. Any clear examples/code samples would be very much appreciated. ;-)

I will also provide a small sample application that includes the algorithms if anyone feels it will help solve the riddle.

EDIT 1:

I'm using Delphi XE2 Update 4 Hotfix 1

EDIT 2:

Thanks to help from @whosrdaddy, I was able to successfully get my encoding/decoding routines to work.

As a side note, I was curious as to why the user data needed to be on a 7-bit boundary when the UDH wasn't encoded with it, but the last sentence in the paragraph from the ETSI specification quoted by @whosrdaddy answered that:

If 7 bit data is used and the TP-UD-Header does not finish on a septet boundary then fill bits are inserted after the last Information Element Data octet so that there is an integral number of septets for the entire TP-UD header. This is to ensure that the SM itself starts on an octet boundary so that an earlier phase mobile will be capable of displaying the SM itself although the TP-UD Header in the TP-UD field may not be understood

My code is based in part on examples from the following resources:

Looking for GSM 7bit encode/decode algorithm

https://en.wikipedia.org/wiki/Concatenated_SMS

http://mobiletidings.com/2009/02/18/combining-sms-messages/

http://mobiletidings.com/2009/07/06/how-to-pack-gsm7-into-septets/

http://mobileforensics.files.wordpress.com/2007/06/understanding_sms.pdf

http://www.dreamfabric.com/sms/

http://www.mediaburst.co.uk/blog/concatenated-sms/

Here's the code for anyone else who's had trouble with SMS encoding/decoding. I'm sure it can be simplified/optimized (and comments are welcome), but I've tested it with several different permutations and UDH header lengths with success. I hope it helps.

unit SmsUtils;

interface

uses Windows, Classes, Math;

function Encode7Bit(const AText: string; AUdhLen: Byte;
  out ATextLen: Byte): string;

function Decode7Bit(const APduData: string; AUdhLen: Integer): string;

implementation

var
  g7BitToAsciiTable: array [0 .. 127] of Byte;
  gAsciiTo7BitTable: array [0 .. 255] of Byte;

procedure InitializeTables;
var
  AsciiValue: Integer;
  i: Integer;
begin
  // create 7-bit to ascii table
  g7BitToAsciiTable[0] := 64; // @
  g7BitToAsciiTable[1] := 163;
  g7BitToAsciiTable[2] := 36;
  g7BitToAsciiTable[3] := 165;
  g7BitToAsciiTable[4] := 232;
  g7BitToAsciiTable[5] := 223;
  g7BitToAsciiTable[6] := 249;
  g7BitToAsciiTable[7] := 236;
  g7BitToAsciiTable[8] := 242;
  g7BitToAsciiTable[9] := 199;
  g7BitToAsciiTable[10] := 10;
  g7BitToAsciiTable[11] := 216;
  g7BitToAsciiTable[12] := 248;
  g7BitToAsciiTable[13] := 13;
  g7BitToAsciiTable[14] := 197;
  g7BitToAsciiTable[15] := 229;
  g7BitToAsciiTable[16] := 0;
  g7BitToAsciiTable[17] := 95;
  g7BitToAsciiTable[18] := 0;
  g7BitToAsciiTable[19] := 0;
  g7BitToAsciiTable[20] := 0;
  g7BitToAsciiTable[21] := 0;
  g7BitToAsciiTable[22] := 0;
  g7BitToAsciiTable[23] := 0;
  g7BitToAsciiTable[24] := 0;
  g7BitToAsciiTable[25] := 0;
  g7BitToAsciiTable[26] := 0;
  g7BitToAsciiTable[27] := 0;
  g7BitToAsciiTable[28] := 198;
  g7BitToAsciiTable[29] := 230;
  g7BitToAsciiTable[30] := 223;
  g7BitToAsciiTable[31] := 201;
  g7BitToAsciiTable[32] := 32;
  g7BitToAsciiTable[33] := 33;
  g7BitToAsciiTable[34] := 34;
  g7BitToAsciiTable[35] := 35;
  g7BitToAsciiTable[36] := 164;
  g7BitToAsciiTable[37] := 37;
  g7BitToAsciiTable[38] := 38;
  g7BitToAsciiTable[39] := 39;
  g7BitToAsciiTable[40] := 40;
  g7BitToAsciiTable[41] := 41;
  g7BitToAsciiTable[42] := 42;
  g7BitToAsciiTable[43] := 43;
  g7BitToAsciiTable[44] := 44;
  g7BitToAsciiTable[45] := 45;
  g7BitToAsciiTable[46] := 46;
  g7BitToAsciiTable[47] := 47;
  g7BitToAsciiTable[48] := 48;
  g7BitToAsciiTable[49] := 49;
  g7BitToAsciiTable[50] := 50;
  g7BitToAsciiTable[51] := 51;
  g7BitToAsciiTable[52] := 52;
  g7BitToAsciiTable[53] := 53;
  g7BitToAsciiTable[54] := 54;
  g7BitToAsciiTable[55] := 55;
  g7BitToAsciiTable[56] := 56;
  g7BitToAsciiTable[57] := 57;
  g7BitToAsciiTable[58] := 58;
  g7BitToAsciiTable[59] := 59;
  g7BitToAsciiTable[60] := 60;
  g7BitToAsciiTable[61] := 61;
  g7BitToAsciiTable[62] := 62;
  g7BitToAsciiTable[63] := 63;
  g7BitToAsciiTable[64] := 161;
  g7BitToAsciiTable[65] := 65;
  g7BitToAsciiTable[66] := 66;
  g7BitToAsciiTable[67] := 67;
  g7BitToAsciiTable[68] := 68;
  g7BitToAsciiTable[69] := 69;
  g7BitToAsciiTable[70] := 70;
  g7BitToAsciiTable[71] := 71;
  g7BitToAsciiTable[72] := 72;
  g7BitToAsciiTable[73] := 73;
  g7BitToAsciiTable[74] := 74;
  g7BitToAsciiTable[75] := 75;
  g7BitToAsciiTable[76] := 76;
  g7BitToAsciiTable[77] := 77;
  g7BitToAsciiTable[78] := 78;
  g7BitToAsciiTable[79] := 79;
  g7BitToAsciiTable[80] := 80;
  g7BitToAsciiTable[81] := 81;
  g7BitToAsciiTable[82] := 82;
  g7BitToAsciiTable[83] := 83;
  g7BitToAsciiTable[84] := 84;
  g7BitToAsciiTable[85] := 85;
  g7BitToAsciiTable[86] := 86;
  g7BitToAsciiTable[87] := 87;
  g7BitToAsciiTable[88] := 88;
  g7BitToAsciiTable[89] := 89;
  g7BitToAsciiTable[90] := 90;
  g7BitToAsciiTable[91] := 196;
  g7BitToAsciiTable[92] := 204;
  g7BitToAsciiTable[93] := 209;
  g7BitToAsciiTable[94] := 220;
  g7BitToAsciiTable[95] := 167;
  g7BitToAsciiTable[96] := 191;
  g7BitToAsciiTable[97] := 97;
  g7BitToAsciiTable[98] := 98;
  g7BitToAsciiTable[99] := 99;
  g7BitToAsciiTable[100] := 100;
  g7BitToAsciiTable[101] := 101;
  g7BitToAsciiTable[102] := 102;
  g7BitToAsciiTable[103] := 103;
  g7BitToAsciiTable[104] := 104;
  g7BitToAsciiTable[105] := 105;
  g7BitToAsciiTable[106] := 106;
  g7BitToAsciiTable[107] := 107;
  g7BitToAsciiTable[108] := 108;
  g7BitToAsciiTable[109] := 109;
  g7BitToAsciiTable[110] := 110;
  g7BitToAsciiTable[111] := 111;
  g7BitToAsciiTable[112] := 112;
  g7BitToAsciiTable[113] := 113;
  g7BitToAsciiTable[114] := 114;
  g7BitToAsciiTable[115] := 115;
  g7BitToAsciiTable[116] := 116;
  g7BitToAsciiTable[117] := 117;
  g7BitToAsciiTable[118] := 118;
  g7BitToAsciiTable[119] := 119;
  g7BitToAsciiTable[120] := 120;
  g7BitToAsciiTable[121] := 121;
  g7BitToAsciiTable[122] := 122;
  g7BitToAsciiTable[123] := 228;
  g7BitToAsciiTable[124] := 246;
  g7BitToAsciiTable[125] := 241;
  g7BitToAsciiTable[126] := 252;
  g7BitToAsciiTable[127] := 224;

  // create ascii to 7-bit table
  ZeroMemory(@gAsciiTo7BitTable, SizeOf(gAsciiTo7BitTable));
  for i := 0 to High(g7BitToAsciiTable) do
  begin
    AsciiValue := g7BitToAsciiTable[i];
    gAsciiTo7BitTable[AsciiValue] := i;
  end;
end;

function ConvertAsciiTo7Bit(const AText: string; AUdhLen: Byte): AnsiString;
const
  ESC = #27;
  ESCAPED_ASCII_CODES = [#94, #123, #125, #92, #91, #126, #93, #124, #164];
var
  Septet: Byte;
  Ch: AnsiChar;
  i: Integer;
begin
  for i := 1 to Length(AText) do
  begin
    Ch := AnsiChar(AText[i]);
    if not(Ch in ESCAPED_ASCII_CODES) then
      Septet := gAsciiTo7BitTable[Byte(Ch)]
    else
    begin
      Result := Result + ESC;
      case (Ch) of
        #12: Septet := 10;
        #94: Septet := 20;
        #123: Septet := 40;
        #125: Septet := 41;
        #92: Septet := 47;
        #91: Septet := 60;
        #126: Septet := 61;
        #93: Septet := 62;
        #124: Septet := 64;
        #164: Septet := 101;
      else Septet := 0;
      end;
    end;
    Result := Result + AnsiChar(Septet);
  end;
end;

function Convert7BitToAscii(const AText: AnsiString): string;
const
  ESC = #27;
var
  TextLen: Integer;
  Ch: Char;
  i: Integer;
begin
  Result := '';
  TextLen := Length(AText);
  i := 1;
  while (i <= TextLen) do
  begin
    Ch := Char(AText[i]);
    if (Ch <> ESC) then
      Result := Result + Char(g7BitToAsciiTable[Ord(Ch)])
    else
    begin
      Inc(i); // skip ESC
      if (i <= TextLen) then
      begin
        Ch := Char(AText[i]);
        case (Ch) of
          #10: Ch := #12;
          #20: Ch := #94;
          #40: Ch := #123;
          #41: Ch := #125;
          #47: Ch := #92;
          #60: Ch := #91;
          #61: Ch := #126;
          #62: Ch := #93;
          #64: Ch := #124;
          #101: Ch := #164;
        end;
        Result := Result + Ch;
      end;
    end;
    Inc(i);
  end;
end;

function StrToHex(const AText: AnsiString): AnsiString; overload;
var
  TextLen: Integer;
begin
  // set the text buffer size
  TextLen := Length(AText);
  // set the length of the result to double the string length
  SetLength(Result, TextLen * 2);
  // convert the string to hex
  BinToHex(PAnsiChar(AText), PAnsiChar(Result), TextLen);
end;

function StrToHex(const AText: string): string; overload;
begin
  Result := string(StrToHex(AnsiString(AText)));
end;

function HexToStr(const AText: AnsiString): AnsiString; overload;
var
  ResultLen: Integer;
begin
  // set the length of the result to half the Text length
  ResultLen := Length(AText) div 2;
  SetLength(Result, ResultLen);
  // convert the hex back into a string
  if (HexToBin(PAnsiChar(AText), PAnsiChar(Result), ResultLen) <> ResultLen) then
    Result := 'Error Converting Hex To String: ' + AText;
end;

function HexToStr(const AText: string): string; overload;
begin
  Result := string(HexToStr(AnsiString(AText)));
end;

function Encode7Bit(const AText: string; AUdhLen: Byte;
  out ATextLen: Byte): string;
// AText: Ascii text
// AUdhLen: Length of UDH including UDH Len byte (e.g. '050003CC0101' = 6 bytes)
// ATextLen: returns length of text that was encoded.  This can be different
// than Length(AText) due to escape characters
// Returns text as encoded PDU hex string
var
  Text7Bit: AnsiString;
  Pdu: AnsiString;
  PduIdx: Integer;
  PduLen: Byte;
  PaddingBits: Byte;
  BitsToMove: Byte;
  Septet: Byte;
  Octet: Byte;
  PrevOctet: Byte;
  ShiftedOctet: Byte;
  i: Integer;
begin
  Result := '';
  Text7Bit := ConvertAsciiTo7Bit(AText, AUdhLen);
  ATextLen := Length(Text7Bit);
  BitsToMove := 0;
  // determine how many padding bits needed based on the UDH
  if (AUdhLen > 0) then
    PaddingBits := 7 - ((AUdhLen * 8) mod 7)
  else
    PaddingBits := 0;
  // calculate the number of bytes needed to store the 7-bit text
  // along with any padding bits that are required
  PduLen := Ceil(((ATextLen * 7) + PaddingBits) / 8);
  // reserve space for the PDU bytes
  Pdu := AnsiString(StringOfChar(#0, PduLen));
  PduIdx := 1;
  for i := 1 to ATextLen do
  begin
    if (BitsToMove = 7) then
      BitsToMove := 0
    else
    begin
      // convert the current character to a septet (7-bits) and make room for
      // the bits from the next one
      Septet := (Byte(Text7Bit[i]) shr BitsToMove);
      if (i = ATextLen) then
        Octet := Septet
      else
      begin
        // convert the next character to a septet and copy the bits from it
        // to the octet (PDU byte)
        Octet := Septet or
          Byte((Byte(Text7Bit[i + 1]) shl Byte(7 - BitsToMove)));
      end;
      Byte(Pdu[PduIdx]) := Octet;
      Inc(PduIdx);
      Inc(BitsToMove);
    end;
  end;
  // The following code pads the pdu on the *right* by shifting it to the *left*
  // by <PaddingBits>. It does this by using the same bit storage convention as
  // the 7-bit compression routine above, by taking the most significant
  // <PaddingBits> from each PDU byte and moving them to the least significant
  // bits of the next PDU byte. If there is no room in the last PDU byte for the
  // high bits of the previous byte that were removed, then those bits are
  // placed into an additional byte reserved for this purpose.
  // Note: <PduLen> has already been set to account for the reserved byte if
  // it is required.
  if (PaddingBits > 0) then
  begin
    SetLength(Result, (PduLen * 2));
    PrevOctet := 0;
    for PduIdx := 1 to PduLen do
    begin
      Octet := Byte(Pdu[PduIdx]);
      if (PduIdx = 1) then
        ShiftedOctet := Byte(Octet shl PaddingBits)
      else
        ShiftedOctet := Byte(Octet shl PaddingBits) or
          Byte(PrevOctet shr (8 - PaddingBits));
      Byte(Pdu[PduIdx]) := ShiftedOctet;
      PrevOctet := Octet;
    end;
  end;
  Result := string(StrToHex(Pdu));
end;

function Decode7Bit(const APduData: string; AUdhLen: Integer): string;
// APduData: Hex string representation of PDU data
// AUdhLen: Length of UDH including UDH Len (e.g. '050003CC0101' = 6 bytes)
// Returns decoded Ascii text
var
  Pdu: AnsiString;
  NumSeptets: Byte;
  Septets: AnsiString;
  PduIdx: Integer;
  PduLen: Integer;
  by: Byte;
  currBy: Byte;
  left: Byte;
  mask: Byte;
  nextBy: Byte;
  Octet: Byte;
  NextOctet: Byte;
  PaddingBits: Byte;
  ShiftedOctet: Byte;
  i: Integer;
begin
  Result := '';
  PaddingBits := 0;
  // convert hex string to bytes
  Pdu := AnsiString(HexToStr(APduData));
  PduLen := Length(Pdu);
  // The following code removes padding at the end of the PDU by shifting it
  // *right* by <PaddingBits>. It does this by taking the least significant
  // <PaddingBits> from the following PDU byte and moving them to the most
  // significant the current PDU byte.
  if (AUdhLen > 0) then
  begin
    PaddingBits := 7 - ((AUdhLen * 8) mod 7);
    for PduIdx := 1 to PduLen do
    begin
      Octet := Byte(Pdu[PduIdx]);
      if (PduIdx = PduLen) then
        ShiftedOctet := Byte(Octet shr PaddingBits)
      else
      begin
        NextOctet := Byte(Pdu[PduIdx + 1]);
        ShiftedOctet := Byte(Octet shr PaddingBits) or
          Byte(NextOctet shl (8 - PaddingBits));
      end;
      Byte(Pdu[PduIdx]) := ShiftedOctet;
    end;
  end;
  // decode
  // number of septets in PDU after excluding the padding bits
  NumSeptets := ((PduLen * 8) - PaddingBits) div 7;
  Septets := AnsiString(StringOfChar(#0, NumSeptets));
  left := 7;
  mask := $7F;
  nextBy := 0;
  PduIdx := 1;
  for i := 1 to NumSeptets do
  begin
    if mask = 0 then
    begin
      Septets[i] := AnsiChar(nextBy);
      left := 7;
      mask := $7F;
      nextBy := 0;
    end
    else
    begin
      if (PduIdx > PduLen) then
        Break;
      by := Byte(Pdu[PduIdx]);
      Inc(PduIdx);
      currBy := ((by AND mask) SHL (7 - left)) OR nextBy;
      nextBy := (by AND (NOT mask)) SHR left;
      Septets[i] := AnsiChar(currBy);
      mask := mask SHR 1;
      left := left - 1;
    end;
  end; // for
  // remove last character if unused
  // this is kind of a hack, but frankly I don't know how else to compensate
  // for it.
  if (Septets[NumSeptets] = #0) then
    SetLength(Septets, NumSeptets - 1);
  // convert 7-bit alphabet to ascii
  Result := Convert7BitToAscii(Septets);
end;

initialization
  InitializeTables;
end.

解决方案

no you don't include the UDH part when encoding, but you if read the GSM phase 2 specification on page 57, they mention this fact : "If 7 bit data is used and the TP-UD-Header does not finish on a septet boundary then fill bits are inserted after the last Information Element Data octet so that there is an integral number of septets for the entire TP-UD header". When you include a UDH part this could not be the case, so all you need to do is calculate the offset (= number of fill bits)

Calculating the offset, this code assumes that UDHPart is a AnsiString:

Len := Length(UDHPart) shr 1;
Offset := 7 - ((Len * 8) mod 7);  // fill bits

now when encoding the 7bit data, you proceed as normal but at the end, you shift the data Offset bits to the left, this code has the encoded data in variable result (ansistring):

 // fill bits
 if Offset > 0 then
  begin
   v := Result;
   Len := Length(v);
   BytesRemain := ceil(((Len * 7)+Offset) / 8);       
   Result := StringOfChar(#0, BytesRemain);
   for InPos := 1 to BytesRemain do
    begin
     if InPos = 1 then
      Byte(Result[InPos]) := Byte(v[InPos]) shl offset
     else
      Byte(Result[InPos]) := (Byte(v[InPos]) shl offset) or (Byte(v[InPos-1]) shr (8 - offset));
    end;
  end;

Decoding is same thing really, you first shift the 7 bit data offset bits to the right before decoding...

I hope this will set you onto the right track...

这篇关于当我对SMS PDU(GSM 7 Bit)用户数据进行编码/解码时,是否需要先加入UDH?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆