/* * * XASTIR, Amateur Station Tracking and Information Reporting * Copyright (C) 1999,2000 Frank Giannandrea * Copyright (C) 2000-2019 The Xastir Group * * 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. * * Look at the README for more information on the program. */ // // The code currently supports these types of locally-connected or // network-connected weather stations: // // Peet Brothers Ultimeter 2000 (Set to Data logging mode) // Peet Brothers Ultimeter 2000 (Set to Packet mode) // Peet Brothers Ultimeter 2000 (Set to Complete Record Mode) // Peet Brothers Ultimeter-II // Qualimetrics Q-Net? // Radio Shack WX-200/Huger WM-918/Oregon Scientific WM-918 // Dallas One-Wire Weather Station (via OWW network daemon) // Davis Weather Monitor II/Wizard III/Vantage Pro (via meteo/db2APRS link) // // Need to modify code to use WX_rain_gauge_type. Peet brothers. // See http://www.peetbros.com, FAQ's and owner's manuals for // details: // // Peet Bros Ultimeter II: 0.1"/0.5mm or 0.01"/0.25mm // Divide by 10 or 100 from the serial output. // // Peet Bros Ultimeter 2000, 800, & 100: 0.01"/0.25mm or 0.1mm // If 0.01" gauge, divide by 100. If 0.1mm gauge, convert to // proper English units. #ifdef HAVE_CONFIG_H #include "config.h" #endif // HAVE_CONFIG_H #include "snprintf.h" #include #include #include #if TIME_WITH_SYS_TIME #include #include #else // TIME_WITH_SYS_TIME #if HAVE_SYS_TIME_H #include #else // HAVE_SYS_TIME_H #include #endif // HAVE_SYS_TIME_H #endif // TIME_WITH_SYS_TIME #include #include #include "wx.h" #include "main.h" #include "xastir.h" #include "interface.h" #include "lang.h" #include "util.h" // Must be last include file #include "leak_detection.h" #define MAX_RAW_WX_STRING 800 char wx_station_type[100]; char raw_wx_string[MAX_RAW_WX_STRING+1]; #define MAX_WX_STRING 300 #define WX_TYPE 'X' /* rain totals */ float rain_minute[60]; // Total rain for each min. of last hour, hundredths of an inch float rain_minute_total = 0.0; // Total for last hour, hundredths of an inch int rain_minute_last_write = -1; // Write pointer for rain_minute[] array, set to an invalid number float rain_00 = 0.0; // hundredths of an inch float rain_24 = 0.0; // hundredths of an inch float rain_base[24]; // hundredths of an inch int rain_check = 0; // Flag for re-checking rain_total each hour float wind_chill = 0; // holder for wind chill calc // Gust totals float gust[60]; // High wind gust for each min. of last hour int gust_write_ptr = 0; int gust_read_ptr = 0; int gust_last_write = 0; /* Other WX station data */ char wx_dew_point[10]; char wx_dew_point_on; char wx_high_wind[10]; char wx_high_wind_on; char wx_wind_chill[10]; char wx_wind_chill_on; char wx_three_hour_baro[10]; // hPa char wx_three_hour_baro_on; char wx_hi_temp[10]; char wx_hi_temp_on; char wx_low_temp[10]; char wx_low_temp_on; char wx_heat_index[10]; char wx_heat_index_on; /***********************************************************/ /* clear rain data */ /***********************************************************/ void clear_rain_data(void) { int x; // Clear rain_base queue (starting rain total for each hour) for ( x = 0; x < 24; x++ ) { rain_base[x] = 0.0; } rain_00 = 0.0; rain_24 = 0.0; rain_check = 0; // Set flag so we'll recheck rain_total // a few times at start of each hour // Clear hourly rain queue for ( x = 0; x < 60; x++ ) { rain_minute[x] = 0.0; } rain_minute_total = 0.0; rain_minute_last_write = 70; // Invalid number so we'll know we're just starting. } /**************************************************************/ /* compute_rain_hour - rolling average for the last 59.x */ /* minutes of rain. I/O numbers are in hundredths of an inch.*/ /* Output is in "rain_minute_total", a global variable. */ /**************************************************************/ void compute_rain_hour(float rain_total) { int current, j; float lowest; // Deposit the _starting_ rain_total for each minute into a separate bucket. // Subtract lowest rain_total in queue from current rain_total to get total // for the last hour. current = get_minutes(); // Fetch the current minute value. Use this as an index // into our minute "buckets" where we store the data. rain_minute[ (current + 1) % 60 ] = 0.0; // Zero out the next bucket (probably have data in // there from the previous hour). if (rain_minute[current] == 0.0) // If no rain_total stored yet in this minute's bucket { rain_minute[current] = rain_total; // Write into current bucket } // Find the lowest non-zero value for rain_total. The max value is "rain_total". lowest = rain_total; // Set to maximum to get things going for (j = 0; j < 60; j++) { if ( (rain_minute[j] > 0.0) && (rain_minute[j] < lowest) ) // Found a lower non-zero value? { lowest = rain_minute[j]; // Snag it } } // Found it, subtract the two to get total for the last hour rain_minute_total = rain_total - lowest; if (debug_level & 2) { fprintf(stderr,"Rain_total:%0.2f Hourly:%0.2f (Low:%0.2f) ", rain_total, rain_minute_total, lowest); } } /***********************************************************/ /* compute_rain - compute rain totals from the total rain */ /* so far. rain_total (in hundredths of an inch) keeps on */ /* incrementing. */ /***********************************************************/ void compute_rain(float rain_total) { int current, i; float lower; // Skip the routine if input is outlandish (Negative value, zero, or 512 inches!). // We seem to get occasional 0.0 packets from wx200d. This makes them go away. if ( (rain_total <= 0.0) || (rain_total > 51200.0) ) { return; } compute_rain_hour(rain_total); current = get_hours(); // Set rain_base: The first rain_total for each hour. if (rain_base[current] == 0.0) // If we don't have a start value yet for this hour, { rain_base[current] = rain_total; // save it away. rain_check = 0; // Set up rain_check so we'll do the following // "else" clause a few times at start of each hour. } else // rain_base has been set, is it wrong? We recheck three times at start of hour. { if (rain_check < 3) { rain_check++; // Is rain less than base? It shouldn't be. if (rain_total < rain_base[current]) { rain_base[current] = rain_total; } // Difference greater than 10 inches since last reading? It shouldn't be. if (fabs(rain_total - rain_base[current]) > 1000.0) // Remember: Hundredths of an inch { rain_base[current] = rain_total; } } } rain_base[ (current + 1) % 24 ] = 0.0; // Clear next hour's index. // Compute total rain in last 24 hours: Really we'll compute the total rain // in the last 23 hours plus the portion of an hour we've completed (Sum up // all 24 of the hour totals). This isn't the perfect way to do it, but to // really do it right we'd need finer increments of time (to get closer to // the goal of 24 hours of rain). lower = rain_total; for ( i = 0; i < 24; i++ ) // Find the lowest non-zero rain_base value in last 24 hours { if ( (rain_base[i] > 0.0) && (rain_base[i] < lower) ) { lower = rain_base[i]; } } rain_24 = rain_total - lower; // Hundredths of an inch // Compute rain since midnight. Note that this uses whatever local time was set // on the machine. It might not be local midnight if your box is set to GMT. lower = rain_total; for ( i = 0; i <= current; i++ ) // Find the lowest non-zero rain_base value since midnight { if ( (rain_base[i] > 0.0) && (rain_base[i] < lower) ) { lower = rain_base[i]; } } rain_00 = rain_total - lower; // Hundredths of an inch // It is the responsibility of the calling program to save // the new totals in the data structure for our station. // We don't return anything except in global variables. if (debug_level & 2) { fprintf(stderr,"24hrs:%0.2f ", rain_24); fprintf(stderr,"rain_00:%0.2f\n", rain_00); } } /**************************************************************/ /* compute_gust - compute max wind gust during last 5 minutes */ /* */ /**************************************************************/ float compute_gust(float wx_speed, float UNUSED(last_speed), time_t *last_speed_time) { float computed_gust; int current, j; // Deposit max gust for each minute into a different bucket. // Check all buckets for max gust within the last five minutes // (Really 4 minutes plus whatever portion of a minute we've completed). current = get_minutes(); // Fetch the current minute value. We use this as an index // into our minute "buckets" where we store the data. // If we haven't started collecting yet, set up to do so if (gust_read_ptr == gust_write_ptr) // We haven't started yet { gust_write_ptr = current; // Set to write into current bucket gust_last_write = current; gust_read_ptr = current - 1; // Set read pointer back one, modulus 60 if (gust_read_ptr < 0) { gust_read_ptr = 59; } gust[gust_write_ptr] = 0.0; // Zero the max gust gust[gust_read_ptr] = 0.0; // for both buckets. //WE7U: Debug //gust[gust_write_ptr] = 45.9; } // Check whether we've advanced at least one minute yet if (current != gust_write_ptr) // We've advanced to a different minute { gust_write_ptr = current; // Start writing into a new bucket. gust[gust_write_ptr] = 0.0; // Zero the new bucket // Check how many bins of real data we have currently. Note that this '5' is // correct, as we just advanced "current" to the next minute. We're just pulling // along the read_ptr behind us if we have 5 bins worth of data by now. if ( ((gust_read_ptr + 5) % 60) == gust_write_ptr) // We have 5 bins of real data { gust_read_ptr = (gust_read_ptr + 1) % 60; // So advance the read pointer, } // Check for really bad pointers, perhaps the weather station got // unplugged for a while or it's just REALLY slow at sending us data? // We're checking to see if gust_last_write happened in the previous // minute. If not, we skipped a minute or more somewhere. if ( ((gust_last_write + 1) % 60) != current ) { // We lost some time somewhere: Reset the pointers, older gust data is // lost. Start over collecting new gust data. gust_read_ptr = current - 1; // Set read pointer back one, modulus 60 if (gust_read_ptr < 0) { gust_read_ptr = 59; } gust[gust_read_ptr] = 0.0; } gust_last_write = current; } // Is current wind speed higher than the current minute bucket? if (wx_speed > gust[gust_write_ptr]) { gust[gust_write_ptr] = wx_speed; // Save it in the bucket } // Read the last (up to) five buckets and find the max gust computed_gust=gust[gust_write_ptr]; j = gust_read_ptr; while (j != ((gust_write_ptr + 1) % 60) ) { if ( computed_gust < gust[j] ) { computed_gust = gust[j]; } j = (j + 1) % 60; } if (debug_level & 2) { j = gust_read_ptr; while (j != ((gust_write_ptr + 1) % 60) ) { fprintf(stderr,"%0.2f ", gust[j]); j = (j + 1) % 60; } fprintf(stderr,"gust:%0.2f\n", computed_gust); } *last_speed_time = sec_now(); return(computed_gust); } // // cycle_weather - keep the weather queues moving even if data from // weather station is scarce. This is called from main.c:UpdateTime() // on a periodic basis. This routine also does the 30 second timestamp // for the log files. // void cycle_weather(void) { DataRow *p_station; WeatherRow *weather; float last_speed, computed_gust; time_t last_speed_time; // Find my own local weather data if (search_station_name(&p_station,my_callsign,1)) { if (p_station->weather_data != NULL) // If station has WX data { weather = p_station->weather_data; // Cycle the rain queues, feed in the last rain total we had (void)compute_rain((float)atof(weather->wx_rain_total)); // Note: Some weather stations provide the per-hour, 24-hour, // and since-midnight rain rates already. Further, some stations // don't even provide total rain (Davis APRS DataLogger and the // db2APRS program), so anything we compute here is actually wrong. // Do NOT clobber these if so. This flag is set in fill_wx_data // when the station provides its data. if (weather->wx_compute_rain_rates) { // Hourly rain total xastir_snprintf(weather->wx_rain, sizeof(weather->wx_rain), "%0.2f", rain_minute_total); // Last 24 hour rain xastir_snprintf(weather->wx_prec_24, sizeof(weather->wx_prec_24), "%0.2f", rain_24); // Rain since midnight xastir_snprintf(weather->wx_prec_00, sizeof(weather->wx_prec_00), "%0.2f", rain_00); } else { // LaCrosse stations don't provide the since-midnight // numbers and so this will be blank. // So if we have blanks here, fill it in. if ( weather->wx_prec_00[0] == '\0' && weather->wx_rain_total[0] != '\0') { // Rain since midnight xastir_snprintf(weather->wx_prec_00, sizeof(weather->wx_prec_00), "%0.2f", rain_00); } } /* get last gust speed */ if (strlen(weather->wx_gust) > 0) { /* get last speed */ last_speed = (float)atof(weather->wx_gust); last_speed_time = weather->wx_speed_sec_time; } else { last_speed = 0.0; } /* wind speed */ computed_gust = compute_gust((float)atof(weather->wx_speed), last_speed, &last_speed_time); weather->wx_speed_sec_time = sec_now(); xastir_snprintf(weather->wx_gust, sizeof(weather->wx_gust), "%03d", (int)(computed_gust + 0.5)); // Cheater's way of rounding } } } /***********************************************************/ /* clear other wx data */ /***********************************************************/ void clear_local_wx_data(void) { memset(wx_dew_point,0,sizeof(wx_dew_point)); wx_dew_point_on = 0; memset(wx_high_wind,0,sizeof(wx_high_wind)); wx_high_wind_on = 0; memset(wx_wind_chill,0,sizeof(wx_wind_chill)); wx_wind_chill_on = 0; memset(wx_three_hour_baro,0,sizeof(wx_three_hour_baro)); wx_three_hour_baro_on = 0; memset(wx_hi_temp,0,sizeof(wx_hi_temp)); wx_hi_temp_on = 0; memset(wx_low_temp,0,sizeof(wx_low_temp)); wx_low_temp_on = 0; memset(wx_heat_index,0,sizeof(wx_heat_index)); wx_heat_index_on = 0; } /***************************************************/ /* Check last WX data received - clear data if old */ /***************************************************/ void wx_last_data_check(void) { DataRow *p_station; p_station = NULL; if (search_station_name(&p_station,my_callsign,1)) { if (p_station->weather_data != NULL) if (p_station->weather_data->wx_speed_sec_time+360 < sec_now()) if (p_station->weather_data->wx_gust[0] != 0) xastir_snprintf(p_station->weather_data->wx_gust, sizeof(p_station->weather_data->wx_gust), "%03d", 0); } } //********************************************************************* // Decode Peet Brothers Ultimeter 2000 weather data (Data logging mode) // // This function is called from db.c:data_add() only. Used for // decoding incoming packets, not for our own weather station data. // // The Ultimeter 2000 can be in any of three modes, Data Logging Mode, // Packet Mode, or Complete Record Mode. This routine handles only // the Data Logging Mode. //********************************************************************* void decode_U2000_L(int from, unsigned char *data, WeatherRow *weather) { time_t last_speed_time; float last_speed; float computed_gust; char temp_data1[10]; char *temp_conv; last_speed = 0.0; last_speed_time = 0; computed_gust = 0.0; if (debug_level & 1) { fprintf(stderr,"APRS WX3 Peet Bros U-2k (data logging mode): |%s|\n", data); } weather->wx_type = WX_TYPE; xastir_snprintf(weather->wx_station, sizeof(weather->wx_station), "U2k"); /* get last gust speed */ if (strlen(weather->wx_gust) > 0 && !from) { /* get last speed */ last_speed = (float)atof(weather->wx_gust); last_speed_time = weather->wx_speed_sec_time; } // 006B 00 58 // 00A4 00 46 01FF 380E 2755 02C1 03E8 ---- 0052 04D7 0001 007BM // ^ ^ ^ ^ ^ ^ ^ // 0 6 8 12 16 24 40 /* wind speed */ if (data[0] != '-') // '-' signifies invalid data { substr(temp_data1,(char *)data,4); xastir_snprintf(weather->wx_speed, sizeof(weather->wx_speed), "%03.0f", 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137); if (from) { weather->wx_speed_sec_time = sec_now(); } else { /* local station */ computed_gust = compute_gust((float)atof(weather->wx_speed), last_speed, &last_speed_time); weather->wx_speed_sec_time = sec_now(); xastir_snprintf(weather->wx_gust, sizeof(weather->wx_gust), "%03d", (int)(0.5 + computed_gust)); // Cheater's way of rounding } } else { if (!from) { weather->wx_speed[0] = 0; } } /* wind direction */ // // Note that the first two digits here may be 00, or may be FF // if a direction calibration has been entered. We should zero // them. // if (data[4] != '-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+4),4); temp_data1[0] = '0'; temp_data1[1] = '0'; xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "%03.0f", ((strtol(temp_data1,&temp_conv,16)/256.0)*360.0)); } else { xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "000"); if (!from) { weather->wx_course[0]=0; } } /* outdoor temp */ if (data[8] != '-') // '-' signifies invalid data { int temp4; substr(temp_data1,(char *)(data+8),4); temp4 = (int)strtol(temp_data1,&temp_conv,16); if (temp_data1[0] > '7') // Negative value, convert { temp4 = (temp4 & (temp4-0x7FFF)) - 0x8000; } xastir_snprintf(weather->wx_temp, sizeof(weather->wx_temp), "%03d", (int)((float)((temp4<<16)/65536)/10.0)); } else { if (!from) { weather->wx_temp[0]=0; } } /* rain total long term */ if (data[12] != '-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+12),4); xastir_snprintf(weather->wx_rain_total, sizeof(weather->wx_rain_total), "%0.2f", strtol(temp_data1,&temp_conv,16)/100.0); if (!from) { /* local station */ compute_rain((float)atof(weather->wx_rain_total)); /*last hour rain */ xastir_snprintf(weather->wx_rain, sizeof(weather->wx_rain), "%0.2f", rain_minute_total); /*last 24 hour rain */ xastir_snprintf(weather->wx_prec_24, sizeof(weather->wx_prec_24), "%0.2f", rain_24); /* rain since midnight */ xastir_snprintf(weather->wx_prec_00, sizeof(weather->wx_prec_00), "%0.2f", rain_00); } } else { if (!from) { weather->wx_rain_total[0]=0; } } /* baro */ if (data[16] != '-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+16),4); xastir_snprintf(weather->wx_baro, sizeof(weather->wx_baro), "%0.1f", strtol(temp_data1,&temp_conv,16)/10.0); } else { if (!from) { weather->wx_baro[0]=0; } } /* outdoor humidity */ if (data[24] != '-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+24),4); xastir_snprintf(weather->wx_hum, sizeof(weather->wx_hum), "%03.0f", (strtol(temp_data1,&temp_conv,16)/10.0)); } else { if (!from) { weather->wx_hum[0]=0; } } /* todays rain total */ if (data[40] != '-') // '-' signifies invalid data { if (from) { substr(temp_data1,(char *)(data+40),4); xastir_snprintf(weather->wx_prec_00, sizeof(weather->wx_prec_00), "%0.2f", strtol(temp_data1,&temp_conv,16)/100.0); } } else { if (!from) { weather->wx_prec_00[0] = 0; } } } //******************************************************************** // Decode Peet Brothers Ultimeter 2000 weather data (Packet mode) // // This function is called from db.c:data_add() only. Used for // decoding incoming packets, not for our own weather station data. // // The Ultimeter 2000 can be in any of three modes, Data Logging Mode, // Packet Mode, or Complete Record Mode. This routine handles only // the Packet Mode. //******************************************************************** void decode_U2000_P(int from, unsigned char *data, WeatherRow *weather) { time_t last_speed_time; float last_speed; float computed_gust; char temp_data1[10]; char *temp_conv; int len; last_speed = 0.0; last_speed_time = 0; computed_gust = 0.0; len = (int)strlen((char *)data); if (debug_level & 1) { fprintf(stderr,"APRS WX5 Peet Bros U-2k Packet (Packet mode): |%s|\n",data); } weather->wx_type = WX_TYPE; xastir_snprintf(weather->wx_station, sizeof(weather->wx_station), "U2k"); /* get last gust speed */ if (strlen(weather->wx_gust) > 0 && !from) { /* get last speed */ last_speed = (float)atof(weather->wx_gust); last_speed_time = weather->wx_speed_sec_time; } // $ULTW 0031 00 37 02CE 0069 ---- 0000 86A0 0001 ---- 011901CC 0000 0005 // ^ ^ ^ ^ ^ ^ ^ ^ // 0 6 8 12 16 32 44 48 /* wind speed peak over last 5 min */ if (data[0] != '-') // '-' signifies invalid data { substr(temp_data1,(char *)data,4); if (from) { xastir_snprintf(weather->wx_gust, sizeof(weather->wx_gust), "%03.0f", 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137); /* this may be the only wind data */ xastir_snprintf(weather->wx_speed, sizeof(weather->wx_speed), "%03.0f", 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137); } else { /* local station and may be the only wind data */ if (len < 51) { xastir_snprintf(weather->wx_speed, sizeof(weather->wx_speed), "%03.0f", 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137); computed_gust = compute_gust((float)atof(weather->wx_speed), last_speed, &last_speed_time); weather->wx_speed_sec_time = sec_now(); xastir_snprintf(weather->wx_gust, sizeof(weather->wx_gust), "%03d", (int)(0.5 + computed_gust)); } } } else { if (!from) { weather->wx_gust[0] = 0; } } /* wind direction */ // // Note that the first two digits here may be 00, or may be FF // if a direction calibration has been entered. We should zero // them. // if (data[4] != '-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+4),4); temp_data1[0] = '0'; temp_data1[1] = '0'; xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "%03.0f", (strtol(temp_data1,&temp_conv,16)/256.0)*360.0); } else { xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "000"); if (!from) { weather->wx_course[0] = 0; } } /* outdoor temp */ if (data[8] != '-') // '-' signifies invalid data { int temp4; substr(temp_data1,(char *)(data+8),4); temp4 = (int)strtol(temp_data1,&temp_conv,16); if (temp_data1[0] > '7') // Negative value, convert { temp4 = (temp4 & (temp4-0x7FFF)) - 0x8000; } xastir_snprintf(weather->wx_temp, sizeof(weather->wx_temp), "%03d", (int)((float)((temp4<<16)/65536)/10.0)); } else { if (!from) { weather->wx_temp[0] = 0; } } /* todays rain total (on some units) */ if ((data[44]) != '-') // '-' signifies invalid data { if (from) { substr(temp_data1,(char *)(data+44),4); xastir_snprintf(weather->wx_prec_00, sizeof(weather->wx_prec_00), "%0.2f", strtol(temp_data1,&temp_conv,16)/100.0); } } else { if (!from) { weather->wx_prec_00[0] = 0; } } /* rain total long term */ if (data[12] != '-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+12),4); xastir_snprintf(weather->wx_rain_total, sizeof(weather->wx_rain_total), "%0.2f", strtol(temp_data1,&temp_conv,16)/100.0); if (!from) { /* local station */ compute_rain((float)atof(weather->wx_rain_total)); /*last hour rain */ xastir_snprintf(weather->wx_rain, sizeof(weather->wx_rain), "%0.2f", rain_minute_total); /*last 24 hour rain */ xastir_snprintf(weather->wx_prec_24, sizeof(weather->wx_prec_24), "%0.2f", rain_24); /* rain since midnight */ xastir_snprintf(weather->wx_prec_00, sizeof(weather->wx_prec_00), "%0.2f", rain_00); } } else { if (!from) { weather->wx_rain_total[0] = 0; } } /* baro */ if (data[16] != '-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+16),4); xastir_snprintf(weather->wx_baro, sizeof(weather->wx_baro), "%0.1f", strtol(temp_data1,&temp_conv,16)/10.0); } else { if (!from) { weather->wx_baro[0] = 0; } } /* outdoor humidity */ if (data[32] != '-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+32),4); xastir_snprintf(weather->wx_hum, sizeof(weather->wx_hum), "%03.0f", strtol(temp_data1,&temp_conv,16)/10.0); } else { if (!from) { weather->wx_hum[0] = 0; } } /* 1 min wind speed avg */ if (len > 48 && (data[48]) != '-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+48),4); xastir_snprintf(weather->wx_speed, sizeof(weather->wx_speed), "%03.0f", 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137); if (from) { weather->wx_speed_sec_time = sec_now(); } else { /* local station */ computed_gust = compute_gust((float)atof(weather->wx_speed), last_speed, &last_speed_time); weather->wx_speed_sec_time = sec_now(); xastir_snprintf(weather->wx_gust, sizeof(weather->wx_gust), "%03d", (int)(0.5 + computed_gust)); } } else { if (!from) { if (len > 48) { weather->wx_speed[0] = 0; } } } } //***************************************************************** // Decode Peet Brothers Ultimeter-II weather data // // This function is called from db.c:data_add() only. Used for // decoding incoming packets, not for our own weather station data. //***************************************************************** void decode_Peet_Bros(int from, unsigned char *data, WeatherRow *weather, int type) { time_t last_speed_time; float last_speed; float computed_gust; char temp_data1[10]; char *temp_conv; last_speed = 0.0; computed_gust = 0.0; last_speed_time = 0; if (debug_level & 1) { fprintf(stderr,"APRS WX4 Peet Bros U-II: |%s|\n",data); } weather->wx_type = WX_TYPE; xastir_snprintf(weather->wx_station, sizeof(weather->wx_station), "UII"); // '*' = MPH // '#' = km/h // // # 5 0B 75 0082 0082 // * 7 00 76 0000 0000 // ^ ^ ^ ^ // rain [1/100 inch ?] // outdoor temp // wind speed [mph / km/h] // wind dir /* wind direction */ // // 0x00 is N // 0x04 is E // 0x08 is S // 0x0C is W // substr(temp_data1,(char *)data,1); xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "%03.0f", (strtol(temp_data1,&temp_conv,16)/16.0)*360.0); /* get last gust speed */ if (strlen(weather->wx_gust) > 0 && !from) { /* get last speed */ last_speed = (float)atof(weather->wx_gust); last_speed_time = weather->wx_speed_sec_time; } /* wind speed */ substr(temp_data1,(char *)(data+1),2); if (type == APRS_WX4) // '#' speed in km/h, convert to mph { xastir_snprintf(weather->wx_speed, sizeof(weather->wx_speed), "%03d", (int)(0.5 + (float)(strtol(temp_data1,&temp_conv,16)*0.62137))); } else // type == APRS_WX6, '*' speed in mph { xastir_snprintf(weather->wx_speed, sizeof(weather->wx_speed), "%03.0f", 0.5 + (1.0 * strtol(temp_data1,&temp_conv,16)) ); } if (from) { weather->wx_speed_sec_time = sec_now(); } else { /* local station */ computed_gust = compute_gust((float)atof(weather->wx_speed), last_speed, &last_speed_time); weather->wx_speed_sec_time = sec_now(); xastir_snprintf(weather->wx_gust, sizeof(weather->wx_gust), "%03d", (int)(0.5 + computed_gust)); } /* outdoor temp */ if (data[3] != '-') // '-' signifies invalid data { int temp4; substr(temp_data1,(char *)(data+3),2); temp4 = (int)strtol(temp_data1,&temp_conv,16); if (temp_data1[0] > '7') // Negative value, convert { temp4 = (temp4 & (temp4-0x7FFF)) - 0x8000; } xastir_snprintf(weather->wx_temp, sizeof(weather->wx_temp), "%03.0f", (float)(temp4-56) ); } else { if (!from) { weather->wx_temp[0] = 0; } } // Rain divided by 100 for readings in hundredth of an inch if (data[5] != '-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+5),4); xastir_snprintf(weather->wx_rain_total, sizeof(weather->wx_rain_total), "%0.2f", strtol(temp_data1,&temp_conv,16)/100.0); if (!from) { /* local station */ compute_rain((float)atof(weather->wx_rain_total)); /*last hour rain */ xastir_snprintf(weather->wx_rain, sizeof(weather->wx_rain), "%0.2f", rain_minute_total); /*last 24 hour rain */ xastir_snprintf(weather->wx_prec_24, sizeof(weather->wx_prec_24), "%0.2f", rain_24); /* rain since midnight */ xastir_snprintf(weather->wx_prec_00, sizeof(weather->wx_prec_00), "%0.2f", rain_00); } } else { if (!from) { weather->wx_rain_total[0] = 0; } } } /**************************************************/ /* RSW num convert. For Radio Shack WX-200, */ /* converts two decimal nibbles into integer */ /* number. */ /**************************************************/ int rswnc(unsigned char c) { return( (int)( (c>>4) & 0x0f) * 10 + (int)(c&0x0f) ); } //********************************************************* // wx fill data field // from: 0=local station, 1=regular decode (other stations) // type: type of WX packet // data: the packet of WX data // fill: the station data to fill // // This function is called only by wx.c:wx_decode() // // It is always called with a first parameter of 0, so we // use this only for our own serially-connected or network // connected weather station, not for decoding other // people's weather packets. //********************************************************* // // Note that the length of "data" can be up to MAX_DEVICE_BUFFER, // which is currently set to 4096. // void wx_fill_data(int from, int type, unsigned char *data, DataRow *fill) { time_t last_speed_time; float last_speed; float computed_gust; int temp1; int temp2; int temp3; float temp_temp; char temp[MAX_DEVICE_BUFFER+1]; char temp_data1[10]; char *temp_conv; int len; int t2; int hidx_temp; int rh2; int hi_hum; int heat_index; WeatherRow *weather; float tmp1,tmp2,tmp3,tmp4,tmp5,tmp6,tmp9,tmp10,tmp11,tmp12,tmp13,tmp14,tmp15,tmp16,tmp17,tmp18,tmp19; int tmp7,tmp8; int dallas_type = 19; last_speed=0.0; computed_gust=0.0; last_speed_time=0; len=(int)strlen((char*)data); weather = fill->weather_data; // should always be defined switch (type) { //WE7U ///////////////////////////////////// // Dallas One-Wire Weather Station // ///////////////////////////////////// // KB1MTS - Added values for T13 thru T19 for humidity and barometer, // however only current values (not min or max) are used. case (DALLAS_ONE_WIRE): if (debug_level & 1) { fprintf(stderr,"APRS WX Dallas One-Wire %s:<%s>\n",fill->call_sign,data); } weather->wx_type=WX_TYPE; xastir_snprintf(weather->wx_station, sizeof(weather->wx_station), "OWW"); if (19 == sscanf((const char *)data, "%f %f %f %f %f %f %d %d %f %f %f %f %f %f %f %f %f %f %f", &tmp1, &tmp2, &tmp3, &tmp4, &tmp5, &tmp6, &tmp7, &tmp8, &tmp9, &tmp10, &tmp11, &tmp12, &tmp13, &tmp14, &tmp15, &tmp16, &tmp17, &tmp18, &tmp19)) { dallas_type = 19; } else if (12 == sscanf((const char *)data, "%f %f %f %f %f %f %d %d %f %f %f %f", &tmp1, &tmp2, &tmp3, &tmp4, &tmp5, &tmp6, &tmp7, &tmp8, &tmp9, &tmp10, &tmp11, &tmp12)) { dallas_type = 12; } else { fprintf(stderr,"wx_fill_data:sscanf parsing error\n"); } // The format of the data originates here: // http://weather.henriksens.net/ // tmp1: primary temp (C) // tmp2: temp max (C) // tmp3: temp min (C) // tmp4: anemometer (mps) // tmp5: anemometer gust (peak speed MS) // tmp6: anemometer speed max * 0.447040972 (max speed MS) // tmp7: vane bearing - 1 (current wind direction) // tmp8: vane mode (max dir) // tmp9: rain rate // tmp10: rain total today // tmp11: rain total week // tmp12: rain since month // tmp13: Current Humidity // tmp14: Max Humidity // tmp15: Min Humidity // tmp16: Current Barometer // tmp17: Max Barometer // tmp18: Min Barometer // tmp19: Barometer Rate // Temperature xastir_snprintf(weather->wx_temp, sizeof(weather->wx_temp), "%03d", (int)(tmp1 * 9.0 / 5.0 + 32.0 + 0.5)); //fprintf(stderr,"Read: %2.1f C, Storing: %s F\n",tmp1,weather->wx_temp); // Wind direction. Each vane increment equals 22.5 degrees. xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "%03d", (int)(tmp7 * 22.5 + 0.5)); // Check for course = 0. Change to 360. if (strncmp(weather->wx_course,"000",3) == 0) { xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "360"); } // Wind speed. We get it in meters per second, store it // in mph. tmp4 = tmp4 * 3600.0 / 1000.0; // kph tmp4 = tmp4 * 0.62137; // mph xastir_snprintf(weather->wx_speed, sizeof(weather->wx_speed), "%03d", (int)(tmp4 + 0.5)); if (dallas_type == 19) { // Humidity. This is received by percentage. xastir_snprintf(weather->wx_hum, sizeof(weather->wx_hum), "%2.1f", (double)(tmp13)); // Barometer. Sent in inHg xastir_snprintf(weather->wx_baro, sizeof(weather->wx_baro), "%4.4f", (float)(tmp16 * 33.864)); } // Rain: I don't have a rain gauge, and I couldn't tell from the // "OWW" docs exactly which of the four rain fields did what. If // someone can help me with that I'll add rain gauge code for the // Dallas One-Wire. break; //////////////////////////////// // Peet Brothers Ultimeter-II // //////////////////////////////// case (APRS_WX4): // '#', Wind speed in km/h case (APRS_WX6): // '*', Wind speed in mph // This one assumes 0.1" rain gauge. Must correct in software if // any other type is used. if (debug_level & 1) { fprintf(stderr,"APRS WX4 Peet Bros U-II %s:<%s>\n",fill->call_sign,data); } weather->wx_type=WX_TYPE; xastir_snprintf(weather->wx_station, sizeof(weather->wx_station), "UII"); /* wind direction */ // // 0x00 is N // 0x04 is E // 0x08 is S // 0x0C is W // substr(temp_data1,(char *)(data+1),1); xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "%03.0f", (strtol(temp_data1,&temp_conv,16)/16.0)*360.0); // Check for course == 0. Change to 360. if (strncmp(weather->wx_course,"000",3) == 0) { xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "360"); } /* get last gust speed */ if (strlen(weather->wx_gust) > 0 && !from) // From local station { /* get last speed */ last_speed=(float)atof(weather->wx_gust); last_speed_time=weather->wx_speed_sec_time; } /* wind speed */ substr(temp_data1,(char *)(data+2),2); if (type == APRS_WX4) // '#', Data is in km/h, convert to mph { xastir_snprintf(weather->wx_speed, sizeof(weather->wx_speed), "%03d", (int)(0.5 + (float)(strtol(temp_data1,&temp_conv,16)*0.62137))); } else // APRS_WX6 or '*', Data is in MPH { xastir_snprintf(weather->wx_speed, sizeof(weather->wx_speed), "%03.0f", 0.5 + (strtol(temp_data1,&temp_conv,16)*1.0) ); } if (from) // From remote station { weather->wx_speed_sec_time = sec_now(); } else { /* local station */ computed_gust = compute_gust((float)atof(weather->wx_speed), last_speed, &last_speed_time); weather->wx_speed_sec_time = sec_now(); xastir_snprintf(weather->wx_gust, sizeof(weather->wx_gust), "%03d", (int)(0.5 + computed_gust)); } /* outdoor temp */ if (data[4]!='-') // '-' signifies invalid data { int temp4; substr(temp_data1,(char *)(data+4),2); temp4 = (int)strtol(temp_data1,&temp_conv,16); if (temp_data1[0] > '7') // Negative value, convert { temp4 = (temp4 & (temp4-0x7FFF)) - 0x8000; } xastir_snprintf(weather->wx_temp, sizeof(weather->wx_temp), "%03.0f", (float)(temp4-56) ); } else { if (!from) // From local station { weather->wx_temp[0]=0; } } /* rain div by 100 for readings 0.1 inch */ // What? Shouldn't this be /10? if (data[6]!='-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+6),4); if (!from) // From local station { switch (WX_rain_gauge_type) { case 1: // 0.1" rain gauge xastir_snprintf(weather->wx_rain_total, sizeof(weather->wx_rain_total), "%0.2f", strtol(temp_data1,&temp_conv,16)*10.0); break; case 3: // 0.1mm rain gauge xastir_snprintf(weather->wx_rain_total, sizeof(weather->wx_rain_total), "%0.2f", strtol(temp_data1,&temp_conv,16)/2.54); break; case 2: // 0.01" rain gauge case 0: // No conversion default: xastir_snprintf(weather->wx_rain_total, sizeof(weather->wx_rain_total), "%0.2f", strtol(temp_data1,&temp_conv,16)*1.0); break; } /* local station */ compute_rain((float)atof(weather->wx_rain_total)); weather->wx_compute_rain_rates=1; /*last hour rain */ xastir_snprintf(weather->wx_rain, sizeof(weather->wx_rain), "%0.2f", rain_minute_total); /*last 24 hour rain */ xastir_snprintf(weather->wx_prec_24, sizeof(weather->wx_prec_24), "%0.2f", rain_24); /* rain since midnight */ xastir_snprintf(weather->wx_prec_00, sizeof(weather->wx_prec_00), "%0.2f", rain_00); } } else { if (!from) // From local station { weather->wx_rain_total[0]=0; } } break; /////////////////////////////////////////////////////// // Peet Brothers Ultimeter 2000 in data logging mode // /////////////////////////////////////////////////////// case (APRS_WX3): if (debug_level & 1) { fprintf(stderr,"APRS WX3 Peet Bros U-2k (data logging mode) %s:<%s>\n",fill->call_sign,data+2); } weather->wx_type=WX_TYPE; xastir_snprintf(weather->wx_station, sizeof(weather->wx_station), "U2k"); /* get last gust speed */ if (strlen(weather->wx_gust) > 0 && !from) // From local station { /* get last speed */ last_speed=(float)atof(weather->wx_gust); last_speed_time=weather->wx_speed_sec_time; } /* wind speed */ if (data[2]!='-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+2),4); xastir_snprintf(weather->wx_speed, sizeof(weather->wx_speed), "%03.0f", 0.5 + ((strtol(temp_data1,&temp_conv,16) /10.0)*0.62137)); if (from) // From remote station { weather->wx_speed_sec_time = sec_now(); } else { /* local station */ computed_gust = compute_gust((float)atof(weather->wx_speed), last_speed, &last_speed_time); weather->wx_speed_sec_time = sec_now(); xastir_snprintf(weather->wx_gust, sizeof(weather->wx_gust), "%03d", (int)(0.5 + computed_gust)); } } else { if (!from) // From local station { weather->wx_speed[0]=0; } } /* wind direction */ // // Note that the first two digits here may be 00, or may // be FF if a direction calibration has been entered. // We should zero them. // if (data[6]!='-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+6),4); // Zero out the first two bytes temp_data1[0] = '0'; temp_data1[1] = '0'; xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "%03.0f", (strtol(temp_data1,&temp_conv,16)/256.0)*360.0); // Check for course = 0. Change to 360. if (strncmp(weather->wx_course,"000",3) == 0) { xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "360"); } } else { xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "000"); if (!from) // From local station { weather->wx_course[0]=0; } } /* outdoor temp */ if (data[10]!='-') // '-' signifies invalid data { int temp4; substr(temp_data1,(char *)(data+10),4); temp4 = (int)strtol(temp_data1,&temp_conv,16); if (temp_data1[0] > '7') // Negative value, convert { temp4 = (temp4 & (temp4-0x7FFF)) - 0x8000; } xastir_snprintf(weather->wx_temp, sizeof(weather->wx_temp), "%03d", (int)((float)((temp4<<16)/65536)/10.0)); } else { if (!from) // From local station { weather->wx_temp[0]=0; } } /* rain total long term */ if (data[14]!='-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+14),4); if (!from) // From local station { switch (WX_rain_gauge_type) { case 1: // 0.1" rain gauge xastir_snprintf(weather->wx_rain_total, sizeof(weather->wx_rain_total), "%0.2f", strtol(temp_data1,&temp_conv,16)*10.0); break; case 3: // 0.1mm rain gauge xastir_snprintf(weather->wx_rain_total, sizeof(weather->wx_rain_total), "%0.2f", strtol(temp_data1,&temp_conv,16)/2.54); break; case 2: // 0.01" rain gauge case 0: // No conversion default: xastir_snprintf(weather->wx_rain_total, sizeof(weather->wx_rain_total), "%0.2f", strtol(temp_data1,&temp_conv,16)*1.0); break; } /* local station */ compute_rain((float)atof(weather->wx_rain_total)); weather->wx_compute_rain_rates=1; /*last hour rain */ xastir_snprintf(weather->wx_rain, sizeof(weather->wx_rain), "%0.2f", rain_minute_total); /*last 24 hour rain */ xastir_snprintf(weather->wx_prec_24, sizeof(weather->wx_prec_24), "%0.2f", rain_24); /* rain since midnight */ xastir_snprintf(weather->wx_prec_00, sizeof(weather->wx_prec_00), "%0.2f", rain_00); } } else { if (!from) // From local station { weather->wx_rain_total[0]=0; } } /* baro */ if (data[18]!='-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+18),4); xastir_snprintf(weather->wx_baro, sizeof(weather->wx_baro), "%0.1f", strtol(temp_data1,&temp_conv,16)/10.0); } /* outdoor humidity */ if (data[26]!='-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+26),4); xastir_snprintf(weather->wx_hum, sizeof(weather->wx_hum), "%03.0f", strtol(temp_data1,&temp_conv,16)/10.0); } else { if (!from) // From local station { weather->wx_hum[0]=0; } } // Isn't this replaced by the above switch-case? // No, I don't think so. We can get these packets over // RF as well. /* todays rain total */ if (strlen((const char *)data) > 45) { if (data[42]!='-') // '-' signifies invalid data { if (from) // From remote station { substr(temp_data1,(char *)(data+42),4); xastir_snprintf(weather->wx_prec_00, sizeof(weather->wx_prec_00), "%0.2f", strtol(temp_data1,&temp_conv,16)/100.0); } } else { if (!from) // From local station { weather->wx_prec_00[0]=0; } } } break; ///////////////////////////////////////////////// // Peet Brothers Ultimeter 2000 in packet mode // ///////////////////////////////////////////////// case(APRS_WX5): if (debug_level & 1) { fprintf(stderr,"APRS WX5 Peet Bros U-2k Packet (Packet mode) %s:<%s>\n",fill->call_sign,data); } weather->wx_type=WX_TYPE; xastir_snprintf(weather->wx_station, sizeof(weather->wx_station), "U2k"); /* get last gust speed */ if (strlen(weather->wx_gust) > 0 && !from) // From local station { /* get last speed */ last_speed=(float)atof(weather->wx_gust); last_speed_time=weather->wx_speed_sec_time; } /* wind speed peak over last 5 min */ if (data[5]!='-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+5),4); if (from) // From remote station { xastir_snprintf(weather->wx_gust, sizeof(weather->wx_gust), "%03.0f", 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137); /* this may be the only wind data */ xastir_snprintf(weather->wx_speed, sizeof(weather->wx_speed), "%03.0f", 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137); } else { /* local station and may be the only wind data */ if (len<56) { xastir_snprintf(weather->wx_speed, sizeof(weather->wx_speed), "%03.0f", 0.5 + ( strtol(temp_data1,&temp_conv,16)/10.0)*0.62137); computed_gust = compute_gust((float)atof(weather->wx_speed), last_speed, &last_speed_time); weather->wx_speed_sec_time = sec_now(); xastir_snprintf(weather->wx_gust, sizeof(weather->wx_gust), "%03d", (int)(0.5 + computed_gust)); } } } else { if (!from) // From local station { weather->wx_gust[0]=0; } } /* wind direction */ // // Note that the first two digits here may be 00, or may // be FF if a direction calibration has been entered. // We should zero them. // if (data[9]!='-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+9),4); temp_data1[0] = '0'; temp_data1[1] = '0'; xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "%03.0f", (strtol(temp_data1,&temp_conv,16)/256.0)*360.0); // Check for course = 0. Change to 360. if (strncmp(weather->wx_course,"000",3) == 0) { xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "360"); } } else { xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "000"); if (!from) // From local station { weather->wx_course[0]=0; } } /* outdoor temp */ if (data[13]!='-') // '-' signifies invalid data { int temp4; substr(temp_data1,(char *)(data+13),4); temp4 = (int)strtol(temp_data1,&temp_conv,16); if (temp_data1[0] > '7') // Negative value, convert { temp4 = (temp4 & (temp4-0x7FFF)) - 0x8000; } xastir_snprintf(weather->wx_temp, sizeof(weather->wx_temp), "%03d", (int)((float)((temp4<<16)/65536)/10.0)); } else { if (!from) // From local station { weather->wx_temp[0]=0; } } /* todays rain total (on some units) */ if (data[49]!='-') // '-' signifies invalid data { if (from) // From remote station { substr(temp_data1,(char *)(data+49),4); xastir_snprintf(weather->wx_prec_00, sizeof(weather->wx_prec_00), "%0.2f", strtol(temp_data1,&temp_conv,16)/100.0); } } else { if (!from) // From local station { weather->wx_prec_00[0]=0; } } /* rain total long term */ if (data[17]!='-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+17),4); if (!from) // From local station { switch (WX_rain_gauge_type) { case 1: // 0.1" rain gauge xastir_snprintf(weather->wx_rain_total, sizeof(weather->wx_rain_total), "%0.2f", strtol(temp_data1,&temp_conv,16)*10.0); break; case 3: // 0.1mm rain gauge xastir_snprintf(weather->wx_rain_total, sizeof(weather->wx_rain_total), "%0.2f", strtol(temp_data1,&temp_conv,16)/2.54); break; case 2: // 0.01" rain gauge case 0: // No conversion default: xastir_snprintf(weather->wx_rain_total, sizeof(weather->wx_rain_total), "%0.2f", strtol(temp_data1,&temp_conv,16)*1.0); break; } /* local station */ compute_rain((float)atof(weather->wx_rain_total)); weather->wx_compute_rain_rates=1; /*last hour rain */ xastir_snprintf(weather->wx_rain, sizeof(weather->wx_rain), "%0.2f", rain_minute_total); /*last 24 hour rain */ xastir_snprintf(weather->wx_prec_24, sizeof(weather->wx_prec_24), "%0.2f", rain_24); /* rain since midnight */ xastir_snprintf(weather->wx_prec_00, sizeof(weather->wx_prec_00), "%0.2f", rain_00); } } else { if (!from) // From local station { weather->wx_rain_total[0]=0; } } /* baro */ if (data[21]!='-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+21),4); xastir_snprintf(weather->wx_baro, sizeof(weather->wx_baro), "%0.1f", strtol(temp_data1, &temp_conv, 16)/10.0); } else { if (!from) // From local station { weather->wx_baro[0]=0; } } /* outdoor humidity */ if (data[37]!='-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+37),4); xastir_snprintf(weather->wx_hum, sizeof(weather->wx_hum), "%03.0f", strtol(temp_data1,&temp_conv,16)/10.0); } else { if (!from) // From local station { weather->wx_hum[0]=0; } } /* 1 min wind speed avg */ if (len>53 && (data[53]) != '-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+53),4); xastir_snprintf(weather->wx_speed, sizeof(weather->wx_speed), "%03.0f", 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137); if (from) // From remote station { weather->wx_speed_sec_time = sec_now(); } else { /* local station */ computed_gust = compute_gust((float)atof(weather->wx_speed), last_speed, &last_speed_time); weather->wx_speed_sec_time = sec_now(); xastir_snprintf(weather->wx_gust, sizeof(weather->wx_gust), "%03d", (int)(0.5 + computed_gust)); } } else { if (!from) // From local station { if (len>53) { weather->wx_speed[0]=0; } } } break; ////////////////////////////////////////////////////////// // Peet Brothers Ultimeter 2000 in complete record mode // ////////////////////////////////////////////////////////// // // In this mode most fields are 4-bytes two's complement. A // few fields are 2-bytes wide. // // // case(PEET_COMPLETE): if (debug_level & 1) { fprintf(stderr,"Peet Bros U-2k Packet (Complete Record Mode) %s:<%s>\n",fill->call_sign,data); } if (!from) // From local station { int done_with_wx_speed = 0; /* decode only for local station */ weather->wx_type=WX_TYPE; xastir_snprintf(weather->wx_station, sizeof(weather->wx_station), "U2k"); if (data[12]!='-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+12),4); xastir_snprintf(weather->wx_gust, sizeof(weather->wx_gust), "%03.0f", 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137); } else { weather->wx_gust[0]=0; } // Check whether field 115 is available at bytes 452 // through 455. If so, that's the one-minute wind // speed average in 0.1kph, which matches the APRS // spec except for the units (which should be MPH). if ( (len >= 456) && (data[452] != '-') ) // '-' signifies invalid data { substr(temp_data1,(char *)(data+452),4); xastir_snprintf(weather->wx_speed, sizeof(weather->wx_speed), "%03.0f", 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137); done_with_wx_speed++; } // Some Peet units don't have that particular wind // speed field, so snag what wind speed we can from // the other wind speed fields, which don't quite // match the APRS spec as they're instantaneous // values, not one-minute sustained speeds. // KG9AE // Peet Bros CR mode wind values should be selected based on which are highest. /* Wind Speed fields 1, 34, and 71. Wind direction fields 2, 35, 72. */ if (data[4] !='-') // '-' signifies invalid data { substr(temp_data1, (char *)data+4, 4); temp1 = 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137; } else { temp1=0; } if (data[136] !='-') // '-' signifies invalid data { substr(temp_data1, (char *)data+136, 4); temp2 = 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137; } else { temp2=0; } if (data[284] !='-') // '-' signifies invalid data { substr(temp_data1, (char *)data+284, 4); temp3 = 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137; } else { temp3=0; } // fprintf(stderr,"WIND: wind1 %d, wind2 %d, wind3 %d\n", temp1, temp2, temp3); // Select wind speed and direction based on which // wind speed is the highest. Ugh, surely there's a // way to make this pretty. A function might be // better. if ( temp1 >= temp2 && temp1 >= temp3 ) { // fprintf(stderr,"WIND: ***\n"); /* wind speed */ if (!done_with_wx_speed) { substr(temp_data1,(char *)(data+4),4); xastir_snprintf(weather->wx_speed, sizeof(weather->wx_speed), "%03.0f", 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137); } /* wind direction */ // // Note that the first two digits here may be // 00, or may be FF if a direction calibration // has been entered. We should zero them. // if (data[8]!='-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+8),4); temp_data1[0] = '0'; temp_data1[1] = '0'; xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "%03.0f", (strtol(temp_data1,&temp_conv,16)/256.0)*360.0); // Check for course = 0. Change to 360. if (strncmp(weather->wx_course,"000",3) == 0) { xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "360"); } } else { xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "000"); weather->wx_course[0]=0; } } else if ( temp2 >= temp1 && temp2 >= temp3 ) { // fprintf(stderr,"WIND: ***\n"); if (!done_with_wx_speed) { /* wind speed */ substr(temp_data1,(char *)(data+136),4); xastir_snprintf(weather->wx_speed, sizeof(weather->wx_speed), "%03.0f", 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137); } /* wind direction */ // // Note that the first two digits here may be // 00, or may be FF if a direction calibration // has been entered. We should zero them. // if (data[140]!='-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+140),4); temp_data1[0] = '0'; temp_data1[1] = '0'; xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "%03.0f", (strtol(temp_data1,&temp_conv,16)/256.0)*360.0); // Check for course = 0. Change to 360. if (strncmp(weather->wx_course,"000",3) == 0) { xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "360"); } } else { xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "000"); weather->wx_course[0]=0; } } else if ( temp3 >= temp2 && temp3 >= temp1 ) { // fprintf(stderr,"WIND: ***\n"); if (!done_with_wx_speed) { /* wind speed */ substr(temp_data1,(char *)(data+284),4); xastir_snprintf(weather->wx_speed, sizeof(weather->wx_speed), "%03.0f", 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137); } /* wind direction */ // // Note that the first two digits here may be // 00, or may be FF if a direction calibration // has been entered. We should zero them. // if (data[288]!='-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+288),4); temp_data1[0] = '0'; temp_data1[1] = '0'; xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "%03.0f", (strtol(temp_data1,&temp_conv,16)/256.0)*360.0); // Check for course = 0. Change to 360. if (strncmp(weather->wx_course,"000",3) == 0) { xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "360"); } } else { xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "000"); weather->wx_course[0]=0; } } else /* Or default to the first value */ { // fprintf(stderr,"WIND: DEFAULTING!\n"); if (!done_with_wx_speed) { /* wind speed */ substr(temp_data1,(char *)(data+4),4); xastir_snprintf(weather->wx_speed, sizeof(weather->wx_speed), "%03.0f", 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137); } /* wind direction */ // // Note that the first two digits here may be // 00, or may be FF if a direction calibration // has been entered. We should zero them. // if (data[8]!='-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+8),4); temp_data1[0] = '0'; temp_data1[1] = '0'; xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "%03.0f", (strtol(temp_data1,&temp_conv,16)/256.0)*360.0); // Check for course = 0. Change to 360. if (strncmp(weather->wx_course,"000",3) == 0) { xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "360"); } } else { xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "000"); weather->wx_course[0]=0; } } /* outdoor temp */ if (data[24]!='-') // '-' signifies invalid data { int temp4; substr(temp_data1,(char *)(data+24),4); temp4 = (int)strtol(temp_data1,&temp_conv,16); if (temp_data1[0] > '7') // Negative value, convert { temp4 = (temp4 & (temp4-0x7FFF)) - 0x8000; } xastir_snprintf(weather->wx_temp, sizeof(weather->wx_temp), "%03d", (int)((float)((temp4<<16)/65536)/10.0)); } else { weather->wx_temp[0]=0; } // We don't want to parse this here because compute_rain() // calculates this for us from the accumulating long-term rain // total. If we were to do it here as well, we'll get conflicting // results. Since only some units put out today's rain total, we'll // just rely on our own calculations for it instead. It'll work // across more units. /* // todays rain total (on some units) if (data[28]!='-') { // '-' signifies invalid data substr(temp_data1,(char *)(data+28),4); switch (WX_rain_gauge_type) { case 1: // 0.1" rain gauge xastir_snprintf(weather->wx_prec_00, sizeof(weather->wx_prec_00), "%0.2f", (float)strtol(temp_data1,&temp_conv,16)/10.0); break; case 3: // 0.1mm rain gauge xastir_snprintf(weather->wx_prec_00, sizeof(weather->wx_prec_00), "%0.2f", (float)strtol(temp_data1,&temp_conv,16)/254.0); break; case 2: // 0.01" rain gauge case 0: // No conversion default: xastir_snprintf(weather->wx_prec_00, sizeof(weather->wx_prec_00), "%0.2f", (float)strtol(temp_data1,&temp_conv,16)/100.0); break; } } else weather->wx_prec_00[0]=0; */ /* rain total long term */ if ((char)data[432]!='-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+432),4); switch (WX_rain_gauge_type) { case 1: // 0.1" rain gauge xastir_snprintf(weather->wx_rain_total, sizeof(weather->wx_rain_total), "%0.2f", strtol(temp_data1,&temp_conv,16)*10.0); break; case 3: // 0.1mm rain gauge xastir_snprintf(weather->wx_rain_total, sizeof(weather->wx_rain_total), "%0.2f", strtol(temp_data1,&temp_conv,16)/2.54); break; case 2: // 0.01" rain gauge case 0: // No conversion default: xastir_snprintf(weather->wx_rain_total, sizeof(weather->wx_rain_total), "%0.2f", strtol(temp_data1,&temp_conv,16)*1.0); break; } /* Since local station only */ compute_rain((float)atof(weather->wx_rain_total)); weather->wx_compute_rain_rates=1; /*last hour rain */ xastir_snprintf(weather->wx_rain, sizeof(weather->wx_rain), "%0.2f", rain_minute_total); /*last 24 hour rain */ xastir_snprintf(weather->wx_prec_24, sizeof(weather->wx_prec_24), "%0.2f", rain_24); /* rain since midnight */ xastir_snprintf(weather->wx_prec_00, sizeof(weather->wx_prec_00), "%0.2f", rain_00); } else { weather->wx_rain_total[0]=0; } /* baro */ if (data[32]!='-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+32),4); xastir_snprintf(weather->wx_baro, sizeof(weather->wx_baro), "%0.1f", strtol(temp_data1,&temp_conv,16)/10.0); } else { weather->wx_baro[0]=0; } /* outdoor humidity */ if (data[52]!='-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+52),4); xastir_snprintf(weather->wx_hum, sizeof(weather->wx_hum), "%03.0f", strtol(temp_data1,&temp_conv,16)/10.0); } else { weather->wx_hum[0]=0; } /* dew point */ if (data[60]!='-') // '-' signifies invalid data { int temp4; substr(temp_data1,(char *)(data+60),4); temp4 = (int)strtol(temp_data1,&temp_conv,16); if (temp_data1[0] > '7') // Negative value, convert { temp4 = (temp4 & (temp4-0x7FFF)) - 0x8000; } xastir_snprintf(wx_dew_point, sizeof(wx_dew_point), "%03d", (int)((float)((temp4<<16)/65536)/10.0)); wx_dew_point_on = 1; } /*high winds for today*/ if (data[248]!='-') // '-' signifies invalid data { substr(temp_data1,(char *)(data+248),4); xastir_snprintf(wx_high_wind, sizeof(wx_high_wind), "%03.0f", 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137); wx_high_wind_on = 1; } /*wind chill */ if (data[20]!='-') // '-' signifies invalid data { int temp4; substr(temp_data1,(char *)(data+20),4); temp4 = (int)strtol(temp_data1,&temp_conv,16); if (temp_data1[0] > '7') // Negative value, convert { temp4 = (temp4 & (temp4-0x7FFF)) - 0x8000; } xastir_snprintf(wx_wind_chill, sizeof(wx_wind_chill), "%03d", (int)((float)((temp4<<16)/65536)/10.0)); wx_wind_chill_on = 1; } /*3-Hr Barometric Change */ if (data[36]!='-') // '-' signifies invalid data { int temp4; substr(temp_data1,(char *)(data+36),4); temp4 = (int)strtol(temp_data1,&temp_conv,16); if (temp_data1[0] > '7') // Negative value, convert { temp4 = (temp4 & (temp4-0x7FFF)) - 0x8000; } xastir_snprintf(wx_three_hour_baro, sizeof(wx_three_hour_baro), "%0.2f", // Old code // (float)((strtol(temp_data1,&temp_conv,16)<<16)/65536)/100.0/3.38639); // New code, fix by Matt Werner, kb0kqa: (float)((temp4<<16)/65536)/10.0); wx_three_hour_baro_on = 1; } /* High Temp for Today*/ if (data[276]!='-') // '-' signifies invalid data { int temp4; substr(temp_data1,(char *)(data+276),4); temp4 = (int)strtol(temp_data1,&temp_conv,16); if (temp_data1[0] > '7') // Negative value, convert { temp4 = (temp4 & (temp4-0x7FFF)) - 0x8000; } xastir_snprintf(wx_hi_temp, sizeof(wx_hi_temp), "%03d", (int)((float)((temp4<<16)/65536)/10.0)); wx_hi_temp_on = 1; } else { wx_hi_temp_on = 0; } /* Low Temp for Today*/ if (data[100]!='-') // '-' signifies invalid data { int temp4; substr(temp_data1,(char *)(data+100),4); temp4 = (int)strtol(temp_data1,&temp_conv,16); if (temp_data1[0] > '7') // Negative value, convert { temp4 = (temp4 & (temp4-0x7FFF)) - 0x8000; } xastir_snprintf(wx_low_temp, sizeof(wx_low_temp), "%03d", (int)((float)((temp4<<16)/65536)/10.0)); wx_low_temp_on = 1; } else { wx_low_temp_on = 0; } /* Heat Index Calculation*/ hi_hum=atoi(weather->wx_hum); rh2= atoi(weather->wx_hum); rh2=(rh2 * rh2); hidx_temp=atoi(weather->wx_temp); t2= atoi(weather->wx_temp); t2=(t2 * t2); if (hidx_temp >= 70) { heat_index=-42.379+2.04901523 * hidx_temp+10.1433127 * hi_hum-0.22475541 * hidx_temp * hi_hum-0.00683783 * t2-0.05481717 * rh2+0.00122874 * t2 * hi_hum+0.00085282 * hidx_temp * rh2-0.00000199 * t2 * rh2; xastir_snprintf (wx_heat_index, sizeof(wx_heat_index), "%03d", heat_index); wx_heat_index_on = 1; } else { wx_heat_index_on = 0; } } break; //////////////////////// // Qualimetrics Q-Net // //////////////////////// case(QM_WX): if (debug_level & 1) { fprintf(stderr,"Qualimetrics Q-Net %s:<%s>\n",fill->call_sign,data); } weather->wx_type=WX_TYPE; xastir_snprintf(weather->wx_station, sizeof(weather->wx_station), "Q-N"); // Can this sscanf overflow the "temp" buffer? I // changed the length of temp to MAX_DEVICE_BUFFER to // avoid this problem. if (6 != sscanf((char *)data,"%19s %d %19s %d %19s %d",temp,&temp1,temp,&temp2,temp,&temp3)) { fprintf(stderr,"wx_fill_data:sscanf parsing error\n"); } /* outdoor temp */ xastir_snprintf(weather->wx_temp, sizeof(weather->wx_temp), "%03d", (int)((temp2/10.0))); /* baro */ xastir_snprintf(weather->wx_baro, sizeof(weather->wx_baro), "%0.1f", ((float)temp3/100.0)*33.864); /* outdoor humidity */ xastir_snprintf(weather->wx_hum, sizeof(weather->wx_hum), "%03d", temp1); if (!from) // From local station { weather->wx_gust[0]=0; weather->wx_course[0]=0; weather->wx_rain[0]=0; weather->wx_prec_00[0]=0; weather->wx_prec_24[0]=0; weather->wx_rain_total[0]=0; weather->wx_gust[0]=0; weather->wx_speed[0]=0; } break; /////////////////////////////////////////////////////////// // Radio Shack WX-200 or Huger/Oregon Scientific WM-918 // /////////////////////////////////////////////////////////// case(RSWX200): // Notes: Many people run the wx200d daemon connected to the weather station, // with Xastir then connected to wx200d. Note that wx200d changes the protocol // slightly: It only sends frames that have changed to the clients. This means // even if the weather station is sending regular packets, wx200d won't send // them along to Xastir if all the bits are the same as the last packet of that // type. To fix this I had to tie into the main.c:UpdateTime() function to do // regular updates at a 30 second rate, to keep the rain and gust queues cycling // on a regular basis. // // 2nd Note: Some WX-200 weather stations send bogus data. I had to add in a // bunch of filtering to keep the global variables from getting corrupted by // this data. More filtering may need to be done and/or the limits may need to // be changed. if (!from) // From local station { if (debug_level & 1) { fprintf(stderr,"RSWX200 WX (binary)\n"); } weather->wx_type=WX_TYPE; xastir_snprintf(weather->wx_station, sizeof(weather->wx_station), "RSW"); switch (data[0]) { case 0x8f: /* humidity */ if ( (rswnc(data[20]) <= 100) && (rswnc(data[2]) >= 0) ) xastir_snprintf(weather->wx_hum, sizeof(weather->wx_hum), "%03d", rswnc(data[20])); else //sprintf(weather->wx_hum,"100"); { fprintf(stderr,"Humidity out-of-range, ignoring: %03d\n",rswnc(data[20]) ); } break; case 0x9f: /* temp */ /* all data in C ?*/ xastir_snprintf(temp_data1, sizeof(temp_data1), "%c%d%0.1f", ((data[17]&0x08) ? '-' : '+'),(data[17]&0x7),rswnc(data[16])/10.0); /*fprintf(stderr,"temp data: <%s> %d %d %d\n", temp_data1,((data[17]&0x08)==0x08),(data[17]&0x7),rswnc(data[16]));*/ temp_temp = (float)((atof(temp_data1)*1.8)+32); if ( (temp_temp >= -99.0) && (temp_temp < 200.0) ) { xastir_snprintf(weather->wx_temp, sizeof(weather->wx_temp), "%03d", (int)((atof(temp_data1)*1.8)+32)); /*fprintf(stderr,"Temp %s C %0.2f %03d\n",temp_data1,atof(temp_data1),(int)atof(temp_data1)); fprintf(stderr,"Temp F %0.2f %03d\n",(atof(temp_data1)*1.8)+32,(int)(atof(temp_data1)*1.8)+32); */ } else // We don't want to save this one { fprintf(stderr,"Temp out-of-range, ignoring: %0.2f\n", temp_temp); } xastir_snprintf(temp_data1, sizeof(temp_data1), "%c%d%d.%d", ((data[18]&0x80) ? '-' : '+'),(data[18]&0x70)>>4,(data[18]&0x0f),(data[17] & 0xf0) >> 4); xastir_snprintf(wx_hi_temp, sizeof(wx_hi_temp), "%03d", (int)((atof(temp_data1)*1.8)+32)); wx_hi_temp_on=1; xastir_snprintf(temp_data1, sizeof(temp_data1), "%c%d%d.%d", ((data[23]&0x80) ? '-' : '+'),(data[23]&0x70)>>4,(data[23]&0x0f),(data[22] & 0xf0) >> 4); xastir_snprintf(wx_low_temp, sizeof(wx_low_temp), "%03d", (int)((atof(temp_data1)*1.8)+32)); wx_low_temp_on=1; break; case 0xaf: /* baro/dewpt */ // local baro pressure in mb? // sprintf(weather->wx_baro,"%02d%02d",rswnc(data[2]),rswnc(data[1])); // Sea Level Adjusted baro in mb xastir_snprintf(weather->wx_baro, sizeof(weather->wx_baro), "%0d%02d%0.1f", (data[5]&0x0f), rswnc(data[4]), rswnc(data[3])/10.0); /* dew point in C */ temp_temp = (int)((rswnc(data[18])*1.8)+32); if ( (temp_temp >= 32.0) && (temp_temp < 150.0) ) xastir_snprintf(wx_dew_point, sizeof(wx_dew_point), "%03d", (int)((rswnc(data[18])*1.8)+32)); else { fprintf(stderr,"Dew point out-of-range, ignoring: %0.2f\n", temp_temp); } break; case 0xbf: /* Rain */ // All data in mm. Convert to hundredths of an inch. xastir_snprintf(temp_data1, sizeof(temp_data1), "%02d%02d", rswnc(data[6]), rswnc(data[5])); temp_temp = (float)(atof(temp_data1) * 3.9370079); if ( (temp_temp >= 0) && (temp_temp < 51200.0) ) // Between 0 and 512 inches { xastir_snprintf(weather->wx_rain_total, sizeof(weather->wx_rain_total), "%0.2f", atof(temp_data1) * 3.9370079); /* Since local station only */ compute_rain((float)atof(weather->wx_rain_total)); weather->wx_compute_rain_rates=1; /* Last hour rain */ xastir_snprintf(weather->wx_rain, sizeof(weather->wx_rain), "%0.2f", rain_minute_total); /* Last 24 hour rain */ xastir_snprintf(weather->wx_prec_24, sizeof(weather->wx_prec_24), "%0.2f", rain_24); /* Rain since midnight */ xastir_snprintf(weather->wx_prec_00, sizeof(weather->wx_prec_00), "%0.2f", rain_00); } else { fprintf(stderr,"Total Rain out-of-range, ignoring: %0.2f\n", temp_temp); } break; case 0xcf: /* Wind w/chill*/ /* get last gust speed */ if (strlen(weather->wx_gust) > 0) { /* get last speed */ last_speed=(float)atof(weather->wx_gust); last_speed_time=weather->wx_speed_sec_time; } /* all data in m/s */ /* average wind speed */ xastir_snprintf(temp_data1, sizeof(temp_data1), "%01d%0.1f", (data[5]&0xf), (float)( rswnc(data[4]) / 10 )); // Convert to mph xastir_snprintf(weather->wx_speed, sizeof(weather->wx_speed), "%03d", (int)(0.5 + (atof(temp_data1)*2.2369))); /* wind gust */ xastir_snprintf(temp_data1, sizeof(temp_data1), "%01d%0.1f", (data[2]&0xf), (float)( rswnc(data[1]) / 10 )); /*sprintf(weather->wx_gust,"%03d",(int)(0.5 + (atof(temp_data1)*2.2369)));*/ /* do computed gust, convert to mph */ computed_gust = compute_gust((int)(0.5 + (atof(temp_data1)*2.2369)), last_speed, &last_speed_time); weather->wx_speed_sec_time = sec_now(); xastir_snprintf(weather->wx_gust, sizeof(weather->wx_gust), "%03d", (int)(0.5 + computed_gust)); /* high wind gust */ xastir_snprintf(temp_data1, sizeof(temp_data1), "%01d%0.1f", (data[8]&0xf), (float)( rswnc(data[7]) / 10 )); xastir_snprintf(wx_high_wind, sizeof(wx_high_wind), "%03d", (int)(0.5 + (atof(temp_data1)*2.2369))); wx_high_wind_on = 1; xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "%03d", ( ((rswnc(data[3])*10) + ((data[2]&0xf0)>>4)) %1000 ) ); // Check for course = 0. Change to 360. if (strncmp(weather->wx_course,"000",3) == 0) { xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "360"); } /* wind chill in C */ xastir_snprintf(temp_data1, sizeof(temp_data1), "%c%d", ((data[21]&0x20) ? '-' : '+'), rswnc(data[16])); temp_temp = (float)((atof(temp_data1)*1.8)+32); if ( (temp_temp > -200.0) && (temp_temp < 200.0) ) xastir_snprintf(wx_wind_chill, sizeof(wx_wind_chill), "%03d", (int)((atof(temp_data1)*1.8)+32)); else { fprintf(stderr,"Wind_chill out-of-range, ignoring: %0.2f\n", temp_temp); } wx_wind_chill_on = 1; break; default: break; } if (strlen(weather->wx_hum) > 0 && strlen(weather->wx_temp) > 0) { /* Heat Index Calculation*/ hi_hum=atoi(weather->wx_hum); rh2= atoi(weather->wx_hum); rh2=(rh2 * rh2); hidx_temp=atoi(weather->wx_temp); t2= atoi(weather->wx_temp); t2=(t2 * t2); if (hidx_temp >= 70) { heat_index=(-42.379+2.04901523 * hidx_temp+10.1433127 * hi_hum-0.22475541 * hidx_temp * hi_hum-0.00683783 * t2-0.05481717 * rh2+0.00122874 * t2 * hi_hum+0.00085282 * hidx_temp * rh2-0.00000199 * t2 * rh2); xastir_snprintf(wx_heat_index, sizeof(wx_heat_index), "%03d", heat_index); wx_heat_index_on=1; } else { wx_heat_index[0] = 0; } } // end of heat index calculation } // end of if (!from) break; // End of case for RSWX200 weather station /////////////////////////////////////////////////////////// // Davis WMII/WWIII/Vantage Pro via meteo & db2APRS // /////////////////////////////////////////////////////////// // Note: format is really APRS Spec 'positionless' WX string w/tag for X and Davis case(DAVISMETEO) : // todo - need to deal with lack of values, such as c...s...g...t045 string memset(weather->wx_course,0,4); // Keep out fradulent data... memset(weather->wx_speed,0,4); memset(weather->wx_temp,0,5); memset(weather->wx_rain,0,10); memset(weather->wx_prec_00,0,10); memset(weather->wx_prec_24,0,10); memset(weather->wx_rain_total,0,10); memset(weather->wx_hum,0,5); memset(weather->wx_baro,0,10); memset(weather->wx_station,0,MAX_WXSTATION); if ((temp_conv=strchr((char *)data,'c'))) // Wind Direction in Degrees { xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "%s", temp_conv+1); weather->wx_course[3] = '\0'; } // Check for course = 0. Change to 360. if (strncmp(weather->wx_course,"000",3) == 0) { xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "360"); } if ((temp_conv=strchr((char *)data,'s'))) // Wind Speed in MPH - not snowfall { xastir_snprintf(weather->wx_speed, sizeof(weather->wx_speed), "%s", temp_conv+1); weather->wx_speed[3] = '\0'; } if ((temp_conv=strchr((char *)data,'g'))) // Wind Gust in MPH { // Don't read if data is "..." (missing gust data) // If we aren't getting gust data from the station, don't clobber // the gust data we're computing! if (strncmp(temp_conv+1,"...",3) != 0) { memset(weather->wx_gust,0,4); // keep out fraudulent data xastir_snprintf(weather->wx_gust, sizeof(weather->wx_gust), "%s", temp_conv+1); weather->wx_gust[3] = '\0'; // compute high wind if(wx_high_wind[0] == '\0' || // first time (get_hours() == 0 && get_minutes() == 0) || // midnite (atol(weather->wx_gust) > atol(wx_high_wind))) // gust { xastir_snprintf(wx_high_wind, sizeof(wx_high_wind), "%s", weather->wx_gust); } wx_high_wind_on=1; } } if ((temp_conv=strchr((char *)data,'t'))) // Temperature in Degrees F { xastir_snprintf(weather->wx_temp, sizeof(weather->wx_temp), "%s", temp_conv+1); weather->wx_temp[3] = '\0'; // compute hi temp, since APRS doesn't send that if(wx_hi_temp[0] == '\0' || // first time (get_hours() == 0 && get_minutes() == 0) || // midnite (atol(weather->wx_temp) > atol(wx_hi_temp))) { xastir_snprintf(wx_hi_temp, sizeof(wx_hi_temp), "%s", weather->wx_temp); } wx_hi_temp_on=1; // compute low temp, since APRS doesn't send that if(wx_low_temp[0] == '\0' || // first time (get_hours() == 0 && get_minutes() == 0) || // midnite (atol(weather->wx_temp) < atol(wx_low_temp))) { xastir_snprintf(wx_low_temp, sizeof(wx_low_temp), "%s", weather->wx_temp); } wx_low_temp_on=1; } if ((temp_conv=strchr((char *)data,'r'))) // Rain per hour { xastir_snprintf(weather->wx_rain, sizeof(weather->wx_rain), "%s", temp_conv+1); weather->wx_rain[3] = '\0'; } if ((temp_conv=strchr((char *)data,'p'))) // Rain per 24 hrs/total { xastir_snprintf(weather->wx_prec_24, sizeof(weather->wx_prec_24), "%s", temp_conv+1); weather->wx_prec_24[3] = '\0'; } if ((temp_conv=strchr((char *)data,'P'))) // Rain since midnight { xastir_snprintf(weather->wx_prec_00, sizeof(weather->wx_prec_00), "%s", temp_conv+1); weather->wx_prec_00[3] = '\0'; } if ((temp_conv=strchr((char *)data,'T'))) // Total Rain since { // wx station reset xastir_snprintf(weather->wx_rain_total, sizeof(weather->wx_rain_total), "%s", temp_conv+1); weather->wx_rain_total[4] = '\0'; } // Ok, here's the deal --- if we got total rain AND we didn't get // rain-since-midnight, fix it up. // This is a problem with LaCrosse --- no rain-since-midnight // provided. Don't do anything at all if we didn't get total rain // from the station. compute_rain *depends* on "total rain" being // a strictly increasing number that is never reset to zero during // Xastir's run. if (strlen(weather->wx_rain_total) >0 ) { compute_rain((float)atof(weather->wx_rain_total)); if (weather->wx_prec_00[0] == '\0') { /* rain since midnight */ xastir_snprintf(weather->wx_prec_00, sizeof(weather->wx_prec_00), "%0.2f", rain_00); } } // we are should be getting total rain from the station, but // we are also getting the rates. // Davis gives 24-hour, since-midnight, and 1-hour rates. // LaCrosse gives 24 and 1 hour. // Don't recompute what the station already gave us. weather->wx_compute_rain_rates=0; if ((temp_conv=strchr((char *)data,'h'))) // Humidity % { if (!strncmp(temp_conv+1,"00",2)) // APRS says 00 is { xastir_snprintf(weather->wx_hum, // 100% humidity sizeof(weather->wx_hum), "%s", "100"); weather->wx_hum[3] = '\0'; } else { xastir_snprintf(weather->wx_hum, // humidity less than sizeof(weather->wx_hum), // 100% "%s", temp_conv+1); weather->wx_hum[2] = '\0'; } } if ((temp_conv=strchr((char *)data,'b'))) // Air Pressure in 1/10 hPa { memset(temp_data1,0,sizeof(temp_data1)); xastir_snprintf(temp_data1, sizeof(temp_data1), "%s", temp_conv+1); temp_data1[5] = '\0'; temp_temp = (float)(atof(temp_data1))/10.0; xastir_snprintf(temp_data1, sizeof(temp_data1), "%0.1f", temp_temp); xastir_snprintf(weather->wx_baro, sizeof(weather->wx_baro), "%s", temp_data1); } if ((temp_conv=strchr((char *)data,'x'))) // WX Station Identifier { xastir_snprintf(weather->wx_station, sizeof(weather->wx_station), "%s", temp_conv+1); weather->wx_station[MAX_WXSTATION-1] = '\0'; } // now compute wind chill wind_chill = 35.74 + .6215 * atof(weather->wx_temp) - 35.75 * pow(atof(weather->wx_gust), .16) + .4275 * atof(weather->wx_temp) * pow(atof(weather->wx_gust), .16); if((wind_chill < atof(weather->wx_temp)) && (atof(weather->wx_temp) < 50)) { xastir_snprintf(wx_wind_chill, sizeof(wx_wind_chill), "%.0f", wind_chill); wx_wind_chill_on = 1; } else { wx_wind_chill_on = 0; wx_wind_chill[0] = '\0'; } // The rest of the optional WX data is not used by // xastir (Luminosity, etc), except for snow, which // conflicts with wind speed (both are lower case 's') if (debug_level & 1) fprintf(stdout,"Davis Decode: wd-%s,ws-%s,wg-%s,t-%s,rh-%s,r00-%s,r24-%s,rt-%s,h-%s,ap-%s,station-%s\n", weather->wx_course,weather->wx_speed,weather->wx_gust, weather->wx_temp,weather->wx_rain,weather->wx_prec_00, weather->wx_prec_24,weather->wx_rain_total, weather->wx_hum,weather->wx_baro,weather->wx_station); break; // This is the output of the Davis APRS Data Logger. The format // is in fact exactly the same as a regular APRS weather packet, // complete with position information. Ignore that. // @xxxxxxzDDMM.mmN/DDDMM.mmW_CSE/SPDgGGGtTTTrRRRpRRRPRRRhXXbXXXXX.DsVP case (DAVISAPRSDL): memset(weather->wx_course,0,4); // Keep out fradulent data... memset(weather->wx_speed,0,4); memset(weather->wx_gust,0,4); memset(weather->wx_temp,0,5); memset(weather->wx_rain,0,10); memset(weather->wx_prec_00,0,10); memset(weather->wx_prec_24,0,10); memset(weather->wx_rain_total,0,10); memset(weather->wx_hum,0,5); memset(weather->wx_baro,0,10); memset(weather->wx_station,0,MAX_WXSTATION); if (sscanf((char *)data, "%*27s%3s/%3sg%3st%3sr%3sp%3sP%3sh%2sb%5s.DsVP", weather->wx_course, weather->wx_speed, weather->wx_gust, weather->wx_temp, weather->wx_rain, weather->wx_prec_24, weather->wx_prec_00, weather->wx_hum, weather->wx_baro) == 9) { // then we got all the data out of the packet... now process // First null-terminate all the strings: weather->wx_course[3]='\0'; weather->wx_speed[3]='\0'; weather->wx_gust[3]='\0'; weather->wx_temp[3]='\0'; weather->wx_rain[3]='\0'; weather->wx_prec_24[3]='\0'; weather->wx_prec_00[3]='\0'; weather->wx_hum[2]='\0'; weather->wx_baro[6]='\0'; // NOTE: Davis APRS Data Logger does NOT provide total rain, // and so data from compute_rain (which needs total rain) will // be wrong. Set this flag to stop that from clobbering our // good rain rate data. weather->wx_compute_rain_rates=0; // Check for course = 0. Change to 360. if (strncmp(weather->wx_course,"000",3) == 0) { xastir_snprintf(weather->wx_course, sizeof(weather->wx_course), "360"); } // compute high wind if(wx_high_wind[0] == '\0' || // first time (get_hours() == 0 && get_minutes() == 0) || // midnite (atol(weather->wx_gust) > atol(wx_high_wind))) // gust { xastir_snprintf(wx_high_wind, sizeof(wx_high_wind), "%s", weather->wx_gust); } wx_high_wind_on=1; // compute hi temp, since APRS doesn't send that if(wx_hi_temp[0] == '\0' || // first time (get_hours() == 0 && get_minutes() == 0) || // midnite (atol(weather->wx_temp) > atol(wx_hi_temp))) { xastir_snprintf(wx_hi_temp, sizeof(wx_hi_temp), "%s", weather->wx_temp); } wx_hi_temp_on=1; // compute low temp, since APRS doesn't send that if(wx_low_temp[0] == '\0' || // first time (get_hours() == 0 && get_minutes() == 0) || // midnite (atol(weather->wx_temp) < atol(wx_low_temp))) { xastir_snprintf(wx_low_temp, sizeof(wx_low_temp), "%s", weather->wx_temp); } wx_low_temp_on=1; // fix up humidity --- 00 in APRS means 100%: if (strncmp(weather->wx_hum,"00",2)==0) { weather->wx_hum[0]='1'; weather->wx_hum[1]=weather->wx_hum[2]='0'; weather->wx_hum[3]='\0'; } // fix up barometer. APRS sends in 10ths of millibars: temp_temp=(float)(atof(weather->wx_baro))/10.0; weather->wx_baro[0]='\0'; // zero out so snprintf doesn't append xastir_snprintf(weather->wx_baro, sizeof(weather->wx_baro), "%0.1f", temp_temp); // this should terminate Just Fine. // now compute wind chill wind_chill = 35.74 + .6215 * atof(weather->wx_temp) - 35.75 * pow(atof(weather->wx_gust), .16) + .4275 * atof(weather->wx_temp) * pow(atof(weather->wx_gust), .16); if((wind_chill < atof(weather->wx_temp)) && (atof(weather->wx_temp) < 50)) { xastir_snprintf(wx_wind_chill, sizeof(wx_wind_chill), "%.0f", wind_chill); wx_wind_chill_on = 1; } else { wx_wind_chill_on = 0; wx_wind_chill[0] = '\0'; } xastir_snprintf(weather->wx_station, sizeof(weather->wx_station), "%s", (char *) &(data[63])); /* Heat Index Calculation*/ hi_hum=atoi(weather->wx_hum); rh2= atoi(weather->wx_hum); rh2=(rh2 * rh2); hidx_temp=atoi(weather->wx_temp); t2= atoi(weather->wx_temp); t2=(t2 * t2); if (hidx_temp >= 70) { heat_index=-42.379+2.04901523 * hidx_temp+10.1433127 * hi_hum-0.22475541 * hidx_temp * hi_hum-0.00683783 * t2-0.05481717 * rh2+0.00122874 * t2 * hi_hum+0.00085282 * hidx_temp * rh2-0.00000199 * t2 * rh2; xastir_snprintf (wx_heat_index, sizeof(wx_heat_index), "%03d", heat_index); wx_heat_index_on = 1; } else { wx_heat_index_on = 0; } if (debug_level & 1) fprintf(stdout,"Davis APRS DataLogger Decode $Revision$: wd-%s,ws-%s,wg-%s,t-%s,rh-%s,r24-%s,r00-%s,h-%s,ap-%s,station-%s\n", weather->wx_course,weather->wx_speed,weather->wx_gust, weather->wx_temp, weather->wx_rain, weather->wx_prec_24, weather->wx_prec_00,weather->wx_hum,weather->wx_baro, weather->wx_station); } break; } // End of big switch } // End of wx_fill_data() //********************************************************** // Decode WX data line // wx_line: raw wx data to decode // // This is called from main.c:UpdateTime() only. It // decodes data for serially-connected and network-connected // WX interfaces only. It calls wx_fill_data() to do the // real work once it figures out what type of data it has. //********************************************************** // // Note that the length of "wx_line" can be up to MAX_DEVICE_BUFFER, // which is currently set to 4096. // void wx_decode(unsigned char *wx_line, int data_length, int port) { DataRow *p_station; int decoded; int find; int i; int len; char time_data[MAX_TIME]; unsigned int check_sum; int max; WeatherRow *weather; float t1,t2,t3,t4,t5,t6,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18,t19; int t7,t8; //fprintf(stderr,"wx_decode: %s\n",wx_line); //fprintf(stderr,"\nwx_decode: %d bytes\n", data_length); find=0; len = data_length; if (len == 0) { len=strlen((char *)wx_line); } if (len>10 || ((int)wx_line[0]!=0 && port_data[port].data_type==1)) { if (search_station_name(&p_station,my_callsign,1)) { if (get_weather_record(p_station)) // DK7IN: only add record if we found something... { weather = p_station->weather_data; decoded=0; /* Ok decode wx data */ if (wx_line[0]=='!' && wx_line[1]=='!' && is_xnum_or_dash((char *)(wx_line+2),40) && port_data[port].data_type==0) { /* Found Peet Bros U-2k */ if (debug_level & 1) { fprintf(stderr,"Found Peet Bros U-2k WX:%s\n",wx_line+2); } xastir_snprintf(wx_station_type, sizeof(wx_station_type), "%s", langcode("WXPUPSI011")); xastir_snprintf(raw_wx_string, sizeof(raw_wx_string), "%s", wx_line); raw_wx_string[MAX_RAW_WX_STRING] = '\0'; // Terminate it xastir_snprintf(weather->wx_time, sizeof(weather->wx_time), "%s", get_time(time_data)); weather->wx_sec_time=sec_now(); //weather->wx_data=1; wx_fill_data(0,APRS_WX3,wx_line,p_station); decoded=1; } else if (((wx_line[0]=='#') || (wx_line[0]=='*')) && is_xnum_or_dash((char *)(wx_line+1),13) && port_data[port].data_type==0) { /* Found Peet Bros raw U2 data */ xastir_snprintf(wx_station_type, sizeof(wx_station_type), "%s", langcode("WXPUPSI012")); if (debug_level & 1) { fprintf(stderr,"Found Peet Bros raw U2 data WX#:%s\n",wx_line+1); } xastir_snprintf(raw_wx_string, sizeof(raw_wx_string), "%s", wx_line); raw_wx_string[MAX_RAW_WX_STRING] = '\0'; // Terminate it xastir_snprintf(weather->wx_time, sizeof(weather->wx_time), "%s", get_time(time_data)); weather->wx_sec_time=sec_now(); //weather->wx_data=1; if (wx_line[0]=='#') // Wind speed in km/h { wx_fill_data(0,APRS_WX4,wx_line,p_station); } else // '*', Wind speed in mph { wx_fill_data(0,APRS_WX6,wx_line,p_station); } decoded=1; } else if (strncmp("$ULTW",(char *)wx_line,5)==0 && is_xnum_or_dash((char *)(wx_line+5),44) && port_data[port].data_type==0) { /* Found Peet Bros raw U2 data */ xastir_snprintf(wx_station_type, sizeof(wx_station_type), "%s", langcode("WXPUPSI013")); if (debug_level & 1) { fprintf(stderr,"Found Peet Bros Ultimeter Packet data WX#:%s\n",wx_line+5); } xastir_snprintf(raw_wx_string, sizeof(raw_wx_string), "%s", wx_line); raw_wx_string[MAX_RAW_WX_STRING] = '\0'; // Terminate it weather->wx_sec_time=sec_now(); //weather->wx_data=1; wx_fill_data(0,APRS_WX5,wx_line,p_station); decoded=1; } else if (wx_line[2]==' ' && wx_line[5]==' ' && wx_line[8]=='/' && wx_line[11]=='/' && wx_line[14]==' ' && wx_line[17]==':' && wx_line[20]==':' && strncmp(" #0:",(char *)wx_line+23,4)==0 && port_data[port].data_type==0) { find=0; for (i=len; i>23 && !find; i--) { if ((int)wx_line[i]==0x03) { find=1; wx_line[i] = 0; } } if (find) { /* found Qualimetrics Q-Net station */ xastir_snprintf(wx_station_type, sizeof(wx_station_type), "%s", langcode("WXPUPSI016")); if (debug_level & 1) { fprintf(stderr,"Found Qualimetrics Q-Net station data WX#:%s\n",wx_line+23); } xastir_snprintf(weather->wx_time, sizeof(weather->wx_time), "%s", get_time(time_data)); weather->wx_sec_time=sec_now(); //weather->wx_data=1; wx_fill_data(0,QM_WX,wx_line+24,p_station); decoded=1; } } //WE7U // else look for ten ASCII decimal point chars in the input, or do an // sscanf looking for the correct number and types of fields for the // OWW server in ARNE mode. 6 %f's, 2 %d's, 11 %f's. else if (sscanf((const char *)wx_line,"%f %f %f %f %f %f %d %d %f %f %f %f %f %f %f %f %f %f %f", &t1,&t2,&t3,&t4,&t5,&t6,&t7,&t8,&t9,&t10,&t11,&t12,&t13,&t14,&t15,&t16,&t17,&t18,&t19) == 19) { // Found Dallas One-Wire Weather Station if (debug_level & 1) { fprintf(stderr,"Found OWW ARNE-mode(19) one-wire weather station data\n"); } weather->wx_sec_time=sec_now(); wx_fill_data(0,DALLAS_ONE_WIRE,wx_line,p_station); decoded=1; } else if (sscanf((const char *)wx_line,"%f %f %f %f %f %f %d %d %f %f %f %f", &t1,&t2,&t3,&t4,&t5,&t6,&t7,&t8,&t9,&t10,&t11,&t12) == 12) { // Found Dallas One-Wire Weather Station if (debug_level & 1) { fprintf(stderr,"Found OWW ARNE-mode(12) one-wire weather station data\n"); } weather->wx_sec_time=sec_now(); wx_fill_data(0,DALLAS_ONE_WIRE,wx_line,p_station); decoded=1; } else if (strncmp("&CR&",(char *)wx_line,4)==0 && is_xnum_or_dash((char *)(wx_line+5),44) && port_data[port].data_type==0) { if (debug_level & 1) { fprintf(stderr,"Found Peet Complete station data\n"); } xastir_snprintf(wx_station_type, sizeof(wx_station_type), "%s", langcode("WXPUPSI017")); xastir_snprintf(raw_wx_string, sizeof(raw_wx_string), "%s", wx_line); raw_wx_string[MAX_RAW_WX_STRING] = '\0'; // Terminate it weather->wx_sec_time=sec_now(); //weather->wx_data=1; wx_fill_data(0,PEET_COMPLETE,wx_line,p_station); decoded=1; } else if (port_data[port].data_type==1) { // int jj; /* binary data type */ if (debug_level & 1) { fprintf(stderr,"Found binary data: %d bytes\n", len); } /* clear raw string */ memset(raw_wx_string,0,sizeof(raw_wx_string)); max=0; switch (wx_line[0]) { case 0x8f: max=34; break; case 0x9f: max=33; break; case 0xaf: max=30; break; case 0xbf: max=13; break; case 0xcf: max=26; break; default: break; } // fprintf(stderr, "wx_decode binary: "); // for (jj = 0; jj < len+1; jj++) { // fprintf(stderr, "%02x ", wx_line[jj]); // } // fprintf(stderr, "\n"); // fprintf(stderr, "Integers: "); // for (jj = 0; jj < max+1; jj++) { // fprintf(stderr, "%0d ", wx_line[jj]); // } // fprintf(stderr, "\n"); if (len < (max+1)) { fprintf(stderr, " Short NET_WX packet, %d bytes\n", len); } if (max > 0 && len >= (max+1) ) { // Compute the checksum from the data check_sum = 0; for (i = 0; i < max; i++) { check_sum += wx_line[i]; } // fprintf(stderr," Checksum: 0x%02x ", 0x0ff & check_sum); if ( wx_line[max] == (0xff & check_sum) ) { /* good RS WX-200 data */ //fprintf(stderr,"GOOD RS WX-200 %0X data\n",wx_line[0]); /* found RS WX-200 */ xastir_snprintf(wx_station_type, sizeof(wx_station_type), "%s", langcode("WXPUPSI025")); xastir_snprintf(weather->wx_time, sizeof(weather->wx_time), "%s", get_time(time_data)); weather->wx_sec_time=sec_now(); //weather->wx_data=1; wx_fill_data(0,RSWX200,wx_line,p_station); decoded=1; } else { // fprintf(stderr, "bad"); } } } else if (wx_line[0]=='@' && strncmp((char *)&(wx_line[63]),".DsVP",5)==0) { // this is a Davis Vantage Pro with APRS Data Logger if (debug_level & 1) { fprintf(stdout,"Davis VP APRS Data Logger data found ... %s\n", wx_line); } xastir_snprintf(wx_station_type, sizeof(wx_station_type), "%s", langcode("WXPUPSI028")); xastir_snprintf(weather->wx_time, sizeof(weather->wx_time), "%s", get_time(time_data)); weather->wx_sec_time=sec_now(); wx_fill_data(0,DAVISAPRSDL,wx_line,p_station); decoded=1; } else // ASCII data of undetermined type { // Davis Weather via meteo -> db2APRS -> TCP port if (strstr((const char *)wx_line, "xDvs")) // APRS 'postionless' WX data w/ Davis & X tag { if (debug_level & 1) { fprintf(stdout,"Davis Data found... %s\n",wx_line); } xastir_snprintf(wx_station_type, sizeof(wx_station_type), "%s", langcode("WXPUPSI026")); xastir_snprintf(weather->wx_time, sizeof(weather->wx_time), "%s", get_time(time_data)); weather->wx_sec_time=sec_now(); wx_fill_data(0,DAVISMETEO,wx_line,p_station); decoded=1; } else if (debug_level & 1) { fprintf(stderr,"Unknown WX DATA:%s\n",wx_line); } } if (decoded) { /* save data back */ if (begin_critical_section(&port_data_lock, "wx.c:wx_decode(1)" ) > 0) { fprintf(stderr,"port_data_lock, Port = %d\n", port); } port_data[port].decode_errors=0; if (end_critical_section(&port_data_lock, "wx.c:wx_decode(2)" ) > 0) { fprintf(stderr,"port_data_lock, Port = %d\n", port); } statusline(langcode("BBARSTA032"),1); // Decoded WX Data /* redraw now */ //redraw_on_new_data=2; redraw_on_new_data=1; fill_wx_data(); } else { /* Undecoded packet */ memset(raw_wx_string,0,sizeof(raw_wx_string)); if (begin_critical_section(&port_data_lock, "wx.c:wx_decode(3)" ) > 0) { fprintf(stderr,"port_data_lock, Port = %d\n", port); } port_data[port].decode_errors++; // We have errors in decoding if (port_data[port].decode_errors>10) { /* wrong data type? */ port_data[port].data_type++; // Try another data type. 0=ascii, 1=wx binary port_data[port].data_type&=0x01; /*if (debug_level & 1)*/ fprintf(stderr,"Data type %d\n",port_data[port].data_type); port_data[port].decode_errors=0; } if (end_critical_section(&port_data_lock, "wx.c:wx_decode(4)" ) > 0) { fprintf(stderr,"port_data_lock, Port = %d\n", port); } } } } } } /***********************************************************/ /* fill string with WX data for transmit */ /* */ /***********************************************************/ time_t wx_tx_data1(char *st, int st_size) { DataRow *p_station; time_t wx_time; char temp[100]; WeatherRow *weather; st[0] = '\0'; wx_time = 0; if (search_station_name(&p_station,my_callsign,1)) { if (get_weather_record(p_station)) // station has wx data { weather = p_station->weather_data; //WE7U: For debugging purposes only //weather->wx_sec_time=sec_now(); //sprintf(weather->wx_course,"359"); // degrees //sprintf(weather->wx_speed,"001"); // mph //sprintf(weather->wx_gust,"010"); // mph //sprintf(weather->wx_temp,"069"); // Farenheit //if ( strlen(weather->wx_rain_total) == 0) // sprintf(weather->wx_rain_total,"1900.40"); //sprintf(weather->wx_rain_total,"1987.6"); // hundredths of an inch //compute_rain(atof(weather->wx_rain_total)); //sprintf(weather->wx_rain_total,"1988.6"); //compute_rain(atof(weather->wx_rain_total)); //sprintf(weather->wx_rain_total,"1990.6"); //compute_rain(atof(weather->wx_rain_total)); //sprintf(weather->wx_rain,"%0.2f",rain_minute_total); //sprintf(weather->wx_prec_24,"%0.2f",rain_24); //sprintf(weather->wx_prec_00,"%0.2f",rain_00); //sprintf(weather->wx_rain,"0"); // hundredths of an inch //sprintf(weather->wx_prec_00,"0"); // hundredths of an inch //sprintf(weather->wx_prec_24,"0"); // hundredths of an inch //sprintf(weather->wx_hum,"92"); // % //sprintf(weather->wx_baro,"1013.0"); // hPa //weather->wx_type = WX_TYPE; //xastir_snprintf(weather->wx_station,sizeof(weather->wx_station),"RSW"); // 359/000g000t065r010P020p030h92b01000 if (strlen(weather->wx_course) > 0 && strlen(weather->wx_speed) > 0) { // We have enough wx_data wx_time=weather->wx_sec_time; xastir_snprintf(temp, sizeof(temp), "%s", weather->wx_course); if (strlen(temp) > 3) { if (debug_level & 1) { fprintf(stderr,"wx_course too long: %s\n", temp); } xastir_snprintf(temp, sizeof(temp), "..."); } if ( (atoi(weather->wx_course) > 360) || (atoi(weather->wx_course) < 0) ) { if (debug_level & 1) { fprintf(stderr,"wx_course out-of-range: %s\n", weather->wx_course); } xastir_snprintf(temp, sizeof(temp), "..."); } //sprintf(st,"%s/%s",weather->wx_course,weather->wx_speed); strncat(st, temp, st_size - 1 - strlen(st)); strncat(st, "/", st_size - 1 - strlen(st)); xastir_snprintf(temp, sizeof(temp), "%s", weather->wx_speed); if (strlen(temp) > 3) { if (debug_level & 1) { fprintf(stderr,"wx_speed too long: %s\n", temp); } xastir_snprintf(temp, sizeof(temp), "..."); } if ( (atoi(weather->wx_speed) < 0) || (atoi(weather->wx_speed) > 999) ) { if (debug_level & 1) { fprintf(stderr,"wx_speed out-of-range: %s\n", weather->wx_speed); } xastir_snprintf(temp, sizeof(temp), "..."); } strncat(st, temp, st_size - 1 - strlen(st)); } else { // We don't have enough wx_data, may be from a Qualimetrics Q-Net? wx_time=weather->wx_sec_time; xastir_snprintf(st, st_size, ".../..."); if (debug_level & 1) { fprintf(stderr,"\n\nAt least one field was empty: Course: %s\tSpeed: %s\n", weather->wx_course, weather->wx_speed); fprintf(stderr,"Will be sending '.../...' instead of real values.\n\n\n"); } } if (strlen(weather->wx_gust) > 0) { xastir_snprintf(temp, sizeof(temp), "g%s", weather->wx_gust); if (strlen(temp) > 4) { if (debug_level & 1) { fprintf(stderr,"wx_gust too long: %s\n", temp); } xastir_snprintf(temp, sizeof(temp), "g..."); } if (atoi(weather->wx_gust) < 0) { if (debug_level & 1) { fprintf(stderr,"wx_gust out-of-range: %s\n", weather->wx_gust); } xastir_snprintf(temp, sizeof(temp), "g..."); } strncat(st, temp, st_size - 1 - strlen(st)); } else { strncat(st, "g...", st_size - 1 - strlen(st)); } if (strlen(weather->wx_temp) > 0) { xastir_snprintf(temp, sizeof(temp), "t%s", weather->wx_temp); if (strlen(temp) > 4) { if (debug_level & 1) { fprintf(stderr,"wx_temp too long: %s\n", temp); } xastir_snprintf(temp, sizeof(temp), "t..."); } if ( (atoi(weather->wx_temp) > 999) || (atoi(weather->wx_temp) < -99) ) { if (debug_level & 1) { fprintf(stderr,"wx_temp out-of-bounds: %s\n", weather->wx_temp); } xastir_snprintf(temp, sizeof(temp), "t..."); } strncat(st, temp, st_size - 1 - strlen(st)); } else { strncat(st, "t...", st_size - 1 - strlen(st)); } if (strlen(weather->wx_rain) > 0) { xastir_snprintf(temp, sizeof(temp), "r%03d", (int)(atof(weather->wx_rain) + 0.5) ); // Cheater's way of rounding if (strlen(temp)>4) { if (debug_level & 1) { fprintf(stderr,"wx_rain too long: %s\n", temp); } // Don't transmit this field if it's not valid xastir_snprintf(temp, sizeof(temp), "r "); } if (atoi(weather->wx_rain) < 0) { if (debug_level & 1) { fprintf(stderr,"wx_rain out-of-bounds: %s\n", weather->wx_rain); } // Don't transmit this field if it's not valid xastir_snprintf(temp, sizeof(temp), "r..."); } strncat(st, temp, st_size - 1 - strlen(st)); } else { // Don't transmit this field if it's not valid //strncat(st, "r...", st_size - 1 - strlen(st)); } if (strlen(weather->wx_prec_00) > 0) { xastir_snprintf(temp, sizeof(temp), "P%03d", (int)(atof(weather->wx_prec_00) + 0.5) ); // Cheater's way of rounding if (strlen(temp)>4) { if (debug_level & 1) { fprintf(stderr,"wx_prec_00 too long: %s\n", temp); } // Don't transmit this field if it's not valid xastir_snprintf(temp, sizeof(temp), "P "); } if (atoi(weather->wx_prec_00) < 0) { if (debug_level & 1) { fprintf(stderr,"wx_prec_00 out-of-bounds: %s\n", weather->wx_prec_00); } // Don't transmit this field if it's not valid xastir_snprintf(temp, sizeof(temp), "P..."); } strncat(st, temp, st_size - 1 - strlen(st)); } else { // Don't transmit this field if it's not valid //strncat(st, "P...", st_size - 1 - strlen(st)); } if (strlen(weather->wx_prec_24) > 0) { xastir_snprintf(temp, sizeof(temp), "p%03d", (int)(atof(weather->wx_prec_24) + 0.5) ); // Cheater's way of rounding if (strlen(temp)>4) { if (debug_level & 1) { fprintf(stderr,"wx_prec_24 too long: %s\n", temp); } // Don't transmit this field if it's not valid xastir_snprintf(temp, sizeof(temp), "p "); } if (atoi(weather->wx_prec_24) < 0) { if (debug_level & 1) { fprintf(stderr,"wx_prec_24 out-of-bounds: %s\n", weather->wx_prec_24); } // Don't transmit this field if it's not valid xastir_snprintf(temp, sizeof(temp), "p..."); } strncat(st, temp, st_size - 1 - strlen(st)); } else { // Don't transmit this field if it's not valid //strncat(st, "p...", st_size - 1 - strlen(st)); } if (strlen(weather->wx_hum) > 0) { if (atoi(weather->wx_hum)>99) { xastir_snprintf(temp, sizeof(temp), "h00"); } else xastir_snprintf(temp, sizeof(temp), "h%02d", atoi(weather->wx_hum)); if (strlen(temp) > 4) { if (debug_level & 1) { fprintf(stderr,"wx_hum too long: %s\n", temp); } // Don't transmit this field if it's not valid //xastir_snprintf(temp,sizeof(temp),"h.."); } if (atoi(weather->wx_hum) < 0) { if (debug_level & 1) { fprintf(stderr,"wx_hum out-of-bounds: %s\n", weather->wx_hum); } // Don't transmit this field if it's not valid //xastir_snprintf(temp,sizeof(temp),"h.."); } strncat(st, temp, st_size - 1 - strlen(st)); } else { // Don't transmit this field if it's not valid //strncat(st, "h..", st_size - 1 - strlen(st)); } if (strlen(weather->wx_baro) > 0) { xastir_snprintf(temp, sizeof(temp), "b%05d", (int)((atof(weather->wx_baro) * 10.0)) ); if (strlen(temp)>6) { if (debug_level & 1) { fprintf(stderr,"wx_baro too long: %s\n", temp); } // Don't transmit this field if it's not valid //xastir_snprintf(temp,sizeof(temp),"b....."); } if ((int)((atof(weather->wx_baro) * 10.0) < 0)) { if (debug_level & 1) { fprintf(stderr,"wx_baro out-of-bounds: %s\n", weather->wx_baro); } // Don't transmit this field if it's not valid //xastir_snprintf(temp,sizeof(temp),"b....."); } strncat(st, temp, st_size - 1 - strlen(st)); } else { // Don't transmit this field if it's not valid //strncat(st, "b.....", st_size - 1 - strlen(st)); } xastir_snprintf(temp, sizeof(temp), "%c%s", weather->wx_type, weather->wx_station); strncat(st, temp, st_size - 1 - strlen(st)); } } if (debug_level & 1) { fprintf(stderr,"Weather String: %s\n", st); } return(wx_time); } /***********************************************************/ /* transmit raw wx data */ /* */ /***********************************************************/ void tx_raw_wx_data(void) { if (strlen(raw_wx_string)>10) { output_my_data(raw_wx_string,-1,0,0,0,NULL); if (debug_level & 1) { fprintf(stderr,"Sending Raw WX data <%s>\n",raw_wx_string); } } }