added rx Quality

This commit is contained in:
2026-06-15 11:17:10 +03:00
parent e20b81c817
commit 23eb7ffb91
7 changed files with 100 additions and 24 deletions
@@ -29,6 +29,7 @@ public final class RadioSnapshot {
public final Double txPktPerS;
public final Double rxPktPerS;
public final Double perPercent;
public final Double rxQualityPercent;
public final Map<String, String> extraFields;
public RadioSnapshot(
@@ -46,6 +47,7 @@ public final class RadioSnapshot {
Double txPktPerS,
Double rxPktPerS,
Double perPercent,
Double rxQualityPercent,
Map<String, String> extraFields
) {
this.role = role;
@@ -62,12 +64,13 @@ public final class RadioSnapshot {
this.txPktPerS = txPktPerS;
this.rxPktPerS = rxPktPerS;
this.perPercent = perPercent;
this.rxQualityPercent = rxQualityPercent;
this.extraFields = extraFields != null ? extraFields : Map.of();
}
public static RadioSnapshot empty() {
return new RadioSnapshot(null, null, null, null, null, null, null, null,
null, null, null, null, null, null, Map.of());
null, null, null, null, null, null, null, Map.of());
}
public static RadioSnapshot fromMeta(String metaJson, String roleFallback, Double rssiFallback) {
@@ -75,7 +78,7 @@ public final class RadioSnapshot {
RadioSnapshot snap = empty();
if (roleFallback != null || rssiFallback != null) {
return new RadioSnapshot(roleFallback, null, null, null, null, null,
rssiFallback, null, null, null, null, null, null, null, Map.of());
rssiFallback, null, null, null, null, null, null, null, null, Map.of());
}
return snap;
}
@@ -115,11 +118,12 @@ public final class RadioSnapshot {
dbl(o, "tx_pkt_per_s"),
dbl(o, "rx_pkt_per_s"),
dbl(o, "per_percent"),
dbl(o, "rx_quality_percent"),
extra
);
} catch (Exception ignored) {
return new RadioSnapshot(roleFallback, null, null, null, null, null,
rssiFallback, null, null, null, null, null, null, null, Map.of());
rssiFallback, null, null, null, null, null, null, null, null, Map.of());
}
}
@@ -141,6 +145,7 @@ public final class RadioSnapshot {
cmp(changed, "packet", packet, prev.packet);
cmp(changed, "payload", payload, prev.payload);
cmp(changed, "per", perPercent, prev.perPercent);
cmp(changed, "rxQuality", rxQualityPercent, prev.rxQualityPercent);
cmp(changed, "txSpeed", txPktPerS, prev.txPktPerS);
cmp(changed, "rxSpeed", rxPktPerS, prev.rxPktPerS);
cmp(changed, "frequency", frequencyMhz, prev.frequencyMhz);
@@ -163,7 +168,7 @@ public final class RadioSnapshot {
|| n.equals("snr") || n.contains("spreading") || n.contains("bandwidth")
|| n.equals("packet") || n.contains("packet number") || n.equals("payload")
|| n.contains("on air") || n.contains("tx speed") || n.contains("rx speed")
|| n.equals("per");
|| n.equals("per") || n.contains("rx quality");
}
private static String text(JsonObject o, String key) {
@@ -36,6 +36,7 @@ public final class LoraStatsFormatter {
StringBuilder sb = new StringBuilder();
appendLine(sb, "RSSI", fmtDbm(s.rssiDbm), "rssi", changed);
appendLine(sb, "SNR", fmtSuffix(s.snrDb, " dB"), "snr", changed);
appendLine(sb, "RX Quality", fmtSuffix(s.rxQualityPercent, " %"), "rxQuality", changed);
appendLine(sb, "Пакет", fmtInt(s.packet), "packet", changed);
appendLine(sb, "Payload", s.payload, "payload", changed);
appendLine(sb, "PER", fmtSuffix(s.perPercent, " %"), "per", changed);
@@ -33,6 +33,8 @@ public class StatsExtractor {
private static final Pattern TX_SPEED = Pattern.compile("TX Speed\\s*:\\s*([\\d.]+)", Pattern.CASE_INSENSITIVE);
private static final Pattern RX_SPEED = Pattern.compile("RX Speed\\s*:\\s*([\\d.]+)", Pattern.CASE_INSENSITIVE);
private static final Pattern PER = Pattern.compile("PER\\s*:\\s*([\\d.]+)", Pattern.CASE_INSENSITIVE);
private static final Pattern RX_QUALITY = Pattern.compile(
"RX Quality\\s*:\\s*([\\d.]+)", Pattern.CASE_INSENSITIVE);
private final Pattern rssiPattern;
private final Pattern rangePattern;
@@ -98,6 +100,7 @@ public class StatsExtractor {
putDouble(meta, "tx_pkt_per_s", matchDouble(TX_SPEED, normalized));
putDouble(meta, "rx_pkt_per_s", matchDouble(RX_SPEED, normalized));
putDouble(meta, "per_percent", matchDouble(PER, normalized));
putDouble(meta, "rx_quality_percent", matchDouble(RX_QUALITY, normalized));
if (!fields.isEmpty()) {
meta.put("fields", fields);
@@ -140,7 +143,7 @@ public class StatsExtractor {
|| n.equals("snr") || n.contains("spreading factor") || n.equals("bandwidth")
|| n.equals("packet") || n.contains("packet number") || n.equals("payload")
|| n.contains("on air") || n.contains("tx speed") || n.contains("rx speed")
|| n.equals("per");
|| n.equals("per") || n.contains("rx quality");
}
private static ExtractedStats empty(String frame) {
@@ -87,6 +87,8 @@ public class RadioComparePanel extends LinearLayout {
if (dynamic) {
addRow(table, "RSSI", fmtDbm(tx.rssiDbm), fmtDbm(rx.rssiDbm), "rssi", changedTx, changedRx);
addRow(table, "SNR", fmtSuffix(tx.snrDb, " dB"), fmtSuffix(rx.snrDb, " dB"), "snr", changedTx, changedRx);
addRow(table, "RX Quality", fmtSuffix(tx.rxQualityPercent, " %"), fmtSuffix(rx.rxQualityPercent, " %"),
"rxQuality", changedTx, changedRx);
addRow(table, "Пакет", fmtInt(tx.packet), fmtInt(rx.packet), "packet", changedTx, changedRx);
addRow(table, "Payload", str(tx.payload), str(rx.payload), "payload", changedTx, changedRx);
addRow(table, "PER", fmtSuffix(tx.perPercent, " %"), fmtSuffix(rx.perPercent, " %"), "per", changedTx, changedRx);
@@ -86,6 +86,16 @@ public class LoraFrameExtractTest {
assertTrue(stats.metaJson.contains("\"fields\""));
}
@Test
public void parsesRxQualityPercent() {
StatsExtractor extractor = StatsExtractor.withDefaults();
String frame = RECEIVE_FRAME + " RX Quality: 87 %\n";
StatsExtractor.ExtractedStats stats = extractor.extract(frame);
assertTrue(stats.metaJson.contains("\"rx_quality_percent\":87"));
assertTrue(!stats.metaJson.contains("RX Quality"));
}
@Test
public void splitsTwoFramesByReceiveHeaderWithoutEsc() {
List<String> frames = new ArrayList<>();