7:14:16 pm on 3/11/10
Menu
» Home
» About Scott
» Biomed
» Old Stuff
» Archive
» Contact

Categories
» C/C++
» Circuitry
» Dentistry
» DIY ECG
» General
» Linux
» Microcontrollers
» Molecular Biology
» My Website
» PHP
» Prime Numbers
» Python
» Radio
» UCF Lab
» Everything
Writings
» MD Labels
» Streamrip
» AIM Thoughts
» WindowsXP?
» Partitioning
» CD/DVD Repair
» Monitor Info
» CRT Deflection
» Venomcrack
» Flash Thing
» Heart/Brain
» Diabetes
» Triops

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




Archives
» 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

How to Destroy a Radio Operator
Posted by
Scott on March 10th, 2010 | 281 words | No Comments (yet)
Filed under: General

After cwQslspriding myself on my ingenuity a few weeks ago for documenting my homemade stealth indoor apartment antenna for 40m and 20m, it seems that the green movement has contrived a plan to cripple my successes. So far I’ve made a few dozen contacts in Morse code with my humble little setup (~20 watts of power, direct conversion receiver, indoor homemade antenna). The photo shows some QSL cards I’ve gotten. Anyhow, my apartment manager decided that my apartment needed to have solar panels added to it. It’s too early to tell for sure, but spinning the dial a few times and hearing *nothing* makes me think that it dramatically impacted my reception (and likely transmission) in a dramatic way.

A few days ago…
before

yesterday…
workers

today
after

R.I.P. AJ4VD station… [sigh]


» Be the first to comment!



Overseas QRP Transmission
Posted by
Scott on March 9th, 2010 | 389 words | No Comments (yet)
Filed under: General

While working on the spectrograph software I’m so psychotic about completing this spring break, I happened to capture a cool signal from Italy. IW4DXW was sending some cool signals that I captured around 10.140 MHz. See how his callsign is written “visually” on the spectrogram? I thought I’d post it because it’s an encouraging sign that my software is going in the right direction. Also note the numerous QRSS FSK signals around it! So cool.
iw4dxw

UPDATE! The guy emailed me his station information.

Hi Scott! Thank you so much for nice report. This is a short description of my homebrew beacon. The audio source is a normal MP3/WAV player. The file (.WAV) of my hell message (8×8 char) is generated using Chirphel (program by DF6NM) with a fmax~=800Hz and BW=5Hz, and processed with Csound to obtain the phase relation R = L + 90° between channels (hilbert function). A 10.140MHz oven xtal oscillator (adjusted for +880Hz) is a PLL reference used to obtain a x4 frequency. This clock with the 2 audio channels are applied to a “Softrock style” SSB phasing modulator (double H-mixer with 74HC4053 ic).

The LSB output signal (10.140,080MHz @ ~ -3dBm) is amplified by a 2N2222 (driver stage) and a 2SC2314 (linear PA stage ~1W P.E.P. max, but 100 – 200 mW of average power). I’m using a 30m dipole (inverted vee @ about 50 feet). 30 meters looks good today! My signal heard in VK2 too…

Greetings from north Italy, dear Scott!
73
Riccardo, IW4DXW

Holy… 200mW? That’s so awesome. Maybe one day I’ll build something equally as cool and epically useless =oD I will now return to my psychotic programming project what has been eating-up my spring break. [boom] I just snapped a screenshot for the future memory of this break. Yes, I’m on a XP machine. I’m at the W4DFU radio club station (they have nicer computers than I do!)
epicProgramming


» Be the first to comment!



I’m working too hard
Posted by
Scott on March 9th, 2010 | 184 words | No Comments (yet)
Filed under: General

Several days straight of coding all day every day has started to mess with my mind. I’ve got to incorporate some relaxation time! Something good ‘ol youtube can assist with. I share with you the following videos today instead of Python code (there’s always time for that later).


» Be the first to comment!



Dynamic Image Generation with Python
Posted by
Scott on March 7th, 2010 | 852 words | No Comments (yet)
Filed under: General

So here’s the problem. I’m working on a software project and I’d love the startup screen to be flashy, but not tacky. I want the program to be ENTIRELY scripted, so pretty bitmap images are out. I have to generate it from the script, but how can I reliably print text on an image with fonts vary across operating systems? (i.e., many OS’s don’t have “arial.ttf”, or no truetype fonts at all!) The solution was a creative process which will unfold before you.

