You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
406 lines
12 KiB
406 lines
12 KiB
using System; |
|
using System.Collections.Generic; |
|
using System.Diagnostics; |
|
using System.IO.Ports; |
|
|
|
namespace ArducopterConfigurator.PresentationModels |
|
{ |
|
/// <summary> |
|
/// View Model for the sensors monitor and calibration |
|
/// </summary> |
|
/// <remarks> |
|
/// This tab is for the monitoring and calibration of the Gyro/Accel sensors |
|
/// |
|
/// When it is activated, it retrieves the current sensor calibration values |
|
/// Then, it requests the constant stream of sensor readings in order to provide |
|
/// a live view. |
|
/// |
|
/// The user can change the calibration offsets, and upload the new values. |
|
/// When this happens, the model tells the APM to cease sending the realtime updates, |
|
/// then sends the new offset values, then resumes the updates |
|
/// |
|
/// There is a command to automatically fill the sensor offset values, from the |
|
/// current values of the sensors. The user would do this when the copter is |
|
/// sitting still and level |
|
/// |
|
/// When the VM is deactivated, the model tells the APM to cease sending the realtime updates |
|
/// </remarks> |
|
public class SensorsVm : NotifyProperyChangedBase, IPresentationModel |
|
{ |
|
|
|
private const string CALIB_REFRESH = "J"; |
|
private const string CALIB_UPDATE = "I"; |
|
private const string SEND_SENSOR_DATA = "S"; |
|
private const string STOP_UPDATES = "X"; |
|
|
|
private bool waitingForCalibData; |
|
|
|
public SensorsVm() |
|
{ |
|
RefreshCalibrationOffsetsCommand = new DelegateCommand(_ => RefreshCalibValues()); |
|
UpdateCalibrationOffsetsCommand = new DelegateCommand(_ => UpdateCalibValues()); |
|
CalculateCalibrationOffsetsCommand = new DelegateCommand(_ => CalcCalibValues()); |
|
|
|
PropertyChanged += ((sender, e) => |
|
{ |
|
IsArmed = !(MotorFront == 1040 && MotorRear == 1040 && MotorLeft == 1040 && MotorRight == 1040); |
|
}); |
|
} |
|
|
|
public void RefreshCalibValues() |
|
{ |
|
sendString(STOP_UPDATES); |
|
waitingForCalibData = true; |
|
sendString(CALIB_REFRESH); |
|
} |
|
|
|
public void UpdateCalibValues() |
|
{ |
|
sendString(PropertyHelper.ComposePropValuesWithCommand(this, _calibrationPropsInUpdateOrder, CALIB_UPDATE)); |
|
sendString(SEND_SENSOR_DATA); |
|
} |
|
|
|
public void CalcCalibValues() |
|
{ |
|
AccelRollOffset = AccelRollOffset - AccelRoll; |
|
AccelPitchOffset = AccelPitchOffset - AccelPitch; |
|
//AccelZOffset = AccelZOffset - AccelZ; |
|
FirePropertyChanged("AccelRollOffset"); |
|
FirePropertyChanged("AccelPitchOffset"); |
|
//FirePropertyChanged("AccelZOffset"); |
|
} |
|
|
|
public string Name |
|
{ |
|
get { return "Sensor Data"; } |
|
} |
|
|
|
|
|
|
|
|
|
private readonly string[] _sensorPropsInUpdateOrder = new[] |
|
{ |
|
"LoopTime", |
|
"GyroRoll", |
|
"GyroPitch", |
|
"GyroYaw", |
|
"Unused", // Throttle |
|
"ControlRoll", // control roll |
|
"ControlPitch", // control pitch |
|
"ControlYaw", // control yaw |
|
"MotorFront", |
|
"MotorRear", |
|
"MotorRight", |
|
"MotorLeft", |
|
"AccelRoll", |
|
"AccelPitch", |
|
"AccelZ", |
|
"CompassHeading", // AP_Compass.heading |
|
"UnusedFloat", // AP_Compass.heading_x |
|
"UnusedFloat", // AP_Compass.heading_y |
|
"Unused", // AP_Compass.mag_x |
|
"Unused", // AP_Compass.mag_y |
|
"Unused", // AP_Compass.mag_z |
|
|
|
}; |
|
|
|
private readonly string[] _calibrationPropsInUpdateOrder = new[] |
|
{ |
|
"GyroRollOffset", |
|
"GyroPitchOffset", |
|
"GyroYawOffset", |
|
"AccelRollOffset", |
|
"AccelPitchOffset", |
|
"AccelZOffset" |
|
}; |
|
|
|
|
|
private void sendString(string str) |
|
{ |
|
if (sendTextToApm != null) |
|
sendTextToApm(this, new sendTextToApmEventArgs(str)); |
|
} |
|
|
|
public void Activate() |
|
{ |
|
// Get the calib. data first |
|
waitingForCalibData = true; |
|
sendString(CALIB_REFRESH); |
|
} |
|
|
|
public void DeActivate() |
|
{ |
|
sendString(STOP_UPDATES); |
|
} |
|
|
|
public void handleLineOfText(string strRx) |
|
{ |
|
if ( waitingForCalibData) |
|
{ |
|
PropertyHelper.PopulatePropsFromUpdate(this, _calibrationPropsInUpdateOrder, strRx, true); |
|
waitingForCalibData = false; |
|
sendString(SEND_SENSOR_DATA); |
|
} |
|
else |
|
{ |
|
PropertyHelper.PopulatePropsFromUpdate(this, _sensorPropsInUpdateOrder, strRx, false); |
|
} |
|
} |
|
|
|
public event EventHandler updatedByApm; |
|
|
|
public event EventHandler<sendTextToApmEventArgs> sendTextToApm; |
|
|
|
public ICommand RefreshCalibrationOffsetsCommand { get; private set; } |
|
|
|
public ICommand UpdateCalibrationOffsetsCommand { get; private set; } |
|
|
|
public ICommand CalculateCalibrationOffsetsCommand { get; private set; } |
|
|
|
|
|
#region Calibration Properties |
|
|
|
public float GyroRollOffset { get; set; } |
|
public float GyroPitchOffset { get; set; } |
|
public float GyroYawOffset { get; set; } |
|
|
|
public float AccelRollOffset { get; set; } |
|
public float AccelPitchOffset { get; set; } |
|
public float AccelZOffset { get; set; } |
|
|
|
|
|
#endregion |
|
|
|
|
|
private bool _isArmed; |
|
|
|
|
|
/// <summary> |
|
/// Whether the Arducopter is Armed or not |
|
/// </summary> |
|
/// <remarks> |
|
/// We don't get this information directly, but we can infer it if all motors are |
|
/// at 1040 then the thing is NOT armed. |
|
/// </remarks> |
|
public bool IsArmed |
|
{ |
|
get { return _isArmed; } |
|
set |
|
{ |
|
if (_isArmed == value) return; |
|
_isArmed = value; |
|
FirePropertyChanged("IsArmed"); |
|
} |
|
} |
|
|
|
|
|
#region Sensor Properties |
|
|
|
private int _loopTime; |
|
public int LoopTime |
|
{ |
|
get { return _loopTime; } |
|
set |
|
{ |
|
if (_loopTime == value) return; |
|
_loopTime = value; |
|
FirePropertyChanged("LoopTime"); |
|
} |
|
} |
|
|
|
|
|
private int motorFront; |
|
public int MotorFront |
|
{ |
|
get { return motorFront; } |
|
set { |
|
if (motorFront == value) return; |
|
motorFront = value; |
|
FirePropertyChanged("MotorFront"); |
|
} |
|
} |
|
|
|
private int motorRear; |
|
public int MotorRear |
|
{ |
|
get { return motorRear; } |
|
set |
|
{ |
|
if (motorRear == value) return; |
|
motorRear = value; |
|
FirePropertyChanged("MotorRear"); |
|
} |
|
} |
|
|
|
private int motorLeft; |
|
public int MotorLeft |
|
{ |
|
get { return motorLeft; } |
|
set |
|
{ |
|
if (motorLeft == value) return; |
|
motorLeft = value; |
|
FirePropertyChanged("MotorLeft"); |
|
} |
|
} |
|
|
|
private int motorRight; |
|
public int MotorRight |
|
{ |
|
get { return motorRight; } |
|
set |
|
{ |
|
if (motorRight == value) return; |
|
motorRight = value; |
|
FirePropertyChanged("MotorRight"); |
|
} |
|
} |
|
|
|
private int gyroPitch; |
|
public int GyroPitch |
|
{ |
|
get { return gyroPitch; } |
|
set |
|
{ |
|
if (gyroPitch == value) return; |
|
gyroPitch = value; |
|
FirePropertyChanged("GyroPitch"); |
|
} |
|
} |
|
|
|
private int gyroRoll; |
|
public int GyroRoll |
|
{ |
|
get { return gyroRoll; } |
|
set |
|
{ |
|
if (gyroRoll == value) return; |
|
gyroRoll = value; |
|
FirePropertyChanged("GyroRoll"); |
|
} |
|
} |
|
|
|
private int gyroYaw; |
|
public int GyroYaw |
|
{ |
|
get { return gyroYaw; } |
|
set |
|
{ |
|
if (gyroYaw == value) return; |
|
gyroYaw = value; |
|
FirePropertyChanged("GyroYaw"); |
|
} |
|
} |
|
|
|
private int accelPitch; |
|
public int AccelPitch |
|
{ |
|
get { return accelPitch; } |
|
set |
|
{ |
|
if (accelPitch == value) return; |
|
accelPitch = value; |
|
FirePropertyChanged("AccelPitch"); |
|
} |
|
} |
|
|
|
private int accelRoll; |
|
public int AccelRoll |
|
{ |
|
get { return accelRoll; } |
|
set |
|
{ |
|
if (accelRoll == value) return; |
|
accelRoll = value; |
|
FirePropertyChanged("AccelRoll"); |
|
} |
|
} |
|
|
|
private int accelZ; |
|
|
|
public int AccelZ |
|
{ |
|
get { return accelZ; } |
|
set |
|
{ |
|
if (accelZ == value) return; |
|
accelZ = value; |
|
FirePropertyChanged("AccelZ"); |
|
} |
|
} |
|
|
|
private int _controlRoll; |
|
|
|
public int ControlRoll |
|
{ |
|
get { return _controlRoll; } |
|
set |
|
{ |
|
if (_controlRoll == value) return; |
|
_controlRoll = value; |
|
FirePropertyChanged("ControlRoll"); |
|
} |
|
} |
|
|
|
private int _controlPitch; |
|
|
|
public int ControlPitch |
|
{ |
|
get { return _controlPitch; } |
|
set |
|
{ |
|
if (_controlPitch == value) return; |
|
_controlPitch = value; |
|
FirePropertyChanged("ControlPitch"); |
|
} |
|
} |
|
|
|
private int _controlYaw; |
|
|
|
public int ControlYaw |
|
{ |
|
get { return _controlYaw; } |
|
set |
|
{ |
|
if (_controlYaw == value) return; |
|
_controlYaw = value; |
|
FirePropertyChanged("ControlYaw"); |
|
} |
|
} |
|
|
|
private float _compassHeading; |
|
|
|
|
|
/// <summary> |
|
/// The current compass Heading as read from the magnetometer in radians? |
|
/// </summary> |
|
public float CompassHeading |
|
{ |
|
get { return _compassHeading; } |
|
set |
|
{ |
|
if (_compassHeading == value) return; |
|
_compassHeading = value; |
|
FirePropertyChanged("CompassHeading"); |
|
FirePropertyChanged("CompassHeadingDegrees"); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// The current compass Heading as read from the magnetometer in degrees |
|
/// </summary> |
|
public float CompassHeadingDegrees |
|
{ |
|
get |
|
{ |
|
var degrees = (float) ((CompassHeading) * (180 / Math.PI)); |
|
return (360 + degrees) % 360; |
|
} |
|
} |
|
|
|
#endregion |
|
|
|
public int Unused { get; set; } |
|
public float UnusedFloat { get; set; } |
|
} |
|
} |