# BINEX Conventions

### Binary Exchange format for GPS/GNSSData/Metadata/Ephemerides/Orbits/Solutions

decimal: All numbers written without special notation should be assumed to be in ordinary base-10 representation, i.e. "11" means eleven; the most significant digits are to the left, as usual

hexidecimal: All numbers preceeded by the notation "0x" should be assumed to be in hexidecimal base-16 representation, i.e. 0x11 = 17 and means seventeen; the most significant digits are to the left, as usual; 0x00 = 0, ..., 0x09 = 9, 0x0a = 10, ..., 0x0f = 15, 0x10 = 16, and so on

binary: To show the actual binary structure of certain bytes, all 0/1 numbers bracketed by square brackets, [   ], in groups of 8 should be assumed to represent a byte in binary base-2 representation, i.e.  = 0x03 = 3; the most significant digits (bits) are to the left in each set of brackets

Furthermore, to describe specific bits, the bit numbering starts at 0 for the least significant bit (LSB). Bit 7 would be the most significant bit (MSB) of any 8-bit byte.

endian: To represent multi-byte values, two byte orders are allowed in BINEX. One is "big-endian" in which the most significant bytes are stored first (i.e. at the lowest memory/storage address), and "little-endian" in which the least significant bytes are stored first (for any particular multi-byte value, e.g. 2-byte integer, 8-byte floating point, etc.); see also whatis.techtarget.com and search on the word [endian], should arrive at:

and review both "big-endian" and "little-endian".

byte_order: To represent the actual byte order in part of a file or data stream, two-digit hexadecimal numbers are used, sans the leading "0x", for each byte and separating bytes with an underscore, with the first occurring bytes being to the left. For example, the 8-byte floating-point number 23456789.012 would show up as 41_76_5e_c1_50_31_26_e9 in a big-endian record and as e9_26_31_50_c1_5e_76_41 in a little-endian record using the ANSI/IEEE Std 754-1985 format (i.e. in this case, 0x41 is the most significant byte, with bit 7 being the sign bit, being = 0 in this case) (this would be the order of bytes if using UNIX "od -x" or "od -t x1" to display the byte values--but be careful!: Executing "od -x" on little-endian UNIX systems may read in two-byte words and then display the byte-swapped result. Then use "od -t x1" instead.)

record: the largest granularity of a BINEX file or data stream, made up of the generalized record structure; each record has its own unique identification (ID) number

subrecord: a specific type of BINEX record interpretation for certain record IDs; if a particular BINEX record has subrecord designations, only one subrecord per record is allowed; basically, the mechanism of sub-defining a record; a notation of 0x7f-00 may be used to indicate record 0x7f, subrecord 0x00

field: a specific component of a BINEX record for certain record IDs; if a particular BINEX record has field designations, multiple fields per record are allowed; a good example of a record with fields is record 0x00 for site metadata

sint1: a 1-byte signed integer stored as two's complement, having a value of -128 (= 0x80) to +127 (= 0x7f)

sint2: a 2-byte signed integer stored as two's complement, having a value of -32768 (= 0x8000) to +32767 (= 0x7fff)

sint4: a 4-byte signed integer stored as two's complement, having a value of -2147483648 (= 0x80000000) to +2147483647 (= 0x7fffffff)

2cNb: N-bit signed integer stored as two's complement, having a range of -2^(N-1) + 1 to +2^(N-1) - 1 and -2^(N-1) is reserved to indicate an invalid or unknown value; if N is 8, 16, or 32 and on an even byte alignment, instead refer to as sint1, sint2, or sint4 respectively

s+Nb: N+1 bits, where the MSB is a sign bit (1 = negative, 0 = positive) and the N LSBs represent the magnitude; "+0" (0 for all bits) indicates a value of zero and "-0" (1 in the MSB sign bit and 0 for all other bits) indicates an invalid or unknown value; range of ±(2^N - 1)

uint1: a 1-byte unsigned integer, having a value of 0 (= 0x00) to 255 (= 0xff)

uint2: a 2-byte unsigned integer, having a value of 0 (= 0x0000) to 65535 (= 0xffff)

uint4: a 4-byte unsigned integer, having a value of 0 (= 0x00000000) to 4294967295 (= 0xffffffff)

