По просьбе одного из любителей применения Щ-кодов в электронных поделках возникла необходимость написать функцию (подпрограмму, процедуру), которая будет издавать трель из последовательности точек и тире. В коде Морзе длина символа может быть от 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]) отрабатываются без межбуквенных пауз.

Комментарии (15)


  1. Akon32
    25.10.2019 11:56
    +1

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


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


    1. trapwalker
      25.10.2019 12:39

      А если учесть, что 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-граф
          тут  вообще что угодно можно намутить
      


      1. Rsa97
        25.10.2019 13:05

        Ещё один вариант — кодировать необходимость паузы в последнем бите. Тогда получим
        111110X1 - 1 знак
        ...
        0XXXXXX1 - 6 знаков
        0XXXXXX0 111110X1 - 7 знаков
        ...
        0XXXXXX0 0XXXXXX1 - 12 знаков


        1. trapwalker
          25.10.2019 13:22

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


          1. Rsa97
            25.10.2019 20:05

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

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


    1. ovsp Автор
      26.10.2019 08:22

      Благодарю за комментарий. Разрешите развить:

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

      111110XX — [2 знака] + пауза длительностью 2 интервала
      1111110X — [1 знак] + пауза длительностью 2 интервала
      11111110 — только пауза длительностью 4 интервала
      11111111 — [SOS] + пауза длительностью 2 интервала
      начальный счетчик цикла равен 8
      далее пошаговый сдвиг влево через Carry флаг
      комбинации 00000000, 11111111, 11111110 обрабатываются вне основного цикла


      1. AndreyHenneberg
        28.10.2019 02:14

        Мне кажется, что, просто ради систематизации, лучше вынести все «неформатные» символы, которые обрабатываются отдельно, в группу, начинающуюся с 0. Тогда первые три будут [SOS], [$] и [HH], а остальное… Ну да, можно использовать для управления «терминалом», как предложили выше. Не уверен, зачем такое может понадобиться, но всё таки.


  1. saboteur_kiev
    25.10.2019 12:16

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

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

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

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


    1. Astroscope
      26.10.2019 20:08

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


      1. AndreyHenneberg
        28.10.2019 01:58
        +1

        SOS — не три буквы, а триграф: между «буквами» нет пауз, они передаются такой длинной «буквой». То же самое и с другими ди- и триграфами.


      1. saboteur_kiev
        28.10.2019 18:48

        Смысл есть в ускорении передачи. Не забывайте, что морзянка использовалась, когда скорость была даже не 300 бод, а гораздо, гораздо медленнее — 1-2 символа в секунду.

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


        1. Astroscope
          28.10.2019 19:19

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


  1. trapwalker
    25.10.2019 12:21

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


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


  1. Astroscope
    26.10.2019 20:15
    +1

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


  1. AndreyHenneberg
    28.10.2019 02:18
    +1

    trapwalker, похоже прав: коды не совпадают с "эталоном". На сам принцип не влияет, но… Царапает, в общем.