mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +00:00
Tweak AVIF tuning and preferences
This commit is contained in:
@@ -3,6 +3,15 @@ import 'dart:typed_data';
|
||||
const int _maxCompanionFrameBytes = 172; // MeshCore MAX_FRAME_SIZE
|
||||
const int _cmdSendRawDataOverheadBytes = 2; // cmd + pathLen
|
||||
const int _maxMeshPacketPayloadBytes = 184; // MeshCore MAX_PACKET_PAYLOAD
|
||||
const int _meshPacketHeaderBytes = 2; // mesh header bytes before path/payload
|
||||
const int _imagePacketHeaderBytes = 8; // image packet binary header in payload
|
||||
const int _defaultLoRaSf = 10; // MeshCore companion defaults (SF10)
|
||||
const int _defaultLoRaCr = 5; // MeshCore companion defaults (4/5)
|
||||
const int _defaultLoRaBwHz = 250000; // MeshCore companion defaults (250kHz)
|
||||
const int _defaultLoRaPreambleSymbols = 8;
|
||||
const int _defaultLoRaCrcEnabled = 1;
|
||||
const int _defaultLoRaExplicitHeader = 1;
|
||||
const double _defaultAirtimeBudgetFactor = 1.0; // one half duty-cycle
|
||||
|
||||
/// Compressed image format used in the image packet protocol.
|
||||
enum ImageFormat {
|
||||
@@ -119,6 +128,118 @@ int safeImageDataBytesForPath(int pathLen) {
|
||||
return maxData.clamp(1, 255).toInt();
|
||||
}
|
||||
|
||||
/// Approximate end-to-end transmit time for image fragments on MeshCore LoRa.
|
||||
///
|
||||
/// The estimate uses:
|
||||
/// - LoRa airtime math with MeshCore companion defaults (SF10/BW250/CR5),
|
||||
/// - MeshCore airtime budget pacing (default factor 1.0),
|
||||
/// - hop multiplier (`pathLen + 1`) for direct routed packets.
|
||||
Duration estimateImageTransmitDuration({
|
||||
required int fragmentCount,
|
||||
required int sizeBytes,
|
||||
int pathLen = 0,
|
||||
int? radioBw,
|
||||
int? radioSf,
|
||||
int? radioCr,
|
||||
}) {
|
||||
if (fragmentCount <= 0 || sizeBytes <= 0) return Duration.zero;
|
||||
|
||||
final safePathLen = pathLen.clamp(0, 64);
|
||||
final hops = safePathLen + 1;
|
||||
final baseDataPerFragment = sizeBytes ~/ fragmentCount;
|
||||
final extraBytes = sizeBytes % fragmentCount;
|
||||
var totalMs = 0.0;
|
||||
|
||||
for (var i = 0; i < fragmentCount; i++) {
|
||||
final fragmentDataBytes = baseDataPerFragment + (i < extraBytes ? 1 : 0);
|
||||
final loraLen =
|
||||
_meshPacketHeaderBytes +
|
||||
safePathLen +
|
||||
_imagePacketHeaderBytes +
|
||||
fragmentDataBytes;
|
||||
final airtimeMs = _estimateLoRaAirtimeMs(
|
||||
loraLen,
|
||||
radioBw: radioBw,
|
||||
radioSf: radioSf,
|
||||
radioCr: radioCr,
|
||||
);
|
||||
totalMs += airtimeMs * (1.0 + _defaultAirtimeBudgetFactor) * hops;
|
||||
}
|
||||
|
||||
return Duration(milliseconds: totalMs.round());
|
||||
}
|
||||
|
||||
double _estimateLoRaAirtimeMs(
|
||||
int payloadLenBytes, {
|
||||
int? radioBw,
|
||||
int? radioSf,
|
||||
int? radioCr,
|
||||
}) {
|
||||
final sf = _normalizeSf(radioSf);
|
||||
final bw = _resolveBandwidthHz(radioBw).toDouble();
|
||||
final cr = (_normalizeCr(radioCr) - 4).clamp(1, 4);
|
||||
final ih = _defaultLoRaExplicitHeader == 1 ? 0 : 1;
|
||||
final de = (sf >= 11 && _defaultLoRaBwHz <= 125000) ? 1 : 0;
|
||||
|
||||
final symbolMs = ((1 << sf) / bw) * 1000.0;
|
||||
final preambleMs = (_defaultLoRaPreambleSymbols + 4.25) * symbolMs;
|
||||
|
||||
final num =
|
||||
(8 * payloadLenBytes) -
|
||||
(4 * sf) +
|
||||
28 +
|
||||
(16 * _defaultLoRaCrcEnabled) -
|
||||
(20 * ih);
|
||||
final den = 4 * (sf - (2 * de));
|
||||
final payloadSymCoeff = den <= 0 ? 0 : (num / den).ceil();
|
||||
final payloadSymbols =
|
||||
8 + (payloadSymCoeff < 0 ? 0 : payloadSymCoeff) * (cr + 4);
|
||||
final payloadMs = payloadSymbols * symbolMs;
|
||||
|
||||
return preambleMs + payloadMs;
|
||||
}
|
||||
|
||||
int _normalizeSf(int? value) {
|
||||
if (value == null) return _defaultLoRaSf;
|
||||
if (value >= 5 && value <= 12) return value;
|
||||
return _defaultLoRaSf;
|
||||
}
|
||||
|
||||
int _normalizeCr(int? value) {
|
||||
if (value == null) return _defaultLoRaCr;
|
||||
if (value >= 5 && value <= 8) return value;
|
||||
return _defaultLoRaCr;
|
||||
}
|
||||
|
||||
int _resolveBandwidthHz(int? rawBw) {
|
||||
if (rawBw == null) return _defaultLoRaBwHz;
|
||||
if (rawBw > 1000) return rawBw;
|
||||
switch (rawBw) {
|
||||
case 0:
|
||||
return 7800;
|
||||
case 1:
|
||||
return 10400;
|
||||
case 2:
|
||||
return 15600;
|
||||
case 3:
|
||||
return 20800;
|
||||
case 4:
|
||||
return 31250;
|
||||
case 5:
|
||||
return 41700;
|
||||
case 6:
|
||||
return 62500;
|
||||
case 7:
|
||||
return 125000;
|
||||
case 8:
|
||||
return 250000;
|
||||
case 9:
|
||||
return 500000;
|
||||
default:
|
||||
return _defaultLoRaBwHz;
|
||||
}
|
||||
}
|
||||
|
||||
/// Envelope announcing image availability (control plane).
|
||||
///
|
||||
/// Text format:
|
||||
|
||||
Reference in New Issue
Block a user