Posts: 1,298
Threads: 38
Joined: Sep 2016
Jan-18-2018, 03:56 PM
(This post was last modified: Feb-04-2018, 08:52 PM by sparkz_alot.)
I created this script for myself which I thought I would share for those interested in learning or improving their Morse Code skills. I have not included all possible tests, but if you follow the menu items, 1 than 2 than 3, you should be o.k. The script will create a group of 5 random characters per word depending on the file type selected. The number of 'words' is determined by line 45, in this case it is set for 10 words, though for myself, I set it for 500 or (range(2500)).
The 'Words per minute' (wpm) are based on the formula from the ARRL (American Radio Relay League) site located here:
http://www.arrl.org/files/file/Technology/x9004008.pdf.
I suggest not going above 70 wpm because, at least for my sound system, it becomes to distorted.
As for the frequency, I would suggest starting with 400 and adjusting from there to suit your tastes. This value must be given in Hertz.
The file 'code_file.txt' will be created in the working directory.
One final note, although I've included all characters in the standard Morse Code, there is one that I had to skip and that is the single 'close quote' or back slant quote. There is no equivalent on the keyboard, so I tried to use the Unicode equivalent, but this raises an error I can't figure out how to correct (see line 98).
EDIT: The error has been corrected in the included code posted here
Lastly, if you don't want to see the answer printed on screen, comment out line 168.
As always any comments, critisisms, improvements are appreciated.
Enjoy.
#! /usr/bin/env/ python3
import os
import random
import string
import time
from itertools import zip_longest
from pysine import sine
def words(words_per_minute):
""" Calculates the proper duration and spacing based on the selected words per minute """
# For 18 wpm and greater, use:
if words_per_minute >= 18:
unit = 1.2 / words_per_minute
dit = round(unit, 2)
dah = round(unit * 3, 2)
elem_space = round(unit, 2)
char_space = round(unit * 3, 2)
word_space = round(unit * 7, 2)
wpm_settings = (dit, dah, elem_space, char_space, word_space)
# For wpm less than 18, use:
else:
unit = 1.2 / 18
dit = round(unit, 2)
dah = round(unit * 3, 2)
elem_space = round(unit, 2)
char_speed = 18
char_space = round(unit * 3, 2)
word_space = round(unit * 7, 2)
delay = ((60 * char_speed) - (37.2 * words_per_minute)) / (char_speed * words_per_minute)
char_delay = 3 * delay / 19
word_delay = 7 * delay / 19
char_space += char_delay
word_space += word_delay
wpm_settings = (dit, dah, elem_space, round(char_space, 2), round(word_space, 2))
return wpm_settings
def grouper(iterable, n=5, fillvalue=None):
""" Creates 5 (n) letter 'words' from single string of characters"""
big_iterable = []
for c in range(50): # Set the number of groups; 2500 characters = 500 groups. Characters must be divisible by 5
big_iterable.append(random.choice(iterable))
args = [iter(big_iterable)] * n
return zip_longest(*args, fillvalue=fillvalue)
def settings():
"""
This function generates the settings for words-per-minute (wpm), play back frequency and character set
"""
print("Here you will be able to create settings for:")
print("\t** Words per minute\n\t** Frequency of play back\n\t** Type of characters\n")
setting = []
while True:
try:
wrds_p_min = int(input("How many words per minute would you like? (whole numbers only between 5 and 70): "))
if wrds_p_min < 5 or wrds_p_min > 70:
print("Must be a positive whole number greater than or equal than 5 and less than or equal to 70.")
continue
else:
break
except ValueError as e:
print("Must be a positive whole number greater than 0 and less than or equal to 70.", e)
continue
wpm = words(wrds_p_min) # Get the appropriate values based on words per minute
setting.append(wpm)
while True:
try:
freq = int(input("What play back frequency would you like? (whole numbers only): "))
if freq < 400:
print("Must be a positive whole number greater than or equal to 400.")
continue
else:
break
except ValueError as e:
print("Must be a positive whole number greater than or equal to 400.", e)
continue
setting.append(freq)
while True:
try:
print("1 Alpha\n2 Numeric\n3 Punctuation\n4 Alpha and Numeric\n5 Alpha and Punctuation\n6 Numeric and "
"Punctuation\n7 All")
char_set = int(input("Which character set(s) would you like? (select one): "))
if char_set < 1 or char_set > 7:
print("Must be a number 1 through 7")
continue
else:
break
except ValueError as e:
print("Must be a positive whole number greater than or equal to 1.", e)
continue
punc = '.,?`!/()&:;=+-_$@\u00b4' # added close quote '\u00b4'
if char_set == 1:
char_set = string.ascii_lowercase
elif char_set == 2:
char_set = string.digits
elif char_set == 3:
char_set = punc
elif char_set == 4:
char_set = string.ascii_lowercase + string.digits
elif char_set == 5:
char_set = string.ascii_lowercase + punc
elif char_set == 6:
char_set = string.digits + punc
elif char_set == 7:
char_set = string.ascii_lowercase + string.digits + punc
setting.append(char_set)
print("Settings have been saved.")
time.sleep(3)
return setting
def write_file(setting):
""" Creates a text file of selected character set(s) """
groups = grouper(setting)
content = list(groups)
with open('code_file.txt', 'w', encoding='utf-8') as file:
total = 0
w = 0
while total < len(content):
for g in content[w]:
file.write(g)
w += 1
file.write(' ')
total += 1
print('File code_file.txt has been created in the current working directory.')
time.sleep(3)
return
def play_file(wpm, freq):
""" Play back the file code_file.txt """
dit = wpm[0]
dah = wpm[1]
elem_space = wpm[2]
char_space = wpm[3]
word_space = wpm[4]
morse = {'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': '-----', '.': '.-.-.-',
',': '--..--', '?': '..--..', '`': '.----.', '!': '-.-.--',
'/': '-..-.', '(': '-.--.', ')': '-.--.-', '&': '.-...',
':': '---...', ';': '-.-.-.', '=': '-...-', '+': '.-.-.',
'-': '-....-', '_': '..--.-', '$': '...-..-', '@': '.--.-.',
'\u00b4': '.-..-.'}
print('Commencing in 5 seconds ...')
time.sleep(5)
with open('code_file.txt', 'r', encoding='utf-8') as file:
for line in file:
for word in line.split():
for character in word:
print('Character: ', character.capitalize()) # REMOVE IF YOU DON"T WANT TO CHEAT :-D
letter = morse[character]
for element in letter:
if element == '.':
sine(freq, dit)
sine(0, elem_space)
elif element == '-':
sine(freq, dah)
sine(0, elem_space)
sine(0, char_space)
sine(0, word_space)
print("\nEnd of file reached\n")
exit_prog()
def exit_prog():
""" Function to exit the program """
print("You are now exiting the program ...")
def main_mnu():
while True:
os.system('cls' if os.name == 'nt' else 'clear')
print('This is the Main Menu. Please make a selection.')
print('\t1) Settings')
print('\t2) Create Code File')
print('\t3) Play Code')
print('\n\t4) Exit')
choice = input('\nEnter choice: ')
try:
if choice == '1':
setting_list = list(settings())
continue
if choice == '2':
if not setting_list != []:
print("You need to create the settings first, please enter '1' at the prompt")
continue
else:
write_file(setting_list[2])
continue
if choice == '3':
exists = os.path.exists('code_file.txt')
if exists:
play_file(setting_list[0], setting_list[1])
break
else:
print("The file 'code_file.txt' does not exist")
continue
if choice == '4':
exit_prog()
break
except KeyError as err:
print('Not an option, try again.', err)
if __name__ == '__main__':
main_mnu()
If it ain't broke, I just haven't gotten to it yet.
OS: Windows 10, openSuse 42.3, freeBSD 11, Raspian "Stretch"
Python 3.6.5, IDE: PyCharm 2018 Community Edition
Posts: 12,026
Threads: 485
Joined: Sep 2016
Posts: 4,646
Threads: 1,493
Joined: Sep 2016
next, i'd like a program to take audio in, in real time, and decode all morse code streams being heard (with SSB wide filters, you often get many streams), showing each stream as a separate horizontally scrolling line. if the streams are too close, i can understand them not being separated. but it is amazing what DSP (software) can do that hardware (filters) cannot for separating signals, even with wide bandwidths (due to high modulation rates).
Tradition is peer pressure from dead people
What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Posts: 12,026
Threads: 485
Joined: Sep 2016
Jan-24-2018, 10:38 AM
(This post was last modified: Jan-24-2018, 10:38 AM by Larz60+.)
Morse to text: https://code.google.com/archive/p/morse-...ult/source
(don't think it's python)
there are .py files...
Posts: 70
Threads: 23
Joined: Jan 2018
Jan-24-2018, 10:46 AM
(This post was last modified: Jan-24-2018, 10:46 AM by league55.)
Did you try the configparser module for the configuration?
Posts: 1,298
Threads: 38
Joined: Sep 2016
(Jan-24-2018, 10:46 AM)league55 Wrote: Did you try the configparser module for the configuration?
I did not, no. I had thought about a config file at the beginning, but there didn't seem to be enough options to justify one.
Quote:next, i'd like a program to take audio in, in real time, and decode all morse code streams being heard (with SSB wide filters, you often get many streams), showing each stream as a separate horizontally scrolling line.
There are certainly some very good programs out there that do just that and encompass several, if not all, digital modes (including Morse).
Quote:Morse to text:
Come on Larz, that's just flat out cheating
If it ain't broke, I just haven't gotten to it yet.
OS: Windows 10, openSuse 42.3, freeBSD 11, Raspian "Stretch"
Python 3.6.5, IDE: PyCharm 2018 Community Edition
Posts: 12,026
Threads: 485
Joined: Sep 2016
Jan-24-2018, 06:05 PM
(This post was last modified: Jan-24-2018, 06:06 PM by Larz60+.)
No, It's a fun project.
I'm a big cw fan, though haven't been on air for quite some time.
When I am, I like QRP, and use a Yaesu FT-817 with an Alexloop antenna.
Lots of fun.
Posts: 70
Threads: 23
Joined: Jan 2018
Jan-24-2018, 11:13 PM
(This post was last modified: Jan-24-2018, 11:14 PM by league55.)
Another thought is that words(>18wpm) and words(<18wmp) look a lot like two objects of the Words class. They have all (or nearly all) of their attributes in common and the values of the attributes are just different. But I don't know whether defining a class for just two objects would be worth it. Maybe if there were some reason to make that wmp value a (variable) attribute too, or to have one for a minimum and one for a maximum value, but I don't know enough about the use case to know whether that even makes any sense. (E.g. you can define a Birthday class and overload the __add__ and __radd__ operators to add two birthdays together with the expression birthday1 + birthday2, but why? I don't know if I'm talking about the same kind of folly here.)
Posts: 4,646
Threads: 1,493
Joined: Sep 2016
in morse to text you don't know the wpm in advance but can figure out dits and dahs to an extent ... enough to evaluate them where the speed remains close over some number of them. the real fun happens when two signals are so close that their sidebands overlap. even more fun happens when a 3rd signal is near these 2. then it is not a simple as cutting off the sidebands between 2 signals as you would destroy the middle signal. you need to demod the 2 outer signals (demod whatever comes through ok) and reproduce it and subtract. you now have a "2nd layer" of signals to demod and decode. this can work to some extent because morse code and A1 modulation is actually very redundant. don't expect to do much with overlapping digital signals in use today.
Tradition is peer pressure from dead people
What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Posts: 1,298
Threads: 38
Joined: Sep 2016
Just an update, I made some changes to the original code posted above (thought it was better than re-posting the entire code again).
Line 98: added the 'single close quote' (\u00b4) back into the punc string, so it now reads:
punc = '.,?`!/()&:;=+-_$@\u00b4' # ยด = '\u00b4' Line 164: added encoding to the open statement, so it now reads:
with open('code_file.txt', 'r', encoding='utf-8') as file: I will try and run it on Linux this weekend to make sure in works on that OS as well.
If it ain't broke, I just haven't gotten to it yet.
OS: Windows 10, openSuse 42.3, freeBSD 11, Raspian "Stretch"
Python 3.6.5, IDE: PyCharm 2018 Community Edition
|