How To Draw A Compass
- Download Compass_Sources_and_Build.zip - 114.6 KB
Introduction
This lawmaking shows how to draw a compass and how to use it with a NMEA uniform GPS device.
Background
For me this was just a prelude for some other projection, testing out how to access GPS data from a NMEA device and information technology was fun to draw a Compass.
Using the code
You lot tin easily modify the code to support your custom adruino electronic compass if you similar. The compass class is independently from the data source reusable. Just call the static DrawCompass method to depict the compass.
pictureBox1.Image = Compass.DrawCompass(degree, pitch, lxxx, tilt, 80, pictureBox1.Size);
Points of Interest
I found quite abrasive that NMEA devices seem not to ship an identifier, and so when opening Com ports to scan for one you really have to expect for the next data to arrive, parse it and if it is NMEA data accept it, and the close all other Com Ports, or y'all force the user configure the used Com Port to make it less userfriendly ;-). Important is to abort all open up threads on Exiting the App, for else it won't get out due to the blocked threads. To have them Blocked at opening/closing all ports was necessary equally it seems that the SerialPort Object seems not threadsafe in that it looses it's eventhandlers otherwise or the object disposes itself despite nevertheless having references, when opened from within another thread, a thread.Join seems to alleviate the event but creating some few blocked threads for each airtight port that notwithstanding have to be aborted at the end of the application. I presume this approach could be used for other types of serial devices equally well. Delight send me a note if you find a better solution to this trouble.
void DisconnectGPS() { if (serialPort1 != null) { try { if (serialPort1.IsOpen) serialPort1.Close(); serialPort1.Dispose(); } catch { } } if (_Serial_Ports != null) { for (int i = _Serial_Ports.Length - i; i >= 0; i--) { System.IO.Ports.SerialPort p = _Serial_Ports[i]; if (p != nil) { try { if (p.IsOpen) p.Close(); p.Dispose(); } take hold of { } } } } foreach (Thread t in _Gps_Threads) { t.Abort(); } } void ConnectGPS() { String[] portnames = Arrangement.IO.Ports.SerialPort.GetPortNames(); _Serial_Ports = new System.IO.Ports.SerialPort[portnames.Length]; _Gps_Threads = new Thread[portnames.Length]; for (int i = 0; i < portnames.Length; i++) { System.IO.Ports.SerialPort ssp = new Organisation.IO.Ports.SerialPort(portnames[i]); try { object data0 = (object)new object[] { ssp, i }; System.Threading.Thread t1 = new Thread(delegate(object data) { System.IO.Ports.SerialPort sspt1 = (System.IO.Ports.SerialPort)((object[])data)[0]; int it1 = (int)((object[])information)[one]; _Serial_Ports[it1] = sspt1; endeavour { sspt1.DataReceived += serialPort1_DataReceived; sspt1.Open(); } grab { } System.Threading.Thread.Sleep(3000); endeavour { foreach (System.IO.Ports.SerialPort sspt2 in _Serial_Ports.Where(r => !r.PortName.Equals(serialPort1.PortName))) { if (sspt2.IsOpen) sspt2.Close(); sspt2.Dispose(); } } grab { } Arrangement.Threading.Thread.CurrentThread.Bring together(); }); _Gps_Threads[i] = t1; t1.Start(data0); } catch { } } }
To identify a NMEA compatible device you merely read out what's coming from the port. Does it lucifer your expected NMEA sentences, then you got it and fix your serialPort1=p. The beneath code works for Magellan Explorist devices. Not certain if GPRMC sentence is provided with all NMEA device. You can easily alter information technology to match it your device. NMEA Sentence Specs that worked for me, I constitute hither: http://aprs.gids.nl/nmea/
It'south pretty easy to piece of work out I'd say. Consider that the NMEA device may loose the satelite connectedness and you may get blanks from the device in those lines.
void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) { try { System.IO.Ports.SerialPort p = ((Organisation.IO.Ports.SerialPort)sender); cord data = p.ReadExisting(); cord[] strArr = data.Split(' $'); for (int i = 0; i < strArr.Length; i++) { string strTemp = strArr[i]; string[] nmea = strTemp.Split(' ,'); if (nmea[0] == " GPRMC") { serialPort1 = p; if (!String.IsNullOrEmpty(nmea[eight]) ) { degree = Convert.ToDouble(nmea[8]); pitch = 0; tilt = 0; pictureBox1.Image = Compass.DrawCompass(degree, pitch, 80, tilt, 80, pictureBox1.Size); } } } } catch {} }
To draw the Compass itself is pretty straight frontwards. Simply using a little bit of geometry and DrawLine, DrawElipse, DrawString... run into beneath what y'all can do with these simple commands. Nearly amazing well-nigh it is that it runs in only a few milliseconds (on ii year quondam my notebook its only 5ms per DrawCompass).
To draw the compass onto an existing Bitmap simply modify the method to not use "result" merely to utilize the passed on Bitmap and utilize the size of the existing bitmap or whichever size yous want he compass to exist and to modify it'due south location on the bitmap is easy enough by modifying the xcenterpoint and ycenterpoint.
Pitch and Tilt is not used when with a GPS device, equally you lot become your heading from the device's location and movement, but if you utilise a magnetic compass (i.due east. adruino device) you may want to show the pitch or tilt when calibrating, then I added the pitch and tilt so you lot tin can roll information technology, just similar on your smart telephone and see how it deviates from a 0 caste pitch / tilt roll.
public class Compass { public static Bitmap DrawCompass(double caste, double pitch, double maxpitch, double tilt, double maxtilt, Size s) { double maxRadius = due south.Width > s.Elevation ? s.Height / 2 : south.Width / ii; double sizeMultiplier = maxRadius / 200; double relativepitch = pitch / maxpitch; double relativetilt = tilt / maxtilt; Bitmap result=null; SolidBrush drawBrushWhite = new SolidBrush(Colour.FromArgb(255, 244, 255)); SolidBrush drawBrushRed = new SolidBrush(Color.FromArgb(240, 255, 0, 0)); SolidBrush drawBrushOrange = new SolidBrush(Color.FromArgb(240, 255, 150, 0)); SolidBrush drawBrushBlue = new SolidBrush(Color.FromArgb(100, 0, 250, 255)); SolidBrush drawBrushWhiteGrey = new SolidBrush(Color.FromArgb(20, 255, 255, 255)); double outerradius = (((maxRadius - sizeMultiplier * 60) / maxRadius) * maxRadius); double innerradius = (((maxRadius - sizeMultiplier * 90) / maxRadius) * maxRadius); double degreeRadius = outerradius + 37 * sizeMultiplier; double dirRadius = innerradius - 30 * sizeMultiplier; double TriRadius = outerradius + xx * sizeMultiplier; double PitchTiltRadius = innerradius * 0.55; if (s.Width * s.Summit > 0) { event=new Bitmap(s.Width, s.Top); using (Font font2 = new Font(" Arial", (bladder)(16 * sizeMultiplier))) { using (Font font1 = new Font(" Arial", (float)(14 * sizeMultiplier))) { using (Pen penblue = new Pen(Color.FromArgb(100, 0, 250, 255), ((int)(sizeMultiplier) < 4 ? 4 : (int)(sizeMultiplier)))) { using (Pen penorange = new Pen(Color.FromArgb(255, 150, 0), ((int)(sizeMultiplier) < one ? 1 : (int)(sizeMultiplier)))) { using (Pen penred = new Pen(Color.FromArgb(255, 0, 0), ((int)(sizeMultiplier) < 1 ? 1 : (int)(sizeMultiplier)))) { using (Pen pen1 = new Pen(Color.FromArgb(255, 255, 255), (int)(sizeMultiplier * 4))) { using (Pen pen2 = new Pen(Color.FromArgb(255, 255, 255), ((int)(sizeMultiplier) < ane ? ane : (int)(sizeMultiplier)))) { using (Pen pen3 = new Pen(Color.FromArgb(0, 255, 255, 255), ((int)(sizeMultiplier) < 1 ? 1 : (int)(sizeMultiplier)))) { using (Graphics grand = Graphics.FromImage(upshot)) { double sourcewidth = s.Width; double sourceheight = south.Height; int xcenterpoint = (int)(s.Width / two); int ycenterpoint = (int)((s.Acme / two)); Bespeak pA1 = new Point(xcenterpoint, ycenterpoint - (int)(sizeMultiplier * 45)); Betoken pB1 = new Signal(xcenterpoint - (int)(sizeMultiplier * seven), ycenterpoint - (int)(sizeMultiplier * 45)); Point pC1 = new Indicate(xcenterpoint, ycenterpoint - (int)(sizeMultiplier * 90)); Betoken pB2 = new Point(xcenterpoint + (int)(sizeMultiplier * 7), ycenterpoint - (int)(sizeMultiplier * 45)); Betoken[] a2 = new Bespeak[] { pA1, pB1, pC1 }; Signal[] a3 = new Point[] { pA1, pB2, pC1 }; m.DrawPolygon(penred, a2); yard.FillPolygon(drawBrushRed, a2); g.DrawPolygon(penred, a3); g.FillPolygon(drawBrushWhite, a3); double[] Cos = new double[360]; double[] Sin = new double[360]; 1000.DrawLine(pen2, new Betoken(((int)(xcenterpoint - (PitchTiltRadius - sizeMultiplier * 50))), ycenterpoint), new Point(((int)(xcenterpoint + (PitchTiltRadius - sizeMultiplier * 50))), ycenterpoint)); g.DrawLine(pen2, new Signal(xcenterpoint, (int)(ycenterpoint - (PitchTiltRadius - sizeMultiplier * l))), new Signal(xcenterpoint, ((int)(ycenterpoint + (PitchTiltRadius - sizeMultiplier * 50))))); Point PitchTiltCenter = new Betoken((int)(xcenterpoint + PitchTiltRadius * relativetilt), (int)(ycenterpoint - PitchTiltRadius * relativepitch)); int rad = (int)(sizeMultiplier * 8); int rad2 = (int)(sizeMultiplier * 25); Rectangle r = new Rectangle((int)(PitchTiltCenter.Ten - rad2), (int)(PitchTiltCenter.Y - rad2), (int)(rad2 * 2), (int)(rad2 * 2)); g.DrawEllipse(pen3, r); yard.FillEllipse(drawBrushWhiteGrey, r); g.DrawLine(penorange, PitchTiltCenter.X - rad, PitchTiltCenter.Y, PitchTiltCenter.10 + rad, PitchTiltCenter.Y); thousand.DrawLine(penorange, PitchTiltCenter.X, PitchTiltCenter.Y - rad, PitchTiltCenter.X, PitchTiltCenter.Y + rad); for (int d = 0; d < 360; d++) { double angleInRadians = ((((double)d) + 270d) - degree) / 180F * Math.PI; Cos[d] = Math.Cos(angleInRadians); Sin[d] = Math.Sin(angleInRadians); } for (int d = 0; d < 360; d++) { Bespeak p1 = new Point((int)(outerradius * Cos[d]) + xcenterpoint, (int)(outerradius * Sin[d]) + ycenterpoint); Signal p2 = new Betoken((int)(innerradius * Cos[d]) + xcenterpoint, (int)(innerradius * Sin[d]) + ycenterpoint); if (d % 30 == 0) { m.DrawLine(penblue, p1, p2); Point p3 = new Point((int)(degreeRadius * Cos[d]) + xcenterpoint, (int)(degreeRadius * Sin[d]) + ycenterpoint); SizeF s1 = chiliad.MeasureString(d.ToString(), font1); p3.X = p3.Ten - (int)(s1.Width / 2); p3.Y = p3.Y - (int)(s1.Height / 2); m.DrawString(d.ToString(), font1, drawBrushWhite, p3); Point pA = new Point((int)(TriRadius * Cos[d]) + xcenterpoint, (int)(TriRadius * Sin[d]) + ycenterpoint); int width = (int)(sizeMultiplier * 3); int dp = d + width > 359 ? d + width - 360 : d + width; int dm = d - width < 0 ? d - width + 360 : d - width; Betoken pb = new Betoken((int)((TriRadius - (15 * sizeMultiplier)) * Cos[dm]) + xcenterpoint, (int)((TriRadius - (15 * sizeMultiplier)) * Sin[dm]) + ycenterpoint); Point pC = new Indicate((int)((TriRadius - (15 * sizeMultiplier)) * Cos[dp]) + xcenterpoint, (int)((TriRadius - (fifteen * sizeMultiplier)) * Sin[dp]) + ycenterpoint); Pen p = penblue; Brush b = drawBrushBlue; if (d == 0) { p = penred; b = drawBrushRed; } Point[] a = new Indicate[] { pA, atomic number 82, pC }; g.DrawPolygon(p, a); g.FillPolygon(b, a); } else if (d % ii == 0) g.DrawLine(pen2, p1, p2); if (d % 90 == 0) { string dir = (d == 0 ? " N" : (d == ninety ? " E" : (d == 180 ? " S" : " W"))); Point p4 = new Point((int)(dirRadius * Cos[d]) + xcenterpoint, (int)(dirRadius * Sin[d]) + ycenterpoint); SizeF s2 = thousand.MeasureString(dir, font1); p4.X = p4.Ten - (int)(s2.Width / two); p4.Y = p4.Y - (int)(s2.Height / 2); g.DrawString(dir, font1, d == 0 ? drawBrushRed : drawBrushBlue, p4); } } String deg = Math.Circular(degree, 2).ToString(" 0.00") + " °"; SizeF s3 = g.MeasureString(deg, font1); g.DrawString(deg, font2, drawBrushOrange, new Point(xcenterpoint - (int)(s3.Width / 2), ycenterpoint - (int)(sizeMultiplier * xl))); } } } } } } } } } } return result; } }
The Truth nearly Northward
I intentionally called it a "simple Compass" not to open up the can of worms regarding all the other topics of navigation. Only now that the can has been opened we may equally well mention and bespeak out is that GPS devices determine ane's heading ordinarily by computing the vector between the GPS Ready points. Correct me if I am wrong, that significant that a GPS compass will work only as long as yous are moving, unless information technology has a secondary magnetic sensor, or other means of determining the heading (i.e. cell tower triangulation, wireless network information, etc.) built in too. Magnetic due north nevertheless would demand to be corrected to calculate True N for navigational purposes considering magnetic declination and magnetic departure. (come across http://en.wikipedia.org/wiki/Magnetic_deviation)
Electronic compasses furthermore crave recalibration to compensate for the environmental deviations.
GPS compass Pros: True north
GPS compass Cons: No heading when stationary, Accuracy depending satellite data receptions and its interpretation
Magnetic Compass Pros: Headings when stationary, based on Earth's magnetic fields
Magnetic Compass Cons: Inaccuracies deviating from True North due to magnetic declination and deviation
i.eastward. my analogue magnetic scuba diving compasses deviate towards my torch (it having magnetic switches), and my European Scuba diving compass is off by roughly 5 degrees here in Australia.
Clearly I am non an expert at this matter, but information technology'south mentioned now. The Compass.DrawCompass feature volition piece of work accurately and correclty in either way. Whether your information input is accurate for your purposes remains within your control choosing your about suitable Compass - data source and adjusting the data against your individually applicable declination/deviation before drawing the Compass.
History
2014-05-26 Added a point of interest as suggested past SteveHolle regarding the "truth" most northward and device specific caveats on that.
Source: https://www.codeproject.com/Articles/788611/A-simple-Compass
Posted by: drummondtals1968.blogspot.com
0 Response to "How To Draw A Compass"
Post a Comment