Skip to content

Commit

Permalink
Merge pull request #1123 from doingnz/bug-SwitchingAnemometer-Speed
Browse files Browse the repository at this point in the history
Fix speed calculation in SwitchingAnemometer
  • Loading branch information
ctacke authored Feb 14, 2025
2 parents 25f7207 + 546f50d commit 42a8b64
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,23 @@ public class SwitchingAnemometer : PollingSensorBase<Speed>, IAnemometer, IDispo
/// <summary>
/// Time to wait if no events come in to register a zero speed wind
/// </summary>
/// <remarks>
/// This value determines the minimum wind speed that can be measured.
/// i.e. minimum speed is KmhPerSwitchPerSecond / NoWindTimeout(seconds)
/// e.g. (2.4 km/hr/s) / (4s) = 600 m/s
/// <see cref="KmhPerSwitchPerSecond"/>
/// </remarks>
public TimeSpan NoWindTimeout { get; set; } = TimeSpan.FromSeconds(4);

/// <summary>
/// Define the theoretical maximum speed that can be measured. Higher speeds are ignored.
/// </summary>
/// <remarks>
/// Category 5 Hurricane is > 300 km/hr.
/// </remarks>
public Speed? MaxSpeed { get; set; } = new Speed(500, SU.KilometersPerHour);


/// <summary>
/// Time to capture samples for a one time Read if IsSampling is false
/// </summary>
Expand All @@ -49,7 +64,7 @@ public int SampleCount
/// once per second. Used to calculate the wind speed based on the time
/// duration between switch events. Default is `2.4kmh`.
/// </summary>
public float KmhPerSwitchPerSecond { get; set; } = 2.4f;
public double KmhPerSwitchPerSecond { get; set; } = 2.4;

private readonly IDigitalInterruptPort inputPort;
private bool running = false;
Expand All @@ -70,6 +85,10 @@ public int SampleCount
/// on the device.
/// </summary>
/// <param name="digitalInputPin"></param>
/// <remarks>
/// Debounce will limit upper speed to approximately half of (2.4/0.002) = 1200 km/hr.
/// i.e. +/- 600 km/hr assuming events can be raised that quickly.
/// </remarks>
public SwitchingAnemometer(IPin digitalInputPin)
: this(digitalInputPin.CreateDigitalInterruptPort(InterruptMode.EdgeFalling,
ResistorMode.InternalPullUp,
Expand Down Expand Up @@ -155,28 +174,45 @@ protected override async Task<Speed> ReadSensor()

lock (samples)
{
if (samples?.Count > 0 && (DateTime.UtcNow - samples?.Peek().New.Time > NoWindTimeout))
int count = 0;

if (samples?.Count > 0 && samples?.Peek().Delta > NoWindTimeout)
{ //we've exceeded the no wind interval time
samples?.Clear(); //will force a zero reading
return new Speed(0);
}
// if we've reached our sample count
else if (samples?.Count >= SampleCount)

// do we have enough samples to calculate a result?
if (samples?.Count >= SampleCount)
{
float speedSum = 0f;
double speedSum = 0f;

// sum up the speeds
foreach (var sample in samples)
{ // skip the first (old will be null)
if (sample.Old is { } old)
{
// Check delta is not null and reasonable. (0 ms would be infinite speed. What is a reasonable maximum speed?)
if (sample.Delta is { Milliseconds: > 0 } delta)
{
speedSum += SwitchIntervalToKmh(sample.New.Time - old.Time);
double speed = SwitchIntervalToKmh(delta);

// skip speeds that are unreasonably high
if (MaxSpeed?.KilometersPerHour >= speed )
{
speedSum += speed;
count++;

//Resolver.Log.Info($"count {count} : delta={delta} speed={speed:N4} speedSum={speedSum:N4} sample={sample} ");
}
}
}

// average the speeds
float oversampledSpeed = speedSum / (samples.Count - 1);

return new Speed(oversampledSpeed, SU.KilometersPerHour);
// do we have enough samples to report an observation?
if (count >= SampleCount)
{
// average the speeds
double oversampledSpeed = speedSum / count;
return new Speed(oversampledSpeed, SU.KilometersPerHour);
}
}
}

Expand All @@ -189,9 +225,9 @@ protected override async Task<Speed> ReadSensor()
/// </summary>
/// <param name="interval">The interval between signals</param>
/// <returns></returns>
protected float SwitchIntervalToKmh(TimeSpan interval)
protected double SwitchIntervalToKmh(TimeSpan interval)
{
return KmhPerSwitchPerSecond / (float)interval.TotalSeconds;
return KmhPerSwitchPerSecond / (double)interval.TotalSeconds;
}

///<inheritdoc/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,33 @@
using Meadow;
using System.IO;
using Meadow;
using Meadow.Devices;
using Meadow.Foundation.Sensors.Weather;
using System.Threading.Tasks;
using Meadow.Hardware;
using Meadow.Units;

namespace MeadowApp
{
public class MeadowApp : App<F7FeatherV2>
public class MeadowApp : App<F7CoreComputeV2>
{
//<!=SNIP=>

SwitchingAnemometer anemometer;
private SwitchingAnemometer anemometer;

public override Task Initialize()
{
Resolver.Log.Info("Initialize...");

anemometer = new SwitchingAnemometer(Device.Pins.A01);

// Uncomment to test SwitchingAnemometer implementation.
// Assumes external wire connected between a PWM capable output and the above configured
// SwitchingAnemometer input.
//double speed = 24; // km/hr
//double frequency = speed / (anemometer.KmhPerSwitchPerSecond);
//IPwmPort pwm = Device.CreatePwmPort(Device.Pins.D20, new Frequency(frequency, Frequency.UnitType.Hertz));
//pwm.Start();

//==== classic events example
anemometer.Updated += (sender, result) =>
{
Expand Down

0 comments on commit 42a8b64

Please sign in to comment.