解压 IEEE 754 浮点数

Unpack IEEE 754 Floating Point Number

我正在使用 pymodbus module. The two registers make up a 32 bit IEEE 754 编码的浮点数从 TCP 客户端读取两个 16 位寄存器。目前我有下面代码中显示的寄存器的 32 位二进制值。

start_address = 0x1112
reg_count = 2
client = ModbusTcpClient(<IP_ADDRESS>)
response = client.read_input_registers(start_address,reg_count)
reg_1 = response.getRegister(0)<<(16 - (response.getRegister(0).bit_length())) #Get in 16 bit format
reg_2 = response.getRegister(1)<<(16 - (response.getRegister(1).bit_length())) #Get in 16 bit format 
volts = (reg_1 << 16) | reg_2 #Get the 32 bit format

上面的方法可以很好地获取编码值,问题是解码它。我打算在这个 video but I came across the 'f' format in the struct 模块中为 IEEE 754 编码编写类似的代码。我尝试使用结构模块中的 unpack 方法将上面代码中 volts 中存储的 32 位浮点数解码,但 运行 出现以下错误。

val = struct.unpack('f',volts)
>>> TypeError: a bytes-like object is required, not 'int'

好的尝试将其转换为 32 位二进制字符串。

temp = bin(volts)
val = struct.unpack('f',temp)
>>> TypeError: a bytes-like object is required, not 'str'

试图将其转换为类似字节的对象,如此 并以不同的方式格式化。

val = struct.unpack('f',bytes(volts))
>>> TypeError: string argument without an encoding

temp = "{0:b}".format(volts)
val = struct.unpack('f',temp)
>>> ValueError: Unknown format code 'b' for object of type 'str'

val = struct.unpack('f',volts.encode())
>>> struct.error: unpack requires a buffer of 4 bytes

我在哪里添加这个缓冲区,文档中的什么地方说我需要这个缓冲区和解包方法?它确实在文档中说

The string must contain exactly the amount of data required by the format (len(string) must equal calcsize(fmt)).

calcsize(fmt) 函数 returns 一个以字节为单位的值,但是 len(string) returns 一个字符串长度的值,不是吗?

欢迎提出任何建议。

编辑

下面有一个解码解决方案,但是与问题中的原始解决方案相比,下面显示了从两个 16 位寄存器值获取 32 位寄存器值的更好解决方案。

start_address = 0x1112
reg_count = 2
client = ModbusTcpClient(<IP_ADDRESS>)
response = client.read_input_registers(start_address,reg_count)
reg_1 = response.getRegister(0)
reg_2 = response.getRegister(1)
# Shift reg 1 by 16 bits
reg_1s = reg_1 << 16
# OR with the reg_2
total = reg_1s | reg_2

我找到了使用 BinaryPayloadDecoder.fromRegisters() from the pymodbus 模块而不是结构模块来解决问题的方法。请注意,此解决方案特定于我正在使用的 modbus 智能仪表设备,因为寄存器的字节顺序和字顺序可能会在其他设备中发生变化。它可能仍然可以在其他设备上解码寄存器,但我建议首先阅读设备的文档以确保。我在下面的代码中留下了评论,但是当我参考第 24 页时,这仅适用于我的设备。

from pymodbus.client.sync import ModbusTcpClient
from pymodbus.constants import Endian
from pymodbus.payload import BinaryPayloadDecoder

start_address = 0x1112
reg_count = 2
client = ModbusTcpClient(<IP_ADDRESS>)
response = client.read_input_registers(start_address,reg_count)
# The response will contain two registers making a 32 bit floating point number
# Use the BinaryPayloadDecoder.fromRegisters() function to decode
# The coding scheme for a 32 bit float is IEEE 754 https://en.wikipedia.org/wiki/IEEE_754
# The MS Bytes are stored in the first address and the LS bytes are stored in the second address,
# this corresponds to a big endian byte order (Second parameter in function)
# The documentation for the Modbus registers for the smart meter on page 24 says that
# the low word is the first priority, this correspond to a little endian word order (Third parameter in function)
decoder = BinaryPayloadDecoder.fromRegisters(response.registers, Endian.Big, wordorder=Endian.Little)
final_val = (decoder.decode_32bit_float())
client.close()

编辑 归功于 juanpa-arrivillaga and chepner 这个问题也可以通过 byteorder='little' 使用 struct 模块来解决。如果字节顺序为 little 或字节顺序为 big,则可以使用下面代码中的两个函数,具体取决于实现。

import struct
from pymodbus.client.sync import ModbusTcpClient

def big_endian(response):
    reg_1 = response.getRegister(0)
    reg_2 = response.getRegister(1)
    # Shift reg 1 by 16 bits
    reg_1s = reg_1 << 16
    # OR with the reg_2
    total = reg_1s | reg_2
    return total

def little_endian(response):
    reg_1 = response.getRegister(0)
    reg_2 = response.getRegister(1)
    # Shift reg 2 by 16 bits
    reg_2s = reg_2 << 16
    # OR with the reg_1
    total = reg_2s | reg_1
    return(total)

start_address = 0x1112
reg_count = 2
client = ModbusTcpClient(<IP_ADDRESS>)
response = client.read_input_registers(start_address,reg_count)

# Little 
little = little_endian(response)
lit_byte = little.to_bytes(4,byteorder='little')
print(struct.unpack('f',lit_byte))

# Big 
big = big_endian(response)
big_byte = big.to_bytes(4,byteorder='big')
print(struct.unpack('f',big_byte))