[ Swingley Development ] [ Jump to Content ]
animals baseball beer blog / photolog books bookbinding me other weather woodworking

DW1454 Station Details

Christopher Swingley

Station

[ station site ]

station site

In the time we’ve lived in our house on Goldstream Creek we’ve noticed that we’re one of the coldest places in the Fairbanks area, but none of the instruments we had could log data, and most of the temperature sensors quit working once the temperature got below -40. In October 2008, we installed a RainWise MKIII-RTI weather station on the dog yard fence door. We chose the RainWise system for several reasons: it’s made in the United States, the temperature is rated down to -66°F, the indoor display is bright, and data retrieval is easy to program.

The station sends temperature, relative humidity, barometric pressure, wind speed, wind direction, gust wind speed, precipitation, and voltage every two seconds. The signal is picked up by the display unit, as well as by a separate data logger that we have connected to a Linux box in the junk room. I wrote a Python script that stores the data in a text file that rotates every day. Two additional programs insert the data into a PostgreSQL database every five minutes, and send data to the CWOP servers every ten minutes. I have my own procesing scripts for the data in the database, and NOAA makes the CWOP data available via MADIS. Our station (DW1454) regularly shows up in Weather Service Public Information Statements because we are in such a cold spot.

[ station ]

station

The station is attached to a 20 foot 2x4 that’s bolted to the gate to our dog yard. It comes from the factory attached to a two foot section of 1¼" schedule 40 PVC pipe. In our setup, it sits inside a section of 2" schedule 40 pipe, and is held in place by three machine bolts at the top and bottom. The bolts allow for some adjustment of the station to keep it level. The station should be 5-10 feet higher and in a more open area to accurately measure wind speed and direction. There are also tall trees about 10 feet away, which affect wind, and will likley deposit leaves in the rain collector. The other problem with the location is that it’s connected to the dog yard gate, which transmits some vibration to the station when the door is opened and closed. It’s possible that this will trip the rain rain gauge’s tipping buckets even when they’re not full. We haven’t observered this particular issue, but we will need to wait for summer to see if it’s a problem. Next summer we’ll explore other possible sites for the station.

[ display ]

display

The manufacturer says the solar panel will keep the system fully charged year round at latitudes below 60 degrees. We’re at 64+ degrees North, so RainWise included a charger port on the station and 120VAC charger so we could manually charge it when the battery voltage gets too low.

The display shows (clockwise from the upper left) relative humidity, time, temperature (rotating between outdoor, indoor, dewpoint and wind chill), precipitation and barometric pressure. The center of the display shows wind speed and direction. It also records maximums and minimums for all values, and can display in English and metric units. I really like the bright LEDs, especially by comparison to the LCD displays on other stations. It’s easy to read from across the room.

Software

The data logger receives data every two seconds, and has a serial port that accepts commands to reset values and read the data stream. I wrote a simple Python script that polls the logger every two seconds, reads the data string, and writes the data to a text file. The program also rotates the text file at midnight to make it easier to archive the raw data. The only non-standard module the script depends on is the pySerial module, but this module is available for all platforms, so the script should work everywhere.

A second Python script reads the tail end of the raw text file every five minutes, calculates minimums, maximums, and averages for all parameters, and inserts the aggredated data into a PostgreSQL database. Wind data is treated specially because wind direction can’t be averaged numerically (0 degrees and 360 degrees are both Northerly wind but the average would be 180, or Southerly).

One final script reads the same raw text file every ten minutes, calculates averages, converts the results in APRSWXNET format, and sends it to the CWOP servers.

Database

There’s not much to the database design, except for all the views I have set up to view the data summarized in different ways. The main table, observations looks like this:

           Table "public.observations"
 Column |            Type             | Modifiers 
--------+-----------------------------+-----------
 obs_dt | timestamp without time zone | not null
 temp   | numeric(6,2)                | 
 rh     | numeric(5,2)                | 
 pres   | numeric(5,3)                | 
 wdir   | numeric(4,1)                | 
 wspd   | numeric(4,1)                | 
 gdir   | integer                     | 
 gspd   | integer                     | 
 precip | numeric(4,2)                | 
 batt   | numeric(3,2)                | 
Indexes:
    "observations_pkey" PRIMARY KEY, btree (obs_dt)

More indexes may be needed later, but for now, almost all of the queries are selecting on the obs_dt column.

The fun part of working with this data is finding ways to summarize it by creating a series of custom views. One of the more entertaining views is the one showing daily (or hourly) wind directions. Here’s a week’s worth of wind data from this view:

select * from daily_wind_dir_bins where date > current_timestamp - interval ’7 day’;
    date    | n | nne | ne | ene | e | ese | se | sse | s  | ssw | sw | wsw | w  | wnw | nw | nnw | count 
------------+---+-----+----+-----+---+-----+----+-----+----+-----+----+-----+----+-----+----+-----+-------
 2008-10-04 | 0 |   0 |  0 |   0 | 0 |   6 | 31 |  21 | 14 |   0 |  0 |   0 |  0 |   0 |  0 |   0 |    72
 2008-10-05 | 0 |   0 |  0 |   0 | 0 |   1 |  6 |   6 |  4 |   1 |  0 |   0 |  0 |   0 |  0 |   0 |    18
 2008-10-06 | 0 |   0 |  0 |   1 | 1 |   0 | 12 |   6 |  1 |   0 |  0 |   0 |  0 |   0 |  0 |   0 |    21
 2008-10-07 | 0 |   0 |  0 |   0 | 0 |   0 |  0 |   0 |  0 |   0 |  0 |   2 | 15 |  20 |  9 |   0 |    46
 2008-10-08 | 0 |   0 |  0 |   0 | 0 |   0 |  6 |  18 | 16 |   8 |  4 |   4 |  2 |   1 |  2 |   0 |    61
 2008-10-09 | 0 |   2 |  1 |   1 | 3 |   8 |  9 |   2 |  1 |   0 |  1 |   0 |  1 |   0 |  0 |   1 |    31
 2008-10-10 | 0 |   0 |  0 |   2 | 1 |  23 | 27 |  14 |  4 |   3 |  1 |   1 |  4 |   6 |  5 |   0 |    92

This shows how many wind direction observations were recorded for each direction on each day, when the wind was blowing (the gust speed wasn’t zero during the observation period). The count in the last column is the total number of observations with wind (Fairbanks isn’t a very windy place in the winter…).

This view is created by grouping by day and using a series of sum(CASE WHILE THEN ELSE END) statements like this one for the northerly direction:

sum(
    CASE WHEN observations.wdir > 348.75 AND observations.wdir <= 11.25 AND observations.gspd > 0 
        THEN 1 
        ELSE 0 
    END) AS n

The grouping collects all the rows within each day, and then calculates the sum of the CASE function. For each direction “bin,” the CASE fuction returns a 1 when the row’s observation is in that bin, and a 0 when it’s not. The database could divide each bin by the total in the count column, but I prefer to see the raw counts.

Wind speed and direction is complicated by the semi-numerical nature of wind direction. To calculate average wind direction, you need to convert the winds into u and v vectors, average those, and convert back to direction. I have a wind_vectors view with these vectors for each observation:

CREATE VIEW wind_vectors AS
SELECT observations.obs_dt,
    observations.wspd,
    observations.wdir,
    observations.gspd,
    observations.gdir,
    ((-1) * observations.wspd) * sin(radians(observations.wdir)) AS u,
    ((-1) * observations.wspd) * cos(radians(observations.wdir)) AS v,
    ((-1) * (1 + observations.wspd)) * sin(radians(observations.wdir)) AS up1,
    ((-1) * (1 + observations.wspd)) * cos(radians(observations.wdir)) AS vp1 
FROM observations
ORDER BY observations.obs_dt;

The up1 and vp1 columns have 1 mph of wind added to the existing wind speed so I can get valid vectors when the wind wasn’t blowing. I’m not exactly certain if this is the correct approach, or if it’d be better to just set the vectors to NULL when there’s no wind. I could also have a second set of vectors that don’t have the wind speed included at all, but averages calculated using these vectors wouldn’t scale the average direction according to when the wind was blowing the hardest. Wind is complicated, and I haven’t figured out the best way to deal with it yet.

To get the wind direction from the vectors, the function is

(CASE WHEN degrees(atan2((-1) * avg(wv.up1), (-1) * avg(wv.vp1))) < 0
    THEN 360 + degrees(atan2((-1) * avg(wv.up1), (-1) * avg(wv.vp1)))
    ELSE degrees(atan2((-1) * avg(wv.up1), (-1) * avg(wv.vp1)))
 END) AS wdir_avg

Plotting

Plotting is done primarily with gnuplot. A shell script extracts the relevant data from the database, dumps it to a temporary file, and then calls the appropriate gnuplot script.

The wind rose plot is done with python and the simpleSVG module. The author of that module also wrote the wind rose code, available from the same site. SVG is well supported by all of the modern browsers except Internet Explorer. Microsoft has apparently decided (once again), not to support web standards. I like SVG because it's a vector format which will look good at any scale.

Other pages

The following are some other pages relating to our weather station.

[ Page last updated 14-Oct-2008 ]