The personal website of Scott W Harden

Simple Python Spectrograph with PyGame

While thinking of ways to improve my QRSS VD high-definitions spectrograph software, I often wish I had a better way to display large spectrographs. Currently I'm using PIL (the Python Imaging Library) with TK and it's slow as heck. I looked into the PyGame project, and it seems to be designed with speed in mind. I whipped-up this quick demo, and it's a simple case audio spectrograph which takes in audio from your sound card and graphs it time vs. frequency. This method is far superior to the method I was using previously to display the data, because while QRSS VD can only update the entire GUI (500px by 8,000 px) every 3 seconds, early tests with PyGame suggests it can do it about 20 times a second (wow!). With less time/CPU going into the GUI, the program can be more responsivle and my software can be less of a drain.

import pygame
import numpy
import threading
import pyaudio
import scipy
import scipy.fftpack
import scipy.io.wavfile
import wave
rate = 12000  # try 5000 for HD data, 48000 for realtime
soundcard = 2
windowWidth = 500
fftsize = 512
currentCol = 0
scooter = []
overlap = 5  # 1 for raw, realtime - 8 or 16 for high-definition


def graphFFT(pcm):
    global currentCol, data
    ffty = scipy.fftpack.fft(pcm)  # convert WAV to FFT
    ffty = abs(ffty[0:len(ffty)/2])/500  # FFT is mirror-imaged
    # ffty=(scipy.log(ffty))*30-50 # if you want uniform data
    print "MIN:t%stMAX:t%s" % (min(ffty), max(ffty))
    for i in range(len(ffty)):
        if ffty[i] < 0:
            ffty[i] = 0
        if ffty[i] > 255:
            ffty[i] = 255
    scooter.append(ffty)
    if len(scooter) < 6:
        return
    scooter.pop(0)
    ffty = (scooter[0]+scooter[1]*2+scooter[2]*3+scooter[3]*2+scooter[4])/9
    data = numpy.roll(data, -1, 0)
    data[-1] = ffty[::-1]
    currentCol += 1
    if currentCol == windowWidth:
        currentCol = 0


def record():
    p = pyaudio.PyAudio()
    inStream = p.open(format=pyaudio.paInt16, channels=1, rate=rate,
                      input_device_index=soundcard, input=True)
    linear = [0]*fftsize
    while True:
        linear = linear[fftsize/overlap:]
        pcm = numpy.fromstring(inStream.read(
            fftsize/overlap), dtype=numpy.int16)
        linear = numpy.append(linear, pcm)
        graphFFT(linear)


pal = [(max((x-128)*2, 0), x, min(x*2, 255)) for x in xrange(256)]
print max(pal), min(pal)
data = numpy.array(numpy.zeros((windowWidth, fftsize/2)), dtype=int)
# data=Numeric.array(data) # for older PyGame that requires Numeric
pygame.init()  # crank up PyGame
pygame.display.set_caption("Simple Spectrograph")
screen = pygame.display.set_mode((windowWidth, fftsize/2))
world = pygame.Surface((windowWidth, fftsize/2), depth=8)  # MAIN SURFACE
world.set_palette(pal)
t_rec = threading.Thread(target=record)  # make thread for record()
t_rec.daemon = True  # daemon mode forces thread to quit with program
t_rec.start()  # launch thread
clk = pygame.time.Clock()
while 1:
    for event in pygame.event.get():  # check if we need to exit
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
    pygame.surfarray.blit_array(world, data)  # place data in window
    screen.blit(world, (0, 0))
    pygame.display.flip()  # RENDER WINDOW
    clk.tick(30)  # limit to 30FPS
Markdown source code last modified on January 18th, 2021
---
title: Simple Python Spectrograph with PyGame
date: 2010-06-19 21:53:25
tags: python, old
---

# Simple Python Spectrograph with PyGame

<b style="font-size: inherit;">While thinking of ways to improve my QRSS VD high-definitions spectrograph software,</b><span style="font-size: inherit;"> I often wish I had a better way to display large spectrographs. Currently I'm using PIL (the Python Imaging Library) with TK and it's slow as heck. I looked into the </span><a href="http://www.pygame.org" style="font-size: inherit;">PyGame</a><span style="font-size: inherit;"> project, and it seems to be designed with speed in mind. I whipped-up this quick demo, and it's a simple case audio spectrograph which takes in audio from your sound card and graphs it time vs. frequency. This method is far superior to the method I was using previously to display the data, because while QRSS VD can only update the entire GUI (500px by 8,000 px) every 3 seconds, early tests with PyGame suggests it can do it about 20 times a second (wow!). With less time/CPU going into the GUI, the program can be more responsivle and my software can be less of a drain.</span>

<div class="text-center img-border">

[![](simple-spectrograph_thumb.jpg)](simple-spectrograph.png)

</div>

</div>

