1:02:51 am on 2/9/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
» 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

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

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


» Be the first to comment!



How I Cheaply Created Custom Glossy Double-Sided QSL Cards in 1 Day
Posted by
Scott on February 7th, 2010 | 1,195 words | No Comments (yet)
Filed under: General

I’m posting this information to the world hoping that someone else in a position similar to mine can benefit from the experience I gained through trial and error when trying to rapidly design/develop professional-looking QSL cards for as little cost and risk as possible. I Googled around for this information, but didn’t find anything too helpful, so I figured I’d share my story! For those of you who don’t know, QSL cards are like postcards which amateur radio operators often mail to one another after making long distance contacts. In addition to providing tangible proof of the communication, they’re cool mementos to tote around to show people / remind yourself who you’ve made contact with over the years. QSL cards display information bout the contact (time, date, call sign, frequency, signal report, etc.) and sometimes contain extra pictures/graphics which make them unique and appealing.

Once I got a HF rig for my apartment (a Century 21 CW-only HF rig which puts out ~30 watts, pictured below), I started making contacts and getting QSL cards myself, so I wanted to send some nice ones in return. Being a poor college student (and a graduate student at that), I was extremely cash-limited, and didn’t want to sit around for weeks while my cards were professionally printed. I wanted a fast solution. This post describes how I created amazingly cool QSL cards in a few hours, and for less than $0.25 each!

My QSL card:
12_donefront

Step 1: Design the cards with PERFECT dimensions. Here’s the deal. The most cost-effective way to print nice digital images is my local Target (a store with a 1-hr photo lab which accepts JPEGs as the image source for $0.20 cents a picture), but the snag was that they only print 4” x 6”. QSL cards need to be 3.5” by 5.25”. I used Inkscape to create an image exactly 4” by 6”, and inside of it I drew a border 3.5” by 5.25”. Everything outside that border I made black. I designed my QSL card INSIDE that border, such that when the images would be printed I could trim-off the black border and have a perfect 3.5” by 5.25” QSL card.

This is how the image turned out:
florida

This is what the 1-hr photos looked like:
01_qsl_photos

Step 2: Print the reverse side on full-size label paper. All I needed was some framed boxes for QSL information, so I quickly sketched up the design in Inkscape and saved it in the same format as before (4” by 6”). I left a LOT of white space around the edges so it’s very forgiving down the line. I then printed the design on full-page label paper (full-sheet stickers, available at most office stores cheaply in the printer paper section), placing 4 “backs” per page.

Here’s what the adhesive paper looked like after printing:
03_backs

Step 3: Attach backings to QSL cards. This part is easy if you have a paper cutter. I purchased mine ~5yrs ago and I *LOVE* it. It’s almost as useful as my soldering iron. Seriously, so convenient. I wouldn’t dream of doing this with scissors! Anyhow, roughly cut the sticker paper into quarters:
04_cutback
05_back

Next, peel and stick on the backs of cards. DONT WORRY ABOUT OVERHANG! We’ll take care of that later…
06_peel
07_overhang

Step 4: Trim the edges. Make sure you do this step AFTER applying the sticker. This was the secret that I wish I realized a while ago. If you trim first, sticker placement is critical and difficult. If you place the sticker BEFORE you trim, you get *PERFECT* edges every time. Way more professional…
09_cut
10_nice

How nice does that look? If you did your math correctly, your new dimensions should be EXACTLY 3.5” by 5.25”. Let’s view the back…
11_niceback

Perfect!!!

Step 5: fill-out information. I decided to use a metallic Sharpie to write the name of the call sign I send this to on the front of my card. How cool does that look? This is what the front/back of this card looks like after filling it out (Hi W2BFE in Maine! You’re a celebrity!).
12_silver2
14_scribe

I hope this information helps you. If you print your own QSL cards using this (or a similar) method, let me know about it! I have to say, for ~5 / $1, these don’t look to bad. It’s especially useful if you only want to print a few cards! Good luck.
–Scott, AJ4VD

12_donefront


» Be the first to comment!



Converting ASCII Text to CW Morse Code with Linux
Posted by
Scott on February 2nd, 2010 | 453 words | No Comments (yet)
Filed under: General

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 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 0: GET THE REQUIRED PROGRAMS! Yes, there’s a step zero. Make sure you have installed Python, cwtext, and lame. Now you’re ready to roll!

Step 1: PREPARE SOME TEXT! 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 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"""

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

Step 2: MAKE MP3s OF THE TEXT! 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…

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"""
import os
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!
–Scott (AJ4VD)


» Be the first to comment!



Rainy Mornings and Boring Bicuspids
Posted by
Scott on January 30th, 2010 | 1,437 words | No Comments (yet)
Filed under: General

Alas, another fleeting patch of free time has been bestowed upon me. Just like last semester, this semester (my second in a 4 year dental school) started off tough from day one. After Christmas break it was hard to walk into class at 8am and hit the ground running, but I managed to get the hang of it after a few days. I’ve heard medical school being described as “trying to drink from a fire hydrant”, but I think a more appropriate analogy would involve a treadmill set too fast. You have to work as hard as you can as soon as your feet touch the ground, and you might be able to keep up for now but you don’t know how much longer you can go before you tumble. I never really tumble, but I always feel like I’m about to. Overall, I can’t complain. I’ve managed to compartmentalize dental school into a chunk of my schedule (albeit a massive chunk), leaving time to spend with my family (wife) and when she’s at work, time to spend playing with electronics (which seems to be radio at this stage of my life).

Rather than bore the internet with descriptions of what I’ve been up to in dental school, I’ll focus on the interesting aspects of my most recent endeavors. A few weeks ago I took the final (third-level, extra class) amateur radio license exam. 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 passed [whew!] and applied for a new call sign (extra class operators can have shorter call signs). 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 [.- .--- ....- ...- -..]. Speaking of code, I made my first contact in Morse code from my apartment! Let me set the scene for you…
Ten_Tec_Century_21
This is the radio I’m using. It’s a Ten-Tec Century 21 HF CW transceiver which puts out ~30W.
antennaBig
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)…
antennaBigger
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…
antennaCoil
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! How cool is that?

Now, for a QSL card… For anyone who doesn’t know, one hobby associated with amateur radio is the sending and receiving of QSL cards from around the country/world. A QSL card is basically a post card from the station you talked with which provides physical, tangible confirmation that the conversation successfully took place. Wikipedia shows a few examples. Anyway, I’d love to send/receive some (I know it’s silly, but it’s motivating goal to keep working on improving my code). I’m working on designs but none of them seem to “pop”. I want to get a few printed ASAP, but I don’t want to pull the trigger too early.

I’m going for something Florida-ish. Here’s what I’ve come up with so far…

qsl_space2

qsl_gator

qsl_liz

qsl_gator2

qsl_sunset2

I’ve ordered them from my most to not-so favorite. They’re all “good” (I don’t really care for the last one; it’s too light) but none of them explode in my brain when I see them. I want something cool! These seem so… plain. [sigh] Maybe I’ll get a cool idea in the next few days…

And, as a closing note, Misia performing “Everything” (my favorite song) in Seoul, Korea:


» Be the first to comment!



CEJ, QRSS, and Life Recontemplation
Posted by
Scott on January 1st, 2010 | 1,324 words | 2 Comments
Filed under: Circuitry, General, Radio

Yes, I did it. I’m probably the first (and let’s hope the last) human to ever write CEJ (the abbreviation for the cemento-enamel junction, a dentistry term) immediately before the letters QRSS (extremely slow speed Morse code transmissions, a radio frequency term). Anyhow, thanks in part to the temporary cessation of the tortuously monotonous dental school I’m enrolled in, I have had some time to put into random obscure hobbies. For example, I’ve become somewhat obsessed with QRSS, an obscure sub-niche of amateur radio (ham radio).

I only have a couple of minutes to write, so I’ll be concise. In brief, QRSS uses extremely simple radio transmitters at extremely low power to send an extremely slow Morse code message over an extremely large distance to extremely sensitive receivers which are extremely dependent on computers to decode. While you might be able to send a voice message across the ocean with ~100 watts of power, there are guys sending messages with 100 milliwatts (one tenth of a watt! you can get more than that from a couple AA batteries!). The theory is that if you send the signals slow enough, and average the audio data (fast Fourier transformation) over a long enough time, weak signals below the noise threshold will stand out enough to be copied visually.
qrss_kj4ldf

Without going into more detail than that, this is the kind of stuff I’ve been copying the last couple days. The image is a slow time-averaged waterfall-type FFT display of 10.140mhz from a Mosley-pro 67 yagi mounted ~180 ft in the air connected to a Kenwood TS-940S transceiver sending data to a PC through a SignaLink USB sound card. Red ticks represent 10 seconds. Therefore the frame above is ~10 minutes of audio. The trace on the image is from two different transmitters. The upper trace is from VA3STL’s QRSS quarter-watt transmitter from Canada described here and pictured below. The lower trace is from WA5DJJ’s QRSS quarter-watt transmitter in New Hampshire, described and pictured here. Notice my call sign (KJ4LDF) at the bottom of the page!
qrss_transmitter

^^^ That’s the ACTUAL transmitter I’m hearing from Canada!!!

I don’t know why I’m drawn to QRSS. Perhaps it’s the fact that it’s a hobby which only a handful of people have ever participated in. It uses computers and software, but unlike SDRs (software-defined radios) they don’t require complicated equipment, and a QRSS transmitter or receiver can be built from a few bucks’ worth of parts.

HELPFUL FOR ANYONE WHO USES ARGO!!! 10_01_01_00009There’s a popular QRSS “grabber” software for Windows called Argo. It dumps out screenshots of itself every few minutes, but doesn’t assemble them together!!!!! It’s so annoying. I therefore took it upon myself to write a script to assemble several (or thousands) of Argo screen dumps together as a single image. It’s a script for ImageJ. To use it, first install MBF’s ImageJ. Open ImageJ, drag and drop a DIRECTORY of screenshots / captures into the program to open them as a stack, make a new macro, copy/paste the following code into it, and hit CTRL+R to run it, and poof! The output is a gorgeous panoramic shot like below.long

And here’s the script to automate the process…

makeRectangle(13, 94, 560, 320);
run("Crop");
rename("source");
frames = nSlices();
newImage("long", "RGB White", (frames-1)*560, 320, 1);
for (i=0; i<frames; i++) {
	selectWindow("source");
	setSlice(i+1);
	run("Select All");
	run("Cut");
	selectWindow("long");
	run("Paste");
	makeRectangle(i*560, 0, 560, 320);
}
//run("8-bit");
//run("Enhance Contrast", "saturated=0.5");
selectWindow("source");
close();

As far as life recontemplation goes, I’m discovering that it’s not the attainment of a goal that gives me pleasure; it’s the pursuit of the goal. Perhaps that’s why I peruse hobbies (goals?) which are notoriously difficult, and further challenge myself by doing things in weird, quirky ways. For example, I’d love to get into radio, BUT I HAVE NO MONEY!. Yeah, an all-band 100-watt HF/VHF/UHF rig would be nice, but I don’t have hundreds of dollars to fork over. Worse yet, in the technical sense, I do, I’m just trying to be responsible and saving it for emergencies / tuition and waiting until I’m a dentist (a.k.a. have a job) before I spend money on things that make me happy. Anyway, without complaining I built a non-elegant but surprisingly functional base-loaded vertical HF antenna for my apartment balcony (don’t worry neighbors, it’s taken inside after every use). It’s mainly for receive, but I don’t see any reason why it couldn’t be used for QRP transmitting! Here it is on my balcony…
ant_1

As you can see, it’s ghetto. Yes, that’s an antenna made from copper pipe, wire, and toilet paper rolls. I’ve wound the wire around the base and created various tap points so it serves as a variable inductor depending on where I gator-clip the radio. Not pictured are 33′ radials running inside my apartment serving as adequate grounding for 40m operation. The antenna feeds into a Pixie II direct conversion receiver / QRP transmitter which dumps its output to a laptop computer. Note that I did *NOT* use this setup to receive my beautiful QRSS signals. With that being said, I have copied PSK-31 transmissions from Canada with this setup. It works way better than a long / random wire antenna because it dramatically reduces noise. Here’s a closeup of the tap points on the inductor base…
ant_2

That’s it for today folks! Back to my crazy projects. Take care!

UPDATE: VA3STL mentioned me on his site! Woo hoo!


» 2 Comments!



HF CW on the Cheap
Posted by
Scott on December 10th, 2009 | 611 words | 4 Comments
Filed under: Circuitry, General, Microcontrollers, Radio

Okay, here’s a preview of my project plans for Christmas break! My goal is to make a fun and comfortably functional HF CW transmitter / direct-conversion receiver capable of working any HF band by merging the incredibly simple and cheap (~$10) Pixie II CW transceiver with a SI 570 digital programmable oscillator controlled by an ATTiny 2313 microcontroller! (it’s often used in SDRs controlled by USB as seen here).

AUDIO FILES!!! [80m.mp3] and [40m.mp3] are already ready already!

Preliminary work demonstrates a functional receiver powered by a crystal. I don’t think the currently-configured digital oscillator is putting out enough power to run the circuit, but it’ll take more time to get that up and running. For now, here are some photos of what I’ve got working and real sound clips of the thing are below. I’m happy with the case I built it in (thanks Ron!), and happy with my level drilling of the holes for power, output, and the CW key!
cw_everything
Briefly, the device is powered by a 9V battery. It’s hooked up to an 80m dipole antenna. Output is fed into a computer sound card for amplification / PSK31 analysis.
cw_coke
cw_close
cw_open2
The internals reveal that it’s a simple circuit powered by a single crystal. More crystals are tucked in the case, stuck in foam, for easy transport.
40mPSK
Decoding of PSK31 is happening here, using output from my circuit with the 40m crystal in it.

AUDIO FILES approx. 1 minute in length from my circuit:
>> [80m.mp3]
>> [40m.mp3]

Wish me luck! This project is just beginning…

now for a random video/song:


» 4 Comments!



Dental School is Boring
Posted by
Scott on December 5th, 2009 | 395 words | 1 Comment
Filed under: General

Yeah, I said it. I have a couple minutes to write tonight so I decided to record what I’ve been up to just for the record. I’m approaching the end of my first semester of dental school. I had a very shaky start, and quite a few low grades in the beginning. Luckily, things turned around for me a few months ago. I have a small group of people I study with, I’m getting decent grades, I seem to be doing well in the dental lab, and I actually have a wee bit of free time to purse personal interests (namely programming [python, C, and perl], amateur radio, and a [declassified] top-secret SheevaPlug project). However, as of now, dental school is unbelievably boring to me. I’m sure it will become more interesting with time, but right now it’s nothing but monotonously memorizing boring facts. I’m hanging onto whatever little side projects I can just to keep myself sane. So, with that being said, I’ll get back to my work. I’m posting a short interview I found interesting. This is the guy who runs IsoHunt.com.


» 1 Comment so far...



