Life, Technology, and Meteorology

Year: 2007 (Page 1 of 3)

MacSanta Brings Seasonality and Dash Monitors to You!

MacSanta has been bringing popular Mac software from indie developers to your stocking this year, and Gaucho Software is joining in on the fun.  Save 20% when you purchase Seasonality or Dash Monitors today, just in time to watch that big storm slamming the Eastern U.S. with rain and snow… 🙂

Since many of you are offline during the weekend, enjoy the same 20% discount tomorrow too!  Just use coupon code MACSANTA07 when checking out from the Gaucho Software Store.  After that, you can save 10% through December 31st by using the coupon code MACSANTA07TEN.

Happy Holidays!

Inside DynDNS Updater 2.0

I’m thrilled to link to a major update of DynDNS’s Mac client, DynDNS Updater 2.0. I’ve been working on the DynDNS Updater for quite some time, and this release is a complete re-write that has a lot of new functionality. Looking at 1.x and 2.0, you would never know the projects were related; there are far too many changes to even list. To try and summarize, DynDNS Updater 2.0 is built from the ground up to give a completely modern Mac experience to people who use the services provided by DynDNS. The updater now has auto Sparkle updating built-in, Growl integration, a custom Dashboard Widget, and an interface that looks right at home on both Tiger and Leopard. Core technologies like OpenSSL, libcurl, and pthreads are used on the back-end.

In case you aren’t familiar with DynDNS as a company, their products solve the problem of managing DNS for just about every type of user, from individuals to large companies. I believe Dynamic DNS is the project that is most well-known (at least that is how I heard of them years ago), giving users with dynamic IPs a method of having a static name point to their computer. The DynDNS Updater’s primary function is to watch out for IP address changes on your Mac, and let the DynDNS servers know when there is a change. The updater is broken up into two separate executables, one is a Cocoa application where you configure your accounts and hosts. The other executable is a background daemon running 24/7 that performs all the grunt work.

I started working on version 2.0 last year. Jeremy Hitchcock and the rest of the DynDNS crew had some nice feature ideas for the next version, and I mocked up a design that would implement some of these features. I was pretty excited about the project and started working on what was to be a fairly advanced update. Fast forward to around March or April of this year when the first beta was being wrapped up. This beta had the foundation of a solid background daemon where I tried to stick to the basic POSIX libraries, so it could run on multiple platforms. At one point, I was regularly compiling it on Linux and it was working well (I haven’t tried this for quite some time since then). The daemon was completely threaded, and supported multiple network connections. Unfortunately, while the code design was solid and the product had a lot of nice new features, the UI was horribly difficult to use and the interface to configure these complex setups was not pretty. The mock-ups looked nice, but actually using it is where we ran into problems. Seemingly simple operations took multiple steps, because a lot of advanced options had to be taken into account.

Enter FJ de Kermadec and his team at Webstellung. They suggested bringing the project back to basics–thinking more about the typical use case and not all the features we could give to power users in unique situations. Webstellung put together some very impressive UI design mock-ups to get things started. Jeremy gave the go-ahead, and it was back to the drawing board as I began coding up the fresh interface. Fortunately, since the daemon code was designed with flexibility in mind, it could be re-used by dropping the new interface on top of it. The initial UI creation went pretty quickly, and within a few months a new application was born. The first beta was ready just before WWDC this year. As soon as I started using this UI, I knew it was a keeper. There were a few adjustments that had to be made, but overall the interface is very intuitive. After the functionality was mostly complete, it was time to focus on the interface polish. We went through several iterations of the software, catching small changes here and there that all added up to a nice shiny app.

If you’re currently using DynDNS Updater 1.2, definitely upgrade to version 2.0. And if you aren’t, download the app anyway and check it out. Dynamic DNS accounts are free for up to 5 hosts, and it’s pretty easy to set everything up.

Populated Places

When developing weather software, one of the most important pieces of data you will work with is a location list. A weather application is useless if the user can’t find a city and download weather data for it. There are several different methods users might want to be able to search for a weather location. The methods Seasonality supports include zip codes (U.S. only, for now), city, state, country, and ICAO. Actually, ICAO was just a convenience method I added, so when a user double-clicks on an ICAO weather station in the satellite imagery, the add location dialog box will pop up with a listing of locations that use that ICAO.

