aristotle73 has asked for the wisdom of the Perl Monks concerning the following question:

I want to write a perl script that can interpret morse code from the sound card's mic input (so the script can be used with a keyer) but I'm not sure how to accomplish this. I looked at the Acme modules and couldn't find anything relevant to my problem. Any suggestions?

Replies are listed 'Best First'.
Re: Morse code via sound card input
by Trizor (Pilgrim) on Jul 06, 2007 at 20:42 UTC

    Your first mistake was believing the answers you seeked were in the Acme:: namespace. Acme is for jokes, just look at Acme::BooK::Is::My::Bitch.

    However even if you had searched all of CPAN you would be sadly disappointed to find that there are no modules that currently do this. The problem is that for computers pattern recognition is very difficult. I don't know where to start on solving this problem, but if you could get the base audio wave and sufficently filter out any extraneous noise you would probably want to use PDL to do the necessary analysis to separate out the dits and dahs. From there the decoding is simple.

    Another option, if you are a HAM who has recently gotten onto General or Extra bandplan since the morse restriction was lifted is to acutally learn Morse. It doesn't take too long to pick up if you use the Koch method (about 2 weeks) and you'll find the experience much more enjoyable.

    Signal analysis is a topic that takes up entire university courses, which is why commercial Morse decoders are sold and are the price they are.

Re: Morse code via sound card input
by shigetsu (Hermit) on Jul 06, 2007 at 20:25 UTC

    Acme::* modules are, as I understand it, mostly not meant to be serious or used in production code, hence the namespace.

    Have you had a look at Convert::Morse, which I think is worthwhile mentioning, because it has two functions (albeit the documentation says (erroneously?) otherwise regarding the implementation), as_ascii() and as_morse(), where former one allows converting from morse to ASCII and vice versa for latter one.

Re: Morse code via sound card input
by zentara (Cardinal) on Jul 07, 2007 at 17:15 UTC

    Reading code will be trickier, since you will need to detect silences (as opposed to signal) and time them. Here is a start, which will display a number between 0 and ~65500, depending on what your soundcard is playing. Start it, and play a sound and you should see the numbers and 0 for silence , also see a VU-Meter based on this below: UPDATE: Code in readmore is a noise meter, ineffective for reading tones


    I'm not really a human, but I play one on earth. Cogito ergo sum a bum
      Is autodetection of the sender's speed really necessary?

      I may be way off base here, but I think there's supposed to be a distinct ratio between the duration of the dit, the duration of the dah, the duration of the intercharacter pause and the duration of the interword pause.

      If true, and the sender has a good fist, that may offer an approach to avoiding the need for autodetection.

      ...or, I may be all wet.

        Those intervals are specified in terms of ratios. The absolute intervals are a direct function of the sending speed - the ratios remain fixed, the actual intervals change according to the notional sending speed.

        The complication for detection is that not only do you need to be able to compensate for a drift in sending speed over time, but also you have to accommodate deviations from the ideal ratios of the various intervals which may occur on a letter by letter basis, and may also change depending on the context of individual letters.

        Autospeed detection is actually essential for a practical decoder because the speed does change over time. A hint can help, but it's unlikely to be enough on its own.


        DWIM is Perl's answer to Gödel
        This would be an interesting project to test. I mean I could take the code I showed above, and while sampling every 10 milliseconds for a 0 or 1 condition, draw a line on a Tk canvas and see what it might look like, and whether I could produce a program to convert it to letters. I would expect it to look something like Morse code, but the width of each on-state would have to be put through some artificial intelligence program to detect whether it was a dit or dash. Then there is the problem of the silence between letters, and it's duration, compared to the silence within a sent letter. For instance, How would I know whether a single "dit dit" was sent, rather than 2 sucessive "dits"?

        It all seems doable, but what happens if the sender's speed and intervals subtlely change over time? So now the AI code needs to keep running averages of the sender's current delay-intervals.

        All possible, but it would be pretty complex code to gaurantee good transcription accuracy.


        I'm not really a human, but I play one on earth. Cogito ergo sum a bum
