Опубликован: 05.11.2013 | Доступ: свободный | Студентов: 542 / 46 | Длительность: 11:51:00
Лекция 10:

Обмен данными и вопросы кодирования

< Лекция 9 || Лекция 10: 1234 || Лекция 11 >

Для восьмого входного символа все семь значащих бит записываются в 7-й байт результата. После каждого перехода к следующему символу во входной строке необходимо сохранить текущий выходной байт в переменную tmpVall, чтобы "освободить" для следующего шага переменную tmpVal2:

if (shift != 7)
{
  ...
    tmpVal1 = tmpVal2;
}  
    

Такое перемещение не надо выполнять только для каждого восьмого входного символа, когда все семь значащих бит сохраняются в предыдущий выходной байт, а текущий оказывается незаполненным (shift = 7).

Как уже было отмечено, кодирование происходит в один проход. В результате выполнения функции получается не бинарный код, реально отправляемый в SMS, а его шестнадцатеричное представление, которое можно передать GSM-модему посредством терминала. По-этому каждый выходной байт представляется в виде пары шестнадцатеричных цифр:

res[ j] = decToHex(tmpVal1 / 16);
res[ j + 1] = decToHex(tmpVal1 % 16);
  
    

Функция decToHex преобразует десятичную цифру в шестнадцатеричную.

char decToHex(unsigned char val)
{
  if ( (val >= 0) && (val <= 9) )
  {
    return (char)(val + (unsigned char)'0');
  } else
  {
    if ( (val >= 10) && (val <= 15) )
    {
      return (char)(val + (unsigned char)'A' - 10);
    } else
    { return 0; }
  }
}  
    

Деление десятичного числа на 16 дает первый разряд шестнадцатеричного числа, а остаток от деления - второй. Отметим, что в выходную строку записывается не текущий получаемый выходной байт (tmpVal2), а предыдущий - tmpVal1, так как к нему в старшие биты уже добавлены биты следующего входного символа (получаемые как curNum << (8 - shift)). Поэтому после прохождения всей входной строки в цикле while (i < len) {...} в переменной tmpVal2 остается последний формируемый выходной символ, который в конце цикла записывается в tmpVal1. Его приходится дополнительно кодировать в шестнадцатеричное представление после завершения цикла:

res[j] = decToHex(tmpVal1 / 16);
res[j + 1] = decToHex(tmpVal1 % 16);
res[j + 2] = '\0';  
    

В конец строки согласно правилам представления строки в языке Си дописывается символ '\0 '.

Теперь рассмотрим функцию декодирования сообщения decodeFromPDU:

void decodeFromPDU(char *str, char *res)
{
  int len;
  int i, j;
  int shift;
  unsigned char prevNum, d1, d2, curNum, curRes;
  j = 0;
  len = length(str);
  i = 1;
  prevNum = 0;
  while(str[i-1])
  {
    if(str[i])
    {
      d1 = hexToDec(str[i-1]);
      d2 = hexToDec(str[i]);
      curNum = d1*16 + d2;
      shift = ((((i + 1) / 2)-1) % 7);
      if(((shift) == 0)&&(i>1))
      {
        prevNum = prevNum & 127;
        res[j++] = prevNum;
        /* special code for symbol @ */
        if (res[(j-1)] == 0)
        {res[(j-1)] = (unsigned char)'@';}
        prevNum = (curNum >> (7 - shift));
      }
      curRes = (curNum << shift) + prevNum;
      curRes = curRes & 127;
      res[j++] = curRes;
      /* special code for symbol @ */
      if (res[(j-1)] == 0)
      {res[(j-1)] = (unsigned char)'@';}
      prevNum = (curNum >> (7 - shift));
    } else
    {
      break;
    }
    i+=2;
  }
  if(shift == 6)
  {
    prevNum = prevNum & 127;
    /* special code for symbol @ */
    if (res[(j-1)] == 0) {res[(j-1)] = (unsigned char)'@';}
    res[j++] = prevNum;
  }
  res[j] = '\0';
}  
    

Она также последовательно движется по входной строке str, в которой закодированный текст SMS-сообщения представлен в шестнадцатеричном текстовом виде.

Поэтому шаг равен двум (на каждой итерации цикла прохода по строке str выполняется i+=2) и очередные два символа переводятся в один байт кода SMS:

