sr0wx/lib/cw.py

165 wiersze
6.1 KiB
Python
Executable File

#!/usr/bin/python -tt
# -*- coding: utf-8 -*-
#
# Copyright 2009-2011 Michal Sadowski (sq6jnx at hamradio dot pl)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import numpy.oldnumeric as Numeric
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": "-----",
# I have plans with special characters, ie make them sound like _SK_,
# _AR_, etc ...
".": ".-.-.-",", ": "--..--", "'": ".----.", "_": "..--.-", ":": "---...",
"?": "..--..", "-": "-....-", "/": "-..-.", "(": "-.--.",")": "-.--.-",
"=": "-...-", "@": ".--.-.",
"^": "........" # error = 8 dits
# ... or with these little creatures ;)
## "znak rozdziału": ".-..-",
## "początek kontaktu (VVV)": "...-...-...-",
## "początek nadawania": "-.-.-",
## "koniec nadawania": ".-.-.",
## "błąd (8 kropek)": "........",
## "prośba o powtórzenie": "..--..",
## "zrozumiano": "...-.",
## "czekaj": "--...",
## "wezwanie": "-.-",
## "koniec kontaktu": "...-.-",
## "międzynarodowy sygnał alarmowy (SOS)": "...---..."
}
def cw(text, wpm=25, farnsworth=None, weight=None, pitch=600, volume=0.1, sampleRate=44100):
# Would be nice to cope with farnsworth<0 ==> slow down by ... WPM
# Would be nice to implement weight
text = text.upper()
if farnsworth is not None:
try:
farnsworth = abs(farnsworth)
if farnsworth>wpm: farnsworth=wpm
except:
farnsworth=wpm
pass
else: farnsworth=wpm
volume=abs(volume)
if volume>1.0: volume=1.0
if sampleRate not in (11025,22050,44100):
sampleRate = 44100
# First of all we have to count lengths of pauses, "dits" and "dahs".
# There are three kinds of pauses: between dits and dahs (interBeep),
# between letters (interLetter) and between words (interWord).
#
# Normally (standard weight) "dit" is 1 unit long, "dah" is 3 units long,
# interBeep, interLetter and interword are 1, 3 and 7 units long.
#
# Note, that:
# * interLetter = interBeep + 2 units,
# * interWord = interBeep + interLetter + 3 units.
# We'll use this fact to simplify the code (I hope it will be clear).
#
# Last fact: how long (in seconds) is one unit? For further informations
# see http://www.ac6v.com/morseaids.htm, or belive me that typical word
# "PARIS" is 50 units long (incl. *all* interWord pauses). If we want to
# play it with 5 WPM (Words per Minute) we have to play 250 units, so...
#
# >>> 60.0/250
# 0.23999999999999999
#
# 1 unit is 0.24 seconds long.
#
# This function returns ``pygame.sndarray`` compatible data (``Numeric.aray``).
unit = 60.0/(50*wpm)
interBeep = Numeric.zeros( (int(unit*sampleRate)) )
# Farnsworth (after Farnsworth method, see Google) lest us to make interLetter
# and interWord pauses longer, but we need to recalculate ``unit``'s length:
fUnit = 60.0/(50*farnsworth)
interLetter = Numeric.zeros( (int(2*fUnit*sampleRate)) )
interWord = Numeric.zeros( (int(3*fUnit*sampleRate)) )
dit = Numeric.concatenate( (sine_array(pitch,volume,sampleRate,1*unit), interBeep) )
dah = Numeric.concatenate( (sine_array(pitch,volume,sampleRate,3*unit), interBeep) )
letters = {}
for letter in set(text.replace(" ","")):
if letter in morse:
for beep in morse[letter]:
if beep==".":
if letters.has_key(letter):
letters[letter]=Numeric.concatenate( (letters[letter],dit) )
else: letters[letter]=dit
else:
if letters.has_key(letter):
letters[letter]=Numeric.concatenate( (letters[letter],dah) )
else: letters[letter]=dah
letters[letter]=Numeric.concatenate( (letters[letter],interLetter) )
message = interBeep
for letter in text:
if letter != " " and letter in morse:
message = Numeric.concatenate( (message, letters[letter]) )
else:
message = Numeric.concatenate( (message, interWord) )
return Numeric.transpose(Numeric.array((message,message)))
# http://www.nabble.com/Chord-player-td21350708.html
def sine_array_onecycle(hz, peak=0.9,sampleRate=44100):
# Compute one cycle of an N-Hz sine wave with given peak amplitude
length = sampleRate / float(hz)
omega = Numeric.pi * 2 / length
xvalues = Numeric.arange(int(length)) * omega
return ((peak * 32767) * Numeric.sin(xvalues)).astype(Numeric.Int16)
def sine_array(hz, peak, sampleRate=44100,length=1.0):
#Compute N samples of a sine wave with given frequency and peak amplitude (defaults to one second).
return Numeric.resize(sine_array_onecycle(hz, peak), (int(sampleRate*length),))
def play(text, wpm=25, farnsworth=None, weight=None, pitch=800, volume=1, sampleRate=44100):
s= pygame.sndarray.make_sound( cw(text, wpm, farnsworth, weight, pitch, volume, sampleRate) )
# uncomment this line if you hear cw playing twice.
s = pygame.sndarray.make_sound(pygame.sndarray.array(s)[:len(pygame.sndarray.array(s))/2])
c = s.play()
while c.get_busy() == True:
pygame.time.wait(25)
if __name__ == '__main__':
import pygame
pygame.mixer.pre_init(44100,-16,2,1024)
pygame.mixer.init(44100,-16,2,1024)
#pygame.init()
play("vvv= test")