Hi @bdkelly welcome!
You are right, there is a problem here, we missed this limitation of the CCSDS format and the workaround is not straightforward.
Internally, the output of double values is performed by the Generator
passed to the writeMessage
method (or the underlying writeHeader
, writeSegment
, writeFooter
and similar methods). Orekit provides two implementations of Generator
: KvnGenerator
and XmlGenerator
, both extending a general purpose AbstractGenerator
class. It is AbstractGenerator
that converts the double values in SI to strings taking the CCSDS units into account. It does it be delegating the conversion to strings to the Ryū algorithm. This design choice was deliberate as it ensures proper round-trip operations while still ensuring shortest string representation, i.e. when someone parses the value that has been written, one get the exact same double number. This means that despite we use a character-based intermediate message, we don’t lose any accuracy, which is something I have experienced a lot and that induces a lot of problems when doing accurate computation.
Ensuring round-trip safety means that for some values, a lot of digits may be required, and sometimes the shortest representation is not that short. In fact, with 64 bits numbers following IEEE754 binary64 representation (which is what Java primitive double do), it is known that round-trip safety is always ensured if one uses 17 decimal digits (for the record, this is called the p10 value and is equal to 9 digits for the binary32 representation, 17 digits for the binary64 representation and 36 digits for the binary128 representation, as explained in section 3.1.5 of Muller et al. Handbook of Floating-Point Arithmetic), so we know the output of Ryū algorithm will always have 17 digits at most… which is one digit more than allowed by CCSDS! This is a pity, and I would say it is an overlook of the CCSDS standard which probably did not know about p10 being 17 for double numbers (it is not widely known, many people think double numbers are 15 or 16 digits accurate and don’t know the subtlety of round-trip safety, also known as error-free write-read cycle in Muller’s book).
So, how can we handle that?
I first will notify the problem to CCSDS, telling them that the limitation to 16 digits hinders round-trip safety but I think they will not change the 16 into 17 as it is most probably used everywhere in many CCSDS recommendations.
You could open an issue in our issue tracker referring to this forum thread, so we implement a global fix.
You may try to set up a workaround by setting up your own implementation of Generator
, perhaps by extending either KvnGenerator
or XmlGenerator
and overriding the doubleToString
method they originally inherit from AbstractGenerator
. The current implementation in AbstractGenerator
reads:
public String doubleToString(final double value) {
return Double.isNaN(value) ? null : AccurateFormatter.format(value);
}
Maybe you can try something like:
public String doubleToString(final double value) {
if (Double.isNaN(value)) {
return null;
} else {
return String.format(Locale.US,
(value < 1.0e-3 || value > 1.0e7) ? "%.16e" : "%.16f",
value);
}
}
Warning: I did not try this suggestion myself, it may be wrong, I just wrote this code directly in the forum without any test.