float wvutilsCalculateAirDensity (float tempF, float bp, float dp) { float tempC = wvutilsConvertFToC (tempF); float bpMB = wvutilsConvertINHGToHPA (bp); float dewpoint = wvutilsConvertFToC (dp); float emb = getVaporPressure (dewpoint); float density = calcDensity (bpMB, emb, tempC); return density; }
// Calculate the apparent temperature: // Australian Bureau of Meteorology http://reg.bom.gov.au/info/thermal_stress/#atapproximation [^] // Robert G. Steadman. 1994: Norms of apparent temperature in Australia. float wvutilsCalculateApparentTemp(float temp, float windspeed, float humidity) { double AT,Ta,e,ws; double result; Ta = wvutilsConvertFToC(temp); e = humidity / 100.0 * 6.105 * exp(17.27 * Ta/( 237.7 + Ta)); ws = wvutilsConvertMPHToMPS(windspeed); AT = Ta + 0.33 * e - 0.70 * ws - 4.00; result = wvutilsConvertCToF(AT); return (float)result; }
// Calculate the "e ^ (-mgh/RT)" term for pressure conversions: static double calcPressureTerm(float tempF, float elevationFT) { double exponent; double elevMeters = (double)wvutilsConvertFeetToMeters(elevationFT); double tempKelvin = (double)wvutilsConvertFToC(tempF) + 273.15; // e ^ -elevMeters/(tempK * 29.263) exponent = (-elevMeters); // degrees Kelvin (T) exponent /= (tempKelvin * 29.263); // e ^ (-mgh/RT) exponent = exp(exponent); return exponent; }
// Calculate the wet bulb temperature: // Based on NOAA Java Script http://www.srh.noaa.gov/epz/?n=wxcalc_rh float wvutilsCalculateWetBulbTemp( float temp, float humidity, float pressure) { float tempC = wvutilsConvertFToC(temp); float pressureMB = wvutilsConvertINHGToHPA(pressure); float Ewguess, Eguess; float Es = 6.112 * exp(17.67 * tempC / (tempC + 243.5)); float Twguess = 0.0; float incr = 10.0; int previoussign = 1; int cursign; float Edifference = 1; float E2 = Es * (humidity/100.0); while (fabs(Edifference) > 0.05) { Ewguess = 6.112 * exp((17.67 * Twguess) / (Twguess + 243.5)); Eguess = Ewguess - pressureMB * (tempC - Twguess) * 0.00066 * (1.0 + (0.00115 * Twguess)); Edifference = E2 - Eguess; if (Edifference == 0) { break; } else { if (Edifference < 0) cursign = -1; else cursign = 1; if (cursign != previoussign) { previoussign = cursign; incr = incr/10; } } Twguess = Twguess + incr * previoussign; } return wvutilsConvertCToF(Twguess); }
static void drawGrid (MULTICHART_ID id) { register int i; int xlabelwidth = 0, ylabelwidth = 0; double units, step; double minR, stepR; int dataPoints, xnumhash, xhashindexlen; double hashwidth, xhashwidth; int numhash, x, y; char text[64][32], ylabelfmt[24]; char textR[64][32], ylabelfmtR[24]; // ... calculate the maximum number of hash marks and the units per hash // ... for the x-axis // build labels and calculate the maximum x-label width for (i = 0; i < id->numpoints; i ++) { if (strlen (id->pointnames[i]) > xlabelwidth) xlabelwidth = strlen (id->pointnames[i]); } xhashwidth = (xlabelwidth*gdFontSmall->h)/2; dataPoints = id->numpoints - 1; if (strlen (id->DualUnit) > 0) { // ((gdFontSmall->h/2) * strlen (id->DualUnit)))/2) - 2 id->imbx -= (gdFontMediumBold->h); if ((strcmp(id->DualUnit,"inches") == 0) || (strcmp(id->DualUnit,"knots") == 0) || (strcmp(id->DualUnit,"m/s") == 0)) id->imbx -= 7; } // see if a suggestion was given if (id->xnumhashes != 0) { xnumhash = id->xnumhashes; } else { xnumhash = (id->imbx - id->imtx)/xhashwidth; // try to normalize the number of hashmarks // try with the endpoint removed for (i = 1; i < dataPoints/2; i ++) { if (dataPoints % i != 0) continue; if (dataPoints/i <= xnumhash) { xnumhash = dataPoints/i; break; } } } xhashwidth = (double)(id->imbx - id->imtx)/(double)xnumhash; id->pointpixels = (double)(id->imbx - id->imtx)/(double)dataPoints; xhashindexlen = dataPoints/xnumhash; // ... calculate the maximum number of hash marks and the units per hash // ... for the y-axis hashwidth = (gdFontSmall->h * 3)/2; numhash = (id->imby - id->imty)/hashwidth; if (numhash > 64) numhash = 64; units = (id->max - id->min)/id->ystepSize; step = id->ystepSize; i = 0; while ((int)units > numhash && i < MAX_STEP_MULTIPLIER) { step = id->ystepSize * stepSizeMultipliers[i]; units = (id->max - id->min)/step; i ++; } id->ystepSize = step; numhash = (int)units; if ((id->min + (id->ystepSize * numhash)) < id->max) { numhash ++; } hashwidth = (double)(id->imby - id->imty)/(double)numhash; id->max = id->min + (id->ystepSize * numhash); id->ypixelconstant = (double)(id->imby - id->imty)/(double)(id->max - id->min); numhash ++; if (numhash > 64) numhash = 64; // build labels and calculate the maximum y-label width sprintf (ylabelfmt, "%%.%1.1df", id->ydecPlaces); for (i = 0; i < numhash; i ++) { sprintf (text[i], ylabelfmt, id->min + (i * id->ystepSize)); // now fill the right hand array with alternate scale @Rcm if (strlen(id->DualUnit) > 0) { textR[i][0] = ' '; if (strcmp(id->DualUnit,"C") == 0) { minR = wvutilsConvertFToC (id->min); stepR = ((id->ystepSize) * (5.0/9.0)); sprintf (textR[i], "%.1f", minR + (i * stepR)); } else if (strcmp(id->DualUnit,"F") == 0) { minR = wvutilsConvertCToF (id->min); stepR = ((id->ystepSize) * (9.0/5.0)); sprintf (textR[i], "%.1f", minR + (i * stepR)); } else if (strcmp(id->DualUnit,"mm") == 0) { minR = wvutilsConvertINToMM (id->min); stepR = wvutilsConvertINToMM (id->ystepSize); if (wvutilsConvertINToMM (id->max) >= 100) sprintf (textR[i], "%.0f", minR + (i * stepR)); else sprintf (textR[i], "%.1f", minR + (i * stepR)); } else if (strcmp(id->DualUnit,"inches") == 0) { minR = wvutilsConvertMMToIN (id->min); stepR = wvutilsConvertMMToIN (id->ystepSize); if (wvutilsConvertMMToIN (id->max) >= 100) sprintf (textR[i], "%.0f", minR + (i * stepR)); else sprintf (textR[i], "%.1f", minR + (i * stepR)); } else if (strcmp(id->DualUnit,"km/h") == 0) { minR = wvutilsConvertMilesToKilometers (id->min); stepR = wvutilsConvertMilesToKilometers (id->ystepSize); sprintf (textR[i], "%.0f", minR + (i * stepR)); } else if (strcmp(id->DualUnit,"mph") == 0) { minR = wvutilsConvertKilometersToMiles (id->min); stepR = wvutilsConvertKilometersToMiles (id->ystepSize); sprintf (textR[i], "%.0f", minR + (i * stepR)); } else if (strcmp(id->title,"m/s") == 0) { if (id->isMetric) { minR = wvutilsConvertMPSToKPH (id->min); stepR = wvutilsConvertMPSToKPH (id->ystepSize); sprintf (textR[i], "%.0f", minR + (i * stepR)); } else { minR = wvutilsConvertMPSToMPH (id->min); stepR = wvutilsConvertMPSToMPH (id->ystepSize); sprintf (textR[i], "%.0f", minR + (i * stepR)); } } else if (strcmp(id->title,"knots") == 0) { if (id->isMetric) { minR = wvutilsConvertKnotsToKPH (id->min); stepR = wvutilsConvertKnotsToKPH (id->ystepSize); sprintf (textR[i], "%.0f", minR + (i * stepR)); } else { minR = wvutilsConvertKnotsToMPH (id->min); stepR = wvutilsConvertKnotsToMPH (id->ystepSize); sprintf (textR[i], "%.0f", minR + (i * stepR)); } } else //all else: eg '%' or watts/m^2 (hum/ rad) { // same value both sides minR = id->min * 25.4; stepR = id->ystepSize * 25.4; sprintf (textR[i], "%s", text[i]); } if (strlen (text[i]) > ylabelwidth) ylabelwidth = strlen (text[i])+2; } else { if (strlen (text[i]) > ylabelwidth) ylabelwidth = strlen (text[i]); } } // ... now we know the chart area, draw it gdImageFilledRectangle (id->im, id->imtx - GLC_CONTENTS_OFFSET, id->imty - GLC_CONTENTS_OFFSET, id->imbx + GLC_CONTENTS_OFFSET, id->imby + GLC_CONTENTS_OFFSET, id->chartcolor); gdImageRectangle (id->im, id->imtx, id->imty, id->imbx, id->imby, id->gridcolor); // ... draw the y-axis gdImageSetThickness (id->im, 1); for (i = 0; i < numhash; i ++) { y = id->imby - (i * hashwidth); gdImageLine (id->im, id->imtx, y, id->imbx, y, id->gridcolor); y -= gdFontSmall->h/2; gdImageString (id->im, gdFontSmall, (id->imtx - 5) - (strlen(text[i]) * gdFontSmall->h/2), y, (UCHAR *)text[i], id->textcolor); if (strlen(id->DualUnit) > 0 ) { // right side x = id->imbx + (GLC_CONTENTS_OFFSET - 2); gdImageString (id->im, gdFontSmall, x + gdFontSmall->h/2, y, (UCHAR *)textR[i], id->textcolor); } } // ... draw the x-axis gdImageSetThickness (id->im, 1); for (i = 0; i <= xnumhash; i ++) // one more for right-most { int dataPoint = i * xhashindexlen; x = id->imtx + (i * xhashwidth); gdImageLine (id->im, x, id->imty, x, id->imby, id->gridcolor); if (strlen(id->pointnames[dataPoint]) < 2) x -= gdFontSmall->h/4; gdImageString (id->im, gdFontSmall, x - ((gdFontSmall->h/2) * (strlen(id->pointnames[dataPoint])/2)), (id->height - (3 * gdFontMediumBold->h)) + 4, (UCHAR *)id->pointnames[dataPoint], id->textcolor); } // ... finally, draw date gdImageString (id->im, gdFontSmall, ((id->width - ((gdFontSmall->h/2) * strlen (id->datetime)))/2) - 2, id->height - (gdFontMediumBold->h + 2), (UCHAR *)id->datetime, id->textcolor); }
static void processAlarms (LOOP_PKT *loopData) { WVIEW_ALARM *alarm; float tempFloat; // process the local alarms: for (alarm = (WVIEW_ALARM *) radListGetFirst (&alarmsWork.alarmList); alarm != NULL; alarm = (WVIEW_ALARM *) radListGetNext (&alarmsWork.alarmList, (NODE_PTR)alarm)) { // first check to see if we are in abatement if (alarm->triggered) { if ((radTimeGetSECSinceEpoch () - alarm->abateStart) < alarm->abateSecs) { // abatement - go to the next alarm continue; } else { // clear trigger for future alarms alarm->triggered = FALSE; } } // switch on alarm type switch (alarm->type) { case Barometer: if (alarmsWork.isMetric) { tempFloat = wvutilsConvertINHGToHPA(loopData->barometer); } else { tempFloat = loopData->barometer; } break; case InsideTemp: if (alarmsWork.isMetric) { tempFloat = wvutilsConvertFToC(loopData->inTemp); } else { tempFloat = loopData->inTemp; } break; case InsideHumidity: tempFloat = (float)loopData->inHumidity; break; case OutsideTemp: if (alarmsWork.isMetric) { tempFloat = wvutilsConvertFToC(loopData->outTemp); } else { tempFloat = loopData->outTemp; } break; case WindSpeed: if (alarmsWork.isMetric) { tempFloat = wvutilsConvertMPHToKPH((float)loopData->windSpeed); } else { tempFloat = (float)loopData->windSpeed; } break; case TenMinuteAvgWindSpeed: if (alarmsWork.isMetric) { tempFloat = wvutilsConvertMPHToKPH((float)loopData->tenMinuteAvgWindSpeed); } else { tempFloat = (float)loopData->tenMinuteAvgWindSpeed; } break; case WindDirection: tempFloat = (float)loopData->windDir; break; case OutsideHumidity: tempFloat = (float)loopData->outHumidity; break; case RainRate: if (alarmsWork.isMetric) { tempFloat = wvutilsConvertRainINToMetric(loopData->rainRate); } else { tempFloat = loopData->rainRate; } break; case StormRain: if (alarmsWork.isMetric) { tempFloat = wvutilsConvertRainINToMetric(loopData->stormRain); } else { tempFloat = loopData->stormRain; } break; case DayRain: if (alarmsWork.isMetric) { tempFloat = wvutilsConvertRainINToMetric(loopData->dayRain); } else { tempFloat = loopData->dayRain; } break; case MonthRain: if (alarmsWork.isMetric) { tempFloat = wvutilsConvertRainINToMetric(loopData->monthRain); } else { tempFloat = loopData->monthRain; } break; case YearRain: if (alarmsWork.isMetric) { tempFloat = wvutilsConvertRainINToMetric(loopData->yearRain); } else { tempFloat = loopData->yearRain; } break; case TxBatteryStatus: tempFloat = (float)loopData->txBatteryStatus; break; case ConsoleBatteryVoltage: tempFloat = (((float)loopData->consBatteryVoltage * 300)/512)/100; break; case DewPoint: if (alarmsWork.isMetric) { tempFloat = wvutilsConvertFToC(loopData->dewpoint); } else { tempFloat = loopData->dewpoint; } break; case WindChill: if (alarmsWork.isMetric) { tempFloat = wvutilsConvertFToC(loopData->windchill); } else { tempFloat = loopData->windchill; } break; case HeatIndex: if (alarmsWork.isMetric) { tempFloat = wvutilsConvertFToC(loopData->heatindex); } else { tempFloat = loopData->heatindex; } break; case Radiation: tempFloat = (float)loopData->radiation; break; case UV: tempFloat = (float)loopData->UV; break; case ET: if (alarmsWork.isMetric) { tempFloat = wvutilsConvertRainINToMetric(loopData->dayET); } else { tempFloat = loopData->dayET; } break; case ExtraTemp1: if (alarmsWork.isMetric) { tempFloat = wvutilsConvertFToC(loopData->extraTemp1); } else { tempFloat = loopData->extraTemp1; } break; case ExtraTemp2: if (alarmsWork.isMetric) { tempFloat = wvutilsConvertFToC(loopData->extraTemp2); } else { tempFloat = loopData->extraTemp2; } break; case ExtraTemp3: if (alarmsWork.isMetric) { tempFloat = wvutilsConvertFToC(loopData->extraTemp3); } else { tempFloat = loopData->extraTemp3; } break; case SoilTemp1: if (alarmsWork.isMetric) { tempFloat = wvutilsConvertFToC(loopData->soilTemp1); } else { tempFloat = loopData->soilTemp1; } break; case SoilTemp2: if (alarmsWork.isMetric) { tempFloat = wvutilsConvertFToC(loopData->soilTemp2); } else { tempFloat = loopData->soilTemp2; } break; case SoilTemp3: if (alarmsWork.isMetric) { tempFloat = wvutilsConvertFToC(loopData->soilTemp3); } else { tempFloat = loopData->soilTemp3; } break; case SoilTemp4: if (alarmsWork.isMetric) { tempFloat = wvutilsConvertFToC(loopData->soilTemp4); } else { tempFloat = loopData->soilTemp4; } break; case LeafTemp1: if (alarmsWork.isMetric) { tempFloat = wvutilsConvertFToC(loopData->leafTemp1); } else { tempFloat = loopData->leafTemp1; } break; case LeafTemp2: if (alarmsWork.isMetric) { tempFloat = wvutilsConvertFToC(loopData->leafTemp2); } else { tempFloat = loopData->leafTemp2; } break; case ExtraHumid1: tempFloat = (float)loopData->extraHumid1; break; case ExtraHumid2: tempFloat = (float)loopData->extraHumid2; break; case Wxt510Hail: if (alarmsWork.isMetric) { tempFloat = wvutilsConvertRainINToMetric(loopData->wxt510Hail); } else { tempFloat = loopData->wxt510Hail; } break; case Wxt510Hailrate: if (alarmsWork.isMetric) { tempFloat = wvutilsConvertRainINToMetric(loopData->wxt510Hailrate); } else { tempFloat = loopData->wxt510Hailrate; } break; case Wxt510HeatingTemp: if (alarmsWork.isMetric) { tempFloat = wvutilsConvertFToC(loopData->wxt510HeatingTemp); } else { tempFloat = loopData->wxt510HeatingTemp; } break; case Wxt510HeatingVoltage: tempFloat = loopData->wxt510HeatingVoltage; break; case Wxt510SupplyVoltage: tempFloat = loopData->wxt510SupplyVoltage; break; case Wxt510ReferenceVoltage: tempFloat = loopData->wxt510ReferenceVoltage; break; default: // no match, blow it off continue; } // see if we tripped the breaker here if (alarm->isMax) { if (tempFloat >= alarm->bound) { // we did! alarm->triggered = TRUE; alarm->triggerValue = tempFloat; alarm->abateStart = radTimeGetSECSinceEpoch (); // run user script here statusIncrementStat(ALARM_STATS_SCRIPTS_RUN); if (executeScript (alarm) != 0) { radMsgLog (PRI_MEDIUM, "processAlarms: script %s failed", alarm->scriptToRun); } } } else { if (tempFloat <= alarm->bound) { // we did! alarm->triggered = TRUE; alarm->triggerValue = tempFloat; alarm->abateStart = radTimeGetSECSinceEpoch (); // run user script here statusIncrementStat(ALARM_STATS_SCRIPTS_RUN); if (executeScript (alarm) != 0) { radMsgLog (PRI_MEDIUM, "processAlarms: script %s failed", alarm->scriptToRun); } } } } return; }