Initial commit: Конвертор координат GPS → APRS, Maidenhead
- Kotlin + Jetpack Compose (Material 3) - Конвертация в APRS, Maidenhead, DMS форматы - Полноэкранный режим - Иконка приложения - Min SDK: 21, Target SDK: 34 Версия: 1.0.0 Дата: 2026-03-02 Автор: UA1ZBE Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
107
app/src/main/java/com/example/aprs/LocationUtils.kt
Normal file
107
app/src/main/java/com/example/aprs/LocationUtils.kt
Normal file
@@ -0,0 +1,107 @@
|
||||
package com.example.aprs
|
||||
|
||||
import kotlin.math.floor
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.sin
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.atan
|
||||
import kotlin.math.sqrt
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.ln
|
||||
|
||||
object LocationUtils {
|
||||
// Convert decimal degrees to APRS position format: DDMM.MM N / DDDMM.MM E
|
||||
fun toAprs(lat: Double, lon: Double): String {
|
||||
val latHem = if (lat >= 0) "N" else "S"
|
||||
val lonHem = if (lon >= 0) "E" else "W"
|
||||
|
||||
val latAbs = abs(lat)
|
||||
val lonAbs = abs(lon)
|
||||
|
||||
val latDeg = floor(latAbs).toInt()
|
||||
val latMin = (latAbs - latDeg) * 60.0
|
||||
|
||||
val lonDeg = floor(lonAbs).toInt()
|
||||
val lonMin = (lonAbs - lonDeg) * 60.0
|
||||
|
||||
return String.format(
|
||||
"%02d%05.2f%s/%03d%05.2f%s",
|
||||
latDeg,
|
||||
latMin,
|
||||
latHem,
|
||||
lonDeg,
|
||||
lonMin,
|
||||
lonHem
|
||||
)
|
||||
}
|
||||
|
||||
// Maidenhead locator (6 chars) from lat/lon
|
||||
fun toMaidenhead(lat: Double, lon: Double): String {
|
||||
var adjLon = lon + 180.0
|
||||
var adjLat = lat + 90.0
|
||||
|
||||
val fieldLon = (adjLon / 20.0).toInt()
|
||||
val fieldLat = (adjLat / 10.0).toInt()
|
||||
|
||||
val squareLon = ((adjLon % 20) / 2).toInt()
|
||||
val squareLat = ((adjLat % 10) / 1).toInt()
|
||||
|
||||
val subsLon = (((adjLon - fieldLon * 20 - squareLon * 2) * 60) / 5).toInt()
|
||||
val subsLat = (((adjLat - fieldLat * 10 - squareLat * 1) * 60) / 2.5).toInt()
|
||||
|
||||
val a = 'A'.code
|
||||
|
||||
val fieldChars = charArrayOf((a + fieldLon).toChar(), (a + fieldLat).toChar())
|
||||
val squareChars = charArrayOf(('0'.code + squareLon).toChar(), ('0'.code + squareLat).toChar())
|
||||
val subsChars = charArrayOf((a + subsLon).toChar(), (a + subsLat).toChar())
|
||||
|
||||
return String(charArrayOf(fieldChars[0], fieldChars[1], squareChars[0], squareChars[1], subsChars[0], subsChars[1]))
|
||||
}
|
||||
|
||||
// Convert lat/lon to UTM coordinates
|
||||
fun toUTM(lat: Double, lon: Double): String {
|
||||
val k0 = 0.9996
|
||||
val a = 6378137.0
|
||||
val eSquared = 0.00669438
|
||||
val e = sqrt(eSquared)
|
||||
val ePrimeSquared = eSquared / (1 - eSquared)
|
||||
|
||||
val latRad = lat * PI / 180.0
|
||||
val lonRad = lon * PI / 180.0
|
||||
|
||||
val zone = ((lon + 180) / 6).toInt() + 1
|
||||
|
||||
val lonOrigin = (zone - 1) * 6 - 180 + 3
|
||||
val lonOriginRad = lonOrigin * PI / 180.0
|
||||
|
||||
val n = a / sqrt(1 - eSquared * sin(latRad) * sin(latRad))
|
||||
val T = tan(latRad) * tan(latRad)
|
||||
val C = ePrimeSquared * cos(latRad) * cos(latRad)
|
||||
val A = cos(latRad) * (lonRad - lonOriginRad)
|
||||
|
||||
val M = a * ((1 - eSquared / 4 - 3 * eSquared * eSquared / 64 - 5 * eSquared * eSquared * eSquared / 256) * latRad
|
||||
- (3 * eSquared / 8 + 3 * eSquared * eSquared / 32 + 45 * eSquared * eSquared * eSquared / 1024) * sin(2 * latRad)
|
||||
+ (15 * eSquared * eSquared / 256 + 45 * eSquared * eSquared * eSquared / 1024) * sin(4 * latRad)
|
||||
- (35 * eSquared * eSquared * eSquared / 3072) * sin(6 * latRad))
|
||||
|
||||
val UTMEasting = (k0 * n * (A + (1 - T + C) * A * A * A / 6
|
||||
+ (5 - 18 * T + T * T + 72 * C - 58 * ePrimeSquared) * A * A * A * A * A / 120)
|
||||
+ 500000.0).toLong()
|
||||
|
||||
val UTMNorthing = (k0 * (M + n * tan(latRad) * (A * A / 2 + (5 - T + 9 * C + 4 * C * C) * A * A * A * A / 24
|
||||
+ (61 - 58 * T + T * T + 600 * C - 330 * ePrimeSquared) * A * A * A * A * A * A / 720))
|
||||
+ (if (lat < 0) 10000000.0 else 0.0)).toLong()
|
||||
|
||||
val zoneLetter = getUtmZoneLetter(lat, lon)
|
||||
|
||||
return String.format("%d%s %06d %07d", zone, zoneLetter, UTMEasting, UTMNorthing)
|
||||
}
|
||||
|
||||
private fun tan(rad: Double): Double = sin(rad) / cos(rad)
|
||||
|
||||
private fun getUtmZoneLetter(lat: Double, lon: Double): String {
|
||||
val letters = "CDEFGHJKLMNPQRSTUVWXX"
|
||||
val latIndex = ((lat + 80) / 8).toInt().coerceIn(0, letters.length - 1)
|
||||
return letters[latIndex].toString()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user