Automatic Conversion of Physical Units
by Petr Ivankov

Main picture

Useful links

1 Introduction.

Any advanced software contains a third-party software. Usually third party software should be adapted to own software. Different software can use different physical units. For example Empirical, global model of the Earth's atmosphere from ground to space (NRLMSISE-00) source code is very convenient for research of atmospheric physics, but it is not convenient for ballistics because ballistics uses other physical units. Conversion of physical units is very a important task.

++++++++++++++++++++++

One example of the importance of agreed units is the failure of the NASA Mars Climate Orbiter, which was accidentally destroyed on a mission to Mars in September 1999 instead of entering orbit due to miscommunications about the value of forces: different computer programs used different units of measurement (newton versus pound force). Considerable amounts of effort, time, and money were wasted.[6][7]

On April 15, 1999 Korean Air cargo flight 6316 from Shanghai to Seoul was lost due to the crew confusing tower instructions (in metres) and altimeter readings (in feet). Three crew and five people on the ground were killed. Thirty seven were injured.[8][9]

In 1983, a Boeing 767 (which came to be known as the Gimli Glider) ran out of fuel in mid-flight because of two mistakes in figuring the fuel supply of Air Canada's first aircraft to use metric measurements.[10] This accident is apparently the result of confusion both due to the simultaneous use of metric & Imperial measures as well as mass & volume measures.

Cited from here.

++++++++++++++++++++

Sometimes conversion of physical units requires a lot of work, however this work can be particulary or fully automated. This article is devoted to physical unit automatization issues. My article contains very many details because some of my modules can be interested for some readers. This article can be also regarded as practical guide of low cohesion.

2 Background

Internet contains a lot of useful information. However as rule this information should be adapted, to own purposes. This article is devoted to automatic conversion of physical units. If we have provider and consumer of information

Provider & consumer

and physical units of consumer are different from provider's one then we should supply conversion of units. My soft makes this operation particulary or fully automatic.

2.1 Theoretic issues

Theoretic issues are explained here, following formula represent logarithm of a conversion coefficient.

Conversion formula.

I used exponential version of the above formula.

2.2 Calculation of coefficients

Following code contains enumerations of physical units.

     /// <summary>
    /// Types of angle
    /// </summary>
    public enum AngleType
    {
        Radian, // Radian
        Circle, // Circle
        Degree  // Degree
    }

    /// <summary>
    /// Types of length
    /// </summary>
    public enum LengthType
    {
        Meter,      // Meter
        Centimeter, // Centimeter
        Kilometer   // Kilometer
    }

    /// <summary>
    /// Types of time
    /// </summary>
    public enum TimeType
    {
        Second, // Second
        Day     // Day
    }

    /// <summary>
    /// Types of mass
    /// </summary>
    public enum MassType
    {
        Kilogram,   // Kilogram
        Gram        // Gram
    }