Mental Viscosity
Posted by
Scott on September 23rd, 2009 | 400 words | 1 Comment
Filed under: Circuitry, Dentistry, General, Microcontrollers, Prime Numbers

A few weeks into dental school I feel I’m fairing decently. I have reached a point where I know everything will be okay, but am still disappointed at the [immense] amount of time it requires. There are so many things I wish I could do, but all of my projects need to be placed on a 4-year hiatus. I can bask in the satisfaction of the few projects I completed this summer, and I only hope that it’s enough to last me for four years. Dentistry, while important, is nothing more than emulation/repetition of what everybody else does. I simply have to satisfy my creative and ingenuitive desires in my hobbies, whatever they may be. For now, this website will cease to grow. Perhaps when I become more in control of my studies I will contribute to it, but in all likelihood I won’t be able to do anything worth writing about until 4 years from now [sigh]. With that being said, adieu, and goodnight.

I realized I never posted video of my finished prime number generator, so here it is. Full details are described on the project page. In brief, the 2-digit display on the left is the last two digits (in base-10, decimal) of a number currently being tested for primeness. This number is also displayed on the bottom red bar above the yellow lights (in base-2, binary). Once proven to be prime (by attempting to divide it by every number between 2 and its square root, every 1000th attempted number shown in yellow lights in binary), it’s loaded onto the top row of red lights (binary) and on the character LCD. N represents the Nth prime, with V representing its value. Half way through the video, the display says that the 16,595,044′th prime (N) equals 306,692,621 (V). Don’t believe it? Check my work.


» 1 Comment so far...



Wrapping-Up the Summer
Posted by
Scott on August 22nd, 2009 | 318 words | 1 Comment
Filed under: General

I only have today and tomorrow left! I begin dental school [shrieks] on Monday, and with the summer nearing its end I’m trying to wrap-up a bunch of project loose ends. Most notably, I still have to complete my microcontroller-powered prime number generator. It’s 99% complete, but if I don’t finish it now I never well! There’s also the halfway finished SQL HTTP hit logger hackaday_ecgwhich is fully implemented, but lacks graphing capabilities. I have two scientific manuscripts I’m trying to complete and have ready for publication soon as well. I did, however, manage to complete my DIY ECG project! It was even featured on HackADay.com – score! Just think, this summer I graduated from UCF’s School of Biomedical Science with my Master’s in molecular biology, got accepted to UF’s College of Dentistry, submitted manuscripts to the Journal of Comparative Neurology, got featured on HackADay, and got my amateur radio license, and even upgraded to general class! Oh yeah, and there’s that oscilloscope I landed for $5. Yes, this summer is one to be proud of. [smiles softly]


» 1 Comment so far...



DIY ECG Machine On The Cheap
Posted by
Scott on August 14th, 2009 | 6,672 words | 21 Comments
Filed under: Circuitry, DIY ECG, General, Microcontrollers, Python

Note from the Author: This page documents how I made an incredibly simple ECG machine with a minimum of parts to view the electrical activity of my own heart. Feel free to repeat my experiment, but do so at your own risk. There are similar projects floating around on the internet, but I aim to provide a more complete, well-documented, and cheaper solution, with emphasis on ECG processing and analysis, rather than just visualization. If you have any questions or suggestions please contact me. Also, if you attempt this project yourself I’d love to post your results! Good luck!
–Scott

Background

You’ve probably seen somebody in a hospital setting hooked up to a big mess of wires used to analyze their heartbeat. ecgmanThe goal of such a machine (called an electrocardiograph, or ECG) is to amplify, measure, and record the natural electrical potential created by the heart. Note that cardiac electrical signals are different than heart sounds, which are listened to with a stethoscope. The intrinsic cardiac pacemaker system is responsible for generating these electrical signals which serve to command and coordinate contraction of the four chambers at the heart at the appropriate intervals [atria (upper chambers) first, then the ventricles (lower chambers) a fraction of a second later], and their analysis reveals a wealth of information about cardiac regulation, as well insights into pathological conditions. Each heartbeat produces a similar pattern in the ECG signal, called a PQRST wave. ecg_principle_slow [picture] The smooth curve in the ECG (P) is caused by the stimulation of the atria via the Sinoatrial (SA) node in the right atrium. There is a brief pause, as the electrical impulse is slowed by the Atrioventricular (AV) node and Purkinje fibers in the bundle of His. The prominent spike in the ECG (the QRS complex) is caused by this step, where the electrical impulse travels through the inter-ventricular septum and up through the outer walls of the ventricles. The sharp peak is the R component, and exact heart rate can be calculated as the inverse of the R-to-R interval (RRi). Fancy, huh?

Project Goal

The goal of this project is to generate an extremely cheap, functional ECG machine made from common parts, most of which can be found around your house. This do-it-yourself (DIY) ECG project is different than many others on the internet in that it greatly simplifies the circuitry by eliminating noise reduction components, accomplishing this via software-based data post-processing. Additionally, this writeup is intended for those without any computer, electrical, or biomedical experience, and should be far less convoluted than the suspiciously-cryptic write-ups currently available online. In short, I want to give everybody the power to visualize and analyze their own heartbeat!

The ECG of my own heart:

ecg31

Video Overview

I know a lot of Internet readers aren’t big fans of reading. Therefore, I provided an outline of the process in video form. Check out the videos, and if you like what you see read more!

Video 1/3: Introducing my ECG machine

Video 2/3: Recording my ECG

Video 3/3: Analyzing my ECG

Electrical Theory

Measurement: The electrical signals which command cardiac musculature can be detected on the surface of the skin. In theory one could grab the two leads of a standard volt meter, one with each hand, and see the voltage change as their heart beats, but the fluctuations are rapid and by the time these signals reach the skin they are extremely weak (a few millionths of a volt) and difficult to detect with simple devices. Therefore, amplification is needed.

Amplification: A simple way to amplify the electrical difference between two points is to use a operational amplifier, otherwise known as an op-amp. The gain (multiplication factor) of an op-amp is controlled by varying the resistors attached to it, and an op-amp with a gain of 1000 will take a 1 millivolt signal and amplify it to 1 volt. There are many different types of microchip op-amps, and they’re often packaged with multiple op-amps in one chip (such as the quad-op-amp lm324, or the dual-op-amp lm358n). Any op-amp designed for low voltage will do for our purposes, and we only need one.

Noise: Unfortunately, the heart is not the only source of voltage on the skin. Radiation from a variety of things (computers, cell phones, lights, and especially the wiring in your walls) is absorbed by your skin and is measured with your ECG, in many cases masking your ECG in a sea of electrical noise. The traditional method of eliminating this noise is to use complicated analog circuitry, but since this noise has a characteristic, repeating, high-frequency wave pattern, it can be separated from the ECG (which is much slower in comparison) using digital signal processing computer software!

Digitization: Once amplified, the ECG signal along with a bunch of noise is in analog form. You could display the output with an oscilloscope, but to load it into your PC you need an analog-to-digital converter. Don’t worry! If you’ve got a sound card with a microphone input, you’ve already got one! It’s just that easy. We’ll simply wire the output of our ECG circuit to the input of our sound card, record the output of the op-amp using standard sound recording software, remove the noise from the ECG digitally, and output gorgeous ECG traces ready for visualization and analysis!

Parts/Cost

I’ll be upfront and say that I spent $0.00 making my ECG machine, because I was able to salvage all the parts I needed from a pile of old circuit boards. If you need specific components, check your local RadioShack. If that’s a no-go, hit-up Digikey (it’s probably cheaper too). Also, resistor values are flexible. Use mine as a good starter set, and vary them to suit your needs. If you buy everything from Digikey, the total cost of this project would be about $1. For now, here’s a list of all the parts you need:

  • 1x low voltage op-amp LM358N $0.40
  • 1x 100kOhm resistor (brn,blk,yel) virtually free
  • 1x 1kOhm resistor (brn,blk,red) virtually free
  • 1x 0.1uF capacitor (104Z) virtually free
  • Microphone cable to get from the op-amp to your PC
  • Electrodes 3 pennies should do. ($0.03)

Making the Device

Keep in mind that I’m not an electrical engineer (I have a masters in molecular biology but I’m currently a dental student if you must know) and I’m only reporting what worked well for me. I don’t claim this is perfect, and I’m certainly open for (and welcome) suggestions for improvement. With that in mind, here’s what I did!

img_2694

This is pretty much it. First off is a power source. If you want to be safe, use three AAA batteries in series. If you’re a daredevil and enjoy showing off your ghettorigging skills, do what I did and grab 5v from a free USB plug! Mua ha ha ha. The power goes into the circuit and so do the leads/electrodes connected to the body. You can get pretty good results with only two leads, but if you want to experiment try hooking up an extra ground lead and slap it on your foot. More on the electrodes later. The signal from the leads is amplified by the circuit and put out the headphone cable, ready to enter your PC’s sound card through the microphone jack!

img_2686

Note how I left room in the center of the circuit board. That was intentional! I wanted to expand this project by adding a microcontroller to do some on-board, real-time analysis. Specifically, an ATMega8! I never got around to it though. Its purpose would be to analyze the output of the op-amp and graph the ECG on a LCD screen, or at least measure the time between beats and display HR on a screen. (More ideas are at the bottom of this document.) Anyway, too much work for now, maybe I’ll do it one day in the future.

ECG circuit diagram:

simple_ecg_circuit

This is the circuit diagram. This is a classical high-gain analog differential amplifier. It just outputs the multiplied difference of the inputs. The 0.1uF capacitor helps stabilize the signal and reduce high frequency noise (such as the audio produced by a nearby AM radio station). Use Google if you’re interested in learning exactly how it works.

ECG schematic:

simple_ecg_circuit2

This is how I used my LM358N to create the circuit above. Note that there is a small difference in my board from the photos and this diagram. This diagram is correct, but the circuit in some of the pictures is not. Briefly, when I built it I accidentally connected the (-) lead directly to ground, rather than to the appropriate pin on the microchip. This required me to place a 220kOhm between the leads to stabilize the signal. I imagine if you wire it CORRECTLY (as shown in these circuit diagrams) it will work fine, but if you find it too finicky (jumping quickly from too loud to too quiet), try tossing in a high-impedance resistor between the leads like I did. Overall, this circuit is extremely flexible and I encourage you to build it on a breadboard and try different things. Use this diagram as a starting point and experiment yourself!

The Electrodes:

img_2704

You can make electrodes out of anything conductive. The most recent graphs were created from wires with gator clips on them clamping onto pennies (pictured). Yeah, I know I could solder directly to the pennies (they’re copper) but gator clips are fast, easy, and can be clipped to different materials (such as aluminum foil) for testing. A dot of moisturizing lotion applied to the pennies can be used to improve conduction between the pennies and the skin, but I didn’t find this to be very helpful. If pressed firmly on the body, conduction seems to be fine. Oh! I just remembered. USE ELECTRICAL TAPE TO ATTACH LEADS TO YOUR BODY! I tried a million different things, from rubber bands to packaging tape. The bottom line is that electrical tape is stretchy enough to be flexible, sticky enough not to fall off (even when moistened by the natural oils/sweat on your skin), and doesn’t hurt that bad to peel off.

Some of the best electrodes I used were made from aluminum cans! Rinse-out a soda can, cut it into “pads”, and use the sharp edge of a razor blade or pair of scissors to scrape off the wax coating on all contact surfaces. Although a little unconformable and prone to cut skin due to their sharp edges, these little guys work great!

Hooking it Up

This part is the most difficult part of the project! This circuit is extremely finicky. The best way to get it right is to open your sound editor (In Windows I use GoldWave because it’s simple, powerful, and free, but similar tools exist for Linux and other Unix-based OSes) and view the low-frequency bars in live mode while you set up. When neither electrode is touched, it should be relatively quiet. When only the + electrode is touched, it should go crazy with noise. When you touch both (one with each hand) the noise should start to go away, possibly varying by how much you squeeze (how good of a connection you have). The whole setup process is a game between too much and too little conduction. You’ll find that somewhere in the middle, you’ll see (and maybe hear) a low-frequency burst of noise once a second corresponding to your heartbeat. [note: Did you know that's how the second was invented? I believe it was ] Once you get that good heartbeat, tape up your electrodes and start recording. If you can’t get it no matter what you do, start by putting the ground electrode in your mouth (yeah, I said it) and pressing the + electrode firmly and steadily on your chest. If that works (it almost always does), you know what to look for, so keep trying on your skin. For short recordings (maybe just a few beats) the mouth/chest method works beautifully, and requires far less noise reduction (if any), but is simply impractical for long-term recordings. I inside vs. outside potential is less susceptible to noise-causing electrical radiation. Perhaps other orifices would function similarly? I’ll leave it at that. I’ve also found that adding a third electrode (another ground) somewhere else on my body helps a little, but not significantly. Don’t give up at this step if you don’t get it right away! If you hear noise when + is touched, your circuit is working. Keep trying and you’ll get it eventually.

Recording the ECG

This is the easy part. Keep an eye on your “bars” display in the audio program to make sure something you’re doing (typing, clicking, etc) isn’t messing up the recording. If you want, try surfing the net or playing computer games to see how your heart varies. Make sure that as you tap the keyboard and click the mouse, you’re not getting noise back into your system. If this is a problem, try powering your device by batteries (a good idea for safety’s sake anyway) rather than another power source (such as USB power). Record as long as you want! Save the file as a standard, mono, wave file.

Digitally Eliminating Noise

Now it’s time to clean-up the trace. Using GoldWave, first apply a lowpass filter at 30 Hz. This kills most of your electrical noise (> 30hz), while leaving the ECG intact (< 15Hz). However, it dramatically decreases the volume (potential) of the audio file. Increase the volume as necessary to maximize the window with the ECG signal. You should see clear heartbeats at this point. You may want to apply an auto-gain filter to normalize the heartbeats potentials. Save the file as a raw sound file (.snd) at 1000 Hz (1 kHz) resolution.

Presentation and Analysis

Now you’re ready to analyze! Plop your .snd file in the same folder as my [ecg.py script], edit the end of the script to reflect your .snd filename, and run the script by double-clicking it. (Keep in mind that my script was written for python 2.5.4 and requires numpy 1.3.0rc2 for python 2.5, and matplotlib 0.99 for python 2.5 – make sure you get the versions right!) Here’s what you’ll see!

diy_ecg_sample_trace

This is a small region of the ECG trace. The “R” peak is most obvious, but the details of the other peaks are not as visible. If you want more definition in the trace (such as the blue one at the top of the page), consider applying a small collection of customized band-stop filters to the audio file rather than a single, sweeping lowpass filter. Refer to earlier posts in the DIY ECG category for details. Specifically, code on Circuits vs. Software for noise reduction entry can help. For our purposes, calculating heart rate from R-to-R intervals (RRIs) can be done accurately with traces such as this.

diy_ecg_heart_rate_over_time

Your heart rate fluctuates a lot over time! By plotting the inverse of your RRIs, you can see your heart rate as a function of time. Investigate what makes it go up, go down, and how much. You’d be surprised by what you find. I found that checking my email raises my heart rate more than first-person-shooter video games. I get incredibly anxious when I check my mail these days, because I fear bad news from my new university (who knows why, I just get nervous about it). I wonder if accurate RRIs could be used to assess nervousness for the purposes of lie detection?

diy_ecg_rr_beat_interval

This is the RRI plot where the value of each RRI (in milliseconds) is represented for each beat. It’s basically the inverse of heart rate. Miscalculated heartbeats would show up as extremely high or extremely low dots on this graph. However, excluding points above or below certain bounds means that if your heart did double-beat, or skip a beat, you wouldn’t see it. Note that I just realized my axis label is wrong (it should be sec, not ms). Oh well =o\

diy_ecg_poincare_plot

A Poincare Plot is a commonly-used method to visually assess heart rate variability as a function of RRIs. In this plot, each RRI is plotted against the RRI of the next subsequent beat. In a heart which beats at the same speed continuously, only a single dot would be visible in the center. In a heart which beats mostly-continuously, and only changes its rate very slowly, a linear line of dots would be visible in a 1:1 ratio. However, in real life the heart varies RRIs greatly from beat to beat, producing a small cloud of dots. The size of the cloud corresponds to the speed at which the autonomic nervous system can modulate heart rate in the time frame of a single beat.

diy_ecg_rr_deviation_histogram

The frequency of occurrence of various RRIs can be expressed by a histogram. The center peak corresponds to the standard heart rate. Peaks to the right and left of the center peak correspond to increased and decreased RRIs, respectively. A gross oversimplification of the interpretation of such data would be to state that the upper peak represents the cardio-inhibitory parasympathetic autonomic nervous system component, and the lower peak represents the cardio-stimulatory sympathetic autonomic nervous system component.

diy_ecg_power_spectrum_raw

Taking the Fast Fourier Transformation of the data produces a unique trace whose significance is extremely difficult to interpret. Near 0Hz (infinite time) the trace heads toward ∞ (infinite power). To simplify the graph and eliminate the near-infinite, low-frequency peak we will normalize the trace by multiplying each data point by its frequency, and dividing the vertical axis units by Hz to compensate. This will produce the following graph…

diy_ecg_power_spectrum_weighted
This is the power spectrum density (PSD) plot of the ECG data we recorded. Its physiological interpretation is extraordinarily difficult to understand and confirm, and is the subject of great debate in the field of autonomic neurological cardiac regulation. An oversimplified explanation of the significance of this graph is that the parasympathetic (cardio-inhibitory) branch of the autonomic nervous system works faster than the sympathetic (cardio-stimulatory) branch. Therefore, the lower peak corresponds to the sympathetic component (combined with persistent parasympathetic input, it’s complicated), while the higher-frequency peak corresponds to the parasympathetic component, and the sympathetic/parasympathetic relationship can be assessed by the ratio of the integrated areas of these peaks after a complicated curve fitting processes which completely separates overlapping peaks. To learn more about power spectral analysis of heart rate over time in the frequency domain, I recommend skimming this introduction to heart rate variability website and the article on Heart Rate Variability following Myocardial Infarction (heart attack). Also, National Institute of Health (NIH) funded studies on HRV should be available from pubmed.org. If you want your head to explode, read Frequency-Domain Characteristics and Filtering of Blood Flow Following the Onset of Exercise: Implications for Kinetics Analysis for a lot of good frequency-domain-analysis-related discussion and rationalization.

Encouraging Words:

Please, if you try this don’t die. The last thing I want is to have some kid calling me up and yelling at me that he nearly electrocuted himself when he tried to plug my device directly into a wall socket and now has to spend the rest of his life with two Abraham Lincolns tattooed onto his chest resembling a second set of nipples. Please, if you try this use common sense, and of course you’re responsible for your own actions. I provide this information as a description of what I did and what worked for me. If you make something similar that works, I’ve love to see it! Send in your pictures of your circuit, charts of your traces, improved code, or whatever you want and I’ll feature it on the site. GOOD LUCK!

Fancier Circuit:

If you want to try this, go for it! Briefly, this circuit uses 6 op-amps to help eliminate effects of noise. It’s also safer, because of the diodes interconnecting the electrodes. It’s the same circuit as on [this page].

Last minute thoughts:

  • More homemade ECG information can be found on my earlier posts in the DIY ECG category, however this page is the primary location of my most recent thoughts and ideas.
  • You can use moisturizing lotion between the electrodes and your skin to increase conduction. However, keep in mind that better conduction is not always what you want. You’ll have to experiment for yourself.
  • Variation in location of electrodes will vary the shape of the ECG. I usually place electrodes on each side of my chest near my arms. If your ECG appears upside-down, reverse the leads!
  • Adding extra leads can improve grounding. Try grounding one of your feet with a third lead to improve your signal. Also, if you’re powering your device via USB power consider trying battery power – it should be less noisy.
  • While recording, be aware of what you do! I found that if I’m not well-grounded, my ECG is fine as long as I don’t touch my keyboard. If I start typing, every keypress shows up as a giant spike, bigger than my heartbeat!
  • If you get reliable results, I wonder if you could make the device portable? Try using a portable tape recorder, voice recorder, or maybe even minidisc recorder to record the output of the ECG machine for an entire day. I haven’t tried it, but why wouldn’t it work? If you want to get fancy, have a microcontroller handle the signal processing and determine RRIs (should be easy) and save this data to a SD card or fancy flash logger.
  • The microcontroller could output heart rate via the serial port.
  • If you have a microcontroller on board, why not display heart rate on a character LCD?
  • While you have a LCD on there, display the ECG graphically!
  • Perhaps a wireless implementation would be useful.
  • Like, I said, there are other, more complicated analog circuits which reduce noise of the outputted signal. I actually built Jason Nguyen’s fancy circuit which used 6 op-amps but the result wasn’t much better than the simple, 1 op-amp circuit I describe here once digital filtering was applied.
  • Arrhythmic heartbeats (where your heart screws-up and misfires, skips a beat, double-beats, or beats awkwardly) are physiological (normal) and surprisingly common. Although shocking to hear about, sparse, single arrhythmic heartbeats are normal and are a completely different ball game than chronic, potentially deadly heart arrhythmias in which every beat is messed-up. If you’re in tune with your body, you might actually feel these occurrences happening. About three times a week I feel my heart screw up a beat (often when it’s quiet), and it feels like a sinking feeling in my chest. I was told by a doctor that it’s totally normal and happens many times every day without me noticing, and that most people never notice these single arrhythmic beats. I thought it was my heart skipping a beat, but I wasn’t sure. That was my motivation behind building this device – I wanted to see what my arrhythmic beats looked like. It turns out that it’s more of a double-beat than a skipped beat, as observed when I captured a single arrhythmic heartbeat with my ECG machine, as described in this entry.
  • You can improve the safety of this device by attaching diodes between leads, similar to the more complicated circuit. Theory is that if a huge surge of energy does for whatever reason get into the ECG circuit, it’ll short itself out at the circuit level (conducting through the diodes) rather than at your body (across your chest / through your heart).
  • Alternatively, use an AC opto-isolator between the PC sound card and the ECG circuit to eliminate the possibility of significant current coming back from the PC.
  • On the Hackaday post, Flemming Frandsen noted that an improperly grounded PC could be dangerous because the stored charge would be manifest in the ground of the microphone jack. If you were to ground yourself to true ground (using a bench power supply or sticking your finger in the ground socket of an AC wall plug) this energy could travel through you! So be careful to only ground yourself with respect to the circuit using only battery power to minimize this risk.
  • Do not attempt anything on this page. Ever. Don’t even read it. You read it already! You’re sill reading it aren’t you? Yeah. You don’t follow directions well do you?

SAMPLE FILTERED RECORDING:

I think this is the same one I used in the 3rd video from my single op-amp circuit. [scottecg.snd] It’s about an hour long, and in raw sound format (1000 Hz). It’s already been filtered (low-pass filtered at 30Hz). You can use it with my code below!

CODE

print "importing libraries..."
import numpy, pylab
print "DONE"

class ECG:

    def trim(self, data,degree=100):
        print 'trimming'
        i,data2=0,[]
        while i<len(data):
            data2.append(sum(data[i:i+degree])/degree)
            i+=degree
        return data2

    def smooth(self,list,degree=15):
        mults=[1]
        s=[]
        for i in range(degree): mults.append(mults[-1]+1)
        for i in range(degree): mults.append(mults[-1]-1)
        for i in range(len(list)-len(mults)):
            small=list[i:i+len(mults)]
            for j in range(len(small)):
                small[j]=small[j]*mults[j]
            val=sum(small)/sum(mults)
            s.append(val)
        return s

    def smoothWindow(self,list,degree=10):
        list2=[]
        for i in range(len(list)):
            list2.append(sum(list[i:i+degree])/float(degree))
        return list2

    def invertYs(self):
        print 'inverting'
        self.ys=self.ys*-1

    def takeDeriv(self,dist=5):
        print 'taking derivative'
        self.dys=[]
        for i in range(dist,len(self.ys)):
            self.dys.append(self.ys[i]-self.ys[i-dist])
        self.dxs=self.xs[0:len(self.dys)]

    def genXs(self, length, hz):
        print 'generating Xs'
        step = 1.0/(hz)
        xs=[]
        for i in range(length): xs.append(step*i)
        return xs

    def loadFile(self, fname, startAt=None, length=None, hz=1000):
        print 'loading',fname
        self.ys = numpy.memmap(fname, dtype='h', mode='r')*-1
        print 'read %d points.'%len(self.ys)
        self.xs = self.genXs(len(self.ys),hz)
        if startAt and length:
            self.ys=self.ys[startAt:startAt+length]
            self.xs=self.xs[startAt:startAt+length]

    def findBeats(self):
        print 'finding beats'
        self.bx,self.by=[],[]
        for i in range(100,len(self.ys)-100):
          if self.ys[i]<15000: continue # SET THIS VISUALLY
          if self.ys[i]<self.ys[i+1] or self.ys[i]<self.ys[i-1]: continue
          if self.ys[i]-self.ys[i-100]>5000 and self.ys[i]-self.ys[i+100]>5000:
              self.bx.append(self.xs[i])
              self.by.append(self.ys[i])
        print "found %d beats"%(len(self.bx))

    def genRRIs(self,fromText=False):
        print 'generating RRIs'
        self.rris=[]
        if fromText: mult=1
        else: 1000.0
        for i in range(1,len(self.bx)):
            rri=(self.bx[i]-self.bx[i-1])*mult
            #if fromText==False and len(self.rris)>1:
                #if abs(rri-self.rris[-1])>rri/2.0: continue
            #print i, "%.03f\t%.03f\t%.2f"%(bx[i],rri,60.0/rri)
            self.rris.append(rri)

    def removeOutliers(self):
        beatT=[]
        beatRRI=[]
        beatBPM=[]
        for i in range(1,len(self.rris)):
            #CHANGE THIS AS NEEDED
            if self.rris[i]<0.5 or self.rris[i]>1.1: continue
            if abs(self.rris[i]-self.rris[i-1])>self.rris[i-1]/5: continue
            beatT.append(self.bx[i])
            beatRRI.append(self.rris[i])
        self.bx=beatT
        self.rris=beatRRI

    def graphTrace(self):
        pylab.plot(self.xs,self.ys)
        #pylab.plot(self.xs[100000:100000+4000],self.ys[100000:100000+4000])
        pylab.title("Electrocardiograph")
        pylab.xlabel("Time (seconds)")
        pylab.ylabel("Potential (au)")

    def graphDeriv(self):
        pylab.plot(self.dxs,self.dys)
        pylab.xlabel("Time (seconds)")
        pylab.ylabel("d/dt Potential (au)")

    def graphBeats(self):
        pylab.plot(self.bx,self.by,'.')

    def graphRRIs(self):
        pylab.plot(self.bx,self.rris,'.')
        pylab.title("Beat Intervals")
        pylab.xlabel("Beat Number")
        pylab.ylabel("RRI (ms)")

    def graphHRs(self):
        #HR TREND
        hrs=(60.0/numpy.array(self.rris)).tolist()
        bxs=(numpy.array(self.bx[0:len(hrs)])/60.0).tolist()
        pylab.plot(bxs,hrs,'g',alpha=.2)
        hrs=self.smooth(hrs,10)
        bxs=bxs[10:len(hrs)+10]
        pylab.plot(bxs,hrs,'b')
        pylab.title("Heart Rate")
        pylab.xlabel("Time (minutes)")
        pylab.ylabel("HR (bpm)")

    def graphPoincare(self):
        #POINCARE PLOT
        pylab.plot(self.rris[1:],self.rris[:-1],"b.",alpha=.5)
        pylab.title("Poincare Plot")
        pylab.ylabel("RRI[i] (sec)")
        pylab.xlabel("RRI[i+1] (sec)")

    def graphFFT(self):
        #PSD ANALYSIS
        fft=numpy.fft.fft(numpy.array(self.rris)*1000.0)
        fftx=numpy.fft.fftfreq(len(self.rris),d=1)
        fftx,fft=fftx[1:len(fftx)/2],abs(fft[1:len(fft)/2])
        fft=self.smoothWindow(fft,15)
        pylab.plot(fftx[2:],fft[2:])
        pylab.title("Raw Power Sprectrum")
        pylab.ylabel("Power (ms^2)")
        pylab.xlabel("Frequency (Hz)")

    def graphFFT2(self):
        #PSD ANALYSIS
        fft=numpy.fft.fft(numpy.array(self.rris)*1000.0)
        fftx=numpy.fft.fftfreq(len(self.rris),d=1)
        fftx,fft=fftx[1:len(fftx)/2],abs(fft[1:len(fft)/2])
        fft=self.smoothWindow(fft,15)
        for i in range(len(fft)):
            fft[i]=fft[i]*fftx[i]
        pylab.plot(fftx[2:],fft[2:])
        pylab.title("Power Sprectrum Density")
        pylab.ylabel("Power (ms^2)/Hz")
        pylab.xlabel("Frequency (Hz)")

    def graphHisto(self):
        pylab.hist(self.rris,bins=20,ec='none')
        pylab.title("RRI Deviation Histogram")
        pylab.ylabel("Frequency (count)")
        pylab.xlabel("RRI (ms)")
        #pdf, bins, patches = pylab.hist(self.rris,bins=100,alpha=0)
        #pylab.plot(bins[1:],pdf,'g.')
        #y=self.smooth(list(pdf[1:]),10)
        #x=bins[10:len(y)+10]
        #pylab.plot(x,y)

    def saveBeats(self,fname):
        print "writing to",fname
        numpy.save(fname,[numpy.array(self.bx)])
        print "COMPLETE"

    def loadBeats(self,fname):
        print "loading data from",fname
        self.bx=numpy.load(fname)[0]
        print "loadded",len(self.bx),"beats"
        self.genRRIs(True)

def snd2txt(fname):
    ## SND TO TXT ##
    a=ECG()
    a.loadFile(fname)#,100000,4000)
    a.invertYs()
    pylab.figure(figsize=(7,4),dpi=100);pylab.grid(alpha=.2)
    a.graphTrace()
    a.findBeats()
    a.graphBeats()
    a.saveBeats(fname)
    pylab.show()

def txt2graphs(fname):
    ## GRAPH TXT ##
    a=ECG()
    a.loadBeats(fname+'.npy')
    a.removeOutliers()
    pylab.figure(figsize=(7,4),dpi=100);pylab.grid(alpha=.2)
    a.graphHRs();pylab.subplots_adjust(left=.1,bottom=.12,right=.96)
    pylab.savefig("DIY_ECG_Heart_Rate_Over_Time.png");
    pylab.figure(figsize=(7,4),dpi=100);pylab.grid(alpha=.2)
    a.graphFFT();pylab.subplots_adjust(left=.13,bottom=.12,right=.96)
    pylab.savefig("DIY_ECG_Power_Spectrum_Raw.png");
    pylab.figure(figsize=(7,4),dpi=100);pylab.grid(alpha=.2)
    a.graphFFT2();pylab.subplots_adjust(left=.13,bottom=.12,right=.96)
    pylab.savefig("DIY_ECG_Power_Spectrum_Weighted.png");
    pylab.figure(figsize=(7,4),dpi=100);pylab.grid(alpha=.2)
    a.graphPoincare();pylab.subplots_adjust(left=.1,bottom=.12,right=.96)
    pylab.savefig("DIY_ECG_Poincare_Plot.png");
    pylab.figure(figsize=(7,4),dpi=100);pylab.grid(alpha=.2)
    a.graphRRIs();pylab.subplots_adjust(left=.1,bottom=.12,right=.96)
    pylab.savefig("DIY_ECG_RR_Beat_Interval.png");
    pylab.figure(figsize=(7,4),dpi=100);pylab.grid(alpha=.2)
    a.graphHisto();pylab.subplots_adjust(left=.1,bottom=.12,right=.96)
    pylab.savefig("DIY_ECG_RR_Deviation_Histogram.png");
    pylab.show();

fname='publish_05_10min.snd' #CHANGE THIS AS NEEDED
#raw_input("\npress ENTER to analyze %s..."%(fname))
snd2txt(fname)
#raw_input("\npress ENTER to graph %s.npy..."%(fname))
txt2graphs(fname)

» 21 Comments!



Bloated Software Makes Me Sick
Posted by
Scott on August 11th, 2009 | 512 words | 3 Comments
Filed under: General

Tonight’s brief entry is nothing more than a rant against a disturbing trend in the PC industry. I sit here half past nightnight screaming at my computer screen. To make a long story short, I need to do an incredibly simple task: burn a video file to a DVD. It’s a ~30min slideshow in AVI format. This should be incredibly easy, right? In linux it’s only a process of typing a few commands into a console. No biggie.

I’d use this method, but neroI only have one linux box with a DVD burner and it’s flaking out very badly. I try to burn DVDs, but it has power issues and it fails 9 out of 10 times. I should throw away the drive. The only functional DVD drive is on my wife’s PC, a computer running Microsoft Windows Vista.

How do you burn an AVI to a DVD in Windows? You can’t. Well, not easily. Sure, there are simple commands for Linux and it can do everything using free, small software only a few MB in size. Apparently the only way to do similar things for windows is to use pirated software, or fool around with a million steps to use a combination of multiple quasi-free programs. I know I’m being somewhat irrational about its complexity on Windows, but I’m mad, so I’m forgoing logic at this point. I knew that Nero 9 should be able to do this. I looked for it on a popular torrent website (yes, it seems the only way to be productive in Windows is to use pirated software) and downloaded it.

I can’t wrap my head around what Nero 9 does that requires almost 1GB of my hard drive space, requiring over 45 minutes to install on my wife’s 64-bit dual-core2.4 GHz Vista-running PC with 4GB of ram. Have software companies COMPLETELY abandoned the principals of simplicity, speed, reliability, and functionality? Why is it that common common software distributions (think about Microsoft Office programs and virtually every Adobe product) increase in size exponentially with every release, offer virtually no improvement in speed, and with questionable feature upgrades?

FREAKING UPDATE: After going through an hour of frustration, I concluded that Nero 9 is incapable of creating a DVD from my AVI file. My wife reminded me that my linux-running laptop has a DVD burner. I hopped on it, typed apt-get install devede; devede and burned my DVD in 5 minutes with a 14MB program. Soon, people will start rising up and demanding lite, cheap, and FUNCTIONAL products for Windows. Until they do, I will just avoid it when I can.

Bah! I need to go to bed. I’m getting mad at my computer -_-


» 3 Comments!



ECG Circuit is Getting More Reliable!
Posted by
Scott on August 9th, 2009 | 350 words | No Comments (yet)
Filed under: General

Although I made a functional ECG circuit, it was extremely finnicky. If you attached the electrodes too weakly (not a good enough connection), you would get no signal. If you attached the electrodes too tightly (too good of a connection), you wouldn’t get a signal either. You had to have just the right resistance between the electrodes and the body for them to work. I tried some things and finally discovered that a resistor between the circuit and me (on the ground lead) significantly improved the situation, but requries a really good body connection. My leads (2) were made from wires (non-shielded) with gator clips at the end clamping onto pennies. I added a dab of moisterizer to the pennies to get a really good connection and used electical tape to attach them to my chest (+) and leg (GND). I recorded heart data in 10 minute blocks, and it worked amazingly well! Here is a video if me recording my ECG while playing Counter-Strike.

If you look closely you can see my heartbeat as the two leftmost bars on the display of the laptop. I’m continuing to work on this circuit, and will release details when it’s a little more complete. My plan is to write it up formally, provide a ton of examples/documentation, and really dive into the analysis aspect of it (RRI calculations, variability analysis, etc) and post it to Hackaday. DIY ECGs are nothing new, but no one who’s made one has really gone deep into its interpretation. My goal is to have this complete by next week! For now, enjoy the pretty videos.


» Be the first to comment!



Fringe Culture Seared Into My Brain
Posted by
Scott on August 7th, 2009 | 319 words | No Comments (yet)
Filed under: General

I’m super-busy tonight and only have a couple minutes to write. To make a long story short, I’m converting images from various formats to various other formats (could I be any more vague?) preparing for insertion into a scientific manuscript which [crosses fingers] I hope will be published soon. While working, I was listening to sky.fm’s classical stream and a song popped on that completely knocked my mind (and productivity) off track. The song is Johann Sebastian Bach’s Air from the Suite No 3 (in D major). Even if you don’t know it by name, you’d recognize its tune. Daaaa da da da da da da da da daaa da da daaa… sorry, I’m not a good singer. Anyhow, the second I heard it my brain instantly loaded-up images of Japanese schoolchildren violently murdering eachother. Yes, I’m referring to Battle Royale. I read the book and even saw the movie back when I was a teenager. I highly recommend both, but it’s been years since I’ve read/seen either one. In the movie, I love the way they inserted incredibly peaceful music at the most disturbing of times. Below is the clip of the film that I will forever image in my head whenever I hear Bach’s famous air.


» Be the first to comment!



Defibrillating My DIY ECG Project
Posted by
Scott on August 6th, 2009 | 628 words | No Comments (yet)
Filed under: Circuitry, DIY ECG, General, Python

I’ve done a lot of random things the last few months, but few things were as random, cool, or googled-for as my Do-It-Yourself Electrocardiography project . My goal was to produce an effective ECG machine which interfaced the computer sound card for as little cost as possible. I started out small with an extremely simple circuit which technically worked, but required a lot of custom-written software to do a ton of math to decipher the ECG signal from the noise (such as inverse fast flourier transformations after band-stopping several bands of predictable, high-frequency noise). I later started building more complicated circuits in an attempt to minimize the noise, which worked well but were much more difficult to construct. For some reason, my nice ECG circuit died (burned? broke? don’t know why) right after I started to actually generate useful data about my occasional double-beats (which apparently are common, normal, and even expected during basal physiological states).
UPDATE: [2am, nextday] Here’s some video of the prototype briefly demonstrating the concept of how to use a minimum of parts to generate a great ECG trace using digital signal processing on the PC side.

simple_ecg_circuit_output

I’ve decided to revitalize this project quickly and effectively, going back to its roots and focusing on cost-minimal solutions, and using software (rather than complicated analog circuitry) to eliminate the noise. This will be a beautiful marriage of biomedical analog circuitry with software-based processing and linear data analysis, all on the cheap. If there were ever a project that represented my early 20s life, this would be it. Briefly, I built a circuit with only 3 components (!) which produces extraordinary results (above). That’s the signal after minimal processing.

Check it out yourself! I’ll provide data file for this trace (snd2.zip) along with the Python code to graph it (below) which requires numpy and matplotlib in addition to the Python scripting language. I’ll post the circuity along with some more intricate code when my project progresses a little further.

import numpy, pylab

def trim(data,degree=100):
    i,data2=0,[]
    while i&ltlen(data):
        data2.append(sum(data[i:i+degree])/degree)
        i+=degree
    return data2

def genXs(length,trim=100,hz=44100):
    step = 1.0/(hz/trim)
    xs=[]
    for i in range(length):
        xs.append(step*i)
    return xs

data = numpy.memmap("ecg2.snd", dtype='h', mode='r')
data = trim(data)
pylab.grid(alpha=.2)
pylab.plot(genXs(len(data)),data)
pylab.title("Simplified ECG Circuit Output")
pylab.xlabel("Time (seconds)")
pylab.ylabel("Potential (Au)")
pylab.show()

» Be the first to comment!



Generate Apache-Style HTTP Access Logs via SQL and PHP
Posted by
Scott on August 4th, 2009 | 1,427 words | No Comments (yet)
Filed under: General, My Website, PHP

Does your web hosting company block access to access.log, the text file containing raw website log files? If so, you’re like me, and it sucks. There’s a plethora of gorgeous and extremely insightful website traffic analyzers, but all of them require access to raw HTTP access logs. Today I propose a semi-efficient way to generate such logs utilizing PHP to determine page load data (time, user IP, requested page, referring page, user client, etc) and SQL to save such data for easy retrieval later. Note that this method is a HUGE improvement of my previous project which used PHP scripts to store HTTP access logs as flat files. Although it worked in theory, in all practicality the process of opening, writing to, and closing a text file (which grew a few MB a week) was too cumbersome for my server to comfortable handle. The method described on this page utilizes SQL, a database engine well-suited to meet these exact demands. When we’re done, you’ll be able to use a web interface to view your access log (pictured, converting long, complicated search queries to web search and image search strings automatically), or have the option to export it directly to an access.log text file in a standard Apache-style format.
sql_php_http_log_viewer

First, make sure your database is structured appropriately. This page is written for those with a working knowledge of PHP and SQL, but if you’re new to the field I encourage you to learn! W3Schools.com is an awesome resource to rapidly learn new languages. Also, when starting-out with SQL (like me), phpMyAdmin is a awesome. The code, as it’s currently written (below) is designed to store data in the “nibjb” database under the “logs” table. Briefly, it uses PHP to determine user data (time, ip, requested page, etc.) and injects this information into the SQL database. In fact, it’s doing it to you right now! Don’t believe me? View the source of this web page and scroll to the bottom. BAM! There you are.

// logme.php
<?php

if ( !isset($wp_did_header) ) {
	$wp_did_header = true;
	require_once( '/home/content/n/i/b/nibjb/html/blog/wp-load.php' );
	//wp();
	//require_once( '/home/content/n/i/b/nibjb/html/blog/wp-includes/template-loader.php' );
}

function logwriter_handlevar($varname,$defaultvalue){
    $tempvar = getenv($varname);
    if(!empty($tempvar)) {
        return $tempvar;
    } else {
        return $defaultvalue;
    }
} 

if (!empty($REMOTE_HOST)) {
$logwriter_remote_vistor = $REMOTE_HOST;
}else{
$logwriter_remote_vistor = logwriter_handlevar("REMOTE_ADDR","-");
} 

$logwriter_remote_ident = logwriter_handlevar("REMOTE_IDENT","-");
$logwriter_remote_user = logwriter_handlevar("REMOTE_USER","-");
$logwriter_date = date("d/M/Y:H:i:s");
$logwriter_request_method = logwriter_handlevar("REQUEST_METHOD","GET");
$logwriter_request_uri = logwriter_handlevar("REQUEST_URI","");
$logwriter_server_protocol = logwriter_handlevar("SERVER_PROTOCOL","HTTP/1.1");
$logwriter_http_referer = logwriter_handlevar("HTTP_REFERER","-");
$logwriter_http_user_agent = logwriter_handlevar("HTTP_USER_AGENT","");
$logwriter_logstring = "$logwriter_remote_vistor $logwriter_remote_ident $logwriter_remote_user [$logwriter_date $logwriter_timezone] \"$logwriter_request_method $logwriter_request_uri $logwriter_server_protocol\" 200 - \"$logwriter_http_referer\" \"$logwriter_http_user_agent\"\n";
?>

<?php
$username="YOUR_USERNAME";
$password="YOUR_PASSWORD";
$database="nibjb";
mysql_connect('mysql157.secureserver.net',$username,$password);
//mysql_connect(localhost,$username,$password);

$query = "INSERT INTO logs VALUES ('','$logwriter_date','$logwriter_remote_vistor','$logwriter_request_method','$logwriter_request_uri','$logwriter_server_protocol','$logwriter_http_referer','$logwriter_http_user_agent')";
mysql_query($query);
mysql_close();
?>

<!--
LOG DETAILS:
time: <?php echo($logwriter_date); ?>
vistor: <?php echo($logwriter_remote_vistor); ?>
method: <?php echo($logwriter_request_method); ?>
request: <?php echo($logwriter_request_uri); ?>
protocol: <?php echo($logwriter_server_protocol); ?>
referrer: <?php echo($logwriter_http_referer); ?>
agent: <?php echo($logwriter_http_user_agent); ?>
HTML LOG LINE:
<?php echo($logwriter_logstring); ?>
 -->

All right, that was easy. Every time we load logme.php, it adds the data to the SQL database. To add data every time you go to a particular web page, you could use a PHP include() statement in each webpage, or you could take advantage of the PHP’s auto_append_file feature! Simply insert the following line into your php.ini file if you have access to yours:

auto_append_file = "/path/to/html/logme.php"

How do we access this data once it’s been loaded into the database? There are many different ways, but I’ve chosen to get a little creative with a sleek, yet minimalistic web-based fronted. It basically just shows the last [x] number of entries in the access log. You can adjust the number of entries displayed by slapping on some arguments to the URL, transforming viewLast.php into viewLast.php?limit=123 or something (see the screenshot above). I won’t discuss the details of this script. It’s self-explanatory.

// viewLast.php
<html>
<head>
<style type="text/css">
td {
font-family: verdana, arial;
font-size:10px;
}
</style>
</head>
<body>
<?php

$limit = (int)$_GET['limit'];
if ($limit===0) {$limit=25;}

$username="YOUR_USERNAME";
$password="YOUR_PASSWORD";
$database="nibjb";
mysql_connect('mysql157.secureserver.net',$username,$password);
mysql_select_db($database) or die( "Unable to select database");
$query="
SELECT * FROM logs WHERE
request NOT LIKE \"%testlog.php%\"
AND request NOT LIKE  \"%/logs/%\"
AND request NOT LIKE \"%/wp-admin/%\"
ORDER BY ID DESC LIMIT 0,$limit
";
//$query="SELECT * FROM logs WHERE referrer LIKE \"%&q=%\" or referrer LIKE \"%&prev=%\" ";
$result=mysql_query($query);
$num=mysql_numrows($result);
mysql_close();
?>

<b><?php echo($query); ?></b>
<table border="1">
<tr>
<td>id</td>
<td>time</td>
<td>visitor</td>
<td>request</td>
<td>referrer</td>
</tr>

<?php
$i=1;
while ($i<$num) {
$id=mysql_result($result,$i,"id");
$time=mysql_result($result,$i,"time");
$visitor=mysql_result($result,$i,"visitor");
$method=mysql_result($result,$i,"method");
$request=mysql_result($result,$i,"request");
$protocol=mysql_result($result,$i,"protocol");
$referrer=mysql_result($result,$i,"referrer");
$referrer2=str_replace("&", "& ", $referrer);
$agent=mysql_result($result,$i,"agent");
$searchWords="";
$searchEngine="";
if (strpos($referrer, "q=")>0 and strpos($referrer, "google")>0) {$searchEngine="Google Web Search: ";}
if (strpos($referrer, "prev=/images")>0 and strpos($referrer, "google")>0) {$searchEngine="Google Image Search: ";}

// SEARCH EXTRACTION //
$j=0;
$rTemp=str_replace("prev=/images%3Fq%3D", "q=", $referrer);
$rTemp=str_replace("?q=","&q=", $rTemp);
$rTemp=str_replace("%2B"," ", $rTemp);
$rTemp=str_replace("%26"," ", $rTemp);
$rTemp=str_replace("%3D"," ", $rTemp);
$rTemp=str_replace("+"," ", $rTemp);
$wvars=split("&",$rTemp);
while ($j<count($wvars)){
	if (substr($wvars[$j],0,2) === "q=") {
		$searchWords = $searchWords . $wvars[$j] . " ";
		}
	$j++;
}

$searchWords=substr($searchWords,strpos($searchWords, "q=")+2);
if (strlen($searchWords)<3) {$searchWords=$referrer;}
////////////////////////

echo "
<tr>
<td>$id</td>
<td>$time</td>
<td>$visitor</td>
<td><a href='$request'>$request</a></td>
<td>$searchEngine <a href='$referrer'>$searchWords</a></td>
</td>
";
$i++;
}
?>
</table>
</body>
</html>

And you’re done! This example is a simplified, bare bones example. You can take this a long way if you’d like. My goal is lite & flexible. A quick query from Python and Matplotlib (for example) yields gorgeous visual representations of otherwise-convoluted data!

If you have any questions, or end-up developing something awesome with this code, shoot me an email! It’s not luxurious, but this code works for me, and I share it with the best of intentions.


» Be the first to comment!



PySquelch: A Python-Based Frequency Audio Activity Monitor
Posted by
Scott on July 26th, 2009 | 324 words | 1 Comment
Filed under: General, Python, Radio

I’m pretty much done with this project so it’s time to formally document it. This project is a collaboration between Fred, KJ4LFJ who supplied the hardware and myself, Scott, KJ4LDF who supplied the software. Briefly, a scanner is set to a single frequency (147.120 MHz, the output of an active repeater in Orlando, FL) and the audio output is fed into the microphone hole of a PC sound card. The scripts below (run in the order they appear) detect audio activity, log the data, and display such data graphically. Here is some sample output:
test_24hr-1
test_average
test_alltime-1
test_60min

Live-running software is current available at: Fred’s Site. The most current code can be found in its working directory. For archival purposes, I’ll provide the code for pySquelch in ZIP format. Now, onto other things…


» 1 Comment so far...



Transition
Posted by
Scott on July 23rd, 2009 | 271 words | No Comments (yet)
Filed under: Dentistry, My Website

I’m briefly suspending entries on this website. I currently have no projects I’m working on, and I’m going to try to keep it that way for a few weeks. I really need to re-gear my brain and get ready for dental school next month. I’m struggling with a plethora of random emotions, and I think the best thing for me is to take it easy for a little bit and try to let go of the things I feel are important to me (projects, electrical, mechanical, computational, painting, or otherwise). I’m going to try my best to organize data from my past life (about a decade worth) in an attempt to preserve it. I’ve been thrown back into my early teen years by uncovering ~10 GB of music I used to listen to. Nostalgia? Yeah, I’m feeling it. I had totally forgotten about random, obscure Japanese bands such as Rip Slyme. For example, Hot Chocolate [a must hear / must see youtube video]. In fact, [youtubes some more], check out this randomness [embeds below]. I love non-mainstream awkwardness. What’s that I hear? 8-bit tones?


» Be the first to comment!



Microcontroller-Powered Prime Calculator is [Mostly] Complete!
Posted by
Scott on July 6th, 2009 | 1,159 words | 1 Comment
Filed under: C/C++, Circuitry, General, Microcontrollers, Prime Numbers

My microcontroller-powered prime number generator/calculator is virtually complete! Although I’m planning on improving the software (better menus, the addition of sound, and implementation of a more efficient algorithm) and hardware (a better enclosure would be nice, battery/DC wall power, and a few LEDs on the bottom row are incorrectly wired), this device is currently functional therefore theoretically complete (I met my goal). This entry will serve as the primary reference page for the project, so I will provide a brief description of what it is and what it does. First, here’s a picture of the device in its current state (click to enlarge):
primepic1

BRIEF DESCRIPTION: This device generates large prime numbers (v) while keeping track of how many prime numbers have been identified (N). The 5′th prime number is 11. Therefore, at one time this device displayed N=5 and V=11. N/V values are displayed on the 20×2 LCD. In the photo, the 16,521,486th prime is 305,257,039 (see for yourself!). The LCD had some history. In December, 2003 (6 years ago) I worked with this SAME display, and I even located the blog entry on November 25′th, 2003 where I mentioned I was thinking of buying the LCD (it was $19 at the time). Funny stuff. Okay, fast forward to today. Primes (Ns and Vs) are displayed on the LCD, but what’s with all those other LED lights? I’ll tell you:
primepic2

In short, each row of LEDs displays a number. Each row of 30 LEDs allows me to represent numbers up to 2^31-1 (2,147,483,647, about 2.15 billion) in the binary numeral system. Since there’s no known algorithm to generate prime numbers (especially the Nth prime), the only way to generate large Nth primes is to start small (2) and work up (to 2 billion) testing every number along the way for primeness. The number being tested is displayed on the middle row (Ntest). The last two digits of Ntest are shown on the top left. To test a number (Ntest) for primeness, it is divided by every number from 2 to the square root of Ntest. If any divisor divides evenly (with a remainder of zero) it’s assumed not to be prime, and Ntest is incremented. If it can’t be evenly divided by any number, it’s assumed to be prime and loaded into the top row. In the photo (with the last prime found over 305 million) the device is generating new primes every ~10 seconds. Not bad! Let’s discuss technical details.
primepic3

I’d like to emphasize that this device is not so much technologically innovative as it is creative. I made it because no one’s ever made one. It’s not realistic, practical, or particularly useful. It’s just unique. The brain behind it is an ATMEL ATMega8 AVR microcontroller (What is a microcontroller?), the big 28-pin microchip near the center of the board. (Note: I usually work with ATTiny2313 chips, but for this project I went with the ATMega8 in case I wanted to do analog-to-digital conversions. The fact that the ATMega8 is the heart of the Arduino is coincidental, as I’m not a fan of Arduino for purposes I won’t go into here).

I’d like to thank my grandmother’s brother and his wife (my great uncle and aunt I guess) for getting me interested in microcontrollers almost 10 years ago when they gave me BASIC Stamp kit (similar to this one) for Christmas. I didn’t fully understand it or grasp its significance at the time, but every few years I broke it out and started working with it, until a few months ago when my working knowledge of circuitry let me plunge way into it. I quickly outgrew it and ventured into directly programming cheaper microcontrollers which were nearly disposable (at $2 a pop, compared to $70 for a BASIC stamp), but that stamp kit was instrumental in my transition from computer programming to microchip programming.

The microcontroller is currently running at 1 MHz, but can be clocked to run faster. The PC I’m writing this entry on is about 2,100 MHz (2.1 GHz) to put it in perspective. This microchip is on par with computers of the 70s that filled up entire rooms. I program it with the C language (a language designed in the 70s with those room-sized computers in mind, perfectly suited for these microchips) and load software onto it through the labeled wires two pictures up. The microcontroller uses my software to bit-bang data through a slew of daisy-chained shift registers (74hc595s, most of the 16-pin microchips), allowing me to control over 100 pin states (on/off) using only 3 pins of the microcontroller. There are also 2 4511-type CMOS chips which convert data from 4 pins (a binary number) into the appropriate signals to illuminate a 7-segment display. Add in a couple switches, buttons, and a speaker, and you’re ready to go!

I’ll post more pictures, videos, and the code behind this device when it’s a little more polished. For now it’s technically complete and functional, and I’m very pleased. I worked on it a little bit every day after work. From its conception on May 27th to completion July 5th (under a month and a half) I learned a heck of a lot, challenged my fine motor skills to complete an impressive and confusing soldering job, and had a lot of fun in the process.

By the way, here’s a simplified schematic:
text5130


» 1 Comment so far...



Summer’s End is Nearing
Posted by
Scott on July 1st, 2009 | 704 words | No Comments (yet)
Filed under: Circuitry, General, Microcontrollers, Prime Numbers

My most glorious summer yet is reaching its end. With about a month and a half before I begin dental school, I pause to reflect on what I’ve done, and what I still plan to do. Unlike previous summers where my time was devoted to academic/thesis requirements, this summer hosted a 9am-5pm job with time to do whatever I want to after. I’ve made great progress in the realm of microcontroller programming, and am nearing the completion of my prime number calculator. I’m very happy with its progress. I think it’s time for some photos.
scott_working

Here I can be seen working on my prime number calculator. The primary display is nearing completion, and now it’s time to start wiring the buttons, switches, speaker, etc. Note the vintage scope in the background. In the photo it’s showing 60Hz (I couldn’t think of anything more profound to display?) which I’ll say is a representation of the fact that your body is continuously bombarded by electromagnetic radiation whenever you set foot in a house.
wiremess

This is the current state of the back panel of the prime number calculator. It’s becoming quite complicated.
lightson

As you can see, most of the LEDs are working but I’m still missing a few 74hc595 shift registers. It’s not that they’re missing, so much as I broke them. (D’oh!) I have to wait for a dozen more to come in the mail so I can continue this project. Shift registers are also responsible for powering the binary-to-7-segment chips on the upper left, whose sockets are currently empty. Since this project is on pause, I began work hacking a VFD I heard about at Skycraft. It’s a 20×2 character display (forgot to photograph the front) and if I can make it light up, it will be gorgeous.
vfd

Here’s a high resolution photo of the back panel of the VFD. I believe it used to belong to an old cash register, and it has some digital interfacing circuitry between the driver chips (the big OKI ones) and the 9-pin input connector. I think my best bet for being able to control this guy as much as I want is to attack those driver chips, with help from the Oki C1162A datasheet. It looks fairly straightforward. As long as I don’t screw up my surface-mount soldering, and assuming that I come up with 65 volts to power the thing (!) I think it’s a doable project.

Update: I found a funny photo from field day. After the tents, antennas, and radios were mostly set up, everyone was exhausted. I was ready to make some contacts! I fired-up my ‘ol netbook and tried communicating over 40m using psk (a digital mode), a mode I’ve never used, with software I’ve never used, on a band I’ve never used. It wasn’t working either. I spent the first several hours in frustration because what I was trying to do wasn’t working, and I couldn’t figure out why. This photo was taken at the height of my frustration =o)
me