So where do you find a good location listing online? The best resource I’ve found is the GeoNames database. They have slightly over 80,000 cities, each having a population over 1,000 people in a simple CSV file you can download from their website. Each city has a state (if applicable), a country, latitude/longitude, elevation, population, time zone, and some other fields as well. Our basic needs are only the city name and it’s latitude/longitude, but some of these other fields come in handy when trying to calculate the local time, deciding which cities to mark on the map (higher population cities should take precedence), etc.

It’s not much use to have this location data unless you can correlate it with your weather data sources. You’ll definitely need a relational database to keep track of everything. I prefer PostgreSQL for my master location database. There are several aspects of data correlation that you’ll need to think about here. Everything from matching every location with the closest weather station ICAO, to getting zip code listings and matching each one with an appropriate city. It’s a lot of work (you’ll learn a lot of SQL along the way) but eventually you’ll have a giant web of location data that you can pull from.

Now that you have this master location database, you’ll want to trim it down to something you can actually ship with your application. For example, Seasonality’s master location database is over 4.5GB in size, which would obviously be too large to include in an application archive. A small download size is desirable, so only include the data you absolutely need. Also, PostgreSQL is a bit of overkill to be running in the background for a database this size, so convert your dataset to something different, like SQLite. Selecting a subset of data and converting it from PostgreSQL to SQLite all takes place in a custom script I wrote, so each time I update a location (and trust me, with so many locations there will be errors and corrections) all I need to do is re-run the export script and I have a fresh location database to include in the next version of Seasonality.

MicroNet G-Force MegaDisk NAS Review

If you have been following my Twitter feed, you know that I just ordered a 1TB NAS last week for the office network here. I wanted some no-fuss storage sitting on the network so I could backup my data and store some archive information there instead of burning everything to DVD. (In reality, I’ll still probably burn archive data to DVD just to have a backup.)

Earlier this month, MicroNet released the G-Force MegaDisk NAS (MDN1000). The features were good and the price was right so I bought one. It finally arrived today and I’ve been spending some time getting to know the system and performing some benchmarks.

When opening the box, the first thing that surprised me was the size of the device. It’s really not much bigger than 2 3.5″ hard drives stacked on top of each other. The case is pretty sturdy, made out of aluminum, but the stand is a joke. Basically, two metal pieces came with rubber pads on them. You’re supposed to put a metal piece on each side to support the case. It’s not very sturdy, and a pain to setup like this, so I doubt I’ll use them.

I had a few problems reaching the device on my network when I plugged it in. I had to cycle the power a couple of times before I was finally able to pick it up on the network and login to the web interface. I’m guessing future firmware updates will make the setup process easier. It’s running Linux, which is nice. The firmware version is 2.6.1, so I’m guessing that means the kernel is version 2.6 (nmap identifies it as kernel 2.6.11 – 2.6.15). Hopefully it’s only a matter of time before someone’s hacked it with ssh access. MicroNet’s website claims there is an embedded dual-core processor on board, which again sounds pretty cool. The OS requires just under 61MB of space on one of the hard drives. There are two 500GB drives in this unit. Both are Hitachi (HDT725050VLA360) models, which are SATA2 drives that run at 7200 RPM with 16MB of cache. From the web interface, it looks like the disks are mounted at /dev/hdc and /dev/hdd.

Disk management is pretty straightforward. You can select a format for each disk (ext2, ext3, fat32), and there is an option to encrypt the content on the disk. The drives are monitored via the SMART interface, and you can view the reports in detail via the web. By default, the drives come in a striped RAID format, but I was able to remove the RAID and access each disk separately (contrary to the documentation’s claims). Unfortunately, for some reason I was unable to access the second disk over NFS. It looks like you might be able to mess with the web configuration page to get around this limitation though.

Moving on to the RAID configuration, you can choose between RAID 0, RAID 1, and Linear (JBOD). Ext2 and ext3 are your filesystem options. Building a RAID 1 took a very long time (~ 4 hours), which I’m guessing is because the disks require a full sync of all 500GB of data when initializing such a partition.

So let’s bust out the benchmarks! I benchmarked by performing 2 different copies. One copy was a single 400.7MB file (LARGE FILE), and the other was a directory with 4,222 files totally 68.7MB (SMALL FILES). All tests were performed over a gigabit Ethernet network from my 2.5Ghz G5 desktop machine. Transfers were done via the Terminal with the time command, to remove any human-error from the equation.

