feat(lora): default US region to LongTurbo preset (#6009)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
James Rich
2026-06-30 21:27:46 -05:00
committed by GitHub
parent 77b4ba19de
commit 1ad756205f
4 changed files with 43 additions and 2 deletions

View File

@@ -73,3 +73,16 @@ fun LoRaRegionPresetMap?.repairPresetFor(region: RegionCode, current: ModemPrese
else -> constraint.presets.firstOrNull() ?: current
}
}
/**
* The app's built-in default modem presets for regions whose default differs from the global [ChannelOption.DEFAULT].
* Mirrors the per-region defaults newer firmware advertises in [LoRaRegionPresetMap], so the default channel and a
* fresh setup over old firmware (which sends no map) land on the same preset a new node would.
*/
private val REGION_DEFAULT_PRESETS = mapOf(RegionCode.US to ModemPreset.LONG_TURBO)
/**
* The app's preferred default preset for [region], or `null` when it has no region-specific default and the caller
* should fall back to [ChannelOption.DEFAULT] / the current preset.
*/
fun defaultPresetFor(region: RegionCode): ModemPreset? = REGION_DEFAULT_PRESETS[region]

View File

@@ -138,4 +138,10 @@ class LoRaRegionPresetsTest {
)
assertEquals(ModemPreset.MEDIUM_FAST, oddMap.repairPresetFor(RegionCode.US, ModemPreset.SHORT_FAST))
}
@Test
fun `region default preset is LongTurbo for US and absent for other regions`() {
assertEquals(ModemPreset.LONG_TURBO, defaultPresetFor(RegionCode.US))
assertNull(defaultPresetFor(RegionCode.EU_868))
}
}

View File

@@ -63,6 +63,7 @@ import org.jetbrains.compose.resources.stringResource
import org.koin.compose.viewmodel.koinViewModel
import org.meshtastic.core.model.Channel
import org.meshtastic.core.model.ConnectionState
import org.meshtastic.core.model.defaultPresetFor
import org.meshtastic.core.model.util.getChannelUrl
import org.meshtastic.core.navigation.Route
import org.meshtastic.core.resources.Res
@@ -198,8 +199,15 @@ fun ChannelScreen(
messageRes = Res.string.are_you_sure_change_default,
onConfirm = {
Logger.d { "Switching back to default channel" }
// The default channel takes the region's preferred preset (e.g. US -> LongTurbo) so its derived name,
// hash, and frequency match what a freshly-set-up node in that region would use.
val preset = defaultPresetFor(viewModel.region) ?: Channel.default.loraConfig.modem_preset
val lora =
(Channel.default.loraConfig).copy(region = viewModel.region, tx_enabled = viewModel.txEnabled)
Channel.default.loraConfig.copy(
region = viewModel.region,
modem_preset = preset,
tx_enabled = viewModel.txEnabled,
)
installSettings(Channel.default.settings, lora)
showResetDialog = false
},

View File

@@ -33,6 +33,7 @@ import org.meshtastic.core.model.ChannelOption
import org.meshtastic.core.model.RegionInfo
import org.meshtastic.core.model.RegionPresetConstraint
import org.meshtastic.core.model.constraintFor
import org.meshtastic.core.model.defaultPresetFor
import org.meshtastic.core.model.numChannels
import org.meshtastic.core.model.repairPresetFor
import org.meshtastic.core.resources.Res
@@ -70,6 +71,7 @@ import org.meshtastic.feature.settings.radio.RadioConfigViewModel
import org.meshtastic.feature.settings.util.hopLimits
import org.meshtastic.proto.Config
import org.meshtastic.proto.Config.LoRaConfig.ModemPreset
import org.meshtastic.proto.Config.LoRaConfig.RegionCode
private val SPREAD_FACTOR_RANGE = 7..12
private val CODING_RATE_RANGE = 5..8
@@ -159,10 +161,22 @@ fun LoRaConfigScreen(viewModel: RadioConfigViewModel, onBack: () -> Unit) {
items = RegionInfo.entries.map { it.regionCode to it.description },
selectedItem = formState.value.region,
onItemSelected = { region ->
val freshSetup = formState.value.region == RegionCode.UNSET
// When the region changes, snap the preset to the region's default if the current one is
// no longer legal there (R7); a no-op when the region is unconstrained.
val repaired = regionPresetMap.repairPresetFor(region, formState.value.modem_preset)
formState.value = formState.value.copy(region = region, modem_preset = repaired)
// At fresh setup (region was UNSET) adopt the region's advertised default rather than keeping
// a merely-legal preset: the firmware map's default when present, else the app's built-in
// default (e.g. US -> LongTurbo on pre-2.8 firmware that sends no map).
val preset =
if (freshSetup) {
regionPresetMap.constraintFor(region)?.defaultPreset
?: defaultPresetFor(region)
?: repaired
} else {
repaired
}
formState.value = formState.value.copy(region = region, modem_preset = preset)
},
)
HorizontalDivider()