你可能已经注意到,在上面显示的网页中,对于低于15WPM的选项,我们使用了“Farnsworth spacing”。那么这个“Farnsworth spacing”又是个什么鬼?
当报务员学习用耳朵来解码莫斯代码的时候,他就会意识到,当播放速度变化的时候,字符出现的节奏也会跟着变化。当播放速度低于10WPM的时候,他能够从容的识别“嘀”和“嗒”,并且知道发送的哪个字符。但是当播放速度超过10WPM的时候,报务员的识别就会出错,他识别出来的字符会多于实际的“嘀”和“嗒”。当一个学习的时候习惯低速莫斯代码的人,在处理高速播放代码的时候,就会出现问题。因为节奏变了,他潜意识的识别就会出错。
为了解决这个问题,“Farnsworth spacing”就被发明出来了。本质上来讲,字母和符号的播放速度依然采取高于15WPM的速度,同时,通过在字符之间插入更多的空格,来使整体的播放速度降低。这样,报务员就能够以一个合理的速度和节奏来识别每个字符,一旦所有的字符都学习完毕,就可以增加速度,而接收员只需要加快识别字符的速度就可以了。本质上来说,“Farnsworth spacing”这个技巧解决了节奏变化这个问题,使接收员能够快速学习。
所以,在整个系统中,对于更低的播放速度,都统一成15WPM。相对应的,一个“嘀”的长度是0.08秒,但是字符之间和单词之间的间隔就不再是3个dit或者7个dit,而是进行的调整以适应整体速度。
生成声音
在PHP代码中,一个字符(即前面数组的索引)代表一组由“嘀”、“嗒”和空白间隔组成的莫斯声音。我们用数字采样来组成音频序列,并且将其写入到文件中,同时加上适当的头信息来将其定义成WAVE格式。
生成声音的代码其实相当简单,你可以在项目中PHP文件中找到它们。我发现定义一个“数字振荡器”相当方便。每调用一次osc(),它就会返回一个从正玄波产生的定时采样。运用声音采样和声频规范,生成WAVE格式的音频已经足够了。在产生的正玄波中的-1到+1之间是被移动和调整过的,这样声音的字节数据可以用0到255来表示,同时128表示零振幅。
同时,在生成声音方面我们还要考虑另外一个问题。一般来讲,我们是通过正玄波的开关来生成莫斯代码。但是你直接这样来做的话,就会发现你生成的信号会占用非常大的带宽。所以,通常无线电设备会对其加以修正,以减少带宽占用。
在我们的项目中,也会做这样的修正,只不过是用数字的方式。既然我们已经知道了一个最小声音样本“嘀”的时间长度,那么,可以证明,最小带宽的声幅发生在长度等于“嘀”的正玄波半周期。事实上,我们使用低通滤波器(low pass filter)来过滤音频信号也能达到同样的效果。不过,既然我们已经知道所有的信号字符,我们直接简单的过滤一下每一个字符信号就可以了。
生成“嘀”、“嗒”和空白信号的PHP代码就像下面这样:
while ($dt < $DitTime) { $x = Osc(); if ($dt < (0.5*$DitTime)) { // Generate the rising part of a dit and dah up to half the dit-time $x = $x*sin((M_PI/2.0)*$dt/(0.5*$DitTime)); $ditstr .= chr(floor(120*$x+128)); $dahstr .= chr(floor(120*$x+128)); } else if ($dt > (0.5*$DitTime)) { // For a dah, the second part of the dit-time is constant amplitude $dahstr .= chr(floor(120*$x+128)); // For a dit, the second half decays with a sine shape $x = $x*sin((M_PI/2.0)*($DitTime-$dt)/(0.5*$DitTime)); $ditstr .= chr(floor(120*$x+128)); } else { $ditstr .= chr(floor(120*$x+128)); $dahstr .= chr(floor(120*$x+128)); } // a space has an amplitude of 0 shifted to 128 $spcstr .= chr(128); $dt += $sampleDT; } // At this point the dit sound has been generated // For another dit-time unit the dah sound has a constant amplitude $dt = 0; while ($dt < $DitTime) { $x = Osc(); $dahstr .= chr(floor(120*$x+128)); $dt += $sampleDT; } // Finally during the 3rd dit-time, the dah sound must be completed // and decay during the final half dit-time $dt = 0; while ($dt < $DitTime) { $x = Osc(); if ($dt > (0.5*$DitTime)) { $x = $x*sin((M_PI/2.0)*($DitTime-$dt)/(0.5*$DitTime)); $dahstr .= chr(floor(120*$x+128)); } else { $dahstr .= chr(floor(120*$x+128)); } $dt += $sampleDT; }
WAVE格式的文件
WAVE是一种通用的音频格式。从最简单的形式来看,WAVE文件通过在头部包含一个整数序列来表示指定采样率的音频振幅。关于WAVE文件的详细信息请查看这里Audio File Format Specifications website。对于产生莫斯代码,我们并不需要用到WAVE格式的所有参数选项,仅仅需要一个8位的单声道就可以了,所以,so easy。需要注意的是,多字节数据需要采用低位优先(little-endian)的字节顺序。WAVE文件使用一种由叫做“块(chunks)”的记录组成的RIFF格式。
WAVE文件由一个ASCII标识符RIFF开始,紧跟着一个4字节的“块”,然后是一个包含ASCII字符WAVE的头信息,最后是定义格式的数据和声音数据。