A note about testing Samba with SMALL FILES: I started running a write test and let it go for around 8 minutes. At that point, it was still only done copying around a quarter of the files, and the transfer rate averaged less than 20KB/sec. This was absurdly slow, so I didn’t bother waiting for the full test to go through. It’s difficult to say if this is a limitation of the NAS, Samba, Mac OS X or all of the above.

Striped RAID (Standard) NFS Samba
Write LARGE FILE 1:13 (5,544 KB/sec) 0:42 (9,542 KB/sec)
Read LARGE FILE 0:42 (9,769 KB/sec) 0:35 (11,723 KB/sec)
Write SMALL FILES 3:46 (310 KB/sec) DNF
Read SMALL FILES 0:39 (1,759 KB/sec) DNF
Mirrored RAID NFS Samba
Write LARGE FILE 1:17 (5,328 KB/sec) 0:47 (8,730 KB/sec)
Read LARGE FILE 0:40 (10,257 KB/sec) 0:41 (10,007 KB/sec)
Write SMALL FILES 3:44 (314 KB/sec) DNF
Read SMALL FILES 0:43 (1,636 KB/sec) DNF
Separate Disks NFS Samba
Write LARGE FILE 1:13 (5,620 KB/sec) 0:43 (9,542 KB/sec)
Read LARGE FILE 0:46 (8,919 KB/sec) 0:35 (11,723 KB/sec)
Write SMALL FILES 3:11 (368 KB/sec) DNF
Read SMALL FILES 0:42 (1,675 KB/sec) DNF

All of these were using standard mounting, either through the Finder’s browse window, or mount -t nfs with no options on the console. I decided to try tweaking the NFS parameters to see if I could squeeze any more speed out of it. The following results are all using a striped RAID configuration…

  no options wsize=16384
rsize=16384
wsize=16384
rsize=16384
noatime
intr
Write LARGE FILE 1:13
(5,544 KB/sec)
1:00
(6,838 KB/sec)
0:59
(6,954 KB/sec)
Read LARGE FILE 0:42
(9,769 KB/sec)
0:32
(12,822 KB/sec)
0:32
(12,822 KB/sec)
Write SMALL FILES 3:46
(311 KB/sec)
3:47
(310 KB/sec)
3:09
(372 KB/sec)
Read SMALL FILES 0:39
(1,759 KB/sec)
0:42
(1,675 KB/sec)
0:40
(1,758 KB/sec)

In summary, while this NAS isn’t necessarily the fastest out there, it’s certainly fast enough, especially after some tweaking. A RAID configuration doesn’t necessarily improve performance on this device. All of the transfer rates were about the same, regardless of format. You’ll notice slightly slower speeds for a RAID 1, but the difference is minimal. Before tweaking, Samba had a clear lead in transfer rates on large files, but it was completely unusable with smaller files. After modifying the NFS mount parameters, it seems to give the best of both worlds.

Update: I researched the Samba performance (or lack thereof) and found that it is not the fault of the NAS. Using a Windows XP box, writing small files went at a reasonable pace (around the same as using NFS above). Then, testing from my MacBook Pro with an OS that shall not be named, performance was similar to the Windows XP machine. I’m going to attribute this to a bug in the Samba code between version 3.0.10 on the G5 and 3.0.25 on the MacBook Pro.

Software Announcements

Some very cool apps were released/upgraded recently. Just wanted to send out some props here.

First, Daniel Jalkut has been working on MarsEdit 2.0 for quite some time, and I have to say the results are spectacular. The blog post window has been much improved, and now includes the ability to add new blog categories without going to my WordPress admin interface. This feature alone is worth the upgrade price. Overall, the app seems a lot slicker, and blends into Tiger very nicely.

Next is Gus Mueller’s newest application, Acorn. I was fortunate enough to be shown a demo of a beta version back at C4 last month. This really is a good alternative to Photoshop Elements. It’s easy to use, has a very clean interface, and has some advanced features like Layers and Core Image filters. Definitely give Acorn a look-see, and take advantage of the introductory pricing offered.

Using Compressed Textures in OpenGL

I’m not sure if it’s just me, but for some reason OpenGL coding involves a lot of trial and error before getting a feature such as lighting, blending, or texture mapping to work correctly. The past few days I have been working on adding texture compression to my OpenGL map test project. Ultimately, this code will be merged with the rest of the Seasonality source tree, and it’s going to look pretty cool.

