Strona główna » Algorytmy » Szyfry » Kod Baudot
 

Kod Baudot

Wstęp

Tuż po telegrafach do przekazywania informacji używane były Dalekopisy. Były to urządzenia, które służyły do przekazywania informacji w postaci alfanumerycznej. Po ponad 100 latach (od 1910 w USA do 2013 w Indiach) obecności na rynku Dalekopisy przeszły do historii. Jednak pozostało po nich szereg informacji związanych z ich działalnością. Jedną z takich pozostałości jest kod Baudot, którym posługiwano się podczas wymiany informacji poprzez Dalekopis. Jest to kod, którego pierwsza wersja ukazała się w 1874 roku i była pierwowzorem dzisiejszych systemów kodowania takich jak ASCII.

Kod Baudot

Kod Baudot pozwalał przesyłać zarówno litery jak i cyfry przy użyciu zaledwie 5 bitów. Twórca podczas tworzenia kodu dodał przełącznik FIG/LTR, który w zależności od stanu interpretował przysłany kod binarny jako figurę lub literę. W celu przesłania liter należało przełączyć się do trybu liter znakiem LTRS (111-11). Potem w trakcie przesyłania informacji do przesłania cyfr, znaków interpunkcyjnych itd. należało wpierw przesłać przycisk FIGS (110-11).

W celu sformatowania przesyłanego dokumentu można było się posłużyć znakiem LF (000-10) do utworzenia nowej linii. Należało jednak dodatkowo przesłać znak CR (010-00), który służył do powrotu do początku linijki. Dawniej urządzenia nie wiedziały, że muszą przejść do następnej linijki, dlatego używano tego znaku, aby odpowiednio ustawić maszynę piszącą. Wszystkie pozostałe znaki były przesyłane zgodnie z poniższą tabelką:

[LTR][Fig]ZapisKomentarz
NULL000-00Null
E3000-01
LF000-10Nowa linia
A-000-11
SP001-00Spacja
S'001-01
I8001-10
U7001-11
CR010-00Powrót karetki
DENC010-01(po prawej) Zapytanie, kto jesteś ?
R4010-10
JBELL010-11(po prawej) Dźwięk na drugim końcu
N,011-00
F!011-01
C:011-10
K(011-11
T5100-00
Z+100-01
L)100-10
W2100-11
H$101-00(po prawej) Symbol waluty
Y6101-01
P0101-10
Q1101-11
O9110-00
B?110-01
G&110-10
FIGS110-11Przełączenie do trybu Figur
M.111-00
X/111-01
V;111-10
LTRS111-11Przełączenie do trybu Liter

Implementacja

Analiza problemu

Przechowywanie informacji w kodzie Baudot nie jest zbyt wygodna przy wykorzystaniu dzisiejszej technologii. Podstawą przechowywania w dzisiejszych czasach jest bajt, który ma 8 bitów, a nie 5. Oczywiście dane 5 bitowe można zapisać jako 8 bitowe. Wiąże się to jednak ze wzrostem o 37,5% rozmiaru przechowywanej wiadomości. Dzisiejsze komputery posiadają bardzo dużą pojemność, więc takie rozwiązanie dla użytkownika byłoby całkowicie nieodczuwalne, ale dla archiwa wiadomości przesłanych kodem Baudot rozmiar powinien być jak najmniejszy. Jest to możliwe poprzez napisanie własnego interpretatora kolejnych bitów wczytywanych danych. Wtedy maksymalnie zostanie utracone do 7 bitów.

Dane w kodzie Baudot będą przechowywane w postaci tablicy char. W trakcie konwersji należy zauważyć, że prócz samych znaków zakodowane muszą być dodatkowo znaki FIGS oraz LTRS, które będą przełączały pomiędzy odpowiednimi trybami. Zaletą kodowania Baudot jest kompatybilność znaku końca danych Null. W C++ jest zerowy bajt 00000000, a dla kodu Baudot 000-00.

Ponadto pojawia się problem przechowywania tabelki w trakcie wykonywania programu. W celu uniknięcie warunków wszystkie znaki zostaną zapisane w jednowymiarowej tablicy o 64 elementach. Rozpoznawane oddzielnie będą jedynie znaki specjalne takie jak FIGS. lub LTRS. Dostaną one powiązane z odpowiednikami w kodzie ASCII SI i SO. Pominięty zostanie znak ENC (010-01), ponieważ wiadomość nie będzie nigdzie wysyłana.

Konwersja na kod Baudot

Zakładamy, że użytkownik wprowadzi do programu jakąś informacją zapisaną przy pomocy kodu ASCII. Funkcja ASCIInaBaudot() ma za zadanie zwrócić tablicę znaków char, która będzie zawierała wprowadzony tekst przy pomocy kodu Baudot.

  1. char* ASCIInaBaudot(char* txt) {
  2.   char* table = "\0E\nA SIU\rDRJNFCKTZLWHYPQOBG\x0fMXV\x0e\0003\n- '87\x0d 4\x07,!:(5+)2$6019?&\x0f./;\x0e";
  3.   int dl = strlen(txt) + 1;
  4.   bool Fig = false;
  5.   for (int i = 0; txt[i]; i++) {
  6.     if (Fig != (findChar(table, txt[i]) / 32 == 1)) {
  7.       dl++;
  8.       Fig = !Fig;
  9.     }
  10.   }

(1.) Funkcja ASCIInaBaudot() przyjmuje jeden argument txt - tekst do zaszyfrowania. Zwrócona wartość to tablica typu char na której dane będą zapisane zgodnie z kodem Baudot. (2.) Zadeklarowanie tablicy według której odbędzie się kodowanie. (3.) Ustalenie ile znaków jest do zakodowania. Wartość nie uwzględnia na razie znaków LTRS i FIGS. Miejsca dla tych znaków (4. - 10.) są dopisywane poprzez analizę tekstu do przekodowania.

  1.   int dl_wynik = (dl * 5 + 7) / 8;
  2.   char* wynik = new char[dl_wynik + 1];
  3.   wynik[dl_wynik - 1] = wynik[dl_wynik] = '\0';
  4.   int bit = 7, bajt = 0;
  5.   Fig = false;
  6.   for (int i = 0; txt[i]; i++) {
  7.     int pos = findChar(table, txt[i]);
  8.     if (Fig != (pos / 32 == 1)) {
  9.       writeData(wynik, findChar(table, (Fig) ? '\x0e' : '\x0f'), bit, bajt);
  10.       Fig = !Fig;
  11.     }
  12.     writeData(wynik, pos, bit, bajt);
  13.   }
  14.   writeData(wynik, findChar(table, '\0'), bit, bajt);
  15.   return wynik;
  16. }

(11.) Wyliczenie tekstu wynikowego wiedząc, że każdy znak zajmie tylko 5 bitów, a długość tekstu wynikowego może być wielokrotnością 8. (12.) Alokacja tablicy wynikowej i (13.) ustawienie dwóch ostatnich znaków listy na \0. (14. - 23.) Przepisywanie kolejnych znaków i ich kodowanie przy pomocy funkcji writeData(). W przypadku dopisywanie kolejnych znaków to (17.) w przypadku wykrycia zmiany z liter na cyfry i inne lub odwrotnie dopisywany jest odpowiedni znak, aby kodowanie było prawidłowe. (24.) Dopisanie znaku końca danych w kodzie Baudot i (25.) zwrócenie wyniku.

Zapisz dane

Zapisywanie danych na liście wynikowej odbywa się poprzez funkcję writeData(), która przyjmuje cztery argumenty: wynik - tekst wynikowe, pos - pozycja znaku w tablicy, bit i bajt - aktualnie zapisywany bit na pozycji bajt.

  1. void writeData(char* wynik, int pos, int& bit, int& bajt) {
  2.   for (int j = 4; j >= 0; j--) {
  3.     if (bit == -1) {
  4.       bit = 7;
  5.       bajt++;
  6.     }
  7.     wynik[bajt] ^= (-((pos >> j) & 1) ^ wynik[bajt]) & (1 << bit);
  8.     bit--;
  9.   }
  10. }

Znajdź na tablicy

Przeszukiwanie tablicy jest lekko zmienioną wersją implementacji wyszukiwania na dowolnej liście.

  1. int findChar(char* table, char c) {
  2.   for (int i = 0; i <= 63; i++) {
  3.     if (table[i] == c) {
  4.       return i;
  5.     }
  6.   }
  7.   return 0;
  8. }

Dekodowanie danych

Podczas dekodowania należy pamiętać o przełączniku FIG/LTR, który będzie modyfikował odpowiednie dane.

  1. char* BaudotnaASCII(char* txt) {
  2.   char* table = "\0E\nA SIU\rDRJNFCKTZLWHYPQOBG\x0fMXV\x0e\0003\n- '87\x0d 4\x07,!:(5+)2$6019?&\x0f./;\x0e";
  3.   int dl = 1;
  4.   int bit = 7, bajt = 0;
  5.   for (int i = 0; txt[i - 1]; i++) {
  6.     int num = 0;
  7.     for (int j = 4; j >= 0; j--) {
  8.       num ^= (-((txt[bajt] >> bit) & 1) ^ num) & (1 << j);
  9.       bit--;
  10.       if (bit == -1) {
  11.         bit = 7;
  12.         bajt++;
  13.       }
  14.     }
  15.     if (num != findChar(table, '\x0e') && num != findChar(table, '\x0e'))
  16.       dl++;
  17.   }

(2.) Deklaracja tabeli Baduot. (3.) Ustalenie początkowej długości tekstu na 0. (3. - 17.) Analiza każdego z zakodowanych znaków. W przypadku, gdy (15.) trafi się znak FIG/LTR to jest on ignorowany, ponieważ jest on tylko modyfikatorem i nie będzie on widoczny w tekście wynikowym. Po wyliczeniu długości tekstu wynikowego można przejść do dekodowania danych.

  1.   char* wynik = new char[dl + 1];
  2.   int write = 0;
  3.   bool Fig = false;
  4.   bit = 7, bajt = 0;
  5.   for (int i = 0; txt[i - 2]; i++) {
  6.     int num = 0;
  7.     for (int j = 4; j >= 0; j--) {
  8.       num ^= (-((txt[bajt] >> bit) & 1) ^ num) & (1 << j);
  9.       bit--;
  10.       if (bit == -1) {
  11.         bit = 7;
  12.         bajt++;
  13.       }
  14.     }
  15.     if (num == findChar(table, '\x0f')) {
  16.       Fig = true;
  17.     } else if (num == findChar(table, '\x0e')) {
  18.       Fig = false;
  19.     } else {
  20.       wynik[write++] = table[num + 32 * Fig];
  21.     }
  22.   }
  23.   wynik[write] = '\0';
  24.   return wynik;
  25. }

(18.) Alokacja pamięci pod tekst wynikowy i (19.) dopisanie znaku końca danych. (20.) Ustalenie pierwszej pozycji zapisu. (21. - 22.) Przygotowanie do dekodowania. (23.) Dla każdego bajtu danych zakodowanych: (24. - 32.) Odczyt i-tej zakodowanej liczby. (33. - 36.) Jeśli odczytany kod dotyczy przełączania FIG/LTR to ustaw Fig na odpowiednią wartość. (37.) Jednak dla innych znaków: (38.) pobierz odpowiedni znak na podstawie wartości przełącznika. (41.) Zwróć wynik.

Testowanie funkcji

Wypisywanie kodu Baudot

Ze względu na fakt, że dane są inaczej zakodowane niż program spodziewa się wypisywać to należy napisać własną funkcję wypisującą tablicą zawierającą zakodowane dane. Funkcja wypiszKodBaudot() wypisuje kolejne bity zakodowanej tablicy z przerwami co pięć wypisanych bitów.

  1. void wypiszKodBaudot(char* data) {
  2.   int bit = 0;
  3.   for (int i = 0; data[i - 1]; i++) {
  4.     for (int j = 7; j >= 0; j--) {
  5.       cout << ((data[i] >> j) & 1);
  6.       bit++;
  7.       if (bit % 5 == 0) {
  8.         cout << " ";
  9.         bit = 0;
  10.       }
  11.     }
  12.   }
  13.   cout << endl;
  14. }

Testowanie

Wszystkie funkcję można przetestować przy pomocy poniższej funkcji main(). Po uruchomieniu programu program zapyta o tekst do zakodowania.

  1. int main() {
  2.   const int BUFF = 1024;
  3.   char* txt = new char[BUFF];
  4.   cout << "Podaj tekst do zakodowania: ";
  5.   cin.getline(txt, BUFF);
  6.   char* txtk = ASCIInaBaudot(txt);
  7.   wypiszKodBaudot(txtk);
  8.   cout << BaudotnaASCII(txtk) << endl;
  9.   delete[] txt, txtk;
  10.   system("pause");
  11.   return 0;
  12. }