Re: Morse code via sound card input
by aristotle73 (Sexton) on Jul 06, 2007 at 21:20 UTC
    Morse code is really just a simple digital signal. So either there is sound (True) or there isn't (False). Is there a module that can return whether there is noise present above a specified audio level (be it either volume or frequency)? If so, then the dits and dahs can be approximated based on how often True is returned within a given length of time. Then all that would be required to insure accuracy would be to specify the estimated wpm to scale the approximations.
Re: Morse code via sound card input
by quester (Vicar) on Jul 07, 2007 at 06:50 UTC
    Actually, it would help if you explained why you are need to do this. It makes a lot of difference whether you are planning to use it to help someone who is trying to practice sending Morse code, or if it would be an alternate means of accessing the computer for a partly disabled person who cannot type but can still send Morse.

    Also, if the keyer is close enough to the computer to run wires, you might want to consider having it key a line on a parallel port, or one of the inputs (DSR or CTS) on a serial port. The keyer will the input on and off, and you sample the signal periodically (around 10-100 times per secord.) A digital input can be easier to get working than a sound card connection, which may require you to find and fix problems with ground loop induced hum.

      I hadn't thought about its use for the handicapped. I had originally intended to use the script as a plugin for XChat to allow the use of a keyer instead of a keyboard for text. This way I could practice morse code without having to use practice drills which are really just repetitive quizzes. Morse code is like a very simple language and practicing in this manner can greatly speed learning (for me anyway). I hadn't considered using the serial port. That might be a better option given that using an audio waveform is more troublesome.
Re: Morse code via sound card input
by zentara (Cardinal) on Jul 08, 2007 at 18:45 UTC
    Just to persue discussion on this, and to clarify something, my method of reading the dsp will not work very well. It actually outputs the Amplitude of the wave tone at any moment. This WILL NOT give a clean "on-off" pattern when fed morse code.

    The more I think about it, you probably need to use something like Fourier Analysis on the signal, to map out the on-off tone pattern. A nice article on this is at Analyzing wave files with Perl which uses PDL and PDL::FFT to do it.


    I'm not really a human, but I play one on earth. Cogito ergo sum a bum
Re: Morse code via sound card input
by rcaputo (Chaplain) on Jul 09, 2007 at 06:04 UTC

    I've done this in C in DOS before. Pulling tones out of shortwave noise isn't easy. I eventually used an integer FFT to isolate certain frequencies from the static. That worked pretty well.

    If you just need to know when a local key is down, you might have an easier time using a signal line from a spare serial port, or one of the data lines on a parallel port. This would eliminate the DSP issues. All you'd need is the right connector and some wire. The ports even provide their own voltage.

Re: Morse code via sound card input
by ww (Archbishop) on Jul 09, 2007 at 14:30 UTC
    With a bow to GrandFather and Zentara (above):

    Re the definitions, from http://www.journal.au.edu/ijcim/jan99/ijcim_ar1.html

    "A tone ratio (dot to dash) has to be 1:3 according to the definition of Morse code. That means if the duration of a dash (sic. I think this should be dih or dit or dot. ww) is taken to be one unit, then that of a dash is three units. In addition, the silent ratio (dot-space : character-space : word-space) has to be 1:3:7. In another words, the space between the components of one character is one unit, between characters is three units and between words is seven units."

    Another way of looking at the ratios is expounded at

    http://www.arsqrp.com/ars/pages/back_issues/2000_text/1200_text/morse.html

    where the author begins with this background:

    "There is a clock running . . . tick tick tick . . . with a constant time between each tick (for a given fixed Morse speed)."

    and proceeds to assert:

    "Definition: WEIGHT is the percent of time a signal is found in a timeslot.

    "Definition: A DOT is a timeslot filled 100% by a signal/key down/Mark.

    ....

    "Theorem (Truth): The WEIGHT of a DOT is 100%.

    "Definition: A SPACE is a 100% empty timeslot.

    "Let's solve the mess with these definitions:

    "Definition: A DIT is a DOT followed immediately by a SPACE.

    "Theorem (Truth): The duration of a DIT is two timeslots.

    "Definition: A DAH is a DASH followed immediately by a SPACE.

    "Theorem (Truth): The duration of a DAH is four timeslots.

    "The truth of all this lies in the fact that we do NOT learn the Morse Code in DOTs and DASHes, rather we learn it using DITs and DAHs! We MUST have the trailing SPACE at the end of DOTs and DASHes to distinguish one in time from the other. Built in spacing!"

    As both Zentara and Grandfather noted (above), the keying of an individual character or space may vary with physical condition, the sender's "sense of rhythm" and code skills, the inherent difficulty of particular characters (think about trying to type "~!" which -- at least on a US standard keyboard requires what I find to be an awkward shift of the left little finger to the left-shift-key and of the ring-finger [which is substituting for the little finger], first to the tilde and then to the bang), and "over time" in addition to what are likely many other factors.

    That's certainly going to make recognizing manual keying difficult. One -- very complicated -- solution might involve creating a hash of character sequences with values for some empirically-identified, typical-variances from "standard Morse." Alternately (and this is merely lurking in some corner of my brain that I can't quite surface), might it not be possible to use modulus division, testing for each of the ratios above, and from the value of the remainder, make some pretty good guesses about the sender's intentions?

    However, the OP's case ("I want to write a perl script that can interpret morse code from the sound card's mic input (so the script can be used with a keyer)" (emphasis supplied) appears simpler. Were one to try to extend this for broacast reception -- that is, the Morse found in the wild -- one may wish to note that very few HAM operators use traditional hand keying any more; rather, even if they use a traditional key (the OP's case) they also use automation to convert the input of the keying devices to output CW that more nearly matches the Morse spec.

    And in the case of machine-generated Morse (think weather, navaids, and bulk commercial comms) the translation program would not have to deal with the detractors induced by hand keying; at least, not significantly over a short (values less than a few minutes, I would think) period. In that case, the chief preliminary work of a translation program would be identifying the duration of the dit, the basic unit for all the work that follows.

    Caveat: Farnsworth Morse, widely used, "is composed of higher speed characters sent with longer than standard spacing between them."

    http://www.nzart.org.nz/nzart/Exam/AMATEUR%20RADIO%20STUDY%20GUIDE%2007A/Course%20Files/MORSE%20CODE.htm

    Also cf Ming-Che Hsieh; Ching-Hsing Luo re "an interactive training approach implemented on a single-chip microcomputer with immediate visual and auditory feedback...." at

    (unfortunately, $$$) http://www.informaworld.com/smpp/content~content=a714043328~db=all

    ... and Ming-Che Hsieh & Ching-Hsing Luo re "Automatic Morse code recognition with adaptive variable-ratiothreshold prediction" at

    http://ieeexplore.ieee.org/Xplore/login.jsp?url=/iel5/6513/17397/00802743.pdf?arnumber=802743

    "In this study, an Adaptive Variable-Ratio Threshold Prediction (AVRTP) algorithm is proposed to analyze the Morse code time series with variable unit time period and ratio. Two least-mean-square (LMS) predictors are applied to trace the dot interval and the dot-dash difference concurrently, and then a predicted threshold based on a variable-ratio decision rule is used to distinguish between dots and dashes. The same method is also applied to identify the character-space..."

    So, achieving the OP's objective will definitely require a non-trival script, it does NOT seem out of the category "do-able with intermediate Perl skills."

    Update: un-linkified a bracketed remark inside parens. Duh.
      Yeah, I remember I never got my HAM license back in the day because of the Morse requirement( which is no longer needed). The human ear can use some sort of "Quantuum Superposition" on the sound, and filter it to clarity. The listener has clues when a poorly sent letter is received, like does it fit into the word correctly, does it make sense, etc? The listener "knows" what to expect, and what is "reasonable". This is all tough to do with computer logic.... computers are fast, but they are not reasonable. :-)

      I'm not really a human, but I play one on earth. Cogito ergo sum a bum
