#!/usr/bin/python # # $ Revision2A: beta 032 $ # $ Author: WhimickRH $ # $ US - UK $ # $ Date: 2017-10-31 # $ # $ Raspi_Met Version 2 Beta 032 $ # $ For For GrovePi Weather Station $ # $ With Weather Display CSV output $ # -------------------------------IMPORT DEPENDENCIES----------------------------------------- from __future__ import with_statement import os import os.path import sys import atexit import time import datetime import cPickle import logging import traceback import random import threading import multiprocessing import csv import shutil # -------------------------------GROVEPI BOARD AND SENSORS----------------------------------- import smbus import RPi.GPIO as GPIO import grovepi from grovepi import * import BMP280.BMP280 as BMP280 # -------------------------------WEEWX DRIVERS----------------------------------------------- # import weeutil.weeutil # import weewx.drivers # import weewx.wxformulas # -------------------------------PATH AND MODULES-------------------------------------------- sys.path.append('/home/pi/Weather_Sensors') import Air_Sensors import SDL_Pi_SI1145 import SI1145Lux from grove_i2c_temp_hum_hdc1000 import HDC1000 import SDL_DS3231 import Wind_Rain6 # -------------------------------GROVEPI BOARD & SENSOR SET UP------------------------------- rev = GPIO.RPI_REVISION if rev == 2 or rev == 3: bus = smbus.SMBus(1) else: bus = smbus.SMBus(0) GPIO.setwarnings(False) record_no = 0 # -------------------------------RESET LEDS-------------------------------------------------- green_led = 4 digitalWrite(green_led, 0) # -------------------------------ANALOG SENSORS---------------------------------------------- Water = 0 # Pin 15 is A0 Port. Grove - Water_Sensor(LeafWetness) Moisture = 1 # Pin 16 is A1 Port. Grove - Moisture_Sensor(PlantPot) grovepi.pinMode(Water, "INPUT") # Grove - Water_Sensor(LeafWetness) grovepi.pinMode(Moisture, "INPUT") # Grove - Moisture Sensor(PlantPot) water_sensor = 0 moisture_sensor = 0 ws = 0 # -------------------------------SUNLIGHT SENSOR Si1145-------------------------------------- # sunsensor = SI1145.SI1145() sunsensor = SDL_Pi_SI1145.SDL_Pi_SI1145() # -------------------------------12C SENSORS------------------------------------------------- bmp280 = BMP280.BMP280() # GROVE 12C TEMPERATURE-HUMID-BAROMOMETER hdc = HDC1000() hdc.Config() # -------------------------------RTL DS3231 RTC---------------------------------------------- ds3231 = SDL_DS3231.SDL_DS3231(1, 0x68) # -------------------------------ERROR LOGGING----------------------------------------------- logging.basicConfig(filename='/home/pi/WxRam/Raspi_Met2WD_Error.txt', filemode='a', level=logging.ERROR, format='%(asctime)s %(levelname)s %(name)s %(message)s') logger=logging.getLogger(__name__) # -------------------------------GROVEPI WEATHER STATION------------------------------------- DRIVER_NAME = 'Raspi_Met2WX' DRIVER_VERSION = '032' # def loader(config_dict, _): # return Raspi_Met2WXDriver(**config_dict[DRIVER_NAME]) class Raspi_Met2WXDriver(object): # class Raspi_Met2WXDriver(weewx.drivers.AbstractDevice): def __init__(self): # def __init__(self, **stn_dict): # self.poll_interval = int(poll_interval) # seconds self.starttime = time.time() self.start_hour = int(time.strftime("%H")) self.Software_Version = "Raspi_Met Version 2 Beta 032" self.record_no = record_no self.na = "" # Temperature&Humidity Sensor (HDC1000) self.outTemp_C = 0 self.outTemp_F = 0 self.outHumidity = 0 self.inTemp_C = 0 self.inTemp_F = 0 self.mps = 0 self.mph = 0 self.kph = 0 self.kts = 0 self.bf = 0 self.beaufort_description = 0 self.voltageValue = 0 self.degrees = 0 self.bearing = 0 self.rain_in = 0 self.rain_mm = 0 rain_mm = 0 self.new_val = 0 self.av_mph = 0 self.mx_mph = 0 self.av_kph = 0 self.mx_kph = 0 self.moisture_sensor = 0 self.water_sensor = 0 self.lowpulseoccupancy = 0 self.windchill_idx_C = 0 self.windchill_idx_F = 0 def hardware_name(self): return DRIVER_NAME # -------------------------------READ SDL_DS3231 TIME---------------------------------------- def ds3231_time(self): self.rtc_time = "%s" % ds3231.read_datetime() self.rtc_temp = ds3231.getTemp() ds3231_t = ds3231.read_datetime() self.epoch = int(time.mktime(time.strptime(ds3231_t.strftime("%Y-%m-%d %H:%M:%S"), "%Y-%m-%d %H:%M:%S"))) GPIO.cleanup() # print("Times",self.rtc_time, self.rtc_time, self.epoch) # --------DATE TIME--------------- self.year = int(time.strftime("%Y")) self.month = int(time.strftime("%m")) self.day = int(time.strftime("%d")) self.hour = int(time.strftime("%H")) self.minutes = int(time.strftime("%M")) self.seconds = int(time.strftime("%S")) return self.rtc_time, self.rtc_time, self.epoch # -------------------------------RASPI TEMPERATURE SENSORS----------------------------------- def pi_temp(self): f = open('/sys/class/thermal/thermal_zone0/temp', 'r') input = f.readline() if input: s_temp = float(input) / 1000 self.system_temp_C = round(s_temp, 1) # self.system_temp_F = round(9.0 / 5.0 * self.system_temp_C + 32, 1) self.system_temp_F = (self.system_temp_C * 1.8) + 32 else: self.system_temp_C = 0 self.system_temp_F = 0 # print("system_temp_C", self.system_temp_C) return self.system_temp_C, self.system_temp_F # -------------------------------READ DUST SENSOR FILE--------------------------------------- def read_air_sensors(self): if os.path.exists("/home/pi/WxRam/air.txt"): air_file = open("/home/pi/WxRam/air.txt", 'r') self.lowpulseoccupancy = cPickle.load(air_file) air_file.close() # print("Dust PPM",self.lowpulseoccupancy) return self.lowpulseoccupancy # -----------READ ANALOG SENSORS----------------------------------------- def analog(self): water_sensor = 0 moisture_sensor = 0 ws = 0 ws = grovepi.analogRead(Water) wv = ([950, 870, 790, 710, 630, 550, 470, 390, 310, 230, 150]) # lw = ([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) lw = ([10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) eof_wv = len(wv) for l in range(0, eof_wv): if ws <= wv[l]: self.water_sensor = lw[l] # print("Analog Water_(Leafwet)sensor", ws, self.water_sensor) moisture_sensor = grovepi.analogRead(Moisture) if moisture_sensor >= 6000: moisture_sensor = 0 self.moisture_sensor = moisture_sensor # print("Analog Moisture(Plantpot)", self.moisture_sensor) return self.water_sensor, self.moisture_sensor # -------------------------------LIGHT SENSORS----------------------------------------------- def light_sensors(self): self.vis = sunsensor.readVisible() self.IR = sunsensor.readIR() self.UV = sunsensor.readUV() self.IR_Lux = round(SI1145Lux.SI1145_IR_to_Lux(self.IR), 2) self.vis_Lux = round(SI1145Lux.SI1145_VIS_to_Lux(self.vis), 2) self.uvIndex = self.UV / 100.0 self.wm2 = int(self.vis_Lux * 0.0079) # print("Vis", self.vis, "Vis_Lux", self.vis_Lux, "IR", self.IR, "IR_Lux", self.IR_Lux) # print("UV", self.UV, "uvIndex", self.uvIndex, "WM/2", self.wm2) return self.vis, self.IR, self.UV, self.uvIndex, self.wm2, self.vis_Lux # -------------------------------READ TEMPERATURE & BARO SENSORS--------------- def temp_baro_sensors(self): # this function gets temp values from the bmp # mspl = 0 # mspl_inHg = 0 self.inTemp_C, self.barometer = bmp280.read_temperature_pressure() self.barometer = round((float(self.barometer) * 0.01), 2) self.inHg = round(0.029529980164712 * self.barometer, 2) self.mspl_inHg = round(self.barometer * 0.02952998751, 2) self.mspl = round(float(bmp280.read_sealevel_pressure(altitude_m=50.0)) * 0.01, 2) # Takes new messurement so the sensor_data might have changed self.altitude = round(bmp280.read_altitude(sealevel_pa=101000.0), 2) # print("BPM280","InTemp_C", self.inTemp_C, "Barometer", self.barometer, "Altitude", self.altitude) # Read HDC1000 self.outTemp_C = round(hdc.Temperature(), 2) self.outTemp_F = round((self.outTemp_C* 1.8) + 32, 2) self.outHumidity = int(hdc.Humidity()) self.inTemp_F = round((self.inTemp_C * 1.8) + 32, 2) # print("Read HDC1000","OutTemp_C",self.outTemp_C, "OutHumidity",self.outHumidity) return self.outTemp_C, self.inTemp_F, self.outTemp_F, self.inHg, self.mspl_inHg, self.altitude, self.outHumidity # -------------------------------READ WIND VANE---------------------------------------------- def read_windvane(self): wf = open("/home/pi/WxRam/wind_list.txt", 'r') wind_l = cPickle.load(wf) wf.close() # print(wind_l) self.mps = wind_l[0] self.mph = wind_l[1] self.kph = wind_l[2] self.kts = wind_l[3] self.bf = wind_l[4] self.beaufort_description = wind_l[5] self.mph_av = wind_l[6] self.mph_mx = wind_l[7] self.kph_av = wind_l[8] self.kph_mx = wind_l[9] self.voltageValue = wind_l[10] self.degrees = wind_l[11] self.bearing = wind_l[12] self.rain_in = wind_l[13] self.rain_mm = wind_l[14] return self.mps, self.mph, self.kph, self.kts, self.bf, self.beaufort_description, self.mph_av, self.mph_mx, \ self.kph_av, self.kph_mx, self.voltageValue, self.degrees, self.bearing, self.rain_in, self.rain_mm # -------------------------------MET CALCULATIONS-------------------------------------------- def calculations(self): # Define the constants windchill_C = 0 windchill_offset = 13.12 windchill_factor1 = 0.6215 windchill_factor2 = -11.37 windchill_factor3 = 0.3965 windchill_exp = 0.16 temp = int(self.outTemp_C) speed = int(self.mph) windchill_C = windchill_offset + (windchill_factor1 * temp) + (windchill_factor2 * speed ** windchill_exp) + (windchill_factor3 * temp * speed ** windchill_exp) self.windchill_idx_C = int(windchill_C) self.windchill_idx_F = int((windchill_C * 1.8) + 32) # self.windchill_idx_C = int(35.74 + (0.6215 * t) - 35.75 * (v ** 0.16) + 0.4275 * t * (v ** 0.16)) # self.windchill_idx_F = int(self.inTemp_C * 9 / 5 + 32) # Dewpoint x = 1 - 0.01 * self.outHumidity dewp = (14.55 + 0.114 * temp) * x dew = dewp + ((2.5 + 0.007 * temp) * x) ** 3 self.dewpoint_C = round(dew + (15.9 + 0.117 * temp) * x ** 14, 2) self.dewpoint_F = round((self.dewpoint_C * 1.8) + 32, 2) self.tempDif_C = round(self.inTemp_C - self.outTemp_C, 2) self.tempDif_F = round(self.inTemp_F - self.outTemp_F, 2) # print("Caculations","Windchill_C",self.windchill_idx_C, "Dewpoint",self.dewpoint_C, self.dewpoint_F) return self.windchill_idx_F, self.windchill_idx_C, self.dewpoint_C, self.dewpoint_F, self.tempDif_F, self.tempDif_C # -------------------------------CSV FIELDS-------------------------------------------------- def writeCSVfile(self): csv_data_us = ([self.year, self.month, self.day, self.hour, self.minutes, self.seconds]) csv_data_us.extend([self.outTemp_F, self.outHumidity, self.inTemp_F, self.na, self.tempDif_F]) csv_data_ms = ([self.year, self.month, self.day, self.hour, self.minutes, self.seconds]) csv_data_ms.extend([self.outTemp_C, self.outHumidity, self.inTemp_C, self.na, self.tempDif_C]) csv_data_us.extend([self.inHg, self.mspl_inHg, self.mph_av, self.bearing, self.degrees, self.mph]) csv_data_ms.extend([self.barometer, self.mspl, self.kph_av, self.bearing, self.degrees, self.kph]) csv_data_us.extend([self.na, self.rain_in, self.wm2, self.UV, self.uvIndex]) csv_data_ms.extend([self.na, self.rain_mm, self.wm2, self.UV, self.uvIndex]) csv_data_us.extend([self.na, self.na, self.na, self.system_temp_F, self.water_sensor, self.moisture_sensor]) csv_data_ms.extend([self.na, self.na, self.na, self.system_temp_C, self.water_sensor, self.moisture_sensor]) # -------------------------------WRITE CSV FILES--------------------------------------------- us = open('/home/pi/WxRam/Weather_Data_us.csv', 'a') ms = open('/home/pi/WxRam/Weather_Data_ms.csv', 'a') with us: writer_us = csv.writer(us, delimiter=",") writer_ms = csv.writer(ms, delimiter=",") writer_us.writerow(csv_data_us) writer_ms.writerow(csv_data_ms) return # -------------------------------OVER WRITE CSV's-------------------------------------------- def overwrite(self): us = open('/home/pi/WxRam/Weather_Data_us.csv', 'w') ms = open('/home/pi/WxRam/Weather_Data_ms.csv', 'w') us.close() ms.close() print("Replacing csv's Files") return # -------------------------------TXT FILE FIELDS--------------------------------------------- def writeTXTfile(self): # ----FOR US DATA---- wxi_fields = (["Data No:", "Year", "Month", "Day", "Hour", "Minutes", "Seconds", ]) wxm_fields = (["Data No:", "Year", "Month", "Day", "Hour", "Minutes", "Seconds", ]) vwsi_data = ([self.record_no, self.year, self.month, self.day, self.hour, self.minutes, self.seconds, ]) vwsm_data = ([self.record_no, self.year, self.month, self.day, self.hour, self.minutes, self.seconds, ]) wxi_fields.extend(["Outside Temp..(F)", "OutSide Hum..(%)", "House Temp..(F)", "House Hum..(%)", "Temperature Dif..(C)", ]) wxm_fields.extend(["Outside Temp..(C)", "OutSide Hum..(%)", "House Temp..(C)", "House Hum..(%)", "Temperature Dif..(F)", ]) vwsi_data.extend([self.outTemp_F, self.outHumidity, self.inTemp_F, self.na, self.tempDif_F, ]) vwsm_data.extend([self.outTemp_C, self.outHumidity, self.inTemp_C, self.na, self.tempDif_C, ]) wxi_fields.extend(["Barometer(inHg)", "Pressure(inHg)", "Windspeed Average (mph)", "Wind Beaing", "Wind Dir..(Deg)", ]) wxm_fields.extend(["Barometer(hPa)", "Pressure(hPa)", "Windspeed Average(kph)", "Wind Beaing", "Wind Dir..(Deg)", ]) vwsi_data.extend([self.inHg, self.mspl_inHg, self.mx_mph, self.bearing, self.degrees, ]) vwsm_data.extend([self.barometer, self.mspl, self.mx_kph, self.bearing, self.degrees, ]) wxi_fields.extend(["Wind Gust(mph)", "Rain Hour(ins)", "Rain Day(ins)", "Solar Radiation(wm/2)", "UV", "UVI", "Lumens", ]) wxm_fields.extend(["Wind Gust(kph)", "Rain Hour(mm)", "Rain Day(mm)", "Solar Radiation(wm/2)", "UV", "UVI", "Lumens", ]) vwsi_data.extend([self.mph, self.rain_in, self.na, self.wm2, self.UV, self.uvIndex, self.vis_Lux, ]) vwsm_data.extend([self.kph, self.rain_mm, self.na, self.wm2, self.UV, self.uvIndex, self.vis_Lux, ]) wxi_fields.extend(["Rain Week(ins)", "Rain Month(ins)", "Rain Year(ins)", ]) wxm_fields.extend(["Rain Week(mm)", "Rain Month(mm)", "Rain Year(mm)", ]) vwsi_data.extend([self.na, self.na, self.na, ]) vwsm_data.extend([self.na, self.na, self.na, ]) wxi_fields.extend(["Extra Tempurature_1(F)", "Leaf Wetness(cb)", "Soil Moisture(cb)", "Wind mph", "Dust Lowpulseoccupancy"]) wxm_fields.extend(["Extra Tempurature_1(C)", "Leaf Wetness(cb)", "Soil Moisture(cb)", "Wind kph", "Dust Lowpulseoccupancy"]) vwsi_data.extend([self.system_temp_F, self.water_sensor, self.moisture_sensor, self.mph, self.lowpulseoccupancy]) vwsm_data.extend([self.system_temp_C, self.water_sensor, self.moisture_sensor, self.kph, self.lowpulseoccupancy]) eof_f = len(wxi_fields) # -------------------------------WRITE TEXT FILES-------------------------------------------- wxt = open('/home/pi/WxRam/Weather_Data_Current_US.txt', 'w') wxm = open('/home/pi/WxRam/Weather_Data_Current_Metric.txt', 'w') for x in range(0, eof_f): y = x + 1 wxnow_i = "%s. %s = %s\n" % (y, wxi_fields[x], vwsi_data[x]) wxnow_m = "%s. %s = %s\n" % (y, wxm_fields[x], vwsm_data[x]) wxt.write(wxnow_i) wxm.write(wxnow_m) wxt.close() wxm.close() return # -------------------------------SCREEN OUTPUT----------------------------------------------- def Screen_out(self): print print("------------------------------------------------------------------------------------------------------ ") print("Record No: ", self.record_no, self.Software_Version) print("Date & Time", time.strftime("%Y %m %d - %H:%M:%S"), "DS3231 Time:", self.rtc_time, "Epoch Time", self.epoch) print("Outside Temperature C:", self.outTemp_C, "Outside Temperature F:", self.outTemp_F, "Raspberry Pi Temp C:" , self.system_temp_C, "DS3231 Temp(C):", self.rtc_temp) print("Barometer", self.barometer, "inHg", self.inHg, "Outside Humidity %", self.outHumidity, "Dewpoint C", self.dewpoint_C) print("Light", "Visible", self.vis, "Vis_Lux", self.vis_Lux, "IR", self.IR, "IR_Lux", self.IR_Lux, "UV", self.UV, "uvIndex", self.uvIndex, "WM/2", self.wm2) print("Water Sensor:", self.water_sensor, "Moisture Sensor:", self.moisture_sensor) # print("Water Sensor:", self.water_sensor, "Moisture Sensor:",self.moisture_sensor) print("Wind speed mp/s:", self.mps, "Wind speed MPH:", self.mph, "Wind speed KPH:", self.kph, "Wind speed KNOTS:", self.kts) print("Beaufort_description:", self.beaufort_description, "Beaufort_number", self.bf, "Wind Chill idx C", self.windchill_idx_C) print("ADC voltage volts:", self.voltageValue, "Wind direction degrees:", self.degrees, "Wind bearing:", self.bearing) print("Rain mm", self.rain_mm, "Rain inches", self.rain_in, "Dust lowpulseoccupancy", self.lowpulseoccupancy) print("------------------------------------------------------------------------------------------------------ ") return # -------------------------------ARCHIVE WEEWX DATA------------------------------------------ def archive_sdb(self): if os.path.exists("/home/pi/Archive/weewx.sdb"): os.remove("/home/pi/Archive/weewx.sdb") shutil.copy2("/home/pi/WxRam/weewx.sdb", "/home/pi/Archive/weewx.sdb") return # -------------------------------GENLOOPPACKETS---------------------------------------------- def genLoopPackets(self): while True: try: self.ds3231_time() if self.hour != self.start_hour: self.overwrite() self.start_hour = self.hour self.pi_temp() self.read_air_sensors() self.analog() self.light_sensors() self.temp_baro_sensors() self.read_windvane() self.calculations() self.writeCSVfile() self.writeTXTfile() self.Screen_out() time.sleep(5.0 - ((time.time() - self.starttime) % 5.0)) self.record_no += 1 except Exception as e: digitalWrite(green_led, 0) logging.exception(str(e)) print(logging.exception(str(e))) sys.exit() # -------------------------------UPDATE DS3231 RTC------------------------------------------- def set_ds3231(): # Main Program print("") print("Test SDL_DS3231 Version 1.0 - SwitchDoc Labs") print("") print("Program Started at:" + time.strftime("%Y-%m-%d %H:%M:%S")) print("") filename = time.strftime("%Y-%m-%d%H:%M:%SRTCTest") + ".txt" starttime = datetime.datetime.utcnow() ds3231 = SDL_DS3231.SDL_DS3231(1, 0x68) # comment out the next line after the clock has been initialized ds3231.write_now() # Main Loop - sleeps 10 seconds, then reads and prints values of all clocks # Also reads two bytes of EEPROM and writes the next value to the two bytes # do the AT24C32 eeprom print("-----------------") print(" Test the AT24C32 EEPROM") print("-----------------") print("writing first 10 addresses with random data") for x in range(0, 10): value = random.randint(0, 255) print("address = %i writing value=%i" % (x, value)) ds3231.write_AT24C32_byte(x, value) print("----------------- ") print("reading first 10 addresses") for x in range(0, 10): print("address = %i value = %i" % (x, ds3231.read_AT24C32_byte(x))) print("-----------------") currenttime = datetime.datetime.utcnow() deltatime = currenttime - starttime print("") print("Raspberry Pi=\t" + time.strftime("%Y-%m-%d %H:%M:%S")) print("DS3231=\t\t%s" % ds3231.read_datetime()) print("DS3231 Temp=", ds3231.getTemp()) GPIO.cleanup() return # -------------------------------RECOVER WEEWX DATABASE--------------------------------------- def recover_sdb(): if os.path.exists("/home/pi/Archive/weewx.sdb"): os.remove("/home/pi/WxRam/weewx.sdb") shutil.copy2("/home/pi/Archive/weewx.sdb", "/home/pi/WxRam/weewx.sdb") return # -------------------------------MAIN-------------------------------------------------------- def main(): # print("Recovering Weewx DataBase") # recover_data = multiprocessing.Process(target=recover_sdb) # recover_data.daemon = True # recover_data.start() print("Updating RTC") rtc_time = multiprocessing.Process(target=set_ds3231) # rtctime.daemon = True rtc_time.start() print("Starting the dust sensor") AirSensors = multiprocessing.Process(target=Air_Sensors.main) AirSensors.start() print("Starting WindVane)") # Wind_Rain = threading.Thread(name='Wind_vane', target=Wind_Rain6.main) Wind_Rain = multiprocessing.Process(target=Wind_Rain6.main) # Wind_Rain.setDaemon(True) Wind_Rain.start() print("Starting Air Sensors and Wind Vane & Waiting 10 Seconds)") time.sleep(10) digitalWrite(green_led, 1) print("Starting Main Weather Station)") station = Raspi_Met2WXDriver() # StartMet = multiprocessing.Process(target=station.genLoopPackets) StartMet = threading.Thread(name='GetMet', target=station.genLoopPackets) StartMet.setDaemon(True) StartMet.start() return # -------------------------------START MAIN AND ERROR TRAPPING------------------------------- if __name__ == '__main__': try: main() except Exception as e: digitalWrite(green_led, 0) logging.exception(str(e)) print(logging.exception(str(e))) #print(logging.error(traceback.format_exc())) sys.exit() # -------------------------------INFORMATION-------------------------------------------------- """ The (25) HEADERS for a CSV file that WD can now use, if interested, are: 1. YR 2. MO 3. DAY 4. HR 5. MIN 6. SEC 7. TEMP OUTSIDE (F) 8. HUMIDITY OUTSIDE 9. TEMP INSIDE (F) 10. HUMIDITY INSIDE 11. TEMP DIFF (IN minus OUT) 12. ABSOLUTE PRESSURE (IN) 13. RELATIVE PRESSURE (IN) 14. AVG WIND (MPH) 15. WIND BEARING (S, N, E, W, NNE, ETC.) 16. WIND DIRECTION (DEG) 17. WIND GUST (MPH) 18. RAIN-HOURLY (INCHES) 19. RAIN-DAILY (INCHES) 20. SOLAR RADIATION (WATTS/M2) 21. UV 22. UVI 23. RAIN-WEEK (INCHES) 24. RAIN-MONTH (INCHES) 25. RAIN-YR (INCHES) # -------------------------------DEWPOINT CALCULATIONS--------------------------------------- # approximation valid for # 0 degC < T < 60 degC # 1% < RH < 100% # 0 degC < Td < 50 degC # constants a = 17.271 b = 237.7 # degC # sys.argv[0] is program name T = float(sys.argv[1]) RH = float(sys.argv[2]) 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) return g Td = dewpoint_approximation(T, RH) print('T, RH', T, RH) print('Td=', Td) """