fix(nav,radar,wavelog): 4.5.2 third overwrite - log page entry visible, freq format 3 decimals, direct freq extraction
User-test fixes (3rd overwrite, version stays 4.5.2/452):
1. Log page was invisible everywhere outside the radar tab:
- allNavItems in MainScreen.kt was missing Screen.WavelogLog ->
not in bottom nav, not in More menu
- SettingsScreen UI-order lists were missing the WavelogLog row ->
not in page order settings either
Now the Log page appears in the More menu (default sub menu tail,
migration appends it) AND in UI settings page-order lists.
2. Frequency display: formatFrequency was MHz.kHz.Hz (145.900.000);
now MHz.kHz (145.900) per request. Applies to transceiver panel
and Log page alike (shared formatter).
3. Log tab frequency: now extracts the exact numbers shown in the
transceiver panel (txBaseFrequencyHz for TX, uplinkLow/uplinkHigh
range for linear) - no re-computation, no extra decimals. Upload
uses the same tuned frequency (Hz precision kept internally).
4. Log page (More menu) table: date+time column (MM-dd HH:mm),
row separators (table lines), uploaded checkmark kept.
Verified: check_strings OK (8 files); :app:compileDebugKotlin
BUILD SUCCESSFUL (includes upstream merge 1a552417).
This commit is contained in:
1 parent
1a55241791
commit
ab1a0c0891
5 files changed
+53
-61
No files matched your search
@@ -148,7 +148,7 @@ fun MainScreen(navigateToRadar: () -> Unit = {}) {
|
|||||||
val trackingState by container.radioTrackingService.state.collectAsStateWithLifecycle()
|
val trackingState by container.radioTrackingService.state.collectAsStateWithLifecycle()
|
||||||
val otherSettings by container.settingsRepo.otherSettings.collectAsStateWithLifecycle()
|
val otherSettings by container.settingsRepo.otherSettings.collectAsStateWithLifecycle()
|
||||||
// UI 设置: 按 screenOrder 排序(空 = 默认顺序), 再按 hiddenScreens 过滤(设置页固定保留)
|
// UI 设置: 按 screenOrder 排序(空 = 默认顺序), 再按 hiddenScreens 过滤(设置页固定保留)
|
||||||
val allNavItems = listOf(Screen.Satellites, Screen.Passes, Screen.Radar, Screen.Mutual, Screen.Roaming, Screen.CwDecode, Screen.Map, Screen.Settings)
|
val allNavItems = listOf(Screen.Satellites, Screen.Passes, Screen.Radar, Screen.Mutual, Screen.Roaming, Screen.CwDecode, Screen.WavelogLog, Screen.Map, Screen.Settings)
|
||||||
.sortedBy { screen ->
|
.sortedBy { screen ->
|
||||||
// 未知(新页面如 CwDecode 不在旧持久化顺序里): 用默认顺序位置(漫游↔地图), 再兜底最后
|
// 未知(新页面如 CwDecode 不在旧持久化顺序里): 用默认顺序位置(漫游↔地图), 再兜底最后
|
||||||
val idx = otherSettings.screenOrder.indexOf(screen.screenId)
|
val idx = otherSettings.screenOrder.indexOf(screen.screenId)
|
||||||
|
|||||||
+1
-2
@@ -518,6 +518,5 @@ fun formatFrequency(frequencyHz: Long): String {
|
|||||||
if (frequencyHz <= 0) return "---"
|
if (frequencyHz <= 0) return "---"
|
||||||
val mhz = frequencyHz / 1_000_000
|
val mhz = frequencyHz / 1_000_000
|
||||||
val khz = (frequencyHz % 1_000_000) / 1_000
|
val khz = (frequencyHz % 1_000_000) / 1_000
|
||||||
val hz = frequencyHz % 1_000
|
return String.format(Locale.ENGLISH, "%d.%03d", mhz, khz)
|
||||||
return String.format(Locale.ENGLISH, "%d.%03d.%03d", mhz, khz, hz)
|
|
||||||
}
|
}
|
||||||
@@ -229,26 +229,14 @@ private fun ExpandedLogInput(
|
|||||||
var callsign by remember { mutableStateOf("") }
|
var callsign by remember { mutableStateOf("") }
|
||||||
var mode by remember { mutableStateOf(radio.uplinkMode ?: "FM") }
|
var mode by remember { mutableStateOf(radio.uplinkMode ?: "FM") }
|
||||||
|
|
||||||
// 回车那一刻的频率(与雷达页 Doppler 面板一致): 优先用当前调谐 TX 频率
|
|
||||||
fun currentFreqs(): Pair<Long, Long> {
|
|
||||||
val uplinkBase = radio.uplinkLow ?: radio.downlinkLow ?: 0L
|
|
||||||
val downlinkBase = radio.downlinkLow ?: radio.uplinkLow ?: 0L
|
|
||||||
// 调谐基准(用户在当前转发器上调的)优先; 否则转发器标称下限
|
|
||||||
val tx = txBaseFrequencyHz ?: (if (orbitalPos != null) orbitalPos.getUplinkFreq(uplinkBase) else uplinkBase)
|
|
||||||
val rx = if (orbitalPos != null) {
|
|
||||||
// 线性转发器: 按映射算下行; FM: 标称下行加多普勒
|
|
||||||
DopplerFrequencyCalculator.computeDownlinkFromUplink(tx, radio, orbitalPos)
|
|
||||||
?: orbitalPos.getDownlinkFreq(downlinkBase)
|
|
||||||
} else downlinkBase
|
|
||||||
return tx to rx
|
|
||||||
}
|
|
||||||
|
|
||||||
val savedMsg = stringResource(id = R.string.wavelog_saved)
|
val savedMsg = stringResource(id = R.string.wavelog_saved)
|
||||||
|
|
||||||
fun submit() {
|
fun submit() {
|
||||||
val call = callsign.trim().uppercase()
|
val call = callsign.trim().uppercase()
|
||||||
if (call.length < 3) return
|
if (call.length < 3) return
|
||||||
val (tx, rx) = currentFreqs()
|
// 频率直接取转发器栏的数字(不再多普勒再计算): TX = 当前调谐, RX = 下行基准
|
||||||
|
val tx = txBaseFrequencyHz ?: (radio.uplinkLow ?: radio.downlinkLow ?: 0L)
|
||||||
|
val rx = radio.downlinkLow ?: radio.uplinkLow ?: 0L
|
||||||
queue.add(
|
queue.add(
|
||||||
WavelogQso(
|
WavelogQso(
|
||||||
id = UUID.randomUUID().toString(),
|
id = UUID.randomUUID().toString(),
|
||||||
@@ -265,35 +253,31 @@ private fun ExpandedLogInput(
|
|||||||
showToast(savedMsg)
|
showToast(savedMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
val (txHz, rxHz) = currentFreqs()
|
// 频率显示与转发器栏完全一致(不再另算): TX = 当前调谐(txBaseFrequencyHz), 线性附加频段范围
|
||||||
// 线性转发器: 显示频段区间(多普勒修正); FM: 单频
|
val txForDisplay = txBaseFrequencyHz ?: (radio.uplinkLow ?: radio.downlinkLow ?: 0L)
|
||||||
val isLinear = DopplerFrequencyCalculator.isLinearTransponder(radio)
|
val upLow = radio.uplinkLow
|
||||||
val uplinkLow = radio.uplinkLow
|
val upHigh = radio.uplinkHigh
|
||||||
val uplinkHigh = radio.uplinkHigh
|
val isLinearRange = upLow != null && upHigh != null && upLow != upHigh
|
||||||
val downlinkLow = radio.downlinkLow
|
|
||||||
val downlinkHigh = radio.downlinkHigh
|
|
||||||
val txRange = if (isLinear && uplinkLow != null && uplinkHigh != null && orbitalPos != null) {
|
|
||||||
val lo = orbitalPos.getUplinkFreq(uplinkLow)
|
|
||||||
val hi = orbitalPos.getUplinkFreq(uplinkHigh)
|
|
||||||
"${formatFrequency(minOf(lo, hi))}–${formatFrequency(maxOf(lo, hi))}"
|
|
||||||
} else null
|
|
||||||
val rxRange = if (isLinear && downlinkLow != null && downlinkHigh != null && orbitalPos != null) {
|
|
||||||
val lo = orbitalPos.getDownlinkFreq(downlinkLow)
|
|
||||||
val hi = orbitalPos.getDownlinkFreq(downlinkHigh)
|
|
||||||
"${formatFrequency(minOf(lo, hi))}–${formatFrequency(maxOf(lo, hi))}"
|
|
||||||
} else null
|
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
Text(
|
Text(
|
||||||
text = "TX ${txRange ?: formatFrequency(txHz)}",
|
text = "TX ",
|
||||||
fontSize = 12.sp,
|
fontSize = 12.sp,
|
||||||
color = MaterialTheme.colorScheme.primary
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = "RX ${rxRange ?: formatFrequency(rxHz)}",
|
text = "${formatFrequency(txForDisplay)} MHz",
|
||||||
fontSize = 12.sp,
|
fontSize = 14.sp,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
color = MaterialTheme.colorScheme.primary
|
color = MaterialTheme.colorScheme.primary
|
||||||
)
|
)
|
||||||
|
if (isLinearRange) {
|
||||||
|
Text(
|
||||||
|
text = " (${formatFrequency(upLow)} – ${formatFrequency(upHigh)})",
|
||||||
|
fontSize = 11.sp,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = callsign,
|
value = callsign,
|
||||||
|
|||||||
+29
-21
@@ -21,6 +21,7 @@ import androidx.compose.foundation.layout.width
|
|||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -81,34 +82,41 @@ fun WavelogLogScreen(
|
|||||||
modifier = Modifier.padding(vertical = 12.dp)
|
modifier = Modifier.padding(vertical = 12.dp)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
entries.forEach { entry ->
|
entries.forEachIndexed { index, entry ->
|
||||||
SwipeDeleteRow(
|
SwipeDeleteRow(
|
||||||
onDelete = {
|
onDelete = {
|
||||||
queue.remove(entry.id)
|
queue.remove(entry.id)
|
||||||
refreshTick++
|
refreshTick++
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Row(
|
Column(modifier = Modifier.fillMaxWidth()) {
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
Row(
|
||||||
modifier = Modifier
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
.fillMaxWidth()
|
modifier = Modifier
|
||||||
.background(MaterialTheme.colorScheme.surfaceContainer)
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 10.dp, vertical = 8.dp)
|
.background(MaterialTheme.colorScheme.surfaceContainer)
|
||||||
) {
|
.padding(horizontal = 10.dp, vertical = 8.dp)
|
||||||
Cell(formatLocalTime(entry.timeUtcMs), 52.dp)
|
) {
|
||||||
Cell(formatFrequency(entry.freqTxHz), 70.dp)
|
Cell(formatLocalTime(entry.timeUtcMs), 74.dp)
|
||||||
Cell(entry.satName, 0.dp, weight = 1.2f)
|
Cell(formatFrequency(entry.freqTxHz), 62.dp)
|
||||||
Cell(entry.call, 0.dp, weight = 1f)
|
Cell(entry.satName, 0.dp, weight = 1.2f)
|
||||||
Box(modifier = Modifier.width(44.dp)) {
|
Cell(entry.call, 0.dp, weight = 1f)
|
||||||
if (entry.uploaded) {
|
Box(modifier = Modifier.width(44.dp)) {
|
||||||
Text(
|
if (entry.uploaded) {
|
||||||
text = "✓",
|
Text(
|
||||||
fontSize = 15.sp,
|
text = "✓",
|
||||||
fontWeight = FontWeight.Bold,
|
fontSize = 15.sp,
|
||||||
color = CheckGreen
|
fontWeight = FontWeight.Bold,
|
||||||
)
|
color = CheckGreen
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 表格分隔线
|
||||||
|
HorizontalDivider(
|
||||||
|
thickness = 1.dp,
|
||||||
|
color = MaterialTheme.colorScheme.outline.copy(alpha = 0.25f)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,5 +151,5 @@ private fun RowScope.Cell(text: String, width: androidx.compose.ui.unit.Dp, weig
|
|||||||
private fun formatLocalTime(utcMs: Long): String {
|
private fun formatLocalTime(utcMs: Long): String {
|
||||||
val cal = Calendar.getInstance(TimeZone.getDefault())
|
val cal = Calendar.getInstance(TimeZone.getDefault())
|
||||||
cal.timeInMillis = utcMs
|
cal.timeInMillis = utcMs
|
||||||
return "%02d:%02d".format(cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE))
|
return "%02d-%02d %02d:%02d".format(cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH), cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE))
|
||||||
}
|
}
|
||||||
+1
@@ -704,6 +704,7 @@ private fun UiSettingsCard(
|
|||||||
R.string.nav_mutual to "Mutual",
|
R.string.nav_mutual to "Mutual",
|
||||||
R.string.nav_roaming to "Roaming",
|
R.string.nav_roaming to "Roaming",
|
||||||
R.string.nav_cw to "CwDecode",
|
R.string.nav_cw to "CwDecode",
|
||||||
|
R.string.nav_log to "WavelogLog",
|
||||||
R.string.nav_map to "Map",
|
R.string.nav_map to "Map",
|
||||||
R.string.nav_prefs to "Settings"
|
R.string.nav_prefs to "Settings"
|
||||||
) // name 必须与 Screen.screenId 一致 (R8 安全)
|
) // name 必须与 Screen.screenId 一致 (R8 安全)
|
||||||
|
|||||||
Reference in new issue
Block a user