Jump to content
The simFlight Network Forums

Recommended Posts

Posted

Hi,

 

I am creating a PFD class for our app. In the class below, I am trying to figure out how to use the timer to update the properties, then trigger an event. The problem is when the properties are updated, I either get 0 for the value, or a constantly rotating value. The AslAltitude property is one good example. In the current implementation, I keep getting the AslAltitudeChanged event triggering, even when there shouldn't be a new change in value. With the plane on the ground and with the parking brake on, the asl altitude property gives me numbers like 1100, 600, 899, 711, 1258... All you really have to do is put this in a project with a connection/process routine, subscribe to the AslAltitude changed event, then assing the value to a textbox. The AircraftValueChangedEventArgs class is nothing more than a class that takes a double. Here is the code.

 

public class AircraftValueChangedEventArgs: EventArgs

{

 

public double NewValue {get; set;}

 

public AircraftValueChangedEventArgs(double value)

{

this.NewValue = value;

}

}

 

Now, on to the more important code..

 

using FSUIPC;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Threading;

namespace tfm.Airplanes.Shared
{
    public class PFD
    {

        // Events.
        #region
        public event EventHandler<AircraftValueChangedEventArgs> AslAltitudeChanged;
        public event EventHandler<AircraftValueChangedEventArgs> AglAltitudeChanged;
        public event EventHandler<AircraftValueChangedEventArgs> TrueHeadingChanged;
        public event EventHandler<AircraftValueChangedEventArgs> IndicatedAirspeedChanged;
        public event EventHandler<AircraftValueChangedEventArgs> GroundSpeedChanged;
        public event EventHandler<AircraftValueChangedEventArgs> MachSpeedChanged;
        public event EventHandler<AircraftValueChangedEventArgs> VerticalSpeedChanged;
        public event EventHandler<AircraftValueChangedEventArgs> PitchChanged;
        public event EventHandler<AircraftValueChangedEventArgs> BankChanged;
        public event EventHandler<AircraftValueChangedEventArgs> AltimeterChanged;
        #endregion
        // FSUIPC offsets.
        #region
        private Offset<int> _aslAltitudeOffset = new Offset<int>(0x3324); // Feet.
        private Offset<int> _aglAltitudeOffset = new Offset<int>(0x0020); // In meters (* 256).
        private Offset<uint> _indicatedAirSpeedOffset = new Offset<uint>(0x02bc); // Knots (*128).
        private Offset<uint> _groundSpeedOffset = new Offset<uint>(0x02b4); 
        private Offset<short> _machSpeedOffset = new Offset<short>(0x11c6); // Mach speed (*20480).
        private Offset<double> _trueHeadingOffset= new Offset<double>(0x6038); 
        private Offset<short> _verticalSpeedOffset = new Offset<short>(0x02C8); // FPM = value *60*3.28084/256
        private Offset<ushort> _altimeterOffset = new Offset<ushort>(0x0330);
        private Offset<double> _fdPitchOffset = new Offset<double>(0x2ee8); // In degrees (+ for up, - for down).
        private Offset<double> _fdBankOffset = new Offset<double>(0x2ef0); // In degrees (- left, + right).
        private Offset<int> _pitchOffset = new Offset<int>(0x0578); // In degrees (- = down, + = up).
        private Offset<int> _bankOffset = new Offset<int>(0x057c); // In degrees (+ for up, - for down).
        #endregion

        // Private fields.
        #region
        private DispatcherTimer _timer;
        private double _aslAltitude;
        private double _aglAltitude;
        private double _groundSpeed;
        private double _indicatedAirSpeed;
        private double _machSpeed;
        private double _trueHeading;
        private double _fdPitch;
        private double _fdBank;
        private double _pitch;
        private double _bank;
        private double _verticalSpeed;
        private double _altimeter;
        // Enter ILS if appropriate.
        #endregion

        // public properties.
        #region
        public double AslAltitude
        {
            
            // No change needed, so return the original value.
            get { return _aslAltitudeOffset.Value;  }
            set
            {
                _aslAltitude = _aslAltitudeOffset.Value;
                            }
        }

