Hi!
I'm using weewx with a airmar 150LB, which I found in a bin (about to be 
thrown out) at my previous employer. It has a broken humidity sensor, so in 
effect it operates like a 200 WX. 

I updated the driver script to handle the case where the humidity and 
temperature is missing, but also I have a separate python script pulling in 
humidity and temperature from a cheap DHT22. So I updated the WIMDA message 
handling to parse messages where not all of the message is complete, so it 
only pulls out values which are available. I've attached the updated driver.

I'm using kplex to allow multiple inputs NMEA2000 inputs into weewx. Here's 
my kplex configuration;
[serial]
filename=/dev/ttyAMA0
direction=both
baud=4800
checksum=no
strict=no

[tcp]
mode=server
port=10111
direction=in
checksum=no
strict=no

[tcp]
mode=server
port=10110
direction=out

Here's the relevant section of the weewx config;
[Airmar]
    # This section is for the Airmar series of weather stations.
    #
    # Serial port such as /dev/ttyS0, /dev/ttyUSB0, or /dev/cuaU0
    port = socket://localhost:10110
    
    # The station model, e.g., Airmar 150WX
    model = Airmar
    
    # The driver to use:
    driver = weewx.drivers.airmar

I might see if I can update my version of weewx, and if the driver's still 
working, make a pull request to include the driver in the weewx package.


On Monday, 10 August 2020 at 21:26:20 UTC+1 [email protected] wrote:

> Hi Jim,
>
> thank you for your answer. I'll try it with the "string methode". Before I 
> tried to adapt the lines at the bottom of the aleksander's (the original) 
> driver version:
>
>
>
> *            if 'windSpeed_mwd' in data and data['windSpeed_mwd'] is not 
> None:                data['windSpeed'] = data['windSpeed_mwd']            
> elif 'windSpeed_mda' in data and data['windSpeed_mda'] is not 
> None:                data['windSpeed'] = data['windSpeed_mda']*
> With my above added dictonary entry "SOG_kn":
>
>
>
>
>
>
>
> *                try:                    data['COG_degTrue'] = 
> float(buf[1])                    data['COG_degMag'] = 
> float(buf[3])                    data['SOG_kn'] = 
> float(buf[5])                    data['SOG_kmh'] = 
> float(buf[7])                except (ValueError):                    
> pass                    loginf("Wrong data format '%s, %s, %s, %s'" % 
> (buf[1], buf[3], buf[5], buf[7]))*
> But weewx always complains about *KeyError: 'SOG_kn'. *After some 
> google-sessions I found out, that this is python typical, if there is no 
> dictonary entry for SOG_kn...which is fine, as long as I am testing on the 
> desk with no GPS signal, but outside with values for SOG_kn (even 
> displayable in grafana) I'm still receiving this error.
>
> And, I'm afraid, that I am not the Dennis who works with Cooper in Denmark 
> ;-)
> I'm here in Munich, Germany ;-)
> [email protected] schrieb am Montag, 10. August 2020 um 21:10:19 UTC+2:
>
>>
>> by the way,  are you the same Dennis that works with Cooper in Denmark?
>> On Monday, August 10, 2020 at 1:28:40 PM UTC-4 [email protected] wrote:
>>
>>>
>>> I wonder if your values are strings rather than floating point numbers?
>>>
>>> If so, then you would need:
>>> *data['windSpeed_true'] = 
>>> str(float(data['windSpeed_rel_mwv'])-float(data['SOG_kn'])* ) 
>>>
>>> I had to run my version of "airmar.py" independent of WeeWx in order to 
>>> more easily debug.
>>> When I was satisfied with my tests, then I went back to WeeWx.
>>>
>>> I had my driver working for awhile but then had some trouble related, I 
>>> think, to the RaspberryPi not having a good clock.
>>> After I get a real clock and get things going again, I will document my 
>>> driver development for Airmar 200Wx modified from Rob's.
>>>
>>> -JiM.
>>>
>>> On Saturday, August 8, 2020 at 3:14:11 PM UTC-4 [email protected] wrote:
>>>
>>>> Edit: I ment subtract not divide...sorry about that
>>>>
>>>> [email protected] schrieb am Samstag, 8. August 2020 um 21:07:44 UTC+2:
>>>>
>>>>> First of all thank you Jim and Rob for your patience ;-)
>>>>>
>>>>> Second I'd like to ask, if someone (more python versed) can tell me 
>>>>> how to divide two values in the airmar driver. In my specific case it 
>>>>> would 
>>>>> be the *windSpeed_rel_mwv* - *SOG_kn.* SOG_kn is an value I extracted 
>>>>> from the message 
>>>>> *$GPVTD. *
>>>>> If I use *data['windSpeed_true'] = 
>>>>> data['windSpeed_rel_mwv']-data['SOG_kn']* I get an error. So can 
>>>>> anyone tell me, where my mistake is?
>>>>> What I have learned, it is not the exact or 100% right way to 
>>>>> calculate "true wind" but I think I would be close enough ;-)
>>>>>
>>>>> Thank you in advance and best regards,
>>>>>
>>>>> Dennis
>>>>>
>>>>> [email protected] schrieb am Samstag, 1. August 2020 um 22:29:51 UTC+2:
>>>>>
>>>>>> Hi Rob,
>>>>>>
>>>>>> if I didn't get the technical manual for the wx unit wrong not all 
>>>>>> messages are enabled by default. So some messages have to be activated 
>>>>>> manually. Did anyone managed to do so? When I start minicom the sensor 
>>>>>> ist 
>>>>>> talking but never listening.
>>>>>> @James: how are you coming on with this topic?
>>>>>>
>>>>>> Thank you and best regards,
>>>>>>
>>>>>> Dennis
>>>>>>
>>>>>> [email protected] schrieb am Donnerstag, 9. Juli 2020 um 22:04:31 
>>>>>> UTC+2:
>>>>>>
>>>>>>> Hi Dennis,
>>>>>>>
>>>>>>> One option to see the raw data in the terminal would be to put kplex 
>>>>>>> between the usb and the weewx. Configure kplex to receive the messages 
>>>>>>> from 
>>>>>>> usb, and publish on a tcp server socket. Configure weewx to connect to 
>>>>>>> that 
>>>>>>> socket for messages.
>>>>>>>
>>>>>>> Kplex will log to a file if you configure it so, or you can simply 
>>>>>>> telnet to the kplex tcp port and see the raw messages. You're looking 
>>>>>>> for 
>>>>>>> $WIVWT messages.
>>>>>>>
>>>>>>> http://www.stripydog.com/kplex/configuration.html
>>>>>>>
>>>>>>> I hope this helps,
>>>>>>> Rob.
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On Thu, 9 Jul 2020 at 20:02, Dennis Füß <[email protected]> wrote:
>>>>>>>
>>>>>>>> Hi Jim and Rob,
>>>>>>>>
>>>>>>>> first of all: thank you for your fast reply.
>>>>>>>> For the moment I have used windSpeed_rel_mwv but it turned out, 
>>>>>>>> that the data I'm getting is the windspeed without the subtracted 
>>>>>>>> vessel 
>>>>>>>> speed. 
>>>>>>>> The main problem is, that I'm the "engineer" for the system but 
>>>>>>>> have no possibility to test it in person on the lake, where the vessel 
>>>>>>>> is 
>>>>>>>> located :-/
>>>>>>>>
>>>>>>>> Maybe I explain my setup:
>>>>>>>> Airmar 150WX goes via NMEA 0183 cable to an RS422 to USB converter 
>>>>>>>> to a raspberry pi 3 running weewx. Weewx stores the data in an 
>>>>>>>> influxdb and 
>>>>>>>> grafana is showing them.
>>>>>>>>
>>>>>>>> @Jim: thank you. But where have you specified the mentioned data? I 
>>>>>>>> mean in which configuration file?
>>>>>>>>
>>>>>>>> @Rob: you asked if I would receive the values for 
>>>>>>>> windSpeed_true_vess etc. -> I tried to call it in grafana but it 
>>>>>>>> didn't 
>>>>>>>> show me data, so I guess I don't receive them. Or is there a way to 
>>>>>>>> see all 
>>>>>>>> received data/variables in terminal (probably a stupid question, but 
>>>>>>>> for 
>>>>>>>> the moment I don't know). When I open grafana it shows me the 
>>>>>>>> available 
>>>>>>>> data from its database, so I don't see any reason why available data 
>>>>>>>> shouldn't be stored in there, right?
>>>>>>>>
>>>>>>>> So I hope, that I didn't forget anything and stay thrilled for 
>>>>>>>> Jim's findings ;-)
>>>>>>>> Thank you in advance and best regards,
>>>>>>>>
>>>>>>>> Dennis
>>>>>>>> Am Mittwoch, 8. Juli 2020 10:24:16 UTC+2 schrieb Rob Fuller:
>>>>>>>>>
>>>>>>>>> Hi Dennis,
>>>>>>>>>
>>>>>>>>> The device we use is at a fixed position so it's not something 
>>>>>>>>> I've looked at before.
>>>>>>>>>
>>>>>>>>> Looking quickly at the code I see it does try to 
>>>>>>>>> parse windAngle_true_vess and windSpeed_true_vess from the $WIVWT 
>>>>>>>>> message. 
>>>>>>>>> Does your device send that?
>>>>>>>>>
>>>>>>>>> Otherwise I'm waiting to see what James Manning comes up with ;-)
>>>>>>>>>
>>>>>>>>> Kind regards,
>>>>>>>>> Rob.
>>>>>>>>>
>>>>>>>>> On Tue, 7 Jul 2020 at 20:38, Dennis Füß <[email protected]> wrote:
>>>>>>>>>
>>>>>>>>>> Hi Rob,
>>>>>>>>>>
>>>>>>>>>> maybe you can help me out since Aleksandar seems to be gone for a 
>>>>>>>>>> while.
>>>>>>>>>> I have an Airmar 150WX up and running but i doesn't seem to give 
>>>>>>>>>> me any values for *windSpeed_true_vess*. Or in other words I 
>>>>>>>>>> don't get the windspeed subtracted from the vessel's speed. 
>>>>>>>>>>
>>>>>>>>>> Do you have any idea, how to accomplish this?
>>>>>>>>>>
>>>>>>>>>> Thank you and best regards in advance,
>>>>>>>>>>
>>>>>>>>>> Dennis
>>>>>>>>>>
>>>>>>>>>> Am Mittwoch, 10. Juni 2020 09:12:48 UTC+2 schrieb Rob Fuller:
>>>>>>>>>>>
>>>>>>>>>>> Hi James,
>>>>>>>>>>>
>>>>>>>>>>> I haven't looked at this for a while, but anyhow it was working 
>>>>>>>>>>> okay. At Marine Institute we did something like this:
>>>>>>>>>>>
>>>>>>>>>>> wget -O bin/weewx/drivers/airmar.py 
>>>>>>>>>>> https://raw.githubusercontent.com/fullergalway/weewx-airmar-150wx-driver/master/airmar.py
>>>>>>>>>>>  
>>>>>>>>>>> && chmod 755 bin/weewx/drivers/airmar.py
>>>>>>>>>>>
>>>>>>>>>>> In the weewx conf we included these lines
>>>>>>>>>>> [Station]
>>>>>>>>>>> ...
>>>>>>>>>>> station_type = Airmar
>>>>>>>>>>>
>>>>>>>>>>> [Airmar]
>>>>>>>>>>>     model = Airmar 200WX
>>>>>>>>>>>     port = socket://10.11.104.5:10000
>>>>>>>>>>>     driver = weewx.drivers.airmar 
>>>>>>>>>>>
>>>>>>>>>>> I hope this helps!
>>>>>>>>>>> Rob.
>>>>>>>>>>>
>>>>>>>>>>> On Tue, 9 Jun 2020 at 20:50, 'James Manning - NOAA Federal' via 
>>>>>>>>>>> weewx-development <[email protected]> wrote:
>>>>>>>>>>>
>>>>>>>>>>>> Hi Rob-
>>>>>>>>>>>>
>>>>>>>>>>>> First, thanks MUCH for posting your "airmar.py" on github.
>>>>>>>>>>>>
>>>>>>>>>>>> I am a very novice weewx user.  I just got the "simulator" 
>>>>>>>>>>>> working on my RaspberryPi and now I want to hook up my Arimar 
>>>>>>>>>>>> 200WX.
>>>>>>>>>>>> From what I understand,  I need to take two basic steps:
>>>>>>>>>>>>
>>>>>>>>>>>> 1) modify the weewx.conf to point to new station_type and set 
>>>>>>>>>>>> driver=weewx.drivers.airmar
>>>>>>>>>>>> 2) put the "airmar.py" in my "user" directory which I think is 
>>>>>>>>>>>> /home/pi
>>>>>>>>>>>>
>>>>>>>>>>>> Is that right?
>>>>>>>>>>>> -JiM.
>>>>>>>>>>>>
>>>>>>>>>>>> On Wednesday, May 20, 2015 at 12:05:58 PM UTC-4, Rob Fuller 
>>>>>>>>>>>> wrote:
>>>>>>>>>>>>>
>>>>>>>>>>>>> Thanks Aleksandar,
>>>>>>>>>>>>>
>>>>>>>>>>>>> I needed to make a couple of small changes for it to work with 
>>>>>>>>>>>>> our device (Airmar 200WX). Mostly:
>>>>>>>>>>>>>
>>>>>>>>>>>>> 1. use pyserial socket:// url, since we are exposing the 
>>>>>>>>>>>>> weatherstation using ftdi driver and kplex
>>>>>>>>>>>>> 2. added some detail in the log messages
>>>>>>>>>>>>> 3. added some leniency on the checksum verification (" F" == 
>>>>>>>>>>>>> "0F")
>>>>>>>>>>>>>
>>>>>>>>>>>>> For the 200WX I'm still getting a couple of warnings, but 
>>>>>>>>>>>>> haven't looked into it yet, and generally things seem ok. The 
>>>>>>>>>>>>> warnings are:
>>>>>>>>>>>>>
>>>>>>>>>>>>> weewx[11]: airmar: Wrong data format for windchill_rel ''
>>>>>>>>>>>>> weewx[11]: airmar: Wrong data format for windchill ''
>>>>>>>>>>>>> weewx[11]: airmar: Wrong data format for heatindex ''
>>>>>>>>>>>>>
>>>>>>>>>>>>> I put the source on github for the moment here if someone is 
>>>>>>>>>>>>> running with it:
>>>>>>>>>>>>> https://github.com/fullergalway/weewx-airmar-150wx-driver
>>>>>>>>>>>>>
>>>>>>>>>>>>> Thanks again,
>>>>>>>>>>>>> Rob.
>>>>>>>>>>>>>
>>>>>>>>>>>>> On Tuesday, April 28, 2015 at 11:20:47 AM UTC+1, Aleksandar 
>>>>>>>>>>>>> Tsankov wrote:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Hi,
>>>>>>>>>>>>>> I followed the mwall's advice and went back to original 
>>>>>>>>>>>>>> wxservices.py. The logic for deciding which windSpeed and 
>>>>>>>>>>>>>> windDir to be 
>>>>>>>>>>>>>> used is putted in the driver.
>>>>>>>>>>>>>> Now everything works as supposed.
>>>>>>>>>>>>>> Since I want to have some real-time application running on 
>>>>>>>>>>>>>> tablet, is it possible weewx to generate Weather Display's 
>>>>>>>>>>>>>> clientraw.txt 
>>>>>>>>>>>>>> file in order WDL (or something similar) to be used?
>>>>>>>>>>>>>> I'm attaching the new version of driver.
>>>>>>>>>>>>>>
>>>>>>>>>>>>> -- 
>>>>>>>>>>>> You received this message because you are subscribed to a topic 
>>>>>>>>>>>> in the Google Groups "weewx-development" group.
>>>>>>>>>>>> To unsubscribe from this topic, visit 
>>>>>>>>>>>> https://groups.google.com/d/topic/weewx-development/q6IHzkmyhBo/unsubscribe
>>>>>>>>>>>> .
>>>>>>>>>>>> To unsubscribe from this group and all its topics, send an 
>>>>>>>>>>>> email to [email protected].
>>>>>>>>>>>> To view this discussion on the web visit 
>>>>>>>>>>>> https://groups.google.com/d/msgid/weewx-development/b9035cdf-fd4d-41b4-ab20-95c9535968ddo%40googlegroups.com
>>>>>>>>>>>>  
>>>>>>>>>>>> <https://groups.google.com/d/msgid/weewx-development/b9035cdf-fd4d-41b4-ab20-95c9535968ddo%40googlegroups.com?utm_medium=email&utm_source=footer>
>>>>>>>>>>>> .
>>>>>>>>>>>>
>>>>>>>>>>> -- 
>>>>>>>>>> You received this message because you are subscribed to a topic 
>>>>>>>>>> in the Google Groups "weewx-development" group.
>>>>>>>>>> To unsubscribe from this topic, visit 
>>>>>>>>>> https://groups.google.com/d/topic/weewx-development/q6IHzkmyhBo/unsubscribe
>>>>>>>>>> .
>>>>>>>>>> To unsubscribe from this group and all its topics, send an email 
>>>>>>>>>> to [email protected].
>>>>>>>>>> To view this discussion on the web visit 
>>>>>>>>>> https://groups.google.com/d/msgid/weewx-development/a35bd773-b6b8-4c2d-979d-22337bbcd127o%40googlegroups.com
>>>>>>>>>>  
>>>>>>>>>> <https://groups.google.com/d/msgid/weewx-development/a35bd773-b6b8-4c2d-979d-22337bbcd127o%40googlegroups.com?utm_medium=email&utm_source=footer>
>>>>>>>>>> .
>>>>>>>>>>
>>>>>>>>> -- 
>>>>>>>> You received this message because you are subscribed to a topic in 
>>>>>>>> the Google Groups "weewx-development" group.
>>>>>>>> To unsubscribe from this topic, visit 
>>>>>>>> https://groups.google.com/d/topic/weewx-development/q6IHzkmyhBo/unsubscribe
>>>>>>>> .
>>>>>>>>
>>>>>>> To unsubscribe from this group and all its topics, send an email to 
>>>>>>>> [email protected].
>>>>>>>> To view this discussion on the web visit 
>>>>>>>> https://groups.google.com/d/msgid/weewx-development/ff51ce20-4659-405e-90b6-b981e671f096o%40googlegroups.com
>>>>>>>>  
>>>>>>>> <https://groups.google.com/d/msgid/weewx-development/ff51ce20-4659-405e-90b6-b981e671f096o%40googlegroups.com?utm_medium=email&utm_source=footer>
>>>>>>>> .
>>>>>>>>
>>>>>>>

