Алгоритм декодирования СМС (SMS 7-bit to 8-bit decoder ) — Bluetooth и все, что с ним связано

Алгоритм декодирования СМС (SMS 7-bit to 8-bit decoder )


Недавно занимался задачей декодирования входящих СМС, т.к. готовых алгоритмов не нашел, пришлось курить спецификацию GSM 03.38 и заголовочник «RIL.h».
Начну с того, что СМС может кодироваться 3мя методами:
— Метод кодирования «По умолчанию» (Default alphabet) — использует 7 бит на символ, длина СМС считается в символах.
— Метод кодирования «UCS2» — использует 16 бит на символ, длина СМС считается в октетах.
— Метод кодирования «8-ми битные данные, определенные пользователем» (8 bit data is user defined) — использует 8 бит на символ, длина СМС считается в октетах.

Для того, что бы декодировать данные, надо сначала знать как они кодируются :).

Метод «По умолчанию»

Для кодирования (упаковки) методом «По умолчанию», используется следующий алгоритм:
Если символ «q» представить в виде последовательности 7-ми бит:

 cpp | 
 
 copy code |
?

1
b7 b6 b5 b4 b3 b2 b1
2
qa qb qc qd qe qf qg

то упаковка 1го 7ми битного символа в октет заключается в дополнении до октета путем вставки бита «0» слева.
Пример упаковки:
— 1го символа в 1 октет:

 cpp | 
 
 copy code |
?

1
7 6  5  4  3  2  1  0
2
0 1a 1b 1c 1d 1e 1f 1g

— 2х символов в 2 октета:

 cpp | 
 
 copy code |
?

1
7  6  5  4  3  2  1  0
2
2g 1a 1b 1c 1d 1e 1f 1g
3
0  0  2a 2b 2c 2d 2e 2f
4

— 3х символов в 3 октета:

 cpp | 
 
 copy code |
?

1
7  6  5  4  3  2  1  0
2
2g 1a 1b 1c 1d 1e 1f 1g
3
3f 3g 2a 2b 2c 2d 2e 2f
4
0  0  0  3a 3b 3c 3d 3e

и т.д.
Визуально можно отобразить так:
Допустим сообщение содержит 10 символов и выглядит следующим образом: «1234567890»

1 2 3 4 5 6 7 8 9 0
31h 32h 33h 34h 35h 36h 37h 38h 39h 30h
0110001 0110010 0110011 0110100 0110101 0110110 0110111 0111000 0111001 0110000
0110001 0110010 0110011 0110100 0110101 0110110 0110111 0111000 0111001 0110000

Первый септет «1» превращается в октет путем добавления правого бита второго септета. Этот бит вставляется слева: 0 + 0110001 = 00110001 (31h). Правый бит второго символа выбрасывается, после чего второму символу (септету), что бы стать октетом, нужно добавить 2 бита (красные), взятые из 3го символа. Этот процесс повторяется до конца строки символов.

00110001 11011001 10001100 01010110 10110011 11011101 01110000 00111001 00011000
31 D9 8C 56 B3 DD 70 39 18

Получилось 9 октетов: 31 D9 8C 56 B3 DD 70 39 18

Как же теперь распаковать эти октеты для того, что бы получить в каждом байте по символу?
Надо всего лишь сделать обратное преобразование 🙂
Я набросал алгоритм (критика приветствуется :)), который преобразует входящую последовательность в набор 8ми байтовых символов или в стандарт ASCII:

 cpp | 
 
 copy code |
?

