You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
172 lines
5.2 KiB
172 lines
5.2 KiB
#!/usr/bin/env python3
|
|
"""
|
|
Decoder Module - vzug-e-hinge
|
|
==============================
|
|
Decodes raw UART and I2C data into telemetry fields.
|
|
|
|
Current Implementation: Simple pass-through
|
|
TODO: Implement actual byte unpacking later
|
|
|
|
Author: Kynsight
|
|
Version: 1.0.0 (pass-through)
|
|
Date: 2025-11-09
|
|
"""
|
|
|
|
from typing import Dict, Any
|
|
|
|
|
|
def decode_uart_packet(packet_bytes: bytes) -> Dict[str, Any]:
|
|
"""
|
|
Decode UART packet bytes into telemetry fields.
|
|
|
|
Current Implementation: Pass-through (returns raw data)
|
|
TODO: Implement actual decoding based on packet format
|
|
|
|
Expected packet format: EF FE [14 bytes] EE (17 bytes total)
|
|
|
|
Future implementation should extract:
|
|
- motor_current (2 bytes)
|
|
- encoder_value (2 bytes)
|
|
- relative_encoder_value (2 bytes)
|
|
- v24_pec_diff (2 bytes)
|
|
- pwm (1 byte)
|
|
|
|
Args:
|
|
packet_bytes: Raw packet bytes (including start/end markers)
|
|
|
|
Returns:
|
|
Dictionary with decoded fields (currently just raw data)
|
|
|
|
Example:
|
|
packet_bytes = b'\\xEF\\xFE...[14 bytes]...\\xEE'
|
|
decoded = decode_uart_packet(packet_bytes)
|
|
# Currently returns: {'raw_hex': 'ef fe ... ee', 'raw_bytes': b'...'}
|
|
# Future: {'motor_current': 123, 'encoder_value': 456, ...}
|
|
"""
|
|
return {
|
|
'raw_hex': packet_bytes.hex(' '),
|
|
'raw_bytes': packet_bytes,
|
|
'packet_length': len(packet_bytes)
|
|
}
|
|
|
|
|
|
def decode_i2c_sample(i2c_bytes: bytes) -> Dict[str, Any]:
|
|
"""
|
|
Decode I2C sample bytes into angle telemetry.
|
|
|
|
Current Implementation: Pass-through (returns raw data)
|
|
TODO: Implement actual decoding based on I2C format
|
|
|
|
Expected I2C format: 2 bytes (14-bit angle value)
|
|
|
|
Future implementation should extract:
|
|
- i2c_raw14 (14-bit raw value)
|
|
- i2c_angle_deg (converted to degrees)
|
|
- i2c_zero_raw14 (zero position)
|
|
- i2c_delta_raw14 (delta from zero)
|
|
|
|
Args:
|
|
i2c_bytes: Raw I2C bytes (typically 2 bytes for angle)
|
|
|
|
Returns:
|
|
Dictionary with decoded fields (currently just raw data)
|
|
|
|
Example:
|
|
i2c_bytes = b'\\x3F\\xFF' # 14-bit angle
|
|
decoded = decode_i2c_sample(i2c_bytes)
|
|
# Currently returns: {'raw_hex': '3f ff', 'raw_bytes': b'...'}
|
|
# Future: {'i2c_raw14': 16383, 'i2c_angle_deg': 359.98, ...}
|
|
"""
|
|
return {
|
|
'raw_hex': i2c_bytes.hex(' '),
|
|
'raw_bytes': i2c_bytes,
|
|
'sample_length': len(i2c_bytes)
|
|
}
|
|
|
|
|
|
# =============================================================================
|
|
# Future Implementation Template
|
|
# =============================================================================
|
|
|
|
# def decode_uart_packet_full(packet_bytes: bytes) -> Dict[str, Any]:
|
|
# """
|
|
# Full UART packet decoder (to be implemented).
|
|
#
|
|
# Packet format: EF FE [14 bytes] EE
|
|
#
|
|
# Byte layout:
|
|
# [0-1]: Start marker (EF FE)
|
|
# [2-3]: motor_current (signed 16-bit, little-endian)
|
|
# [4-5]: encoder_value (unsigned 16-bit, little-endian)
|
|
# [6-7]: relative_encoder_value (signed 16-bit, little-endian)
|
|
# [8-9]: v24_pec_diff (signed 16-bit, little-endian)
|
|
# [10]: pwm (unsigned 8-bit)
|
|
# [11-15]: Reserved
|
|
# [16]: End marker (EE)
|
|
# """
|
|
# # Verify packet length
|
|
# if len(packet_bytes) != 17:
|
|
# raise ValueError(f"Invalid packet length: {len(packet_bytes)}, expected 17")
|
|
#
|
|
# # Verify markers
|
|
# if packet_bytes[0:2] != b'\xEF\xFE':
|
|
# raise ValueError("Invalid start marker")
|
|
# if packet_bytes[16:17] != b'\xEE':
|
|
# raise ValueError("Invalid end marker")
|
|
#
|
|
# # Extract data bytes (skip markers)
|
|
# data = packet_bytes[2:16]
|
|
#
|
|
# return {
|
|
# 'motor_current': int.from_bytes(data[0:2], 'little', signed=True),
|
|
# 'encoder_value': int.from_bytes(data[2:4], 'little', signed=False),
|
|
# 'relative_encoder_value': int.from_bytes(data[4:6], 'little', signed=True),
|
|
# 'v24_pec_diff': int.from_bytes(data[6:8], 'little', signed=True),
|
|
# 'pwm': data[8]
|
|
# }
|
|
|
|
|
|
# def decode_i2c_sample_full(i2c_bytes: bytes) -> Dict[str, Any]:
|
|
# """
|
|
# Full I2C sample decoder (to be implemented).
|
|
#
|
|
# I2C format: 2 bytes (14-bit angle value)
|
|
#
|
|
# Byte layout:
|
|
# [0]: High byte (bits 13-6)
|
|
# [1]: Low byte (bits 5-0 in upper 6 bits)
|
|
# """
|
|
# if len(i2c_bytes) != 2:
|
|
# raise ValueError(f"Invalid I2C sample length: {len(i2c_bytes)}, expected 2")
|
|
#
|
|
# # Extract 14-bit value
|
|
# raw14 = ((i2c_bytes[0] << 6) | (i2c_bytes[1] >> 2)) & 0x3FFF
|
|
#
|
|
# # Convert to degrees (14-bit = 0-360°)
|
|
# angle_deg = (raw14 / 16384.0) * 360.0
|
|
#
|
|
# return {
|
|
# 'i2c_raw14': raw14,
|
|
# 'i2c_angle_deg': angle_deg
|
|
# }
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# Simple test
|
|
print("Decoder Module - Pass-Through Mode")
|
|
print("=" * 60)
|
|
|
|
# Test UART packet decoding
|
|
test_uart = b'\xEF\xFE' + b'\x01' * 14 + b'\xEE'
|
|
decoded_uart = decode_uart_packet(test_uart)
|
|
print(f"UART packet decoded: {decoded_uart}")
|
|
|
|
# Test I2C sample decoding
|
|
test_i2c = b'\x3F\xFF'
|
|
decoded_i2c = decode_i2c_sample(test_i2c)
|
|
print(f"I2C sample decoded: {decoded_i2c}")
|
|
|
|
print()
|
|
print("✓ Decoder ready (pass-through mode)")
|
|
print("TODO: Implement actual decoding later")
|