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 com.geeksville.mesh.R
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.theme.AppTheme
@ -21,7 +22,7 @@ fun signalInfo(
modifier: Modifier = Modifier,
node: NodeEntity,
isThisNode: Boolean
): Boolean {
) {
val text = if (isThisNode) {
stringResource(R.string.channel_air_util).format(
node.deviceMetrics.channelUtilization,
@ -40,24 +41,22 @@ fun signalInfo(
if (node.channel > 0) {
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)
}.joinToString(" | ")
}
return if (text.isNotEmpty()) {
if (text.isNotEmpty()) {
Text(
modifier = modifier,
text = text,
color = MaterialTheme.colors.onSurface,
fontSize = MaterialTheme.typography.button.fontSize
)
true
} else {
false
}
/* We only know the Signal Quality from direct nodes aka 0 hop. */
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.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
@ -38,45 +40,46 @@ private enum class Quality(
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
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
fun NodeSignalQuality(snr: Float, rssi: Int) {
val quality = determineSignalQuality(snr, rssi)
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Snr(snr)
Rssi(rssi)
Text(text = "${stringResource(R.string.signal)} ${stringResource(quality.nameRes)}")
Icon(
imageVector = quality.imageVector,
contentDescription = stringResource(R.string.signal_quality),
tint = quality.color
)
}
@Composable
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
)
}
/**
* Displays the `snr` and `rssi` with color depending on the values respectively.
*/
@Composable
fun SnrAndRssi(snr: Float, rssi: Int) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Snr(snr)
Rssi(rssi)
}
}
/**
* Displays a human readable description and icon representing the signal quality.
*/
@Composable
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 {
snr > SNR_GOOD_THRESHOLD && rssi > RSSI_GOOD_THRESHOLD -> Quality.GOOD
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))
/* SNR and RSSI */
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Snr(meshPacket.rxSnr)
Rssi(meshPacket.rxRssi)
}
SnrAndRssi(meshPacket.rxSnr, meshPacket.rxRssi)
}
}