» Be the first to comment!



Prime Schematics
Posted by
Scott on June 30th, 2009 | 152 words | No Comments (yet)
Filed under: Circuitry, General, Microcontrollers, Prime Numbers

Here’s a rough approximation of the current schematic of the prime number calculator I’m working on. Last night I finished wiring all 12 shift registers for the primary display, so now it’s time to start working on software. Notice that we have a lot of pins free still. This will be advantageous if I decide to go crazy adding extraneous functionality, such as fancy displays (LCD?, 7-segment LEDs?, VFD?, all 3?!) or creative input systems (how about a numerical keypad?). After feeling the stink of paying almost $15 for 100′ of yellow, 24 gauge, solid-core wire from DigiKey I was relieved (and a little embarrassed) to find I could score 1,000′ of yellow, 24 gauge, threaded wire for $10 at Skycraft! Anyway, here’s the current schematic:
text5130


» Be the first to comment!



Field Day 2009
Posted by
Scott on June 29th, 2009 | 515 words | 2 Comments
Filed under: General, Radio

Last weekend was field day, a disaster simulation / competition for amateur radio operators. In a sentence, people are encouraged to make as many contacts as they can around the world (earning points) using emergency radio preparations (battery and solar powered radios, temporary antennas, etc) for a full 24 hours (2pm to 2pm). I spent the time with the UCF Amateur Radio Club who set up big antennas in a grassy field on campus. It was a fun experience, and the first time I ever got to see a HF rig in operation. A representative for the UCF newspaper showed up, took some interviews, and I ended-up being quoted in the article. I can also be seen in the photo, if you look close enough (yellow square).
scottpaper

