317 lines
10 KiB
C#

using Assets.Scripts.Devices.Ant.Interfaces;
using Assets.Scripts.Devices.Ant.Messages;
using Assets.Scripts.Devices.Ant.Pages;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
namespace Assets.Scripts.Devices.Ant
{
/// <summary>
/// 功率计
/// </summary>
public class PowerDevice : AbstractAntDevice, IPowerDevice //, ICadenceDevice
{
private DateTime _lastEventChange = DateTime.UtcNow;
//private byte _previousEventCount;
private readonly TorquePowerDataCalculator _torquePowerDataCalculator = new TorquePowerDataCalculator();
private readonly CrankTorqueFrequencyData _crankTorqueFrequency = new CrankTorqueFrequencyData();
private System.Timers.Timer timer;
//List<IPageHandler> pageHandlers;
public PowerDevice(string id)
: base(id, "Ant+ Power", racerSportType.Biking, SensorType.Power)
{
Priority = 2;
//pageHandlers = new List<IPageHandler>();
//pageHandlers.Add(new ManufacturerDataPageHandler());
pageHandlers.Add(new PowerOnlyPageHandler());
pageHandlers.Add(new CrankPowerPageHandler());
pageHandlers.Add(new CtfPageHandler());
pageHandlers.Add(new WheelPowerPageHandler());
pageHandlers.Add(new CalibrationPageHandler());
this.StateChange = (state) =>
{
if (state == DeviceState.Connected)
{
Calibrate();
}
else if (state == DeviceState.Disconnected)
{
count = 0;
if (timer != null)
{
timer.Stop();
timer = null;
DoCalibrationFailure();
}
}
};
}
private int _Power;
public int Power
{
get
{
return _Power;
}
set
{
_Power = DeviceValueFilter.Power(value);
}
}
private int _Cadence;
public int Cadence
{
get
{
return _Cadence;
}
set
{
_Cadence = DeviceValueFilter.Cadence(value);
}
}
public DateTime LastCalibration { get; protected set; }
public int? Offset { get; set; }
public AutoZeroStatus AutoZeroStatus { get; private set; }
public bool IsCalibrating { get; set; }
public bool IsCalibrationReady { get; set; }
public bool CalibrationCompleted { get; set; }
protected override AntChannelProfile getDefaultSearchProfile()
{
return new AntChannelProfile()
{
rfOffset = 57,
transType = 0,
deviceType = 11,
deviceNumber = 0,
messagePeriod = 8182,
pairingEnabled = false,
};
}
//List<int> list = new List<int>();
public override void handleChannelResponse(ANT_Managed_Library.ANT_Response response)
{
//if (!list.Contains(response.messageContents[1]))
//{
// list.Add(response.messageContents[1]);
// Console.WriteLine(response.messageContents[1]);
//}
//Console.WriteLine(string.Join(",", response.messageContents));
if (response.responseID == (byte)ANT_Managed_Library.ANT_ReferenceLibrary.ANTMessageID.BROADCAST_DATA_0x4E)
{
var pageNumber = response.messageContents[1];
foreach (var handler in pageHandlers)
{
if (handler.CanHandle(pageNumber))
{
handler.Handle(response.messageContents.Skip(1).ToArray(), this);
}
}
}
//System.IO.File.AppendAllText(System.Environment.CurrentDirectory + "\\data.txt", string.Join(",", response.messageContents) + ";" + Power+"\r\n");
//Console.WriteLine();
}
internal void DoCalibrationSuccess(int? offset, AutoZeroStatus autoZeroStatus)
{
this.AutoZeroStatus = autoZeroStatus;
this.LastCalibration = DateTime.UtcNow;
if (this.Offset.HasValue == false)
{
this.Offset = offset;
//this.Offset = 570;
}
this.IsCalibrating = false;
this.CalibrationCompleted = true;
//Trace.WriteLine($"校准成功:{ offset }");
}
internal void DoCalibrationFailure()
{
this.IsCalibrating = false;
this.CalibrationCompleted = false;
}
/// <summary>
/// 发送校准命令
/// </summary>
public void Calibrate()
{
if (this.IsCalibrating)
return;
this.IsCalibrating = true;
var channelId = GetChannelId();
base.SendMessage(new ManualCalibrate(channelId));
if (timer == null)
{
timer = new System.Timers.Timer(10000);
timer.Elapsed += Timer_Elapsed;
timer.Start();
}
}
int count = 0;
/// <summary>
/// 检查是否已自动校准,自动发送3次
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
if (count > 3)
{
timer.Stop();
return;
}
if (this.CalibrationCompleted)
{
timer.Stop();
return;
}
count++;
this.IsCalibrating = false;
this.Calibrate();
}
}
public class TorquePowerDataCalculator
{
private DateTime _lastEventChange = DateTime.UtcNow;
private byte _previousEventCount;
private ushort _previousPeriod;
private ushort _previousAccumulatedTorque;
public int Power { get; private set; }
public int Rpm { get; private set; }
public DateTime LastUpdatedTime { get; private set; }
public TorquePowerDataCalculator()
{
this.LastUpdatedTime = DateTime.MinValue;
}
public void Update(byte eventCount, ushort period, ushort accumulatedTorque)
{
byte difference1 = ByteExtensions.GetDifference(eventCount, this._previousEventCount);
ushort difference2 = UshortExtensions.GetDifference(period, this._previousPeriod);
if ((int)difference1 > 0)
this._lastEventChange = DateTime.UtcNow;
if (this.PeriodRolloverPossible() || this.IsNotMovingDueToNoEventChanges() || (int)difference2 == 0 && (int)difference1 > 0)
{
this.Power = 0;
this.Rpm = 0;
}
else if ((int)difference1 > 0)
{
this.Power = (int)(402.123859659494 * ((double)UshortExtensions.GetDifference(accumulatedTorque, this._previousAccumulatedTorque) / (double)difference2));
this.Rpm = 122880 * (int)difference1 / (int)difference2;
}
this.LastUpdatedTime = DateTime.UtcNow;
this._previousEventCount = eventCount;
this._previousPeriod = period;
this._previousAccumulatedTorque = accumulatedTorque;
}
private bool PeriodRolloverPossible()
{
return DateTime.UtcNow - this.LastUpdatedTime > TimeSpan.FromSeconds(32.0);
}
private bool IsNotMovingDueToNoEventChanges()
{
return this._lastEventChange.AddSeconds(3.0) <= DateTime.UtcNow;
}
}
public class CrankTorqueFrequencyData
{
private byte _previousEventCount;
private ushort _previousTimestamp;
private ushort _previousTorqueTicksStamp;
private int _noEventCount;
public int? Power { get; private set; }
public int Cadence { get; private set; }
public DateTime LastUpdatedTime { get; set; }
public CrankTorqueFrequencyData()
{
this.LastUpdatedTime = DateTime.MinValue;
}
public void Update(byte eventCount, ushort timestamp, ushort torqueTicksStamp, ushort slope, int offset)
{
byte difference1 = ByteExtensions.GetDifference(eventCount, this._previousEventCount);
ushort difference2 = UshortExtensions.GetDifference(timestamp, this._previousTimestamp);
ushort difference3 = UshortExtensions.GetDifference(torqueTicksStamp, this._previousTorqueTicksStamp);
this._previousEventCount = eventCount;
this._previousTimestamp = timestamp;
this._previousTorqueTicksStamp = torqueTicksStamp;
if (this.HasMoreThanOneRolloverOccured())
{
this.LastUpdatedTime = DateTime.UtcNow;
this.Power = new int?(0);
this.Cadence = 0;
}
else
{
this.LastUpdatedTime = DateTime.UtcNow;
if ((int)difference1 > 0)
{
this._noEventCount = 0;
double num = (double)difference2 / (double)difference1 * 0.0005;
if ((int)difference2 == 0)
return;
this.Cadence = (int)Math.Round(60.0 / num);
if ((int)difference3 == 0)
this.Power = new int?();
else
this.Power = new int?((int)((1.0 / ((double)difference2 * 0.0005 / (double)difference3) - (double)offset) / ((double)slope / 10.0) * (double)this.Cadence * 3.14159265358979 / 30.0));
}
else
{
int num = this._noEventCount + 1;
this._noEventCount = num;
if (num < 12)
return;
this.Power = new int?(0);
this.Cadence = 0;
}
}
}
private bool HasMoreThanOneRolloverOccured()
{
return DateTime.UtcNow - this.LastUpdatedTime > TimeSpan.FromSeconds(32.7);
}
}
}