-
Notifications
You must be signed in to change notification settings - Fork 0
State Estimations & Telemetry Updates #75
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
a6e2651
0aead6f
22a749f
eccba73
262c2a0
bd9fbcd
9df8b7e
a3dc3df
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,6 +12,7 @@ | |
| import enum | ||
| import struct | ||
| import math | ||
| import time | ||
| from dataclasses import dataclass, asdict | ||
| from typing import ClassVar | ||
| from SDECv2.SerialController import SerialObj | ||
|
|
@@ -21,11 +22,11 @@ | |
| from typing import Any | ||
|
|
||
| UID_SIZE = 12 | ||
| LORA_INTERNAL_HEADER_SIZE = 20 | ||
| LORA_PAYLOAD_SIZE = 76 | ||
| LORA_INTERNAL_HEADER_SIZE = 8 | ||
| LORA_PAYLOAD_SIZE = 40 | ||
| LORA_MESSAGE_SIZE = LORA_INTERNAL_HEADER_SIZE + LORA_PAYLOAD_SIZE | ||
| FLIGHT_ID_SIZE = 16 | ||
| DASHBOARD_DUMP_TYPE_SIZE = 72 | ||
| DASHBOARD_DUMP_TYPE_SIZE = 36 | ||
|
|
||
| class LoRaMessageTypes(enum.IntEnum): | ||
| """LORA_MESSAGE_TYPES (uint32 on wire).""" | ||
|
|
@@ -38,11 +39,10 @@ class LoRaMessageTypes(enum.IntEnum): | |
|
|
||
| @dataclass(frozen=True) | ||
| class LoRaInternalHeaderType: | ||
| """LORA_INTERNAL_HEADER_TYPE — uid, mid, timestamp (packed, 20 bytes).""" | ||
| """LORA_INTERNAL_HEADER_TYPE — mid, timestamp (packed, 8 bytes).""" | ||
|
|
||
| STRUCT: ClassVar[str] = f"<{UID_SIZE}sII" | ||
| STRUCT: ClassVar[str] = f"<II" | ||
|
|
||
| uid: bytes | ||
| mid: int | ||
| timestamp: int | ||
|
|
||
|
|
@@ -60,38 +60,29 @@ def parse(cls, data: bytes) -> LoRaInternalHeaderType: | |
| f"LoRaInternalHeaderType.parse expects {LORA_INTERNAL_HEADER_SIZE} bytes, " | ||
| f"got {len(data)}" | ||
| ) | ||
| uid, mid_u32, ts = struct.unpack(cls.STRUCT, data[:LORA_INTERNAL_HEADER_SIZE]) | ||
| return cls(bytes(uid), mid_u32, ts) | ||
| mid_u32, ts = struct.unpack(cls.STRUCT, data[:LORA_INTERNAL_HEADER_SIZE]) | ||
| return cls(mid_u32, ts) | ||
|
|
||
|
|
||
| @dataclass(frozen=True) | ||
| class DashboardDumpType: | ||
| """DASHBOARD_DUMP_TYPE — 18 × float32, 72 bytes.""" | ||
|
|
||
| STRUCT: ClassVar[str] = "<18f" | ||
|
|
||
| accXconv: float | ||
| accYconv: float | ||
| accZconv: float | ||
| gyroXconv: float | ||
| gyroYconv: float | ||
| gyroZconv: float | ||
| rollDeg: float | ||
| pitchDeg: float | ||
| yawDeg: float | ||
| rollRate: float | ||
| pitchRate: float | ||
| yawRate: float | ||
| pres: float | ||
| temp: float | ||
| """DASHBOARD_DUMP_TYPE — 9 × float32, 36 bytes.""" | ||
|
|
||
| STRUCT: ClassVar[str] = "<9f" | ||
|
|
||
| quat_w: float | ||
| quat_x: float | ||
| quat_y: float | ||
| quat_z: float | ||
| alt: float | ||
| bvelo: float | ||
| long: float | ||
| lat: float | ||
| acc_z: float | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. CR: if we're looking for the acceleration along the length of the rocket this should be
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I forgor to flip it back, oops. Will resolve on dashboard side as well |
||
| roll_rate: float | ||
|
|
||
| @classmethod | ||
| def parse(cls, data: bytes) -> DashboardDumpType: | ||
| if len(data) < DASHBOARD_DUMP_TYPE_SIZE: | ||
| if len(data) != DASHBOARD_DUMP_TYPE_SIZE: | ||
| raise ParserError( | ||
| f"DashboardDumpType.parse expects {DASHBOARD_DUMP_TYPE_SIZE} bytes, got {len(data)}" | ||
| ) | ||
|
|
@@ -114,8 +105,9 @@ def to_json(self) -> dict[str, Any]: | |
| class LoRaMsgVehicleIdType: | ||
| """LORA_MSG_VEHICLE_ID_TYPE — trailing explicit padding on the wire is discarded.""" | ||
|
|
||
| STRUCT: ClassVar[str] = "<BBI16s" | ||
| STRUCT: ClassVar[str] = f"<{UID_SIZE}sBBI16s" | ||
|
|
||
| uid: bytes | ||
| hw_opcode: int | ||
| fw_opcode: int | ||
| version: int | ||
|
|
@@ -127,9 +119,9 @@ def parse(cls, data: bytes) -> LoRaMsgVehicleIdType: | |
| raise ParserError( | ||
| f"LoRaMsgVehicleIdType.parse expects {LORA_PAYLOAD_SIZE} bytes, got {len(data)}" | ||
| ) | ||
| hw, fw, ver, fid = struct.unpack(cls.STRUCT, data[: 6 + FLIGHT_ID_SIZE]) | ||
| uid, hw, fw, ver, fid = struct.unpack(cls.STRUCT, data[: 18 + FLIGHT_ID_SIZE]) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: magic number
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. boo (willfix) |
||
| flight_id = fid.split(b"\x00", 1)[0].decode("utf-8", errors="replace") | ||
| return cls(hw, fw, ver, flight_id) | ||
| return cls(uid, hw, fw, ver, flight_id) | ||
|
|
||
|
|
||
| @dataclass(frozen=True) | ||
|
|
@@ -152,7 +144,7 @@ def parse(cls, data: bytes) -> LoRaMsgDashboardDumpType: | |
|
|
||
| @dataclass(frozen=True) | ||
| class LoRaMsgTextMessageType: | ||
| """LORA_MSG_TEXT_MESSAGE_TYPE — TEXT_MESSAGE occupies the full 76-byte slot.""" | ||
| """LORA_MSG_TEXT_MESSAGE_TYPE — TEXT_MESSAGE occupies the full 36-byte slot.""" | ||
|
|
||
| msg: str | ||
|
|
||
|
|
@@ -239,6 +231,22 @@ def __init__(self): | |
| "status": "Connecting..." | ||
| } | ||
| self.last_msg_time = None | ||
| self.msg_rx_callback = None | ||
|
|
||
| def __put_to_rx_callback(self, msgType: str, timestamp: float, msg: dict[str, Any]): | ||
| if self.msg_rx_callback is None: | ||
| return | ||
| self.msg_rx_callback(msgType, timestamp, msg) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The removed
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm, it might not. we jsonify the output of this regardless, but good for robustness |
||
|
|
||
| def register_rx_callback(self, msg_rx_callback: callable[str, float, dict[str, Any]]): | ||
| """ | ||
| Register the receive callback for this telemetry object. | ||
| The callback takes the following parameters and returns nothing: | ||
| - messageType (as a string) | ||
| - timestamp (as a float, timebase is seconds) | ||
| - message contents (json/dict) | ||
| """ | ||
| self.msg_rx_callback = msg_rx_callback | ||
|
|
||
| def dashboard_dump(self, serial_connection: SerialObj): | ||
| """ | ||
|
|
@@ -250,12 +258,15 @@ def dashboard_dump(self, serial_connection: SerialObj): | |
| serial_connection.send(b'\x30') | ||
| if( serial_connection.target == None ): | ||
| print("Warning: The serial connection does not have an associated hardware/firmware platform. Report this.") | ||
| return | ||
| elif( serial_connection.target.controller.id == b'\x05'): | ||
| # Flight Computer: Parse dashboard dump | ||
| data = serial_connection.read(DASHBOARD_DUMP_TYPE_SIZE) | ||
| parsed = DashboardDumpType.parse(data) | ||
| with( self.telem_lock ): | ||
| self.last_dashboard_dump = parsed | ||
| self.__put_to_rx_callback(LoRaMessageTypes.DASHBOARD_DATA.name, time.time(), parsed.to_json()) | ||
| return | ||
| elif( serial_connection.target.controller.id == b'\x10'): | ||
| # Ground station: Interpret message & save | ||
| data = serial_connection.read(LORA_MESSAGE_SIZE) | ||
|
|
@@ -273,6 +284,7 @@ def dashboard_dump(self, serial_connection: SerialObj): | |
| # Update message | ||
| if( parsed.header.mid_enum == LoRaMessageTypes.DASHBOARD_DATA ): | ||
| self.last_dashboard_dump = parsed.dashboard_dump.data | ||
| self.__put_to_rx_callback(parsed.header.mid_enum.name, parsed.header.timestamp / 1000.0, self.last_dashboard_dump.to_json()) | ||
| elif( parsed.header.mid_enum == LoRaMessageTypes.VEHICLE_ID ): | ||
| hw = parsed.vehicle_id.hw_opcode.to_bytes(1) | ||
| fw = parsed.vehicle_id.fw_opcode.to_bytes(1) | ||
|
|
@@ -284,10 +296,11 @@ def dashboard_dump(self, serial_connection: SerialObj): | |
| "sig_strength": 0, | ||
| "status": "OK" | ||
| } | ||
| self.__put_to_rx_callback(parsed.header.mid_enum.name, parsed.header.timestamp / 1000.0, self.last_wireless_stats) | ||
| except (ValueError, ParserError, TypeError, AttributeError) as e: | ||
| print("Malformed vehicle ID message received. Discarding.") | ||
| print(f"HW: {hw}; FW: {fw};") | ||
|
|
||
| return | ||
|
|
||
| def get_latest_dashboard_dump(self) -> dict[str, Any] | None: | ||
| with( self.telem_lock ): | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should rename these to something like "Attitude Quaternion W" to be a bit more descriptive
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NAT we dont actually use these anywhere iirc. maybe sensor dump?