kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
feat: implement MSL altitude using `AltitudeConverterCompat` (#1094)
- Added `androidx.core:core-location-altitude:1.0.0-alpha02`; - Implemented `AltitudeConverterCompat.addMslAltitudeToLocation(context, location)` to convert `altitude` (above the WGS84 reference ellipsoid) to Mean Sea Level (MSL) and add MSL altitude and accuracy to the location object. Reference: - https://issuetracker.google.com/issues/195660815 - Brian Julian and Michael Angermann. "Resource efficient and accurate altitude conversion to Mean Sea Level." [2023 IEEE/ION Position, Location and Navigation Symposium (PLANS)](https://www.ion.org/plans/abstracts.cfm?paperID=12011).pull/1088/head
rodzic
8c53908eb5
commit
001b18be95
|
@ -152,6 +152,7 @@ dependencies {
|
|||
implementation "androidx.emoji2:emoji2-emojipicker:1.4.0"
|
||||
|
||||
implementation 'androidx.core:core-ktx:1.13.1'
|
||||
implementation 'androidx.core:core-location-altitude:1.0.0-alpha02'
|
||||
implementation 'androidx.fragment:fragment-ktx:1.7.1'
|
||||
implementation 'androidx.cardview:cardview:1.0.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.3.2'
|
||||
|
|
|
@ -3,9 +3,11 @@ package com.geeksville.mesh.repository.location
|
|||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import android.location.LocationManager
|
||||
import androidx.core.location.LocationCompat
|
||||
import androidx.core.location.LocationListenerCompat
|
||||
import androidx.core.location.LocationManagerCompat
|
||||
import androidx.core.location.LocationRequestCompat
|
||||
import androidx.core.location.altitude.AltitudeConverterCompat
|
||||
import com.geeksville.mesh.android.GeeksvilleApplication
|
||||
import com.geeksville.mesh.android.Logging
|
||||
import com.geeksville.mesh.android.hasBackgroundPermission
|
||||
|
@ -43,6 +45,13 @@ class LocationRepository @Inject constructor(
|
|||
.build()
|
||||
|
||||
val locationListener = LocationListenerCompat { location ->
|
||||
if (location.hasAltitude() && !LocationCompat.hasMslAltitude(location)) {
|
||||
try {
|
||||
AltitudeConverterCompat.addMslAltitudeToLocation(context, location)
|
||||
} catch (e: Exception) {
|
||||
errormsg("addMslAltitudeToLocation() failed", e)
|
||||
}
|
||||
}
|
||||
// info("New location: $location")
|
||||
trySend(location)
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import android.content.pm.ServiceInfo
|
|||
import android.os.IBinder
|
||||
import android.os.RemoteException
|
||||
import androidx.core.app.ServiceCompat
|
||||
import androidx.core.location.LocationCompat
|
||||
import com.geeksville.mesh.analytics.DataPair
|
||||
import com.geeksville.mesh.android.GeeksvilleApplication
|
||||
import com.geeksville.mesh.android.Logging
|
||||
|
@ -175,16 +176,19 @@ class MeshService : Service(), Logging {
|
|||
if (locationFlow?.isActive == true) return
|
||||
|
||||
if (hasBackgroundPermission()) {
|
||||
locationFlow = locationRepository.getLocations()
|
||||
.onEach { location ->
|
||||
sendPosition(
|
||||
location.latitude,
|
||||
location.longitude,
|
||||
location.altitude.toInt(),
|
||||
(location.time / 1000).toInt(),
|
||||
)
|
||||
}
|
||||
.launchIn(serviceScope)
|
||||
locationFlow = locationRepository.getLocations().onEach { location ->
|
||||
sendPosition(
|
||||
position {
|
||||
latitudeI = Position.degI(location.latitude)
|
||||
longitudeI = Position.degI(location.longitude)
|
||||
altitude = LocationCompat.getMslAltitudeMeters(location).toInt()
|
||||
altitudeHae = location.altitude.toInt()
|
||||
time = (location.time / 1000).toInt()
|
||||
groundSpeed = location.speed.toInt()
|
||||
groundTrack = location.bearing.toInt()
|
||||
}
|
||||
)
|
||||
}.launchIn(serviceScope)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -802,13 +806,14 @@ class MeshService : Service(), Logging {
|
|||
// Nodes periodically send out position updates, but those updates might not contain a lat & lon (because no GPS lock)
|
||||
// We like to look at the local node to see if it has been sending out valid lat/lon, so for the LOCAL node (only)
|
||||
// we don't record these nop position updates
|
||||
if (myNodeNum == fromNum && p.latitudeI == 0 && p.longitudeI == 0)
|
||||
if (myNodeNum == fromNum && p.latitudeI == 0 && p.longitudeI == 0) {
|
||||
debug("Ignoring nop position update for the local node")
|
||||
else
|
||||
} else {
|
||||
updateNodeInfo(fromNum) {
|
||||
debug("update position: ${it.user?.longName?.toPIIString()} with ${p.toPIIString()}")
|
||||
it.position = Position(p, (defaultTime / 1000L).toInt())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Update our DB of users based on someone sending out a Telemetry subpacket
|
||||
|
@ -1573,10 +1578,7 @@ class MeshService : Service(), Logging {
|
|||
* Send a position (typically from our built in GPS) into the mesh.
|
||||
*/
|
||||
private fun sendPosition(
|
||||
lat: Double = 0.0,
|
||||
lon: Double = 0.0,
|
||||
alt: Int = 0,
|
||||
time: Int = currentSecond(),
|
||||
position: MeshProtos.Position,
|
||||
destNum: Int? = null,
|
||||
wantResponse: Boolean = false
|
||||
) {
|
||||
|
@ -1584,30 +1586,19 @@ class MeshService : Service(), Logging {
|
|||
val mi = myNodeInfo
|
||||
if (mi != null) {
|
||||
val idNum = destNum ?: mi.myNodeNum // when null we just send to the local node
|
||||
debug("Sending our position/time to=$idNum lat=${lat.anonymize}, lon=${lon.anonymize}, alt=$alt, time=$time")
|
||||
|
||||
val position = MeshProtos.Position.newBuilder().also {
|
||||
it.longitudeI = Position.degI(lon)
|
||||
it.latitudeI = Position.degI(lat)
|
||||
|
||||
it.altitude = alt
|
||||
it.time = time
|
||||
}.build()
|
||||
debug("Sending our position/time to=$idNum ${Position(position)}")
|
||||
|
||||
// Also update our own map for our nodeNum, by handling the packet just like packets from other users
|
||||
handleReceivedPosition(mi.myNodeNum, position)
|
||||
|
||||
val fullPacket = newMeshPacketTo(idNum).buildMeshPacket(
|
||||
sendToRadio(newMeshPacketTo(idNum).buildMeshPacket(
|
||||
channel = if (destNum == null) 0 else nodeDBbyNodeNum[destNum]?.channel ?: 0,
|
||||
priority = MeshPacket.Priority.BACKGROUND,
|
||||
) {
|
||||
portnumValue = Portnums.PortNum.POSITION_APP_VALUE
|
||||
payload = position.toByteString()
|
||||
this.wantResponse = wantResponse
|
||||
}
|
||||
|
||||
// send the packet into the mesh
|
||||
sendToRadio(fullPacket)
|
||||
})
|
||||
}
|
||||
} catch (ex: BLEException) {
|
||||
warn("Ignoring disconnected radio during gps location update")
|
||||
|
@ -1916,16 +1907,15 @@ class MeshService : Service(), Logging {
|
|||
wantResponse = true
|
||||
})
|
||||
} else {
|
||||
// send fixed position (local only/no remote method, so we force destNum to null)
|
||||
val (lat, lon, alt) = position
|
||||
sendPosition(destNum = null, lat = lat, lon = lon, alt = alt)
|
||||
// send fixed position (local only/no remote method)
|
||||
sendToRadio(newMeshPacketTo(destNum).buildAdminPacket {
|
||||
if (position != Position(0.0, 0.0, 0)) {
|
||||
setFixedPosition = position {
|
||||
longitudeI = Position.degI(lon)
|
||||
latitudeI = Position.degI(lat)
|
||||
altitude = alt
|
||||
latitudeI = Position.degI(position.latitude)
|
||||
longitudeI = Position.degI(position.longitude)
|
||||
altitude = position.altitude
|
||||
}
|
||||
.also { sendPosition(it) } // TODO remove after minDeviceVersion >= 2.3.3
|
||||
} else {
|
||||
removeFixedPosition = true
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue