Morse code transceiver

Share your advanced PureBasic knowledge/code with the community.
User avatar
Joakim Christiansen
Addict
Addict
Posts: 2452
Joined: Wed Dec 22, 2004 4:12 pm
Location: Norway
Contact:

Morse code transceiver

Post by Joakim Christiansen »

Called transceiver since it can both send and decode morse code signals.
I will share more code on how to use the decoder later, but I have tested it with my microphone and it seem to work well, I just have to clean up that code first...

Code: Select all

;Morse code transceiver by Joakim L. Christiansen a.k.a JLC
;v1.0 coded in PB v5.20
;Morse code reference:
;http://en.wikipedia.org/wiki/Morse_code
;http://morsecode.scphillips.com/morse2.html

EnableExplicit

Global NewMap morseCode$(), format.WAVEFORMATEX

Procedure setWaveFormat(bitsPerSample=16,samplesPerSec=44100)
  format\wFormatTag = #WAVE_FORMAT_PCM
  format\nChannels = 1 ;mono
  format\wBitsPerSample = bitsPerSample ;8/16
  format\nSamplesPerSec =  samplesPerSec ;8000 Hz, 11025 Hz, 22050 Hz, and 44100 Hz
  format\nBlockAlign = (format\nChannels * format\wBitsPerSample) / 8
  format\nAvgBytesPerSec = format\nSamplesPerSec * format\nBlockAlign
EndProcedure
Procedure addWaveHeader(*address,dataSize,channels,samplesPerSec,blockAlign,bitsPerSample)
  PokeL(*address+ 0, 'FFIR')
  PokeL(*address+ 4, dataSize + 36)
  PokeL(*address+ 8, 'EVAW')
  PokeL(*address+ 12, ' tmf')
  PokeL(*address+ 16, $0010)
  PokeW(*address+ 20, $01)
  PokeW(*address+ 22, channels)
  PokeL(*address+ 24, samplesPerSec)
  PokeL(*address+ 28, samplesPerSec * channels * (bitsPerSample / 8) )
  PokeW(*address+ 32, blockAlign)
  PokeW(*address+ 34, bitsPerSample)
  PokeL(*address+ 36, 'atad')
  PokeL(*address+ 40, dataSize)
EndProcedure
Procedure.l createTone(*address,amplitude,duration,frequency)
  Protected multiplier.f, angle.f, sample, x, size, numSamples
  
  size = Int((format\nSamplesPerSec/1000)*duration) * (format\wBitsPerSample / 8)
  If *address = 0 ;return size needed
    ProcedureReturn size
  EndIf
  numSamples = size / 2
  
  multiplier = (360 / format\nSamplesPerSec)  * frequency
  For x=0 To numSamples-1
    angle + multiplier
    sample = Sin(Radian(angle))*amplitude
    PokeW(*address+(x*2),sample)
  Next
EndProcedure
Procedure catchTone(volume,duration,frequency)
  Protected *memory, id, size
  size = createTone(0,volume,duration,frequency) ;get size
  *memory = AllocateMemory(44+size)
  createTone(*memory+44,volume,duration,frequency)
  addWaveHeader(*memory,size,format\nChannels,format\nSamplesPerSec,format\nBlockAlign,format\wBitsPerSample)
  id = CatchSound(#PB_Any,*memory)
  FreeMemory(*memory)
  ProcedureReturn id
EndProcedure
Procedure initializeMorsecode() ;fills the map
  morseCode$("a") = ".-"
  morseCode$("b") = "-..."
  morseCode$("c") = "-.-."
  morseCode$("d") = "-.."
  morseCode$("e") = "."
  morseCode$("f") = "..-."
  morseCode$("g") = "--."
  morseCode$("h") = "...."
  morseCode$("i") = ".."
  morseCode$("j") = ".---"
  morseCode$("k") = "-.-"
  morseCode$("l") = ".-.."
  morseCode$("m") = "--"
  morseCode$("n") = "-."
  morseCode$("o") = "---"
  morseCode$("p") = ".--."
  morseCode$("q") = "--.-"
  morseCode$("r") = ".-."
  morseCode$("s") = "..."
  morseCode$("t") = "-"
  morseCode$("u") = "..-"
  morseCode$("v") = "...-"
  morseCode$("w") = ".--"
  morseCode$("x") = "-..-"
  morseCode$("y") = "-.--"
  morseCode$("z") = "--.."
  morseCode$("0") = "-----"
  morseCode$("1") = ".----"
  morseCode$("2") = "..---"
  morseCode$("3") = "...--"
  morseCode$("4") = "....-"
  morseCode$("5") = "....."
  morseCode$("6") = "-...."
  morseCode$("7") = "--..."
  morseCode$("8") = "---.."
  morseCode$("9") = "----."
  morseCode$(".") = ".-.-.-"
  morseCode$(",") = "--..--"
  morseCode$("?") = "..--.."
  morseCode$("'") = ".----."
  morseCode$("!") = "-.-.--"
  morseCode$("/") = "-..-."
  morseCode$("(") = "-.--."
  morseCode$(")") = "-.--.-"
  morseCode$("&") = ".-..."
  morseCode$(":") = "---..."
  morseCode$(";") = "-.-.-."
  morseCode$("=") = "-...-"
  morseCode$("+") = ".-.-."
  morseCode$("-") = "-....-"
  morseCode$("_") = "..--.-"
  morseCode$(#DQUOTE$) = ".-..-."
  morseCode$("$") = "...-..-"
  morseCode$("@") = ".--.-."
  ;non English extensions (not complete)
  morseCode$("æ") = ".-.-" ;Ä
  morseCode$("ø") = "---." ;Ö
  morseCode$("å") = ".--.-";Á
EndProcedure
Procedure.s textToMorse(string$)
  Protected i, result$, char$
  For i=1 To Len(string$)
    char$ = LCase(Mid(string$,i,1))
    Select char$
      Case " "
        result$ + "       "
      Default
        If FindMapElement(morseCode$(),char$)
          result$ + morseCode$() + "   "
        Else
          result$ = "This character could not be translated: "+char$: Break
        EndIf
     EndSelect
  Next
  ProcedureReturn result$
EndProcedure
Procedure differenseInPercentage(value,nominalValue)
  ProcedureReturn Abs((value/nominalValue)*100-100)
EndProcedure
Procedure.s decodeSignal(signal,allowedDifferense=60) ;either high or low, it keeps track of time itself
  ;the allowedDifferense variable defines how inaccurate (in percentage) the pulse widths are allowed to be
  ;recommended values are 60-70 but 50-100 could also work
  Static pSignal, pPulseWidth, pulseStart, offStart, shortPulse, longPulse
  Protected ms.q, pulseWidth, offWidth, result$
  
  CompilerIf #PB_Compiler_OS = #PB_OS_Windows
    ;ms = ElapsedMilliseconds() ;not accurate enough for very fast morse code
    QueryPerformanceCounter_(@ms) ;hence this
  CompilerElse ;for Linux or Mac
    ms = ElapsedMilliseconds() ;please replace this with something like "elapsedMicroseconds()"
  CompilerEndIf

  
  If signal = 1
    If pSignal = 0 ;if changed since last call
      pulseStart = ms
      offWidth = ms-offStart
      If shortPulse <> 0 ;must first detect short pulse width
        If differenseInPercentage(offWidth,shortPulse) < allowedDifferense ;if the pulse is within range of a short pulse
          result$ = ""
        ElseIf differenseInPercentage(offWidth,longPulse) < allowedDifferense
          result$ = "   "
        ElseIf differenseInPercentage(offWidth,longPulse) > allowedDifferense
          result$ = "       "
        EndIf
      EndIf
    EndIf
  Else
    If pSignal = 1
      pulseWidth = ms-pulseStart
      offStart = ms
      If pPulseWidth <> 0 ;must first have two pulses to compare
        If differenseInPercentage(pulseWidth,pPulseWidth) > allowedDifferense ;if the pulse width is much different from the last one
          If pulseWidth > pPulseWidth
            shortPulse = pPulseWidth
            longPulse  = pulseWidth
          Else
            shortPulse = pulseWidth
            longPulse  = pPulseWidth
          EndIf
          Debug Str(shortPulse)+" - "+Str(longPulse)
        EndIf
        If differenseInPercentage(pulseWidth,shortPulse) < allowedDifferense ;if the pulse is within range of a short pulse
          result$ = "."
        ElseIf differenseInPercentage(pulseWidth,longPulse) < allowedDifferense
          result$ = "-"
        EndIf
      EndIf
      pPulseWidth = pulseWidth
    EndIf
  EndIf
  pSignal = signal
  ProcedureReturn result$
EndProcedure
Procedure playMorse(morse$,speed)
  Protected i, char$, nextChar$
  Protected snd_short,snd_long;, decoded$
  snd_short = catchTone(20000,speed,700)
  snd_long  = catchTone(20000,speed*3,700)
  For i=1 To Len(morse$)
    char$ = LCase(Mid(morse$,i,1))
    nextChar$ = Mid(morse$,i+1,1)
    Select char$
      Case "."
        ;decoded$ + decodeSignal(1)
        PlaySound(snd_short)
        Delay(speed)
        ;decoded$ + decodeSignal(0)
      Case "-"
        ;decoded$ + decodeSignal(1)
        PlaySound(snd_long)
        Delay(speed*3)
        ;decoded$ + decodeSignal(0)
      Case " "
        Delay(speed)
    EndSelect
    If char$ <> " " And nextChar$ <> " "
      Delay(speed)
    EndIf
  Next
  ;Debug decoded$
EndProcedure
Procedure.s morseToText(morse$)
  Protected result$, i, morseCode$, spaceCount, lastSpacePos
  For i=1 To Len(morse$)
    Select Mid(morse$,i,1)
      Case ".","-"
        spaceCount = 0
      Case " "
        spaceCount + 1
        morseCode$ = Mid(morse$,lastSpacePos+1,i-lastSpacePos-1)
        lastSpacePos = i
        If Len(morseCode$) > 0
          ForEach morseCode$()
            If morseCode$ = morseCode$();CompareMemoryString(@morseCode$,@morseCode$()) = #PB_String_Equal
              result$ + MapKey(morseCode$()): Break
            EndIf
          Next
        EndIf
        If spaceCount > 5
          spaceCount = 0
          result$ + " "
        EndIf
      Default
        result$ = "Error in parsed morse code!": Break
    EndSelect
  Next
  ProcedureReturn result$
EndProcedure

If 1
  InitSound()
  setWaveFormat(16,44100)
  initializeMorsecode()
  Define text$, morse$
  text$ = "hello morse code world"
  morse$ = textToMorse(text$)
  Debug morse$
  Debug morseToText(morse$)
  playMorse(morse$,100)
EndIf
I like logic, hence I dislike humans but love computers.
User avatar
electrochrisso
Addict
Addict
Posts: 989
Joined: Mon May 14, 2007 2:13 am
Location: Darling River

Re: Morse code transceiver

Post by electrochrisso »

Good stuff, and thanks for sharing. 8)
I will keep an eye on this post, Morse code used to be my second language, I am a bit rusty on it now though, it will be intersting to have a go at decoding some automatic Morse code from my HF receiver, something I have always wanted to do, but never got around to it. :)
I think FangBeast will be interested too. :)
PureBasic! Purely the best 8)
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5494
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Morse code transceiver