-- 
You received this message because you are subscribed to the Google Groups 
"weewx-development" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/weewx-development/e206cd5e-ed36-40e8-b309-676b498760f7n%40googlegroups.com.
import sys
import Adafruit_DHT
import math
import socket
import operator
import re

def checksum(sentence):
    nmeadata,cksum = sentence.split('*')
    nmeadata = nmeadata[1:]
    cs_new = 0
    for d in nmeadata:
        cs_new = cs_new ^ ord(d)
    cs_new = "%2X" % cs_new
    return cs_new

# 0 degC < T < 60 degC
# 1% < RH < 100%
# 0 degC < Td < 50 degC 
 
 # constants
a = 17.271
b = 237.7 # degC
  
def dewpoint_approximation(T,RH):
    Td = (b * gamma(T,RH)) / (a - gamma(T,RH))
    return Td
              
              
def gamma(T,RH):
#    g = (a * T / (b + T)) + np.log(RH/100.0)        
    g = (a * T / (b + T)) + math.log(RH/100.0)        
    return g
                       

# Parse command line parameters.
sensor = Adafruit_DHT.DHT22
pin = 4

HOST = ''                # Symbolic name meaning all available interfaces
PORT = 10111             # Arbitrary non-privileged port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
f = s.makefile()
while 1:
    # Try to grab a sensor reading.  Use the read_retry method which will retry up
    # to 15 times to get a sensor reading (waiting 2 seconds between each retry).
    humidity, temperature = Adafruit_DHT.read_retry(sensor, pin)

    # Note that sometimes you won't get a reading and
    # the results will be null (because Linux can't
    # guarantee the timing of calls to read the sensor).
    # If this happens try again!
    if humidity is not None and temperature is not None:
        print('Temp={0:0.1f}*  Humidity={1:0.1f}%'.format(temperature, humidity))
        dew_point = dewpoint_approximation(temperature, humidity)
        nmea_string = '$WIMDA,,I,,B,{0:0.1f},C,,,{1:0.1f},,{2:0.1f},C,,T,,M,,N,,M*'.format(temperature, humidity, dew_point)
        chk_sum = checksum(nmea_string)
        #nmea_string += '{0:01d}\n'.format(chk_sum)
        nmea_string += chk_sum + '\n'
        print(nmea_string)
        f.write(nmea_string)
        f.flush()
