|
陶思良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 '.' ; .--...
愚文浅薄,故只增笑尔!
|