Tree based Morse Code decoder
Posted: Wed Oct 30, 2013 11:13 am
Hi,
If you have an interest in decoding Morse code here are some Procedures() snipped from some code I wrote.
I've also added a simple exerciser to show how it works. Compile it in Debug mode.
The tree based decoder has some advantages:
1 - There is no need to carry out a linear search when an end-of-character space is detected.
2 - It lends itself to decoding multiple input streams.
In a real-time decoder the characters would not all come in at the same time
In a multi channel application the variable *P would be one of an array from Dim *P(NumOfChans) and the channel number at any time probably derived from an FFT bin number, or group thereof.
73
RichardL (G8CDD)
If you have an interest in decoding Morse code here are some Procedures() snipped from some code I wrote.
I've also added a simple exerciser to show how it works. Compile it in Debug mode.
The tree based decoder has some advantages:
1 - There is no need to carry out a linear search when an end-of-character space is detected.
2 - It lends itself to decoding multiple input streams.
In a real-time decoder the characters would not all come in at the same time

In a multi channel application the variable *P would be one of an array from Dim *P(NumOfChans) and the channel number at any time probably derived from an FFT bin number, or group thereof.
73
RichardL (G8CDD)
Code: Select all
; This snip illustrates a method of decoding incoming morse code as symbols
; (Dits and Dahs) are received.
; (C)Richard Leman 2013
; Free to use at your own risk.
;{ Useful docs
; http://www.giangrandi.ch/electronics/radio/morse/morse.html
; http://www.learnmorsecode.com/
;}
Procedure Morse_BuildTree() ; Make a tree structured morse character table
; Each node has the address' of the nodes at the next level down and the character
; if this happens to be the last symbol.
; Returns the address of the entry node for use by decoders.
Protected *Table,CharCount,m,n,*P, AddMorse$,AddChar$
;{- Morse code table
DataSection
CodeList:
Data.s "a", ".-"
Data.s "b", "-..."
Data.s "c", "-.-."
Data.s "d", "-.."
Data.s "e", "."
Data.s "f", "..-."
Data.s "g", "--."
Data.s "h", "...."
Data.s "i", ".."
Data.s "j", ".---"
Data.s "k", "-.-"
Data.s "l", ".-.."
Data.s "m", "--"
Data.s "n", "-."
Data.s "o", "---"
Data.s "p", ".--."
Data.s "q", "--.-"
Data.s "r", ".-."
Data.s "s", "..."
Data.s "t", "-"
Data.s "u", "..-"
Data.s "v", "...-"
Data.s "w", ".--"
Data.s "x", "-..-"
Data.s "y", "-.--"
Data.s "z", "--.."
Data.s "0", "-----"
Data.s "1", ".----"
Data.s "2", "..---"
Data.s "3", "...--"
Data.s "4", "....-"
Data.s "5", "....."
Data.s "6", "-...."
Data.s "7", "--..."
Data.s "8", "---.."
Data.s "9", "----."
Data.s ".", ".-.-.-"
Data.s ",", "--..--"
Data.s "?", "..--.."
Data.s "'", ".----."
Data.s "!", "-.-.--"
Data.s "/", "-..-."
Data.s "(", "-.--."
Data.s ")", "-.--.-"
Data.s "&", ".-..."
Data.s ":", "---..."
Data.s ";", "-.-.-."
Data.s "=", "-...-"
Data.s "+", ".-.-."
Data.s "-", "-....-"
Data.s "_", "..--.-"
Data.s #DQUOTE$,".-..-."
Data.s "$", "...-..-"
Data.s "@", ".--.-."
EndDataSection
Debug "Number of characters = 54"
CharCount = 54
;}
Structure MORSE_NODE
DitBranch.i
DahBranch.i
NormLen.i
Char.s
EndStructure
Debug "Size of MORSE_NODE = " + Str(SizeOf(MORSE_NODE))
;- Memory for tree table
*Table = AllocateMemory((CharCount+12) * SizeOf(MORSE_NODE))
;- Create start node and first pair of sub-nodes
*MorseDat.MORSE_NODE = *Table
*MorseDat\DitBranch = *Table + SizeOf(MORSE_NODE)
*MorseDat\DahBranch = *Table + (2*SizeOf(MORSE_NODE))
*Empty = *Table + (3*SizeOf(MORSE_NODE)); Pointer to space for remaining nodes
;- Build tree
Restore CodeList
For m = 1 To CharCount
Read.s AddChar$ : Read.s AddMorse$
Debug ""
Debug Str(m)+" " + AddChar$+" "+AddMorse$+" ("+Str(Len(AddMorse$))+ " Symbols)"
*P = *Table ; Entry point to table
For n = 1 To Len(AddMorse$)
*MorseDat.MORSE_NODE = *P
With *MorseDat
Select Mid(AddMorse$,n,1)
Case "-" ; Got a DAH...
If \DahBranch = 0 ; If no DAH branch...
\DahBranch = *Empty ; use an Empty node...
*Empty + SizeOf(MORSE_NODE) ; make a new empty node for later
EndIf
*P = \DahBranch
Debug "DAH "+Str(*P-*Table)
Case "." ; Got a DIT...
If \DitBranch = 0
\DitBranch = *Empty
*Empty + SizeOf(MORSE_NODE)
EndIf
*P = \DitBranch
Debug "DIT "+Str(*P-*Table)
EndSelect
EndWith
Next
*MorseDat.MORSE_NODE = *P
*MorseDat\Char = AddChar$
Next
ProcedureReturn *Table
EndProcedure
Procedure Morse_GotDit(*K) ; A DIT has been received, move down the tree from current tree position
*MorseDat.MORSE_NODE = *K
*K = *MorseDat\DitBranch
ProcedureReturn *K
EndProcedure
Procedure Morse_GotDah(*K) ; A DAH has been received, move down the tree from current tree position
*MorseDat.MORSE_NODE = *K
*K = *MorseDat\DahBranch
ProcedureReturn *K
EndProcedure
Procedure.s Morse_GetChar(*K) ; An end of char space was detected, get the character at the current tree position
*MorseDat.MORSE_NODE = *K
ProcedureReturn *MorseDat\Char
EndProcedure
; Demo
*MorseTable = Morse_BuildTree() ; Build table
*P = *MorseTable ; Init pointer
Repeat
k$ = InputRequester("Morse entry","Type a string of Dits and Dahs ('.' and '-')","")
If k$ = "" : Break : EndIf
For n = 1 To Len(k$) ; For each symbol...
Select Mid(k$,n,1)
Case "." : *P=Morse_GotDit(*P) ; It is a '.' so move down the table.
Case "-" : *P=Morse_GotDah(*P) ; It is a '-' so move down teh table.
Default : *P = 0 : Break ; Typo in input.
EndSelect
Next
If *P
Debug Morse_GetChar(*P)
EndIf
*P = *MorseTable ; Re-init pointer ready for next symbol/char
ForEver