d1 = hexToDec(str[i-1]);
d2 = hexToDec(str[i]);
curNum = d1*16 + d2;  
    

Функция hexToDec переводит шестнадцатеричную текстовую цифру в десятичное число:

unsigned char hexToDec(char hexDigit)
{
  if ((hexDigit >= '0') && (hexDigit <= '9'))
  {
    return (unsigned char)hexDigit - (unsigned char)'0';
  } else
  {
    if ((hexDigit >= 'A') && (hexDigit <= 'F'))
    {
      return (unsigned char)hexDigit -
      (unsigned char)'A' + 10;
    } else
    {
      return 0;
    }
  }
}  
    

Далее рассчитывается сдвиг, на который в текущем кодированном байте сдвинут код текущего символа: shift =( (((i + 1) / 2) -1) % 7);

Здесь ((i + 1) / 2) дает номер входного байта (i - это счетчик шестнадцатеричных символов, начинающийся с 0, на каждый входной кодированный байт занято две шестнадцатеричные цифры).

На первом шаге (i=1) сдвиг равен нулю, на втором он равен 1, для 7-го входного кодированного байта (i=13) сдвиг равен 6. Для 8-го входного байта сдвиг снова равен 0, так как в его 7-ми младших байтах полностью содержится 9-й выходной символ. А 8-й выходной символ хранится в 7-ми старших битах 7-го входного байта.

На каждом 7-м входном байте, для которого сдвиг оказывается равен 0, надо сделать двойную обработку - "вынуть" символ из предыдущего байта с учетом одного бита из текущего байта и "вынуть" символ из текущего байта, для чего дополнительно выполняется следующий код:

if(((shift) == 0)&&(i>1))
{
  prevNum = prevNum & 127;
  res[j++] = prevNum;
  /* special code for symbol @ */
  if (res[(j-1)] == 0)
  {res[(j-1)] = (unsigned char)'@';}
  prevNum = (curNum >> (7 - shift));
}  
    

Следует отметить, что если количество байт кода кратно восьми, то на последнем шаге обрабатывается лишь один младший бит последнего байта кода, а сдвиг равен шести. "Вынимание" последнего закодированного символа (из первых семи бит последнего байта кода) по алгоритму выполняется отдельно, поэтому надо после цикла сформировать последний символ:

if(shift == 6)
{
prevNum = prevNum & 127;
/* special code for symbol @ */
if (res[(j-1)] == 0)
{res[(j-1)] = (unsigned char)'@';}
res[j++] = prevNum;
}
res[j] = '\0';  
    

В GSM кодировка символов определяется стандартом GSM 03.38 и несколько отличается от стандартной таблицы ASCII. В частности, символ '@' имеет нулевой код и совпадает с кодом конца строки в языке Си. Из-за этого в приведенном примере символ с кодом '\0' заменяется символом '@':

if (res[ (j-1)] == 0) {res[ (j-1)] = (unsigned char)'@';}  
    

А при работе с кодировкой GSM 03.38 на языке Си необходимо знать длину строки, иначе в случае восьми символов текста, в которых последний символ равен '@', последний значащий байт строки равен нулю, что соответствует символу конца строки, и он не будет корректно обрабатываться стандартными функциями как значащий.

В случае когда необходимо передать символы, отсутствующие в GSM 03.38, используется Unicode, в котором один символ кодируется двумя байтами. В Unicode можно хранить сразу несколько алфавитов, но в этом случае 140 байт вмещают лишь 70 символов текста. Поэтому если в SMS-сообщении содержатся русские буквы (которых нет в таблице GSM 03.38), то оно кодируется в формате Unicode и содержит максимум 70 символов. Если же использовать только английский алфавит, то, как уже было показано, в сообщении можно использовать целых 160 символов!

Вопросы и задачи для самостоятельного решения

  • Придумайте и опишите протокол общения двух программ при игре "Морской бой". Предусмотрите упаковку координат (X, Y) на поле боя в один байт 16-битовой команды протокола.
  • Реализуйте программу кодирования (шифрования) и декодирования сообщения по алгоритму Цезаря (каждая буква сообщения заменяется на другую букву алфавита, циклически сдвинутую на позицию буквы ключа).
< Лекция 9 || Лекция 10: 1234 || Лекция 11 >