Пример #1
0
    def get_gts(self, obs_type, sod_ts, soy_ts):
        """ read GTS value out of the array """

        if obs_type == 'GTS':
            # Gruenlandtemperatursumme GTS
            try:
                if soy_ts is None or soy_ts not in self.gts_values:
                    __x = None
                else:
                    __x = self.gts_values[soy_ts][dayOfGTSYear(sod_ts, soy_ts)]
                return weewx.units.ValueTuple(__x, 'degree_C_day',
                                              'group_degree_day')
            except (ValueError, TypeError, IndexError, KeyError):
                logerr("soy_ts=%s sod_ts=%s" % (soy_ts, sod_ts))
                raise weewx.CannotCalculate(obs_type)
        elif obs_type == 'GTSdate':
            # date of value 200
            if soy_ts is None or soy_ts not in self.gts_date:
                return weewx.units.ValueTuple(None, 'unix_epoch', 'group_time')
            else:
                return weewx.units.ValueTuple(self.gts_date[soy_ts],
                                              'unix_epoch', 'group_time')
        else:
            # unknown type (should not happen here)
            raise weewx.UnknownType(obs_type)
Пример #2
0
    def add_record(self, record):
        """Add a new record, then return the difference in value divided by the difference in time."""

        # If the type does not appear in the incoming record, then we can't calculate the derivative
        if self.obs_type not in record:
            raise weewx.CannotCalculate(self.obs_type)

        derivative = None
        if record[self.obs_type] is not None:
            # We can't calculate anything if we don't have an old record
            if self.old_timestamp:
                # Check to make sure the incoming record is later than the retained record
                if record['dateTime'] < self.old_timestamp:
                    raise weewx.ViolatedPrecondition(
                        "Records presented out of order (%s vs %s)" %
                        (record['dateTime'], self.old_timestamp))
                # Calculate the time derivative only if the old record is not too old.
                if record['dateTime'] - self.old_timestamp <= self.stale_age:
                    # It's OK.
                    derivative = (record[self.obs_type] - self.old_value) / (
                        record['dateTime'] - self.old_timestamp)
            # Save the current values
            self.old_timestamp = record['dateTime']
            self.old_value = record[self.obs_type]

        return derivative
Пример #3
0
    def get_scalar(self, obs_type, record, db_manager):
        # We only know how to calculate 'vapor_p'. For everything else, raise an exception
        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 = weewx.units.ValueTuple(record['outTemp'], *unit_and_group)

        # We need the temperature in Kelvin
        outTemp_K_vt = weewx.units.convert(outTemp_vt, 'degree_K')

        # Now we can use the formula. Results will be in mmHg. Create a ValueTuple out of it:
        p_vt = weewx.units.ValueTuple(math.exp(20.386 - 5132.0 / outTemp_K_vt[0]),
                                      'mmHg',
                                      'group_pressure')

        # 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'])
Пример #4
0
 def get_scalar(obs_type, record, db_manager=None):
     log.debug('get_scalar(%s)' % obs_type)
     if obs_type not in [
             'pm2_5_aqi', 'pm2_5_aqi_color', 'pm2_5_lrapa',
             'pm2_5_lrapa_aqi', 'pm2_5_lrapa_aqi_color', 'pm2_5_unbc',
             'pm2_5_unbc_aqi', 'pm2_5_unbc_aqi_color'
     ]:
         raise weewx.UnknownType(obs_type)
     log.debug('get_scalar(%s)' % obs_type)
     if record is None:
         log.debug('get_scalar called where record is None.')
         raise weewx.CannotCalculate(obs_type)
     if 'pm2_5' not in record:
         log.info('get_scalar called where record does not contain pm2_5.')
         raise weewx.CannotCalculate(obs_type)
     if record['pm2_5'] is None:
         log.info('get_scalar called where record[pm2_5] is None.')
         raise weewx.CannotCalculate(obs_type)
     try:
         pm2_5 = record['pm2_5']
         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))
         elif obs_type == 'pm2_5_lrapa':
             value = AQI.compute_pm2_5_lrapa(pm2_5)
         elif obs_type == 'pm2_5_lrapa_aqi':
             pm2_5_lrapa = AQI.compute_pm2_5_lrapa(pm2_5)
             value = AQI.compute_pm2_5_aqi(pm2_5_lrapa)
         elif obs_type == 'pm2_5_lrapa_aqi_color':
             value = AQI.compute_pm2_5_aqi_color(
                 AQI.compute_pm2_5_aqi(AQI.compute_pm2_5_lrapa(pm2_5)))
         elif obs_type == 'pm2_5_unbc':
             value = AQI.compute_pm2_5_unbc(pm2_5)
         elif obs_type == 'pm2_5_unbc_aqi':
             pm2_5_unbc = AQI.compute_pm2_5_unbc(pm2_5)
             value = AQI.compute_pm2_5_aqi(pm2_5_unbc)
         elif obs_type == 'pm2_5_unbc_aqi_color':
             value = AQI.compute_pm2_5_aqi_color(
                 AQI.compute_pm2_5_aqi(AQI.compute_pm2_5_unbc(pm2_5)))
         t, g = weewx.units.getStandardUnitType(record['usUnits'], obs_type)
         # Form the ValueTuple and return it:
         return weewx.units.ValueTuple(value, t, g)
     except KeyError:
         # Don't have everything we need. Raise an exception.
         raise weewx.CannotCalculate(obs_type)
