kopia lustrzana https://github.com/ge0rg/aprsdroid
refactor GPS position into own class
rodzic
bc72505fce
commit
9a84b4dd0d
|
@ -24,8 +24,6 @@ object AprsService {
|
|||
val STATUS = PACKAGE + ".STATUS"
|
||||
val PACKET = PACKAGE + ".PACKET"
|
||||
|
||||
val FAST_LANE_ACT = 30000
|
||||
|
||||
def intent(ctx : Context, action : String) : Intent = {
|
||||
new Intent(action, null, ctx, classOf[AprsService])
|
||||
}
|
||||
|
@ -39,26 +37,23 @@ object AprsService {
|
|||
|
||||
}
|
||||
|
||||
class AprsService extends Service with LocationListener {
|
||||
class AprsService extends Service {
|
||||
import AprsService._
|
||||
val TAG = "APRSdroid.Service"
|
||||
|
||||
lazy val prefs = new PrefsWrapper(this)
|
||||
|
||||
lazy val locMan = getSystemService(Context.LOCATION_SERVICE).asInstanceOf[LocationManager]
|
||||
|
||||
val handler = new Handler()
|
||||
|
||||
lazy val db = StorageDatabase.open(this)
|
||||
|
||||
lazy val msgService = new MessageService(this)
|
||||
lazy val locSource = new PeriodicGPS(this, prefs)
|
||||
lazy val msgNotifier = msgService.createMessageNotifier()
|
||||
|
||||
var poster : AprsIsUploader = null
|
||||
|
||||
var singleShot = false
|
||||
var lastLoc : Location = null
|
||||
var fastLaneLoc : Location = null
|
||||
|
||||
override def onStart(i : Intent, startId : Int) {
|
||||
Log.d(TAG, "onStart: " + i + ", " + startId);
|
||||
|
@ -72,25 +67,6 @@ class AprsService extends Service with LocationListener {
|
|||
Service.START_REDELIVER_INTENT
|
||||
}
|
||||
|
||||
def requestLocations(stay_on : Boolean) {
|
||||
// get update interval and distance
|
||||
val upd_int = prefs.getStringInt("interval", 10)
|
||||
val upd_dist = prefs.getStringInt("distance", 10)
|
||||
val gps_act = prefs.getString("gps_activation", "med")
|
||||
if (stay_on || (gps_act == "always")) {
|
||||
locMan.requestLocationUpdates(LocationManager.GPS_PROVIDER,
|
||||
0, 0, this)
|
||||
} else {
|
||||
// for GPS precision == medium, we use getGpsInterval()
|
||||
locMan.requestLocationUpdates(LocationManager.GPS_PROVIDER,
|
||||
upd_int * 60000 - getGpsInterval(), upd_dist * 1000, this)
|
||||
}
|
||||
if (prefs.getBoolean("netloc", false)) {
|
||||
locMan.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
|
||||
upd_int * 60000, upd_dist * 1000, this)
|
||||
}
|
||||
}
|
||||
|
||||
def handleStart(i : Intent) {
|
||||
// get update interval and distance
|
||||
val upd_int = prefs.getStringInt("interval", 10)
|
||||
|
@ -98,11 +74,10 @@ class AprsService extends Service with LocationListener {
|
|||
|
||||
// display notification (even though we are not actually started yet,
|
||||
// but we need this to prevent error message reordering)
|
||||
fastLaneLoc = null
|
||||
if (i.getAction() == SERVICE_ONCE) {
|
||||
// if already running, we want to send immediately and continue;
|
||||
// otherwise, we finish after a single position report
|
||||
lastLoc = null
|
||||
locSource.restart()
|
||||
// set to true if not yet running or already running singleShot
|
||||
singleShot = !running || singleShot
|
||||
if (singleShot)
|
||||
|
@ -120,7 +95,7 @@ class AprsService extends Service with LocationListener {
|
|||
}
|
||||
|
||||
// continuous GPS tracking for single shot mode
|
||||
requestLocations(singleShot)
|
||||
locSource.start(singleShot)
|
||||
|
||||
val callssid = prefs.getCallSsid()
|
||||
val message = "%s: %d min, %d km".format(callssid, upd_int, upd_dist)
|
||||
|
@ -151,150 +126,17 @@ class AprsService extends Service with LocationListener {
|
|||
showToast(getString(R.string.service_stop))
|
||||
}
|
||||
msgService.stop()
|
||||
locMan.removeUpdates(this);
|
||||
locSource.stop()
|
||||
unregisterReceiver(msgNotifier)
|
||||
ServiceNotifier.instance.stop(this)
|
||||
}
|
||||
|
||||
def getGpsInterval() : Int = {
|
||||
val gps_act = prefs.getString("gps_activation", "med")
|
||||
if (gps_act == "med") FAST_LANE_ACT
|
||||
else 0
|
||||
}
|
||||
|
||||
def startFastLane() {
|
||||
Log.d(TAG, "switching to fast lane");
|
||||
// request fast update rate
|
||||
locMan.removeUpdates(this);
|
||||
requestLocations(true)
|
||||
handler.postDelayed({ stopFastLane(true) }, FAST_LANE_ACT)
|
||||
}
|
||||
|
||||
def stopFastLane(post : Boolean) {
|
||||
if (!running)
|
||||
return;
|
||||
Log.d(TAG, "switching to slow lane");
|
||||
if (post && fastLaneLoc != null) {
|
||||
Log.d(TAG, "stopFastLane: posting " + fastLaneLoc);
|
||||
postLocation(fastLaneLoc)
|
||||
}
|
||||
fastLaneLoc = null
|
||||
// reset update speed
|
||||
locMan.removeUpdates(this);
|
||||
requestLocations(false)
|
||||
}
|
||||
|
||||
def goingFastLane(location : Location) : Boolean = {
|
||||
if (fastLaneLoc == null) {
|
||||
// need to set fastLaneLoc before re-requesting locations
|
||||
fastLaneLoc = location
|
||||
startFastLane()
|
||||
} else
|
||||
fastLaneLoc = location
|
||||
return true
|
||||
}
|
||||
|
||||
def smartBeaconSpeedRate(speed : Float) : Int = {
|
||||
val SB_FAST_SPEED = 28 // [m/s] = ~100km/h
|
||||
val SB_FAST_RATE = 60
|
||||
val SB_SLOW_SPEED = 1 // [m/s] = 3.6km/h
|
||||
val SB_SLOW_RATE = 1200
|
||||
if (speed <= SB_SLOW_SPEED)
|
||||
SB_SLOW_RATE
|
||||
else if (speed >= SB_FAST_SPEED)
|
||||
SB_FAST_RATE
|
||||
else
|
||||
((SB_SLOW_RATE - SB_FAST_RATE) * (SB_FAST_SPEED - speed) / (SB_FAST_SPEED-SB_SLOW_SPEED)).toInt
|
||||
}
|
||||
|
||||
// returns the angle between two bearings
|
||||
def getBearingAngle(alpha : Float, beta : Float) : Float = {
|
||||
val delta = math.abs(alpha-beta)%360
|
||||
if (delta <= 180) delta else (360-delta)
|
||||
}
|
||||
// obtain max speed in [m/s] from moved distance, last and current location
|
||||
def getSpeed(location : Location) : Float = {
|
||||
val dist = location.distanceTo(lastLoc)
|
||||
val t_diff = location.getTime - lastLoc.getTime
|
||||
math.max(math.max(dist*1000/t_diff, location.getSpeed), lastLoc.getSpeed)
|
||||
}
|
||||
|
||||
def smartBeaconCornerPeg(location : Location) : Boolean = {
|
||||
val SB_TURN_TIME = 15
|
||||
val SB_TURN_MIN = 10
|
||||
val SB_TURN_SLOPE = 240.0
|
||||
|
||||
val speed = getSpeed(location)
|
||||
val t_diff = location.getTime - lastLoc.getTime
|
||||
val turn = getBearingAngle(location.getBearing, lastLoc.getBearing)
|
||||
|
||||
// no bearing / stillstand -> no corner pegging
|
||||
if (!location.hasBearing || speed == 0)
|
||||
return false
|
||||
|
||||
// if last bearing unknown, deploy turn_time
|
||||
if (!lastLoc.hasBearing)
|
||||
return (t_diff/1000 >= SB_TURN_TIME)
|
||||
|
||||
// threshold depends on slope/speed [mph]
|
||||
val threshold = SB_TURN_MIN + SB_TURN_SLOPE/(speed*2.23693629)
|
||||
|
||||
Log.d(TAG, "smartBeaconCornerPeg: %1.0f < %1.0f %d/%d".format(turn, threshold,
|
||||
t_diff/1000, SB_TURN_TIME))
|
||||
// need to corner peg if turn time reached and turn > threshold
|
||||
(t_diff/1000 >= SB_TURN_TIME && turn > threshold)
|
||||
}
|
||||
|
||||
// return true if current position is "new enough" vs. lastLoc
|
||||
def smartBeaconCheck(location : Location) : Boolean = {
|
||||
if (lastLoc == null)
|
||||
return true
|
||||
if (smartBeaconCornerPeg(location))
|
||||
return true
|
||||
val dist = location.distanceTo(lastLoc)
|
||||
val t_diff = location.getTime - lastLoc.getTime
|
||||
val speed = getSpeed(location)
|
||||
//if (location.hasSpeed && location.hasBearing)
|
||||
val speed_rate = smartBeaconSpeedRate(speed)
|
||||
Log.d(TAG, "smartBeaconCheck: %1.0fm, %1.2fm/s -> %d/%ds - %s".format(dist, speed,
|
||||
t_diff/1000, speed_rate, (t_diff/1000 >= speed_rate).toString))
|
||||
if (t_diff/1000 >= speed_rate)
|
||||
true
|
||||
else
|
||||
false
|
||||
}
|
||||
|
||||
// LocationListener interface
|
||||
override def onLocationChanged(location : Location) {
|
||||
val upd_int = prefs.getStringInt("interval", 10) * 60000
|
||||
val upd_dist = prefs.getStringInt("distance", 10) * 1000
|
||||
if (prefs.getBoolean("smartbeaconing", true)) {
|
||||
if (!smartBeaconCheck(location))
|
||||
return
|
||||
} else /* no smartbeaconing */
|
||||
if (lastLoc != null &&
|
||||
(location.getTime - lastLoc.getTime < (upd_int - getGpsInterval()) ||
|
||||
location.distanceTo(lastLoc) < upd_dist)) {
|
||||
//Log.d(TAG, "onLocationChanged: ignoring premature location")
|
||||
return
|
||||
}
|
||||
// check if we need to go fast lane
|
||||
val gps_act = prefs.getString("gps_activation", "med")
|
||||
if (gps_act == "med" && location.getProvider == LocationManager.GPS_PROVIDER) {
|
||||
if (goingFastLane(location))
|
||||
return
|
||||
}
|
||||
postLocation(location)
|
||||
}
|
||||
|
||||
def appVersion() : String = {
|
||||
val pi = getPackageManager().getPackageInfo(getPackageName(), 0)
|
||||
"APDR%s".format(pi.versionName filter (_.isDigit) take 2)
|
||||
}
|
||||
|
||||
def postLocation(location : Location) {
|
||||
lastLoc = location
|
||||
|
||||
val i = new Intent(UPDATE)
|
||||
i.putExtra(LOCATION, location)
|
||||
|
||||
|
@ -329,23 +171,6 @@ class AprsService extends Service with LocationListener {
|
|||
}
|
||||
}
|
||||
|
||||
override def onProviderDisabled(provider : String) {
|
||||
Log.d(TAG, "onProviderDisabled: " + provider)
|
||||
val netloc_available = locMan.getProviders(true).contains(LocationManager.NETWORK_PROVIDER)
|
||||
val netloc_usable = netloc_available && prefs.getBoolean("netloc", false)
|
||||
if (provider == LocationManager.GPS_PROVIDER &&
|
||||
netloc_usable == false) {
|
||||
// GPS was our last data source, we have to complain!
|
||||
Toast.makeText(this, R.string.service_no_location, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
override def onProviderEnabled(provider : String) {
|
||||
Log.d(TAG, "onProviderEnabled: " + provider)
|
||||
}
|
||||
override def onStatusChanged(provider : String, st: Int, extras : Bundle) {
|
||||
Log.d(TAG, "onStatusChanged: " + provider)
|
||||
}
|
||||
|
||||
def parsePacket(ts : Long, message : String) {
|
||||
try {
|
||||
val fap = new Parser().parse(message)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.aprsdroid.app
|
||||
|
||||
import _root_.android.app.Service
|
||||
import _root_.android.content.Context
|
||||
import _root_.android.location.{Location, LocationManager}
|
||||
import _root_.android.util.Log
|
||||
import _root_.java.io.{BufferedReader, InputStreamReader, OutputStreamWriter, PrintWriter}
|
||||
|
@ -23,7 +24,8 @@ class TcpUploader(service : AprsService, prefs : PrefsWrapper) extends AprsIsUpl
|
|||
val filterdist = prefs.getStringInt("tcp.filterdist", 50)
|
||||
val userfilter = prefs.getString("tcp.filter", "")
|
||||
val lastloc = AprsPacket.formatRangeFilter(
|
||||
service.locMan.getLastKnownLocation(LocationManager.GPS_PROVIDER), filterdist)
|
||||
service.getSystemService(Context.LOCATION_SERVICE).asInstanceOf[LocationManager]
|
||||
.getLastKnownLocation(LocationManager.GPS_PROVIDER), filterdist)
|
||||
if (filterdist == 0) return " filter %s %s".format(userfilter, lastloc)
|
||||
else return " filter m/%d %s %s".format(filterdist, userfilter, lastloc)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,207 @@
|
|||
package org.aprsdroid.app
|
||||
|
||||
import _root_.android.content.Context
|
||||
import _root_.android.location._
|
||||
import _root_.android.os.{Bundle, Handler}
|
||||
import _root_.android.util.Log
|
||||
import _root_.android.widget.Toast
|
||||
|
||||
class PeriodicGPS(service : AprsService, prefs : PrefsWrapper) extends LocationListener {
|
||||
val TAG = "APRSdroid.PeriodicGPS"
|
||||
|
||||
val FAST_LANE_ACT = 30000
|
||||
|
||||
lazy val locMan = service.getSystemService(Context.LOCATION_SERVICE).asInstanceOf[LocationManager]
|
||||
|
||||
var lastLoc : Location = null
|
||||
var fastLaneLoc : Location = null
|
||||
|
||||
def start(singleShot : Boolean) {
|
||||
requestLocations(singleShot)
|
||||
}
|
||||
|
||||
def restart() {
|
||||
fastLaneLoc = null
|
||||
lastLoc = null
|
||||
}
|
||||
|
||||
def requestLocations(stay_on : Boolean) {
|
||||
// get update interval and distance
|
||||
val upd_int = prefs.getStringInt("interval", 10)
|
||||
val upd_dist = prefs.getStringInt("distance", 10)
|
||||
val gps_act = prefs.getString("gps_activation", "med")
|
||||
if (stay_on || (gps_act == "always")) {
|
||||
locMan.requestLocationUpdates(LocationManager.GPS_PROVIDER,
|
||||
0, 0, this)
|
||||
} else {
|
||||
// for GPS precision == medium, we use getGpsInterval()
|
||||
locMan.requestLocationUpdates(LocationManager.GPS_PROVIDER,
|
||||
upd_int * 60000 - getGpsInterval(), upd_dist * 1000, this)
|
||||
}
|
||||
if (prefs.getBoolean("netloc", false)) {
|
||||
locMan.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
|
||||
upd_int * 60000, upd_dist * 1000, this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def stop() {
|
||||
locMan.removeUpdates(this);
|
||||
}
|
||||
|
||||
def getGpsInterval() : Int = {
|
||||
val gps_act = prefs.getString("gps_activation", "med")
|
||||
if (gps_act == "med") FAST_LANE_ACT
|
||||
else 0
|
||||
}
|
||||
|
||||
def startFastLane() {
|
||||
import AprsService.block2runnable
|
||||
Log.d(TAG, "switching to fast lane");
|
||||
// request fast update rate
|
||||
locMan.removeUpdates(this);
|
||||
requestLocations(true)
|
||||
service.handler.postDelayed({ stopFastLane(true) }, FAST_LANE_ACT)
|
||||
}
|
||||
|
||||
def stopFastLane(post : Boolean) {
|
||||
if (!AprsService.running)
|
||||
return;
|
||||
Log.d(TAG, "switching to slow lane");
|
||||
if (post && fastLaneLoc != null) {
|
||||
Log.d(TAG, "stopFastLane: posting " + fastLaneLoc);
|
||||
postLocation(fastLaneLoc)
|
||||
}
|
||||
fastLaneLoc = null
|
||||
// reset update speed
|
||||
locMan.removeUpdates(this);
|
||||
requestLocations(false)
|
||||
}
|
||||
|
||||
def goingFastLane(location : Location) : Boolean = {
|
||||
if (fastLaneLoc == null) {
|
||||
// need to set fastLaneLoc before re-requesting locations
|
||||
fastLaneLoc = location
|
||||
startFastLane()
|
||||
} else
|
||||
fastLaneLoc = location
|
||||
return true
|
||||
}
|
||||
|
||||
def smartBeaconSpeedRate(speed : Float) : Int = {
|
||||
val SB_FAST_SPEED = 28 // [m/s] = ~100km/h
|
||||
val SB_FAST_RATE = 60
|
||||
val SB_SLOW_SPEED = 1 // [m/s] = 3.6km/h
|
||||
val SB_SLOW_RATE = 1200
|
||||
if (speed <= SB_SLOW_SPEED)
|
||||
SB_SLOW_RATE
|
||||
else if (speed >= SB_FAST_SPEED)
|
||||
SB_FAST_RATE
|
||||
else
|
||||
((SB_SLOW_RATE - SB_FAST_RATE) * (SB_FAST_SPEED - speed) / (SB_FAST_SPEED-SB_SLOW_SPEED)).toInt
|
||||
}
|
||||
|
||||
// returns the angle between two bearings
|
||||
def getBearingAngle(alpha : Float, beta : Float) : Float = {
|
||||
val delta = math.abs(alpha-beta)%360
|
||||
if (delta <= 180) delta else (360-delta)
|
||||
}
|
||||
// obtain max speed in [m/s] from moved distance, last and current location
|
||||
def getSpeed(location : Location) : Float = {
|
||||
val dist = location.distanceTo(lastLoc)
|
||||
val t_diff = location.getTime - lastLoc.getTime
|
||||
math.max(math.max(dist*1000/t_diff, location.getSpeed), lastLoc.getSpeed)
|
||||
}
|
||||
|
||||
def smartBeaconCornerPeg(location : Location) : Boolean = {
|
||||
val SB_TURN_TIME = 15
|
||||
val SB_TURN_MIN = 10
|
||||
val SB_TURN_SLOPE = 240.0
|
||||
|
||||
val speed = getSpeed(location)
|
||||
val t_diff = location.getTime - lastLoc.getTime
|
||||
val turn = getBearingAngle(location.getBearing, lastLoc.getBearing)
|
||||
|
||||
// no bearing / stillstand -> no corner pegging
|
||||
if (!location.hasBearing || speed == 0)
|
||||
return false
|
||||
|
||||
// if last bearing unknown, deploy turn_time
|
||||
if (!lastLoc.hasBearing)
|
||||
return (t_diff/1000 >= SB_TURN_TIME)
|
||||
|
||||
// threshold depends on slope/speed [mph]
|
||||
val threshold = SB_TURN_MIN + SB_TURN_SLOPE/(speed*2.23693629)
|
||||
|
||||
Log.d(TAG, "smartBeaconCornerPeg: %1.0f < %1.0f %d/%d".format(turn, threshold,
|
||||
t_diff/1000, SB_TURN_TIME))
|
||||
// need to corner peg if turn time reached and turn > threshold
|
||||
(t_diff/1000 >= SB_TURN_TIME && turn > threshold)
|
||||
}
|
||||
|
||||
// return true if current position is "new enough" vs. lastLoc
|
||||
def smartBeaconCheck(location : Location) : Boolean = {
|
||||
if (lastLoc == null)
|
||||
return true
|
||||
if (smartBeaconCornerPeg(location))
|
||||
return true
|
||||
val dist = location.distanceTo(lastLoc)
|
||||
val t_diff = location.getTime - lastLoc.getTime
|
||||
val speed = getSpeed(location)
|
||||
//if (location.hasSpeed && location.hasBearing)
|
||||
val speed_rate = smartBeaconSpeedRate(speed)
|
||||
Log.d(TAG, "smartBeaconCheck: %1.0fm, %1.2fm/s -> %d/%ds - %s".format(dist, speed,
|
||||
t_diff/1000, speed_rate, (t_diff/1000 >= speed_rate).toString))
|
||||
if (t_diff/1000 >= speed_rate)
|
||||
true
|
||||
else
|
||||
false
|
||||
}
|
||||
|
||||
// LocationListener interface
|
||||
override def onLocationChanged(location : Location) {
|
||||
val upd_int = prefs.getStringInt("interval", 10) * 60000
|
||||
val upd_dist = prefs.getStringInt("distance", 10) * 1000
|
||||
if (prefs.getBoolean("smartbeaconing", true)) {
|
||||
if (!smartBeaconCheck(location))
|
||||
return
|
||||
} else /* no smartbeaconing */
|
||||
if (lastLoc != null &&
|
||||
(location.getTime - lastLoc.getTime < (upd_int - getGpsInterval()) ||
|
||||
location.distanceTo(lastLoc) < upd_dist)) {
|
||||
//Log.d(TAG, "onLocationChanged: ignoring premature location")
|
||||
return
|
||||
}
|
||||
// check if we need to go fast lane
|
||||
val gps_act = prefs.getString("gps_activation", "med")
|
||||
if (gps_act == "med" && location.getProvider == LocationManager.GPS_PROVIDER) {
|
||||
if (goingFastLane(location))
|
||||
return
|
||||
}
|
||||
postLocation(location)
|
||||
}
|
||||
|
||||
override def onProviderDisabled(provider : String) {
|
||||
Log.d(TAG, "onProviderDisabled: " + provider)
|
||||
val netloc_available = locMan.getProviders(true).contains(LocationManager.NETWORK_PROVIDER)
|
||||
val netloc_usable = netloc_available && prefs.getBoolean("netloc", false)
|
||||
if (provider == LocationManager.GPS_PROVIDER &&
|
||||
netloc_usable == false) {
|
||||
// GPS was our last data source, we have to complain!
|
||||
Toast.makeText(service, R.string.service_no_location, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
override def onProviderEnabled(provider : String) {
|
||||
Log.d(TAG, "onProviderEnabled: " + provider)
|
||||
}
|
||||
override def onStatusChanged(provider : String, st: Int, extras : Bundle) {
|
||||
Log.d(TAG, "onStatusChanged: " + provider)
|
||||
}
|
||||
|
||||
|
||||
def postLocation(location : Location) {
|
||||
lastLoc = location
|
||||
|
||||
service.postLocation(location)
|
||||
}
|
||||
}
|
Ładowanie…
Reference in New Issue