Skip to content

Commit ac017e5

Browse files
yashhzdamilcarlucas
authored andcommitted
fix(export): prevent silent truncation of parameter values smaller than 1e-6
The `.6f` format specifier in `_format_params()` silently truncates any value below 1e-6 to "0", causing data loss on save→load round-trips. Changed to `.10f` which preserves all IEEE 754 single-precision float significant digits while remaining in decimal notation. Fixes #1400 Signed-off-by: Yash Goel <yashgoel249@gmail.com>
1 parent d0cd906 commit ac017e5

2 files changed

Lines changed: 37 additions & 7 deletions

File tree

ardupilot_methodic_configurator/data_model_par_dict.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -255,19 +255,19 @@ def _format_params(self, file_format: str = "missionplanner") -> list[str]:
255255
sorted_dict = ParDict(sorted_items)
256256
formatted_params = [
257257
(
258-
f"{key},{format(parameter.value, '.6f').rstrip('0').rstrip('.')} # {parameter.comment}"
258+
f"{key},{format(parameter.value, '.10f').rstrip('0').rstrip('.')} # {parameter.comment}"
259259
if isinstance(parameter, Par) and parameter.comment
260-
else f"{key},{format(parameter.value if isinstance(parameter, Par) else parameter, '.6f').rstrip('0').rstrip('.')}" # noqa: E501 # pylint: disable=line-too-long
260+
else f"{key},{format(parameter.value if isinstance(parameter, Par) else parameter, '.10f').rstrip('0').rstrip('.')}" # noqa: E501 # pylint: disable=line-too-long
261261
)
262262
for key, parameter in sorted_dict.items()
263263
]
264264
elif file_format == "mavproxy":
265265
sorted_dict = ParDict(dict(sorted(self.items())))
266266
formatted_params = [
267267
(
268-
f"{key:<16} {parameter.value:<8.6f} # {parameter.comment}"
268+
f"{key:<16} {parameter.value:<8.10f} # {parameter.comment}"
269269
if isinstance(parameter, Par) and parameter.comment
270-
else f"{key:<16} {parameter.value if isinstance(parameter, Par) else parameter:<8.6f}"
270+
else f"{key:<16} {parameter.value if isinstance(parameter, Par) else parameter:<8.10f}"
271271
)
272272
for key, parameter in sorted_dict.items()
273273
]

tests/test_data_model_par_dict.py

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -563,8 +563,8 @@ def test_user_can_export_parameters_to_mavproxy_format(self, parameter_dict) ->
563563
content = f.read()
564564

565565
# MAVProxy format uses fixed-width columns
566-
assert "ACRO_YAW_P 4.500000 # Yaw P gain" in content
567-
assert "GPS_TYPE 1.000000" in content
566+
assert "ACRO_YAW_P 4.5000000000 # Yaw P gain" in content
567+
assert "GPS_TYPE 1.0000000000" in content
568568
finally:
569569
os.unlink(output_file)
570570

@@ -943,7 +943,7 @@ def test_user_can_format_parameters_for_mavproxy(self, parameter_dict) -> None:
943943

944944
# Assert: Correct MAVProxy format (space-separated, fixed width)
945945
yaw_line = next((line for line in formatted if "ACRO_YAW_P" in line), "")
946-
assert yaw_line == "ACRO_YAW_P 4.500000 # Yaw P gain"
946+
assert yaw_line == "ACRO_YAW_P 4.5000000000 # Yaw P gain"
947947

948948
def test_user_receives_error_for_unsupported_format(self, parameter_dict) -> None:
949949
"""
@@ -959,6 +959,36 @@ def test_user_receives_error_for_unsupported_format(self, parameter_dict) -> Non
959959
with pytest.raises(SystemExit, match="Unsupported file format"):
960960
parameter_dict._format_params("invalid_format") # pylint: disable=protected-access
961961

962+
def test_user_preserves_small_parameter_values_on_export(self) -> None:
963+
"""
964+
User preserves parameter values smaller than 1e-6 during export.
965+
966+
GIVEN: A user has parameters with very small but nonzero values
967+
WHEN: They export and reimport using MissionPlanner or MAVProxy format
968+
THEN: The values should survive the round-trip without silent data loss
969+
"""
970+
# Arrange: Parameters with values that would be truncated by .6f formatting
971+
small_value_params = ParDict(
972+
{
973+
"TINY_VAL": Par(0.0000001, "Very small value"),
974+
"MICRO_VAL": Par(0.0000005, "Another small value"),
975+
"BOUNDARY_VAL": Par(0.000001, "Boundary value"),
976+
"NORMAL_VAL": Par(1.5, "Normal value"),
977+
}
978+
)
979+
980+
for file_format in ("missionplanner", "mavproxy"):
981+
formatted = small_value_params._format_params(file_format) # pylint: disable=protected-access
982+
983+
# Extract the numeric value strings from the formatted output
984+
for line in formatted:
985+
if "TINY_VAL" in line:
986+
assert "0.0000001" in line, f"Value 1e-7 lost in {file_format} format: {line}"
987+
if "MICRO_VAL" in line:
988+
assert "0.0000005" in line, f"Value 5e-7 lost in {file_format} format: {line}"
989+
if "BOUNDARY_VAL" in line:
990+
assert "0.000001" in line, f"Value 1e-6 lost in {file_format} format: {line}"
991+
962992
def test_user_can_annotate_parameters_with_comments(self, parameter_dict) -> None:
963993
"""
964994
User can add comments to parameters using a lookup table.

0 commit comments

Comments
 (0)