/* * ultimeter.c - Peet Bros Ultimeter weather parsing functions * * These functions were developed for APRS World. * Copyright 2002 David L Norris * This program serves as a simple testbed for these functions. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "metric.h" // weather struct is simply copied from the APRSWorld sources. struct weather { char source[10]; // AX.25 Source address int wind_direction; int wind_speed; // Kilometers / hour int wind_gust; // kilometers / hour int wind_sustained; // kilometers / hour int temperature; // celcius float rain_hour; // centimeters float rain_calendar_day; // centimeters float rain_24hour_day; // centimeters int humidity; float barometer; // milibars int luminosity; }; // Declare Functions void parse_peet_packet (struct weather *wx, char *p); void parse_peet_datalog (struct weather *wx, char *p); void parse_peet_complete (struct weather *wx, char *p); int axtoi( const char *digits ); void substr (char *dest, char *src, int size) ; // Functions start here void substr (char *dest, char *src, int size) { strncpy (dest, src, (size_t) size); dest[size] = '\0'; } // Parse Peet Bros Packet mode data. Header is "$ULTW" void parse_peet_packet (struct weather *wx, char *p) { char temp[5]; // Wind Peak Speed, Gust, Sustained. Ultimeter only reports 5-min gust in Packet Mode. substr(temp, p+5, 4); wx->wind_speed = wx->wind_gust = wx->wind_sustained = axtoi( temp ) * 0.1; // Convert 1/10 KPH to KPH // Wind Peak Direction substr(temp, p+9, 4); wx->wind_direction = axtoi( temp ) * 1.41176; // Convert 1/255 into 1/360 // Outdoor Temperature substr(temp, p+13, 4); wx->temperature = F_TO_C( axtoi( temp ) * 0.1 ); // Convert 1/10 F to C // Rain Total (since Ultimeter manual reset) substr(temp, p+17, 4); wx->rain_hour = wx->rain_calendar_day = wx->rain_24hour_day = IN_TO_CM( axtoi( temp ) * 0.01 ); // Convert 1/100 IN to CM // Altimeter Pressure substr(temp, p+21, 4); wx->barometer = axtoi( temp ) * 0.1; // Already in millibars // Percent Relative Humidity substr(temp, p+37, 4); wx->humidity = axtoi( temp ) * 0.1; // Convert 1/10 rh to rh // No luminousity sensors wx->luminosity = 0; } // Parse peet bros data logging format. Header is "!!" void parse_peet_datalog (struct weather *wx, char *p) { char temp[5]; // Wind Peak Speed, Gust, Sustained. substr(temp, p+2, 4); wx->wind_speed = wx->wind_gust = wx->wind_sustained = axtoi( temp ) * 0.1; // Convert 1/10 KPH to KPH // Wind Peak Direction substr(temp, p+6, 4); wx->wind_direction = axtoi( temp ) * 1.41176; // Convert 1/255 into 1/360 // Outdoor Temperature substr(temp, p+10, 4); wx->temperature = F_TO_C( axtoi( temp ) * 0.1 ); // Convert 1/10 F to C // Rain Total (since Ultimeter manual reset) substr(temp, p+14, 4); wx->rain_hour = wx->rain_calendar_day = wx->rain_24hour_day = IN_TO_CM( axtoi( temp ) * 0.01 ); // Convert 1/100 IN to CM // Altimeter Pressure substr(temp, p+18, 4); wx->barometer = axtoi( temp ) * 0.1; // Already in millibars // Percent Relative Humidity substr(temp, p+26, 4); wx->humidity = axtoi( temp ) * 0.1; // Convert 1/10 rh to rh // No luminousity sensors wx->luminosity = 0; } // Parse Peet Bros complete record format. Header is "&CR&" void parse_peet_complete (struct weather *wx, char *p) { char temp[5]; unsigned int tempval; unsigned int wind1, wind2, wind3; // Wind Speed fields 1, 34, and 71. Wind direction fields 2, 35, 72. substr(temp, p+4, 4); wind1 = axtoi(temp); substr(temp, p+136, 4); wind2 = axtoi(temp); substr(temp, p+284, 4); wind3 = axtoi(temp); // Select wind speed and direction based on which value is the highest. // Ugh, surely there's a way to make this pretty. if( wind1 >= wind2 && wind1 >= wind3 ){ wx->wind_speed = wind1 * 0.1; // Convert 1/10 KPH to KPH // Wind Direction. field 2. substr(temp, p+8, 4); wx->wind_direction = axtoi( temp ) * 1.41176; // Convert 1/255 into 1/360 } else if( wind2 >= wind1 && wind2 >= wind3 ){ wx->wind_speed = wind2 * 0.1; // Convert 1/10 KPH to KPH // Wind Direction. field 35. substr(temp, p+140, 4); wx->wind_direction = axtoi( temp ) * 1.41176; // Convert 1/255 into 1/360 } else if( wind3 >= wind2 && wind3 >= wind1 ){ wx->wind_speed = wind3 * 0.1; // Convert 1/10 KPH to KPH // Wind Direction. field 72. substr(temp, p+288, 4); wx->wind_direction = axtoi( temp ) * 1.41176; // Convert 1/255 into 1/360 } else{ // Just default to the first value rather than burn more CPU time. wx->wind_speed = wind1 * 0.1; // Convert 1/10 KPH to KPH // Wind Direction. field 2. substr(temp, p+8, 4); wx->wind_direction = axtoi( temp ) * 1.41176; // Convert 1/255 into 1/360 } // Wind Peak Speed. field 3. 5 minute peak speed substr(temp, p+12, 4); wx->wind_gust = axtoi( temp ) * 0.1; // Convert 1/10 KPH to KPH // Wind Sustained. 1 minute average. field 115 does not exist on some models. substr(temp, p+460, 4); tempval = axtoi( temp ); if( ( tempval > 0 ) && ( tempval < 150 ) ){ wx->wind_sustained = tempval * 0.1; // Convert 1/10 KPH to KPH } else { wx->wind_sustained = wx->wind_speed; // Convert 1/10 KPH to KPH } // Outdoor Temperature. field 6 substr(temp, p+24, 4); wx->temperature = F_TO_C( axtoi( temp ) * 0.1 ); // Convert 1/10 F to C // Rain Total since midnight. field 7. We can't determine hourly or 24-hourly without history. substr(temp, p+28, 4); wx->rain_hour = wx->rain_calendar_day = wx->rain_24hour_day = IN_TO_CM( axtoi( temp ) * 0.01 ); // Convert 1/100 IN to CM // Altimeter Pressure. field 8 substr(temp, p+32, 4); wx->barometer = axtoi( temp ) * 0.1; // Already in millibars // Percent Relative Humidity. field 13 substr(temp, p+52, 4); wx->humidity = axtoi( temp ) * 0.1; // Convert 1/10 rh to rh // No luminousity sensors wx->luminosity = 0; } // ASCII Hexadecimal to integer // Similar to atoi() but assumes Hexadecimal (base-16) int axtoi( const char *digits ){ unsigned int retval,i; // Initialize i = retval = 0; // While we have a digit 0-9 or A-F. Meaning, we break on the first non-hexadecimal character just like atoi. while( (digits[i] >= '0' && digits[i] <= '9') || (digits[i] >= 'A' && digits[i] <= 'F') ){ // Multiply value by 16. No effect if this is our first pass retval = retval * 16; // Convert ASCII into Integer by subtracting either 0x37 from A-F or 0x30 from 0-9 retval += (unsigned int)( (digits[i] > '9') ? digits[i] - 0x37 : digits[i] - 0x30 ); i++; } return retval; } // Here's a good example of how you would initialize the structure and call the functions. int main ( void ){ char record[1024]; struct weather wx = { -1, -1, -1, -1, 0, -1, -1, -1, 0, -1, -1 }; while( fgets( record, 1024, stdin ) ){ switch( record[0] ){ case '$': printf("Peet Bros Packet Format\n"); parse_peet_packet( &wx, record ); break; case '!': printf("Peet Bros Data Logging Format\n"); parse_peet_datalog( &wx, record ); break; case '&': printf("Peet Bros Complete Record\n"); parse_peet_complete( &wx, record ); break; default: printf("Unknown data format!\n"); } // Print out our values. printf("wind speed: %i\n", wx.wind_speed); printf("wind gusting: %i\n", wx.wind_gust); printf("wind sustain: %i\n", wx.wind_sustained); printf("wind direction: %i\n", wx.wind_direction); printf("temperature: %i\n", wx.temperature); printf("humidity: %i\n", wx.humidity); printf("rainfall: %f\n", wx.rain_hour); printf("barometer: %f\n", wx.barometer); printf("\n\n"); } return 0; }