Andrew Que Sites list Photos
Projects Contact

April 10, 2006

Comment system revamp

   At long last, I have completed the changes to the comment system. This should make is easier for people to add comments, and hopefully, more people will do so.
   The system has a bit of AJAX. I really did this because I didn't like how after each comment posting, the page was repositioned to the top. If you added a comment to the last article on the page, after submitting the page would again be at the top. The AJAX method submits the form and fetches the entire comment table for the article after the post. That eliminated the need to reload the page and eliminated the repositioning.
   Since I was on an AJAX kick, I figured the sign in and sign up should also be reload free. For both, I made a centering "pop-up DIV". I'm not a fan of pop-up windows, even if it's for a good cause. This DIV tag holds a table with the login or sign up form. Initially, I saw this table didn't stand out enough against the regular page. I've been using PNGs with alpha layers to "dim" tables for sometime. So I decided to apply it to the body of the page while the pop-up DIV was active. It had the added benefit of rendering the links of the background page unusable, so it's acts as a pseudo-modal.
   The dim function went quite smoothly for Firefox and Opera. However, Internet Explorer was an other story. Both Firefox and Opera have the document.body.offsetHeight which returns the total heights of the rendered page. In IE, this method returns the heights of just the visible window—it does not include what is beneath the scroll bar. In fact, IE has no function (as far as I could tell) that will return the total length of a rendered page. This is a problem because the dim function works by placing a DIV tag overtop the entire page. It can not be smaller then the rendered page (obviously) and if it is made larger, the scroll bars are extended to reflect the page's new size
    The work around isn't perfect. In IE, you have to add document.documentElement.scrollTop to document.body.offsetHeight. This will get a height for the DIV tag that will cover the visible window starting from the top. However, anything below the visible window is not covered.
An other work around was required in IE. When a comment span is expanded, the focus is set to the comment block. However, this doesn't work in IE. This may be due to a bug in IE 6 dealing with setting focus I read about. Since the focus isn't a show stopped, there is a simple work around. Try it and let it fail. This line will do that::
 try{ document.getElementById( 'Comments' ).focus(); } catch( e ) {}
   In transferring the database from the old comment table to the new comment table, I tried something new: INSERT INTO ... SELECT. A single SQL line allowed me to translate the entries in one table directly into the new table. It looked like this
INSERT INTO CommentsNew SELECT 0 as ID , Page, Author, TimeWritten, '' as IP, DisplayName, Country, Website, Comment FROM Comments, Login WHERE Comments.Author = Login.ID
   What is going on here is the columns from the table 'Comments' and 'Login' are selected in the order in which they will be entered into the new table. And that's all—it just works. Sure beats writing a custom PHP script to for translating tables :)
Dencker and His New Toy

Dencker and His New Toy

   Kept working on the new comment system.  I could have completed it some time ago if I'd just stop adding new fetchers.  I did some testing and so far, everything works in Firefox, Opera 8.5 and IE 6.0.2800.1106.
   Pictured is Dencker, and his shinny new guitar. This guy has as many guitars as I have computers :)


   April of the Watertown staking crowd.  Greetings to April, Heather, Jake, Justin, Michel, Miles, Nick and Shane.

1 comment has been made.

From Heather

Watertown, WI

April 17, 2006 at 12:25 PM

You forgot your cousin Alysia dorkus. Also, it's MIchelle. Not Michel. see ya next saturday...
   I'm currently working on retooling the comment system in an attempt to get more people to leave comments   One day, I might even finished.
   In doing my FFT write up (should be below this article), I had to update the program "cpp2html" again.  I had done this one before to add more keywords.  I commonly use names like "uint" and "uint16", which I wanted marked as keywords.  This time, I was working on getting the code produced XHTML 1.0 compliant.  I ran into something I don't like about CSS.  Seems you can only declare CSS in an external file, or in the <head> section of the HTML.  I wanted to do in-line CSS, declaring classes right before using them.  I did this in older articles, and it worked fine.  However, that isn't alright by XHTML standards. 
   To get around this, rather then use CSS classes, I simply use in-line styles, "style="..."".  That's not quite as versatile as CSS classes, but functional for writing articles that need code blocks.
   Pictured is Connie.

1 comment has been made.

