Decode 7-Bit Gsm

decode 7-bit GSM

For Python2:

import binascii
gsm = ("@£$¥èéùìòÇ\nØø\rÅåΔ_ΦΓΛΩΠΨΣΘΞ\x1bÆæßÉ !\"#¤%&'()*+,-./0123456789:;<=>?"
"¡ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÑÜ`¿abcdefghijklmnopqrstuvwxyzäöñüà")
ext = ("````````````````````^```````````````````{}`````\\````````````[~]`"
"|````````````````````````````````````€``````````````````````````")

def gsm_encode(plaintext):
result = []
for c in plaintext:
idx = gsm.find(c)
if idx != -1:
result.append(chr(idx))
continue
idx = ext.find(c)
if idx != -1:
result.append(chr(27) + chr(idx))
return ''.join(result).encode('hex')

def gsm_decode(hexstr):
res = hexstr.decode('hex')
res = iter(res)
result = []
for c in res:
if c == chr(27):
c = next(res)
result.append(ext[ord(c)])
else:
result.append(gsm[ord(c)])
return ''.join(result)

code = gsm_encode("Hello World {}")
print(code)
# 64868d8d903a7390938d853a1b281b29
print(gsm_decode(code))
# Hello World {}

Decode 7-bit GSM in Lua

function GSM7_to_ASCII(hex_string)

local GSM_Base = {
[0] = '@', '£', '$', '¥', 'è', 'é', 'ù', 'ì', 'ò', 'Ç', '\n',
'Ø', 'ø', '\r', 'Å', 'å', 'Δ', '_', 'Φ', 'Γ', 'Λ', 'Ω', 'Π',
'Ψ', 'Σ', 'Θ', 'Ξ', '', 'Æ', 'æ', 'ß', 'É', [36] = '¤',
[64] = '¡', [91] = 'Ä', [92] = 'Ö', [93] = 'Ñ', [94] = 'Ü',
[95] = '§', [96] = '¿', [123] = 'ä', [124] = 'ö', [125] = 'ñ',
[126] = 'ü', [127] = 'à'
}
local GSM_Ext = {
[20] = '^', [40] = '{', [41] = '}', [47] = '\\', [60] = '[',
[61] = '~', [62] = ']', [64] = '|', [101] = '€'
}

local buffer = 0
local buffer_width = 0
local esc = false
local result = {}

for hh in hex_string:gmatch"%x%x" do
buffer = buffer + 2^buffer_width * tonumber(hh, 16)
buffer_width = buffer_width + 8
repeat
local c = buffer % 128
buffer = (buffer - c) / 128
buffer_width = buffer_width - 7
if c == 27 then
esc = true
else
local symbol = esc and GSM_Ext[c] or GSM_Base[c] or string.char(c)
esc = false
table.insert(result, symbol)
end
until buffer_width < 7
end

if buffer_width == 0 and result[#result] == "\r" then
table.remove(result) -- remove padding
end

return table.concat(result)

end

Usage:

local string_coded_7bit_GSM = "737AD80D9AB16E3510394C0F8362B1562CD692C1623910ECA6A3C174B31B"
local decoded_string_ascii = GSM7_to_ASCII(string_coded_7bit_GSM)
print(decoded_string_ascii) --> stan 3,75 data 11-11-2019 07:40:37

When I encode/decode SMS PDU (GSM 7 Bit) user data, do I need prepend the UDH first?

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...

Decode GSM 7 bit in C#

  class GSM7BitDecoder
{
// Basic Character Set
private const string BASIC_SET =
"@£$¥èéùìòÇ\nØø\rÅåΔ_ΦΓΛΩΠΨΣΘΞ\x1bÆæßÉ !\"#¤%&'()*+,-./0123456789:;<=>?" +
"¡ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÑܧ¿abcdefghijklmnopqrstuvwxyzäöñüà";

// Basic Character Set Extension
private const string EXTENSION_SET =
"````````````````````^```````````````````{}`````\\````````````[~]`" +
"|````````````````````````````````````€``````````````````````````";


string[] BASIC_SET_ARRAY = BASIC_SET.Select(x => x.ToString()).ToArray();
string[] EXTENSION_SET_ARRAY = EXTENSION_SET.Select(x => x.ToString()).ToArray();

enum circle { Start=1, Complete=8 }

string GetChar(string bin)
{
try
{
if (Convert.ToInt32(bin, 2).Equals(27))
return EXTENSION_SET_ARRAY[Convert.ToInt32(bin, 2)];
else
return BASIC_SET_ARRAY[Convert.ToInt32(bin, 2)];
}
catch { return string.Empty; }
}
public string DecodeGSM7bit(string strGsm7bit)
{

var suffix = string.Empty;
var septet = string.Empty;
var CurSubstr = string.Empty;
var counter = 1;
List<string> septets = new List<string>();
List<string> sectets = new List<string>();

//Prepare Octets
var octets = Enumerable.Range(0, strGsm7bit.Length / 2).Select(i =>
{
return Convert.ToString(Convert.ToInt64(strGsm7bit.Substring(i * 2, 2), 16), 2).PadLeft(8,'0');

}).ToList();


for (var index=0; index < octets.Count; index = index +1)
{
//Generate Septets
septet = octets[index].Substring(counter);
CurSubstr = octets[index].Substring(0, counter);

if (counter.Equals((int)circle.Start))
septets.Add(septet);
else
septets.Add(septet + suffix);

//Organize Sectets
sectets.Add(GetChar(septets[index]));

suffix = CurSubstr;
counter++;

//Reset counter when the circle is complete.
if (counter == (int)circle.Complete)
{
counter = (int)circle.Start;
sectets.Add(GetChar(suffix));
}

}
return string.Join("", sectets);
}

Looking for GSM 7bit encode/decode algorithm

See if this is of any use to you. Code taken from one of my very old projects - may be used as you wish.

unit SMSCodec;

interface

const
//:Default 7-bit alphabet.
CPDU7bit = #10#13' !"#$&''()*+,-./0123456789:;<=>?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';

type
{:Encoder result.
@enum esEncoded Message encoded successfully.
@enum esTruncated Message encoded successfully, but truncated because it was too long.
@enum esError Error.
}
TEncoderStatus = (esEncoded, esTruncated, esError);

{:Decoder result.
@enum dsDecoded Message decoded successfully.
@enum dsError Error.
}
TDecoderStatus = (dsDecoded, dsError);

{:Message format.
@enum mf0340 ETS 300 901 (GSM 03.40)
@enum mf0705 GSM 07.05
}
TMessageFormat = (mf0340, mf0705);

{:Message Type
}
TMessageType = (mtSMSDeliver, mtSMSDeliverReport, mtSMSSubmitReport,
mtSMSSubmit, mtSMSStatusReport, mtSMSCommand, mtReserved);

{:TP-Status major information.
}
TTPStatusMajor = (tpsmDelivered, tpsmTemporaryError, tpsmPermanentError,
tpsmReserved);

{:TP-Status detailed information.
}
TTPStatusDetailed = (
// tpsmDelivered
tpsdReceived, // Short message received by the SME
tpsdForwardedNotConfirmed, // Short message forwarded by the SC to the SME but the SC is unable to confirm delivery
tpsdReplaced, // Short message replaced by the SC
// tpsmTemporaryError
tpsdCongestion, // Congestion
tpsdSMEBusy, // SME busy
tpsdNoResponseFromSME, // No response from SME
tpsdServiceRejected, // Service rejected
tpsdErrorInSME, // Error in SME
// tpsmPermanentError
tpsdRemoteProcedureError, // Remote procedure error
tpsdIncompatibleDestination, // Incompatible destination
tpsdConnectionRejectedBySME, // Connection rejected by SME
tpsdNotObtainable, // Not obtainable
tpsdNoInternetworkingAvailable, // No interworking available
tpsdSMValitidyPerionExpired, // SM Validity Period Expired
tpsdSMDeletedByOriginatingSME, // SM Deleted by originating SME
tpsdSMDeletedBySCAdministration, // SM Deleted by SC Administration
tpsdSMDoesNotExist, // SM does not exist (The SM may have previously existed in the SC but the SC no longer has knowledge of it or the SM may never have previously existed in the SC)
// tpsmTemporaryError and tpsmPermanentError
tpsdQOSNotAvailable, // Quality of service not available
// all major classes
tpsdReserved
);

{:Decoded TP-Status, as specified in ETSI GSM 03.40 specification, 9.2.3.15
}
TTPStatus = record
tpsMajor : TTPStatusMajor;
tpsWillContinue : boolean;
tpsDetailed : TTPStatusDetailed;
tpsOriginal : byte;
end;

{:Decoded message
}
TSMSDecodedMessage = record
sdcOriginalMessage : string;
sdcMessageType : TMessageType;
// Set if sdcMessageType =
sdcSMSCenterNumber : string; // *
sdcNumber : string; // mtSMSDeliver, mtSMSSubmit
sdcShortMessage : string; // mtSMSDeliver, mtSMSSubmit
sdcValidityMinutes : integer; // mtSMSSubmit
sdcRequestReport : boolean; // mtSMSSubmit
sdcMessageReference: byte; // mtSMSSubmit, mtSMSStatusReport
sdcRecipientAddress: string; // mtSMSStatusReport
sdcSCTimeStamp : TDateTime; // mtSMSStatusReport
sdcDischargeTime : TDateTime; // mtSMSStatusReport
sdcStatus : TTPStatus; // mtSMSStatusReport
sdcMessageFormat : TMessageFormat;// mtSMSDeliver, mtSMSStatusReport
sdcFFPadded : boolean; // mtSMSDeliver, mtSMSStatusReport
end;

{:SMS PDU coder/decoder.
}
TSMSCodec = class
private
tbl7bit: array [char] of byte;
tbl8bit: array [byte] of char;
procedure Create7bitLookupTable;
function Decode7bitText(var pdu: string; txtLen: byte; var decoded: boolean): string;
function DecodeDischargeTime(dtime: string): TDateTime;
function DecodeNumber(var pdu: string; countOctets: boolean; var decoded: boolean): string;
function DecodeTimeStamp(tstamp: string): TDateTime;
function DecodeTPStatus(status: string): TTPStatus;
function Encode7bitText(txt: string; maxLen: byte; var truncated: boolean): string;
function EncodeNumber(num: string; countOctets: boolean): string;
function EncodeTP_VP(validityMin: integer): string;
public
constructor Create;
destructor Destroy; override;
function DecodeMessage(PDUMessage: string;
var DecodedMessage: TSMSDecodedMessage): TDecoderStatus;
function EncodeMessage(smsMessage: TSMSDecodedMessage;
var PDUMessage: string; var tpDataLen: integer): TEncoderStatus;
end;

function GetSMSDetailedErrorMessage(status: TTPStatusDetailed): string;

implementation

uses
SysUtils,
GpIFF,
Gp17String;

resourcestring
SSmsDetailedErrReserved = '(reserved)';
SSmsDetailedErrCongestion = 'Congestion';
SSmsDetailedErrConnectionRejectedBySME = 'Connection rejected by SME';
SSmsDetailedErrErrorInSME = 'Error in SME';
SSmsDetailedErrIncompatibleDestination = 'Incompatible destination';
SSmsDetailedErrMValidityPeriodExpired = 'SM Validity Period Expired';
SSmsDetailedErrNoInterworkingAvailable = 'No interworking available';
SSmsDetailedErrNoResponseFromSME = 'No response from SME';
SSmsDetailedErrNotObtainable = 'Not obtainable';
SSmsDetailedErrQualityOfServiceNotAvailable = 'Quality of service not available';
SSmsDetailedErrRemoteProcedureError = 'Remote procedure error';
SSmsDetailedErrServiceRejected = 'Service rejected';
SSmsDetailedErrShortMessageForwardedByTheSCtoThe = 'Short message forwarded by the SC to the SME but the SC is unable to confirm delivery';
SSmsDetailedErrShortMessageReceivedByTheSME = 'Short message received by the SME';
SSmsDetailedErrShortMessageReplacedByTheSC = 'Short message replaced by the SC';
SSmsDetailedErrSMDeletedByOriginatingSME = 'SM Deleted by originating SME';
SSmsDetailedErrSMDeletedBySCAdministration = 'SM Deleted by SC Administration';
SSmsDetailedErrSMdoesNotExist = 'SM does not exist (The SM may have previously existed in the SC but the SC no longer has knowledge of it or the SM may never have previously existed in the SC)';
SSmsDetailedErrSMEbusy = 'SME busy';
SSmsDetailedErrSMValidityPeriodExpired = 'SM validity period expired';

function GetSMSDetailedErrorMessage(status: TTPStatusDetailed): string;
begin
case status of
tpsdReceived: Result := SSmsDetailedErrShortMessageReceivedByTheSME;
tpsdForwardedNotConfirmed: Result := SSmsDetailedErrShortMessageForwardedByTheSCtoThe;
tpsdReplaced: Result := SSmsDetailedErrShortMessageReplacedByTheSC;
tpsdCongestion: Result := SSmsDetailedErrCongestion;
tpsdSMEBusy: Result := SSmsDetailedErrSMEbusy;
tpsdNoResponseFromSME: Result := SSmsDetailedErrNoResponseFromSME;
tpsdServiceRejected: Result := SSmsDetailedErrServiceRejected;
tpsdErrorInSME: Result := SSmsDetailedErrErrorInSME;
tpsdRemoteProcedureError: Result := SSmsDetailedErrRemoteProcedureError;
tpsdIncompatibleDestination: Result := SSmsDetailedErrIncompatibleDestination;
tpsdConnectionRejectedBySME: Result := SSmsDetailedErrConnectionRejectedBySME;
tpsdNotObtainable: Result := SSmsDetailedErrNotObtainable;
tpsdNoInternetworkingAvailable: Result := SSmsDetailedErrNoInterworkingAvailable;
tpsdSMValitidyPerionExpired: Result := SSmsDetailedErrSMValidityPeriodExpired;
tpsdSMDeletedByOriginatingSME: Result := SSmsDetailedErrSMDeletedByOriginatingSME;
tpsdSMDeletedBySCAdministration: Result := SSmsDetailedErrSMDeletedBySCAdministration;
tpsdSMDoesNotExist: Result := SSmsDetailedErrSMdoesNotExist;
tpsdQOSNotAvailable: Result := SSmsDetailedErrQualityOfServiceNotAvailable;
else Result := SSmsDetailedErrReserved;
end; //case
end;

{ TSMSCodec }

{: TSMSCodec constructor. Prepares lookup table for character conversion.
}
constructor TSMSCodec.Create;
begin
inherited;
Create7bitLookupTable;
end;

{:Creates lookup table to convert from 8-bit to 7-bit character codes.
}
procedure TSMSCodec.Create7bitLookupTable;
var
b : byte;
i : integer;
ch: char;
const
eqlASCII : string = CPDU7bit;
begin
// TODO 1 -oPrimoz Gabrijelcic: Incomplete: all Greek characters and umlauts are missing
for ch := Low(tbl7bit) to High(tbl7bit) do
tbl7bit[ch] := $20; // space
for i := 1 to Length(eqlASCII) do
tbl7bit[eqlASCII[i]] := Ord(eqlASCII[i]);
tbl7bit['@'] := $00;
tbl7bit['$'] := $02;

for b := Low(tbl8bit) to High(tbl8bit) do
tbl8bit[b] := ' ';
for ch := Low(tbl7bit) to High(tbl7bit) do
if tbl7bit[ch] <> $20 then
tbl8bit[tbl7bit[ch]] := ch;
end;

{:Decodes 7-bit "packed" form (coded in hexadecimal - as received in PDU SMS)
into 8-bit text.
@param pdu Hexadecimal representation of packed form.
@param txtLen Length of unpacked string.
@param decoded True if decoded successfully.
@returns Unpacked string.
}
function TSMSCodec.Decode7bitText(var pdu: string; txtLen: byte;
var decoded: boolean): string;
var
by : byte;
currBy: byte;
i : integer;
left : byte;
mask : byte;
nextBy: byte;
begin
decoded := false;
Result := '';
left := 7;
mask := $7F;
nextBy := 0;
for i := 1 to txtLen do begin
if mask = 0 then begin
Result := Result + tbl8bit[nextBy];
left := 7;
mask := $7F;
nextBy := 0;
end
else begin
if pdu = '' then Exit;
by := StrToInt('$'+Copy(pdu,1,2)); Delete(pdu,1,2);
currBy := ((by AND mask) SHL (7-left)) OR nextBy;
nextBy := (by AND (NOT mask)) shr left;
Result := Result + tbl8bit[currBy];
mask := mask SHR 1;
left := left-1;
end;
end; //for
decoded := true;
end;

{:Decodes 7-byte discharge time.
@param dtime Discharge time in hexadecimal form (0340530S.PDF, 9.2.3.13).
@returns Decoded discharge time.
@since 2000-09-05 (1.02)
}
function TSMSCodec.DecodeDischargeTime(dtime: string): TDateTime;
begin
Result := DecodeTimestamp(dtime);
end;

{:Decodes PDU message. Most flags are ignored.
@param PDUMessage Encoded message, represented in hexadecimal form (as received from GSM 07.05 device).
@param DecodedMessage (out) Decoded message.
@returns Error status.
}
function TSMSCodec.DecodeMessage(PDUMessage: string;
var DecodedMessage: TSMSDecodedMessage): TDecoderStatus;

// Mobitel (293 41) sometimes pads PDU with FF bytes up to maximum length -
// this function detects this condition. It is called with unparsed part of
// PDU as parameter. This parameter should be empty or at least contain only
// 'F' characters.
function AllFF(s: string): boolean;
var
iCh: integer;
begin
Result := false;
for iCh := 1 to Length(s) do
if s[iCh] <> 'F' then
Exit;
Result := true;
end;

var
decoded : boolean;
origPDU : string;
PDUtype : byte;
UDL : byte;
workaround: boolean;
// DCS : byte;
// PID : byte;
// SCTC : int64;
begin
DecodedMessage.sdcMessageType := mtReserved; // not decoded
DecodedMessage.sdcOriginalMessage := PDUMessage;
DecodedMessage.sdcFFPadded := false;
Result := dsError;
origPDU := PDUMessage;
try
DecodedMessage.sdcMessageFormat := mf0340;
for workaround := false to true do begin
PDUMessage := origPDU;
if workaround then begin
// Try to detect whether message is in 03.40 format (without SMS Center
// Number) or in 07.05 format (with SMS Center Number).
DecodedMessage.sdcSMSCenterNumber := DecodeNumber(PDUMessage,true,decoded);
if not decoded then
Exit;
DecodedMessage.sdcMessageFormat := mf0705;
end;
PDUtype := StrToInt('$'+Copy(PDUMessage,1,2)); Delete(PDUMessage,1,2);
case PDUtype AND $03 of
0: DecodedMessage.sdcMessageType := mtSMSDeliver;
1: DecodedMessage.sdcMessageType := mtSMSSubmitReport;
2: DecodedMessage.sdcMessageType := mtSMSStatusReport;
3: DecodedMessage.sdcMessageType := mtReserved;
end; //case
if (not workaround) and (DecodedMessage.sdcMessageType = mtReserved) then
continue; // ??? maybe we are decoding PDU from not-completely-compliant telephone ???
case DecodedMessage.sdcMessageType of
mtSMSDeliver:
begin
DecodedMessage.sdcNumber := DecodeNumber(PDUMessage,false,decoded);
if not decoded then
Exit;
{PID := StrToInt('$'+Copy(PDUMessage,1,2));} Delete(PDUMessage,1,2);
{DCS := StrToInt('$'+Copy(PDUMessage,1,2));} Delete(PDUMessage,1,2);
{SCTC := StrToInt64('$'+Copy(PDUMessage,1,14));} Delete(PDUMessage,1,14);
UDL := StrToInt('$'+Copy(PDUMessage,1,2)); Delete(PDUMessage,1,2);
DecodedMessage.sdcShortMessage := Decode7bitText(PDUMessage,UDL,decoded);
if not decoded then
if not workaround then
continue // ??? maybe we are decoding PDU from not-completely-compliant telephone ???
else
Exit;
end; //mtSMSDeliver
mtSMSSubmitReport:
begin
// don't know how to decode, yet
if workaround then // if first way round, assume that we only tried the wrong approach
PDUMessage := '';
end; //mtSMSSubmitReport
mtSMSStatusReport:
begin // 0340530S.PDF, 9.2.2.3 SMS-STATUS-REPORT type
DecodedMessage.sdcMessageReference := StrToInt('$'+Copy(PDUMessage,1,2)); Delete(PDUMessage,1,2);
DecodedMessage.sdcRecipientAddress := DecodeNumber(PDUMessage,false,decoded);
if not decoded then
Exit;
DecodedMessage.sdcSCTimeStamp := DecodeTimeStamp(Copy(PDUMessage,1,14)); Delete(PDUMessage,1,14);
DecodedMessage.sdcDischargeTime := DecodeDischargeTime(Copy(PDUMessage,1,14)); Delete(PDUMessage,1,14);
DecodedMessage.sdcStatus := DecodeTPStatus(Copy(PDUMessage,1,2)); Delete(PDUMessage,1,2);
end; //mtSMSStatusReport
mtReserved:
begin
// don't know how to decode - obviously
PDUMessage := '';
end; //mtReserved
end; //case
if PDUMessage = '' then begin
Result := dsDecoded;
break;
end;
if AllFF(PDUMessage) then begin
DecodedMessage.sdcFFPadded := true;
Result := dsDecoded;
break;
end;
end; //for workaround
except
on EConvertError do ;
end;
end;

{:Decodes number by GSM standards. Understands two formats - prefixed with
number of bytes (if countOctets is set) or number of digits in original number.
@param pdu (in, out) PDU string. Number will be cut from it.
@param countOctets If true, number is written with number of resulting bytes prepended.
@param decoded (out) Set to true if number was decoded successfully.
@returns Decoded number.
}
function TSMSCodec.DecodeNumber(var pdu: string;
countOctets: boolean; var decoded: boolean): string;
var
iOct : integer;
n1 : integer;
n2 : integer;
numLen : byte;
numType: byte;
begin
Result := '';
decoded := false;
if pdu <> '' then begin
try
numLen := StrToInt('$'+Copy(pdu,1,2)); Delete(pdu,1,2);
numType := StrToInt('$'+Copy(pdu,1,2)); Delete(pdu,1,2);
if (numType AND $90) = $90 then
Result := '+';
if not countOctets then
numLen := (numLen+1) div 2 + 1;
for iOct := 1 to numLen-1 do begin
n1 := StrToInt('$'+Copy(pdu,1,1)); Delete(pdu,1,1);
n2 := StrToInt('$'+Copy(pdu,1,1)); Delete(pdu,1,1);
Result := Result + IntToStr(n2);
if n1 <> $F then
Result := Result + IntToStr(n1);
end; //for
decoded := true;
except
on EConvertError do Result := '';
on ERangeError do Result := '';
end;
end;
end;

{:Decodes 7-byte timestamp.
@param tstamp Timestamp in hexadecimal form (0340530S.PDF, 9.2.3.11).
@returns Decoded timestamp.
@since 2000-09-05 (1.02)
}
function TSMSCodec.DecodeTimeStamp(tstamp: string): TDateTime;
var
day : integer;
gmt : integer;
gmtSign: integer;
hour : integer;
minute : integer;
month : integer;
second : integer;
year : integer;
begin
year := StrToInt(tstamp[ 2]+tstamp[ 1]);
month := StrToInt(tstamp[ 4]+tstamp[ 3]);
day := StrToInt(tstamp[ 6]+tstamp[ 5]);
hour := StrToInt(tstamp[ 8]+tstamp[ 7]);
minute := StrToInt(tstamp[10]+tstamp[ 9]);
second := StrToInt(tstamp[12]+tstamp[11]);
gmtSign := IFF(StrToInt(tstamp[14]) AND 8 = 0, 1, -1);
gmt := (StrToInt(tstamp[13]) + 10*(StrToInt(tstamp[14]) AND (NOT 8))) * gmtSign;
if year > 80 then
year := 1900 + year
else
year := 2000 + year;
try
Result := EncodeDate(year,month,day) + EncodeTime(hour, minute, second, 0) - gmt;
except
on EConvertError do
Result := 0;
end;
end;

{:Decodes TP-Status.
@param status TP-Status (0340530S.PDF, 9.2.3.15).
@returns Decoded status
@since 2000-09-05 (1.02)
}
function TSMSCodec.DecodeTPStatus(status: string): TTPStatus;
begin
Result.tpsOriginal := StrToInt('$'+status);
if Result.tpsOriginal <= 2 then
Result.tpsMajor := tpsmDelivered
else if (Result.tpsOriginal AND $60) = $20 then begin
Result.tpsMajor := tpsmTemporaryError;
Result.tpsWillContinue := true;
end
else if (Result.tpsOriginal AND $60) = $40 then begin
Result.tpsMajor := tpsmPermanentError;
Result.tpsWillContinue := false;
end
else if (Result.tpsOriginal AND $60) = $40 then begin
Result.tpsMajor := tpsmTemporaryError;
Result.tpsWillContinue := false;
end
else
Result.tpsMajor := tpsmReserved;
case Result.tpsMajor of
tpsmDelivered:
begin
case Result.tpsOriginal of
0: Result.tpsDetailed := tpsdReceived;
1: Result.tpsDetailed := tpsdForwardedNotConfirmed;
2: Result.tpsDetailed := tpsdReplaced;
else Result.tpsDetailed := tpsdReserved;
end; //case
end; // tmspDelivered
tpsmTemporaryError:
begin
case Result.tpsOriginal AND (NOT $40) of
32: Result.tpsDetailed := tpsdCongestion;
33: Result.tpsDetailed := tpsdSMEBusy;
34: Result.tpsDetailed := tpsdNoResponseFromSME;
35: Result.tpsDetailed := tpsdServiceRejected;
36: Result.tpsDetailed := tpsdQOSNotAvailable;
37: Result.tpsDetailed := tpsdErrorInSME;
else Result.tpsDetailed := tpsdReserved;
end; //case
end; // tpsmTemporaryError
tpsmPermanentError:
begin
case Result.tpsOriginal of
64: Result.tpsDetailed := tpsdRemoteProcedureError;
65: Result.tpsDetailed := tpsdIncompatibleDestination;
66: Result.tpsDetailed := tpsdConnectionRejectedBySME;
67: Result.tpsDetailed := tpsdNotObtainable;
68: Result.tpsDetailed := tpsdQOSNotAvailable;
69: Result.tpsDetailed := tpsdNoInternetworkingAvailable;
70: Result.tpsDetailed := tpsdSMValitidyPerionExpired;
71: Result.tpsDetailed := tpsdSMDeletedByOriginatingSME;
72: Result.tpsDetailed := tpsdSMDeletedBySCAdministration;
73: Result.tpsDetailed := tpsdSMDoesNotExist;
else Result.tpsDetailed := tpsdReserved;
end; //case
end; // tpsmPermanentError
tpsmReserved:
begin
Result.tpsDetailed := tpsdReserved;
end; // tpsmReserved
end; //case
end;

{: TSMSCodec destructor. No special cleanup required.
}
destructor TSMSCodec.Destroy;
begin
inherited;
end;

{:Encodes 8-bit text into 7-bit "packed" form. 160 8-bit characters can be
packed into 140 bytes (consisting of 160 7-bit characters). Packed string is
converted into hexadecimal form as required by GSM standards. If input string
is longer that maxLen parameter, truncated flag is set and string is truncated.
@param txt Original 8-bit character string.
@param maxLen Maximum length of original string.
@param truncated (out) Set if original string is longer than maxLen.
@returns Packed string in hexadecimal form.
}
function TSMSCodec.Encode7bitText(txt: string; maxLen: byte; var truncated: boolean): string;
var
buffer : byte;
ch : byte;
i : integer;
leftover: byte;
begin
truncated := (Length(txt) > maxLen);
if truncated then
txt := First(txt,maxLen);
Result := '';
buffer := 0;
leftover := 0;
for i := 1 to Length(txt) do begin
ch := tbl7bit[txt[i]];
if leftover = 0 then begin
buffer := ch;
leftover := 1;
end
else begin
buffer := buffer OR byte(ch SHL (8-leftover));
Result := Result + HexStr(buffer,1);
if leftover < 7 then begin
buffer := ch SHR leftover;
Inc(leftover);
end
else begin
buffer := 0;
leftover := 0;
end;
end;
end; //for
if leftover > 0 then
Result := Result + HexStr(buffer,1);
end;

{:Prepares PDU message. If original message is longer than 160 characters, it
will be truncated. Most of the parameters are currently hardcoded.
@param decodedMessage Message record.
@param PDUMessage (out) Encoded message, represented in hexadecimal form (suitable for sending to GSM 07.05 device).
@param tpDataLen (out) Number of bytes in TP layer data unit.
@returns Error status.
}
function TSMSCodec.EncodeMessage(smsMessage: TSMSDecodedMessage;
var PDUMessage: string; var tpDataLen: integer): TEncoderStatus;
var
DCS : byte;
MR : byte;
PDUtype: byte;
PID : byte;
TP_VP : string;
TP_VPF : integer;
tpLayer: string;
trunc : boolean;
UD : string;
UDL : byte;
begin
// Some parameters are hardcoded
if smsMessage.sdcValidityMinutes = 0 then begin
TP_VPF := 0; // TP-VP field not present
TP_VP := '';
end
else begin
TP_VPF := 2; // TP-VP field present and integer represented (relative)
TP_VP := EncodeTP_VP(smsMessage.sdcValidityMinutes);
end;
PDUtype := $01 OR (TP_VPF SHL 3) OR ((Ord(smsMessage.sdcRequestReport) AND 1) SHL 5);

MR := smsMessage.sdcMessageReference;
PID := $00;
DCS := $00;
UD := Encode7bitText(smsMessage.sdcShortMessage,160,trunc);
UDL := Length(smsMessage.sdcShortMessage);
tpLayer :=
HexStr(PDUtype,1) +
HexStr(MR,1) +
E

Related Topics



Leave a reply



Submit