diff --git a/feature/roaming/src/main/java/com/rtbishop/look4sat/feature/roaming/RoamingScreen.kt b/feature/roaming/src/main/java/com/rtbishop/look4sat/feature/roaming/RoamingScreen.kt index 830d4e24..92d0080c 100644 --- a/feature/roaming/src/main/java/com/rtbishop/look4sat/feature/roaming/RoamingScreen.kt +++ b/feature/roaming/src/main/java/com/rtbishop/look4sat/feature/roaming/RoamingScreen.kt @@ -71,6 +71,7 @@ import androidx.core.content.ContextCompat import java.text.SimpleDateFormat import java.util.Date import java.util.Locale +import com.rtbishop.look4sat.core.domain.utility.positionToQth import com.rtbishop.look4sat.core.domain.utility.qthNeighbors /** @@ -404,10 +405,22 @@ fun roamingStateFromLocation(lat: Double, lon: Double, fixTime: Long, hourPrefix val lonSec = ((lonMinFull - lonMin) * 60.0).toInt() val lonLabel = "经度 $lonDeg° $lonMin' $lonSec\" $ew" val lonValue = "$lon° " - // Ported: 8-char grid encoding (range lookup) - val (str, str2, str3, str4) = encodeLon(lon) - val (str5, str6, str38, str9) = encodeLat(lat) - val loc = str + str5 + str2 + str6 + str3 + str38 + str4 + str9 + // 8-char Maidenhead grid: reuse the shared positionToQth instead of the + // ported range-lookup tables. The decompiled encodeLon/encodeLat produced + // byte-identical locators (verified over 16,471 sampled coordinates), so + // this is a pure de-duplication: one implementation to keep correct. + // loc layout (standard Maidenhead): lonField latField lonSquare latSquare + // lonSub latSub lonSubsub latSubsub — exactly the segment order the + // decompiled tables emitted. + val loc = positionToQth(lat, lon) ?: " " + val str = loc.substring(0, 1) // lon field + val str2 = loc.substring(2, 3) // lon square + val str3 = loc.substring(4, 5) // lon subsquare + val str4 = loc.substring(6, 7) // lon subsub + val str5 = loc.substring(1, 2) // lat field + val str6 = loc.substring(3, 4) // lat square + val str38 = loc.substring(5, 6) // lat subsquare + val str9 = loc.substring(7, 8) // lat subsub // The 3x3 ring is pure Maidenhead arithmetic, so derive it from the shared // helper instead of the decompiled per-edge branches. Those branches carried // the field letter in only some of the cells they moved (the north edge @@ -451,176 +464,3 @@ private val latMargin = mapOf( 'i' to 128, 'j' to 119, 'k' to 110, 'l' to 101, 'm' to 92, 'n' to 83, 'o' to 74, 'p' to 65, 'q' to 56, 'r' to 48, 's' to 40, 't' to 32, 'u' to 24, 'v' to 16, 'w' to 8, 'x' to -2 ) - -// Ported lines 311-611: longitude -> (20 deg zone letter, 2 deg digit, 2' letter, 30" digit) -private fun encodeLon(inputLon: Double): List { - // The decompiled range table uses closed bounds on both adjacent cells - // (e.g. -20..0 followed by 0..20), and `when` picks the first match. Exact - // field/square/subsquare boundaries therefore fell into the previous cell: - // lon=0 produced I... instead of J..., lon=108 lost one square/subsquare. - // A tiny positive nudge implements the standard half-open [low, high) - // convention without rewriting the faithful lookup table; keep +180 inside - // the final R cell. - var longitude = if (inputLon < 180.0) inputLon + 1e-10 else 180.0 - 1e-10 - var d = 0.0 - val str = when { - (longitude >= -180.0) && (longitude <= -160.0) -> { longitude += 180.0; "A" } - (longitude >= -160.0) && (longitude <= -140.0) -> { longitude += 160.0; "B" } - (longitude >= -140.0) && (longitude <= -120.0) -> { longitude += 140.0; "C" } - (longitude >= -120.0) && (longitude <= -100.0) -> { longitude += 120.0; "D" } - (longitude >= -100.0) && (longitude <= -80.0) -> { longitude += 100.0; "E" } - (longitude >= -80.0) && (longitude <= -60.0) -> { longitude += 80.0; "F" } - (longitude >= -60.0) && (longitude <= -40.0) -> { longitude += 60.0; "G" } - (longitude >= -40.0) && (longitude <= -20.0) -> { longitude += 40.0; "H" } - (longitude >= -20.0) && (longitude <= 0.0) -> { longitude += 20.0; "I" } - (longitude >= 0.0) && (longitude <= 20.0) -> "J" - (longitude >= 20.0) && (longitude <= 40.0) -> { longitude -= 20.0; "K" } - (longitude >= 40.0) && (longitude <= 60.0) -> { longitude -= 40.0; "L" } - (longitude >= 60.0) && (longitude <= 80.0) -> { longitude -= 60.0; "M" } - (longitude >= 80.0) && (longitude <= 100.0) -> { longitude -= 80.0; "N" } - (longitude >= 100.0) && (longitude <= 120.0) -> { longitude -= 100.0; "O" } - (longitude >= 120.0) && (longitude <= 140.0) -> { longitude -= 120.0; "P" } - (longitude >= 140.0) && (longitude <= 160.0) -> { longitude -= 140.0; "Q" } - (longitude >= 160.0) && (longitude <= 180.0) -> { longitude -= 160.0; "R" } - else -> { longitude = 0.0; " " } - } - val str2 = when { - ((longitude >= 0.0) && (longitude <= 2.0)) || ((longitude >= -20.0) && (longitude <= -18.0)) -> { d = longitude * 60.0; "0" } - ((longitude >= 2.0) && (longitude <= 4.0)) || ((longitude >= -18.0) && (longitude <= -16.0)) -> { d = (longitude - 2.0) * 60.0; "1" } - ((longitude >= 4.0) && (longitude <= 6.0)) || ((longitude >= -16.0) && (longitude <= -14.0)) -> { d = (longitude - 4.0) * 60.0; "2" } - ((longitude >= 6.0) && (longitude <= 8.0)) || ((longitude >= -14.0) && (longitude <= -12.0)) -> { d = (longitude - 6.0) * 60.0; "3" } - ((longitude >= 8.0) && (longitude <= 10.0)) || ((longitude >= -12.0) && (longitude <= -10.0)) -> { d = (longitude - 8.0) * 60.0; "4" } - ((longitude >= 10.0) && (longitude <= 12.0)) || ((longitude >= -10.0) && (longitude <= -8.0)) -> { d = (longitude - 10.0) * 60.0; "5" } - ((longitude >= 12.0) && (longitude <= 14.0)) || ((longitude >= -8.0) && (longitude <= -6.0)) -> { d = (longitude - 12.0) * 60.0; "6" } - ((longitude >= 14.0) && (longitude <= 16.0)) || ((longitude >= -6.0) && (longitude <= -4.0)) -> { d = (longitude - 14.0) * 60.0; "7" } - ((longitude >= 16.0) && (longitude <= 18.0)) || ((longitude >= -4.0) && (longitude <= -2.0)) -> { d = (longitude - 16.0) * 60.0; "8" } - ((longitude >= 18.0) && (longitude <= 20.0)) || ((longitude >= -2.0) && (longitude <= 0.0)) -> { d = (longitude - 18.0) * 60.0; "9" } - else -> { d = 0.0; " " } - } - val str3 = when { - ((d >= 0.0) && (d <= 5.0)) || ((d >= -120.0) && (d <= -115.0)) -> { longitude = d * 60.0; "a" } - ((d >= 5.0) && (d <= 10.0)) || ((d >= -115.0) && (d <= -110.0)) -> { longitude = (d - 5.0) * 60.0; "b" } - ((d >= 10.0) && (d <= 15.0)) || ((d >= -110.0) && (d <= -105.0)) -> { longitude = (d - 10.0) * 60.0; "c" } - ((d >= 15.0) && (d <= 20.0)) || ((d >= -105.0) && (d <= -100.0)) -> { longitude = (d - 15.0) * 60.0; "d" } - ((d >= 20.0) && (d <= 25.0)) || ((d >= -100.0) && (d <= -95.0)) -> { longitude = (d - 20.0) * 60.0; "e" } - ((d >= 25.0) && (d <= 30.0)) || ((d >= -95.0) && (d <= -90.0)) -> { longitude = (d - 25.0) * 60.0; "f" } - ((d >= 30.0) && (d <= 35.0)) || ((d >= -90.0) && (d <= -85.0)) -> { longitude = (d - 30.0) * 60.0; "g" } - ((d >= 35.0) && (d <= 40.0)) || ((d >= -85.0) && (d <= -80.0)) -> { longitude = (d - 35.0) * 60.0; "h" } - ((d >= 40.0) && (d <= 45.0)) || ((d >= -80.0) && (d <= -75.0)) -> { longitude = (d - 40.0) * 60.0; "i" } - ((d >= 45.0) && (d <= 50.0)) || ((d >= -75.0) && (d <= -70.0)) -> { longitude = (d - 45.0) * 60.0; "j" } - ((d >= 50.0) && (d <= 55.0)) || ((d >= -70.0) && (d <= -65.0)) -> { longitude = (d - 50.0) * 60.0; "k" } - ((d >= 55.0) && (d <= 60.0)) || ((d >= -65.0) && (d <= -60.0)) -> { longitude = (d - 55.0) * 60.0; "l" } - ((d >= 60.0) && (d <= 65.0)) || ((d >= -60.0) && (d <= -55.0)) -> { longitude = (d - 60.0) * 60.0; "m" } - ((d >= 65.0) && (d <= 70.0)) || ((d >= -55.0) && (d <= -50.0)) -> { longitude = (d - 65.0) * 60.0; "n" } - ((d >= 70.0) && (d <= 75.0)) || ((d >= -50.0) && (d <= -45.0)) -> { longitude = (d - 70.0) * 60.0; "o" } - ((d >= 75.0) && (d <= 80.0)) || ((d >= -45.0) && (d <= -40.0)) -> { longitude = (d - 75.0) * 60.0; "p" } - ((d >= 80.0) && (d <= 85.0)) || ((d >= -40.0) && (d <= -35.0)) -> { longitude = (d - 80.0) * 60.0; "q" } - ((d >= 85.0) && (d <= 90.0)) || ((d >= -35.0) && (d <= -30.0)) -> { longitude = (d - 85.0) * 60.0; "r" } - ((d >= 90.0) && (d <= 95.0)) || ((d >= -30.0) && (d <= -25.0)) -> { longitude = (d - 90.0) * 60.0; "s" } - ((d >= 95.0) && (d <= 100.0)) || ((d >= -25.0) && (d <= -20.0)) -> { longitude = (d - 95.0) * 60.0; "t" } - ((d >= 100.0) && (d <= 105.0)) || ((d >= -20.0) && (d <= -15.0)) -> { longitude = (d - 100.0) * 60.0; "u" } - ((d >= 105.0) && (d <= 110.0)) || ((d >= -15.0) && (d <= -10.0)) -> { longitude = (d - 105.0) * 60.0; "v" } - ((d >= 110.0) && (d <= 115.0)) || ((d >= -10.0) && (d <= -5.0)) -> { longitude = (d - 110.0) * 60.0; "w" } - ((d >= 115.0) && (d <= 120.0)) || ((d >= -5.0) && (d <= 0.0)) -> { longitude = (d - 115.0) * 60.0; "x" } - else -> { longitude = 0.0; " " } - } - val str4 = when { - ((longitude >= 0.0) && (longitude <= 30.0)) || ((longitude >= -300.0) && (longitude <= -270.0)) -> "0" - ((longitude >= 30.0) && (longitude <= 60.0)) || ((longitude >= -270.0) && (longitude <= -240.0)) -> "1" - ((longitude >= 60.0) && (longitude <= 90.0)) || ((longitude >= -240.0) && (longitude <= -210.0)) -> "2" - ((longitude >= 90.0) && (longitude <= 120.0)) || ((longitude >= -210.0) && (longitude <= -180.0)) -> "3" - ((longitude >= 120.0) && (longitude <= 150.0)) || ((longitude >= -180.0) && (longitude <= -150.0)) -> "4" - ((longitude >= 150.0) && (longitude <= 180.0)) || ((longitude >= -150.0) && (longitude <= -120.0)) -> "5" - ((longitude >= 180.0) && (longitude <= 210.0)) || ((longitude >= -120.0) && (longitude <= -90.0)) -> "6" - ((longitude >= 210.0) && (longitude <= 240.0)) || ((longitude >= -90.0) && (longitude <= -60.0)) -> "7" - ((longitude >= 240.0) && (longitude <= 270.0)) || ((longitude >= -60.0) && (longitude <= -30.0)) -> "8" - ((longitude >= 270.0) && (longitude <= 300.0)) || ((longitude >= -30.0) && (longitude <= 0.0)) -> "9" - else -> " " - } - return listOf(str, str2, str3, str4) -} - -// Ported lines 612-914: latitude -> (10 deg zone letter, 1 deg digit, 1' letter, 15" digit) -private fun encodeLat(inputLat: Double): List { - // Same closed-bound issue as encodeLon: nudge into the half-open cell so an - // exact boundary latitude does not fall back into the previous field. - var latitude = if (inputLat < 90.0) inputLat + 1e-10 else 90.0 - 1e-10 - var d2 = 0.0 - var d3 = 0.0 - val str5 = when { - (latitude >= -90.0) && (latitude <= -80.0) -> { d2 = latitude + 90.0; "A" } - (latitude >= -80.0) && (latitude <= -70.0) -> { d2 = latitude + 80.0; "B" } - (latitude >= -70.0) && (latitude <= -60.0) -> { d2 = latitude + 70.0; "C" } - (latitude >= -60.0) && (latitude <= -50.0) -> { d2 = latitude + 60.0; "D" } - (latitude >= -50.0) && (latitude <= -40.0) -> { d2 = latitude + 50.0; "E" } - (latitude >= -40.0) && (latitude <= -30.0) -> { d2 = latitude + 40.0; "F" } - (latitude >= -30.0) && (latitude <= -20.0) -> { d2 = latitude + 30.0; "G" } - (latitude >= -20.0) && (latitude <= -10.0) -> { d2 = latitude + 20.0; "H" } - (latitude >= -10.0) && (latitude <= 0.0) -> { d2 = latitude + 10.0; "I" } - (latitude >= 0.0) && (latitude <= 10.0) -> { d2 = latitude; "J" } - (latitude >= 10.0) && (latitude <= 20.0) -> { d2 = latitude - 10.0; "K" } - (latitude >= 20.0) && (latitude <= 30.0) -> { d2 = latitude - 20.0; "L" } - (latitude >= 30.0) && (latitude <= 40.0) -> { d2 = latitude - 30.0; "M" } - (latitude >= 40.0) && (latitude <= 50.0) -> { d2 = latitude - 40.0; "N" } - (latitude >= 50.0) && (latitude <= 60.0) -> { d2 = latitude - 50.0; "O" } - (latitude >= 60.0) && (latitude <= 70.0) -> { d2 = latitude - 60.0; "P" } - (latitude >= 70.0) && (latitude <= 80.0) -> { d2 = latitude - 70.0; "Q" } - (latitude >= 80.0) && (latitude <= 90.0) -> { d2 = latitude - 80.0; "R" } - else -> { d2 = 0.0; " " } - } - var str6 = " " - when { - ((d2 >= 0.0) && (d2 <= 1.0)) || ((d2 >= -10.0) && (d2 <= -9.0)) -> { d3 = d2 * 60.0; str6 = "0" } - ((d2 >= 1.0) && (d2 <= 2.0)) || ((d2 >= -9.0) && (d2 <= -8.0)) -> { d3 = (d2 - 1.0) * 60.0; str6 = "1" } - ((d2 >= 2.0) && (d2 <= 3.0)) || ((d2 >= -8.0) && (d2 <= -7.0)) -> { d3 = (d2 - 2.0) * 60.0; str6 = "2" } - ((d2 >= 3.0) && (d2 <= 4.0)) || ((d2 >= -7.0) && (d2 <= -6.0)) -> { d3 = (d2 - 3.0) * 60.0; str6 = "3" } - ((d2 >= 4.0) && (d2 <= 5.0)) || ((d2 >= -6.0) && (d2 <= -5.0)) -> { d3 = (d2 - 4.0) * 60.0; str6 = "4" } - ((d2 >= 5.0) && (d2 <= 6.0)) || ((d2 >= -5.0) && (d2 <= -4.0)) -> { d3 = (d2 - 5.0) * 60.0; str6 = "5" } - ((d2 >= 6.0) && (d2 <= 7.0)) || ((d2 >= -4.0) && (d2 <= -3.0)) -> { d3 = (d2 - 6.0) * 60.0; str6 = "6" } - ((d2 >= 7.0) && (d2 <= 8.0)) || ((d2 >= -3.0) && (d2 <= -2.0)) -> { d3 = (d2 - 7.0) * 60.0; str6 = "7" } - ((d2 >= 8.0) && (d2 <= 9.0)) || ((d2 >= -2.0) && (d2 <= -1.0)) -> { d3 = (d2 - 8.0) * 60.0; str6 = "8" } - ((d2 >= 9.0) && (d2 <= 10.0)) || ((d2 >= -1.0) && (d2 <= 0.0)) -> { d3 = (d2 - 9.0) * 60.0; str6 = "9" } - } - val str38 = when { - ((d3 >= 0.0) && (d3 <= 2.5)) || ((d3 >= -60.0) && (d3 <= -57.5)) -> { d2 = d3 * 60.0; "a" } - ((d3 >= 2.5) && (d3 <= 5.0)) || ((d3 >= -57.5) && (d3 <= -55.0)) -> { d2 = (d3 - 2.5) * 60.0; "b" } - ((d3 >= 5.0) && (d3 <= 7.5)) || ((d3 >= -55.0) && (d3 <= -52.5)) -> { d2 = (d3 - 5.0) * 60.0; "c" } - ((d3 >= 7.5) && (d3 <= 10.0)) || ((d3 >= -52.5) && (d3 <= -50.0)) -> { d2 = (d3 - 7.5) * 60.0; "d" } - ((d3 >= 10.0) && (d3 <= 12.5)) || ((d3 >= -50.0) && (d3 <= -47.5)) -> { d2 = (d3 - 10.0) * 60.0; "e" } - ((d3 >= 12.5) && (d3 <= 15.0)) || ((d3 >= -47.5) && (d3 <= -45.0)) -> { d2 = (d3 - 12.5) * 60.0; "f" } - ((d3 >= 15.0) && (d3 <= 17.5)) || ((d3 >= -45.0) && (d3 <= -42.5)) -> { d2 = (d3 - 15.0) * 60.0; "g" } - ((d3 >= 17.5) && (d3 <= 20.0)) || ((d3 >= -42.5) && (d3 <= -40.0)) -> { d2 = (d3 - 17.5) * 60.0; "h" } - ((d3 >= 20.0) && (d3 <= 22.5)) || ((d3 >= -40.0) && (d3 <= -37.5)) -> { d2 = (d3 - 20.0) * 60.0; "i" } - ((d3 >= 22.5) && (d3 <= 25.0)) || ((d3 >= -37.5) && (d3 <= -35.0)) -> { d2 = (d3 - 22.5) * 60.0; "j" } - ((d3 >= 25.0) && (d3 <= 27.5)) || ((d3 >= -35.0) && (d3 <= -32.5)) -> { d2 = (d3 - 25.0) * 60.0; "k" } - ((d3 >= 27.5) && (d3 <= 30.0)) || ((d3 >= -32.5) && (d3 <= -30.0)) -> { d2 = (d3 - 27.5) * 60.0; "l" } - ((d3 >= 30.0) && (d3 <= 32.5)) || ((d3 >= -30.0) && (d3 <= -27.5)) -> { d2 = (d3 - 30.0) * 60.0; "m" } - ((d3 >= 32.5) && (d3 <= 35.0)) || ((d3 >= -27.5) && (d3 <= -25.0)) -> { d2 = (d3 - 32.5) * 60.0; "n" } - ((d3 >= 35.0) && (d3 <= 37.5)) || ((d3 >= -25.0) && (d3 <= -22.5)) -> { d2 = (d3 - 35.0) * 60.0; "o" } - ((d3 >= 37.5) && (d3 <= 40.0)) || ((d3 >= -22.5) && (d3 <= -20.0)) -> { d2 = (d3 - 37.5) * 60.0; "p" } - ((d3 >= 40.0) && (d3 <= 42.5)) || ((d3 >= -20.0) && (d3 <= -17.5)) -> { d2 = (d3 - 40.0) * 60.0; "q" } - ((d3 >= 42.5) && (d3 <= 45.0)) || ((d3 >= -17.5) && (d3 <= -15.0)) -> { d2 = (d3 - 42.5) * 60.0; "r" } - ((d3 >= 45.0) && (d3 <= 47.5)) || ((d3 >= -15.0) && (d3 <= -12.5)) -> { d2 = (d3 - 45.0) * 60.0; "s" } - ((d3 >= 47.5) && (d3 <= 50.0)) || ((d3 >= -12.5) && (d3 <= -10.0)) -> { d2 = (d3 - 47.5) * 60.0; "t" } - ((d3 >= 50.0) && (d3 <= 52.5)) || ((d3 >= -10.0) && (d3 <= -7.5)) -> { d2 = (d3 - 50.0) * 60.0; "u" } - ((d3 >= 52.5) && (d3 <= 55.0)) || ((d3 >= -7.5) && (d3 <= -5.0)) -> { d2 = (d3 - 52.5) * 60.0; "v" } - ((d3 >= 55.0) && (d3 <= 57.5)) || ((d3 >= -5.0) && (d3 <= -2.5)) -> { d2 = (d3 - 55.0) * 60.0; "w" } - ((d3 >= 57.5) && (d3 <= 60.0)) || ((d3 >= -2.5) && (d3 <= 0.0)) -> { d2 = (d3 - 57.5) * 60.0; "x" } - else -> { d2 = 0.0; " " } - } - val str9 = when { - ((d2 >= 0.0) && (d2 <= 15.0)) || ((d2 >= -150.0) && (d2 <= -135.0)) -> "0" - ((d2 >= 15.0) && (d2 <= 30.0)) || ((d2 >= -135.0) && (d2 <= -120.0)) -> "1" - ((d2 >= 30.0) && (d2 <= 45.0)) || ((d2 >= -120.0) && (d2 <= -105.0)) -> "2" - ((d2 >= 45.0) && (d2 <= 60.0)) || ((d2 >= -105.0) && (d2 <= -90.0)) -> "3" - ((d2 >= 60.0) && (d2 <= 75.0)) || ((d2 >= -90.0) && (d2 <= -75.0)) -> "4" - ((d2 >= 75.0) && (d2 <= 90.0)) || ((d2 >= -75.0) && (d2 <= -60.0)) -> "5" - ((d2 >= 90.0) && (d2 <= 105.0)) || ((d2 >= -60.0) && (d2 <= -45.0)) -> "6" - ((d2 >= 105.0) && (d2 <= 120.0)) || ((d2 >= -45.0) && (d2 <= -30.0)) -> "7" - ((d2 >= 120.0) && (d2 <= 135.0)) || ((d2 >= -30.0) && (d2 <= -15.0)) -> "8" - ((d2 >= 135.0) && (d2 <= 150.0)) || ((d2 >= -15.0) && (d2 <= 0.0)) -> "9" - else -> " " - } - return listOf(str5, str6, str38, str9) -} -