First, I created a 2D binary array to represent the alphabet using pixel fonts such as this as a reference. The word I’m trying to create is “QRSS VD” (the name of my program). I store the data in strings, as seen below. I use 1’s to mark pixels, and spaces to mark empty spaces.

data="""
1111 1111 1111 1111   1  1 111
1  1 1  1 1    1      1  1 1  1
1  1 1111 1111 1111   1  1 1  1
1 11 1 1     1    1   1 1  1  1
1111 1 11 1111 1111    1   111
"""

Once I think it looks nice, I replace the spaces with zeros to make it take up a lot of visual space…

data2="""
1111011110111101111000100101110
1001010010100001000000100101001
1001011110111101111000100101001
1011010100000100001000101001001
1111010110111101111000010001110
"""

Then I further obscure it by replacing linebreaks with a different symbol, such as the number 2, then break the lines so they’re not lined up…

b="1111011110111101111000100101110210010100101000010000001001010012"
b+="1001011110111101111000100101001210110101000001000010001010010012"
b+="1111010110111101111000010001110"

The result is a pretty cool way to obscure the text. I don’t know why you’d want to, but if you want to make sure that no one goes in and changes the letters around (at least without making them think pretty hard about it) you could look at ways to further encrypt this data stream. From here, I create an image using the Python Imaging Library, setting pixel values to 255*b[x,y] (so 0 stays 0 and 1 becomes 255, perfect for an 8-bit image). After enlarging, here’s the result:

nearest

That’s cool huh? Now let’s make it a little bit less pixelated. It’s not a cure-all method, but blurring it up a little with a bilinear filter helps a lot…

bilinear

Then I apply the code below which applies a cool colormap to the pixel values. I’ll provide cleaner code for this later (I have a really cool way of generating colormaps and saving them as arrays of RGB tuples). Then I go through and plot some random sin wavs on top of it. Sweet! Here are 6 images generated from the program run 6 times. Notice the randomness of the sine wavs!

qrss vd

qrss vd

qrss vd

qrss vd

qrss vd

qrss vd

Vwa la! A different image is generated every time the script runs, and it requires no external files (bitmaps or fonts) and should work well on all operating systems. Take the idea and run with it!

from PIL import Image
from PIL import ImageOps
from PIL import ImageFilter
from random import randint
import scipy

def genLogo():
	colormap=[(0, 0, 129), (0, 0, 134), (0, 0, 139), (0, 0, 143), (0, 0, 148), (0, 0, 152), (0, 0, 157), (0, 0, 161), (0, 0, 166), (0, 0, 170), (0, 0, 175), (0, 0, 180), (0, 0, 184), (0, 0, 189), (0, 0, 193), (0, 0, 198), (0, 0, 202), (0, 0, 207), (0, 0, 211), (0, 0, 216), (0, 0, 220), (0, 0, 225), (0, 0, 230), (0, 0, 234), (0, 0, 239), (0, 0, 243), (0, 0, 248), (0, 0, 252), (0, 0, 255), (0, 0, 255), (0, 0, 255), (0, 0, 255), (0, 2, 255), (0, 7, 255), (0, 11, 255), (0, 14, 255), (0, 18, 255), (0, 23, 255), (0, 27, 255), (0, 31, 255), (0, 34, 255), (0, 39, 255), (0, 43, 255), (0, 47, 255), (0, 51, 255), (0, 54, 255), (0, 59, 255), (0, 63, 255), (0, 67, 255), (0, 71, 255), (0, 75, 255), (0, 79, 255), (0, 83, 255), (0, 87, 255), (0, 91, 255), (0, 95, 255), (0, 99, 255), (0, 103, 255), (0, 107, 255), (0, 111, 255), (0, 115, 255), (0, 119, 255), (0, 123, 255), (0, 127, 255), (0, 131, 255), (0, 135, 255), (0, 139, 255), (0, 143, 255), (0, 147, 255), (0, 151, 255), (0, 155, 255), (0, 159, 255), (0, 163, 255), (0, 167, 255), (0, 171, 255), (0, 175, 255), (0, 179, 255), (0, 183, 255), (0, 187, 255), (0, 191, 255), (0, 195, 255), (0, 199, 255), (0, 203, 255), (0, 207, 255), (0, 211, 255), (0, 215, 255), (0, 219, 254), (0, 223, 251), (0, 227, 248), (2, 231, 245), (5, 235, 241), (7, 239, 238), (11, 243, 235), (14, 247, 232), (18, 251, 228), (21, 255, 225), (23, 255, 222), (27, 255, 219), (31, 255, 215), (34, 255, 212), (37, 255, 208), (40, 255, 205), (44, 255, 203), (47, 255, 199), (50, 255, 195), (54, 255, 192), (57, 255, 189), (60, 255, 186), (63, 255, 183), (66, 255, 179), (70, 255, 176), (73, 255, 173), (76, 255, 170), (79, 255, 166), (83, 255, 163), (86, 255, 160), (89, 255, 157), (92, 255, 154), (95, 255, 150), (99, 255, 147), (102, 255, 144), (105, 255, 141), (108, 255, 137), (112, 255, 134), (115, 255, 131), (118, 255, 128), (121, 255, 125), (124, 255, 121), (128, 255, 118), (131, 255, 115), (134, 255, 112), (137, 255, 108), (141, 255, 105), (144, 255, 102), (147, 255, 99), (150, 255, 95), (154, 255, 92), (157, 255, 89), (160, 255, 86), (163, 255, 83), (166, 255, 79), (170, 255, 76), (173, 255, 73), (176, 255, 70), (179, 255, 66), (183, 255, 63), (186, 255, 60), (189, 255, 57), (192, 255, 54), (195, 255, 50), (199, 255, 47), (202, 255, 44), (205, 255, 41), (208, 255, 37), (212, 255, 34), (215, 255, 31), (218, 255, 28), (221, 255, 24), (224, 255, 21), (228, 255, 18), (231, 255, 15), (234, 255, 12), (238, 255, 8), (241, 252, 5), (244, 248, 2), (247, 244, 0), (250, 240, 0), (254, 236, 0), (255, 233, 0), (255, 229, 0), (255, 226, 0), (255, 221, 0), (255, 218, 0), (255, 215, 0), (255, 211, 0), (255, 207, 0), (255, 203, 0), (255, 199, 0), (255, 196, 0), (255, 192, 0), (255, 188, 0), (255, 184, 0), (255, 180, 0), (255, 177, 0), (255, 173, 0), (255, 169, 0), (255, 165, 0), (255, 162, 0), (255, 159, 0), (255, 155, 0), (255, 151, 0), (255, 147, 0), (255, 143, 0), (255, 140, 0), (255, 136, 0), (255, 132, 0), (255, 128, 0), (255, 125, 0), (255, 121, 0), (255, 117, 0), (255, 114, 0), (255, 110, 0), (255, 106, 0), (255, 102, 0), (255, 99, 0), (255, 95, 0), (255, 91, 0), (255, 88, 0), (255, 84, 0), (255, 80, 0), (255, 76, 0), (255, 73, 0), (255, 69, 0), (255, 65, 0), (255, 62, 0), (255, 58, 0), (255, 54, 0), (255, 51, 0), (255, 47, 0), (255, 43, 0), (255, 39, 0), (255, 36, 0), (255, 32, 0), (255, 28, 0), (255, 25, 0), (255, 21, 0), (253, 17, 0), (248, 14, 0), (244, 10, 0), (240, 6, 0), (235, 2, 0), (230, 0, 0), (225, 0, 0), (221, 0, 0), (217, 0, 0), (212, 0, 0), (207, 0, 0), (203, 0, 0), (198, 0, 0), (194, 0, 0), (189, 0, 0), (185, 0, 0), (180, 0, 0), (175, 0, 0), (171, 0, 0), (166, 0, 0), (162, 0, 0), (157, 0, 0), (152, 0, 0), (148, 0, 0), (144, 0, 0), (139, 0, 0), (134, 0, 0), (130, 0, 0), (134, 0, 0), (130, 0, 0)]
	def red(val):
		return colormap[val][0]
	def green(val):
		return colormap[val][1]
	def blue(val):
		return colormap[val][2]
	def colorize(im):
		r=Image.eval(im,red)
		g=Image.eval(im,green)
		b=Image.eval(im,blue)
		im=Image.merge("RGB",(r,g,b))
		return im
	b="1111011110111101111000100101110210010100101000010000001001010012"
	b+="1001011110111101111000100101001210110101000001000010001010010012"
	b+="1111010110111101111000010001110"
	b=b.split("2")
	im=Image.new("L",(33+15,7+13))
	data=im.load()
	for y in range(len(b)):
		for x in range(len(b[y])):
			data[x+6,y+6]=int(b[y][x])*255
	scale=15
	im=im.resize((im.size[0]*scale,im.size[1]*scale))
	data=im.load()
	def drawSin(width,height,vertoffset,horizoffset,thickness,darkness):
		for x in range(im.size[0]):
			y=scipy.sin((x-horizoffset)/float(width))*height+vertoffset
			for i in range(thickness):
				if 0<=y+i<im.size[1] and 0<=x<im.size[0]:
					#print x,im.size[0],y+i,im.size[1]
					data[x,y+i]=data[x,y+i]+darkness

	for i in range(5):
		print "line",i
		drawSin(randint(5,75),randint(-100,200),randint(0,im.size[1]),\
				randint(0,im.size[0]),randint(3,15),70)
	for i in range(10):
		im=im.filter(ImageFilter.SMOOTH_MORE)
	im=colorize(im)
	return im