uNb: N bits representing a zero or positive value (i.e. no sign bit, therefore unsigned); range of 0 to 2^N - 1; if N is 8, 16, or 32 and on an even byte alignment, instead refer to as uint1, uint2, or uint4 respectively

real4: a 4-byte floating-point representation, stored according to ANSI/IEEE Std 754-1985

real8: a 8-byte floating-point representation, stored according to ANSI/IEEE Std 754-1985

ubnxi: (pronounced "YOU-bin-ox-ee") an unsigned integer stored using 1, 2, 3, or 4 bytes to present integers of 0 to 536870911, used to represent BINEX record IDs, subrecord IDs, field IDs, and so on. See details for more information.

mGFZi: (pronounced "mug-FUZZ-ee") a signed integer stored using 1, 2, 4, or 8 bytes to present integers of about -1.15292e18 to +1.15292e18 using a modified version of a compression scheme developed by GFZ, plus using "special" numbers to flag certain conditions, such as using the 1-byte mGFZi to store "-0" to mean "no value". See details for more information.

mGFZI: (pronounced "mug-FUZZ-ee") a signed integer stored using 1, 2, 3, 4, 5, 6, 7, or 8 bytes to present integers of about -1.15292e18 to +1.15292e18 using a modified version of a compression scheme developed by GFZ, plus using "special" numbers to flag certain conditions, such as using the 1-byte mGFZI to store "-0" to mean "no value". See details for more information.

SVid1: 1 byte used to encode the satellite system and related satellite number; can be used for up to 8 satellite systems (currently defined for NAVSTAR GPS, GLONASS, SBAS, Galileo, Beidou-2/Compass, and QZSS); however the corresponding satellite ID number must be in the range of 1-32. See details for more information.

Unsigned BINEX 1-4 byte integer:
The following algorithm is used to store unsigned integers from 0 to 536870911. This was developed to store the BINEX record ID, the BINEX record length, any BINEX subrecord ID, any BINEX field ID, and so on, where an unsigned integer value is needed.

The algorithm is as follows. The first byte has the high-bit set aside as an indicator of whether an additional byte needs to be read to extract the final unsigned integer value:

• bit 7 = 0: don't read any more bytes for this integer value
• bit 7 = 1: read next byte and evaluate
and so on (if bit 7 = 1) for the second and third bytes if necessary. Finally, if bit 7 = 1 for the first, second, and third bytes is set, a final fouth byte is read in which all 8 bits are included in determining the integer value. (Hence, the total number of possible bits for a value is 32 - 3 = 29, the three bits not used representing the "read next byte" flags.)

There are then four possible type of byte sequences possible:

• 1 byte; [0xxxxxxx] = values of 0-127
• 2 bytes; [1xxxxxxx][0xxxxxxx] = values of 128-16383
• 3 bytes; [1xxxxxxx][1xxxxxxx][0xxxxxxx] = values of 16384-2097151
• 4 bytes; [1xxxxxxx][1xxxxxxx][1xxxxxxx][xxxxxxxx] = values of 2097152-536870911
The value bits (7, 14, 21, or 29 bits in total, represented by an "x" above) are to be interpreted, either in little or big endian order (depending on the synchronization/endian byte value), as the final unsigned integer value.

