feat: Signal Quality in NodeItem (#1425)

* Composable that provides the snr and rssi within a row, along with docs for the two exposed functions in LoraSignalIndicator.kt.

* Fancied up the signal data within the NodeItem.
pull/1426/head
Robert-0410 2024-11-21 02:52:54 -08:00 zatwierdzone przez GitHub
rodzic c7841b18e7
commit 75003bb6f0
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
3 zmienionych plików z 86 dodań i 51 usunięć

Wyświetl plik

@ -10,6 +10,7 @@ import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import com.geeksville.mesh.R import com.geeksville.mesh.R
import com.geeksville.mesh.database.entity.NodeEntity import com.geeksville.mesh.database.entity.NodeEntity
import com.geeksville.mesh.ui.components.NodeSignalQuality
import com.geeksville.mesh.ui.preview.NodeEntityPreviewParameterProvider import com.geeksville.mesh.ui.preview.NodeEntityPreviewParameterProvider
import com.geeksville.mesh.ui.theme.AppTheme import com.geeksville.mesh.ui.theme.AppTheme
@ -21,7 +22,7 @@ fun signalInfo(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
node: NodeEntity, node: NodeEntity,
isThisNode: Boolean isThisNode: Boolean
): Boolean { ) {
val text = if (isThisNode) { val text = if (isThisNode) {
stringResource(R.string.channel_air_util).format( stringResource(R.string.channel_air_util).format(
node.deviceMetrics.channelUtilization, node.deviceMetrics.channelUtilization,
@ -40,24 +41,22 @@ fun signalInfo(
if (node.channel > 0) { if (node.channel > 0) {
add("ch:${node.channel}") add("ch:${node.channel}")
} }
if (node.hopsAway <= 0) {
if (node.snr < MAX_VALID_SNR && node.rssi < MAX_VALID_RSSI) {
add("RSSI: %d SNR: %.1f".format(node.rssi, node.snr))
}
}
if (node.hopsAway != 0) add(hopsString) if (node.hopsAway != 0) add(hopsString)
}.joinToString(" | ") }.joinToString(" | ")
} }
return if (text.isNotEmpty()) { if (text.isNotEmpty()) {
Text( Text(
modifier = modifier, modifier = modifier,
text = text, text = text,
color = MaterialTheme.colors.onSurface, color = MaterialTheme.colors.onSurface,
fontSize = MaterialTheme.typography.button.fontSize fontSize = MaterialTheme.typography.button.fontSize
) )
true }
} else { /* We only know the Signal Quality from direct nodes aka 0 hop. */
false if (node.hopsAway <= 0) {
if (node.snr < MAX_VALID_SNR && node.rssi < MAX_VALID_RSSI) {
NodeSignalQuality(node.snr, node.rssi)
}
} }
} }

Wyświetl plik

@ -2,7 +2,9 @@ package com.geeksville.mesh.ui.components
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material.Icon import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
@ -38,45 +40,46 @@ private enum class Quality(
GOOD(R.string.good, Icons.Default.SignalCellular4Bar, Color.Green) GOOD(R.string.good, Icons.Default.SignalCellular4Bar, Color.Green)
} }
/**
* Displays the `snr` and `rssi` color coded based on the signal quality, along with
* a human readable description and related icon.
*/
@Composable @Composable
fun Snr(snr: Float) { fun NodeSignalQuality(snr: Float, rssi: Int) {
val color: Color = if (snr > SNR_GOOD_THRESHOLD) { val quality = determineSignalQuality(snr, rssi)
Quality.GOOD.color Row(
} else if (snr > SNR_FAIR_THRESHOLD) { modifier = Modifier.fillMaxWidth(),
Quality.FAIR.color verticalAlignment = Alignment.CenterVertically,
} else { horizontalArrangement = Arrangement.SpaceBetween
Quality.BAD.color ) {
} Snr(snr)
Rssi(rssi)
Text( Text(text = "${stringResource(R.string.signal)} ${stringResource(quality.nameRes)}")
text = "%s %.2fdB".format( Icon(
stringResource(id = R.string.snr), imageVector = quality.imageVector,
snr contentDescription = stringResource(R.string.signal_quality),
), tint = quality.color
color = color,
fontSize = MaterialTheme.typography.button.fontSize
) )
} }
}
/**
* Displays the `snr` and `rssi` with color depending on the values respectively.
*/
@Composable @Composable
fun Rssi(rssi: Int) { fun SnrAndRssi(snr: Float, rssi: Int) {
val color: Color = if (rssi > RSSI_GOOD_THRESHOLD) { Row(
Quality.GOOD.color modifier = Modifier.fillMaxWidth(),
} else if (rssi > RSSI_FAIR_THRESHOLD) { horizontalArrangement = Arrangement.SpaceBetween
Quality.FAIR.color ) {
} else { Snr(snr)
Quality.BAD.color Rssi(rssi)
} }
Text(
text = "%s %ddB".format(
stringResource(id = R.string.rssi),
rssi
),
color = color,
fontSize = MaterialTheme.typography.button.fontSize
)
} }
/**
* Displays a human readable description and icon representing the signal quality.
*/
@Composable @Composable
fun LoraSignalIndicator(snr: Float, rssi: Int) { fun LoraSignalIndicator(snr: Float, rssi: Int) {
@ -98,6 +101,45 @@ fun LoraSignalIndicator(snr: Float, rssi: Int) {
} }
} }
@Composable
private fun Snr(snr: Float) {
val color: Color = if (snr > SNR_GOOD_THRESHOLD) {
Quality.GOOD.color
} else if (snr > SNR_FAIR_THRESHOLD) {
Quality.FAIR.color
} else {
Quality.BAD.color
}
Text(
text = "%s %.2fdB".format(
stringResource(id = R.string.snr),
snr
),
color = color,
fontSize = MaterialTheme.typography.button.fontSize
)
}
@Composable
private fun Rssi(rssi: Int) {
val color: Color = if (rssi > RSSI_GOOD_THRESHOLD) {
Quality.GOOD.color
} else if (rssi > RSSI_FAIR_THRESHOLD) {
Quality.FAIR.color
} else {
Quality.BAD.color
}
Text(
text = "%s %ddB".format(
stringResource(id = R.string.rssi),
rssi
),
color = color,
fontSize = MaterialTheme.typography.button.fontSize
)
}
private fun determineSignalQuality(snr: Float, rssi: Int): Quality = when { private fun determineSignalQuality(snr: Float, rssi: Int): Quality = when {
snr > SNR_GOOD_THRESHOLD && rssi > RSSI_GOOD_THRESHOLD -> Quality.GOOD snr > SNR_GOOD_THRESHOLD && rssi > RSSI_GOOD_THRESHOLD -> Quality.GOOD
snr > SNR_GOOD_THRESHOLD && rssi > RSSI_FAIR_THRESHOLD -> Quality.FAIR snr > SNR_GOOD_THRESHOLD && rssi > RSSI_FAIR_THRESHOLD -> Quality.FAIR

Wyświetl plik

@ -266,13 +266,7 @@ private fun SignalMetricsCard(meshPacket: MeshPacket) {
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
/* SNR and RSSI */ /* SNR and RSSI */
Row( SnrAndRssi(meshPacket.rxSnr, meshPacket.rxRssi)
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Snr(meshPacket.rxSnr)
Rssi(meshPacket.rxRssi)
}
} }
} }