Пример #5
0
 def calc_GDD_integral(self, obs_type, timespan, db_manager, base_t,
                       limit_t, stop_t):
     """ calculate growing degree days as integral over time"""
     try:
         # make sure limit_t and stop_t are not None
         if not limit_t: limit_t = 1000.0
         if not stop_t: stop_t = 1000.0
         logdbg("GDD integral base=%s limit=%s stop=%s" %
                (base_t, limit_t, stop_t))
         # maximum growing degree value
         __gdlimit = limit_t - base_t
         # query data base and calculate integral
         _result = db_manager.getSql(
             'SELECT sum('
             '  CASE'
             '    WHEN `%s`>%.1f THEN 0.0'
             '    WHEN `%s`>%.1f THEN %.1f'
             '    WHEN `%s`<%.1f THEN 0.0'
             '    ELSE `%s`-%.1f'
             '  END*`interval`/1440.0),'
             '  MIN(usUnits),MAX(usUnits) '
             'FROM %s '
             'WHERE dateTime>? AND dateTime<=?' %
             (obs_type, stop_t, obs_type, limit_t, __gdlimit, obs_type,
              base_t, obs_type, base_t, db_manager.table_name), timespan)
         if _result is None:
             raise weewx.CannotCalculate(
                 "calculate GDD: no temperature data in database")
         if _result[0] is not None:
             if not _result[1] == _result[2]:
                 raise weewx.CannotCalculate(
                     "calculate GDD: inconsistent units")
         _unit, _ugroup = weewx.units.getStandardUnitType(
             _result[1], obs_type, 'GDD')
         logdbg("GDD integral unit=%s unitgroup=%s" % (_unit, _ugroup))
         return weewx.units.ValueTuple(_result[0], _unit, _ugroup)
     except weedb.OperationalError as e:
         raise weewx.CannotCalculate(
             "calculate GDD integral: Database OperationalError '%s'" % e)
     except (ValueError, TypeError) as e:
         raise weewx.CannotCalculate("calculate GDD integral: %s" % e)
     return None
Пример #6
0
    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'])
Пример #7
0
    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'])
Пример #8
0
    def get_aggregate(self, obs_type, timespan, aggregate_type, db_manager,
                      **option_dict):

        if obs_type is None:
            raise weewx.UnknownType("obs_type is None")

        # time offset of local mean time (LMT)
        if obs_type == 'utcoffsetLMT':
            return weewx.units.ValueTuple(
                self.lmt_tz.utcoffset(None).total_seconds(), 'second',
                'group_deltatime')

        # energy_integral can be calculated for group_radiation observation
        # types like 'radiation' and 'maxSolarRad'
        if aggregate_type == 'energy_integral':
            if obs_type in weewx.units.obs_group_dict and weewx.units.obs_group_dict[
                    obs_type] == 'group_radiation':
                return self.calc_radiation_integral(obs_type, timespan,
                                                    db_manager)

        # growing degree days == Wachstumsgradtage
        # https://de.wikipedia.org/wiki/Wachstumsgradtag
        # https://en.wikipedia.org/wiki/Growing_degree-day
        if aggregate_type in ['growdeg', 'GDD']:
            # growing degree day can only be calculated for a temperature
            if weewx.units.obs_group_dict.get(obs_type,
                                              '') != 'group_temperature':
                raise weewx.CannotCalculate(
                    "%s is not temperature for aggregation %s" %
                    (obs_type, aggregate_type))
            # if the base value is defined in skin.conf or weewx.conf, get
            # it for default
            units_dict = option_dict.get('skin_dict', {}).get('Units', {})
            dd_dict = units_dict.get('DegreeDays', {})
            base_vt = dd_dict.get(
                'growing_base',
                weewx.xtypes.AggregateHeatCool.default_growbase)
            # if parameters are specified get them
            val = option_dict.get('val')
            #loginf("%s" % type(val))
            #loginf(val)
            if val:
                # GDD with parameters
                try:
                    # dict
                    method = val.get('method', 'integral')
                    base_vt = val.get('base', base_vt)
                    limit_vt = val.get('limit', self.GDD_LIMIT_VT)
                    stop_vt = val.get('stop')
                except TypeError:
                    # tuple used as base temperature
                    base_vt = weewx.units.ValueTuple(float(val[0]), val[1],
                                                     'group_temperature')
                    limit_vt = None
                    stop_vt = None
                    method = 'integral'
            else:
                # GDD alone: use defaults
                method = 'integral'
                limit_vt = self.GDD_LIMIT_VT
                stop_vt = None
            # Convert to a ValueTuple in the same unit system as the database
            __base = weewx.units.convertStd(
                (float(base_vt[0]), base_vt[1], 'group_temperature'),
                db_manager.std_unit_system)[0]
            if limit_vt:
                try:
                    __limit = weewx.units.convertStd(
                        (float(limit_vt[0]), limit_vt[1], 'group_temperature'),
                        db_manager.std_unit_system)[0]
                except IndexError:
                    if limit_vt.lower() == 'none': __limit = None
            else:
                __limit = None
            if stop_vt:
                try:
                    __stop = weewx.units.convertStd(
                        (float(stop_vt[0]), stop_vt[1], 'group_temperature'),
                        db_manager.std_unit_system)[0]
                except IndexError:
                    if stop_vt.lower() == 'none': __stop = None
            else:
                __stop = None
            #loginf("method %s" % method)
            #loginf(base_vt)
            #loginf(limit_vt)
            # calculate GDD sum
            if method == 'integral':
                # integral over timespan
                return self.calc_GDD_integral(obs_type, timespan, db_manager,
                                              __base, __limit, __stop)
            if method in ['hiloavgA', 'hiloavgB', 'dayavg']:
                # based on daily average or average of high and low.
                # Check if day border should be based on Local Mean Time
                # or local timezone time
                __lmt_tz = option_dict.get('LMT', {}).get('timezone')
                if __lmt_tz is None:
                    __lmt_tz = option_dict.get('dayboundary',
                                               {}).get('timezone')
                return self.calc_GDD_avg(obs_type, timespan, db_manager,
                                         method, __base, __limit, __stop,
                                         __lmt_tz)
            if method == 'weewx' and obs_type == 'outTemp':
                # call builtin method of WeeWX for outTemp
                return weewx.xtypes.get_aggregate('growdeg', timespan, 'sum',
                                                  db_manager, **option_dict)
            raise weewx.CannotCalculate("GDD %s: unknown method" % method)

        # accumulated growing degree days
        if obs_type == 'yearGDD' or obs_type == 'seasonGDD':
            #loginf("GDD %s" % option_dict)
            #loginf("GDD %s" % aggregate_type)
            if aggregate_type.lower() == 'avg':
                if timespan.start > time.time() or (
                        timespan.start +
                        timespan.stop) / 2 > time.time() + 90000:
                    return weewx.units.ValueTuple(None, 'degree_C_day',
                                                  'group_degree_days')
                return self.get_scalar(
                    obs_type,
                    {'dateTime': (timespan.start + timespan.stop) / 2},
                    db_manager, **option_dict)
            if aggregate_type.lower() == 'last':
                return self.get_scalar(obs_type, {'dateTime': timespan.stop},
                                       db_manager, **option_dict)
            raise weewx.UnknownAggregation("%s undefinded aggregation %s" %
                                           (obs_type, aggregation_type))

        # This function handles 'GTS' and 'GTSdate'.
        if obs_type != 'GTS' and obs_type != 'GTSdate':
            raise weewx.UnknownType(obs_type)

        # aggregation types that are defined for those values
        if aggregate_type not in [
                'avg', 'max', 'min', 'last', 'maxtime', 'mintime', 'lasttime'
        ]:
            raise weewx.UnknownAggregation("%s undefinded aggregation %s" %
                                           (obs_type, aggregation_type))

        if timespan is None:
            raise weewx.CannotCalculate("%s %s no timespan" %
                                        (obs_type, aggregate_type))
        if db_manager is None:
            if self.db_manager_ok:
                logerr("%s: no database reference" % obs_type)
                self.db_manager_ok = False
            raise weewx.CannotCalculate("%s: no database reference" % obs_type)

        # needed timestamps
        _soya_ts = startOfYearTZ(timespan.start + 1, self.lmt_tz)
        _soye_ts = startOfYearTZ(timespan.stop, self.lmt_tz)

        # calculate GTS values for the years included in timespan
        # (if time span is within the current year, the
        # value is calculated up to the current day (today))
        __max = 0
        __maxtime = None
        __min = 10000000
        __mintime = None
        __ts = _soya_ts
        # Even if the time span starts after May 31st, the end value
        # is needed for some aggregations. So we have to calculate
        # that year, too.
        while __ts <= _soye_ts:
            # calculate GTS values for the year
            self.calc_gts(__ts, db_manager)
            # update minimum and maximum
            if __ts in self.gts_values:
                for __i, __val in enumerate(self.gts_values[__ts]):
                    if __val is not None and __val > __max:
                        __max = __val
                        __maxtime = __ts + __i * 86400
                    if __val is not None and __val < __min:
                        __min = __val
                        __mintime = __ts + __i * 86400
            # next year
            __ts = startOfYearTZ(__ts + 31708800, self.lmt_tz)

        if obs_type == 'GTS':
            if aggregate_type == 'avg':
                # 1 day is 86400s, but once a year it is 90000s or 82800s
                # when the daylight savings time is switched on or off.
                if timespan.stop - timespan.start <= 90000:
                    __a = startOfDayTZ(timespan.start, _soya_ts)
                    __b = startOfDayTZ(timespan.stop, _soye_ts)
                    if __a != __b:
                        # begin and end of timespan are different days
                        # according to timezone self.lmt_tz
                        # timespan.start <= __b <= timespan.stop
                        if __b - timespan.start > timespan.stop - __b:
                            __b = __a
                            _soye_ts = _soya_ts
                    if __b >= _soye_ts + 13046400:
                        __x = weewx.units.ValueTuple(None, 'degree_C_day',
                                                     'group_degree_day')
                    else:
                        __x = self.get_gts(obs_type, __b, _soye_ts)
                elif _soya_ts == _soye_ts and _soya_ts in self.gts_values:
                    # timespan within the same year but more than one day
                    # (not much use, but calculated anyway)
                    __a = dayOfGTSYear(timespan.start, _soya_ts)
                    __b = dayOfGTSYear(timespan.stop, _soye_ts)
                    if __a == __b:
                        __x = self.gts_values[_soya_ts][__a]
                    else:
                        __x = 0
                        for __i in xrange(__a, __b):
                            if self.gts_values[_soya_ts][__i] is not None:
                                __x += self.gts_values[_soya_ts][__i]
                        __x /= __b - __a
                else:
                    raise weewx.CannotCalculate(
                        "%s %s invalid timespan %s %s" %
                        (obs_type, aggregate_type,
                         timespan.stop - timespan.start,
                         time.strftime("%Y-%m-%d %H:%M:%S",
                                       time.localtime(timespan.start))))
            elif aggregate_type == 'lasttime':
                if timespan.stop >= self.last_gts_date:
                    # today or in future
                    __ts = self.last_gts_date
                else:
                    # before today
                    if _soye_ts not in self.gts_values:
                        raise weewx.CannotCalculate("%s %s" %
                                                    (obs_type, aggregate_type))
                    __ts = dayOfGTSYear(timespan.stop, _soye_ts)
                    for __i, __v in reversed(
                            enumerate(self.gts_values[_soye_ts])):
                        if __v is not None and __i <= __ts:
                            __ts = _soye_ts + 86400 * __i
                            break
                    else:
                        __ts = _soye_ts
                __x = weewx.units.ValueTuple(__ts, 'unix_epoch', 'group_time')
            elif aggregate_type == 'last':
                if timespan.stop >= _soye_ts + 13046400:
                    # after May 31st
                    __ts = _soye_ts + 13046400 - 86400
                else:
                    # between Jan 1st and May 31st
                    __ts = startOfDayTZ(timespan.stop, _soye_ts)
                    # startOfDay() returns for 24:00 the start of the
                    # next day. So we need to look for the day before.
                    if __ts == timespan.stop:
                        __ts -= 86400
                    # for today there is no value so far
                    if __ts == startOfDayTZ(time.time(), _soye_ts):
                        __ts -= 86400
                __x = self.get_gts(obs_type, __ts, _soye_ts)
            elif aggregate_type == 'max':
                __x = weewx.units.ValueTuple(__max, 'degree_C_day',
                                             'group_degree_day')
            elif aggregate_type == 'maxtime':
                __x = weewx.units.ValueTuple(__maxtime, 'unix_epoch',
                                             'group_time')
            elif aggregate_type == 'min':
                __x = weewx.units.ValueTuple(__min, 'degree_C_day',
                                             'group_degree_day')
            elif aggregate_type == 'mintime':
                __x = weewx.units.ValueTuple(__mintime, 'unix_epoch',
                                             'group_time')
            else:
                raise weewx.CannotCalculate("%s %s" %
                                            (obs_type, aggregate_type))
            """
            try:
              a=str(__x[0])
            except:
              logerr("get_aggregate 0")
              a=""
            try:
              b=str(__x[1])
            except:
              logerr("get_aggregate 1")
              b=""
            try:
              c=str(__x[2])
            except:
              logerr("get_aggregate 2")
              c=""
            loginf("get_aggregate %s,%s,%s" % (a,b,c))
            """
            return __x

        if obs_type == 'GTSdate':
            if aggregate_type == 'last' or aggregate_type == 'max':
                if _soye_ts in self.gts_date and self.gts_date[
                        _soye_ts] is not None and timespan.stop >= self.gts_date[
                            _soye_ts]:
                    __x = self.gts_date[_soye_ts]
                else:
                    __x = None
                return weewx.units.ValueTuple(__x, 'unix_epoch', 'group_time')

        raise weewx.CannotCalculate("%s %s" % (obs_type, aggregate_type))
