The personal website of Scott W Harden
March 5th, 2010

Animated Realtime Spectrograph with Scrolling Waterfall Display in 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 real time scrolling spectrograph entirely with Python! It 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 de-trend 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 real time 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 "de-trending filter" activated.

import pyaudio
import scipy
import struct
import scipy.fftpack

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

import Image
import 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:
        # 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:
        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)
        data = chunks.pop(0)
        data = scipy.array(struct.unpack("%dB" % (bufferSize*2), data))
        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)
        updatePic(fftx, ffty)
        reloadPic()

    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.

Markdown source code last modified on January 18th, 2021
---
title: Animated Realtime Spectrograph with Scrolling Waterfall Display in Python
date: 2010-03-05 22:51:21
tags: python, old
---

# Animated Realtime Spectrograph with Scrolling Waterfall Display in 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 real time scrolling spectrograph entirely with Python! It 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 de-trend 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 real time 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 "de-trending filter" activated.

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

[![](spectrogram-scrollbars_thumb.jpg)](spectrogram-scrollbars.png)

</div>

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

[![](nofilter_thumb.jpg)](nofilter.png)
[![](filter_thumb.jpg)](filter.png)

</div>

```python
import pyaudio
import scipy
import struct
import scipy.fftpack

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

import Image
import 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:
        # 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:
        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)
        data = chunks.pop(0)
        data = scipy.array(struct.unpack("%dB" % (bufferSize*2), data))
        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)
        updatePic(fftx, ffty)
        reloadPic()

    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.
March 3rd, 2010

Display large Images with Scrollbars with Python, Tk, and PIL

I wrote a program to display extremely large images in Python using TK. It's interesting how simple this program is, yet frustrating how long it took me to figure out.

This little Python program will load an image (pretty much any format) using the Python Imaging Library (PIL, 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:

What is that image? I won't get ahead of myself, but it's about 5kHz of audio from 10.140mHz which includes a popular QRSS calling frequency. The image displays an hour of data. My ultimate goal is to have it scroll in the TK window, with slide-adjustable brightness/contrast/etc.

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(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()
Markdown source code last modified on January 18th, 2021
---
title: Display large Images with Scrollbars with Python, Tk, and PIL
date: 2010-03-03 19:56:55
tags: python, old
---

# Display large Images with Scrollbars with Python, Tk, and PIL

__I wrote a program to display extremely large images in Python using TK. __It's interesting how simple this program is, yet frustrating how long it took me to figure out.

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

[![](specview_thumb.jpg)](specview.png)

</div>

__This little Python program__ will load an image (pretty much any format) using the Python Imaging Library (PIL, 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:

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

[![](1hr_original_thumb.jpg)](1hr_original.jpg)

</div>

__What is that image?__ I won't get ahead of myself, but it's about 5kHz of audio from 10.140mHz which includes a popular QRSS calling frequency. The image displays an hour of data. My ultimate goal is to have it scroll in the TK window, with slide-adjustable brightness/contrast/etc.

```python
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(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()
```
February 7th, 2010

Simple DIY Stealth Apartment Antenna for HF

I don't want to spend lots of 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 communicated with New York on 40m at 20W and Maine on 20m using 20W. Keep in mind that I'm brand new to CW, and that 90% 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 this antenna is four-fold. First, it's cheap (a few bucks worth of parts). Second, it's off the floor and out of the way (unlike my vertical antenna designs). Third, it doesn't require a tuner to operate 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.

So, will this fly for you? That's between you and your XYL. 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!

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.

Don't 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!

Markdown source code last modified on January 18th, 2021
---
title: Simple DIY Stealth Apartment Antenna for HF
date: 2010-02-07 17:49:49
tags: amateur radio, old
---

# Simple DIY Stealth Apartment Antenna for HF

__I don't want to spend lots of 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](http://www.swharden.com/blog/2010-01-30-rainy-mornings-and-boring-bicuspids/), 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 communicated with New York on 40m at 20W and Maine on 20m using 20W. Keep in mind that I'm brand new to CW, and that 90% 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.

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

[![](dipole_apartment_1_thumb.jpg)](dipole_apartment_1.jpg)
[![](dipole_apartment_2_thumb.jpg)](dipole_apartment_2.png)

</div>

__The beauty of this antenna is four-fold.__ First, it's cheap (a few bucks worth of parts). Second, it's off the floor and out of the way (unlike my vertical antenna designs). Third, it doesn't require a tuner to operate _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.

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

[![](IMG_3084_thumb.jpg)](IMG_3084.jpg)
[![](IMG_3091_thumb.jpg)](IMG_3091.jpg)
[![](IMG_3075_thumb.jpg)](IMG_3075.jpg)
[![](IMG_3074_thumb.jpg)](IMG_3074.jpg)

</div>

__So, will this fly for you?__ That's between you and your XYL. 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!

__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.

__Don't 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!

February 2nd, 2010

Convert Text to CW Morse Code with Linux

I wanted a way to have a bunch of Morse code mp3s on my mp3 player (with a WPM/speed that I decide and I found an easy way to do it with Linux. Rather than downloading existing mp3s of boring text, I wanted to be able to turn ANY text into Morse code, so I could copy something interesting (perhaps the news? hackaday? bash.org?). It's a little devious, but my plan is to practice copying Morse code during class when lectures become monotonous. [The guy who teaches about infectious diseases is the most boring person I ever met, I learn nothing from class, and on top of that he doesn't allow laptops to be out!] So, here's what I did in case it helps anyone else out there...

Step 1: Get the Required Programs

Make sure you have installed Python, cwtext, and lame. Now you're ready to roll!

Step 2: Prepare the Text to Encode

I went to Wikipedia and copy/pasted an ENTIRE article into a text file called in.txt. Don't worry about special characters (such as " and * and #), we'll fix them with the following python script.

import os
import time
f = open("out.txt")
raw = f.read()
f.close()

cmd = """echo "TEST" | cwpcm -w 7 | """
cmd += """lame -r -m m -b 8 --resample 8 -q9 - - > text.mp3"""

i = 0
for chunk in raw.split("n")[5:]:
    if chunk.count(" ") > 50:
        i += 1
        print "nnfile", i, chunk.count(" "), "wordsn"
        do = cmd.replace("TEST", chunk).replace("text", "%02d" % i)
        print "running:", do,
        time.sleep(1)
        print "nnSTART ...",
        os.system(do)
        print "DONE"

Step 3: Generate Morse Code Audio

There should be a new file, out.txt, which is cleaned-up nicely. Run the following script to turn every paragraph of text with more than 50 words into an mp3 file...

import os
f = open("out.txt")
raw = f.read()
f.close()
cmd = """echo "TEST" | cwpcm -w 13 | sox -r 44k -u -b 8 -t raw - text.wav"""
cmd += """; lame --preset phone text.wav text.mp3; rm text.wav"""
i = 0
for chunk in raw.split("n")[5:]:
    if chunk.count(" ") > 50:
        i += 1
        print i, chunk.count(" "), "words"
        os.system(cmd.replace("TEST", chunk).replace("text", "%02d" % i))

Now you should have a directory filled with mp3 files which you can skip through (or shuffle!) using your handy dandy mp3 player. Note that "-w 13" means 13 WPM (words per minute). Simply change that number to change the speed.

Good luck with your CW practice!

Markdown source code last modified on January 18th, 2021
---
title: Convert Text to CW Morse Code with Linux
date: 2010-02-02 10:58:54
tags: amateur radio, python, old
---

# Convert Text to CW Morse Code with Linux

__I wanted a way to have a bunch of Morse code mp3s on my mp3 player (with a WPM/speed that I decide__ and I found an easy way to do it with Linux. Rather than downloading existing mp3s of boring text, I wanted to be able to turn ANY text into Morse code, so I could copy something interesting (perhaps the news? hackaday? bash.org?). It's a little devious, but my plan is to practice copying Morse code during class when lectures become monotonous. \[The guy who teaches about infectious diseases is the most boring person I ever met, I learn nothing from class, and on top of that he doesn't allow laptops to be out!\] So, here's what I did in case it helps anyone else out there...

### Step 1: Get the Required Programs

Make sure you have installed [Python](http://www.Python.org), [cwtext](http://cwtext.sourceforge.net/), and [lame](http://lame.sourceforge.net/). Now you're ready to roll!

### Step 2: Prepare the Text to Encode

I went to Wikipedia and copy/pasted an ENTIRE article into a text file called in.txt. Don't worry about special characters (such as " and \* and \#), we'll fix them with the following python script.

```python
import os
import time
f = open("out.txt")
raw = f.read()
f.close()

cmd = """echo "TEST" | cwpcm -w 7 | """
cmd += """lame -r -m m -b 8 --resample 8 -q9 - - > text.mp3"""

i = 0
for chunk in raw.split("n")[5:]:
    if chunk.count(" ") > 50:
        i += 1
        print "nnfile", i, chunk.count(" "), "wordsn"
        do = cmd.replace("TEST", chunk).replace("text", "%02d" % i)
        print "running:", do,
        time.sleep(1)
        print "nnSTART ...",
        os.system(do)
        print "DONE"
```

### Step 3: Generate Morse Code Audio

There should be a new file, out.txt, which is cleaned-up nicely. Run the following script to turn every paragraph of text with more than 50 words into an mp3 file...

```python
import os
f = open("out.txt")
raw = f.read()
f.close()
cmd = """echo "TEST" | cwpcm -w 13 | sox -r 44k -u -b 8 -t raw - text.wav"""
cmd += """; lame --preset phone text.wav text.mp3; rm text.wav"""
i = 0
for chunk in raw.split("n")[5:]:
    if chunk.count(" ") > 50:
        i += 1
        print i, chunk.count(" "), "words"
        os.system(cmd.replace("TEST", chunk).replace("text", "%02d" % i))
```

Now you should have a directory filled with mp3 files which you can skip through (or shuffle!) using your handy dandy mp3 player. Note that "-w 13" means 13 WPM (words per minute). Simply change that number to change the speed.

Good luck with your CW practice!

January 30th, 2010

More Antenna Tinkering

Dental school is taking a lot of time away from me. I try my best to compartmentalize dental school into a chunk of my schedule (a massive chunk), trying to use the rest of the time to spend with my family (wife) and when she's at work work on electronics (which seems to be radio these days). A few weeks ago I took the final amateur radio license exam and received my Amateur Extra license. It's a bunch of technical questions about radio circuitry, antenna theory, and other random stuff. You can see what I mean by taking an online practice test! I applied for a new call sign (extra class operators can get shorter call signs). It seems the FCC gave me a VD. AJ4VD that is! Yes, my old call sign KJ4LDF has gone out the window as I am now AJ4VD! In Morse code, that's .- .--- ....- ...- -..

I made my first Morse code contact from my apartment! This is the radio I'm using. It's a Ten-Tec Century 21 HF CW transceiver which puts out ~30W. I'm using a super-cheap but surprisingly functional homebrew base-loaded vertical antenna. The main vertical element is quarter-inch copper pipe from Home Depot (a couple bucks) cut with 1'' to spare from my 10ft ceiling. Therefore, it's a less-than quarter-wave vertical element, requiring a tuning coil (variable inductor at the base)...

Here you can start to see the tuning coils. Briefly, I scraped a deep gash in the copper pipe such that a big glob of solder would adhere to it, and stuck a wire (yellow, coated) into that solder so it's a good connection to the pipe. I then started wrapping the wire around a few toilet paper rolls [it's all I could find at the time!] adding tap points (regions of exposed wire) every other turn. This functioned somewhat, but didn't allow for fine-tuning (pun intended). I therefore scrapped the bottom half of the cardboard cylinder/coil and constructed a slightly more elegant solution...

That's an Olvaltine container. Yeah, I know, "More chocolaty Olvaltine please!" I used a rotary tool to scrape some measured/templated gashes on each side to give the wire (picture frame hanging wire from Target, 50' for $1.99) something to rest in. It turned out not to be enough, so I hot-glued the wire into the holes. This gives me a lot of exposed wire space to allow me to "tap" the coil wherever I want. By modifying where I clip onto the coil, I modify the length of wire in the coil that's used, therefore modifying the inductance of the coil, allowing for some tuning capabilities. Although it has a narrow tuning range, using the current setup I'm able to get my SWR down to 1:1 on 40m (nice!).

I made a couple of contacts since I got the rig last night. First was K4KOR in central TN, who was calling CQ. I replied (slowly), and he came back to me (blazing fast Morse code). I was unable to copy ANYTHING he said (I'm not that good of an auditory decoder yet!) I'm sure he's incredibly nice and it wasn't intentional, but I had to give up the QSO. I know he copied my call, and I copied his, but I didn't copy ANYTHING else he said. Does that count as my first contact? This morning I fired up the rig at 9:15 and heard W4HAY calling CQ from Northeast TN. I replied, stating that I'm new to CW so go slowly, and he was AMAZINGLY nice at sending me code at a snails pace. I was able to copy 90% of what he said, and will consider him my first solid contact!

Markdown source code last modified on January 18th, 2021
---
title: More Antenna Tinkering
date: 2010-01-30 13:41:59
tags: amateur radio
---

# More Antenna Tinkering

__Dental school is taking a lot of time away from me.__ I try my best to compartmentalize dental school into a chunk of my schedule (a massive chunk), trying to use the rest of the time to spend with my family (wife) and when she's at work work on electronics (which seems to be radio these days). A few weeks ago I took the final amateur radio license exam and received my Amateur Extra license. It's a bunch of technical questions about radio circuitry, antenna theory, and other random stuff. You can see what I mean by taking an [online practice test](http://www.eham.net/exams/)! I applied for a new call sign (extra class operators can get shorter call signs). It seems the FCC gave me a VD. AJ4VD that is! Yes, my old call sign [KJ4LDF](http://www.qrz.com/callsign/KJ4LDF) has gone out the window as I am now [AJ4VD](http://www.qrz.com/callsign/AJ4VD)! In Morse code, that's `.- .--- ....- ...- -..`

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

[![](Ten_Tec_Century_21_thumb.jpg)](Ten_Tec_Century_21.jpg)

</div>

__I made my first Morse code contact__ from my apartment! This is the radio I'm using. It's a Ten-Tec Century 21 HF CW transceiver which puts out ~30W. I'm using a super-cheap but surprisingly functional homebrew base-loaded vertical antenna. The main vertical element is quarter-inch copper pipe from Home Depot (a couple bucks) cut with 1'' to spare from my 10ft ceiling. Therefore, it's a less-than quarter-wave vertical element, requiring a tuning coil (variable inductor at the base)...

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

[![](antennaBig_thumb.jpg)](antennaBig.jpg)

</div>

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

[![](antennaBigger_thumb.jpg)](antennaBigger.jpg)
[![](antennaCoil_thumb.jpg)](antennaCoil.jpg)

</div>

__Here you can start to see the tuning coils.__ Briefly, I scraped a deep gash in the copper pipe such that a big glob of solder would adhere to it, and stuck a wire (yellow, coated) into that solder so it's a good connection to the pipe. I then started wrapping the wire around a few toilet paper rolls \[it's all I could find at the time!\] adding tap points (regions of exposed wire) every other turn. This functioned somewhat, but didn't allow for fine-tuning (pun intended). I therefore scrapped the bottom half of the cardboard cylinder/coil and constructed a slightly more elegant solution...

__That's an [Olvaltine](http://en.wikipedia.org/wiki/Ovaltine) container.__ Yeah, I know, "More chocolaty Olvaltine please!" I used a rotary tool to scrape some measured/templated gashes on each side to give the wire (picture frame hanging wire from Target, 50' for $1.99) something to rest in. It turned out not to be enough, so I hot-glued the wire into the holes. This gives me a lot of exposed wire space to allow me to "tap" the coil wherever I want. By modifying where I clip onto the coil, I modify the length of wire in the coil that's used, therefore modifying the inductance of the coil, allowing for some tuning capabilities. Although it has a narrow tuning range, using the current setup I'm able to get my SWR down to 1:1 on 40m (nice!).

__I made a couple of contacts since I got the rig last night.__ First was [K4KOR](http://www.qrz.com/db/k4kor) in central TN, who was calling CQ. I replied (slowly), and he came back to me (blazing fast Morse code). I was unable to copy ANYTHING he said (I'm not that good of an auditory decoder yet!) I'm sure he's incredibly nice and it wasn't intentional, but I had to give up the QSO. I know he copied my call, and I copied his, but I didn't copy ANYTHING else he said. Does that count as my first contact? This morning I fired up the rig at 9:15 and heard [W4HAY](http://www.qrz.com/db/w4hay) calling CQ from Northeast TN. I replied, stating that I'm new to CW so go slowly, and he was AMAZINGLY nice at sending me code at a snails pace. I was able to copy 90% of what he said, and will consider him my first solid contact!

Pages