Following attribute contains information about usage of physical units.

    /// <summary>
    /// Attribute of physical parameters
    /// </summary>
    [Serializable()]
    public class PhysicalUnitTypeAttribute : Attribute, ISerializable
    {

        #region Fields

        static internal readonly Dictionary<Type, PropertyInfo> Properties =
            new Dictionary<Type, PropertyInfo>();


        /// <summary>
        /// Type of angle
        /// </summary>
        private AngleType angleUnit;

        /// <summary>
        /// Type of length
        /// </summary>
        private LengthType lengthUnit;

        /// <summary>
        /// Type of time
        /// </summary>
        private TimeType timeUnit;

        /// <summary>
        /// Type of mass
        /// </summary>
        private MassType massUnit;

        private event Action change = () => { };

        #endregion

        #region Ctor

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="angleType">Type of angle</param>
        /// <param name="lengthType">Type of length</param>
        /// <param name="timeType">Type of time</param>
        /// <param name="massType">Type of mass</param>
        public PhysicalUnitTypeAttribute(AngleType angleType = AngleType.Radian,
            LengthType lengthType = LengthType.Meter,
            TimeType timeType = TimeType.Second,
            MassType massType = MassType.Kilogram)
        {
            this.angleUnit = angleType;
            this.lengthUnit = lengthType;
            this.timeUnit = timeType;
            this.massUnit = massType;
        }

        /// <summary>
        /// Default constructor
        /// </summary>
        public PhysicalUnitTypeAttribute()
        {
            angleUnit = AngleType.Radian;
            lengthUnit = LengthType.Meter;
            timeUnit = TimeType.Second;
            massUnit = MassType.Kilogram;
        }

        /// <summary>
        /// Deserialization constructor
        /// </summary>
        /// <param name="info">Serialization info</param>
        /// <param name="context">Streaming context</param>
        protected PhysicalUnitTypeAttribute(SerializationInfo info, StreamingContext context)
        {
            angleUnit = (AngleType)info.GetValue("AngleType", typeof(AngleType));
            lengthUnit = (LengthType)info.GetValue("LengthType", typeof(LengthType));
            timeUnit = (TimeType)info.GetValue("TimeType", typeof(TimeType));
            massUnit = (MassType)info.GetValue("MassType", typeof(MassType));
        }

        /// <summary>
        /// Constructor
        /// </summary>
        static PhysicalUnitTypeAttribute()
        {
            PropertyInfo[] pi = typeof(PhysicalUnitTypeAttribute).GetProperties();
            foreach (PropertyInfo i in pi)
            {
                Type t = i.PropertyType;
                if (!t.Equals(typeof(object)))
                {
                    Properties[i.PropertyType] = i;
                }
            }
        }


        #endregion

        #region ISerializable Members

        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("AngleType", AngleType, typeof(AngleType));
            info.AddValue("LengthType", LengthType, typeof(LengthType));
            info.AddValue("TimeType", TimeType, typeof(TimeType));
            info.AddValue("MassType", MassType, typeof(MassType));
        }

        #endregion

        #region Public

        /// <summary>
        /// Change event
        /// </summary>
        public event Action Change
        {
            add { change += value; }
            remove { change -= value; }
        }


        /// <summary>
        /// Type of angle
        /// </summary>
        public AngleType AngleType
        {
            get
            {
                return angleUnit;
            }
            set
            {
                angleUnit = value;
                change();
            }
        }

        /// <summary>
        /// Type of length
        /// </summary>
        public LengthType LengthType
        {
            get
            {
                return lengthUnit;
            }
            set
            {
                lengthUnit = value;
                change();
            }
        }

        /// <summary>
        /// Type of time
        /// </summary>
        public TimeType TimeType
        {
            get
            {
                return timeUnit;
            }
            set
            {
                timeUnit = value;
                change();
            }
        }

        /// <summary>
        /// Type of mass
        /// </summary>
        public MassType MassType
        {
            get
            {
                return massUnit;
            }
            set
            {
                massUnit = value;
                change();
            }
        }

        #endregion

    }

If we have a type with constant physical units then above class is used as an attribute, otherwise it is used as a property of the following interface.

     /// <summary>
    /// Holder of attribute of physical parameters
    /// </summary>
    public interface IPhysicalUnitTypeAttribute
    {
        /// <summary>
        /// Attribute of physical parameters
        /// </summary>
        PhysicalUnitTypeAttribute PhysicalUnitTypeAttribute
        {
            get;
            set;
        }
    }

Following dictionaries are used for calculation of coefficients.

        /// <summary>
        /// Dictionaries which contain coefficients
        /// </summary>
        private static readonly Dictionary<Type, object> dCoefficients = new Dictionary<Type, object>()
       {
           {typeof(AngleType),   new Dictionary<AngleType, double>()
            {
                {AngleType.Radian, 1},
                {AngleType.Degree, 180 / Math.PI},
                {AngleType.Circle, 2 * Math.PI}
            }
            },
           {typeof(LengthType),    new Dictionary<LengthType, double>()
            {
                {LengthType.Meter, 1},
                {LengthType.Kilometer, 1000},
                {LengthType.Centimeter, 0.01}
            }
           },
           {typeof(MassType),   new Dictionary<MassType, double>()
            {
                {MassType.Kilogram, 1},
                {MassType.Gram, 0.001}
            }
           },
           {typeof(TimeType),   new Dictionary<TimeType, double>()
            {
                {TimeType.Day, 1},
                {TimeType.Second, 86400}
            }
            }
       };

