200 lines
6.9 KiB
C#
200 lines
6.9 KiB
C#
using System.ComponentModel;
|
|
using System.Windows;
|
|
using System.Windows.Media;
|
|
using UserControl = System.Windows.Controls.UserControl;
|
|
using Color = System.Windows.Media.Color;
|
|
|
|
namespace OperationControl.Controls
|
|
{
|
|
/// <summary>
|
|
/// Interaction logic for AttitudeIndicator.xaml
|
|
/// </summary>
|
|
public partial class AttitudeIndicator : UserControl, INotifyPropertyChanged
|
|
{
|
|
public AttitudeIndicator()
|
|
{
|
|
InitializeComponent();
|
|
|
|
DataContext = this;
|
|
UpdateChrome();
|
|
}
|
|
|
|
// ===== Dependency Properties =====
|
|
public static readonly DependencyProperty RollDegProperty =
|
|
DependencyProperty.Register(nameof(RollDeg), typeof(double), typeof(AttitudeIndicator),
|
|
new PropertyMetadata(0.0, OnAngleChanged));
|
|
|
|
public static readonly DependencyProperty PitchDegProperty =
|
|
DependencyProperty.Register(nameof(PitchDeg), typeof(double), typeof(AttitudeIndicator),
|
|
new PropertyMetadata(0.0, OnAngleChanged));
|
|
|
|
public static readonly DependencyProperty PxPerDegProperty =
|
|
DependencyProperty.Register(nameof(PxPerDeg), typeof(double), typeof(AttitudeIndicator),
|
|
new PropertyMetadata(2.0, OnAngleChanged));
|
|
|
|
public static readonly DependencyProperty WarningLimitProperty =
|
|
DependencyProperty.Register(nameof(WarningLimit), typeof(double), typeof(AttitudeIndicator),
|
|
new PropertyMetadata(10.0, OnAngleChanged));
|
|
|
|
public static readonly DependencyProperty CriticalLimitProperty =
|
|
DependencyProperty.Register(nameof(CriticalLimit), typeof(double), typeof(AttitudeIndicator),
|
|
new PropertyMetadata(18.0, OnAngleChanged));
|
|
|
|
private static readonly DependencyPropertyKey PitchOffsetPxPropertyKey =
|
|
DependencyProperty.RegisterReadOnly(
|
|
nameof(PitchOffsetPx),
|
|
typeof(double),
|
|
typeof(AttitudeIndicator),
|
|
new PropertyMetadata(0.0));
|
|
|
|
public static readonly DependencyProperty PitchOffsetPxProperty = PitchOffsetPxPropertyKey.DependencyProperty;
|
|
|
|
public double PitchOffsetPx
|
|
{
|
|
get => (double)GetValue(PitchOffsetPxProperty);
|
|
private set => SetValue(PitchOffsetPxPropertyKey, value);
|
|
}
|
|
|
|
public double RollDeg
|
|
{
|
|
get => (double)GetValue(RollDegProperty);
|
|
set => SetValue(RollDegProperty, value);
|
|
}
|
|
public double PitchDeg
|
|
{
|
|
get => (double)GetValue(PitchDegProperty);
|
|
set => SetValue(PitchDegProperty, value);
|
|
}
|
|
public double PxPerDeg
|
|
{
|
|
get => (double)GetValue(PxPerDegProperty);
|
|
set => SetValue(PxPerDegProperty, value);
|
|
}
|
|
public double WarningLimit
|
|
{
|
|
get => (double)GetValue(WarningLimitProperty);
|
|
set => SetValue(WarningLimitProperty, value);
|
|
}
|
|
public double CriticalLimit
|
|
{
|
|
get => (double)GetValue(CriticalLimitProperty);
|
|
set => SetValue(CriticalLimitProperty, value);
|
|
}
|
|
|
|
|
|
// === LATERAL ERROR ===
|
|
public static readonly DependencyProperty LateralErrorProperty =
|
|
DependencyProperty.Register(
|
|
nameof(LateralError),
|
|
typeof(double),
|
|
typeof(AttitudeIndicator),
|
|
new PropertyMetadata(0.0, OnLateralChanged));
|
|
|
|
public static readonly DependencyProperty LateralMaxAbsProperty =
|
|
DependencyProperty.Register(
|
|
nameof(LateralMaxAbs),
|
|
typeof(double),
|
|
typeof(AttitudeIndicator),
|
|
new PropertyMetadata(50.0, OnLateralChanged)); // 0,5 m por padrão
|
|
|
|
private static readonly DependencyPropertyKey LateralNormalizedPropertyKey =
|
|
DependencyProperty.RegisterReadOnly(
|
|
nameof(LateralNormalized),
|
|
typeof(double),
|
|
typeof(AttitudeIndicator),
|
|
new PropertyMetadata(0.0));
|
|
|
|
public static readonly DependencyProperty LateralNormalizedProperty = LateralNormalizedPropertyKey.DependencyProperty;
|
|
|
|
public double LateralError
|
|
{
|
|
get => (double)GetValue(LateralErrorProperty);
|
|
set => SetValue(LateralErrorProperty, value);
|
|
}
|
|
|
|
public double LateralMaxAbs
|
|
{
|
|
get => (double)GetValue(LateralMaxAbsProperty);
|
|
set => SetValue(LateralMaxAbsProperty, value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Erro lateral normalizado em [-1, 1]
|
|
/// -1 = limite à esquerda, 0 = centro, +1 = limite à direita
|
|
/// </summary>
|
|
public double LateralNormalized
|
|
{
|
|
get => (double)GetValue(LateralNormalizedProperty);
|
|
private set => SetValue(LateralNormalizedPropertyKey, value);
|
|
}
|
|
|
|
// Derivados
|
|
public double MaxAbsAngle => Math.Max(Math.Abs(RollDeg), Math.Abs(PitchDeg));
|
|
|
|
public string RollTexto => $"LAT {FormatSignedDeg(RollDeg)}";
|
|
public string PitchTexto => $"FRT {FormatSignedDeg(PitchDeg)}";
|
|
|
|
private static string FormatSignedDeg(double value)
|
|
{
|
|
if (double.IsNaN(value) || double.IsInfinity(value))
|
|
return "--.-°";
|
|
|
|
return value.ToString("+0.0;-0.0;0.0") + "°";
|
|
}
|
|
|
|
public event PropertyChangedEventHandler PropertyChanged;
|
|
void Raise(string n) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(n));
|
|
|
|
static void OnAngleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
|
{
|
|
var c = (AttitudeIndicator)d;
|
|
|
|
c.PitchOffsetPx = -c.PitchDeg * c.PxPerDeg;
|
|
|
|
c.Raise(nameof(PitchOffsetPx));
|
|
c.Raise(nameof(MaxAbsAngle));
|
|
c.Raise(nameof(RollTexto));
|
|
c.Raise(nameof(PitchTexto));
|
|
|
|
c.UpdateChrome();
|
|
}
|
|
|
|
void UpdateChrome()
|
|
{
|
|
if (Chrome == null) return;
|
|
|
|
var a = MaxAbsAngle;
|
|
if (a >= CriticalLimit)
|
|
Chrome.BorderBrush = new SolidColorBrush(Color.FromRgb(0xE7, 0x4C, 0x3C)); // vermelho
|
|
else if (a >= WarningLimit)
|
|
Chrome.BorderBrush = new SolidColorBrush(Color.FromRgb(0xF1, 0xC4, 0x0F)); // amarelo
|
|
else
|
|
Chrome.BorderBrush = new SolidColorBrush(Color.FromRgb(0x2E, 0xCC, 0x71)); // verde
|
|
}
|
|
|
|
static void OnLateralChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
|
{
|
|
var c = (AttitudeIndicator)d;
|
|
c.UpdateLateral();
|
|
c.Raise(nameof(LateralNormalized));
|
|
}
|
|
|
|
void UpdateLateral()
|
|
{
|
|
var max = Math.Max(0.0001, LateralMaxAbs);
|
|
|
|
// normaliza para [-1, 1] e faz clamp
|
|
var n = LateralError / max;
|
|
if (n > 1) n = 1;
|
|
if (n < -1) n = -1;
|
|
|
|
LateralNormalized = n;
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|