s.close()
#!/usr/bin/env python


from __future__ import with_statement
import serial
import syslog
import time
import traceback

import weewx.drivers

DRIVER_NAME = 'Airmar'
DRIVER_VERSION = '0.29'

INHG_PER_BAR = 29.5333727
METER_PER_FOOT = 0.3048
MILE_PER_KM = 0.621371

DEBUG_SERIAL = 0

def loader(config_dict, _):
    return Airmar(**config_dict[DRIVER_NAME])

def confeditor_loader():
    return AirmarConfEditor()


DEFAULT_PORT = '/dev/ttyS0'

def logmsg(level, msg):
    syslog.syslog(level, 'airmar: %s' % msg)

def logdbg(msg):
    logmsg(syslog.LOG_DEBUG, msg)

def loginf(msg):
    logmsg(syslog.LOG_INFO, msg)

def logerr(msg):
    logmsg(syslog.LOG_ERR, msg)


class Airmar(weewx.drivers.AbstractDevice):
    """weewx driver that communicates with an Airmar Weather Station

    model: station model, e.g., 'Airmar 150WX'
    [Optional. Default is 'Airmar']

    port - serial port
    [Required. Default is /dev/ttyS0]

    max_tries - how often to retry serial communication before giving up
    [Optional. Default is 10]
    """
    def __init__(self, **stn_dict):
        self.model = stn_dict.get('model', 'Airmar')
        self.port = stn_dict.get('port', DEFAULT_PORT)
        self.max_tries = int(stn_dict.get('max_tries', 10))
        self.retry_wait = int(stn_dict.get('retry_wait', 10))
        self.last_rain = None

        global DEBUG_SERIAL
        DEBUG_SERIAL = int(stn_dict.get('debug_serial', 0))

        loginf('driver version is %s' % DRIVER_VERSION)
        loginf('using serial port %s' % self.port)
        self.station = Station(self.port)
        self.station.open()

    def closePort(self):
        if self.station is not None:
            self.station.close()
            self.station = None

    @property
    def hardware_name(self):
        return self.model

    def genLoopPackets(self):
        while True:
            packet = {'dateTime': int(time.time() + 0.5),
                      'usUnits': weewx.US}
            readings = self.station.get_readings_with_retry(self.max_tries,
                                                            self.retry_wait)
            #data = Station.parse_readings(readings)
            data = self.station.parse_readings(readings)
            packet.update(data)
            self._augment_packet(packet)
            yield packet

    def _augment_packet(self, packet):
        # calculate the rain delta from rain total
        if self.last_rain is not None:
            packet['rain'] = packet['long_term_rain'] - self.last_rain
        else:
            packet['rain'] = None
        self.last_rain = packet['long_term_rain']

        # no wind direction when wind speed is zero
        if 'windSpeed' in packet and not packet['windSpeed']:
            packet['windDir'] = None