```python
import pygame
import numpy
import threading
import pyaudio
import scipy
import scipy.fftpack
import scipy.io.wavfile
import wave
rate = 12000  # try 5000 for HD data, 48000 for realtime
soundcard = 2
windowWidth = 500
fftsize = 512
currentCol = 0
scooter = []
overlap = 5  # 1 for raw, realtime - 8 or 16 for high-definition


def graphFFT(pcm):
    global currentCol, data
    ffty = scipy.fftpack.fft(pcm)  # convert WAV to FFT
    ffty = abs(ffty[0:len(ffty)/2])/500  # FFT is mirror-imaged
    # ffty=(scipy.log(ffty))*30-50 # if you want uniform data
    print "MIN:t%stMAX:t%s" % (min(ffty), max(ffty))
    for i in range(len(ffty)):
        if ffty[i] < 0:
            ffty[i] = 0
        if ffty[i] > 255:
            ffty[i] = 255
    scooter.append(ffty)
    if len(scooter) < 6:
        return
    scooter.pop(0)
    ffty = (scooter[0]+scooter[1]*2+scooter[2]*3+scooter[3]*2+scooter[4])/9
    data = numpy.roll(data, -1, 0)
    data[-1] = ffty[::-1]
    currentCol += 1
    if currentCol == windowWidth:
        currentCol = 0


def record():
    p = pyaudio.PyAudio()
    inStream = p.open(format=pyaudio.paInt16, channels=1, rate=rate,
                      input_device_index=soundcard, input=True)
    linear = [0]*fftsize
    while True:
        linear = linear[fftsize/overlap:]
        pcm = numpy.fromstring(inStream.read(
            fftsize/overlap), dtype=numpy.int16)
        linear = numpy.append(linear, pcm)
        graphFFT(linear)


pal = [(max((x-128)*2, 0), x, min(x*2, 255)) for x in xrange(256)]
print max(pal), min(pal)
data = numpy.array(numpy.zeros((windowWidth, fftsize/2)), dtype=int)
# data=Numeric.array(data) # for older PyGame that requires Numeric
pygame.init()  # crank up PyGame
pygame.display.set_caption("Simple Spectrograph")
screen = pygame.display.set_mode((windowWidth, fftsize/2))
world = pygame.Surface((windowWidth, fftsize/2), depth=8)  # MAIN SURFACE
world.set_palette(pal)
t_rec = threading.Thread(target=record)  # make thread for record()
t_rec.daemon = True  # daemon mode forces thread to quit with program
t_rec.start()  # launch thread
clk = pygame.time.Clock()
while 1:
    for event in pygame.event.get():  # check if we need to exit
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
    pygame.surfarray.blit_array(world, data)  # place data in window
    screen.blit(world, (0, 0))
    pygame.display.flip()  # RENDER WINDOW
    clk.tick(30)  # limit to 30FPS
```

Simple-Case PyGame Example

I'm starting to investigate PyGame as an alternative to PIL and K for my QRSS VD spectrograph project. This sample code makes a box bounce around a window.

import pygame, sys
pygame.init() #load pygame modules
size = width, height = 320, 240 #size of window
speed = [2, 2] #speed and direction
screen = pygame.display.set_mode(size) #make window
s=pygame.Surface((100,50)) #create surface 100px by 50px
s.fill((33,66,99)) #color the surface blue
r=s.get_rect() #get the rectangle bounds for the surface
clock=pygame.time.Clock() #make a clock
while 1: #infinite loop
        clock.tick(30) #limit framerate to 30 FPS
        for event in pygame.event.get(): #if something clicked
                if event.type == pygame.QUIT: #if EXIT clicked
                        sys.exit() #close cleanly
        r=r.move(speed) #move the box by the "speed" coordinates
        #if we hit a  wall, change direction
        if r.left < 0 or r.right > width: speed[0] = -speed[0]
        if r.top < 0 or r.bottom > height: speed[1] = -speed[1]
        screen.fill((0,0,0)) #make redraw background black
        screen.blit(s,r) #render the surface into the rectangle
        pygame.display.flip() #update the screen
Markdown source code last modified on January 18th, 2021
---
title: Simple-Case PyGame Example
date: 2010-06-15 08:29:03
tags: python, old
---

# Simple-Case PyGame Example

__I'm starting to investigate PyGame__ as an alternative to PIL and K for my QRSS VD spectrograph project. This sample code makes a box bounce around a window.

<div class="text-center img-border">

[![](example_pygame_thumb.jpg)](example_pygame.png)

</div>

