8-разрядный код Морзе

    По просьбе одного из любителей применения Щ-кодов в электронных поделках возникла необходимость написать функцию (подпрограмму, процедуру), которая будет издавать трель из последовательности точек и тире. В коде Морзе длина символа может быть от 1 знака (буквы Е и Т) до 9 знаков (триграф SOS). Что передавать вышеназванной функции в качестве параметра? Если вам не в тягость условия лицензии приглашаю познакомиться с процессом запихивания кода Морзе в 1 байт.

    В коде Морзе наиболее часто применяются символы длиной 1-6 знаков.

    ;   .        Ee     Ее Ёё
    ;   -        Tt     Тт
    ;   ..       Ii     Ии
    ;   .-       Aa     Аа
    ;   -.       Nn     Нн
    ;   --       Mm     Мм
    ;   ...      Ss     Сс
    ;   ..-      Uu     Уу
    ;   .-.      Rr     Рр
    ;   .--      Ww     Вв
    ;   -..      Dd     Дд
    ;   -.-      Kk     Кк
    ;   --.      Gg     Гг
    ;   ---      Oo     Оо
    ;   ....     Hh     Хх
    ;   ...-     Vv     Жж
    ;   ..-.     Ff     Фф
    ;   ..--            Юю
    ;   .-..     Ll     Лл
    ;   .-.-            Яя    [AA] digraph     UNKNOWN STATION
    ;   .--.     Pp     Пп
    ;   .---     Jj     Йй
    ;   -...     Bb     Бб
    ;   -..-     Xx     Ьь Ъъ
    ;   -.-.     Cc     Цц
    ;   -.--     Yy     Ыы
    ;   --..     Zz     Зз
    ;   --.-     Qq     Щщ
    ;   ---.            Чч
    ;   ----            Шш
    ;   .----    1
    ;   ..---    2
    ;   ...--    3
    ;   ....-    4
    ;   .....    5
    ;   -....    6
    ;   --...    7
    ;   ---..    8
    ;   ----.    9
    ;   -----    0
    ;   ..-..           Ээ
    ;   ..-.-    [INT] trigraph - military network question marker
    ;   -..-.    Slash/Fraction Bar [/]
    ;   -.--.    Parenthesis (Open)
    ;   .-...    [AS] digraph - Ampersand (or "Wait") [&]      
    ;   -...-    [BT] digraph - Double Dash = or --
    ;   .-.-.    Plus sign [+] 
    ;   .-.-.    [AR] digraph   - New Page Signal
    ;   -.-.-    Starting Signal
    ;   ...-.    Understood
    ;   .--.-.          Ъъ
    ;   .-.-.-   Period [.]
    ;   --..--   Comma [,]
    ;   ..--..   [UD] digraph Question Mark [?]
    ;   .----.   Apostrophe [']
    ;   -.-.--   [KW] digraph  - Exclamation Point [!]   
    ;   -.--.-   Parenthesis (Close)
    ;   ---...   Colon [:] 
    ;   -.-.-.   Semicolon [;] 
    ;   -....-   Hyphen, Minus Sign [-] 
    ;   ..--.-   Underscore [_] 
    ;   .-..-.   Quotation mark ["]  
    ;   .--.-.   [AC] digraph - At Sign [@]               
    ;   ...-.-   End of work
    ;   ...-..-  [SX] digraph - Dollar sign [$]           
    ;   ........ [HH] digraph - Error/correction
    ;   ...---...   [SOS] trigraph
    

    Эти символы будем помещать в 8-разрядный аргумент. Байт должен содержать последовательность знаков (от 1 до 6) и их количество (также от 1 до 6). Последовательность знаков должна быть выровнена по младшему или старшему биту для удобства проталкивания во флаг переноса (Carry) командами сдвига. Получаем два варианта расположения счетчика ( c ) и последовательности ( s ) знаков:

    ; arg[s, x, x, x, x, c, c, c] – 1 знак
    ; arg[s, s, x, x, x, c, c, c] – 2 знака
    ; arg[s, s, s, x, x, c, c, c] – 3 знака
    ; arg[s, s, s, s, x, c, c, c] – 4 знака
    ; arg[s, s, s, s, s, c, c, c] – 5 знаков
    ; arg[s, s, s, s, s, s/c, c, c] – 6 знаков

    ; arg[c, c, c, x, x, x, x, s] – 1 знак
    ; arg[c, c, c, x, x, x, s, s] – 2 знака
    ; arg[c, c, c, x, x, s, s, s] – 3 знака
    ; arg[c, c, c, x, s, s, s, s] – 4 знака
    ; arg[c, c, c, s, s, s, s, s] – 5 знаков
    ; arg[c, c, c/s, s, s, s, s, s] – 6 знаков

    В первом варианте при максимальной длине последовательности 6-й знак накладывается на старший бит счетчика.

    Во втором варианте при максимальной длине последовательности 1-й знак накладывается на младший бит счетчика. В данном случае младший бит счетчика можно считать незначащим, так как за максимальное значение счетчика (6) можно принять обе комбинации 110 и 111. При значении счетчика 5 и меньше значащие знаки не накладывается на разряды счетчика.

    Выбираем второй вариант. Называем точки нулями, тире – единицами. Так как последовательность знаков выровнена по младшему биту аргумента последовательность знаков располагаем в обратном порядке для проталкивания сдвигом вправо. Получаем кодировку аргумента:

    ; arg[c2, c1, c0/s6, s5, s4, s3, s2, s1]

    В коде Морзе за единичный временной интервал принята длительность точки. Длительность тире – 3 интервала. Пауза между знаками внутри символа – 1 интервал. Пауза между символами – 4 интервала. Пауза между словами – 7 интервалов. Для сообщения функции, что не надо отрабатывать знаки, а только паузу введем комбинацию:

    ; arg[0, 0, 0, 0, 0, 0, 0, 0]

    Приняв аргумент arg[c2, c1, c0/s6, s5, s4, s3, s2, s1] функция должна извлечь из него счетчик и последовательность, «отпиликать» символ и завершить его паузой длительностью 3 интервала.

    Для формирования паузы между словами передаем функции аргумент arg[0, 0, 0, 0, 0, 0, 0, 0]. Функция должна в дополнение к постпаузе длительностью 3 интервала от предыдущего символа отработать паузу длительностью 4 интервала (итого 7 интервалов).

    Для извлечения счетчика функция должна сдвинуть копию содержимого arg вправо на 5 битов, применить маску И(00000111), приравнять счетчик к 6 если равен 7. Далее пошаговыми сдвигами вправо извлечь знаки из оригинала arg. Если “0” – точка: 1 интервал beep, 1 интервал пауза. Если ”1” – тире: 3 интервала beep, 1 интервал пауза. После отработки последнего знака – 2 интервала пауза. Если arg=0: только пауза длительностью 4 интервала.

    Даная 8-разрядная кодировка покрывает все символы и диграфы Морзе длиной от 1 до 6 знаков. Рассмотрим примеры:

    ;   .        Ee     Ее Ёё arg[0, 0, 1, x, x, x, x, 0]
    ;   -        Tt     Тт    arg[0, 0, 1, x, x, x, x, 1]
    ;   ..       Ii     Ии    arg[0, 1, 0, x, x, x, 0, 0]
    ;   .-       Aa     Аа    arg[0, 1, 0, x, x, x, 1, 0]
    ;   -.       Nn     Нн    arg[0, 1, 0, x, x, x, 0, 1]
    ;   --       Mm     Мм    arg[0, 1, 0, x, x, x, 1, 1]
    ;   ...      Ss     Сс    arg[0, 1, 1, x, x, 0, 0, 0]
    ;   ..-      Uu     Уу    arg[0, 1, 1, x, x, 1, 0, 0]
    ;   .-.      Rr     Рр    arg[0, 1, 1, x, x, 0, 1, 0]
    ;   .--      Ww     Вв    arg[0, 1, 1, x, x, 1, 1, 0]
    ;   -..      Dd     Дд    arg[0, 1, 1, x, x, 0, 0, 1]
    ;   -.-      Kk     Кк    arg[0, 1, 1, x, x, 1, 0, 1]
    ;   --.      Gg     Гг    arg[0, 1, 1, x, x, 0, 1, 1]
    ;   ---      Oo     Оо    arg[0, 1, 1, x, x, 1, 1, 1]
    ;   ....     Hh     Хх    arg[1, 0, 0, x, 0, 0, 0, 0]
    ;   ...-     Vv     Жж    arg[1, 0, 0, x, 1, 0, 0, 0]
    ;   ..-.     Ff     Фф    arg[1, 0, 0, x, 0, 1, 0, 0]
    ;   ..--            Юю    arg[1, 0, 0, x, 1, 1, 0, 0]
    ;   .-..     Ll     Лл    arg[1, 0, 0, x, 0, 0, 1, 0]
    ;   .-.-            Яя    arg[1, 0, 0, x, 1, 0, 1, 0]
    ;   .--.     Pp     Пп    arg[1, 0, 0, x, 0, 1, 1, 1]
    ;   .---     Jj     Йй    arg[1, 0, 0, x, 1, 1, 1, 0]
    ;   -...     Bb     Бб    arg[1, 0, 0, x, 0, 0, 0, 1]
    ;   -..-     Xx     Ьь Ъъ arg[1, 0, 0, x, 1, 0, 0, 1]
    ;   -.-.     Cc     Цц    arg[1, 0, 0, x, 0, 1, 0, 1]
    ;   -.--     Yy     Ыы    arg[1, 0, 0, x, 1, 1, 0, 1]
    ;   --..     Zz     Зз    arg[1, 0, 0, x, 0, 0, 1, 1]
    ;   --.-     Qq     Щщ    arg[1, 0, 0, x, 1, 0, 1, 1]
    ;   ---.            Чч    arg[1, 0, 0, x, 0, 1, 1, 1]
    ;   ----            Шш    arg[1, 0, 0, x, 1, 1, 1, 1]
    ;   .----    1            arg[1, 0, 1, 1, 1, 1, 1, 0]
    ;   ..---    2            arg[1, 0, 1, 1, 1, 1, 0, 0]
    ;   ...--    3            arg[1, 0, 1, 1, 1, 0, 0, 0]
    ;   ....-    4            arg[1, 0, 1, 1, 0, 0, 0, 0]
    ;   .....    5            arg[1, 0, 1, 0, 0, 0, 0, 0]
    ;   -....    6            arg[1, 0, 1, 0, 0, 0, 0, 1]
    ;   --...    7            arg[1, 0, 1, 0, 0, 0, 1, 1]
    ;   ---..    8            arg[1, 0, 1, 0, 0, 1, 1, 1]
    ;   ----.    9            arg[1, 0, 1, 0, 1, 1, 1, 1]
    ;   -----    0            arg[1, 0, 1, 1, 1, 1, 1, 1]
    ;   ..-..           Ээ    arg[1, 0, 1, 0, 0, 1, 0, 0]
    ;   ..-.-    [INT]        arg[1, 0, 1, 1, 0, 1, 0, 0]
    ;   -..-.    [/]          arg[1, 0, 1, 0, 1, 0, 0, 1]
    ;   -.--.    Parenthesis  arg[1, 0, 1, 1, 0, 1, 1, 0]
    ;   .-...    [&]          arg[1, 0, 1, 0, 0, 0, 1, 0]
    ;   -...-    [=]          arg[1, 0, 1, 1, 0, 0, 0, 1]
    ;   .-.-.    [+]          arg[1, 0, 1, 0, 1, 0, 1, 0]
    ;   -.-.-    Starting Signal arg[1, 0, 1, 1, 0, 1, 0, 1]
    ;   ...-.    Understood   arg[1, 0, 1, 0, 1, 0, 0, 0]
    ;   .--.-.          Ъъ    arg[1, 1, 0, 1, 0, 1, 1, 0]
    ;   .-.-.-   [.]          arg[1, 1, 1, 0, 1, 0, 1, 0]
    ;   --..--   [,]          arg[1, 1, 1, 1, 0, 0, 1, 1]
    ;   ..--..   [?]          arg[1, 1, 0, 0, 1, 1, 0, 0]
    ;   .----.   [']          arg[1, 1, 0, 1, 1, 1, 1, 0]
    ;   -.-.--   [!]          arg[1, 1, 1, 1, 0, 1, 0, 1]
    ;   -.--.-   Parenthesis  arg[1, 1, 1, 0, 1, 1, 0, 1]
    ;   ---...   [:]          arg[1, 1, 0, 0, 0, 1, 1, 1]
    ;   -.-.-.   [;]          arg[1, 1, 0, 1, 0, 1, 0, 1]
    ;   -....-   [-]          arg[1, 1, 1, 0, 0, 0, 0, 1]
    ;   ..--.-   [_]          arg[1, 1, 1, 0, 1, 1, 0, 0]
    ;   .-..-.   ["]          arg[1, 1, 0, 1, 0, 0, 1, 0]
    ;   .--.-.   [@]          arg[1, 1, 0, 1, 0, 1, 1, 0]
    ;   ...-.-   End of work  arg[1, 1, 1, 0, 1, 0, 0, 0]
    

    Если присмотреться к тому, что осталось в сухом остатке:

    ;   ...-..-     Dollar sign [$]   [SX] digraph
    ;   ........    Error/correction  [HH] digraph or [EEEEEEEE]
    ;   ...---...   [SOS] 
    

    логично было бы внести дополнительную функцию void dot3woPostPause() после которой отработать [X](-..-), [5](.....) или [:](---...).

    Для полноты картины рассмотрим «сложный» путь. Для отработки диграфов и триграфов Морзе длиной более 6 знаков необходимо внести дополнение в кодировку с целью отработки «лишних» знаков без межсимвольной паузы (без постпаузы длительностью 2 интервала после «лишних» знаков).

    Количество «лишних» знаков от 1 до 3. Разрядность счетчика 2. Расположим счетчик в разрядах arg[4,3], а последовательность в разрядах arg[2,1,0]:

    ; arg[0, 0, 0, c1, c0, s3, s2, s1]

    При arg[7,6,5]=000 для извлечения счетчика функция должна сдвинуть копию содержимого arg вправо на 3 бита, применить маску И(00000011). Далее пошаговыми сдвигами вправо извлечь знаки из оригинала arg. Если “0” – точка: 1 интервал beep, 1 интервал пауза. Если ”1” – тире: 3 интервала beep, 1 интервал пауза. После отработки последнего знака не добавляются какие либо дополнительные паузы.

    Теперь чтобы отработать «длинный» символ надо сначала обработать знаки без постпаузы потом знаки с постпаузой. Для этого нужны два 8-разрядных аргумента. Суммарное количество знаков в аргументах должно соответствовать длине символа:

    ;   ...-..-     Dollar sign [$]   [SX] digraph
    ;   arg1[0, 0, 0, 0, 1, x, x, 0] arg2[1, 1, 1, 0, 0, 1, 0, 0]
    ;   arg1[0, 0, 0, 1, 0, x, 0, 0] arg2[1, 0, 1, 1, 0, 0, 1, 0]
    ;   arg1[0, 0, 0, 1, 1, 0, 0, 0] arg2[1, 0, 0, x, 1, 0, 0, 1]
    ;
    ;   ........    Error/correction  [HH] digraph or [EEEEEEEE]
    ;   arg1[0, 0, 0, 1, 0, x, 0, 0] arg2[1, 1, 0, 0, 0, 0, 0, 0]
    ;   arg1[0, 0, 0, 1, 1, 0, 0, 0] arg2[1, 0, 1, 0, 0, 0, 0, 0]
    ;
    ;   ...---...   [SOS] 
    ;   arg1[0, 0, 0, 1, 1, 0, 0, 0] arg2[1, 1, 0, 0, 0, 1, 1, 1]
    

    Упаковка символов кода Морзе в 8-разрядный код может быть реализована на разных языках программирования и на разных платформах. Для Макса (любителя Щ-кодов) приготовил исходный код «рыбы» на STM8 asm.

    Альтернативная 8-разрядная кодировка от пользователя «Akon32», позволяющая избавиться от второго аргумента:
    ;   arg[0, 0,  0,   0,  0,  0,  0,  0] — [HH] + пауза длительностью 2 интервала
    ;   arg[0, 0,  0,   0,  1,  0,  0,  1] — [$] + пауза длительностью 2 интервала
    ;   arg[1, 0, s1, s2, s3, s4, s5, s6] — [6 знаков] + пауза длительностью 2 интервала
    ;   arg[1, 1,  0, s1, s2, s3, s4, s5]  — [5 знаков] + пауза длительностью 2 интервала
    ;   arg[1, 1,  1,  0, s1, s2, s3, s4]  — [4 знака] + пауза длительностью 2 интервала
    ;   arg[1, 1,  1,  1,  0, s1, s2, s3]  — [3 знака] + пауза длительностью 2 интервала
    ;   arg[1, 1,  1,  1,  1,  0, s1, s2]  — [2 знака] + пауза длительностью 2 интервала
    ;   arg[1, 1,  1,  1,  1,  1,  0, s1]  — [1 знак] + пауза длительностью 2 интервала
    ;   arg[1, 1,  1,  1,  1,  1,  1,  0]  — только пауза длительностью 4 интервала
    ;   arg[1, 1,  1,  1,  1,  1,  1,  1]  — [SOS] + пауза длительностью 2 интервала
    ; начальный счетчик цикла равен 8
    ; далее пошаговый сдвиг влево через Carry флаг
    ; старшие единицы ('1') незначащие
    ; наличие нуля ('0') во флаге переноса (Carry) - условие для передачи последующих знаков
    ; комбинации 0b00000000, 0b11111111, 0b11111110 обрабатываются вне основного цикла
    


    В отличие от последовательности букв (напр. [S], [O], [S]) диграфы и триграфы (напр. [SOS]) отрабатываются без межбуквенных пауз.
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 18

      +2

      А почему для записи длины не использовали унарный код? Так можно вместить от 0 до 7 знаков.


      0XXXXXXX - 7 знаков
      10XXXXXX - 6 знаков
      ...
      111110XX - 2 знака
      1111110X - 1 знак
      11111110 - 0 знаков
      
      (X - это точка или тире. Число знаков = 7-число единиц перед первым нулём)
        0

        А если учесть, что 7-знаковый код всего один, можно один бит вообще зарезервировать под индикатор служебного кода. Так все 6-знаки кодируются вашим способом, а если служебный бит "1", то все остальные кодируют 7-битовый служебный код.
        Среди них можно разместить много полезного:


        • три выбивающихся кода (7, 8 и 9-знак),
        • управляющий сигнал на включение/выключение лентопротяжного механизма,
        • включение/выключение передатчика...
        • можно научить вашу функцию помнить состояние и реализовать Escape последовательности, что даст возможность объединять любые N-графы без пауз, однако кодировка перестаёт при этом быть однобайтной для таких случаев и напоминает UTF-8

        1-0XXXXXX - 6 знаков
        1-10XXXXX - 5 знаков
        ...
        1-111110X - 1 знак
        1-1111110 - 0 знаков
        0-YYYYYYY  - все 7-битные служебные коды:
        
        0-0000000 - символ начала escape последовательности
        0-1000111 - $ [SX]
        0-1001000 - [HH]
        0-1000101 - [SOS]
        0-0ZZZZZZ - количество последующих знаков без разбиения N-граф
            тут  вообще что угодно можно намутить
        
          0
          Ещё один вариант — кодировать необходимость паузы в последнем бите. Тогда получим
          111110X1 - 1 знак
          ...
          0XXXXXX1 - 6 знаков
          0XXXXXX0 111110X1 - 7 знаков
          ...
          0XXXXXX0 0XXXXXX1 - 12 знаков
            0

            О, да, так ещё лучше. Если не нужны служебные управляющие коды, то вообще идеально

              0
              Признаком начала служебного кода может быть байт, в котором только единицы. При таком кодировании он как обычный символ возникнуть не может.
              Или же можно использовать комбинацию 111111YY, если хватит четырёх служебных кодов.
              Или даже так, с двумя специальными комбинацияами:
              1111110X - 1 знак
              ...
              0XXXXXXX - 7 знаков
              11111110 - Error/correction [HH]
              11111111 - [SOS]

              Служебных кодов не будет, но все стандартные символы умещаются в один байт, требуется только обработка двух специальных случаев.
          0
          Благодарю за комментарий. Разрешите развить:

          00000000 — [HH] + пауза длительностью 2 интервала
          00001001 — [$] + пауза длительностью 2 интервала
          10XXXXXX — [6 знаков] + пауза длительностью 2 интервала

          111110XX — [2 знака] + пауза длительностью 2 интервала
          1111110X — [1 знак] + пауза длительностью 2 интервала
          11111110 — только пауза длительностью 4 интервала
          11111111 — [SOS] + пауза длительностью 2 интервала
          начальный счетчик цикла равен 8
          далее пошаговый сдвиг влево через Carry флаг
          комбинации 00000000, 11111111, 11111110 обрабатываются вне основного цикла
            0
            Мне кажется, что, просто ради систематизации, лучше вынести все «неформатные» символы, которые обрабатываются отдельно, в группу, начинающуюся с 0. Тогда первые три будут [SOS], [$] и [HH], а остальное… Ну да, можно использовать для управления «терминалом», как предложили выше. Не уверен, зачем такое может понадобиться, но всё таки.
          +2
          Не очень понятен смысл такой сложности.
          Вы придумали свой алгоритм, как в байте указать точки и тире в виде единиц и нулей, но из-за разного количества знаков, вам в тот же байт нужно также прописать это длину. В результате выходит сложный и неудобный алгоритм, который может не сработать на всех имеющихся знаках.

          При этом всего, у вас 67 различных сигналов, а в байт можно запихнуть 256 без вообще каких-либо алгоритмов, банальной таблицей соответствий.

          Какая стоит цель данного алгоритма? Сэкономить 100 байт кода? Но он получается не универсален для разных языков.

          И да, выше привели алгоритм попроще, за исключением что нужно сделать отдельный сигнал для девятисимвольного SOS
            0

            Тем более, что SOS это три отдельных буквы: S, O и, неожиданно, снова S. Нет никакой объективной необходимости создавать отдельный сигнал вместо последовательной передачи составляющих его отдельных букв.

              +1
              SOS — не три буквы, а триграф: между «буквами» нет пауз, они передаются такой длинной «буквой». То же самое и с другими ди- и триграфами.
                0
                Смысл есть в ускорении передачи. Не забывайте, что морзянка использовалась, когда скорость была даже не 300 бод, а гораздо, гораздо медленнее — 1-2 символа в секунду.

                Также в морзянке были отдельные «буквы» для «конец связи» и «ошибка передачи»
                  0

                  Конец связи — SK, часто действительно передаваемые слитно, а не как положено отдельными буквами.

                    0
                    Не «1-2 символа в секунду», а до 310 символов в минуту. Это порядка 30 бод или больше 5 символов в секунду.
                      0
                      Насколько я знаю, есть определенная градация среди радистов. На чемпионатах по скоростному радиоспорту 180-200 знаков — это уже чемпионы. А в среднем 120-160 знаков это профессиональный радист.

                      310 символов в минуту — может быть и существуют уникомы, но это явно перебор для абсолютного большинства.
                +1

                А тут неуникальные строчки — это так задумано?


                    ".-.-."    : "+",  # Plus sign [+]
                    ".-.-."    : ("[AR]", "digraph   - New Page Signal"),
                
                    ".--.-."   : "  Ъъ",
                    ".--.-."   : "@",  # [AC] digraph - At Sign [@]
                  +1

                  У меня, наверное, профдеформация, но в реальной практике использования телеграфа нужны только буквы стандартной латиницы, цифры, дробь и единственный знак вопроса. Даже точка и запятая редкость. Это не значит, что они не нужны в принципе, но значит, что с поправкой на специфику использования (которая мне не вполне понятна из статьи) малоиспользуемые знаки может быть можно исключить, если этим достигается упрощение программы или устройства, на котором эта программа будет работать.

                    0
                    «Казнить нельзя помиловать».
                    +1
                    trapwalker, похоже прав: коды не совпадают с "эталоном". На сам принцип не влияет, но… Царапает, в общем.

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                    Самое читаемое