class Station(object):
    def __init__(self, port):
        self.port = port
        self.baudrate = 4800
        self.timeout = 3 # seconds
        self.serial_port = None

    def __enter__(self):
        self.open()
        return self

    def __exit__(self, _, value, traceback):
        self.close()

    def open(self):
        logdbg("open serial port %s" % self.port)
        if "://" in self.port:
           self.serial_port = serial.serial_for_url(self.port,
                                baudrate=self.baudrate,timeout=self.timeout)
        else:
          self.serial_port = serial.Serial(self.port, self.baudrate,
                                         timeout=self.timeout)

    def close(self):
        if self.serial_port is not None:
            logdbg("close serial port %s" % self.port)
            self.serial_port.close()
            self.serial_port = None

    def get_readings(self):
        buf = self.serial_port.readline()
        if DEBUG_SERIAL:
            logdbg("station said: %s" % buf)
        buf = buf.strip() # FIXME: is this necessary?
        return buf

    def validate_string(self, buf):
        if not buf:
            return
        if buf[0:1] != '$' or buf[-3:-2] != '*':
            loginf("Garbled message")
            return
        else:
            [mess, cs]  = buf.split("*")
            mess = mess[1:]
            cs_new = 0
            for d in mess:
                cs_new = cs_new ^ ord(d)
            cs_new = "%2X" % cs_new
            if cs_new != cs:
                loginf("Unexpected checksum error %s, %s" % (cs_new, cs))
                return
            else:
                return buf

    def get_readings_with_retry(self, max_tries=5, retry_wait=10):
        for ntries in range(0, max_tries):
            try:
                buf = self.get_readings()
                reading = self.validate_string(buf)
                return reading
            except (serial.serialutil.SerialException, weewx.WeeWxIOError), e:
                loginf("Failed attempt %d of %d to get readings: %s" %
                       (ntries + 1, max_tries, e))
                time.sleep(retry_wait)
        else:
            msg = "Max retries (%d) exceeded for readings" % max_tries
            logerr(msg)
            raise weewx.RetriesExceeded(msg)

    def calc_WCHR(self, buf, idx):
        data = dict()
        try:
            data['value'] = float(buf[idx-2]) * 1.8 + 32
            data['name'] = "windchill_rel"
        except (ValueError):
            pass
            #loginf("Wrong data fromat '%s'" % buf[idx-2])
        return data
    
    def calc_WCHT(self, buf, idx):
        data = dict()
        try:
            data['value'] = float(buf[idx-2]) * 1.8 + 32
            data['name'] = "windchill"
        except (ValueError):
            pass
            #loginf("Wrong data fromat '%s'" % buf[idx-2])
        return data
    
    def calc_HINX(self, buf, idx):
        data = dict()
        try:
            data['value'] = float(buf[idx-2]) * 1.8 + 32
            data['name'] = "heatindex"
        except (ValueError):
            pass
            #loginf("Wrong data fromat '%s'" % buf[idx-2])
        return data
    
    def calc_STNP(self, buf, idx):
        data = dict()
        try:
            data['value'] = float(buf[idx-2]) * INHG_PER_BAR
            data['name'] = "pressure"
        except (ValueError):
            pass
            #loginf("Wrong data fromat '%s'" % buf[idx-2])
        return data
    
    def calc_PTCH(self, buf, idx):
        data = dict()
        try:
            data['value'] = float(buf[idx-2])
            data['name'] = "pitch"
        except (ValueError):
            pass
            #loginf("Wrong data fromat '%s'" % buf[idx-2])
        return data
    
    def calc_ROLL(self, buf, idx):
        data = dict()
        try:
            data['value'] = float(buf[idx-2])
            data['name'] = "roll"
        except (ValueError):
            pass
            #loginf("Wrong data fromat '%s'" % buf[idx-2])
        return data

    def calc_XACC(self, buf, idx):
        data = dict()
        try:
            data['value'] = float(buf[idx-2])
            data['name'] = "x_accel"
        except (ValueError):
            pass
            #loginf("Wrong data fromat '%s'" % buf[idx-2])
        return data

    def calc_YACC(self, buf, idx):
        data = dict()
        try:
            data['value'] = float(buf[idx-2])
            data['name'] = "y_accel"
        except (ValueError):
            pass
            #loginf("Wrong data fromat '%s'" % buf[idx-2])
        return data

    def calc_ZACC(self, buf, idx):
        data = dict()
        try:
            data['value'] = float(buf[idx-2])
            data['name'] = "z_accel"
        except (ValueError):
            pass
            #loginf("Wrong data fromat '%s'" % buf[idx-2])
        return data
    
    def calc_RRAT(self, buf, idx):
        data = dict()
        try:
            data['value'] = float(buf[idx-2])
            data['name'] = "rollRate"
        except (ValueError):
            pass
            #loginf("Wrong data fromat '%s'" % buf[idx-2])
        return data
    
    def calc_PRAT(self, buf, idx):
        data = dict()
        try:
            data['value'] = float(buf[idx-2])
            data['name'] = "pitchRate"
        except (ValueError):
            pass
            #loginf("Wrong data fromat '%s'" % buf[idx-2])
        return data
    
    def calc_YRAT(self, buf, idx):
        data = dict()
        try:
            data['value'] = float(buf[idx-2])
            data['name'] = "yawRate"
        except (ValueError):
            pass
            #loginf("Wrong data fromat '%s'" % buf[idx-2])
        return data
    
    def calc_RRTR(self, buf, idx):
        data = dict()
        try:
            data['value'] = float(buf[idx-2])
            data['name'] = "rollRate_raw"
        except (ValueError):
            pass
            #loginf("Wrong data fromat '%s'" % buf[idx-2])
        return data
    
    def calc_PRTR(self, buf, idx):
        data = dict()
        try:
            data['value'] = float(buf[idx-2])
            data['name'] = "pitchRate_raw"
        except (ValueError):
            pass
            #loginf("Wrong data fromat '%s'" % buf[idx-2])
        return data
    
    def calc_YRTR(self, buf, idx):
        data = dict()
        try:
            data['value'] = float(buf[idx-2])
            data['name'] = "yawRate_raw"
        except (ValueError):
            pass
            #loginf("Wrong data fromat '%s'" % buf[idx-2])
        return data
    
    def calc_PLAT(self, buf, idx):
        data = dict()
        try:
            data['value'] = float(buf[idx-2]) * 1.8 + 32
            data['name'] = "heatingTemp_plate"
        except (ValueError):
            pass
            #loginf("Wrong data fromat '%s'" % buf[idx-2])
        return data
    
    def calc_CAPT(self, buf, idx):
        data = dict()
        try:
            data['value'] = float(buf[idx-2]) * 1.8 + 32
            data['name'] = "heatingTemp_cap"
        except (ValueError):
            pass
            #loginf("Wrong data fromat '%s'" % buf[idx-2])
        return data
    
    def calc_PLAV(self, buf, idx):
        data = dict()
        try:
            data['value'] = float(buf[idx-2])
            data['name'] = "heatingVoltage_pl"
        except (ValueError):
            pass
            #loginf("Wrong data fromat '%s'" % buf[idx-2])
        return data
    
    def calc_CAPV(self, buf, idx):
        data = dict()
        try:
            data['value'] = float(buf[idx-2])
            data['name'] = "heatingVoltage_cap"
        except (ValueError):
            pass
            #loginf("Wrong data fromat '%s'" % buf[idx-2])
        return data
    
    def calc_HUMT(self, buf, idx):
        data = dict()
        try:
            data['value'] = float(buf[idx-2]) * 1.8 + 32
            data['name'] = "inTemp"
        except (ValueError):
            pass
            #loginf("Wrong data fromat '%s'" % buf[idx-2])
        return data
    
    def calc_BRDT(self, buf, idx):
        data = dict()
        try:
            data['value'] = float(buf[idx-2]) * 1.8 + 32
            data['name'] = "mbTemp"
        except (ValueError):
            pass
            #loginf("Wrong data fromat '%s'" % buf[idx-2])
        return data
    
    def calc_UPPT(self, buf, idx):
        data = dict()
        try:
            data['value'] = float(buf[idx-2]) * 1.8 + 32
            data['name'] = "upTemp"
        except (ValueError):
            pass
            #loginf("Wrong data fromat '%s'" % buf[idx-2])
        return data
    
    def calc_BRDV(self, buf, idx):
        data = dict()
        try:
            data['value'] = float(buf[idx-2])
            data['name'] = "supplyVoltage"
        except (ValueError):
            pass
            #loginf("Wrong data fromat '%s'" % buf[idx-2])
        return data

    def hasNumbers(self, inputString):
        return any(char.isdigit() for char in inputString)

    #@staticmethod
    def parse_readings(self, raw):
        """Airmar.......
        """
        loginf('raw is %s' % raw)
        data = dict()
        yx_data = dict()
        data['long_term_rain'] = None
        if raw:
            try:
                (interm, cs) = raw.split("*")
                buf = interm.split(",")
                if buf[0] == '$HCHDG':
                    try:
                        data['heading_magn'] = float(buf[1])
                        data['deviation_magn'] = float(buf[2])
                        data['variation_magn'] = float(buf[4])
                    except (ValueError):
                        pass
                        #loginf("Wrong data fromat '%s, %s, %s'" % (buf[1], buf[2], buf[4]))
                    if buf[3] == 'E':
                        data['deviation_magn'] *= -1 #?????????????
                    if buf[5] == 'E':
                        data['variation_magn'] *= -1 #?????????????
                elif buf[0] == '$HCHDT':
                    try:
                        data['heading_true'] = float(buf[1])
                    except (ValueError):
                        loginf("Wrong data format '%s'" % buf[1])
                        pass
                elif buf[0] == '$WIMDA':
                    try:
                        if (self.hasNumbers(buf[1].strip())):
                            data['altimeter'] = float(buf[1])
                        if (self.hasNumbers(buf[5].strip())):
                            data['outTemp'] = float(buf[5]) * 1.8 + 32
                        if (self.hasNumbers(buf[9].strip())):
                            data['outHumidity'] = float(buf[9])
                        if (self.hasNumbers(buf[11].strip())):
                            data['dewpoint'] = float(buf[11]) * 1.8 + 32
                        if (self.hasNumbers(buf[13].strip())):
                            data['windDir_true_mda'] = float(buf[13])
                        if (self.hasNumbers(buf[15].strip())):
                            data['windDir_magn_mda'] = float(buf[15])
                        if (self.hasNumbers(buf[17].strip())):
                            data['windSpeed_mda'] = float(buf[17]) / 1.15077945 #Wind speed, mph

                    except (ValueError):
                        loginf("Wrong data fromat '%s, %s, %s, %s, %s, %s, %s'" % (buf[1], buf[5], buf[9], buf[11], buf[13], buf[15], buf[17]))
                        pass
                elif buf[0] == '$WIMWD':
                    try:
                        data['windDir_true_mwd'] = float(buf[1])
                        data['windDir_magn_mwd'] = float(buf[3])
                        data['windSpeed_mwd'] = float(buf[5]) / 1.15077945
                    except (ValueError):
                        loginf("Wrong data format '%s, %s, %s'" % (buf[1], buf[3], buf[5]))
                        pass
                elif buf[0] == '$WIMWV':
                    if buf[5] == 'A':
                        if buf[2] == 'R':
                            try:
                                data['windAngle_rel_mwv'] = float(buf[1])
                                data['windSpeed_rel_mwv'] = float(buf[3]) / 1.15077945
                            except (ValueError):
                                loginf("Wrong data format '%s, %s'" % (buf[1], buf[3]))
                                pass
                        elif buf[2] == 'T':
                            try:
                                data['windAngle_theor_mwv'] = float(buf[1])
                                data['windSpeed_theor_mwv'] = float(buf[3]) / 1.15077945
                            except (ValueError):
                                loginf("Wrong data format '%s, %s'" % (buf[1], buf[3]))
                                pass
                elif buf[0] == '$TIROT':
                    if buf[2] == 'A':
                        try:
                            data['tiRot'] = float(buf[1])
                        except (ValueError):
                            loginf("Wrong data format '%s'" % buf[1])
                            pass
                elif buf[0] == '$HCTHS':
                    if buf[2] == 'A':
                        try:
                            data['true_north_heading'] = float(buf[1])
                        except (ValueError):
                            loginf("Wrong data format '%s'" % buf[1])
                            pass
                elif buf[0] == '$WIVWR':
                    try:
                        data['windAngle_rel_vess'] = float(buf[1])
                        data['windSpeed_rel_vess'] = float(buf[3]) / 1.15077945
                    except (ValueError):
                        loginf("Wrong data format '%s, %s'" % (buf[1], buf[3]))
                        pass
                    if buf[2] == 'R': #R = right
                        data['windAngle_rel_vess'] *= -1 #???????????????????
                elif buf[0] == '$WIVWT':
                    try:
                        data['windAngle_true_vess'] = float(buf[1])
                        data['windSpeed_true_vess'] = float(buf[3]) / 1.15077945
                    except (ValueError):
                        loginf("Wrong data format '%s, %s'" % (buf[1], buf[3]))
                        pass
                    if buf[2] == 'R': #R = right
                        data['windAngle_true_vess'] *= -1 #???????????????????????
                elif buf[0] == '$YXXDR':
                    for idx in [4, 8, 12, 16]:
                        yx_data.clear()
                        try:
                            typestr = buf[idx]
                            yx_data = getattr(self, 'calc_'+typestr)(buf, idx)
                            if 'value' in yx_data:
                                data[yx_data['name']] = yx_data['value']
                        except (IndexError):
                            break
                elif buf[0] == '$WIXDR':
                    if buf[4] == 'RAIN':
                        try:
                            data['long_term_rain'] = float(buf[2]) * 0.03937007874015748
                            data['duration_of_rain'] = float(buf[6])
                            data['rain_intensity'] = float(buf[10]) * 0.03937007874015748
                            data['peak_rain_intensity'] = float(buf[14]) * 0.03937007874015748
                        except (ValueError):
                            loginf("Wrong data format '%s, %s, %s, %s'" % (buf[2], buf[6], buf[10], buf[14]))
                            pass
                    if buf[4] == 'WNDA':
                        try:
                            data['windAngle_unfilt'] = float(buf[2])
                            data['windSpeed_unfilt'] = float(buf[6]) / 1.15077945
                        except (ValueError):
                            loginf("Wrong data format '%s, %s'" % (buf[2], buf[6]))
                            pass
                #else: #Processing of other data sentences
                if 'windDir_true_mwd' in data and data['windDir_true_mwd'] is not None:
                    data['windDir'] = data['windDir_true_mwd']
                elif 'windDir_true_mda' in data and data['windDir_true_mda'] is not None:
                    data['windDir'] = data['windDir_true_mda']

                if 'windSpeed_mwd' in data and data['windSpeed_mwd'] is not None:
                    data['windSpeed'] = data['windSpeed_mwd']
                elif 'windSpeed_mda' in data and data['windSpeed_mda'] is not None:
                    data['windSpeed'] = data['windSpeed_mda']
            except (ValueError, AttributeError):
                loginf(traceback.format_exc())
                loginf("Wrong data packet")
        return data