Post by Kwai chang caine »

I did not have the "chance" to learn morse in my forced year of french army.
My brother has done it, all his year, and say to me it's very difficult..
So thanks for sharing, this code can help a day, we never know 8)
ImageThe happiness is a road...
Not a destination
User avatar
electrochrisso
Addict
Addict
Posts: 989
Joined: Mon May 14, 2007 2:13 am
Location: Darling River

Re: Morse code transceiver

Post by electrochrisso »

Kwaï chang caïne wrote:I did not have the "chance" to learn morse in my forced year of french army.
My brother has done it, all his year, and say to me it's very difficult..
So thanks for sharing, this code can help a day, we never know 8)
Interesting to hear about your brother learning Morse code KCC, I learned it in the Australian army, had to learn to read it at up to 35wpm, very hard at that speed, lucky to be able to read at 5wpm these days. I also had to learn touch typing and type the encrypted, automated Morse code messages, not sure if it is still done this way anymore, probably all computerized these days. :)
When the web goes down in a big way one day, we may need to go back to using radio and Morse code again. :lol:
PureBasic! Purely the best 8)
User avatar
Fangbeast
PureBasic Protozoa
PureBasic Protozoa
Posts: 4790
Joined: Fri Apr 25, 2003 3:08 pm
Location: Not Sydney!!! (Bad water, no goats)

Re: Morse code transceiver

Post by Fangbeast »

I think FangBeast will be interested too. :)
I have various programs to decode morse from my HF rig and have an 80 Meter dipole up to play with but without a doubt, most of those programs are huge monsters.

I feed everything through a SignalLink USb sound card but the chip support for the CT62 com to USB is broken under win 8.1 and I am having trouble getting it to be stable without literally crashing everything.

Might have to replace the com cable first and then try this code.

Tell you, if I didn't have 2 meters permanently running, I might go mad with the conditions HF is running at this moment.
Amateur Radio/VK3HAF, (D-STAR/DMR and more), Arduino, ESP32, Coding, Crochet
Ulix
User
User
Posts: 48
Joined: Wed Jan 23, 2008 12:45 pm
Location: France, Montpellier

Re: Morse code transceiver

Post by Ulix »

Hi to all!

Interesting as a program, who knows if day ...

Waiting does not work under linux is not cross-platform! damage :twisted:

(What is the equivalence of the structure WAVEFORMATEX Linux?)


A + :?

Ulix
User avatar
Demivec
Addict
Addict
Posts: 4270
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: Morse code transceiver

Post by Demivec »

Ulix wrote:(What is the equivalence of the structure WAVEFORMATEX Linux?)

Code: Select all

