Freesteel Blog » Grabbing the phone flight computer software

Grabbing the phone flight computer software

Wednesday, June 7th, 2017 at 3:28 pm Written by:

I don’t know why I refrained from looking into hacking the XCSoar flight software that I have on the phone that’s bolted to the stick to which I’ve hot-glued my temperature and orientation sensor technology.

It is now the ugliest piece of electronic junk in flight today.


But the fact that the Air-Where project seemed to have done something amazing in the last year with Lora networks and an ESP8266 to display all your flying buddies onto the same flight map as the airspace without my noticing indicated that I had some catching up to do.

Even working full time on this I can’t remotely keep up with the tech.

Here’s some of the stuff I learned in the last couple of days.

It’s hard to believe, but there’s enough vol libre hacker capacity in Europe to squander it on two completely independent open source flight computer projects, XCSoar and LK8000 which got forked acrimoniously from one another back in 2009.

And merrily they have been implementing the same things as each other over and again (see below).

I’ve downloaded and built both systems from source, following the Make instructions. (I’m terrible at OS stuff like Make; the code is in C++ and if I do anything it’ll have to be blind and without a debugger.)

The XCSoar code seems marginally more hackable at the moment, but I should check I can deploy the Android version. (Getting all this C++ stuff to run on a Java phone environment with a bunch of different sensors is an amazing achievement.) Most people go with Kobos, but I can’t cope with the lack of colour and it looks like it’s got even more difficult Operating System problems I don’t have time to learn about.

The architecture of XCSoar is given as follows:


The key therefore is the NMEA data stream, which the XCSoar program can point to as one of its inputs.

So, in the case of Air-Where, they’ve used an ESP8266 to connect to a Kobo or Notepad computer as a standard wifi hotspot (like I’ve been doing with my other ESP8266 projects) and somehow obtaining a stream of data composed of NMEA statements through a port called /dev/ttymxc0.

The most common source of NMEA is the GPS unit, like so:


I did not know this was part of an extended language, but it turns out there’s an air collision avoidance FLARM protocol in NMEA form as well.

According to the manual:


is read as:

There is a glider in the south-east direction, 1.7km away (1.2km south, 1.2km east), 220m higher flying on south track with a ground speed of 30m/s in a slight left turn with 4.5°/s turning rate, sinking with 1.4m/s. Its ID is a static FLARM-ID “DD8F12”. There is no danger.

The final number before the * is <AcftType> and it is chosen from the following real list:

0=unknown; 1=glider/motor-glider; 2=tow/tug plane; 3=helicopter/rotorcraft; 4=parachute; 5=drop plane for parachutes; 6=hang-glider (hard); 7=para-glider (soft); 8=powered aircraft; 9=jet aircraft; 10=flying saucer (UFO); 11=balloon; 12=airship; 13=unmanned aerial vehicle (UAV); 15=static object

Well, it’s good to know that if you want a softer bump you should go for the paraglider rather than the crunchier hang-glider.

But missing are the free-fall parachutists, wingsuiters, weather balloons and hobby rockets who can all carry transponders of one form or another. But you try getting the little green men from Alpha Centauri to comply with FLARM by putting the right transponder on their flying saucer.

And what’s a “static object”? There’s no such thing as antigravity, so the only kind of static object in the air is going to be a very tall building or a tethered balloon. You’d think that the existence of such a structure would be relevant to any aerial collision avoidance system, wouldn’t you? Its not the telephone poles that matter; it’s the wires between them.

In any case, both LK8000 and XCSoar have implemented this FLARM protocol, so all the Air-Where folks needed to do to achieve something amazing was to program in the relevant text strings and then the whole dynamic mapping thing works automatically on the display.

Meanwhile, I have my temperature/humidity sensors, barometer and accelerometer that I’d like to insert into the XCSoar display system so it stands a chance of showing up on a screen I’m actually going to see. I’ve learnt in practice that I’m never going to see that little OLED display I’ve lavished so much time programming up.

Accordingly, I found similar parameters in the source code and chased through all the functions to see where they are set. As a result I discovered the Compass C-Probe which contains pretty much everything I have built over time, except in a properly designed productized package:

* Pitot tube (resolution pressure of 0.01 Pa);
* Three-axis gyroscope (resolution 2000°/s);
* Three-axis accelerometer (up to 8G per axis, with 0.1 G);
* Magnetic compass gyro (1° resolution);
* Pressure sensor high resolution.
* High resolution thermometer

Their main selling point is that it gets accurate air-speed “thanks to our patent-pending automatic calibration system“.

I can’t find their particular pending patent, but it is possible that in their application for a government backed monopoly of this obvious software-implemented idea, their professional paid-for patent attorneys overlooked US Patent application 20110238373 A1 filed in 2010 by staff at NASA.

