Skip to content

Instantly share code, notes, and snippets.

@pedro2555
Created July 23, 2017 16:57
Show Gist options
  • Save pedro2555/caf2583c81fdd85ec89d8b96728aeece to your computer and use it in GitHub Desktop.
Save pedro2555/caf2583c81fdd85ec89d8b96728aeece to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using FSUIPC;
namespace FSUIPCClientExample_CSharp
{
public partial class Form1 : Form
{
// Private Static Members
private static readonly string AppTitle = "FSUIPCClientApplication_CSharp";
// Create the Offsets we're interested in for this Application
private Offset<int> airspeed = new Offset<int>(0x02BC); // Basic integer read example
private Offset<int> avionics = new Offset<int>(0x2E80); // Basic integer read and write example
private Offset<byte[]> fsLocalDateTime = new Offset<byte[]>(0x0238, 10); // Example of reading an arbitary set of bytes.
private Offset<string> aircraftType = new Offset<string>("AircraftInfo", 0x3160, 24); // Example of string and use of a group
private Offset<BitArray> lights = new Offset<BitArray>(0x0D0C, 2); // Example of BitArray used to manage a bit field type offset.
private Offset<Double> compass = new Offset<double>(0x02CC); // Example for disconnecting/reconnecting
private Offset<short> pause = new Offset<short>(0x0262, true); // Example of a write only offset.
private Offset<short> com2bcd = new Offset<short>(0x3118); // Example of reading a frequency coded in Binary Coded Decimal
private Offset<long> playerLatitude = new Offset<long>(0x0560); // Offset for Lat/Lon features
private Offset<long> playerLongitude = new Offset<long>(0x0568); // Offset for Lat/Lon features
private Offset<short> onGround = new Offset<short>(0x0366); // Offset for Lat/Lon features
private Offset<short> magVar = new Offset<short>(0x02A0); // Offset for Lat/Lon features
private Offset<uint> playerHeadingTrue = new Offset<uint>(0x0580); // Offset for moving the plane
private Offset<long> playerAltitude = new Offset<long>(0x0570); // Offset for moving the plane
private Offset<short> slewMode = new Offset<short>(0x05DC, true); // Offset for moving the plane
private Offset<int> sendControl = new Offset<int>(0x3110, true); // Offset for moving the plane
private readonly int REFRESH_SCENERY = 65562; // Control number to refresh the scenery
private FsLatLonPoint EGLL; // Holds the position of London Heathrow (EGLL)
private FsLatLonQuadrilateral runwayQuad; // defines the four corners of the runway (27L at EGLL)
private AITrafficServices AI; // Holds a reference to the AI Traffic Services object
public Form1()
{
InitializeComponent();
// Setup the example data for London Heathrow
// 1. The position
// This shows an FsLongitude and FsLatitude class made from the Degrees/Minutes/Seconds constructor.
// The Timer1_Tick() method shows a different contructor (using the RAW FSUIPC values).
FsLatitude lat = new FsLatitude(51, 28, 39.0d);
FsLongitude lon = new FsLongitude(0, -27, -41.0d);
EGLL = new FsLatLonPoint(lat, lon);
// Now define the Quadrangle for the 27L (09R) runway.
// We could just define the four corner Lat/Lon points if we knew them.
// In this example however we're using the helper function to calculate the points
// from the runway information. This is the kind of info you can find in the output files
// from Pete Dowson's MakeRunways program.
FsLatitude rwyThresholdLat = new FsLatitude(51.464943d);
FsLongitude rwyThresholdLon = new FsLongitude(-0.434046d);
double rwyMagHeading = 272.7d;
double rwyMagVariation = -3d;
double rwyLength = 11978d;
double rwyWidth = 164d;
// Call the static helper on the FsLatLonQuarangle class to generate the Quadrangle for this runway...
FsLatLonPoint thresholdCentre = new FsLatLonPoint(rwyThresholdLat, rwyThresholdLon);
double trueHeading = rwyMagHeading + rwyMagVariation;
runwayQuad = FsLatLonQuadrilateral.ForRunway(thresholdCentre, trueHeading, rwyWidth, rwyLength);
// Set the default value for the distance units and AI Radar range
this.cbxDistanceUnits.Text = "Nautical Miles";
this.cbxRadarRange.Text = "50";
}
// Application started so try to open the connection to FSUIPC
private void Form1_Load(object sender, EventArgs e)
{
openFSUIPC();
}
// User pressed connect button so try again...
private void btnStart_Click(object sender, EventArgs e)
{
openFSUIPC();
}
// Opens FSUIPC - if all goes well then starts the
// timer to drive start the main application cycle.
// If can't open display the error message.
private void openFSUIPC()
{
try
{
// Attempt to open a connection to FSUIPC (running on any version of Flight Sim)
FSUIPCConnection.Open();
// Opened OK so disable the Connect button
this.btnStart.Enabled = false;
this.chkEnableAIRadar.Enabled = true;
// Start the timer ticking to drive the rest of the application
this.timer1.Interval = 200;
this.timer1.Enabled = true;
// Set the AI object
AI = FSUIPCConnection.AITrafficServices;
}
catch (Exception ex)
{
// Badness occurred - show the error message
MessageBox.Show(ex.Message, AppTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
FSUIPCConnection.Close();
Debug.WriteLine(ex.Message);
}
}
// Application is unloading so call close to cleanup the
// UNMANAGED memory used by FSUIPC.
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
FSUIPCConnection.Close();
}
// The timer handles the real-time updating of the Form.
// The default group (ie, no group specified) is
// Processed and every Offset in the default group is updated.
private void timer1_Tick(object sender, EventArgs e)
{
// Process the default group
try
{
FSUIPCConnection.Process();
// IAS - Simple integer returned so just divide as per the
// FSUIPC documentation for this offset and display the result.
double airpeedKnots = ((double)airspeed.Value / 128d);
this.txtIAS.Text = airpeedKnots.ToString("f1");
// Avionics Master Switch
this.chkAvionics.Checked = (avionics.Value > 0); // 0 = Off, 1 = On.
// Advanced Concept: Reading Raw Blocks of Data.
// FS Local Date and Time
// This demonstrates getting back an arbitrary number of bytes from an offset.
// Here we're getting 10 back from Offset 0x0328 which contain info about the
// Local date and time in FS.
// Because it's returned as a byte array we need to handle everything ourselves...
// 1. Year (starts at Byte 8) for 2 bytes. (Int16)
// Use the BitConverter class to get it into a native Int16 variable
short year = BitConverter.ToInt16(fsLocalDateTime.Value, 8);
// You could also do it manually if you know about such things...
// short year = (short)(fsLocalDateTime.Value[8] + (fsLocalDateTime.Value[9] * 0x100));
// 2. Make new datetime with the the time value at 01/01 of the year...
// Time - in bytes 0,1 and 2. (Hour, Minute, Second):
DateTime fsTime = new DateTime(year, 1, 1, fsLocalDateTime.Value[0], fsLocalDateTime.Value[1], fsLocalDateTime.Value[2]);
// 3. Get the Day of the Year back (not given as Day and Month)
// and add this on to the Jan 1 date we created above
// to give the final date:
short dayNo = BitConverter.ToInt16(fsLocalDateTime.Value, 6);
fsTime = fsTime.Add(new TimeSpan(dayNo - 1, 0, 0, 0));
// Now print it out
this.txtFSDateTime.Text = fsTime.ToString("dddd, MMMM dd yyyy hh:mm:ss");
// Lights
// This demonstrates using the BitArray type to handle
// a bit field type offset. The lights are a 2 byte (16bit) bit field
// starting in offset 0D0C.
// To make the code clearer and easier to write in the first
// place - I created a LightType Enum (bottom of this file).
// You could of course just use the literal values 0-9 if you prefer.
// For the first three, I've put alternative lines in comments
// that use a literal indexer instead of the enum.
// Update each checkbox according to the relevent bit in the BitArray...
this.chkBeacon.Checked = lights.Value[(int)LightType.Beacon];
//this.chkBeacon.Checked = lights.Value[1];
this.chkCabin.Checked = lights.Value[(int)LightType.Cabin];
//this.chkCabin.Checked = lights.Value[9];
this.chkInstuments.Checked = lights.Value[(int)LightType.Instruments];
//this.chkInstuments.Checked = lights.Value[5];
this.chkLanding.Checked = lights.Value[(int)LightType.Landing];
this.chkLogo.Checked = lights.Value[(int)LightType.Logo];
this.chkNavigation.Checked = lights.Value[(int)LightType.Navigation];
this.chkRecognition.Checked = lights.Value[(int)LightType.Recognition];
this.chkStrobes.Checked = lights.Value[(int)LightType.Strobes];
this.chkTaxi.Checked = lights.Value[(int)LightType.Taxi];
this.chkWing.Checked = lights.Value[(int)LightType.Wing];
// Compass heading
// Used to demonstrate disconnecting and reconnecting an Offset.
// We display the data in the field regardless of whether
// it's been updated or not.
this.txtCompass.Text = compass.Value.ToString("F2");
// COM2 frequency
// Shows decoding a DCD frequency to a string
// a. Convert to a string in Hexadecimal format
string com2String = com2bcd.Value.ToString("X");
// b. Add the assumed '1' and insert the decimal point
com2String = "1" + com2String.Substring(0, 2) + "." + com2String.Substring(2, 2);
this.txtCOM2.Text = com2String;
// Latitude and Longitude
// Shows using the FsLongitude and FsLatitude classes to easily work with Lat/Lon
// Create new instances of FsLongitude and FsLatitude using the raw 8-Byte data from the FSUIPC Offsets
FsLongitude lon = new FsLongitude(playerLongitude.Value);
FsLatitude lat = new FsLatitude(playerLatitude.Value);
// Use the ToString() method to output in human readable form:
// (note that many other properties are avilable to get the Lat/Lon in different numerical formats)
this.txtLatitude.Text = lat.ToString();
this.txtLongitude.Text = lon.ToString();
// Using fsLonLatPoint to calculate distance and bearing between two points
// First get the point for the current plane position
FsLatLonPoint currentPosition = new FsLatLonPoint(lat, lon);
// Get the distance between here and EGLL
double distance = 0;
switch (this.cbxDistanceUnits.Text)
{
case "Nautical Miles":
distance = currentPosition.DistanceFromInNauticalMiles(EGLL);
break;
case "Statute Miles":
distance = currentPosition.DistanceFromInFeet(EGLL) / 5280d;
break;
case "Kilometres":
distance = currentPosition.DistanceFromInMetres(EGLL) / 1000d;
break;
}
// Write the distance to the text box formatting to 2 decimal places
this.txtDistance.Text = distance.ToString("N2");
// Get the bearing (True)
double bearing = currentPosition.BearingTo(EGLL);
// Get the magnetic variation
double variation = (double)magVar.Value * 360d / 65536d;
// convert bearing to magnetic bearing by subtracting the magnetic variation
bearing -= variation;
// Display the bearing in whole numbers and tag on a degree symbol
this.txtBearing.Text = bearing.ToString("F0") + "\u00B0";
// Now check if the player is on the runway:
// Test is the plane is on the ground and if the current position is in the bounds of
// the runway Quadrangle we calculated in the constructor above.
chkPlaneOnRunway.Checked = (this.onGround.Value == 1 && runwayQuad.ContainsPoint(currentPosition));
}
catch (FSUIPCException ex)
{
if (ex.FSUIPCErrorCode == FSUIPCError.FSUIPC_ERR_SENDMSG)
{
// Send message error - connection to FSUIPC lost.
// Show message, disable the main timer loop and relight the
// connection button:
// Also Close the broken connection.
this.timer1.Enabled = false;
this.btnStart.Enabled = true;
this.chkEnableAIRadar.Enabled = false;
this.chkEnableAIRadar.Checked = false;
this.AIRadarTimer.Enabled = false;
FSUIPCConnection.Close();
MessageBox.Show("The connection to Flight Sim has been lost.", AppTitle, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
else
{
// not the disonnect error so some other baddness occured.
// just rethrow to halt the application
throw ex;
}
}
catch (Exception)
{
// Sometime when the connection is lost, bad data gets returned
// and causes problems with some of the other lines.
// This catch block just makes sure the user doesn't see any
// other Exceptions apart from FSUIPCExceptions.
}
}
private void CheckPlayerIsOnRunway()
{
FsLongitude lon = new FsLongitude(playerLongitude.Value);
FsLatitude lat = new FsLatitude(playerLatitude.Value);
// Get the point for the current plane position
FsLatLonPoint currentPosition = new FsLatLonPoint(lat, lon);
// Now define the Quadrangle for the 27L (09R) runway.
// We could just define the four corner Lat/Lon points if we knew them.
// In this example however we're using the helper function to calculate the points
// from the runway information. This is the kind of info you can find in the output files
// from Pete Dowson's MakeRunways program.
FsLatitude rwyThresholdLat = new FsLatitude(51.464943d);
FsLongitude rwyThresholdLon = new FsLongitude(-0.434046d);
double rwyMagHeading = 272.7d;
double rwyMagVariation = -3d;
double rwyLength = 11978d;
double rwyWidth = 164d;
// Call the static helper on the FsLatLonQuarangle class to generate the Quadrangle for this runway...
FsLatLonPoint thresholdCentre = new FsLatLonPoint(rwyThresholdLat, rwyThresholdLon);
double trueHeading = rwyMagHeading + rwyMagVariation;
runwayQuad = FsLatLonQuadrilateral.ForRunway(thresholdCentre, trueHeading, rwyWidth, rwyLength);
// Now check if the player is on the runway:
// Test is the plane is on the ground and if the current position is in the bounds of
// the runway Quadrangle we calculated in the constructor above.
if (this.onGround.Value == 1 && runwayQuad.ContainsPoint(currentPosition))
{
// Player is on the runway
// Do Stuff
}
}
// Demonstrates the Grouping facility and also returning a string.
// The AircraftType Offset is in a Group called "AircraftInfo".
// With the Group system you can gain control over which
// Offsets are processed.
private void btnGetAircraftType_Click(object sender, EventArgs e)
{
// Aircraft type is in the "AircraftInfo" data group so we only want to proccess that here.
try
{
FSUIPCConnection.Process("AircraftInfo");
// OK so display the string
// With strings the DLL automatically handles the
// ASCII/Unicode conversion and deals with the
// zero terminators.
this.txtAircraftType.Text = aircraftType.Value;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, AppTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
// Next few eventhandlers deal with writing the lights.
// Again we use the BitArray to manage the individual bits.
// I've also used the LightType enum again for the bit numbers, although
// that's not to everyone's taste. Some alternative lines using literal index
// numbers are included as comments for the first two.
private void chkNavigation_CheckedChanged(object sender, EventArgs e)
{
this.lights.Value[(int)LightType.Navigation] = this.chkNavigation.Checked;
//this.lights.Value[0] = this.chkNavigation.Checked;
}
private void chkBeacon_CheckedChanged(object sender, EventArgs e)
{
this.lights.Value[(int)LightType.Beacon] = this.chkBeacon.Checked;
//this.lights.Value[1] = this.chkBeacon.Checked;
}
private void chkLanding_CheckedChanged(object sender, EventArgs e)
{
this.lights.Value[(int)LightType.Landing] = this.chkLanding.Checked;
}
private void chkTaxi_CheckedChanged(object sender, EventArgs e)
{
this.lights.Value[(int)LightType.Taxi] = this.chkTaxi.Checked;
}
private void chkStrobes_CheckedChanged(object sender, EventArgs e)
{
this.lights.Value[(int)LightType.Strobes] = this.chkStrobes.Checked;
}
private void chkInstuments_CheckedChanged(object sender, EventArgs e)
{
this.lights.Value[(int)LightType.Instruments] = this.chkInstuments.Checked;
}
private void chkRecognition_CheckedChanged(object sender, EventArgs e)
{
this.lights.Value[(int)LightType.Recognition] = this.chkRecognition.Checked;
}
private void chkWing_CheckedChanged(object sender, EventArgs e)
{
this.lights.Value[(int)LightType.Wing] = this.chkWing.Checked;
}
private void chkLogo_CheckedChanged(object sender, EventArgs e)
{
this.lights.Value[(int)LightType.Logo] = this.chkLogo.Checked;
}
private void chkCabin_CheckedChanged(object sender, EventArgs e)
{
this.lights.Value[(int)LightType.Cabin] = this.chkCabin.Checked;
}
// Demonstrates a simple 'write' to an Offset.
// To send a value to FSUIPC, just change the Value property.
// The new data will be written during the next Process().
private void chkAvionics_CheckedChanged(object sender, EventArgs e)
{
this.avionics.Value = chkAvionics.Checked ? 1 : 0;
}
// This demonstrates disconnecting an individual Offset.
// After it's disconnected it doesn't get updated from FSUIPC
// and changed to the value of this Offset do not get written
// when Process() is called.
private void btnDisconnect_Click(object sender, EventArgs e)
{
// Disconnect immediatley.
this.compass.Disconnect();
}
// Same as disconnect, except the disconnect happens after
// the next Process. So one more read/write is done and then
// the Offset is disconnected.
private void btnDisconnectAfterNext_Click(object sender, EventArgs e)
{
// Diconnect after the next process.
this.compass.Disconnect(true);
}
// This demonstrates reconnecting an Offset. It's value
// will be read/written during the subsequent Process()
// calls.
private void btnReconnect_Click(object sender, EventArgs e)
{
// Reconnect
this.compass.Reconnect();
}
// Same as reconnect except the the Offset will be disconnected
// again after the next Process() call.
private void btnReconnectOnce_Click(object sender, EventArgs e)
{
// Reconnect, but only for one Process().
// The Offset is then disconnected again.
this.compass.Reconnect(true);
}
// The pause Offset is Write only. It's value is never updated from
// FSUIPC. When it's value changes the new value is written
// to FSUIPC during the next Process().
private void chkPause_CheckedChanged(object sender, EventArgs e)
{
pause.Value = (short)(this.chkPause.Checked ? 1 : 0);
}
private enum LightType
{
Navigation,
Beacon,
Landing,
Taxi,
Strobes,
Instruments,
Recognition,
Wing,
Logo,
Cabin
}
private void chkEnableAIRadar_CheckedChanged(object sender, EventArgs e)
{
// Turn on/off the radar timer to update the radar info (runs every second)
this.AIRadarTimer.Enabled = this.chkEnableAIRadar.Checked;
// Force the panel to redraw now rather than wait one second
this.pnlAIRadar.Invalidate();
}
private void AIRadarTimer_Tick(object sender, EventArgs e)
{
// Every second we update the Ground and Airborne AI trafic info
AI.RefreshAITrafficInformation(this.chkShowGroundAI.Checked, this.chkShowAirborneAI.Checked);
// Apply a filter according to the range set by the user
// Filtering ground and airborne traffic, no bearing filter (include from 0-360)
// No altitude filter (passing nulls)
// Range as set by the combo box.
AI.ApplyFilter(true, true, 0, 360, null, null, double.Parse(this.cbxRadarRange.Text));
// Invalidate the radar panel so it redraws.
this.pnlAIRadar.Invalidate();
}
private void pnlAIRadar_Paint(object sender, PaintEventArgs e)
{
// This gets called whenever the panel needs to draw itself.
if (this.chkEnableAIRadar.Checked)
{
// First Clear the panel and make a black background
e.Graphics.Clear(Color.Black);
// Start by working out the centre of the radar and draw the centre cross
Point centre = new Point(this.pnlAIRadar.ClientSize.Width / 2, this.pnlAIRadar.ClientSize.Height / 2);
e.Graphics.DrawLine(Pens.White, centre.X - 4, centre.Y, centre.X + 4, centre.Y);
e.Graphics.DrawLine(Pens.White, centre.X, centre.Y - 4, centre.X, centre.Y + 4);
double range = double.Parse(this.cbxRadarRange.Text);
// work out the scale using the range and the smallest size of the panel
double scale = range / (double)(this.pnlAIRadar.ClientSize.Width < this.pnlAIRadar.ClientSize.Height ? this.pnlAIRadar.ClientSize.Width : this.pnlAIRadar.ClientSize.Height) * 2d;
// Go through each plane and draw it on the radar
// Note: We are using the seperate collections for the ground and airborne
// There is a collection of all AI traffic called 'AllTraffic' which can be used if
// you do not want to deal with these seperatley.
// First, draw the ground AI if required
if (this.chkShowGroundAI.Checked)
{
// Loop through the collection of plane objects in the GroundTraffic collection.
foreach (AIPlaneInfo plane in AI.GroundTraffic)
{
// Here we just pass the planeInfo off to the draw routine.
// There is quite a lot of information available in the AIPlaneInfo object.
// See the reference manual or Intellisense for details.
drawTarget(e.Graphics, scale, centre, plane);
}
}
// Next, draw the Airborne AI if required
if (this.chkShowAirborneAI.Checked)
{
// Loop through the collection of plane objects in the GroundTraffic collection.
foreach (AIPlaneInfo plane in AI.AirbourneTraffic)
{
drawTarget(e.Graphics, scale, centre, plane);
}
}
}
else
{
// Radar turned off so just clear it with white
e.Graphics.Clear(Color.White);
}
}
private void drawTarget(Graphics graphics, double scale, Point centre, AIPlaneInfo plane)
{
// We are going to use some of the info from the plane object to draw the target.
// Lots more info is avilable for other application.
// See the reference manual or Intellisense for details.
// Work out the range of the target in pixels by multiplying by the scale
double distancePixels = plane.DistanceNM / scale;
// Work out the position from the centre using this distance and the bearing
double dx = Math.Cos(degreeToRadian(plane.BearingTo)) * distancePixels;
double dy = Math.Sin(degreeToRadian(plane.BearingTo)) * distancePixels;
PointF target = new PointF((float)centre.X + (float)dx, (float)centre.Y + (float)dy);
// Draw the target circle around this point oriented to the plane's heading
graphics.DrawEllipse(Pens.LightGreen, target.X - 4f, target.Y - 4f, 8f, 8f);
// Draw a line from the circle to indicate heading
double tailHeading = 180d + plane.HeadingDegrees;
dx = Math.Cos(degreeToRadian(tailHeading)) * 12;
dy = Math.Sin(degreeToRadian(tailHeading)) * 12;
PointF tailEnd = new PointF(target.X + (float)dx, target.Y + (float)dy);
graphics.DrawLine(new Pen(new LinearGradientBrush(target, tailEnd, Color.LightGreen, Color.DarkGreen)), target, tailEnd);
// Work out the position of the data block
PointF dataBlock = new PointF(target.X + 20, target.Y - 20);
// Draw the line to the datablock
graphics.DrawLine(Pens.LightGreen, new PointF(target.X + 5, target.Y - 5), new PointF(dataBlock.X - 5, dataBlock.Y + 7));
// Draw the data block
// Line 1 - the Callsign
graphics.DrawString(plane.ATCIdentifier, this.pnlAIRadar.Font, Brushes.LightGreen, dataBlock);
// Line 2 - the Altitude (hundreds of feet) and speed
string line2 = "";
line2 += ((int)(plane.AltitudeFeet / 100d)).ToString("d3");
// Put a +,- or = depending on if the plane is decending, climbing or level
if (plane.VirticalSpeedFeet < 0)
{
line2 += "-";
}
else if (plane.VirticalSpeedFeet > 0)
{
line2 += "+";
}
else
{
line2 += "=";
}
graphics.DrawString(line2, this.pnlAIRadar.Font, Brushes.LightGreen, new PointF(dataBlock.X, dataBlock.Y + 12));
// Line 3 - origin, destination and assigned runway
graphics.DrawString(plane.DepartureICAO + "->" + plane.DestinationICAO + " " + plane.RunwayAssigned.ToString(), this.pnlAIRadar.Font, Brushes.LightGreen, new PointF(dataBlock.X, dataBlock.Y + 24));
}
private double degreeToRadian(double angle)
{
return Math.PI * angle / 180.0;
}
private void btnMoveToEGLL_Click(object sender, EventArgs e)
{
// Suspend the timers
if (this.timer1.Enabled)
{
this.timer1.Enabled = false;
if (this.chkEnableAIRadar.Checked)
{
this.AIRadarTimer.Enabled = false;
}
// Put the sim into Slew mode
slewMode.Value = 1;
FSUIPCConnection.Process();
// Make a new point representing the centre of the threshold for 27L
FsLatitude lat = new FsLatitude(51.464943d);
FsLongitude lon = new FsLongitude(-0.434046d);
FsLatLonPoint newPos = new FsLatLonPoint(lat, lon);
// Now move this point 150 metres up the runway
// Use one of the OffsetBy methods of the FsLatLonPoint class
double rwyTrueHeading = 269.7d;
newPos = newPos.OffsetByMetres(rwyTrueHeading, 150);
// Set the new position
playerLatitude.Value = newPos.Latitude.ToFSUnits8();
playerLongitude.Value = newPos.Longitude.ToFSUnits8();
// set the heading and altitude
playerAltitude.Value = 0;
playerHeadingTrue.Value = (uint)(rwyTrueHeading * (65536d * 65536d) / 360d);
FSUIPCConnection.Process();
// Turn off the slew mode
slewMode.Value = 0;
FSUIPCConnection.Process();
// Refresh the scenery
sendControl.Value = REFRESH_SCENERY;
FSUIPCConnection.Process();
// Reenable the timers
this.timer1.Enabled = true;
this.AIRadarTimer.Enabled = this.chkEnableAIRadar.Checked;
}
}
}
// A double buffered panel used for the radar scope.
// The normal panel that .NET supplies doesn't use double buffering
// and therefore suffers from massive flickering issues.
public class DoubleBufferPanel : Panel
{
public DoubleBufferPanel()
{
// Set the value of the double-buffering style bits to true.
this.SetStyle(ControlStyles.DoubleBuffer |
ControlStyles.UserPaint |
ControlStyles.AllPaintingInWmPaint,
true);
this.UpdateStyles();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment