• Which the release of FS2020 we see an explosition of activity on the forun and of course we are very happy to see this. But having all questions about FS2020 in one forum becomes a bit messy. So therefore we would like to ask you all to use the following guidelines when posting your questions:

    • Tag FS2020 specific questions with the MSFS2020 tag.
    • Questions about making 3D assets can be posted in the 3D asset design forum. Either post them in the subforum of the modelling tool you use or in the general forum if they are general.
    • Questions about aircraft design can be posted in the Aircraft design forum
    • Questions about airport design can be posted in the FS2020 airport design forum. Once airport development tools have been updated for FS2020 you can post tool speciifc questions in the subforums of those tools as well of course.
    • Questions about terrain design can be posted in the FS2020 terrain design forum.
    • Questions about SimConnect can be posted in the SimConnect forum.

    Any other question that is not specific to an aspect of development or tool can be posted in the General chat forum.

    By following these guidelines we make sure that the forums remain easy to read for everybody and also that the right people can find your post to answer it.

MSFS MSFS 2020 C# SimConnect decreas of the showen Character

Messages
3
Country
germany
Hello,

I put together a small test plugin for MSFS 2020 in C#.
Now it shows me a number with far too many decimal places for the defined variable of the height of the aircraft. How can I eliminate these decimal places? I would also like to create an if function that, at a flight altitude of e.g.
5000 a sound is played, but I don't know how to define the flight altitude variable.

I would be happy if someone can help me there

using Microsoft.FlightSimulator.SimConnect;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System;
using System.Media;

namespace Test
{

public partial class Form1 : Form
{

private Microsoft.FlightSimulator.SimConnect.SimConnect my_simconnect;
private const int WM_USER_SIMCONNECT = 0x402;
private enum DATA_REQUESTS
{
REQUEST_1
}

private enum DEFINITIONS
{
Struct1
}

public Form1()
{
InitializeComponent();

try
{
my_simconnect = new Microsoft.FlightSimulator.SimConnect.SimConnect("Managed Data Request", base.Handle, 0x402, null, 0);
InitDataRequest();
timer1.Enabled = true;
}
catch (COMException)
{
textBox1.Text = "Unable to connect to sim";
}

while (textBox1.Text == "Unable to connect to sim")
{
if (my_simconnect == null)
{
try
{
my_simconnect = new Microsoft.FlightSimulator.SimConnect.SimConnect("Managed Data Request", base.Handle, 0x402, null, 0);
InitDataRequest();
timer1.Enabled = true;
}
catch (COMException)
{
textBox1.Text = "Unable to connect to sim";
}
}
else
{
break;
}
}
}

private void closeConnection()
{
if (my_simconnect != null)
{
my_simconnect.Dispose();
my_simconnect = null;
textBox1.Text = "Connection closed";
textBox_atitude.Text = "";
}
}

protected override void DefWndProc(ref Message m)
{
if (m.Msg == 0x402)
{
if (my_simconnect != null)
{
my_simconnect.ReceiveMessage();
}
}
else
{
base.DefWndProc(ref m);
}
}

private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
closeConnection();
timer1.Enabled = false;
}

private void InitDataRequest()
{
try
{
my_simconnect.OnRecvOpen += new SimConnect.RecvOpenEventHandler(simconnect_OnRecvOpen);
my_simconnect.OnRecvQuit += new SimConnect.RecvQuitEventHandler(simconnect_OnRecvQuit);
my_simconnect.OnRecvException += new SimConnect.RecvExceptionEventHandler(simconnect_OnRecvException);
my_simconnect.AddToDataDefinition(DEFINITIONS.Struct1, "Title", null, SIMCONNECT_DATATYPE.STRING256, 0.0f, SimConnect.SIMCONNECT_UNUSED);
my_simconnect.AddToDataDefinition(DEFINITIONS.Struct1, "INDICATED ALTITUDE", "Feet", SIMCONNECT_DATATYPE.FLOAT64, 0, SimConnect.SIMCONNECT_UNUSED);
my_simconnect.RegisterDataDefineStruct<Struct1>(DEFINITIONS.Struct1);
my_simconnect.OnRecvSimobjectDataBytype += new SimConnect.RecvSimobjectDataBytypeEventHandler(simconnect_OnRecvSimobjectDataBytype);
}
catch (COMException)
{
MessageBox.Show("COMException");
}
}

private void simconnect_OnRecvException(SimConnect sender, SIMCONNECT_RECV_EXCEPTION data)
{
textBox1.Text = "Exception received: " + ((uint)data.dwException);
}

private void simconnect_OnRecvOpen(SimConnect sender, SIMCONNECT_RECV_OPEN data)
{
textBox1.Text = "Connected to Sim";
}

private void simconnect_OnRecvQuit(SimConnect sender, SIMCONNECT_RECV data)
{
textBox1.Text = "sim has exited";
closeConnection();
timer1.Enabled = false;
}

private void simconnect_OnRecvSimobjectDataBytype(SimConnect sender, SIMCONNECT_RECV_SIMOBJECT_DATA_BYTYPE data)
{
if (data.dwRequestID == 0)
{
Struct1 struct1 = (Struct1)data.dwData[0];
textBox_atitude.Text = "FL: " + struct1.altitude.ToString() + " Feet";
}
else
{
textBox1.Text = "Unknown request ID: " + ((uint)data.dwRequestID);
textBox_atitude.Text = "";
}
}

private void timer1_Tick_1(object sender, EventArgs e)
{

my_simconnect.RequestDataOnSimObjectType(DATA_REQUESTS.REQUEST_1, DEFINITIONS.Struct1, 0, SIMCONNECT_SIMOBJECT_TYPE.USER);
textBox1.Text = "Request sent...";
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct Struct1
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string title;
public double altitude;
};
}
}
 

