Data Format and Semantic Specification for Telemetry Data in the Field of Aeromodeling

Version 1.0.1.  → Change List helmut@stettmaier.de

Abstract

A System of data formats and vocabularies is presented. The definitions are used to fully describe, formally and semantically, a data item received from the telemetry data receiving unit in a RC transmitter device.

Following these definitions shall make it easier to process and render these data and it shall support portability of software and usefulness of rendering devices.

State of This Article

Currently this article is a request for comments.

Copyright Notice

Copyright © 2018 Helmut Stettmaier. All rights reserved.

This document may freely be copied or published but it must not be modified in any way. Requests for modifications must be sent to the author or a person authorized by the author for review and maintenance.

Table of Contents

1      Motivation
2      Overview
3      Data Formats
3.1      Internal Data Formats
3.2      External Data Format
4      Data Semantics (Vocabularies)
4.1      Numerical Properties
4.2      Physical Meaning
5      Glossary
6      Few Detailed Considerations
        Change List

1    Motivation

Telemetry data are acquired in the aeromodel by sensor devices - there are very different kinds of such sensor devices and data acquisition system architectures. The data are sent “down” to the RC transmitter (acting as a receiver in this case). The protocols fit somehow into the WiFi framework, WiFi hardware is employed, but the used systems differ significantly and are not compatible.

Many or most RC transmitter devices allow the down coming telemetry data stream to be listened to by other devices as long as the data stream protocol and the data formats are understood. Not all manufacturers publish their “telemetry protocol” and the data formats, but perhaps some see, with some eye twinkle, “hacking” these informations as a qualification for joining the game.

Currently computers, which extensively (as the reason for existence) process and render telemetry data do not belong to the RC transmitter but work externally and it would be a really good idea when such “Render Engines” can be connected to different RC brands' transmitters and differ only in their drivers for the RC equipment but are equivalent otherwise. It, of course, should be possible to use data processing or rendering routines without changes.

Render Engines will offer fields for experiments and experimenters! Data processing or rendering routines may be shared as libraries or as source programs and working with standard data formats with unified semantics is a prerequisite to achieve this.

Telemetry data may be logged, post processed and compared to others. In the future such data may be exchanged “live” over a local network. All this is easier to perform when standard data formats are used.

2    Overview

The proposed Standard for data description consists of several parts:

For these vocabularies / dictionaries exists a common “root” file for all the name / code pairs and comment texts defining the semantics. Any other files MUST be derived from this root document. A simple awk script is provided to generate such translations into header files and into a declaration and definition file for a dictionary data structure in C++.

The intended usage is as follows:

Data, as acquired by sensor devices, are subjects to limitations: The most common limitation is the resolution, e.g. many aeromodelers' altimeters yield the height with a resolution of 1m.

The data are transferred “down” to the RC transmitter and during transmission additional restrictions may come into effect, mainly because of the transmission formats dictated by low bandwidth.

When the data are read out of the radio module of the RC "transmitter" device into the Render Engine the input driver in the Render Engine “must know” about the type of the data and the limitations of the sensor device and the transmission channel; it must choose the corresponding type code and the numerical properties for the received data item. Finding out the semantic meaning and the numerical properties depends on the transmission protocol and data format: its position in a large block (Graupner's HoTT®) or on several additional information per (shorter) block (Multiplex' MLink® / MSB®) and must be hard coded and/or controlled by user defined operational parameters.

An Example: The input driver in my Render Engine reads MLink / MSB data. An MSB-block contains a number, an alarm flag, a data type code and a channel number. The user configures the sensor device on which channel the value is to be sent (this controls the standard rendering); in this example I set channel=2. The type code used by MSB sensor devices for “height / short distance” is 8. So I can state that a value of type 8 and transmitted over channel 2 is the height above ground level (AGL) in [m]. The Render Engine reads an operational parameter how “to translate a data item <channel 2 & Type 8> into <Altitude relative / float / resolution 1m / range -16347..+16347>” - see Numerical Properties.
So, when the data has passed the input driver, is well formatted as vehicle's height in [m] in a standardized numerical format.

Other information for the telemetry data item may be generated by the driver, most prominently the time-stamp.

