Strona główna » Algorytmy » Szyfry » Alfabet Morse'a
 

Alfabet Morse'a

O alfabecie

Kod Morse'a został opracowany przez Samuela Finleya Breeze Morse'a w 1840. Początkowo alfabet służył do komunikacji przy użyciu telegrafu elektromagnetycznego. Jednak ze względu na wykorzystanie tylko dwóch znaków oraz przerw czasowych alfabet ten nadaje się również do komunikacji poprzez sygnały świetlne, dźwiękowe czy przy pomocy rąk. Obecnie alfabet ten został wyparty przez nowoczesne technologie, ale wciąż jest używany np. w harcerstwie.

Zamiana z tradycyjnego alfabetu na alfabet Morse'a polega na zamianie każdego znaku zgodnie z poniższą tabelką. Przyjmuje się, że pomiędzy dwoma kolejnymi znakami należy umieścić przerwę, a pomiędzy dwoma wyrazami długą przerwą, która na komputerze można reprezentować przez podwójny znak przerwy.

ZnakKod
A•-
B-•••
C-•-•
D-••
E
F••-•
G--•
H••••
I••
J•---
ZnakKod
K-•-
L•-••
M--
N-•
O---
P•--•
Q--•-
R•-•
S•••
T-
ZnakKod
U••-
V•••-
W•--
X-••-
Y-•--
Z--••
Ą•-•-
Ć•-•••
Ę••-••
Ł•-••-
ZnakKod
Ń--•--
Ó---•
Ś•••-•••
Ź--••-•
Ż--••-
1•----
2••---
3•••--
4••••-
5•••••
ZnakKod
6-••••
7--•••
8---••
9----•
0-----
•-•-•-
,--••--
'•----•
"•-••-•
+••--•-
ZnakKod
:---•••
;-•-•-•
?••--••
!-•-•--
--••••-
+•-•-•
/-••-•
(-•--•
)-•--•-
=-•••-

Przykład

Przykład chcąc zapisać wyraz Alfabet Morse'a należy znaleźć kod każdego znaku, a następnie znaki rozdzielić znakiem przerwy, a wyrazy dłuższą przerwą• W tym przypadku tekstem wynikowym będzie: "•- •-•• ••-• •- -••• • -  -- --- •-• ••• • •----• •-"• Podczas dekodowania należy pamiętać, aby pobierać grupę kropek i kresek i dopiero takiej wartości szukać w tabelce w celu zamiany z powrotem.

Implementacja

Tabelka kodowania

Pierwszą podstawową rzeczą podczas pisania programu do kodowania alfabetem Morse'a jest ustalenie jak będzie przechowywana tabelka służąca do zamiany liter na odpowiadający jej kod oraz odwrotnie. Należy także zauważyć, że w zależności od języka alfabet ten może zawierać więcej par znak / kod. Z tego powodu warto napisać funkcję wczytajAlfabet(), która wczyta wybrany plik z dysku w celu utworzenia tabelki. Znaki i kody będą przechowywane w zmiennej typu string jako para na liście typu vector. Takie rozwiązanie pozwoli załadować dowolny alfabet. W pliku każda para będzie zapisana w oddzielnej linijce, a wartości w parze będą rozdzielone tabulatorem. Przykładowo plik będzie wyglądać następująco:

  1. A .-
  2. B -...
  3. C -.-.
  4. D -..

Funkcja, która będzie czytała plik alfabetc.txt w celu odczytania tabelki będzie wyglądać następująco:

  1. vector <pair<string, string>> wczytajAlfabet() {
  2.   vector <pair<string, string>> w;
  3.   pair<string, string> para;
  4.   ifstream myfile("alfabetc.txt");
  5.   if (myfile.is_open()) {
  6.     while (!myfile.eof()) {
  7.       myfile >> para.first >> para.second;
  8.       w.push_back(para);
  9.     }
  10.     myfile.close();
  11.   } else {
  12.     cout << "Brak pliku alfabetu";
  13.   }
  14.   return w;
  15. }

(1.) Funkcja wczytajAlfabet() jest bezparametrowa i zwraca tabelkę zapisaną zgodnie z wcześniejszymi założeniami. Przygotuj (2.) listę wynikową oraz (3.) parę tymczasową. (4.) Spróbuj otworzyć plik i (5.) czytaj dane do końca pliku: (6.) pobierz znak i kod, a następnie (7.) dopisz na listę. Na koniec (9.) zamknij strumień pliku. (10.) W przypadku niepowodzenia otworzenia pliku (11.) zgłoś błąd. (13.) Zwróć gotową listę.

Kodowanie

Podczas kodowania przyda się funkcja znajdzOdpowiednikZnaku(), która na podstawie argumentów zwróci wartość kodu dla podanego znaku.

  1. string znajdzOdpowiednikZnaku(vector <pair<string, string>> alfabet, string szukana) {
  2.   for (int i = 0; i < alfabet.size(); i++) {
  3.     if (alfabet[i].first == szukana) {
  4.       return alfabet[i].second;
  5.     }
  6.   }
  7.   return "";
  8. }

(1.) Funkcja przyjmuje następujące argumenty: alfabet - lista znak / kod, szukana - poszukiwany znak. (2.) W całej tabeli (3.) szukaj znaku i jeśli znajdziesz to (4.) zwróć powiązaną z nim wartość. (7.) Jeśli znak nie występuje na liście to nic nie zwracaj.

