Looking for GSM 7bit encode/decode algorithm

2019-06-09 15:02发布

I need to Send Short message in pdu mode. Anybody can give me a GSM 7bit encode/decode algorithm?

标签: delphi gsm pdu
2条回答
爷、活的狠高调
2楼-- · 2019-06-09 15:52

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)                        +
    EncodeNumber(smsMessage.sdcNumber,false) +
    HexStr(PID,1)                       +
    HexStr(DCS,1)                       +
    TP_VP                               +
    HexStr(UDL,1)                       +
    UD;
  PDUMessage :=
    EncodeNumber(smsMessage.sdcSMSCenterNumber,true) +
    tpLayer;
  tpDataLen := Length(tpLayer) div 2;
  if trunc then
    Result := esTruncated
  else
    Result := esEncoded;
end;

{:Encodes number by GSM standards. Prefixes it with either number of bytes
  (if countOctets is set) or number of digits in original number.
  @param   num         Telephone number if international (starts with + or 00) or local form.
  @param   countOctets If true, number of resulting bytes will be prepended, if false, number of digits in num.
  @returns Encoded number.
}
function TSMSCodec.EncodeNumber(num: string; countOctets: boolean): string;
var
  numLen : byte;
  numType: byte;
begin
  num := Replace(ReplaceAllSet(num,[#0..#255]-['0'..'9','+'],' '),' ','');
  if num <> '' then begin
    if num[1] = '+' then begin
      Delete(num,1,1);
      numType := $91;
    end
    else if First(num,2) = '00' then begin
      Delete(num,1,2);
      numType := $91;
    end
    else numType := $81;
    if countOctets then
      numLen := ((Length(num)+1) div 2) + 1
    else
      numLen := Length(num);
    if Odd(Length(num)) then num := num + 'F';
    Result := HexStr(numLen,1) + HexStr(numType,1);
    while num <> '' do begin
      Result := Result + num[2] + num[1];
      Delete(num,1,2);
    end; //while
  end
  else Result := '00';
end;

{:Encodes relative validity into TP_VP parameter. GSM 03.40, 9.2.3.12.
  @param   validityMin Validity period in minutes.
  @returns Encoded TP_VP.
  @since   2000-09-06 (1.02)
}
function TSMSCodec.EncodeTP_VP(validityMin: integer): string;
var
  value: byte;
begin
//  5 minute intervals :    5 minutes               to 12 hours (720 minutes)
//  30 minute intervals: 12.5 hours (750 minutes)   to 24 hours (1440 minutes)
//  1 hour intervals   :    2 days  (2880 minutes)  to 30 days  (43200 minutes)
//  1 week intervals   :    5 weeks (50400 minutes) to 63 weeks (635040 minutes)

  if validityMin <= 720 then begin
    if validityMin < 5 then
      validityMin := 5;
    value := ((validityMin-1) div 5);
  end
  else if validityMin <= 1440 then
    value := (((validityMin-1) - 720) div 30) + 144
  else if validityMin <= 43200 then
    value := ((validityMin-1) div 1440) + 167
  else begin
    if validityMin > 635040 then
      validityMin := 635040;
    value := ((validityMin-1) div 10080) + 193;
  end;
  Result := HexStr(value,1);
end;

end.
查看更多
太酷不给撩
3楼-- · 2019-06-09 15:56

Take a look for SMS component in TurboPower's AsyncPro.

查看更多
登录 后发表回答