Examples:
7f_81_7f_....: First byte is 0x7f = , with bit 7 = 0, so the final value is 0x7f = 127 (don't read any more bytes).
83_7a_00_....: First byte is 0x83 = , with bit 7 = 1, therefore examine the next byte. Second byte is 0x7a = , with bit 7 = 0 (so don't read a third byte). The value interpretation depends on whether this is from a big-endian or little-endian BINEX record:

• If part of a big-endian record, the masked and shifted bit pattern for the integer is  = 0x01fa = 506.
• If part of a little-endian record, the masked, shifted, and byte-swapped bit pattern for the integer is  = 0x3d03 = 15619

The C/C++ functions read_ubnxi() and ubnxi_to_uint4() are provided as prototype code for read a file or stream and interpreting a ubnxi as a uint4. The C/C++ funtion uint4_to_ubnxi() is provided as prototype code for converting a uint4 to a ubnxi and storing the result at a uint1 pointer.

Modified signed GFZ 1, 2, 4, or 8 byte integer:
The original GFZ algorithm for storing a signed integer in 1, 2, 4 or 8 bytes is:

• leading byte = [xxxxxx00] is for 0 to +63, absolute value in 6 most significant bits (1 byte in total needed)
• leading byte = [xxxxxx10] is for 0 to -63, absolute value in 6 most significant bits (1 byte in total needed)
• leading byte = [xxxxx001] is for 0 to +8191, absolute value in 5 most significant bits of this byte plus 8 bits of next byte (2 bytes in total needed)
• leading byte = [xxxxx101] is for 0 to -8191, absolute value in 5 most significant bits of this byte plus 8 bits of next byte (2 bytes in total needed)
• leading byte = [xxxx0011] is for 0 to +268435455, absolute value in 4 most significant bits of this byte plus 24 bits of next three bytes (4 bytes in total needed)
• leading byte = [xxxx1011] is for 0 to -268435455, absolute value in 4 most significant bits of this byte plus 24 bits of next three bytes (4 bytes in total needed)
• leading byte = [xxxx0111] is for 0 to +(2^60 - 1), absolute value in 4 most significant bits of this byte plus 56 bits of next seven bytes (8 bytes in total needed)
• leading byte = [xxxx1111] is for 0 to -(2^60 - 1), absolute value in 4 most significant bits of this byte plus 56 bits of next seven bytes (8 bytes in total needed)

For BINEX, this algorithm is modified to get a bit more compression, to allow for special values, and to allow for little- and big-endian records. In a little-endian record:

• leading byte = [xxxxxx00] is for 0 to +63, absolute value in 6 most significant bits (1 byte in total needed)
• leading byte = [xxxxxx10] is for -1 to -63, absolute value in 6 most significant bits (1 byte in total needed)
• leading byte =  = 0x02 (value of "-0" in the original GFZ algorithm) is reserved to mean "no valid data"
• leading byte = [xxxxx001] is for +64 to +8253, absolute value - 62 in 5 most significant bits of this byte plus 8 bits of next byte (2 bytes in total needed)
• leading byte = [xxxxx101] is for -64 to -8253, absolute value - 62 in 5 most significant bits of this byte plus 8 bits of next byte (2 bytes in total needed)
• leading bytes of  = 0x0d,  = 0x05,  = 0x01,  = 0x09 (values of "-1", "-0", "+0", and "+1" in the original GFZ algorithm) are reserved
• leading byte = [xxxx0011] is for +8254 to +268443708, absolute value - 8253 in 4 most significant bits of this byte plus 24 bits of next three bytes (4 bytes in total needed)
• leading byte = [xxxx1011] is for -8254 to -268443708, absolute value - 8253 in 4 most significant bits of this byte plus 24 bits of next three bytes (4 bytes in total needed)
• leading byte = [xxxx0111] is for +268443709 to +(2^60 - 1 + 268443708), absolute value - 268443708 in 4 most significant bits of this byte plus 56 bits of next seven bytes (8 bytes in total needed)
• leading byte = [xxxx1111] is for -268443709 to -(2^60 - 1 + 268443708), absolute value - 268443708 in 4 most significant bits of this byte plus 56 bits of next seven bytes (8 bytes in total needed)

In a big-endian record, the two, three, or four "indicator" bits in the leading record occur as most significant bits (rather than least significant bits) and in reverse order from the little-endian record; the six, five, or four "value" bits in the leading record occur as least significant bits, but only shifted in the byte. Thus, the reserved value of  = 0x40 means "no valid data":

• leading byte = [00xxxxxx] is for 0 to +63, absolute value in 6 least significant bits (1 byte in total needed)
• leading byte = [01xxxxxx] is for -1 to -63, absolute value in 6 least significant bits (1 byte in total needed)
• leading byte =  = 0x40 (value of "-0" in the original GFZ algorithm) is reserved to mean "no valid data"
• leading byte = [100xxxxx] is for +64 to +8253, absolute value - 62 in 5 least significant bits of this byte plus 8 bits of next byte (2 bytes in total needed)
• leading byte = [101xxxxx] is for -64 to -8253, absolute value - 62 in 5 least significant bits of this byte plus 8 bits of next byte (2 bytes in total needed)
• leading bytes of  = 0xb0,  = 0xa0,  = 0x80,  = 0x90 (values of "-1", "-0", "+0", and "+1" in the original GFZ algorithm) are reserved
• leading byte = [1100xxxx] is for +8254 to +268443708, absolute value - 8253 in 4 least significant bits of this byte plus 24 bits of next three bytes (4 bytes in total needed)
• leading byte = [1101xxxx] is for -8254 to -268443708, absolute value - 8253 in 4 least significant bits of this byte plus 24 bits of next three bytes (4 bytes in total needed)
• leading byte = [1110xxxx] is for +268443709 to +(2^60 - 1 + 268443708), absolute value - 268443708 in 4 least significant bits of this byte plus 56 bits of next seven bytes (8 bytes in total needed)
• leading byte = [1111xxxx] is for -268443709 to -(2^60 - 1 + 268443708), absolute value - 268443708 in 4 least significant bits of this byte plus 56 bits of next seven bytes (8 bytes in total needed)

The C/C++ function binex_extract_mGFZi() is provided as prototype code to read a location in a uint1 array as a modified GFZ integer and return this value as a real8. The C/C++ function binex_append_mGFZi() is provided as prototype code to convert a real8 value into the nearest modified GFZ integer and store this in a uint1 array.

Modified signed GFZ 1-8 byte integer:
(For the original GFZ algorithm for storing a signed integer in 1, 2, 4 or 8 bytes, see this.) For BINEX, this algorithm is modified to allow for more compression using 1-8 bytes as needed, to allow for special values, and to allow for little- and big-endian records. The dynamic range of the 1 and 2 byte cases are sacrificed a bit to allow for more dynamic range of the indicator bits. Below, "s" is the sign bit: "0" means positive, "1" means negative. In a little-endian record, the indicator bits are always lowest 4 least significant bits of the leading byte:

• leading byte = [xxxxs000], 1 byte,  absolute value is < 0x0000000000000010 = 16, "-0" =  = 0x08 is reserved to mean "no valid data"
• leading byte = [xxxxs001], 2 bytes, absolute value is < 0x000000000000100e = 4110, "-1", "-0", "+0", "+1" are reserved
• leading byte = [xxxxs010], 3 bytes, absolute value is < 0x000000000010100d = 1052685, "-0" is reserved
• leading byte = [xxxxs011], 4 bytes, absolute value is < 0x000000001010100c = 269488140, "-0" is reserved
• leading byte = [xxxxs100], 5 bytes, absolute value is < 0x000000101010100b = 68988964875, "-0" is reserved
• leading byte = [xxxxs101], 6 bytes, absolute value is < 0x000010101010100a = 17661175009290, "-0" is reserved
• leading byte = [xxxxs110], 7 bytes, absolute value is < 0x0010101010101009 = 4521260802379785, "-0" is reserved
• leading byte = [xxxxs111], 8 bytes, absolute value is < 0x1010101010101008, "-0" is reserved

In a big-endian record, the "indicator" bits are always the highest 4 most significant bits of the leading byte, with the same bit order as the little-endian record:

• leading byte = [s000xxxx], 1 byte,  absolute value is < 0x0000000000000010 = 16, "-0" =  = 0x80 is reserved to mean "no valid data"
• leading byte = [s001xxxx], 2 bytes, absolute value is < 0x000000000000100e = 4110, "-1", "-0", "+0", "+1" are reserved
• leading byte = [s010xxxx], 3 bytes, absolute value is < 0x000000000010100d = 1052685, "-0" is reserved
• leading byte = [s011xxxx], 4 bytes, absolute value is < 0x000000001010100c = 269488140, "-0" is reserved
• leading byte = [s100xxxx], 5 bytes, absolute value is < 0x000000101010100b = 68988964875, "-0" is reserved
• leading byte = [s101xxxx], 6 bytes, absolute value is < 0x000010101010100a = 17661175009290, "-0" is reserved
• leading byte = [s110xxxx], 7 bytes, absolute value is < 0x0010101010101009 = 4521260802379785, "-0" is reserved
• leading byte = [s111xxxx], 8 bytes, absolute value is < 0x1010101010101008, "-0" is reserved

The C/C++ function binex_extract_mGFZI() is provided as prototype code to read a location in a uint1 array as a modified GFZ integer and return this value as a real8. The C/C++ function binex_append_mGFZI() is provided as prototype code to convert a real8 value into the nearest modified GFZ integer and store this in a uint1 array.

1-byte satellite ID:
A single byte can be used to specify most foreseeable combinations for satellite system and corresponding satellite number, as long as the number of discreet satellite systems is eight or less, and the corresponding satellite number can be mapped from 0 to 31. In this byte, bits 5 and 6 (and in the future bit 7) indicate the satellite system; the lowest 5 bits indicate the satellite number minus 1. Using C/C++ syntax:

• satellite system = byte >> 5 & 0x07 (for up to and including 8 systems)
• satellite number = (byte & 0x1f) + 0x01
Currently, for the satellite system:
For the satellite number:
• satellite number = PRN # (1-32) if SV in NAVSTAR GPS
• satellite number = slot # (1-24) if SV in GLONASS
• satellite number = PRN # - 119 if SV in WAAS/EGNOS/MSAS (i.e. PRN # - 120 == byte & 0x1f)
• satellite number = PRN # (1-32) if SV in Galileo (i.e. cannot be used for PRN # > 32)
• (note: satellite number for Beidou system is awaiting definition pending more information)
• satellite number = PRN # - 192 if SV in QZSS (i.e. PRN # - 193 == byte & 0x1f)

BINEX to RINEX considerations:
For most cases, conversion of BINEX to RINEX poses no special problems. There is one situation, however, that deserves a discussion. This is the conversion of integer values representing rounded floating-point numbers (e.g. see mGFZi or mGFZI) back to floating-point numbers.

An example is the floating-point to integer compression used in record/subrecord 0x7f-00. In 0x7f-00, all floating-point pseudorange values are rounded and stored to the nearest 0.001 meter, and all floating-point phase values are rounded and stored to the nearest 0.0001 cycle, both using (in this case) mGFZI integers, after pre-multiplying the pseudorange values by 1000 and the phase values by 10000. The absolute value of the difference between the original floating point value and the reconstructed value is less than 0.001 meter for all pseudorange values and less than 0.0001 cycle for phase values.

However, RINEX format only allows the pseudorange and phase values to be recorded to the nearest 0.001 meter and 0.001 cycle, respectively. In the case of 0x7f-00 conversion to RINEX, pseudorange values are recorded exactly as one would expect, since both have a resolution of 0.001 meters. For phase values, however, there is an occasional error introduced into the RINEX phase values of +-0.001 cycle. This is not due to any error in the corresponding BINEX phase value; remember, these are valid to the nearest 0.0001 cycle. This is due to the fact that we are taking a value rounded to the nearest 0.0001 (BINEX) and re-rounding the value to the nearest 0.001 (RINEX).

To see how this occurs, consider the floating-point values X.261 - X.262:

``` floating-point    nearest 0.0001   nearest 0.001 round nearest 0.0001
(native format)   (BINEX 0x7f-00)      (RINEX)      to nearest 0.001

X.2613000...         X.2613             X.261            X.261
X.2613499...         X.2613             X.261            X.261
X.2613500...         X.2614             X.261            X.261
X.2614000...         X.2614             X.261            X.261
X.2614499...         X.2614             X.261            X.261
X.2614500...         X.2615             X.261            X.262
X.2615000...         X.2615             X.262            X.262
X.2615499...         X.2615             X.262            X.262
X.2615500...         X.2616             X.262            X.262
X.2616000...         X.2616             X.262            X.262
```
So, for all values [X.26145, X.2615), i.e. from X.26145 up to but not including X.2615, this "double rounding" (i.e. floating-point value -> BINEX 0x7f-00 -> RINEX) would result in an error of 0.001 in the RINEX from what would have been obtained if we had started with the original floating-point value and done "single rounding" (i.e. floating-point value -> RINEX).

If the floating-point values are randomly distributed, this rounding error of 0.001 in RINEX would occur in about 1/20 of the phase values, where the BINEX phase value has been stored to the nearest 0.0001 cycle. In test files, the occurrance has been found to be less than 1/30 of the phase values.

The important point here is that this is not a property of BINEX or RINEX, but occurs when we round a floating-point number to some high precision, and then round that number again to some lower precision.

The best strategy, for example, to take advantage of the higher precision of BINEX 0x7f-00 phase values over RINEX, and to avoid these types of double-rounding errors when converting some BINEX data to RINEX, would be to by-pass RINEX altogether by replacing any existing RINEX reader with a direct BINEX reader.