mirror of
https://github.com/weewx/weewx.git
synced 2026-04-21 18:17:01 -04:00
Now saves average console battery voltage in the archive.
This commit is contained in:
@@ -1,17 +1,11 @@
|
||||
NEW FEATURES to be added in decreasing order of importance.
|
||||
|
||||
Save LOOP battery data in the archive file.
|
||||
Move archiving and stats into a separate service.
|
||||
|
||||
Calibration capability.
|
||||
Calibration capability. Should be a separate service.
|
||||
|
||||
Allow rain-to-date to be added to a partial rainyear.
|
||||
|
||||
Noticed during one test that VantagePro.py downloaded a record with
|
||||
timestamp 18:02. I deleted the record in archive.sdb and tried again.
|
||||
Did it again. Obviously, the assumption that all archive intervals are
|
||||
the same is not correct. I think this happened during a lengthy download
|
||||
from the VP2 into a virgin database, which spanned the 18:00 boundary.
|
||||
|
||||
Add support for MySQL.
|
||||
|
||||
Support for APT package.
|
||||
|
||||
@@ -17,6 +17,7 @@ import time
|
||||
from weewx.crc16 import crc16
|
||||
import weeutil.weeutil
|
||||
import weewx
|
||||
import weewx.accum
|
||||
import weewx.wxformulas
|
||||
|
||||
# A few handy constants:
|
||||
@@ -46,33 +47,51 @@ class SerialWrapper(object):
|
||||
except:
|
||||
pass
|
||||
|
||||
class WxStation (object) :
|
||||
"""Class that represents a connection to a VantagePro console.
|
||||
|
||||
After initialization, the serial port (e.g., '/dev/ttyUSB0') specified in the
|
||||
configuration dictionary will have been opened.
|
||||
|
||||
"""
|
||||
def __init__(self, config_dict) :
|
||||
class VantagePro (object) :
|
||||
"""Class that represents a connection to a VantagePro console."""
|
||||
def __init__(self, **vp_dict) :
|
||||
"""Initialize an object of type VantagePro.
|
||||
|
||||
config_dict: The 'VantagePro' section of a configuration dictionary.
|
||||
The port (e.g., '/dev/ttyUSB0'), baudrate, and other parameters are
|
||||
extracted from this dictionary.
|
||||
PARAMETERS:
|
||||
|
||||
port: The serial port of the VP. [Required]
|
||||
|
||||
baudrate: Baudrate of the port. [Optional. Default 19200]
|
||||
|
||||
timeout: How long to wait before giving up on a response from the
|
||||
serial port. [Optional. Default is 5]
|
||||
|
||||
wait_before_retry: How long to wait before retrying. [Optional.
|
||||
Default is 1.2 seconds]
|
||||
|
||||
max_tries: How many times to try again before giving up. [Optional.
|
||||
Default is 4]
|
||||
|
||||
archive_delay: How long to wait after an archive record is due
|
||||
before retrieving it. [Optional. Default is 15 seconds]
|
||||
|
||||
max_drift: Maximum drift allowed on the on board VP clock before
|
||||
it will be corrected. [Optional. Default is 5 seconds]
|
||||
|
||||
iss_id: The station number of the ISS [Optional. Default is 1]
|
||||
|
||||
unit_system: What unit system to use on the VP. [Optional.
|
||||
Default is 1 (US Customary), and the only system supported
|
||||
in this version.]
|
||||
"""
|
||||
# TODO: These values should really be retrieved dynamically from the VP:
|
||||
self.iss_id = int(config_dict.get('iss_id', '1'))
|
||||
self.iss_id = int(vp_dict.get('iss_id', 1))
|
||||
self.model_type = 2 # = 1 for original VantagePro, = 2 for VP2
|
||||
|
||||
# These come from the configuration dictionary:
|
||||
self.port = config_dict['port']
|
||||
self.timeout = float(config_dict.get('timeout', 5.0))
|
||||
self.wait_before_retry= float(config_dict.get('wait_before_retry', 1.2))
|
||||
self.max_tries = int(config_dict.get('max_tries' , 4))
|
||||
self.baudrate = int(config_dict.get('baudrate' , 19200))
|
||||
self.archive_delay = int(config_dict.get('archive_delay', 15))
|
||||
self.unit_system = int(config_dict.get('unit_system' , 1))
|
||||
self.max_drift = int(config_dict.get('max_drift' , 5))
|
||||
self.port = vp_dict['port']
|
||||
self.baudrate = int(vp_dict.get('baudrate' , 19200))
|
||||
self.timeout = float(vp_dict.get('timeout', 5.0))
|
||||
self.wait_before_retry= float(vp_dict.get('wait_before_retry', 1.2))
|
||||
self.max_tries = int(vp_dict.get('max_tries' , 4))
|
||||
self.archive_delay = int(vp_dict.get('archive_delay', 15))
|
||||
self.max_drift = int(vp_dict.get('max_drift' , 5))
|
||||
self.unit_system = int(vp_dict.get('unit_system' , 1))
|
||||
|
||||
# Get the archive interval dynamically:
|
||||
self.archive_interval = self.getArchiveInterval()
|
||||
@@ -91,6 +110,7 @@ class WxStation (object) :
|
||||
for _loopPacket in self.genDavisLoopPackets(200):
|
||||
# Translate the LOOP packet to one with physical units:
|
||||
_physicalPacket = self.translateLoopPacket(_loopPacket)
|
||||
self.accumulateLoop(_physicalPacket)
|
||||
yield _physicalPacket
|
||||
|
||||
# Check to see if it's time to get new archive data. If so, cancel the loop
|
||||
@@ -124,7 +144,8 @@ class WxStation (object) :
|
||||
# Fetch a packet
|
||||
buffer = serial_port.read(99)
|
||||
if len(buffer) != 99 :
|
||||
syslog.syslog(syslog.LOG_DEBUG, "VantagePro: LOOP #%d; buffer not full (%d)... retrying" % (loop,len(buffer)))
|
||||
syslog.syslog(syslog.LOG_DEBUG,
|
||||
"VantagePro: LOOP #%d; buffer not full (%d)... retrying" % (loop,len(buffer)))
|
||||
continue
|
||||
# ... decode it
|
||||
pkt_dict = DavisLoopPacket(buffer[:89])
|
||||
@@ -132,7 +153,8 @@ class WxStation (object) :
|
||||
yield pkt_dict
|
||||
break
|
||||
else:
|
||||
syslog.syslog(syslog.LOG_ERR, "VantagePro: Max retries exceeded while getting LOOP packets")
|
||||
syslog.syslog(syslog.LOG_ERR,
|
||||
"VantagePro: Max retries exceeded while getting LOOP packets")
|
||||
raise weewx.RetriesExceeded, "While getting LOOP packets"
|
||||
|
||||
def genArchivePackets(self, since_ts):
|
||||
@@ -198,6 +220,8 @@ class WxStation (object) :
|
||||
if _record['dateTime'] <= _last_good_ts :
|
||||
# The time stamp is declining. We're done.
|
||||
return
|
||||
# Add any LOOP data we've been accumulating:
|
||||
self.addAccumulators(_record)
|
||||
# Set the last time to the current time, and yield the packet
|
||||
_last_good_ts = _record['dateTime']
|
||||
yield _record
|
||||
@@ -208,7 +232,57 @@ class WxStation (object) :
|
||||
continue
|
||||
syslog.syslog(syslog.LOG_ERR, "VantagePro: Max retries exceeded while getting archive packets")
|
||||
raise weewx.RetriesExceeded, "Max retries exceeded while getting archive packets"
|
||||
|
||||
|
||||
# List of VP2 types for which archive records will be explicitly calculated.
|
||||
special = ['consBatteryVoltage']
|
||||
|
||||
def accumulateLoop(self, physicalLOOPPacket):
|
||||
"""Process LOOP data, calculating averages within an archive period."""
|
||||
try:
|
||||
# Gather the LOOP data for each special type. An exception
|
||||
# will be thrown if either the accumulators have not been initialized
|
||||
# yet, or if the timestamp of the packet is outside the timespan held
|
||||
# by the accumulator.
|
||||
for obs_type in self.special:
|
||||
self.current_accumulators[obs_type].addToSum(physicalLOOPPacket)
|
||||
# For battery status, OR every status field together:
|
||||
self.txBatteryStatus |= physicalLOOPPacket['txBatteryStatus']
|
||||
except (AttributeError, weewx.accum.OutOfSpan):
|
||||
# Initialize the accumulators:
|
||||
self.clearAccumulators(physicalLOOPPacket['dateTime'])
|
||||
# Try again, calling myself recursively:
|
||||
self.accumulateLoop(physicalLOOPPacket)
|
||||
|
||||
def clearAccumulators(self, time_ts):
|
||||
"""Initialize or clear the accumulators"""
|
||||
try:
|
||||
# Shuffle accumulators. An exception will be thrown
|
||||
# if they have never been initialized.
|
||||
self.last_accumulators=self.current_accumulators
|
||||
except:
|
||||
pass
|
||||
# Calculate the interval timespan that contains time_ts
|
||||
start_of_interval = weeutil.weeutil.startOfInterval(time_ts, self.archive_interval)
|
||||
timespan = weeutil.weeutil.TimeSpan(start_of_interval, start_of_interval+self.archive_interval)
|
||||
# Initialize current_accumulators with instances of StdAccum
|
||||
self.current_accumulators = {}
|
||||
for obs_type in VantagePro.special:
|
||||
self.current_accumulators[obs_type] = weewx.accum.StdAccum(obs_type, timespan)
|
||||
self.txBatteryStatus = 0
|
||||
|
||||
def addAccumulators(self, record):
|
||||
"""Add the results of the accumulators to the current archive record."""
|
||||
try:
|
||||
# For each special type, add its average to the archive record. An exception
|
||||
# will be thrown if there is no accumulator (first time through).
|
||||
for obs_type in VantagePro.special:
|
||||
# Make sure the times match:
|
||||
if self.last_accumulators[obs_type].timespan.stop == record['dateTime']:
|
||||
record[obs_type] = self.last_accumulators[obs_type].avg
|
||||
record['txBatteryStatus'] = float(self.txBatteryStatus)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def getTime(self) :
|
||||
"""Get the current time from the console and decode it, returning it as a time-tuple
|
||||
|
||||
@@ -240,9 +314,7 @@ class WxStation (object) :
|
||||
newtime_tt: A time tuple with the time to which the
|
||||
clock should be set. If 'None', then the host's time will
|
||||
be used. In this case, if the clock has drifted less than
|
||||
maxdiff seconds, nothing is done.
|
||||
|
||||
"""
|
||||
maxdiff seconds, nothing is done. """
|
||||
# Unfortunately, this algorithm takes a little while to execute, so the clock
|
||||
# usually ends up a few hundred milliseconds slow
|
||||
if newtime_tt is None:
|
||||
@@ -471,8 +543,8 @@ class WxStation (object) :
|
||||
'leafWet2' : _little_val,
|
||||
'leafWet3' : _little_val,
|
||||
'leafWet4' : _little_val,
|
||||
'transmitBattery' : _null_int,
|
||||
'consoleBattery' : lambda v : float((v * 300) >> 9) / 100.0
|
||||
'txBatteryStatus' : _null_int,
|
||||
'consBatteryVoltage' : lambda v : float((v * 300) >> 9) / 100.0
|
||||
}
|
||||
|
||||
if packet['usUnits'] != weewx.US :
|
||||
@@ -722,7 +794,7 @@ class DavisLoopPacket(dict) :
|
||||
'dayRain', 'monthRain', 'yearRain', 'dayET', 'monthET', 'yearET',
|
||||
'soilMoist1', 'soilMoist2', 'soilMoist3', 'soilMoist4',
|
||||
'leafWet1', 'leafWet2', 'leafWet3', 'leafWet4',
|
||||
'transmitBattery', 'consoleBattery')
|
||||
'txBatteryStatus', 'consBatteryVoltage')
|
||||
|
||||
loop_format = struct.Struct("<3sbBHHHBHBBH7B4B4BB7BHBHHHHHHHHH4B4B16xBH")
|
||||
|
||||
@@ -938,7 +1010,7 @@ if __name__ == '__main__':
|
||||
ans = raw_input("about to configure VantagePro. OK (y/n)? ")
|
||||
if ans == 'y' :
|
||||
# Open up the weather station:
|
||||
station = WxStation(config_dict['VantagePro'])
|
||||
station = VantagePro(config_dict['VantagePro'])
|
||||
station.config(config_dict)
|
||||
print "Done."
|
||||
else :
|
||||
|
||||
@@ -88,6 +88,10 @@ class StdAccum(object):
|
||||
self.sum += val
|
||||
self.count += 1
|
||||
|
||||
@property
|
||||
def avg(self):
|
||||
return self.sum/self.count if self.count else None
|
||||
|
||||
def getStatsTuple(self):
|
||||
"""Return a stats-tuple. That is, a tuple containing the
|
||||
gathered statistics."""
|
||||
|
||||
@@ -163,12 +163,13 @@ class StdEngine(object):
|
||||
# Look for and load the module that handles this hardware type:
|
||||
_moduleName = "weewx." + stationType
|
||||
__import__(_moduleName)
|
||||
hardware_module = sys.modules[_moduleName]
|
||||
|
||||
try:
|
||||
|
||||
# Now open up the weather station:
|
||||
self.station = hardware_module.WxStation(self.config_dict[stationType])
|
||||
self.station = weeutil.weeutil._get_object(_moduleName + '.' + stationType,
|
||||
**self.config_dict[stationType])
|
||||
|
||||
|
||||
except Exception, ex:
|
||||
# Caught unrecoverable error. Log it:
|
||||
|
||||
Reference in New Issue
Block a user