Files
aprs-converter/app/src/main/java/com/example/aprs/LocationUtils.kt
UA1ZBE 17d5722560 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>
2026-03-03 01:34:35 +03:00

108 lines
3.9 KiB
Kotlin

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()
}
}