Skip to content

Commit a3c9a07

Browse files
committed
Modify CMD_GET_STATS with sub-types for core, radio, and packet statistics. Consolidated to a single RESP_CODE_STATS with a second byte to identify response structure. Updated documentation and examples to reflect the new command structure and response parsing.
1 parent 39f83ef commit a3c9a07

File tree

2 files changed

+200
-104
lines changed

2 files changed

+200
-104
lines changed

docs/stats_binary_frames.md

Lines changed: 144 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -6,37 +6,53 @@ Binary frame structures for companion radio stats commands. All multi-byte integ
66

77
| Command | Code | Description |
88
|---------|------|-------------|
9-
| `CMD_GET_STATS_CORE` | 56 | Get core device statistics |
10-
| `CMD_GET_STATS_RADIO` | 57 | Get radio statistics |
11-
| `CMD_GET_STATS_PACKETS` | 58 | Get packet statistics |
9+
| `CMD_GET_STATS` | 56 | Get statistics (2-byte command: code + sub-type) |
10+
11+
### Stats Sub-Types
12+
13+
The `CMD_GET_STATS` command uses a 2-byte frame structure:
14+
- **Byte 0:** `CMD_GET_STATS` (56)
15+
- **Byte 1:** Stats sub-type:
16+
- `STATS_TYPE_CORE` (0) - Get core device statistics
17+
- `STATS_TYPE_RADIO` (1) - Get radio statistics
18+
- `STATS_TYPE_PACKETS` (2) - Get packet statistics
1219

1320
## Response Codes
1421

1522
| Response | Code | Description |
1623
|----------|------|-------------|
17-
| `RESP_CODE_STATS_CORE` | 24 | Core stats response |
18-
| `RESP_CODE_STATS_RADIO` | 25 | Radio stats response |
19-
| `RESP_CODE_STATS_PACKETS` | 26 | Packet stats response |
24+
| `RESP_CODE_STATS` | 24 | Statistics response (2-byte response: code + sub-type) |
25+
26+
### Stats Response Sub-Types
27+
28+
The `RESP_CODE_STATS` response uses a 2-byte header structure:
29+
- **Byte 0:** `RESP_CODE_STATS` (24)
30+
- **Byte 1:** Stats sub-type (matches command sub-type):
31+
- `STATS_TYPE_CORE` (0) - Core device statistics response
32+
- `STATS_TYPE_RADIO` (1) - Radio statistics response
33+
- `STATS_TYPE_PACKETS` (2) - Packet statistics response
2034

2135
---
2236

23-
## RESP_CODE_STATS_CORE (24)
37+
## RESP_CODE_STATS + STATS_TYPE_CORE (24, 0)
2438

25-
**Total Frame Size:** 10 bytes
39+
**Total Frame Size:** 11 bytes
2640

2741
| Offset | Size | Type | Field Name | Description | Range/Notes |
2842
|--------|------|------|------------|-------------|-------------|
2943
| 0 | 1 | uint8_t | response_code | Always `0x18` (24) | - |
30-
| 1 | 2 | uint16_t | battery_mv | Battery voltage in millivolts | 0 - 65,535 |
31-
| 3 | 4 | uint32_t | uptime_secs | Device uptime in seconds | 0 - 4,294,967,295 |
32-
| 7 | 2 | uint16_t | errors | Error flags bitmask | - |
33-
| 9 | 1 | uint8_t | queue_len | Outbound packet queue length | 0 - 255 |
44+
| 1 | 1 | uint8_t | stats_type | Always `0x00` (STATS_TYPE_CORE) | - |
45+
| 2 | 2 | uint16_t | battery_mv | Battery voltage in millivolts | 0 - 65,535 |
46+
| 4 | 4 | uint32_t | uptime_secs | Device uptime in seconds | 0 - 4,294,967,295 |
47+
| 8 | 2 | uint16_t | errors | Error flags bitmask | - |
48+
| 10 | 1 | uint8_t | queue_len | Outbound packet queue length | 0 - 255 |
3449

3550
### Example Structure (C/C++)
3651

3752
```c
3853
struct StatsCore {
3954
uint8_t response_code; // 0x18
55+
uint8_t stats_type; // 0x00 (STATS_TYPE_CORE)
4056
uint16_t battery_mv;
4157
uint32_t uptime_secs;
4258
uint16_t errors;
@@ -46,24 +62,26 @@ struct StatsCore {
4662

4763
---
4864

49-
## RESP_CODE_STATS_RADIO (25)
65+
## RESP_CODE_STATS + STATS_TYPE_RADIO (24, 1)
5066

51-
**Total Frame Size:** 13 bytes
67+
**Total Frame Size:** 14 bytes
5268

5369
| Offset | Size | Type | Field Name | Description | Range/Notes |
5470
|--------|------|------|------------|-------------|-------------|
55-
| 0 | 1 | uint8_t | response_code | Always `0x19` (25) | - |
56-
| 1 | 2 | int16_t | noise_floor | Radio noise floor in dBm | -140 to +10 |
57-
| 3 | 1 | int8_t | last_rssi | Last received signal strength in dBm | -128 to +127 |
58-
| 4 | 1 | int8_t | last_snr | SNR scaled by 4 | Divide by 4.0 for dB |
59-
| 5 | 4 | uint32_t | tx_air_secs | Cumulative transmit airtime in seconds | 0 - 4,294,967,295 |
60-
| 9 | 4 | uint32_t | rx_air_secs | Cumulative receive airtime in seconds | 0 - 4,294,967,295 |
71+
| 0 | 1 | uint8_t | response_code | Always `0x18` (24) | - |
72+
| 1 | 1 | uint8_t | stats_type | Always `0x01` (STATS_TYPE_RADIO) | - |
73+
| 2 | 2 | int16_t | noise_floor | Radio noise floor in dBm | -140 to +10 |
74+
| 4 | 1 | int8_t | last_rssi | Last received signal strength in dBm | -128 to +127 |
75+
| 5 | 1 | int8_t | last_snr | SNR scaled by 4 | Divide by 4.0 for dB |
76+
| 6 | 4 | uint32_t | tx_air_secs | Cumulative transmit airtime in seconds | 0 - 4,294,967,295 |
77+
| 10 | 4 | uint32_t | rx_air_secs | Cumulative receive airtime in seconds | 0 - 4,294,967,295 |
6178

6279
### Example Structure (C/C++)
6380

6481
```c
6582
struct StatsRadio {
66-
uint8_t response_code; // 0x19
83+
uint8_t response_code; // 0x18
84+
uint8_t stats_type; // 0x01 (STATS_TYPE_RADIO)
6785
int16_t noise_floor;
6886
int8_t last_rssi;
6987
int8_t last_snr; // Divide by 4.0 to get actual SNR in dB
@@ -74,19 +92,20 @@ struct StatsRadio {
7492

7593
---
7694

77-
## RESP_CODE_STATS_PACKETS (26)
95+
## RESP_CODE_STATS + STATS_TYPE_PACKETS (24, 2)
7896

79-
**Total Frame Size:** 25 bytes
97+
**Total Frame Size:** 26 bytes
8098

8199
| Offset | Size | Type | Field Name | Description | Range/Notes |
82100
|--------|------|------|------------|-------------|-------------|
83-
| 0 | 1 | uint8_t | response_code | Always `0x1A` (26) | - |
84-
| 1 | 4 | uint32_t | recv | Total packets received | 0 - 4,294,967,295 |
85-
| 5 | 4 | uint32_t | sent | Total packets sent | 0 - 4,294,967,295 |
86-
| 9 | 4 | uint32_t | flood_tx | Packets sent via flood routing | 0 - 4,294,967,295 |
87-
| 13 | 4 | uint32_t | direct_tx | Packets sent via direct routing | 0 - 4,294,967,295 |
88-
| 17 | 4 | uint32_t | flood_rx | Packets received via flood routing | 0 - 4,294,967,295 |
89-
| 21 | 4 | uint32_t | direct_rx | Packets received via direct routing | 0 - 4,294,967,295 |
101+
| 0 | 1 | uint8_t | response_code | Always `0x18` (24) | - |
102+
| 1 | 1 | uint8_t | stats_type | Always `0x02` (STATS_TYPE_PACKETS) | - |
103+
| 2 | 4 | uint32_t | recv | Total packets received | 0 - 4,294,967,295 |
104+
| 6 | 4 | uint32_t | sent | Total packets sent | 0 - 4,294,967,295 |
105+
| 10 | 4 | uint32_t | flood_tx | Packets sent via flood routing | 0 - 4,294,967,295 |
106+
| 14 | 4 | uint32_t | direct_tx | Packets sent via direct routing | 0 - 4,294,967,295 |
107+
| 18 | 4 | uint32_t | flood_rx | Packets received via flood routing | 0 - 4,294,967,295 |
108+
| 22 | 4 | uint32_t | direct_rx | Packets received via direct routing | 0 - 4,294,967,295 |
90109

91110
### Notes
92111

@@ -98,7 +117,8 @@ struct StatsRadio {
98117

99118
```c
100119
struct StatsPackets {
101-
uint8_t response_code; // 0x1A
120+
uint8_t response_code; // 0x18
121+
uint8_t stats_type; // 0x02 (STATS_TYPE_PACKETS)
102122
uint32_t recv;
103123
uint32_t sent;
104124
uint32_t flood_tx;
@@ -110,15 +130,38 @@ struct StatsPackets {
110130

111131
---
112132

113-
## Usage Example (Python)
133+
## Command Usage Example (Python)
134+
135+
```python
136+
# Send CMD_GET_STATS command
137+
def send_get_stats_core(serial_interface):
138+
"""Send command to get core stats"""
139+
cmd = bytes([56, 0]) # CMD_GET_STATS (56) + STATS_TYPE_CORE (0)
140+
serial_interface.write(cmd)
141+
142+
def send_get_stats_radio(serial_interface):
143+
"""Send command to get radio stats"""
144+
cmd = bytes([56, 1]) # CMD_GET_STATS (56) + STATS_TYPE_RADIO (1)
145+
serial_interface.write(cmd)
146+
147+
def send_get_stats_packets(serial_interface):
148+
"""Send command to get packet stats"""
149+
cmd = bytes([56, 2]) # CMD_GET_STATS (56) + STATS_TYPE_PACKETS (2)
150+
serial_interface.write(cmd)
151+
```
152+
153+
---
154+
155+
## Response Parsing Example (Python)
114156

115157
```python
116158
import struct
117159

118160
def parse_stats_core(frame):
119-
"""Parse RESP_CODE_STATS_CORE frame (10 bytes)"""
120-
response_code, battery_mv, uptime_secs, errors, queue_len = \
121-
struct.unpack('<B H I H B', frame)
161+
"""Parse RESP_CODE_STATS + STATS_TYPE_CORE frame (11 bytes)"""
162+
response_code, stats_type, battery_mv, uptime_secs, errors, queue_len = \
163+
struct.unpack('<B B H I H B', frame)
164+
assert response_code == 24 and stats_type == 0, "Invalid response type"
122165
return {
123166
'battery_mv': battery_mv,
124167
'uptime_secs': uptime_secs,
@@ -127,9 +170,10 @@ def parse_stats_core(frame):
127170
}
128171

129172
def parse_stats_radio(frame):
130-
"""Parse RESP_CODE_STATS_RADIO frame (13 bytes)"""
131-
response_code, noise_floor, last_rssi, last_snr, tx_air_secs, rx_air_secs = \
132-
struct.unpack('<B h b b I I', frame)
173+
"""Parse RESP_CODE_STATS + STATS_TYPE_RADIO frame (14 bytes)"""
174+
response_code, stats_type, noise_floor, last_rssi, last_snr, tx_air_secs, rx_air_secs = \
175+
struct.unpack('<B B h b b I I', frame)
176+
assert response_code == 24 and stats_type == 1, "Invalid response type"
133177
return {
134178
'noise_floor': noise_floor,
135179
'last_rssi': last_rssi,
@@ -139,9 +183,10 @@ def parse_stats_radio(frame):
139183
}
140184

141185
def parse_stats_packets(frame):
142-
"""Parse RESP_CODE_STATS_PACKETS frame (25 bytes)"""
143-
response_code, recv, sent, flood_tx, direct_tx, flood_rx, direct_rx = \
144-
struct.unpack('<B I I I I I I', frame)
186+
"""Parse RESP_CODE_STATS + STATS_TYPE_PACKETS frame (26 bytes)"""
187+
response_code, stats_type, recv, sent, flood_tx, direct_tx, flood_rx, direct_rx = \
188+
struct.unpack('<B B I I I I I I', frame)
189+
assert response_code == 24 and stats_type == 2, "Invalid response type"
145190
return {
146191
'recv': recv,
147192
'sent': sent,
@@ -154,7 +199,34 @@ def parse_stats_packets(frame):
154199

155200
---
156201

157-
## Usage Example (JavaScript/TypeScript)
202+
## Command Usage Example (JavaScript/TypeScript)
203+
204+
```typescript
205+
// Send CMD_GET_STATS command
206+
const CMD_GET_STATS = 56;
207+
const STATS_TYPE_CORE = 0;
208+
const STATS_TYPE_RADIO = 1;
209+
const STATS_TYPE_PACKETS = 2;
210+
211+
function sendGetStatsCore(serialInterface: SerialPort): void {
212+
const cmd = new Uint8Array([CMD_GET_STATS, STATS_TYPE_CORE]);
213+
serialInterface.write(cmd);
214+
}
215+
216+
function sendGetStatsRadio(serialInterface: SerialPort): void {
217+
const cmd = new Uint8Array([CMD_GET_STATS, STATS_TYPE_RADIO]);
218+
serialInterface.write(cmd);
219+
}
220+
221+
function sendGetStatsPackets(serialInterface: SerialPort): void {
222+
const cmd = new Uint8Array([CMD_GET_STATS, STATS_TYPE_PACKETS]);
223+
serialInterface.write(cmd);
224+
}
225+
```
226+
227+
---
228+
229+
## Response Parsing Example (JavaScript/TypeScript)
158230

159231
```typescript
160232
interface StatsCore {
@@ -183,34 +255,49 @@ interface StatsPackets {
183255

184256
function parseStatsCore(buffer: ArrayBuffer): StatsCore {
185257
const view = new DataView(buffer);
258+
const response_code = view.getUint8(0);
259+
const stats_type = view.getUint8(1);
260+
if (response_code !== 24 || stats_type !== 0) {
261+
throw new Error('Invalid response type');
262+
}
186263
return {
187-
battery_mv: view.getUint16(1, true),
188-
uptime_secs: view.getUint32(3, true),
189-
errors: view.getUint16(7, true),
190-
queue_len: view.getUint8(9)
264+
battery_mv: view.getUint16(2, true),
265+
uptime_secs: view.getUint32(4, true),
266+
errors: view.getUint16(8, true),
267+
queue_len: view.getUint8(10)
191268
};
192269
}
193270

194271
function parseStatsRadio(buffer: ArrayBuffer): StatsRadio {
195272
const view = new DataView(buffer);
273+
const response_code = view.getUint8(0);
274+
const stats_type = view.getUint8(1);
275+
if (response_code !== 24 || stats_type !== 1) {
276+
throw new Error('Invalid response type');
277+
}
196278
return {
197-
noise_floor: view.getInt16(1, true),
198-
last_rssi: view.getInt8(3),
199-
last_snr: view.getInt8(4) / 4.0, // Unscale SNR
200-
tx_air_secs: view.getUint32(5, true),
201-
rx_air_secs: view.getUint32(9, true)
279+
noise_floor: view.getInt16(2, true),
280+
last_rssi: view.getInt8(4),
281+
last_snr: view.getInt8(5) / 4.0, // Unscale SNR
282+
tx_air_secs: view.getUint32(6, true),
283+
rx_air_secs: view.getUint32(10, true)
202284
};
203285
}
204286

205287
function parseStatsPackets(buffer: ArrayBuffer): StatsPackets {
206288
const view = new DataView(buffer);
289+
const response_code = view.getUint8(0);
290+
const stats_type = view.getUint8(1);
291+
if (response_code !== 24 || stats_type !== 2) {
292+
throw new Error('Invalid response type');
293+
}
207294
return {
208-
recv: view.getUint32(1, true),
209-
sent: view.getUint32(5, true),
210-
flood_tx: view.getUint32(9, true),
211-
direct_tx: view.getUint32(13, true),
212-
flood_rx: view.getUint32(17, true),
213-
direct_rx: view.getUint32(21, true)
295+
recv: view.getUint32(2, true),
296+
sent: view.getUint32(6, true),
297+
flood_tx: view.getUint32(10, true),
298+
direct_tx: view.getUint32(14, true),
299+
flood_rx: view.getUint32(18, true),
300+
direct_rx: view.getUint32(22, true)
214301
};
215302
}
216303
```

0 commit comments

Comments
 (0)