Click here to Skip to main content
Email Password   helpLost your password?

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 instuments.

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 his 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 details the implementation of the basic functions defined in the InstrumentControl class.

Rotate Image

Implementation

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

First : the rotation of the PaintEventArgs coordinate system around the upper left corner of the drowing area.

Second : the drowing 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 applies the translation correction.

AvionicsControlDemo_RotationPhase2.JPG

Corresponding code sample :

// Dispay 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 explain 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 identified the G1G0 translation and applied 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


Corresponding code sample :

  // 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

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralConnect up to FlightGear flight simulator
steveyork
7:25 28 Oct '09  
Hi All,

Thanks Guillaume for some great software. I have modified DemoWinow() to use a WinSock TCP/IP connection to direct network output of instrument data from the Flight Gear simulator. It works very well.

I have removed the user controls, added a few additional dials (e.g. for engine) and am in the process of mounting the display in a physical aircraft mockup that will include 2 degrees of freedom motion, with motors driven via x,y,z acceleration outputs from Flightgear.

Guillaume, if you read this, would it be ok to photo and publish the 'new' software?

I can be emailed thrugh this site.

Many thanks.
GeneralAdd to asp.net page
girishdpatil
21:09 24 Feb '09  
This is very good Gauges for Windows form. Can you please tell me how to use these controls in C#,ASP.NET

Regards
Girish
GeneralAdd to projects
Frank Shearer
15:28 20 Feb '09  
How would you add the controls to another project? I keep getting failed to create component error
GeneralTransform
NicolasG
11:59 25 Jul '08  
Nice looking controls.
Just a remark : i think the Graphics class can do the exact rotation by using TranslateTransform to change the rotation center.
GeneralRe: Transform
DrGanjoo
0:08 12 Nov '08  
Excellent controls Chootair.

NicolasG, since transformation matrices can be combined together I also think it would work if we translate the rotation point to origin, rotate and then translate back. e.g. the following would work in case of Attitude control where the rotation is about the mid point say 150,150:

GraphicsState oldState = graphics.Save(); // save existing matrices

// make rotation point the origin
graphics.TranslateTransform(-150, -150);

// rotate
graphics.RotateTransform((float)(alphaRot * 180 / Math.PI), MatrixOrder.Append);

// translate back
graphics.TranslateTransform(150, 150, MatrixOrder.Append);

// code to draw image comes here
....
....

graphics.Restore(oldState); // restore old

In any case, the controls work great as is.
GeneralExcellent look and feel to these controls - well done
BradOsterloo
13:05 21 Jul '08  
Thanks for sharing these
GeneralCool!
bigals
14:55 20 Jul '08  
Nice job!

A few things;

Plural of foot is feet, not "feet's" (trivial I know, but I hold an ATPL so the more realistic it is the better Wink

An altimeter has a window to set the ambient pressure with the standard ISA atmosphere being 1013 hPa or 29.92 millibars (US).

Would be really cool to custom draw atleast the ASI, so you can set various V speeds, also building a Pitot Static interface so you can demonstrate what would happen if your Pitot or Static sources are blocked during an climb or decent.

Heading bugs and V speed bugs would also be awesome.

Drawing Flight Director bars and/or LOC/GS bars to show what an analog display looks like would be cool too. Smile


Al
GeneralFantastic job.... But there is something missing...
The Infomercial King
8:11 18 Jul '08  
Hi,

Nice job but you left one thing out...

I am a pilot and I fly fixed and rotary wing, and lots of different aircraft....

You left out "shudder" !!!!

All flight instruments I have seen have "shudder" where the needle is never pegged but vibrates around a value... adding shudder would give your controls a sense of realism....

Bill

The Infomercial King(tm)

GeneralRe: Fantastic job.... But there is something missing...
andre12345
9:53 18 Jul '08  
Have you tried A/C with "Glass Cockpits" display systems? I wounder if a A380 pilot would want "shudder" on is display, but I am not a pilot... Smile
GeneralRe: Fantastic job.... But there is something missing...
The Infomercial King
10:55 18 Jul '08  
Hi,

I am a pilot and not an engineer but I think the "shudder" in the prop planes and jets I have flown is due to the vibration... even a gentle vibration in a Lear or Falcon or Tri-Star jet will cause the needle to of non-digital displays to shudder.

I guess I should have also mention that you don't have shudder with all digital displays but with real needles they vibrate.

Great controls!

Bill

The Infomercial King(tm)

GeneralAH for 90 degree pitch
jknickel
1: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
Chootair
12:59 17 Jul '08  
Thank you for your improvements on the code, it is cool.
About the altimeter scrolling it is true that is not as smooth as on real instrument. I tried several solution and I finally chose to just offset the draw of the "9". In fact my main constraint was that the number of digits could be defined as a function parameter so I do not difference the unit digit, the ten digit, the hundred digit ect... there are draw equals.
But I'll try to improve that, inspired by your solution.

Merci,
A plus.
GeneralGreat stuff
jknickel
23:54 7 Jul '08  
I have been working on aircraft instrument for some time now as well. These are well done. I used photographs of the actual instrument and edited it in GIMP so I can draw the mechanics. Nice to see someone else is also keen on this. I should add my code some time as well. I built mine for a pocket PC, but will work for any DotNet platform. Transparency is a problem for the Pocket PC, so I had to draw the needles with polygons.

Regards,
Jeff
GeneralSuperb but...
Joe Sonderegger
23:01 7 Jul '08  
This is superb code.
Only the artificial horizon needs to be able to go to 90° (so that you can fly upside down...

Have a nice life!!

GeneralGreat work....
Peter Villadsen
15:33 7 Jul '08  
but the plural of foot is feet, not feets. This is not easy to change as it is on a bitmap, but it would drive me crazy. Other than that trivial little typo, this is very impressive work.
GeneralRe: Great work....
Chootair
13:03 17 Jul '08  
Thank you for your remark.
I'll correct this mistake for the next release.

Merci,
A plus.
JokeImpressive
Ivertheengine
10:32 7 Jul '08  
Just as long as M$ Dot Nuts doesn't get used in a real airplanes!
( If you go .NET you go Dot Nuts )
GeneralWow!
Rafferty Uy
16:48 3 Jul '08  
I don't know if this is really an article because there is not much text BUT this is an awesome gadget!

Rafferty

GeneralVery nice indeed. Question...
thompsons
7:42 2 Jul '08  
Could you tell us how the bitmaps were generated? Which software used?

Regards,
Steve.
GeneralVery Good
JBAK_CP
4:41 2 Jul '08  
Keep up the good work!
GeneralRemarkable!
LastZolex
0:15 2 Jul '08  
Wow, this is great.

I wrote a commercial instrument flight simulator 10 years ago in MFC C++... nothing was as neat as your controls are, thank you for sharing!

Smile
GeneralExcellento!
Tage Lejon
22:08 1 Jul '08  
Cool controls!
GeneralThis could be very cool
Bert delaVega
18:28 1 Jul '08  
But it lacks an article. If you give some background on the real controls, how you've simulated them in a UI and other interesting notes, it would be great! I don't recall seeing anything else like it here on cp.

Definitely a good start and idea.
GeneralAlitmeter
sgenie68
15:17 1 Jul '08  
Altimeter should have pressure setting window - normally where you output digital alt reading. Otherwise - very impressive set of controls Smile


Last Updated 17 Jul 2008 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010