Example project: Appease Detekt (#3125)

pull/3010/merge
Phil Oliver 2025-09-16 19:38:54 -04:00 zatwierdzone przez GitHub
rodzic 07d798d506
commit 299dac415d
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
5 zmienionych plików z 235 dodań i 276 usunięć

Wyświetl plik

@ -22,17 +22,14 @@ import android.os.Parcelable
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable
/**
* Generic [Parcel.readParcelable] Android 13 compatibility extension.
*/
private inline fun <reified T : Parcelable> Parcel.readParcelableCompat(loader: ClassLoader?): T? {
return if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.TIRAMISU) {
/** Generic [Parcel.readParcelable] Android 13 compatibility extension. */
private inline fun <reified T : Parcelable> Parcel.readParcelableCompat(loader: ClassLoader?): T? =
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.TIRAMISU) {
@Suppress("DEPRECATION")
readParcelable(loader)
} else {
readParcelable(loader, T::class.java)
}
}
@Parcelize
enum class MessageStatus : Parcelable {
@ -41,17 +38,16 @@ enum class MessageStatus : Parcelable {
QUEUED, // Waiting to send to the mesh as soon as we connect to the device
ENROUTE, // Delivered to the radio, but no ACK or NAK received
DELIVERED, // We received an ack
ERROR // We received back a nak, message not delivered
ERROR, // We received back a nak, message not delivered
}
/**
* A parcelable version of the protobuf MeshPacket + Data subpacket.
*/
/** A parcelable version of the protobuf MeshPacket + Data subpacket. */
@Serializable
data class DataPacket(
var to: String? = ID_BROADCAST, // a nodeID string, or ID_BROADCAST for broadcast
val bytes: ByteArray?,
val dataType: Int, // A port number for this packet (formerly called DataType, see portnums.proto for new usage instructions)
// A port number for this packet (formerly called DataType, see portnums.proto for new usage instructions)
val dataType: Int,
var from: String? = ID_LOCAL, // a nodeID string, or ID_LOCAL for localhost
var time: Long = System.currentTimeMillis(), // msecs since 1970
var id: Int = 0, // 0 means unassigned
@ -61,55 +57,57 @@ data class DataPacket(
var wantAck: Boolean = true, // If true, the receiver should send an ack back
) : Parcelable {
/**
* If there was an error with this message, this string describes what was wrong.
*/
/** If there was an error with this message, this string describes what was wrong. */
var errorMessage: String? = null
/**
* Syntactic sugar to make it easy to create text messages
*/
constructor(to: String?, channel: Int, text: String) : this(
/** Syntactic sugar to make it easy to create text messages */
constructor(
to: String?,
channel: Int,
text: String,
) : this(
to = to,
bytes = text.encodeToByteArray(),
dataType = Portnums.PortNum.TEXT_MESSAGE_APP_VALUE,
channel = channel
channel = channel,
)
/**
* If this is a text message, return the string, otherwise null
*/
/** If this is a text message, return the string, otherwise null */
val text: String?
get() = if (dataType == Portnums.PortNum.TEXT_MESSAGE_APP_VALUE) {
bytes?.decodeToString()
} else {
null
}
get() =
if (dataType == Portnums.PortNum.TEXT_MESSAGE_APP_VALUE) {
bytes?.decodeToString()
} else {
null
}
val alert: String?
get() = if (dataType == Portnums.PortNum.ALERT_APP_VALUE) {
bytes?.decodeToString()
} else {
null
}
get() =
if (dataType == Portnums.PortNum.ALERT_APP_VALUE) {
bytes?.decodeToString()
} else {
null
}
constructor(to: String?, channel: Int, waypoint: MeshProtos.Waypoint) : this(
to = to,
bytes = waypoint.toByteArray(),
dataType = Portnums.PortNum.WAYPOINT_APP_VALUE,
channel = channel
)
constructor(
to: String?,
channel: Int,
waypoint: MeshProtos.Waypoint,
) : this(to = to, bytes = waypoint.toByteArray(), dataType = Portnums.PortNum.WAYPOINT_APP_VALUE, channel = channel)
val waypoint: MeshProtos.Waypoint?
get() = if (dataType == Portnums.PortNum.WAYPOINT_APP_VALUE) {
MeshProtos.Waypoint.parseFrom(bytes)
} else {
null
}
get() =
if (dataType == Portnums.PortNum.WAYPOINT_APP_VALUE) {
MeshProtos.Waypoint.parseFrom(bytes)
} else {
null
}
// Autogenerated comparision, because we have a byte array
constructor(parcel: Parcel) : this(
constructor(
parcel: Parcel,
) : this(
parcel.readString(),
parcel.createByteArray(),
parcel.readInt(),
@ -169,9 +167,7 @@ data class DataPacket(
parcel.writeInt(if (wantAck) 1 else 0)
}
override fun describeContents(): Int {
return 0
}
override fun describeContents(): Int = 0
// Update our object from our parcel (used for inout parameters
fun readFromParcel(parcel: Parcel) {
@ -203,14 +199,11 @@ data class DataPacket(
const val PKC_CHANNEL_INDEX = 8
fun nodeNumToDefaultId(n: Int): String = "!%08x".format(n)
fun idToDefaultNodeNum(id: String?): Int? = runCatching { id?.toLong(16)?.toInt() }.getOrNull()
override fun createFromParcel(parcel: Parcel): DataPacket {
return DataPacket(parcel)
}
override fun createFromParcel(parcel: Parcel): DataPacket = DataPacket(parcel)
override fun newArray(size: Int): Array<DataPacket?> {
return arrayOfNulls(size)
}
override fun newArray(size: Int): Array<DataPacket?> = arrayOfNulls(size)
}
}
}

Wyświetl plik

@ -23,21 +23,22 @@ import kotlinx.parcelize.Parcelize
// MyNodeInfo sent via special protobuf from radio
@Parcelize
data class MyNodeInfo(
val myNodeNum: Int,
val hasGPS: Boolean,
val model: String?,
val firmwareVersion: String?,
val couldUpdate: Boolean, // this application contains a software load we _could_ install if you want
val shouldUpdate: Boolean, // this device has old firmware
val currentPacketId: Long,
val messageTimeoutMsec: Int,
val minAppVersion: Int,
val maxChannels: Int,
val hasWifi: Boolean,
val channelUtilization: Float,
val airUtilTx: Float,
val deviceId: String?,
val myNodeNum: Int,
val hasGPS: Boolean,
val model: String?,
val firmwareVersion: String?,
val couldUpdate: Boolean, // this application contains a software load we _could_ install if you want
val shouldUpdate: Boolean, // this device has old firmware
val currentPacketId: Long,
val messageTimeoutMsec: Int,
val minAppVersion: Int,
val maxChannels: Int,
val hasWifi: Boolean,
val channelUtilization: Float,
val airUtilTx: Float,
val deviceId: String?,
) : Parcelable {
/** A human readable description of the software/hardware version */
val firmwareString: String get() = "$model $firmwareVersion"
}
/** A human readable description of the software/hardware version */
val firmwareString: String
get() = "$model $firmwareVersion"
}

Wyświetl plik

@ -19,9 +19,9 @@ package com.geeksville.mesh
import android.graphics.Color
import android.os.Parcelable
import com.geeksville.mesh.util.anonymize
import com.geeksville.mesh.util.bearing
import com.geeksville.mesh.util.latLongToMeter
import com.geeksville.mesh.util.anonymize
import kotlinx.parcelize.Parcelize
//
@ -38,33 +38,27 @@ data class MeshUser(
val role: Int = 0,
) : Parcelable {
override fun toString(): String {
return "MeshUser(id=${id.anonymize}, " +
"longName=${longName.anonymize}, " +
"shortName=${shortName.anonymize}, " +
"hwModel=$hwModelString, " +
"isLicensed=$isLicensed, " +
"role=$role)"
}
override fun toString(): String = "MeshUser(id=${id.anonymize}, " +
"longName=${longName.anonymize}, " +
"shortName=${shortName.anonymize}, " +
"hwModel=$hwModelString, " +
"isLicensed=$isLicensed, " +
"role=$role)"
/** Create our model object from a protobuf.
/** Create our model object from a protobuf. */
constructor(p: MeshProtos.User) : this(p.id, p.longName, p.shortName, p.hwModel, p.isLicensed, p.roleValue)
/**
* a string version of the hardware model, converted into pretty lowercase and changing _ to -, and p to dot or null
* if unset
*/
constructor(p: MeshProtos.User) : this(
p.id,
p.longName,
p.shortName,
p.hwModel,
p.isLicensed,
p.roleValue
)
/** a string version of the hardware model, converted into pretty lowercase and changing _ to -, and p to dot
* or null if unset
* */
val hwModelString: String?
get() =
if (hwModel == MeshProtos.HardwareModel.UNSET) null
else hwModel.name.replace('_', '-').replace('p', '.').lowercase()
if (hwModel == MeshProtos.HardwareModel.UNSET) {
null
} else {
hwModel.name.replace('_', '-').replace('p', '.').lowercase()
}
}
@Parcelize
@ -80,16 +74,22 @@ data class Position(
) : Parcelable {
companion object {
/// Convert to a double representation of degrees
// / Convert to a double representation of degrees
fun degD(i: Int) = i * 1e-7
fun degI(d: Double) = (d * 1e7).toInt()
fun currentTime() = (System.currentTimeMillis() / 1000).toInt()
}
/** Create our model object from a protobuf. If time is unspecified in the protobuf, the provided default time will be used.
/**
* Create our model object from a protobuf. If time is unspecified in the protobuf, the provided default time will
* be used.
*/
constructor(position: MeshProtos.Position, defaultTime: Int = currentTime()) : this(
constructor(
position: MeshProtos.Position,
defaultTime: Int = currentTime(),
) : this(
// We prefer the int version of lat/lon but if not available use the depreciated legacy version
degD(position.latitudeI),
degD(position.longitudeI),
@ -98,28 +98,25 @@ data class Position(
position.satsInView,
position.groundSpeed,
position.groundTrack,
position.precisionBits
position.precisionBits,
)
/// @return distance in meters to some other node (or null if unknown)
// / @return distance in meters to some other node (or null if unknown)
fun distance(o: Position) = latLongToMeter(latitude, longitude, o.latitude, o.longitude)
/// @return bearing to the other position in degrees
// / @return bearing to the other position in degrees
fun bearing(o: Position) = bearing(latitude, longitude, o.latitude, o.longitude)
// If GPS gives a crap position don't crash our app
fun isValid(): Boolean {
return latitude != 0.0 && longitude != 0.0 &&
(latitude >= -90 && latitude <= 90.0) &&
(longitude >= -180 && longitude <= 180)
}
fun isValid(): Boolean = latitude != 0.0 &&
longitude != 0.0 &&
(latitude >= -90 && latitude <= 90.0) &&
(longitude >= -180 && longitude <= 180)
override fun toString(): String {
return "Position(lat=${latitude.anonymize}, lon=${longitude.anonymize}, alt=${altitude.anonymize}, time=${time})"
}
override fun toString(): String =
"Position(lat=${latitude.anonymize}, lon=${longitude.anonymize}, alt=${altitude.anonymize}, time=$time)"
}
@Parcelize
data class DeviceMetrics(
val time: Int = currentTime(), // default to current time in secs (NOT MILLISECONDS!)
@ -133,16 +130,11 @@ data class DeviceMetrics(
fun currentTime() = (System.currentTimeMillis() / 1000).toInt()
}
/** Create our model object from a protobuf.
*/
constructor(p: TelemetryProtos.DeviceMetrics, telemetryTime: Int = currentTime()) : this(
telemetryTime,
p.batteryLevel,
p.voltage,
p.channelUtilization,
p.airUtilTx,
p.uptimeSeconds,
)
/** Create our model object from a protobuf. */
constructor(
p: TelemetryProtos.DeviceMetrics,
telemetryTime: Int = currentTime(),
) : this(telemetryTime, p.batteryLevel, p.voltage, p.channelUtilization, p.airUtilTx, p.uptimeSeconds)
}
@Parcelize
@ -172,7 +164,7 @@ data class NodeInfo(
var deviceMetrics: DeviceMetrics? = null,
var channel: Int = 0,
var environmentMetrics: EnvironmentMetrics? = null,
var hopsAway: Int = 0
var hopsAway: Int = 0,
) : Parcelable {
val colors: Pair<Int, Int>
@ -184,13 +176,17 @@ data class NodeInfo(
return (if (brightness > 0.5) Color.BLACK else Color.WHITE) to Color.rgb(r, g, b)
}
val batteryLevel get() = deviceMetrics?.batteryLevel
val voltage get() = deviceMetrics?.voltage
val batteryStr get() = if (batteryLevel in 1..100) String.format("%d%%", batteryLevel) else ""
val batteryLevel
get() = deviceMetrics?.batteryLevel
/**
* true if the device was heard from recently
*/
val voltage
get() = deviceMetrics?.voltage
val batteryStr
get() = if (batteryLevel in 1..100) String.format("%d%%", batteryLevel) else ""
/** true if the device was heard from recently */
@Suppress("MagicNumber")
val isOnline: Boolean
get() {
val now = System.currentTimeMillis() / 1000
@ -198,35 +194,39 @@ data class NodeInfo(
return (now - lastHeard <= timeout)
}
/// return the position if it is valid, else null
// / return the position if it is valid, else null
val validPosition: Position?
get() {
return position?.takeIf { it.isValid() }
}
/// @return distance in meters to some other node (or null if unknown)
// / @return distance in meters to some other node (or null if unknown)
fun distance(o: NodeInfo?): Int? {
val p = validPosition
val op = o?.validPosition
return if (p != null && op != null) p.distance(op).toInt() else null
}
/// @return bearing to the other position in degrees
// / @return bearing to the other position in degrees
fun bearing(o: NodeInfo?): Int? {
val p = validPosition
val op = o?.validPosition
return if (p != null && op != null) p.bearing(op).toInt() else null
}
/// @return a nice human readable string for the distance, or null for unknown
// / @return a nice human readable string for the distance, or null for unknown
fun distanceStr(o: NodeInfo?, prefUnits: Int = 0) = distance(o)?.let { dist ->
when {
dist == 0 -> null // same point
prefUnits == ConfigProtos.Config.DisplayConfig.DisplayUnits.METRIC_VALUE && dist < 1000 -> "%.0f m".format(dist.toDouble())
prefUnits == ConfigProtos.Config.DisplayConfig.DisplayUnits.METRIC_VALUE && dist >= 1000 -> "%.1f km".format(dist / 1000.0)
prefUnits == ConfigProtos.Config.DisplayConfig.DisplayUnits.IMPERIAL_VALUE && dist < 1609 -> "%.0f ft".format(dist.toDouble()*3.281)
prefUnits == ConfigProtos.Config.DisplayConfig.DisplayUnits.IMPERIAL_VALUE && dist >= 1609 -> "%.1f mi".format(dist / 1609.34)
prefUnits == ConfigProtos.Config.DisplayConfig.DisplayUnits.METRIC_VALUE && dist < 1000 ->
"%.0f m".format(dist.toDouble())
prefUnits == ConfigProtos.Config.DisplayConfig.DisplayUnits.METRIC_VALUE && dist >= 1000 ->
"%.1f km".format(dist / 1000.0)
prefUnits == ConfigProtos.Config.DisplayConfig.DisplayUnits.IMPERIAL_VALUE && dist < 1609 ->
"%.0f ft".format(dist.toDouble() * 3.281)
prefUnits == ConfigProtos.Config.DisplayConfig.DisplayUnits.IMPERIAL_VALUE && dist >= 1609 ->
"%.1f mi".format(dist / 1609.34)
else -> null
}
}
}
}

Wyświetl plik

@ -15,6 +15,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
@file:Suppress("TooManyFunctions")
package com.geeksville.mesh.util
import com.geeksville.mesh.MeshProtos
@ -24,6 +26,8 @@ import mil.nga.mgrs.MGRS
import mil.nga.mgrs.utm.UTM
import org.osmdroid.util.BoundingBox
import org.osmdroid.util.GeoPoint
import java.util.Locale
import kotlin.math.PI
import kotlin.math.abs
import kotlin.math.acos
import kotlin.math.atan2
@ -31,78 +35,72 @@ import kotlin.math.cos
import kotlin.math.log2
import kotlin.math.pow
import kotlin.math.sin
import kotlin.math.PI
/*******************************************************************************
/**
* ****************************************************************************
* Revive some of my old Gaggle source code...
*
* GNU Public License, version 2
* All other distribution of Gaggle must conform to the terms of the GNU Public License, version 2. The full
* text of this license is included in the Gaggle source, see assets/manual/gpl-2.0.txt.
******************************************************************************/
* GNU Public License, version 2 All other distribution of Gaggle must conform to the terms of the GNU Public License,
* version 2. The full text of this license is included in the Gaggle source, see assets/manual/gpl-2.0.txt.
* ****************************************************************************
*/
object GPSFormat {
fun DEC(p: Position): String {
return String.format("%.5f %.5f", p.latitude, p.longitude).replace(",", ".")
}
fun dec(p: Position): String =
String.format(Locale.getDefault(), "%.5f %.5f", p.latitude, p.longitude).replace(",", ".")
fun DMS(p: Position): String {
@Suppress("MagicNumber")
fun dms(p: Position): String {
val lat = degreesToDMS(p.latitude, true)
val lon = degreesToDMS(p.longitude, false)
fun string(a: Array<String>) = String.format("%s°%s'%.5s\"%s", a[0], a[1], a[2], a[3])
fun string(a: Array<String>) = String.format(Locale.getDefault(), "%s°%s'%.5s\"%s", a[0], a[1], a[2], a[3])
return string(lat) + " " + string(lon)
}
fun UTM(p: Position): String {
val UTM = UTM.from(Point.point(p.longitude, p.latitude))
fun utm(p: Position): String {
val utm = UTM.from(Point.point(p.longitude, p.latitude))
return String.format(
Locale.getDefault(),
"%s%s %.6s %.7s",
UTM.zone,
UTM.toMGRS().band,
UTM.easting,
UTM.northing
utm.zone,
utm.toMGRS().band,
utm.easting,
utm.northing,
)
}
fun MGRS(p: Position): String {
val MGRS = MGRS.from(Point.point(p.longitude, p.latitude))
fun mgrs(p: Position): String {
val mgrs = MGRS.from(Point.point(p.longitude, p.latitude))
return String.format(
Locale.getDefault(),
"%s%s %s%s %05d %05d",
MGRS.zone,
MGRS.band,
MGRS.column,
MGRS.row,
MGRS.easting,
MGRS.northing
mgrs.zone,
mgrs.band,
mgrs.column,
mgrs.row,
mgrs.easting,
mgrs.northing,
)
}
fun toDEC(latitude: Double, longitude: Double): String {
return "%.5f %.5f".format(latitude, longitude).replace(",", ".")
}
fun toDEC(latitude: Double, longitude: Double): String = "%.5f %.5f".format(latitude, longitude).replace(",", ".")
@Suppress("MagicNumber")
fun toDMS(latitude: Double, longitude: Double): String {
val lat = degreesToDMS(latitude, true)
val lon = degreesToDMS(longitude, false)
fun string(a: Array<String>) = "%s°%s'%.5s\"%s".format(a[0], a[1], a[2], a[3])
fun string(a: Array<String>) = "%s°%s'%.5s\"%s".format(Locale.getDefault(), a[0], a[1], a[2], a[3])
return string(lat) + " " + string(lon)
}
fun toUTM(latitude: Double, longitude: Double): String {
val UTM = UTM.from(Point.point(longitude, latitude))
return "%s%s %.6s %.7s".format(UTM.zone, UTM.toMGRS().band, UTM.easting, UTM.northing)
val utm = UTM.from(Point.point(longitude, latitude))
return "%s%s %.6s %.7s".format(Locale.getDefault(), utm.zone, utm.toMGRS().band, utm.easting, utm.northing)
}
fun toMGRS(latitude: Double, longitude: Double): String {
val MGRS = MGRS.from(Point.point(longitude, latitude))
return "%s%s %s%s %05d %05d".format(
MGRS.zone,
MGRS.band,
MGRS.column,
MGRS.row,
MGRS.easting,
MGRS.northing
)
val mgrs = MGRS.from(Point.point(longitude, latitude))
return "%s%s %s%s %05d %05d"
.format(Locale.getDefault(), mgrs.zone, mgrs.band, mgrs.column, mgrs.row, mgrs.easting, mgrs.northing)
}
}
@ -113,75 +111,71 @@ object GPSFormat {
* @param isLatitude
* @return a string like 120deg
*/
fun degreesToDMS(
_degIn: Double,
isLatitude: Boolean
): Array<String> {
var degIn = _degIn
@Suppress("MagicNumber")
fun degreesToDMS(degIn: Double, isLatitude: Boolean): Array<String> {
var degIn = degIn
val isPos = degIn >= 0
val dirLetter =
if (isLatitude) if (isPos) 'N' else 'S' else if (isPos) 'E' else 'W'
if (isLatitude) if (isPos) 'N' else 'S'
else if (isPos) {
'E'
} else {
'W'
}
degIn = abs(degIn)
val degOut = degIn.toInt()
val minutes = 60 * (degIn - degOut)
val minwhole = minutes.toInt()
val seconds = (minutes - minwhole) * 60
return arrayOf(
degOut.toString(), minwhole.toString(),
seconds.toString(),
dirLetter.toString()
)
return arrayOf(degOut.toString(), minwhole.toString(), seconds.toString(), dirLetter.toString())
}
fun degreesToDM(_degIn: Double, isLatitude: Boolean): Array<String> {
var degIn = _degIn
@Suppress("MagicNumber")
fun degreesToDM(degIn: Double, isLatitude: Boolean): Array<String> {
var degIn = degIn
val isPos = degIn >= 0
val dirLetter =
if (isLatitude) if (isPos) 'N' else 'S' else if (isPos) 'E' else 'W'
if (isLatitude) if (isPos) 'N' else 'S'
else if (isPos) {
'E'
} else {
'W'
}
degIn = abs(degIn)
val degOut = degIn.toInt()
val minutes = 60 * (degIn - degOut)
val seconds = 0
return arrayOf(
degOut.toString(), minutes.toString(),
seconds.toString(),
dirLetter.toString()
)
return arrayOf(degOut.toString(), minutes.toString(), seconds.toString(), dirLetter.toString())
}
fun degreesToD(_degIn: Double, isLatitude: Boolean): Array<String> {
var degIn = _degIn
fun degreesToD(degIn: Double, isLatitude: Boolean): Array<String> {
var degIn = degIn
val isPos = degIn >= 0
val dirLetter =
if (isLatitude) if (isPos) 'N' else 'S' else if (isPos) 'E' else 'W'
if (isLatitude) if (isPos) 'N' else 'S'
else if (isPos) {
'E'
} else {
'W'
}
degIn = abs(degIn)
val degOut = degIn
val minutes = 0
val seconds = 0
return arrayOf(
degOut.toString(), minutes.toString(),
seconds.toString(),
dirLetter.toString()
)
return arrayOf(degOut.toString(), minutes.toString(), seconds.toString(), dirLetter.toString())
}
/**
* A not super efficent mapping from a starting lat/long + a distance at a
* certain direction
* A not super efficent mapping from a starting lat/long + a distance at a certain direction
*
* @param lat
* @param longitude
* @param distMeters
* @param theta
* in radians, 0 == north
* @param theta in radians, 0 == north
* @return an array with lat and long
*/
fun addDistance(
lat: Double,
longitude: Double,
distMeters: Double,
theta: Double
): DoubleArray {
@Suppress("MagicNumber")
fun addDistance(lat: Double, longitude: Double, distMeters: Double, theta: Double): DoubleArray {
val dx = distMeters * sin(theta) // theta measured clockwise
// from due north
val dy = distMeters * cos(theta) // dx, dy same units as R
@ -190,20 +184,14 @@ fun addDistance(
return doubleArrayOf(lat + dLat, longitude + dLong)
}
/**
* @return distance in meters along the surface of the earth (ish)
*/
fun latLongToMeter(
lat_a: Double,
lng_a: Double,
lat_b: Double,
lng_b: Double
): Double {
/** @return distance in meters along the surface of the earth (ish) */
@Suppress("MagicNumber")
fun latLongToMeter(latA: Double, lngA: Double, latB: Double, lngB: Double): Double {
val pk = (180 / PI)
val a1 = lat_a / pk
val a2 = lng_a / pk
val b1 = lat_b / pk
val b2 = lng_b / pk
val a1 = latA / pk
val a2 = lngA / pk
val b1 = latB / pk
val b2 = lngB / pk
val t1 = cos(a1) * cos(a2) * cos(b1) * cos(b2)
val t2 = cos(a1) * sin(a2) * cos(b1) * sin(b2)
val t3 = sin(a1) * sin(b1)
@ -213,14 +201,8 @@ fun latLongToMeter(
}
// Same as above, but takes Mesh Position proto.
fun positionToMeter(a: MeshProtos.Position, b: MeshProtos.Position): Double {
return latLongToMeter(
a.latitudeI * 1e-7,
a.longitudeI * 1e-7,
b.latitudeI * 1e-7,
b.longitudeI * 1e-7
)
}
fun positionToMeter(a: MeshProtos.Position, b: MeshProtos.Position): Double =
latLongToMeter(a.latitudeI * 1e-7, a.longitudeI * 1e-7, b.latitudeI * 1e-7, b.longitudeI * 1e-7)
/**
* Convert degrees/mins/secs to a single double
@ -231,44 +213,24 @@ fun positionToMeter(a: MeshProtos.Position, b: MeshProtos.Position): Double {
* @param isPostive
* @return
*/
fun DMSToDegrees(
degrees: Int,
minutes: Int,
seconds: Float,
isPostive: Boolean
): Double {
return (if (isPostive) 1 else -1) * (degrees + minutes / 60.0 + seconds / 3600.0)
}
@Suppress("MagicNumber")
fun dmsToDegrees(degrees: Int, minutes: Int, seconds: Float, isPostive: Boolean): Double =
(if (isPostive) 1 else -1) * (degrees + minutes / 60.0 + seconds / 3600.0)
fun DMSToDegrees(
degrees: Double,
minutes: Double,
seconds: Double,
isPostive: Boolean
): Double {
return (if (isPostive) 1 else -1) * (degrees + minutes / 60.0 + seconds / 3600.0)
}
@Suppress("MagicNumber")
fun dmsToDegrees(degrees: Double, minutes: Double, seconds: Double, isPostive: Boolean): Double =
(if (isPostive) 1 else -1) * (degrees + minutes / 60.0 + seconds / 3600.0)
/**
* Computes the bearing in degrees between two points on Earth.
*
* @param lat1
* Latitude of the first point
* @param lon1
* Longitude of the first point
* @param lat2
* Latitude of the second point
* @param lon2
* Longitude of the second point
* @return Bearing between the two points in degrees. A value of 0 means due
* north.
* @param lat1 Latitude of the first point
* @param lon1 Longitude of the first point
* @param lat2 Latitude of the second point
* @param lon2 Longitude of the second point
* @return Bearing between the two points in degrees. A value of 0 means due north.
*/
fun bearing(
lat1: Double,
lon1: Double,
lat2: Double,
lon2: Double
): Double {
fun bearing(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Double {
val lat1Rad = Math.toRadians(lat1)
val lat2Rad = Math.toRadians(lat2)
val deltaLonRad = Math.toRadians(lon2 - lon1)
@ -277,17 +239,16 @@ fun bearing(
return radToBearing(atan2(y, x))
}
/**
* Converts an angle in radians to degrees
*/
fun radToBearing(rad: Double): Double {
return (Math.toDegrees(rad) + 360) % 360
}
/** Converts an angle in radians to degrees */
@Suppress("MagicNumber")
fun radToBearing(rad: Double): Double = (Math.toDegrees(rad) + 360) % 360
/**
* Calculates the zoom level required to fit the entire [BoundingBox] inside the map view.
*
* @return The zoom level as a Double value.
*/
@Suppress("MagicNumber")
fun BoundingBox.requiredZoomLevel(): Double {
val topLeft = GeoPoint(this.latNorth, this.lonWest)
val bottomRight = GeoPoint(this.latSouth, this.lonEast)
@ -300,6 +261,7 @@ fun BoundingBox.requiredZoomLevel(): Double {
/**
* Creates a new bounding box with adjusted dimensions based on the provided [zoomFactor].
*
* @return A new [BoundingBox] with added [zoomFactor]. Example:
* ```
* // Setting the zoom level directly using setZoom()
@ -322,6 +284,6 @@ fun BoundingBox.zoomIn(zoomFactor: Double): BoundingBox {
center.latitude + newLatDiff / 2,
center.longitude + newLonDiff / 2,
center.latitude - newLatDiff / 2,
center.longitude - newLonDiff / 2
center.longitude - newLonDiff / 2,
)
}

Wyświetl plik

@ -50,6 +50,7 @@ class MainActivity : AppCompatActivity() {
private var isMeshServiceBound = false
@RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
@Suppress("TooGenericExceptionCaught", "LongMethod")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
this.enableEdgeToEdge()
@ -109,6 +110,7 @@ class MainActivity : AppCompatActivity() {
val meshtasticReceiver: BroadcastReceiver =
object : BroadcastReceiver() {
@SuppressLint("SetTextI18n")
@Suppress("ReturnCount")
override fun onReceive(context: Context?, intent: Intent?) {
if (intent == null) {
Log.w(TAG, "Received null intent")
@ -166,7 +168,7 @@ class MainActivity : AppCompatActivity() {
Log.d(TAG, "Position App NodeInfo: $ni")
mainTextView.text = "Position App NodeInfo: $ni"
} catch (e: Exception) {
e.printStackTrace()
Log.e(TAG, "onReceive: $e")
return
}
}
@ -189,7 +191,8 @@ class MainActivity : AppCompatActivity() {
while (!bindMeshService()) {
try {
Thread.sleep(1000)
@Suppress("MagicNumber")
Thread.sleep(1_000)
} catch (e: InterruptedException) {
Log.e(TAG, "Binding interrupted", e)
break