Click here to Skip to main content
Licence CPOL
First Posted 1 Jul 2008
Views 95,906
Downloads 2,711
Bookmarked 131 times

C# Avionic Instrument Controls

By Chootair | 17 Jul 2008
The aim of this C# project is to purpose six aircraft cockpit instruments usable in forms as any other C# controls.
1 vote, 2.6%
1

2
1 vote, 2.6%
3
5 votes, 13.2%
4
31 votes, 81.6%
5
4.84/5 - 38 votes
2 removed
μ 4.66, σa 1.34 [?]

Introduction

The aim of this C# project is to purpose six aircraft cockpit instruments usable in forms as any other C# controls and to define a generic instrument class in order to design any kind of dashboard instruments.

Description

The controls are built with bitmaps which are rotated, translated or scaled before to be displayed. The basic methods for rotate, translate and scale images are defined in the mother class. Each control then uses its dedicated parameters (related to a physical signification) in order to manipulates the images.

Aircraft Instruments

  • Air speed indicator: airspeed (kts)
  • Attitude Indicator: pitch (deg), roll (deg)
  • Altimeter: altitude (ft)
  • Turn Coordinator: turn rate (deg/min)
  • Vertical speed indicator: vertical speed (ft/min)
  • Heading indicator: heading (deg)

Details of the Key Functions

This section explains in detail the implementation of the basic functions defined in the InstrumentControl class.

Rotate Image

Implementation

The rotation of the image is divided in two main parts:

First, the rotation of the PaintEventArgs coordinate system around the upper left corner of the drawing area.

Second, the drawing of the image corrected by translation offset in order to display the image as if it has turned around a user defined point.

AvionicsControlDemo_RotationMainSteps.JPG

Let’s see step by step:

Step 0: Initial situation.

AvionicsControlDemo_RotationPhase0.JPG

Step 1: Rotate the PaintEventArgs coordinate system around the left upper corner of the paint area.

AvionicsControlDemo_RotationPhase1.JPG

Corresponding code sample:

// Rotate image support
pe.Graphics.RotateTransform((float)(alpha * 180 / Math.PI));

Step 2: Draw the image and apply the translation correction.

AvionicsControlDemo_RotationPhase2.JPG

Corresponding code sample:

// Display image
pe.Graphics.DrawImage(img, (ptImg.X + deltaX) * scaleFactor, (ptImg.Y + deltaY) * 
	scaleFactor, img.Width * scaleFactor, img.Height * scaleFactor);

Step 3 (Final step): Put the PainEventArgs coordinate system as found.

AvionicsControlDemo_RotationPhase3.JPG

Corresponding code sample:

// Put image support as found
pe.Graphics.RotateTransform((float)(-alpha * 180 / Math.PI));

The key point in those operations is the calculation of the translation correction coefficients.

The next figure explains the geometrics considerations:

AvionicsControlDemo_RotationDetailedScheme.JPG

G0 is the user defined rotation center
G1 is the G0 position after the step 1.

The aim of this section is to identify the G1G0 translation and apply the corresponding offset in order to draw the rotation point as if it has not moved.

Then we work with the geometrics definitions:

a. AvionicsControlDemo_RotationCalcs1.JPG
b. AvionicsControlDemo_RotationCalcs2.JPG
c. AvionicsControlDemo_RotationCalcs3.JPG
d. AvionicsControlDemo_RotationCalcs4.JPG

As a result, the offset coefficients are:

AvionicsControlDemo_RotationCalcs5.JPG

The corresponding code sample is as follows:

// Computed offset
deltaX = (float)(d * (Math.Cos(alpha - beta) - Math.Cos(alpha)* 
	Math.Cos(alpha + beta) - Math.Sin(alpha) * Math.Sin(alpha+ beta)));
deltaY = (float)(d * (Math.Sin(beta - alpha) + Math.Sin(alpha)* 
	Math.Cos(alpha + beta) - Math.Cos(alpha) * Math.Sin(alpha + beta)));

Parameters

  • "pe": The paint area event where the image will be displayed
  • "img": The image to display
  • "alpha": The angle of rotation in radian
  • "ptImg": The location of the left upper corner of the image to display in the paint area in nominal situation
  • "ptRot": The location of the rotation point in the paint area
  • "scaleFactor": Multiplication factor on the display image

AvionicsControlDemo_RotationFunctionParameters.JPG

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Chootair

Engineer

France France