Brakuje jeszcze funkcji koduj(), która opierając się na znajdzOdpowiednikZnaku() zbierze wszystkie wartości do jednej zmiennej:

  1. string koduj(vector <pair<string, string>> alfabet, string data) {
  2.   string w = "";
  3.   for (int i = 0; data[i]; i++)
  4.     w += znajdzOdpowiednikZnaku(alfabet, string(1, data[i])) + " ";
  5.   return w;
  6. }

(1.) Na podstawie alfabetu oraz tekstu do zakodowania data zwróć tekst zapisy przy pomocy alfabetu Morse'a. (2.) Przygotuj zmienną wynikową i (3. - 4.) zakoduj wszystkie znaki. (5.) Zwróć zmienną w.

Dekodowanie

Podczas dekodowania użyta zostanie funkcja znajdzOdpowiednikKodu(), który analogicznie konwertuje kod na znak tak jak funkcja znajdzOdpowiednikZnaku() znak na kod.

  1. string znajdzOdpowiednikKodu(vector <pair<string, string>> alfabet, string szukana) {
  2.   for (int i = 0; i < alfabet.size(); i++) {
  3.     if (alfabet[i].second == szukana) {
  4.       return alfabet[i].first;
  5.     }
  6.   }
  7.   return "";
  8. }

W celu pełnego rozkodowania tekstu służy funkcja rozkoduj(), która potrafi wybierać kolejne grupy kropek i kresek, aby zamienić je na znaki. Tak samo jak podczas kodowania potrzebny jest tylko alfabet oraz data - tekst do rozkodowania.

  1. string rozkoduj(vector <pair<string, string>> alfabet, string data) {
  2.   string w = "", kod = "";
  3.   for (int i = 0; data[i]; i++) {
  4.     kod = "";
  5.     while (data[i] != ' ') {
  6.       kod += data[i];
  7.       i++;
  8.     }
  9.     if(kod != "")
  10.       w += znajdzOdpowiednikKodu(alfabet, kod);
  11.     else if (data[i] == data[i - 1] && data[i] == ' ')
  12.       w += " ";
  13.   }
  14.   return w;
  15. }

(2.) Zmienna w przechowa tekst wynikowy, a kod - będzie odczytywać kolejne grupy kropek i kresek. (3.) Dopóki są dane do odczytania: (4.) usuń zawartość zmiennej kod i (5. - 8.) wybieraj kolejne znaki dopóki nie napotkasz znaku przerwy. (9.) Jeśli wczytane wyrażenie nie jest puste to (10.) rozkoduj i dopisz do wyniku. (11. - 12.) Jeśli wystąpiły dwa znaki przerwy to dopisz znak przerwy. (14.) Zwróć tekst wynikowy w.

Nadawanie sygnału

Jednym ze sposobów komunikacji przy pomocy alfabetu Morse'a jest nadawanie sygnałów dźwiękowych. W C++ sygnały można wydawać przy pomocy funkcji Beep(). Każdy znak i przerwy obowiązują różne zasady nadawania. Przyjęte międzynarodowo standardy to:

ZnakSposób transmisji
. (kropka)sygnał przez jedną jednostkę czasu
- (kreska)sygnał przez trzy jednostki czasu
(przerwa między symbolami)cisza przez jednostkę czasu
(przerwa między literami)cisza przez trzy jednostki czasu
(przerwa między wyrazami)cisza przez siedem jednostek czasu

Funkcja odgrywająca kod Morse'a będzie przyjmowała od jednej do trzech argumentów: txt tekst zapisany alfabetem Morse'a i opcjonalnie time - długość trwania jednostki czasu w ms oraz Hz - częstotliwość wydawania dźwięku podana w Hz.

  1. void grajKod(string txt, int time = 200, double Hz = 1046.5) {
  2.   for (int i = 0; txt[i]; i++) {
  3.     switch (txt[i]) {
  4.       case '.':
  5.         Beep(Hz, time);
  6.         break;
  7.       case '-':
  8.         Beep(Hz, 3 * time);
  9.         break;
  10.       case ' ':
  11.         Sleep(2 * time);
  12.         break;
  13.     }
  14.     Sleep(time);
  15.   }
  16. }

Testowanie funkcji

Poniższa funkcja main() wczyta tekst do zakodowania, pokaże zakodowaną wersję i ją odegra, następnie wypisz rozkodowany tekst na podstawie utworzonego wcześniej tekstu zapisanego alfabetem Morse'a.

  1. int main () {
  2.   vector <pair<string, string>> alfabet = wczytajAlfabet();
  3.   string txt;
  4.   getline(cin, txt);
  5.   string txtk = koduj(alfabet, txt);
  6.   cout << txtk << endl;
  7.   grajKod(txtk);
  8.   string txtr = rozkoduj(alfabet, txtk);
  9.   cout << txtr << endl;
  10.   system("pause");
  11.   return 0;
  12. }

Zadania

Zadanie 1

Napisz wersję programu, która będzie umożliwiała podanie różnych aliasów dla jednego kodu tj. zarówno a i A będzie kodowana jako .-. Jednak podczas dekodowania .- zostanie zapisane jako pierwszy Alias. W związku z podanymi zmianami zmienia się postać pliku. W tej chwili w każdej linijce pierwsze wyrażenie to jeden lub więcej znaków. Przykładowo:

  1. Aa .-
  2. Bb -...
  3. Cc -.-.
  4. Dd -..

Podczas dekodowania danych odpowiednikiem dla danego kodu będzie jedynie pierwszy znak z pierwszego wyrazu w parze. Przykładowo tekst Koduj tekst da następujący rezultat:

  1. -.- --- -.. ..- .---  - . -.- ... -
  2. KODUJ TEKST