im=genLogo()
im.save('logo.png',"PNG")

» Be the first to comment!



Brewing Excitement
Posted by
Scott on March 6th, 2010 | 257 words | No Comments (yet)
Filed under: General

Wow, I can’t believe I took-on such a massive challenge this week! Some irony lies in the fact that I’ve worked harder and learned more in the last 4 days of all-day work (researching, reading, skimming other peoples’ code, and writing my own) than of the last 9 months of dental school. This is an incredible feeling of accomplishment. My program, *MINE*, which I coded 100% from scratch (using Python’s scripting platform as a strong base coupled with the Python Imaging Library (PIL), Tk bindings (Tkinter)). It polls the soundcard continuously and makes incredibly large spectrographs. I’ll explain more about it and its rationale later. It’s not finished, but it’s working… and working pretty darn well. I’m floored!

Here’s what it looks like when it’s running…
gotit

Pretty nice ‘eh? Yeah, that’s a GUI, but it can be run entirely from a headless server through a console as well. (see where I’m going with this?) Here’s some more of the output, cropped to emphasize QRSS signals. Keep in mind that the image below was cropped to less than 3,000 pixels high, whereas the original is over 8,000 pixels high!!!
qrss_big


» Be the first to comment!



Animated Realtime Spectrograph with Scrolling Waterfall Display in Python
Posted by
Scott on March 5th, 2010 | 843 words | No Comments (yet)
Filed under: General, Python

My project is coming along nicely. This isn’t an incredibly robust spectrograph program, but it sure gets the job done quickly and easily. The code below will produce a realtime scrolling spectrograph entirely with Python! spectrogram scrollbarsIt polls the microphone (or default recording device), should work on any OS, and can be adjusted for vertical resolution / FFT frequency discretion resolution. It has some simple functions for filtering (check out the detrend filter!) and might serve as a good start to a spectrograph / frequency analysis project. It took my a long time to reach this point! I’ve worked with Python before, and dabbled with the Python Imaging Library (PIL), but this is my first experience with realtime linear data analysis and high-demand multi-threading. I hope it helps you. Below are screenshots of the program (two running at the same time) listening to the same radio signals (mostly Morse code) with standard output and with the “detrending filter” activated.
nofilter
filter

And the code…

import pyaudio
import scipy
import struct
import scipy.fftpack

from Tkinter import *
import threading
import time, datetime
import wckgraph
import math

import Image, ImageTk
from PIL import ImageOps
from PIL import ImageChops
import time
import random
import threading
import scipy

#ADJUST RESOLUTION OF VERTICAL FFT
bufferSize=2**11
#bufferSize=2**8

#ADJUSTS AVERAGING SPEED NOT VERTICAL RESOLUTION
#REDUCE HERE IF YOUR PC CANT KEEP UP
sampleRate=24000
#sampleRate=64000

p = pyaudio.PyAudio()
chunks=[]
ffts=[]
def stream():
        global chunks, inStream, bufferSize
        while True:
                chunks.append(inStream.read(bufferSize))

def record():
        global w, inStream, p, bufferSize
        inStream = p.open(format=pyaudio.paInt16,channels=1,\
                rate=sampleRate,input=True,frames_per_buffer=bufferSize)
        threading.Thread(target=stream).start()
        #stream()

def downSample(fftx,ffty,degree=10):
        x,y=[],[]
        for i in range(len(ffty)/degree-1):
                x.append(fftx[i*degree+degree/2])
                y.append(sum(ffty[i*degree:(i+1)*degree])/degree)
        return [x,y]

def smoothWindow(fftx,ffty,degree=10):
        lx,ly=fftx[degree:-degree],[]
        for i in range(degree,len(ffty)-degree):
                ly.append(sum(ffty[i-degree:i+degree]))
        return [lx,ly]

def smoothMemory(ffty,degree=3):
        global ffts
        ffts = ffts+[ffty]
        if len(ffts)< =degree: return ffty
        ffts=ffts[1:]
        return scipy.average(scipy.array(ffts),0)