Most OpenGL developers will use regular images and possibly compress them when loading them as a texture on the GPU. This is fairly straightforward, and just involves changing one line of code when loading the texture. Note that this is a huge gain when it comes to graphics memory savings, as I was using about 128MB of VRAM when texture compression was disabled and only around 30MB with compression enabled. I wanted to accomplish something a bit more difficult though. I’m going to be using several thousand textures, so I would like to have OpenGL compress them the first time Seasonality is launched, and then save the compressed images back to disk so concurrent launches will not require the re-compression of the imagery.

The problem I ran into was not enough developers are using this technique to speed up their application, so sample code was scarce. I found some in a book I bought awhile back called “More OpenGL Game Programming,” but the code was written for Windows, and it didn’t work on Mac OS X. So I dove deep into the OpenGL API reference and hacked my way through it. The resulting code is a simplification of the method I’m using. It should integrate with your OpenGL application, but I can’t guaranty this completely because it is excerpted from my project. If you’re having a problem integrating it though, post a comment or send me an email.

First, we have some code that will check for a compressed texture file on disk. If the compressed file doesn’t exist, then we are being launched for the first time and should create a compressed texture file.

- (bool) setupGLImageName:(NSString *)imageName
         toTextureNumber:(unsigned int)textureNumber
{
   GLint width, height, size;
   GLenum compressedFormat;
   GLubyte *pData = NULL;

   // Attempt to load the compressed texture data.
   if (pData = LoadCompressedImage("/path/to/compressed/image", &width, &height,
       &compressedFormat, &size))
   {
      // Compressed texture was found, image bytes are in pData.
      // Bind to this texture number.
      glBindTexture(GL_TEXTURE_2D, textureNumber);

      // Define how to scale the texture.
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

      // Create the texture from the compressed bytes.
      glCompressedTexImage2D(GL_TEXTURE_2D, 0, compressedFormat,
                             width, height, 0, size, pData);


      // Define your texture edge handling, here I'm clamping.
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP);
      glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
      glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
      // Free the buffer (allocated in LoadCompressedImage)
      free(pData);
      return YES;
   }
   else {
      // A compressed texture doesn't exist yet, run the standard texture code.
      NSImage *baseImage = [NSImage imageNamed:imageName];
      return [self setupGLImage:baseImage toTextureNumber:textureNumber];
   }
}

Next is the code to load a standard texture. Here we get the bitmap image rep and compress the texture to the GPU. Next we’ll grab the compressed texture and write it to disk.

- (bool) setupGLImage:(NSImage *)image
         toTextureNumber:(unsigned int)textureNumber
{
   NSData *imageData = [image TIFFRepresentation];
   NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithData:imageData];
   // Add your own error checking here.

   NSSize size = [rep size];
   // Again, more error checking.  Here we aren't using
   // MIPMAPs, so make sure your dimensions are a power of 2.

   int bpp = [rep bitsPerPixel];

   // Bind to the texture number.
   glBindTexture(GL_TEXTURE_2D, textureNumber);
   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

   // Define how to scale the texture.
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

   // Figure out what our image format is (alpha?)
   GLenum format, internalFormat;
   if (bpp == 24) {
      format = GL_RGB;
      internalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
   }
   else if (bpp == 32) {
      format = GL_RGBA;
      internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
   }

   // Read in and compress the texture.
   glTexImage2D(GL_TEXTURE_2D, 0, internalFormat,
                size.width, size.height, 0,
                format, GL_UNSIGNED_BYTE, [rep bitmapData]);

   // If our compressed size is reasonable, write the compressed image to disk.
   GLint compressedSize;
   glGetTexLevelParameteriv(GL_TEXTURE_2D, 0,
                            GL_TEXTURE_COMPRESSED_IMAGE_SIZE,
                            &compressedSize);
   if ((compressedSize > 0) && (compressedSize < 100000000)) {
      // Allocate a buffer to read back the compressed texture.
      GLubyte *compressedBytes = malloc(sizeof(GLubyte) * compressedSize);

      // Read back the compressed texture.
      glGetCompressedTexImage(GL_TEXTURE_2D, 0, compressedBytes);

      // Save the texture to a file.
      SaveCompressedImage("/path/to/compressed/image", size.width, size.height,
                          internalFormat, compressedSize, compressedBytes);

      // Free our buffer.
      free(compressedBytes);
   }

   // Define your texture edge handling, again here I'm clamping.
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP);
   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

   // Release the bitmap image rep.
   [rep release];

   return YES;
}

