Wading Into the Mud

One of the things I love about embedded programming is the almost "bare metal" programming environment. It's as if I can say "If I didn't write it, it isn't there!". That's not true, especially of .NET Micro Framework where .NET brings much of the functionality you'd have to write for yourself in Arduino. But there's a nice tangible distinction between what's part of the language (GCC compiler/.NET Framework) and what I've written myself. Basically, I can trust the inbuilt stuff and can't trust my own work. If there's a fault it's mine! Easy.

Yesterday I built a simple set of classes. It was even simpler, but then I decided to enforce the rule absolutely no copy-paste coding! So I added a class to help with this. The goal is to have a class that defines a two dimensional point on the earth by latitude and longitude. If I were hacking away in C (or C++) I'm sure a struct would be the way to go. But I'm complicating things using classes (perhaps unnecessarily) to ensure that, once created, a Point2D object is not modifiable.

Latitude and longitude data are simply stored as floating point numbers. However my main use for this data has been to output it to my OLED screen, so formatting with ToString() is the first objective. This is where I had initially duplicated code. I built a Latitude class (stores a float) and wrote a ToString() method. I then needed basically the same code for the Longitude class. Argh!

I've understood for a while about inheritance of classes but have never really implemented it for myself. Sure I've done test code that works and makes sense, but I've yet to use inheritance as the fundamental of code architecture that it is. So here goes.

I literally cut out the common code from Latitude and Longitude and created a LatLongBase class to contain this code. I then made Latitude and Longitude inherit from LatLongBase and then had to work out how to pass constructor parameters to the base class. So Latitude and Longitude are separate classes inheriting from LatLongBase. Then Point2D has both a Latitude and Longitude private member variable.

Here it would be appropriate for me to paste in the classes. But do you think I can get my code from Visual Studio 2010 to paste in has HTML? Hmmm. Another to do. EDIT: To do complete. Found this. Code below.

   1:  using System;
   2:  using Microsoft.SPOT;
   3:   
   4:  namespace BikeComputer.Data
   5:  {
   6:      public enum Axis { Latitude, Longitude };
   7:      public enum Direction { North, South, East, West };
   8:   
   9:      class LatLongBase
  10:      {
  11:          float value;
  12:   
  13:          public LatLongBase(float value)
  14:          {
  15:              this.value = value;
  16:          }
  17:   
  18:          public float Value
  19:          {
  20:              get
  21:              {
  22:                  return value;
  23:              }
  24:              protected set
  25:              {
  26:                  this.value = value;
  27:              }
  28:          }
  29:   
  30:          public override string ToString()
  31:          {
  32:              float value = Value;
  33:              if (value < 0) value *= -1;
  34:              int degrees = (int)value;
  35:              float denominator = value - degrees;
  36:              int decimalPlaces = (int)(denominator * 10000);
  37:              string output = Helper.Format.PadNumber(degrees, 3, ' ')
  38:                  + "." + Helper.Format.PadNumber(decimalPlaces, 4, '0');
  39:              return output;
  40:          }
  41:      }
  42:  }

   1:  using System;
   2:  using Microsoft.SPOT;
   3:   
   4:  namespace BikeComputer.Data
   5:  {
   6:      class Latitude : LatLongBase
   7:      {
   8:          public Latitude(float value)
   9:              : base(0)
  10:          {
  11:              if (value > 90) throw new Exception("Invalid latitude. Values greater than 90 are meaningless");
  12:              else base.Value = value;
  13:          }
  14:   
  15:          public static implicit operator float(Latitude latitude)
  16:          {
  17:              return latitude.Value;
  18:          }
  19:   
  20:          public override string ToString()
  21:          {
  22:              return base.ToString() + ((base.Value < 0) ? "S" : "N");
  23:          }
  24:      }
  25:  }

   1:  using System;
   2:  using Microsoft.SPOT;
   3:   
   4:  namespace BikeComputer.Data
   5:  {
   6:      class Longitude : LatLongBase
   7:      {
   8:          public Longitude(float value)
   9:              : base(0)
  10:          {
  11:              if (value > 180) throw new Exception("Invalid longitude. Values greater than 180 are meaningless");
  12:              else base.Value = value;
  13:          }
  14:   
  15:          public static implicit operator float(Longitude longitude)
  16:          {
  17:              return longitude.Value;
  18:          }
  19:   
  20:          public override string ToString()
  21:          {
  22:              return base.ToString() + ((base.Value < 0) ? "W" : "E");
  23:          }
  24:      }
  25:  }

   1:  using System;
   2:  using Microsoft.SPOT;
   3:   
   4:  namespace BikeComputer.Data
   5:  {
   6:      class Point2D
   7:      {
   8:          private Latitude latitude;
   9:          private Longitude longitude;
  10:   
  11:          public Point2D(float latitude, float longitude)
  12:          {
  13:              this.latitude = new Latitude(latitude);
  14:              this.longitude = new Longitude(longitude);
  15:          }
  16:   
  17:          public Point2D(string latitude, string northSouth, string longitude, string eastWest)
  18:          {
  19:              this.latitude = new Latitude(Helper.NMEA.ParseLatLong(latitude, northSouth));
  20:              this.longitude = new Longitude(Helper.NMEA.ParseLatLong(longitude, eastWest));
  21:          }
  22:   
  23:          public override string ToString()
  24:          {
  25:              return this.latitude.ToString() + " " + this.longitude.ToString();
  26:          }
  27:   
  28:          public float distance(Point2D alpha, Point2D omega)
  29:          {
  30:              //http://en.wikipedia.org/wiki/Great-circle_distance
  31:              //http://en.wikipedia.org/wiki/Haversine_formula
  32:              throw new NotImplementedException();
  33:          }
  34:      }
  35:  }

Assuming my Point2D class works ok, the first thing I want to implement is a Point2D.DistanceTo(Point2D otherPoint) method. I needed to learn about spherical geometry and great circle distance in particular. This reveals I should implement the Haversine formula. Cool. Then a brick wall. Where's System.Math in .NET Micro Framework? There isn't one? Really?

This makes sense. An embedded device has limited processing power and mathematics ("maths", with an s, as we call it in Australia) is a great way to soak up all the processing power available. I scanned Bob Craven's post about GPS using the Netduino and saw that he used a maths library from Elze Kool (a cool name if ever there was one.) But on reading the comments, I don't want to use it! It seems from the comments that some of the basic functionality is buggy and the posts from the author saying "I'll look into it and fix it" have gone unanswered for a couple of years.

My usual response is "I'd rather build it myself", but really? A maths library? That's a lot of work and testing. It's almost as much work to download a known-to-be-buggy library and working through the issues as they come up. But it appears this is the best option. The potential investment in time here is enormous. So it's worth some more time to explore other options first. After that it's into the mud.

That is all.

Comments