Being that amateur radio was something I got into independently (I didn’t know anyone else with a license) I was (and still am) very isolated in the hobby. I’m really thankful I found the UCF ARC, even though it wasn’t until I’d already been going to UCF for 2 years and was already on my way out. scottpaperzoomSeeing (and actually get to use) a HF rig was an eye-opening experience for me, and one I’m a little regretful I participated in. Before yesterday, I had already come to terms with my situation (going to dental school in a few weeks and virtually dropping all of my hobbies) and was content with my summer accomplishments so far. My summer goal was to get into radio, and before yesterday I felt I had. I studied for my exam, got my license, learned how to use repeaters on VHF to easily make local contacts, and I was satisfied. I knew HF was out there, and that it allowed communication over thousands of miles, but I ignored it knowing I wouldn’t get into it this summer (the equipment is just too expensive for me to justify purchasing). Now, after sitting in front of a rig for several hours, I wish I had the time to upgrade my license, earn a little cash to blow on a HF radio, and spend a few weeks sitting in front of it scouring the waves for random voices around the world. I know it’s a little morbid, but I’d probably have to compare the feeling I’m experiencing with what an old person feels like when they realize their end is near and that they won’t be able to do the things they always dreamed they would. Oh well, at least I’ll be able to fill holes in teeth soon. [smiles convincingly]


» 2 Comments!



Jedi Soldering Skills – Part 2
Posted by
Scott on June 25th, 2009 | 403 words | No Comments (yet)
Filed under: C/C++, Circuitry, Microcontrollers, Prime Numbers