Member


Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
QuestionImport to VB.Net PinmemberJTmetoo10:06 28 Aug '11  
QuestionGreat Job PinmemberLamar Seifuddin22:36 29 Jul '11  
QuestionC# avionic controls PinmemberJPC062404:31 20 Jun '11  
Generalthank you Pinmemberyardloun5:56 4 Apr '11  
GeneralMy vote of 5 PinmemberYEBM Knight4:14 8 Feb '11  
GeneralGreat work - I am using these with the AR Drone Pinmembershobley7:52 17 Oct '10  
Generali love the control I hope PinmemberStringDotEmpty3:23 16 May '10  
GeneralWell done. PinmemberSerhat21:47 20 Apr '10  
GeneralConnect up to FlightGear flight simulator Pinmembersteveyork7:25 28 Oct '09  
GeneralAdd to asp.net page Pinmembergirishdpatil21:09 24 Feb '09  
GeneralAdd to projects PinmemberFrank Shearer15:28 20 Feb '09  
GeneralRe: Add to projects Pinmemberalejos4:26 30 Mar '10  
GeneralRe: Add to projects PinmemberSerhat22:14 20 Apr '10  
GeneralRe: Add to projects PinmemberMember 77961737:29 20 Apr '11  
GeneralRe: Add to projects PinmemberMember 779617312:30 20 Apr '11  
GeneralTransform PinmemberNicolasG11:59 25 Jul '08  
GeneralRe: Transform PinmemberDrGanjoo0:08 12 Nov '08  
GeneralExcellent look and feel to these controls - well done PinmemberBradOsterloo13:05 21 Jul '08  
GeneralCool! Pinmemberbigals14:55 20 Jul '08  
GeneralFantastic job.... But there is something missing... PinmemberThe Infomercial King8:11 18 Jul '08  
GeneralRe: Fantastic job.... But there is something missing... Pinmemberandre123459:53 18 Jul '08  
GeneralRe: Fantastic job.... But there is something missing... PinmemberThe Infomercial King10:55 18 Jul '08  
GeneralAH for 90 degree pitch Pinmemberjknickel1:24 8 Jul '08  
I changed the Horizon Bitmap so you can display a full +/-90 degree pitch angle, i.e. a loop. I had to stretch the image to include 90 degree and past that point to 60 and -60 degrees in both directions. Then I had to modify the OnPaint method as below
        protected override void OnPaint(PaintEventArgs pe)
        {
            // Calling the base class OnPaint
            base.OnPaint(pe);
 
            // Pre Display computings

            Point ptBoule = new Point(25, - 330);
            Point ptRotation = new Point(150, 150);
 
            float scale = (float)this.Width / bmpCadran.Width;
 
            // Affichages - - - - - - - - - - - - - - - - - - - - - - 

            bmpCadran.MakeTransparent(Color.Yellow);
            bmpAvion.MakeTransparent(Color.Yellow);
 
            // display Horizon
            RotateAndTranslate(pe, bmpBoule, RollAngle, 0, ptBoule, (int)(4*PitchAngle), ptRotation, scale);
 
            // diplay mask
            Pen maskPen = new Pen(this.BackColor,30*scale);
            pe.Graphics.DrawRectangle(maskPen, 0, 0, bmpCadran.Width * scale, bmpCadran.Height * scale);
 
            // display cadran
            pe.Graphics.DrawImage(bmpCadran, 0, 0, (float)(bmpCadran.Width * scale), (float)(bmpCadran.Height * scale));
 
            // display aircraft symbol
            pe.Graphics.DrawImage(bmpAvion, (float)((0.5 * bmpCadran.Width - 0.5 * bmpAvion.Width) * scale), (float)((0.5 * bmpCadran.Height - 0.5 * bmpAvion.Height) * scale), (float)(bmpAvion.Width * scale), (float)(bmpAvion.Height * scale));
 
        }
 
It would also be nice if the altitude digits scrolled more smoothly, instead of just on step based on the previous decade digit. e.g It should scroll for values around 10 feet before the digit must change.
 
