Introduction
[Note: The link to the download for the app on the Intel AppUp store was removed due to Intel closing the AppUp store in March 2014, the source is still available on the post competition article I wrote. See this link.]
This article is a conceptual look at a proposed application to demonstrate the potential of the sensory devices embedded within the ultrabooks.
For those of you who enjoy motor-sport, and maybe watch the likes of the Formula 1, you will have probably noticed the on-screen graphics of vehicle performance.
Modern sports cars, such as the Nissan GT-R also have onboard visual displays to convey the vehicle performance to the driver.
What if you are a track day fanatic, or just a general petrol head or gear head (or whatever you call them in your area), what if you could just take your ultrabook into the car, secure on the dash, etc. and then have it display performance characteristics?
Well, that is the concept behind this app. (And yes, I am a petrol head!)
What are the Aims of this App
Using the GPS sensors, accelerometer, inclinometers, etc., it should be possible to display information to the vehicle occupants such as acceleration/deceleration data, positional data, and if you know the vehicles + occupants weight, it should be possible to calculate power characteristics.
Some of the statistics that you should be capable of displaying are:
- Acceleration/deceleration
- Cornering G
- 0 to 60 (62mph) / 100 km/h (or custom)
- 0 to 100 to 0
- Quarter mile (or custom)
- Sure there are others!
Using the GPS data, it should also be possible to map the data over your favourite mapping tools such as Google maps/earth.
Data can also be stored for historical reference, or comparison between vehicles or following modifications.
Displays
The data could potentially be displayed in a number of formats, e.g., numerical data, bar graphs, line graphs, analogue dials.
It may also be possible to allow the user to build custom dashboards to present the data as they see fit.
Using the Code
Project Update 1 (24th October, 2012)
I have been dipping in and out of this project over the last week, when I can. The main coding effort is going to take place when I get home in a week's time, but that will leave things a bit tight for the next deadline, so I need to be doing something now!
Initially, I am developing the code using Visual Studio 2012 Pro on a standard laptop, so I have no way to test the sensors, and had to come up with a suitable approach that could allow for the UI to be tested regardless of the underlying hardware. Can't wait to get my hands on the ultrabook to actually see if things move in the right direction, etc.!
After initially creating a blank Winforms C# application, I had to modify the project file to allow access to the Windows 8 Sensor APIs. This is covered in several articles, but in essence, you need to add a new property group to the project file containing:
<propertygroup />
<targetplatformversion />8.0</targetplatformversion />
</propertygroup />
After doing this, you can then add suitable references to the runtime files; Windows.Devices.Sensors.dll and System.Runtime.InteropServices.WindowsRuntime.dll.
Sensor Wrappers
As mentioned above, I need a way to test the UI without having access to the live sensor data. In order to achieve this, I created some wrapper classes that act as a middle-man between the UI and the actual sensors. This allows me to enable a Simulate Mode and inject test data into the code. Also, if any sensor is not available, that particular sensor is automatically switched to simulate mode. So far, I have created wrappers for the AmbientLightSensor
, Accelerometer
, Compass
, Gyrometer
and Inclinometer
. Still have to look at the GPS sensor.
Below is what the wrapper MyLightSensor
looks like:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Devices.Sensors;
namespace UltraDynamo.Sensors
{
public class MyLightSensor
{
public static bool Available { get; private set; }
public static bool Simulated { get; private set; }
public static float LightReading {get; private set; }
public static float rawLightReading {get; private set; }
public static float simLightReading {get; private set; }
private static LightSensor lightSensor;
public static event ChangeHandler Change;
public static EventArgs e = null;
public delegate void ChangeHandler(MyLightSensor sender, EventArgs e);
public MyLightSensor()
{
lightSensor = LightSensor.GetDefault();
if (lightSensor != null)
{
setAvailable(true);
setSimulated(false);
lightSensor.ReadingChanged += lightSensor_ReadingChanged;
}
else
{
setAvailable(false);
setSimulated(true);
}
}
void lightSensor_ReadingChanged(LightSensor sender, LightSensorReadingChangedEventArgs args)
{
rawLightReading = args.Reading.IlluminanceInLux;
if (!Simulated)
{
setLightLevel(rawLightReading);
}
if (Change != null)
Change(this, e);
}
private void setLightLevel(float value)
{
LightReading = value;
}
private void setAvailable(bool available)
{
Available = available;
if (Change != null)
Change(this, e);
}
public void setSimulated(bool simulated)
{
Simulated = simulated;
if (Change != null)
Change(this, e);
}
public void setSimulatedValue(float value)
{
simLightReading = value;
if (Simulated)
{
setLightLevel(simLightReading);
}
if (Change != null)
Change(this, e);
}
}
}
As you can see, there are a number of static
properties, that show if the Sensor
is currently Simulated
and/or Available
, as well as the Used
, Raw
and Simulated
values. A couple of methods are provided to set simulated data values and switch modes between Simulated
and Live
. In the code, you can also see the private static
field that is the actual sensor where the live data is accessed, as well as a change event for tracking sensor data updates.
Sensor Simulation Controller
After creating the sensor wrappers, the next step was to create a number of Forms that can be used to manipulate the sensor data. The image below shows the simulate form created for the Inclinometer
:
Sensor Data Presentation
Having the data available now, I have started to create UserControl
s that can be dropped into any part of the application, either on standalone forms so the user can customise the layout, or onto pre-defined dashboards. Below shows the three user controls created for the Inclinometer
's Pitch
, Roll
and Yaw
and show the data represented in the simulate form above.
Graphics are not one of my strong areas, so I have used some crude image manipulation just to get the core functionality working, and can go back and enhance things with future updates. You will also notice on the forms above the status blobs on the top left and top right. The top left blue blob is a flag that the data is Simulated. The blob on the top right shows the real sensor status, Red=Unavailable, Green=Available.
Another control that has been created is a compass rose indicator, this is shown below:
If we now take a look at one of the simpler controls, the CompassHeading
control, this takes the Compass Heading and converts the numerical value to either of "N, NE, E, SE, S, SW, W, NW
". You can see how this control listens to events from the Sensor
wrapper:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using UltraDynamo.Sensors;
namespace UltraDynamo.Controls
{
public partial class UICompassHeadingLetters : UserControl
{
MyCompass myCompass;
public UICompassHeadingLetters()
{
InitializeComponent();
myCompass = new MyCompass();
MyCompass.Change += new MyCompass.ChangeHandler(myCompass_Change);
myCompass_Change(myCompass, new EventArgs());
}
void myCompass_Change(MyCompass sender, EventArgs e)
{
this.Refresh();
}
private void UICompassHeadingLetters_Paint(object sender, PaintEventArgs e)
{
String output = "N";
double heading = MyCompass.Heading;
if (heading >= 22.5 && heading < 67.5)
output = "NE";
if (heading >= 67.5 && heading < 112.5)
output = "E";
if (heading >= 112.5 && heading < 157.5)
output = "SE";
if (heading >= 157.5 && heading < 202.5)
output = "S";
if (heading >= 202.5 && heading < 247.5)
output = "SW";
if (heading >= 247.5 && heading < 292.5)
output = "W";
if (heading >= 292.5 && heading < 337.5)
output = "NW";
Graphics g = e.Graphics;
Font f = this.Font;
Font newFont = this.Font;
SizeF testSize;
float fHeight=0;
float fWidth=0;
bool fontGood = true;
for (int newSize = 1; fontGood; newSize++)
{
newFont=new Font(f.Name,newSize,f.Style);
testSize = g.MeasureString(output,newFont);
fHeight = testSize.Height;
fWidth = testSize.Width;
if ((fHeight > (this.Height)) | (fWidth > (this.Width)))
{
fontGood = false;
newSize--;
if (newSize <6 ) { newSize=6;};
newFont = new Font(f.Name,newSize,f.Style);
testSize = g.MeasureString(output,newFont);
fHeight = testSize.Height;
fWidth = testSize.Width;
}
}
g.DrawString(output, newFont, Brushes.Green,
new Point((this.Width - (int)fWidth) / 2, (this.Height - (int)fHeight) / 2));
if (MyCompass.Simulated)
g.FillEllipse(Brushes.Blue, 0, 0, 5, 5);
if (MyCompass.Available)
g.FillEllipse(Brushes.Green, this.Width - 5, 0, 5, 5);
else
g.FillEllipse(Brushes.Red, this.Width - 5, 0, 5, 5);
}
}
}
The next stages of the development are to look at some real time trends, analog gauge dials and some of the other planned functionality as mentioned in the initial outline brief.
That is the end of Update 1. So far so good (I think!).
Project Update 2 (13th November, 2012)
Things have been a bit chaotic over the last couple of weeks working on this application.
Numerous problems have been encountered, and given the time frame, everything feels a bit rushed to get it "out the door". Some of the issues encountered are:
- Typical cross threading problems
- Sensors eventing conflicts
- Inherent bugs in hardware/software
- Code Signing and package creation
So where am I with things?
There was a big rewrite of some of the eventing to combat the cross threading problems. I still want to further improve this area, but can't risk breaking things at this stage. So maybe for a later date.
The simulation code shown above, was great for the initial testing, but given the rewrite of the sensor stuff, it no longer works, so has been tucked away in the background until it can be revisited at a later date.
There also appears to be a bug with the GPS subsystem at a hardware/driver level. So unfortunately, I cannot fully test or implement the features in the introduction list until this issue is resolved. However, the application still has useful functionality for an initial release.
A basic overview dashboard has been created which shows all the main sensor elements, as well as a net horsepower gauge. The HP is calculated by using the accelerometer and the vehicle (and occupants) weight. This is Net HP and does not take into account for drivetrain losses or drag losses. These can perhaps be factored in at a later date. The dashboard is shown below:
There are also a number of realtime trends that show you what has been going on, for example, the one below is the accelerometer trend.
I had problems getting a 'free' code signing certificate through and still waiting for the validation call. So I went on my own and got one through GlobalSign. That then introduced the problem of how to setup up Visual Studio to build the packages, code sign them in the post-build events. InstallShield was all new to me as well, so getting everything to package up nicely, sign the installers, etc. was a bit of a headache. This was followed by some pain of actually getting my hands on the MSI installer from setup wrapper.
Plenty of things to possibly write about at a later date, but in the meantime, I think my heads bust, and that concludes Update 2!
Project Update 3 (28th November, 2012)
After all, the hassle with submitting the app on the 14th, then the weeks delay waiting for it to be published, I just wanted to add a little update as I noticed I hadn't really mentioned it elsewhere already. In the dashboard display above, you will notice that the right hand dial indicator is displaying Net Horsepower. This is a calculated figure that is based on:
- The vehicle weight
- the Acceleration and
- the vehicle speed
If you refer to the wikipedia article and look at the calculations for the drawbar horsepower, you can see how they relate to each other.
James Watt introduced this unit of power in the 18th century and he stated that 1 horsepower was the power required to lift a 550lb weight in 1 second. It is possible to calculate this using the parameters shown and data from the Ultrabook sensors as:
Net HP = (Weight (in lbs) x Acceleration (g) x Speed (mph) ) / 375
The weight is provided by the user on the configuration page of the application, the other data is sensor derived. The 375 is a constant derived from the conversion of the 550 lb/second to miles per hour.
It would be easy to add in calculated constants for drivetrain losses and drag coefficients, but that is a job for another day.
After I had published the App, I noticed on the controls and displays, I had forgotten to enable "double buffering", so there is a bit of a flicker during updates. Since publishing, I have already also begun to rework the eventing system and various other parts of the application so hopefully get them out and released in a future update.
Other Blue Sky Idea
It may also be possible to further extend the core functionality to interface with the vehicles OBDII or CAN bus to make use of other reference data, e.g., vehicle parameters like speed, gear, throttle angles.
This is beyond the initial scope, but just a thought.
Points of Interest
This should provide plenty of research opportunity into the features of the ultrabooks and provide something interesting to get my teeth into.
History
- 13th October, 2012 - First draft of conceptual app description
- 24th October, 2012 - Project Update 1 added
- 13th November, 2012 - Project Update 2 added
- 28th November, 2013 - Project Update 3 added