I’ll update my progress on this project as I go. I added a lot more light bars to the shift registers on my prime number generator project. I’m up to 5 daisy-chained shift registers completed (powering 40 LEDs) with 7 more to go! I’m using 22 gauge solid-core (fancy and expensive, from digikey, 100′ 14$!) wire for the back of this project. Being that I plan to keep it for many years, I want it to look crazy awesome. Remember, I’m only about 1/3 done so far…
img_2445

I powered the device up and it produced proper output. Yay! I was so discouraged yesterday when I wired-up an entire row (the top one), powered it on, and 1/2 the LEDs didn’t work. At first I thought it was software, but then I realized that I burned the LEDs out in the soldering process by getting them too hot. I had to de-solder EVERYTHING, rip out the destroyed LED bars, and start over. I’ll have to pick up some more light bars at Skycraft soon. This is what it looks like currently:
img_2453

I’m making this project a priority because I only have a few weeks before I move to Gainesville, FL for dental school (the cutoff date for all electronics/radio/programming projects). I’ll be busy the next few days with other obligations (work, apartment hunting, field day, etc.) but I hope to resume this project soon.

UPDATE (June 26, 2009 @ 7:30pm): I finished wiring all the light bars I have. I need to purchase 3 more 10-led bars at Skycraft to replace the ones I melted with my soldering iron. D’oh! Anyway, here’s the beaut:
dark_bars
wires1


» Be the first to comment!



Reading, Writing, and Flipping Bits in C
Posted by
Scott on June 24th, 2009 | 312 words | No Comments (yet)
Filed under: C/C++, Circuitry, General, Microcontrollers

Bitwise programming techniques (manipulating binary numbers) is simple in theory, but it’s often hard to remember how to do specific tasks if you don’t do them often. Recently in my microcontroller programming endeavors (where you’re pressed to conserve every bit of memory) I’ve needed to perform a lot of bitwise operations. If I’m storing true/false (1-bit) information in variables, it’s a waste to assign a whole variable to the task (even a char, the smallest variable in C is a waste because it uses 8 bits of memory!). When cramming multiple values into individual variables, it’s nice to know how to manipulate each bit of a variable.

Questions like “how do I retrieve the value of a certain bit in a variable”, “how do I set the value of a certain bit in a variable”, and “how do I flip a certain bit in a variable” can eventually be answered by twiddling around with bitwise operators in C, but often the solutions you randomly discover this way are not elegant or efficient. This afternoon I ran across the following chart on an Arduino help site and although I’m not a fan of Arduino, I can certainly appreciate the chart. I hope you find it as useful as I did.

y = (x>>n)&1;    // stores nth bit of x in y.  y becomes 0 or 1.

x&=~(1<<n);      // forces nth bit of x to be 0.  all other bits left alone.

x&=(1<<(n+1))-1;   // leaves lowest n bits of x; all higher bits set to 0.

x|=(1<<n);       // forces nth bit of x to be 1.  all other bits left alone.

x^=(1<<n);       // toggles nth bit of x.  all other bits left alone.

x=~x;              // toggles ALL the bits in x.

» Be the first to comment!



Jedi Soldering Skills
Posted by
Scott on June 22nd, 2009 | 917 words | No Comments (yet)
Filed under: Circuitry, Microcontrollers, Prime Numbers

I’m currently challenging myself by creating a microcontroller-based project a few orders of magnitude more complex than anything I’ve ever done before. Although this is probably on par with projects you might see being created by senior electrical engineering seniors, keep in mind that I have no formal training in engineering, and that my MS is in molecular biology. I just started learning about circuitry / microcontrollers a few months ago, and challenge myself to learn more by continually attacking greater and greater challenges. Here’s what I began working on last night:
layout

This if the first entry describing the creation of my non-prototype microcontroller-powered prime number generator. I made a proof of concept device a few weeks ago which calculates prime numbers (up to 2^25, about 33.5 million) and displays the results in binary form using 25 LEDs assembled in a 5×5 matrix. I added an extra column of 5 LEDs for a final matrix size of 6×5, illuminated by multiplexing through 11 IO pins of an ATMEL ATTiny2313. My new project will do the same thing, except it can calculate prime numbers up to 2^30 (over 1 billion!). Instead of only displaying 1 number, it will display 3 numbers (last prime, test number, and the divisor) using 90 LEDs. The picture above is of the main circuit board before I began soldering. The empty sockets will house a combination of 8-bit shift registers, binary-to-digital converters, and 7-segment display drivers all powered by an ATMEL ATMega8 microcontroller crystal-clocked at 10.042 MHz (arbitrary, but stable). Here’s what the underside looked like before I began soldering:
underside

I anticipate that this project will develop into a soldering nightmare. The board is nearly too small as it is, and I don’t have good wire for soldering. (I’m actually using the small wires from an old phone cord right now.) I included a potentiometer, 2 buttons, and 3 switches to aid with various settings (brightness, menus, etc). 3 Rows of LEDs (60 pins each) requires 12 shift registers (16 pins each) plus the 28-pin microcontroller makes about 400 solder points (YIKES!), so I anticipate the underside of this project will quickly grow to become a daunting mess of wires. Last night I finished the connections necessary to program the microcontroller, and for the microcontroller to control a single 8-bit shift register, allowing the first 8 LEDs of the first number to be controlled. Here’s what the soldering looked like. Remember, the dense clump of connections only controls 8 LEDs, so multiply this by more than 10 and that’s what I’ll have to do JUST to power the display.
wires

After programming with a straight-through DAPA style parallel-port programmer, I was able to shift data out to the single HC595 I had wired. I spent hours banging my head against the wall because nothing I did in the software would make the LEDs illuminate. I thought I was sending signals to the shift register wrong, or that I soldered something wrong. I finally concluded that somehow (probably when I was troubleshooting by applying 5v of power directly to the pins of the shift registers) I managed to burn out all 8 LEDs of the first light bar. I had to de-solder ALL of the connections you see in that picture, replace the bar with a new one (thank goodness I had an extra), and re-solder everything. I have a feeling that by the end of this project, I’ll be an expert at soldering. Here’s the program running controlling the first 8 bits only:
running

Supposedly the hard part is done for the display. The software was written in such a way that it will automatically begin lighting-up more LEDs as I wire them. The small node for the first shift register and 8 LEDs will be identical to the other 11, and as I solder them one by one I’ll get closer to my end goal. It will probably be many hours of soldering. In retrospect, I wish I purchased a bigger perfboard. Actually, in retrospect I wish I made a PCB!!

Update: After a few more hours (of soldering, troubleshooting, desoldering, and rewiring, and resoldering) I have my second 8-bit segment working. Note all the yellow (newly-added) wires. Multiply this by 10, and that’s what I have left to wire for the display alone!
img_2336
img_2326


» Be the first to comment!



Reading PCM Audio with Python
Posted by
Scott on June 19th, 2009 | 833 words | 2 Comments
Filed under: Python

When I figured this out I figured it was simply way too easy and way to helpful to keep to myself. Here I post (for the benefit of friends, family, and random Googlers alike) two examples of super-simplistic ways to read PCM data from Python using Numpy to handle the data and Matplotlib to display it. First, get some junk audio in PCM format (test.pcm).

import numpy
data = numpy.memmap("test.pcm", dtype='h', mode='r')
print "VALUES:",data

This code prints the values of the PCM file. Output is similar to:
VALUES: [-115 -129 -130 ..., -72 -72 -72]

To graph this data, use matplotlib like so:

import numpy, pylab
data = numpy.memmap("test.pcm", dtype='h', mode='r')
print data
pylab.plot(data)
pylab.show()

This will produce a graph that looks like this:
audiograph

Could it have been ANY easier? I’m so in love with python I could cry right now. With the powerful tools Numpy provides to rapidly and efficiently analyze large arrays (PCM potential values) combined with the easy-to-use graphing tools Matplotlib provides, I’d say you can get well on your way to analyzing PCM audio for your project in no time. Good luck!

FOR MORE INFORMATION AND CODE check out the highly-relevant posts:
Linear Data Smoothing In Python
Signal Filtering With Python
Circuits Vs. Software
and pretty much anything in the DIY ECG category of entries.

Let’s get fancy and use this concept to determine the number of seconds in a 1-minute PCM file in which a radio transmission occurs. I was given a 1-minute PCM file with a ~45 second transmission in the middle. Here’s the graph of the result of the code posted below it. (Detailed descriptions are at the bottom)
secpermin
Figure description: The top trace (light blue) is the absolute value of the raw sound trace from the PCM file. The solid black line is the average (per second) of the raw audio trace. The horizontal dotted line represents the threshold, a value I selected. If the average volume for a second is above the threshold, that second is considered as “transmission” (1), if it’s below the threshold it’s “silent” (0). By graphing these 60 values in bar graph form (bottom window) we get a good idea of when the transmission starts and ends. Note that the ENTIRE graphing steps are for demonstration purposes only, and all the math can be done in the 1st half of the code. Graphing may be useful when determining the optimal threshold though. Even when the radio is silent, the microphone is a little noisy. The optimal threshold is one which would consider microphone noise as silent, but consider a silent radio transmission as a transmission.

### THIS CODE DETERMINES THE NUMBER OF SECONDS OF TRANSMISSION
### FROM A 60 SECOND PCM FILE (MAKE SURE PCM IS 60 SEC LONG!)
import numpy
threshold=80 # set this to suit your audio levels
dataY=numpy.memmap("test.pcm", dtype='h', mode='r') #read PCM
dataY=dataY-numpy.average(dataY) #adjust the sound vertically the avg is at 0
dataY=numpy.absolute(dataY) #no negative values
valsPerSec=float(len(dataY)/60) #assume audio is 60 seconds long
dataX=numpy.arange(len(dataY))/(valsPerSec) #time axis from 0 to 60
secY,secX,secA=[],[],[]
for sec in xrange(60):
    secData=dataY[valsPerSec*sec:valsPerSec*(sec+1)]
    val=numpy.average(secData)
    secY.append(val)
    secX.append(sec)
    if val>threshold: secA.append(1)
    else: secA.append(0)
print "%d sec of 60 used = %0.02f"%(sum(secA),sum(secA)/60.0)
raw_input("press ENTER to graph this junk...")

### CODE FROM HERE IS ONLY USED TO GRAPH THE DATA
### IT MAY BE USEFUL FOR DETERMINING OPTIMAL THRESHOLD
import pylab
ax=pylab.subplot(211)
pylab.title("PCM Data Fitted to 60 Sec")
pylab.plot(dataX,dataY,'b',alpha=.5,label="sound")
pylab.axhline(threshold,color='k',ls=":",label="threshold")
pylab.plot(secX,secY,'k',label="average/sec",alpha=.5)
pylab.legend()
pylab.grid(alpha=.2)
pylab.axis([None,None,-1000,10000])
pylab.subplot(212,sharex=ax)
pylab.title("Activity (Yes/No) per Second")
pylab.grid(alpha=.2)
pylab.bar(secX,secA,width=1,linewidth=0,alpha=.8)
pylab.axis([None,None,-0.5,1.5])
pylab.show()

The output of this code:46 sec of 60 used = 0.77


» 2 Comments!



pySquelch – Frequency Activity Reports via Python
Posted by
Scott on June 18th, 2009 | 1,130 words | No Comments (yet)
Filed under: Circuitry, General, Linux, Python, Radio

I’ve been working on the pySquelch project which is basically a method to graph frequency usage with respect to time. The code I’m sharing below listens to the microphone jack on the sound card (hooked up to a radio) and determines when transmissions begin and end. First, I’ll entice you by showing some nice graphs of the output! I ran the code below for 24 hours and this is the result…
1png
Pretty good ‘eh? This graph represents traces of the frequency activity with respect to time. The semi-transparent gray line represents the raw frequency usage in fractional minutes the frequency was tied-up by transmissions. The solid blue line represents the same data but smoothed by 10 minutes (in both directions) by the Gaussian smoothing method modified slightly from my linear data smoothing with Python page.
2png

I used the code below to generate the log, and the code further below to create the graph from the log file. Assuming your microphone is enabled and everything else is working, this software will require you to determine your own threshold for talking vs. no talking. Read the code and you’ll figure out how test your sound card settings.

If you want to try this yourself you need a Linux system (a Windows system version could be created simply by replacing getVolEach() with a Windows-based audio level detection system) with Python and the alsaaudio, numpy, and matplotlib libraries. Try running the code on your own, and if it doesn’t recognize a library “aptitude search” for it. Everything you need can be installed from packages in the common repository.

#pySquelchLogger.py
import time, random, alsaaudio, audioop
inp = alsaaudio.PCM(alsaaudio.PCM_CAPTURE,alsaaudio.PCM_NONBLOCK)
inp.setchannels(2)
inp.setrate(1000)
inp.setformat(alsaaudio.PCM_FORMAT_S8)
inp.setperiodsize(100)
addToLog=""
lastLogTime=0

testLevel=False ### SET THIS TO 'True' TO TEST YOUR SOUNDCARD

def getVolEach():
        # this is a quick way to detect activity.
        # modify this function to use alternate methods of detection.
	while True:
		l,data = inp.read() # poll the audio device
		if l>0: break
	vol = audioop.max(data,1) # get the maximum amplitude
	if testLevel: print vol
	if vol>10: return True ### SET THIS NUMBER TO SUIT YOUR NEEDS ###
	return False

def getVol():
        # reliably detect activity by getting 3 consistant readings.
	a,b,c=True,False,False
	while True:
		a=getVolEach()
		b=getVolEach()
		c=getVolEach()
		if a==b==c:
			if testLevel: print "RESULT:",a
			break
	if a==True: time.sleep(1)
	return a

def updateLog():
        # open the log file, append the new data, and save it again.
	global addToLog,lastLogTime
	#print "UPDATING LOG"
	if len(addToLog)>0:
        	f=open('log.txt','a')
        	f.write(addToLog)
        	f.close()
        	addToLog=""
	lastLogTime=time.mktime(time.localtime())

def findSquelch():
        # this will record a single transmission and store its data.
	global addToLog
	while True: # loop until we hear talking
		time.sleep(.5)
		if getVol()==True:
			start=time.mktime(time.localtime())
			print start,
			break
	while True: # loop until talking stops
		time.sleep(.1)
		if getVol()==False:
			length=time.mktime(time.localtime())-start
			print length
			break
	newLine="%d,%d "%(start,length)
	addToLog+=newLine
	if start-lastLogTime>30: updateLog() # update the log

while True:
	findSquelch()

The logging code (above) produces a log file like this (below). The values represent the start time of each transmission (in seconds since epoch) followed by the duration of the transmission.

