| 
 | 
 
 
陶思良tase@163.com  原创文章,首发  www.hellocq.net 前言:
  前些日子开发一个小玩意,由于体积限制,只能有一个按钮,而设置功能又比较多,无意中想到了morse code,就一只电键,千军万马都能呼来唤去,于是就萌发了用morse 按钮来控制设备的想法。在网上搜了一下,morse的解释器还真不少,下载了很多但没有我想要的,传统的方法是将.-(嘀嗒)形成01序列,然后再循环查找,但大多摆脱不了字符或字符串比较,单片机中字符串的操作是非常缓慢的,我是一个追求代码效率的人,就想简单,快捷,那些冗长的代码我看了看就放弃了!后来无意中找到了一种新的思路,就是下面我归纳的morse识别算法,(这里是归纳,不是创作)。它让我只用几百个字节就实现了morse代码的解释引擎,并且速度比传统的方法快很多,因为它没有二维查表,没有多重循环,也没有字符串或者字符的比较。最后这个小玩意非常好用,完全满足我的要求。(-.) n 开关机(防止误触发),(---)o 进入设置程序,(.)e 触发最常用的功能....
  虽然很早就在hellocq.net 上注册了,但潜水多,冒泡少,更不要提贡献了,多年来就对cw通讯有所渴望,由于长期在生命线上挣扎,业余爱好只能搁置多年,近期有空重温旧梦,浸泡在www.hellocq.net 各个板块中,受益菲浅,突有灵感,想把近期对morse代码的理解写成文章,其中不免有错漏,请各位前辈批评指教。
 
  正文:
  我们把0定义成“嘀”,把 1 定义成 “嗒” 0=. 1=-
  1 bit 的二进制数最大表示范围为2 0    .   e 1    -   t
  2 bit 的二进制数最大表示范围为4 00   ..  i 01   .-  a 10   -.  n 11   --  m
  3 bit 的二进制数最大表示范围为8 000  ... s  001  ..- u 010  .-. r 011  .-- w 100  -.. d 101  -.- k 110  --. g 111  --- o
  4 bit 的二进制数最大表示范围为16 0000 .... h 0001 ...- v 0010 ..-. f 0011 ..-- ü 拼音里面的u:,两点在u的上面 0100 .-.. l 0101 .-.- a: 两点在a的上面 0110 .--. p 0111 .--- j 1000 -... b 1001 -..- x 1010 -.-. c 1011 -.-- y 1100 --.. z 1101 --.- q 1110 ---. ó  1111 ---- 
  5 bit 以上的情况就不详细列举了。 2^5 = 32, 2^6= 64
  通常在4码以内,可能的字符有  2+4+8+16 = 30 ,这里面包含了英文的26个字母,和4个自定义字符,但1111 ---- 我没有找到,这么好记的编码没有分配字符太可惜了。
  5bit 能表示32 码,6bit 能表示 64码,通常我们的数字长码都在5bit 范围内。
  11111 -----  0    x (罗马数字好像没有0,x是10) 01111 .----  1    i 00111 ..---  2    ii 00011 ...--  3    iii 00001 ....-  4    iv 00000 .....  5    v 10000 -....  6    vi 11000 --...  7    vii 11100 ---..  8    viii 11110 ----.  9    ix
  从上述可见,数字编码比较有趣,只利用了5bit编码的很少一部分,编码规则有点象罗马数字,是以5为中心的,左右延伸。
  下面一些常用的标点符号也是5bit的, 10001  -...-  =  10110  -.--.  ( 10010  -..-.  / 01010  .-.-.  +  或者 end of message 01000  .-...  |  或者 wait 00010  ...-.  *  或者 understood  10101  -.-.-  >  这个是起始信号 starting signal
  还有一些5bit的编码,基本都是一些生僻的字母,在我们的键盘上一般是敲不出来的,这里就不列举了。
  从上面两个5bit的编码组看,个人感觉它的分配规则基本上是视觉规则,或者说是听觉规则,人类最容易识别的编码方法,当然即使再复杂的5bit编码,通过训练也是能正确识别的。 但从代码的空间上看,5bit的编码存着严重的浪费。
  6bit 基本都是标点符号和生僻字母了,编码空间64个,很大,不过这个空间内代码的利用率应该是最低的,这里就不罗唆了。
  6bit以内的代码空间能表示的字符数最多是:  2+4+8+16+32+64 = 126 个 ,英文ascii码表中,一共有127个字符,也就是说,理论上morse 的编码可以处理ascii码表中的所有可打印字符,还能近似完全的处理那些不可打印的字符,对于那些怀疑morse代码的表示能力的朋友,应该能放心了吧!
 
 
 
 
 
 
  morse 代码的取反和派生
 
 
  在morse代码中我们能发现许多有趣的事,其中有一点就是取反(逻辑非)和派生,还有一些是“约等于”(近似),发现这些“规律”,morse 代码变得非常好记。
 
  取反的情况:
 
  先看看第一组:
  0    .   e 1    -   t 它们简单的让我我无法用语言描述了。
  接下来: 00   ..  i 11   --  m
  01   .-  a 10   -.  n
  3bit 的情况 000  ... s  111  --- o   这时我才发现原来它们如此的对称,难怪 会出现一个常用的 sos,原来就是 000 111 000
  001  ..- u 110  --. g
  010  .-. r 101  -.- k
  011  .-- w 100  -.. d
  4bit 的情况 0010 ..-. f 1101 --.- q
  0100 .-.. l 1011 -.-- y
  0111 .--- j 1000 -... b
  0110 .--. p 1001 -..- x
  01010  .-.-.  +  或者 end of message 10101  -.-.-  >  这个是起始信号 starting signal
  这里归纳的取反,有点牵强,因为实际在听的时候,它们差别很大,就像是磁带上的一首歌,即使歌词你很熟悉,倒过来放虽然你可能会觉得很好听,但基本听不懂,不过这也许是一种记忆方法。
 
 
 
  派生的情况:
 
  01   .-  a 010  .-. r 011  .-- w
  0    .    e 00   ..   i 000  ...  s  0000 .... h
  1    -    t 11   --   m 111  ---  o   100  -.. d 1000 -... b
  001  ..- u 0010 ..-. f
  101  -.- k 1011 -.-- y
  010  .-. r 0100 .-.. l
 
  派生很容易举一反三,通过联想记忆法,很容易记住派生的字母,例如我记f就是这样记的,u多一点就是f。
  像一些特殊的符号可以把它们记成2个字母的连发,如下所示 ar .-.-. 停止 (消息结束 end of message) as .-... 等待wait | sk ...-.- 终止 (联络结束) bt -...- 分隔符 sn ...-. (我将重新发送最後一个单词)
  近似的情况:
  100  -.. d 110  --. g
  1010 -.-. c 1011 -.-- y
  0001 ...- v 0010 ..-. f
  000  ...  s  0000 .... h 可能近似的还有很多,尤其是快速抄报的时候,像我这样的初学者还是很糊涂的,关于近似的概念,可能每个cw的学习者对它的理解还是很个性化的,不同的人可能有不同的近似感觉。
  morse代码的识别:
  上述文章我们叙述了morse代码的二进制关系,下面我想和大家讨论一下morse代码的计算机处理。 既然morse代码有唯一的编码规则,那么我们是否可以将它对应的数值唯一化? 回答是肯定的。
  回顾上文中提到的:
  1 bit 的二进制数最大表示范围为2 0    .   e      十进制数值 0 1    -   t      十进制数值 1
  2 bit 的二进制数最大表示范围为4 00   ..  i      十进制数值 0 01   .-  a      十进制数值 1 10   -.  n      十进制数值 2 11   --  m      十进制数值 3
  我们很快就看到一个问题,仅仅在1bit 和 2bit 的编码中我们就遇到了严重的重码的现象(重复50%),那么在3bit,4bit,5bit,6bit 的编码中一定还会有更多的重码。 下面我们引入一种非常简单的算法将它们对应的十进制数值唯一化。
  这个算法我把它编成了一句口诀,“索引遇0加权值,最后一位加权值,权值初始总是1,遇0遇1左移1。” 这个算法并不是我设计的,我是根据varela morales 的思想将它实现的,但口诀绝对是原创,希望在这里能抛砖引玉。
  下面我举几个例子来解释这个算法:
  1就是这个权值的初始值,我们用mask 来表示权值,0 是索引的初始值,我们用index表示索引值。
  0    .   e       e的二进制编码是0b,索引值index初始化是0,权值mask初始化是1,当前位0b符合“索引遇0加权值”,index=index+mask =0+1=1,“遇0遇1左移1”,权值mask左移1 ,1<<1 =2 ,1 的进制代码是001b(为了区别二进制和10进制,二进制结尾用b结束),左移1位后变成010b 换成10进制就是2,索引现在是1,在这里由于0也是最后一位,符合“最后一位加权值”。index = index + mask = 1+2 = 3,计算出10进制数值3。
 
  1    -   t      t的二进制编码是1,index初始值是0,mask初始值是1,由于当前位是1b,因此不符合“索引遇0加权值”,所以index还是0。“遇0遇1左移1”,权值mask左移1 ,1<<1 =2,在这里由于1b也是最后一位,符合“最后一位加权值”。index = index + mask = 0+2 = 2。
  读者可能会说我耍赖,e 和 t 太简单,这个算法未必适合所有的morse代码,那么我们随便再举几个例子
  0000 .... h  ,直接看对应的10进制值也是0, 那么套用我创作口诀:这个编码有4位,我们分为5轮计算 1) index = 0, mask = 1 ,“索引遇0加权值” index = index+mask = 0+1 = 1; “遇0遇1左移1”,权值mask左移1, mask = mask <<1 = 1<<1 = 2; 2) index = 1, mask = 2 ,“索引遇0加权值” index = index+mask = 1+2 = 3; “遇0遇1左移1”,权值mask左移1, mask = mask <<1 = 2<<1 = 4; 3) index = 3, mask = 4 ,“索引遇0加权值” index = index+mask = 3+4 = 7; “遇0遇1左移1”,权值mask左移1, mask = mask <<1 = 4<<1 = 8; 4) index = 7, mask = 8 ,“索引遇0加权值” index = index+mask = 7+8 = 15; “遇0遇1左移1”,权值mask左移1, mask = mask <<1 = 8<<1 = 16; 5)“最后一位加权值” index = index + mask = 15 + 16 = 31. 最终计算出来h的索引值是31
  1011 -.-- y  再来一个例子,这样更容易理解我的算法,为什么选择y,y比较复杂,并且y并不是对称的代码,更容易让读者理解计算的顺序,仍旧是5轮计算。 1) index = 0, mask = 1 ,“索引遇0加权值”,当前位1xxxb不符合(非当前位用x表示),跳过,“遇0遇1左移1”,权值mask左移1, mask = mask <<1 = 1<<1 = 2; 2) index = 0, mask = 2 ,“索引遇0加权值”,当前位x0xxb符合, index = index+mask = 0+2 = 2; “遇0遇1左移1”,权值mask左移1, mask = mask <<1 = 4<<1 = 4; 3) index = 2, mask = 4 ,“索引遇0加权值”,当前位xx1xb不符合,跳过,“遇0遇1左移1”,权值mask左移1, mask = mask <<1 = 2<<1 = 8; 4) index = 2, mask = 8 ,“索引遇0加权值”当前位xxx1b不符合,跳过,“遇0遇1左移1”,权值mask左移1, mask = mask <<1 = 8<<1 = 16; 5)“最后一位加权值” index = index + mask = 2 + 16 = 18. 最终计算出来y的索引值是18
  其他的morse代码读者可以自行计算,下表是一张计算好的列表,那么在程序中只需要简单的查表即可,众所周知,一个好的hash算法能够提高搜索的速度,这里提供的就是一种morse hash算法,简单实用。
 
  附表:
  索引index   morse代码 ------------------------- 0  '.' ;无效字符,占位,因为计算机编程语言中数组通常是从下标0开始 1  '.' ;占位  2 't'       ; - 3 'e'       ; . 4 'm'       ; -- 5 'a'       ; .- 6 'n'       ; -. 7 'i'       ; .. 8 'o'       ; --- 9 'w'       ; .-- 10 'k'       ; -.- 11 'u'       ; ..- 12 'g'       ; --.  13 'r'       ; .-. 14 'd'       ; -.. 15 's'       ; ... 16 '.'       ; ---- 17 'j'       ; .--- 18 'y'       ; -.-- 19 0xf5       ; ..-- ? 20 'q'       ; --.- 21 0xe1       ; .-.- ? 22 'x'       ; -..- 23 'v'       ; ...- 24 0xef       ; ---. ? 25 'p'       ; .--. 26 'c'       ; -.-. 27 'f'       ; ..-. 28 'z'       ; --.. 29 'l'       ; .-.. 30 'b'       ; -... 31 'h'       ; .... 32 Ɔ'       ; ----- 33 Ƈ'       ; .---- 34 '.'       ; -.--- 35 ƈ'       ; ..--- 36 0xee       ; --.--       ? 37 '.'       ; .-.-- 38 '.'       ; -..-- 39 Ɖ'       ; ...-- 40 '.'       ; ---.- 41 'a'       ; .--.- ? 42 0x7e       ; -.-.- starting signal 43 '.'       ; ..-.- 44 '.'       ; --..- 45 '.'       ; .-..- 46 '='       ; -...- 47 Ɗ'       ; ....- 48 Ə'       ; ----. 49 '.'       ; .---. 50 '('       ; -.--. 51 '.'       ; ..--. 52 '.'       ; --.-. 53 '+'       ; .-.-. end of message 54 '/'       ; -..-. 55 '*'       ; ...-. understood 56 Ǝ'       ; ---.. 57 '.'       ; .--.. 58 '.'       ; -.-.. 59 'e'       ; ..-..       ? 60 ƍ'       ; --... 61 0x7c       ; .-... wait 62 ƌ'       ; -.... 63 Ƌ'       ; ..... 64 '.'       ; ------ 65 '.'       ; .----- 66 '.'       ; -.---- 67 '.'       ; ..---- 68 '.'       ; --.--- 69 '.'       ; .-.--- 70 '.'       ; -..--- 71 '.'       ; ...--- 72 '.'       ; ---.-- 73 '.'       ; .--.-- 74 '.'       ; -.-.-- 75 '.'       ; ..-.-- 76 ','       ; --..--       ',' 77 '.'       : .-..-- 78 '.'       ; -...-- 79 '.'       ; ....-- 80 '.'       ; ----.- 81 '.'       ; .---.- 82 ')'       ; -.--.- 83 '_'       ; ..--.- 84 '.'       ; --.-.- 85 '.'       ; .-.-.-       '.' 86 '.'       ; -..-.- 87 0xff       ; ...-.- end of work 88 '.'       ; ---..- 89 '.'       ; .--..- 90 '.'       ; -.-..- 91 '.'       ; ..-..- 92 '.'       ; --...- 93 '.'       ; .-...- 94 '-'       ; -....- 95 '.'       ; .....- 96 '.'       ; -----. 97 '.'       ; .----. 98 '.'       ; -.---. 99 '.'       ; ..---. 100 '.'       ; --.--. 101 '.'       ; .-.--. 102 '.'       ; -..--. 103 '.'       ; ...--. 104 '.'       ; ---.-. 105 '.'       ; .--.-. 106 ''       ; -.-.-. 107 '.'       ; ..-.-. 108 '.'       ; --..-. 109 '"'       ; .-..-. 110 '.'       ; -...-. 111 '.'       ; ....-. 112 '.'       ; ----.. 113 '.'       ; .---.. 114 '.'       ; -.--.. 115 '?'       ; ..--..        116 '.'       ; --.-.. 117 0xf0       ; .-.-.. paragraph 118 '.'       ; -..-.. 119 '.'       ; ...-.. 120 ':'       ; ---... 121 '.'       ; .--...
 
 
  愚文浅薄,故只增笑尔!
 
 
 |