```python
import pygame, sys
pygame.init() #load pygame modules
size = width, height = 320, 240 #size of window
speed = [2, 2] #speed and direction
screen = pygame.display.set_mode(size) #make window
s=pygame.Surface((100,50)) #create surface 100px by 50px
s.fill((33,66,99)) #color the surface blue
r=s.get_rect() #get the rectangle bounds for the surface
clock=pygame.time.Clock() #make a clock
while 1: #infinite loop
        clock.tick(30) #limit framerate to 30 FPS
        for event in pygame.event.get(): #if something clicked
                if event.type == pygame.QUIT: #if EXIT clicked
                        sys.exit() #close cleanly
        r=r.move(speed) #move the box by the "speed" coordinates
        #if we hit a  wall, change direction
        if r.left < 0 or r.right > width: speed[0] = -speed[0]
        if r.top < 0 or r.bottom > height: speed[1] = -speed[1]
        screen.fill((0,0,0)) #make redraw background black
        screen.blit(s,r) #render the surface into the rectangle
        pygame.display.flip() #update the screen
```

MEPT Insulation Improves Stability

While it may not be perfect, it's a whole lot better. Below is a capture from this morning of my signal (the waves near the bottom). Compare that to how it was before and you should notice a dramatic improvement! The MEPT is inside a metal box inside a 1-inch-thick Styrofoam box. Very cool!

Markdown source code last modified on January 18th, 2021
---
title: MEPT Insulation Improves Stability
date: 2010-06-12 09:18:14
tags: qrss
---

# MEPT Insulation Improves Stability

__While it may not be perfect__, it's a whole lot better. Below is a capture from this morning of my signal (the waves near the bottom). Compare that to how it was before and you should notice a dramatic improvement! The MEPT is inside a metal box inside a 1-inch-thick Styrofoam box. Very cool!

<div class="text-center img-border">

![](stable.jpg)

</div>

<div class="text-center img-border">

[![](assembled-squished_thumb.jpg)](assembled-squished.jpg)

</div>

June 11th, 2010

Failing Oscilloscope

My oscilloscope decided to die on my right as I finally was able to view my 10 MHz waveform. I used a piece of coax with a load at the connector to the o-scope, and ran the coax to my test points. It was beautiful! ... and lasted about 30 seconds. The culprit seems to be a failing "focus" knob. My images had been getting blurrier by the day, and now it's completely black unless I twist pretty hard on the focus knob. I'd stick a small pot in there, but I have no idea how much voltage/current is being regulated. I'm sure the schematics are posted somewhere, but for now I'm going to try to clean out the potentiometer manually and see if the situation improves. Here are some photos of the circuitry inside this old scope. They don't make stuff like this anymore!

Update: I never got this scope to work again!

Markdown source code last modified on January 18th, 2021
---
title: Failing Oscilloscope
date: 2010-06-11 17:12:36
---

# Failing Oscilloscope

__My oscilloscope decided to die on my right as I finally was able to view my 10 MHz waveform.__ I used a piece of coax with a load at the connector to the o-scope, and ran the coax to my test points. It was beautiful! ... and lasted about 30 seconds. The culprit seems to be a failing "focus" knob. My images had been getting blurrier by the day, and now it's completely black unless I twist pretty hard on the focus knob. I'd stick a small pot in there, but I have no idea how much voltage/current is being regulated. I'm sure the schematics are posted somewhere, but for now I'm going to try to clean out the potentiometer manually and see if the situation improves. Here are some photos of the circuitry inside this old scope. They don't make stuff like this anymore!

<div class="text-center img-border">

[![](panelfront_thumb.jpg)](panelfront.jpg)

[![](ron_thumb.jpg)](ron.jpg)

</div>

Update: I never got this scope to work again!

QRSS Receiver Works... Barely

I completed work on my first RF receiver, and for what it is it seems to work decently. It should be self-explanatory from the photos. It's based around an SA602. As with everything, I don't plan on posting schematics until the project is complete because I don't want people re-creating junky circuits! It's stationed at the University of Florida's club station W4DFU and its spectrograph can be viewed in real time from the QRSS VD - Web Grabber - W4DFU page.

Markdown source code last modified on January 18th, 2021
---
title: QRSS Receiver Works... Barely
date: 2010-06-10 23:27:40
tags: amateur radio, qrss
---

# QRSS Receiver Works... Barely

__I completed work on my first RF receiver__, and for what it is it seems to work decently. It should be self-explanatory from the photos. It's based around an SA602. As with everything, I don't plan on posting schematics until the project is complete because I don't want people re-creating junky circuits! It's stationed at the University of Florida's club station W4DFU and its spectrograph can be viewed in real time from the [__QRSS VD - Web Grabber - W4DFU__](http://ham.w4dfu.ufl.edu:8080/qrss_vd/website/) page.

<div class="text-center img-border img-medium">

[![](IMG_3475_thumb.jpg)](IMG_3475.jpg)

[![](IMG_3482_thumb.jpg)](IMG_3482.jpg)

[![](IMG_34792_thumb.jpg)](IMG_34792.jpg)

[![](dc_qrss_thumb.jpg)](dc_qrss.jpg)

[![](capture_thumb.jpg)](capture.jpg)

</div>

Pages