01
void ConvertTo(BYTE* Msg, DWORD cchMsgLength, TCHAR* _Dest, DWORD DestSize, DWORD DataCoding)
02
 {
03
 //переменная, в которой мы храним число, переносимое в другой октет при сжатии
04
 //например во второй таблице в первой колонке выделенный "0"
05
 BYTE t = 0;
06
 //переменная, указывающая на сколько надо сдвинуть текущий октет, что бы получить "чистое значение "t"
07
 //инициализируем в 7, т.к. в первом байте нам надо взять 1 бит, а значит байт надо сдвинуть на 7 бит вправо
08
 int R = 7;
09
 //переменная, хранящая предыдущее значение "t"
10
 BYTE p = 0;
11
 //переменная служит для очистки "не своих" бит в октете, а так же для получения "чистого значения" 
12
 //инициализируется  в 0, т.к. первый октет у нас имеет актуальное значение если выбросить старший бит.  
13
 int L = 0;
14
 //индекс, по которому мы вставляем распакованные байты в приемник.
15
 int index = 0;
16
17
 //создаем массив байт для приема раскодированных данных
18
 BYTE* Drft = new BYTE[DestSize];
19
 //в цикле перебираем все октеты входящей последовательности
20
 for (DWORD i = 0; i < cchMsgLength; i++)
21
 {
22
  //как Вы заметили из выше опубликованной таблицы, при упаковке теряется 1 октет, а сдвиги повторяются каждые 7 раз
23
  //по этому как только R достигнет нуля, мы по новой инициализируем переменные R и L
24
  //и в приемный массив кладем сформировавшийся за это время "лишний" септет (превращенный в байт) 
25
  if (R == 0)
26
  {
27
   R = 7;
28
   L = 0;
29
   Drft[index] = p;
30
   index++;
31
   p = 0;
32
  }
33
  //сдвигаем i-й байт сообщения вправо на R бит, что бы вычленить число, относящееся к следующему септету
34
  t = Msg[i]>>R;
35
  //2 строки ниже "чистят" i-й байт от выше вычленного числа и помещают в приемный буфер 
36
  Drft[index] = (Msg[i]<<(L+1));
37
  Drft[index] = (Drft[index]>>L);
38
  //приемный буфер совмещается с ранее вычлененным числом
39
  Drft[index] |= p;
40
  p = t;
41
  R--;
42
  L++;
43
  index++;
44
 }
45
 delete[] Drft; 
46
 }
47

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

Метод «UCS2»

Алгоритм прост — думаем что входящий массив — это массив слов,  меняем местами байты в слове и получаем Юникод:

 cpp | 
 
 copy code |
?

1
{
2
 WORD* Drft = (WORD*)Msg;
3
 WORD s = 0;
4
 for (DWORD i = 0; i < cchMsgLength; i++)
5
 {
6
 s = (Drft[i]>>8) | (Drft[i]<<8);
7
 _Dest[i] = s;
8
}

Удачи!

Have any Question or Comment?

4 comments on “Алгоритм декодирования СМС (SMS 7-bit to 8-bit decoder )

Alex

Приветствую!
Большое спасибо за статью! Очень помогла!
Но в строке 37 есть ошибка:
Drft[index] = (Drft[index]>>L);

Тут L надо заменить на 1, так как обратно сдвигаем всегда на 1:
Drft[index] = (Drft[index]>>1);

Здравствуйте, Alex.
Вы правы. Действительно в представленном коде есть ошибка.
В рабочем коде вместо строк 36 и 37 написано вот так:

 cpp | 
 
 copy code |
?

1

2
//помещаем в буфер октет, сдвинутый влево на L+1
3
Drft[index] = (Msg[i]<<(L+1));
4
//этот же октет возвращаем в первоначальное состояние, сдвигая вправо тоже на L+1 
5
Drft[index] = (Drft[index]>>(L+1));
6
//теперь поучаем чистое значение
7
Drft[index] = (Drft[index]<
8

2я и 3я строка работают так же, как и в Вашем исправлении.
Код конечно дебильный, но написал я с таким извратом потому,
что код, использующий сдвиговые операторы, почему-то криво работает в виндовз-мобиле
(для которой создавалось приложение). Я столкнулся с тем, что после сдвигов
данные были не те, что ожидались.

Alex

Не сочтите меня занудой, но нашел еще одну ошибку:
после 30 строки необходимо дописать p = 0, иначе после каждого цикла буква будет неверной.
Т.е. получится так:
if (R == 0)
{
R = 7;
L = 0;
Drft[index] = p;
index++;
p = 0;
}

А по поводу того, что код дебильный — я не согласен! 🙂 Потому как у меня лично после этих сдвигов голова начинает болеть 😉 И с нуля я бы долго вкуривал как правильно реализовать данный алгоритм, но Ваша статья меня просто спасла! Удивительно, но в интернете мало чего толкового нашел по данному вопросу.

Так что ещё раз СПАСИБО автору огромное за данный труд! 🙂

> после 30 строки необходимо дописать p = 0

Согласен с Вами.

Добавить комментарий

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.

Март 2021
Пн Вт Ср Чт Пт Сб Вс
1234567
891011121314
15161718192021
22232425262728
293031