def detrend(fftx,ffty,degree=10):
        lx,ly=fftx[degree:-degree],[]
        for i in range(degree,len(ffty)-degree):
                ly.append((ffty[i]-sum(ffty[i-degree:i+degree])/(degree*2))\
                          *2+128)
                #ly.append(fft[i]-(ffty[i-degree]+ffty[i+degree])/2)
        return [lx,ly]

def graph():
        global chunks, bufferSize, fftx,ffty, w
        if len(chunks)>0:
                data = chunks.pop(0)
                data=scipy.array(struct.unpack("%dB"%(bufferSize*2),data))
                #print "RECORDED",len(data)/float(sampleRate),"SEC"
                ffty=scipy.fftpack.fft(data)
                fftx=scipy.fftpack.rfftfreq(bufferSize*2, 1.0/sampleRate)
                fftx=fftx[0:len(fftx)/4]
                ffty=abs(ffty[0:len(ffty)/2])/1000
                ffty1=ffty[:len(ffty)/2]
                ffty2=ffty[len(ffty)/2::]+2
                ffty2=ffty2[::-1]
                ffty=ffty1+ffty2
                ffty=(scipy.log(ffty)-1)*120
                fftx,ffty=downSample(fftx,ffty,2)
                #fftx,ffty=detrend(fftx,ffty,30)
                #fftx,ffty=smoothWindow(fftx,ffty,10)
                #ffty=smoothMemory(ffty,3)
                #fftx,ffty=detrend(fftx,ffty,3)
                #print len(ffty)
                #print min(ffty),max(ffty)
                updatePic(fftx,ffty)
                reloadPic()
                #w.clear()
                #w.add(wckgraph.Axes(extent=(0, -1, 6000, 3)))
                #w.add(wckgraph.LineGraph([fftx,ffty]))
                #w.update()

        if len(chunks)>20:
                print "falling behind...",len(chunks)

def go(x=None):
        global w,fftx,ffty
        print "STARTING!"
        threading.Thread(target=record).start()
        while True:
                #record()
                graph()

def updatePic(datax,data):
     global im, iwidth, iheight
     strip=Image.new("L",(1,iheight))
     if len(data)>iheight:
             data=data[:iheight-1]
     #print "MAX FREQ:",datax[-1]
     strip.putdata(data)
     #print "%03d, %03d" % (max(data[-100:]), min(data[-100:]))
     im.paste(strip,(iwidth-1,0))
     im=im.offset(-1,0)
     root.update()

def reloadPic():
     global im, lab
     lab.image = ImageTk.PhotoImage(im)
     lab.config(image=lab.image)

root = Tk()
im=Image.open('./ramp.tif')
im=im.convert("L")
iwidth,iheight=im.size
im=im.crop((0,0,500,480))
#im=Image.new("L",(100,1024))
iwidth,iheight=im.size
root.geometry('%dx%d' % (iwidth,iheight))
lab=Label(root)
lab.place(x=0,y=0,width=iwidth,height=iheight)
go()

UPDATE! I’m not going to post the code for this yet (it’s very messy) but I got this thing to display a spectrograph on a canvas. What’s the advantage of that? Huge, massive spectrographs (thousands of pixels in all directions) can now be browsed in real time using scrollbars, and when you scroll it doesn’t stop recording, and you don’t lose any data! Super cool.

spectrogram scrollbars


» Be the first to comment!



Realtime FFT Graph of Audio WAV File or Microphone Input with Python, Scipy, and WCKgraph
Posted by
Scott on March 5th, 2010 | 637 words | No Comments (yet)
Filed under: General, Python

I’m stretching the limits of what these software platforms were designed to to, but I’m impressed such a haphazard hacked-together code as this produces fast, functional results. The code below is the simplest case code I could create which would graph the audio spectrum of the microphone input (or a WAV file or other sound as it’s being played). There’s some smoothing involved (moving window down-sampling along the frequency axis and sequence averaging along the time axis) but the darn thing seems to keep up with realtime audio input at a good 30+ FPS on my modest maching. It should work on Windows and Linux. I chose not to go with matplotlib because I didn’t think it was fast enough for my needs in this one case (although I love it in every other way). Here’s what the code below looks like running:
python real time tk wav fft

NOTE that this program was designed with the intent of recording the FFTs, therefore if the program “falls behind” the realtime input, it will buffer the sound on its own and try to catch up (accomplished by two layers of threading). In this way, *EVERY MOMENT* of audio is interpreted. If you’re just trying to create a spectrograph for simple purposes, have it only sample the audio when it needs to, rather than having it sample audio continuously.