Re: Morse code via sound card input
by zentara (Cardinal) on Jul 09, 2007 at 16:48 UTC
    I just did some playing with the PDL::FFT method (which I mentioned above by toma). It's only drawback, is it needs wav files to analyze.... no realtime reading from the dsp (unless maybe a chunk method of reading the dsp could be done?). I used a mechanical generator (Perl) to make a wav file 'A A A'. That is a di dah pause di dah pause di dah. The tone frequency was 400Hz. The resultant output follows below, it is a timestamp followed by frequency. As you can see, the 400 Hz shows up nicely, and the inter-di-dah slience is 105. The silence between letters can seen as 8.23. Now I can't say whether this is related to my particular soundcard, or what or non-standard conditions are in place, but it does show a method for doing the FFT on the audio..... the big glitch now is reading it off off the dsp directly. FWIW, I did see a hardwired unit that did this for $89 dollars on the internet. Maybe it's easier to do which real electronics?

    I'm not really a human, but I play one on earth. Cogito ergo sum a bum

      Using an FFT is probably counter productive. One cycle of 400 Hz is 2.5 ms so your 5 ms "tone" comprises just two cycles! It's computationally much easier to time "zero" crossings (with a modest amount of hysteresis) and that is likely to work pretty well for fairly clean signals. By the time you have to start digging into the noise to retrieve the signal things get a whole lot trickier.

      Note that a 5 ms dot time represents 240 words per minute. More achievable speeds are less than 1/10th of that. For test purposes around 5 words per minute with a Farnsworth speed of around 12 wpm is fairly typical.


      DWIM is Perl's answer to Gödel
        It's computationally much easier to time "zero" crossings

        Well actually I did use that method from toma's Freqext.pm module at analyzing wavs with Perl under the section "Tracking Frequency by Finding Zero Crossings". I may have mislead you above where I said I used FFT.


        I'm not really a human, but I play one on earth. Cogito ergo sum a bum
      So, if I read this correctly, starting at the top, we have

      a dit of five-plus miliseconds of 400Hz (0.00 through 0.05),
      followed by an intercharacter break of eight-plus ms of 105.26Hz (audible, but unlikely to be confused with 400Hz),
      followed by a dah of something like 18 ms of 400Hz,
      followed by a pause of 109 ms at 8.23 Hz (inaudible even to dogs, I think),
      ... and so on.

      The 5+ to 18 is close for the 3:1 dit:dah ratio. the inter-character break of 8 ms doesn't fit so well; and the pause (inter-word break?) is not even close to the 1:7... but maybe it wasn't intended to be. But the next "A" fits less well.

      So, if typical CW is like your sample, my theories go up in smoke ...which HAS happened before :-).

Re: Morse code via sound card input
by zentara (Cardinal) on Jul 10, 2007 at 13:02 UTC
    The code I used to generate the Morse is below: (I got it on the net, the sox line is to convert to unsigned wav for the Audio::Wav object in the Freqext module of toma)... it may have flaws as you point out :-) Also I did some sprintf rounding to make the timestamp and frequencies more readable.... the timestamps are actually more accurate.

    But as I was trying to think of ways to overcome all these obstacles, the thought occurred.... why even use Morse? Frequency Shift Keying (30 different frequencies, separated by 250 Hz), with each freq assigned a letter or punctuation mark, would be so much more reliable, faster, and easier to decode with digital audio analysis.

    I guess old Morse still has its uses though.


    I'm not really a human, but I play one on earth. Cogito ergo sum a bum
Re: Morse code via sound card input
by Anonymous Monk on Nov 28, 2007 at 15:31 UTC
    This is a Sample text