class AirmarConfEditor(weewx.drivers.AbstractConfEditor):
    @property
    def default_stanza(self):
        return """
[Airmar]
    # This section is for the Airmar series of weather stations.

    # Serial port such as /dev/ttyS0, /dev/ttyUSB0, or /dev/cuaU0
    port = /dev/ttyUSB0

    # The station model, e.g., Airmar 150WX
    model = Airmar

    # The driver to use:
    driver = weewx.drivers.airmar
"""

    def prompt_for_settings(self):
        print "Specify the serial port on which the station is connected, for"
        print "example /dev/ttyUSB0 or /dev/ttyS0."
        port = self._prompt('port', '/dev/ttyUSB0')
        return {'port': port}


# define a main entry point for basic testing of the station without weewx
# engine and service overhead.  invoke this as follows from the weewx root dir:
#
# PYTHONPATH=bin python bin/weewx/drivers/airmar.py

if __name__ == '__main__':
    import optparse

    usage = """%prog [options] [--help]"""

    syslog.openlog('airmar', syslog.LOG_PID | syslog.LOG_CONS)
    syslog.setlogmask(syslog.LOG_UPTO(syslog.LOG_DEBUG))
    parser = optparse.OptionParser(usage=usage)
    parser.add_option('--version', dest='version', action='store_true',
                      help='display driver version')
    parser.add_option('--port', dest='port', metavar='PORT',
                      help='serial port to which the station is connected',
                      default=DEFAULT_PORT)
    (options, args) = parser.parse_args()

    if options.version:
        print "airmar driver version %s" % DRIVER_VERSION
        exit(0)

    with Station(options.port) as s:
        s.set_logger_mode()
        while True:
            print time.time(), s.get_readings()

Reply via email to