fix #6: Add separate headwind speed data field

This commit is contained in:
Tim Kluge 2024-12-08 12:16:13 +01:00
parent 0830674d62
commit 767e5fe769
8 changed files with 107 additions and 43 deletions

View File

@ -20,6 +20,7 @@ import kotlinx.coroutines.channels.trySendBlocking
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
@ -32,6 +33,7 @@ import kotlinx.coroutines.time.debounce
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import java.time.Duration
import kotlin.math.abs
import kotlin.math.absoluteValue
import kotlin.math.roundToInt
import kotlin.time.Duration.Companion.seconds
@ -142,8 +144,42 @@ suspend fun KarooSystemService.makeOpenMeteoHttpRequest(gpsCoordinates: GpsCoord
}.single()
}
fun signedAngleDifference(angle1: Double, angle2: Double): Double {
val a1 = angle1 % 360
val a2 = angle2 % 360
var diff = abs(a1 - a2)
val sign = if (a1 < a2) {
if (diff > 180.0) -1 else 1
} else {
if (diff > 180.0) 1 else -1
}
if (diff > 180.0) {
diff = 360.0 - diff
}
return sign * diff
}
fun KarooSystemService.getRelativeHeadingFlow(context: Context): Flow<Double> {
val currentWeatherData = context.streamCurrentWeatherData()
return getHeadingFlow()
.filter { it >= 0 }
.combine(currentWeatherData) { cardinalDirection, data -> cardinalDirection to data }
.map { (cardinalDirection, data) ->
val bearing = cardinalDirection * 45.0
val windBearing = data.current.windDirection + 180
val diff = (signedAngleDifference(bearing, windBearing) + 360) % 360
Log.d(KarooHeadwindExtension.TAG, "Wind bearing: $bearing vs $windBearing => $diff")
diff
}
}
fun KarooSystemService.getHeadingFlow(): Flow<Int> {
return flowOf(2)
// return flowOf(2)
return streamDataFlow(DataType.Type.HEADING)
.mapNotNull { (it as? StreamState.Streaming)?.dataPoint?.singleValue }
@ -153,7 +189,7 @@ fun KarooSystemService.getHeadingFlow(): Flow<Int> {
@OptIn(FlowPreview::class)
fun KarooSystemService.getGpsCoordinateFlow(): Flow<GpsCoordinates> {
return flowOf(GpsCoordinates(52.5164069,13.3784))
// return flowOf(GpsCoordinates(52.5164069,13.3784))
return streamDataFlow("TYPE_LOCATION_ID")
.mapNotNull { (it as? StreamState.Streaming)?.dataPoint?.values }

View File

@ -8,8 +8,8 @@ import de.timklge.karooheadwind.datatypes.RelativeHumidityDataType
import de.timklge.karooheadwind.datatypes.SurfacePressureDataType
import de.timklge.karooheadwind.datatypes.WindDirectionDataType
import de.timklge.karooheadwind.datatypes.WindGustsDataType
import de.timklge.karooheadwind.datatypes.WindSpeedDataType
import de.timklge.karooheadwind.datatypes.RelativeWindDirectionDataType
import de.timklge.karooheadwind.datatypes.HeadwindSpeedDataType
import de.timklge.karooheadwind.datatypes.HeadwindDirectionDataType
import de.timklge.karooheadwind.datatypes.WeatherDataType
import de.timklge.karooheadwind.screens.HeadwindStats
import io.hammerhead.karooext.KarooSystemService
@ -40,11 +40,12 @@ class KarooHeadwindExtension : KarooExtension("karoo-headwind", "1.0.0-beta2") {
override val types by lazy {
listOf(
RelativeWindDirectionDataType(karooSystem, applicationContext),
HeadwindDirectionDataType(karooSystem, applicationContext),
HeadwindSpeedDataType(karooSystem, applicationContext),
WeatherDataType(karooSystem, applicationContext),
HeadwindSpeedDataType(karooSystem, applicationContext),
RelativeHumidityDataType(applicationContext),
CloudCoverDataType(applicationContext),
WindSpeedDataType(applicationContext),
WindGustsDataType(applicationContext),
WindDirectionDataType(karooSystem, applicationContext),
PrecipitationDataType(applicationContext),

View File

@ -8,6 +8,7 @@ import androidx.glance.appwidget.GlanceRemoteViews
import de.timklge.karooheadwind.KarooHeadwindExtension
import de.timklge.karooheadwind.OpenMeteoCurrentWeatherResponse
import de.timklge.karooheadwind.getHeadingFlow
import de.timklge.karooheadwind.getRelativeHeadingFlow
import de.timklge.karooheadwind.screens.HeadwindSettings
import de.timklge.karooheadwind.streamCurrentWeatherData
import de.timklge.karooheadwind.streamDataFlow
@ -34,44 +35,16 @@ import kotlin.math.cos
import kotlin.math.roundToInt
@OptIn(ExperimentalGlanceRemoteViewsApi::class)
class RelativeWindDirectionDataType(
class HeadwindDirectionDataType(
private val karooSystem: KarooSystemService,
private val applicationContext: Context
) : DataTypeImpl("karoo-headwind", "headwind") {
private val glance = GlanceRemoteViews()
private fun signedAngleDifference(angle1: Double, angle2: Double): Double {
val a1 = angle1 % 360
val a2 = angle2 % 360
var diff = abs(a1 - a2)
val sign = if (a1 < a2) {
if (diff > 180.0) -1 else 1
} else {
if (diff > 180.0) 1 else -1
}
if (diff > 180.0) {
diff = 360.0 - diff
}
return sign * diff
}
override fun startStream(emitter: Emitter<StreamState>) {
val job = CoroutineScope(Dispatchers.IO).launch {
val currentWeatherData = applicationContext.streamCurrentWeatherData()
karooSystem
.getHeadingFlow()
.filter { it >= 0 }
.combine(currentWeatherData) { cardinalDirection, data -> cardinalDirection to data }
.collect { (cardinalDirection, data) ->
val bearing = cardinalDirection * 45.0
val windBearing = data.current.windDirection + 180
val diff = (signedAngleDifference(bearing, windBearing) + 360) % 360
Log.d(KarooHeadwindExtension.TAG, "Wind bearing: $bearing vs $windBearing => $diff")
karooSystem.getRelativeHeadingFlow(applicationContext)
.collect { diff ->
emitter.onNext(StreamState.Streaming(DataPoint(dataTypeId, mapOf(DataType.Field.SINGLE to diff))))
}
}
@ -107,7 +80,7 @@ class RelativeWindDirectionDataType(
val windSpeedText = if(streamData.settings.showWindspeedOverlay) "${headwindSpeed.roundToInt()}" else null
val result = glance.compose(context, DpSize.Unspecified) {
RelativeWindDirection(windDirection.roundToInt(), config.textSize, windSpeedText)
HeadwindDirection(windDirection.roundToInt(), config.textSize, windSpeedText)
}
emitter.updateView(result.remoteViews)

View File

@ -47,7 +47,7 @@ fun getArrowResourceByBearing(bearing: Int): Int {
@OptIn(ExperimentalGlancePreviewApi::class)
@Preview(widthDp = 200, heightDp = 150)
@Composable
fun RelativeWindDirection(bearing: Int, fontSize: Int, overlayText: String? = null) {
fun HeadwindDirection(bearing: Int, fontSize: Int, overlayText: String? = null) {
Box(
modifier = GlanceModifier.fillMaxSize().padding(5.dp),
contentAlignment = Alignment(

View File

@ -0,0 +1,45 @@
package de.timklge.karooheadwind.datatypes
import android.content.Context
import de.timklge.karooheadwind.OpenMeteoCurrentWeatherResponse
import de.timklge.karooheadwind.getRelativeHeadingFlow
import de.timklge.karooheadwind.screens.HeadwindSettings
import de.timklge.karooheadwind.streamCurrentWeatherData
import de.timklge.karooheadwind.streamSettings
import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.extension.DataTypeImpl
import io.hammerhead.karooext.internal.Emitter
import io.hammerhead.karooext.models.DataPoint
import io.hammerhead.karooext.models.DataType
import io.hammerhead.karooext.models.StreamState
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
import kotlin.math.cos
class HeadwindSpeedDataType(
private val karooSystem: KarooSystemService,
private val context: Context) : DataTypeImpl("karoo-headwind", "headwindSpeed"){
data class StreamData(val value: Double, val data: OpenMeteoCurrentWeatherResponse, val settings: HeadwindSettings)
override fun startStream(emitter: Emitter<StreamState>) {
val job = CoroutineScope(Dispatchers.IO).launch {
karooSystem.getRelativeHeadingFlow(context)
.combine(context.streamCurrentWeatherData()) { value, data -> value to data }
.combine(context.streamSettings()) { (value, data), settings -> StreamData(value, data, settings) }
.collect { streamData ->
val windSpeed = streamData.data.current.windSpeed
val windDirection = streamData.value
val headwindSpeed = cos( (windDirection + 180) * Math.PI / 180.0) * windSpeed
emitter.onNext(StreamState.Streaming(DataPoint(dataTypeId, mapOf(DataType.Field.SINGLE to headwindSpeed))))
}
}
emitter.setCancellable {
job.cancel()
}
}
}

View File

@ -49,7 +49,7 @@ fun Weather(current: WeatherInterpretation, windBearing: Int, windSpeed: Int, wi
modifier = GlanceModifier.height(imageH.dp).width(imageW.dp),
provider = ImageProvider(getWeatherIcon(current)),
contentDescription = "Current weather information",
contentScale = ContentScale.FillBounds,
contentScale = ContentScale.Fit,
colorFilter = ColorFilter.tint(ColorProvider(Color.Black, Color.White))
)
}

View File

@ -1,8 +1,8 @@
<resources>
<string name="app_name">WindDir</string>
<string name="app_name">Headwind</string>
<string name="extension_name">headwind</string>
<string name="headwind">Wind direction</string>
<string name="headwind_description">Current wind direction relative to the riding direction</string>
<string name="headwind">Headwind</string>
<string name="headwind_description">Current headwind direction</string>
<string name="relativeHumidity">Humidity</string>
<string name="relativeHumidity_description">Relative humidity in percent</string>
<string name="cloudCover">Cloud cover</string>
@ -19,4 +19,6 @@
<string name="surfacePressure_description">Atmospheric pressure at surface in configured unit</string>
<string name="weather">Weather</string>
<string name="weather_description">Current weather conditions</string>
<string name="headwind_speed">Headwind speed</string>
<string name="headwind_speed_description">Current headwind speed</string>
</resources>

View File

@ -18,6 +18,13 @@
icon="@drawable/ic_launcher"
typeId="weather" />
<DataType
description="@string/headwind_speed_description"
displayName="@string/headwind_speed"
graphical="false"
icon="@drawable/ic_launcher"
typeId="headwindSpeed" />
<DataType
description="@string/relativeHumidity_description"
displayName="@string/relativeHumidity"