Das Thema von rollod hatte mich an meine frühjugendlichen Erfahrungen mit dem Morsen erinnert (schon weit über 40 Jahre her). Also habe ich mich mal rangesetzt und eine UDF erstellt, die geeignet ist um sowohl das Geben als auch das Hören zu trainieren. Frei nach Hans Grade heißt es beim Morsen: "Lerne Hören, dann kannst du geben." - Ich weiß noch, wie wir die ersten Monate immer nur die Kopfhörer auf hatten und mit dem Bleistift dann die empfangenen Dit's und Dah's notiert haben. Dabei wollten wir doch endlich mal die Morsetaste bedienen.
Übrigens: Die Computermaus ist nicht optimal geeignet zum Morsen. Beim Geben soll der Geber sich vor (und nicht unter) der Hand befinden. Ist aber sicher nur für Hardcorefunker von entscheidender Bedeutung. In der UDF wird aber als Gebertaste "rechte CTRL-Taste" verwendet.
• Mit der UDF kann ich eine Gui zur Entgegennahme der Morsezeichen registrieren. Nur wenn diese aktiv ist, wird R-CTRL ausgewertet.
• Es lassen sich Ctrl (Input/Edit) zum Anzeigen der erkannten Zeichen und zum Anzeigen des aus den Zeichen dekodierten Textes registrieren.
• Weiterhin kann ein Ctrl (Edit empfohlen) als Log registriert werden. Hier werden die erkannten DOWN-Time und UP-Time der Gebertaste angezeigt. Überschreitet die UP-Time 7 Basiseinheiten (> Wortpause), wird [UP IDLE] angezeigt.
• Text kann in Morsezeichen konvertiert werden.
• Eine Zeichenfolge von Morsezeichen kann akustisch (Beep-Funktion) ausgegeben werden, wahlweise mit zusätzlicher Konsolenausgabe jedes dekodierten Zeichens. Die Basiseinheit bei Beep-Ausgabe ist auf 120 ms festgelegt. Das ist relativ lang, aber wenn man das verkürzt, kommt die Audioausgabe nicht mehr mit.
Natürlich wäre es schick, wenn man beim Geben auch einen Beep hören könnte. Aber das übersteigt die Fähigkeiten eines PC. Beep ist eine Funktion mit definierter Zeit, somit könnte ich erst mit abgeschlossenem Tastendruck die DOWN-Time für Beep verwenden, hätte also immer ein Delay in der Länge des zuletzt gegebenen Zeichens. Wie ein Film mit schlecht synchronisierter Tonspur.
Und nacheinander Beeps mit kurzem Intervall auszuführen solange die Taste gedrückt ist, ist nur solange eine gute Idee, bis man das Ergebnis zum ersten mal gehört hat.
In der UDF setze ich die Keyboardeinstellungen auf max. Delay und min. Wiederholfrequenz (wird beim Beenden zurückgesetzt). Das ist nicht zwingend erforderlich, reduziert u.U. aber die Aufrufhäufigkeit der Callback-Funktion.
Ich habe ausgiebig kommentiert, ein Blick in den Code sollte somit eventuelle Fragen schon weitestgehend klären.
Übrigens ist es gar nicht so einfach mehrere Dah hintereinander einzugeben. "3-Units-Down/1-Unit-Up/3-Units-Down/1-Unit-Up/3-Units-Down/Up" - da braucht man schon ein gutes Zeitgefühl. Anfangs wird man meist die 1-Unit Sequenzpause überschreiten und schon wird die Eingabe als Zeichenpause gewertet und statt dem gewollten O steht da TTT.
- Die Punkte bzw. Striche, die jeweils ein Zeichen darstellen, werden durch eine Pause von einer Zeiteinheit getrennt.
- Die Pause zwischen Zeichensequenzen, die zwei verschiedene Wörter darstellen, beträgt 7 Zeiteinheiten.
Full stop (period) ............................................................................. [.] .−.−.−
Comma .......................................................................................... [,] −−..−−
Colon or division sign ......................................................................... [:] −−−...
Question mark (note of interrogation or request for repetition of a transmission not understood) [?] ..−−..
Apostrophe ..................................................................................... [’] .−−−−.
Hyphen or dash or subtraction sign ............................................................. [–] −....−
Fraction bar or division sign .................................................................. [/] −..−.
Left-hand bracket (parenthesis) ................................................................ [(] −.−−.
Right-hand bracket (parenthesis) ............................................................... [)] −.−−.−
Inverted commas (quotation marks) (before and after the words) ................................. ["] .−..−.
Double hyphen .................................................................................. [=] −...−
Understood ..................................................................................... VE ...−.
Error (eight dots) ............................................................................. HH ........
Cross or addition sign ......................................................................... [+] .−.−.
Invitation to transmit ......................................................................... K −.− (als Wort)
Wait ........................................................................................... AS .−...
End of work .................................................................................... SK ...−.−
Starting signal (to precede every transmission)................................................. CT −.−.−
Multiplication sign ............................................................................ X −..− (als Wort)
Commercial at .................................................................................. [@] .––.-.
Global $g_MorseBeepUnit = 120 ; Dauer einer Einheit in ms für die Audioausgabe - Langsamer ist nicht sinnvoll, da kommt die Ausgabe nicht hinterher
Global $g_MorseCharWB = ' ' ; Symbol für Wortpause (darf nicht dasselbe Zeichen, wie Zeichenpause sein!)
Global $g_bMorseIDLE = True ; Wenn keine neue Eingabe erfolgt, würde jede Pause der Länge 7 als Sequenz "Wortpause" gewertet.
['A','.-'],['B','-...'],['C','-.-.'],['D','-..'],['E','.'],['F','..-.'],['G','--.'],['H','....'],['I','..'],['J','.---'],['K','-.-'],['L','.-..'],['M','--'], _
['N','-.'],['O','---'],['P','.--.'],['Q','--.-'],['R','.-.'],['S','...'],['T','-'],['U','..-'],['V','...-'],['W','.--'],['X','-..-'],['Y','-.--'],['Z','--..'], _
['1','.----'],['2','..---'],['3','...--'],['4','....-'],['5','.....'],['6','-....'],['7','--...'],['8','---..'],['9','----.'],['0','-----'], _
['.','.-.-.-'],[',','--.--'],[':','---...'],['?','..--..'],["'",'.----.'],['-','-....-'],['/','-..-.'],['(','−.−−.'],[')','−.−−.−'],['"','.-..-.'],['=','-...-'], _
['VE','...-.'],['HH','........'],['+','.-.-.'],['AS','.-...'],['SK','...-.-'],['CT','-.-.-'],['@','.--.-.']]
Global $g_MorseDecIn = Null ; [optional] Ctrl, in das die aus den erkannten Morsesymbolen dekodierten Textzeichen eingetragen werden
Global $g_MorseLog = Null ; [optional] Ctrl, in das die Zeiten (DOWN-Time, UP-Time) protokolliert werden
Global $g_MorseRawBreakCount ; [optional] für mehrzeiliges Ctrl, Anzahl der Zeichen, nach denen ein Zeilenumbruch eingefügt wird
Global $g_MorseDecBreakCount ; [optional] für mehrzeiliges Ctrl, Anzahl der Zeichen, nach denen ein Zeilenumbruch eingefügt wird
Global $g_MorseLogCount ; [optional] für mehrzeiliges Ctrl, Anzahl der Aufrufe, nach denen ein Zeilenumbruch eingefügt wird
; Gui für Morse-UDF registrieren, Keyboard-Hook aktivieren, Adlib-Funktion zur Morse-Sequenz-Verarbeitung starten
_KeyboardSetting(3, 0) ; Delay auf 1s und Wiederholrate auf 2 Zeichen/s (nicht zwingend erforderlich)
$g_hHook = _WinAPI_SetWindowsHookEx($WH_KEYBOARD_LL, DllCallbackGetPtr($g_hKeyProc), _WinAPI_GetModuleHandle(0))
; $_iCharBreak: optional für mehrzeiliges Ctrl, Anzahl der Zeichen, nach denen ein Zeilenumbruch eingefügt wird
; $_iCharBreak: optional für mehrzeiliges Ctrl, Anzahl der Zeichen, nach denen ein Zeilenumbruch eingefügt wird
; $_iCallBreak: optional für mehrzeiliges Ctrl, Anzahl der Aufrufe, nach denen ein Zeilenumbruch eingefügt wird
If $iCount = $g_MorseLogCount Then ; $g_MorseLogCount/2 Tastendrücke (Down und Up) nebeneinander, dann neue Zeile
$_sMorse = StringReplace($_sMorse, $g_MorseCharWB, '^') ; Zeichen- u. Wortende durch Platzhalter mit 1 Zeichen Länge ersetzen ...
$_sMorse = StringReplace($_sMorse, $g_MorseCharCB, '°') ; ... um zeichenweises Verarbeiten zu gewährleisten
Beep($g_MorseF, $oLen.Item($aSplit[$i]) * $g_MorseBeepUnit) ; Beep in definierter Frequenz und Länge des Dit/Dah
If $i = $aSplit[0] Then ConsoleWrite($oMorseRecv.Item($sCh) & @CRLF) ; letztes Zeichen erreicht -> Ausgabe erkanntes Zeichen
If $aSplit[$i] = '^' Then ConsoleWrite(' ') ; Wortpause: Leerzeichen als Worttrenner (Text) ausgeben
Local Static $iUnit1_Max = 1.4 * $g_MorseUnit ; max. Dit-Länge und Pausenlänge zwischen zwei aufeinanderfolgenden Dit oder Dah
Local Static $iUnit3_Max = 1.4 * 3*$g_MorseUnit ; max Dah-Länge und Pausenlänge zwischen zwei aufeinanderfolgenden Zeichen
If ($nCode < 0) Or (Not BitAND(WinGetState($g_MorseGui), 8)) Then _ ; nicht verarbeiten, wenn registrierte Gui inaktiv
If $vkCode <> 163 Then Return _WinAPI_CallNextHookEx($g_hHook, $nCode, $wParam, $lParam) ; nur R-CTRL auswerten
__Morse_AddLog(StringFormat('[DIT: <= %i ms, DAH: %i ms bis %i ms, SEQ: < %i ms, CHAR: %i ms bis %i ms]\n', _
$g_MorseKeyTimeDown = TimerDiff($g_MorseKeyDownTimer) ; DOWN-Time (Taste gedrückt Zeit) messen und auswerten
; #FUNCTION# ====================================================================================================================
; Parameters ....: $_iDelay (Default = -1) Verzögerung bis bei gedrückter Taste die Zeichenwiederholung aktiviert wird
; ...............: $_iSpeed (Default = -1) Geschwindigkeit, mit der bei aktivierter Zeichenwiederholung das Zeichen wiederholt wird
; Der Wert für Numlock wird beim Abmelden des Users mit dem dann aktuellen Zustand der Taste überschrieben.
; Return values .: Struktur mit den aktuellen Keyboardeinstellungen: ".Delay", ".Speed", ".NumlockOn"
; ===============================================================================================================================
Local $iInitIndicator = RegRead("HKEY_CURRENT_USER\Control Panel\Keyboard", "InitialKeyboardIndicators")
RegWrite("HKEY_CURRENT_USER\Control Panel\Keyboard", "InitialKeyboardIndicators", "REG_SZ", $_iNumlockOnAtStartup)
_Morse_RegisterGui($g_hGui) ; Gui registrieren, nur wenn diese Gui aktiv ist, wird R-CTRL als Morsetaste ausgewertet
Global $inMorseReceived = GUICtrlCreateInput('', 10, 30, 680, 30, BitOR($ES_LEFT, $ES_AUTOHSCROLL, $ES_READONLY))
Global $inTextReceived = GUICtrlCreateInput('', 10, 95, 680, 30, BitOR($ES_LEFT, $ES_AUTOHSCROLL, $ES_READONLY))
_Morse_RegisterDecodeIn($inTextReceived) ; Input registrieren zur Ausgabe der aus den Morsezeichen dekodierten Textzeichen
GUICtrlCreateLabel('Erfasste Tastendruck- und Pausenlängen. MORSETASTE ist <STRG-RECHTS>.', 10, 145)
Global $edButtonReceived = GUICtrlCreateEdit('', 10, 160, 680, 200, BitOR($GUI_SS_DEFAULT_EDIT, $ES_READONLY))
_Morse_RegisterLog($edButtonReceived, 8) ; Edit registrieren zum Loggen DOWN/UP-Zeiten, nach 8 Aufrufen (je 4 DOWN und UP) erfolgt Zeilenumbruch