// check interval HILOWs against the station-generated archive record // (the VP is a goofy animal in terms of protocol) int computedDataCheckHiLows (WVIEWD_WORK *work, ARCHIVE_PKT *newRecord) { WV_SENSOR *sample = work->sensors.sensor[STF_INTERVAL]; float chill, dew, heat; if (sensorGetHigh(&sample[SENSOR_OUTTEMP]) < ((float)newRecord->value[DATA_INDEX_outTemp])) { // update the high value only sensorUpdateHighValue (&sample[SENSOR_OUTTEMP], (float)newRecord->value[DATA_INDEX_outTemp]); } if (sensorGetLow(&sample[SENSOR_OUTTEMP]) > ((float)newRecord->value[DATA_INDEX_outTemp])) { // update the low value only sensorUpdateLowValue (&sample[SENSOR_OUTTEMP], (float)newRecord->value[DATA_INDEX_outTemp]); } if (sensorGetHigh(&sample[SENSOR_WGUST]) < (float)newRecord->value[DATA_INDEX_windGust]) { // update the high and when high values only sensorUpdateWhenHighValue (&sample[SENSOR_WGUST], (float)newRecord->value[DATA_INDEX_windGust], (float)newRecord->value[DATA_INDEX_windGustDir]); } if (sensorGetHigh(&sample[SENSOR_RAINRATE]) < ((float)newRecord->value[DATA_INDEX_rainRate])) { // update the high value only sensorUpdateHighValue (&sample[SENSOR_RAINRATE], (float)newRecord->value[DATA_INDEX_rainRate]); } if ((newRecord->value[DATA_INDEX_rain] == 0) || (sensorGetCumulative(&sample[SENSOR_RAIN]) < ((float)newRecord->value[DATA_INDEX_rain]))) { // update the cumulative value only sensorUpdateCumulative (&sample[SENSOR_RAIN], (float)newRecord->value[DATA_INDEX_rain]); } if ((newRecord->value[DATA_INDEX_ET] == 0) || (sensorGetCumulative(&sample[SENSOR_ET]) < ((float)newRecord->value[DATA_INDEX_ET]))) { // update the cumulative value only sensorUpdateCumulative (&sample[SENSOR_ET], (float)newRecord->value[DATA_INDEX_ET]); } return OK; }
int stationSendArchiveNotifications (WVIEWD_WORK *work, float sampleRain) { WVIEW_MSG_ARCHIVE_NOTIFY notify; float tempfloat; notify.dateTime = work->archiveDateTime; notify.intemp = (int)floorf(work->loopPkt.inTemp * 10); notify.inhumidity = work->loopPkt.inHumidity; notify.temp = (int)floorf(work->loopPkt.outTemp * 10); notify.humidity = work->loopPkt.outHumidity; notify.barom = (int)floorf(work->loopPkt.barometer * 1000); notify.stationPressure = (int)floorf(work->loopPkt.stationPressure * 1000); notify.altimeter = (int)floorf(work->loopPkt.altimeter * 1000); notify.winddir = work->loopPkt.windDir; notify.wspeed = work->loopPkt.windSpeed; notify.dewpoint = (int)floorf(work->loopPkt.dewpoint * 10); notify.hiwspeed = work->loopPkt.windGust; notify.rxPercent = work->loopPkt.rxCheckPercent; notify.sampleRain = sampleRain; tempfloat = sensorGetCumulative (&work->sensors.sensor[STF_HOUR][SENSOR_RAIN]); tempfloat *= 100; tempfloat += 0.5; notify.rainHour = (int)tempfloat; tempfloat = sensorGetCumulative (&work->sensors.sensor[STF_DAY][SENSOR_RAIN]); tempfloat *= 100; tempfloat += 0.5; notify.rainDay = (int)tempfloat; if (radMsgRouterMessageSend (WVIEW_MSG_TYPE_ARCHIVE_NOTIFY, ¬ify, sizeof(notify)) == ERROR) { // can't send! radMsgLog (PRI_HIGH, "radMsgRouterMessageSend failed: notify"); return ERROR; } return OK; }
static int daemonStationLoopComplete (void) { float tempf, sampleRain, sampleET; if (!wviewdWork.runningFlag) { return OK; } // Adjust for calibrations: // DO NOT calibrate pressure here: wviewdWork.loopPkt.inTemp *= wviewdWork.calMInTemp; wviewdWork.loopPkt.inTemp += wviewdWork.calCInTemp; wviewdWork.loopPkt.outTemp *= wviewdWork.calMOutTemp; wviewdWork.loopPkt.outTemp += wviewdWork.calCOutTemp; wviewdWork.loopPkt.inHumidity *= wviewdWork.calMInHumidity; wviewdWork.loopPkt.inHumidity += wviewdWork.calCInHumidity; wviewdWork.loopPkt.outHumidity *= wviewdWork.calMOutHumidity; wviewdWork.loopPkt.outHumidity += wviewdWork.calCOutHumidity; wviewdWork.loopPkt.windSpeed *= wviewdWork.calMWindSpeed; wviewdWork.loopPkt.windSpeed += wviewdWork.calCWindSpeed; wviewdWork.loopPkt.windDir *= wviewdWork.calMWindDir; wviewdWork.loopPkt.windDir += wviewdWork.calCWindDir; wviewdWork.loopPkt.windDir %= 360; wviewdWork.loopPkt.sampleRain *= wviewdWork.calMRain; wviewdWork.loopPkt.sampleRain += wviewdWork.calCRain; wviewdWork.loopPkt.rainRate *= wviewdWork.calMRainRate; wviewdWork.loopPkt.rainRate += wviewdWork.calCRainRate; // now calculate a few after all calibrations: wviewdWork.loopPkt.dewpoint = wvutilsCalculateDewpoint(wviewdWork.loopPkt.outTemp, (float)wviewdWork.loopPkt.outHumidity); wviewdWork.loopPkt.windchill = wvutilsCalculateWindChill(wviewdWork.loopPkt.outTemp, (float)wviewdWork.loopPkt.windSpeed); wviewdWork.loopPkt.heatindex = wvutilsCalculateHeatIndex(wviewdWork.loopPkt.outTemp, (float)wviewdWork.loopPkt.outHumidity); // store the results: computedDataStoreSample (&wviewdWork); // lose any trace amounts to avoid rounding up by sprintf: sampleRain = sensorGetCumulative(&wviewdWork.sensors.sensor[STF_INTERVAL][SENSOR_RAIN]); sampleRain *= 100; sampleRain = floorf (sampleRain); sampleRain /= 100; sampleET = sensorGetCumulative(&wviewdWork.sensors.sensor[STF_INTERVAL][SENSOR_ET]); sampleET *= 1000; sampleET = floorf (sampleET); sampleET /= 1000; // do some post-processing on the LOOP data: tempf = stormRainGet(); if (tempf > 0) tempf += sampleRain; wviewdWork.loopPkt.stormRain = tempf; wviewdWork.loopPkt.stormStart = stormRainGetStartTimeT(); tempf = sensorGetCumulative(&wviewdWork.sensors.sensor[STF_DAY][SENSOR_RAIN]); tempf += sampleRain; wviewdWork.loopPkt.dayRain = tempf; tempf = sensorGetCumulative(&wviewdWork.sensors.sensor[STF_MONTH][SENSOR_RAIN]); tempf += sampleRain; wviewdWork.loopPkt.monthRain = tempf; tempf = sensorGetCumulative(&wviewdWork.sensors.sensor[STF_YEAR][SENSOR_RAIN]); tempf += sampleRain; wviewdWork.loopPkt.yearRain = tempf; if (sampleET > ARCHIVE_VALUE_NULL) { tempf = sensorGetCumulative(&wviewdWork.sensors.sensor[STF_DAY][SENSOR_ET]); tempf += sampleET; wviewdWork.loopPkt.dayET = tempf; tempf = sensorGetCumulative(&wviewdWork.sensors.sensor[STF_MONTH][SENSOR_ET]); tempf += sampleET; wviewdWork.loopPkt.monthET = tempf; tempf = sensorGetCumulative(&wviewdWork.sensors.sensor[STF_YEAR][SENSOR_ET]); tempf += sampleET; wviewdWork.loopPkt.yearET = tempf; } wviewdWork.loopPkt.yearRainMonth = wviewdWork.stationRainSeasonStart; statusIncrementStat(WVIEW_STATS_LOOP_PKTS_RX); return OK; }
static void daemonStoreArchiveRecord (ARCHIVE_PKT *newRecord) { float carryOverRain, carryOverET, sampleRain, tempf; int deltaTime, tempInt; USHORT tempRainBits; if (newRecord == NULL) { radMsgLog (PRI_MEDIUM, "daemonStoreArchiveRecord: record is NULL!"); return; } deltaTime = newRecord->dateTime - wviewdWork.archiveDateTime; if (deltaTime == 0) { // discard it, same as previous record radMsgLog (PRI_MEDIUM, "daemonStoreArchiveRecord: record has same timestamp as previous!"); return; } else if (deltaTime < 0) { // chunk it, it is just wrong radMsgLog (PRI_MEDIUM, "StoreArchiveRecord: record has earlier timestamp than previous (DST change?)"); return; } wviewdWork.archiveDateTime = newRecord->dateTime; wvutilsLogEvent (PRI_STATUS, "storing record for %4.4d-%2.2d-%2.2d %2.2d:%2.2d", wvutilsGetYear(newRecord->dateTime), wvutilsGetMonth(newRecord->dateTime), wvutilsGetDay(newRecord->dateTime), wvutilsGetHour(newRecord->dateTime), wvutilsGetMin(newRecord->dateTime)); if (dbsqliteArchiveStoreRecord(newRecord) == ERROR) { radMsgLog (PRI_MEDIUM, "daemonStoreArchiveRecord: dbsqliteArchiveStoreRecord failed!!!"); emailAlertSend(ALERT_TYPE_FILE_IO); return; } // if we are running normally (out of init), do normal activities: if (wviewdWork.runningFlag) { // Check to see if a DST change has occured: // Note: wvutilsDetectDSTChange can only be called once per process per // DST event. if (wvutilsDetectDSTChange() != WVUTILS_DST_NO_CHANGE) { radMsgLog (PRI_STATUS, "DST change: scheduling station time update (if supported)"); // Update the time zone info: tzset(); // Adjust station time: stationSyncTime(&wviewdWork); } // save trace accumulator amounts: if (newRecord->value[DATA_INDEX_rain] > ARCHIVE_VALUE_NULL) { sampleRain = (float)newRecord->value[DATA_INDEX_rain]; carryOverRain = sensorGetCumulative(&wviewdWork.sensors.sensor[STF_INTERVAL][SENSOR_RAIN]); carryOverRain -= sampleRain; if (carryOverRain < 0) { carryOverRain = 0; } } if (newRecord->value[DATA_INDEX_ET] > ARCHIVE_VALUE_NULL) { carryOverET = sensorGetCumulative(&wviewdWork.sensors.sensor[STF_INTERVAL][SENSOR_ET]); carryOverET -= (float)newRecord->value[DATA_INDEX_ET]; if (carryOverET < 0) { carryOverET = 0; } } // compute HILOW data: computedDataUpdate (&wviewdWork, newRecord); // clear for the next archive period (saving trace amounts): computedDataClearInterval (&wviewdWork, carryOverRain, carryOverET); // compute storm rain: if (newRecord->value[DATA_INDEX_rain] > ARCHIVE_VALUE_NULL && newRecord->value[DATA_INDEX_rainRate] > ARCHIVE_VALUE_NULL) { stormRainUpdate ((float)newRecord->value[DATA_INDEX_rainRate], (float)newRecord->value[DATA_INDEX_rain]); } // sync to sensors: wviewdWork.loopPkt.stormRain = stormRainGet(); wviewdWork.loopPkt.stormStart = stormRainGetStartTimeT(); wviewdWork.loopPkt.dayRain = sensorGetCumulative(&wviewdWork.sensors.sensor[STF_DAY][SENSOR_RAIN]); wviewdWork.loopPkt.monthRain = sensorGetCumulative(&wviewdWork.sensors.sensor[STF_MONTH][SENSOR_RAIN]); wviewdWork.loopPkt.yearRain = sensorGetCumulative(&wviewdWork.sensors.sensor[STF_YEAR][SENSOR_RAIN]); wviewdWork.loopPkt.dayET = sensorGetCumulative(&wviewdWork.sensors.sensor[STF_DAY][SENSOR_ET]); wviewdWork.loopPkt.monthET = sensorGetCumulative(&wviewdWork.sensors.sensor[STF_MONTH][SENSOR_ET]); wviewdWork.loopPkt.yearET = sensorGetCumulative(&wviewdWork.sensors.sensor[STF_YEAR][SENSOR_ET]); // send archive notification: stationSendArchiveNotifications (&wviewdWork, sampleRain); } statusIncrementStat(WVIEW_STATS_ARCHIVE_PKTS_RX); return; }
// get a daily cumulative value based on the RTF_INTERVAL and RTF_DAY time frames - float sensorGetDailyCumulative (WV_SENSOR set[STF_MAX][SENSOR_MAX], SENSOR_TYPES type) { return (sensorGetCumulative(&set[STF_INTERVAL][type]) + sensorGetCumulative(&set[STF_DAY][type])); }
ARCHIVE_PKT *computedDataGenerateArchive (WVIEWD_WORK *work) { WV_SENSOR *sample = work->sensors.sensor[STF_INTERVAL]; time_t nowtime = time (NULL); int tempInt; float tempfloat, tempfloat1; struct tm bknTime; Data_Indices index; // create the time_t time for the record: localtime_r (&nowtime, &bknTime); bknTime.tm_sec = 0; ArcRecStore.dateTime = (int32_t)mktime(&bknTime); ArcRecStore.usUnits = 1; ArcRecStore.interval = work->archiveInterval; // Set all values to NULL by default: for (index = DATA_INDEX_barometer; index < DATA_INDEX_MAX; index ++) { ArcRecStore.value[index] = ARCHIVE_VALUE_NULL; } // have we received any LOOP updates this interval? if (sensorGetSamples(&sample[SENSOR_OUTTEMP]) == 0) { // we have not - squawk about it and exit radMsgLog (PRI_MEDIUM, "computedDataGenerateArchive: no samples for this interval!"); return NULL; } // Set the values we can: ArcRecStore.value[DATA_INDEX_outTemp] = (float)sensorGetAvg (&sample[SENSOR_OUTTEMP]); ArcRecStore.value[DATA_INDEX_rain] = (float)sensorGetCumulative (&sample[SENSOR_RAIN]); ArcRecStore.value[DATA_INDEX_rainRate] = (float)sensorGetHigh (&sample[SENSOR_RAINRATE]); ArcRecStore.value[DATA_INDEX_barometer] = (float)sensorGetAvg (&sample[SENSOR_BP]); ArcRecStore.value[DATA_INDEX_pressure] = wvutilsConvertSLPToSP((float)ArcRecStore.value[DATA_INDEX_barometer], (float)ArcRecStore.value[DATA_INDEX_outTemp], (float)work->elevation); ArcRecStore.value[DATA_INDEX_altimeter] = wvutilsConvertSPToAltimeter((float)ArcRecStore.value[DATA_INDEX_pressure], (float)work->elevation); ArcRecStore.value[DATA_INDEX_inTemp] = (float)sensorGetAvg (&sample[SENSOR_INTEMP]); ArcRecStore.value[DATA_INDEX_inHumidity] = (float)sensorGetAvg (&sample[SENSOR_INHUMID]); ArcRecStore.value[DATA_INDEX_outHumidity] = (float)sensorGetAvg (&sample[SENSOR_OUTHUMID]); ArcRecStore.value[DATA_INDEX_windSpeed] = (float)sensorGetAvg (&sample[SENSOR_WSPEED]); if (sensorGetHigh (&sample[SENSOR_WGUST]) >= 0) { ArcRecStore.value[DATA_INDEX_windGust] = (float)sensorGetHigh (&sample[SENSOR_WGUST]); } else { ArcRecStore.value[DATA_INDEX_windGust] = 0; } // save the high wind speed in the loop packet work->loopPkt.windGust = (uint16_t)ArcRecStore.value[DATA_INDEX_windGust]; ArcRecStore.value[DATA_INDEX_dewpoint] = wvutilsCalculateDewpoint ((float)ArcRecStore.value[DATA_INDEX_outTemp], (float)ArcRecStore.value[DATA_INDEX_outHumidity]); ArcRecStore.value[DATA_INDEX_windchill] = wvutilsCalculateWindChill ((float)ArcRecStore.value[DATA_INDEX_outTemp], (float)ArcRecStore.value[DATA_INDEX_windSpeed]); ArcRecStore.value[DATA_INDEX_heatindex] = wvutilsCalculateHeatIndex ((float)ArcRecStore.value[DATA_INDEX_outTemp], (float)ArcRecStore.value[DATA_INDEX_outHumidity]); if (ArcRecStore.value[DATA_INDEX_windSpeed] > 0 || ArcRecStore.value[DATA_INDEX_windGust] > 0) { ArcRecStore.value[DATA_INDEX_windDir] = (float)windAverageCompute(&work->sensors.wind[STF_INTERVAL]); ArcRecStore.value[DATA_INDEX_windGustDir] = (float)sensorGetWhenHigh(&sample[SENSOR_WGUST]); } // These are conditional based on loop data being populated: if (work->loopPkt.radiation != 0xFFFF) ArcRecStore.value[DATA_INDEX_radiation] = (float)sensorGetAvg (&sample[SENSOR_SOLRAD]); if (work->loopPkt.UV != 0xFFFF) ArcRecStore.value[DATA_INDEX_UV] = (float)sensorGetAvg (&sample[SENSOR_UV]); if (work->loopPkt.sampleET != ARCHIVE_VALUE_NULL) ArcRecStore.value[DATA_INDEX_ET] = (float)sensorGetCumulative (&sample[SENSOR_ET]); if (work->loopPkt.wxt510Hail != ARCHIVE_VALUE_NULL) ArcRecStore.value[DATA_INDEX_hail] = (float)sensorGetCumulative (&sample[SENSOR_HAIL]); if (work->loopPkt.wxt510Hailrate != ARCHIVE_VALUE_NULL) ArcRecStore.value[DATA_INDEX_hailrate] = (float)sensorGetHigh (&sample[SENSOR_HAILRATE]); // Get a few directly from the last LOOP_PKT: if (work->loopPkt.rxCheckPercent != 0xFFFF) ArcRecStore.value[DATA_INDEX_rxCheckPercent] = (float)work->loopPkt.rxCheckPercent; if (work->loopPkt.wxt510HeatingTemp != ARCHIVE_VALUE_NULL) ArcRecStore.value[DATA_INDEX_heatingTemp] = (float)work->loopPkt.wxt510HeatingTemp; if (work->loopPkt.wxt510HeatingVoltage != ARCHIVE_VALUE_NULL) ArcRecStore.value[DATA_INDEX_heatingVoltage] = (float)work->loopPkt.wxt510HeatingVoltage; if (work->loopPkt.wxt510SupplyVoltage != ARCHIVE_VALUE_NULL) ArcRecStore.value[DATA_INDEX_supplyVoltage] = (float)work->loopPkt.wxt510SupplyVoltage; if (work->loopPkt.wxt510ReferenceVoltage != ARCHIVE_VALUE_NULL) ArcRecStore.value[DATA_INDEX_referenceVoltage] = (float)work->loopPkt.wxt510ReferenceVoltage; if (work->loopPkt.wmr918WindBatteryStatus != 0xFF) ArcRecStore.value[DATA_INDEX_windBatteryStatus] = (float)work->loopPkt.wmr918WindBatteryStatus; if (work->loopPkt.wmr918RainBatteryStatus != 0xFF) ArcRecStore.value[DATA_INDEX_rainBatteryStatus] = (float)work->loopPkt.wmr918RainBatteryStatus; if (work->loopPkt.wmr918OutTempBatteryStatus != 0xFF) ArcRecStore.value[DATA_INDEX_outTempBatteryStatus] = (float)work->loopPkt.wmr918OutTempBatteryStatus; if (work->loopPkt.wmr918InTempBatteryStatus != 0xFF) ArcRecStore.value[DATA_INDEX_inTempBatteryStatus] = (float)work->loopPkt.wmr918InTempBatteryStatus; return &ArcRecStore; }