UKHAS Wiki

UK High Altitude Society

User Tools

Site Tools


guides:common_coding_errors_payload_testing

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
guides:common_coding_errors_payload_testing [2012/08/28 09:11] – [Simulating a Flight] daveakeguides:common_coding_errors_payload_testing [2012/10/09 18:54] (current) – [Prime Meridian] danielrichman
Line 5: Line 5:
 {{:guides:paddingerrors2.jpg|}} {{:guides:paddingerrors2.jpg|}}
  
-===== Coordinates and Floating Points ====+===== Coordinates and turning floats into strings ====
  
-With limited memory on a typical microcontroller, the printf() series of functions in [[http://www.nongnu.org/avr-libc/|AVR Libc]] (used by Arduino) do not by default support printing of floating point values using the %f operator. Although versions with float support can be used they require a significant amount of memory, sometimes more than the program itself. Because of this it is common to divide up a floating point value into two integers and print it with two %i operators. A typical example might be:+==== The easy way ====
  
-<code c>char s[30]; +**Wherever possible, use functions provided by libc and gcc rather than writing your own!**
-float lat = 54.4566;+
  
-int i1 = lat; // == 54 +Seriously. The GCC and libc functions are probably faster and much more likely to be free of bugs.
-int i2 = (lat - i1) * 10000; // == 4566+
  
-snprintf(s30, "%i.%i"i1i2); // == "54.4566"+Here are two functions that can turn floats into strings: 
 + 
 +<code c> 
 +char s[30]; 
 +double lat = 54.086611; 
 +dtostrf(lat86, s); // == "54.086611"
 </code> </code>
  
-This code will work most of the timebut it contains two important errors.+<code c> 
 +char s[30]; 
 +double lat = 54.086611; 
 +snprintf(s30, "%.6f", lat); // == "54.086611" 
 +</code>
  
-==== Padding Issues ====+Unfortunately the second one might not always compile. With limited memory on a typical microcontroller, the printf() series of functions in [[http://www.nongnu.org/avr-libc/|AVR Libc]] (used by Arduino) do not by default support printing of floating point values using the %f operator. You can enable floating point support in printf, though it's difficult.
  
-The padding error will appear when the floating point value is below 0.1, so a float of 54.0055 will get split into 54 and 55, and the resulting string will contain "54.55" instead of 54.0055. To correctly print this the second integer must be zero padded to the correct number of decimal places:+==== The hard way ====
  
-<code c>snprintf(s, 30, "%i.%04i"i1, i2);</code>+It is common to divide up a floating point value into two integers and print it with two %i operatorsWe quite often see code like the below. Honestlyusing dtostrf should be much easier. The notes below might be a useful read if you're not familiar with these common C mistakes anyway.
  
-In our example we are using four decimal placesso %04i will ensure that the second integer (55is printed as "0055giving a final correct result of "54.0055".+<code c> 
 +char s[30]; 
 +double lat = 54.086611; 
 + 
 +int i1 = lat; // == 54, the integer part 
 +int i2 = (lat - i1* 1000000; // == 21076 ... uh oh 
 + 
 +snprintf(s, 30, "%i.%i", i1, i2); // == "54.21076-- that's not good. 
 +</code> 
 + 
 +We wanted i1 to contain 54, i2 to contain 86611 and s to be the string "54.086611" - but that's not what happened. 
 + 
 +Don't use this code! Let's have a look at its problems.
  
 ==== Overflow Issues ==== ==== Overflow Issues ====
  
-Overflow issues in conversion of float to string i.e 51.82181 overflows to 51.16645\\  +On the Arduino, an 'int' is 16 bits (and signed), so can only contain values from -32768 to 32767. Instead of 456611, the i2 contains -2414. 
-Note on Arduino int can only hold a number up to 32768 so when converting the decimal part of the above into an integer value you should be using the long data type.+When converting the decimal part of the above into an integer value you should be using the long data type, which is 32 bits wide and can therefore store from −2,147,483,648 to 2,147,483,647. 
 + 
 +Fixed code: 
 +<code c>long i2 = (lat - i1) * 1000000; // == 86611, the decimal part.</code> 
 + 
 +==== Floating point rounding errors ==== 
 + 
 +You might actually get 86610 due to the wonders of floating point and the fact that the default conversion from float to int floors the float rather than rounds it. This is not really a big deal, but if you want to you can fix this: 
 + 
 +<code c>long i2 = lround((lat - i1) * 1000000);</code> 
 + 
 +==== Padding Issues ==== 
 + 
 +Excellent! However, s now contains the string "54.86611", not "54.086611". Oops. 
 + 
 +The padding error will appear when the decimal part is below 0.1. Our float of 54.086611 gets split into 54 and 86611, and the resulting string is "54.86611" instead of 54.086611. To correctly print this the second integer must be zero padded to the correct number of decimal places: 
 + 
 +<code c>snprintf(s, 30, "%i.%06li", i1, i2);</code> 
 + 
 +In our example we are using six decimal places, so %06li will ensure that the second integer (86611) is printed as "086611" giving a final correct result of "54.086611"
 +The 'l' is there because i2 is now a long integer, and printf likes to know. 
 + 
 +==== Prime Meridian ==== 
 + 
 +In the UK and especially with our launch sites located around Cambridge the [[http://en.wikipedia.org/wiki/Prime_Meridian|Prime Meridian]] comes into play. This is the 0' line of Longitude where the values of longitude go from east to west, or in decimal form from positive to negative. 
 + 
 +Suppose we used the same code as above to print our longitude values.
  
-Example code : 
 <code c> <code c>
-long LATITUDE_DECIMAL LATITUDE_MINUTES 1000000L 60 + LATITUDE_SECONDS * 1000000L 3600+char s[30]; 
 +double lon = -1.001234; 
 + 
 +int i1 = lon; // == -1, the integer part 
 +long i2 lround((lon - i1) 1000000); // == -1234 ... hmm 
 + 
 +snprintf(s, 30, "%i.%06li", i1, i2)// == "-1.-1234" -- lol.
 </code> </code>
-                   
-Then in sprintf use %06li to avoid the padding issues discussed above. 
-==== Prime Meridian ==== 
  
-In the UK and especially with our launch sites located around Cambridge the [[http://en.wikipedia.org/wiki/Prime_Meridian|Prime Meridian]] comes into playThis is the 0' line of Longitude where the values of longitude go from east to west, or in decimal form from positive to negative.\\ +The solution is quite simple. We simply need to make sure i2 is always positive, using the labs() function. 
 + 
 +<code c>long i2 = labs(lround((lon - i1) * 1000000)); // == 1234</code> 
 + 
 +There's another case to consider. Clearly the idea is to split the float into the an integer and a fraction of an integer (i1 and i2 respectively). 
 + 
 +The intention is the above code is that the integer part will supply the negative sign if required. However, consider -0.7. i1 would be -0, but -0 == 0, and the result would be "0.7". We'll have to handle negative numbers explicitly: 
 + 
 +<code c>snprintf(where, buf_size, "%s%i.%06li", (what < 0 ? "-" : ""), labs(i1), i2);</code>
  
-This is fairly easy to code for but it should be checked. The tracker does accept a + in front of location information. 
  
 ==== Equatorial ==== ==== Equatorial ====
  
-Theoretical Equatorial not really an issue but worth mentioning for our Kenyan viewers.\\ +Practically the Equator is not really an issue for us but worth mentioning for our Kenyan viewers. Really, you should place your float to string code in a separate function and call it for latitude and longitude. This also means that this function can be unit tested. 
 + 
 +==== Fixed version ==== 
 + 
 +You might need to #include stdlib for labs and lround. 
 + 
 +<code c> 
 +#include <stdlib.h> 
 + 
 +void print_float(float what, char *where, int buf_size) 
 +
 +    int i1 = what; 
 +    long i2 = labs(lround((what - i1) * 1000000)); 
 +  
 +    snprintf(where, buf_size, "%s%i.%06li", (what < 0 ? "-" : ""), labs(i1), i2); 
 +
 + 
 +void loop() 
 +
 +    double latitude, longitude; 
 +    char lat_str[30], lon_str[30]; 
 + 
 +    // get latitude, longitude 
 + 
 +    print_float(latitude, lat_str, 30); 
 +    print_float(longitude, lon_str, 30); 
 + 
 +    // do something with lat_str, lon_str 
 +
 +</code>
  
 ==== Negative Altitudes ==== ==== Negative Altitudes ====
  
-Not so much a bug but it's advised you use a data type that can handle negative numbers for the altitude. Occasionally the GPS can report negative altitudes on the ground, if you're using an unsigned integer this overflows. As an example -4 meters would become 65531 meters. Impressive but we wouldn't be able to accept this as a record :)+It's advised you use a data type that can handle negative numbers for the altitude. Occasionally the GPS can report negative altitudes on the ground, if you're using an unsigned integer this overflows. As an example -4 meters would become 65531 metres. Impressive but we wouldn't be able to accept this as a record :) 
 + 
 +However, note also that flights regularly go above 32767 metres. Therefore storing it in a signed integer (int) is probably not appropriate. Use a long.
  
 ====== Testing Your Payload ====== ====== Testing Your Payload ======
Line 57: Line 142:
 ==== Checking the Tracker can Decode Your Telemetry Strings ==== ==== Checking the Tracker can Decode Your Telemetry Strings ====
  
-[[http://www.robertharrison.org/listen/test.php|Submit Test Data to Listener here]] +You can view the debug log from the parser [[http://habitat.habhub.org/logtail/|here]]. Simply upload a string or two with dl-fldigi and check that it said that it parsed it.
  
 ==== Simulating a Flight ==== ==== Simulating a Flight ====
guides/common_coding_errors_payload_testing.1346145074.txt.gz · Last modified: 2012/08/28 09:11 (external edit)

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki