guides:common_coding_errors_payload_testing
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
guides:common_coding_errors_payload_testing [2011/07/17 12:55] – [Equatorial] jcoxon | guides:common_coding_errors_payload_testing [2012/10/09 18:54] (current) – [Prime Meridian] danielrichman | ||
---|---|---|---|
Line 5: | Line 5: | ||
{{: | {{: | ||
- | ===== Coordinates and Floating Points | + | ===== Coordinates and turning floats into strings |
- | With limited memory on a typical microcontroller, | + | ==== 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(s, 30, "%i.%i", i1, i2); // == "54.4566" | + | Here are two functions that can turn floats into strings: |
+ | |||
+ | <code c> | ||
+ | char s[30]; | ||
+ | double lat = 54.086611; | ||
+ | dtostrf(lat, 8, 6, s); // == "54.086611" | ||
</ | </ | ||
- | This code will work most of the time, but it contains two important errors. | + | <code c> |
+ | char s[30]; | ||
+ | double lat = 54.086611; | ||
+ | snprintf(s, 30, "%.6f", lat); // == " | ||
+ | </ | ||
- | ==== Padding Issues ==== | + | Unfortunately the second one might not always compile. With limited memory on a typical microcontroller, |
- | 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 " | + | ==== The hard way ==== |
- | <code c> | + | It is common to divide up a floating point value into two integers and print it with two %i operators. We quite often see code like the below. Honestly, using 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 places, so %04i will ensure that the second | + | <code c> |
+ | char s[30]; | ||
+ | double lat = 54.086611; | ||
+ | |||
+ | int i1 = lat; // == 54, the integer | ||
+ | int i2 = (lat - i1) * 1000000; // == 21076 ... uh oh | ||
+ | |||
+ | snprintf(s, 30, "%i.%i", i1, i2); // == "54.21076" | ||
+ | </ | ||
+ | |||
+ | We wanted i1 to contain 54, i2 to contain 86611 and s to be the string " | ||
+ | |||
+ | 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, |
+ | |||
+ | Fixed code: | ||
+ | <code c>long i2 = (lat - i1) * 1000000; // == 86611, the decimal part.</ | ||
+ | |||
+ | ==== 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);</ | ||
+ | |||
+ | ==== Padding Issues ==== | ||
+ | |||
+ | Excellent! However, s now contains the string " | ||
+ | |||
+ | 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 " | ||
+ | |||
+ | <code c> | ||
+ | |||
+ | In our example we are using six decimal places, so %06li will ensure that the second integer (86611) is printed as " | ||
+ | The ' | ||
+ | |||
+ | ==== Prime Meridian ==== | ||
+ | |||
+ | In the UK and especially with our launch sites located around Cambridge the [[http:// | ||
+ | |||
+ | Suppose we used the same code as above to print our longitude values. | ||
- | Example code : | ||
<code c> | <code c> | ||
- | long LATITUDE_DECIMAL | + | char s[30]; |
+ | double lon = -1.001234; | ||
+ | |||
+ | int i1 = lon; // == -1, the integer part | ||
+ | long i2 = lround((lon - i1) * 1000000); | ||
+ | |||
+ | snprintf(s, 30, " | ||
</ | </ | ||
- | | ||
- | 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 play. This is the 0' line of Longitude where the values of longitude go from east to west, or in decimal form from positive | + | 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)); | ||
+ | |||
+ | There' | ||
+ | |||
+ | The intention | ||
+ | |||
+ | <code c> | ||
- | 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 | + | Practically the Equator is not really an issue for us but worth mentioning for our Kenyan viewers. |
+ | |||
+ | ==== Fixed version ==== | ||
+ | |||
+ | You might need to #include stdlib for labs and lround. | ||
+ | |||
+ | <code c> | ||
+ | #include < | ||
+ | |||
+ | void print_float(float what, char *where, int buf_size) | ||
+ | { | ||
+ | int i1 = what; | ||
+ | long i2 = labs(lround((what - i1) * 1000000)); | ||
+ | |||
+ | snprintf(where, | ||
+ | } | ||
+ | |||
+ | void loop() | ||
+ | { | ||
+ | double latitude, longitude; | ||
+ | char lat_str[30], | ||
+ | |||
+ | // get latitude, longitude | ||
+ | |||
+ | print_float(latitude, | ||
+ | print_float(longitude, | ||
+ | |||
+ | // do something with lat_str, lon_str | ||
+ | } | ||
+ | </ | ||
==== 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' | + | 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' |
+ | |||
+ | 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 | + | You can view the debug log from the parser |
==== Simulating a Flight ==== | ==== Simulating a Flight ==== | ||
Line 116: | Line 200: | ||
Compile up the C programs | Compile up the C programs | ||
- | GPSgen takes a KML file and converts it to nema GPS strings. | + | GPSgen takes a KML file and converts it to NMEA GPS strings. |
< | < |
guides/common_coding_errors_payload_testing.1310907337.txt.gz · Last modified: 2011/07/17 12:55 by jcoxon