I'm trying to convert data from a Native Modbus Register (Signed) to a 32-bit float with a Little-endian byte swap in Python, but I can't seem to get the transformation right. I've tried a few different approaches, but none have worked so far, and I’m unsure if the issue lies in my method or the data itself.
In my reference, the initial data type is "Native Modbus Register - Signed." The transformation I'm trying to achieve is from this type to "32-bit float - Little-endian byte swap." Here's a sample of the input and expected output values:
SlewEncoder_input_and_output = [
(2100, -1533, -601.2821),
(2100, -1533, -3395.887),
(1592, -1502, 19834.04),
(-305, 1807, -106.4646),
(-463, -1566, -1045.991)
]
I also have a different reference containing some sample data that seems to be in the 32-bit float - Little-endian byte swap format (the result). However, I'm not entirely sure about the initial data type; it appears to be similar to the previous one (Native Modbus Register - Signed) but merged into a single signed register value:
SlewEncoder_input_and_output = [
(1505910957, -0.963)
]
Here's the last thing I've tried (without success, the output is just a really huge value):
upper = 2100
lower = -1533
int_value = ((upper & 0xFFFF) << 16) | (lower & 0xFFFF)
# Convert the integer to bytes
bytes_value = int_value.to_bytes(4, byteorder='little', signed=True)
# Interpret the bytes as a float (little-endian)
import struct
reading_value = struct.unpack('<f', bytes_value)[0]
print(reading_value) # 147925204992.0
Any help is very welcome, thanks.
Your test data is wrong. I cheated and converted -601.2821 to two 16-bit values and found your data is missing the lowest digit (21006 and -15338). I then just played with your code until it worked (I had to swap upper and and lower) and once you're working with bits instead of ints set signed=False
so Python doesn't worry about the sign bit.
Here's the working code:
lower = 21006
upper = -15338
int_value = ((upper & 0xFFFF) << 16) | (lower & 0xFFFF)
# Convert the integer to bytes
bytes_value = int_value.to_bytes(4, byteorder='little', signed=False)
# Interpret the bytes as a float (little-endian)
import struct
reading_value = struct.unpack('<f', bytes_value)[0]
print(reading_value) # -601.2821044921875
EDIT: Running Konstantin Makarov's code against each of your floats, we can see that your original data was shifted by 1 row, in addition to the previously mentioned missing digits. Here's the corrected data (assuming the floats were correct):
SlewEncoder_input_and_output = [
(21006, -15338, -601.2821),
(15921, -15020, -3395.887),
(-3052, 18074, 19834.04),
(-4640, -15660, -106.4646),
(-16458, -15230, -1045.991)
]