import pyaudio
import scipy
import struct
import scipy.fftpack

from Tkinter import *
import threading
import time, datetime
import wckgraph
import math

#ADJUST THIS TO CHANGE SPEED/SIZE OF FFT
bufferSize=2**11
#bufferSize=2**8

# ADJUST THIS TO CHANGE SPEED/SIZE OF FFT
sampleRate=48100
#sampleRate=64000

p = pyaudio.PyAudio()
chunks=[]
ffts=[]
def stream():
        global chunks, inStream, bufferSize
        while True:
                chunks.append(inStream.read(bufferSize))

def record():
        global w, inStream, p, bufferSize
        inStream = p.open(format=pyaudio.paInt16,channels=1,\
                rate=sampleRate,input=True,frames_per_buffer=bufferSize)
        threading.Thread(target=stream).start()

def downSample(fftx,ffty,degree=10):
        x,y=[],[]
        for i in range(len(ffty)/degree-1):
                x.append(fftx[i*degree+degree/2])
                y.append(sum(ffty[i*degree:(i+1)*degree])/degree)
        return [x,y]

def smoothWindow(fftx,ffty,degree=10):
        lx,ly=fftx[degree:-degree],[]
        for i in range(degree,len(ffty)-degree):
                ly.append(sum(ffty[i-degree:i+degree]))
        return [lx,ly]

def smoothMemory(ffty,degree=3):
        global ffts
        ffts = ffts+[ffty]
        if len(ffts)< =degree: return ffty
        ffts=ffts[1:]
        return scipy.average(scipy.array(ffts),0)

def detrend(fftx,ffty,degree=10):
        lx,ly=fftx[degree:-degree],[]
        for i in range(degree,len(ffty)-degree):
                ly.append(ffty[i]-sum(ffty[i-degree:i+degree])/(degree*2))
                #ly.append(fft[i]-(ffty[i-degree]+ffty[i+degree])/2)
        return [lx,ly]

def graph():
        global chunks, bufferSize, fftx,ffty, w
        if len(chunks)>0:
                data = chunks.pop(0)
                data=scipy.array(struct.unpack("%dB"%(bufferSize*2),data))
                #print "RECORDED",len(data)/float(sampleRate),"SEC"
                ffty=scipy.fftpack.fft(data)
                fftx=scipy.fftpack.rfftfreq(bufferSize*2, 1.0/sampleRate)
                fftx=fftx[0:len(fftx)/4]
                ffty=abs(ffty[0:len(ffty)/2])/1000
                ffty1=ffty[:len(ffty)/2]
                ffty2=ffty[len(ffty)/2::]+2
                ffty2=ffty2[::-1]
                ffty=ffty1+ffty2
                ffty=scipy.log(ffty)-2
                #fftx,ffty=downSample(fftx,ffty,5)
                #fftx,ffty=detrend(fftx,ffty,30)
                #fftx,ffty=smoothWindow(fftx,ffty,10)
                ffty=smoothMemory(ffty,3)
                #fftx,ffty=detrend(fftx,ffty,10)
                w.clear()
                #w.add(wckgraph.Axes(extent=(0, -1, fftx[-1], 3)))
                w.add(wckgraph.Axes(extent=(0, -1, 6000, 3)))
                w.add(wckgraph.LineGraph([fftx,ffty]))
                w.update()
        if len(chunks)>20:
                print "falling behind...",len(chunks)

def go(x=None):
        global w,fftx,ffty
        print "STARTING!"
        threading.Thread(target=record).start()
        while True:
                graph()

root = Tk()
root.title("SPECTRUM ANALYZER")
root.geometry('500x200')
w = wckgraph.GraphWidget(root)
w.pack(fill=BOTH, expand=1)
go()
mainloop()

» Be the first to comment!



Smoothly Scroll an Image Across a Window with Tkinter vs. PyGame
Posted by
Scott on March 5th, 2010 | 470 words | No Comments (yet)
Filed under: General, Python

The goal is simple: have a super-large image (larger than the window) automatically scroll across a Python-generated GUI window. I already have the code created to generate spectrograph images in realtime, now I just need a way to have them displayed in realtime. tk scrollingAt first I tried moving the coordinates of my images and even generating new images with create_image(), but everything I did resulted in a tacky “flickering” effect (not to mention it was slow). Thankfully I found that self.canv.move(self.imgtag,-1,0) can move a specific item (self.imgtag) by a specified amount and it does it smoothly (without flickering). Here’s some sample code. Make sure “snip.bmp” is a big image in the same folder as this script

from Tkinter import *
import Image, ImageTk

class scrollingImage(Frame):

     def go(self):
		self.canv.move(self.imgtag,-1,0)
		self.canv.update()
		self.after(100,self.go)

     def __init__(self, parent=None):
		Frame.__init__(self, parent)
		self.master.title("Spectrogram Viewer")
		self.pack(expand=YES, fill=BOTH)
		self.canv = Canvas(self, relief=SUNKEN)
		self.canv.config(width=200, height=200)
		self.canv.config(highlightthickness=0)

		sbarV = Scrollbar(self, orient=VERTICAL)
		sbarH = Scrollbar(self, orient=HORIZONTAL)

		sbarV.config(command=self.canv.yview)
		sbarH.config(command=self.canv.xview)

		self.canv.config(yscrollcommand=sbarV.set)
		self.canv.config(xscrollcommand=sbarH.set)

		sbarV.pack(side=RIGHT, fill=Y)
		sbarH.pack(side=BOTTOM, fill=X)

		self.canv.pack(side=LEFT, expand=YES, fill=BOTH)
		self.im=Image.open("./snip.bmp")
		width,height=self.im.size
		#self.canv.config(scrollregion=(0,0,width,height))
		self.canv.config(scrollregion=(0,0,300,300))
		self.im2=ImageTk.PhotoImage(self.im)
		x,y=0,0
		self.imgtag=self.canv.create_image(x,y,\
		     anchor="nw",image=self.im2)
		self.go()

scrollingImage().mainloop()

Alternatively, I found a way to accomplish a similar thing with PyGame. I’ve decided not to use PyGame for my software package however, because it’s too specific and can’t be run well alongside Tk windows, and it would be insanely hard to add scrollbars to the window. However it’s extremely effective at scrolling images smoothly. Anyhow, here’s the code:

import pygame
from PIL import Image

im=Image.open("1hr_original.jpg")
graphic = pygame.image.fromstring(im.tostring(),im.size,im.mode)
screen = pygame.display.set_mode((400, 300))
clock = pygame.time.Clock()
running = 1
x,y=0,0
while running:
   clock.tick(30)
   for event in pygame.event.get():    #get user input
      if event.type == pygame.QUIT:    #if user clicks the close X
           running = 0                 #make running 0 to break out of loop
   screen.blit(graphic, (x, y))
   pygame.display.flip()   #Update screen
   x-=1

» Be the first to comment!



Viewing large Images with Scrollbars Using Python, Tk, and PIL
Posted by
Scott on March 3rd, 2010 | 447 words | No Comments (yet)
Filed under: General, Python, Radio

I don’t have much time to write (I have a million things to do over this spring break!) but I’ll try to post what I discover as I go with the hope that it might help someone else out there. It’s amazing how simple this program is, and it’s frustrating how long it took me to figure out how to code the darn image viewer thing. So, for what it’s worth, here it is.
specview

This little Python program will load an image (pretty much any format) using the Python Imaging Library (which must be installed) and allows you to see it on a scrollable canvas (in two directions) with Tkinter and ImageTk. The above screenshot is of the program viewing the image below:
1hr_original

What is that image? I won’t get ahead of myself, but it’s about 5kHz above 10.140mHz (30m radio band) including a popular QRSS calling frequency representing an entire hour of data. Also, I did all of the math myself with my own python scripts (yes, it eats WAV files and spits out spectrographs). Note that the JPG I uplaoded “full size” is no where near the ACTUAL full size of the image. It’s gagillions of pixels in all directions. Sweet. My goal is to have it scroll in the TK window, with slide-adjustable brightness/contrast/etc. I’ll get there eventually.

from Tkinter import *
import Image, ImageTk

class ScrolledCanvas(Frame):
     def __init__(self, parent=None):
          Frame.__init__(self, parent)
          self.master.title("Spectrogram Viewer")
          self.pack(expand=YES, fill=BOTH)
          canv = Canvas(self, relief=SUNKEN)
          canv.config(width=400, height=200)
          #canv.config(scrollregion=(0,0,1000, 1000))
          #canv.configure(scrollregion=canv.bbox('all'))
          canv.config(highlightthickness=0)

          sbarV = Scrollbar(self, orient=VERTICAL)
          sbarH = Scrollbar(self, orient=HORIZONTAL)

          sbarV.config(command=canv.yview)
          sbarH.config(command=canv.xview)

          canv.config(yscrollcommand=sbarV.set)
          canv.config(xscrollcommand=sbarH.set)

          sbarV.pack(side=RIGHT, fill=Y)
          sbarH.pack(side=BOTTOM, fill=X)

          canv.pack(side=LEFT, expand=YES, fill=BOTH)
          self.im=Image.open("./1hr_original.jpg")
          width,height=self.im.size
          canv.config(scrollregion=(0,0,width,height))
          self.im2=ImageTk.PhotoImage(self.im)
          self.imgtag=canv.create_image(0,0,anchor="nw",image=self.im2)

ScrolledCanvas().mainloop()


» Be the first to comment!



Simple DIY Stealth Apartment Antenna for 20m and 40m!
Posted by
Scott on February 7th, 2010 | 936 words | 4 Comments
Filed under: General, Radio

I have no money for a HF antenna, and even if I did my apartment complex wouldn’t allow it! This is my story, and while I’m no expert I hope that sharing my experience will help encourage others to try crazy things in the spirit of invention. A friend loaned me a Century 21 HF CW-only transceiver which puts out ~20W. As far as an antenna, I was limited to what I could build. I tried a bunch of different designs, including a trash-brew 40m base-loaded vertical, but it didn’t work that well. I found that a “contorted dipole” (I heard it’s officially called a zig-zag design) strung up on my ceiling works surprisingly well. I’ve only had it up a few days, but from Florida I’ve talked to New York on 40m at 20W and Maine on 20m using 20W. Keep in mind that I’m brand new to CW, and that 99% of the conversations out there are way too fast for me to copy, so my greatest limitation is finding a CQ slow enough that I can respond to it.

The beauty of my antenna is four-fold. First, it’s super-cheap (a few bucks worth of parts). Second, it’s off the floor and out of the way (unlike my vertical). Third, it doesn’t require a tuner once it’s set up. Forth, it’s virtually invisible! Seriously, if you walk in my apartment you’d have no idea it’s there unless someone points it out. Check it out…

FROM MY FRONT DOOR:
dipole_apartment_1

THERE ARE 7 WIRES! CAN YOU SEE THEM? BARELY!
dipole_apartment_2

So, will this fly for you? That’s between you and your XYL. Here’s a diagram of my apartment and the antennas which hopefully should make a lot more sense…
apartment_dipole
The orange lines represent a 20m dipole with 2 ground radials rather than 1. The purple lines represent a 40m dipole. Dotted gray lines represent fishing line tied to the end of a wire to keep it stretched. Blue circles are plant hooks. Measurements are similar to regular dipoles (approx. quarter wavelength per leg), but I cut these long and used an antenna tuner to shorten them until I reached a 1:1 SWR. Once the SWR was set, I returned my borrowed antenna analyzer and the resulting antenna network seems pretty stable! I’m totally impressed with myself.
IMG_3091
not too shabby ‘eh?
IMG_3084

The physical assembly involved a package of ceiling-mount (screw-type) plant hooks and a couple packages of 50′ of picture hanging wire from Target (a few bucks total). The coax to the radio is pretty straightforward. Just a short patch of cable running up to the ceiling, then the shield goes one direction (to the 3 ground wires) and the center wire goes in the other direction (to the antenna elements). Both antennas are permanently soldered together, which is fine because SWR stays low and I don’t have to jumper things around when I want to change bands.

Here are some photos to show it more clearly…
IMG_3074
and, yes, that’s a sketch of a painting I haven’t painted yet
IMG_3075

DONT GET CONFUSED BY THOSE COILS! They’re not used for the antenna!!! They’re just there to help weigh down the wire to prevent it from wobbling due to the AC. Seriously, they do nothing, you don’t need them. They’re not even touching the antenna! Which reminds me, the two 20m radials were made from actual wire (because I had it lying around), so they’re coated in yellow. No biggie! No reason other than convenience that I didn’t use the picture hanging wire. Okay, that sums it up.

I hope this information helps! If you build a similar setup, let me know – I’d love to see it. If you have questions, feel free to email me. Remember, I didn’t put much math into this – I just went with approximately quarter wavelength legs and started cutting them until the SWR was down to 1:1, then I didn’t adjust it any more. It’s been several days and SWR seems stable, so no antenna analyzer is needed anymore. Good luck with your project, and with any luck I’ll work ya’ on the band. 73!
–Scott, AJ4VD


» 4 Comments!



« Earlier Posts |
copyright © 2006 swharden@gmail.com