Structure WAVEFORMATEX
  wFormatTag.u
  nChannels.u
  nSamplesPerSec.l
  nAvgBytesPerSec.l
  nBlockAlign.u
  wBitsPerSample.u
  cbSize.u
EndStructure
User avatar
Joakim Christiansen
Addict
Addict
Posts: 2452
Joined: Wed Dec 22, 2004 4:12 pm
Location: Norway
Contact:

Re: Morse code transceiver

Post by Joakim Christiansen »

Demivec wrote:
Ulix wrote:(What is the equivalence of the structure WAVEFORMATEX Linux?)

Code: Select all

Structure WAVEFORMATEX
  wFormatTag.u
  nChannels.u
  nSamplesPerSec.l
  nAvgBytesPerSec.l
  nBlockAlign.u
  wBitsPerSample.u
  cbSize.u
EndStructure
Oh, sorry, I thought that the PureBasic compiler would include that structure :shock:

About playing around with radio equipment btw, that was what inspired me to make this! I have recently gotten myself a super cheap software defined radio (SDR) from here:
http://dx.com/p/rtl2832u-r820t-mini-dvb ... ver-218228
Use that with a better antenna and the right software and you'll get yourself a new hobby!
This cheap equipment is currently taking the world by storm and there is a dedicated blog for it:
http://www.rtl-sdr.com <- Check it out!

I haven't been able to get good result with decoding from radio yet since I must first find a way to filter out the noise, but I'm on it.
I like logic, hence I dislike humans but love computers.
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5494
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Morse code transceiver

Post by Kwai chang caine »

electrochrisso wrote:we may need to go back to using radio and Morse code again.
Try to remember your old lessons, we never know :wink:
ImageThe happiness is a road...
Not a destination
dell_jockey
Enthusiast
Enthusiast
Posts: 767
Joined: Sat Jan 24, 2004 6:56 pm

Re: Morse code transceiver

Post by dell_jockey »

Is morse-code still used these days? I used to be a ham-radio operator (VHF/UHF) and know for a fact that morse code as a requirement for SW ham-radio operator licensing (as well as licensing professional services on SW) was dropped some ten years ago?
cheers,
dell_jockey
________
http://blog.forex-trading-ideas.com
User avatar
Fangbeast
PureBasic Protozoa
PureBasic Protozoa
Posts: 4790
Joined: Fri Apr 25, 2003 3:08 pm
Location: Not Sydney!!! (Bad water, no goats)

Re: Morse code transceiver

Post by Fangbeast »

dell_jockey wrote:Is morse-code still used these days? I used to be a ham-radio operator (VHF/UHF) and know for a fact that morse code as a requirement for SW ham-radio operator licensing (as well as licensing professional services on SW) was dropped some ten years ago?
Not a requirement in Australia. It was dropped here and the exams simplified because Amateur Radio in this country was dying as a hobby and very, very expensive to get good gear in this country.

Don't know about the other countries.

@ElectroChrisso..I solved the CAT cable problem under Windows 8.1, now I can test this code, play with HRD and DigiPan and others. Yay!
Amateur Radio/VK3HAF, (D-STAR/DMR and more), Arduino, ESP32, Coding, Crochet
User avatar
utopiomania
Addict
Addict
Posts: 1655
Joined: Tue May 10, 2005 10:00 pm
Location: Norway

Re: Morse code transceiver

Post by utopiomania »

Cool, like! :D CQD Come Quick Danger.. — · — · — — · — — · · :)

Titanic.. Just bought a 6m antenna, CB am/fm radio and afterburner.. nick is convoy
in a a wek or two.. :)
User avatar
electrochrisso
Addict
Addict
Posts: 989
Joined: Mon May 14, 2007 2:13 am
Location: Darling River

Re: Morse code transceiver

Post by electrochrisso »

Fangbeast wrote:@ElectroChrisso..I solved the CAT cable problem under Windows 8.1, now I can test this code, play with HRD and DigiPan and others. Yay!
--. --- --- -.. | --- -. . | ..-. .- -. --. :)
PureBasic! Purely the best 8)
RichardL
Enthusiast
Enthusiast
Posts: 532
Joined: Sat Sep 11, 2004 11:54 am
Location: UK

Re: Morse code transceiver

Post by RichardL »

RichardL
Enthusiast
Enthusiast
Posts: 532
Joined: Sat Sep 11, 2004 11:54 am
Location: UK

Re: Morse code transceiver

Post by RichardL »

Hi Joakim,

http://www.g4fon.net/CW%20Trainer.htm

This is a rather useful Morse code tutor program that works at speeds up to 80 Words Per Minute.
The CW enthusiasts at my local amateur radio society think it is the best.

I have been working on a tree based decoder in Pure Basic and it now receives with almost 100% accuracy at 70 WPM, that's more than good enough. :D

73
Richard
Post Reply