If the XCSoar and LK800 software upgraded themselves to the GPLv3 license, we could explicitly do away with this sort of nonsense, waste of money and waste of time.

My favourite bit is about this procude is this page from their website, titled: “The reason why we do not use the accelerometer to integrate the variometer”

Many pilots ask us the reason why we do not integrate the variometer by an accelerometer…

This idea was soon abandoned… By integrating an accelerometer, the vario becomes more responsive, but we found a problem. If the pilot starts to turn (or already is in a turn) the accelerometer feels not only the lift force, but the response of the sensor is affected by the angular velocity. This can false the variometer’s reliability, in terms of climb rate. Therefore, to compensate, it becomes necessary to perform a series of calculations. But, in turn, this operation increases the electronic noise. As consequence, the variometer becomes less precise, and the unit is compelled to use some more bits to sample the data.

Luckily I didn’t even try this. With a lot of research it might be possible to invent an entirely new instrument, one that perhaps registered changes in horizontal airflow.

I’m also pleased to see all their notes on their website about electronic noise wrecking the precision of these sensors, because that’s exactly what I found early on.

A C-probe NMEA statement looks like this:


Both LK8000 and XCSoar have implemented CProbe NMEA statement parsers.

In XCSoar it looks like this:

static bool
ParseData(NMEAInputLine &line, NMEAInfo &info)
  // $PCPROBE,T,Q0,Q1,Q2,Q3,ax,ay,az,temp,rh,batt,delta_press,abs_press,C,
  // see

  unsigned _q[4];
  bool q_available = line.ReadHexChecked(_q[0]);
  if (!line.ReadHexChecked(_q[1]))
    q_available = false;
  if (!line.ReadHexChecked(_q[2]))
    q_available = false;
  if (!line.ReadHexChecked(_q[3]))
    q_available = false;

  if (q_available) {
    double q[4];
    for (unsigned i = 0; i < 4; ++i)
      // Cast to int16_t to interpret the 16th bit as the sign bit
      q[i] = int16_t(_q[i]) / 1000.;

    double sin_pitch = -2 * (q[0] * q[2] - q[3] * q[1]);
    if (sin_pitch <= 1 && sin_pitch >= -1) {
      info.attitude.pitch_angle = Angle::asin(sin_pitch);

      Angle heading = Angle::HalfCircle() +
        Angle::FromXY(Square(q[3]) - Square(q[0]) - Square(q[1]) + Square(q[2]),
                      2 * (q[1] * q[2] + q[3] * q[0]));

      info.attitude.heading = heading;

      Angle roll = Angle::FromXY(Square(q[3]) + Square(q[0]) - Square(q[1]) - Square(q[2]),
                                 2 * (q[0] * q[1] + q[3] * q[2]));

      info.attitude.bank_angle = roll;

And in LK8000 it looks like this:

BOOL CDevCProbe::ParseData( tnmeastring& wiss, NMEA_INFO *pINFO ) {

	double q0 = int16toDouble(HexStrToInt(wiss.GetNextString())) * 0.001;
	double q1 = int16toDouble(HexStrToInt(wiss.GetNextString())) * 0.001;
	double q2 = int16toDouble(HexStrToInt(wiss.GetNextString())) * 0.001;
	double q3 = int16toDouble(HexStrToInt(wiss.GetNextString())) * 0.001;

	double sin_pitch = -2 * (q0 * q2 - q3 * q1); // if sin_pitch > 1 or sin_pitch < -1, discard the data

	if(sin_pitch < 1.0 && sin_pitch > -1.0){
		pINFO->MagneticHeading = (PI + atan2(2*(q1 * q2 + q3 * q0), q3 * q3 - q0 * q0 - q1 * q1 + q2 * q2))*RAD_TO_DEG;

		pINFO->Pitch = asin(sin_pitch)*RAD_TO_DEG;
		pINFO->Roll = atan2( 2 * (q0 * q1 + q3 * q2), q3 * q3 + q0 * q0 - q1 * q1 - q2 * q2)*RAD_TO_DEG;

I’m a big fan of long lines to perform geometrical calculations, rather than line-breaking them up in short chunks so they become unreadable.

You’ve also got the different ideas for when you have four variables and you can call them q[0], q[1], q[2], q[3] or q0, q1, q2, q3.

And finally, the second code snippet realizes that angles should just be a number in straightforward Degrees (never evil Radians), and not encapsulated into a complicated 500 line C++ class.

I have written a vast amounts of this sort of code for running machine tools, so I do like to see the existence of contrasting styles where at least one side agrees with me. Most of the time I lost my arguments, even though I was right. Radians are the purest evil and should never be in any program concerned with geometry rather than differential calculus.

Nevertheless, I’m going to start trying to hack the XCSoar code because it builds slightly more quickly. It’ll probably be a mistake.

Leave a comment

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <blockquote cite=""> <code> <em> <strong>