Processing routines may generate new data out of more received data, e.g. TEK calculation: The CAS (calibrated air speed) may be used to compensate the climb speed by eliminating “stick thermal”. In this case two data items (uncalibrated climb speed and CAS) are used to create another item (TEK calibrated climb speed). Such computations need not only the “correct formula” but also thorough checks for overflows or other numerical traps and thoroughly evaluating the numerical properties of each input item and also formulating the resulting numerical properties of the result.

3    Data Formats

Telemetry data are processed internally in the Render Engine, processing routines may be exchanged as sources or as libraries - this makes it necessary to use standard data structures. I define such structures for C and C++, other programming languages will follow.

Telemetry data are stored permanently (logged) and read by “other” programs, or, in the future, even exchanged when RC transmitters form ad hoc networks. Thus an external data format for storing and transmitting these data is defined.

These definitions do not contain specifications for semantics (except for the time-stamp), this is described later in Data Semantics.

3.1    Internal Data Formats

The Render Engine's input driver yields telemetry data items one by one in real time as they come in. Input data formats may deliver more than one data items as they may be clustered in one transmission block. Therefore it is possible (even usual) that the input driver delivers few data items together. Optionally the driver could deliver these data items “together” in an array or a similar method, but the application reading these items SHOULD NOT rely on this, as this might be a handicap for designing portable applications (portability regarding different RC systems). In contrary, the application SHOULD store received data items in its own local storage and, when combining them, thoroughly check the time-stamps to see if the data items really are related.

An example: The HoTT system may transmit true climb speed and speed (IAS) together in one transmission block and when the one item is received correctly, the other will also be correct. For the MLink / MSB system this is not necessarily the case as the true climb speed and speed (IAS) are transmitted in different blocks, possibly with different frequencies (for example height prioritized about 10 times per second, speed about 2 times per second), and one of them may be lost. This might affect TEK calculation.

These formats are defined using programming languages. This may be affected by the endianess of the computer in use, therefore block graphics are not used. Applications SHALL NOT depend on specific orders of bytes in words or bits in bytes other than explicitly specified here.

A telemetry data item consist of:

The full data structure

... for a telemetry data item is in C:

typedef struct {
    TDISpec_t s;
    uValue_T v;
    uint32_t timestamp;
} TDItem_t;
void serialize(TDItem_t &this, void *(uint8_t b)sendByte());
void deserialize(TDItem_t &this, uint8_t *()receiveByte());

The full C++ class declaration for a telemetry data item is:

class CTDItem {
protected:
    TDISpec_t s;
    uValue_T v;
    uint32_t timestamp;
public:
    CTDItem();
    void clear() { s.w= v.uiValue= timestamp= 0; };
    uint16_t getTDSemantic();
    void setTCSemantic(uint16_t semcode);
    int8_t getNumCode();
    int8_t getNumSubCode();
    void setTDNum(uint8_t numcode);
    uint8_t getTDFlags();
    void setTDFlags(uint8_t statcode);
    int isTDItemDefined();
    int isTDItemValueDefined();
    int isTDItemTimestampDefined();
    int32_t getiValue();
    void setiValue(int32_t val, uint8_t numsubcode= TDNUM_UNDEF);
    uint32_t getuiValue();
    void setuiValue(uint32_t val, uint8_t numsubcode= TDNUM_UNDEF);
    float getfValue();
    void setfValue(float val, uint8_t numsubcode= TDNUM_UNDEF);
    q1516_t getq1516Value();
    void setq1516Value(q1516_t val, uint8_t numsubcode= TDNUM_UNDEF);
    qwgs84_t getqwgs84Value();
    void setqwgs84Value(qwgs84_t val, uint8_t numsubcode= TDNUM_UNDEF);
    uint32_t getTimestamp();
    void setTimestamp(uint32_t ts);
    void serialize(void *(uint8_t b)sendByte());
    void deserialize(uint8_t *()receiveByte());
}

Normally the getter and setter functions are defined in the header file as inlines, but this is out of scope for this paper.

The time-stamp has a resolution of 1 ms. The time corresponding to the value 0 is less precise defined: Usually you may use the power on time. This may cause minor challenges when logs are to be evaluated, or it may cause more important questions when several Render Engines are connected via a network, as they are switched on at different times. This question is not treated now in detail as such networks are not projected; such a protocol will include CTDItem instances with only a time-stamp defined for synchronization.

