一、git仓库
https://github.com/tardigrade888/py-pdu
二、代码
2.1 ul_gsm.py
#!/usr/bin/python # -*- coding: UTF-8 -*- # ------------------------------------------------------------------------------ def b_swap( b ): 'swap nibbles' return ((b >> 4) & 0xF) | ((b << 4) & 0xF0) # ------------------------------------------------------------------------------ def bin2bcd( n ): 'convert binary to BCD' bcd,shift = 0,0 while n != 0: bcd |= (n % 10) << shift n //= 10 shift += 4 return bcd def bcd2bin( bcd ): 'convert BCD to binary' n,mul = 0,1 while bcd != 0: n += (bcd & 0xF) * mul bcd >>= 4 mul *= 10 return n # ------------------------------------------------------------------------------ def n2h4( n ): 'convert 4 bits to 1 hex char' n &= 0xF return chr( (n + 48) if ( n < 10 ) else (n + 55) ) def n2h8( n ): 'convert 8 bits to 2 hex chars' return n2h4( n >> 4 ) + n2h4( n ) def n2h16( n ): 'convert 16 bits to 4 hex chars' return n2h8( n >> 8 ) + n2h8( n ) def n2h( n ): 'convert number to Hex' h = [] while (n > 0): h.insert(0, n2h4(n)) n >>= 4 h = ['0'] if len(h) == 0 else h return ''.join(h) # ------------------------------------------------------------------------------ def h2n4( h ): 'convert Hex digit to number' b = ord(h[0]) b = (b - 48) if (48 <= b <= 57) else b b = (b - 55) if (65 <= b <= 90) else b b = (b - 87) if (97 <= b <= 122) else b return b & 0xF def h2n( h ): 'convert Hex string to number' n = 0 for c in h: n = (n << 4) | h2n4(c) return n # ------------------------------------------------------------------------------ def s_is_7bit( s ): 'check if all chars of s is 7-bit' for c in s: if ( ord(c) > 127 ): return False return True # ------------------------------------------------------------------------------ def s_to_bytes( s ): 'convert string to array of bytes' return [ ord(c) for c in s ] def bytes_to_s( a ): 'convert array of bytes to string' return u''.join([ chr(b) for b in a ]) # ------------------------------------------------------------------------------ def bytes_to_hex( a ): 'convert array of bytes to Hex string' return ''.join([ n2h8(b) for b in a ]) def hex_to_bytes( h ): 'convert Hex string to bytes' return [ h2n(h[i*2 : (i+1)*2]) for i in range(len(h) // 2) ] # ------------------------------------------------------------------------------ def phone_clean( s ): 'clean non-number chars from phone string' p = [] for c in s: if ( 48 <= ord(c) <= 57 ): p.append(c) return ''.join(p) def phone_digits( s ): 'number of phone digits' s = phone_unpack(s) if phone_packed(s) else s return len( phone_clean( s )) def phone_packed( s ): 'check if s is packed' return not isinstance(s, str) def phone_pack( p ): 'pack phone number string to byte array' p = phone_clean(p) p = hex_to_bytes( (p + 'F') if ((len(p) % 2) == 1) else p ) for i in range(len(p)): p[i] = b_swap(p[i]) return p def phone_unpack( p ): 'unpack phone number bytes to string' s = ['+'] for n in p: s.append( n2h8(b_swap(n)) ) s = ''.join(s) return s[:-1] if (s[-1] == 'F') else s # ------------------------------------------------------------------------------ def pdu_7to8( a ): 'pack 7-bit array to 8-bit' if len(a) > 0: lenOut = len(a) - (len(a) >> 3) a.append(0) for i1 in range(len(a)-2): for i2 in range(len(a)-2, i1-1, -1): a[i2] |= ( 0x80 if ((a[i2+1] & 1) != 0) else 0 ) a[i2+1] >>= 1 a = a[0:lenOut] return a def pdu_8to7( a ): 'unpack 8-bit array to 7-bit' lenOut = len(a) + (len(a) >> 3) #+ (1 if ((len(a) & 7) != 0) else 0) while (len(a) < lenOut): a.append(0) if (len(a) > 0): for i1 in range(lenOut-1): for i2 in range(lenOut-2, i1-1, -1): a[i2+1] = (a[i2+1] << 1) | (1 if ((a[i2] & 0x80) != 0) else 0) for i in range(len(a)): a[i] &= 0x7F return a def pdu_ucs2_to_bytes( a ): 'convert UCS2 words to bytes' b = [] for w in a: b.append( (w >> 8) & 0xFF ) b.append( w & 0xFF ) return b def pdu_s_len( s ): 'calculate length of char/UCS2 string' return len(s) if s_is_7bit(s) else len(s)*2 def pdu_s_pack( s ): 'pack char/UCS2 string to bytes' a = s_to_bytes(s) return pdu_7to8(a) if s_is_7bit(s) else pdu_ucs2_to_bytes(a) def pdu_s_unpack( a, ucs2 ): 'unpack bytes to char/UCS2 string, ucs2=False/True' return bytes_to_s( [(a[i] << 8) + a[i+1] for i in range(0, len(a), 2)] if ucs2 else pdu_8to7(a) ) # ------------------------------------------------------------------------------ def date_pack( year,month,date, hour,minute,second, tz ): 'pack date fields to byte array' return [ b_swap( bin2bcd( year % 100)), b_swap( bin2bcd( month % 13)), b_swap( bin2bcd( date % 32)), b_swap( bin2bcd( hour % 25)), b_swap( bin2bcd( minute % 60)), b_swap( bin2bcd( second % 60)), b_swap( bin2bcd( tz)) ] def date_unpack( a ): 'unpack packed date array to unpacked [year,month,date, hour,minute,second,tz]' return [ bcd2bin( b_swap( a[0] )) + 2000, bcd2bin( b_swap( a[1] )), bcd2bin( b_swap( a[2] )), bcd2bin( b_swap( a[3] )), bcd2bin( b_swap( a[4] )), bcd2bin( b_swap( a[5] )), bcd2bin( b_swap( a[6] )), ] # ------------------------------------------------------------------------------ class Sms: 'Sms message class, for generation and parsing' def __init__(self, o={}): self.error = True self.smsc,self.smscLen,self.smscType = '',0,145 self.sender,self.senderLen,self.senderType = '',0,145 self.text,self.textLen,self.textBytes,self.textRaw = '',0,0,[] self.smsSubmit,self.firstOctet = 0,0x11 self.tpMsgRef,self.tpPID,self.tpDCS,self.tpVP = 0,0,0xFF,0xAA self.year,self.month,self.date,self.hour,self.minute,self.second,self.tz = 2000,1,1,0,0,0,0 self.pdu,self.pduHex,self.pduLen = [],'',0 self.join(o) def join(self, o={}): 'joins o dict with this Sms' for k in o: if k in self.__dict__: self.__dict__[k] = o[k] def decode_in( self, pdu ): 'decode incoming SMS PDU hex/bytes' if isinstance(pdu, str): pdu = hex_to_bytes(pdu) self.pdu,self.pduHex,i = pdu,bytes_to_hex(pdu),0 if len(pdu) >= 25: self.smscLen = pdu[0] if self.smscLen > 0: self.smscType = pdu[1] self.smsc = phone_unpack( pdu[2 : self.smscLen + 1] ) i += self.smscLen + 1 self.firstOctet = pdu[i]; i += 1 self.senderLen = (pdu[i] >> 1) + (pdu[i] & 1); i += 1 self.senderType = pdu[i]; i += 1 self.sender = phone_unpack( pdu[ i : i+self.senderLen ] ); i += self.senderLen self.tpPID = pdu[i]; i += 1 self.tpDCS = pdu[i]; i += 1 date = date_unpack( pdu[i : i + 7] ) self.year,self.month,self.date,self.hour,self.minute,self.second,self.tz = date[0],date[1],date[2], date[3],date[4],date[5], date[6] i += 7 self.textLen = pdu[i]; i += 1 self.textBytes = (self.textLen - (self.textLen >> 3)) if (self.tpDCS == 0) else self.textLen print( bytes_to_hex(pdu[ i : i+self.textBytes ])) self.text = pdu_s_unpack( pdu[ i : i+self.textBytes ], self.tpDCS == 8 ) ''' sms.textLen = sms.bytes[sms.smscLen + sms.senderLen + 13]; sms.textRaw = sms.bytes.slice(sms.smscLen + sms.senderLen + 14, sms.smscLen + sms.senderLen + 14 + sms.textLen); sms.text = tools.bytesToString(tools.gsm.pdu.to7bit(sms.textRaw)); ''' self.error = False return self def encode_out( self, o={} ): 'encode outcoming PDU SMS' self.join( o ) pdu = [] if len(self.smsc) != 0: smsc = phone_pack( self.smsc ) pdu = [ len(smsc) + 1, self.smscType ] + smsc else: pdu = [0] self.tpDCS = 0 if s_is_7bit(self.text) else 8 pdu += [ self.firstOctet, self.tpMsgRef ] pdu += [ phone_digits( self.sender ), self.senderType ] + phone_pack( self.sender ) pdu += [ self.tpPID, self.tpDCS, self.tpVP ] pdu += [ len( self.text ) if (self.tpDCS == 0) else len( self.text ) * 2 ] pdu += pdu_s_pack( self.text ) self.pdu,self.pduHex = pdu,bytes_to_hex(pdu) return self
2.2 使用教程
import ul_gsm as gsm # SMS-SUBMIT encode test sms = gsm.Sms().encode_out( { 'smsc':'+38068240656', 'sender':'+380993435400', 'text':u'юыо' } ) print( sms.pduHex ) print( gsm.pdu_s_pack(u'юыо') ) # SMS-DELIVER decode test pdu = '07917238010010F5040BC87238880900F10000993092516195800AE8329BFD4697D9EC37' pdu = '07911326040000F0040B911346610089F60000208062917314080CC8F71D14969741F977FD07' sms = gsm.Sms().decode_in( pdu ) print(sms.smsc, sms.smscType, sms.firstOctet, sms.senderLen, sms.senderType, sms.sender, sms.tpPID, sms.tpDCS) print(sms.year,sms.month,sms.date, sms.hour,sms.minute,sms.second, sms.tz) print(sms.textLen, sms.textBytes, sms.text) # date pack/unpack print( gsm.date_unpack( gsm.date_pack( 2014,10,12, 17,5,30, 0 ))) print( gsm.bytes_to_hex( gsm.date_pack( 2014,10,12, 17,5,30, 0 ))) # string pack/unpack print( bytes_to_hex( pdu_s_pack(u'0123456789') ) ) print( pdu_s_unpack( pdu_s_pack(u'0123456789абв'), True ) ) # phone pack/unpack print(phone_unpack(phone_pack('+38099-343-54-07-01'))) print hex_to_bytes('0001020304aabbeeff') print(bytes_to_s( s_to_bytes( u'01234абвыв' ) ))
三、注意
encode_out会自动调整短信字符位数,当内容为字符串是,长度为7位,当有中文是长度为16位