From d70acae76e44db92bb82f9e27eec96523cbb9c84 Mon Sep 17 00:00:00 2001
From: Mark Crossley <1196094+mcrossley@users.noreply.github.com>
Date: Tue, 10 May 2022 19:41:44 +0100
Subject: [PATCH 1/5] Add Simulator Remove lock debug messages
---
CumulusMX/Cumulus.cs | 19 +-
CumulusMX/CumulusMX.csproj | 1 +
CumulusMX/DavisAirLink.cs | 6 +-
CumulusMX/DavisStation.cs | 12 +-
CumulusMX/DavisWllStation.cs | 47 ++---
CumulusMX/EcowittApi.cs | 10 +-
CumulusMX/EmailSender.cs | 12 +-
CumulusMX/FOStation.cs | 6 +-
CumulusMX/GW1000Station.cs | 14 +-
CumulusMX/HttpStationEcowitt.cs | 17 +-
CumulusMX/Properties/AssemblyInfo.cs | 6 +-
CumulusMX/Simulator.cs | 285 +++++++++++++++++++++++++++
CumulusMX/TempestStation.cs | 6 +-
CumulusMX/WeatherStation.cs | 41 +++-
Updates.txt | 9 +
15 files changed, 423 insertions(+), 68 deletions(-)
create mode 100644 CumulusMX/Simulator.cs
diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs
index f969ebd5..ee47ed88 100644
--- a/CumulusMX/Cumulus.cs
+++ b/CumulusMX/Cumulus.cs
@@ -749,10 +749,11 @@ public struct MqttSettings
"HTTP WUnderground", // 13
"HTTP Ecowitt", // 14
"HTTP Ambient", // 15
- "WeatherFlow Tempest" // 16
+ "WeatherFlow Tempest", // 16
+ "Simulator"
};
- public string[] APRSstationtype = { "DsVP", "DsVP", "WMR928", "WM918", "EW", "FO", "WS2300", "FOs", "WMR100", "WMR200", "IMET", "DsVP", "Ecow", "Unkn", "Ecow", "Ambt", "Tmpt" };
+ public string[] APRSstationtype = { "DsVP", "DsVP", "WMR928", "WM918", "EW", "FO", "WS2300", "FOs", "WMR100", "WMR200", "IMET", "DsVP", "Ecow", "Unkn", "Ecow", "Ambt", "Tmpt", "Simul" };
public string loggingfile;
@@ -1478,9 +1479,9 @@ public Cumulus(int HTTPport, bool DebugEnabled, string startParms)
}
Console.WriteLine();
- LogDebugMessage("Lock: Cumulus waiting for the lock");
+ //LogDebugMessage("Lock: Cumulus waiting for the lock");
syncInit.Wait();
- LogDebugMessage("Lock: Cumulus has lock");
+ //LogDebugMessage("Lock: Cumulus has lock");
LogMessage("Opening station");
@@ -1549,6 +1550,10 @@ public Cumulus(int HTTPport, bool DebugEnabled, string startParms)
Manufacturer = AMBIENT;
station = new HttpStationAmbient(this);
break;
+ case StationTypes.Simulator:
+ Manufacturer = SIMULATOR;
+ station = new Simulator(this);
+ break;
default:
LogConsoleMessage("Station type not set", ConsoleColor.Red);
LogMessage("Station type not set");
@@ -1652,7 +1657,7 @@ public Cumulus(int HTTPport, bool DebugEnabled, string startParms)
StartTimersAndSensors();
}
- if ((StationType == StationTypes.WMR100) || (StationType == StationTypes.EasyWeather) || (Manufacturer == OREGON))
+ if ((StationType == StationTypes.WMR100) || (StationType == StationTypes.EasyWeather) || (Manufacturer == OREGON) || StationType == StationTypes.Simulator)
{
station.StartLoop();
}
@@ -1662,7 +1667,7 @@ public Cumulus(int HTTPport, bool DebugEnabled, string startParms)
station.CreateEodGraphDataFiles();
}
- LogDebugMessage("Lock: Cumulus releasing the lock");
+ //LogDebugMessage("Lock: Cumulus releasing the lock");
syncInit.Release();
}
@@ -6638,6 +6643,7 @@ private void ReadStringsFile()
public int HTTPSTATION = 7;
public int AMBIENT = 8;
public int WEATHERFLOW = 9;
+ public int SIMULATOR = 10;
//public bool startingup = true;
public string ReportPath;
@@ -10577,6 +10583,7 @@ public static class StationTypes
public const int HttpEcowitt = 14;
public const int HttpAmbient = 15;
public const int Tempest = 16;
+ public const int Simulator = 17;
}
/*
diff --git a/CumulusMX/CumulusMX.csproj b/CumulusMX/CumulusMX.csproj
index e3d89f50..485d89da 100644
--- a/CumulusMX/CumulusMX.csproj
+++ b/CumulusMX/CumulusMX.csproj
@@ -237,6 +237,7 @@
Component
+
diff --git a/CumulusMX/DavisAirLink.cs b/CumulusMX/DavisAirLink.cs
index e0f5da34..0e5f0c58 100644
--- a/CumulusMX/DavisAirLink.cs
+++ b/CumulusMX/DavisAirLink.cs
@@ -312,9 +312,9 @@ private async void GetAlCurrent(object source, ElapsedEventArgs e)
var urlCurrent = $"http://{ip}/v1/current_conditions";
- cumulus.LogDebugMessage($"GetAlCurrent: {locationStr} - Waiting for lock");
+ //cumulus.LogDebugMessage($"GetAlCurrent: {locationStr} - Waiting for lock");
WebReq.Wait();
- cumulus.LogDebugMessage($"GetAlCurrent: {locationStr} - Has the lock");
+ //cumulus.LogDebugMessage($"GetAlCurrent: {locationStr} - Has the lock");
// The AL will error if already responding to a request from another device, so add a retry
do
@@ -356,7 +356,7 @@ private async void GetAlCurrent(object source, ElapsedEventArgs e)
}
} while (retry < 3);
- cumulus.LogDebugMessage($"GetAlCurrent: {locationStr} - Releasing lock");
+ //cumulus.LogDebugMessage($"GetAlCurrent: {locationStr} - Releasing lock");
WebReq.Release();
}
else
diff --git a/CumulusMX/DavisStation.cs b/CumulusMX/DavisStation.cs
index 93a28f23..99869d83 100644
--- a/CumulusMX/DavisStation.cs
+++ b/CumulusMX/DavisStation.cs
@@ -772,24 +772,24 @@ private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
private void bw_DoStart(object sender, DoWorkEventArgs e)
{
- cumulus.LogDebugMessage("Lock: Station waiting for lock");
+ //cumulus.LogDebugMessage("Lock: Station waiting for lock");
Cumulus.syncInit.Wait();
- cumulus.LogDebugMessage("Lock: Station has the lock");
+ //cumulus.LogDebugMessage("Lock: Station has the lock");
// Wait a short while for Cumulus initialisation to complete
Thread.Sleep(500);
StartLoop();
- cumulus.LogDebugMessage("Lock: Station releasing lock");
+ //cumulus.LogDebugMessage("Lock: Station releasing lock");
Cumulus.syncInit.Release();
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
int archiveRun = 0;
- cumulus.LogDebugMessage("Lock: Station waiting for the lock");
+ //cumulus.LogDebugMessage("Lock: Station waiting for the lock");
Cumulus.syncInit.Wait();
- cumulus.LogDebugMessage("Lock: Station has the lock");
+ //cumulus.LogDebugMessage("Lock: Station has the lock");
try
{
// set this temporarily, so speed is done from average and not peak gust from logger
@@ -805,7 +805,7 @@ private void bw_DoWork(object sender, DoWorkEventArgs e)
{
cumulus.LogMessage("Exception occurred reading archive data: "+ex.Message);
}
- cumulus.LogDebugMessage("Lock: Station releasing the lock");
+ //cumulus.LogDebugMessage("Lock: Station releasing the lock");
Cumulus.syncInit.Release();
}
diff --git a/CumulusMX/DavisWllStation.cs b/CumulusMX/DavisWllStation.cs
index f8818283..93cf8545 100644
--- a/CumulusMX/DavisWllStation.cs
+++ b/CumulusMX/DavisWllStation.cs
@@ -243,9 +243,9 @@ public override void Start()
try
{
// Wait for the lock
- cumulus.LogDebugMessage("Lock: Station waiting for lock");
+ //cumulus.LogDebugMessage("Lock: Station waiting for lock");
Cumulus.syncInit.Wait();
- cumulus.LogDebugMessage("Lock: Station has the lock");
+ //cumulus.LogDebugMessage("Lock: Station has the lock");
// Create a realtime thread to periodically restart broadcasts
GetWllRealtime(null, null);
@@ -352,7 +352,7 @@ public override void Start()
}
finally
{
- cumulus.LogDebugMessage("Lock: Station releasing lock");
+ //cumulus.LogDebugMessage("Lock: Station releasing lock");
Cumulus.syncInit.Release();
}
}
@@ -402,9 +402,9 @@ private async void GetWllRealtime(object source, ElapsedEventArgs e)
{
var retry = 2;
- cumulus.LogDebugMessage("GetWllRealtime: GetWllRealtime waiting for lock");
+ //cumulus.LogDebugMessage("GetWllRealtime: GetWllRealtime waiting for lock");
WebReq.Wait();
- cumulus.LogDebugMessage("GetWllRealtime: GetWllRealtime has the lock");
+ //cumulus.LogDebugMessage("GetWllRealtime: GetWllRealtime has the lock");
// The WLL will error if already responding to a request from another device, so add a retry
do
@@ -465,7 +465,7 @@ private async void GetWllRealtime(object source, ElapsedEventArgs e)
}
} while (retry > 0);
- cumulus.LogDebugMessage("GetWllRealtime: Releasing lock");
+ //cumulus.LogDebugMessage("GetWllRealtime: Releasing lock");
WebReq.Release();
}
@@ -486,9 +486,9 @@ private async void GetWllCurrent(object source, ElapsedEventArgs e)
// wait a random time of 0 to 5 seconds before making the request to try and avoid continued clashes with other software or instances of MX
await Task.Delay(random.Next(0, 5000));
- cumulus.LogDebugMessage("GetWllCurrent: Waiting for lock");
+ //cumulus.LogDebugMessage("GetWllCurrent: Waiting for lock");
WebReq.Wait();
- cumulus.LogDebugMessage("GetWllCurrent: Has the lock");
+ //cumulus.LogDebugMessage("GetWllCurrent: Has the lock");
// The WLL will error if already responding to a request from another device, so add a retry
do
@@ -524,7 +524,7 @@ private async void GetWllCurrent(object source, ElapsedEventArgs e)
}
} while (retry < 3);
- cumulus.LogDebugMessage("GetWllCurrent: Releasing lock");
+ //cumulus.LogDebugMessage("GetWllCurrent: Releasing lock");
WebReq.Release();
}
else
@@ -814,12 +814,12 @@ private void DecodeCurrent(string currentJson)
// pesky null values from WLL when it is calm
int wdir = data1.wind_dir_last.HasValue ? data1.wind_dir_last.Value : 0;
- double wind = data1.wind_speed_last.HasValue ? data1.wind_speed_last.Value : 0;
+ double wind = ConvertWindMPHToUser(data1.wind_speed_last ?? 0);
double wspdAvg10min = ConvertWindMPHToUser(data1.wind_speed_avg_last_10_min ?? 0);
- DoWind(ConvertWindMPHToUser(wind), wdir, wspdAvg10min, dateTime);
+ DoWind(wind, wdir, wspdAvg10min, dateTime);
- WindAverage = wspdAvg10min * cumulus.Calib.WindSpeed.Mult;
+ //WindAverage = wspdAvg10min * cumulus.Calib.WindSpeed.Mult;
// Wind data can be a bit out of date compared to the broadcasts (1 minute update), so only use gust broadcast data
/*
@@ -1245,11 +1245,6 @@ private void DecodeCurrent(string currentJson)
cumulus.LogDebugMessage($"WLL current: found an unknown transmitter type [{type}]!");
break;
}
-
- DoForecast(string.Empty, false);
-
- UpdateStatusPanel(DateTime.Now);
- UpdateMQTT();
}
// Now we have the primary data, calculate the derived data
@@ -1271,6 +1266,11 @@ private void DecodeCurrent(string currentJson)
DoFeelsLike(dateTime);
DoHumidex(dateTime);
+ DoForecast(string.Empty, false);
+
+ UpdateStatusPanel(DateTime.Now);
+ UpdateMQTT();
+
SensorContactLost = localSensorContactLost;
// If the station isn't using the logger function for WLL - i.e. no API key, then only alarm on Tx battery status
@@ -1442,15 +1442,15 @@ private void bw_ReadHistoryCompleted(object sender, RunWorkerCompletedEventArgs
/*
private void bw_DoStart(object sender, DoWorkEventArgs e)
{
- cumulus.LogDebugMessage("Lock: Station waiting for lock");
+ //cumulus.LogDebugMessage("Lock: Station waiting for lock");
Cumulus.syncInit.Wait();
- cumulus.LogDebugMessage("Lock: Station has the lock");
+ //cumulus.LogDebugMessage("Lock: Station has the lock");
// Wait a short while for Cumulus initialisation to complete
Thread.Sleep(500);
StartLoop();
- cumulus.LogDebugMessage("Lock: Station releasing lock");
+ //cumulus.LogDebugMessage("Lock: Station releasing lock");
Cumulus.syncInit.Release();
}
*/
@@ -1460,9 +1460,9 @@ private void bw_ReadHistory(object sender, DoWorkEventArgs e)
BackgroundWorker worker = sender as BackgroundWorker;
int archiveRun = 0;
- cumulus.LogDebugMessage("Lock: Station waiting for the lock");
+ //cumulus.LogDebugMessage("Lock: Station waiting for the lock");
Cumulus.syncInit.Wait();
- cumulus.LogDebugMessage("Lock: Station has the lock");
+ //cumulus.LogDebugMessage("Lock: Station has the lock");
try
{
@@ -1495,7 +1495,8 @@ private void bw_ReadHistory(object sender, DoWorkEventArgs e)
{
cumulus.LogMessage("Exception occurred reading archive data: " + ex.Message);
}
- cumulus.LogDebugMessage("Lock: Station releasing the lock");
+
+ //cumulus.LogDebugMessage("Lock: Station releasing the lock");
Cumulus.syncInit.Release();
bwDoneEvent.Set();
}
diff --git a/CumulusMX/EcowittApi.cs b/CumulusMX/EcowittApi.cs
index c6511518..9ad62cfe 100644
--- a/CumulusMX/EcowittApi.cs
+++ b/CumulusMX/EcowittApi.cs
@@ -415,6 +415,7 @@ private void ProcessHistoryData(EcowittHistoricData data)
}
}
}
+
// Dewpoint
if (data.outdoor.dew_point != null && data.outdoor.dew_point.list != null)
{
@@ -493,6 +494,7 @@ private void ProcessHistoryData(EcowittHistoricData data)
}
}
}
+
// Direction
if (data.wind.wind_direction != null && data.wind.wind_direction.list != null)
{
@@ -688,6 +690,7 @@ private void ProcessHistoryData(EcowittHistoricData data)
}
}
}
+
// uvi
if (data.solar_and_uvi.uvi != null && data.solar_and_uvi.uvi.list != null)
{
@@ -802,6 +805,7 @@ private void ProcessHistoryData(EcowittHistoricData data)
}
}
}
+
// humidity
if (srcTH.humidity != null && srcTH.humidity.list != null)
{
@@ -949,6 +953,7 @@ private void ProcessHistoryData(EcowittHistoricData data)
}
}
}
+
// 24 Avg
if (data.indoor_co2.average24h != null && data.indoor_co2.average24h.list != null)
{
@@ -1004,6 +1009,7 @@ private void ProcessHistoryData(EcowittHistoricData data)
}
}
}
+
// 24 Avg
if (data.co2_aqi_combo.average24h != null && data.co2_aqi_combo.average24h.list != null)
{
@@ -1186,11 +1192,13 @@ private void ProcessHistoryData(EcowittHistoricData data)
ApplyHistoricData(rec);
// add in archive period worth of sunshine, if sunny
- if (station.SolarRad > station.CurrentSolarMax * cumulus.SolarOptions.SunThreshold / 100 &&
+ if (station.CurrentSolarMax > 0 &&
+ station.SolarRad > station.CurrentSolarMax * cumulus.SolarOptions.SunThreshold / 100 &&
station.SolarRad >= cumulus.SolarOptions.SolarMinimum &&
!cumulus.SolarOptions.UseBlakeLarsen)
{
station.SunshineHours += 5 / 60.0;
+ cumulus.LogDebugMessage("Adding 5 minutes to Sunshine Hours");
}
diff --git a/CumulusMX/EmailSender.cs b/CumulusMX/EmailSender.cs
index 3b1b0c61..eb2f4a72 100644
--- a/CumulusMX/EmailSender.cs
+++ b/CumulusMX/EmailSender.cs
@@ -27,9 +27,9 @@ public async void SendEmail(string[] to, string from, string subject, string mes
{
try
{
- cumulus.LogDebugMessage($"SendEmail: Waiting for lock...");
+ //cumulus.LogDebugMessage($"SendEmail: Waiting for lock...");
await _writeLock.WaitAsync();
- cumulus.LogDebugMessage($"SendEmail: Has the lock");
+ //cumulus.LogDebugMessage($"SendEmail: Has the lock");
var logMessage = ToLiteral(message);
@@ -80,7 +80,7 @@ public async void SendEmail(string[] to, string from, string subject, string mes
}
finally
{
- cumulus.LogDebugMessage($"SendEmail: Releasing lock...");
+ //cumulus.LogDebugMessage($"SendEmail: Releasing lock...");
_writeLock.Release();
}
}
@@ -91,9 +91,9 @@ public string SendTestEmail(string[] to, string from, string subject, string mes
try
{
- cumulus.LogDebugMessage($"SendEmail: Waiting for lock...");
+ //cumulus.LogDebugMessage($"SendEmail: Waiting for lock...");
_writeLock.Wait();
- cumulus.LogDebugMessage($"SendEmail: Has the lock");
+ //cumulus.LogDebugMessage($"SendEmail: Has the lock");
cumulus.LogDebugMessage($"SendEmail: Sending Test email, to [{string.Join("; ", to)}], subject [{subject}], body [{message}]...");
@@ -145,7 +145,7 @@ public string SendTestEmail(string[] to, string from, string subject, string mes
}
finally
{
- cumulus.LogDebugMessage($"SendEmail: Releasing lock...");
+ //cumulus.LogDebugMessage($"SendEmail: Releasing lock...");
_writeLock.Release();
}
diff --git a/CumulusMX/FOStation.cs b/CumulusMX/FOStation.cs
index 888d6dec..66c6799a 100644
--- a/CumulusMX/FOStation.cs
+++ b/CumulusMX/FOStation.cs
@@ -161,9 +161,9 @@ private void bw_DoWork(object sender, DoWorkEventArgs e)
{
//var ci = new CultureInfo("en-GB");
//System.Threading.Thread.CurrentThread.CurrentCulture = ci;
- cumulus.LogDebugMessage("Lock: Station waiting for the lock");
+ //cumulus.LogDebugMessage("Lock: Station waiting for the lock");
Cumulus.syncInit.Wait();
- cumulus.LogDebugMessage("Lock: Station has the lock");
+ //cumulus.LogDebugMessage("Lock: Station has the lock");
try
{
getAndProcessHistoryData();
@@ -172,7 +172,7 @@ private void bw_DoWork(object sender, DoWorkEventArgs e)
{
cumulus.LogMessage("Exception occurred reading archive data: " + ex.Message);
}
- cumulus.LogDebugMessage("Lock: Station releasing the lock");
+ //cumulus.LogDebugMessage("Lock: Station releasing the lock");
Cumulus.syncInit.Release();
}
diff --git a/CumulusMX/GW1000Station.cs b/CumulusMX/GW1000Station.cs
index 626c49b0..72a93897 100644
--- a/CumulusMX/GW1000Station.cs
+++ b/CumulusMX/GW1000Station.cs
@@ -54,6 +54,10 @@ public GW1000Station(Cumulus cumulus) : base(cumulus)
// GW1000 does not provide 10 min average wind speeds
cumulus.StationOptions.UseWind10MinAvg = true;
+ // GW1000 does not provide an interval gust value, it gives us a 30 second high
+ // so force using the wind speed for the average calculation
+ cumulus.StationOptions.UseSpeedForAvgCalc = true;
+
LightningTime = DateTime.MinValue;
LightningDistance = 999;
@@ -249,9 +253,9 @@ public override void Stop()
public override void getAndProcessHistoryData()
{
- cumulus.LogDebugMessage("Lock: Station waiting for the lock");
+ //cumulus.LogDebugMessage("Lock: Station waiting for the lock");
Cumulus.syncInit.Wait();
- cumulus.LogDebugMessage("Lock: Station has the lock");
+ //cumulus.LogDebugMessage("Lock: Station has the lock");
if (string.IsNullOrEmpty(cumulus.EcowittApplicationKey) || string.IsNullOrEmpty(cumulus.EcowittUserApiKey) || string.IsNullOrEmpty(cumulus.EcowittMacAddress))
{
@@ -279,7 +283,7 @@ public override void getAndProcessHistoryData()
}
}
- cumulus.LogDebugMessage("Lock: Station releasing the lock");
+ //cumulus.LogDebugMessage("Lock: Station releasing the lock");
_ = Cumulus.syncInit.Release();
if (cancellationToken.IsCancellationRequested)
@@ -1173,8 +1177,9 @@ private void GetLiveData()
if (gustLast > -999 && windSpeedLast > -999 && windDirLast > -999)
{
- //DoWind(gustLast, windDirLast, windSpeedLast, dateTime);
+ DoWind(gustLast, windDirLast, windSpeedLast, dateTime);
+ /*
// The protocol does not provide an average value
// so feed in current MX average
DoWind(windSpeedLast, windDirLast, WindAverage / cumulus.Calib.WindSpeed.Mult, dateTime);
@@ -1192,6 +1197,7 @@ private void GetLiveData()
RecentMaxGust = gustLastCal;
}
+ */
}
if (rainLast > -999 && rainRateLast > -999)
diff --git a/CumulusMX/HttpStationEcowitt.cs b/CumulusMX/HttpStationEcowitt.cs
index 29dbf3e8..655fdb99 100644
--- a/CumulusMX/HttpStationEcowitt.cs
+++ b/CumulusMX/HttpStationEcowitt.cs
@@ -46,6 +46,11 @@ public HttpStationEcowitt(Cumulus cumulus, WeatherStation station = null) : base
// does not provide 10 min average wind speeds
cumulus.StationOptions.UseWind10MinAvg = true;
+ // GW1000 does not provide an interval gust value, it gives us a 2 minute high
+ // So CMX porces the latest speed into the gust
+ // Therefore we need to force using the gust (but we actually input the speed) for the average calculation
+ cumulus.StationOptions.UseSpeedForAvgCalc = false;
+
// does not send DP, so force MX to calculate it
cumulus.StationOptions.CalculatedDP = true;
// Same for Wind Chill
@@ -149,9 +154,9 @@ public override void Stop()
public override void getAndProcessHistoryData()
{
- cumulus.LogDebugMessage("Lock: Station waiting for the lock");
+ //cumulus.LogDebugMessage("Lock: Station waiting for the lock");
Cumulus.syncInit.Wait();
- cumulus.LogDebugMessage("Lock: Station has the lock");
+ //cumulus.LogDebugMessage("Lock: Station has the lock");
if (string.IsNullOrEmpty(cumulus.EcowittApplicationKey) || string.IsNullOrEmpty(cumulus.EcowittUserApiKey) || string.IsNullOrEmpty(cumulus.EcowittMacAddress))
{
@@ -179,7 +184,7 @@ public override void getAndProcessHistoryData()
}
}
- cumulus.LogDebugMessage("Lock: Station releasing the lock");
+ //cumulus.LogDebugMessage("Lock: Station releasing the lock");
_ = Cumulus.syncInit.Release();
StartLoop();
@@ -323,6 +328,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null)
DoWind(spdVal, dirVal, WindAverage / cumulus.Calib.WindSpeed.Mult, recDate);
var gustLastCal = gustVal * cumulus.Calib.WindGust.Mult;
+
if (gustLastCal > RecentMaxGust)
{
cumulus.LogDebugMessage("Setting max gust from current value: " + gustLastCal.ToString(cumulus.WindFormat));
@@ -1196,6 +1202,10 @@ private void SetCustomServer(GW1000Api api, bool main)
idx = 5 + data2[4];
var wuPath = Encoding.ASCII.GetString(data2, idx + 1, data2[idx]);
+ // Ecowitt actually sends data at interval + 1 seconds! :(
+ customIntv -= 1;
+ if (customIntv < 1)
+ customIntv = 1;
cumulus.LogMessage($"Ecowitt Gateway Custom Server config: Server={server}, Port={port}, Path={ecPath}, Interval={intv}, Protocol={type}, Enabled={active}");
@@ -1256,6 +1266,7 @@ private void SetCustomServer(GW1000Api api, bool main)
else
{
cumulus.LogMessage($"Set Ecowitt Gateway Custom Server config to: Server={customServer}, Port={customPort}, Interval={customIntv}, Protocol={0}, Enabled={1}");
+ cumulus.LogMessage("Ecowitt Gateway Custom Server. Note, the set interval should be 1 less than the value set in the CMX configuration");
}
}
diff --git a/CumulusMX/Properties/AssemblyInfo.cs b/CumulusMX/Properties/AssemblyInfo.cs
index 09994fda..1aed9117 100644
--- a/CumulusMX/Properties/AssemblyInfo.cs
+++ b/CumulusMX/Properties/AssemblyInfo.cs
@@ -6,7 +6,7 @@
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Cumulus MX")]
-[assembly: AssemblyDescription("Version 3.16.1 - Build 3183")]
+[assembly: AssemblyDescription("Version 3.17.0 - Build 3184")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Cumulus MX")]
@@ -32,5 +32,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("3.16.1.3183")]
-[assembly: AssemblyFileVersion("3.16.1.3183")]
+[assembly: AssemblyVersion("3.17.0.3184")]
+[assembly: AssemblyFileVersion("3.17.0.3184")]
diff --git a/CumulusMX/Simulator.cs b/CumulusMX/Simulator.cs
new file mode 100644
index 00000000..16d4daa9
--- /dev/null
+++ b/CumulusMX/Simulator.cs
@@ -0,0 +1,285 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace CumulusMX
+{
+ internal class Simulator : WeatherStation
+ {
+ private bool stop;
+ private CancellationTokenSource tokenSource = new CancellationTokenSource();
+ private CancellationToken cancellationToken;
+
+ private DataSet currData;
+
+ private int dataUpdateRate = 5000; // 5 second data update rate
+
+ private new readonly Random random;
+ private bool solarIntialised;
+
+ public Simulator(Cumulus cumulus) : base(cumulus)
+ {
+
+ cumulus.LogMessage("Station type = Simulator");
+
+ cumulus.LogMessage("Last update time = " + cumulus.LastUpdateTime);
+
+ cancellationToken = tokenSource.Token;
+
+ random = new Random();
+
+ currData = new DataSet();
+
+ cumulus.StationOptions.CalculatedDP = true;
+ cumulus.StationOptions.CalculatedET = true;
+ cumulus.StationOptions.CalculatedWC = true;
+ cumulus.StationOptions.UseWind10MinAvg = true;
+ cumulus.StationOptions.UseCumulusPresstrendstr = true;
+ cumulus.StationOptions.UseSpeedForAvgCalc = false;
+
+ WindAverage = 0;
+
+ timerStartNeeded = true;
+ LoadLastHoursFromDataLogs(cumulus.LastUpdateTime);
+ DoDayResetIfNeeded();
+ DoTrendValues(DateTime.Now);
+
+ }
+
+
+ public override void Start()
+ {
+ while (!stop)
+ {
+ try
+ {
+ var now = DateTime.Now;
+
+ currData.SetNewData(now);
+
+ applyData(now);
+
+ DoForecast(string.Empty, false);
+
+ UpdateStatusPanel(now);
+ UpdateMQTT();
+
+ if (cancellationToken.WaitHandle.WaitOne(TimeSpan.FromMilliseconds(dataUpdateRate)))
+ {
+ break;
+ }
+ }
+ catch (ThreadAbortException) // Catch the ThreadAbortException
+ {
+ cumulus.LogMessage("Simulator Start: ThreadAbortException");
+ // and exit
+ stop = true;
+ }
+ catch (Exception ex)
+ {
+ // any others, log them and carry on
+ cumulus.LogMessage("Simulator Start: Exception = " + ex.Message);
+ }
+ }
+
+ cumulus.LogMessage("Ending normal reading loop");
+ }
+
+ public override void Stop()
+ {
+ StopMinuteTimer();
+
+ cumulus.LogMessage("Stopping data generation task");
+ try
+ {
+ if (tokenSource != null)
+ tokenSource.Cancel();
+ cumulus.LogMessage("Waiting for data generation to complete");
+ }
+ catch (Exception ex)
+ {
+ cumulus.LogMessage("Error stopping the simulator" + ex.Message);
+ }
+ }
+
+
+ private void applyData(DateTime recDate)
+ {
+ cumulus.LogDataMessage($"Simulated data: temp={ConvertTempCToUser(currData.tempVal):f1}, hum={currData.humVal}, gust={ConvertWindMPHToUser(currData.windSpeedVal):f2}, dir={currData.windBearingVal}, press={ConvertPressMBToUser(currData.pressureVal):f2}, r.rate={ConvertRainMMToUser(currData.rainRateVal):f2}");
+
+ DoWind(ConvertWindMPHToUser(currData.windSpeedVal), currData.windBearingVal, WindAverage / cumulus.Calib.WindSpeed.Mult, recDate);
+
+ var rain = Raincounter + ConvertRainMMToUser(currData.rainRateVal * dataUpdateRate / 1000 / 3600);
+
+ DoRain(rain, ConvertRainMMToUser(currData.rainRateVal), recDate);
+
+ DoIndoorTemp(ConvertTempCToUser(currData.tempInVal));
+ DoIndoorHumidity(currData.humInVal);
+
+ DoOutdoorHumidity(currData.humVal, recDate);
+ DoOutdoorTemp(ConvertTempCToUser(currData.tempVal), recDate);
+
+ DoPressure(ConvertPressMBToUser(currData.pressureVal), recDate);
+ UpdatePressureTrendString();
+
+ doSolar(recDate);
+
+ DoOutdoorDewpoint(0, recDate);
+ DoWindChill(0, recDate);
+ DoHumidex(recDate);
+ DoApparentTemp(recDate);
+ DoFeelsLike(recDate);
+ }
+
+ private void doSolar(DateTime recDate)
+ {
+ // For the solar random walk we are chasing the theoretical solat max value
+ double solar = SolarRad;
+
+ // if we are starting up, set the intial solar rad value to 90% of theoretical
+ if (!solarIntialised)
+ {
+ CurrentSolarMax = AstroLib.SolarMax(recDate, cumulus.Longitude, cumulus.Latitude, AltitudeM(cumulus.Altitude), out SolarElevation, cumulus.SolarOptions);
+ solar = CurrentSolarMax * 0.9;
+ solarIntialised = true;
+ }
+
+ // aim for 85% of theoretical in the morning, 75% after local noon
+ double factor;
+ if (recDate.IsDaylightSavingTime())
+ {
+ factor = recDate.Hour < 13 ? 0.85 : 0.75;
+ }
+ else
+ {
+ factor = recDate.Hour < 12 ? 0.85 : 0.75;
+ }
+
+ // If it's raining, make it dull!
+ if (RainRate > 0)
+ {
+ factor = 0.3;
+ }
+
+ var volatility = CurrentSolarMax * 0.05;
+ if (volatility < 2)
+ volatility = 2;
+ else if (volatility > 30)
+ volatility = 30;
+
+ solar -= (solar - CurrentSolarMax * factor) * 0.02;
+ solar += volatility * (2 * random.NextDouble() - 1);
+ if (solar < 0 || CurrentSolarMax == 0)
+ solar = 0;
+ DoSolarRad((int)solar, recDate);
+ }
+
+
+ private class DataSet
+ {
+ private MeanRevertingRandomWalk temperature;
+ private MeanRevertingRandomWalk humidity;
+ private MeanRevertingRandomWalk windSpeed;
+ private MeanRevertingRandomWalk windDirection;
+ private MeanRevertingRandomWalk insideTemp;
+ private MeanRevertingRandomWalk insideHum;
+ private MeanRevertingRandomWalk pressure;
+ private MeanRevertingRandomWalk rainRate;
+
+ public double tempVal { get; set; }
+ public int humVal { get; set; }
+ public double windSpeedVal { get; set; }
+ public int windBearingVal { get; set; }
+ public double rainRateVal { get; set; }
+ public double pressureVal { get; set; }
+ public double tempInVal { get; set; }
+ public int humInVal { get; set; }
+
+
+ public DataSet()
+ {
+ // Temperature - both annual and daily variations, daily offset by 0.1 of a day
+ var tempMean = new Func((x) => 15 + 10 * Math.Cos(x.DayOfYear / 365.0 * 2 * Math.PI) - 10 * Math.Cos((x.TimeOfDay.TotalDays - 0.1) * 2 * Math.PI));
+ // Wind - daily variation, offset by 0.1 of a day
+ var windMean = new Func((x) => 10 - 9.5 * Math.Cos((x.TimeOfDay.TotalDays - 0.1) * 2 * Math.PI));
+ var windVolatility = new Func((x) => 2 - 1.5 * Math.Cos((x.TimeOfDay.TotalDays - 0.1) * 2 * Math.PI));
+ // Humidity - daily variation, offset by 0.1 of a day
+ var humMean = new Func((x) => 60 + 30 * Math.Cos((x.TimeOfDay.TotalDays - 0.1) * 2 * Math.PI));
+ // Pressure - vary the range over a two day period
+ var pressMean = new Func((x) => 1010 + 25 * Math.Cos((x.DayOfYear + x.TimeOfDay.TotalDays + 0.2) % 4 / 4.0 * 2 * Math.PI) - 6 * Math.Cos((x.TimeOfDay.TotalDays + 0.65) * 2 * Math.PI));
+ // Inside Temp - assume heating between 07:00 and 23:00
+ var inTempMean = new Func((x) => (x.Hour < 7 || x.Hour > 22) ? 16 : 21);
+ // RainRate - lets try two blocks of rain per day, determined by day of the year, mean rate to be 3 mm/hr
+ var rainRateMean = new Func((x) => x.Hour == x.DayOfYear % 24 || x.Hour == (x.DayOfYear + 1) % 24 || x.Hour == (x.DayOfYear + 12) % 24 || x.Hour == (x.DayOfYear + 13) % 24 ? 3 : -100);
+
+ temperature = new MeanRevertingRandomWalk(tempMean, (x) => 0.1, 0.01, -10, 30);
+ humidity = new MeanRevertingRandomWalk(humMean, (x) => 1, 0.01, 10, 100);
+ windSpeed = new MeanRevertingRandomWalk(windMean, windVolatility, 0.02, 0, 50);
+ windDirection = new MeanRevertingRandomWalk((x) => 191, (x) => 10, 0.005, 0, 720);
+ pressure = new MeanRevertingRandomWalk(pressMean, (x) => .1, 0.05, 950, 1050);
+ rainRate = new MeanRevertingRandomWalk(rainRateMean, (x) => 5, 0.05, 0, 30);
+ insideTemp = new MeanRevertingRandomWalk(inTempMean, (x) => 0.1, 0.01, 15, 25);
+ insideHum = new MeanRevertingRandomWalk((x) => 50, (x) => 0.5, 0.005, 35, 75);
+ }
+
+ public void SetNewData(DateTime readTime)
+ {
+ tempVal = temperature.GetValue(readTime);
+ humVal = (int)humidity.GetValue(readTime);
+ windSpeedVal = Math.Round(windSpeed.GetValue(readTime), 1);
+ if (windSpeedVal > 0)
+ {
+ windBearingVal = ((int)windDirection.GetValue(readTime) % 360) + 1;
+ }
+ rainRateVal = rainRate.GetValue(readTime);
+ pressureVal = pressure.GetValue(readTime);
+ tempInVal = insideTemp.GetValue(readTime);
+ humInVal = (int)insideHum.GetValue(readTime);
+ }
+ }
+
+ private class MeanRevertingRandomWalk
+ {
+ private readonly Func _meanCurve;
+ private readonly Func _volatility;
+ private readonly double _meanReversion;
+ private readonly double _cropMin;
+ private readonly double _cropMax;
+
+ private double _value;
+ private bool _initialised = false;
+ private readonly Random _random;
+
+ public MeanRevertingRandomWalk(Func meanCurve, Func volatility, double meanReversion, double cropMin, double cropMax)
+ {
+ _meanCurve = meanCurve;
+ _volatility = volatility;
+ _meanReversion = meanReversion;
+ _cropMin = cropMin;
+ _cropMax = cropMax;
+ _random = new Random();
+ }
+
+ public double GetValue(DateTime date)
+ {
+ if (!_initialised)
+ {
+ _value = _meanCurve(date);
+ _initialised = true;
+ }
+
+
+ _value -= (_value - _meanCurve(date)) * _meanReversion;
+ _value += _volatility(date) * (2 * _random.NextDouble() - 1);
+ if (_value < _cropMin) return _cropMin;
+ if (_value > _cropMax) return _cropMax;
+ return _value;
+ }
+ }
+
+ }
+}
diff --git a/CumulusMX/TempestStation.cs b/CumulusMX/TempestStation.cs
index 962ac582..6c0928b2 100644
--- a/CumulusMX/TempestStation.cs
+++ b/CumulusMX/TempestStation.cs
@@ -36,9 +36,9 @@ public TempestStation(Cumulus cumulus) : base(cumulus)
public override void getAndProcessHistoryData()
{
- cumulus.LogDebugMessage("Lock: Station waiting for the lock");
+ //cumulus.LogDebugMessage("Lock: Station waiting for the lock");
Cumulus.syncInit.Wait();
- cumulus.LogDebugMessage("Lock: Station has the lock");
+ //cumulus.LogDebugMessage("Lock: Station has the lock");
try
{
var stTime = cumulus.LastUpdateTime;
@@ -61,7 +61,7 @@ public override void getAndProcessHistoryData()
te = te.InnerException;
}
}
- cumulus.LogDebugMessage("Lock: Station releasing the lock");
+ //cumulus.LogDebugMessage("Lock: Station releasing the lock");
Cumulus.syncInit.Release();
StartLoop();
}
diff --git a/CumulusMX/WeatherStation.cs b/CumulusMX/WeatherStation.cs
index 0a44c162..b397cc0d 100644
--- a/CumulusMX/WeatherStation.cs
+++ b/CumulusMX/WeatherStation.cs
@@ -18,6 +18,7 @@
using Timer = System.Timers.Timer;
using ServiceStack.Text;
using System.Web;
+using System.Threading.Tasks;
namespace CumulusMX
{
@@ -42,6 +43,7 @@ public struct TWindVec
public readonly Object yearIniThreadLock = new Object();
public readonly Object alltimeIniThreadLock = new Object();
public readonly Object monthlyalltimeIniThreadLock = new Object();
+ private readonly Object webSocketThreadLock = new Object();
// holds all time highs and lows
public AllTimeRecords AllTime = new AllTimeRecords();
@@ -1426,11 +1428,26 @@ public void SecondTimer(object sender, ElapsedEventArgs e)
}
}
- if ((int)timeNow.TimeOfDay.TotalMilliseconds % 2500 <= 500)
+ // send current data to web-socket every 5 seconds, unless it has already been sent within the 10 seconds
+ if (LastDataReadTimestamp.AddSeconds(5) < timeNow && (int)timeNow.TimeOfDay.TotalMilliseconds % 10000 <= 500)
{
- // send current data to web-socket every 3 seconds
- try
+ _ = sendWebSocketData();
+ }
+ }
+
+ private async Task sendWebSocketData()
+ {
+ // Return control to the calling method immediately.
+ await Task.Yield();
+
+ // send current data to web-socket
+ try
+ {
+ // wait for the ws lock object
+ lock (webSocketThreadLock);
{
+ //cumulus.LogDebugMessage("WebSocket: Sending message");
+
StringBuilder windRoseData = new StringBuilder(80);
lock (windcounts)
@@ -1478,12 +1495,21 @@ public void SecondTimer(object sender, ElapsedEventArgs e)
stream.Position = 0;
+ cumulus.LogDebugMessage("WebSocket: Send message");
WebSocket.SendMessage(new StreamReader(stream).ReadToEnd());
+
+ // We can't be sure when the broadcast completes because it is async internally, so the best we can do is wait a short time
+ await Task.Delay(500);
}
- catch (Exception ex)
- {
- cumulus.LogMessage(ex.Message);
- }
+ }
+ catch (Exception ex)
+ {
+ cumulus.LogMessage("sendWebSocketData: Error - " + ex.Message);
+ }
+ finally
+ {
+ //cumulus.LogDebugMessage("WebSocket: End message");
+ //webSocketLocked = false;
}
}
@@ -7109,6 +7135,7 @@ public logfilerec ParseLogFileRec(string data, bool minMax)
internal void UpdateStatusPanel(DateTime timestamp)
{
LastDataReadTimestamp = timestamp;
+ sendWebSocketData();
}
diff --git a/Updates.txt b/Updates.txt
index efd783e4..e229099a 100644
--- a/Updates.txt
+++ b/Updates.txt
@@ -1,3 +1,12 @@
+3.17.0 - b3184
+——————————————
+
+- New: Adds a PWS Simulator station type for testing or trial purposes
+
+- Change: The "live" dashboard screens now refresh whenever new data is received, or every five seconds
+
+
+
3.16.1 - b3183
——————————————
- Fix: Error message about Ecowitt sensor mapping when saving the station settings for non-Ecowitt stations
From 70bdb31f18de7b82894513b7448a5b7b3d28e76e Mon Sep 17 00:00:00 2001
From: Mark Crossley <1196094+mcrossley@users.noreply.github.com>
Date: Wed, 11 May 2022 15:45:51 +0100
Subject: [PATCH 2/5] - Fix: Cloud base being set to large value at start-up
---
CumulusMX/Cumulus.cs | 8 +++---
CumulusMX/DavisAirLink.cs | 8 +++---
CumulusMX/DavisStation.cs | 2 ++
CumulusMX/DavisWllStation.cs | 8 +++---
CumulusMX/EasyWeather.cs | 1 +
CumulusMX/EcowittApi.cs | 29 ++++++--------------
CumulusMX/FOStation.cs | 10 ++++---
CumulusMX/GW1000Api.cs | 4 +--
CumulusMX/GW1000Station.cs | 45 +++----------------------------
CumulusMX/HttpStationAmbient.cs | 1 +
CumulusMX/HttpStationEcowitt.cs | 28 +++++---------------
CumulusMX/HttpStationWund.cs | 1 +
CumulusMX/ImetStation.cs | 5 ++++
CumulusMX/Simulator.cs | 23 ++++++++--------
CumulusMX/StationSettings.cs | 6 ++---
CumulusMX/TempestStation.cs | 14 ++++------
CumulusMX/WM918Station.cs | 1 +
CumulusMX/WMR100Station.cs | 1 +
CumulusMX/WMR200Station.cs | 2 ++
CumulusMX/WMR928Station.cs | 2 ++
CumulusMX/WS2300Station.cs | 3 +++
CumulusMX/WeatherStation.cs | 47 ++++++++++++++++-----------------
Updates.txt | 4 ++-
23 files changed, 103 insertions(+), 150 deletions(-)
diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs
index ee47ed88..31e1701a 100644
--- a/CumulusMX/Cumulus.cs
+++ b/CumulusMX/Cumulus.cs
@@ -492,8 +492,8 @@ public struct TExtraFiles
private List OWMList = new List();
// Use thread safe queues for the MySQL command lists
- private ConcurrentQueue MySqlList = new ConcurrentQueue();
- private ConcurrentQueue MySqlFailedList = new ConcurrentQueue();
+ private readonly ConcurrentQueue MySqlList = new ConcurrentQueue();
+ private readonly ConcurrentQueue MySqlFailedList = new ConcurrentQueue();
// Calibration settings
///
@@ -757,7 +757,7 @@ public struct MqttSettings
public string loggingfile;
- private PingReply pingReply;
+ //private PingReply pingReply;
public Cumulus(int HTTPport, bool DebugEnabled, string startParms)
{
@@ -10512,7 +10512,7 @@ private void PingCompletedCallback(object sender, PingCompletedEventArgs e)
LogMessage("Ping reply: " + e.Reply.Status);
}
- pingReply = e.Reply;
+ //pingReply = e.Reply;
}
private void CreateRequiredFolders()
diff --git a/CumulusMX/DavisAirLink.cs b/CumulusMX/DavisAirLink.cs
index 0e5f0c58..4a352f86 100644
--- a/CumulusMX/DavisAirLink.cs
+++ b/CumulusMX/DavisAirLink.cs
@@ -16,10 +16,10 @@ namespace CumulusMX
{
internal class DavisAirLink
{
- private Cumulus cumulus;
- private WeatherStation station;
+ private readonly Cumulus cumulus;
+ private readonly WeatherStation station;
- private string ipaddr;
+ private readonly string ipaddr;
private readonly System.Timers.Timer tmrCurrent;
private System.Timers.Timer tmrHealth;
private readonly object threadSafer = new object();
@@ -42,7 +42,7 @@ internal class DavisAirLink
private readonly bool standaloneHistory; // Used to flag if we need to get history data on catch-up
private DateTime airLinkLastUpdateTime;
- private DiscoveredDevices discovered = new DiscoveredDevices();
+ private readonly DiscoveredDevices discovered = new DiscoveredDevices();
public DavisAirLink(Cumulus cumulus, bool indoor, WeatherStation station)
{
diff --git a/CumulusMX/DavisStation.cs b/CumulusMX/DavisStation.cs
index 99869d83..4f83d2c3 100644
--- a/CumulusMX/DavisStation.cs
+++ b/CumulusMX/DavisStation.cs
@@ -1636,6 +1636,7 @@ private void GetAndProcessLoopData(int number)
DoApparentTemp(now);
DoFeelsLike(now);
DoHumidex(now);
+ DoCloudBaseHeatIndex(now);
var forecastRule = loopData.ForecastRule < cumulus.DavisForecastLookup.Length ? loopData.ForecastRule : cumulus.DavisForecastLookup.Length - 1;
@@ -2507,6 +2508,7 @@ private void GetArchiveData()
DoApparentTemp(timestamp);
DoFeelsLike(timestamp);
DoHumidex(timestamp);
+ DoCloudBaseHeatIndex(timestamp);
// add in 'archivePeriod' minutes worth of wind speed to windrun
WindRunToday += ((WindAverage * WindRunHourMult[cumulus.Units.Wind] * interval) / 60.0);
diff --git a/CumulusMX/DavisWllStation.cs b/CumulusMX/DavisWllStation.cs
index 93cf8545..33226f81 100644
--- a/CumulusMX/DavisWllStation.cs
+++ b/CumulusMX/DavisWllStation.cs
@@ -40,10 +40,10 @@ internal class DavisWllStation : WeatherStation
private bool broadcastReceived;
private int weatherLinkArchiveInterval = 16 * 60; // Used to get historic Health, 16 minutes in seconds only for initial fetch after load
private bool wllVoltageLow;
- private CancellationTokenSource tokenSource = new CancellationTokenSource();
+ private readonly CancellationTokenSource tokenSource = new CancellationTokenSource();
private CancellationToken cancellationToken;
private Task broadcastTask;
- private AutoResetEvent bwDoneEvent = new AutoResetEvent(false);
+ private readonly AutoResetEvent bwDoneEvent = new AutoResetEvent(false);
private readonly List sensorList = new List();
private readonly bool useWeatherLinkDotCom = true;
@@ -733,7 +733,7 @@ private void DecodeCurrent(string currentJson)
DoOutdoorTemp(ConvertTempFToUser(data1.temp.Value), dateTime);
if (data1.dew_point.HasValue)
- DoOutdoorDewpoint(ConvertTempFToUser(data1.dew_point.Value), dateTime);
+ DoOutdoorDewpoint(ConvertTempFToUser(data1.dew_point.Value), dateTime);
if (!cumulus.StationOptions.CalculatedWC && data1.wind_chill.HasValue)
{
@@ -1265,6 +1265,7 @@ private void DecodeCurrent(string currentJson)
DoApparentTemp(dateTime);
DoFeelsLike(dateTime);
DoHumidex(dateTime);
+ DoCloudBaseHeatIndex(dateTime);
DoForecast(string.Empty, false);
@@ -1814,6 +1815,7 @@ private void GetWlHistoricData(BackgroundWorker worker)
DoApparentTemp(timestamp);
DoFeelsLike(timestamp);
DoHumidex(timestamp);
+ DoCloudBaseHeatIndex(timestamp);
// Log all the data
cumulus.DoLogFile(timestamp, false);
diff --git a/CumulusMX/EasyWeather.cs b/CumulusMX/EasyWeather.cs
index 4940b42b..675ab45f 100644
--- a/CumulusMX/EasyWeather.cs
+++ b/CumulusMX/EasyWeather.cs
@@ -161,6 +161,7 @@ private void EWGetData(object sender, ElapsedEventArgs elapsedEventArgs)
DoApparentTemp(now);
DoFeelsLike(now);
DoHumidex(now);
+ DoCloudBaseHeatIndex(now);
DoForecast(string.Empty, false);
diff --git a/CumulusMX/EcowittApi.cs b/CumulusMX/EcowittApi.cs
index 9ad62cfe..6cf43ab4 100644
--- a/CumulusMX/EcowittApi.cs
+++ b/CumulusMX/EcowittApi.cs
@@ -85,8 +85,8 @@ internal bool GetHistoricData(DateTime startTime, DateTime endTime)
sb.Append($"application_key={cumulus.EcowittApplicationKey}");
sb.Append($"&api_key={cumulus.EcowittUserApiKey}");
sb.Append($"&mac={cumulus.EcowittMacAddress}");
- sb.Append($"&start_date={apiStartDate.ToString("yyyy-MM-dd'%20'HH:mm:ss")}");
- sb.Append($"&end_date={apiEndDate.ToString("yyyy-MM-dd'%20'HH:mm:ss")}");
+ sb.Append($"&start_date={apiStartDate:yyyy-MM-dd'%20'HH:mm:ss}");
+ sb.Append($"&end_date={apiEndDate:yyyy-MM-dd'%20'HH:mm:ss}");
// Request the data in the correct units
sb.Append($"&temp_unitid={cumulus.Units.Temp + 1}"); // 1=C, 2=F
@@ -182,7 +182,7 @@ internal bool GetHistoricData(DateTime startTime, DateTime endTime)
var url = sb.ToString();
- var msg = $"Processing history data from {startTime.ToString("yyyy-MM-dd HH:mm")} to {endTime.AddMinutes(5).ToString("yyyy-MM-dd HH:mm")}...";
+ var msg = $"Processing history data from {startTime:yyyy-MM-dd HH:mm} to {endTime.AddMinutes(5):yyyy-MM-dd HH:mm}...";
cumulus.LogMessage($"API.GetHistoricData: " + msg);
cumulus.LogConsoleMessage(msg);
@@ -260,8 +260,10 @@ internal bool GetHistoricData(DateTime startTime, DateTime endTime)
// have we reached the retry limit?
if (--retries <= 0)
+ {
cumulus.LastUpdateTime = endTime;
- return false;
+ return false;
+ }
cumulus.LogMessage("API.GetHistoricData: System Busy or Rate Limited, waiting 5 secs before retry...");
System.Threading.Thread.Sleep(5000);
@@ -1255,24 +1257,8 @@ private void ApplyHistoricData(KeyValuePair r
var spdVal = (double)rec.Value.WindSpd;
var dirVal = (int)rec.Value.WindDir.Value;
- // The protocol does not provide an average value
- // so feed in current MX average
- station.DoWind(spdVal, dirVal, station.WindAverage / cumulus.Calib.WindSpeed.Mult, rec.Key);
+ station.DoWind(gustVal, dirVal, spdVal, rec.Key);
- var gustLastCal = gustVal * cumulus.Calib.WindGust.Mult;
- if (gustLastCal > station.RecentMaxGust)
- {
- cumulus.LogDebugMessage("Setting max gust from current value: " + gustLastCal.ToString(cumulus.WindFormat));
- station.CheckHighGust(gustLastCal, dirVal, rec.Key);
-
- // add to recent values so normal calculation includes this value
- station.WindRecent[station.nextwind].Gust = gustVal; // use uncalibrated value
- station.WindRecent[station.nextwind].Speed = station.WindAverage / cumulus.Calib.WindSpeed.Mult;
- station.WindRecent[station.nextwind].Timestamp = rec.Key;
- station.nextwind = (station.nextwind + 1) % WeatherStation.MaxWindRecent;
-
- station.RecentMaxGust = gustLastCal;
- }
}
}
catch (Exception ex)
@@ -1580,6 +1566,7 @@ private void ApplyHistoricData(KeyValuePair r
try
{
station.DoHumidex(rec.Key);
+ station.DoCloudBaseHeatIndex(rec.Key);
// === Apparent & Feels Like === - requires temp, hum, and windspeed
if (rec.Value.WindSpd.HasValue)
diff --git a/CumulusMX/FOStation.cs b/CumulusMX/FOStation.cs
index 66c6799a..63dfffc9 100644
--- a/CumulusMX/FOStation.cs
+++ b/CumulusMX/FOStation.cs
@@ -526,6 +526,7 @@ private void ProcessHistoryData()
DoApparentTemp(timestamp);
DoFeelsLike(timestamp);
DoHumidex(timestamp);
+ DoCloudBaseHeatIndex(timestamp);
if (hasSolar)
{
@@ -1007,10 +1008,8 @@ private void GetAndProcessData()
StationPressure = ConvertPressMBToUser(pressure);
UpdatePressureTrendString();
- UpdateStatusPanel(now);
- UpdateMQTT();
- DoForecast(string.Empty, false);
}
+
var status = data[15];
if ((status & 0x40) != 0)
{
@@ -1098,6 +1097,7 @@ private void GetAndProcessData()
DoApparentTemp(now);
DoFeelsLike(now);
DoHumidex(now);
+ DoCloudBaseHeatIndex(now);
}
// Rain ============================================================
@@ -1163,6 +1163,10 @@ private void GetAndProcessData()
DoUV(UVreading, now);
}
}
+
+ UpdateStatusPanel(now);
+ UpdateMQTT();
+ DoForecast(string.Empty, false);
}
if (cumulus.SensorAlarm.Enabled)
{
diff --git a/CumulusMX/GW1000Api.cs b/CumulusMX/GW1000Api.cs
index 1a64b486..c7f935ad 100644
--- a/CumulusMX/GW1000Api.cs
+++ b/CumulusMX/GW1000Api.cs
@@ -11,7 +11,7 @@ namespace CumulusMX
{
internal class GW1000Api
{
- private Cumulus cumulus;
+ private readonly Cumulus cumulus;
private NetworkStream stream;
private TcpClient socket;
private string ipAddress = null;
@@ -130,8 +130,6 @@ internal byte[] DoCommand(Commands command, byte[] data = null)
var bytesRead = 0;
var cmdName = command.ToString();
- var readBuffer = new byte[2028];
-
byte[] bytes;
if (data == null)
{
diff --git a/CumulusMX/GW1000Station.cs b/CumulusMX/GW1000Station.cs
index 72a93897..0fa906c4 100644
--- a/CumulusMX/GW1000Station.cs
+++ b/CumulusMX/GW1000Station.cs
@@ -27,17 +27,15 @@ internal class GW1000Station : WeatherStation
private int maxArchiveRuns = 1;
- private TcpClient socket;
- private NetworkStream stream;
private bool connectedOk = false;
private bool dataReceived = false;
private readonly System.Timers.Timer tmrDataWatchdog;
- private CancellationTokenSource tokenSource = new CancellationTokenSource();
+ private readonly CancellationTokenSource tokenSource = new CancellationTokenSource();
private CancellationToken cancellationToken;
- private Task historyTask;
+ private readonly Task historyTask;
private Task liveTask;
//private readonly NumberFormatInfo invNum = CultureInfo.InvariantCulture.NumberFormat;
@@ -1220,6 +1218,7 @@ private void GetLiveData()
DoApparentTemp(dateTime);
DoFeelsLike(dateTime);
DoHumidex(dateTime);
+ DoCloudBaseHeatIndex(dateTime);
}
DoForecast("", false);
@@ -1576,44 +1575,6 @@ public Discovery()
}
}
- private bool ChecksumOk(byte[] data, int lengthBytes)
- {
- ushort size;
-
- // general response 1 byte size 2 byte size
- // 0 - 0xff - header 0 - 0xff - header
- // 1 - 0xff 1 - 0xff
- // 2 - command 2 - command
- // 3 - total size of response 3 - size1
- // 4-X - data 4 - size2
- // X+1 - checksum 5-X - data
- // X+1 - checksum
-
- if (lengthBytes == 1)
- {
- size = (ushort)data[3];
- }
- else
- {
- size = GW1000Api.ConvertBigEndianUInt16(data, 3);
- }
-
- byte checksum = (byte)(data[2] + data[3]);
- for (var i = 4; i <= size; i++)
- {
- checksum += data[i];
- }
-
- if (checksum != data[size + 1])
- {
- cumulus.LogMessage("Bad checksum");
- return false;
- }
-
- return true;
- }
-
-
private void DataTimeout(object source, ElapsedEventArgs e)
{
if (dataReceived)
diff --git a/CumulusMX/HttpStationAmbient.cs b/CumulusMX/HttpStationAmbient.cs
index 2c6351d6..837da487 100644
--- a/CumulusMX/HttpStationAmbient.cs
+++ b/CumulusMX/HttpStationAmbient.cs
@@ -368,6 +368,7 @@ public string ProcessData(IHttpContext context, bool main)
if (data["tempf"] != null && data["humidity"] != null)
{
DoHumidex(recDate);
+ DoCloudBaseHeatIndex(recDate);
// === Apparent === - requires temp, hum, and windspeed
if (data["windspeedmph"] != null)
diff --git a/CumulusMX/HttpStationEcowitt.cs b/CumulusMX/HttpStationEcowitt.cs
index 655fdb99..164957f3 100644
--- a/CumulusMX/HttpStationEcowitt.cs
+++ b/CumulusMX/HttpStationEcowitt.cs
@@ -47,9 +47,9 @@ public HttpStationEcowitt(Cumulus cumulus, WeatherStation station = null) : base
cumulus.StationOptions.UseWind10MinAvg = true;
// GW1000 does not provide an interval gust value, it gives us a 2 minute high
- // So CMX porces the latest speed into the gust
- // Therefore we need to force using the gust (but we actually input the speed) for the average calculation
- cumulus.StationOptions.UseSpeedForAvgCalc = false;
+ // The speed is the average for that update
+ // Therefore we need to force using the speed for the average calculation
+ cumulus.StationOptions.UseSpeedForAvgCalc = true;
// does not send DP, so force MX to calculate it
cumulus.StationOptions.CalculatedDP = true;
@@ -323,25 +323,8 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null)
var dirVal = Convert.ToInt32(dir, invNum);
var spdVal = ConvertWindMPHToUser(Convert.ToDouble(spd, invNum));
- // The protocol does not provide an average value
- // so feed in current MX average
- DoWind(spdVal, dirVal, WindAverage / cumulus.Calib.WindSpeed.Mult, recDate);
+ DoWind(gustVal, dirVal, spdVal, recDate);
- var gustLastCal = gustVal * cumulus.Calib.WindGust.Mult;
-
- if (gustLastCal > RecentMaxGust)
- {
- cumulus.LogDebugMessage("Setting max gust from current value: " + gustLastCal.ToString(cumulus.WindFormat));
- CheckHighGust(gustLastCal, dirVal, recDate);
-
- // add to recent values so normal calculation includes this value
- WindRecent[nextwind].Gust = gustVal; // use uncalibrated value
- WindRecent[nextwind].Speed = WindAverage / cumulus.Calib.WindSpeed.Mult;
- WindRecent[nextwind].Timestamp = recDate;
- nextwind = (nextwind + 1) % MaxWindRecent;
-
- RecentMaxGust = gustLastCal;
- }
}
}
catch (Exception ex)
@@ -851,6 +834,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null)
if (thisTemp != null && thisHum != null)
{
DoHumidex(recDate);
+ DoCloudBaseHeatIndex(recDate);
// === Apparent === - requires temp, hum, and windspeed
if (data["windspeedmph"] != null)
@@ -1289,7 +1273,7 @@ private void SetCustomServer(GW1000Api api, bool main)
}
else
{
- cumulus.LogMessage($"Set Ecowitt Gateway Custom Server path={path}");
+ cumulus.LogMessage($"Set Ecowitt Gateway Custom Server Path={customPath}");
}
}
}
diff --git a/CumulusMX/HttpStationWund.cs b/CumulusMX/HttpStationWund.cs
index dbb6a858..41314421 100644
--- a/CumulusMX/HttpStationWund.cs
+++ b/CumulusMX/HttpStationWund.cs
@@ -317,6 +317,7 @@ GET Parameters - all fields are URL escaped
if (data["tempf"] != null && data["humidity"] != null && data["tempf"] != "-9999" && data["humidity"] != "-9999")
{
DoHumidex(recDate);
+ DoCloudBaseHeatIndex(recDate);
}
else
{
diff --git a/CumulusMX/ImetStation.cs b/CumulusMX/ImetStation.cs
index a114940b..49ff1883 100644
--- a/CumulusMX/ImetStation.cs
+++ b/CumulusMX/ImetStation.cs
@@ -756,9 +756,11 @@ public override void getAndProcessHistoryData()
// Cause wind chill calc
DoWindChill(0, timestamp);
+ DoOutdoorDewpoint(0, timestamp);
DoApparentTemp(timestamp);
DoFeelsLike(timestamp);
DoHumidex(timestamp);
+ DoCloudBaseHeatIndex(timestamp);
// sunshine hours
if (sl[SUNPOS].Length > 0)
@@ -990,7 +992,10 @@ private void ImetGetData()
if (temp1 > -999 && humidity > -999)
{
+ DoOutdoorDewpoint(0, now);
DoHumidex(now);
+ DoCloudBaseHeatIndex(now);
+
if (windspeed > -999)
{
DoApparentTemp(now);
diff --git a/CumulusMX/Simulator.cs b/CumulusMX/Simulator.cs
index 16d4daa9..d49f0ef7 100644
--- a/CumulusMX/Simulator.cs
+++ b/CumulusMX/Simulator.cs
@@ -11,12 +11,12 @@ namespace CumulusMX
internal class Simulator : WeatherStation
{
private bool stop;
- private CancellationTokenSource tokenSource = new CancellationTokenSource();
+ private readonly CancellationTokenSource tokenSource = new CancellationTokenSource();
private CancellationToken cancellationToken;
- private DataSet currData;
+ private readonly DataSet currData;
- private int dataUpdateRate = 5000; // 5 second data update rate
+ private readonly int dataUpdateRate = 5000; // 5 second data update rate
private new readonly Random random;
private bool solarIntialised;
@@ -133,6 +133,7 @@ private void applyData(DateTime recDate)
DoHumidex(recDate);
DoApparentTemp(recDate);
DoFeelsLike(recDate);
+ DoCloudBaseHeatIndex(recDate);
}
private void doSolar(DateTime recDate)
@@ -181,14 +182,14 @@ private void doSolar(DateTime recDate)
private class DataSet
{
- private MeanRevertingRandomWalk temperature;
- private MeanRevertingRandomWalk humidity;
- private MeanRevertingRandomWalk windSpeed;
- private MeanRevertingRandomWalk windDirection;
- private MeanRevertingRandomWalk insideTemp;
- private MeanRevertingRandomWalk insideHum;
- private MeanRevertingRandomWalk pressure;
- private MeanRevertingRandomWalk rainRate;
+ private readonly MeanRevertingRandomWalk temperature;
+ private readonly MeanRevertingRandomWalk humidity;
+ private readonly MeanRevertingRandomWalk windSpeed;
+ private readonly MeanRevertingRandomWalk windDirection;
+ private readonly MeanRevertingRandomWalk insideTemp;
+ private readonly MeanRevertingRandomWalk insideHum;
+ private readonly MeanRevertingRandomWalk pressure;
+ private readonly MeanRevertingRandomWalk rainRate;
public double tempVal { get; set; }
public int humVal { get; set; }
diff --git a/CumulusMX/StationSettings.cs b/CumulusMX/StationSettings.cs
index 8988ddd6..cad3d574 100644
--- a/CumulusMX/StationSettings.cs
+++ b/CumulusMX/StationSettings.cs
@@ -28,6 +28,7 @@ internal string GetAlpacaFormData()
// Build the settings data, convert to JSON, and return it
var optionsAdv = new JsonStationSettingsOptionsAdvanced()
{
+ usespeedforavg = cumulus.StationOptions.UseSpeedForAvgCalc,
avgbearingmins = cumulus.StationOptions.AvgBearingMinutes,
avgspeedmins = cumulus.StationOptions.AvgSpeedMinutes,
peakgustmins = cumulus.StationOptions.PeakGustMinutes,
@@ -41,7 +42,6 @@ internal string GetAlpacaFormData()
{
usezerobearing = cumulus.StationOptions.UseZeroBearing,
calcwindaverage = cumulus.StationOptions.UseWind10MinAvg,
- usespeedforavg = cumulus.StationOptions.UseSpeedForAvgCalc,
use100for98hum = cumulus.StationOptions.Humidity98Fix,
calculatedewpoint = cumulus.StationOptions.CalculatedDP,
calculatewindchill = cumulus.StationOptions.CalculatedWC,
@@ -743,7 +743,6 @@ internal string UpdateConfig(IHttpContext context)
{
cumulus.StationOptions.UseZeroBearing = settings.Options.usezerobearing;
cumulus.StationOptions.UseWind10MinAvg = settings.Options.calcwindaverage;
- cumulus.StationOptions.UseSpeedForAvgCalc = settings.Options.usespeedforavg;
cumulus.StationOptions.Humidity98Fix = settings.Options.use100for98hum;
cumulus.StationOptions.CalculatedDP = settings.Options.calculatedewpoint;
cumulus.StationOptions.CalculatedWC = settings.Options.calculatewindchill;
@@ -754,6 +753,7 @@ internal string UpdateConfig(IHttpContext context)
cumulus.StationOptions.RoundWindSpeed = settings.Options.roundwindspeeds;
cumulus.StationOptions.NoSensorCheck = settings.Options.nosensorcheck;
+ cumulus.StationOptions.UseSpeedForAvgCalc = settings.Options.advanced.usespeedforavg;
cumulus.StationOptions.AvgBearingMinutes = settings.Options.advanced.avgbearingmins;
cumulus.StationOptions.AvgSpeedMinutes = settings.Options.advanced.avgspeedmins;
cumulus.StationOptions.PeakGustMinutes = settings.Options.advanced.peakgustmins;
@@ -1553,6 +1553,7 @@ internal class JsonStationSettingsUnits
internal class JsonStationSettingsOptionsAdvanced
{
+ public bool usespeedforavg { get; set; }
public int avgbearingmins { get; set; }
public int avgspeedmins { get; set; }
public int peakgustmins { get; set; }
@@ -1566,7 +1567,6 @@ internal class JsonStationSettingsOptions
{
public bool usezerobearing { get; set; }
public bool calcwindaverage { get; set; }
- public bool usespeedforavg { get; set; }
public bool use100for98hum { get; set; }
public bool calculatedewpoint { get; set; }
public bool calculatewindchill { get; set; }
diff --git a/CumulusMX/TempestStation.cs b/CumulusMX/TempestStation.cs
index 6c0928b2..73adb2d7 100644
--- a/CumulusMX/TempestStation.cs
+++ b/CumulusMX/TempestStation.cs
@@ -165,7 +165,7 @@ private void ProcessHistoryData(List datalist)
DoApparentTemp(timestamp);
DoFeelsLike(timestamp);
DoHumidex(timestamp);
-
+ DoCloudBaseHeatIndex(timestamp);
DoUV((double) historydata.UV, timestamp);
@@ -309,6 +309,8 @@ private void WeatherPacketReceived(WeatherPacket wp)
DoFeelsLike(ts);
DoWindChill(userTemp,ts);
DoHumidex(ts);
+ DoCloudBaseHeatIndex(ts);
+
UpdateStatusPanel(ts);
UpdateMQTT();
DoForecast(string.Empty, false);
@@ -746,10 +748,7 @@ public DeviceStatus(WeatherPacket packet)
if (!int.TryParse(packet.firmware_revision.ToString(), out var i)) i = -1;
FirmwareRevision = i;
}
- catch (Exception e)
- {
- var ex = e.Message;
- }
+ catch {}
RSSI = packet.rssi;
HubRSSI = packet.hub_rssi;
@@ -858,10 +857,7 @@ public Observation(WeatherPacket packet)
if (!int.TryParse(packet.firmware_revision.ToString(), out i)) i = -1;
FirmwareRevision = i;
}
- catch (Exception e)
- {
- var ex = e.Message;
- }
+ catch {}
if (packet.obs[0].Length >= 18)
{
diff --git a/CumulusMX/WM918Station.cs b/CumulusMX/WM918Station.cs
index ea6225a1..d3fa72ab 100644
--- a/CumulusMX/WM918Station.cs
+++ b/CumulusMX/WM918Station.cs
@@ -377,6 +377,7 @@ private void WM918Temp(List buff)
DoApparentTemp(DateTime.Now);
DoFeelsLike(DateTime.Now);
DoHumidex(DateTime.Now);
+ DoCloudBaseHeatIndex(DateTime.Now);
}
private void WM918Rain(List buff)
diff --git a/CumulusMX/WMR100Station.cs b/CumulusMX/WMR100Station.cs
index 71e2c43a..7b4ee86c 100644
--- a/CumulusMX/WMR100Station.cs
+++ b/CumulusMX/WMR100Station.cs
@@ -463,6 +463,7 @@ private void ProcessTempPacket()
DoApparentTemp(Now);
DoFeelsLike(Now);
DoHumidex(Now);
+ DoCloudBaseHeatIndex(Now);
// battery status
//if (PacketBuffer[0] & 0x40 == 0x40 )
diff --git a/CumulusMX/WMR200Station.cs b/CumulusMX/WMR200Station.cs
index fe39bb1b..f57f47fc 100644
--- a/CumulusMX/WMR200Station.cs
+++ b/CumulusMX/WMR200Station.cs
@@ -593,6 +593,7 @@ private void ProcessTempHumPacket()
DoApparentTemp(now);
DoFeelsLike(now);
DoHumidex(now);
+ DoCloudBaseHeatIndex(now);
}
else if (sensor == 0)
{
@@ -1598,6 +1599,7 @@ private void ProcessHistoryDataPacket()
DoApparentTemp(timestamp);
DoFeelsLike(timestamp);
DoHumidex(timestamp);
+ DoCloudBaseHeatIndex(timestamp);
cumulus.DoLogFile(timestamp,false);
cumulus.MySqlRealtimeFile(999, false, timestamp);
diff --git a/CumulusMX/WMR928Station.cs b/CumulusMX/WMR928Station.cs
index 468db982..95c396db 100644
--- a/CumulusMX/WMR928Station.cs
+++ b/CumulusMX/WMR928Station.cs
@@ -316,6 +316,7 @@ private void WMR928ExtraTempOnly(List buff)
DoApparentTemp(DateTime.Now);
DoFeelsLike(DateTime.Now);
DoHumidex(DateTime.Now);
+ DoCloudBaseHeatIndex(DateTime.Now);
}
}
@@ -475,6 +476,7 @@ private void WMR928Outdoor(List buff)
DoApparentTemp(DateTime.Now);
DoFeelsLike(DateTime.Now);
DoHumidex(DateTime.Now);
+ DoCloudBaseHeatIndex(DateTime.Now);
}
}
diff --git a/CumulusMX/WS2300Station.cs b/CumulusMX/WS2300Station.cs
index 6caad28a..8ccec9c6 100644
--- a/CumulusMX/WS2300Station.cs
+++ b/CumulusMX/WS2300Station.cs
@@ -374,6 +374,7 @@ private void ProcessHistoryData()
DoApparentTemp(timestamp);
DoFeelsLike(timestamp);
DoHumidex(timestamp);
+ DoCloudBaseHeatIndex(timestamp);
CalculateDominantWindBearing(Bearing, WindAverage, historydata.interval);
@@ -724,6 +725,8 @@ private void GetAndProcessData()
DoApparentTemp(now);
DoFeelsLike(now);
DoHumidex(now);
+ DoCloudBaseHeatIndex(now);
+
UpdateStatusPanel(now);
UpdateMQTT();
}
diff --git a/CumulusMX/WeatherStation.cs b/CumulusMX/WeatherStation.cs
index b397cc0d..a7f65710 100644
--- a/CumulusMX/WeatherStation.cs
+++ b/CumulusMX/WeatherStation.cs
@@ -1444,7 +1444,7 @@ private async Task sendWebSocketData()
try
{
// wait for the ws lock object
- lock (webSocketThreadLock);
+ lock (webSocketThreadLock)
{
//cumulus.LogDebugMessage("WebSocket: Sending message");
@@ -1499,7 +1499,7 @@ private async Task sendWebSocketData()
WebSocket.SendMessage(new StreamReader(stream).ReadToEnd());
// We can't be sure when the broadcast completes because it is async internally, so the best we can do is wait a short time
- await Task.Delay(500);
+ Thread.Sleep(500);
}
}
catch (Exception ex)
@@ -2976,7 +2976,6 @@ public void DoOutdoorTemp(double temp, DateTime timestamp)
// update global temp
OutdoorTemperature = CalibrateTemp(temp);
- double tempinF = ConvertUserTempToF(OutdoorTemperature);
double tempinC = ConvertUserTempToC(OutdoorTemperature);
first_temp = false;
@@ -3043,25 +3042,25 @@ public void DoOutdoorTemp(double temp, DateTime timestamp)
if ((cumulus.StationOptions.CalculatedDP || cumulus.DavisStation) && (OutdoorHumidity != 0) && (!cumulus.FineOffsetStation))
{
// Calculate DewPoint.
- // dewpoint = TempinC + ((0.13 * TempinC) + 13.6) * Ln(humidity / 100);
OutdoorDewpoint = ConvertTempCToUser(MeteoLib.DewPoint(tempinC, OutdoorHumidity));
CheckForDewpointHighLow(timestamp);
}
+ TempReadyToPlot = true;
+ HaveReadData = true;
+ }
+
+
+ public void DoCloudBaseHeatIndex(DateTime timestamp)
+ {
+ var tempinF = ConvertUserTempToF(OutdoorTemperature);
+ var tempinC = ConvertUserTempToC(OutdoorTemperature);
+
// Calculate cloud base
- if (cumulus.CloudBaseInFeet)
- {
- CloudBase = (int)Math.Floor(((tempinF - ConvertUserTempToF(OutdoorDewpoint)) / 4.4) * 1000);
- if (CloudBase < 0)
- CloudBase = 0;
- }
- else
- {
- CloudBase = (int)Math.Floor((((tempinF - ConvertUserTempToF(OutdoorDewpoint)) / 4.4) * 1000) / 3.2808399);
- if (CloudBase < 0)
- CloudBase = 0;
- }
+ CloudBase = (int)Math.Floor((tempinF - ConvertUserTempToF(OutdoorDewpoint)) / 4.4 * 1000 / (cumulus.CloudBaseInFeet ? 1 : 3.2808399));
+ if (CloudBase < 0)
+ CloudBase = 0;
HeatIndex = ConvertTempCToUser(MeteoLib.HeatIndex(tempinC, OutdoorHumidity));
@@ -3091,7 +3090,6 @@ public void DoOutdoorTemp(double temp, DateTime timestamp)
CheckMonthlyAlltime("HighHeatIndex", HeatIndex, true, timestamp);
- //DoApparentTemp(timestamp);
// Find estimated wet bulb temp. First time this is called, required variables may not have been set up yet
try
@@ -3102,9 +3100,6 @@ public void DoOutdoorTemp(double temp, DateTime timestamp)
{
WetBulb = OutdoorTemperature;
}
-
- TempReadyToPlot = true;
- HaveReadData = true;
}
public void DoApparentTemp(DateTime timestamp)
@@ -3993,8 +3988,12 @@ public void DoWind(double gustpar, int bearingpar, double speedpar, DateTime tim
}
var uncalibratedgust = gustpar;
calibratedgust = uncalibratedgust * cumulus.Calib.WindGust.Mult;
- WindLatest = calibratedgust;
- windspeeds[nextwindvalue] = uncalibratedgust;
+
+ // If we are using speed for average it means the gustpar is a period gust value not a latest.
+ // So we have to use the speed for the latest
+ WindLatest = cumulus.StationOptions.UseSpeedForAvgCalc ? speedpar * cumulus.Calib.WindSpeed.Mult : calibratedgust;
+
+ windspeeds[nextwindvalue] = gustpar;
windbears[nextwindvalue] = Bearing;
// Recalculate wind rose data
@@ -4046,7 +4045,7 @@ public void DoWind(double gustpar, int bearingpar, double speedpar, DateTime tim
// check for monthly all time records (and set)
CheckMonthlyAlltime("HighGust", calibratedgust, true, timestamp);
- WindRecent[nextwind].Gust = uncalibratedgust;
+ WindRecent[nextwind].Gust = gustpar;
WindRecent[nextwind].Speed = speedpar;
WindRecent[nextwind].Timestamp = timestamp;
nextwind = (nextwind + 1) % MaxWindRecent;
@@ -7135,7 +7134,7 @@ public logfilerec ParseLogFileRec(string data, bool minMax)
internal void UpdateStatusPanel(DateTime timestamp)
{
LastDataReadTimestamp = timestamp;
- sendWebSocketData();
+ _ = sendWebSocketData();
}
diff --git a/Updates.txt b/Updates.txt
index e229099a..1bd67b62 100644
--- a/Updates.txt
+++ b/Updates.txt
@@ -1,10 +1,12 @@
3.17.0 - b3184
——————————————
+- Fix: Cloud base being set to large value at start-up
- New: Adds a PWS Simulator station type for testing or trial purposes
- Change: The "live" dashboard screens now refresh whenever new data is received, or every five seconds
-
+- Change: The "Speed for average calc" station option has been moved from Common Options to Common Options | Advanced Options
+- Change: The Ecowitt stations now force the "Speed for average calc" option to be enabled at start-up
3.16.1 - b3183
From e4d5874bfaaef9a07042368ee0a337ef7fea67f2 Mon Sep 17 00:00:00 2001
From: Mark Crossley <1196094+mcrossley@users.noreply.github.com>
Date: Fri, 13 May 2022 14:39:57 +0100
Subject: [PATCH 3/5] web socket locking changes
---
CumulusMX/CumulusMX.csproj | 1 -
CumulusMX/EcowittApi.cs | 9 +++
CumulusMX/GW1000Station.cs | 8 ++-
CumulusMX/HttpStationEcowitt.cs | 21 +++++++
CumulusMX/WeatherStation.cs | 103 ++++++++++++++++----------------
CumulusMX/webtags.cs | 4 +-
Updates.txt | 1 +
7 files changed, 91 insertions(+), 56 deletions(-)
diff --git a/CumulusMX/CumulusMX.csproj b/CumulusMX/CumulusMX.csproj
index 485d89da..00520b40 100644
--- a/CumulusMX/CumulusMX.csproj
+++ b/CumulusMX/CumulusMX.csproj
@@ -266,7 +266,6 @@
Designer
-
diff --git a/CumulusMX/EcowittApi.cs b/CumulusMX/EcowittApi.cs
index 6cf43ab4..61e970bd 100644
--- a/CumulusMX/EcowittApi.cs
+++ b/CumulusMX/EcowittApi.cs
@@ -1249,6 +1249,9 @@ private void ProcessHistoryData(EcowittHistoricData data)
private void ApplyHistoricData(KeyValuePair rec)
{
// === Wind ==
+ // WindGust = max for period
+ // WindSpd = avg for period
+ // WindDir = avg for period
try
{
if (rec.Value.WindGust.HasValue && rec.Value.WindSpd.HasValue && rec.Value.WindDir.HasValue)
@@ -1267,6 +1270,7 @@ private void ApplyHistoricData(KeyValuePair r
}
// === Humidity ===
+ // = avg for period
try
{
if (rec.Value.IndoorHum.HasValue)
@@ -1285,6 +1289,7 @@ private void ApplyHistoricData(KeyValuePair r
}
// === Pressure ===
+ // = avg for period
try
{
if (rec.Value.Pressure.HasValue)
@@ -1300,6 +1305,7 @@ private void ApplyHistoricData(KeyValuePair r
}
// === Indoor temp ===
+ // = avg for period
try
{
if (rec.Value.IndoorTemp.HasValue)
@@ -1314,6 +1320,7 @@ private void ApplyHistoricData(KeyValuePair r
}
// === Outdoor temp ===
+ // = avg for period
try
{
if (rec.Value.Temp.HasValue && cumulus.Gw1000PrimaryTHSensor == 0)
@@ -1356,6 +1363,7 @@ private void ApplyHistoricData(KeyValuePair r
}
// === Solar ===
+ // = max for period
try
{
if (rec.Value.Solar.HasValue)
@@ -1369,6 +1377,7 @@ private void ApplyHistoricData(KeyValuePair r
}
// === UVI ===
+ // = max for period
try
{
if (rec.Value.UVI.HasValue)
diff --git a/CumulusMX/GW1000Station.cs b/CumulusMX/GW1000Station.cs
index 0fa906c4..a321effa 100644
--- a/CumulusMX/GW1000Station.cs
+++ b/CumulusMX/GW1000Station.cs
@@ -41,6 +41,7 @@ internal class GW1000Station : WeatherStation
//private readonly NumberFormatInfo invNum = CultureInfo.InvariantCulture.NumberFormat;
private readonly Version fwVersion;
+ private readonly string gatewayType;
public GW1000Station(Cumulus cumulus) : base(cumulus)
@@ -57,7 +58,7 @@ public GW1000Station(Cumulus cumulus) : base(cumulus)
cumulus.StationOptions.UseSpeedForAvgCalc = true;
LightningTime = DateTime.MinValue;
- LightningDistance = 999;
+ LightningDistance = -1.0;
tmrDataWatchdog = new System.Timers.Timer();
@@ -126,6 +127,7 @@ public GW1000Station(Cumulus cumulus) : base(cumulus)
var fwString = GW1000FirmwareVersion.Split(new string[] { "_V" }, StringSplitOptions.None);
if (fwString.Length > 1)
{
+ gatewayType = fwString[0];
fwVersion = new Version(fwString[1]);
}
else
@@ -639,7 +641,7 @@ private bool PrintSensorInfoNew(byte[] data, int idx)
// if a WS90 is connected, it has a 8.8 second update rate, so reduce the MX update rate from the default 10 seconds
if (updateRate > 8000 && updateRate != 8000)
{
- cumulus.LogMessage($"PrintSensorInfoNew: WS90 sensor detected, changing the update rate from {updateRate / 1000} seconds to 8 seconds");
+ cumulus.LogMessage($"PrintSensorInfoNew: WS90 sensor detected, changing the update rate from {(updateRate / 1000):D} seconds to 8 seconds");
updateRate = 8000;
}
battV = data[battPos] * 0.02;
@@ -669,7 +671,7 @@ private bool PrintSensorInfoNew(byte[] data, int idx)
// if a WS80 is connected, it has a 4.75 second update rate, so reduce the MX update rate from the default 10 seconds
if (updateRate > 4000 && updateRate != 4000)
{
- cumulus.LogMessage($"PrintSensorInfoNew: WS80 sensor detected, changing the update rate from {updateRate/1000} seconds to 4 seconds");
+ cumulus.LogMessage($"PrintSensorInfoNew: WS80 sensor detected, changing the update rate from {(updateRate/1000):D} seconds to 4 seconds");
updateRate = 4000;
}
battV = data[battPos] * 0.02;
diff --git a/CumulusMX/HttpStationEcowitt.cs b/CumulusMX/HttpStationEcowitt.cs
index 164957f3..596dc3e0 100644
--- a/CumulusMX/HttpStationEcowitt.cs
+++ b/CumulusMX/HttpStationEcowitt.cs
@@ -22,6 +22,7 @@ class HttpStationEcowitt : WeatherStation
private bool stopping = false;
private readonly NumberFormatInfo invNum = CultureInfo.InvariantCulture.NumberFormat;
private bool reportStationType = true;
+ private int lastMinute = -1;
private EcowittApi ecowittApi;
private int maxArchiveRuns = 1;
@@ -220,6 +221,8 @@ POST Parameters - all fields are URL escaped
PASSKEY=&stationtype=GW1000A_V1.6.8&dateutc=2021-07-23+17:13:34&tempinf=80.6&humidityin=50&baromrelin=29.940&baromabsin=29.081&tempf=81.3&humidity=43&winddir=296&windspeedmph=2.46&windgustmph=4.25&maxdailygust=14.09&solarradiation=226.28&uv=1&rainratein=0.000&eventrainin=0.000&hourlyrainin=0.000&dailyrainin=0.000&weeklyrainin=0.000&monthlyrainin=4.118&yearlyrainin=29.055&totalrainin=29.055&temp1f=83.48&humidity1=39&temp2f=87.98&humidity2=40&temp3f=82.04&humidity3=40&temp4f=93.56&humidity4=34&temp5f=-11.38&temp6f=87.26&humidity6=38&temp7f=45.50&humidity7=40&soilmoisture1=51&soilmoisture2=65&soilmoisture3=72&soilmoisture4=36&soilmoisture5=48&pm25_ch1=11.0&pm25_avg_24h_ch1=10.8&pm25_ch2=13.0&pm25_avg_24h_ch2=15.0&tf_co2=80.8&humi_co2=48&pm25_co2=4.8&pm25_24h_co2=6.1&pm10_co2=4.9&pm10_24h_co2=6.5&co2=493&co2_24h=454&lightning_time=1627039348&lightning_num=3&lightning=24&wh65batt=0&wh80batt=3.06&batt1=0&batt2=0&batt3=0&batt4=0&batt5=0&batt6=0&batt7=0&soilbatt1=1.5&soilbatt2=1.4&soilbatt3=1.5&soilbatt4=1.5&soilbatt5=1.6&pm25batt1=4&pm25batt2=4&wh57batt=4&co2_batt=6&freq=868M&model=GW1000_Pro
PASSKEY=&stationtype=GW1100A_V2.0.2&dateutc=2021-09-08+11:58:39&tempinf=80.8&humidityin=42&baromrelin=29.864&baromabsin=29.415&temp1f=87.8&tf_ch1=64.4&batt1=0&tf_batt1=1.48&freq=868M&model=GW1100A
+ PASSKEY=&stationtype=GW1100A_V2.1.4&runtime=2336207&dateutc=2022-05-13+08:55:11&tempinf=73.0&humidityin=38&baromrelin=30.156&baromabsin=29.297&tempf=63.0&humidity=49&winddir=71&windspeedmph=2.68&windgustmph=11.86&maxdailygust=15.21&solarradiation=694.87&uv=5&rainratein=0.000&eventrainin=0.000&hourlyrainin=0.000&dailyrainin=0.000&weeklyrainin=0.000&monthlyrainin=0.591&yearlyrainin=14.591&temp1f=67.8&humidity1=43&temp2f=68.7&humidity2=47&temp3f=62.6&humidity3=51&temp4f=62.6&humidity4=51&temp5f=-1.5&temp6f=76.1&humidity6=47&temp7f=44.4&humidity7=49&soilmoisture1=14&soilmoisture2=50&soilmoisture3=20&soilmoisture4=25&pm25_ch1=7.0&pm25_avg_24h_ch1=7.6&pm25_ch2=6.0&pm25_avg_24h_ch2=8.3&tf_co2=72.9&humi_co2=44&pm25_co2=1.2&pm25_24h_co2=2.6&pm10_co2=1.5&pm10_24h_co2=3.0&co2=387&co2_24h=536&lightning_num=0&lightning=31&lightning_time=1652304268&leak_ch2=0&wh65batt=0&wh80batt=2.74&wh26batt=0&batt1=0&batt2=0&batt3=0&batt4=0&batt5=0&batt6=0&batt7=0&soilbatt1=1.3&soilbatt2=1.4&soilbatt3=1.4&soilbatt4=1.4&pm25batt1=4&pm25batt2=4&wh57batt=5&leakbatt2=4&co2_batt=6&freq=868M&model=GW1100A
+
*/
var procName = main ? "ProcessData" : "ProcessExtraData";
@@ -283,6 +286,21 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null)
// We will ignore the dateutc field if this "live" data to avoid any clock issues
recDate = ts.HasValue ? ts.Value : DateTime.Now;
+ if (recDate.Minute != lastMinute)
+ {
+
+ // at start-up or every 10 minutes trigger output of uptime
+ if ((recDate.Minute % 10) == 0 || lastMinute == -1 && data["runtime"] != null)
+ {
+ var runtime = Convert.ToInt32(data["runtime"]);
+ var uptime = TimeSpan.FromSeconds(runtime);
+
+ cumulus.LogMessage($"Ecowitt Gateway uptime = {runtime} secs - {uptime:c}");
+ }
+
+ lastMinute = recDate.Minute;
+ }
+
// we only really want to do this once
if (reportStationType && !ts.HasValue)
{
@@ -526,6 +544,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null)
}
}
+
// === Extra Humidity ===
if (main || cumulus.EcowittExtraUseTempHum)
{
@@ -744,6 +763,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null)
}
}
+
// === Firmware Version ===
try
{
@@ -794,6 +814,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null)
return "Failed: Error in dew point data - " + ex.Message;
}
+
// === Wind Chill ===
try
{
diff --git a/CumulusMX/WeatherStation.cs b/CumulusMX/WeatherStation.cs
index a7f65710..c9087114 100644
--- a/CumulusMX/WeatherStation.cs
+++ b/CumulusMX/WeatherStation.cs
@@ -43,7 +43,8 @@ public struct TWindVec
public readonly Object yearIniThreadLock = new Object();
public readonly Object alltimeIniThreadLock = new Object();
public readonly Object monthlyalltimeIniThreadLock = new Object();
- private readonly Object webSocketThreadLock = new Object();
+
+ private static readonly SemaphoreSlim webSocketSemaphore = new SemaphoreSlim(1, 1);
// holds all time highs and lows
public AllTimeRecords AllTime = new AllTimeRecords();
@@ -697,7 +698,7 @@ public void ReadTodayFile()
AlltimeRecordTimestamp = ini.GetValue("Records", "Alltime", DateTime.MinValue);
// Lightning (GW1000 for now)
- LightningDistance = ini.GetValue("Lightning", "Distance", -1);
+ LightningDistance = ini.GetValue("Lightning", "Distance", -1.0);
LightningTime = ini.GetValue("Lightning", "LastStrike", DateTime.MinValue);
}
@@ -1443,64 +1444,67 @@ private async Task sendWebSocketData()
// send current data to web-socket
try
{
- // wait for the ws lock object
- lock (webSocketThreadLock)
+ // if we already have an update queued, don't add to the wait queue. Otherwise we get hundreds queued up during catch-up
+ if (webSocketSemaphore.CurrentCount == 0)
{
- //cumulus.LogDebugMessage("WebSocket: Sending message");
+ cumulus.LogDebugMessage("sendWebSocketData: Update already queued, dropping this one");
+ return;
+ }
- StringBuilder windRoseData = new StringBuilder(80);
+ // wait for the ws lock object
+ webSocketSemaphore.Wait();
- lock (windcounts)
- {
- windRoseData.Append((windcounts[0] * cumulus.Calib.WindGust.Mult).ToString(cumulus.WindFormat, CultureInfo.InvariantCulture));
+ StringBuilder windRoseData = new StringBuilder(80);
- for (var i = 1; i < cumulus.NumWindRosePoints; i++)
- {
- windRoseData.Append(",");
- windRoseData.Append((windcounts[i] * cumulus.Calib.WindGust.Mult).ToString(cumulus.WindFormat, CultureInfo.InvariantCulture));
- }
+ lock (windcounts)
+ {
+ windRoseData.Append((windcounts[0] * cumulus.Calib.WindGust.Mult).ToString(cumulus.WindFormat, CultureInfo.InvariantCulture));
+
+ for (var i = 1; i < cumulus.NumWindRosePoints; i++)
+ {
+ windRoseData.Append(",");
+ windRoseData.Append((windcounts[i] * cumulus.Calib.WindGust.Mult).ToString(cumulus.WindFormat, CultureInfo.InvariantCulture));
}
+ }
- string stormRainStart = StartOfStorm == DateTime.MinValue ? "-----" : StartOfStorm.ToString("d");
+ string stormRainStart = StartOfStorm == DateTime.MinValue ? "-----" : StartOfStorm.ToString("d");
- var data = new DataStruct(cumulus, OutdoorTemperature, OutdoorHumidity, TempTotalToday / tempsamplestoday, IndoorTemperature, OutdoorDewpoint, WindChill, IndoorHumidity,
- Pressure, WindLatest, WindAverage, RecentMaxGust, WindRunToday, Bearing, AvgBearing, RainToday, RainYesterday, RainMonth, RainYear, RainRate,
- RainLastHour, HeatIndex, Humidex, ApparentTemperature, temptrendval, presstrendval, HiLoToday.HighGust, HiLoToday.HighGustTime.ToString("HH:mm"), HiLoToday.HighWind,
- HiLoToday.HighGustBearing, cumulus.Units.WindText, BearingRangeFrom10, BearingRangeTo10, windRoseData.ToString(), HiLoToday.HighTemp, HiLoToday.LowTemp,
- HiLoToday.HighTempTime.ToString("HH:mm"), HiLoToday.LowTempTime.ToString("HH:mm"), HiLoToday.HighPress, HiLoToday.LowPress, HiLoToday.HighPressTime.ToString("HH:mm"),
- HiLoToday.LowPressTime.ToString("HH:mm"), HiLoToday.HighRainRate, HiLoToday.HighRainRateTime.ToString("HH:mm"), HiLoToday.HighHumidity, HiLoToday.LowHumidity,
- HiLoToday.HighHumidityTime.ToString("HH:mm"), HiLoToday.LowHumidityTime.ToString("HH:mm"), cumulus.Units.PressText, cumulus.Units.TempText, cumulus.Units.RainText,
- HiLoToday.HighDewPoint, HiLoToday.LowDewPoint, HiLoToday.HighDewPointTime.ToString("HH:mm"), HiLoToday.LowDewPointTime.ToString("HH:mm"), HiLoToday.LowWindChill,
- HiLoToday.LowWindChillTime.ToString("HH:mm"), (int)SolarRad, (int)HiLoToday.HighSolar, HiLoToday.HighSolarTime.ToString("HH:mm"), UV, HiLoToday.HighUv,
- HiLoToday.HighUvTime.ToString("HH:mm"), forecaststr, getTimeString(cumulus.SunRiseTime), getTimeString(cumulus.SunSetTime),
- getTimeString(cumulus.MoonRiseTime), getTimeString(cumulus.MoonSetTime), HiLoToday.HighHeatIndex, HiLoToday.HighHeatIndexTime.ToString("HH:mm"), HiLoToday.HighAppTemp,
- HiLoToday.LowAppTemp, HiLoToday.HighAppTempTime.ToString("HH:mm"), HiLoToday.LowAppTempTime.ToString("HH:mm"), (int)CurrentSolarMax,
- AllTime.HighPress.Val, AllTime.LowPress.Val, SunshineHours, CompassPoint(DominantWindBearing), LastRainTip,
- HiLoToday.HighHourlyRain, HiLoToday.HighHourlyRainTime.ToString("HH:mm"), "F" + cumulus.Beaufort(HiLoToday.HighWind), "F" + cumulus.Beaufort(WindAverage), cumulus.BeaufortDesc(WindAverage),
- LastDataReadTimestamp.ToString("HH:mm:ss"), DataStopped, StormRain, stormRainStart, CloudBase, cumulus.CloudBaseInFeet ? "ft" : "m", RainLast24Hour,
- cumulus.LowTempAlarm.Triggered, cumulus.HighTempAlarm.Triggered, cumulus.TempChangeAlarm.UpTriggered, cumulus.TempChangeAlarm.DownTriggered, cumulus.HighRainTodayAlarm.Triggered, cumulus.HighRainRateAlarm.Triggered,
- cumulus.LowPressAlarm.Triggered, cumulus.HighPressAlarm.Triggered, cumulus.PressChangeAlarm.UpTriggered, cumulus.PressChangeAlarm.DownTriggered, cumulus.HighGustAlarm.Triggered, cumulus.HighWindAlarm.Triggered,
- cumulus.SensorAlarm.Triggered, cumulus.BatteryLowAlarm.Triggered, cumulus.SpikeAlarm.Triggered, cumulus.UpgradeAlarm.Triggered,
- cumulus.HttpUploadAlarm.Triggered, cumulus.MySqlUploadAlarm.Triggered,
- FeelsLike, HiLoToday.HighFeelsLike, HiLoToday.HighFeelsLikeTime.ToString("HH:mm"), HiLoToday.LowFeelsLike, HiLoToday.LowFeelsLikeTime.ToString("HH:mm"),
- HiLoToday.HighHumidex, HiLoToday.HighHumidexTime.ToString("HH:mm"));
+ var data = new DataStruct(cumulus, OutdoorTemperature, OutdoorHumidity, TempTotalToday / tempsamplestoday, IndoorTemperature, OutdoorDewpoint, WindChill, IndoorHumidity,
+ Pressure, WindLatest, WindAverage, RecentMaxGust, WindRunToday, Bearing, AvgBearing, RainToday, RainYesterday, RainMonth, RainYear, RainRate,
+ RainLastHour, HeatIndex, Humidex, ApparentTemperature, temptrendval, presstrendval, HiLoToday.HighGust, HiLoToday.HighGustTime.ToString("HH:mm"), HiLoToday.HighWind,
+ HiLoToday.HighGustBearing, cumulus.Units.WindText, BearingRangeFrom10, BearingRangeTo10, windRoseData.ToString(), HiLoToday.HighTemp, HiLoToday.LowTemp,
+ HiLoToday.HighTempTime.ToString("HH:mm"), HiLoToday.LowTempTime.ToString("HH:mm"), HiLoToday.HighPress, HiLoToday.LowPress, HiLoToday.HighPressTime.ToString("HH:mm"),
+ HiLoToday.LowPressTime.ToString("HH:mm"), HiLoToday.HighRainRate, HiLoToday.HighRainRateTime.ToString("HH:mm"), HiLoToday.HighHumidity, HiLoToday.LowHumidity,
+ HiLoToday.HighHumidityTime.ToString("HH:mm"), HiLoToday.LowHumidityTime.ToString("HH:mm"), cumulus.Units.PressText, cumulus.Units.TempText, cumulus.Units.RainText,
+ HiLoToday.HighDewPoint, HiLoToday.LowDewPoint, HiLoToday.HighDewPointTime.ToString("HH:mm"), HiLoToday.LowDewPointTime.ToString("HH:mm"), HiLoToday.LowWindChill,
+ HiLoToday.LowWindChillTime.ToString("HH:mm"), (int)SolarRad, (int)HiLoToday.HighSolar, HiLoToday.HighSolarTime.ToString("HH:mm"), UV, HiLoToday.HighUv,
+ HiLoToday.HighUvTime.ToString("HH:mm"), forecaststr, getTimeString(cumulus.SunRiseTime), getTimeString(cumulus.SunSetTime),
+ getTimeString(cumulus.MoonRiseTime), getTimeString(cumulus.MoonSetTime), HiLoToday.HighHeatIndex, HiLoToday.HighHeatIndexTime.ToString("HH:mm"), HiLoToday.HighAppTemp,
+ HiLoToday.LowAppTemp, HiLoToday.HighAppTempTime.ToString("HH:mm"), HiLoToday.LowAppTempTime.ToString("HH:mm"), (int)CurrentSolarMax,
+ AllTime.HighPress.Val, AllTime.LowPress.Val, SunshineHours, CompassPoint(DominantWindBearing), LastRainTip,
+ HiLoToday.HighHourlyRain, HiLoToday.HighHourlyRainTime.ToString("HH:mm"), "F" + cumulus.Beaufort(HiLoToday.HighWind), "F" + cumulus.Beaufort(WindAverage), cumulus.BeaufortDesc(WindAverage),
+ LastDataReadTimestamp.ToString("HH:mm:ss"), DataStopped, StormRain, stormRainStart, CloudBase, cumulus.CloudBaseInFeet ? "ft" : "m", RainLast24Hour,
+ cumulus.LowTempAlarm.Triggered, cumulus.HighTempAlarm.Triggered, cumulus.TempChangeAlarm.UpTriggered, cumulus.TempChangeAlarm.DownTriggered, cumulus.HighRainTodayAlarm.Triggered, cumulus.HighRainRateAlarm.Triggered,
+ cumulus.LowPressAlarm.Triggered, cumulus.HighPressAlarm.Triggered, cumulus.PressChangeAlarm.UpTriggered, cumulus.PressChangeAlarm.DownTriggered, cumulus.HighGustAlarm.Triggered, cumulus.HighWindAlarm.Triggered,
+ cumulus.SensorAlarm.Triggered, cumulus.BatteryLowAlarm.Triggered, cumulus.SpikeAlarm.Triggered, cumulus.UpgradeAlarm.Triggered,
+ cumulus.HttpUploadAlarm.Triggered, cumulus.MySqlUploadAlarm.Triggered,
+ FeelsLike, HiLoToday.HighFeelsLike, HiLoToday.HighFeelsLikeTime.ToString("HH:mm"), HiLoToday.LowFeelsLike, HiLoToday.LowFeelsLikeTime.ToString("HH:mm"),
+ HiLoToday.HighHumidex, HiLoToday.HighHumidexTime.ToString("HH:mm"));
- //var json = jss.Serialize(data);
+ //var json = jss.Serialize(data);
- var ser = new DataContractJsonSerializer(typeof(DataStruct));
+ var ser = new DataContractJsonSerializer(typeof(DataStruct));
- var stream = new MemoryStream();
+ var stream = new MemoryStream();
- ser.WriteObject(stream, data);
+ ser.WriteObject(stream, data);
- stream.Position = 0;
+ stream.Position = 0;
- cumulus.LogDebugMessage("WebSocket: Send message");
- WebSocket.SendMessage(new StreamReader(stream).ReadToEnd());
+ WebSocket.SendMessage(new StreamReader(stream).ReadToEnd());
- // We can't be sure when the broadcast completes because it is async internally, so the best we can do is wait a short time
- Thread.Sleep(500);
- }
+ // We can't be sure when the broadcast completes because it is async internally, so the best we can do is wait a short time
+ Thread.Sleep(500);
}
catch (Exception ex)
{
@@ -1508,8 +1512,7 @@ private async Task sendWebSocketData()
}
finally
{
- //cumulus.LogDebugMessage("WebSocket: End message");
- //webSocketLocked = false;
+ webSocketSemaphore.Release();
}
}
@@ -9844,8 +9847,8 @@ public string GetLightning()
{
var json = new StringBuilder("{\"data\":[", 256);
- json.Append($"[\"Distance to last strike\",\"{LightningDistance.ToString(cumulus.WindRunFormat)}\",\"{cumulus.Units.WindRunText}\"],");
- json.Append($"[\"Time of last strike\",\"{LightningTime}\",\"\"],");
+ json.Append($"[\"Distance to last strike\",\"{(LightningDistance == -1 ? "-" : LightningDistance.ToString(cumulus.WindRunFormat))}\",\"{cumulus.Units.WindRunText}\"],");
+ json.Append($"[\"Time of last strike\",\"{(DateTime.Equals(LightningTime, DateTime.MinValue) ? "-" : LightningTime.ToString("g"))}\",\"\"],");
json.Append($"[\"Number of strikes today\",\"{LightningStrikesToday}\",\"\"]");
json.Append("]}");
return json.ToString();
diff --git a/CumulusMX/webtags.cs b/CumulusMX/webtags.cs
index e52d14b7..64300e5a 100644
--- a/CumulusMX/webtags.cs
+++ b/CumulusMX/webtags.cs
@@ -3890,12 +3890,12 @@ private string TagLeakSensor4(Dictionary tagParams)
private string TagLightningDistance(Dictionary tagParams)
{
- return station.LightningDistance == 999 ? "--" : CheckRcDp(station.LightningDistance, tagParams, cumulus.WindRunDPlaces);
+ return station.LightningDistance == -1 ? "--" : CheckRcDp(station.LightningDistance, tagParams, cumulus.WindRunDPlaces);
}
private string TagLightningTime(Dictionary tagParams)
{
- return DateTime.Compare(station.LightningTime, new DateTime(1900, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)) == 0 ? "---" : GetFormattedDateTime(station.LightningTime, "t", tagParams);
+ return DateTime.Equals(station.LightningTime, DateTime.MinValue) ? "---" : GetFormattedDateTime(station.LightningTime, "t", tagParams);
}
private string TagLightningStrikesToday(Dictionary tagParams)
diff --git a/Updates.txt b/Updates.txt
index 1bd67b62..40c38ad2 100644
--- a/Updates.txt
+++ b/Updates.txt
@@ -1,6 +1,7 @@
3.17.0 - b3184
——————————————
- Fix: Cloud base being set to large value at start-up
+- Fix: Reading lightning distance from today.ini
- New: Adds a PWS Simulator station type for testing or trial purposes
From edc5b331adf112428d6ca80d7b5431cc194c39e7 Mon Sep 17 00:00:00 2001
From: Mark Crossley <1196094+mcrossley@users.noreply.github.com>
Date: Fri, 20 May 2022 09:39:47 +0100
Subject: [PATCH 4/5] Improved solar position calculation Fixed potential crash
getting local IP
---
CumulusMX/AstroLib.cs | 571 +++++++++++++++++++++++++++++---
CumulusMX/Cumulus.cs | 4 +
CumulusMX/HttpStationEcowitt.cs | 16 +-
CumulusMX/Utils.cs | 41 ++-
CumulusMX/WeatherStation.cs | 5 +-
Updates.txt | 2 +
6 files changed, 573 insertions(+), 66 deletions(-)
diff --git a/CumulusMX/AstroLib.cs b/CumulusMX/AstroLib.cs
index 3cb20357..cde6eb68 100644
--- a/CumulusMX/AstroLib.cs
+++ b/CumulusMX/AstroLib.cs
@@ -4,10 +4,10 @@ namespace CumulusMX
{
internal class AstroLib
{
- public static double BrasSolar(double el, double r, double nfac)
+ public static double BrasSolar(double el, double erv, double nfac)
{
// el solar elevation deg from horizon
- // r distance from earth to sun in AU
+ // erv distance from earth to sun in AU
// nfac atmospheric turbidity parameter (2=clear, 4-5=smoggy)
double sinal = Math.Sin(DegToRad(el)); // Sine of the solar elevation angle
@@ -16,10 +16,10 @@ public static double BrasSolar(double el, double r, double nfac)
return 0;
// solar radiation on horizontal surface at top of atmosphere
- double i0 = (1367 / (r * r)) * sinal;
+ double i0 = (1367 / (erv * erv)) * sinal;
// optical air mass
- double m = 1/(sinal + (0.15 * Math.Pow(el + 3.885, -1.253)));
+ double m = 1 / (sinal + (0.15 * Math.Pow(el + 3.885, -1.253)));
// molecular scattering coefficient
double al = 0.128 - (0.054 * Math.Log(m) / Math.Log(10));
@@ -43,21 +43,21 @@ public static double RyanStolzSolar(double el, double erv, double atc, double z)
double al = Math.Asin(sinal);
double a0 = RadToDeg(al); // convert the radians to degree
- double rm = Math.Pow(((288.0 - 0.0065*z)/288.0), 5.256)/(sinal + 0.15*Math.Pow((a0 + 3.885), (-1.253)));
+ double rm = Math.Pow(((288.0 - 0.0065 * z) / 288.0), 5.256) / (sinal + 0.15 * Math.Pow((a0 + 3.885), (-1.253)));
- double rsToa = 1360*sinal/(erv*erv); // RS on the top of atmosphere
+ double rsToa = 1360 * sinal / (erv * erv); // RS on the top of atmosphere
return rsToa * Math.Pow(atc, rm); //RS on the ground
}
private static double DegToRad(double angle)
{
- return Math.PI*angle/180.0;
+ return Math.PI * angle / 180.0;
}
private static double RadToDeg(double angle)
{
- return angle*(180.0/Math.PI);
+ return angle * (180.0 / Math.PI);
}
public static double SolarMax(DateTime timestamp, double longitude, double latitude, double altitude,
@@ -76,7 +76,7 @@ public static double SolarMax(DateTime timestamp, double longitude, double latit
}
- public static double SolarMax(DateTime timestamp, double longitude, double latitude, double altitude,
+ private static double SolarMax(DateTime timestamp, double longitude, double latitude, double altitude,
out double solarelevation, double factor, int method)
{
DateTime utctime = timestamp.ToUniversalTime();
@@ -106,10 +106,478 @@ private static double GetFactor(DateTime timestamp, double jun, double dec)
return dec + Math.Cos((doy - 172) / 183.0 * Math.PI / 2) * range;
}
+
+
+ // Uses the VBA from NOAA solrad_ver16 spreadsheet
+ #region solrad calculations
+
+ /*
+ private static void CalculateSunPosition(DateTime dateTime, double latitude, double lon, out double altitude, out double azimuth)
+ {
+ // change sign convention for longitude from negative to positive in western hemisphere
+ var longitude = lon * -1.0;
+
+ if (latitude > 89.8)
+ latitude = 89.8;
+
+ if (latitude < -89.8)
+ latitude = -89.8;
+
+ // timenow is GMT time for calculation in hours since 0Z
+ //var timenow = dateTime.TimeOfDay.TotalHours;
+
+ double julianDate = 367.0 * dateTime.Year -
+ (int)((7.0 / 4.0) * (dateTime.Year + (int)((dateTime.Month + 9.0) / 12.0))) +
+ (int)((275.0 * dateTime.Month) / 9.0) +
+ dateTime.Day - 730531.5;
+
+ double t = julianDate / 36525.0;
+ //double r = calcSunRadVector(t);
+ //double alpha = calcSunRtAscension(t);
+ double solardec = calcSunDeclination(t);
+ double eqtime = calcEquationOfTime(t);
+
+ double zone = TimeZoneInfo.Local.BaseUtcOffset.Hours;
+ zone = -7;
+
+ double solarTimeFix = eqtime - 4.0 * longitude + 60.0 * zone;
+ double trueSolarTime = dateTime.Hour * 60 + dateTime.Minute + dateTime.Second / 60.0 + solarTimeFix; // in minutes
+
+ while (trueSolarTime > 1440)
+ {
+ trueSolarTime -= 1440;
+ }
+
+ double hourangle = trueSolarTime / 4.0 - 180;
+
+ if (hourangle < -180)
+ hourangle += 360;
+
+ double harad = DegToRad(hourangle);
+
+ double csz = Math.Sin(DegToRad(latitude)) * Math.Sin(DegToRad(solardec)) + Math.Cos(DegToRad(latitude)) * Math.Cos(DegToRad(solardec)) * Math.Cos(harad);
+
+ if (csz > 1)
+ csz = 1;
+ else if (csz < -1)
+ csz = -1;
+
+ double zenith = RadToDeg(Math.Acos(csz));
+ double azDenom = Math.Cos(DegToRad(latitude)) * Math.Sin(DegToRad(zenith));
+
+ double azRad;
+
+ if (Math.Abs(azDenom) > 0.001)
+ {
+ azRad = (Math.Sin(DegToRad(latitude) * Math.Cos(DegToRad(zenith))) - Math.Sin(DegToRad(solardec))) / azDenom;
+
+ if (Math.Abs(azRad) > 1)
+ {
+ if (azRad < 0)
+ azRad = -1;
+ else
+ azRad = 1;
+ }
+
+ azimuth = 180.0 - RadToDeg(Math.Acos(azRad));
+
+ if (hourangle > 0)
+ azimuth = -azimuth;
+ }
+ else
+ {
+ if (latitude > 0)
+ azimuth = 180;
+ else
+ azimuth = 0;
+
+ }
+
+ if (azimuth < 0)
+ azimuth += 360;
+
+ var exoatmElevation = 90.0 - zenith;
+ double refractionCorrection;
+
+ if (exoatmElevation > 85)
+ {
+ refractionCorrection = 0;
+ }
+ else
+ {
+
+ double te = Math.Tan(DegToRad(exoatmElevation));
+
+ if (exoatmElevation > 5)
+ {
+ refractionCorrection = 58.1 / te - 0.07 / (te * te * te) + 0.000086 / (te * te * te * te * te);
+ }
+ else if (exoatmElevation > -0.575)
+ {
+ double step1 = -12.79 + exoatmElevation * 0.711;
+ double step2 = 103.4 + exoatmElevation * step1;
+ double step3 = -518.2 + exoatmElevation * step2;
+
+ refractionCorrection = 1735.0 + exoatmElevation * step3;
+ }
+ else
+ {
+ refractionCorrection = -20.774 / te;
+ }
+
+ refractionCorrection = refractionCorrection / 3600.0;
+ }
+
+ double solarzen = zenith - refractionCorrection;
+
+ altitude = 90.0 - solarzen;
+ }
+
+
+
+ private static double calcSunRadVector(double t)
+ {
+ // ***********************************************************************
+ // Name: calcSunRadVector (not used by sunrise, solarnoon, sunset)
+ // Type: Function
+ // Purpose: calculate the distance to the sun in AU
+ // Arguments:
+ // t : number of Julian centuries since J2000.0
+ // Return value:
+ // sun radius vector in AUs
+ // ***********************************************************************
+
+ double v = calcSunTrueAnomaly(t);
+
+ double e = calcEccentricityEarthOrbit(t);
+
+ return 1.000001018 * (1 - e * e) / (1 + e * Math.Cos(DegToRad(v)));
+ }
+
+ private static double calcSunTrueAnomaly(double t)
+ {
+ // ***********************************************************************
+ // Name: calcSunTrueAnomaly (not used by sunrise, solarnoon, sunset)
+ // Type: Function
+ //Purpose: calculate the true anamoly of the sun
+ // Arguments:
+ // t : number of Julian centuries since J2000.0
+ // Return value:
+ // sun's true anamoly in degrees
+ // ***********************************************************************
+
+ double m = calcGeomMeanAnomalySun(t);
+
+ double c = calcSunEqOfCenter(t);
+
+ return m + c;
+ }
+
+ private static double calcGeomMeanAnomalySun(double t)
+ {
+ // ***********************************************************************
+ // Name: calGeomAnomalySun
+ // Type: Function
+ // Purpose: calculate the Geometric Mean Anomaly of the Sun
+ // Arguments:
+ // t : number of Julian centuries since J2000.0
+ // Return value:
+ // the Geometric Mean Anomaly of the Sun in degrees
+ // ***********************************************************************
+
+ return 357.52911 + t * (35999.05029 - 0.0001537 * t);
+ }
+
+ private static double calcSunEqOfCenter(double t)
+ {
+ // ***********************************************************************
+ // Name: calcSunEqOfCenter
+ // Type: Function
+ // Purpose: calculate the equation of center for the sun
+ // Arguments:
+ // t : number of Julian centuries since J2000.0
+ // Return value:
+ // in degrees
+ // ***********************************************************************
+
+ double m = calcGeomMeanAnomalySun(t);
+
+ double mrad = DegToRad(m);
+ double sinm = Math.Sin(mrad);
+ double sin2m = Math.Sin(mrad + mrad);
+ double sin3m = Math.Sin(mrad + mrad + mrad);
+
+ return sinm * (1.914602 - t * (0.004817 + 0.000014 * t)) + sin2m * (0.019993 - 0.000101 * t) + sin3m * 0.000289;
+ }
+
+ private static double calcEccentricityEarthOrbit(double t)
+ {
+ // ***********************************************************************
+ // Name: calcEccentricityEarthOrbit
+ // Type: Function
+ // Purpose: calculate the eccentricity of earth's orbit
+ // Arguments:
+ // t : number of Julian centuries since J2000.0
+ // Return value:
+ // the unitless eccentricity
+ // ***********************************************************************
+
+ return 0.016708634 - t * (0.000042037 + 0.0000001267 * t);
+ }
+
+
+ private static double calcSunRtAscension(double t)
+ {
+ // ***********************************************************************
+ // Name: calcSunRtAscension (not used by sunrise, solarnoon, sunset)
+ // Type: Function
+ // Purpose: calculate the right ascension of the sun
+ // Arguments:
+ // t : number of Julian centuries since J2000.0
+ // Return value:
+ // sun's right ascension in degrees
+ // ***********************************************************************
+
+ double e = calcObliquityCorrection(t);
+
+ double lambda = calcSunApparentLong(t);
+ double tananum = Math.Cos(DegToRad(e)) * Math.Sin(DegToRad(lambda));
+ double tanadenom = Math.Cos(DegToRad(lambda));
+
+ //original NOAA code using javascript Math.Atan2(y,x) convention:
+ // var alpha = radToDeg(Math.atan2(tananum, tanadenom));
+ // alpha = radToDeg(Application.WorksheetFunction.Atan2(tananum, tanadenom))
+ //
+ //translated using Excel VBA Application.WorksheetFunction.Atan2(x,y) convention:
+
+ return RadToDeg(Math.Atan2(tanadenom, tananum));
+ }
+
+ private static double calcSunDeclination(double t)
+ {
+ // ***********************************************************************
+ // Name: calcSunDeclination
+ // Type: Function
+ // Purpose: calculate the declination of the sun
+ // Arguments:
+ // t : number of Julian centuries since J2000.0
+ // Return value:
+ // sun's declination in degrees
+ // ***********************************************************************
+
+ double e = calcObliquityCorrection(t);
+
+ double lambda = calcSunApparentLong(t);
+ double sint = Math.Sin(DegToRad(e)) * Math.Sin(DegToRad(lambda));
+
+ return RadToDeg(Math.Asin(sint));
+ }
+
+ private static double calcSunApparentLong(double t)
+ {
+ // ***********************************************************************
+ // Name: calcSunApparentLong (not used by sunrise, solarnoon, sunset)
+ // Type: Function
+ // Purpose: calculate the apparent longitude of the sun
+ // Arguments:
+ // t : number of Julian centuries since J2000.0
+ // Return value:
+ // sun's apparent longitude in degrees
+ // ***********************************************************************
+
+ double O = calcSunTrueLong(t);
+
+ double omega = 125.04 - 1934.136 * t;
+
+ return O - 0.00569 - 0.00478 * Math.Sin(DegToRad(omega));
+ }
+
+ private static double calcSunTrueLong(double t)
+ {
+ // ***********************************************************************
+ // Name: calcSunTrueLong
+ // Type: Function
+ // Purpose: calculate the true longitude of the sun
+ // Arguments:
+ // t : number of Julian centuries since J2000.0
+ // Return value:
+ // sun's true longitude in degrees
+ // ***********************************************************************
+
+ double l0 = calcGeomMeanLongSun(t);
+
+ double c = calcSunEqOfCenter(t);
+
+ return l0 + c;
+ }
+
+ private static double calcGeomMeanLongSun(double t)
+ {
+ // ***********************************************************************
+ // Name: calGeomMeanLongSun
+ // Type: Function
+ // Purpose: calculate the Geometric Mean Longitude of the Sun
+ // Arguments:
+ // t : number of Julian centuries since J2000.0
+ // Return value:
+ // the Geometric Mean Longitude of the Sun in degrees
+ // ***********************************************************************
+
+ double l0 = 280.46646 + t * (36000.76983 + 0.0003032 * t);
+
+ do
+ {
+ if (l0 <= 360 && l0 >= 0)
+ break;
+
+ if (l0 > 360)
+ l0 -= 360;
+
+ if (l0 < 0)
+ l0 += 360;
+ } while (true);
+
+ return l0;
+ }
+
+ private static double calcEquationOfTime(double t)
+ {
+ // ***********************************************************************
+ // Name: calcEquationOfTime
+ // Type: Function
+ // Purpose: calculate the difference between true solar time and mean
+ // solar time
+ // Arguments:
+ // t : number of Julian centuries since J2000.0
+ // Return value:
+ // equation of time in minutes of time
+ // ***********************************************************************
+
+ double epsilon = calcObliquityCorrection(t);
+ double l0 = calcGeomMeanLongSun(t);
+ double e = calcEccentricityEarthOrbit(t);
+ double m = calcGeomMeanAnomalySun(t);
+
+ double y = Math.Tan(DegToRad(epsilon) / 2.0);
+ y = y * y;
+
+ double sin2l0 = Math.Sin(2.0 * DegToRad(l0));
+ double sinm = Math.Sin(DegToRad(m));
+ double cos2l0 = Math.Cos(2.0 * DegToRad(l0));
+ double sin4l0 = Math.Sin(4.0 * DegToRad(l0));
+ double sin2m = Math.Sin(2.0 * DegToRad(m));
+
+ double Etime = y * sin2l0 - 2.0 * e * sinm + 4.0 * e * y * sinm * cos2l0 - 0.5 * y * y * sin4l0 - 1.25 * e * e * sin2m;
+
+ return RadToDeg(Etime) * 4.0;
+ }
+
+ private static double calcMeanObliquityOfEcliptic(double t)
+ {
+ // ***********************************************************************
+ // Name: calcMeanObliquityOfEcliptic
+ // Type: Function
+ // Purpose: calculate the mean obliquity of the ecliptic
+ // Arguments:
+ // t : number of Julian centuries since J2000.0
+ // Return value:
+ // mean obliquity in degrees
+ // ***********************************************************************
+
+ double seconds = 21.448 - t * (46.815 + t * (0.00059 - t * (0.001813)));
+
+ return 23.0 + (26.0 + (seconds / 60.0)) / 60.0;
+ }
+
+ private static double calcObliquityCorrection(double t)
+ {
+ // ***********************************************************************
+ // Name: calcObliquityCorrection
+ // Type: Function
+ // Purpose: calculate the corrected obliquity of the ecliptic
+ // Arguments:
+ // t : number of Julian centuries since J2000.0
+ // Return value:
+ // corrected obliquity in degrees
+ // ***********************************************************************
+
+ double e0 = calcMeanObliquityOfEcliptic(t);
+
+ double omega = 125.04 - 1934.136 * t;
+
+ return e0 + 0.00256 * Math.Cos(DegToRad(omega));
+ }
+
+ */
+
+ #endregion
+
+
+
+ // From the NOAA_SolarCalculations_day spreadsheet
+ // https://gml.noaa.gov/grad/solcalc/calcdetails.html
+ #region NOAA_Solar
+
+ private static void CalculateSunPosition(DateTime dateTime, double latitude, double longitude, out double altitude, out double azimuth)
+ {
+ // We will use DateTime.ToOADate() which automatically includes the TZ
+ var zone = 0.0;
+
+ var julianDate = dateTime.ToOADate() + 2415018.5;
+ var julianCentry = (julianDate - 2451545 ) / 36525.0;
+ var geoMeanLongSun = PutIn360Deg(280.46646 + julianCentry * (36000.76983 + julianCentry * 0.0003032));
+ var geoMeanAnomSun = 357.52911 + julianCentry * (35999.05029 - 0.0001537 * julianCentry);
+ var eccEarthOrbit = 0.016708634 - julianCentry * (0.000042037 + 0.0000001267 * julianCentry);
+ var sunEqCentre = Math.Sin(DegToRad(geoMeanAnomSun)) * (1.914602 - julianCentry * (0.004817 + 0.000014 * julianCentry)) + Math.Sin(DegToRad(2 * geoMeanAnomSun)) * (0.019993 - 0.000101 * julianCentry) + Math.Sin(DegToRad(3 * geoMeanAnomSun)) * 0.000289;
+ var sunTrueLong = geoMeanLongSun + sunEqCentre;
+ //var sunTrueAnom = geoMeanAnomSun + sunEqCentre;
+ //var sunRadVector = (1.000001018 * (1 - eccEarthOrbit * eccEarthOrbit)) / (1 + eccEarthOrbit * Math.Cos(DegToRad(sunTrueAnom)));
+ var sunAppLong = sunTrueLong - 0.00569 - 0.00478 * Math.Sin(DegToRad(125.04 - 1934.136 * julianCentry));
+ var meanObliqEcplitic = 23 + (26 + ((21.448 - julianCentry * (46.815 + julianCentry * (0.00059 - julianCentry * 0.001813)))) / 60.0) / 60.0;
+ var obliqCorr = meanObliqEcplitic + 0.00256 * Math.Cos(DegToRad(125.04 - 1934.136 * julianCentry));
+ //var sunRA = RadToDeg(Math.Atan2(Math.Cos(DegToRad(sunAppLong)), Math.Cos(DegToRad(obliqCorr)) * Math.Sin(DegToRad(obliqCorr))));
+ var sunDec = RadToDeg(Math.Asin(Math.Sin(DegToRad(obliqCorr)) * Math.Sin(DegToRad(sunAppLong))));
+ var varY = Math.Tan(DegToRad(obliqCorr / 2.0)) * Math.Tan(DegToRad(obliqCorr / 2.0));
+ var eqOfTime = 4 * RadToDeg(varY * Math.Sin(2 * DegToRad(geoMeanLongSun)) - 2 * eccEarthOrbit * Math.Sin(DegToRad(geoMeanAnomSun)) + 4 * eccEarthOrbit * varY * Math.Sin(DegToRad(geoMeanAnomSun)) * Math.Cos(2 * DegToRad(geoMeanLongSun)) - 0.5 * varY * varY * Math.Sin(4 * DegToRad(geoMeanLongSun)) - 1.25 * eccEarthOrbit * eccEarthOrbit * Math.Sin(2 * DegToRad(geoMeanAnomSun)));
+ //var haSunRise = RadToDeg(Math.Acos(Math.Cos(DegToRad(90.833)) / (Math.Cos(DegToRad(latitude)) * Math.Cos(DegToRad(sunDec))) - Math.Tan(DegToRad(latitude)) * Math.Tan(DegToRad(sunDec))));
+ //var solarNoonLst = (720.0 - 4 * longitude - eqOfTime + zone * 60.0) / 1440.0;
+ //var sunriseTimeLst = solarNoonLst - haSunRise * 4 / 1440.0;
+ //var sunsetTimeLst = solarNoonLst + haSunRise * 4 / 1440.0;
+ //var sunlightDurationMins = 8 * haSunRise;
+ var trueSolarTime = PutInRange(dateTime.TimeOfDay.TotalMinutes + eqOfTime + 4 * longitude - 60 * zone, 1440);
+ var hourAngle = trueSolarTime / 4.0 < 0 ? trueSolarTime / 4.0 + 180 : trueSolarTime / 4.0 - 180;
+ var solarZenithAngle = RadToDeg(Math.Acos(Math.Sin(DegToRad(latitude)) * Math.Sin(DegToRad(sunDec)) + Math.Cos(DegToRad(latitude)) * Math.Cos(DegToRad(sunDec)) * Math.Cos(DegToRad(hourAngle))));
+ var solarElevation = 90 - solarZenithAngle;
+
+ double refraction;
+
+ if (solarElevation > 85)
+ refraction = 0;
+ else if (solarElevation > 5)
+ refraction = 58.1 / Math.Tan(DegToRad(solarElevation)) - 0.07 / Math.Pow(Math.Tan(DegToRad(solarElevation)), 3) + 0.000086 / Math.Pow(Math.Tan(DegToRad(solarElevation)), 5);
+ else if (solarElevation > -0.575)
+ refraction = 1735.0 + solarElevation * (-518.2 + solarElevation * (103.4 + solarElevation * (-12.79 + solarElevation * 0.711)));
+ else
+ refraction = -20.772 / Math.Tan(DegToRad(solarElevation));
+
+ altitude = solarElevation + refraction / 3600.0;
+
+ if (hourAngle > 0)
+ azimuth = PutIn360Deg(DegToRad(Math.Acos(((Math.Sin(DegToRad(latitude)) * Math.Cos(DegToRad(solarZenithAngle))) - Math.Sin(DegToRad(sunDec))) / (Math.Sin(DegToRad(latitude)) * Math.Sin(DegToRad(solarZenithAngle))))) + 180);
+ else
+ azimuth = PutIn360Deg(540 - DegToRad(Math.Acos(((Math.Sin(DegToRad(latitude)) * Math.Cos(DegToRad(solarZenithAngle))) - Math.Sin(DegToRad(sunDec))) / (Math.Cos(DegToRad(latitude)) * Math.Sin(DegToRad(solarZenithAngle))))));
+
+ }
+
+
+ #endregion
+
+
// http://guideving.blogspot.co.uk/2010/08/sun-position-in-c.html
+ #region sun-position-in-c
- public static void CalculateSunPosition(
- DateTime dateTime, double latitude, double longitude, out double altitude, out double azimuth)
+ /*
+ public static void CalculateSunPosition(DateTime dateTime, double latitude, double longitude, out double altitude, out double azimuth)
{
const double Deg2Rad = Math.PI / 180.0;
const double Rad2Deg = 180.0 / Math.PI;
@@ -130,7 +598,7 @@ public static void CalculateSunPosition(
double siderealTime = siderealTimeUT * 15 + longitude;
// Refine to number of days (fractional) to specific time.
- julianDate += dateTime.TimeOfDay.TotalHours / 24.0;
+ julianDate += dateTime.TimeOfDay.TotalDays;
julianCenturies = julianDate / 36525.0;
// Solar Coordinates
@@ -164,9 +632,13 @@ public static void CalculateSunPosition(
hourAngle -= 2 * Math.PI;
}
- altitude = Math.Asin(Math.Sin(latitude * Deg2Rad) *
- Math.Sin(declination) + Math.Cos(latitude * Deg2Rad) *
- Math.Cos(declination) * Math.Cos(hourAngle));
+ altitude = Math.Asin(
+ Math.Sin(latitude * Deg2Rad) *
+ Math.Sin(declination) +
+ Math.Cos(latitude * Deg2Rad) *
+ Math.Cos(declination) *
+ Math.Cos(hourAngle)
+ );
// refraction correction
// work in degrees
@@ -185,10 +657,10 @@ public static void CalculateSunPosition(
}
else if (altitude > -0.575)
{
- double step1 = (-12.79 + altitude * 0.711);
- double step2 = (103.4 + altitude * (step1));
- double step3 = (-518.2 + altitude * (step2));
- refractionCorrection = 1735.0 + altitude * (step3);
+ double step1 = -12.79 + altitude * 0.711;
+ double step2 = 103.4 + altitude * step1;
+ double step3 = -518.2 + altitude * step2;
+ refractionCorrection = 1735.0 + altitude * step3;
}
else
{
@@ -217,17 +689,17 @@ public static void CalculateSunPosition(
}
azimuth *= Rad2Deg;
- //altitude = altitude * Rad2Deg;
}
- /*!
- * \brief Corrects an angle.
- *
- * \param angleInRadians An angle expressed in radians.
- * \return An angle in the range 0 to 2*PI.
- *
- * http://guideving.blogspot.co.uk/2010/08/sun-position-in-c.html
- */
+
+ //
+ // \brief Corrects an angle.
+ //
+ // \param angleInRadians An angle expressed in radians.
+ // \return An angle in the range 0 to 2*PI.
+ //
+ // http://guideving.blogspot.co.uk/2010/08/sun-position-in-c.html
+ //
private static double CorrectAngle(double angleInRadians)
{
@@ -243,8 +715,12 @@ private static double CorrectAngle(double angleInRadians)
return angleInRadians;
}
+ */
+
+ #endregion
- public static double CalcSunDistance(DateTime dDate, DateTime dEpoch)
+
+ private static double CalcSunDistance(DateTime dDate, DateTime dEpoch)
{
const double fAcc = 0.0000001;
double fD = GetDaysBetween(dDate, dEpoch);
@@ -268,7 +744,7 @@ public static double CalcSunDistance(DateTime dDate, DateTime dEpoch)
return fDistance/149597871.0;
}
- public static double GetSunEarthEcc(DateTime dDate, bool b0Epoch)
+ private static double GetSunEarthEcc(DateTime dDate, bool b0Epoch)
{
//Returns the eccentricity of Earth's orbit around the sun for the specified date
@@ -283,7 +759,7 @@ public static double GetSunEarthEcc(DateTime dDate, bool b0Epoch)
}
/*
- public static double GetEarthObliquity(DateTime dDate, bool b0Epoch)
+ private static double GetEarthObliquity(DateTime dDate, bool b0Epoch)
{
//Returns the obliquity of Earth's orbit around the sun for the specified date
@@ -300,7 +776,7 @@ public static double GetEarthObliquity(DateTime dDate, bool b0Epoch)
}
*/
- public static double CalcEccentricAnomaly(double fEGuess, double fMA, double fEcc, double fAcc)
+ private static double CalcEccentricAnomaly(double fEGuess, double fMA, double fEcc, double fAcc)
{
//Calc Eccentric Anomaly to specified accuracy
double fE;
@@ -338,7 +814,7 @@ public static long GetDaysBetween(DateTime dDate, DateTime dSecDate)
return (long) Math.Floor(fDays);
}
- public static double GetJulianDay(DateTime dDate, int iZone)
+ private static double GetJulianDay(DateTime dDate, int iZone)
{
double iGreg;
double fC;
@@ -375,7 +851,7 @@ public static double GetJulianDay(DateTime dDate, int iZone)
return fJD;
}
- public static DateTime CalcUTFromZT(DateTime dDate, int iZone)
+ private static DateTime CalcUTFromZT(DateTime dDate, int iZone)
{
if (iZone >= 0)
{
@@ -385,7 +861,7 @@ public static DateTime CalcUTFromZT(DateTime dDate, int iZone)
return dDate.AddHours(Math.Abs(iZone));
}
- public static double GetSolarMEL(DateTime dDate, bool b0Epoch)
+ private static double GetSolarMEL(DateTime dDate, bool b0Epoch)
{
//Returns the Sun's Mean Ecliptic Longitude for the specified date
@@ -401,7 +877,7 @@ public static double GetSolarMEL(DateTime dDate, bool b0Epoch)
return fLong;
}
- public static double GetSolarPerigeeLong(DateTime dDate, bool b0Epoch)
+ private static double GetSolarPerigeeLong(DateTime dDate, bool b0Epoch)
{
//Returns the Sun's Perigee Longitude for the specified date
@@ -418,17 +894,20 @@ public static double GetSolarPerigeeLong(DateTime dDate, bool b0Epoch)
return fLong;
}
- public static double PutIn360Deg(double pfDeg)
+ private static double PutIn360Deg(double pfDeg)
{
- while (pfDeg >= 360)
- {
- pfDeg -= 360;
- }
- while (pfDeg < 0)
- {
- pfDeg += 360;
- }
- return pfDeg;
+ return PutInRange(pfDeg, 360);
+ }
+
+ private static double PutInRange(double val, double range)
+ {
+ while (val >= range)
+ val -= range;
+
+ while (val < 0)
+ val += range;
+
+ return val;
}
//public static int cSunrise = 1;
diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs
index 31e1701a..8b3d3af5 100644
--- a/CumulusMX/Cumulus.cs
+++ b/CumulusMX/Cumulus.cs
@@ -10144,6 +10144,10 @@ public async void CustomHttpSecondsUpdate()
updatingCustomHttpSeconds = false;
}
}
+ else
+ {
+ LogDebugMessage("CustomHttpSeconds: Query already in progress, skipping this attempt");
+ }
}
public async void CustomHttpMinutesUpdate()
diff --git a/CumulusMX/HttpStationEcowitt.cs b/CumulusMX/HttpStationEcowitt.cs
index 596dc3e0..be790126 100644
--- a/CumulusMX/HttpStationEcowitt.cs
+++ b/CumulusMX/HttpStationEcowitt.cs
@@ -289,8 +289,8 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null)
if (recDate.Minute != lastMinute)
{
- // at start-up or every 10 minutes trigger output of uptime
- if ((recDate.Minute % 10) == 0 || lastMinute == -1 && data["runtime"] != null)
+ // at start-up or every 20 minutes trigger output of uptime
+ if ((recDate.Minute % 20) == 0 || lastMinute == -1 && data["runtime"] != null)
{
var runtime = Convert.ToInt32(data["runtime"]);
var uptime = TimeSpan.FromSeconds(runtime);
@@ -333,7 +333,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null)
if (gust == null || dir == null || spd == null)
{
- cumulus.LogMessage($"ProcessData: Error, missing wind data");
+ cumulus.LogDebugMessage($"ProcessData: Error, missing wind data");
}
else
{
@@ -374,7 +374,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null)
{
if (thisHum == null)
{
- cumulus.LogMessage("ProcessData: Error, missing outdoor humidity");
+ cumulus.LogDebugMessage("ProcessData: Error, missing outdoor humidity");
}
else
{
@@ -400,7 +400,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null)
if (press == null)
{
- cumulus.LogMessage($"ProcessData: Error, missing baro pressure");
+ cumulus.LogDebugMessage($"ProcessData: Error, missing baro pressure");
}
else
{
@@ -425,7 +425,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null)
if (temp == null)
{
- cumulus.LogMessage($"ProcessData: Error, missing indoor temp");
+ cumulus.LogDebugMessage($"ProcessData: Error, missing indoor temp");
}
else
{
@@ -448,7 +448,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null)
{
if (thisTemp == null)
{
- cumulus.LogMessage($"ProcessData: Error, missing outdoor temp");
+ cumulus.LogDebugMessage($"ProcessData: Error, missing outdoor temp");
}
else
{
@@ -514,7 +514,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null)
if (rain == null)
{
- cumulus.LogMessage($"ProcessData: Error, missing rainfall");
+ cumulus.LogDebugMessage($"ProcessData: Error, missing rainfall");
}
else
{
diff --git a/CumulusMX/Utils.cs b/CumulusMX/Utils.cs
index fe26319e..db3c7ee6 100644
--- a/CumulusMX/Utils.cs
+++ b/CumulusMX/Utils.cs
@@ -127,16 +127,37 @@ public static string GetLogFileSeparator(string line, string defSep)
public static IPAddress GetIpWithDefaultGateway()
{
- return NetworkInterface
- .GetAllNetworkInterfaces()
- .Where(n => n.OperationalStatus == OperationalStatus.Up)
- .Where(n => n.NetworkInterfaceType != NetworkInterfaceType.Loopback)
- .Where(n => n.GetIPProperties().GatewayAddresses.Count > 0)
- .SelectMany(n => n.GetIPProperties().UnicastAddresses)
- .Where(n => n.Address.AddressFamily == AddressFamily.InterNetwork)
- .Where(n => n.IPv4Mask.ToString() != "0.0.0.0")
- .Select(g => g.Address)
- .First();
+ try
+ {
+ // First try and find the IPv4 address that also has the default gateway
+ return NetworkInterface
+ .GetAllNetworkInterfaces()
+ .Where(n => n.OperationalStatus == OperationalStatus.Up)
+ .Where(n => n.NetworkInterfaceType != NetworkInterfaceType.Loopback)
+ .Where(n => n.GetIPProperties().GatewayAddresses.Count > 0)
+ .SelectMany(n => n.GetIPProperties().UnicastAddresses)
+ .Where(n => n.Address.AddressFamily == AddressFamily.InterNetwork)
+ .Where(n => n.IPv4Mask.ToString() != "0.0.0.0")
+ .Select(g => g.Address)
+ .First();
+ }
+ catch {}
+ try
+ {
+ // next just return the first IPv4 address found
+ var host = Dns.GetHostEntry(Dns.GetHostName());
+ foreach (var ip in host.AddressList)
+ {
+ if (ip.AddressFamily == AddressFamily.InterNetwork)
+ {
+ return ip;
+ }
+ }
+ }
+ catch {}
+
+ // finally, give up and just return a 0.0.0.0 IP!
+ return IPAddress.Any;
}
}
}
diff --git a/CumulusMX/WeatherStation.cs b/CumulusMX/WeatherStation.cs
index c9087114..bbe240ac 100644
--- a/CumulusMX/WeatherStation.cs
+++ b/CumulusMX/WeatherStation.cs
@@ -1619,9 +1619,10 @@ private void MinuteChanged(DateTime now)
{
CheckForDataStopped();
+ CurrentSolarMax = AstroLib.SolarMax(now, cumulus.Longitude, cumulus.Latitude, AltitudeM(cumulus.Altitude), out SolarElevation, cumulus.SolarOptions);
+
if (!DataStopped)
{
- CurrentSolarMax = AstroLib.SolarMax(now, cumulus.Longitude, cumulus.Latitude, AltitudeM(cumulus.Altitude), out SolarElevation, cumulus.SolarOptions);
if (((Pressure > 0) && TempReadyToPlot && WindReadyToPlot) || cumulus.StationOptions.NoSensorCheck)
{
// increment wind run by one minute's worth of average speed
@@ -1677,7 +1678,7 @@ private void MinuteChanged(DateTime now)
AddRecentDataWithAq(now, WindAverage, RecentMaxGust, WindLatest, Bearing, AvgBearing, OutdoorTemperature, WindChill, OutdoorDewpoint, HeatIndex, OutdoorHumidity,
Pressure, RainToday, SolarRad, UV, Raincounter, FeelsLike, Humidex, ApparentTemperature, IndoorTemperature, IndoorHumidity, CurrentSolarMax, RainRate);
DoTrendValues(now);
- DoPressTrend("Pressure trend");
+ DoPressTrend("Enable Cumulus pressure trend");
// calculate ET just before the hour so it is included in the correct day at roll over - only affects 9am met days really
if (cumulus.StationOptions.CalculatedET && now.Minute == 59)
diff --git a/Updates.txt b/Updates.txt
index 40c38ad2..7f5aee2b 100644
--- a/Updates.txt
+++ b/Updates.txt
@@ -2,12 +2,14 @@
——————————————
- Fix: Cloud base being set to large value at start-up
- Fix: Reading lightning distance from today.ini
+- Fix: Potential crash obtaining the local IP address
- New: Adds a PWS Simulator station type for testing or trial purposes
- Change: The "live" dashboard screens now refresh whenever new data is received, or every five seconds
- Change: The "Speed for average calc" station option has been moved from Common Options to Common Options | Advanced Options
- Change: The Ecowitt stations now force the "Speed for average calc" option to be enabled at start-up
+- Change: Slightly improved solar position calculations
3.16.1 - b3183
From 42211f07bee0ee68797958466634966cb2d008a5 Mon Sep 17 00:00:00 2001
From: Mark Crossley <1196094+mcrossley@users.noreply.github.com>
Date: Mon, 23 May 2022 09:17:24 +0100
Subject: [PATCH 5/5] Minor code tidy
---
CumulusMX/DataEditor.cs | 4 ++--
CumulusMX/HttpStationAmbient.cs | 22 ++++++++++++----------
2 files changed, 14 insertions(+), 12 deletions(-)
diff --git a/CumulusMX/DataEditor.cs b/CumulusMX/DataEditor.cs
index cd2a56cb..4d833fc8 100644
--- a/CumulusMX/DataEditor.cs
+++ b/CumulusMX/DataEditor.cs
@@ -12,7 +12,7 @@ namespace CumulusMX
internal class DataEditor
{
private WeatherStation station;
- private Cumulus cumulus;
+ private readonly Cumulus cumulus;
private WebTags webtags;
private readonly List hourRainLog = new List();
@@ -1758,7 +1758,7 @@ internal string GetMonthlyRecDayFile()
var isDryNow = false;
var thisDateDry = DateTime.MinValue;
var thisDateWet = DateTime.MinValue;
- var monthOffset = 0;
+ int monthOffset;
var firstEntry = true;
var json = new StringBuilder("{", 25500);
diff --git a/CumulusMX/HttpStationAmbient.cs b/CumulusMX/HttpStationAmbient.cs
index 837da487..ebc73205 100644
--- a/CumulusMX/HttpStationAmbient.cs
+++ b/CumulusMX/HttpStationAmbient.cs
@@ -692,22 +692,23 @@ private void ProcessAirQuality(NameValueCollection data, WeatherStation station)
}
}
+
+ /*
+ * Not yet used
private void ProcessCo2(NameValueCollection data, WeatherStation station)
{
// co2 - [int, ppm]
// co2_in - [int, ppm]
// co2_in_24h - [float, ppm]
- /*
- if (data["co2_in"] != null)
- {
- station.CO2 = Convert.ToInt32(data["co2_in"], CultureInfo.InvariantCulture);
- }
- if (data["co2_in_24"] != null)
- {
- station.CO2_24h = Convert.ToInt32(data["co2_in_24"], CultureInfo.InvariantCulture);
- }
- */
+ //if (data["co2_in"] != null)
+ //{
+ // station.CO2 = Convert.ToInt32(data["co2_in"], CultureInfo.InvariantCulture);
+ //}
+ //if (data["co2_in_24"] != null)
+ //{
+ // station.CO2_24h = Convert.ToInt32(data["co2_in_24"], CultureInfo.InvariantCulture);
+ //}
// From FOSKplugin
// co2lvl
@@ -738,6 +739,7 @@ private void ProcessCo2(NameValueCollection data, WeatherStation station)
station.CO2_pm10_24h = Convert.ToDouble(data["pm10_AQIlvl_24h_co2"], CultureInfo.InvariantCulture);
}
}
+ */
private void ProcessLightning(NameValueCollection data, WeatherStation station)
{