Finally we have a few functions to write the file to disk and read it from the disk. These functions were pulled almost verbatim from the OpenGL book. In the first code block above we called LoadCompressedImage to read the texture data from the disk. In the second code block, we called SaveCompressedImage to save the texture to disk. Nothing really special is going on here. We write some parameters to the head of the file, so when we go to read it back in we have the details. Bytes 0-3 of the file are the image width, 4-7 is the image height, 8-11 is the format (GL_COMPRESSED_RGB_S3TC_DXT1_EXT or GL_COMPRESSED_RGBA_S3TC_DXT5_EXT), 12-15 is the size of the image data in bytes, and bytes 16+ are the image data.

void SaveCompressedImage(const char *path, GLint width, GLint height,
                         GLenum compressedFormat, GLint size, GLubyte *pData)
{
   FILE *pFile = fopen(path, "wb");
   if (!pFile)
      return;

GLuint info[4];

info[0] = width;
info[1] = height;
info[2] = compressedFormat;
info[3] = size;

fwrite(info, 4, 4, pFile);
fwrite(pData, size, 1, pFile);
fclose(pFile);
}

GLubyte * LoadCompressedImage(const char *path, GLint *width, GLint *height,
GLenum *compressedFormat, GLint *size)
{
FILE *pFile = fopen(path, “rb”);
if (!pFile)
return 0;
GLuint info[4];

fread(info, 4, 4, pFile);
*width = info[0];
*height = info[1];
*compressedFormat = info[2];
*size = info[3];

GLubyte *pData = malloc(*size);
fread(pData, *size, 1, pFile);
fclose(pFile);
return pData;
// Free pData when done…
}

Hopefully this will save someone development time in the future. If you catch any errors, let me know.

Precipitation Intensity

Today I revisited the code on the Seasonality forecast server that decides on precipitation intensity. I.E., how much rain does it take to have a drizzle, light rain, moderate rain, or heavy rain? This is forecasted in 12 hour blocks, and I take the sum of precipitation over that time frame to get a total precipitation for that block of time.

Previously the intensity chart looked something like this:

  • 0 – 3mm: Drizzle
  • 4 – 7mm: Light Rain
  • 8 – 25mm: Moderate Rain
  • > 25mm: Heavy Rain

What I found was the GFS model output would often place a millimeter or two of precipitation at random locations and this would trigger a “drizzle” forecast far too often. I decided that I needed to revisit the precipitation intensity chart.

I ended up finding this page at the MetOffice (United Kingdom). They have about twice as many intensity categories, from Very slight to Downpour, so I tried to adapt them to my intensity names (which match those of the NDFD forecasts put out by the U.S. National Weather Service). I ended up using these values:

  • 3 – 6mm: Drizzle
  • 7 – 12mm: Light Rain
  • 13 – 25mm: Moderate Rain
  • > 25mm: Heavy Rain

We’ll try these out for awhile to see how accurate they are. If you’re a Seasonality user, these changes were made all on the forecast server, so no software update is necessary.

A Weather Developer's Journey Begins!

A few months ago an idea came to me for a new domain name I should pick up. I wasn’t sure what I was going to do with the domain yet, or if I would even use it at all, but I wanted to jump on it because it was available. It struck me as a good name, and I was a bit surprised that it was still available.

Zoom forward to a couple of weeks ago, when a different idea for a new website came to mind. Oftentimes I post detailed entries here specifically about the weather or very weather-tech related articles when I run into issues while developing Seasonality. The thing is, I’m not sure my typical reader is interested in these postings, and occasionally I will refrain from posting about weather-related issues simply because I don’t think it would fit well in the *Coder blog.

But wait a second…I bought that domain awhile back, maybe that would work. Actually, it ended up being perfect for my new site idea. So I started working on it off and on. I’ve been happily using WordPress to host this blog for quite some time, so I decided to use the same platform for the new site. I set things up, customized a theme, and wrote a posting or two. I think now it is finally ready to be revealed, and I wanted to share. The domain is weatherdeveloper.com, and the site is called “A Weather Developer’s Journey.” With the full-time development of Seasonality 2 coming soon here, I thought it would be a perfect time to start a site like this, as I’ll be spending a lot of time trying to overcome issues with data hosting, manipulation, and weather visualization.