Attachments

  • unknown.png
    unknown.png
    2.3 KB · Views: 75
Last edited:
Hi Simon,

For just a comparison the decimal places don't matter.

C#:
if (struct1.altitude > 5000.0) {
  // play sound, etc
}

For display you'd use formatting directives. For example, to display with 2 decimal places only:
C#:
struct1.altitude.ToString("F2")
References:
To change the actual value to have fewer decimals you could round it:
C#:
struct1.altitude = Math.Round(struct1.altitude, 2);

Bonus: To ignore all changes in altitude which are smaller than, say, 2 decimal places, set a value on the fEpsilon parameter in AddToDataDefinition() to, for example, 0.009 (this way SimConnect doesn't even send a change unless the change is larger than 0.009 feet).

Bonus 2: To eliminate the need for a timer, use RequestDataOnSimObject() (not "ByType") with the desired period and interval. Set up an event handler for OnRecvSimobjectData(). Enjoy data changes "pushed" to your application instead of having to explicitly poll for changes on a regular basis.

Hope that helps!
-Max
 
Hi Max,

Many thanks for the extremely quick help. Your tips and suggestions worked perfectly and helped me a lot. Also many thanks for the "bonus" information which I have already intrigued directly into my code. Now I only have one question or a problem with the if condition.
Since you used a ">" character, the sound is permanently triggered, at least for me. Now I ask myself the question, is it also possible to configure it so that it is only triggered once or at least only reacts at = 5000. "==" or "=" I've already tried that doesn't work.
 
Hi Simon, you're very welcome, and I'm glad it helped.

To compare a "decimal" (double/float) type to an exact value like 5000.0 is tricky because the value (altitude in this case) will almost never be exactly 5000.0 (as you've seen, there are many decimal places of precision in the actual value, so even though theoretically at some point it should equal exactly 5000.0, it's very unlikely that you'd be sent that exact value at the exact right time by SimConnect). Anyway, if you just want to compare to a whole number, one way would be to round the value you're comparing to zero decimal places:

C#:
if (Math.Round(struct1.altitude, 0) == 5000.0) {
    // sound/etc
}

This will still play the sound when the altitude is anywhere in the ~ 4999.5 - 5000.4 range.

Not sure you need this, but for reference, one way to compare decimal types more precisely but still within a range is to use an "epsilon" value (essentially what using that fEpsolon parameter does inside SimConnect):
C#:
// Here we're checking if the currently reported altitude is within ~0.09 feet of 5000.
//  0.1 is the "delta epsilon" (change difference) we're comparing against.
if (System.Math.Abs(struct1.altitude - 5000.0) <= 0.1) {
    // do something
}

If you just want to play the sound once, eg. when passing through 5000 on the way up, you'd need to use some kind of "flag" variable to track when the sound has already been played, and then reset the flag when, in this example, the 'plane drops below 5000 again.

Perhaps something like this (there could be many ways to actually implement the flag of course, this example if fairly simplistic):
C#:
// declare/define a bool somewhere in your class to use as a flag
bool _fl5SoundPlayed = false;

// then in the message processor
if (struct1.altitude >= 5000) {
    // check if we haven't played the sound yet
    if (!_fl5SoundPlayed) {
        // set the flag indicating that the sound is played.
        // It is better to do this first before actually loading/playing the sound file
        // since it will avoid duplicate invocations if the altitude changes again before
        // the audio is finished. This is a.k.a a "race condition."
        _fl5SoundPlayed = true;
        // play sound
    }
}
// EDIT
// Don't use an "else" here or "< 5000" because while hovering around 5000' altitude the flag will likely get reset too often.
else if (struct1.altitude < 4950.0) {
    // reset the flag if dropping below 4950 ft. (just for use as an example, your actual needs may vary)
    _fl5SoundPlayed = true;
}

HTH!
-Max
 
Last edited:
Many Thanks. I have now successfully implemented this and it works as intended.
Now I would like to tackle my next problem. I would like to read the BCD16 code from the COM1/2 and 3 STORED FREQUENCY in order to convert them later... Converting is not a problem at first, but displaying the BCD16 Frequency.
It shows me constantly changing values as a BCD16 code, how can I display the BCD16 of the STORED Frequencys correctly?

my_simconnect.AddToDataDefinition(DEFINITIONS.Struct1, "COM1 STORED FREQUENCY", "BCD", SIMCONNECT_DATATYPE.FLOAT64, 0, SimConnect.SIMCONNECT_UNUSED);

...

textBox_Frequency.Text = struct1.Frequency .ToString();
 

Attachments

  • 1.PNG
    1.PNG
    2.4 KB · Views: 91
Hi Simon,

First, you should know that you can specify different types of Units for your requests, as long as the unit type is compatible with the value you're requesting. For example a value which is by default sent in "feet" can also be requested in "meters" and the conversion will be done for you by SimConnect before you get the final result.

So even though the SimConnect docs list most of the frequencies as being in "Frequency BCD16" (not "BCD" by the way), you can actually request those directly in Hz/KHz/MHz. So just literally put "MHz" for the unit name in AddToDataDefinition() and the value you get back is exactly what is displayed on the radio.

Now if you actually do want BCD values for some reason, there's a neat trick which I haven't seen discussed anywhere. The trick is that a BCD value when viewed in hexadecimal format (base 16) will look exactly like the actual decimal value (here, frequency) being encoded, just "missing" the decimal separator. For example:

Frequency shown on radio: 124.85 MHz
BCD16 encoded value (base 10): 9349
BCD16 encoded value (base 16): 2485 (16 bits cannot represent the top hundreds digit)
BCD32 encoded (base 10): 19170560
BCD32 encoded (base 16): 1248500

Note that the hex value never has letters (A-F). This means one could just parse the hex string as numeric value (eg. double.TryParse()) and do any math on that as needed (for BCD16 that would be divide by 100 and then add 100, for BCD32 just divide by 1000). Granted that's like cheating though... :)

To convert numerically you need some kind of function... I've not used any myself that I can remember, but this seems like a good simple one in C#: https://forums.flightsimulator.com/...equencies-by-sim-variables-in-python/278919/2

Cheers,
-Max
 
As usual, there's a big chunk of information missing in that MSFS post, nor is there any acknowledgment of the source - it was written by Arne Bartels about twenty years ago and I still carry it in the sd2gau series for backwards compatibility. Here's the complete version, albeit in C/C++.
Code:
//------------------------------------------------------
// BCD conversions
#define Bcd2Dec(BcdNum) HornerScheme(BcdNum,0x10,10)
#define  Dec2Bcd(DecNum) HornerScheme(DecNum,10,0x10)

UINT32 HornerScheme(UINT32 Num,UINT32 Divider,UINT32 Factor)
{
    UINT32  Remainder=0,Quotient=0,Result=0;
    Remainder=Num%Divider;
    Quotient=Num/Divider;
    if(!(Quotient==0&&Remainder==0))
    Result+=HornerScheme(Quotient,Divider,Factor)*Factor+Remainder;
    return Result;
}
 
Or while we're at it, back to at least 1969 from Data General Corp. (albeit in machine code):
http://www.bitsavers.org/pdf/dg/software/utility/093-000029-00_Double_Precision_BCD_to_Binary.pdf

🤓

EDIT:
Found Arne's version with instructions in this site's Wiki: https://www.fsdeveloper.com/wiki/index.php?title=C:_Decimal_to_BCD
Also conversion is discussed in this thread (and it seems I was wrong and someone already thought of using hex notation for parsing): https://www.fsdeveloper.com/forum/threads/converting-adf-frequency.21264/ (wiki link found in last post there but claims the function may not always work)
 
Last edited:
No, the Python post was a pretty direct copy of Arne's code in C. So, not sure why you think we should reference machine code when pointing out someone used someone else's code without actual referencing it in any form.
 
No, the Python post was a pretty direct copy of Arne's code in C. So, not sure why you think we should reference machine code when pointing out someone used someone else's code without actual referencing it in any form.
Because it's an interesting bit of history? I never said anyone else "should" do anything. Personally I fully give credit whenever possible. But also sources of little snippets like that aren't always easy to track down. Nor are those sources always crediting _their_ sources either.
 
Last edited:
Back
Top