        public double AglAltitude
        {
            get
            {
                double groundAlt = (double)_aglAltitude / 256d * 3.28084d;
                double agl = (double)this.AslAltitude - groundAlt;
                agl = double.Truncate(agl);

                return agl;
            }
            set
            {
                var newAltitude = value;
                double groundAlt = (double)newAltitude / 256d * 3.28084d;
                double agl = (double)this.AslAltitude - groundAlt;
                agl = double.Truncate(agl);

                if (newAltitude != _aglAltitude)
                {
                    _aglAltitude = newAltitude;                    
                    AglAltitudeChanged?.Invoke(this, new AircraftValueChangedEventArgs(agl));
                }

            }
        }

        public double GroundSpeed
        {
            get
            {
                double speed = ((double)_groundSpeed * 3600d) / (65536d * 1852d);
                speed = double.Truncate(speed);
                return speed;
            }
            set
            {
                double newSpeed = value;
                if(_groundSpeed != newSpeed)
                {
                    _groundSpeed = newSpeed;
                    double speed = ((double)newSpeed * 3600d) / (65536d * 1852d);
                    speed = double.Truncate(speed);
                    GroundSpeedChanged?.Invoke(this, new AircraftValueChangedEventArgs(speed));
                }
            }
        }

        public double IndicatedAirSpeed
        {
            get
            {
                double speed = (double)_indicatedAirSpeed / 128d;
                speed = double.Truncate(speed);
                return speed;
            }

            set
            {
                double newSpeed = value;
                if(_indicatedAirSpeed != newSpeed)
                {
                    _indicatedAirSpeed = newSpeed;
                    double speed = (double)_indicatedAirSpeed / 128d;
                    speed = double.Truncate(speed);
                    IndicatedAirspeedChanged?.Invoke(this, new AircraftValueChangedEventArgs(speed));
                            }
            }
        }

        public double MachSpeed
        {
            get
            {
                double speed = Math.Truncate(_machSpeed / 20480 * 1000) / 1000;
                return speed;
            }
            set
            {
                double newSpeed = value;
                if (_machSpeed != newSpeed)
                {
                    _machSpeed = newSpeed;
                    double speed = Math.Truncate(newSpeed / 20480 * 1000) / 1000;
                    MachSpeedChanged?.Invoke(this, new AircraftValueChangedEventArgs(speed));
                                    }
            }
        }

        public double TrueHeading
        {
            get
            {
                var heading = Utilities.MathServices.ConvertRadiansToDegrees(_trueHeading);
                heading = double.Truncate(heading);
                return heading;
            }
            set
            {
                double newHeading = value;
                if(_trueHeading != newHeading)
                {
                    _trueHeading = newHeading;
                    var heading = Utilities.MathServices.ConvertRadiansToDegrees(newHeading);
                    heading = double.Truncate(heading);
                    TrueHeadingChanged?.Invoke(this, new AircraftValueChangedEventArgs(heading));
                }
            }
        }

        public double FdPitch
        {
            get { return _fdPitch; }
            set { _fdPitch = value; }
        }

        public double FdBank
        {
            get { return _fdBank; }
            set { _fdBank = value; }
        }

        public double Pitch
        {
            get
            {
                double pitch = (double)_pitch * 360d / (65536d * 65536d);
                pitch = Math.Truncate(pitch);
                return pitch;
            }
            set
            {
                double newPitch = value;
                if(_pitch != newPitch)
                {
                    _pitch = newPitch;
                    double pitch = (double)newPitch * 360d / (65536d * 65536d);
                    pitch = Math.Truncate(pitch);
                    PitchChanged?.Invoke(this, new AircraftValueChangedEventArgs(pitch));
                }
            }
        }

        public double Bank
        {
            get
            {
                double bank = (double)_bank * 360d / (65536d * 65536d);
                bank = Math.Truncate(bank);
                return bank;
            }
            set
            {
                double newBank = value;
                if(_bank != newBank)
                {
                    _bank = newBank;
                    double bank = (double)newBank * 360d / (65536d * 65536d);
                    bank = Math.Truncate(bank);
                    BankChanged?.Invoke(this, new AircraftValueChangedEventArgs(bank));
                }
            }
        }

        public double VerticalSpeed
        {
            get
            {
                double speed = _verticalSpeed * 60 * 3.28084 / 256;
                speed = double.Truncate(speed);
                return speed;
            }
            set
            {
                double newSpeed = value;
                if(_verticalSpeed != newSpeed)
                {
                    _verticalSpeed = newSpeed;
                    double speed = newSpeed * 60 * 3.28084 / 256;
                    speed = double.Truncate(speed);
                    VerticalSpeedChanged?.Invoke(this, new AircraftValueChangedEventArgs(speed));
                }
            }
        }

        public double Altimeter
        {
            get
            {
                double AltQNH = (double)_altimeterOffset.Value / 16d;
double                altimeterInches = Math.Floor(((100 * AltQNH * 29.92) / 1013.2) + 0.5);
                altimeterInches = altimeterInches / 100;
                return altimeterInches;

            }
            set
            {
                                                                                        double qnh = value * 33.864;
                    qnh = Math.Round(qnh, 1) * 16;
                    _altimeterOffset.Value = (ushort)qnh;
                    AltimeterChanged?.Invoke(this, new AircraftValueChangedEventArgs(this.Altimeter));
                                                }
        }
        #endregion


        // Constructor
        public PFD()
        {
            _timer = new()
            {
                Interval = TimeSpan.FromMilliseconds(300),
            };

            _timer.Tick += (s, e) =>
            
            {
                if (_aslAltitudeOffset.ValueChanged)
                {
                    this.AslAltitude = _aslAltitudeOffset.Value;
                    AslAltitudeChanged?.Invoke(this, new AircraftValueChangedEventArgs(this.AslAltitude));
                }
if(_aglAltitudeOffset.ValueChanged)
                {
                    this.AglAltitude = _aglAltitudeOffset.Value;
                }
if(_trueHeadingOffset.ValueChanged)
                {
                    this.TrueHeading= _trueHeadingOffset.Value;
                }
if(_indicatedAirSpeedOffset.ValueChanged)
                {
                    this.IndicatedAirSpeed = _indicatedAirSpeedOffset.Value;
                }

if(_groundSpeedOffset.ValueChanged)
                {
                    this.GroundSpeed = _groundSpeedOffset.Value;
                }
if(_machSpeedOffset.ValueChanged)
                {
                    this.MachSpeed = _machSpeedOffset.Value;
                }
if(_verticalSpeedOffset.ValueChanged)
                {
                    this.VerticalSpeed = _verticalSpeedOffset.Value;
                }
if(_pitchOffset.ValueChanged)
                {
                    this.Pitch = _pitchOffset.Value;
                }
if(_bankOffset.ValueChanged)
                {
                    this.Bank = _bankOffset.Value;
                }
if(_altimeterOffset.ValueChanged)
                {
                    this.Altimeter = _altimeterOffset.Value;
                }

                                            };
            
            // Initial readouts.
            this.AslAltitude = _aslAltitudeOffset.Value;
            //this.AglAltitude = _aglAltitudeOffset.Value;
            //this.TrueHeading= _trueHeadingOffset.Value;
            //this.IndicatedAirSpeed = _indicatedAirSpeedOffset.Value;
            //this.GroundSpeed = _groundSpeedOffset.Value;
            //this.MachSpeed = _machSpeedOffset.Value;
            //this.VerticalSpeed = _verticalSpeedOffset.Value;
            //this.Pitch = _pitchOffset.Value;
            //this.Bank = _bankOffset.Value;
            //this.Altimeter = _altimeterOffset.Value;

            _timer.Start();
        }
            }
}
 

Again, I want the AslAltitude to be correct when the event fires, but only fire the event when the AslAltitude actually changes.

Posted

Hi Andy,

I runs fine for me. I pasted your code in (along with your connection manager from your previous thread). I ran it with the following code on a WPF window:

        private PFD _pfd;

        private void btnConnect_Click(object sender, RoutedEventArgs e)
        {
            ConnectionManager.AutoConnect();
            _pfd = new PFD();
            _pfd.AslAltitudeChanged += _pfd_AslAltitudeChanged;
        }

        private void _pfd_AslAltitudeChanged(object sender, AircraftValueChangedEventArgs e)
        {
            this.txtAlt.Text = e.NewValue.ToString();
        }