If you’re at all interested in checking it out, please do so (Website, RSS Feed). I’ll most likely post links from here to the first few articles on the new site, just to get the ball rolling. The first article talks about finding and putting to use 90 meter elevation data.

90 Meter Elevation Data

To generate an international forecast for Seasonality, I take in a GeoPoint and find the 4 closest surrounding data points from the GFS Forecast model. The GFS data is 0.5 degree resolution, so if I had a location at point 50.2° latitude, 30.8° longitude, I would use the following data points when generating the forecast: (50°, 30.5°), (50°, 31°), (50.5°, 30.5°), and (50.5°, 31°). The problem is that these surrounding points could be fairly distant from the weather location being queried, so depending on the terrain a forecast can vary widely. However, the forecast could be made more accurate if the elevations of each GeoPoint is taken into account.

So where does the 90 meter data come from? Well, the SRTM mission a few years back captured this level of detail for the area from -60° to 60° latitude, which includes almost all the land masses. Outside that range, a lower resolution dataset is used to fill in the gaps. A data point every 90 meters doesn’t sound like much; after all that’s elevation points about a football field away from each other. I imagine in some terrain where you have a lot of quick elevation changes, such as canyons or cliffs, this wouldn’t be enough. However, do not underestimate the amount of data here. The compressed download is around 1.2 gigabytes, and it expands to a ~7 gigabyte data file. The entire dataset is 86400×43200 data points, or 3,732,480,000 GeoPoints. Multiply that by 2 bytes per data point and you have a 7 gigabyte file.

Fortunately, this file is easy to parse. There is no header, just the raw data. The map projection is a simple equirectangular projection, giving equal distance across latitude and longitude. The location starts at (90°, -180°) and continues across an entire 86400 point row before moving South to the next row. The final GeoPoint is (-90°, 180°). Each data point is a signed “short” integer (2 bytes long). Perl’s unpack function works wonders here to get a short value from the binary data.This data is going to be perfect for taking elevation into account while forecasting. Its resolution is over 14000x (!) more detailed than my 0.5 degree GFS dataset, giving 120 GeoPoints between each GFS GeoPoint in each dimension. This should be plenty of data to work with. Maybe I’ll talk about how I use elevation to make more accurate forecasts in a future article.

Summer Update

It’s been almost two months since I’ve posted here, so to avoid the risk of this blog becoming a dinosaur, I thought I would post an update.

Katrina and I returned from our 6-7 week road trip in the beginning of July. We drove out to California at the end of May, and stayed with family for several weeks (also hitting WWDC, of course). On the way out there, we took the northern route, hitting Mt. Rushmore, Yellowstone, the Tetons, and the Salt Flats. On the way back, we started in Santa Monica and drove Route 66 all the way through St. Louis, taking the freeway the rest of the way home after running out of time. We’ll have to drive the rest of Route 66 from St. Louis to Chicago sometime soon. Overall, it was quite a trip. Watch my Flickr stream for photos of the trip.

Been working on finishing up DynDNS Updater 2.0, which will hopefully be ready soon. The app is looking pretty good. A lot of smaller details have been improved upon since beta 5, that collectively improve the application quite a bit.

Trying to spend some time working on Seasonality’s international forecast as well. I’ll post more on this at a later point in time, but I’ve created some cool imagery and animations that I’ll be using to tweak the forecast generator to make it more accurate.

C4 is coming up this weekend! I’ll be taking off for Chicago tomorrow for a weekend of Indie fun. I’ll be showing an entry for the Iron Coder Live contest…which reminds me I still need to fix a bug or two there. Should be a blast. I’ll most likely be keeping my Twitter feed up to date more than posting here about stuff.

Speaking of Twitter, I started Twittering (is that a word?) a few months ago, and I’m hooked. If you don’t know, Twitter is a place to post Tweets, which are short bits of text (no longer than 160 characters), usually telling others what you’re up to. My first thought was how much time I would be wasting by doing this, but the whole idea is that posting a Tweet is supposed to be really quick. It provides some nice breaks throughout the day, and the community building around the site is pretty amazing. Check out my Twitter page, and if you’re interested, sign up and start using Twitter yourself.

That’s all folks…

« Older posts

© 2026 *Coder Blog

Theme by Anders NorenUp ↑