Meaning of these dictionaries is clear, following functions use these dictionaries.

          /// <summary>
        /// Coefficient of physical unit
        /// </summary>
        /// <typeparam name="T">From</typeparam>
        /// <param name="source">From unit</param>
        /// <param name="target">To unit</param>
        /// <returns>Coefficient</returns>
        public static double Coefficient<T>(this T source, T target)
        {
            Dictionary<T, double> d = dCoefficients[typeof(T)] as Dictionary<T, double>;
            double kFrom = d[source];
            double kTo = d[target];
            return kTo / kFrom;
        }

These coefficients are used for more general transformations by following way.

         /// <summary>
        /// Coefficient functions
        /// </summary>
        private static readonly Dictionary<Type, Func<object, object, double>> functions = new Dictionary<Type, Func<object, object, double>>()
        {
             {typeof(AngleType), (object a, object b) =>
                 { return Coefficient<AngleType>((AngleType)a, (AngleType)b);} },
             {typeof(LengthType), (object a, object b) =>
                 { return Coefficient<LengthType>((LengthType)a, (LengthType)b);} },
             {typeof(MassType), (object a, object b) =>
                 { return Coefficient<MassType>((MassType)a, (MassType)b);} },
             {typeof(TimeType), (object a, object b) =>
                 { return Coefficient<TimeType>((TimeType)a, (TimeType)b);} }
        };

        /// <summary>
        /// Gets coefficients
        /// </summary>
        /// <param name="source">Source</param>
        /// <param name="target">Target</param>
        /// <param name="dictionary" />Physical unit dictionary</param>
        /// <returns>Coefficient</returns>
        public static double Coefficient(this PhysicalUnitTypeAttribute source,
            PhysicalUnitTypeAttribute target, Dictionary<Type, int> dictionary)
        {
            if (dictionary == null)
            {
                return 1;
            }
            double a = 1;
            Dictionary<Type, PropertyInfo> p =  PhysicalUnitTypeAttribute.Properties;
            foreach (Type t in dictionary.Keys)
            {
                Func<object, object, double> f = functions[t];
                PropertyInfo pi = p[t];
                object from = pi.GetValue(source);
                object to = pi.GetValue(target);
                double k = f(from, to);
                if (k == 1)
                {
                    continue;
                }
                int m = dictionary[t];
                if (m < 0)
                {
                    m = -m;
                    k = 1.0 / k;
                }
                for (int i = 0; i < m; i++)
                {
                    a *= k;
                }
            }
            return a;
        }

The dictionary parameter of the Coefficient function, which is commented as Physical unit dictionary can be easy explained by following example. The gravitational constant is expressed by following way.

gravitational constant

Following Physical unit dictionary corresponds to the gravitational constant.

         /// <summary>
        /// Gravitational constant physical unit dictionary
        /// </summary>
        private static readonly Dictionary<Type, int> GravityConstUnit =
            new Dictionary<Type, int>()
            {
              {typeof(LengthType), 3},
             {typeof(MassType), -1},
             {typeof(TimeType), -2},
            };

2.3 Automatic calculations

There is a set of interfaces which are intended for automatic conversion. Following interface is used for an exchange of parameters.

     /// <summary>
    /// Collection on named data units
    /// </summary>
    public interface IAlias : IAliasBase
    {
        /// <summary>
        /// Names of all data units
        /// </summary>
        IList<string> AliasNames
        {
            get;
        }

        /// <summary>
        /// Access to data unit by name
        /// </summary>
        object this[string name]
        {
            get;
            set;
        }

        /// <summary>
        /// Gets unit type
        /// </summary>
        /// <param name="name">Unit name</param>
        /// <returns>Type of unit</returns>
        object GetType(string name);

        /// <summary>
        /// Change alias event
        /// </summary>
        event Action<IAlias, string> OnChange;

    }