It is to be expected that, at least in “smaller” implementations of Render Engines, those data structures are not created dynamically but are stored in static instances or in queues and are reused when new data come in, therefore the extra function clear() is foreseen.

None of these functions is thread safe. At least in “smaller” implementations of Render Engines concurrency problems may be avoided very simply and in “other” implementations the CTDItems transporting queues are better places to handle concurrency problems.

Flags etc.

The sub structure for the semantical specification is in C:

typedef union {
    uint32_t w;         ///< guarantees atomic access and word alignment
    struct {
        uint8_t flags;  ///< see enum eTDIflagcodes
        uint8_t num;    ///< see TDNUM_...
        uint16_t sem;   ///< see TDEL_SUPPLY etc.
    } b;
} TDISpec_t;

The meanings of the flags are defined in an enumeration:

enum eTDIflagcodes {
    TDIstatUndef= 0,    ///< The sensor device signals "undefined value".
    TDIstatNormal= 1,   ///< Normal state.
    TDIstatWarning= 2,  ///< Sensor device signals "Warning" for the value.
    TDIstatAlarm= 3,    ///< Sensor device signals "Alarm" for the value.
    TDIstatMask= 3,     ///< Masks all stat bits
    TDIvaluedefined= 4, ///< Set when the input driver has set the value
    TDItstampdefined=8, ///< Set when the time-stamp is defined
};

There are left some bits for future extensions. Notes:

Values

... can be of one of several types and here is a definition of a corresponding union type:

typedef union {
    int32_t iValue;
    uint32_t uiValue;
    float fValue;
    q1516_t q1516Value;
    qwgs84_t qCoordinateValue;
} uValue_T;

It depends on the numerical properties of the data item which alternative of this union is chosen to store the value of the item. There is no “default” defined. Any telemetry data item producing code (input driver or application code) MUST use the alternative that is named in the numerical properties code.

3.2    External Data Format

The external data format is used when telemetry data items are stored (logged) or, in the future, are sent to or received from other devices in a local network.

The format is related to RFC 4506 which describes data formats for transmission in the Internet and a specification language, “XDR”. Of course RFC 4506 cannot be applied 1:1 as several data types are not specified, but these main features do apply:

The format is simple:

  1. The first 32-bit word is TDISpec_t s,
  2. The second 32-bit word is uValue_T v. Note that all alternatives are 32 bits long.
  3. The third 32-bit word is the time-stamp.

An example (for detailed codes see below): A CTDItem instance with the listed contents is serialized into the given byte stream:

The hexadecimal byte stream is (from left to right):    e7 42 43 0c  00 21 33 34  00 01 11 11.

There is no escape symbol or synchronization mechanism foreseen, the data link layer must take care of proper framing etc.

4    Data Semantics (Vocabularies, Dictionaries)

Vocabularies and dictionaries are used to specify semantics and number formats. There is a “mother list” of vocables and the corresponding meaning. Other formats can be derived from this list:

Some grouping and refinement is used to improve usability.

4.1    Numerical Properties

Numerical properties of a data item consist of

It's hereby promised that code 0 for the range or resolution always means “undefined”: No information regarding the range or the resolution is available but the value is valid in the sense, that it really was acquired regularly.

The following data types and range or resolution codes are foreseen:

Signed Integer

32-bit integers with two's complement are used, the “natural” range goes from -2147483648 to 2147483647. The resolution of integer values is (naturally) 1.

The range of values for signed integer numbers may be limited, up to 15 codes for explicitly defined ranges are provided:

symbolTDNUM_UNDEFTDNUM_SIGNTDNUM_1...TDNUM_FULLRANGE
code021...15
rangeundefined0-9..+9...full range

(to be revised)

The type code for signed integers is: TDNUM_INT32= 0x10 and it is ORed with the range code. Examples: TDNUM_INT32 | TDNUM_1 denotes a signed integer that may have a value ≥-9 and ≤+9. TDNUM_INT32 | TDNUM_SIGN denotes a signed integer with no digits. It may be used to represent flags to distinguish between “left” and “right” or “lower” and “not lower”.

Unsigned Integer

32-bit integers are used, the “natural” range goes from 0 to 4294967295. The resolution of integer values is (naturally) 1.

The range of values for signed integer numbers may be limited, up to 15 codes for explicitly defined ranges are provided:

symbolTDNUM_UNDEFTDNUM_BOOLTDNUM_RSSITDNUM_1
code0231
rangeundefined0..10..50..9
symbol...TDNUM_FULLRANGE
code...15
range...full range

(to be revised)

The type code for unsigned integers is TDNUM_UINT32= 0x20 and it is ORed with the range code. Example: b.num= TDNUM_UINT32 | TDNUM_1 denotes an unsigned integer that may have a value up to 10.

(Single) float

Usage of this type depends on the MCU on which the Render Engine is running.

When a floating point unit (FPU) is available, as is the case with most ARM Cortex M4 MCUs, using float is the best choice in many cases. On other MCUs float numbers must be processed using a float library and this might be very slow.

When telemetry data are received from the RC transmitter the user dictates what is translated to what and he or she may avoid floats, but when telemetry data items are read from a log or are received via a network, floating point data types can possibly not be avoided and must be converted when no FPU is available.

IEEE-754 defines special codes for +infinity, -infinity and for “not a number” - these codes SHALL be used appropriately and even values, which are received from a network, may contain these codes. C / C++ allows tests using isnan() and isinf(), but distinction between +inf and -inf must be done extra: if (value<0 && isinf(value)) ...

Floating point numbers are used to represent physical values that are measured with limited resolution (precision, measurement errors etc. are NOT discussed here). Sensor devices are specified to deliver such values with step widths of “0.1 V”, “mA” or “100 m” for example. The scaling factors are already counted for during translation: When a sensor device delivers 24 meaning “2.4 V”, the factor 10 is already divided (to get 2.4 out of 24), but further processing might be influenced by the step widths.

There are codes provided for the following resolutions:

symbolTDNUM_UNDEFTDNUM_1TDNUM_10TDNUM_100
code0123
resolutionundefined110100
symbolTDNUM_1000TDNUM_01TDNUM_001TDNUM_0001
code491011
resolution1E30.10.011E-3

(To be revised!)

The resolutions apply to the physical unit that is defined in the physical meaning of the item.

The type code for float numbers is TDNUM_FLOAT32= 0x30 and it is ORed with the resolution code

Examples:
Voltages are measured in V, the “correct” value of 3.5V is stored in a float number +3.5. When the sensor device can only deliver values of multiples of 0.1 V, the resolution code TDNUM_01 is to be used.
And a Shepard Tone rendering of a high definition height cannot be done using a telemetry height data item that has a resolution of 1 m (code TDNUM_1).

q15.16

This is a fixed point rational number format. It is 32 bits long, the 16 less significant bits are used as fractional part, the upper 16 bits are used as integral part, including the sign bit. q15.16 numbers are, in fact, signed integers and, as a convention, the intended value is computed by dividing the integer value by 65536. The numerical range of such numbers goes from about -32768.999985 to +32768.999985 and the ε ≈ 0.000015. The hexadecimal word 0x00010000 represents the number 1.

Trivial looking numbers like 0.1 cannot be represented exactly, as is also the case for float numbers, and must be approximated. An example is: the q15.16 value of 0.1 is approximated by 0x0000199a, what represents the q15.16 value of 0.100006 .

The q15.16 is format is, a bit surprisingly, suitable for most telemetry data in the field of aeromodeling and arithmetic operations can be easily performed on MCUs without floating point unit, but the implementation of q15.16-arithmetic is beyond the scope of this paper.

There are codes provided for the following resolutions:

symbolTDNUM_UNDEFTDNUM_1TDNUM_10TDNUM_100
code0123
resolutionundefined110100
symbolTDNUM_1000TDNUM_01TDNUM_001TDNUM_0001
code491011
resolution1E30.10.011E-3

(To be revised!)

The type code for q15.16 numbers is TDNUM_Q1516= 0x40 and it is ORed with the resolution code.

An example is: TDNUM_Q1516 | TDNUM_01 may be good for a telemetry data item that contains a value from a voltage sensor that can measure in 0.1-Volt-steps and stores the value as a q15.16 number.

qwgs84

This ugly data format is defined for WGS84 coordinates. The standard format for such coordinates is a rational number with an integral part for the degrees and a “continuous” fractional part. The traditional format using degrees, minutes and seconds (the seconds with a fractional part) is impractical, it may be used to find a position in a traditional map and SHOULD therefore only be used for rendering. Example: Standard format: +48.362295°, traditional format: 48°21'44.262"N.

This data format has a 9-bit signed integer part that can hold the numbers -256 to +255, good for the -180° to +180° range. The fractional part holds the part <1°, it has 23 bits, so the ε ≈ 0.00000012 what is viewed sufficient for all practical cases.

The type code for qwgs84_t numbers is TDNUM_QWGS84= 0x90 and it is ORed with an sub-code for the type of the coordinate (longitude, latitude) and for the resolution delivered by the GPS device:

symbolTDNUM_LON_UNDEFTDNUM_LON01TDNUM_LON05TDNUM_LON1
code0123
data typelongitudelongitudelongitudelongitude
resolution [m]undefined0.10.51
symbolTDNUM_LON5TDNUM_LAT_UNDEFTDNUM_LAT01TDNUM_LAT05
code48910
data typelongitudelatitudelatitudelatitude
resolution [m]5undefined0.10.5
symbolTDNUM_LAT1TDNUM_LAT5
code1112
data typelatitudelatitude
resolution [m]15

4.2    Physical Meaning

There are groups of items belonging together. Examples are:

There can be defined up to 255 groups, the constant representing a group occupies the upper byte in s.b.sem. Examples: TDEL_SUPPLY has the code 0cf7 and TDNAV_DRIVE has the code 0cf6.

Items, members of groups, specify the details of what is being meant. Examples:

Item codes occupy the lower byte of s.b.sem. Examples: TD_SPEED_CLIMB has the code 0x40; TD_TEMPERATURE has the same code, but the ambiguity is resolved using the group code, as any speed is never a member in the TDEL_SUPPLY or TDEL_DRIVE etc. groups and any temperature is notmember in a TDNAV_... group.

Some item codes, where appropriate, leave 4 bits free for an index, to specify furtherly what is meant. Example: TDEL_SUPPLY | TD_VOLTAGE | 1 specifies the voltage of the 2nd cell (counted from 0 up) of the accumulator of the RC supply. If more accumulators are in use, the cells SHALL be unique: Cells 0 and 1 for the first accumulator, 2 and 3 for the second accumulator.

More Examples:

5    Glossary

Altitude vs. Height     Altitude is used for distances over a “non regional” reference, mostly the mean sea level (MSL); for flying in the upper airspace the altitude refers to MSL, but a standard pressure is used such that these altitude numbers do not depend on the wheather.
Height is used for distances over regional references, mostly the ground elevation of the next air field - for aeromodeling the elevation (altitude MSL) of the point, where the equipment is switched on, is used.

ε     Finest resolution which is possible with a specific number format.

IAS      Indicated Airspeed, "what is yielded by the instrument", without any error compensation.

CAS    Calibrated Airspeed, Errors yielded by the instrument and errors caused by the flow field around the body are compensated. NOT compensated are errors introduced by the altitude and by the Mach number.

TEK     German, abbreviation for “Total Energie Kompensation”.

6    Few Detailed Considerations

The following details are not closely related to the data formats described above, but yield some explanations or shall complete the specifications with "best practice" recommendations. When SHALL etc. is used it is really meant as described in RFC2119 & RFC8174.

ε
This is the value of the smallest step that can be counted within a numerical data. Example: The q15.16 value (decimal value) that is coded with the hexadecimal word 0x10000 is 1.0 and the decimal value coded with 0x10001, the next possible value, is 1.000015259, the ε for q15.16-numbers is 0.000015259. A (single) float number has an ε of 2^-24≈ 5,96E−8, indicating 7..8 valid decimal digits.

IAS vs. CAS
Officially IAS means “what the instrument indicates” without any compensation for (known or unknown) errors. This is not what is meant in common use of this abbreviation. Instrument errors are thoroughly measured and canceled out during instrument calibration. Other errors like air flow disturbances are also known and compensated… for aviation instruments. The result is the “calibrated air speed”, CAS, but it is mostly called IAS. Most aeromodelers do not care about these errors as it is not easy to measure them without reasonable equipment, and simply set IAS≡CAS. Rendering engines, that are conform to this standard, MUST distinguish between IAS and CAS as sometimes compensations are possible and this must not introduce uncertainties or confusion.
When the sensor device does compensate instrument and air flow errors by itself these speed data MUST be interpreted as CAS already by the input driver. When the sensor device does not compensate these errors and sends IAS data the rendering engine MUST do the translation of IAS data into CAS data, even when this translation is trivial (just change the code from IAS to CAS, when appropriate).