#log.txt
1245300044,5 1245300057,4 1245300063,16 1245300094,13 1245300113,4 1245300120,14 1245300195,4 1245300295,4 1245300348,4 1245300697,7 1245300924,3 1245301157,4 1245301207,12 1245301563,4 1245302104,6 1245302114,6 1245302192,3 1245302349,4 1245302820,4 1245304812,13 1245308364,10 1245308413,14 1245312008,14 1245313953,11 1245314008,6 1245314584,4 1245314641,3 1245315212,5 1245315504,6 1245315604,13 1245315852,3 1245316255,6 1245316480,5 1245316803,3 1245316839,6 1245316848,11 1245316867,5 1245316875,12 1245316893,13 1245316912,59 1245316974,12 1245316988,21 1245317011,17 1245317044,10 1245317060,6 1245317071,7 1245317098,33 1245317140,96 1245317241,15 1245317259,14 1245317277,8 1245317298,18 1245317322,103 1245317435,40 1245317488,18 1245317508,34 1245317560,92 1245317658,29 1245317697,55 1245317755,33 1245317812,5 1245317818,7 1245317841,9 1245317865,25 1245317892,79 1245317972,30 1245318007,8 1245318021,60 1245318083,28 1245318114,23 1245318140,25 1245318167,341 1245318512,154 1245318670,160 1245318834,22 1245318859,9 1245318870,162 1245319042,57 1245319102,19 1245319123,30 1245319154,18 1245319206,5 1245319214,13 1245319229,6 1245319238,6 1245319331,9 1245319341,50 1245319397,71 1245319470,25 1245319497,40 1245319540,8 1245319551,77 1245319629,4 1245319638,36 1245319677,158 1245319837,25 1245319865,40 1245319907,33 1245319948,92 1245320043,26 1245320100,9 1245320111,34 1245320146,8 1245320159,6 1245320167,8 1245320181,12 1245320195,15 1245320212,14 1245320238,18 1245320263,46 1245320310,9 1245320326,22 1245320352,27 1245320381,15 1245320398,24 1245320425,57 1245320483,16 1245320501,40 1245320543,43 1245320589,65 1245320657,63 1245320722,129 1245320853,33 1245320889,50 1245320940,1485 1245322801,7 1245322809,103 1245322923,5 1245322929,66 1245323553,4 1245324203,15 1245324383,5 1245324570,7 1245324835,4 1245325200,8 1245325463,5 1245326414,12 1245327340,12 1245327836,4 1245327973,4 1245330006,12 1245331244,11 1245331938,11 1245332180,5 1245332187,81 1245332573,5 1245333609,12 1245334447,10 1245334924,9 1245334945,4 1245334971,4 1245335031,9 1245335076,11 1245335948,16 1245335965,27 1245335993,113 1245336107,79 1245336187,64 1245336253,37 1245336431,4 1245336588,5 1245336759,7 1245337048,3 1245337206,13 1245337228,4 1245337309,4 1245337486,6 1245337536,8 1245337565,38 1245337608,100 1245337713,25 1245337755,169 1245337930,8 1245337941,20 1245337967,6 1245337978,7 1245337996,20 1245338019,38 1245338060,127 1245338192,30 1245338227,22 1245338250,15 1245338272,15 1245338310,3 1245338508,4 1245338990,5 1245339136,5 1245339489,8 1245339765,4 1245340220,5 1245340233,6 1245340266,10 1245340278,22 1245340307,7 1245340315,28 1245340359,32 1245340395,4 1245340403,41 1245340446,46 1245340494,58 1245340554,17 1245340573,21 1245340599,3 1245340604,5 1245340611,46 1245340661,26 1245340747,4 1245340814,14 1245341043,4 1245341104,4 1245341672,4 1245341896,5 1245341906,3 1245342301,3 1245342649,6 1245342884,5 1245342929,4 1245343314,6 1245343324,10 1245343335,16 1245343353,39 1245343394,43 1245343439,62 1245343561,3 1245343790,4 1245344115,3 1245344189,5 1245344233,4 1245344241,6 1245344408,12 1245344829,3 1245345090,5 1245345457,5 1245345689,4 1245346086,3 1245347112,12 1245348006,14 1245348261,10 1245348873,4 1245348892,3 1245350303,11 1245350355,4 1245350766,5 1245350931,3 1245351605,14 1245351673,55 1245351729,23 1245351754,5 1245352123,37 1245352163,21 1245352186,18 1245352209,40 1245352251,49 1245352305,8 1245352315,5 1245352321,6 1245352329,22 1245352353,48 1245352404,77 1245352483,58 1245352543,17 1245352570,19 1245352635,5 1245352879,3 1245352899,5 1245352954,4 1245352962,6 1245352970,58 1245353031,21 1245353055,14 1245353071,52 1245353131,37 1245353170,201 1245353373,56 1245353431,18 1245353454,47 1245353502,13 1245353519,106 1245353627,10 1245353647,12 1245353660,30 1245353699,42 1245353746,28 1245353776,29 1245353806,9 1245353818,21 1245353841,10 1245353853,6 1245353862,224 1245354226,4 1245354964,63 1245355029,4 1245355036,142 1245355180,148 1245355330,7 1245355338,23 1245355363,9 1245355374,60 1245355437,142 1245355581,27 1245355609,5 1245355615,2 1245355630,64 1245355700,7 1245355709,73 1245355785,45 1245355834,85 1245355925,9 1245356234,5 1245356620,6 1245356629,12 1245356643,29 1245356676,120 1245356798,126 1245356937,62 1245357001,195 1245357210,17 1245357237,15 1245357258,24 1245357284,53 1245357339,2 1245357345,27 1245357374,76 1245357452,28 1245357482,42 1245357529,14 1245357545,35 1245357582,74 1245357661,30 1245357693,19 1245357714,38 1245357758,11 1245357777,37 1245357817,49 1245357868,19 1245357891,31 1245357931,48 1245357990,49 1245358043,24 1245358082,22 1245358108,17 1245358148,18 1245358168,7 1245358179,6 1245358186,19 1245358209,17 1245358229,5 1245358240,9 1245358252,10 1245358263,6 1245358272,9 1245358296,26 1245358328,49 1245358381,6 1245358389,38 1245358453,19 1245358476,24 1245358504,21 1245358533,76 1245358628,24 1245358653,10 1245358669,105 1245358781,20 1245358808,14 1245358836,6 1245358871,61 1245358933,0 1245358936,44 1245358982,11 1245358996,25 1245359023,15 1245359040,32 1245359076,19 1245359099,13 1245359117,16 1245359138,12 1245359161,33 1245359215,32 1245359249,14 1245359272,7 1245359314,10 1245359333,36 1245359371,21 1245359424,10 1245359447,61 1245359514,32 1245359560,42 1245359604,87 1245359700,60 1245359762,23 1245359786,4 1245359791,8 1245359803,6 1245359813,107 1245359922,29 1245359953,22 1245359978,86 1245360069,75 1245360147,22 1245360170,0 1245360184,41 1245360239,15 1245360256,34 1245360301,37 1245360339,1 1245360342,28 1245360372,20 1245360394,32 1245360440,24 1245360526,3 1245360728,3 1245361011,4 1245361026,35 1245361064,137 1245361359,5 1245362172,11 1245362225,21 1245362248,51 1245362302,20 1245362334,42 1245362418,12 1245362468,7 1245362557,9 1245362817,3 1245363175,4 1245363271,4 1245363446,3 1245363539,4 1245363573,4 1245363635,1 1245363637,3 1245363740,5 1245363875,3 1245364075,4 1245364354,14 1245364370,19 1245364391,49 1245364442,34 1245364478,23 1245364502,80 1245364633,15 1245364650,8 1245364673,16 1245364691,47 1245364739,53 1245364795,39 1245364836,25 1245365353,4 1245365640,11 1245365665,5 1245365726,8 1245365778,7 1245365982,4 1245366017,13 1245366042,6 1245366487,4 1245366493,4 1245366500,4 1245366507,3 1245366622,5 1245366690,5 1245366946,4 1245366953,16 1245366975,8 1245366996,7 1245367005,7 1245367031,6 1245367040,9 1245367051,7 1245367059,23 1245367084,76 1245367166,158 1245367740,4 1245367804,3 1245367847,4 1245367887,9 1245369300,10 1245369611,12 1245370038,10 1245370374,8 1245370668,5 1245370883,5 1245370927,7 1245370945,9 1245370961,16 1245370978,414 1245371398,135 1245371535,252 1245371791,238 1245372034,199 1245372621,4 1245372890,5 1245373043,7 1245373060,9 1245373073,6 1245373081,68 1245373151,10 1245373162,49 1245373212,79 1245373300,12 1245373313,38 1245373353,20 1245373374,59 1245373435,28 1245373465,94 1245373560,11 1245373574,53 1245373629,22 1245373654,6 1245373662,334 1245373998,169 1245374176,41 1245374219,26 1245374246,51 1245374299,31 1245374332,57 1245374391,55 1245374535,4 1245374759,7 1245374769,200 1245374971,215 1245375188,181 1245375371,81 1245375455,59 1245375516,33 1245375552,19 1245375572,56 1245375629,220 1245375850,32 1245375884,26 1245375948,7 1245375964,114 1245376473,4 1245376810,13 1245378296,10 1245378950,12 1245379004,3 1245379569,4 1245379582,4 1245379615,6 1245380030,3 1245380211,4 1245380412,14 1245380727,4 1245380850,4 

This log file is only 7.3 KB. At this rate, a years’ worth of log data can be stored in less than 3MB of plain text files. Awesome! The data presented here can be graphed (producing the image at the top of the page) using the following code:

#pySquelchGrapher.py
print "loading libraries...",
import pylab, datetime, numpy
print "complete"

def loadData(fname="log.txt"):
	print "loading data...",
	# load signal/duration from log file
	f=open(fname)
	raw=f.read()
	f.close()
	raw=raw.replace('\n',' ')
	raw=raw.split(" ")
	signals=[]
	for line in raw:
		if len(line)<3: continue
		line=line.split(',')
		sec=datetime.datetime.fromtimestamp(int(line[0]))
		dur=int(line[1])
		signals.append([sec,dur])
	print "complete"
	return signals

def findDays(signals):
	# determine which days are in the log file
	print "finding days...",
	days=[]
	for signal in signals:
		day = signal[0].date()
		if not day in days:
			days.append(day)
	print "complete"
	return days

def genMins(day):
	# generate an array for every minute in a certain day
	print "generating bins...",
	mins=[]
	startTime=datetime.datetime(day.year,day.month,day.day)
	minute=datetime.timedelta(minutes=1)
	for i in xrange(60*60):
		mins.append(startTime+minute*i)
	print "complete"
	return mins

def fillMins(mins,signals):
	print "filling bins...",
	vals=[0]*len(mins)
	dayToDo=signals[0][0].date()
	for signal in signals:
		if not signal[0].date() == dayToDo: continue
		sec=signal[0]
		dur=signal[1]
		prebuf = sec.second
		minOfDay=sec.hour*60+sec.minute
		if dur+prebuf<60: # simple case, no rollover seconds
			vals[minOfDay]=dur
		else: # if duration exceeds the minute the signal started in
			vals[minOfDay]=60-prebuf
			dur=dur+prebuf
			while (dur>0): # add rollover seconds to subsequent minutes
				minOfDay+=1
				dur=dur-60
				if dur< =0: break
				if dur>=60: vals[minOfDay]=60
				else: vals[minOfDay]=dur
	print "complete"
	return vals

def normalize(vals):
	print "normalizing data...",
	divBy=float(max(vals))
	for i in xrange(len(vals)):
		vals[i]=vals[i]/divBy
	print "complete"
	return vals

def smoothListGaussian(list,degree=10):
	print "smoothing...",
	window=degree*2-1
	weight=numpy.array([1.0]*window)
	weightGauss=[]
	for i in range(window):
		i=i-degree+1
		frac=i/float(window)
		gauss=1/(numpy.exp((4*(frac))**2))
		weightGauss.append(gauss)
	weight=numpy.array(weightGauss)*weight
	smoothed=[0.0]*(len(list)-window)
	for i in range(len(smoothed)):
	  smoothed[i]=sum(numpy.array(list[i:i+window])*weight)/sum(weight)
	while len(list)>len(smoothed)+int(window/2):
		smoothed.insert(0,smoothed[0])
	while len(list)>len(smoothed):
		smoothed.append(smoothed[0])
	print "complete"
	return smoothed

signals=loadData()
days=findDays(signals)
for day in days:
	mins=genMins(day)
	vals=normalize(fillMins(mins,signals))
	fig=pylab.figure()
	pylab.grid(alpha=.2)
	pylab.plot(mins,vals,'k',alpha=.1)
	pylab.plot(mins,smoothListGaussian(vals),'b',lw=1)
	pylab.axis([day,day+datetime.timedelta(days=1),None,None])
	fig.autofmt_xdate()
	pylab.title("147.120 MHz Usage for "+str(day))
	pylab.xlabel("time of day")
	pylab.ylabel("fractional usage")
	pylab.show()

If you have any questions, Google first, then feel free to contact me if you still can’t get it. Good luck!!


» Be the first to comment!



Updated pySquelch Code
Posted by
Scott on June 18th, 2009 | 512 words | No Comments (yet)
Filed under: General, Python, Radio

While working to improve the python-based frequency activity logger I wrote a couple of entries back, I greatly increased its accuracy. I won’t go into the details other than saying that it simply polls the sound card a few times a second to listen for when a transmission begins and ends. The new data file format appears like this…

1245316480,5
1245316803,3
1245316839,6
1245316848,11
1245316867,5
1245316875,12
1245316893,13

The major change is that the date and time is recorded as seconds since epoch (Unix time) and the duration of the transmission is given after the comma. This greatly simplifies post-processing. Here is the revised python code to poll the sound card and generate such a log file:

##################################
##### UPDATED CODE IS IN A NEWER ENTRY #####
### CHECK THE PYTHON AND RADIO CATEGORIES ###
##################################
import time, random, alsaaudio, audioop
inp = alsaaudio.PCM(alsaaudio.PCM_CAPTURE,alsaaudio.PCM_NONBLOCK)
inp.setchannels(2)
inp.setrate(1000)
inp.setformat(alsaaudio.PCM_FORMAT_S8)
inp.setperiodsize(100)
addToLog=""
lastLogTime=0

testLevel=False ### SET THIS TO TRUE TO TEST YOUR SOUNDCARD

def getVolEach():
        # this is a quick way to detect activity.
        # modify this function to use alternate methods of detection.
	while True:
		l,data = inp.read() # poll the audio device
		if l>0: break
	vol = audioop.max(data,1) # get the maximum amplitude
	if testLevel: print vol
	if vol>10: return True ### SET THIS NUMBER TO SUIT YOUR NEEDS ###
	return False

def getVol():
        # reliably detect activity by getting 3 consistant readings.
	a,b,c=True,False,False
	while True:
		a=getVolEach()
		b=getVolEach()
		c=getVolEach()
		if a==b==c:
			if testLevel: print "RESULT:",a
			break
	if a==True: time.sleep(1)
	return a

def updateLog():
        # open the log file, append the new data, and save it again.
	global addToLog,lastLogTime
	#print "UPDATING LOG"
	if len(addToLog)>0:
        	f=open('log.txt','a')
        	f.write(addToLog)
        	f.close()
        	addToLog=""
	lastLogTime=time.mktime(time.localtime())

def findSquelch():
        # this will record a single transmission and store its data.
	global addToLog
	while True: # loop until we hear talking
		time.sleep(.5)
		if getVol()==True:
			start=time.mktime(time.localtime())
			print start,
			break
	while True: # loop until talking stops
		time.sleep(.1)
		if getVol()==False:
			length=time.mktime(time.localtime())-start
			print length
			break
	newLine="%d,%d\n"%(start,length)
	addToLog+=newLine
	if start-lastLogTime>30: updateLog() # update the log

while True:
	findSquelch()

To test the functionality of the code visually, you can use this quick and dirty method of graphing the log files output by this program. Keep in mind THIS IS NOT INTENDED TO BE USED other than to simply test the program.

##################################
##### UPDATED CODE IS IN A NEWER ENTRY #####
### CHECK THE PYTHON AND RADIO CATEGORIES ###
##################################
import pylab
import time, datetime
f=open('log.txt')
raw=f.read()
f.close()
raw=raw.split("\n")
pylab.figure()
for line in raw:
        if len(line)<5:continue
        line=line.split(",")
        sec=datetime.datetime.fromtimestamp(int(line[0]))
        dur=int(line[1])
        sec2=datetime.datetime.fromtimestamp(int(line[0])+int(line[1]))
        pylab.fill([sec,sec,sec2,sec2],[0,1,1,0],'k',lw=0,alpha=.5)
pylab.show()

» Be the first to comment!



UCF Tailgate June 2009
Posted by
Scott on June 13th, 2009 | 477 words | 2 Comments
Filed under: Circuitry, General, Radio

This morning I woke up at 4:45am, hopped out of bed, and raced to the university parking lot for field day. It’s pretty much a flea market with an emphasis in ham radio and associated electronics. This is a panorama of the parking lot the tailgate was held in, taken from the roof of a parking garage at about 9am. The UCF ARC (the amateur radio club which sponsored the event) is stationed under the white tent.
ucf_tailgate_2009

My goal was to purchase a [working] oscilloscope, and I lucked-out. I ended-up purchasing two, and I’m glad I did! The 1st one (the one with the green circular screen) crapped-out on me after literally 1 minute. (By crapped-out I mean it started spurring thick gray smoke and made my whole apartment smell like a burned marshmallow). At $5, I’m not crying over it. The second one is a 1969 Tektronix 561A 10 MHz oscilloscope. Just think, these things just started started being produced the same year Neil Armstrong walked on the moon. I tested it and it seems to be functioning well. At $10, I’m very happy!
scopes
The back one there is the one that died on me. The closer one works beautifully, but a lot of the knobs are staticky and there’s a lot of dirt/dust all over it. Inside it looks like a dust bomb detonated. It’ll take enough of my time to restore that I’m thinking of ignoring the older, non-working one. I don’t want to trash it, but I don’t want to store it. Maybe I’ll salvage parts from it? Hmm, vacuum tubes.
scope_box

Here you can see it attached to my prime number generator described in agonizingly-boring detail over the last several weeks’ posts. It’s attached to one of the microcontroller pins responsible for multiplexing the LED display. Finally, a way to assess high speed power output as a function of time. The output of the microcontroller isn’t performing like I expected, and since it’s a series of pulses I can’t use a volt meter to measure its output. Thus, the need [more like desire] for an oscilloscope.


» 2 Comments!



Python-Powered Frequency Activity Logger
Posted by
Scott on June 12th, 2009 | 920 words | 1 Comment
Filed under: General, Linux, Python, Radio

As anyone who visits this website can tell you, I’m inexplicably drawn toward inventing new ways of using Python (often in conjunction with MatPlotLib) to graphically visualize data in new ways. When I found out a fellow ham in Orlando was using his computer to stream (and serve recorded streams of) a popular local repeater frequency over the internet. I got excited because of the potential for generating [quasi-useful, and at least interesting] data from the existing setup. Since this guy already has his radio connected to his PC’s microphone jack, I figured I could write a Python app. to check the microphone input to determine if anyone is using the frequency. By recording when people start and stop talking, I can create a log of frequency activity. Later I can write software to visualize this data. I’ll talk about that in a later post. For now, here’s how I used Python and a Linux box (Ubuntu, with the python-alsaaudio package installed) to generate such logs.

We can visualize this data using some more simple Python code. Long term it would be useful to visualize frequency activity similarly to how I graphed computer usage at work over the last year but for now since I don’t have any large amount of data to work with. I’ll just write cote to visualize a QSO (conversation) with respect to time. It should be self-explanatory. This data came from data points displayed in the video (provided at the end of this post too).
qsographpng

And, of course, the code I used to generate the log files (seen running in video above): Briefly, this program checks the microphone many times every second to determine if its state has changed (talking/no talking) and records this data in a text file (which it updates every 10 seconds). Matplotlib can EASILY be used to graph data from such a text file.

##################################
##### UPDATED CODE IS IN A NEWER ENTRY #####
### CHECK THE PYTHON AND RADIO CATEGORIES ###
##################################
import alsaaudio, time, audioop, datetime
inp = alsaaudio.PCM(alsaaudio.PCM_CAPTURE,alsaaudio.PCM_NONBLOCK)
inp.setchannels(1)
inp.setrate(4000)
inp.setformat(alsaaudio.PCM_FORMAT_S16_LE)
inp.setperiodsize(1)

squelch = False
lastLog = 0
dataToLog = ""

def logIt(nowSquelch):
	global dataToLog, lastLog
	timeNow = datetime.datetime.now()
	epoch = time.mktime(timeNow.timetuple())
	if nowSquelch==True: nowSquelch=1
	else: nowSquelch=0
	logLine="%s %d\n"%(timeNow, nowSquelch)
	print timeNow, nowSquelch
	dataToLog+=logLine
	if epoch-lastLog>10:
		#print "LOGGING..."
		f=open('squelch.txt','a')
		f.write(dataToLog)
		f.close()
		lastLog = epoch
		dataToLog=""

while True:
	l,data = inp.read()
	if l:
		vol = audioop.max(data,2)
		#print vol #USED FOR CALIBRATION
		if vol>800: nowSquelch = True
		else: nowSquelch = False
		if not nowSquelch == squelch:
			logIt(nowSquelch)
			squelch = nowSquelch
	time.sleep(.01)

To use this code make sure that you’ve properly calibrated it. See the “vol>800″ line? That means that if the volume in the microphone is at least 800, it’s counted as talking, and less than it’s silence. Hopefully you can find a value that counts as silence when the squelch is active, but as talking when the squelch is broken (even if there’s silence). This is probably best achieved with the radio outputting at maximum volume. You’ll have to run the program live with that line un-commented to view the data values live. Find which values occur for squelch on/off, and pick your threshold accordingly.

After you run the code, it should output data like this:

##################################
##### UPDATED CODE IS IN A NEWER ENTRY #####
### CHECK THE PYTHON AND RADIO CATEGORIES ###
##################################
2009-06-12 17:13:28.347551 1
2009-06-12 17:13:29.306556 0
2009-06-12 17:13:30.176076 1
2009-06-12 17:13:41.533238 0
2009-06-12 17:13:45.435846 1
2009-06-12 17:14:21.890423 0
2009-06-12 17:14:25.605424 1
2009-06-12 17:14:41.904548 0
2009-06-12 17:14:43.903870 1
2009-06-12 17:14:55.981045 0
2009-06-12 17:14:59.377337 1
2009-06-12 17:15:12.365393 0
2009-06-12 17:15:15.773681 1
2009-06-12 17:15:33.165695 0
2009-06-12 17:15:36.155376 1
2009-06-12 17:16:04.181281 0
2009-06-12 17:16:07.423343 1
2009-06-12 17:16:15.724043 0
2009-06-12 17:16:18.411122 1
2009-06-12 17:16:35.304314 0
2009-06-12 17:16:36.371054 1
2009-06-12 17:16:50.818980 0
2009-06-12 17:16:53.995472 1
2009-06-12 17:17:26.675227 0
2009-06-12 17:17:29.969474 1
2009-06-12 17:17:47.907444 0
2009-06-12 17:17:50.361119 1
2009-06-12 17:18:04.630950 0
2009-06-12 17:18:05.565729 1
2009-06-12 17:18:11.225585 0
2009-06-12 17:18:14.787122 1
2009-06-12 17:18:41.261046 0
2009-06-12 17:18:43.819297 1
2009-06-12 17:18:52.164121 0
2009-06-12 17:18:53.100013 1
2009-06-12 17:19:05.348166 0
2009-06-12 17:19:09.316964 1
2009-06-12 17:19:19.107860 0
2009-06-12 17:19:19.941314 1
2009-06-12 17:19:26.254173 0
2009-06-12 17:19:33.844900 1
2009-06-12 17:19:46.501087 0
2009-06-12 17:19:49.360008 1
2009-06-12 17:19:59.917436 0
2009-06-12 17:20:04.335205 1
2009-06-12 17:20:17.622417 0
2009-06-12 17:20:18.501208 1
2009-06-12 17:20:26.866216 0
2009-06-12 17:20:28.694763 1
2009-06-12 17:20:36.097119 0

After that you can visualize the data with the following code. Note that this is SEVERELY LIMITED and is only useful when graphing a few minutes of data. I don’t have hours/days of data to work with right now, so I won’t bother writing code to graph it. This code produced the graph seen earlier in this page. Make sure matplotlib is installed on your box.

##################################
##### UPDATED CODE IS IN A NEWER ENTRY #####
### CHECK THE PYTHON AND RADIO CATEGORIES ###
##################################
import pylab

def loadData():
	#returns Xs
	import time, datetime, pylab
	f=open('good.txt')
	raw=f.readlines()
	f.close()
	onTimes=[]
	timeStart=None
	lastOn=False
	for line in raw:
		if len(line)<10: continue
		line = line.strip('\n').split(" ")
		t=line[0]+" "+line[1]
		t=t.split('.')
		thisDay=time.strptime(t[0], "%Y-%m-%d %H:%M:%S")
		e=time.mktime(thisDay)+float("."+t[1])
		if timeStart==None: timeStart=e
		if line[-1]==1: stat=True
		else: stat=False
		if not lastOn and line[-1]=="1":
			lastOn=e
		else:
			onTimes.append([(lastOn-timeStart)/60.0,\
							(e-timeStart)/60.0])
			lastOn=False
	return onTimes

times = loadData()
pylab.figure(figsize=(8,3))
for t in times:
	pylab.fill([t[0],t[0],t[1],t[1]],[0,1,1,0],'k',lw=0,alpha=.5)
pylab.axis([None,None,-3,4])
pylab.title("A little QSO")
pylab.xlabel("Time (minutes)")
pylab.show()

» 1 Comment so far...



Prime-ary Prototype: Complete!
Posted by
Scott on June 10th, 2009 | 596 words | No Comments (yet)
Filed under: C/C++, Circuitry, General, Microcontrollers, Prime Numbers

For the last several weeks I’ve been hacking together a small prototype of a microcontroller (ATTiny2313)-powered prime number generator. I can finally say that it (the prototype) is complete, functional, and elegant [get the song I'm referencing while it's up!]. The name says what it does, so I won’t waste time describing it. If you’re interested in how it does what it does, check out the other posts with the same category tag for a full (and I mean full as in “more than you ever wanted to know”) description. A schematic is soon to come. Code is below. For now, here’s a video of the completed project:

And the source code I used. I chose the ATTiny2313 because it was cheap (~$2) and has 18 IO pins to work with. I had to fight memory usage every step of the way. I’m limited to 2kb, and this program compiles within 1% of that! For future projects (more LEDs, more menus) I’ll use the 20-pin and/or 40-pin ATMega series. I’m thinking about having one be in charge of the display (multiplexing) leaving the other to think about generating prime numbers.

#define F_CPU 10240000
#include <avr/interrupt.h>
#include <util/delay.h>
#include <math.h>

// ~~~ PORT D ~~~
#define r1 0b00000100
#define r2 0b00000010
#define r3 0b00001000
#define r4 0b00100000
#define r5 0b00010000
#define f1 0b01000000
#define id 0b00000001
#define f0 0b10111111

// ~~~ PORT B ~~~
#define c1 0b00010000
#define c2 0b00000001
#define c3 0b00000010
#define c4 0b00000100
#define c5 0b00001000

char delay=1;
int redo=0;

char rows[] = {r1,r2,r3,r4,r5};
char cols[] = {c1,c2,c3,c4,c5};
char vals[] = {0,0,0,0,0};
char funcs=f1+id;
unsigned long showNum=5;//33554431;

void displayNum();
void incrimentNum();
void menu_pause();
void menuCheck();
char button();
char isPrime(unsigned long);
unsigned int twoTo(char);

int main(void) {
  DDRD = r1+r2+r3+r4+r5+f1+id;
  DDRB = c1+c2+c3+c4+c5;
  DDRB &= ~_BV(DDB7);
  PORTB = 0;
  PORTD = 0;
  unsigned int i=0;
  int j;
  //convertNum();
  //showNum=5;
  while (1) {

    showNum+=1;   

    if (isPrime(showNum)){
      convertNum();
    }

    for (j=0;j<redo;j++) {
      displayNum();
      menu();
      }
    if (i%10==0){funcs^=r1;
      funcs^=id;
      if (i%100==0){funcs^=r2;
        if (i%1000==0){funcs^=r3;i=0;
        }
      }
    }
    i++;
  }
  return 0;
}

char isPrime(unsigned long test){
  if (test%2==0) return 0;
  unsigned long div = 3;
  while(div*div<test+1){
    if (test%div==0) return 0;
    div+=2;
    displayNum();
  }
  return 1;
}

void menu(){
  //return;
  char j,but;
  but = button();
  if (but==0) return;
  else if (but==1){
    while (1) {
      PORTD=id;
      if (PINB & _BV(PB7)) {
        funcs=f1;
        for (j=0;j<200;j++){
          if (j%25==0) funcs^=r1;
          displayNum();
        }
      }
      else {
        if (redo==0) redo=200;
        else redo=0;
        _delay_ms(300);
        return;
      }
    }
  }
  return;
}

char button(){
  PORTD=id;
  if (PINB & _BV(PB7)) return 0; // not pressed
  _delay_ms(1000);
  if (PINB & _BV(PB7)) return 1; // pressed
  return 2; // held down
}

void convertNum(){
  char col,row,rowcode;
  unsigned long msk=1;
  for (col=0;col<5;col++){
    rowcode=0;
    for (row=0;row<5;row++){
      if (showNum&msk) rowcode+=rows[row];
      msk = msk < < 1;
    }
    vals[col]=rowcode;
  }
  return;
}

void displayNum(){
  char i,j;
    PORTB = 0b0;
    PORTD = funcs;
    _delay_ms(delay);
    for (i=0;i<5;i++){
      PORTD = vals[i];
      PORTB = cols[i];
      _delay_ms(delay);
    }
  return;
}

» Be the first to comment!



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