Fun with 802.11

I’m constantly fiddling with my home wireless setup. It’s probably because I’m easily amused. I have a Linksys WRT54GS (version 2.0 if you know what I mean).

It’s been around in various incarnations for a couple of years now and there is a fairly active community of 3rd party firmwares for it and it’s older brother the WRT54G. Until quite recently, it was built on a Linux platform. Linksys was forced to release the source code to stay in compliance with the GPL. Lots of talented programmers took the ball and ran from it, adding many new features. Even Earthlink released a customized firmware that add IP6 support to the router.

The latest versions are no longer Linux based, they run a customized VxWorks firmware that provides similiar functionality as the stock WRT line, but requires less ram. This version is not currently compatible with the 3rd party firmwares. There are still Linux based ones out in the retail market, you can tell from the outside of the box. Each version has a slightly different serial number and that will identify the hardware version, based on the list on this page.

These custom firmwares come in many flavors. Some are based off of the current Linksys firmwares and add some new functionality. Others have veered quite a bit from the path and do not look or behave like the original firmware. I’m using the DD-WRT firmware and it works great. I’m only using a fraction of the added functionality, but the stuff it adds is pretty cool.

I use the site survey function on router to see what other routers and/or access points are active near me. I’m in a small residential neighborhood and I can usually see 7 to 10 wireless routers. That’s where the fun begins.

In the US, there are 11 channels reserved for 802.11b and 802.11g. The problem is that the channels overlap each other. Only channels 1, 6, and 11 are distinct channels. For the best reception on your network, you want to be on one of those channels and with the fewest number of neighbors on the same channel. The site survey feature in the DD-WRT firmware will display each router it finds, along with it’s channel and signal strength.

Most of the routers will be at channel 6, that seems to be the default channel that the routers use out of the box. So I’ll use channel 1 or 11, depending on which one has the least amount of traffic. And of course, there’s usually some one using one of the overlap channels like 9 or 2.

Another feature added by DD-WRT (and most of the 3rd party firmwares) is control over the power to the transmitter. I saw a small increase in signal strength with the client PC’s by raising the power setting a bit. I got a bigger boost by using Linksys’s HGA7T High Gain Antennas. You basicly replace the little stubby antennas that come with the WRT with ones that are about twice the size. You can find them on eBay or at Walmarts for around $40. It’s worth the money if you have a weak signal between the router and the PC.

Paged recordsets from SQL Server for web pages

I’ve worked on a few web apps where I displayed a paged list of data, but without storing all of the rows in the view state or as a session variable. I prefer to let SQL Server do the heavy lifting for these kinds of situations. What I want to do is to get a set of filtered data from SQL Server (2000 or better, my friend) with arbitrary row numbers assigned so that I can ask for all rows between X and Y.

There are a few ways to do this, one way I like to do it is with a stored procedure that generates the data and sends back only what I need. This eliminates most of the storage requirements on the web side of things. Using a sample table, I’ll write a procedure that lets me grab the data by row numbers.

create table Employee (
  RecordID integer identity(1,1),
  LastName varchar(30),
  FirstName varchar(20),
  IsDriver char(1)

Assume about 1000 or so records, with about 50% of them with the IsDriver field set to ‘Y’. Here’s a procedure for getting a set of data with row numbers included.

CREATE procedure QueryByRow
  @LastName varchar(20),
  @StartRecord int,
  @EndRecord int
DECLARE @MatchCount int

SELECT @MatchCount =
  WHERE IsDriver=’Y’ AND LastName LIKE @LastName

DECLARE @tmp TABLE(ID int identity(1,1), RecordID int, LastName varchar(20), FirstName varchar(20))


INSERT @tmp(RecordID, LastName, FirstName)
SELECT RecordID, LastName, FirstName
WHERE IsDriver=’Y’ AND LastName LIKE @LastName
ORDER BY LastName, FirstName


SELECT @MatchCount AS Count, t.RecordID, t.LastName, t.FirstName
FROM @tmp t
WHERE t.ID BETWEEN @StartRecord AND @EndRecord

This procedure only allows us to filter by last name, but it can be easily extended to do other filtering or even change the sort order. You would start off by calling the procedure with the RecordCount to get your first page by the starting and ending row numbers. You will get back the result set, with the total number of records as the first column. That information you would store as a session variable or in the viewstate. Now that you know the number of records, you can then get any arbitrary set of those records by calling the procedure again, but with different starting and ending row numbers.

EXECUTE QueryByRow ‘%’, 1, 25

Count       RecordID    LastName             FirstName            
———– ———– ——————– ——————–
494         1           Able                John
494         36          Baker               John
494         69          Charles             John
494         6           Dexter              John

If you were showing 25 records per page, and you wanted to display page 3, you would do this:

EXECUTE QueryByRow ‘%’, 0, 51, 75

That would return something like this:

Count       RecordID    LastName             FirstName            
———– ———– ——————– ——————–
494         2           Marro                John
494         46          Martinez             John
494         79          Mitchell             John
494         16          Schwede              John

This would bring back just the 25 rows that you would need. There is, of course, no free lunch. The drawback is that you are executing the same query against the data every time you call this procedure. You have to weigh the performance of that versus the performance of retrieving a full set of data from the SQL Server to the web server and having the web server filter and persist the data. Depending on your data and the load on your server, SQL Server will have the result set cached in memory and each additional call the procedure will be running against data already in memory.

The use of a table variable allows to build the set in memory and create the row numbers. I have seen other examples that used temporary tables and self joins on the table, but table variables seem to place the least demand on the sever resources. This method will work with both SQL Server 2000 and 2005 and is not dependant on any version of ASP.Net.

The other drawback is that other processes could be editing the table between calls to the procedure. This is why the “Count” field is returned with each row. Should that value change between calls to the procedure then you know the data was edited in some way and you would have to make sure that your code could display a different number of records than it expected.

Argh! Now I have to keep track of Remote Desktop

Raymond Chen has a great article that explains why your program may look different under a Remote Desktop or Terminal Services session. If you do double buffering to eliminate screen flickering, you’ll pay a performance penalty when running over a remote connection. His tip is to have the app’s code detect if it’s running in a remote session and disable the double buffering. You get some flickering, but it will be much faster than shoving a large bitmap down the pipe.