From e9f63b4e80844b410988f1aa0f5acbcd899fc1b8 Mon Sep 17 00:00:00 2001 From: andrekir Date: Wed, 10 Jan 2024 05:33:19 -0300 Subject: [PATCH] refactor: add channel hash function --- .../java/com/geeksville/mesh/ChannelTest.kt | 15 ++++ .../java/com/geeksville/mesh/model/Channel.kt | 19 +---- .../geeksville/mesh/model/ChannelOption.kt | 77 +++++++++++-------- .../components/config/LoRaConfigItemList.kt | 4 +- 4 files changed, 67 insertions(+), 48 deletions(-) diff --git a/app/src/androidTest/java/com/geeksville/mesh/ChannelTest.kt b/app/src/androidTest/java/com/geeksville/mesh/ChannelTest.kt index f80113594..d0f814eb0 100644 --- a/app/src/androidTest/java/com/geeksville/mesh/ChannelTest.kt +++ b/app/src/androidTest/java/com/geeksville/mesh/ChannelTest.kt @@ -4,6 +4,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import com.geeksville.mesh.model.Channel import com.geeksville.mesh.model.URL_PREFIX import com.geeksville.mesh.model.getChannelUrl +import com.geeksville.mesh.model.numChannels import com.geeksville.mesh.model.toChannelSet import org.junit.Assert import org.junit.Test @@ -23,6 +24,20 @@ class ChannelTest { Assert.assertEquals(channelUrl.toChannelSet(), ch) } + @Test + fun channelHashGood() { + val ch = Channel.default + + Assert.assertEquals(8, ch.hash) + } + + @Test + fun numChannelsGood() { + val ch = Channel.default + + Assert.assertEquals(104, ch.loraConfig.numChannels) + } + @Test fun channelNumGood() { val ch = Channel.default diff --git a/app/src/main/java/com/geeksville/mesh/model/Channel.kt b/app/src/main/java/com/geeksville/mesh/model/Channel.kt index a41d70cfb..0ee66a4b5 100644 --- a/app/src/main/java/com/geeksville/mesh/model/Channel.kt +++ b/app/src/main/java/com/geeksville/mesh/model/Channel.kt @@ -89,24 +89,13 @@ data class Channel( } /** - * hash a string into an integer using the djb2 algorithm by Dan Bernstein - * http://www.cse.yorku.ca/~oz/hash.html + * Given a channel name and psk, return the (0 to 255) hash for that channel */ - val hash: UInt // using UInt instead of Long to match RadioInterface.cpp results - get() { - var hash: UInt = 5381u - for (c in name) { - hash += (hash shl 5) + c.code.toUInt() - print("$c ${c.code} $hash ") - } - return hash - } + val hash: Int get() = xorHash(name.toByteArray()) xor xorHash(psk.toByteArray()) - val channelNum: Int - get() = if (loraConfig.channelNum != 0) loraConfig.channelNum - else (hash % RegionInfo.numChannels(loraConfig).toUInt()).toInt() + 1 + val channelNum: Int get() = loraConfig.channelNum(name) - val radioFreq: Float get() = RegionInfo.radioFreq(loraConfig, channelNum) + val radioFreq: Float get() = loraConfig.radioFreq(channelNum) override fun equals(other: Any?): Boolean = (other is Channel) && psk.toByteArray() contentEquals other.psk.toByteArray() diff --git a/app/src/main/java/com/geeksville/mesh/model/ChannelOption.kt b/app/src/main/java/com/geeksville/mesh/model/ChannelOption.kt index 5ba50204f..fd4f09362 100644 --- a/app/src/main/java/com/geeksville/mesh/model/ChannelOption.kt +++ b/app/src/main/java/com/geeksville/mesh/model/ChannelOption.kt @@ -5,9 +5,29 @@ import com.geeksville.mesh.ConfigProtos.Config.LoRaConfig.ModemPreset import com.geeksville.mesh.ConfigProtos.Config.LoRaConfig.RegionCode import com.geeksville.mesh.R -fun LoRaConfig.bandwidth() = if (usePreset) { +/** + * hash a string into an integer using the djb2 algorithm by Dan Bernstein + * http://www.cse.yorku.ca/~oz/hash.html + */ +private fun hash(name: String): UInt { // using UInt instead of Long to match RadioInterface.cpp results + var hash: UInt = 5381u + for (c in name) { + hash += (hash shl 5) + c.code.toUInt() + } + return hash +} + +private val ModemPreset.bandwidth: Float + get() { + for (option in ChannelOption.entries) { + if (option.modemPreset == this) return option.bandwidth + } + return 0f + } + +private fun LoRaConfig.bandwidth() = if (usePreset) { val wideLora = region == RegionCode.LORA_24 - ChannelOption.bandwidth(modemPreset) * if (wideLora) 3.25f else 1f + modemPreset.bandwidth * if (wideLora) 3.25f else 1f } else when (bandwidth) { 31 -> .03125f 62 -> .0625f @@ -18,6 +38,28 @@ fun LoRaConfig.bandwidth() = if (usePreset) { else -> bandwidth / 1000f } +val LoRaConfig.numChannels: Int get() { + for (option in RegionInfo.entries) { + if (option.regionCode == region) + return ((option.freqEnd - option.freqStart) / bandwidth()).toInt() + } + return 0 +} + +internal fun LoRaConfig.channelNum(primaryName: String): Int { + return if (channelNum != 0) channelNum + else (hash(primaryName) % numChannels.toUInt()).toInt() + 1 +} + +internal fun LoRaConfig.radioFreq(channelNum: Int): Float { + if (overrideFrequency != 0f) return overrideFrequency + frequencyOffset + for (option in RegionInfo.entries) { + if (option.regionCode == region) + return (option.freqStart + bandwidth() / 2) + (channelNum - 1) * bandwidth() + } + return 0f +} + enum class RegionInfo( val regionCode: RegionCode, val freqStart: Float, @@ -38,26 +80,8 @@ enum class RegionInfo( UA_433(RegionCode.UA_433, 433.0f, 434.7f), UA_868(RegionCode.UA_868, 868.0f, 868.6f), LORA_24(RegionCode.LORA_24, 2400.0f, 2483.5f), - UNSET(RegionCode.UNSET, 902.0f, 928.0f); - - companion object { - fun numChannels(loraConfig: LoRaConfig): Int { - for (option in values()) { - if (option.regionCode == loraConfig.region) - return ((option.freqEnd - option.freqStart) / loraConfig.bandwidth()).toInt() - } - return 0 - } - - fun radioFreq(loraConfig: LoRaConfig, channelNum: Int): Float = with(loraConfig) { - if (overrideFrequency != 0f) return overrideFrequency + frequencyOffset - for (option in values()) { - if (option.regionCode == region) - return (option.freqStart + bandwidth() / 2) + (channelNum - 1) * bandwidth() - } - return 0f - } - } + UNSET(RegionCode.UNSET, 902.0f, 928.0f), + ; } enum class ChannelOption( @@ -74,13 +98,4 @@ enum class ChannelOption( LONG_SLOW(ModemPreset.LONG_SLOW, R.string.modem_config_slow_long, .125f), VERY_LONG_SLOW(ModemPreset.VERY_LONG_SLOW, R.string.modem_config_very_long, .0625f), ; - - companion object { - fun bandwidth(modemPreset: ModemPreset?): Float { - for (option in values()) { - if (option.modemPreset == modemPreset) return option.bandwidth - } - return 0f - } - } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/config/LoRaConfigItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/components/config/LoRaConfigItemList.kt index 889ef2dc0..f690c611f 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/config/LoRaConfigItemList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/config/LoRaConfigItemList.kt @@ -16,7 +16,7 @@ import com.geeksville.mesh.ChannelProtos.ChannelSettings import com.geeksville.mesh.ConfigProtos.Config.LoRaConfig import com.geeksville.mesh.copy import com.geeksville.mesh.model.Channel -import com.geeksville.mesh.model.RegionInfo +import com.geeksville.mesh.model.numChannels import com.geeksville.mesh.ui.components.DropDownPreference import com.geeksville.mesh.ui.components.EditListPreference import com.geeksville.mesh.ui.components.EditTextPreference @@ -136,7 +136,7 @@ fun LoRaConfigItemList( keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }), onFocusChanged = { isFocused = it.isFocused }, onValueChanged = { - if (it <= RegionInfo.numChannels(loraInput)) // max numChannels + if (it <= loraInput.numChannels) // total num of LoRa channels loraInput = loraInput.copy { channelNum = it } }) }