def calcDerivedVariables(self, device, config, device_type): """ Calculate derived variables from available device observations INPUTS: device Device ID config Console configuration object device_type Device type """ # Derive variables from available obs_out_air and obs_st observations # Derive variables from available obs_out_air and obs_st observations if device_type in ('obs_out_air', 'obs_st'): self.derive_obs['feelsLike'] = derive.feelsLike(self.device_obs['outTemp'], self.device_obs['humidity'], self.device_obs['windSpd'], config) self.derive_obs['dewPoint'] = derive.dewPoint(self.device_obs['outTemp'], self.device_obs['humidity']) self.derive_obs['outTempDiff'] = derive.tempDiff(self.device_obs['outTemp'], self.device_obs['obTime'], device, self.api_data, config) self.derive_obs['outTempTrend'] = derive.tempTrend(self.device_obs['outTemp'], self.device_obs['obTime'], device, self.api_data, config) self.derive_obs['outTempMax'] = derive.tempMax(self.device_obs['outTemp'], self.device_obs['obTime'], self.derive_obs['outTempMax'], device, self.api_data, config) self.derive_obs['outTempMin'] = derive.tempMin(self.device_obs['outTemp'], self.device_obs['obTime'], self.derive_obs['outTempMin'], device, self.api_data, config) self.derive_obs['SLP'] = derive.SLP(self.device_obs['pressure'], device, config) self.derive_obs['SLPTrend'] = derive.SLPTrend(self.device_obs['pressure'], self.device_obs['obTime'], device, self.api_data, config) self.derive_obs['SLPMax'] = derive.SLPMax(self.device_obs['pressure'], self.device_obs['obTime'], self.derive_obs['SLPMax'], device, self.api_data, config) self.derive_obs['SLPMin'] = derive.SLPMin(self.device_obs['pressure'], self.device_obs['obTime'], self.derive_obs['SLPMin'], device, self.api_data, config) self.derive_obs['strikeCount'] = derive.strikeCount(self.device_obs['strikeMinute'], self.derive_obs['strikeCount'], device, self.api_data, config) self.derive_obs['strikeFreq'] = derive.strikeFrequency(self.device_obs['obTime'], device, self.api_data, config) self.derive_obs['strikeDeltaT'] = derive.strikeDeltaT(self.device_obs['strikeTime']) # Derive variables from available obs_sky and obs_st observations if device_type in ('obs_sky', 'obs_st'): self.derive_obs['uvIndex'] = derive.UVIndex(self.device_obs['uvIndex']) self.derive_obs['peakSun'] = derive.peakSunHours(self.device_obs['radiation'], self.derive_obs['peakSun'], device, self.api_data, config) self.derive_obs['windSpd'] = derive.beaufortScale(self.device_obs['windSpd']) self.derive_obs['windDir'] = derive.cardinalWindDir(self.device_obs['windDir'], self.device_obs['windSpd']) self.derive_obs['windAvg'] = derive.avgWindSpeed(self.device_obs['windSpd'], self.derive_obs['windAvg'], device, self.api_data, config) self.derive_obs['gustMax'] = derive.maxWindGust(self.device_obs['windGust'], self.derive_obs['gustMax'], device, self.api_data, config) self.derive_obs['rainRate'] = derive.rainRate(self.device_obs['minuteRain']) self.derive_obs['rainAccum'] = derive.rainAccumulation(self.device_obs['dailyRain'], self.derive_obs['rainAccum'], device, self.api_data, config) # Derive variables from available obs_out_air and obs_st observations if device_type == 'obs_in_air': self.derive_obs['inTempMax'] = derive.tempMax(self.device_obs['inTemp'], self.device_obs['obTime'], self.derive_obs['inTempMax'], device, self.api_data, config) self.derive_obs['inTempMin'] = derive.tempMin(self.device_obs['inTemp'], self.device_obs['obTime'], self.derive_obs['inTempMin'], device, self.api_data, config) # Derive variables from available rapid_wind observations if device_type == 'rapid_wind': self.derive_obs['rapidWindDir'] = derive.cardinalWindDir(self.device_obs['rapidWindDir'], self.device_obs['rapidWindSpd']) # Derive variables from available evt_strike observations if device_type == 'evt_strike': self.derive_obs['strikeDeltaT'] = derive.strikeDeltaT(self.device_obs['strikeTime']) # Format derived observations self.format_derived_variables(config, device_type)
def Tempest(Msg, wfpiconsole): """ Handles Websocket messages received from TEMPEST module INPUTS: Msg Websocket messages received from TEMPEST module wfpiconsole wfpiconsole object """ # Replace missing observations from latest TEMPEST Websocket JSON with NaN Ob = [x if x != None else NaN for x in Msg['obs'][0]] # Discard duplicate TEMPEST Websocket messages if 'TempestMsg' in wfpiconsole.Obs: if wfpiconsole.Obs['TempestMsg']['obs'][0] == Ob[0]: print('Discarding duplicate TEMPEST Websocket message') return # Extract TEMPEST device ID, API flag, and station configuration object Device = wfpiconsole.config['Station']['TempestID'] flagAPI = wfpiconsole.flagAPI[0] Config = wfpiconsole.config # Extract required observations from latest TEMPEST Websocket JSON Time = [Ob[0], 's'] WindSpd = [Ob[2], 'mps'] WindGust = [Ob[3], 'mps'] WindDir = [Ob[4], 'degrees'] Pres = [Ob[6], 'mb'] Temp = [Ob[7], 'c'] Humidity = [Ob[8], '%'] UV = [Ob[10], 'index'] Radiation = [Ob[11], 'Wm2'] Rain = [Ob[12], 'mm'] Strikes = [Ob[15], 'count'] # Extract lightning strike data from the latest AIR Websocket JSON "Summary" # object StrikeTime = [ Msg['summary']['strike_last_epoch'] if 'strike_last_epoch' in Msg['summary'] else NaN, 's' ] StrikeDist = [ Msg['summary']['strike_last_dist'] if 'strike_last_dist' in Msg['summary'] else NaN, 'km' ] Strikes3hr = [ Msg['summary']['strike_count_3h'] if 'strike_count_3h' in Msg['summary'] else NaN, 'count' ] # Store latest TEMPEST Websocket message wfpiconsole.Obs['TempestMsg'] = Msg # Extract required derived observations minPres = wfpiconsole.Obs['MinPres'] maxPres = wfpiconsole.Obs['MaxPres'] minTemp = wfpiconsole.Obs['outTempMin'] maxTemp = wfpiconsole.Obs['outTempMax'] StrikeCount = { 'Today': wfpiconsole.Obs['StrikesToday'], 'Month': wfpiconsole.Obs['StrikesMonth'], 'Year': wfpiconsole.Obs['StrikesYear'] } rainAccum = { 'Today': wfpiconsole.Obs['TodayRain'], 'Yesterday': wfpiconsole.Obs['YesterdayRain'], 'Month': wfpiconsole.Obs['MonthRain'], 'Year': wfpiconsole.Obs['YearRain'] } peakSun = wfpiconsole.Obs['peakSun'] avgWind = wfpiconsole.Obs['AvgWind'] maxGust = wfpiconsole.Obs['MaxGust'] # Request TEMPEST data from the previous three hours Data3h = requestAPI.weatherflow.Last3h(Device, Time[0], Config) # Calculate derived variables from TEMPEST observations DewPoint = derive.DewPoint(Temp, Humidity) SLP = derive.SLP(Pres, Config) PresTrend = derive.SLPTrend(Pres, Time, Data3h, Config) FeelsLike = derive.FeelsLike(Temp, Humidity, WindSpd, Config) MaxTemp, MinTemp = derive.TempMaxMin(Time, Temp, maxTemp, minTemp, Device, Config, flagAPI) MaxPres, MinPres = derive.SLPMaxMin(Time, Pres, maxPres, minPres, Device, Config, flagAPI) StrikeCount = derive.StrikeCount(Strikes, StrikeCount, Device, Config, flagAPI) StrikeFreq = derive.StrikeFrequency(Time, Data3h, Config) StrikeDeltaT = derive.StrikeDeltaT(StrikeTime) FeelsLike = derive.FeelsLike(Temp, Humidity, WindSpd, Config) RainRate = derive.RainRate(Rain) rainAccum = derive.RainAccumulation(Rain, rainAccum, Device, Config, flagAPI) AvgWind = derive.MeanWindSpeed(WindSpd, avgWind, Device, Config, flagAPI) MaxGust = derive.MaxWindGust(WindGust, maxGust, Device, Config, flagAPI) WindSpd = derive.BeaufortScale(WindSpd) WindDir = derive.CardinalWindDirection(WindDir, WindSpd) peakSun = derive.peakSunHours(Radiation, peakSun, wfpiconsole.Astro, Device, Config, flagAPI) UVIndex = derive.UVIndex(UV) # Convert observation units as required Temp = observation.Units(Temp, Config['Units']['Temp']) MaxTemp = observation.Units(MaxTemp, Config['Units']['Temp']) MinTemp = observation.Units(MinTemp, Config['Units']['Temp']) DewPoint = observation.Units(DewPoint, Config['Units']['Temp']) FeelsLike = observation.Units(FeelsLike, Config['Units']['Temp']) SLP = observation.Units(SLP, Config['Units']['Pressure']) MaxPres = observation.Units(MaxPres, Config['Units']['Pressure']) MinPres = observation.Units(MinPres, Config['Units']['Pressure']) PresTrend = observation.Units(PresTrend, Config['Units']['Pressure']) StrikeDist = observation.Units(StrikeDist, Config['Units']['Distance']) RainRate = observation.Units(RainRate, Config['Units']['Precip']) TodayRain = observation.Units(rainAccum['Today'], Config['Units']['Precip']) YesterdayRain = observation.Units(rainAccum['Yesterday'], Config['Units']['Precip']) MonthRain = observation.Units(rainAccum['Month'], Config['Units']['Precip']) YearRain = observation.Units(rainAccum['Year'], Config['Units']['Precip']) WindSpd = observation.Units(WindSpd, Config['Units']['Wind']) WindDir = observation.Units(WindDir, Config['Units']['Direction']) WindGust = observation.Units(WindGust, Config['Units']['Wind']) AvgWind = observation.Units(AvgWind, Config['Units']['Wind']) MaxGust = observation.Units(MaxGust, Config['Units']['Wind']) FeelsLike = observation.Units(FeelsLike, Config['Units']['Temp']) # Store derived TEMPEST observations in dictionary derivedObs = {} derivedObs['outTemp'] = observation.Format(Temp, 'Temp') derivedObs['outTempMax'] = observation.Format(MaxTemp, 'Temp') derivedObs['outTempMin'] = observation.Format(MinTemp, 'Temp') derivedObs['DewPoint'] = observation.Format(DewPoint, 'Temp') derivedObs['FeelsLike'] = observation.Format(FeelsLike, 'Temp') derivedObs['Pres'] = observation.Format(SLP, 'Pressure') derivedObs['MaxPres'] = observation.Format(MaxPres, 'Pressure') derivedObs['MinPres'] = observation.Format(MinPres, 'Pressure') derivedObs['PresTrend'] = observation.Format(PresTrend, 'Pressure') derivedObs['StrikeDeltaT'] = observation.Format(StrikeDeltaT, 'TimeDelta') derivedObs['StrikeDist'] = observation.Format(StrikeDist, 'StrikeDistance') derivedObs['StrikeFreq'] = observation.Format(StrikeFreq, 'StrikeFrequency') derivedObs['Strikes3hr'] = observation.Format(Strikes3hr, 'StrikeCount') derivedObs['StrikesToday'] = observation.Format(StrikeCount['Today'], 'StrikeCount') derivedObs['StrikesMonth'] = observation.Format(StrikeCount['Month'], 'StrikeCount') derivedObs['StrikesYear'] = observation.Format(StrikeCount['Year'], 'StrikeCount') derivedObs['Humidity'] = observation.Format(Humidity, 'Humidity') derivedObs['FeelsLike'] = observation.Format(FeelsLike, 'Temp') derivedObs['RainRate'] = observation.Format(RainRate, 'Precip') derivedObs['TodayRain'] = observation.Format(TodayRain, 'Precip') derivedObs['YesterdayRain'] = observation.Format(YesterdayRain, 'Precip') derivedObs['MonthRain'] = observation.Format(MonthRain, 'Precip') derivedObs['YearRain'] = observation.Format(YearRain, 'Precip') derivedObs['WindSpd'] = observation.Format(WindSpd, 'Wind') derivedObs['WindGust'] = observation.Format(WindGust, 'Wind') derivedObs['AvgWind'] = observation.Format(AvgWind, 'Wind') derivedObs['MaxGust'] = observation.Format(MaxGust, 'Wind') derivedObs['WindDir'] = observation.Format(WindDir, 'Direction') derivedObs['Radiation'] = observation.Format(Radiation, 'Radiation') derivedObs['peakSun'] = observation.Format(peakSun, 'peakSun') derivedObs['UVIndex'] = observation.Format(UVIndex, 'UV') # Update wfpiconsole display with derived TEMPEST observations updateDisplay(derivedObs, wfpiconsole, 'Tempest') # Set flags for required API calls wfpiconsole.flagAPI[0] = 0 # Return wfpiconsole object return wfpiconsole
def outdoorAir(Msg, wfpiconsole): """ Handles Websocket messages received from outdoor AIR module INPUTS: Msg Websocket messages received from outdoor AIR module wfpiconsole wfpiconsole object """ # Replace missing observations in latest outdoor AIR Websocket JSON with NaN Ob = [x if x != None else NaN for x in Msg['obs'][0]] # Discard duplicate outdoor AIR Websocket messages if 'outAirMsg' in wfpiconsole.Obs: if wfpiconsole.Obs['outAirMsg']['obs'][0] == Ob[0]: print('Discarding duplicate outdoor AIR Websocket message') return # Extract outdoor AIR device ID and API flag, and station configuration # object Device = wfpiconsole.config['Station']['OutAirID'] flagAPI = wfpiconsole.flagAPI[2] Config = wfpiconsole.config # Extract required observations from latest outdoor AIR Websocket JSON Time = [Ob[0], 's'] Pres = [Ob[1], 'mb'] Temp = [Ob[2], 'c'] Humidity = [Ob[3], '%'] Strikes = [Ob[4], 'count'] # Extract lightning strike data from the latest outdoor AIR Websocket JSON # "Summary" object StrikeTime = [ Msg['summary']['strike_last_epoch'] if 'strike_last_epoch' in Msg['summary'] else NaN, 's' ] StrikeDist = [ Msg['summary']['strike_last_dist'] if 'strike_last_dist' in Msg['summary'] else NaN, 'km' ] Strikes3hr = [ Msg['summary']['strike_count_3h'] if 'strike_count_3h' in Msg['summary'] else NaN, 'count' ] # Extract required derived observations minPres = wfpiconsole.Obs['MinPres'] maxPres = wfpiconsole.Obs['MaxPres'] minTemp = wfpiconsole.Obs['outTempMin'] maxTemp = wfpiconsole.Obs['outTempMax'] StrikeCount = { 'Today': wfpiconsole.Obs['StrikesToday'], 'Month': wfpiconsole.Obs['StrikesMonth'], 'Year': wfpiconsole.Obs['StrikesYear'] } # Request outdoor AIR data from the previous three hours Data3h = requestAPI.weatherflow.Last3h(Device, Time[0], Config) # Store latest outdoor AIR Websocket message wfpiconsole.Obs['outAirMsg'] = Msg # Extract required observations from latest SKY Websocket JSON while not 'SkyMsg' in wfpiconsole.Obs: time.sleep(0.01) Ob = [x if x != None else NaN for x in wfpiconsole.Obs['SkyMsg']['obs'][0]] WindSpd = [Ob[5], 'mps'] # Calculate derived variables from AIR observations DewPoint = derive.DewPoint(Temp, Humidity) SLP = derive.SLP(Pres, Config) PresTrend = derive.SLPTrend(Pres, Time, Data3h, Config) FeelsLike = derive.FeelsLike(Temp, Humidity, WindSpd, Config) MaxTemp, MinTemp = derive.TempMaxMin(Time, Temp, maxTemp, minTemp, Device, Config, flagAPI) MaxPres, MinPres = derive.SLPMaxMin(Time, Pres, maxPres, minPres, Device, Config, flagAPI) StrikeCount = derive.StrikeCount(Strikes, StrikeCount, Device, Config, flagAPI) StrikeFreq = derive.StrikeFrequency(Time, Data3h, Config) StrikeDeltaT = derive.StrikeDeltaT(StrikeTime) # Convert observation units as required Temp = observation.Units(Temp, Config['Units']['Temp']) MaxTemp = observation.Units(MaxTemp, Config['Units']['Temp']) MinTemp = observation.Units(MinTemp, Config['Units']['Temp']) DewPoint = observation.Units(DewPoint, Config['Units']['Temp']) FeelsLike = observation.Units(FeelsLike, Config['Units']['Temp']) SLP = observation.Units(SLP, Config['Units']['Pressure']) MaxPres = observation.Units(MaxPres, Config['Units']['Pressure']) MinPres = observation.Units(MinPres, Config['Units']['Pressure']) PresTrend = observation.Units(PresTrend, Config['Units']['Pressure']) StrikeDist = observation.Units(StrikeDist, Config['Units']['Distance']) # Store derived outdoor AIR observations in dictionary derivedObs = {} derivedObs['outTemp'] = observation.Format(Temp, 'Temp') derivedObs['outTempMax'] = observation.Format(MaxTemp, 'Temp') derivedObs['outTempMin'] = observation.Format(MinTemp, 'Temp') derivedObs['DewPoint'] = observation.Format(DewPoint, 'Temp') derivedObs['FeelsLike'] = observation.Format(FeelsLike, 'Temp') derivedObs['Pres'] = observation.Format(SLP, 'Pressure') derivedObs['MaxPres'] = observation.Format(MaxPres, 'Pressure') derivedObs['MinPres'] = observation.Format(MinPres, 'Pressure') derivedObs['PresTrend'] = observation.Format(PresTrend, 'Pressure') derivedObs['StrikeDeltaT'] = observation.Format(StrikeDeltaT, 'TimeDelta') derivedObs['StrikeDist'] = observation.Format(StrikeDist, 'StrikeDistance') derivedObs['StrikeFreq'] = observation.Format(StrikeFreq, 'StrikeFrequency') derivedObs['Strikes3hr'] = observation.Format(Strikes3hr, 'StrikeCount') derivedObs['StrikesToday'] = observation.Format(StrikeCount['Today'], 'StrikeCount') derivedObs['StrikesMonth'] = observation.Format(StrikeCount['Month'], 'StrikeCount') derivedObs['StrikesYear'] = observation.Format(StrikeCount['Year'], 'StrikeCount') derivedObs['Humidity'] = observation.Format(Humidity, 'Humidity') # Update wfpiconsole display with derived outdoor AIR observations updateDisplay(derivedObs, wfpiconsole, 'outdoorAir') # Set flags for required API calls wfpiconsole.flagAPI[2] = 0 # Return wfpiconsole object return wfpiconsole
def SLPMaxMin(Time, Pres, maxPres, minPres, Device, Config, flagAPI): """ Calculate maximum and minimum pressure since midnight station time INPUTS: Time Current observation time [s] Temp Current pressure [mb] maxPres Current maximum pressure [mb] minPres Current minimum pressure [mb] Device Device ID Config Station configuration flagAPI Flag for required API calls OUTPUT: MaxTemp Maximum pressure [mb] MinTemp Minumum pressure [mb] """ # Calculate sea level pressure SLP = derive.SLP(Pres, Config) # Define current time in station timezone Tz = pytz.timezone(Config['Station']['Timezone']) Now = datetime.now(pytz.utc).astimezone(Tz) # Set time format based on user configuration if Config['Display']['TimeFormat'] == '12 hr': if Config['System']['Hardware'] != 'Other': Format = '%-I:%M %P' else: Format = '%I:%M %p' else: Format = '%H:%M' # If console is initialising, download all data for current day using # Weatherflow API and calculate daily maximum and minimum pressure if maxPres[0] == '-' or flagAPI: # Download pressure data from the current day Data = requestAPI.weatherflow.Today(Device, Config) # Calculate maximum and minimum pressure. Return NaN if API call fails if requestAPI.weatherflow.verifyResponse(Data, 'obs'): # Extract data from API call based on device type Data = Data.json()['obs'] Time = [item[0] for item in Data if item[0] != None] if Config['Station']['OutAirID']: Pres = [[item[1], 'mb'] for item in Data if item[1] != None] elif Config['Station']['TempestID']: Pres = [[item[6], 'mb'] for item in Data if item[6] != None] # Calculate sea level pressure SLP = [derive.SLP(P, Config) for P in Pres] # Define maximum and minimum pressure if len(SLP) > 0: MaxPres = [ max(SLP)[0], 'mb', datetime.fromtimestamp(Time[SLP.index(max(SLP))], Tz).strftime(Format), max(SLP)[0], Now ] MinPres = [ min(SLP)[0], 'mb', datetime.fromtimestamp(Time[SLP.index(min(SLP))], Tz).strftime(Format), min(SLP)[0], Now ] else: MaxPres = [NaN, 'mb', '-', NaN, Now] MinPres = [NaN, 'mb', '-', NaN, Now] else: MaxPres = [NaN, 'mb', '-', NaN, Now] MinPres = [NaN, 'mb', '-', NaN, Now] # Else if midnight has passed, reset maximum and minimum pressure elif Now.date() > maxPres[4].date(): MaxPres = [ SLP[0], 'mb', datetime.fromtimestamp(Time[0], Tz).strftime(Format), SLP[0], Now ] MinPres = [ SLP[0], 'mb', datetime.fromtimestamp(Time[0], Tz).strftime(Format), SLP[0], Now ] # Else if current pressure is greater than maximum recorded pressure, update # maximum pressure elif SLP[0] > maxPres[3]: MaxPres = [ SLP[0], 'mb', datetime.fromtimestamp(Time[0], Tz).strftime(Format), SLP[0], Now ] MinPres = [minPres[3], 'mb', minPres[2], minPres[3], Now] # Else if current pressure is less than minimum recorded pressure, update # minimum pressure and time elif SLP[0] < minPres[3]: MaxPres = [maxPres[3], 'mb', maxPres[2], maxPres[3], Now] MinPres = [ SLP[0], 'mb', datetime.fromtimestamp(Time[0], Tz).strftime(Format), SLP[0], Now ] # Else maximum and minimum pressure unchanged, return existing values else: MaxPres = [maxPres[3], 'mb', maxPres[2], maxPres[3], Now] MinPres = [minPres[3], 'mb', minPres[2], minPres[3], Now] # Return required variables return MaxPres, MinPres