Пример #9
0
    def get_scalar(self, obs_type, record, db_manager, **option_dict):
        """ mandatory function to be defined for XType extensions """

        if obs_type is None:
            raise weewx.UnknownType("obs_type is None")

        # time offset of local mean time (LMT)
        if obs_type == 'utcoffsetLMT':
            return weewx.units.ValueTuple(
                self.lmt_tz.utcoffset(None).total_seconds(), 'second',
                'group_deltatime')

        # dateTime as string in local mean time
        if obs_type == 'LMTtime':
            if record is None:
                raise weewx.UnknownType("%s: no record" % obs_type)
            return weewx.units.ValueTuple(
                datetime.datetime.fromtimestamp(
                    record['dateTime'], self.lmt_tz).strftime("%H:%M:%S"),
                'unix_epoch', 'group_time')

        # This functions handles 'GTS' and 'GTSdate'.
        if obs_type not in [
                'GTS', 'GTSdate', 'dayET', 'ET24', 'yearGDD', 'seasonGDD'
        ]:
            raise weewx.UnknownType(obs_type)

        #if record is None:
        #    if self.record_ok:
        #        logerr("%s: no record (logged only once)" % obs_type)
        #        self.record_ok=False
        #    raise weewx.CannotCalculate("%s: no record" % obs_type)
        if db_manager is None:
            if self.db_manager_ok:
                logerr("%s: db_manager is None" & obs_type)
                self.db_manager_ok = False
            raise weewx.CannotCalculate("%s: no database reference" % obs_type)

        #logdbg("obs_type=%s" % obs_type)

        # needed timestamps
        if record is not None and 'dateTime' in record:
            _time_ts = record['dateTime']
        else:
            # that should never happen but does due to a bug in
            # Belchertown skin
            if self.record_ok:
                logerr("%s: no record (logged only once)" % obs_type)
                self.record_ok = False
            # to do something we deliver the last available value
            _time_ts = time.time()

        if obs_type == 'dayET':
            # dayET should be comparable to dayRain, so use the same
            # time span: not local mean time but archive time
            try:
                # startOfArchiveDay() considers midnight belonging
                # to the previous day. So the time span would be
                # always greater than 0.
                __x = weeutil.weeutil.startOfArchiveDay(_time_ts)
                __x = weeutil.weeutil.TimeSpan(__x, _time_ts)
            except (ValueError, TypeError, IndexError):
                raise weewx.CannotCalculate("dayET: invalid time")
            return weewx.xtypes.get_aggregate('ET', __x, 'sum', db_manager)

        if obs_type == 'ET24':
            try:
                __x = weeutil.weeutil.TimeSpan(_time_ts - 86400, _time_ts)
            except:
                raise weewx.CannotCalculate("ET24: invalid time")
            return weewx.xtypes.get_aggregate('ET', __x, 'sum', db_manager)

        _soy_ts = startOfYearTZ(_time_ts, self.lmt_tz)
        _sod_ts = startOfDayTZ(_time_ts, _soy_ts)  # start of day

        # If the start of the year in question is before the first
        # record in the database, no value can be calculated. The
        # same applies if the given timestamp is in future.
        #if _soy_ts<db_manager.first_timestamp or _sod_ts>db_manager.last_timestamp:
        #    raise weewx.CannotCalculate(obs_type)

        # growing degree days == Wachstumsgradtage
        # https://de.wikipedia.org/wiki/Wachstumsgradtag
        # https://en.wikipedia.org/wiki/Growing_degree-day
        if obs_type in ['yearGDD']:
            try:
                # calculate from the beginning of the year up to the
                # end of the current day
                __timespan = TimeSpan(_soy_ts, _sod_ts + 86400)
                return self.get_aggregate('outTemp', __timespan, 'GDD',
                                          db_manager, **option_dict)
            except (ValueError, TypeError, IndexError, KeyError):
                raise weewx.CannotCalculate("%s" % obs_type)

        # calculate GTS values for the given year
        # (if record['dateTime'] is within the current year, the
        # value is calculated up to the current day (today))
        self.calc_gts(_soy_ts, db_manager)

        # growing degree days == Wachstumsgradtage
        # https://de.wikipedia.org/wiki/Wachstumsgradtag
        # https://en.wikipedia.org/wiki/Growing_degree-day
        if obs_type in ['seasonGDD']:
            try:
                # calculate from the beginning of the year up to the
                # end of the current day
                __start_ts = self.gts_date[_soy_ts]
                if __start_ts and _sod_ts >= __start_ts and _sod_ts < _soy_ts + 26179200:
                    __timespan = TimeSpan(__start_ts, _sod_ts + 86400)
                    return self.get_aggregate('outTemp', __timespan, 'GDD',
                                              db_manager, **option_dict)
            except (ValueError, TypeError, IndexError, KeyError):
                #raise weewx.CannotCalculate("%s" % obs_type)
                pass
            return weewx.units.ValueTuple(None, 'degree_C_day',
                                          'group_degree_day')

        # check if the requested timestamp record['dateTime'] is within
        # the current day (today)
        # Note: After self.calc_gts() is run, self.last_gts_date
        #       points to the beginning of the current day, if
        #       record['dateTime'] is within the current year.
        #       if record['dateTime'] is _not_ within the current
        #       year, self.last_gts_date _may_ be None.
        if record is None:
            # record is not provided, we assume the actual time
            # Note: That should not happen but does due to a bug in
            #       Belchertown skin
            if _sod_ts != self.last_gts_date:
                raise weewx.CannotCalculate("%s: no record" % obs_type)
            __today = True
        elif self.last_gts_date is None or self.gts_value is None:
            # The current year is not calculated so far, that means,
            # record['dateTime'] cannot be within the current day (today).
            __today = False
        elif _time_ts <= self.last_gts_date:
            # record['dateTime'] is before self.last_gts_date.
            # As self.last_gts_date points to the beginning of
            # the current day, that means, record['dateTime'] is not
            # the current day (today).
            __today = False
        else:
            # record['dateTime'] is after the beginning of the
            # current day. If it is additionally before
            # self.last_gts_date+86400 (1d after), it is within
            # the current day (today).
            __today = _time_ts <= self.last_gts_date + 86400

        # get the result
        if __today:
            # current value
            if obs_type == 'GTS':
                # current GTS value
                __x = weewx.units.ValueTuple(self.gts_value, 'degree_C_day',
                                             'group_degree_day')
            elif obs_type == 'GTSdate':
                # current GTSdate value or None, if GTS<200
                if _soy_ts in self.gts_date:
                    __x = self.gts_date[_soy_ts]
                else:
                    __x = None
                __x = weewx.units.ValueTuple(__x, 'unix_epoch', 'group_time')
            else:
                # should not occure
                raise weewx.CannotCalculate(obs_type)
        else:
            # value from memory
            __x = self.get_gts(obs_type, _sod_ts, _soy_ts)
        """
        try:
          a=str(__x[0])
        except:
          logerr("get_scalar 0")
          a=""
        try:
          b=str(__x[1])
        except:
          logerr("get_scalar 1")
          b=""
        try:
          c=str(__x[2])
        except:
          logerr("get_scalar 2")
          c=""
        loginf("get_scalar %s,%s,%s" % (a,b,c))
        """
        if record is None: return __x
        return weewx.units.convertStd(__x, record['usUnits'])