From Zen (


April 10, 2006 at 8:49 AM

I gotta say, between pictures of Connie and Kristy in a hawk, I'm kinda confused what year it is...

April 07, 2006

More on FFT low-pass filtering

   I received a question about my Janurary 26, 2006 article about low-pass filtering using Fast Fourier transforms (FFT).  In the comments, "greg838" wonders how the filter algorithm works.  I can do that:
#include <math.h>
#include <fftw3.h>

double * TimeDomainBuffer;
double * FrequencyDomainBuffer;

fftw_plan FFT_ForwardPlan;
fftw_plan FFT_InversePlan;
int DataSize;

void LowpassFilter( int Data[] , int HighCutoff , int DampenWindow )
   double DampenStep;
   double CosIndex;
   double DampenMultiplier;
   int Index;

   // Copy integer data to prepaired FFT buffer
   for ( Index = 0; Index < DataSize; ++Index )
     TimeDomainBuffer[ Index ] = Data[ Index ];

   // Convert to frequency domain
   fftw_execute( FFT_ForwardPlan );
   // Normilize
   for ( Index = 0; Index < DataSize; Index++ ) 
     FrequencyDomainBuffer[ Index ] /= DataSize;
   // Lowpass filter
   // Zero all frequencies above cutoff, excluding those in the dampen window
   for ( Index = HighCutoff + 1 * DampenWindow ; 
         Index <= ( DataSize / 2 ) - 1 ; 
         Index++ )
     FrequencyDomainBuffer[ Index ]                = 0.0// <- Real
     FrequencyDomainBuffer[ DataSize - 1 - Index ] = 0.0// <- Imaginary
   // Fade off frequencies in dampen window
   DampenStep = M_PI / DampenWindow;
   CosIndex   = DampenStep;
   for ( Index = HighCutoff + 1 * DampenWindow - 1 ; 
         Index > HighCutoff ;
         --Index )
     // Multiplier for index
     DampenMultiplier = 0.5 - 0.5 * cos( CosIndex );
     // Rescale sample
     FrequencyDomainBuffer[ Index ]                *= DampenMultiplier;
     FrequencyDomainBuffer[ DataSize - 1 - Index ] *= DampenMultiplier;
     CosIndex += DampenStep;
   // Convert back to time domain
   fftw_execute( FFT_InversePlan );
   // Convert FFT data (double) to integer data with rounding
   for ( Index = 0; Index < DataSize; ++Index )
     Data[ Index ] = (int)( TimeDomainBuffer[ Index ] + 0.5 );


void InitilizeFFT( int DataSizeParameter )
   DataSizeParameter = DataSize;

   // Allocate transform buffers
   TimeDomainBuffer  = 
     (double*)fftw_malloc( DataSize * sizeof( double ) );

   FrequencyDomainBuffer = 
     (double*)fftw_malloc( DataSize * sizeof( double ) );
   // Time to frequency domain plan
   // (Converts 'TimeDomainBuffer' to 'FrequencyDomainBuffer')
   FFT_ForwardPlan = 
       DataSize , 
       TimeDomainBuffer , 
       FrequencyDomainBuffer , 
       FFTW_R2HC ,
       FFTW_MEASURE );
   // Frequency to time domain plan
   // (Converts 'FrequencyDomainBuffer' to 'TimeDomainBuffer')
   FFT_InversePlan =
       DataSize , 
       FrequencyDomainBuffer , 
       TimeDomainBuffer ,
       FFTW_HC2R ,
       FFTW_MEASURE );

// (C) Copyright 2006 by Andrew Que
// Visit for more information about this code
    This is very close to the code I actually use—I pulled out some application specific items.  To start, 'InitilizeFFT' is called, passed the size of the data (in integer words) to filter.  This must be called before any filtering is attempted, or bad things will happen.  Then, 'LowpassFilter' is called, with the data to be filtered ('Data'), the high cut off point and the size of the dampen window. 
   The frequency of the high cut off depends on what time frame the sample spans.  In the application I wrote the code for, the actually frequency means very little.  Each sample represents one full set of data, and the time it takes to get that data is irrelevant.  However, in other applications, the time frame may be more useful.  For an example,lets say the sample rate is 44 kHz.  If the number of words in 'DataSize' is 44,000, then 'Data' will represent a sample period of one second.  In that case, a 'HighCutoff' of 22,000 would result in passing only frequencies below 22 kHz. 
   The 'DampenWindow' helps cutback on "ringing".  I don't have my equipment setup so that I can provide an example of this, which is too bad, because it's kind of neat (in a geeky way).
   This low-pass filter function takes integer data.  This data has to be copied to floating point storage (doubles in this case) before the transforms can take place.  My application only needed integer data, and deals with the data as integers in further calculations.  Some apps might run faster using all double precision floating point.
   It should be noted the algorithm for filtering is not my own.  The code is based from code written by Jeff Lucius of the U.S. Geological Survey in Denver, Colorado.
   Kristy on her bunk.  This area is part of a loft Kristy and I constructed over her work area.  It sits about 7 feet off the ground.  Originally, Kristy had an air mattress on top.  However, it was about 18 inches thick, which gave her very little room between the bed and ceiling.  She replaced the air mattress with a futon mattress, and can now sit up in her loft area.

April 05, 2006

