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
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.
.
I used exponential version of the above formula.
2.2 Calculation of coefficients
Following code contains enumerations of physical units.
public enum AngleType
{
Radian,
Circle,
Degree
}
public enum LengthType
{
Meter,
Centimeter,
Kilometer
}
public enum TimeType
{
Second,
Day
}
public enum MassType
{
Kilogram,
Gram
}
Following attribute contains information about usage of physical units.
[Serializable()]
public class PhysicalUnitTypeAttribute : Attribute, ISerializable
{
#region Fields
static internal readonly Dictionary<Type, PropertyInfo> Properties =
new Dictionary<Type, PropertyInfo>();
private AngleType angleUnit;
private LengthType lengthUnit;
private TimeType timeUnit;
private MassType massUnit;
private event Action change = () => { };
#endregion
#region Ctor
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;
}
public PhysicalUnitTypeAttribute()
{
angleUnit = AngleType.Radian;
lengthUnit = LengthType.Meter;
timeUnit = TimeType.Second;
massUnit = MassType.Kilogram;
}
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));
}
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
public event Action Change
{
add { change += value; }
remove { change -= value; }
}
public AngleType AngleType
{
get
{
return angleUnit;
}
set
{
angleUnit = value;
change();
}
}
public LengthType LengthType
{
get
{
return lengthUnit;
}
set
{
lengthUnit = value;
change();
}
}
public TimeType TimeType
{
get
{
return timeUnit;
}
set
{
timeUnit = value;
change();
}
}
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.
public interface IPhysicalUnitTypeAttribute
{
PhysicalUnitTypeAttribute PhysicalUnitTypeAttribute
{
get;
set;
}
}
Following dictionaries are used for calculation of coefficients.
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.
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.
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);} }
};
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.
Following Physical unit dictionary corresponds to the gravitational constant.
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.
public interface IAlias : IAliasBase
{
IList<string> AliasNames
{
get;
}
object this[string name]
{
get;
set;
}
object GetType(string name);
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.
public interface IPhysicalUnitAlias
{
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.
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.
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.
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.
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.
public Atmosphere()
{
}
public Atmosphere(string childType)
: this()
{
Type t = Type.GetType(childType);
child = t.GetConstructor(new Type[0]).Invoke(new object[0]) as IAlias;
children = new IAssociatedObject[] { child as IAssociatedObject };
if (child is IAliasConsumer)
{
IAliasConsumer aliasConsumer = child as IAliasConsumer;
aliasConsumer.Add(this);
}
}
So child
object consumes information from parent. Following code contains implementation of the Add
method of the IAliasConsumer
.
void IAliasConsumer.Add(IAlias alias)
{
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];
}
}
}
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.
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.
N | Creation of the object | Comment |
1 | new DynamicAtmosphere.MSISE.Wrapper.Atmosphere() | MSISE Atmospheric model |
2 | new DynamicAtmosphere.Web.Wrapper.Atmosphere() | Atmospheric parameters from Web |
3 | new DynamicAtmosphere.Web.Wrapper.Atmosphere(new DynamicAtmosphere.MSISE.Wrapper.Atmosphere()) | Atmospheric parameters from Web + MSISE Atmospheric model |
Following picture displays these objects.
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.
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.
[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
.
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.
Following table explains meaning of these objects.
N | Name | Creation of the object | Comment |
1 | NORAD | new Celestrak.NORAD.Satellites.SatelliteData() | NORAD data |
2 | Orbital | new CelestialMechanics.Wrapper.Classes.Orbit() | Keplerian orbit prediction algorithm |
3 | NORAD + Orbital | new 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.
Above interfface is clear and explains business logics. Following pictures display user interface of the Orbital object.
Above user interface contais an editor of physical units. Following pictures displays propetrites of the NORAD + Orbital object.
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:
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;
(
See here), however present day value of gravity constat is equal to
6.674 28 (+/- 0.000 67) x 10
-11 m
3 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
Above picture means that parameters of a gravity field model is stored in the http://mathframe.com/databases/gravity/GEM-T3.FLD file.