One object exports parameters to another object, names and types of the parameters should coincide. If above IAlias interface is extended by a physical units manifest (physical unit dictionary) then conversion of units can be given automatically. Following interface represents manifest of a physical unit dictionary.

     /// <summary>
    /// Physical unit dictionary
    /// </summary>
    public interface IPhysicalUnitAlias
    {
        /// <summary>
        /// Gets dictionary of an alias
        /// </summary>
        /// <param name="name">Name</param>
        /// <returns>Physical unit dictionary</returns>
        Dictionary<Type, int> this[string name]
        {
            get;
        }
    }

The name parameter of provider should correspond to name of consumer. Following function uses above interfaces for an automatic conversion of physical parameters.

         /// <summary>
        /// Conversion of physical units
        /// </summary>
        /// <param name="alias">Alias</param>
        /// <param name="source">Source</param>
        /// <param name="target">Target</param>
        /// <returns>Conversion result</returns>
        public static Dictionary<string, double> ConvertPhysicalUnits
            (this IAlias alias, BaseTypes.Attributes.PhysicalUnitTypeAttribute source,
            BaseTypes.Attributes.PhysicalUnitTypeAttribute target)
        {
            Dictionary<string, double> d = new Dictionary<string, double>();
            BaseTypes.Interfaces.IPhysicalUnitAlias a = alias as
                BaseTypes.Interfaces.IPhysicalUnitAlias;
            IList<string> l = alias.AliasNames;
            foreach (string key in l)
            {
                if (!l.Contains(key))
                {
                    continue;
                }
                object t = alias.GetType(key);
                if (!t.Equals(BaseTypes.FixedTypes.Double))
                {
                    continue;
                }
                double x = (double)alias[key];
                Dictionary<Type, int> dt = a[key];
                if (dt != null)
                {
                    x *=  Coefficient(source,
                        target, dt);
                }
                d[key] = x;
            }
            return d;
        }

Above function gets parameters from provider, calculates conversion coefficients, multiplies parameters by coefficients and exports results to the consumer.

2.4 User interface.

There is clear and unified user control for all objects which implement the IPhysicalUnitTypeAttribute interface.

User Interface

Above user control corresponds to the UserControlPhysicalUnit class which is contained in my source code.

3. Samples

3.1 Manual adaptation of physical units

My description of this task looks too long, but this sample has independent interest for some readers. Motion of artificial Earth's satellites depends on Earth's atmosphere. There is an Empirical, global model of the Earth's atmosphere from ground to space (NRLMSISE-00) source code. However this code is static. I would like use it for many simultaneous calculations and so I developed the Msise C++ class (MSISEAtmosphere project). This class has independent interest and can be used without .NET. The MSISEAtmosphere project is a .NET wrapper of the Msise class. The DynamicAtmosphere.MSISE project is a C# extension of MSISEAtmosphere which supplies following additional facilities:

  • Conversion of physical units;
  • Integrated calculation of position of the Sun.

Atmospheric parameters depend on position of the Sun. However I did not integrate calculation of Sun position into MSISEAtmosphere by following reason. Suppose a user have got own soft for the Sun position calculation. If I would integrate my algorithm then user should have two algorithms of the Sun position calculation. Calculation of Sun position is also independent, it is contained in the SunPosition project. The SunPosition is a refactoring of of Plataforma Solar de Almería C++ project to C#. Position of the Sun has indepentent interest, the Plataforma Solar de Almería uses it for solar facilities. Maybe my soft would also be used for solar facilities without atmospheric calculations. Maybe my soft would be used for a task which contains two subtasks: solar facilities and artificial Earth's satellites monitoring. Then the single SunPosition will be used for both subtasks. The DynamicAtmosphere.MSISE.Wrapper is next extension for integration with my software. My software can use this class for different purposes, for example for determination of orbits of artificial satellites. The DynamicAtmosphere.MSISE.Wrapper contains pure business logic layer. So it be used in my Web projects, Windows Forms projects and WPF projects. The DynamicAtmosphere.MSISE.Wrapper.UI project supplies following System.Windows.Forms user interface.

OwnUnits

The Parameters tab page is responsible for space weather indices F10.7, Ap, F10.7 average. The Physical units tab page is responsible for physical units. This object is used in motion equations of a satellite such that kilometer, second and kilogram are used as physical units. We can select any physical units and outputs of this project should correspond these units.

3.2 Automatic export of physical parameters

Above example implies manual input of space weather indices. Manual input is undoubtedly useful for research tasks. But in case of real-time simulation the space weather indices can be automatically given by Internet. The DynamicAtmosphere.Web is a project which provides space weather indices from the http://www.nwra.com/spawx/env_latest.html and http://www.spaceweather.ca/data-donnee/sol_flux/sx-5-mavg-eng.php. The DynamicAtmosphere.Web.Wrapper extends the DynamicAtmosphere.Web for interopreability with my framework. Both these projects are independent from MSISE-90 atmosphere model, so they can be used with Russian Federation model of atmosphere. Main type of the DynamicAtmosphere.Web.Wrapper is the DynamicAtmosphere.Web.Wrapper.Atmosphere class. An object of this class can be independent and can have an integrated child. If object is independent then it is a provider of information for many consumers as it is displayed below.

Many consumers

All consumers of information should use the same physical units. If this object contains an integrated child then it provides information for single child only. Following two constructors explain usage of these options.

         /// <summary>
        /// Constructor of an independent object
        /// </summary>
        public Atmosphere()
        {

        }

        /// <summary>
        /// Constructor with child
        /// </summary>
        /// <param name="childType">Type of child</param>
        public Atmosphere(string childType)
            : this()
        {
            Type t = Type.GetType(childType); // Type of the child object
            child = t.GetConstructor(new Type[0]).Invoke(new object[0]) as IAlias; // Child object
            // Children
            children = new IAssociatedObject[] { child as IAssociatedObject };
            if (child is IAliasConsumer)
            {
                // Adaption of this object by its child
                IAliasConsumer aliasConsumer = child as IAliasConsumer;
                aliasConsumer.Add(this);  // Child object consumes information
            }
        }

So child object consumes information from parent. Following code contains implementation of the Add method of the IAliasConsumer.

         void IAliasConsumer.Add(IAlias alias)
        {
            // ================ Import of parameters ==================
            IAlias al = this;
            List<string> own = al.AliasNames.ToList<string>();
            List<string> external = alias.AliasNames.ToList<string>();
            for (int i = 0; i < external.Count; i++)
            {
                string n = external[i];
                if (dAlias.ContainsKey(n))
                {
                    if (al.GetType(n).Equals(alias.GetType(n)))
                    {
                        al[n] = alias[n];
                    }
                }
            }
            //=============== Export of event handler  ==================
            alias.OnChange += Change;
        }

Export of event handler means that changing of Internet atmospheric parameters forces simultaneous changing of parameters of the child. Names of aliases of the child object coincide with parent ones.

         /// <summary>
        /// Names of aliases
        /// </summary>
        private readonly string[] Names = new string[] { "F10_7", "Ap", "F10_7A" };

The names are names of space weather indices.

Described above objects are very special, so they should be used as plug-ins. The Containers.xml file contains manifest of plugins.

<Root>
  <Assemblies>
    <Assembly file="SunPosition.dll"/>
    <Assembly file="DynamicAtmosphere.MSISE.dll"/>
    <Assembly file="DynamicAtmosphere.MSISE.Wrapper.dll"/>
    <Assembly file="DynamicAtmosphere.MSISE.Wrapper.UI.dll"/>
    <Assembly file="DynamicAtmosphere.Web.dll"/>
    <Assembly file="DynamicAtmosphere.Web.Wrapper.dll"/>
   </Assemblies>
  <Page pageName="Orbital" pageHint="Orbital services">
    <Object icon="Containers\Atmosphere.ico"
           type="DynamicAtmosphere.MSISE.Wrapper.Atmosphere,DynamicAtmosphere.MSISE.Wrapper, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
           param="" hint="Earth's atmosphere" arrow="false" />
   <Object icon="Containers\AtmosphereWeb.ico"
           type="DynamicAtmosphere.Web.Wrapper.Atmosphere,DynamicAtmosphere.Web.Wrapper, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
           param="" hint="Earth's atmosphere Web" arrow="false" />
   <Object icon="Containers\AtmosphereWebChild.ico"
           type="DynamicAtmosphere.Web.Wrapper.Atmosphere,DynamicAtmosphere.Web.Wrapper, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
           param="DynamicAtmosphere.MSISE.Wrapper.Atmosphere,DynamicAtmosphere.MSISE.Wrapper, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
          hint="Earth's atmosphere Web + child" arrow="false" />
   </Page>
 </Root>

Any Object tag corresponds to a plug-in object. Following table explains above XML code.

NCreation of the objectComment
1new DynamicAtmosphere.MSISE.Wrapper.Atmosphere()MSISE Atmospheric model
2new DynamicAtmosphere.Web.Wrapper.Atmosphere()Atmospheric parameters from Web
3new DynamicAtmosphere.Web.Wrapper.Atmosphere(new DynamicAtmosphere.MSISE.Wrapper.Atmosphere())Atmospheric parameters from Web + MSISE Atmospheric model

Following picture displays these objects.

Atmosphere

If Web atmosphere contains an integrated child then editor form contains additional tab. Following two pictures contains properties of Web atmosphere with a child and without it. The child just imports space weather indices, physical units of child's outputs depend on settings.

ChildWithout child

3.3 Automatic export and physical units conversion

There are Keplerian orbit prediction algorithm and two-line element set (TLE) is a data format used to convey sets of orbital elements that describe the orbits of Earth-orbiting satellites. Both this objects can be aggregated to a single object. or used independently. For example Keplerian orbit prediction algorithm can be used for simulation of stars' motion. Two-line element set (TLE), can be used by more accurate methods of orbit prediction (See here). I have developed special soft for extracting data from NORAD Two-Line Element Sets Current Data. This soft contains 3 projects. First project Celestrak.NORAD.Satellites contains business logic only. Main class of this project is presented below.

     /// <summary>
    /// Data of satellite
    /// </summary>
    [Serializable()]
    [PhysicalUnitType(AngleType = AngleType.Degree, LengthType = LengthType.Meter,
        MassType = MassType.Kilogram, TimeType = TimeType.Day)]
    public class SatelliteData : CategoryObject, IChildrenObject, IMeasurements, IPropertiesEditor,
        ISeparatedAssemblyEditedObject, IAlias, ISerializable, IPhysicalUnitAlias, IEventHandler

The PhysicalUnitType(AngleType = AngleType.Degree, LengthType = LengthType.Meter, MassType = MassType.Kilogram, TimeType = TimeType.Day) means that this type used fixed physical units. Following code contains implementation of both IAlias and IPhysicalUnitAlias.

         /// <summary>
        /// Physical unit dictionary
        /// </summary>
        internal static readonly Dictionary<string, Dictionary<Type, int>> AliasNames =
         new Dictionary<string, Dictionary<Type, int>>()
       {
            {"Eccentricity", null},
            {"Inclination", new Dictionary<Type, int>(){{typeof(AngleType), 1}}},
            {"Ascending Node",  new Dictionary<Type, int>(){{typeof(AngleType), 1}}},
            {"Argument Of Periapsis",  new Dictionary<Type, int>(){{typeof(AngleType), 1}}},
            {"Mean Anomaly At Epoch",  new Dictionary<Type, int>(){{typeof(AngleType), 1}}},
            {"Mean Motion", new Dictionary<Type, int>{{typeof(AngleType), 1}, {typeof(TimeType), -1}}},
            {"FD MeanMotion", null},
            {"SD MeanMotion", null},
            {"Perturbation", null},
            {"Mass", new Dictionary<Type, int>(){{typeof(MassType), 1}}},
            {"Epoch",  null}
        };


       #region IPhysicalUnitAlias Members

        Dictionary<Type, int> IPhysicalUnitAlias.this[string name]
        {
            get
            {
                if (AliasNames.ContainsKey(name))
                {
                    return AliasNames[name];
                }
                return null;
            }
        }

       #endregion

        #region IAlias Members

        IList<string> IAlias.AliasNames
        {
            get { return AliasNames.Keys.ToList<string>(); }
        }

        object IAlias.this[string name]
        {
            get
            {
                return aliases[name];
            }
            set
            {
            }
        }

        object IAlias.GetType(string name)
        {
            if (name.Equals("Epoch"))
            {
              return  FixedTypes.DateTime;
            }
            return FixedTypes.Double;
        }


        event Action<IAlias, string> IAlias.OnChange
        {
            add { onChange += value; }
            remove { onChange -= value; }
        }

        #endregion