Photoblog navigation update

   I revamped the photoblog navigation system and finished it today.  The original system simply used page number, with 7 articles per page.  It worked fine.  The problem was, search engines are allowed to index the photoblog.  Each time I added a post, all the articles move down by one slot—some change pages.  This make it a pain for people who arrives looking for an article, since it has likely moved since the search engine indexed it. 
   I had mulled around several ideas for a different navigation system.  The one I ended up choosing is based on weeks of the month, relative to the last day of the month.  For example, June has 30 days and there for, the weeks are as follows: 30-24, 23-17, 16-10, 9-3 and June 2 to May 27.  The makes for some overlap between months, but provides a constant location for each post.
   Implementation took nearly 3 days to complete.  Initially, I tried basing the weeks off the first of the month.  But that didn't work, since the posts are displayed newest to oldest—thus, latter days to earlier days.
   I also implemented a search function.  This took about 30 minutes.  The search area will do a regular expression search on the body of the article.  I suppose I could do more advanced searches, but I don't think they will be all that useful.
   Lesson learned about Internet Explorer: The "id" of a tag becomes a reserved javascript variable.  So if you have a tag called "SearchSpan", you can not do "SearchSpan = document.getElementById( "SearchSpan" );".  In effect, that has already been done-- but only in Internet Explorer.  So be careful of the id names you use in javascript as you may effect your javascript code.
   Pictured is an old, discarded electrical panel from one of the location I worked at this summer.
   I spent most of today trying to clear the paper work off my bench.  Afterwards, I had more bench space.  I also found the 250 gig hard drive from the Red-Dragon that "failed" in the RAID array.  Since it ended up buried, I had forgotten about it.
   This picture was setup purposely to have this odd angle. 
   The new UPS battery arrived today.  The UPSes are in a stack, and getting at the bottom UPS isn't easy.  Changing the battery isn't trivial either—it's practically shoehorned in.  The change took about 45 minutes all together, but that seems to have done the trick.  The UPS is again fully functional and serving power.
   Pictured is Gir from one of my favorite animated show, Invader Zim.

April 03, 2006

Looking back at the Grage site

   Interesting fact: The Garage site is the oldest webpage hosted on  It went on-line sometime in the fall of 1999 to host the original fireball pictures.  At the time, we used a free hosting service by the name of  They offered free, unlimited hosting with no advertising.  I thought to myself while writing this 'I wish I had a screen shot' to prove this.  Well, I do.  Here's crosswinds sign up page in January of 1999, courtesy of the Wayback machine.  Look at "Crosswinds' promises" to members.  Yep:

Crosswinds' promises to our members:

  • Our e-mail and personal web pages will always be free and unlimited.
  • We will never force you to place banners or pop ups on your personal pages.
  • We will never give away, sell, or trade your e-mail address or personal information.
  • Crosswinds lays no claim to your site's content or copyrights, including those members we choose for the Site of the Week!
     In 2000, they started with an ad banners on member pages.  "Due to circumstances, we had to break this last promise, hopefully, temporarily. It is a harsh reality that the revenues we were earning from our current banners and clicks were insufficient simply because we were not able to display enough and generate sufficient clicks. We hope you will help us make this a temporary break."  These days, is a pay-only web hosting site.
  In all honesty,'s evolution is not unexpected.  Perhaps they had good intentions from the beginning, wishing to offer a free,unlimited and ad-less hosting.  They deserve some recognition for trying.  However, in early 2000, the Garage site was deleted from crosswinds without notice. asked they be informed of members hosting MP3 or other audio; presumably to avoid hosting pirated music.  At the time, the Garage site was also home to the Garage band, Malicious Mischief,  We were recording our practice sessions and converting them to MP3 and Real Audio.  (I had selected as a host because of their claim of "free and unlimited".)  However, it was shortly after I submitted an e-mail to the admins about our music, the site was removed.  I can only speculate (since they never contacted me) they objected to the content.  In this case, "free beer" does not mean "freedom".  None the less, going back on your word and censorship should warrant terming "sell outs".
   The Garage site is over 6 years, and for the most part, the site is dormant.  Except for the addition of photos and the Wall, not much on the site has changed. seems to be the living version of the Garage site.  Now that I'm living at the Garage again, I've been kicking around the ideas of what to do with the Garage site.  Naturally, living here will produce new content.  We also have a ton of video footage I've wanted to digitize and publish, but haven't yet.  No answerers yet, but the question has been asked. closed out February with a higher then expected number of visitors: 8,540 over the 5,517 in January and 6,229 in December and a yearly average of 5,487.  Page requests also reflect this: 22,732 in February.  Our average is 16,566 pages served a month.  Hits were a little higher then normal, but not extraordinarily so: 151,483 in February vs. the yearly average of 129,443.  I don't have any explanation for the increase in traffic, but it's welcome :)
   Other statistics we noted: 23.1% of last months traffic was Mozilla/Firefox.  Our most popular searches are for the King's Quest game walk-through, a total of 333 arrivals for that.  Also emerging an increase in searches for animated american flags at 48 arrivals.
   Pictured are Crystal and Nathan from January, 2006.