This is what I did for the altimeter I did a while bac, which is fairly similar. I also use a background image to do the drawing before painting it to the current canvas. It helps with
private void Altimeter_Paint(object sender, PaintEventArgs e)
{
	try
	{
		SolidBrush NeedleBrush = new SolidBrush(Color.Yellow);
		// Draw the background Image
		this.BackgroundImage = new Bitmap(this.Width, this.Height);
		Graphics Background = Graphics.FromImage(this.BackgroundImage);
		// Draw the dial
		Background.DrawImage(this.DialImage, 0, 0);
		// Draw the scrolling numbers
		float imageIndex;
 
		// First the 10s numbers
		imageIndex = this.mAltitude % 100;
		imageIndex = imageIndex / 10;
		float lowDigit = imageIndex;
		imageIndex = (imageIndex * this.DigitHeight_10) + this.DigitStart_10;
		Background.DrawImage(this.TensImage, this.DigitRect_10, new Rectangle(0, (int)imageIndex, this.TensImage.Width, this.DigitRect_10.Height), GraphicsUnit.Pixel);
 
		// Then the 100s
		imageIndex = this.mAltitude / 100;
		imageIndex = imageIndex % 10;
		imageIndex = (float)Math.Floor(imageIndex);
		imageIndex = (imageIndex * this.DigitHeight_10) + this.DigitStart_100;
		// Slowly shift this digit when it reaches 90 feet
		if (lowDigit > 9)
		{
			float shift = (lowDigit - 9) * this.DigitHeight_10;
			imageIndex += shift;
		}
		// Draw the digit
		Background.DrawImage(this.HundredsImage, this.DigitRect_100, new Rectangle(0, (int)imageIndex, this.HundredsImage.Width, this.DigitRect_100.Height), GraphicsUnit.Pixel);
 
		// Then the 1000s
		if (this.mAltitude < 990)
		{
			Background.DrawImage(this.FlagImage, this.DigitRect_1000, new Rectangle(0, 0, this.FlagImage.Width, this.FlagImage.Height), GraphicsUnit.Pixel);
		}
		else
		{
			imageIndex = this.mAltitude / 1000;
			imageIndex = imageIndex % 10;
			// Check to see this digit is moving
			bool updateDigit = false;
			if ((imageIndex % 1) > 0.99)
			{
				updateDigit = true;
			}
			imageIndex = (float)Math.Floor(imageIndex);
			imageIndex = (imageIndex * this.DigitHeight_1000) + this.DigitStart_1000;
			// Slowly shift this digit when it reaches 90 feet
			if ((lowDigit > 9) && updateDigit)
			{
				float shift = (lowDigit - 9) * this.DigitHeight_1000;
				imageIndex += shift;
			}
			Background.DrawImage(this.ThousandsImage, this.DigitRect_1000, new Rectangle(0, (int)imageIndex, this.ThousandsImage.Width, this.DigitRect_1000.Height), GraphicsUnit.Pixel);
		}
 
		// Finally the 10,000s, if there is no 10,000s, then display the number as flagged
		if (this.mAltitude < 9990)
		{
			Background.DrawImage(this.FlagImage, this.DigitRect_10000, new Rectangle(0, 0, this.FlagImage.Width, this.FlagImage.Height), GraphicsUnit.Pixel);
		}
		else
		{
			imageIndex = this.mAltitude / 10000;
			// Check to see this digit is moving
			bool updateDigit = false;
			if ((imageIndex % 1) > 0.999)
			{
				updateDigit = true;
			}
			imageIndex = (float)Math.Floor(imageIndex);
			imageIndex = (imageIndex * this.DigitHeight_1000) + this.DigitStart_1000;
			// Slowly shift this digit when it reaches 90 feet
			if ((lowDigit > 9) && updateDigit)
			{
				float shift = (lowDigit - 9) * this.DigitHeight_1000;
				imageIndex += shift;
			}
			Background.DrawImage(this.ThousandsImage, this.DigitRect_10000, new Rectangle(0, (int)imageIndex, this.ThousandsImage.Width, this.DigitRect_10000.Height), GraphicsUnit.Pixel);
		}
 
		// Draw the pointer
		float altNeedleAngle = (((this.mAltitude % 100) / 100.0F) * 360.0F) - 90.0F;
		// Work out the needle endpoint
		Point altPoint = new Point();
		altPoint.X = this.dialCentre.X + (int)Math.Round(this.NeedleLength * Math.Cos(altNeedleAngle * Math.PI / 180));
		altPoint.Y = this.dialCentre.Y + (int)Math.Round(this.NeedleLength * Math.Sin(altNeedleAngle * Math.PI / 180));
		Point TipPoint1 = new Point();
		TipPoint1.X = this.dialCentre.X + (int)Math.Round((this.NeedleLength - 8) * Math.Cos((altNeedleAngle - 5) * Math.PI / 180));
		TipPoint1.Y = this.dialCentre.Y + (int)Math.Round((this.NeedleLength - 8) * Math.Sin((altNeedleAngle - 5) * Math.PI / 180));
		Point TipPoint2 = new Point();
		TipPoint2.X = this.dialCentre.X + (int)Math.Round((this.NeedleLength - 8) * Math.Cos((altNeedleAngle + 5) * Math.PI / 180));
		TipPoint2.Y = this.dialCentre.Y + (int)Math.Round((this.NeedleLength - 8) * Math.Sin((altNeedleAngle + 5) * Math.PI / 180));
		Point TipPoint3 = new Point();
		TipPoint3.X = this.dialCentre.X + (int)Math.Round(this.NeedleStart * Math.Cos(altNeedleAngle * Math.PI / 180));
		TipPoint3.Y = this.dialCentre.Y + (int)Math.Round(this.NeedleStart * Math.Sin(altNeedleAngle * Math.PI / 180));
 
		// Create the polygon with the points
		Point[] altNeedle = {
			altPoint,			// The end of the needle
			TipPoint1,			// The left side pint
			TipPoint3,			// The inner tip point
			TipPoint2 };		// The right side other side
		// Draw the polygon
		Background.FillPolygon(NeedleBrush, altNeedle);
		// Paste the image to the control
		e.Graphics.DrawImage(this.BackgroundImage, 0, 0);
 
		// Freeup the resources
		NeedleBrush.Dispose();
		this.BackgroundImage.Dispose();
		Background.Dispose();
	}
	catch (Exception ex)
	{
		if (ex.Message != "")
		{ }
	}
}

GeneralRe: AH for 90 degree pitch PinmemberChootair12:59 17 Jul '08  
GeneralGreat stuff Pinmemberjknickel23:54 7 Jul '08  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web04 | 2.5.120210.1 | Last Updated 17 Jul 2008
Article Copyright 2008 by Chootair
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid