11:34:19 pm on 2/4/12

Menu
» Home
» About Scott
» VD Labs
» QRSS VD
» Old Stuff
» Archive
» Publications
» Contact

Categories
» C/C++
» Circuitry
» DIY ECG
» General
» high altitude balloon
» Linux
» Microcontrollers
» Molecular Biology
» My Website
» PHP
» Prime Numbers
» Python
» Radio
» UCF Lab
» Everything
» RF Links

Writings
» MD Labels
» Streamrip
» AIM Thoughts
» WindowsXP?
» Partitioning
» CD/DVD Repair
» Monitor Info
» CRT Deflection
» Venomcrack
» Flash Thing
» Heart/Brain
» Diabetes
» Triops
» Biomed

Friends
» Mike
» Fred
» Kyle W
» Nick
» Louis
» Tom
» Kyle H




Archives
» August 2011
» July 2011
» June 2011
» March 2011
» February 2011
» January 2011
» December 2010
» November 2010
» September 2010
» August 2010
» July 2010
» June 2010
» May 2010
» April 2010
» March 2010
» February 2010
» January 2010
» December 2009
» September 2009
» August 2009
» July 2009
» June 2009
» May 2009
» April 2009
» March 2009
» February 2009
» January 2009
» December 2008
» November 2008
» October 2008
» September 2008
» September 2007
» December 2006
» August 2006
» January 2006
» August 2005
» July 2005
» June 2005
» May 2005
» April 2005
» March 2005
» February 2005
» January 2005
» December 2004
» November 2004
» October 2004
» September 2004
» August 2004
» July 2004
» June 2004
» May 2004
» April 2004
» March 2004
» February 2004
» January 2004
» December 2003
» November 2003
» October 2003
» September 2003
» August 2003
» July 2003
» June 2003
» May 2003
» April 2003
» March 2003
» February 2003
» January 2003
» December 2002
» November 2002
» October 2002
» September 2002
» June 2001
« High Altitude Balloon Transmitter
Converting Numbers to Morse Code with GCC »


Idea: vdFSK modulation
854 words | Posted on July 22nd, 2010
Scott was 24.82 years old when he wrote this!
Filed under: General, Python, Radio

My goal is to create a QRPP (extremely low power) transmitter and modulation method to send QRSS (extremely slow, frequency shifting data) efficiently, able to be decoded visually or with automated image analysis software. This evolving post will document the thought process and development behind AJ4VD’s Frequency Shift Keying method, vdFSK.

Briefly, this is what my idea is. Rather than standard 2-frequencies (low for space, high for tone) QRSS3 (3 seconds per dot), I eliminate the need for pauses between dots by using 3 frequencies (low for a space between letters, medium for dot, high for dash). The following images compare my call sign (AJ4VD) being sent with the old method, and the vdFSK method.

Traditional QRSS:
traditional

Again, both of these images say the same thing: AJ4VD, (.- .— ….- …- -..). However, note that the above image has greater than a 3 second dot, so it’s unfairly long if you look at the time scale. Until I get a more fairly representative image, just appreciate it graphically. It’s obviously faster to send 3 frequencies rather than two. In my case, it’s over 200% faster.

vdFSK method:
modulation

This is the code to generate audio files converting a string of text into vdFSK audio, saving the output as a WAV file. Spectrographs can be created from these WAV files.

### generate_audio.py ###
# converts a string into vdFSK audio saved as a WAV file

import numpy
import wave
from morse import *

def makeTone(freq,duration=1,samplerate=5000,shape=True):
    signal = numpy.arange(duration*samplerate)/float(samplerate)*float(freq)*3.14*2
    signal = numpy.sin(signal)*16384
    if shape==True: #soften edges
        for i in range(100):
            signal[i]=signal[i]*(i/100.0)
            signal[-i]=signal[-i]*(i/100.0)
    ssignal=''
    for i in range(len(signal)): #make it binary
        ssignal += wave.struct.pack('h',signal[i])
    return ssignal

def text2tone(msg,base=800,sep=5):
    audio=''
    mult=3 #secs per beep
    msg=" "+msg+" "
    for char in msg.lower():
        morse=lookup[char]
        print char, morse
        audio+=makeTone(base,mult)
        for step in lookup[char]:
            if step[0]==".":
                audio+=makeTone(base+sep,int(step[1])*mult)
            if step[0]=="-":
                audio+=makeTone(base+sep*2,int(step[1])*mult)
            if step[0]=="|":
                audio+=makeTone(base,3*mult)
    return audio

msg="aj4vd"
file=wave.open('test.wav', 'wb')
file.setparams((1, 2, 5000, 5000*4, 'NONE', 'noncompressed'))
file.writeframes(text2tone(msg))
file.close()

print 'file written'

And the other file needed…

### morse.py ###
# library for converting between text and Morse code
raw_lookup="""
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--..
0----- 1.---- 2..--- 3...-- 4....- 5..... 6-.... 7--... 8---.. 9----.
..-.-.- =-...- :---... ,--..-- /-..-. --....-
""".replace("\n","").split(" ")

lookup={}
lookup[" "]=["|1"]
for char in raw_lookup:
    """This is a silly way to do it, but it works."""
    char,code=char[0],char[1:]
    code=code.replace("-----","x15 ")
    code=code.replace("----","x14 ")
    code=code.replace("---","x13 ")
    code=code.replace("--","x12 ")
    code=code.replace("-","x11 ")
    code=code.replace(".....","x05 ")
    code=code.replace("....","x04 ")
    code=code.replace("...","x03 ")
    code=code.replace("..","x02 ")
    code=code.replace(".","x01 ")
    code=code.replace("x0",'.')
    code=code.replace("x1",'-')
    code=code.split(" ")[:-1]
    #print char,code
    lookup[char]=code

Automated decoding is trivial. The image above was analyzed, turned into the image below, and the string (AJ4VD) was extracted:
produced

The code to do this:

### decode.py ###
# given an image, it finds peaks and pulls data out
from PIL import Image
from PIL import ImageDraw
import pylab
import numpy

pixelSeek=10
pixelShift=15

def findPeak(data):
	maxVal=0
	maxX=0
	for x in range(len(data)):
		if data[x]>maxVal:
			maxVal,maxX=data[x],x
	return maxX

def peaks2morse(peaks):
	baseFreq=peaks[0]
	lastSignal=peaks[0]
	lastChange=0
	directions=[]
	for i in range(len(peaks)):
		if abs(peaks[i]-baseFreq)<pixelSeek:
			baseFreq=peaks[i]
		if abs(peaks[i]-lastSignal)<pixelSeek and i<len(peaks)-1:
			lastChange+=1
		else:
			if abs(baseFreq-lastSignal)<pixelSeek:c=" "
			if abs(baseFreq-lastSignal)<pixelSeek:c=" "
			if abs(baseFreq-lastSignal)<pixelSeek:c=" "
			directions.append([lastSignal,lastChange,baseFreq,baseFreq-lastSignal])
			lastChange=0
		lastSignal=peaks[i]
	return directions

def morse2image(directions):
	im=Image.new("L",(300,100),0)
	draw = ImageDraw.Draw(im)
	lastx=0
	for d in directions:
		print d
		draw.line((lastx,d[0],lastx+d[1],d[0]), width=5,fill=255)
		lastx=lastx+d[1]
	im.show()

im=Image.open('raw.png')
pix=im.load()
data=numpy.zeros(im.size)
for x in range(im.size[0]):
	for y in range(im.size[1]):
		data[x][y]=pix[x,y]

peaks=[]
for i in range(im.size[0]):
	peaks.append(findPeak(data[i]))

morse=peaks2morse(peaks)
morse2image(morse)
print morse




This entry was posted on Thursday, July 22nd, 2010 at 12:39 pmand is filed under General, Python, Radio. You can follow any responses to this entry through the RSS 2.0 feed. You can skip to the end and leave a response. Pinging is currently not allowed.



3 Responses to “Idea: vdFSK modulation”

bill w4hbk wrote the following at 10:13:51 PM on July 22nd, 2010

Scott: I recalled seeing something similar recently when I was looking into DFCW….called DFCWi, where the i means a third frequency or “idle” frequency to indicate space. Here is the link:

http://www.g4jnt.com/DFCW.htm

73 bill w4hbk

Jason NT7S wrote the following at 04:22:55 PM on July 24th, 2010

I think this is a great idea. The only change I would suggest is to perhaps have a short period in between elements where the AFSK tone returns to the “baseline” in order to make it more human-readable. It’s a bit hard to tell how many elements are in characters such as the “4″ when you’ve got four “dits” in a row.

Chris N2MCS wrote the following at 12:19:04 PM on August 15th, 2010

It would be more human readable as well (in my opinion) if dits were + the center frequency and dahs were – the center, like a paddle is configured.

Leave a Reply




copyright © 2006 swharden@gmail.com