Ejemplo n.º 1
0
int wmr918Init (WVIEWD_WORK *work)
{
    WMR918_IF_DATA*     ifWorkData = (WMR918_IF_DATA*)work->stationData;
    fd_set              rfds;
    struct timeval      tv;
    int                 i, length, retVal;
    time_t              nowTime = time(NULL) - (WV_SECONDS_IN_HOUR/(60/WMR918_RAIN_RATE_PERIOD));
    ARCHIVE_PKT         recordStore;
    char                outString[128];

    memset (&wmr918Work, 0, sizeof(wmr918Work));

    // Create the rain accumulator (WMR918_RAIN_RATE_PERIOD minute age) 
    // so we can compute rain rate:
    ifWorkData->rainRateAccumulator = sensorAccumInit(WMR918_RAIN_RATE_PERIOD);

    // Populate the accumulator with the last WMR918_RAIN_RATE_PERIOD minutes:
    while ((nowTime = dbsqliteArchiveGetNextRecord(nowTime, &recordStore)) != ERROR)
    {
        sensorAccumAddSample(ifWorkData->rainRateAccumulator, 
                             recordStore.dateTime, 
                             recordStore.value[DATA_INDEX_rain]);
    }

    radMsgLog (PRI_MEDIUM, 
        "wmr918Init: waiting for first sensor packets (this may take some time):");
    while ((wmr918Work.dataRXMask != WMR918_SENSOR_ALL) && (! work->exiting))
    {
        // Log what we are waiting for:
        length = 0;
        for (i = 0; i < 4; i ++)
        {
            if (! (wmr918Work.dataRXMask & (1 << i)))
            {
                length += sprintf(&outString[length], "%s ", WMRSensorNames[i]);
            }
        }
        radMsgLog (PRI_MEDIUM, "wmr918Init: waiting for sensors: %s", outString);

        tv.tv_sec  = 2;
        tv.tv_usec = 0;

        FD_ZERO(&rfds);
        FD_SET(work->medium.fd, &rfds);
        if (select (work->medium.fd + 1, &rfds, NULL, NULL, &tv) > 0)
        {
            retVal = readStationData (work);
            switch (retVal)
            {
                case WMR918GROUP0:
                    radMsgLog (PRI_MEDIUM, "received WIND packet...");
                    break;
                case WMR918GROUP1:
                    radMsgLog (PRI_MEDIUM, "received RAIN packet...");
                    break;
                case WMR918GROUP2:
                    radMsgLog (PRI_MEDIUM, "received EXTRA TEMP packet...");
                    break;
                case WMR918GROUP3:
                    radMsgLog (PRI_MEDIUM, "received OUT TEMP packet...");
                    break;
                case WMR918GROUP4:
                    radMsgLog (PRI_MEDIUM, "received POOL TEMP packet...");
                    break;
                case WMR918GROUP5:
                case WMR918GROUP6:
                    radMsgLog (PRI_MEDIUM, "received IN TEMP packet...");
                    break;
                default:
                    break;
            }
        }
    }

    if (! work->exiting)
    {
        radMsgLog (PRI_MEDIUM, "wmr918Init: first sensor packets received.");
    }

    // populate the LOOP structure:
    ifWorkData->wmr918Readings = wmr918Work.sensorData;
    storeLoopPkt (work, &work->loopPkt, &ifWorkData->wmr918Readings);

    // we must indicate successful completion here -
    // even though we are synchronous, the daemon wants to see this event
    radProcessEventsSend (NULL, STATION_INIT_COMPLETE_EVENT, 0);

    return OK;
}
Ejemplo n.º 2
0
static void storeLoopPkt (WVIEWD_WORK *work, LOOP_PKT *dest, WMR918_DATA *src)
{
    float               tempfloat;
    WMR918_IF_DATA*     ifWorkData = (WMR918_IF_DATA*)work->stationData;
    time_t              nowTime = time(NULL);

    // Clear optional data:
    stationClearLoopData(work);


    // WMR918 produces station pressure
    if (10 < src->pressure && src->pressure < 50)
    {
        dest->stationPressure               = src->pressure;

        // Apply calibration here so the computed values reflect it:
        dest->stationPressure *= work->calMPressure;
        dest->stationPressure += work->calCPressure;

        if (-150 < src->outTemp && src->outTemp < 150)
        {
            // compute sea-level pressure (BP)
            dest->barometer                 = wvutilsConvertSPToSLP (dest->stationPressure,
                                                                     src->outTemp,
                                                                     (float)ifWorkData->elevation);
        }

        // calculate altimeter
        dest->altimeter                     = wvutilsConvertSPToAltimeter (dest->stationPressure,
                                                                         (float)ifWorkData->elevation);
    }

    if (-150 < src->outTemp && src->outTemp < 150)
        dest->outTemp                       = src->outTemp;

    if (0 <= src->outHumidity && src->outHumidity <= 100)
    {
        tempfloat = src->outHumidity;
        tempfloat += 0.5;
        dest->outHumidity                   = (USHORT)tempfloat;
    }

    if (0 <= src->windSpeed && src->windSpeed <= 250)
    {
        tempfloat = src->windSpeed;
        tempfloat += 0.5;
        dest->windSpeed                     = (USHORT)tempfloat;
    }

    if (0 <= src->windDir && src->windDir <= 360)
    {
        tempfloat = src->windDir;
        tempfloat += 0.5;
        dest->windDir                       = (USHORT)tempfloat;
    }

    if (0 <= src->maxWindSpeed && src->maxWindSpeed <= 250)
    {
        tempfloat = src->maxWindSpeed;
        tempfloat += 0.5;
        dest->windGust                      = (USHORT)tempfloat;
    }

    if (0 <= src->maxWindDir && src->maxWindDir <= 360)
    {
        tempfloat = src->maxWindDir;
        tempfloat += 0.5;
        dest->windGustDir                   = (USHORT)tempfloat;
    }

    if (0 <= src->rain)
    {
        if (!work->runningFlag)
        {
            // just starting, so start with whatever the station reports:
            ifWorkData->totalRain = src->rain;
            dest->sampleRain = 0;
        }
        else
        {
            // process the rain accumulator
            if (src->rain - ifWorkData->totalRain >= 0)
            {
                dest->sampleRain = src->rain - ifWorkData->totalRain;
                ifWorkData->totalRain = src->rain;
            }
            else
            {
                // we had a counter reset...
                dest->sampleRain = src->rain;
                ifWorkData->totalRain = src->rain;
            }
        }

        if (dest->sampleRain > 2)
        {
            // Not possible, filter it out:
            dest->sampleRain = 0;
        }

        // Compute rain rate - the WMR918 rain rate is poor...
        // Update the rain accumulator:
        sensorAccumAddSample (ifWorkData->rainRateAccumulator, nowTime, dest->sampleRain);
        dest->rainRate                      = sensorAccumGetTotal (ifWorkData->rainRateAccumulator);
        dest->rainRate                      *= (60/WMR918_RAIN_RATE_PERIOD);
    }
    else
    {
        dest->sampleRain = 0;
        sensorAccumAddSample (ifWorkData->rainRateAccumulator, nowTime, dest->sampleRain);
        dest->rainRate                      = sensorAccumGetTotal (ifWorkData->rainRateAccumulator);
        dest->rainRate                      *= (60/WMR918_RAIN_RATE_PERIOD);
    }        

    dest->inTemp                        = src->inTemp;
    tempfloat = src->inHumidity;
    tempfloat += 0.5;
    dest->inHumidity                    = (USHORT)tempfloat;

    dest->extraTemp1                    = (float)src->extraTemp[0];
    tempfloat = src->extraHumidity[0];
    tempfloat += 0.5;
    dest->extraHumid1                   = (USHORT)tempfloat;

    dest->extraTemp2                    = (float)src->extraTemp[1];
    tempfloat = src->extraHumidity[1];
    tempfloat += 0.5;
    dest->extraHumid2                   = (USHORT)tempfloat;

    dest->extraTemp3                    = (float)src->extraTemp[2];

    // WMR918 specific:
    tempfloat = src->extraHumidity[2];
    tempfloat += 0.5;
    dest->wmr918Humid3                  = (USHORT)tempfloat;

    dest->wmr918Pool                    = src->pool;

    dest->wmr918WindBatteryStatus       = src->windBatteryStatus;
    dest->wmr918RainBatteryStatus       = src->rainBatteryStatus;
    dest->wmr918OutTempBatteryStatus    = src->outTempBatteryStatus;
    dest->wmr918InTempBatteryStatus     = src->inTempBatteryStatus;
    dest->wmr918poolTempBatteryStatus   = src->poolTempBatteryStatus;
    dest->wmr918extra1BatteryStatus     = src->extraBatteryStatus[0];
    dest->wmr918extra2BatteryStatus     = src->extraBatteryStatus[1];
    dest->wmr918extra3BatteryStatus     = src->extraBatteryStatus[2];
    dest->wmr918Tendency                = src->tendency;

    return;
}
Ejemplo n.º 3
0
static void storeLoopPkt (WVIEWD_WORK* work, LOOP_PKT *dest, TWI_DATA *src)
{
    float           tempfloat;
    static time_t   lastTempUpdate;
    time_t          nowTime = time(NULL);

    // Clear optional data:
    stationClearLoopData(work);

    // Pressure:
    if ((10 < src->bp && src->bp < 50) &&
        (-150 < src->outTemp && src->outTemp < 200))
    {
        if ((time(NULL) - lastTempUpdate) >= (twiWorkData.archiveInterval * 60))
        {
            // Add for the 12-hour temp average sample:
            // mimic an archive interval:
            sensorAccumAddSample(twi12HourTempAvg, time(NULL), src->outTemp);
            lastTempUpdate = time(NULL);
        }

        // TWI produces sea level pressure (SLP):
        dest->barometer = src->bp;

        // Apply calibration here so the computed values reflect it:
        dest->barometer *= work->calMBarometer;
        dest->barometer += work->calCBarometer;

        // calculate station pressure:
        dest->stationPressure = twiConvertSLPToSP(dest->barometer, 
                                                 sensorAccumGetAverage(twi12HourTempAvg), 
                                                 (float)twiWorkData.elevation);

        // now calculate altimeter:
        dest->altimeter = wvutilsConvertSPToAltimeter(dest->stationPressure,
                                                      (float)twiWorkData.elevation);
    }


    if (-150 < src->inTemp && src->inTemp < 200)
    {
        dest->inTemp                        = src->inTemp;
    }

    if (-150 < src->outTemp && src->outTemp < 200)
    {
        dest->outTemp                       = src->outTemp;
    }

    if (0 <= src->humidity && src->humidity <= 100)
    {
        dest->outHumidity                   = (uint16_t)src->humidity;
        dest->inHumidity                    = (uint16_t)src->humidity;
    }

    if (0 <= src->windSpeed && src->windSpeed <= 250)
    {
        tempfloat = src->windSpeed;
        tempfloat += 0.5;
        dest->windSpeed                     = (uint16_t)tempfloat;
        dest->windGust                      = (uint16_t)tempfloat;
    }

    if (0 <= src->windDir && src->windDir < 360)
    {
        tempfloat = src->windDir;
        tempfloat += 0.5;
        dest->windDir                       = (uint16_t)tempfloat;
        dest->windGustDir                   = (uint16_t)tempfloat;
    }

    dest->rainRate                          = src->rainrate;

    // process the rain accumulator:
    if (0 <= src->dayrain)
    {
        if (! work->runningFlag)
        {
            // just starting, so start with whatever the station reports:
            twiWorkData.totalRain = src->dayrain;
            dest->sampleRain = 0;
        }
        else
        {
            // process the rain accumulator
            if (src->dayrain - twiWorkData.totalRain >= 0)
            {
                dest->sampleRain = src->dayrain - twiWorkData.totalRain;
                twiWorkData.totalRain = src->dayrain;
            }
            else
            {
                // we had a counter reset...
                dest->sampleRain = src->dayrain;
                twiWorkData.totalRain = src->dayrain;
            }
        }

        if (dest->sampleRain > 2)
        {
            // Not possible, filter it out:
            dest->sampleRain = 0;
        }

        // Update the rain accumulator:
        sensorAccumAddSample (twiWorkData.rainRateAccumulator, nowTime, dest->sampleRain);
        dest->rainRate    = sensorAccumGetTotal (twiWorkData.rainRateAccumulator);
        dest->rainRate   *= (60/TWI_RAIN_RATE_PERIOD);
    }
    else
    {
        dest->sampleRain = 0;
        sensorAccumAddSample (twiWorkData.rainRateAccumulator, nowTime, dest->sampleRain);
        dest->rainRate   = sensorAccumGetTotal (twiWorkData.rainRateAccumulator);
        dest->rainRate   *= (60/TWI_RAIN_RATE_PERIOD);
    }

    return;
}
Ejemplo n.º 4
0
// station-supplied init function
// -- Can Be Asynchronous - event indication required --
//
// MUST:
//   - set the 'stationGeneratesArchives' flag in WVIEWD_WORK:
//     if the station generates archive records (TRUE) or they should be
//     generated automatically by the daemon from the sensor readings (FALSE)
//   - Initialize the 'stationData' store for station work area
//   - Initialize the interface medium
//   - do initial LOOP acquisition
//   - do any catch-up on archive records if there is a data logger
//   - 'work->runningFlag' can be used for start up synchronization but should
//     not be modified by the station interface code
//   - indicate init is done by sending the STATION_INIT_COMPLETE_EVENT event to
//     this process (radProcessEventsSend (NULL, STATION_INIT_COMPLETE_EVENT, 0))
//
// OPTIONAL:
//   - Initialize a state machine or any other construct required for the
//     station interface - these should be stored in the 'stationData' store
//
// 'archiveIndication' - indication callback used to pass back an archive record
//   generated as a result of 'stationGetArchive' being called; should receive a
//   NULL pointer for 'newRecord' if no record available; only used if
//   'stationGeneratesArchives' flag is set to TRUE by the station interface
//
// Returns: OK or ERROR
//
int stationInit
(
    WVIEWD_WORK     *work,
    void            (*archiveIndication)(ARCHIVE_PKT* newRecord)
)
{
    int             i;
    ARCHIVE_PKT     recordStore;
    time_t          nowTime;

    memset (&twiWorkData, 0, sizeof(twiWorkData));
    pwviewWork = work;
    twiWorkData.baudrate = B19200;

    // save the archive indication callback (we should never need it)
    ArchiveIndicator = archiveIndication;

    // set our work data pointer
    work->stationData = &twiWorkData;

    // set the Archive Generation flag to indicate the TWI DOES NOT
    // generate them
    work->stationGeneratesArchives = FALSE;

    // initialize the medium abstraction based on user configuration
    if (!strcmp (work->stationInterface, "serial"))
    {
        if (serialMediumInit (&work->medium, serialPortConfig, O_RDWR | O_NOCTTY | O_NDELAY) == ERROR)
        {
            radMsgLog (PRI_HIGH, "stationInit: serial MediumInit failed");
            return ERROR;
        }
    }
    else if (!strcmp (work->stationInterface, "ethernet"))
    {
        if (ethernetMediumInit (&work->medium, work->stationHost, work->stationPort)
                == ERROR)
        {
            radMsgLog (PRI_HIGH, "stationInit: ethernet MediumInit failed");
            return ERROR;
        }
    }
    else
    {
        radMsgLog (PRI_HIGH, "stationInit: medium %s not supported",
                   work->stationInterface);
        return ERROR;
    }

    // initialize the interface using the media specific routine
    if ((*(work->medium.init)) (&work->medium, work->stationDevice) == ERROR)
    {
        radMsgLog (PRI_HIGH, "stationInit: medium setup failed");
        return ERROR;
    }

    // Autobaud the station now:
    twiWorkData.baudrate = twiConfig(work);
    if (twiWorkData.baudrate <= 0)
    {
        radMsgLog (PRI_HIGH, "TWI: twiProtocolInit: autobaud failed!");
        return ERROR;
    }

    radMsgLog (PRI_STATUS, "TWI: autobaud = %d", twiWorkData.baudrate);

    // Reconfigure the serial port here to ensure consistency:
    serialPortConfig(work->medium.fd);
    tcflush (work->medium.fd, TCIFLUSH);
    tcflush (work->medium.fd, TCOFLUSH);

    if (!strcmp (work->stationInterface, "serial"))
    {
        radMsgLog (PRI_STATUS, "TWI on %s opened ...",
                   work->stationDevice);
    }
    else if (!strcmp (work->stationInterface, "ethernet"))
    {
        radMsgLog (PRI_STATUS, "TWI on %s:%d opened ...",
                   work->stationHost, work->stationPort);
    }

    // grab the station configuration now
    if (stationGetConfigValueInt (work,
                                  STATION_PARM_ELEVATION,
                                  &twiWorkData.elevation)
            == ERROR)
    {
        radMsgLog (PRI_HIGH, "stationInit: stationGetConfigValueInt ELEV failed!");
        (*(work->medium.exit)) (&work->medium);
        return ERROR;
    }
    if (stationGetConfigValueFloat (work,
                                    STATION_PARM_LATITUDE,
                                    &twiWorkData.latitude)
            == ERROR)
    {
        radMsgLog (PRI_HIGH, "stationInit: stationGetConfigValueInt LAT failed!");
        (*(work->medium.exit)) (&work->medium);
        return ERROR;
    }
    if (stationGetConfigValueFloat (work,
                                    STATION_PARM_LONGITUDE,
                                    &twiWorkData.longitude)
            == ERROR)
    {
        radMsgLog (PRI_HIGH, "stationInit: stationGetConfigValueInt LONG failed!");
        (*(work->medium.exit)) (&work->medium);
        return ERROR;
    }
    if (stationGetConfigValueInt (work,
                                  STATION_PARM_ARC_INTERVAL,
                                  &twiWorkData.archiveInterval)
            == ERROR)
    {
        radMsgLog (PRI_HIGH, "stationInit: stationGetConfigValueInt ARCINT failed!");
        (*(work->medium.exit)) (&work->medium);
        return ERROR;
    }

    // set the work archive interval now
    work->archiveInterval = twiWorkData.archiveInterval;

    // sanity check the archive interval against the most recent record
    if (stationVerifyArchiveInterval (work) == ERROR)
    {
        // bad magic!
        radMsgLog (PRI_HIGH, "stationInit: stationVerifyArchiveInterval failed!");
        radMsgLog (PRI_HIGH, "You must either move old archive data out of the way -or-");
        radMsgLog (PRI_HIGH, "fix the interval setting...");
        (*(work->medium.exit)) (&work->medium);
        return ERROR;
    }
    else
    {
        radMsgLog (PRI_STATUS, "station archive interval: %d minutes",
                   work->archiveInterval);
    }

    twiWorkData.totalRain = 0;

   // Create the rain accumulator (TWI_RAIN_RATE_PERIOD minute age)
    // so we can compute rain rate:
    twiWorkData.rainRateAccumulator = sensorAccumInit(TWI_RAIN_RATE_PERIOD);

    // Populate the accumulator with the last TWI_RAIN_RATE_PERIOD minutes:
    nowTime = time(NULL) - (WV_SECONDS_IN_HOUR/(60/TWI_RAIN_RATE_PERIOD));
    while ((nowTime = dbsqliteArchiveGetNextRecord(nowTime, &recordStore)) != ERROR)
    {
        sensorAccumAddSample(twiWorkData.rainRateAccumulator,
                             recordStore.dateTime,
                             recordStore.value[DATA_INDEX_rain]);
    }

    radMsgLog (PRI_STATUS, "Starting station interface: TWI"); 

    // initialize the station interface
    if (twiProtocolInit(work) == ERROR)
    {
        radMsgLog (PRI_HIGH, "stationInit: twiProtocolInit failed!");
        (*(work->medium.exit)) (&work->medium);
        return ERROR;
    }

    // do the initial GetReadings now
    if (twiProtocolGetReadings(work, &twiWorkData.twiReadings) != OK)
    {
        radMsgLog (PRI_HIGH, "stationInit: initial twiProtocolGetReadings failed!");
        twiProtocolExit (work);
        (*(work->medium.exit)) (&work->medium);
        return ERROR;
    }


    // Initialize the 12-hour temp accumulator:
    twi12HourTempAvg = sensorAccumInit(60 * 12);

    // Load data for the last 12 hours:
    nowTime = time(NULL) - (WV_SECONDS_IN_HOUR * 12);
    while ((nowTime = dbsqliteArchiveGetNextRecord(nowTime, &recordStore)) != ERROR)
    {
        sensorAccumAddSample(twi12HourTempAvg, 
                             recordStore.dateTime, 
                             recordStore.value[DATA_INDEX_outTemp]);
    }

    // populate the LOOP structure
    storeLoopPkt (work, &work->loopPkt, &twiWorkData.twiReadings);

    // we must indicate successful completion here -
    // even though we are synchronous, the daemon wants to see this event
    radProcessEventsSend(NULL, STATION_INIT_COMPLETE_EVENT, 0);

    return OK;
}
Ejemplo n.º 5
0
int wh1080Init (WVIEWD_WORK *work)
{
    WH1080_IF_DATA*     ifWorkData = (WH1080_IF_DATA*)work->stationData;
    fd_set              rfds;
    struct timeval      tv;
    int                 ret;
    time_t              nowTime = time(NULL) - (WV_SECONDS_IN_HOUR/(60/WH1080_RAIN_RATE_PERIOD));
    ARCHIVE_PKT         recordStore;
    unsigned char       controlBlock[WH1080_BUFFER_CHUNK];

    memset (&wh1080Work, 0, sizeof(wh1080Work));

    // Create the rain accumulator (WH1080_RAIN_RATE_PERIOD minute age)
    // so we can compute rain rate:
    ifWorkData->rainRateAccumulator = sensorAccumInit(WH1080_RAIN_RATE_PERIOD);

    // Populate the accumulator with the last WH1080_RAIN_RATE_PERIOD minutes:
    while ((nowTime = dbsqliteArchiveGetNextRecord(nowTime, &recordStore)) != ERROR)
    {
        sensorAccumAddSample(ifWorkData->rainRateAccumulator,
                             recordStore.dateTime,
                             recordStore.value[DATA_INDEX_rain]);
    }

    if ((*(work->medium.usbhidInit))(&work->medium) != OK)
    {
        return ERROR;
    }

    // Set the station to log data once per minute:
    while ((!work->exiting) && (readFixedBlock(work, controlBlock) != OK))
    {
        radMsgLog (PRI_HIGH, "WH1080: Initial fixed block read failed");
        (*(work->medium.usbhidExit))(&work->medium);
        radUtilsSleep(5000);
        (*(work->medium.usbhidInit))(&work->medium);
        radMsgLog (PRI_HIGH, "WH1080: Retrying initial fixed block read");
    }

    // For some reason the WH1080 wants the IF closed between a read and a write:
    (*(work->medium.usbhidExit))(&work->medium);
    if (work->exiting)
    {
        return ERROR;
    }

    controlBlock[WH1080_SAMPLING_INTERVAL] = 1;
    (*(work->medium.usbhidInit))(&work->medium);

    if (writeFixedBlock(work, controlBlock) == ERROR)
    {
        (*(work->medium.usbhidExit))(&work->medium);
        return ERROR;
    }

    radUtilsSleep(2000);

    (*(work->medium.usbhidExit))(&work->medium);

    radUtilsSleep(1000);

    wh1080Work.lastRecord = -1;

    // populate the LOOP structure:
    radMsgLog (PRI_HIGH, "Waiting for the next weather record to be ready "
                         "in the console to populate initial wview sensor readings "
                         "(this could take some time);");
    radMsgLog (PRI_HIGH, "While waiting be sure you are receiving all sensors on the console;");
    radMsgLog (PRI_HIGH, "if not, you may need to relocate the sensors or the console.");
    while ((!work->exiting) && (readStationData(work) != OK))
    {
        radUtilsSleep(1000);
    }
    if (work->exiting)
    {
        return ERROR;
    }

    ifWorkData->wh1080Readings = wh1080Work.sensorData;
    storeLoopPkt (work, &work->loopPkt, &ifWorkData->wh1080Readings);

    // we must indicate successful completion here -
    // even though we are synchronous, the daemon wants to see this event:
    radProcessEventsSend (NULL, STATION_INIT_COMPLETE_EVENT, 0);

    return OK;
}
Ejemplo n.º 6
0
static void storeLoopPkt (WVIEWD_WORK *work, LOOP_PKT *dest, WH1080_DATA *src)
{
    float               tempfloat;
    WH1080_IF_DATA*     ifWorkData = (WH1080_IF_DATA*)work->stationData;
    time_t              nowTime = time(NULL);

    // Clear optional data:
    stationClearLoopData(work);

    if ((10 < src->pressure && src->pressure < 50) &&
        (-150 < src->outtemp && src->outtemp < 150))
    {
        // WH1080 produces station pressure
        dest->stationPressure               = src->pressure;
    
        // Apply calibration here so the computed values reflect it:
        dest->stationPressure *= work->calMPressure;
        dest->stationPressure += work->calCPressure;
    
        // compute sea-level pressure (BP)
        tempfloat = wvutilsConvertSPToSLP(dest->stationPressure,
                                          src->outtemp,
                                          (float)ifWorkData->elevation);
        dest->barometer                     = tempfloat;
    
        // calculate altimeter
        tempfloat = wvutilsConvertSPToAltimeter(dest->stationPressure,
                                                (float)ifWorkData->elevation);
        dest->altimeter                     = tempfloat;
    }

    if (-150 < src->outtemp && src->outtemp < 150)
    {
        dest->outTemp  = src->outtemp;
    }

    if (0 <= src->outhumidity && src->outhumidity <= 100)
    {
        tempfloat = src->outhumidity;
        tempfloat += 0.5;
        dest->outHumidity  = (uint16_t)tempfloat;
    }

    if (0 <= src->windAvgSpeed && src->windAvgSpeed <= 250)
    {
        tempfloat = src->windAvgSpeed;
        dest->windSpeedF  = tempfloat;
    }

    if (0 <= src->windDir && src->windDir <= 360)
    {
        tempfloat = src->windDir;
        tempfloat += 0.5;
        dest->windDir        = (uint16_t)tempfloat;
        dest->windGustDir    = (uint16_t)tempfloat;
    }

    if (0 <= src->windGustSpeed && src->windGustSpeed <= 250)
    {
        tempfloat = src->windGustSpeed;
        dest->windGustF      = tempfloat;
    }

    if (0 <= src->rain)
    {
        if (!work->runningFlag)
        {
            // just starting, so start with whatever the station reports:
            ifWorkData->totalRain = src->rain;
            dest->sampleRain = 0;
        }
        else
        {
            // process the rain accumulator
            if (src->rain - ifWorkData->totalRain >= 0)
            {
                dest->sampleRain = src->rain - ifWorkData->totalRain;
                ifWorkData->totalRain = src->rain;
            }
            else
            {
                // we had a counter reset...
                dest->sampleRain = src->rain;
                dest->sampleRain += (WH1080_RAIN_MAX - ifWorkData->totalRain);
                ifWorkData->totalRain = src->rain;
            }
        }

        if (dest->sampleRain > 2)
        {
            // Not possible, filter it out:
            dest->sampleRain = 0;
        }

        // Update the rain accumulator:
        sensorAccumAddSample (ifWorkData->rainRateAccumulator, nowTime, dest->sampleRain);
        dest->rainRate    = sensorAccumGetTotal (ifWorkData->rainRateAccumulator);
        dest->rainRate   *= (60/WH1080_RAIN_RATE_PERIOD);
    }
    else
    {
        dest->sampleRain = 0;
        sensorAccumAddSample (ifWorkData->rainRateAccumulator, nowTime, dest->sampleRain);
        dest->rainRate                      = sensorAccumGetTotal (ifWorkData->rainRateAccumulator);
        dest->rainRate                      *= (60/WH1080_RAIN_RATE_PERIOD);
    }

    dest->inTemp                        = src->intemp;
    tempfloat = src->inhumidity;
    tempfloat += 0.5;
    dest->inHumidity                    = (uint16_t)tempfloat;

    return;
}