image

Последнее время я занимаюсь изучением азбуки Морзе с помощью данной программы. Но она рассчитана на изучение кодов кириллических букв, что является неактуальным в современной радиосвязи (все используют латинский алфавит, кроме нашей доблестной армии).

Такая ситуация меня не устроила, и было принято решение написать программу для генерации звукового кода Морзе из некоторого текста с настройкой скорости и возможностью добавления кодов динамически. Решение получилось достаточно оригинальным и гибким (ИМХО, конечно же). И я решил поделиться программой с общественностью: возможно, она будет кому-то полезна или покажется интересной.

В качестве инструмента реализации идеи был выбран С++ в связке с Qt.

Основная идея программы


Атомом (единицей времени) кода Морзе является точка, относительно нее формируется длительность всех остальных элементов:

  1. Тире равняется трем звучащим точкам;
  2. Пауза между элементами одного символа (знака) — одна незвучащая точка;
  3. Между знаками — три точки;
  4. Между словами — семь точек.

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

Первоначальный вариант реализации


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

Как вы уже поняли, для получения конечного сигнала я всего лишь «дергал» звук с некоторой задержкой (с помощью таймера, равного длительности точки в миллисекундах) при бесконечно воспроизводящемся .wav файле с записью синуса. Но данный подход имел значительный минус и заключался он в том, что приходилось каждую точку загружать отдельно с помощью перегруженного оператора или специального метода. Из-за такого подхода пришлось писать отдельный макрос для каждой буквы (вроде такого — #define I DOT << false << DOT) и создать огромный жуткий switch для воспроизведения переданной строки. Это было ужасно, но если вам любопытно, то вы можете ознакомиться
с первой версией программы тут (у меня не получилось полностью загрузить на GitHub локальный репозиторий — только последнюю версию).

Кусочек жуткого свитча:
bool Morse::StringToMorse (QString &line) {

  line += '\0';

  for (int i = 0; i < line.size () - 1; ++i) {
    switch (line.at(i).unicode ()) {

      case 'A':
        *this << A;
        if (line.at (i + 1) == ' ')
          continue;
        else
          *this << MINI_SPACE;
      break;

      case 'B':
        *this << B;
        if (line.at (i + 1) == ' ')
          continue;
        else
          *this << MINI_SPACE;
      break;

// И так далее


А вот так происходило включение и выключение звука (собственно, генерация звукового кода):
void Morse::PlayLinePoints () {

  QTimer::singleShot (duration_point_, this, SLOT ( Mute () ));

  sound_.play ();
}

void Morse::Mute () {
  if (line_points_.empty ()) { //Останавливаем воспроизведение
    sound_.stop ();
    return;
  }

  if (line_points_.at (0)) { //Включаем звук
    sound_.setMuted (false);
    line_points_.remove (0);
    QTimer::singleShot (duration_point_, this, SLOT ( Mute () ));
    return;
  } else {
    sound_.setMuted (true); //Выключаем звук
    line_points_.remove (0);
    QTimer::singleShot (duration_point_, this, SLOT ( Mute () ));
    return;
  }
}


Окончательная версия


Очень уж оказались эти макросы громоздки, и мой перфекционизм не смог больше смотреть на эти монструозные конструкции. Поразмыслив немного, пришел к мысли, что идея у меня хорошая, но хранение кодов в виде макросов очень неудобно и, если решить эту проблему, то все будет хорошо. В итоге для хранения кодов стал использоваться QMap:

//Хранит соответствующие комбинации точек и тире символов
  QMap<QChar, QBitArray> codes_;

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

Новая реализация воспроизведения:
void Morse::MiniSpace () {

  if (stop_) {
    this->Stop ();
    return;
  }

  sound_.setMuted (true);

  ++id_element_; //Преходим на другой элемент кода
  if (  id_element_ == codes_.value ( string_to_play_.at (id_char_) ).size ()  ) {
    ++id_char_;
    id_element_ = 0;
    QTimer::singleShot (duration_dot_ * 3, this, SLOT ( Mute() )); //Пауза между символами
    return;
  }

  QTimer::singleShot (duration_dot_, this, SLOT ( Mute() )); //Пауза между элементами символа
}



void Morse::Space () {

  if (stop_) {
    this->Stop ();
    return;
  }

  sound_.setMuted (true);
  //Пауза длится 7 точек
  //Но так как после символа идет пауза в три точки, то доп паузу нужно выставить длиной в 4 точки
  QTimer::singleShot (duration_dot_ * 4, this, SLOT ( Mute() ));
}



void Morse::Mute () {

  if (stop_) {
    this->Stop ();
    return;
  }

  if (id_char_ == string_to_play_.size ()) { // Строка закончилась
    this->Stop ();
    return;
  }

  if (string_to_play_.at (id_char_)  == ' ') {
    Space();
    ++id_char_; //Преходим на другой элемент кода
    return;
  }

  if (codes_.find ( string_to_play_.at (id_char_) ) == codes_.end ()) {
    qDebug() << string_to_play_.at (id_char_) << ": No code!";
    sound_.stop ();
    return;
  }

  sound_.setMuted (false); //Включаем звук

  if (  codes_.value ( string_to_play_.at (id_char_) ).at (id_element_)) {
    QTimer::singleShot (duration_dot_, this, SLOT ( MiniSpace() )); //Воспроизводим точку
  } else {
    QTimer::singleShot (duration_dot_ * 3, this, SLOT ( MiniSpace() )); //Воспроизводим тире
  }

}

bool Morse::Play () {

  if (!stop_)
    return false;

  if (string_to_play_ == "")
    return false;

  stop_ = false;

  id_char_ = 0;
  id_element_ = 0;
  sound_.setMuted (true); //Выключаем звук
  sound_.play ();
  Mute ();
}

void Morse::Stop () {

  if (stop_)
    return;

  sound_.stop ();
  id_char_ = 0;
  id_element_ = 0;
  stop_ = true;
}


Флаг stop_ был введен для предотвращения некорректной работы программы (два вызова подряд Play() и прочих нехороших вещей).
Остальную часть исходных кодов и заголовочные файлы не вижу смысла приводить в теле статьи, так как там все достаточно очевидно и прозрачно.

Полный набор исходников последней версии вы можете скачать на гитхабе. Написание графического интерфейса является тривиальной задачей, но все же, если будет создан GUI, то ссылку я добавлю. Если есть какие-то вопросы или замечания, пишите в комментариях — обязательно отвечу.

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


  1. PapaTramp
    24.09.2015 13:40

    Мы в мореходке учились на тренажере «Ручеёк» :)
    Удобное было устройство, могло выдавать цифры, буквы и, по-моему, цифро-буквенные сочетания. Ну и скорость, само собой, можно было регулировать.


  1. Stas911
    25.09.2015 17:38

    А кто и для чего сейчас Морзе использует?


    1. Swamp_Dok
      25.09.2015 20:43

      Для любительской радиосвязи на КВ и в армии.


    1. UA3MQJ
      26.09.2015 11:19
      +2

      Радиолюбители используют, как один из видов связи, которых очень много. Но в морзянке есть свои прелести:

      • Телеграфный сигнал — это либо наличие несущей частоты в эфире, либо ее отсутствие. То есть по сути это генерация заданной частоты, усиление и подача этого сигнала в антенну. Сделать телеграфный передатчик — очень просто! Вот AM, FM или SSB — уже сложнее.
      • Телеграфный сигнал занимает узкую полосу. Вся энергия сосредоточена на одной частоте (а не размазана по спектру, как в SSB или как в АМ не передается бессмысленная несущая). Можно сужать полосу приемника для улучшения приема. В итоге дальность связи увеличивается, по сравнению с голосом
      • Аура таинственности, свой тайный язык. Осознание радости от того, что человеческий мозг сам может, без компьютера, декодировать цифровые сигналы. А еще очень удобно, если ты человек семейный. Вместо постоянной болтовни в микрофон, типа «ульяна анна тройка… пять девять… QRM… Мой QTH...», тихо колупаешь клоподавом, или автоматическим ключем, или вообще набираешь на клавиатуре текст, а ПК сам передает телеграфом. Прием, естественно, на слух (хотя ПК тоже может)
      • При проведении связей голосом, зачастую все скатывается в болтовню. Целые диапазоны болтающих людей на больших мощностях. Со скромными антеннами и 100 ваттами зачастую и не пробиться. Тут как раз CW дает возможность провести связь. Лаконично и быстро. А еще, морзянка — универсальный международный язык. Нужны минимальные знания, чтобы проводить связи на английском языке (и никаких проблем с произношением).

      Так что, не смотря на возможность голосовой и даже цифровой связи, телеграф до сих пор не потерял актуальности на любительских диапазонах.


      1. Stas911
        28.09.2015 15:40
        +1

        Спасибо за развернутый ликбез!


  1. BalinTomsk
    25.09.2015 18:45

    В советские времена в 80-х учились на обычных автоматических ключах.
    Для быстрой учебы pаспознать на слух учили песенные напевы типа: К — куда ты пошла. А — ай да.

    На соревнованиях по скоростному приму-передачи радиограмм включалась еше такая дисциплина как метание гранат.


    1. Swamp_Dok
      25.09.2015 20:42

      Да, часто учат с напевами, но руководитель местного радиоклуба говорит, что стоит учить без напевов, так как они в перспективе мешать будут.


      1. UA3MQJ
        26.09.2015 11:20

        В apak, на сколько я помню, можно удалить файл с напевами, и будет работать без напевов (через та-ти-та-та). Так же и с русским языком. Есть возможность переключиться на английский, но я уже не помню как.


        1. Swamp_Dok
          26.09.2015 17:45

          С напевами я так и делаю.


  1. murzilka
    25.09.2015 19:01

    const auto factor = codes_.value(string_to_play_.at(id_char_)).at (id_element_)) ? 1 : 3;
    QTimer::singleShot (duration_dot_ * factor, this, SLOT ( MiniSpace() )); 
    


    1. Swamp_Dok
      25.09.2015 19:58

      Красивый вариант, спасибо)


  1. itblogger
    26.09.2015 07:41

    Эх, елки, столько лет прошло! Ночи в наушниках, днем на соревнованиях по охоте на лис, романтика!

    Айда, баки текут, видала, гагарин, домики, есть, железисто, заводики, иди, как же так, лунатики, мама, номер, около, пила поет, решает?, семенить, так, (у не помню), (ф не помню), химичите, цапли-цапли, (ч не помню), (ш не помню, таа-таа-таа-таа), ща вам не ша, электроника, ы не надо, я мал я мал


    1. Swamp_Dok
      26.09.2015 07:46

      Да, как раз такие напевы используются в программе по которой я учу телеграф. Правда учу без напевов.


    1. iUser
      27.09.2015 09:15

      решает
      унесло
      филимончик
      чаша тонет
      шаровары

      Вместо «заводиков» у нас были «закатики», вместо «как же так» — «как дела», вместо «цапли-цапли» — «цыпа-цыпа» и вместо «семенить» — «синее» :)


  1. UA3MQJ
    26.09.2015 10:55

    Спасибо за статью.
    Все правильно — через QMap.


  1. Carry
    27.09.2015 14:57

    Бедный ключ на КДПВ. Ктож его так покрасил (


    1. iUser
      28.09.2015 04:16

      Никто. Это музейный экспонат 1844г., из латуни.