Merge branch 'mapbox' into mapbox-sdkv3
This commit is contained in:
commit
c67f9f9726
14 changed files with 962 additions and 202 deletions
1
.gitattributes
vendored
1
.gitattributes
vendored
|
@ -1 +1,2 @@
|
||||||
* text eol=lf
|
* text eol=lf
|
||||||
|
*.bat text eol=crlf
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -8,3 +8,4 @@
|
||||||
.externalNativeBuild
|
.externalNativeBuild
|
||||||
.cxx
|
.cxx
|
||||||
local.properties
|
local.properties
|
||||||
|
__pycache__/
|
||||||
|
|
|
@ -53,6 +53,7 @@ dependencies {
|
||||||
implementation 'com.google.android.material:material:1.12.0'
|
implementation 'com.google.android.material:material:1.12.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter:5.8.1'
|
testImplementation 'org.junit.jupiter:junit-jupiter:5.8.1'
|
||||||
|
testImplementation 'org.assertj:assertj-core:3.24.2'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
|
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,11 @@ import android.bluetooth.BluetoothDevice
|
||||||
import android.bluetooth.BluetoothSocket
|
import android.bluetooth.BluetoothSocket
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.mapbox.navigation.tripdata.maneuver.model.Maneuver
|
import com.mapbox.navigation.tripdata.maneuver.model.Maneuver
|
||||||
|
import com.mapbox.navigation.core.trip.session.LocationMatcherResult
|
||||||
|
import com.mapbox.navigation.core.trip.session.NavigationSessionState
|
||||||
|
import eu.ztsh.garmin.data.DataCache
|
||||||
|
import eu.ztsh.garmin.data.GarminMapper
|
||||||
|
import eu.ztsh.garmin.data.MapboxMapper
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.SynchronousQueue
|
import java.util.concurrent.SynchronousQueue
|
||||||
|
@ -19,7 +24,7 @@ class Garmin(
|
||||||
|
|
||||||
private lateinit var connection: ConnectThread
|
private lateinit var connection: ConnectThread
|
||||||
private lateinit var processing: ProcessingThread
|
private lateinit var processing: ProcessingThread
|
||||||
private var stateCache: State = State()
|
private val cache = DataCache()
|
||||||
|
|
||||||
fun start() {
|
fun start() {
|
||||||
connection = ConnectThread()
|
connection = ConnectThread()
|
||||||
|
@ -31,28 +36,42 @@ class Garmin(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun process(maneuver: Maneuver) {
|
fun process(maneuver: Maneuver) {
|
||||||
processing = ProcessingThread(maneuver)
|
processing = ManeuverProcessingThread(maneuver)
|
||||||
processing.start()
|
processing.start()
|
||||||
processing.join()
|
processing.join()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun process(location: LocationMatcherResult) {
|
||||||
|
processing = LocationProcessingThread(location)
|
||||||
|
processing.start()
|
||||||
|
processing.join()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun process(navigationSessionState: NavigationSessionState) {
|
||||||
|
cache.update(navigationSessionState)
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class ManeuverProcessingThread(val maneuver: Maneuver) : ProcessingThread() {
|
||||||
|
|
||||||
private inner class ProcessingThread(val maneuver: Maneuver) : Thread() {
|
|
||||||
override fun run() {
|
override fun run() {
|
||||||
send(ManeuverMapper.apply(maneuver))
|
if (cache.hasChanged(maneuver)) {
|
||||||
|
cache.update(maneuver)
|
||||||
|
send(MapboxMapper.apply(maneuver))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun send(incoming: eu.ztsh.garmin.State) {
|
}
|
||||||
if (stateCache.distance != incoming.distance) {
|
|
||||||
setDistance(incoming)
|
private inner class LocationProcessingThread(val location: LocationMatcherResult) : ProcessingThread() {
|
||||||
|
|
||||||
|
override fun run() {
|
||||||
|
if (cache.hasChanged(location)) {
|
||||||
|
cache.update(location)
|
||||||
|
send(MapboxMapper.apply(location))
|
||||||
}
|
}
|
||||||
if (stateCache.direction != incoming.direction) {
|
|
||||||
setDirection(stateCache.direction)
|
|
||||||
}
|
|
||||||
stateCache = incoming
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
private fun setDistance(state: eu.ztsh.garmin.State) {
|
private fun setDistance(state: eu.ztsh.garmin.State) {
|
||||||
connection.enqueue(intArrayOf(
|
connection.enqueue(intArrayOf(
|
||||||
0x03,
|
0x03,
|
||||||
|
@ -65,20 +84,16 @@ class Garmin(
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setDirection(direction: Direction) {
|
private open inner class ProcessingThread : Thread() {
|
||||||
val param1 = when (direction.outAngle) {
|
|
||||||
OutAngle.LeftDown -> 0x10
|
fun send(incoming: eu.ztsh.garmin.data.State) {
|
||||||
OutAngle.RightDown -> 0x20
|
if (cache.hasChanged(incoming.distance)) {
|
||||||
else -> direction.outType.data
|
connection.enqueue(GarminMapper.setDistance(incoming))
|
||||||
}
|
}
|
||||||
val param2: Int = if (direction.outType == OutType.RightRoundabout
|
if (cache.hasChanged(incoming.direction)) {
|
||||||
|| direction.outType == OutType.LeftRoundabout) {
|
connection.enqueue(GarminMapper.setDirection(incoming))
|
||||||
if (direction.roundabout == OutAngle.AsDirection) direction.outAngle.data else direction.roundabout.data
|
|
||||||
} else {
|
|
||||||
0x00
|
|
||||||
}
|
}
|
||||||
val param3: Int = if (direction.outAngle == OutAngle.LeftDown || direction.outAngle == OutAngle.RightDown) 0x00 else direction.outAngle.data
|
cache.update(incoming)
|
||||||
connection.enqueue(intArrayOf(0x01, param1, param2, param3))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun asDigit(input: Double): Int {
|
private fun asDigit(input: Double): Int {
|
||||||
|
@ -111,10 +126,13 @@ class Garmin(
|
||||||
sleep(3000)
|
sleep(3000)
|
||||||
readAll()
|
readAll()
|
||||||
while (true) {
|
while (true) {
|
||||||
val newCurrent = Optional.ofNullable(queue.poll()).orElse(current)
|
val newCurrent = queue.poll()
|
||||||
current = newCurrent
|
if (newCurrent == null) {
|
||||||
|
sleep(500)
|
||||||
|
} else {
|
||||||
|
current = newCurrent
|
||||||
|
}
|
||||||
send(current)
|
send(current)
|
||||||
sleep(900)
|
|
||||||
}
|
}
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
Log.d(TAG, "Not connected", e)
|
Log.d(TAG, "Not connected", e)
|
||||||
|
|
|
@ -51,6 +51,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
mapControl.initNavigation()
|
mapControl.initNavigation()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
private val mapboxToolbox = MapboxToolbox(lifecycle, this)
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
@ -66,14 +67,26 @@ class MainActivity : AppCompatActivity() {
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
mapboxToolbox.onCreate()
|
||||||
bluetoothInit()
|
bluetoothInit()
|
||||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onStart() {
|
||||||
|
super.onStart()
|
||||||
|
mapboxToolbox.onStart()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStop() {
|
||||||
|
super.onStop()
|
||||||
|
mapboxToolbox.onStop()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
mapControl.onDestroy()
|
mapControl.onDestroy()
|
||||||
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
|
mapboxToolbox.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bluetoothInit() {
|
private fun bluetoothInit() {
|
||||||
|
|
96
app/src/main/java/eu/ztsh/garmin/MapboxToolbox.kt
Normal file
96
app/src/main/java/eu/ztsh/garmin/MapboxToolbox.kt
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
package eu.ztsh.garmin
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.location.Location
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.lifecycle.DefaultLifecycleObserver
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import com.mapbox.navigation.base.formatter.DistanceFormatterOptions
|
||||||
|
import com.mapbox.navigation.base.options.NavigationOptions
|
||||||
|
import com.mapbox.navigation.core.formatter.MapboxDistanceFormatter
|
||||||
|
import com.mapbox.navigation.core.lifecycle.MapboxNavigationApp
|
||||||
|
import com.mapbox.navigation.core.trip.session.LocationMatcherResult
|
||||||
|
import com.mapbox.navigation.core.trip.session.LocationObserver
|
||||||
|
import com.mapbox.navigation.core.trip.session.NavigationSessionState
|
||||||
|
import com.mapbox.navigation.core.trip.session.NavigationSessionStateObserver
|
||||||
|
import com.mapbox.navigation.core.trip.session.RouteProgressObserver
|
||||||
|
import com.mapbox.navigation.ui.maneuver.api.MapboxManeuverApi
|
||||||
|
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
|
class MapboxToolbox(lifecycle: Lifecycle, private val context: MainActivity) {
|
||||||
|
|
||||||
|
private val mapboxObserver = MapboxObserver()
|
||||||
|
|
||||||
|
init {
|
||||||
|
lifecycle.addObserver(object : DefaultLifecycleObserver {
|
||||||
|
override fun onResume(owner: LifecycleOwner) {
|
||||||
|
MapboxNavigationApp.attach(owner)
|
||||||
|
MapboxNavigationApp.registerObserver(mapboxObserver)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause(owner: LifecycleOwner) {
|
||||||
|
MapboxNavigationApp.detach(owner)
|
||||||
|
MapboxNavigationApp.unregisterObserver(mapboxObserver)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onCreate() {
|
||||||
|
if (!MapboxNavigationApp.isSetup()) {
|
||||||
|
MapboxNavigationApp.setup {
|
||||||
|
NavigationOptions.Builder(context)
|
||||||
|
.accessToken(BuildConfig.MAPBOX_DOWNLOADS_TOKEN)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MapboxNavigationApp.current()?.startTripSession()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onStart() {
|
||||||
|
MapboxNavigationApp.current()?.registerRouteProgressObserver(routeProgressObserver)
|
||||||
|
MapboxNavigationApp.current()?.registerLocationObserver(locationObserver)
|
||||||
|
MapboxNavigationApp.current()?.registerNavigationSessionStateObserver(navigationStateObserver)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onStop() {
|
||||||
|
MapboxNavigationApp.current()?.unregisterRouteProgressObserver(routeProgressObserver)
|
||||||
|
MapboxNavigationApp.current()?.unregisterLocationObserver(locationObserver)
|
||||||
|
MapboxNavigationApp.current()?.unregisterNavigationSessionStateObserver(navigationStateObserver)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onDestroy() {
|
||||||
|
MapboxNavigationApp.current()?.stopTripSession()
|
||||||
|
maneuverApi.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define distance formatter options
|
||||||
|
private val distanceFormatter: DistanceFormatterOptions by lazy {
|
||||||
|
DistanceFormatterOptions.Builder(context).build()
|
||||||
|
}
|
||||||
|
// Create an instance of the Maneuver API
|
||||||
|
private val maneuverApi: MapboxManeuverApi by lazy {
|
||||||
|
MapboxManeuverApi(MapboxDistanceFormatter(distanceFormatter))
|
||||||
|
}
|
||||||
|
|
||||||
|
private val routeProgressObserver =
|
||||||
|
RouteProgressObserver { routeProgress ->
|
||||||
|
maneuverApi.getManeuvers(routeProgress).value?.apply {
|
||||||
|
context.garmin.process(
|
||||||
|
this[0]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val locationObserver = object : LocationObserver {
|
||||||
|
override fun onNewLocationMatcherResult(locationMatcherResult: LocationMatcherResult) {
|
||||||
|
context.garmin.process(locationMatcherResult)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNewRawLocation(rawLocation: Location) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val navigationStateObserver = NavigationSessionStateObserver { context.garmin.process(it) }
|
||||||
|
|
||||||
|
}
|
|
@ -1,96 +0,0 @@
|
||||||
package eu.ztsh.garmin
|
|
||||||
|
|
||||||
enum class OutType(val data: Int) {
|
|
||||||
|
|
||||||
Off(0x00),
|
|
||||||
Lane(0x01),
|
|
||||||
LongerLane(0x02),
|
|
||||||
LeftRoundabout(0x04),
|
|
||||||
RightRoundabout(0x08),
|
|
||||||
Flag(0x40),
|
|
||||||
ArrowOnly(0x80);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
enum class OutAngle(val data: Int) {
|
|
||||||
|
|
||||||
Down(0x01),
|
|
||||||
SharpRight(0x02),
|
|
||||||
Right(0x04),
|
|
||||||
EasyRight(0x08),
|
|
||||||
Straight(0x10),
|
|
||||||
EasyLeft(0x20),
|
|
||||||
Left(0x40),
|
|
||||||
SharpLeft(0x80),
|
|
||||||
LeftDown(0x81),
|
|
||||||
RightDown(0x82),
|
|
||||||
AsDirection(0x00)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class Unit(val data: Int) {
|
|
||||||
|
|
||||||
Any(0),
|
|
||||||
Metres(1),
|
|
||||||
Kilometres(3),
|
|
||||||
Miles(5),
|
|
||||||
Foot(8)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class Lane(val data: Int) {
|
|
||||||
|
|
||||||
DotsRight(0x01),
|
|
||||||
OuterRight(0x02),
|
|
||||||
MiddleRight(0x04),
|
|
||||||
InnerRight(0x08),
|
|
||||||
InnerLeft(0x10),
|
|
||||||
MiddleLeft(0x20),
|
|
||||||
OuterLeft(0x40),
|
|
||||||
DotsLeft(0x80)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class State {
|
|
||||||
|
|
||||||
var lineArrows: Int = 0
|
|
||||||
var lineOutlines: Int = 0
|
|
||||||
var direction = Direction()
|
|
||||||
var distance: Double = 0.0
|
|
||||||
var unit: Unit = Unit.Any
|
|
||||||
var speed: Int = 0
|
|
||||||
var limit: Int = 0
|
|
||||||
var hours: Int = 0
|
|
||||||
var minutes: Int = 0
|
|
||||||
var traffic: Boolean = false
|
|
||||||
var flag: Boolean = false
|
|
||||||
var control: Boolean = false
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class Direction {
|
|
||||||
var outAngle: OutAngle = OutAngle.AsDirection
|
|
||||||
var outType: OutType = OutType.Lane
|
|
||||||
var roundabout: OutAngle = OutAngle.AsDirection
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (javaClass != other?.javaClass) return false
|
|
||||||
|
|
||||||
other as Direction
|
|
||||||
|
|
||||||
if (outAngle != other.outAngle) return false
|
|
||||||
if (outType != other.outType) return false
|
|
||||||
if (roundabout != other.roundabout) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = outAngle.hashCode()
|
|
||||||
result = 31 * result + outType.hashCode()
|
|
||||||
result = 31 * result + roundabout.hashCode()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
88
app/src/main/java/eu/ztsh/garmin/data/DataCache.kt
Normal file
88
app/src/main/java/eu/ztsh/garmin/data/DataCache.kt
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
package eu.ztsh.garmin.data
|
||||||
|
|
||||||
|
import com.mapbox.navigation.core.trip.session.LocationMatcherResult
|
||||||
|
import com.mapbox.navigation.core.trip.session.NavigationSessionState
|
||||||
|
import com.mapbox.navigation.ui.maneuver.model.Maneuver
|
||||||
|
|
||||||
|
class DataCache {
|
||||||
|
|
||||||
|
private val stateCache: State = State()
|
||||||
|
private var maneuverCache: Maneuver? = null
|
||||||
|
private var locationCache: LocationMatcherResult? = null
|
||||||
|
private var session: NavigationSessionState? = null
|
||||||
|
|
||||||
|
// state
|
||||||
|
fun hasChanged(lanes: Lanes?): Boolean {
|
||||||
|
return stateCache.lineArrows == null || stateCache.lineArrows != lanes
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasChanged(outlines: Outlines?): Boolean {
|
||||||
|
return stateCache.lineOutlines == null || stateCache.lineOutlines != outlines
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasChanged(distance: Distance?): Boolean {
|
||||||
|
return stateCache.distance == null || stateCache.distance != distance
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasChanged(direction: Direction?): Boolean {
|
||||||
|
return stateCache.direction == null || stateCache.direction != direction
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasChanged(speed: Speed?): Boolean {
|
||||||
|
return stateCache.speed == null || stateCache.speed != speed
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasChanged(arrival: Arrival?): Boolean {
|
||||||
|
return stateCache.arrival == null || stateCache.arrival != arrival
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge states
|
||||||
|
fun update(state: State) {
|
||||||
|
if (state.lineArrows != null) {
|
||||||
|
stateCache.lineArrows = state.lineArrows
|
||||||
|
}
|
||||||
|
if (state.lineOutlines != null) {
|
||||||
|
state.lineOutlines = state.lineOutlines
|
||||||
|
}
|
||||||
|
if (state.direction != null) {
|
||||||
|
stateCache.direction = state.direction
|
||||||
|
}
|
||||||
|
if (state.distance != null) {
|
||||||
|
stateCache.distance = state.distance
|
||||||
|
}
|
||||||
|
if (state.speed != null) {
|
||||||
|
stateCache.speed = state.speed
|
||||||
|
}
|
||||||
|
if (state.arrival != null) {
|
||||||
|
stateCache.arrival = state.arrival
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// maneuver
|
||||||
|
fun hasChanged(maneuver: Maneuver): Boolean {
|
||||||
|
return maneuverCache == null || maneuverCache!! != maneuver
|
||||||
|
}
|
||||||
|
|
||||||
|
fun update(maneuver: Maneuver) {
|
||||||
|
maneuverCache = maneuver
|
||||||
|
}
|
||||||
|
|
||||||
|
// location
|
||||||
|
fun hasChanged(locationMatcherResult: LocationMatcherResult): Boolean {
|
||||||
|
return locationCache == null || locationCache!! != locationMatcherResult
|
||||||
|
}
|
||||||
|
|
||||||
|
fun update(locationMatcherResult: LocationMatcherResult) {
|
||||||
|
locationCache = locationMatcherResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// session
|
||||||
|
fun isActive(): Boolean {
|
||||||
|
return session != null && session is NavigationSessionState.ActiveGuidance
|
||||||
|
}
|
||||||
|
|
||||||
|
fun update(sessionState: NavigationSessionState) {
|
||||||
|
session = sessionState
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
151
app/src/main/java/eu/ztsh/garmin/data/GarminMapper.kt
Normal file
151
app/src/main/java/eu/ztsh/garmin/data/GarminMapper.kt
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
package eu.ztsh.garmin.data
|
||||||
|
|
||||||
|
class GarminMapper {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
fun setLines(state: State): IntArray {
|
||||||
|
return intArrayOf(0x02, state.lineOutlines.sumOf { it.value }, state.lineArrows.sumOf { it.value })
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setDirection(state: State): IntArray {
|
||||||
|
return setDirection(state.direction.angle, state.direction.out, state.direction.roundabout)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setDistance(state: State): IntArray {
|
||||||
|
return setDistance(state.distance, state.unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSpeed(state: State): IntArray {
|
||||||
|
return setSpeed(state.speed, state.limit, state.speed > state.limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSpeedFreeRide(state: State): Pair<IntArray, IntArray> {
|
||||||
|
return Pair(setDistance(state.speed), setSpeed(state.limit, limitWarning = state.speed > state.limit))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setTime(hours: Int, minutes: Int, traffic: Boolean = false, flag: Boolean = false): IntArray {
|
||||||
|
val trafficChar = asChar(traffic)
|
||||||
|
val flagChar = asChar(flag)
|
||||||
|
return if (hours > 99) {
|
||||||
|
intArrayOf(
|
||||||
|
0x05,
|
||||||
|
trafficChar,
|
||||||
|
asDigit(hours / 1000),
|
||||||
|
asDigit(hours / 100),
|
||||||
|
0x00,
|
||||||
|
asDigit(hours / 10),
|
||||||
|
asDigit(hours),
|
||||||
|
0xff,
|
||||||
|
flagChar
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
intArrayOf(
|
||||||
|
0x05,
|
||||||
|
trafficChar,
|
||||||
|
asDigit(hours / 10),
|
||||||
|
asDigit(hours),
|
||||||
|
0xff,
|
||||||
|
asDigit(minutes / 10),
|
||||||
|
asDigit(minutes),
|
||||||
|
0x00,
|
||||||
|
flagChar
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSpeedControl(state: State): IntArray {
|
||||||
|
return intArrayOf(0x04, if (state.control) 0x01 else 0x02)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setCompass(state: State): IntArray {
|
||||||
|
// TODO: Implement
|
||||||
|
return setDirection(OutAngle.Straight, OutType.ArrowOnly)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cleanDistance(): IntArray {
|
||||||
|
return intArrayOf(0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setDirection(
|
||||||
|
angle: OutAngle,
|
||||||
|
out: OutType = OutType.Lane,
|
||||||
|
roundabout: OutAngle = OutAngle.AsDirection
|
||||||
|
): IntArray {
|
||||||
|
val param1: Int = when (angle) {
|
||||||
|
OutAngle.LeftDown -> 0x10
|
||||||
|
OutAngle.RightDown -> 0x20
|
||||||
|
else -> out.value
|
||||||
|
}
|
||||||
|
val param2: Int = if (out == OutType.RightRoundabout
|
||||||
|
|| out == OutType.LeftRoundabout
|
||||||
|
) {
|
||||||
|
if (roundabout == OutAngle.AsDirection) angle.value else roundabout.value
|
||||||
|
} else {
|
||||||
|
0x00
|
||||||
|
}
|
||||||
|
val param3: Int =
|
||||||
|
if (angle == OutAngle.LeftDown || angle == OutAngle.RightDown) 0x00 else angle.value
|
||||||
|
return intArrayOf(0x01, param1, param2, param3)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setDistance(distance: Int, unit: Unit = Unit.Any): IntArray {
|
||||||
|
return intArrayOf(
|
||||||
|
0x03, asDigit(distance / 1000), asDigit(distance / 100), asDigit(distance / 10),
|
||||||
|
0x00, asDigit(distance), unit.value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setSpeed(
|
||||||
|
speed: Int,
|
||||||
|
limit: Int = 0,
|
||||||
|
limitWarning: Boolean = false,
|
||||||
|
acc: Boolean = false
|
||||||
|
): IntArray {
|
||||||
|
// TODO: car connection
|
||||||
|
val accChar = asChar(acc)
|
||||||
|
val limitWarningChar = asChar(limitWarning)
|
||||||
|
return if (limit > 0) {
|
||||||
|
intArrayOf(
|
||||||
|
0x06,
|
||||||
|
asDigit(speed / 100),
|
||||||
|
asDigit(speed / 10),
|
||||||
|
asDigit(speed),
|
||||||
|
0xff,
|
||||||
|
asDigit(limit / 100),
|
||||||
|
asDigit(limit / 10),
|
||||||
|
asDigit(limit),
|
||||||
|
limitWarningChar,
|
||||||
|
accChar
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
intArrayOf(
|
||||||
|
0x06,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
asDigit(speed / 100),
|
||||||
|
asDigit(speed / 10),
|
||||||
|
asDigit(speed),
|
||||||
|
limitWarningChar,
|
||||||
|
accChar
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun asDigit(n: Int): Int {
|
||||||
|
if (n == 0) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
val m = n % 10
|
||||||
|
return if (m == 0) 10 else m
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun asChar(boolean: Boolean): Int {
|
||||||
|
return if (boolean) 0xff else 0x00
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,8 +1,9 @@
|
||||||
package eu.ztsh.garmin
|
package eu.ztsh.garmin.data
|
||||||
|
|
||||||
import com.mapbox.navigation.tripdata.maneuver.model.Maneuver
|
import com.mapbox.navigation.tripdata.maneuver.model.Maneuver
|
||||||
|
import com.mapbox.navigation.core.trip.session.LocationMatcherResult
|
||||||
|
|
||||||
class ManeuverMapper {
|
class MapboxMapper {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
@ -12,13 +13,8 @@ class ManeuverMapper {
|
||||||
this.primary.apply {
|
this.primary.apply {
|
||||||
state.direction = Direction()
|
state.direction = Direction()
|
||||||
when (this.type) {
|
when (this.type) {
|
||||||
"turn" -> {
|
|
||||||
state.direction.outType = OutType.Lane
|
|
||||||
state.direction.roundabout = OutAngle.AsDirection
|
|
||||||
}
|
|
||||||
|
|
||||||
"roundabout" -> {
|
"roundabout" -> {
|
||||||
state.direction.outType = OutType.RightRoundabout
|
state.direction!!.out = OutType.RightRoundabout
|
||||||
}
|
}
|
||||||
|
|
||||||
"arrive" -> {
|
"arrive" -> {
|
||||||
|
@ -28,11 +24,11 @@ class ManeuverMapper {
|
||||||
when (this.modifier) {
|
when (this.modifier) {
|
||||||
"right" -> {
|
"right" -> {
|
||||||
when (this.type) {
|
when (this.type) {
|
||||||
"turn" -> state.direction.outAngle = OutAngle.Right
|
"turn" -> state.direction!!.angle = OutAngle.Right
|
||||||
"roundabout" -> {
|
"roundabout" -> {
|
||||||
when (this.degrees) {
|
when (this.degrees) {
|
||||||
137.0 -> state.direction.outAngle = OutAngle.EasyRight
|
137.0 -> state.direction!!.angle = OutAngle.EasyRight
|
||||||
180.0 -> state.direction.outAngle = OutAngle.Straight
|
180.0 -> state.direction!!.angle = OutAngle.Straight
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +36,7 @@ class ManeuverMapper {
|
||||||
|
|
||||||
"left" -> {
|
"left" -> {
|
||||||
when (this.type) {
|
when (this.type) {
|
||||||
"turn" -> state.direction.outAngle = OutAngle.Left
|
"turn" -> state.direction!!.angle = OutAngle.Left
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,14 +44,14 @@ class ManeuverMapper {
|
||||||
this.stepDistance.apply {
|
this.stepDistance.apply {
|
||||||
this.distanceRemaining?.apply {
|
this.distanceRemaining?.apply {
|
||||||
distanceFormatter.formatDistance(distanceRemaining!!).split(" ").apply {
|
distanceFormatter.formatDistance(distanceRemaining!!).split(" ").apply {
|
||||||
state.distance = this[0].replace(',', '.').toDouble()
|
state.distance = Distance(
|
||||||
state.unit = when (this[1]) {
|
this[0].replace(',', '.').toDouble().toInt(),
|
||||||
"m" -> Unit.Metres
|
when (this[1]) {
|
||||||
"km" -> Unit.Kilometres
|
"m" -> Unit.Metres
|
||||||
else -> {
|
"km" -> Unit.Kilometres
|
||||||
Unit.Any
|
else -> Unit.Any
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,6 +65,12 @@ class ManeuverMapper {
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun apply(locationMatcherResult: LocationMatcherResult): State {
|
||||||
|
val state = State()
|
||||||
|
// TODO: speed, limit, location?, bearing
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
141
app/src/main/java/eu/ztsh/garmin/data/Model.kt
Normal file
141
app/src/main/java/eu/ztsh/garmin/data/Model.kt
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
package eu.ztsh.garmin.data
|
||||||
|
|
||||||
|
enum class OutType(val value: Int) {
|
||||||
|
|
||||||
|
Off(0x00),
|
||||||
|
Lane(0x01),
|
||||||
|
LongerLane(0x02),
|
||||||
|
LeftRoundabout(0x04),
|
||||||
|
RightRoundabout(0x08),
|
||||||
|
Flag(0x40),
|
||||||
|
ArrowOnly(0x80);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum class OutAngle(val value: Int) {
|
||||||
|
|
||||||
|
Down(0x01),
|
||||||
|
SharpRight(0x02),
|
||||||
|
Right(0x04),
|
||||||
|
EasyRight(0x08),
|
||||||
|
Straight(0x10),
|
||||||
|
EasyLeft(0x20),
|
||||||
|
Left(0x40),
|
||||||
|
SharpLeft(0x80),
|
||||||
|
LeftDown(0x81),
|
||||||
|
RightDown(0x82),
|
||||||
|
AsDirection(0x00)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Unit(val value: Int) {
|
||||||
|
|
||||||
|
Any(0),
|
||||||
|
Metres(1),
|
||||||
|
Kilometres(3),
|
||||||
|
Miles(5),
|
||||||
|
Foot(8)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Lane(val value: Int) {
|
||||||
|
|
||||||
|
DotsRight(0x01),
|
||||||
|
OuterRight(0x02),
|
||||||
|
MiddleRight(0x04),
|
||||||
|
InnerRight(0x08),
|
||||||
|
InnerLeft(0x10),
|
||||||
|
MiddleLeft(0x20),
|
||||||
|
OuterLeft(0x40),
|
||||||
|
DotsLeft(0x80)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
open class Arrows(val lanes: List<Lane>) {
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as Arrows
|
||||||
|
|
||||||
|
return lanes == other.lanes
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return lanes.hashCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Lanes(lanes: List<Lane>) : Arrows(lanes)
|
||||||
|
|
||||||
|
class Outlines(lanes: List<Lane>) : Arrows(lanes)
|
||||||
|
|
||||||
|
class Distance(val distance: Int, val unit: Unit) {
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as Distance
|
||||||
|
|
||||||
|
if (distance != other.distance) return false
|
||||||
|
if (unit != other.unit) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = distance
|
||||||
|
result = 31 * result + unit.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class Speed(val speed: Int, val limit: Int)
|
||||||
|
|
||||||
|
class Arrival(val hours: Int, val minutes: Int)
|
||||||
|
|
||||||
|
class State {
|
||||||
|
|
||||||
|
var lineArrows: Lanes? = null
|
||||||
|
var lineOutlines: Outlines? = null
|
||||||
|
var direction : Direction? = null
|
||||||
|
var distance: Distance? = null
|
||||||
|
var speed: Speed? = null
|
||||||
|
var arrival: Arrival? = null
|
||||||
|
// TODO: Bearing
|
||||||
|
// TODO: support
|
||||||
|
var traffic: Boolean? = null
|
||||||
|
var flag: Boolean? = null
|
||||||
|
var control: Boolean? = null
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class Direction {
|
||||||
|
var angle: OutAngle = OutAngle.AsDirection
|
||||||
|
var out: OutType = OutType.Lane
|
||||||
|
var roundabout: OutAngle = OutAngle.AsDirection
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as Direction
|
||||||
|
|
||||||
|
if (angle != other.angle) return false
|
||||||
|
if (out != other.out) return false
|
||||||
|
if (roundabout != other.roundabout) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = angle.hashCode()
|
||||||
|
result = 31 * result + out.hashCode()
|
||||||
|
result = 31 * result + roundabout.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
279
app/src/test/java/eu/ztsh/garmin/data/GarminMapperTest.kt
Normal file
279
app/src/test/java/eu/ztsh/garmin/data/GarminMapperTest.kt
Normal file
|
@ -0,0 +1,279 @@
|
||||||
|
package eu.ztsh.garmin.data
|
||||||
|
|
||||||
|
import eu.ztsh.garmin.Garmin
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
class GarminMapperTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun linesTest() {
|
||||||
|
linesTest(
|
||||||
|
listOf(Lane.DotsLeft),
|
||||||
|
listOf(),
|
||||||
|
intArrayOf(2, 128, 0),
|
||||||
|
intArrayOf(16, 123, 9, 3, 0, 0, 0, 85, 21, 2, 128, 0, 141, 16, 3)
|
||||||
|
)
|
||||||
|
linesTest(
|
||||||
|
Lane.OuterRight,
|
||||||
|
Lane.OuterLeft,
|
||||||
|
intArrayOf(2, 2, 64),
|
||||||
|
intArrayOf(16, 123, 9, 3, 0, 0, 0, 85, 21, 2, 2, 64, 203, 16, 3)
|
||||||
|
)
|
||||||
|
linesTest(
|
||||||
|
Lane.MiddleRight,
|
||||||
|
Lane.MiddleLeft,
|
||||||
|
intArrayOf(2, 4, 32),
|
||||||
|
intArrayOf(16, 123, 9, 3, 0, 0, 0, 85, 21, 2, 4, 32, 233, 16, 3)
|
||||||
|
)
|
||||||
|
linesTest(
|
||||||
|
Lane.InnerRight,
|
||||||
|
Lane.InnerLeft,
|
||||||
|
intArrayOf(2, 8, 16),
|
||||||
|
intArrayOf(16, 123, 9, 3, 0, 0, 0, 85, 21, 2, 8, 16, 16, 245, 16, 3)
|
||||||
|
)
|
||||||
|
linesTest(
|
||||||
|
Lane.InnerLeft,
|
||||||
|
Lane.InnerRight,
|
||||||
|
intArrayOf(2, 16, 8),
|
||||||
|
intArrayOf(16, 123, 9, 3, 0, 0, 0, 85, 21, 2, 16, 16, 8, 245, 16, 3)
|
||||||
|
)
|
||||||
|
linesTest(
|
||||||
|
Lane.MiddleLeft,
|
||||||
|
Lane.MiddleRight,
|
||||||
|
intArrayOf(2, 32, 4),
|
||||||
|
intArrayOf(16, 123, 9, 3, 0, 0, 0, 85, 21, 2, 32, 4, 233, 16, 3)
|
||||||
|
)
|
||||||
|
linesTest(
|
||||||
|
Lane.OuterLeft,
|
||||||
|
Lane.OuterRight,
|
||||||
|
intArrayOf(2, 64, 2),
|
||||||
|
intArrayOf(16, 123, 9, 3, 0, 0, 0, 85, 21, 2, 64, 2, 203, 16, 3)
|
||||||
|
)
|
||||||
|
linesTest(
|
||||||
|
listOf(Lane.DotsRight),
|
||||||
|
listOf(Lane.OuterRight, Lane.MiddleRight, Lane.InnerRight, Lane.InnerLeft, Lane.MiddleLeft, Lane.OuterLeft),
|
||||||
|
intArrayOf(2, 1, 126),
|
||||||
|
intArrayOf(16, 123, 9, 3, 0, 0, 0, 85, 21, 2, 1, 126, 142, 16, 3)
|
||||||
|
)
|
||||||
|
linesTest(
|
||||||
|
listOf(),
|
||||||
|
listOf(),
|
||||||
|
intArrayOf(2, 0, 0),
|
||||||
|
intArrayOf(16, 123, 9, 3, 0, 0, 0, 85, 21, 2, 0, 0, 13, 16, 3)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun directionTest() {
|
||||||
|
directionTest(
|
||||||
|
OutAngle.RightDown,
|
||||||
|
intArrayOf(1, 32, 0, 0),
|
||||||
|
intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 32, 0, 0, 236, 16, 3)
|
||||||
|
)
|
||||||
|
directionTest(
|
||||||
|
OutAngle.SharpRight,
|
||||||
|
intArrayOf(1, 1, 0, 2),
|
||||||
|
intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 1, 0, 2, 9, 16, 3)
|
||||||
|
)
|
||||||
|
directionTest(
|
||||||
|
OutAngle.Right,
|
||||||
|
intArrayOf(1, 1, 0, 4),
|
||||||
|
intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 1, 0, 4, 7, 16, 3)
|
||||||
|
)
|
||||||
|
directionTest(
|
||||||
|
OutAngle.EasyRight,
|
||||||
|
intArrayOf(1, 1, 0, 8),
|
||||||
|
intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 1, 0, 8, 3, 16, 3)
|
||||||
|
)
|
||||||
|
directionTest(
|
||||||
|
OutAngle.Straight,
|
||||||
|
intArrayOf(1, 1, 0, 16),
|
||||||
|
intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 1, 0, 16, 16, 251, 16, 3)
|
||||||
|
)
|
||||||
|
directionTest(
|
||||||
|
OutAngle.EasyLeft,
|
||||||
|
intArrayOf(1, 1, 0, 32),
|
||||||
|
intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 1, 0, 32, 235, 16, 3)
|
||||||
|
)
|
||||||
|
directionTest(
|
||||||
|
OutAngle.Left,
|
||||||
|
intArrayOf(1, 1, 0, 64),
|
||||||
|
intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 1, 0, 64, 203, 16, 3)
|
||||||
|
)
|
||||||
|
directionTest(
|
||||||
|
OutAngle.SharpLeft,
|
||||||
|
intArrayOf(1, 1, 0, 128),
|
||||||
|
intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 1, 0, 128, 139, 16, 3)
|
||||||
|
)
|
||||||
|
directionTest(
|
||||||
|
OutAngle.LeftDown,
|
||||||
|
intArrayOf(1, 16, 0, 0),
|
||||||
|
intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 16, 16, 0, 0, 252, 16, 3)
|
||||||
|
)
|
||||||
|
directionTest(
|
||||||
|
OutAngle.Down,
|
||||||
|
intArrayOf(1, 1, 0, 1),
|
||||||
|
intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 1, 0, 1, 10, 16, 3)
|
||||||
|
)
|
||||||
|
directionTest(
|
||||||
|
OutAngle.SharpRight, OutType.LongerLane,
|
||||||
|
intArrayOf(1, 2, 0, 2),
|
||||||
|
intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 2, 0, 2, 8, 16, 3)
|
||||||
|
)
|
||||||
|
directionTest(
|
||||||
|
OutAngle.Right, OutType.LongerLane,
|
||||||
|
intArrayOf(1, 2, 0, 4),
|
||||||
|
intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 2, 0, 4, 6, 16, 3)
|
||||||
|
)
|
||||||
|
directionTest(
|
||||||
|
OutAngle.EasyRight, OutType.LongerLane,
|
||||||
|
intArrayOf(1, 2, 0, 8),
|
||||||
|
intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 2, 0, 8, 2, 16, 3)
|
||||||
|
)
|
||||||
|
directionTest(
|
||||||
|
OutAngle.Straight, OutType.LongerLane,
|
||||||
|
intArrayOf(1, 2, 0, 16),
|
||||||
|
intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 2, 0, 16, 16, 250, 16, 3)
|
||||||
|
)
|
||||||
|
directionTest(
|
||||||
|
OutAngle.EasyLeft, OutType.LongerLane,
|
||||||
|
intArrayOf(1, 2, 0, 32),
|
||||||
|
intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 2, 0, 32, 234, 16, 3)
|
||||||
|
)
|
||||||
|
directionTest(
|
||||||
|
OutAngle.Left, OutType.LongerLane,
|
||||||
|
intArrayOf(1, 2, 0, 64),
|
||||||
|
intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 2, 0, 64, 202, 16, 3)
|
||||||
|
)
|
||||||
|
directionTest(
|
||||||
|
OutAngle.SharpLeft, OutType.LongerLane,
|
||||||
|
intArrayOf(1, 2, 0, 128),
|
||||||
|
intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 2, 0, 128, 138, 16, 3)
|
||||||
|
)
|
||||||
|
directionTest(
|
||||||
|
OutAngle.SharpRight, OutType.RightRoundabout,
|
||||||
|
intArrayOf(1, 8, 2, 2),
|
||||||
|
intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 8, 2, 2, 0, 16, 3)
|
||||||
|
)
|
||||||
|
directionTest(
|
||||||
|
OutAngle.Right, OutType.RightRoundabout,
|
||||||
|
intArrayOf(1, 8, 4, 4),
|
||||||
|
intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 8, 4, 4, 252, 16, 3)
|
||||||
|
)
|
||||||
|
directionTest(
|
||||||
|
OutAngle.EasyRight, OutType.RightRoundabout,
|
||||||
|
intArrayOf(1, 8, 8, 8),
|
||||||
|
intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 8, 8, 8, 244, 16, 3)
|
||||||
|
)
|
||||||
|
directionTest(
|
||||||
|
OutAngle.Straight, OutType.RightRoundabout,
|
||||||
|
intArrayOf(1, 8, 16, 16),
|
||||||
|
intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 8, 16, 16, 16, 16, 228, 16, 3)
|
||||||
|
)
|
||||||
|
directionTest(
|
||||||
|
OutAngle.EasyLeft, OutType.RightRoundabout,
|
||||||
|
intArrayOf(1, 8, 32, 32),
|
||||||
|
intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 8, 32, 32, 196, 16, 3)
|
||||||
|
)
|
||||||
|
directionTest(
|
||||||
|
OutAngle.Left, OutType.RightRoundabout,
|
||||||
|
intArrayOf(1, 8, 64, 64),
|
||||||
|
intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 8, 64, 64, 132, 16, 3)
|
||||||
|
)
|
||||||
|
directionTest(
|
||||||
|
OutAngle.SharpLeft, OutType.RightRoundabout,
|
||||||
|
intArrayOf(1, 8, 128, 128),
|
||||||
|
intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 8, 128, 128, 4, 16, 3)
|
||||||
|
)
|
||||||
|
directionTest(
|
||||||
|
OutAngle.Left, OutType.Flag,
|
||||||
|
intArrayOf(1, 64, 0, 64),
|
||||||
|
intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 64, 0, 64, 140, 16, 3)
|
||||||
|
)
|
||||||
|
directionTest(
|
||||||
|
OutAngle.Right, OutType.Flag,
|
||||||
|
intArrayOf(1, 64, 0, 4),
|
||||||
|
intArrayOf(16, 123, 10, 4, 0, 0, 0, 85, 21, 1, 64, 0, 4, 200, 16, 3)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun distanceTest() {
|
||||||
|
// TODO
|
||||||
|
intArrayOf(3, 0, 9, 9, 0, 9, 0)
|
||||||
|
intArrayOf(16, 123, 13, 7, 0, 0, 0, 85, 21, 3, 0, 9, 9, 0, 9, 0, 233, 16, 3)
|
||||||
|
intArrayOf(3, 0, 9, 9, 0, 9, 3)
|
||||||
|
intArrayOf(16, 123, 13, 7, 0, 0, 0, 85, 21, 3, 0, 9, 9, 0, 9, 3, 230, 16, 3)
|
||||||
|
intArrayOf(3, 0, 9, 9, 0, 9, 1)
|
||||||
|
intArrayOf(16, 123, 13, 7, 0, 0, 0, 85, 21, 3, 0, 9, 9, 0, 9, 1, 232, 16, 3)
|
||||||
|
intArrayOf(3, 0, 9, 9, 0, 9, 8)
|
||||||
|
intArrayOf(16, 123, 13, 7, 0, 0, 0, 85, 21, 3, 0, 9, 9, 0, 9, 8, 225, 16, 3)
|
||||||
|
intArrayOf(3, 0, 9, 9, 0, 9, 5)
|
||||||
|
intArrayOf(16, 123, 13, 7, 0, 0, 0, 85, 21, 3, 0, 9, 9, 0, 9, 5, 228, 16, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun speedTest() {
|
||||||
|
// TODO
|
||||||
|
intArrayOf(7, 1)
|
||||||
|
intArrayOf(16, 123, 8, 2, 0, 0, 0, 85, 21, 7, 1, 9, 16, 3)
|
||||||
|
intArrayOf(6, 0, 0, 0, 0, 0, 5, 10, 0, 0)
|
||||||
|
intArrayOf(16, 123, 16, 16, 10, 0, 0, 0, 85, 21, 6, 0, 0, 0, 0, 0, 5, 10, 0, 0, 236, 16, 3)
|
||||||
|
intArrayOf(6, 0, 5, 10, 255, 1, 10, 10, 0, 0)
|
||||||
|
intArrayOf(16, 123, 16, 16, 10, 0, 0, 0, 85, 21, 6, 0, 5, 10, 255, 1, 10, 10, 0, 0, 216, 16, 3)
|
||||||
|
intArrayOf(6, 1, 5, 10, 255, 1, 10, 10, 255, 0)
|
||||||
|
intArrayOf(16, 123, 16, 16, 10, 0, 0, 0, 85, 21, 6, 1, 5, 10, 255, 1, 10, 10, 255, 0, 216, 16, 3)
|
||||||
|
intArrayOf(6, 0, 5, 10, 255, 1, 10, 10, 0, 255)
|
||||||
|
intArrayOf(16, 123, 16, 16, 10, 0, 0, 0, 85, 21, 6, 0, 5, 10, 255, 1, 10, 10, 0, 255, 217, 16, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun timeTest() {
|
||||||
|
// TODO
|
||||||
|
intArrayOf(5, 0, 2, 2, 255, 2, 2, 0, 0)
|
||||||
|
intArrayOf(16, 123, 15, 9, 0, 0, 0, 85, 21, 5, 0, 2, 2, 255, 2, 2, 0, 0, 247, 16, 3)
|
||||||
|
intArrayOf(5, 255, 2, 2, 255, 2, 2, 0, 0)
|
||||||
|
intArrayOf(16, 123, 15, 9, 0, 0, 0, 85, 21, 5, 255, 2, 2, 255, 2, 2, 0, 0, 248, 16, 3)
|
||||||
|
intArrayOf(5, 0, 2, 2, 255, 2, 2, 0, 255)
|
||||||
|
intArrayOf(16, 123, 15, 9, 0, 0, 0, 85, 21, 5, 0, 2, 2, 255, 2, 2, 0, 255, 248, 16, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun controlTest() {
|
||||||
|
// TODO
|
||||||
|
intArrayOf(4, 1)
|
||||||
|
intArrayOf(16, 123, 8, 2, 0, 0, 0, 85, 21, 4, 1, 12, 16, 3)
|
||||||
|
intArrayOf(4, 0)
|
||||||
|
intArrayOf(16, 123, 8, 2, 0, 0, 0, 85, 21, 4, 0, 13, 16, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun linesTest(outlines: Lane, arrows: Lane, expectedRaw: IntArray, expectedBoxed: IntArray) {
|
||||||
|
linesTest(listOf(outlines), listOf(arrows), expectedRaw, expectedBoxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun linesTest(outlines: List<Lane>, arrows: List<Lane>, expectedRaw: IntArray, expectedBoxed: IntArray) {
|
||||||
|
val state = State()
|
||||||
|
state.lineOutlines = outlines
|
||||||
|
state.lineArrows = arrows
|
||||||
|
makeAssertions(GarminMapper.setLines(state), expectedRaw, expectedBoxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun directionTest(outAngle: OutAngle, expectedRaw: IntArray, expectedBoxed: IntArray) {
|
||||||
|
directionTest(outAngle, OutType.Lane, expectedRaw, expectedBoxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun directionTest(outAngle: OutAngle, outType: OutType, expectedRaw: IntArray, expectedBoxed: IntArray) {
|
||||||
|
val state = State()
|
||||||
|
state.direction.angle = outAngle
|
||||||
|
state.direction.out = outType
|
||||||
|
makeAssertions(GarminMapper.setDirection(state), expectedRaw, expectedBoxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeAssertions(resultRaw: IntArray, expectedRaw: IntArray, expectedBoxed: IntArray) {
|
||||||
|
assertThat(resultRaw).containsExactly(expectedRaw.toTypedArray())
|
||||||
|
val resultBoxed = Garmin.prepareData(resultRaw)
|
||||||
|
assertThat(resultBoxed).containsExactly(expectedBoxed.toTypedArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -52,11 +52,13 @@ class Lane(Enum):
|
||||||
|
|
||||||
class Controller:
|
class Controller:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, device: str = None):
|
||||||
self.gps = False
|
self.gps = False
|
||||||
self.ser = None#serial.Serial('COM9', 9600)
|
self.debug = device is None
|
||||||
time.sleep(2)
|
if not self.debug:
|
||||||
# print(self.ser.read_all())
|
self.ser = serial.Serial(device, 9600)
|
||||||
|
time.sleep(2)
|
||||||
|
print(self.ser.read_all())
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
self.send_hud([0x03, 0, 0, 0, 0x00, 0, 0])
|
self.send_hud([0x03, 0, 0, 0, 0x00, 0, 0])
|
||||||
|
@ -123,8 +125,8 @@ class Controller:
|
||||||
self.send_hud([0x07, 0x01])
|
self.send_hud([0x07, 0x01])
|
||||||
self.gps = state
|
self.gps = state
|
||||||
|
|
||||||
def set_speed_control(self):
|
def set_speed_control(self, on: bool = True):
|
||||||
self.send_hud([0x04, 0x01])
|
self.send_hud([0x04, 0x01 if on else 0x00])
|
||||||
|
|
||||||
def set_compass(self, direction: float):
|
def set_compass(self, direction: float):
|
||||||
if direction > 337.5 or direction <= 22.5:
|
if direction > 337.5 or direction <= 22.5:
|
||||||
|
@ -172,35 +174,18 @@ class Controller:
|
||||||
self.send_packet(chars)
|
self.send_packet(chars)
|
||||||
|
|
||||||
def send_packet(self, buff):
|
def send_packet(self, buff):
|
||||||
print("raw", buff)
|
|
||||||
encoded = [bytes(chr(char), 'raw_unicode_escape') for char in buff]
|
encoded = [bytes(chr(char), 'raw_unicode_escape') for char in buff]
|
||||||
print("enc", encoded)
|
if self.debug:
|
||||||
# for char in buff:
|
print("raw", buff)
|
||||||
# self.ser.write(bytes(chr(char), 'raw_unicode_escape'))
|
print("enc", encoded)
|
||||||
# time.sleep(0.2)
|
else:
|
||||||
# print(self.ser.read_all())
|
for char in buff:
|
||||||
|
self.ser.write(bytes(chr(char), 'raw_unicode_escape'))
|
||||||
|
time.sleep(0.2)
|
||||||
def test():
|
print(self.ser.read_all())
|
||||||
c.set_direction(OutAngle.Straight, OutType.Lane)
|
|
||||||
target = 100
|
|
||||||
for i in range(10):
|
|
||||||
c.set_distance(target, Unit.Kilometres)
|
|
||||||
time.sleep(0.5)
|
|
||||||
target -= 1
|
|
||||||
c.set_speed_control()
|
|
||||||
time.sleep(0.5)
|
|
||||||
for i in range(10):
|
|
||||||
c.set_distance(target, Unit.Kilometres)
|
|
||||||
time.sleep(0.5)
|
|
||||||
target -= 1
|
|
||||||
c.set_direction(OutAngle.Right, OutType.LongerLane)
|
|
||||||
for i in range(10):
|
|
||||||
c.set_distance(target, Unit.Kilometres)
|
|
||||||
time.sleep(0.5)
|
|
||||||
target -= 1
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
c = Controller()
|
import sys
|
||||||
test()
|
device = sys.argv[1] if len(sys.argv) > 1 else None
|
||||||
|
c = Controller(device)
|
||||||
|
|
134
python/test.py
134
python/test.py
|
@ -1,31 +1,111 @@
|
||||||
from numpy import uint8
|
from main import *
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
interval = 0.2
|
||||||
|
|
||||||
|
|
||||||
def send_hud(buf: list):
|
def suite(controller: Controller):
|
||||||
n = len(buf)
|
lines(controller)
|
||||||
chars = []
|
direction(controller)
|
||||||
crc = uint8(0xeb + n + n)
|
distance(controller)
|
||||||
chars.append(0x10)
|
speed(controller)
|
||||||
chars.append(0x7b)
|
time(controller)
|
||||||
chars.append(n + 6)
|
control(controller)
|
||||||
if n == 0xa:
|
compass(controller)
|
||||||
chars.append(0x10)
|
|
||||||
chars.append(n)
|
|
||||||
chars.append(0x00)
|
|
||||||
chars.append(0x00)
|
|
||||||
chars.append(0x00)
|
|
||||||
chars.append(0x55)
|
|
||||||
chars.append(0x15)
|
|
||||||
for char in buf:
|
|
||||||
crc = uint8(crc + char)
|
|
||||||
chars.append(char)
|
|
||||||
if char == 0x10:
|
|
||||||
chars.append(0x10)
|
|
||||||
chars.append((-crc) & 0xff)
|
|
||||||
chars.append(0x10)
|
|
||||||
chars.append(0x03)
|
|
||||||
print(chars)
|
|
||||||
print([bytes(chr(char), 'raw_unicode_escape') for char in chars])
|
|
||||||
|
|
||||||
|
|
||||||
send_hud([0x04, 0x01])
|
def lines(controller: Controller):
|
||||||
|
print("Lines")
|
||||||
|
controller.set_lines([Lane.DotsLeft], [])
|
||||||
|
controller.set_lines([Lane.OuterRight], [Lane.OuterLeft])
|
||||||
|
controller.set_lines([Lane.MiddleRight], [Lane.MiddleLeft])
|
||||||
|
controller.set_lines([Lane.InnerRight], [Lane.InnerLeft])
|
||||||
|
controller.set_lines([Lane.InnerLeft], [Lane.InnerRight])
|
||||||
|
controller.set_lines([Lane.MiddleLeft], [Lane.MiddleRight])
|
||||||
|
controller.set_lines([Lane.OuterLeft], [Lane.OuterRight])
|
||||||
|
controller.set_lines(
|
||||||
|
[Lane.DotsRight],
|
||||||
|
[Lane.OuterRight, Lane.MiddleRight, Lane.InnerRight, Lane.InnerLeft, Lane.MiddleLeft, Lane.OuterLeft]
|
||||||
|
)
|
||||||
|
controller.set_lines([], [])
|
||||||
|
|
||||||
|
|
||||||
|
def direction(controller: Controller):
|
||||||
|
print("Direction")
|
||||||
|
controller.set_direction(OutAngle.RightDown)
|
||||||
|
controller.set_direction(OutAngle.SharpRight)
|
||||||
|
controller.set_direction(OutAngle.Right)
|
||||||
|
controller.set_direction(OutAngle.EasyRight)
|
||||||
|
controller.set_direction(OutAngle.Straight)
|
||||||
|
controller.set_direction(OutAngle.EasyLeft)
|
||||||
|
controller.set_direction(OutAngle.Left)
|
||||||
|
controller.set_direction(OutAngle.SharpLeft)
|
||||||
|
controller.set_direction(OutAngle.LeftDown)
|
||||||
|
controller.set_direction(OutAngle.Down)
|
||||||
|
controller.set_direction(OutAngle.SharpRight, OutType.LongerLane)
|
||||||
|
controller.set_direction(OutAngle.Right, OutType.LongerLane)
|
||||||
|
controller.set_direction(OutAngle.EasyRight, OutType.LongerLane)
|
||||||
|
controller.set_direction(OutAngle.Straight, OutType.LongerLane)
|
||||||
|
controller.set_direction(OutAngle.EasyLeft, OutType.LongerLane)
|
||||||
|
controller.set_direction(OutAngle.Left, OutType.LongerLane)
|
||||||
|
controller.set_direction(OutAngle.SharpLeft, OutType.LongerLane)
|
||||||
|
controller.set_direction(OutAngle.SharpRight, OutType.RightRoundabout)
|
||||||
|
controller.set_direction(OutAngle.Right, OutType.RightRoundabout)
|
||||||
|
controller.set_direction(OutAngle.EasyRight, OutType.RightRoundabout)
|
||||||
|
controller.set_direction(OutAngle.Straight, OutType.RightRoundabout)
|
||||||
|
controller.set_direction(OutAngle.EasyLeft, OutType.RightRoundabout)
|
||||||
|
controller.set_direction(OutAngle.Left, OutType.RightRoundabout)
|
||||||
|
controller.set_direction(OutAngle.SharpLeft, OutType.RightRoundabout)
|
||||||
|
controller.set_direction(OutAngle.Left, OutType.Flag)
|
||||||
|
controller.set_direction(OutAngle.Right, OutType.Flag)
|
||||||
|
|
||||||
|
|
||||||
|
def distance(controller: Controller):
|
||||||
|
print("Distance")
|
||||||
|
controller.set_distance(999)
|
||||||
|
controller.set_distance(999, Unit.Kilometres)
|
||||||
|
controller.set_distance(999, Unit.Metres)
|
||||||
|
controller.set_distance(999, Unit.Foot)
|
||||||
|
controller.set_distance(999, Unit.Miles)
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def speed(controller: Controller):
|
||||||
|
print("Speed")
|
||||||
|
controller.set_gps(True)
|
||||||
|
controller.set_speed(50)
|
||||||
|
controller.set_speed(50, 100)
|
||||||
|
controller.set_speed(150, 100)
|
||||||
|
controller.set_speed(50, 100, True)
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def time(controller: Controller):
|
||||||
|
print("Time")
|
||||||
|
controller.set_time(22, 22)
|
||||||
|
controller.set_time(22, 22, traffic=True)
|
||||||
|
controller.set_time(22, 22, flag=True)
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def control(controller: Controller):
|
||||||
|
print("Speed Control")
|
||||||
|
controller.set_speed_control()
|
||||||
|
controller.set_speed_control(False)
|
||||||
|
|
||||||
|
|
||||||
|
def compass(controller: Controller):
|
||||||
|
print("Compass")
|
||||||
|
controller.set_compass(22.5)
|
||||||
|
controller.set_compass(67.5)
|
||||||
|
controller.set_compass(112.5)
|
||||||
|
controller.set_compass(157.5)
|
||||||
|
controller.set_compass(202.5)
|
||||||
|
controller.set_compass(247.5)
|
||||||
|
controller.set_compass(292.5)
|
||||||
|
controller.set_compass(337.5)
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
instance = Controller('/dev/rfcomm0')
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue