mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-09-15 23:00:09 -04:00
fix(ui): give rx_snr real presence semantics end to end (#6523)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
4846425ff1
commit
2d20cd8a47
46 files changed
+2194
-148
No files matched your search
+6
-4
@@ -290,20 +290,22 @@ private fun UserAndUptimeRow(node: Node) {
|
||||
@Composable
|
||||
private fun SignalRow(node: Node) {
|
||||
Row(modifier = Modifier.fillMaxWidth()) {
|
||||
if (node.snr != Float.MAX_VALUE) {
|
||||
val snr = node.snrOrNull
|
||||
if (snr != null) {
|
||||
InfoItem(
|
||||
label = stringResource(Res.string.snr),
|
||||
value = MetricFormatter.snr(node.snr),
|
||||
value = MetricFormatter.snr(snr),
|
||||
icon = MeshtasticIcons.Snr,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
} else {
|
||||
Spacer(Modifier.weight(1f))
|
||||
}
|
||||
if (node.rssi != Int.MAX_VALUE) {
|
||||
val rssi = node.rssiOrNull
|
||||
if (rssi != null) {
|
||||
InfoItem(
|
||||
label = stringResource(Res.string.rssi),
|
||||
value = MetricFormatter.rssi(node.rssi),
|
||||
value = MetricFormatter.rssi(rssi),
|
||||
icon = MeshtasticIcons.Rssi,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
|
||||
+4
-2
@@ -50,6 +50,7 @@ import org.meshtastic.core.model.evaluateTracerouteMapAvailability
|
||||
import org.meshtastic.core.model.util.GeoConstants
|
||||
import org.meshtastic.core.model.util.UnitConversions
|
||||
import org.meshtastic.core.model.util.rxTimeOrNull
|
||||
import org.meshtastic.core.model.util.snrOrNull
|
||||
import org.meshtastic.core.repository.FileService
|
||||
import org.meshtastic.core.repository.MeshLogRepository
|
||||
import org.meshtastic.core.repository.NodeRepository
|
||||
@@ -455,8 +456,9 @@ open class MetricsViewModel(
|
||||
rows = data,
|
||||
epochSeconds = { (it.rxTimeOrNull() ?: 0).toLong() },
|
||||
) { p ->
|
||||
// An absent rssi exports as an empty field, matching the other optional metrics above.
|
||||
"\"${p.rx_rssi ?: ""}\",\"${p.rx_snr}\""
|
||||
// An absent rssi or snr exports as an empty field, matching the other optional metrics above. An empty
|
||||
// field and "0" must stay distinguishable: 0 dB is a real reading.
|
||||
"\"${p.rx_rssi ?: ""}\",\"${p.snrOrNull() ?: ""}\""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-5
@@ -59,6 +59,7 @@ import org.meshtastic.core.model.TelemetryType
|
||||
import org.meshtastic.core.model.util.TimeConstants.MS_PER_SEC
|
||||
import org.meshtastic.core.model.util.formatUptime
|
||||
import org.meshtastic.core.model.util.rxTimeOrNull
|
||||
import org.meshtastic.core.model.util.snrOrNull
|
||||
import org.meshtastic.core.resources.Res
|
||||
import org.meshtastic.core.resources.busy_noise_floor
|
||||
import org.meshtastic.core.resources.clear
|
||||
@@ -164,7 +165,7 @@ fun SignalMetricsScreen(viewModel: MetricsViewModel, onNavigateUp: () -> Unit, m
|
||||
val data = remember(signalData, localStatsData) { buildSignalLog(signalData, localStatsData) }
|
||||
val hasNoiseFloor = remember(localStatsData) { localStatsData.any { it.local_stats?.noise_floor != 0 } }
|
||||
val hasRssi = remember(signalData) { signalData.any { it.rx_rssi != null } }
|
||||
val hasSnr = remember(signalData) { signalData.any { !it.rx_snr.isNaN() } }
|
||||
val hasSnr = remember(signalData) { signalData.any { it.snrOrNull() != null } }
|
||||
val hasAnyLocalStats = state.localStats.isNotEmpty()
|
||||
val localStatsExportLauncher = rememberSaveFileLauncher { uri -> viewModel.saveLocalStatsCSV(uri, localStatsData) }
|
||||
val signalExportLauncher = rememberSaveFileLauncher { uri -> viewModel.saveSignalMetricsCSV(uri, signalData) }
|
||||
@@ -319,7 +320,7 @@ private fun SignalMetricsChart(
|
||||
if (noiseFloorData.size > 1) listOf(noiseFloorData.first(), noiseFloorData.last()) else emptyList()
|
||||
}
|
||||
val rssiData = remember(meshPackets) { meshPackets.filter { it.rx_rssi != null } }
|
||||
val snrData = remember(meshPackets) { meshPackets.filter { !it.rx_snr.isNaN() } }
|
||||
val snrData = remember(meshPackets) { meshPackets.filter { it.snrOrNull() != null } }
|
||||
val legendData =
|
||||
remember(noiseFloorData, rssiData, snrData) {
|
||||
LEGEND_DATA.filter { legend ->
|
||||
@@ -366,7 +367,9 @@ private fun SignalMetricsChart(
|
||||
}
|
||||
if (snrData.isNotEmpty()) {
|
||||
/* Use a separate lineModel call to associate SNR with the right axis. */
|
||||
lineModel { series(x = snrData.map { it.rxTimeOrNull() ?: 0 }, y = snrData.map { it.rx_snr }) }
|
||||
lineModel {
|
||||
series(x = snrData.map { it.rxTimeOrNull() ?: 0 }, y = snrData.mapNotNull { it.snrOrNull() })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -583,14 +586,17 @@ private fun SignalMetricsCard(meshPacket: MeshPacket, isSelected: Boolean, onCli
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
MetricValueRow(color = SignalMetric.RSSI.color, text = MetricFormatter.rssi(meshPacket.rx_rssi))
|
||||
Spacer(Modifier.width(12.dp))
|
||||
MetricValueRow(color = SignalMetric.SNR.color, text = MetricFormatter.snr(meshPacket.rx_snr))
|
||||
MetricValueRow(
|
||||
color = SignalMetric.SNR.color,
|
||||
text = MetricFormatter.snr(meshPacket.snrOrNull()),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Signal Indicator */
|
||||
Box(modifier = Modifier.weight(weight = 3f).height(IntrinsicSize.Max)) {
|
||||
LoraSignalIndicator(snr = meshPacket.rx_snr)
|
||||
LoraSignalIndicator(snr = meshPacket.snrOrNull())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user