I’m working late in lab (as usual) and I went to open Firefox (my default web browser), so I clicked Start and selected “Mozilla Firefox” at the top (where your default browser usually goes in XP). Nothing happened… then this message popped up! I had to take a screen shot because it’s so bizarre.
I swear this is straight from my screen (PrntScrn -> mspaint (pasted) -> PNG) with no modifications. I especially love how the computer is trying to tell me what could have gone wrong; the internet might have been moved, renamed, or removed. Slick.
DNA provides a compact means of data storage. 1 gram of dehydrated DNA (about the mass of a paperclip, or the amount of sugar in a sugar packet) contains 10^21 bits of data. That’s over 100 billion gigabytes. A basic computer hard drive is about 250 GB and is about 3.5 inches wide, 5 inches long, and 1/2 of an inch thick. If You stacked 250 GB hard drives on on top of each other, you would need 400 million of them to store that much data, creating a stack 3,157 miles high (approximately the distance from Orlando, FL to Seattle, WA). All that data from 1 gram of DNA. Wow.
*I spent the day* in the lab with some random time on my hands between adding reagents to an ongoing immunohistochemical reaction I was performing. At one point I decided to further investigate the field of bioinformatics (is it worth seeking a PhD in this field if I don’t get into dental school again?). UCF offers a PhD in bioinformatics but it’s a new and small department (I think there are only 4 faculty). The degree itself is a degree in computer science (the logic side of computers, more programming than designing hardware). A degree in bioinformatics combines molecular biology (DNA, proteins, etc), computer science (programming), and statistics (developing code to analyze biological data). I feel a need to express what it is, because it’s not something that is commonly understood. Do you know what people who study bioinformatics do?
*I came across a paper* today “Structural Alignment of Pseudoknotted RNA”:http://cseweb.ucsd.edu/users/shzhang/app/RECOMB2005_pseudoknot.pdf (by Han B, Dost B, Bafna V, and Zhang S.) which is a good example of the practice of bioinformatics. Think about what goes on in a cell… the sequence of a gene (a short region of DNA) is copied (letter-by-letter) onto an RNA molecule. The RNA molecule is later read by an enzyme (called a ribosome) and converted into a protein based on its sequence. (This process is the central dogma of molecular biology) Traditionally, it was believed that RNA molecules’ only function was to copy gene sequences from DNA to ribosomes, but recently (the last several years) it was discovered that some small RNA molecules are never read and turned into proteins, but rather serve their own unique functions! For example, some RNA molecules (siRNAs) can actually turn genes on and off, and have been assosiated with cancer development and other immune diseases. Given the human genome (the ~3 billion letter long sequence all of our DNA), how can we determine what regions form these functional RNA molecules which don’t get converted into proteins? The paper I mentioned earlier addresses this. An algorithm was developed and used to test regions of DNA and predict its probability of forming small RNA molecules. Spikes in this trace (figure 7 of the paper) represent areas of the DNA which are likely to form these RNA molecules. (Is this useful? What if you were to compare these results between normal person and someone with cancer?)
*After reading the article* I thought to myself “Hmmm… logically manipulating large amounts of linear data… why does this seem familiar?” Then I realized how similar my current programming projects are with this one. (see “my latest DIY ECG data”:http://www.swharden.com/blog/images/ecg_goodie.png posted a couple days ago) Consider the trace (pictured, figure 7 in “Structural Alignment of Pseudoknotted RNA”:http://cseweb.ucsd.edu/users/shzhang/app/RECOMB2005_pseudoknot.pdf) of score (the likelihood that a region of DNA forms an RNA molecule), where peaks represent likely locations of RNA formation. Just generate the trace, determine the positions of the peaks, and you’re golden. How similar is this to the work I’ve been doing with my homemade ECG machine, where I perform signal analysis to eliminate electrical noise and then analyze the resulting trace to isolate and identify peaks corresponding to heartbeats?
*After reading* I shivered from mental-overload. There are so many exciting Python projects in the field of bioinformatics that are just waiting for me to begin work on! I know I’m like a child sometimes, but hey it’s my personality. I get excited. It’s just that I get excited about tacky things these days. Anyway, I got the itch to write a string-analysis program. What does it do? It reads the content of my website (exported in the form of a SQL backup query generated by PHPmyAdmin, pictured), splits it up by date, and allows for its analysis. Ultimately I want to track the usage of certain words (i.e.: the inverse relationship between the words “girls” and “python”), but for now I wrote a script which plots the number of words I wrote. Observe the output.
*Pretty cool huh?* Check out all those spikes between 2004 and 2005! (previous figure) Not only are they numerous (meaning many posts), but they’re also high (meaning many words per post). As you can see by the top trace, the most significant contribution to my site occurred during this time. So, let’s zoom in on it! (next figure)
*And of course, the code to produce this…* (obviously you have to have a wordpress backup SQL file in the same folder – if you want mine let me know and I’ll email it to ya’)
import datetime, pylab, numpy
# Let's convert SQL-backups of my WordPress blog into charts! yay!
class blogChrono():
baseUrl="http://www.SWHarden.com/blog"
posts=[]
dates=[]
def __init__(self,fname):
self.fname=fname
self.load()
def load(self):
print "loading [%s]..."%self.fname,
f=open(self.fname)
raw=f.readlines()
f.close()
for line in raw:
if "INSERT INTO" in line
and';' in line[-2:-1]
and " 'post'," in line[-20:-1]:
post={}
line=line.split("VALUES(",1)[1][:-3]
line=line.replace(', NULL',', None')
line=line.replace(", '',",", None,")
line=line.replace("''","")
c= line.split(',',4)[4][::-1]
c= c.split(" ,",21)
text=c[-1]
text=text[::-1]
text=text[2:-1]
text=text.replace('"""','###')
line=line.replace(text,'blogtext')
line=line.replace(', ,',', None,')
line=eval("["+line+"]")
if len(line[4])>len('blogtext'):
x=str(line[4].split(', '))[2:-2]
raw=str(line)
raw=raw.replace(line[4],x)
line=eval(raw)
post["id"]=int(line[0])
post["date"]=datetime.datetime.strptime(line[2],
"%Y-%m-%d %H:%M:%S")
post["text"]=eval('"""'+text+' """')
post["title"]=line[5]
post["url"]=line[21]
post["comm"]=int(line[25])
post["words"]=post["text"].count(" ")
self.dates.append(post["date"])
self.posts.append(post)
self.dates.sort()
d=self.dates[:]
i,newposts=0,[]
while len(self.posts)>0:
die=min(self.dates)
for post in self.posts:
if post["date"]==die:
self.dates.remove(die)
newposts.append(post)
self.posts.remove(post)
self.posts,self.dates=newposts,d
print "read %d posts!n"%len(self.posts)
#d=blogChrono('sml.sql')
d=blogChrono('test.sql')
fig=pylab.figure(figsize=(7,5))
dates,lengths,words,ltot,wtot=[],[],[],[0],[0]
for post in d.posts:
dates.append(post["date"])
lengths.append(len(post["text"]))
ltot.append(ltot[-1]+lengths[-1])
words.append(post["words"])
wtot.append(wtot[-1]+words[-1])
ltot,wtot=ltot[1:],wtot[1:]
pylab.subplot(211)
#pylab.plot(dates,numpy.array(ltot)/(10.0**6),label="letters")
pylab.plot(dates,numpy.array(wtot)/(10.0**3),label="words")
pylab.ylabel("Thousand")
pylab.title("Total Blogged Words")
pylab.grid(alpha=.2)
#pylab.legend()
fig.autofmt_xdate()
pylab.subplot(212,sharex=pylab.subplot(211))
pylab.bar(dates,numpy.array(words)/(10.0**3))
pylab.title("Words Per Entry")
pylab.ylabel("Thousand")
pylab.xlabel("Date")
pylab.grid(alpha=.2)
#pylab.axis([min(d.dates),max(d.dates),None,20])
fig.autofmt_xdate()
pylab.subplots_adjust(left=.1,bottom=.13,right=.98,top=.92,hspace=.25)
width=675
pylab.savefig('out.png',dpi=675/7)
pylab.show()
print "DONE"
*I wrote a Python script to analyze the word frequency* of the blogs in my website (extracted from an SQL query WordPress backup file) for frequency. “This is what I came up with”:http://swharden.com/little/worddump.html I then took my giant list over to “Wordie”:http://www.wordle.net/create and had them create a super-cool little word jumble. Neat, huh? Here’s “a picture”:http://www.SWHarden.com/blog/images/wordie2.png that’s cool but not worth posting.
*This is the script to make the worddump:*
import datetime, pylab, numpy
f=open('dump.txt')
body=f.read()
f.close()
body=body.lower()
body=body.split(" ")
tot=float(len(body))
words={}
for word in body:
for i in word:
if 65< =ord(i)<=90 or 97<=ord(i)<=122: pass
else: word=None
if word:
if not word in words:words[word]=0
words[word]=words[word]+1
data=[]
for word in words: data.append([words[word],word])
data.sort()
data.reverse()
out= "Out of %d words...n"%tot
xs=[]
for i in range(1000):
d=data[i]
out += '"%s" ranks #%d used %d times (%.05f%%)n'%
(d[1],i+1,d[0],d[0]/tot)
f=open("dump.html",'w')
f.write(out)
f.close()
print "DONE"
So I was reviewing my website statistics generated by a Python script I wrote when I noticed a peculiarity so bizarre that it made me questin the very purpose of my life. Okay maybe it wasn’t that bizarre, but it was interesting. The python script (which is automatically run every hour) downloads my latest access.log and saves it to its own folder. It then analyzes the data, creates some charts and graphs, and dumps out a bare-bones results file displaying some of the information I found useful. Of note is the number of times each page is hit.
This is where things get funny. Outperforming my home page by nearly double was indexOld.php (now indexOld22.php) – a simple webpage I tossed of for about a year before I put my big blog back online! Why were people still going to this page? Further investigation (from the referring sites section of my stats page) revealed a lot of hits from Google image-searches. I started looking at the actual requests and realized that many of these hits were people searching for the term Dwarf Gouramis “a type of freshwater aquarium fish) which was mentioned on that old webpage. The ironic part about it is what happens when you google image search for dwarf gouramis there is a picture of an extremely rare zebra pleco which is actually a link to my website! However the link APPEARS to be to wallpaperfishtalk.com because on my page I just linked to their image.
My conclusion: People are Google image-searching for ‘dwarf gouramis’, and an amazing picture of a zebra pleco is coming up which links to my site (due to the fact that months ago I talked about dwarf gouramis but posted a photo of a zebra pleco) and people (in their awe at this amazing fish) are clicking on it. So what did I do? I pulled a bait-and-switch! You bet I did. Now when you go to indexOld2.php it just forwards you to my current website – mua ha ha ha ha
PS: I’m appending to this entry at 2:17pm to note that I made a wonderful breakthrough in the lab today. Due to intellectual property protection blah blah and the fact that I don’t want anyone else to beat me to my research goal I will not describe what this is, I’ll just say that it took months of preparation and today – presto! It worked beautifully =oD
I know I wrote less than an hour ago but I found this and had to put it on my site because it is so freaking awesome. How do I randomly find the coolest crap on the internet? I was actually google image-searching for wrist-clamp ECG electrodes and I found this guy.
Only a couple weeks into my self-induced crunch time (caused by the fact my thesis work is supposed to be completed in less than a month) I’m beginning to feel a little overwhelmed. I still don’t feel it’s impossible, but I do feel like I’m making things better than they actually are in my mind. In what free time I do have, I work on side projects (like my homemade ECG machine). Last night I recorded myself sleeping. In the middle of the night I woke up and the electrodes were incredibly uncomfortable, so I ripped them off my chest and arms (ouch!) and threw them on the floor and went back to sleep. I figured that I had at least several hours’ worth of data, so I’d be fine. The next morning when I woke up I examined the recordings, and apparently I had fallen asleep in under a minute, slept for 29 minutes, then woke up and (assuming it was several hours later) ripped off the electrodes. That’s it! A mere 29 minutes of recording. One day I’ll get my whole sleep cycle recorded. Just you wait.
Here’s a little visual goodie from that recording. This data was processed in a new way – no Fourier transformation! I integrated the original trace, smoothed it 3 times, identified peaks, and used the inverse peak-to-peak distance to determine heart rate. The trace itself is not beautiful (see photo and compare it with previous traces) but if I’ll I’m determining is heartrate, it works! If there’s a weird problem (missed beats, double beats, etc) it will show up as a spike on the heartrate trace and then I can use better tools to identify the trouble in the region affected.
Okay, it’s about time I go do something. I’m in the laboratory right now (go fig) but surprisingly I can’t do a whole lot. I just got some new chemicals in today and am performing a trial run which will be completed tomorrow. Until then, I just have to wait for 18 hours for a reaction to complete. Maybe I’ll browse DA or try to draw something groovy in inkscape. See ya!
It’s 2:15 am and I can’t sleep, so what do I do? I hope on my laptop and try out new linux software! First of all, I wanted to note that I’ve been afflicted by the mysterious case of the disappearing xmms. Xmms is a linux-based audio player nearly identical to >”Winamp. Traditionally (several months ago) I could just type sudo apt-get install xmms; xmms and xmms would install and run on my Ubuntu system with a user-friendly frontend. Now it seems that xmms is gone – replaced by xmms2 which appears to be an audio server with no front end. Wha? I didn’t put much effort into trying to figure out how to get a front end to xmms2, but brief Googling suggested that xmms2 doesn’t actually have a front-end. Today I realised that you have to install a completely separate package to get a front-end, based on xmms2. It basically acts just like xmms did (pictured). The program is called audacious, and can be installed with sudo apt-get install audacious on an Ubuntu system. Yay! Back to normal. response to inquiry: the songs in the playlist is a short collection of songs by Splashdown (an extinct late-90s indie band I fell in love with when I was ~13) – believe it or not, all of their music can be downloaded from this website – super awesome huh? I especially recommend halfworld, ironspy, running with scissors, and waterbead (in that order). A few songs in the list are actually from Universal Hall Pass which is a different band with the same lead singer (Melissa Kaplan).
I have to admit that tonight I was blown away by the awesomeness of Inkscape – a vector drawing tool (similar to Adobe Illustrator) which is going to become my next best friend. Like I said, it does pretty much what Illustrator does, but for free! (Illustrator is about $250) It does so much cool stuff and I just realised it would be so freaking awesome to design a website layout using it. I have to admit, for the first 20 minutes I was totally lost – I had a hard time figuring out how to draw a line! But after a little more time, it became really natural, and this program is – jeez – I can’t get over how useful this can be. I decided to get used to this program by drawing-out a schematic of the circuit from my DIY ECG Project (The result is posted at the bottom of this entry, and a screenshot of the creation process is pictured to the side). I know that I will have tremendous use for this software, possibly in the creation of diagrams of the autonomic nervous system for my master’s thesis or perhaps even for a publication… [ponders] Okay I have to get to bed!
Take it from me. Download InkScape (it looks like they have a Windows version, btw) and if you have Ubuntu, install audacious for your music. Scott out.
My web server blocks access to my apache-generated visitor logs (commonly stored in “access.log”). Therefore, many great site usage stats generators (such as awstats – see this example) cannot be used to analyze web traffic to my site. (How many people go what pages? Where do they come from? What search phrases do they type into Google to find my website?) My web host does allow PHP, and access to php.ini, so I figured that I could generate my own access.log using PHP code. I succeeded, but had a hard time doing this because it’s not clearly documented elsewhere – so I’ll make it clear.
Sample line from access.log generated by my PHP script:
132.170.10.227 – - [22/Jan/2009:11:58:49 +0800] “GET /blog/2005-06-29-eva-05-attack-scotts-sanity/ HTTP/1.1″ 200 – “http://www.google.com/search?hl=en&client=firefox-a&rls=org.mozilla%3Aen-US%3Aofficial&hs=8Lk&q=swharden+eva-05&btnG=Search” “Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5″
All I had to do was insert the following line at the end of my php.ini file:
And I placed logme.php in my root folder with the following code:
$logwriter_logformat = "combined"; // log format,combined or common
$logwriter_logdir = "/home/content/n/i/b/nibjb/html/logs/"; // physical path where your log file located
$logwriter_logfilename = "access.log"; // your log file's filename
$logwriter_timezone = "+0800"; // your server's time zone. +0800 means GMT+8
function logwriter_writelog($logstring){
global $logwriter_logdir,$logwriter_logfilename;
$fullpathfilename = $logwriter_logdir.$logwriter_logfilename;
if (!is_file($fullpathfilename)) {
print "Log file doesn't exist or file is corrupt.";
return;
}
if (!is_writeable($fullpathfilename)) {
print "Log file is not writable,please change its permission.";
return;
}
if($fp = @fopen($fullpathfilename, "a")) {
flock($fp, 2);
fputs($fp, $logstring);
fclose($fp);
}
}
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_server_port = logwriter_handlevar("SERVER_PORT","80");
if($logwriter_server_port!="80") {
$logwriter_server_port =
}else{
$logwriter_server_port = "";
}
$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");
if ($logwriter_logformat=="common") {
$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 -
";
}else{
$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"
";
}
logwriter_writelog($logwriter_logstring);
Note that the PHP code must be surrounded with < ? php ?> as demonstrated here
The result? As you can tell, my logme.php dumps data to www.swharden.com/logs/access.log – if you browse a few pages on my website, or even use Google to search for me (ie: google for ’swharden’ and ‘minidisc’) you can see yourself in the logfile – pretty cool huh? Once I have a good volume of log data I’ll demonstrate how to turn it into useful information.
It’s time for a lecture. I’ve been spending a lot of time creating a DIY dlectrocardiogram and it produces fairly noisy signals. I’ve spent some time and effort researching the best ways to clean-up these signals, and the results are incredibly useful! Therefore, I’ve decided to lightly document these results in a blog entry.
Here’s an example of my magic! I take a noisy recording and turn it into a beautiful trace. See the example figure with the blue traces. How is this possible? Well I’ll explain it for you. Mostly, it boils down to eliminating excess high-frequency sine waves which are in the original recording due to electromagnetic noise. A major source of noise can be from the alternating current passing through wires traveling through the walls of your house or building. My original ECG circuit was highly susceptible to this kind of interference, but my improved ECG circuit eliminates most of this noise. However, noise is still in the trace (see the figure to the left), and it needed to be removed.
The key is the FFT (Fast Fourier Transformation) algorithm which can get pretty intimidating to research at first! I’ll simplify this process. Let’s say you have a trace with repeating sine-wave-shaped noise. The output of the FFT transformation of the signal is the breakdown of the signal by frequency. Check out this FFT trace of a noisy signal from a few posts ago (top graph). High peaks represent frequencies which are common. See the enormous peak around 60 Hz? (Hz means “per second” by the way, so 60 Hz is a sine wave that repeats 60 times a second) That’s from my AC noise. Other peaks (shown in colored bands) are other electromagnetic noise sources, such as wireless networks, TVs, telephones, and your computer processor. The heart produces changes in electricity that are very slow (the heartbeat is about 1 Hz, or 1 beat per second), so if we can eliminate all of the sine waves with frequencies higher than what we want to isolate we can get a pretty clear trace. This is called a band-stop filter (we block-out certain bands of frequencies) A band-pass filter is the opposite, where we only allow frequencies which are below (low-pass) or above (high-pass) a given frequency. By eliminating each of the peaks in the colored regions (setting each value to 0), then performing an inverse fast Fourier transformation (going backwards from frequency back to time), the result is the signal trace (seen as light gray on the bottom graph) with those high-frequency sine waves removed! (the gray trace on the bottom graph). A little touch-up smoothing makes a great trace (black trace on the bottom graph).
Here’s some Python code to get you started in cleaning-up your noisy signals! The image below is the output of the Python code at the bottom of this entry. This python file requires that test.wav (~700kb) (an actual ECG recording of my heartbeat) be saved in the same folder. Brief descriptions of each portion of the graph will follow.
(A) The original signal we want to isolate. (IE: our actual heart signal)
(B) Some electrical noise. (3 sine waves of different amplitudes and periods)
(C) Electrical noise (what happens when you add those 3 sine waves together)
(D) Static (random noise generated by a random number generator)
(E) Signal (A) plus static (D)
(F) Signal (A) plus static (D) plus electrical noise (C)
(G) Total FFT trace of (F). Note the low frequency peak due to the signal and electrical noise (near 0) and the high frequency peak due to static (near 10,000)
(H) This is a zoomed-in region of (F) showing 4 peaks (one for the original signal and 3 for high frequency noise). By blocking-out (set it to 0) everything above 10Hz (red), we isolate the peak we want (signal). This is a low-pass filter.
(I) Performing an inverse FFT (iFFT) on the low-pass iFFT, we get a nice trace which is our original signal!
(J) Comparison of our iFFT with our original signal shows that the amplitude is kinda messed up. If we normalize each of these (set minimum to 0, maximum to 1) they line up. Awesome!
(K) How close were we? Graphing the difference of iFFT and the original signal shows that usually we’re not far off. The ends are a problem though, but if our data analysis trims off these ends then our center looks great.
Here’s the code I used to make the image:
import numpy, scipy, pylab, random
# This script demonstrates how to use band-pass (low-pass)
# filtering to eliminate electrical noise and static
# from signal data!
##################
### PROCESSING ###
##################
xs=numpy.arange(1,100,.01) #generate Xs (0.00,0.01,0.02,0.03,...,100.0)
signal = sin1=numpy.sin(xs*.3) #(A)
sin1=numpy.sin(xs) # (B) sin1
sin2=numpy.sin(xs*2.33)*.333 # (B) sin2
sin3=numpy.sin(xs*2.77)*.777 # (B) sin3
noise=sin1+sin2+sin3 # (C)
static = (numpy.random.random_sample((len(xs)))-.5)*.2 # (D)
sigstat=static+signal # (E)
rawsignal=sigstat+noise # (F)
fft=scipy.fft(rawsignal) # (G) and (H)
bp=fft[:]
for i in range(len(bp)): # (H-red)
if i>=10:bp[i]=0
ibp=scipy.ifft(bp) # (I), (J), (K) and (L)
################
### GRAPHING ###
################
h,w=6,2
pylab.figure(figsize=(12,9))
pylab.subplots_adjust(hspace=.7)
pylab.subplot(h,w,1);pylab.title("(A) Original Signal")
pylab.plot(xs,signal)
pylab.subplot(h,w,3);pylab.title("(B) Electrical Noise Sources (3 Sine Waves)")
pylab.plot(xs,sin1,label="sin1")
pylab.plot(xs,sin2,label="sin2")
pylab.plot(xs,sin3,label="sin3")
pylab.legend()
pylab.subplot(h,w,5);pylab.title("(C) Electrical Noise (3 sine waves added together)")
pylab.plot(xs,noise)
pylab.subplot(h,w,7);pylab.title("(D) Static (random noise)")
pylab.plot(xs,static)
pylab.axis([None,None,-1,1])
pylab.subplot(h,w,9);pylab.title("(E) Signal + Static")
pylab.plot(xs,sigstat)
pylab.subplot(h,w,11);pylab.title("(F) Recording (Signal + Static + Electrical Noise)")
pylab.plot(xs,rawsignal)
pylab.subplot(h,w,2);pylab.title("(G) FFT of Recording")
fft=scipy.fft(rawsignal)
pylab.plot(abs(fft))
pylab.text(200,3000,"signals",verticalalignment='top')
pylab.text(9500,3000,"static",verticalalignment='top',
horizontalalignment='right')
pylab.subplot(h,w,4);pylab.title("(H) Low-Pass FFT")
pylab.plot(abs(fft))
pylab.text(17,3000,"sin1",verticalalignment='top',horizontalalignment='left')
pylab.text(37,2000,"sin2",verticalalignment='top',horizontalalignment='center')
pylab.text(45,3000,"sin3",verticalalignment='top',horizontalalignment='left')
pylab.text(6,3000,"signal",verticalalignment='top',horizontalalignment='left')
pylab.axvspan(10,10000,fc='r',alpha='.5')
pylab.axis([0,60,None,None])
pylab.subplot(h,w,6);pylab.title("(I) Inverse FFT")
pylab.plot(ibp)
pylab.subplot(h,w,8);pylab.title("(J) Signal vs. iFFT")
pylab.plot(signal,'k',label="signal",alpha=.5)
pylab.plot(ibp,'b',label="ifft",alpha=.5)
pylab.subplot(h,w,10);pylab.title("(K) Normalized Signal vs. iFFT")
pylab.plot(signal/max(signal),'k',label="signal",alpha=.5)
pylab.plot(ibp/max(ibp),'b',label="ifft",alpha=.5)
pylab.subplot(h,w,12);pylab.title("(L) Difference / Error")
pylab.plot(signal/max(signal)-ibp/max(ibp),'k')
pylab.savefig("SIG.png",dpi=200)
pylab.show()
Am I going to dlie? It’s unlikely. Upon analyzing ~20 minutes of heartbeat data (some of which is depicted in the previous entry) I found a peculiarity. Technically this could be some kind of noise (a ‘pop’ in the microphone signal due to the shuffling of wires or a momentary disconnect from the electrodes or perhaps even a static shock to my body from something), but because this peculiarity happened only once in 20 minutes I’m not ruling out the possibility that this is the first irregular heartbeat I captured with my DIY ECG. Note that single-beat irregularities are common, and that this does not alarm me so much as fascinates me. Below is the section of the data which contains this irregular beat.
In the spirit of improvement I wonder how much more interesting this project would be if I were to combine the already-designed ECG machine with a sensor to detect the physical effect of the heart’s beating on my vasculature. in other words, can I combine my electrical traces with physical traces? (Blood pressure or blood flow) I found an interesting site that shows how someone built a DIY blood flow meter using a piezo film pulse sensor. Pretty clever I must say… but I think I draw my limit at what I’ve done. Although blood flow would be interesting to analyze (does the murmur depicted above produce an alteration in normal blood flow?), it’s not worth the time, hassle or expense of building.
No 3-day weekend would be complete without a project that’s, well, virtually useless. I present to you my new and improved ECG machine! Instead of using a single op-amp circuit like the previous entries which gave me decent but staticky traces, I decided to build a more advanced ECG circuit documented by Jason Nguyen which boasted 6 op amps! (I’d only been using one) Luckily I picked up a couple LM 324 quad op amp chips at radioshack for about $1.40 each, so I had everything I needed. I’ll skip to the results. In short, they’re gorgeous. Noise is almost nothing, so true details of the trace are visible. I can now clearly see the P-Q-R-S-T features in the wave (before the P was invisible). I’ll detail how I did this in a later entry. For now, here are some photos of the little device and a video I uploaded to YouTube. It’s not fancy.
UPDATE: Upon analyzing ~20 minutes of heartbeat data I found a peculiarity. Technically this could be some kind of noise (a ‘pop’ in the microphone signal due to the shuffling of wires or a momentary disconnect from the electrodes or perhaps even a static shock to my body from something), but because this peculiarity happened only once in 20 minutes I’m not ruling out the possibility that this is the first irregular heartbeat I captured with my DIY ECG. Note that single-beat irregularities are common, and that this does not alarm me so much as fascinates me. Below is the section of the data which contains this irregular beat.
While searching for a more efficient bandpass filter for python (to apply to multi-hour WAV files because the WAV->FFT->bandstop->iFFT->smooth process is way too slow) I came across The Stoic Monkey’s VROOM! which apparently is a vector-based racing game game (written in Python with PyGame libraries) . It doesn’t flaunt the capabilities of PyGame for game design, but it was a cool concept because it’s very 3D-ish and is done by mathematically processing anti-aliasing splines at impressively high speed. Check it out! “[download for windows]“http://www.stoicmonkey.com/vroom/VROOM_setup.exe
Last night I finished building my DIY ECG as a prototype (I finally got the circuit off the breadboard and onto a plastic sheet). This is a similar circuit to the one used to record data from the last entry (resister values are now identical to the crude circuit described several posts ago). I left-in the crude band-pass filter (made by grounding my primary electrode sensor through a 0.1µF capacitor) because it seemed to help a great deal, and wasn’t hard to implement. I picked up all of my parts (including the LF324 quad-op-amp microchip) at RadioShack. Of note, the quad-op-amp is overkill because I’m only using one of the 4 op-amps. Theoretically I could add 3 more electrodes to this circuit (which would allow for multi-sensor recording, similar to the electrodes placed at http://en.wikipedia.org/wiki/File:Limb_leads.svg) but this would require multiple microphone jacks, which isn’t very common. I guess I could use 2 microphone jacks, and differentiate right/left channels. Anyway, here are some photos.
I made the prototype by drilling holes in a small rectangular piece of a non-conductive plastic-fiberish material. (I picked up a stack of these rectangular sections for a quarter at a local electrical surplus store and they’re perfect for prototyping). The two green wires coming out the left side attach to a ~5v power supply (either a plugged in AC->DC transformer, 3 or 4 AA batteries, or even a 9V should work). The blue wires on the right attach to the electrodes I put on my chest. The black wires go to a headphone-jack which I plug into the microphone hole of my PC to record the signal.
This is the back of the device which shows my crummy soldering technique. Let it slide folks, I’m a molecular biology not an electrical engineer. Anyhow, basic point-to-point contacts, nothing special. The white/yellow wires correspond to the left/right channels of the microphone connector. I only use the left one (white), but attached the right channel (yellow) to the op-amp just in case I decide to add another sensor later – this is not required.
Here’s the full shabang! You can clearly see the circuit (note its small size – easy to mount inside of a tictac box or something) with the green wires leading to a power supply, black cable being the microphone connector, and the blue wires leading to electrodes made… from… fanta… cans…? Yes, in the spirit of ghetto-rigging electronics (my specialty) I found that surprisingly nice chest electrodes can be made from aluminum soda cans! If you go this route, cut them delicately so you don’t get metal shards in your skin like I did at first. Also, note that you have to firmly scrape each side of the aluminum to get the insulating waxy-plastic stuff off or it just won’t work. (I guess it’s coated with something to prevent the soda from tasting like metal.) Aluminum rapidly transfers heat, so it’s nearly impossible to solder leads onto these pads, so I wrapped a wire (tipped with a bead of solder) with the edge of the aluminum several times and crushed the heck out of it with pliers and it seems to stay on well and make a good connection. Also, before taping these onto your skin, you have to put a highly-conductive goo on it to make the connection better. I chose to use a copious squirt of my wife’s skin moisturizer on each electrode (shh, don’t tell her!) and taped the gooey electrode directly onto my chest.
I recorded ~20 minutes of data last night with this rig and it looked pretty good. I went to analyze it with Python and it kept crashing! The python script I gave you previously loads the entire wave file into an array of numbers, but with a 20 minute wave file (at over 40,000 samples a second) this is just too big for memory. I wrote an updated wave loader function which loads large wave files in parts which is much more efficient. It also performs the condensation method at load time. Basically, it loads 100 data points (or whatever deg is set to), averages them, and adds this value to a list. The result is a smoothed trace with a resolution of ~400Hz instead of ~40,000Hz. I’d apply it to my wave file I recorded last night but it’s on my laptop which is in the car and I’ve got to get back to work. Here’s that function:
def loadWav(fname,deg=100):
global hz
w=wave.open(fname)
nchannel, width, rate, length, comptype, compname = w.getparams()
print "[%s]
rate: %d Hz
frames: %d
length: %.02f sec" %
(fname, rate, length, float(length)/rate)
hz=rate/deg
chunks=int(length/deg)
data=[]
for i in range(chunks):
if i%7777==0:
print "processing chunk %d of %d (%0.02f%%)" %
(i,chunks,100.0*i/chunks)
vals = struct.unpack("%sh" %deg,w.readframes(deg))
data.append(sum(vals)/float(deg))
print "complete!"
return data
Would I rather design circuits or software? I’m a software guy (likely due to my lack of working knowledge of circuits) so I’d rather record noisy signals and write software to eliminate the noise, rather than assembling circuits to eliminate the noise for me. In the case of my DIY ECG machine, I’d say I’ve done a great job of eliminating noise via the software route. Most DIY ECG circuits on the net use multiple op-amps and diodes to do this, and have a hardware-based band-pass filter to eliminate frequencies around 60 Hz. Instead of all that fancy stuff, I made a super-crude circuit (a single op-amp and two resisters) to record my ECG. It was INCREDIBLY noisy! So, how did I clean it up with software? I’ll tell you.
The first step in removing electrical noise is classifying it. Most of the noise in my signal were overlapping sine waves caused by my electrodes picking up signals not from my body. This was determined by simply close-up observation of the original trace. Since this sine-type interference is consistant, power-spectral analysis could be applied to determine the frequencies of the noise so I could block them out. I used the fast Fourier transform algorithm (FFT) on the values from my wave file to generate a plot of noise level with respect to frequency (noise was seen as sharp peaks). I manually blocked-out certain regions of the FFT trace that I thought were noise-related (colored bands, all the values of which were set to zero) – a process known as band-pass filtering (something which is possible to do electronically with a more complicated circuit) – then performed an inverse FFT algorithm on the trace. The result was a trace with greatly reduced noise and after a moving window smoothing algorithm was applied the signal was better than ever. Below are some figures. Note that I recorded the raw WAV file with “sound recorder” (not GoldWave) and did all of the processing (including band-pass filtering) within Python.
What an awesome chart! On top we have the power spectral analysis with band-stop filters applied at the colored regions. Below is the trace of the original WAV file (light gray), the inverse-FFT-filtered trace (dark gray), and the smoothed inverse-FFT-filtered trace (black) – the final ECG signal I intend to use.
This is a magnified view of a few heartbeats after the inverse FFT filtering (multi-band-blocking) process was applied. Not bad! Not bad at all…
And, of course, the updated code:
import wave, struct, numpy, pylab, scipy
fname='./success3.wav'
def readwave(wavfilename):
"""load raw data directly from a WAV file."""
global rate
w=wave.open(wavfilename,'rb')
(nchannel, width, rate, length, comptype, compname) = w.getparams()
print "[%s] %d HZ (%0.2fsec)" %(wavfilename, rate, length/float(rate))
frames = w.readframes(length)
return numpy.array(struct.unpack("%sh" %length*nchannel,frames))
def shrink(data,deg=100):
"""condense a linear data array by a multiple of [deg]."""
global rate
small=[]
print "starting with", len(data)
for i in range(len(data)/deg):
small.append(numpy.average(data[i*deg:(i+1)*deg]))
print "ending with", len(small)
rate = rate/deg
#return small[40000:50000]
return small
def normalize(data):
"""make all data fit between -.5 and +.5"""
data=data-numpy.average(data)
big=float(max(data))
sml=float(min(data))
data=data/abs(big-sml)
data=data+float(abs(min(data)))-.47
return data
def smooth(data,deg=20,expand=False):
"""moving window average (deg = window size)."""
for i in range(len(data)-deg):
if i==0: cur,smooth=sum(data[0:deg]),[]
smooth.append(cur/deg)
cur=cur-data[i]+data[i+deg]
if expand:
for i in range(deg):
smooth.append(smooth[-1])
return smooth
def smoothListGaussian(list,degree=10,expand=False):
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)
if expand:
for i in range((degree*2)-1):
smoothed.append(smoothed[-1])
return smoothed
def goodSmooth(data):
#data=smooth(fix,20,True)
data=smooth(fix,100,True)
#data=smooth(fix,20,True)
return data
def makeabs(data):
"""center linear data to its average value."""
for i in range(len(data)): data[i]=abs(data[i])
return data
def invert(data):
"""obviously."""
for i in range(len(data)): data[i]=-data[i]
return data
def loadwav(fname):
"""a do-everything function to get usable, smoothed data from a WAV."""
wav=readwave(fname)
wav=shrink(wav)
wav=invert(wav)
wav=smooth(wav)
wav=smooth(wav,10)
wav=normalize(wav)
Xs=getXs(wav)
return Xs,wav
def getXs(datalen):
"""calculate time positions based on WAV frequency resolution."""
Xs=[]
for i in range(len(datalen)):
Xs.append(i*(1/float(rate)))
print len(datalen), len(Xs)
return Xs
def integrate(data):
"""integrate the function with respect to its order."""
inte=[]
for i in range(len(data)-1):
inte.append(abs(data[i]-data[i+1]))
inte.append(inte[-1])
return inte
def getPoints(Xs,data,res=10):
"""return X,Y coordinates of R peaks and calculate R-R based heartrate."""
pXs,pYs,pHRs=[],[],[]
for i in range(res,len(data)-res):
if data[i]>data[i-res]+.1 and data[i]>data[i+res]+.1:
if data[i]>data[i-1] and data[i]>data[i+1]:
pXs.append(Xs[i])
pYs.append(data[i])
if len(pXs)>1:
pHRs.append((1.0/(pXs[-1]-pXs[-2]))*60.0)
pHRs.append(pHRs[-1])
return pXs,pYs,pHRs
def bandStop(fft,fftx,low,high,show=True):
lbl="%d-%d"%(low,high)
print "band-stopping:",lbl
if show:
col=pylab.cm.spectral(low/1200.)
pylab.axvspan(low,high,alpha=.4,ec='none',label=lbl,fc=col)
#pylab.axvspan(-low,-high,fc='r',alpha=.3)
for i in range(len(fft)):
if abs(fftx[i])>low and abs(fftx[i])<high :
fft[i]=0
return fft
def getXs(data):
xs=numpy.array(range(len(data)))
xs=xs*(1.0/rate)
return xs
def clip(x,deg=1000):
return numpy.array(x[deg:-deg])
pylab.figure(figsize=(12,8))
raw = invert(shrink(readwave(fname),10))
xs = getXs(raw)
fftr = numpy.fft.fft(raw)
fft = fftr[:]
fftx= numpy.fft.fftfreq(len(raw), d=(1.0/(rate)))
pylab.subplot(2,1,1)
pylab.plot(fftx,abs(fftr),'k')
fft=bandStop(fft,fftx,30,123)
fft=bandStop(fft,fftx,160,184)
fft=bandStop(fft,fftx,294,303)
fft=bandStop(fft,fftx,386,423)
fft=bandStop(fft,fftx,534,539)
fft=bandStop(fft,fftx,585,610)
fft=bandStop(fft,fftx,654,660)
fft=bandStop(fft,fftx,773,778)
fft=bandStop(fft,fftx,893,900)
fft=bandStop(fft,fftx,1100,max(fftx))
pylab.axis([0,1200,0,2*10**6])
pylab.legend()
pylab.title("Power Spectral Analysis",fontsize=28)
pylab.ylabel("Power",fontsize=20)
pylab.xlabel("Frequency (Hz)",fontsize=20)
pylab.subplot(2,1,2)
pylab.title("Original Trace",fontsize=28)
pylab.ylabel("Potential",fontsize=20)
pylab.xlabel("Time (sec)",fontsize=20)
pylab.plot(clip(xs),clip(raw),color='.8',label='1: raw')
fix = scipy.ifft(fft)
pylab.plot(clip(xs),clip(fix)+5000,color='.6',label='2: band-stop')
pylab.plot(clip(xs),clip(goodSmooth(fix))-5000,'k',label='3: smoothed')
pylab.legend()
pylab.title("Band-Stop Filtered Trace",fontsize=28)
pylab.ylabel("Potential",fontsize=20)
pylab.xlabel("Time (sec)",fontsize=20)
pylab.subplots_adjust(hspace=.5)
pylab.savefig('out.png',dpi=100)
pylab.show()
print "COMPLETE"
I’ve succeeded in building my own electrocardiograph (ECG) to record the electrical activity of my own heart! Briefly, I built a micropotential amplifier using an op-amp and attached it to makeshift electrodes on my chest (pennies and shampoo lol), fed the amplified signal into my sound card, and recorded it as a WAV. The signal is INCREDIBLY noisy though. I was able to do a great job at removing this noise using band/frequency filters in GoldWave (audio editing software designed to handle WAV files). I band-blocked 50-70 Hz (which removed the oscillations from the 60 Hz AC lines running around my apartment). I then wrote the Python code (at the bottom of this entry) to load this WAV file as a single list of numbers (voltage potentials). I performed a data condensation algorithm (converting 100 points of raw WAV data into a single, averaged point, lessening my processing load by 100x), followed by two consecutative moving window averages (20-point window, performed on the condensed data). The result was a voltage reading that had most of the random interference oscillations removed and, behold, a BEAUTIFUL ECG signal!!! I also tossed in some code to determine the peak of the R wave, label it (yellow dot), and use the inverse R-R time distance to calculate heart rate. Nifty, huh?
I swear, this is legitimate data that I recorded myself tonight! This is my actual ECC signal as record by a circuit similar to the one in the previous entry, recorded through my sound card, and processed with the Python script below. Incredible, huh? You can clearly see the Q, R, S, and T spikes (described in the previous couple entries)! I can’t wait to solder-up a prototype (it’s currently breadboarded) and try to analyze hours of data rather than just a few seconds! I’ll take pictures of this device soon. Until then, here’s some real data!
This is a zoomed-in view of a representative QRST wave of mine. Amazing, huh? I’m proud of my ticker! =o)
This is the output of the python script I wrote tonight
And here’s the code I used: note that it relies on the WAV file I recorded – if you want it just ask me for it and I’ll email it to you – it’s about 12 sec long. Oh yeah, not all of the functions in this script are used to create the image (such as integration calculations), but I left ‘em in because you may find them useful.
import wave, struct, numpy, pylab, scipy
def readwave(wavfilename):
"""load raw data directly from a WAV file."""
global rate
w=wave.open(wavfilename,'rb')
(nchannel, width, rate, length, comptype, compname) = w.getparams()
print "[%s] %d HZ (%0.2fsec)" %(wavfilename, rate, length/float(rate))
frames = w.readframes(length)
return numpy.array(struct.unpack("%sh" %length*nchannel,frames))
def shrink(data,deg=100):
"""condense a linear data array by a multiple of [deg]."""
global rate
small=[]
print "starting with", len(data)
for i in range(len(data)/deg):
small.append(numpy.average(data[i*deg:(i+1)*deg]))
print "ending with", len(small)
rate = rate/deg
return small
def normalize(data):
"""make all data fit between -.5 and +.5"""
data=data-numpy.average(data)
big=float(max(data))
sml=float(min(data))
data=data/abs(big-sml)
data=data+float(abs(min(data)))-.47
return data
def smooth(data,deg=20):
"""moving window average (deg = window size)."""
for i in range(len(data)-deg):
if i==0: cur,smooth=sum(data[0:deg]),[]
smooth.append(cur/deg)
cur=cur-data[i]+data[i+deg]
return smooth
def makeabs(data):
"""center linear data to its average value."""
for i in range(len(data)): data[i]=abs(data[i])
return data
def invert(data):
"""obviously."""
for i in range(len(data)): data[i]=-data[i]
return data
def loadwav(fname='./wavs/bandpassed.wav'):
"""a do-everything function to get usable, smoothed data from a WAV."""
wav=readwave(fname)
wav=shrink(wav)
wav=invert(wav)
wav=smooth(wav)
wav=smooth(wav,10)
wav=normalize(wav)
Xs=getXs(wav)
return Xs,wav
def getXs(datalen):
"""calculate time positions based on WAV frequency resolution."""
Xs=[]
for i in range(len(datalen)):
Xs.append(i*(1/float(rate)))
print len(datalen), len(Xs)
return Xs
def integrate(data):
"""integrate the function with respect to its order."""
inte=[]
for i in range(len(data)-1):
inte.append(abs(data[i]-data[i+1]))
inte.append(inte[-1])
return inte
def getPoints(Xs,data,res=10):
"""return X,Y coordinates of R peaks and calculate R-R based heartrate."""
pXs,pYs,pHRs=[],[],[]
for i in range(res,len(data)-res):
if data[i]>data[i-res]+.1 and data[i]>data[i+res]+.1:
if data[i]>data[i-1] and data[i]>data[i+1]:
pXs.append(Xs[i])
pYs.append(data[i])
if len(pXs)>1:
pHRs.append((1.0/(pXs[-1]-pXs[-2]))*60.0)
pHRs.append(pHRs[-1])
return pXs,pYs,pHRs
Xs,Ys=loadwav()
px,py,pHR=getPoints(Xs,Ys)
pylab.figure(figsize=(12,6))
pylab.subplot(2,1,1)
#pylab.axhline(color='.4',linestyle=':')
pylab.plot(Xs,Ys,'b-')
pylab.plot(px,py,'y.')
pylab.axis([None,None,-.6,.6])
pylab.title("DIY Electrocardiogram - Trial 1",fontsize=26)
pylab.ylabel("Normalized Potential",fontsize=16)
#pylab.xlabel("Time (sec)")
ax=pylab.axis()
pylab.subplot(2,1,2)
pylab.plot(px,pHR,'k:')
pylab.plot(px,pHR,'b.')
pylab.axis([ax[0],ax[1],None,None])
pylab.ylabel("Heart Rate (BPM)",fontsize=16)
pylab.xlabel("Time (seconds)",fontsize=16)
pylab.savefig("test.png",dpi=120)
pylab.show()
print "DONE"
I kept working on my homemade ECG machine (I had to change the values of some of the resisters) and it looks like I’m getting some valid signals! By recording the potential using my sound card (microphone hole = a nice analog to digital converter that every PC has) I was able record my ECG with sound recording software, smooth it, and this is what it looks like. Pretty cool huh?
This was based on a circuit I made using a single op-amp (A LM324 from RadioShack $1.49). Basically the op-amp just amplifies micropotential generated by my heart and outputs it in such a way that I can connect it to a standard headphone jack to plug into my microphone hole. The signal is very noisy though. I’m thinking about making the intricate circuit (with 6 op-amps) to produce a better signal-to-noise ratio, but first I’ll try coding my way out of the noise.
So I followed-through on yesterday’s post and actually tried to build an ECG machine. I had a very small amount of time to work on it, so instead of building the fancy circuit (with 6 band-pass filtered op-amps and diodes posted in the previous entry) I built the most crude circuit that would theoretically work. I used one of the 4 available op-amps from a LM324 chip (pictured to the left) . I was working late at night, and I’m quite colorblind, so I had to take a gamble (as usual) with the resistors I used. Resistors are color coded with bands that represent their resistance. But, since I’m highly colorblind, red, orange, and black all look the same to me. So I can’t be sure I go the resistances right. I’d check it with my digital multimeter but I seem to have lost it and my wife doesn’t remember seeing it recently. Blast! Anyway, I built the sucker, hooked it up to my sound card, and made electrodes by soldering wires to pennies. After a good lick, I attached the pennies to my chest with tape and tried recording. Every time the pennies made contact with my skin, I would see noise on the trace, but I couldn’t seem to isolate a strong heartbeat signal. This is what I saw and the circuit I build to see it:
When I have more time I’ll locate my multimeter and build the full circuit described below. I’ll update everyone on my progress later. Wish me luck!
Perhaps this project will be working soon. How cool is that? Many techno-savvy people have made these DIY ECG machines, but no where on the net do these people describe how to interpret the data. Since I’m planning on building it, testing it, recording ECG data, and processing/analyzing it, I’ll have something truly unique on the internet. Perhaps it will be worthy of mention on Hack-a-day when it’s complete! How cool would that be?
Last night my wife put her head on my chest while we were watching a movie. A minute or two later I felt a light sinking feeling in my upper chest, and my wife looked up at me in horror. “Your heart stopped beating!” I assured her that everything was okay (it quickly resumed), and that it happens all the time. I feel the sinking feeling often, know it’s because my heart is briefly beating irregularly, and assume it’s normal. After all, your heart isn’t a robot, it’s a living organ doing the best it can. It’s never perfectly regular, and presumably everybody has momentary irregularities, they just don’t notice them. When I got in bed I began wondering how regular irregular heartbeats are. What would the chances be that I have some kind of arrhythmia? I’ve had a checkup not too long ago by a family practice physician who used a stethoscope on my back to listen to my heartbeat, and he didn’t notice anything. Then again, how often do general practice doctors detect subtle arrhythmia? I know that whatever problem I have is likely too small to cause any serious troubles, but at the same time I’m becoming obsessed as to determining exactly what my problem is. How many times a day does my heart skip beats? What about nighttime? If only there were some way to record heartbeat data, then I could analyze it and determine the severity of my problem. But wait, data? That would be hours of heartbeat recordings… that means… YES!!!! HOME MADE DO-IT-YOURSELF HARDWARE! WRITING SOFTWARE TO ANALYZE LARGE AMOUNT OF DATA! SUPER-COOL SCIENCY STUFF THAT I CAN MAKE MYSELF, COMBINING BASIC ELECTRONICS WITH BIOMEDICAL DATA ANALYSIS AND, HARAY, PROGRAMMING WITH PYTHON! Naturally, my thoughts began to overwhelm my reality as soon as Python entered the scene. I wondered how I could use my PC to record my heartbeat, without spending much money on hardware, and only using software I write myself. I pondered this on the way to work this morning, and came up with two possible methods: Method 1: acoustical recordings. This would be the easiest way to record my heartbeat. I could tape a stethoscope to my chest, insert a small microphone in the earpiece, connect the microphone to my PC, and record sound data for several hours. Theoretically it would work, but it would be highly prone to noise from breathing, and I would have to lay perfectly still to avoid noise caused by movements. The data (trace) would have to be smoothed, processed with a band-pass filter (to eliminate interference), and heartbeats could be calculated. However, this would only give me heart beat time information… Method 1: electrical recordings. This would be a little more complicated, but generate much more information. I could record the electrical activity of my heart, and the charts would look like the cool electrocardiograms (ECGs) that you see on TV shows and stuff. I would have to build some circuity to amplify the 1mV heartbeat signal recorded from electrodes taped to my chest, but then again what’s more fun than ghetto-building circuits! (I apologize for any people reading this blog who actually live in the ghetto and enjoy building circuits.) I did a little Googling and found that similar things have been done before with a handful of diodes, resisters, and 6 op-amps. I think I’m going to follow the guide on this page and build the circuit seen below:
Schematic of a crude ECG circuit
Supposedly, the data I can obtain looks something like the image at the bottom of this blog entry. I’d attach 3 electrodes to my body (chest, arm, and leg), hook them up to my little circuit, then connect to circuit to my PCs sound card. I’d record the trace (maybe while I sleep?) and analyze it with Python, Numpy, Scipy, and Matplotlib (gosh I love Python). There are several websites which demonstrate how to build DIY ECG recording devices, but none of these actually ANALYZE the data they obtain! Hopefully I could fill this little niche on the internet. We’ll see what happens. I have my thesis to work on, and a whole bunch of other stuff on my plate right now.
A sample recording from the circuit pictured above
The terms assigned to different parts of the heartbeat
UPDATE: I found an extremely crude ECG circuit which I can make from parts I already have at my house. It has tons of noise, but maybe I can filter it out? Perhaps I’ll try this tonight? [ponders]
My eyes are sore from all of the blood that shot out of them this morning. Yes, you guessed it – I spent another another grueling morning sitting through a biomolecular lecture. This one was about lipid rafts in endothelia. I always manage to shrug off my total total lack of interest in lectures of required classes because, hey, they’re required. It’s just a means to an end, ya’ know? This morning I attended a one-time lecture from a guest speaker (which was totally optional, mind you). Lectures like this happen a couple times a week, and since I couldn’t care less about molecular biology (yes, I’ve come out about that) attending them is excruciating. I only attended this one because it was requested of me (I assumed that no one would be there and I was supposed to show support for the person by attending), but after I got there and sat down I realized there were over 30 people in the room. Over the last few months I forgot what it feels like to do things like this. The entire time I sat there with fire in my eyes covered with a glaze of total boredom and disinterest. On one hand, I’m so mad I’m here. The only reason is because I got rejected from dental school after I got my bachelors in biology (a subject I only chose because it looked good for dental school). I did not want to end up here (I’m now about to get a master’s in molecular biology and microbiology), and the thought of staying to get a PhD (if I’m rejected from dental school again) makes me nauseous. The whole time I sat there, I became saddened and maddened at what happened to my academic career. Things like this degrade my psyche by the minute. It’s like, every day I go to school/work and get mocked. I feel hopelessly lost when I’m presented with things I have zero interest in (and little knowledge of), while being surrounded by people who are passionate about biomolecules, and seem to wake up invigorated by the prospect of learning something new about a protein signaling pathway. I’ve been doing this for a year and a half now, and I don’t know how much longer I can take it. -_-
I’ve decided that I’m kinda into posting YouTube videos on my blog these days. They lighten my life, what can I say. True, it’s doubtful these videos will be playable 20 years from now when I’m reading these super-old entries, but they’re fun to put in there anyway. I had to share this one. It needs no introduction. I guess, if I had to introduce it, I’d say something about watching a 14 year old girl strip down, which would sound awkward.