It works as expected: No bad values and only updates when the altitude changes.

Some things to note about my test:

1. I don't have the Truncate() extension on double, so I had to comment those lines out.

2. I don't have  Utilities.MathServices.ConvertRadiansToDegrees so again, commented out.

3. I don't have MSFS - I used FSX - This shouldn't make any difference unless 0x3324 is broken in FSUIPC7 which is unlikely. 

I think the problem is likely to be somewhere else in your code. Try using a simple window like my code above to see if that works.

Paul

 

Posted

Just the stock Cessna. I don't have any PMDG aircraft. The Altitude offset is the built-in flight sim altitude. It's not specific to the PMDG aircraft. Have you tried a stock aircraft? If that's fine then it's PMDG messing with that offset.

Paul

Posted

So, everything works as expected once the aircraft starts moving. Until then, the PFD reports 0s for everything. Is there a way to get the actual values before it moves?

Posted

The offset values are updated regardless of if the aircraft is moving or not. The Altitude offset should definitely contain the correct value while on the runway. It certainly does here.

I suspect there's a problem with your event system. It's probably missing the first Process() call where all the offsets will have ValueChanged set to true. From the second process() onwards this property will be false until the value changes. Which it won't until the aircraft moves.

You probably need to do an initial sweep of all the values before your event system starts.

This also brings up another potential problem with your event system... If it misses a process() call (i.e. two Process() calls happen between your PDF class checking the offsets), it will miss the change in the offset.

Your event system should really be called after each process() (on the same timer) to make sure it's 1:1, not on its own timer where its likely to get out of sync.

Paul 

Posted

Would it make sense in the timer for the PFD class to introduce the process method with a group name of PFD, then assign the PFD groupname to all of the offsets in the pfd class? Or, will I have to do it differently?

Posted
Quote

Would it make sense in the timer for the PFD class to introduce the process method with a group name of PFD, then assign the PFD groupname to all of the offsets in the pfd class?

Yes, that's a good way of doing it.

Paul

Posted

I did that and the asl, agl, IAS, and altimeter cycle between random values again. If I remove the process call and the group name, everything returns to the original behavior (inital values are all 0s).

Posted

I just tried reading offset<double>(0x6020) and got 255.664........ This is in meters at KMSP. When I convert it to feet (*3.281), I get 836.55......... in feet. Which is the correct answer. This particular offset will sit and wait until the value changes before updating itself, and it allows me to pull its initial value. I wonder if 0x3324 is broken in MSFS for some strange reason. When I tried 3324 in P3D, it works as expected. Unfortunately, in MSFS, it is busted. I wonder of the rest of my PFD values are doing the same for that reason? That the offsets are busted... It is weird because older versions of TFM are producing the same behavior (rotating random numbers with the older FSX/P3D offsets).

Posted
Quote

 When I tried 3324 in P3D, it works as expected. Unfortunately, in MSFS, it is busted.

Did you try with a stock aircraft? It might be the PMDG aircraft that's messing it up the sim variable that feeds that offset.

You should also try just logging 3324 using the FSUIPC logging options.

If it's producing bad values in the logging with one of the stock aircraft you need to ask John Dowson about it.

If it's fine in the FSUIPC logging then it's something in your code.

Paul

  • 2 weeks later...
Posted

For PMDG aircraft, maybe also check the specific PMDG offsets. For example, the MCP Altitude is held in offset 0x65CE, and there are various offsets for the FMC cruise/landing/transition altitudes. Also some air display altitudes are available (AIR_DisplayFltAlt and AIR_DisplayLandAlt) as strings.

But I have just checked the indicated altitude in offset 0x03324 (and 0x0590 - indicated altitude calibrated) via FSUIPC logging (as suggested by Paul) and those offsets do seem to be working in the PMDG 737 and hold the correct value.

John

 

 

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use. Guidelines Privacy Policy We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.