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

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

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

9.4. Кодирование для сжатия информации

Алгоритмы кодирования нередко используются для сжатия не только при передаче, но и при архивации данных. Одним из таких примеров является кодирование текстовой информации в сервисе коротких сообщений в мобильной связи (SMS-сервис). Текст в сообщении передается в сжатом виде.

В большинстве случаев для каждого символа текста отводится один байт, что верно и для представления текстовой (строковой) информации в языке Си. Однако из ASCII-таблицы чаще всего используются лишь первые 127 символов, включающие в себя английский алфавит, цифры, основные знаки препинания и служебные дополнительные символы (например, * или \ ). Очевидно, что для хранения этих символов достаточно лишь 7 бит (127 значений для кода символа), т.е. несколько меньше байта. Такой подход как раз и используется при кодировании SMS-сообщений, что позволяет передать больше символов в одном сообщении.

Первые поколения GSM-сетей не позволяли передавать данные на большой скорости и не позволяли совмещать передачу данных и голосовой звонок. Поэтому чем короче сообщение, тем меньше занимает эфирного времени его передача и тем меньше времени абонент занят для голосового звонка. Стандарт GSM выделяет максимум 160 байт для одного текстового сообщения, 20 из которых - служебная информация. На сам текст остается 140 байт, в которых при 7-битовом кодировании можно разметить 160 текстовых символов.

Рассмотрим алгоритм реализации такого кодирования. Однако прежде чем перейти к коду алгоритма, следует учесть еще один факт. Большинство устройств, отвечающих за передачу данных в GSM-сетях, являются модемами, которые взаимодействуют с другими устройствами посредством стандартного протокола, т.е. при помощи так называемых AT-команд. Эти команды можно посылать модему через терминал (последовательный поток ввода и поток вывода). При этом передаваемая бинарная информация представляется побайтово в шестнадцатеричном виде, чтобы не возникало конфликтов с управляющими AT-командами. Например, если в тексте сообщения содержится символ "1" (имеющий ASCII-код 49), то для 1-байтового кодирования он имел бы двоичное представление: 00110001. Для 7битного кодирования он имеет вид: 0110001.

А при передаче его через терминал он будет представлен двумя символами как "31", т.е. в виде шестнадцатеричного числа 31, представленного как текст (именно двумя символами, а не одним символом с кодом 31). Поэтому при кодировании текста для передачи SMS необходимо его не просто закодировать в 7-битный код, но затем еще и представить его в виде шестнадцатеричного текстового кода, "понятного" GSM-модему.

Из 1-байтового представления текста 7-битный код получается следующим образом. Для первого символа берутся семь младших бит и записываются в семь младших бит первого байта результата. Далее от второго символа исходного текста берутся шесть младших бит и записываются в шесть младших бит второго байта результата. От второго символа остается один (7-й) значащий бит, который записывается в старший 8-й бит первого байта результата (который как раз не использован для хранения первого символа). Для третьего входного символа в третий байт результата записывается только пять младших бит. А два остающихся значащих бита (7-й и 6-й) сохраняются соответственно в 8-м и 7-м битах второго байта результата (они как раз не использованы для хранения второго символа). При обработке восьмого символа оказывается, что его нулевой бит надо записать в восьмой байт результата, а все его значащие биты поместятся в старшие семь разрядов 7-го байта результата. Таким образом, для восьми символов потребуется всего семь байт. Далее процесс повторяется. Описанная схема представлена на рис. 9.1.

Семибитное кодирование строки "Hello C!" в SMS

Рис. 9.1. Семибитное кодирование строки "Hello C!" в SMS
Работа функции encodeToPDU для строки "Hello C!"

Рис. 9.2. Работа функции encodeToPDU для строки "Hello C!"

Реализация метода кодирования на языке Си может иметь следующий вид:

void encodeToPDU(char *text, char *res)
{
  int len;
  int i, j;
  int shift;
  unsigned char prevNum, curNum, tmpVal1, tmpVal2, curValue;
  j = 0;
  i = 0;
  prevNum = 0;
  len = length(text);
  tmpVal1 = 0;
  tmpVal2 = 0;
  while(i < len)
  {
    curNum = (unsigned char)text[i];
    /* special code for symbol @ */
    if (text[i] == '@') {curNum = 0;}
    shift = (i %8);
    curValue = curNum & 127;
    curValue = (curValue >> shift);
    tmpVal2 = curValue;
    if (i > 0)
    {
      prevNum = (curNum << (8 - shift));
      tmpVal1 = tmpVal1 | prevNum;
    }
    if (shift != 7)
    {
      if (i > 0)
      {
        res[j] = decToHex(tmpVal1 / 16);
        res[j + 1] = decToHex(tmpVal1 % 16);
        j+=2;
      }
      tmpVal1 = tmpVal2;
    }
    i++;
  }
  res[j] = decToHex(tmpVal1 / 16);;
  res[j + 1] = decToHex(tmpVal1 % 16);
  res[j + 2] = '\0';
}
    

Здесь используется функция нахождения длины строки length:

int length(char *str)
{
  int i;
  i = 0;
  while(*str++)
  {
    i++;
  }
  return i;
}  
    

Выделение нужного количества бит в байте реализуется в языке Си посредством сдвига. При помощи этой же операции можно выставить значащие биты на нужные позиции. Для "удаления" незначащих бит можно использовать логическую операцию И с числом-маской, в котором на позиции незначащих бит выставляются нули, а на позиции значащих - единицы. После операции И с таким числом-маской все незначащие биты становятся равными нулю.

Приведенная функция encodeToPDU принимает на вход текст из параметра text и двигается по нему посимвольно, для задания текущей позиции используется переменная i. Выходная строка res также формируется последовательно в один проход (рис. 9.2).

Для всех входных символов перед их обработкой выполняется операция И с маской 127 (01111111) для выставления в 0 незначащего старшего бита:

curNum = (unsigned char) text[i] ; 
curValue = curNum & 127;
    

После этого для каждого символа входной строки вычисляется сдвиг, на который необходимо сдвинуть значащие биты вправо, чтобы далее записать оставшееся количество значащих бит в текущий выходной байт. Текущий выходной байт сохраняется в переменную

tmpVal2:
curValue = (curValue >> shift);
tmpVal2 = curValue;
    

Количество бит, записываемых в текущий выходной байт, как раз равно сдвигу. Сдвиг считается как

shift = (i % 8);  
    

Для первого символа он равен нулю, далее возрастает на каждом шаге на единицу. Для восьмого символа он оказывается равен семи, т.е. все значащие биты сдвигаются и ни один из них не записывается в текущий выходной байт (в 8-й выходной байт). Все сдвинутые биты записываются в предыдущий выходной байт, который сохраняется в переменной tmpVal1. Для этого текущий входной байт сдвигается на величину (8 - shift) влево, т.е. нужное количество значащих бит оказывается в старших разрядах:

if (i > 0)
{
  prevNum = (curNum << (8 - shift));
  tmpVal1 = tmpVal1 | prevNum;
}    

И только для первого входного символа и соответствующего ему первого выходного байта не надо сохранять ничего в предыдущий выходной байт.

< Лекция 9 || Лекция 10: 1234 || Лекция 11 >
Ильдус Кучкаров
Ильдус Кучкаров
Россия, Санкт-Петербург, СПбГУ
Антон Конычев
Антон Конычев
Россия, Москва, МГУПИ, 2013