meshcore-open/lib/models/companion_radio_stats.dart
zjs81 834850fb51 Add companion radio stats, adaptive backoff, path hash width, and UI improvements
- Companion radio stats: poll and display noise floor, RSSI, SNR, airtime
  with dedicated ValueNotifier and ref-counted polling
- Adaptive RF-aware TX backoff based on radio conditions instead of fixed 5s
- Variable-width path hash support (1-3 bytes per hop)
- Air activity dot indicator in app bar with tap to open stats screen
- Jump to oldest unread setting for chat screens
- 1s send cooldown on DM and channel messages
- Link style: theme-aware orange, added EmailLinkifier
- New languages: Hungarian, Japanese, Korean
- Remove dead DeviceScreen and BatteryIndicatorChip
- Remove wakelock_plus dependency
- TX power fields now read as signed int8
2026-03-23 19:26:05 -07:00

48 lines
1.3 KiB
Dart

import 'dart:typed_data';
import '../connector/meshcore_protocol.dart';
import '../utils/app_logger.dart';
/// Parsed `RESP_CODE_STATS` + `STATS_TYPE_RADIO` (14 bytes total).
class CompanionRadioStats {
final int noiseFloorDbm;
final int lastRssiDbm;
final double lastSnrDb;
final int txAirSecs;
final int rxAirSecs;
final DateTime receivedAt;
const CompanionRadioStats({
required this.noiseFloorDbm,
required this.lastRssiDbm,
required this.lastSnrDb,
required this.txAirSecs,
required this.rxAirSecs,
required this.receivedAt,
});
static CompanionRadioStats? tryParse(Uint8List frame) {
if (frame.length < 14) return null;
if (frame[0] != respCodeStats || frame[1] != statsTypeRadio) return null;
try {
final reader = BufferReader(frame);
reader.skipBytes(2);
final noise = reader.readInt16LE();
final rssi = reader.readInt8();
final snrRaw = reader.readInt8();
final txAir = reader.readUInt32LE();
final rxAir = reader.readUInt32LE();
return CompanionRadioStats(
noiseFloorDbm: noise,
lastRssiDbm: rssi,
lastSnrDb: snrRaw / 4.0,
txAirSecs: txAir,
rxAirSecs: rxAir,
receivedAt: DateTime.now(),
);
} catch (e) {
appLogger.warn('CompanionRadioStats parse error: $e');
return null;
}
}
}