PDU decoding support
[ussd-widget] / ussd-common / src / usr / lib / python2.5 / gsmdecode.py
index 4fc1392..5a8d3e3 100644 (file)
@@ -1,3 +1,12 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published
+## by the Free Software Foundation; version 2 and higer.
+##
+## Martin Grimme (martin.grimme # gmail.com) 2010
+## Guseynov Alexey (kibergus # gmail.com) 2010
+
 LANG_DE = 0x0
 LANG_EN = 0x1
 LANG_IT = 0x2
@@ -115,7 +124,7 @@ GSM_DEFAULT_ALPHABET = [
     u"\u00d6",
     u"\u00d1",
     u"\u00dc",
-    u"ยง",
+    u"\u00a7",
 
     u"\u00bf",
     u"a",
@@ -165,7 +174,7 @@ def decode(s, n):
     # separate into nibbles
     hbits = (n & 0xf0) >> 4
     lbits = (n & 0x0f)
-
+    
     if (hbits == 0x0):
         # language
         return _decode_language(s, lbits)
@@ -176,7 +185,7 @@ def decode(s, n):
         
     elif (0x4 <= hbits <= 0x7):
         # general data coding indication
-        return _decode_general_data_coding(s, h, l)
+        return _decode_general_data_coding(s, hbits, lbits)
         
     elif (0x8 <= hbits <= 0xe):
         # reserved coding group
@@ -194,10 +203,9 @@ def _decode_language(s, lang):
 
 def _decode_default_alphabet(s):
     
-    # TODO: we really might have to do 7 bit character unpacking here
-    
     # ought to be all in the 7 bit GSM character map
-    chars = [ _GSM_DEFAULT_ALPHABET[ord(c)] for c in s ]
+    # modem is in 8 bit mode, so it makes 7 bit unpacking itself
+    chars = [ GSM_DEFAULT_ALPHABET[ord(c)] for c in s ]
     u_str = "".join(chars)
     return u_str.encode("utf-8")
 
@@ -217,20 +225,81 @@ def _decode_general_data_coding(s, h, l):
     is_compressed = (h & 0x2)
     
     alphabet = (l & 0xc) >> 2
-    if (l == 0x0):
+
+    if (alphabet == 0x0):
         # default alphabet
-        return _decode_defaul_alphabet(s)
+        return _decode_default_alphabet(s)
         
-    elif (l == 0x1):
+    elif (alphabet == 0x1):
         # 8 bit
         # actually, encoding is user-defined, but let's assume hex'd ASCII
         # for now
         return _decode_hex(s)
         
-    elif (l == 0x2):
+    elif (alphabet == 0x2):
         # USC2 (16 bit, BE)
         return _decode_usc2(s)
-    elif (l == 0x3):
+    elif (alphabet == 0x3):
         # reserved
         return s
 
+def decode_number(number):
+       dnumber = ""
+       for i in number:
+               if i & 0xf < 10:
+                       dnumber += str(int((i & 0xf)))
+               if (i & 0xf0) >> 4 < 10:
+                       dnumber += str(int((i & 0xf0) >> 4))
+       return dnumber
+
+def decode_timestamp(timestamp):
+       res = {}
+       res['year'] =  str(timestamp[0] & 0xf) + str((timestamp[0] & 0xf0) >> 4)
+       res['month'] = str(timestamp[1] & 0xf) + str((timestamp[1] & 0xf0) >> 4)
+       res['day'] = str(timestamp[2] & 0xf) + str((timestamp[2] & 0xf0) >> 4)
+       res['hour'] = str(timestamp[3] & 0xf) + str((timestamp[3] & 0xf0) >> 4)
+       res['minute'] = str(timestamp[4] & 0xf) + str((timestamp[4] & 0xf0) >> 4)
+       res['second'] = str(timestamp[5] & 0xf) + str((timestamp[5] & 0xf0) >> 4)
+       res['timezone'] = str(timestamp[6] & 0xf) + str((timestamp[6] & 0xf0) >> 4)
+       return res
+
+def decode_pdu (pdumsg):
+       pdu = {}
+       pdu['type'] = int(pdumsg[0])
+       if pdu['type'] & 0x3 == 0x0:
+               pdu['address_len'] = int(pdumsg[1])
+               pdu['type_of_address'] = int(pdumsg[2])
+               base = 3+(pdu['address_len']+1)/2
+               pdu['sender'] = decode_number(pdumsg[3:base])
+               pdu['pid'] = int(pdumsg[base])
+               pdu['dcs'] = int(pdumsg[base+1])
+               pdu['timestamp'] = decode_timestamp (pdumsg[base+2:base+9]);
+               pdu['udl'] = int(pdumsg[base+9])
+               pdu['user_data'] = pdumsg[base+10:len(pdumsg)]
+
+               alphabet = (pdu['dcs'] & 0xc) >> 2
+               if alphabet == 0x0:
+                       # This is 7-bit data. Taking of only pdu['udl'] bytes is important because we can't distinguish 0 at the end from @ if only one bit is used in last bite
+                       pdu['user_data'] = _decode_default_alphabet(deoctify(pdu['user_data']))[0:pdu['udl']]
+               elif alphabet == 0x1:
+                       # actually, encoding is user-defined, but let's assume ASCII
+                       pdu['user_data'] = ''.join([chr(i) for i in pdu['user_data']])
+               elif (alphabet == 0x2):
+                       # USC2 (16 bit, BE)
+                       pdu['user_data'] = (''.join([chr(i) for i in pdu['user_data'][6:len(pdu['user_data'])]])).decode("utf-16-be").encode("utf-8")
+               elif (alphabet == 0x3):
+                       # reserved
+                       pdu['user_data'] = ''.join([chr(i) for i in pdu['user_data']])
+
+               if pdu['type'] & 0x4 == 0:
+                       pdu['part'] = True
+               else:
+                       pdu['part'] = False
+       else:
+               # TODO support other types of messages
+               # This is not incoming message
+               # pdu['type'] & 0x3 == 2 means delivery report
+               return None
+
+       return pdu
+