Discussion of the qwgs84_t format
One degree (°) on a orthodrome (e.g. the equator) around the earth, viewed as a sphere, represents a length of 4E4km/360°= 111.1… km/°. A number that can resolve 1 m must distinguish values with a difference of 1/111111.1…= 0.000009 and therefore must have (log(1/9E6)≈−5,046)→5 decimal digits for the fractional part (in addition to the 3 digits of the integer part). As the numbers are binary numbers a similar calculation for binary digits yields log(1/4E7)/(log(2)≈−16,76, meaning that at least 17 bits are needed for the fractional part (and another 9 bits for the integer part and the sign).
q15.16 numbers can not store such numbers and (single) float numbers can just store such numbers (squeezing the last bit), but there is absolutely no reserve and the 1m resolution cannot be surpassed. But a 32-bit word is really long enough to store these specific numbers. So the 32-bit word is divided into an integer and a fractional part such that the standard format of a coordinate can be stored, converted (e.g. to double) and processed or rendered.
As decimal fractional parts cannot be represented exactly binary numbers containing the minimal number of bits tend to introduce an effect which looks like “irregular aliasing” for decimal representations. This can be damped by… using more bits. Additionally, future developments will make affordable GPS/Galileo-devices possible which can resolve down to 10 cm and perhaps users will come who need such positions, therefore any available bit should be used in the fractional part of WGS84-numbers. This results in 9 bits for the integer part and the sign and 23 bits for the fractional part.
Edit: This data format is primarily used for transport and for the very first (simple) calculations, before easier to be used formats become feasible. In most practical use cases 2 positions, acquired at different locations, are to be subtracted to calculate azimuth and distance of the one location (aeromodel) to the other (pilot). This subtraction suffers under the subtraction of 2 large but nearly equal numbers which reduces relative precision dramatically - therefore as many bits in the fractional part as ever possible are a good investment. 32 Bits - 9 bits for the integral part yield 23 bits for the fractional part, 111111.11m/223≈0,013m. This is the resolution of N-S distances (for W-E distances it is improved by cos(lat)). Let only be 3 bits "bad" and the resolution is only about 10cm. After this subtraction is is advised to convert the distances to more usable data formats, e.g float or q1516.

Best practice dealing with numerical errors
Here is not the place to discuss try-catch-blocks.
Even smaller modern ARM cores (most Cortex-M4) have a floating point unit and using such numbers reduces some problems, but exception handling and error reporting must be considered. Cortex M4 MCUs provide only a single float FPU, double or extended operations are executed by library functions. An IEEE 754 conformant FPU provides a standard error and exceptions scheme.
See GNU-clib chapter 20.5.4 to see what happens when a c floating point routine is called with illegal arguments or cannot do what expected because of other reasons.
In general, when the (expensive) try-catch-functionality is used, it SHOULD always be used.
When a telemetry data item has an undefined value this is signaled in its flags byte s.b.flags. In this case the value is really undefined, there is no statement what it contains, 0.0, NaN or anything else. When a sensor device generates an "undefined" value the input driver MUST set the flag accordingly, but writing an NaN into the value (if it is a float) is optional.

Vocabulary vs. Dictionary
Any attempt to specify semantics employs some vocabulary to say “This means that and this2 means that2”. “This” and “this2” are words (vocables) with a distinct meaning and “that” / “that2” is a formulation of the meaning. We have no other applicable means for formulation of semantics than human speech. The programmer understands this explanation and the application code is written to act accordingly.

Please forgive me when I am too …basic: Vocables (symbolic names) are ok for the human reader, the programmer, but they must be mapped to binary constants - this mapping is done via dictionaries:

References

IEEE Standard 754 (WikiPedia)

RFC 4506, “XDR: External Data Representation Standard” from May 2006 by M. Eisler (Ed.),
Network Appliance, Inc., and others

RFC 2119, RFC 8174 (Explaining MUST, MUST NOT, SHALL, ...)

GNU clib: 20.5.4 Error Reporting by Mathematical Functions

Change List

VersionWhendescription
1.0.0May, 31st, 2018Initial version
1.0.1October, 3rd, 2018Additional remark in "Discussion of the qwgs84_t format" in Ch.6

⇒Impressum & Privacy