def _precalc(self): """Precalculate local variables.""" self.moon_index, self._moon_fullness = weeutil.Moon.moon_phase_ts( self.time_ts) self.moon_phase = self.moon_phases[self.moon_index] self.time_djd = timestamp_to_djd(self.time_ts) # Check to see whether the user has module 'ephem'. if 'ephem' in sys.modules: self.hasExtras = True else: # No ephem package. Use the weeutil algorithms, which supply a minimum of functionality (y, m, d) = time.localtime(self.time_ts)[0:3] (sunrise_utc_h, sunset_utc_h) = weeutil.Sun.sunRiseSet(y, m, d, self.lon, self.lat) sunrise_ts = weeutil.weeutil.utc_to_ts(y, m, d, sunrise_utc_h) sunset_ts = weeutil.weeutil.utc_to_ts(y, m, d, sunset_utc_h) self._sunrise = weewx.units.ValueHelper(ValueTuple( sunrise_ts, "unix_epoch", "group_time"), context="ephem_day", formatter=self.formatter, converter=self.converter) self._sunset = weewx.units.ValueHelper(ValueTuple( sunset_ts, "unix_epoch", "group_time"), context="ephem_day", formatter=self.formatter, converter=self.converter) self.hasExtras = False
def zip_vectors(self): size = len(self.vectors['date'][ZERO]) result = [] while size: result.append({}) size -= 1 for key in [ 'date', 'daily_max', 'daily_min', 'daily_avg', 'dd', 'dd_cumulative', ]: (vals, units, unit_group) = self.vectors[key] for (ndx, val) in enumerate(vals): val_t = ValueTuple(val, units, unit_group) result[ndx][key] = weewx.units.ValueHelper(val_t, 'day', self.formatter, self.converter) horizon_labels = [(cumulative_dd, horizon_label) for ((cumulative_dd, dd_units, dd_group), horizon_label) in self.plot.horizons] horizon_labels.sort() for rec in result: remarks = [] while horizon_labels: (horizon_dd, horizon_event) = horizon_labels[ZERO] if rec['dd_cumulative'].raw > horizon_dd: remarks.append(horizon_event) horizon_labels.pop(ZERO) else: break val_t = ValueTuple('; '.join(remarks), None, None) rec['remark'] = weewx.units.ValueHelper(val_t, 'day', self.formatter, self.converter) return result
def get_series(obs_type, timespan, db_manager, aggregate_type=None, aggregate_interval=None, **option_dict): """Get a series, possibly with aggregation, for special 'wind vector' types. These are typically used for the wind vector plots. """ # Check to see if the requested type is not 'windvec' or 'windgustvec' if obs_type not in WindVec.windvec_types: # The type is not one of the extended wind types. We can't handle it. raise weewx.UnknownType(obs_type) # It is an extended wind type. Prepare the lists that will hold the # final results. start_vec = list() stop_vec = list() data_vec = list() # Is aggregation requested? if aggregate_type: # Yes. Just use the regular series function. When it comes time to do the aggregation, # the specialized function WindVec.get_aggregate() (defined below), will be used. return ArchiveTable.get_series(obs_type, timespan, db_manager, aggregate_type, aggregate_interval) else: # No aggregation desired. However, we have will have to assemble the wind vector from # its flattened types. This SQL select string will select the proper wind types sql_str = 'SELECT dateTime, %s, %s, usUnits, `interval` FROM %s ' \ 'WHERE dateTime >= ? AND dateTime <= ?' \ % (WindVec.windvec_types[obs_type][0], WindVec.windvec_types[obs_type][1], db_manager.table_name) std_unit_system = None for record in db_manager.genSql(sql_str, timespan): ts, magnitude, direction, unit_system, interval = record if std_unit_system: if std_unit_system != unit_system: raise weewx.UnsupportedFeature( "Unit type cannot change within a time interval.") else: std_unit_system = unit_system value = weeutil.weeutil.to_complex(magnitude, direction) start_vec.append(ts - interval * 60) stop_vec.append(ts) data_vec.append(value) unit, unit_group = weewx.units.getStandardUnitType( std_unit_system, obs_type, aggregate_type) return (ValueTuple(start_vec, 'unix_epoch', 'group_time'), ValueTuple(stop_vec, 'unix_epoch', 'group_time'), ValueTuple(data_vec, unit, unit_group))
def get_series(obs_type, timespan, db_manager, aggregate_type=None, aggregate_interval=None): """Get a series, possibly with aggregation, from the main archive database. The general strategy is that if aggregation is asked for, chop the series up into separate chunks, calculating the aggregate for each chunk. Then assemble the results. If no aggregation is called for, just return the data directly out of the database. """ startstamp, stopstamp = timespan start_vec = list() stop_vec = list() data_vec = list() if aggregate_type: # With aggregation unit, unit_group = None, None if aggregate_type == 'cumulative': total = 0 for stamp in weeutil.weeutil.intervalgen(startstamp, stopstamp, aggregate_interval): agg_vt = get_aggregate(obs_type, stamp, aggregate_type, db_manager) if unit: # It's OK if the unit is unknown (=None). if agg_vt[1] is not None and (unit != agg_vt[1] or unit_group != agg_vt[2]): raise weewx.UnsupportedFeature("Cannot change unit groups within an aggregation.") else: unit, unit_group = agg_vt[1:] start_vec.append(stamp.start) stop_vec.append(stamp.stop) if aggregate_type == 'cumulative': total += agg_vt[0] data_vec.append(total) else: data_vec.append(agg_vt[0]) else: # Without aggregation. We only know how to get series that are in the database schema: if obs_type not in db_manager.sqlkeys: raise weewx.UnknownType(obs_type) # No aggregation sql_str = "SELECT dateTime, %s, usUnits, `interval` FROM %s " \ "WHERE dateTime >= ? AND dateTime <= ?" % (obs_type, db_manager.table_name) std_unit_system = None for record in db_manager.genSql(sql_str, (startstamp, stopstamp)): if std_unit_system: if std_unit_system != record[2]: raise weewx.UnsupportedFeature("Unit type cannot change within an aggregation interval.") else: std_unit_system = record[2] start_vec.append(record[0] - record[3] * 60) stop_vec.append(record[0]) data_vec.append(record[1]) unit, unit_group = weewx.units.getStandardUnitType(std_unit_system, obs_type, aggregate_type) return (ValueTuple(start_vec, 'unix_epoch', 'group_time'), ValueTuple(stop_vec, 'unix_epoch', 'group_time'), ValueTuple(data_vec, unit, unit_group))
def get_series(obs_type, timespan, db_manager, aggregate_type=None, aggregate_interval=None): """Get a series, possibly with aggregation. """ if obs_type not in [ 'pm2_5_aqi', 'pm2_5_aqi_color' ]: raise weewx.UnknownType(obs_type) log.debug('get_series(%s, %s, %s, aggregate:%s, aggregate_interval:%s)' % ( obs_type, timestamp_to_string(timespan.start), timestamp_to_string( timespan.stop), aggregate_type, aggregate_interval)) # Prepare the lists that will hold the final results. start_vec = list() stop_vec = list() data_vec = list() # Is aggregation requested? if aggregate_type: # Yes. Just use the regular series function. return weewx.xtypes.ArchiveTable.get_series(obs_type, timespan, db_manager, aggregate_type, aggregate_interval) else: # No aggregation. sql_str = 'SELECT dateTime, usUnits, `interval`, pm2_5 FROM %s ' \ 'WHERE dateTime >= ? AND dateTime <= ? AND pm2_5 IS NOT NULL' \ % db_manager.table_name std_unit_system = None for record in db_manager.genSql(sql_str, timespan): ts, unit_system, interval, pm2_5 = record if std_unit_system: if std_unit_system != unit_system: raise weewx.UnsupportedFeature( "Unit type cannot change within a time interval.") else: std_unit_system = unit_system if obs_type == 'pm2_5_aqi': value = AQI.compute_pm2_5_aqi(pm2_5) if obs_type == 'pm2_5_aqi_color': value = AQI.compute_pm2_5_aqi_color(AQI.compute_pm2_5_aqi(pm2_5)) log.debug('get_series(%s): %s - %s - %s' % (obs_type, timestamp_to_string(ts - interval * 60), timestamp_to_string(ts), value)) start_vec.append(ts - interval * 60) stop_vec.append(ts) data_vec.append(value) unit, unit_group = weewx.units.getStandardUnitType(std_unit_system, obs_type, aggregate_type) return (ValueTuple(start_vec, 'unix_epoch', 'group_time'), ValueTuple(stop_vec, 'unix_epoch', 'group_time'), ValueTuple(data_vec, unit, unit_group))
def get_series(obs_type, timespan, db_manager, aggregate_type=None, aggregate_interval=None): """Get a series, possibly with aggregation, for special 'wind' types.""" # Check to see if the requested type is not 'windvec' or 'windgustvec' if obs_type not in Wind.windvec_types: # The type is not one of the extended wind types. We can't handle it. raise weewx.UnknownType(obs_type) # It is an extended wind type. Prepare the lists that will hold the # final results. start_vec = list() stop_vec = list() data_vec = list() # Is aggregation requested? if aggregate_type: # Yes. Just use the regular series function, but with the special wind aggregate type. It will # call the proper aggregation function. return SeriesArchive.get_series(obs_type, timespan, db_manager, aggregate_type, aggregate_interval) else: # No aggregation desired. However, we have will have to assemble the wind vector from its flattened types. # This SQL select string will select the proper wind types sql_str = 'SELECT dateTime, %s, %s, usUnits, `interval` FROM %s WHERE dateTime >= ? AND dateTime <= ?' \ % (Wind.windvec_types[obs_type][0], Wind.windvec_types[obs_type][1], db_manager.table_name) std_unit_system = None for record in db_manager.genSql(sql_str, timespan): start_vec.append(record[0] - record[4] * 60) stop_vec.append(record[0]) if std_unit_system: if std_unit_system != record[3]: raise weewx.UnsupportedFeature("Unit type cannot change within a time interval.") else: std_unit_system = record[3] # Break the mag and dir down into x- and y-components. (magnitude, direction) = record[1:3] if magnitude is None or direction is None: data_vec.append(None) else: x = magnitude * math.cos(math.radians(90.0 - direction)) y = magnitude * math.sin(math.radians(90.0 - direction)) if weewx.debug: # There seem to be some little rounding errors that # are driving my debugging crazy. Zero them out if abs(x) < 1.0e-6: x = 0.0 if abs(y) < 1.0e-6: y = 0.0 data_vec.append(complex(x, y)) unit, unit_group = weewx.units.getStandardUnitType(std_unit_system, obs_type, aggregate_type) return (ValueTuple(start_vec, 'unix_epoch', 'group_time'), ValueTuple(stop_vec, 'unix_epoch', 'group_time'), ValueTuple(data_vec, unit, unit_group))
def get_vectors(self, stamps, csv_file_name, threshold_lo_t, threshold_hi_t): (minstamp, maxstamp) = stamps threshold_lo = (threshold_lo_t)[ZERO] threshold_hi = (threshold_hi_t)[ZERO] result = { 'date': ValueTuple([], 'unix_epoch', 'group_time'), 'daily_max': ValueTuple([], 'degree_C', 'group_temperature'), 'daily_min': ValueTuple([], 'degree_C', 'group_temperature'), 'dd': ValueTuple([], 'degree_C_day', 'group_degree_day'), 'dd_cumulative': ValueTuple([], 'degree_C_day', 'group_degree_day'), } try: with open(csv_file_name) as csv_file: csv_dict = csv.DictReader(csv_file) recs = [] for (ndx, rec) in enumerate(csv_dict): try: date_string = '%(YR)s/%(MO)s/%(DAY)s' % rec stamp = time.mktime( time.strptime(date_string, '%Y/%m/%d')) except ValueError: stamp = None if stamp is None: pass else: recs.append([stamp, rec]) recs.sort() dd_cumulative = ZERO for (ndx, (stamp, rec)) in enumerate(recs): if (minstamp <= stamp) and (stamp <= maxstamp): result['date'][ZERO].append(stamp) try: daily_max = float(rec.get('TMPMAX_C')) result['daily_max'][ZERO].append(daily_max) daily_min = float(rec.get('TMPMIN_C')) result['daily_min'][ZERO].append(daily_min) dd = dd_clipped(daily_max, daily_min, threshold_lo, threshold_hi) result['dd'][ZERO].append(dd) dd_cumulative += dd result['dd_cumulative'][ZERO].append(dd_cumulative) except ValueError: pass except TypeError: pass except IOError as e: log.info("Unable to read file '%s' %s:", csv_file_name, e) return result
def get_vectors(self, stamps, csv_file_name, c_temp_base_t, c4_temp_t): (minstamp, maxstamp) = stamps c_temp_base = self.to_degree_f.convert(c_temp_base_t)[ZERO] c4_temp = self.to_degree_f.convert(c4_temp_t)[ZERO] result = { 'date': ValueTuple([], 'unix_epoch', 'group_time'), 'daily_max': ValueTuple([], 'degree_F', 'group_temperature'), 'daily_min': ValueTuple([], 'degree_F', 'group_temperature'), 'dd': ValueTuple([], 'count', 'group_count'), 'dd_cumulative': ValueTuple([], 'count', 'group_count'), } try: with open(csv_file_name) as csv_file: csv_dict = csv.DictReader(csv_file) recs = [] for (ndx, rec) in enumerate(csv_dict): try: date_string = '%(YR)s/%(MO)s/%(DAY)s' % rec stamp = time.mktime( time.strptime(date_string, '%Y/%m/%d')) except ValueError: stamp = None if stamp is None: pass else: recs.append([stamp, rec]) recs.sort() dd_cumulative = ZERO for (ndx, (stamp, rec)) in enumerate(recs): if (minstamp <= stamp) and (stamp <= maxstamp): result['date'][ZERO].append(stamp) try: daily_max = float(rec.get('TMPMAX_F')) result['daily_max'][ZERO].append(daily_max) daily_min = float(rec.get('TMPMIN_F')) result['daily_min'][ZERO].append(daily_min) dd = get_growth10(daily_max, daily_min, c_temp_base, c4_temp) result['dd'][ZERO].append(dd) dd_cumulative += dd result['dd_cumulative'][ZERO].append(dd_cumulative) except ValueError: pass except TypeError: pass except IOError as e: log.critical("growth10Generator: Unable to read file '%s' %s:", csv_file_name, e) return result
def get_series(obs_type, timespan, db_manager, aggregate_type=None, aggregate_interval=None, **option_dict): """Get a series of an xtype, by using the main archive table. Works only for no aggregation. """ start_vec = list() stop_vec = list() data_vec = list() if aggregate_type: # This version does not know how to do aggregations, although this could be # added in the future. raise weewx.UnknownAggregation(aggregate_type) else: # No aggregation std_unit_system = None # Hit the database. for record in db_manager.genBatchRecords(*timespan): if std_unit_system: if std_unit_system != record['usUnits']: raise weewx.UnsupportedFeature( "Unit system cannot change " "within a series.") else: std_unit_system = record['usUnits'] # Given a record, use the xtypes system to calculate a value: try: value = get_scalar(obs_type, record, db_manager) data_vec.append(value[0]) except weewx.CannotCalculate: data_vec.append(None) start_vec.append(record['dateTime'] - record['interval'] * 60) stop_vec.append(record['dateTime']) unit, unit_group = weewx.units.getStandardUnitType( std_unit_system, obs_type) return (ValueTuple(start_vec, 'unix_epoch', 'group_time'), ValueTuple(stop_vec, 'unix_epoch', 'group_time'), ValueTuple(data_vec, unit, unit_group))
def test_AggregateDaily(self): """Test special aggregates that can be used against the daily summaries.""" with weewx.manager.open_manager_with_config( self.config_dict, 'wx_binding') as db_manager: month_start_tt = (2010, 3, 1, 0, 0, 0, 0, 0, -1) month_stop_tt = (2010, 4, 1, 0, 0, 0, 0, 0, -1) start_ts = time.mktime(month_start_tt) stop_ts = time.mktime(month_stop_tt) min_ge_vt = weewx.xtypes.DailySummaries.get_aggregate( 'outTemp', TimeSpan(start_ts, stop_ts), 'min_ge', db_manager, val=ValueTuple(15, 'degree_F', 'group_temperature')) self.assertEqual(min_ge_vt[0], 6) min_le_vt = weewx.xtypes.DailySummaries.get_aggregate( 'outTemp', TimeSpan(start_ts, stop_ts), 'min_le', db_manager, val=ValueTuple(0, 'degree_F', 'group_temperature')) self.assertEqual(min_le_vt[0], 2) minmax_vt = weewx.xtypes.DailySummaries.get_aggregate( 'outTemp', TimeSpan(start_ts, stop_ts), 'minmax', db_manager) self.assertAlmostEqual(minmax_vt[0], 39.28, 2) max_wind_vt = weewx.xtypes.DailySummaries.get_aggregate( 'wind', TimeSpan(start_ts, stop_ts), 'max', db_manager) self.assertAlmostEqual(max_wind_vt[0], 24.0, 2) avg_wind_vt = weewx.xtypes.DailySummaries.get_aggregate( 'wind', TimeSpan(start_ts, stop_ts), 'avg', db_manager) self.assertAlmostEqual(avg_wind_vt[0], 10.21, 2) # Double check this last one against the average calculated from the archive avg_wind_vt = weewx.xtypes.ArchiveTable.get_aggregate( 'windSpeed', TimeSpan(start_ts, stop_ts), 'avg', db_manager) self.assertAlmostEqual(avg_wind_vt[0], 10.21, 2) vecavg_wind_vt = weewx.xtypes.DailySummaries.get_aggregate( 'wind', TimeSpan(start_ts, stop_ts), 'vecavg', db_manager) self.assertAlmostEqual(vecavg_wind_vt[0], 5.14, 2) vecdir_wind_vt = weewx.xtypes.DailySummaries.get_aggregate( 'wind', TimeSpan(start_ts, stop_ts), 'vecdir', db_manager) self.assertAlmostEqual(vecdir_wind_vt[0], 88.77, 2)
def calc_windGustDir(self, key, data, db_manager): """ Set windGustDir to None if windGust is zero. Otherwise, raise weewx.NoCalculate.If""" if 'windGust' not in data \ or not self.force_null\ or data['windGust']: raise weewx.NoCalculate return ValueTuple(None, 'degree_compass', 'group_direction')
def temperature(self, input, item, event): system = event.packet['usUnits'] unit_group = weewx.units.getStandardUnitType(system, item) temp_c = self.val(input) / 1000.0 temp_v = ValueTuple(temp_c, "degree_C", "group_temperature") temp_u = weewx.units.convert(temp_v, unit_group[0]) event.packet[item] = temp_u[0]
def pressure(self, record, dbmanager): """Calculate the observation type 'pressure'.""" # All of the following keys are required: if any(key not in record for key in ['usUnits', 'outTemp', 'barometer', 'outHumidity']): raise weewx.CannotCalculate('pressure') # Get the temperature in Fahrenheit from 12 hours ago temp_12h_vt = self._get_temperature_12h(record['dateTime'], dbmanager) if temp_12h_vt is None \ or temp_12h_vt[0] is None \ or record['outTemp'] is None \ or record['barometer'] is None \ or record['outHumidity'] is None: pressure = None else: # The following requires everything to be in US Customary units. # Rather than convert the whole record, just convert what we need: record_US = weewx.units.to_US({ 'usUnits': record['usUnits'], 'outTemp': record['outTemp'], 'barometer': record['barometer'], 'outHumidity': record['outHumidity'] }) # Get the altitude in feet altitude_ft = weewx.units.convert(self.altitude_vt, "foot") # The outside temperature in F. temp_12h_F = weewx.units.convert(temp_12h_vt, "degree_F") pressure = weewx.uwxutils.uWxUtilsVP.SeaLevelToSensorPressure_12( record_US['barometer'], altitude_ft[0], record_US['outTemp'], temp_12h_F[0], record_US['outHumidity']) return ValueTuple(pressure, 'inHg', 'group_pressure')
def pressure(self, input, item, event): system = event.packet['usUnits'] unit_group = weewx.units.getStandardUnitType(system, item) pres_r = self.val(input)/10.0 pres_p = ValueTuple(pres_r, "kPa", "group_pressure") pres_u = weewx.units.convert(pres_p, unit_group[0]) event.packet[item] = pres_u[0]
def calc_windGustDir(self, key, data, db_manager): # Return the current gust direction if windGust is non-zero, otherwise, None if 'windGust' not in data: raise weewx.CannotCalculate if self.ignore_zero_wind or data['windGust']: val = data.get('windGustDir') else: val = None return ValueTuple(val, 'degree_compass', 'group_direction')
def get_float_t(txt, unit_group): if txt is None: result = None elif isinstance(txt, str): if txt.lower() in [NULL, 'none']: result = None else: result = ValueTuple(float(txt[ZERO]), txt[1], unit_group) return result
def calc_windDir(self, key, data, db_manager): # Return the current wind direction if windSpeed is non-zero, otherwise, None if 'windSpeed' not in data or 'windDir' not in data: raise weewx.CannotCalculate if self.force_null and data['windSpeed'] == 0: val = None else: val = data['windDir'] return ValueTuple(val, 'degree_compass', 'group_direction')
def __getattr__(self, obs_type): """Return the trend for the given observation type.""" # This is to get around bugs in the Python version of Cheetah's namemapper: if obs_type in ['__call__', 'has_key']: raise AttributeError db_manager = self.db_lookup(self.data_binding) # Get the current record, and one "time_delta" ago: now_record = db_manager.getRecord(self.nowtime, self.time_grace_val) then_record = db_manager.getRecord(self.nowtime - self.time_delta_val, self.time_grace_val) # Do both records exist? if now_record is None or then_record is None: # No. One is missing. trend = ValueTuple(None, None, None) else: # Both records exist. Check to see if the observation type is known if obs_type not in now_record or obs_type not in then_record: # obs_type is unknown. Signal it trend = weewx.units.UnknownType(obs_type) else: # Both records exist, both types are known. We can proceed. now_vt = weewx.units.as_value_tuple(now_record, obs_type) then_vt = weewx.units.as_value_tuple(then_record, obs_type) # Do the unit conversion now, rather than lazily. This is because the temperature # conversion functions are not distributive. That is, # F_to_C(68F - 50F) # is not equal to # F_to_C(68F) - F_to_C(50F) # We want the latter, not the former, so we perform the conversion immediately. now_vtc = self.converter.convert(now_vt) then_vtc = self.converter.convert(then_vt) if now_vtc.value is None or then_vtc.value is None: # One of the values is None, so the trend will be None. trend = ValueTuple(None, now_vtc.unit, now_vtc.group) else: # All good. Calculate the trend. trend = now_vtc - then_vtc # Return the results as a ValueHelper. Use the formatting and labeling options from the # current time record. The user can always override these. return weewx.units.ValueHelper(trend, 'current', self.formatter, self.converter)
def get_scalar(self, obs_type, record, db_manager): # We only know how to calculate some sun and moon related stuff. For everything else, raise an exception UnknownType if obs_type not in ('sun_altitude', 'sun_azimuth'): raise weewx.UnknownType(obs_type) # we only report sun values if the sun is above the horizon almanac = Almanac(record['dateTime'], self.lat, self.lng, 0) # TODO convert alt to meters sun_altitude = almanac.sun.alt if obs_type == 'sun_altitude': value = ValueTuple(sun_altitude if sun_altitude >= 0 else None, 'degree_compass', 'group_direction') elif obs_type == 'sun_azimuth': value = ValueTuple(almanac.sun.az if sun_altitude >= 0 else None, 'degree_compass', 'group_direction') # We have the calculated values as ValueTuples. Convert them back to the units used by # the incoming record and return it return weewx.units.convertStd(value, record['usUnits'])
def get_scalar(self, obs_type, record, db_manager): # We only know how to calculate 'vapor_p'. For everything else, raise an exception UnknownType if obs_type != 'vapor_p': raise weewx.UnknownType(obs_type) # We need outTemp in order to do the calculation. if 'outTemp' not in record or record['outTemp'] is None: raise weewx.CannotCalculate(obs_type) # We have everything we need. Start by forming a ValueTuple for the outside temperature. # To do this, figure out what unit and group the record is in ... unit_and_group = weewx.units.getStandardUnitType( record['usUnits'], 'outTemp') # ... then form the ValueTuple. outTemp_vt = ValueTuple(record['outTemp'], *unit_and_group) # Both algorithms need temperature in Celsius, so let's make sure our incoming temperature # is in that unit. Use function convert(). The results will be in the form of a ValueTuple outTemp_C_vt = weewx.units.convert(outTemp_vt, 'degree_C') # Get the first element of the ValueTuple. This will be in Celsius: outTemp_C = outTemp_C_vt[0] if self.algorithm == 'simple': # Use the "Simple" algorithm. # We need temperature in Kelvin. outTemp_K = weewx.units.CtoK(outTemp_C) # Now we can use the formula. Results will be in mmHg. Create a ValueTuple out of it: p_vt = ValueTuple(math.exp(20.386 - 5132.0 / outTemp_K), 'mmHg', 'group_pressure') elif self.algorithm == 'tetens': # Use Teten's algorithm. # Use the formula. Results will be in kPa: p_kPa = 0.61078 * math.exp(17.27 * outTemp_C_vt[0] / (outTemp_C_vt[0] + 237.3)) # Form a ValueTuple p_vt = ValueTuple(p_kPa, 'kPa', 'group_pressure') else: # Don't recognize the exception. Fail hard: raise ValueError(self.algorithm) # We have the vapor pressure as a ValueTuple. Convert it back to the units used by # the incoming record and return it return weewx.units.convertStd(p_vt, record['usUnits'])
def __getattr__(self, obs_type): """Return the trend for the given observation type.""" # This is to get around bugs in the Python version of Cheetah's namemapper: if obs_type in ['__call__', 'has_key']: raise AttributeError db_manager = self.db_lookup(self.data_binding) # Get the current record, and one "time_delta" ago: now_record = db_manager.getRecord(self.nowtime, self.time_grace_val) then_record = db_manager.getRecord(self.nowtime - self.time_delta_val, self.time_grace_val) # Do both records exist? if now_record is None or then_record is None: # No. One is missing. trend = ValueTuple(None, None, None) else: # Both records exist. # Check to see if the observation type is known if obs_type not in now_record or obs_type not in then_record: # obs_type is unknown. Signal it trend = weewx.units.UnknownType(obs_type) else: now_vt = weewx.units.as_value_tuple(now_record, obs_type) then_vt = weewx.units.as_value_tuple(then_record, obs_type) # Do the unit conversion now, rather than lazily. This is because, # in the case of temperature, the difference between two converted # values is not the same as the conversion of the difference # between two values. E.g., 20C - 10C is not equal to # F_to_C(68F - 50F). We want the former, not the latter. now_vtc = self.converter.convert(now_vt) then_vtc = self.converter.convert(then_vt) if now_vtc.value is None or then_vtc.value is None: trend = ValueTuple(None, now_vtc.unit, now_vtc.group) else: trend = now_vtc - then_vtc # Return the results as a ValueHelper. Use the formatting and labeling # options from the current time record. The user can always override # these. return weewx.units.ValueHelper(trend, 'current', self.formatter, self.converter)
def calc_beaufort(key, data, db_manager=None): global first_time if first_time: print("Type beaufort has been deprecated. Use unit beaufort instead.") log.info("Type beaufort has been deprecated. Use unit beaufort instead.") first_time = False if 'windSpeed' not in data: raise weewx.CannotCalculate windspeed_vt = weewx.units.as_value_tuple(data, 'windSpeed') windspeed_kn = weewx.units.convert(windspeed_vt, 'knot')[0] return ValueTuple(weewx.wxformulas.beaufort(windspeed_kn), None, None)
def get_scalar(self, obs_type, record, db_manager): # We only know how to calculate 'makkink'. For everything else, raise an exception UnknownType if obs_type != 'makkink': raise weewx.UnknownType(obs_type) # We need outTemp and the radiation in order to do the calculation. if 'outTemp' not in record or record['outTemp'] is None: raise weewx.CannotCalculate(obs_type) if 'radiation' not in record or record['radiation'] is None: raise weewx.CannotCalculate(obs_type) # We have everything we need. Start by forming a ValueTuple for the outside temperature. # To do this, figure out what unit and group the record is in ... unit_and_group = weewx.units.getStandardUnitType( record['usUnits'], 'outTemp') # ... then form the ValueTuple. outTemp_vt = ValueTuple(record['outTemp'], *unit_and_group) # same for radiation unit_and_group = weewx.units.getStandardUnitType( record['usUnits'], 'radiation') # ... then form the ValueTuple. radiation_vt = ValueTuple(record['radiation'], *unit_and_group) outTemp_C_vt = weewx.units.convert(outTemp_vt, 'degree_C') # Get the first element of the ValueTuple. This will be in Celsius: T = outTemp_C_vt[0] # just to make sure nothing fancy happens we do this for radiation as well # even though this is W/m2 in us as well as metric radiation_Wm2_vt = weewx.units.convert(radiation_vt, 'watt_per_meter_squared') Kin = radiation_Wm2_vt[0] makkink = ValueTuple( self.Eref(T, Kin) * 3600, 'mm_per_hour', 'group_rainrate') # convert from kg/m2*s --> mm/hour # Convert it back to the units used by # the incoming record and return it return weewx.units.convertStd(makkink, record['usUnits'])
def calc_humidex(key, data, db_manager=None): if 'outTemp' not in data or 'outHumidity' not in data: raise weewx.CannotCalculate(key) if data['usUnits'] == weewx.US: val = weewx.wxformulas.humidexF(data['outTemp'], data['outHumidity']) u = 'degree_F' else: val = weewx.wxformulas.humidexC(data['outTemp'], data['outHumidity']) u = 'degree_C' return ValueTuple(val, u, 'group_temperature')
def calc_inDewpoint(key, data, db_manager=None): if 'inTemp' not in data or 'inHumidity' not in data: raise weewx.CannotCalculate(key) if data['usUnits'] == weewx.US: val = weewx.wxformulas.dewpointF(data['inTemp'], data['inHumidity']) u = 'degree_F' else: val = weewx.wxformulas.dewpointC(data['inTemp'], data['inHumidity']) u = 'degree_C' return ValueTuple(val, u, 'group_temperature')
def calc_maxSolarRad(self, key, data, db_manager): altitude_m = weewx.units.convert(self.altitude_vt, 'meter')[0] if self.maxSolarRad_algo == 'bras': val = weewx.wxformulas.solar_rad_Bras(self.latitude_f, self.longitude_f, altitude_m, data['dateTime'], self.nfac) elif self.maxSolarRad_algo == 'rs': val = weewx.wxformulas.solar_rad_RS(self.latitude_f, self.longitude_f, altitude_m, data['dateTime'], self.atc) else: raise weewx.ViolatedPrecondition("Unknown solar algorithm '%s'" % self.maxSolarRad_algo) return ValueTuple(val, 'watt_per_meter_squared', 'group_radiation')
def get_extension_list(self, timespan, db_lookup): """ Returns various tags. Returns: Easter: A ValueHelper containing the date of the next Easter Sunday. The time represented is midnight at the start of Easter Sunday. """ # # Easter. Calculate date for Easter Sunday this year # def calc_easter(year): """Calculate Easter date. Uses a modified version of Butcher's Algorithm. Refer New Scientist, 30 March 1961 pp 828-829 https://books.google.co.uk/books?id=zfzhCoOHurwC&printsec=frontcover&source=gbs_ge_summary_r&cad=0#v=onepage&q&f=false """ a = year % 19 b = year // 100 c = year % 100 d = b // 4 e = b % 4 g = (8 * b + 13) // 25 h = (19 * a + b - d - g + 15) % 30 i = c // 4 k = c % 4 l = (2 * e + 2 * i - h - k + 32) % 7 m = (a + 11 * h + 19 * l) // 433 n = (h + l - 7 * m + 90) // 25 p = (h + l - 7 * m + 33 * n + 19) % 32 _dt = datetime.datetime(year=year, month=n, day=p) _ts = time.mktime(_dt.timetuple()) return _ts _year = date.today().year easter_ts = calc_easter(_year) # check to see if we have past this calculated date, if so we want next # years date so increment year and recalculate if date.fromtimestamp(easter_ts) < date.today(): easter_ts = calc_easter(_year + 1) easter_vt = ValueTuple(easter_ts, 'unix_epoch', 'group_time') easter_vh = ValueHelper(easter_vt, formatter=self.generator.formatter, converter=self.generator.converter) # Create a small dictionary with the tag names (keys) we want to use search_list_extension = { 'Easter': easter_vh, } return [search_list_extension]
def time_at(expression): """When an sql expression evaluated true""" db_manager = db_lookup() sql_stmt = "SELECT dateTime FROM %s WHERE %s AND dateTime <= %d ORDER BY dateTime DESC LIMIT 1" \ % (db_manager.table_name, expression, timespan.stop) row = db_manager.getSql(sql_stmt) val = row[0] if row else None vt = ValueTuple(val, 'unix_epoch', 'group_time') vh = ValueHelper(vt, formatter=self.generator.formatter, converter=self.generator.converter) return vh
def time_since(expression): """Time since a sql expression evaluted true""" db_manager = db_lookup() sql_stmt = "SELECT dateTime FROM %s WHERE %s AND dateTime <= %d ORDER BY dateTime DESC LIMIT 1" \ % (db_manager.table_name, expression, timespan.stop) row = db_manager.getSql(sql_stmt) val = timespan.stop - row[0] if row else None vt = ValueTuple(val, 'second', 'group_deltatime') vh = ValueHelper(vt, context="long_delta", formatter=self.generator.formatter, converter=self.generator.converter) return vh
def calc_cloudbase(self, key, data, db_manager): if 'outTemp' not in data or 'outHumidity' not in data: raise weewx.CannotCalculate(key) # Convert altitude to the same unit system as the incoming record altitude = weewx.units.convertStd(self.altitude_vt, data['usUnits']) # Use the appropriate formula if data['usUnits'] == weewx.US: formula = weewx.wxformulas.cloudbase_US u = 'foot' else: formula = weewx.wxformulas.cloudbase_Metric u = 'meter' val = formula(data['outTemp'], data['outHumidity'], altitude[0]) return ValueTuple(val, u, 'group_altitude')