The AliasNames represents physical unit dictionary. The Celestrak.NORAD.Satellites.Wpf.UI project supplies WPF user interface which which is used in both WPF and System.Windows.Forms applications. The Celestrak.NORAD.Satellites.UI contains a System.Windows.Forms user interface. As well as DynamicAtmosphere.Web.Wrapper.Atmosphere data of this class can be exported to its integrated child. The Keplerian orbit prediction algorithm can be used as the child. The Keplerian orbit prediction algorithm is implemented in Celestia. Real-time 3D visualization of space project, I developed C# version of it. The CelestialMechanics project contains fully independent version of the algorithm. The CelestialMechanics.Wrapper is intended for compatibility with my framework. Names of aliases in CelestialMechanics.Wrapper.CelestialMechanics.Wrapper.Classes.Orbit class coincide with Celestrak.NORAD.Satellites.SatelliteData ones. Since Celestrak.NORAD.Satellites.SatelliteData implements the IPhysicalUnitAlias interface then besides export of data it implements automatic conversion of physical units. The ConvertPhysicalUnits function is used for this purpose. Following picture contains tree objects.

3 Orbital

Following table explains meaning of these objects.

NNameCreation of the objectComment
1NORADnew Celestrak.NORAD.Satellites.SatelliteData()NORAD data
2Orbitalnew CelestialMechanics.Wrapper.Classes.Orbit()Keplerian orbit prediction algorithm
3NORAD + Orbitalnew Celestrak.NORAD.Satellites.SatelliteData(new CelestialMechanics.Wrapper.Classes.Orbit())NORAD data + Keplerian orbit prediction algorithm

Following two pictures display user interface of the NORAD object.

Data 1Data 2

Above interfface is clear and explains business logics. Following pictures display user interface of the Orbital object.

Satellite 1Satellite 2

Above user interface contais an editor of physical units. Following pictures displays propetrites of the NORAD + Orbital object.

NORAD + OrbitalNORAD + Orbital

NORAD + OrbitalNORAD + Orbital

Last two pictures contain properties of the child. Parameters of orbit depend on parent, i.e. they coincide with parameters of a selected satellite. So this parameters are not editable. If child does not depend on parent then parameters of orbit are editable. However if we change physical units then numerical values shall be changed as it is displayed below:

NORAD + OrbitalNORAD + Orbital

Points of Interest

Sometimes a physical units conversion is more complicated then other parts of an engineering task.

History

I had tried manually convert Fortran version of MSISE-90 to C++. I think that the code of this model is not clear for its developers. My attempt was unsuccessful. Then I found f2c to convert Fortran 77 to C code. However I found a bug. Two calls of the same function occur different result. Then I found C code of MSISE-90. This code is not clear also. My colleagues told me that Aerospace has a lot of unclear Fortran code. Then I asked myself:

  • Is my own code clear?
  • Can I make my own code clear?

I translated a Celestia. Real-time 3D visualization of space I found that this project contains folowing code:

const double astro::G = 6.672e-11; // N m^2 / kg^2
(See here), however present day value of gravity constat is equal to 6.674 28 (+/- 0.000 67) x 10-11 m3 kg-1 s -2.

Read more: http://www.universetoday.com/43227/gravity-constant/#ixzz2r7kVBVH5

This fact inspires me for modification of my own code. Now my model of Earth's gravity field can be automatically updated. User interface of my new gravity field is presented below

Gravity

Above picture means that parameters of a gravity field model is stored in the http://mathframe.com/databases/gravity/GEM-T3.FLD file.