kopia lustrzana https://github.com/rt-bishop/Look4Sat
Fixes and refactoring for LocationSource
rodzic
8f66cd379b
commit
e47923f693
|
@ -18,73 +18,67 @@
|
|||
package com.rtbishop.look4sat.framework
|
||||
|
||||
import android.location.Criteria
|
||||
import android.location.Location
|
||||
import android.location.LocationManager
|
||||
import androidx.core.location.LocationListenerCompat
|
||||
import androidx.core.location.LocationManagerCompat
|
||||
import androidx.core.os.CancellationSignal
|
||||
import com.rtbishop.look4sat.domain.ILocationSource
|
||||
import com.rtbishop.look4sat.domain.ISettingsSource
|
||||
import com.rtbishop.look4sat.model.StationPos
|
||||
import com.rtbishop.look4sat.model.GeoPos
|
||||
import com.rtbishop.look4sat.utility.QthConverter
|
||||
import com.rtbishop.look4sat.utility.round
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
class LocationSource(
|
||||
private val manager: LocationManager,
|
||||
private val settings: ISettingsSource,
|
||||
) : ILocationSource, LocationListenerCompat {
|
||||
) : ILocationSource {
|
||||
|
||||
private val defaultProvider = LocationManager.PASSIVE_PROVIDER
|
||||
private val _stationPosition = MutableStateFlow(settings.loadStationPosition())
|
||||
override val stationPosition: StateFlow<StationPos> = _stationPosition
|
||||
private val defaultProvider = LocationManager.PASSIVE_PROVIDER
|
||||
private val executor = Executors.newSingleThreadExecutor()
|
||||
private val timeoutSignal = CancellationSignal().apply {
|
||||
setOnCancelListener { _stationPosition.value = _stationPosition.value }
|
||||
}
|
||||
override val stationPosition: StateFlow<GeoPos> = _stationPosition
|
||||
|
||||
override fun setGpsPosition(): Boolean {
|
||||
if (!LocationManagerCompat.isLocationEnabled(manager)) return false
|
||||
val criteria = Criteria().apply { isCostAllowed = true }
|
||||
val bestProvider = manager.getBestProvider(criteria, true) ?: defaultProvider
|
||||
forceLocationUpdate(bestProvider)
|
||||
try {
|
||||
val criteria = Criteria().apply { isCostAllowed = true }
|
||||
val provider = manager.getBestProvider(criteria, true) ?: defaultProvider
|
||||
Timber.d("Requesting location for $provider provider")
|
||||
LocationManagerCompat.getCurrentLocation(manager, provider, timeoutSignal, executor) {
|
||||
it?.let { setGeoPosition(it.latitude, it.longitude, it.altitude) }
|
||||
}
|
||||
} catch (exception: SecurityException) {
|
||||
Timber.d("No permissions were given")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun setGeoPosition(latitude: Double, longitude: Double): Boolean {
|
||||
manager.removeUpdates(this)
|
||||
override fun setGeoPosition(latitude: Double, longitude: Double, altitude: Double): Boolean {
|
||||
val newLongitude = if (longitude > 180.0) longitude - 180 else longitude
|
||||
val locator = QthConverter.positionToQth(latitude, newLongitude) ?: return false
|
||||
saveStationPosition(latitude, newLongitude, locator)
|
||||
saveGeoPos(latitude, newLongitude, altitude, locator)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun setQthPosition(locator: String): Boolean {
|
||||
val position = QthConverter.qthToPosition(locator) ?: return false
|
||||
saveStationPosition(position.lat, position.lon, locator)
|
||||
saveGeoPos(position.latitude, position.longitude, 0.0, locator)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onLocationChanged(location: Location) {
|
||||
setGeoPosition(location.latitude, location.longitude)
|
||||
}
|
||||
|
||||
private fun forceLocationUpdate(provider: String) {
|
||||
try {
|
||||
val location = manager.getLastKnownLocation(defaultProvider)
|
||||
if (location == null || System.currentTimeMillis() - location.time > 600000) {
|
||||
Timber.d("Requesting $provider location update")
|
||||
manager.requestLocationUpdates(provider, 0L, 0f, this)
|
||||
} else {
|
||||
setGeoPosition(location.latitude, location.longitude)
|
||||
}
|
||||
} catch (exception: SecurityException) {
|
||||
Timber.d("No permissions were given")
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveStationPosition(latitude: Double, longitude: Double, locator: String) {
|
||||
private fun saveGeoPos(latitude: Double, longitude: Double, altitude: Double, locator: String) {
|
||||
val newLat = latitude.round(4)
|
||||
val newLon = longitude.round(4)
|
||||
Timber.d("Received new Position($newLat, $newLon) & Locator $locator")
|
||||
_stationPosition.value = StationPos(newLat, newLon, locator, System.currentTimeMillis())
|
||||
val newAlt = altitude.round(1)
|
||||
val timestamp = System.currentTimeMillis()
|
||||
Timber.d("Received new Position($newLat, $newLon, $newAlt) & Locator $locator")
|
||||
_stationPosition.value = GeoPos(newLat, newLon, newAlt, locator, timestamp)
|
||||
settings.saveStationPosition(stationPosition.value)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,10 @@ package com.rtbishop.look4sat.framework
|
|||
import android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
import com.rtbishop.look4sat.domain.ISettingsSource
|
||||
import com.rtbishop.look4sat.model.StationPos
|
||||
import com.rtbishop.look4sat.model.GeoPos
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.onCompletion
|
||||
|
||||
class SettingsSource(private val prefs: SharedPreferences) : ISettingsSource {
|
||||
|
||||
|
@ -42,22 +45,25 @@ class SettingsSource(private val prefs: SharedPreferences) : ISettingsSource {
|
|||
const val keyBTFormat = "BTFormat"
|
||||
const val keyLatitude = "stationLat"
|
||||
const val keyLongitude = "stationLon"
|
||||
const val keyAltitude = "stationAlt"
|
||||
const val keyLocator = "stationQTH"
|
||||
const val keyLocTimestamp = "locTimestamp"
|
||||
const val keySelection = "selection"
|
||||
}
|
||||
|
||||
override fun loadStationPosition(): StationPos {
|
||||
val latitude = prefs.getString(keyLatitude, null) ?: "0.0"
|
||||
val longitude = prefs.getString(keyLongitude, null) ?: "0.0"
|
||||
override fun loadStationPosition(): GeoPos {
|
||||
val latitude = (prefs.getString(keyLatitude, null) ?: "0.0").toDouble()
|
||||
val longitude = (prefs.getString(keyLongitude, null) ?: "0.0").toDouble()
|
||||
val altitude = (prefs.getString(keyAltitude, null) ?: "0.0").toDouble()
|
||||
val qthLocator = prefs.getString(keyLocator, null) ?: "null"
|
||||
val timestamp = prefs.getLong(keyLocTimestamp, 0L)
|
||||
return StationPos(latitude.toDouble(), longitude.toDouble(), qthLocator, timestamp)
|
||||
return GeoPos(latitude, longitude, altitude, qthLocator, timestamp)
|
||||
}
|
||||
|
||||
override fun saveStationPosition(stationPos: StationPos) = prefs.edit {
|
||||
override fun saveStationPosition(stationPos: GeoPos) = prefs.edit {
|
||||
putString(keyLatitude, stationPos.latitude.toString())
|
||||
putString(keyLongitude, stationPos.longitude.toString())
|
||||
putString(keyAltitude, stationPos.altitude.toString())
|
||||
putString(keyLocator, stationPos.qthLocator)
|
||||
putLong(keyLocTimestamp, stationPos.timestamp)
|
||||
}
|
||||
|
@ -209,4 +215,24 @@ class SettingsSource(private val prefs: SharedPreferences) : ISettingsSource {
|
|||
private fun SharedPreferences.Editor.putDouble(key: String, double: Double) {
|
||||
putLong(key, double.toRawBits())
|
||||
}
|
||||
|
||||
inline fun <reified T> SharedPreferences.observeKey(key: String, default: T): Flow<T> {
|
||||
val flow = MutableStateFlow(getItem(key, default))
|
||||
val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, k ->
|
||||
if (key == k) flow.value = getItem(key, default)
|
||||
}
|
||||
registerOnSharedPreferenceChangeListener(listener)
|
||||
return flow.onCompletion { unregisterOnSharedPreferenceChangeListener(listener) }
|
||||
}
|
||||
|
||||
inline fun <reified T> SharedPreferences.getItem(key: String, default: T): T {
|
||||
return when (default) {
|
||||
is Boolean -> getBoolean(key, default) as T
|
||||
is Int -> getInt(key, default) as T
|
||||
is Long -> getLong(key, default) as T
|
||||
is Float -> getFloat(key, default) as T
|
||||
is String -> getString(key, default) as T
|
||||
else -> throw IllegalArgumentException("Could not handle ${T::class.java.name}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,6 @@ import androidx.compose.foundation.clickable
|
|||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Close
|
||||
import androidx.compose.material.icons.outlined.PlayArrow
|
||||
import androidx.compose.material.icons.outlined.Search
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
|
@ -96,7 +92,8 @@ private fun TopBar(setQuery: (String) -> Unit, saveSelection: () -> Unit) {
|
|||
@Composable
|
||||
private fun SearchBar(setQuery: (String) -> Unit, modifier: Modifier = Modifier) {
|
||||
val currentQuery = rememberSaveable { mutableStateOf("") }
|
||||
OutlinedTextField(value = currentQuery.value,
|
||||
OutlinedTextField(
|
||||
value = currentQuery.value,
|
||||
onValueChange = { newValue ->
|
||||
currentQuery.value = newValue
|
||||
setQuery(newValue)
|
||||
|
@ -104,9 +101,14 @@ private fun SearchBar(setQuery: (String) -> Unit, modifier: Modifier = Modifier)
|
|||
maxLines = 1,
|
||||
// label = { Text(text = stringResource(id = R.string.entries_search_hint)) },
|
||||
placeholder = { Text(text = stringResource(id = R.string.entries_search_hint)) },
|
||||
leadingIcon = { Icon(imageVector = Icons.Outlined.Search, contentDescription = null) },
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_search),
|
||||
contentDescription = null
|
||||
)
|
||||
},
|
||||
trailingIcon = {
|
||||
Icon(imageVector = Icons.Outlined.Close,
|
||||
Icon(painter = painterResource(id = R.drawable.ic_close),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.onClick {
|
||||
currentQuery.value = ""
|
||||
|
@ -150,7 +152,7 @@ private fun EntryTypeCard(type: String, onClick: () -> Unit, modifier: Modifier
|
|||
.height(48.dp)
|
||||
.clickable { onClick() }) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.PlayArrow,
|
||||
painter = painterResource(id = R.drawable.ic_next),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.size(36.dp)
|
||||
|
|
|
@ -89,7 +89,7 @@ private fun setStationPosition(stationPos: GeoPos, mapView: MapView) {
|
|||
setInfoWindow(null)
|
||||
setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM)
|
||||
icon = ContextCompat.getDrawable(mapView.context, R.drawable.ic_position)
|
||||
position = GeoPoint(stationPos.lat, stationPos.lon)
|
||||
position = GeoPoint(stationPos.latitude, stationPos.longitude)
|
||||
mapView.overlays[0] = this
|
||||
mapView.invalidate()
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ private fun setPositions(
|
|||
setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER)
|
||||
icon = getCustomTextIcon(it.key.data.name, mapView)
|
||||
try {
|
||||
position = GeoPoint(it.value.lat, it.value.lon)
|
||||
position = GeoPoint(it.value.latitude, it.value.longitude)
|
||||
} catch (exception: IllegalArgumentException) {
|
||||
println(exception.stackTraceToString())
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ private fun setSatelliteTrack(satTrack: List<List<GeoPos>>, mapView: MapView) {
|
|||
val trackOverlay = FolderOverlay()
|
||||
try {
|
||||
satTrack.forEach { track ->
|
||||
val trackPoints = track.map { GeoPoint(it.lat, it.lon) }
|
||||
val trackPoints = track.map { GeoPoint(it.latitude, it.longitude) }
|
||||
Polyline().apply {
|
||||
setPoints(trackPoints)
|
||||
outlinePaint.set(trackPaint)
|
||||
|
@ -160,7 +160,7 @@ private fun setSatelliteTrack(satTrack: List<List<GeoPos>>, mapView: MapView) {
|
|||
}
|
||||
|
||||
private fun setFootprint(satPos: SatPos, mapView: MapView) {
|
||||
val footprintPoints = satPos.getRangeCircle().map { GeoPoint(it.lat, it.lon) }
|
||||
val footprintPoints = satPos.getRangeCircle().map { GeoPoint(it.latitude, it.longitude) }
|
||||
try {
|
||||
val footprintOverlay = Polyline().apply {
|
||||
outlinePaint.set(footprintPaint)
|
||||
|
|
|
@ -53,8 +53,8 @@ class MapViewModel @Inject constructor(
|
|||
private var selectedSatellite: Satellite? = null
|
||||
|
||||
val stationPos = flow {
|
||||
val osmLat = clipLat(stationPosition.lat)
|
||||
val osmLon = clipLon(stationPosition.lon)
|
||||
val osmLat = clipLat(stationPosition.latitude)
|
||||
val osmLon = clipLon(stationPosition.longitude)
|
||||
emit(GeoPos(osmLat, osmLon))
|
||||
}
|
||||
|
||||
|
@ -127,20 +127,20 @@ class MapViewModel @Inject constructor(
|
|||
val osmLat = clipLat(satPos.latitude.toDegrees())
|
||||
val osmLon = clipLon(satPos.longitude.toDegrees())
|
||||
val currentPosition = GeoPos(osmLat, osmLon)
|
||||
if (oldLongitude < -170.0 && currentPosition.lon > 170.0) {
|
||||
if (oldLongitude < -170.0 && currentPosition.longitude > 170.0) {
|
||||
// adding left terminal position
|
||||
currentTrack.add(GeoPos(osmLat, -180.0))
|
||||
val finishedTrack = mutableListOf<GeoPos>().apply { addAll(currentTrack) }
|
||||
satTracks.add(finishedTrack)
|
||||
currentTrack.clear()
|
||||
} else if (oldLongitude > 170.0 && currentPosition.lon < -170.0) {
|
||||
} else if (oldLongitude > 170.0 && currentPosition.longitude < -170.0) {
|
||||
// adding right terminal position
|
||||
currentTrack.add(GeoPos(osmLat, 180.0))
|
||||
val finishedTrack = mutableListOf<GeoPos>().apply { addAll(currentTrack) }
|
||||
satTracks.add(finishedTrack)
|
||||
currentTrack.clear()
|
||||
}
|
||||
oldLongitude = currentPosition.lon
|
||||
oldLongitude = currentPosition.longitude
|
||||
currentTrack.add(currentPosition)
|
||||
}
|
||||
satTracks.add(currentTrack)
|
||||
|
@ -183,7 +183,7 @@ class MapViewModel @Inject constructor(
|
|||
val osmLat = clipLat(satPos.latitude.toDegrees())
|
||||
val osmLon = clipLon(satPos.longitude.toDegrees())
|
||||
val osmPos = GeoPos(osmLat, osmLon)
|
||||
val qthLoc = QthConverter.positionToQth(osmPos.lat, osmPos.lon) ?: "-- --"
|
||||
val qthLoc = QthConverter.positionToQth(osmPos.latitude, osmPos.longitude) ?: "-- --"
|
||||
val velocity = satPos.getOrbitalVelocity()
|
||||
val phase = satPos.phase.toDegrees()
|
||||
val visibility = satPos.eclipsed
|
||||
|
|
|
@ -9,6 +9,7 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
|
@ -94,8 +95,8 @@ private fun TransmitterItem(radio: SatRadio) {
|
|||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_prev),
|
||||
contentDescription = null
|
||||
painter = painterResource(id = R.drawable.ic_next),
|
||||
contentDescription = null, modifier = Modifier.rotate(180f)
|
||||
)
|
||||
Text(
|
||||
text = radio.info,
|
||||
|
|
|
@ -52,7 +52,7 @@ class RadarViewModel @Inject constructor(
|
|||
private val locationSource: ILocationSource
|
||||
) : ViewModel() {
|
||||
|
||||
private val stationPos = locationSource.stationPosition.value.toGeoPos()
|
||||
private val stationPos = locationSource.stationPosition.value
|
||||
private val magDeclination = getMagDeclination(stationPos)
|
||||
val transmitters = mutableStateOf<List<SatRadio>>(emptyList())
|
||||
val radarData = mutableStateOf<RadarData?>(null)
|
||||
|
@ -102,8 +102,8 @@ class RadarViewModel @Inject constructor(
|
|||
fun getShowSweep(): Boolean = settings.isSweepEnabled()
|
||||
|
||||
private fun getMagDeclination(geoPos: GeoPos, time: Long = System.currentTimeMillis()): Float {
|
||||
val latitude = geoPos.lat.toFloat()
|
||||
val longitude = geoPos.lon.toFloat()
|
||||
val latitude = geoPos.latitude.toFloat()
|
||||
val longitude = geoPos.longitude.toFloat()
|
||||
return GeomagneticField(latitude, longitude, 0f, time).declination
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ import androidx.compose.ui.unit.sp
|
|||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.rtbishop.look4sat.BuildConfig
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.model.StationPos
|
||||
import com.rtbishop.look4sat.model.GeoPos
|
||||
import com.rtbishop.look4sat.presentation.CardButton
|
||||
import com.rtbishop.look4sat.presentation.MainTheme
|
||||
import com.rtbishop.look4sat.presentation.dialogs.LocatorDialog
|
||||
|
@ -164,7 +164,7 @@ private fun CardAbout(version: String, modifier: Modifier = Modifier) {
|
|||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun LocationCardPreview() = MainTheme {
|
||||
val stationPos = StationPos(0.0, 0.0, "IO91vl", 0L)
|
||||
val stationPos = GeoPos(0.0, 0.0, 0.0, "IO91vl", 0L)
|
||||
LocationCard(
|
||||
settings = LocationSettings(true, stationPos, {}, { _, _ -> }, { }),
|
||||
setGpsLoc = {}, togglePosDialog = {}) {}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package com.rtbishop.look4sat.presentation.settings
|
||||
|
||||
import com.rtbishop.look4sat.model.StationPos
|
||||
import com.rtbishop.look4sat.model.GeoPos
|
||||
|
||||
data class LocationSettings(
|
||||
val isUpdating: Boolean,
|
||||
val stationPos: StationPos,
|
||||
val stationPos: GeoPos,
|
||||
val setGpsLoc: () -> Unit,
|
||||
val setManualLoc: (Double, Double) -> Unit,
|
||||
val setQthLoc: (String) -> Unit
|
||||
|
|
|
@ -25,7 +25,6 @@ import com.rtbishop.look4sat.domain.ILocationSource
|
|||
import com.rtbishop.look4sat.domain.ISettingsSource
|
||||
import com.rtbishop.look4sat.model.DataState
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
@ -66,7 +65,6 @@ class SettingsViewModel @Inject constructor(
|
|||
init {
|
||||
viewModelScope.launch {
|
||||
locationSource.stationPosition.collect { stationPos ->
|
||||
delay(125)
|
||||
locationSettings.value = locationSettings.value.copy(
|
||||
isUpdating = false, stationPos = stationPos
|
||||
)
|
||||
|
|
|
@ -6,5 +6,5 @@
|
|||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M16,6v14l-11,-7z" />
|
||||
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z" />
|
||||
</vector>
|
|
@ -6,5 +6,5 @@
|
|||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M8,6v14l11,-7z" />
|
||||
android:pathData="M10,8.64L15.27,12 10,15.36V8.64M8,5v14l11,-7L8,5z" />
|
||||
</vector>
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:autoMirrored="true"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z" />
|
||||
</vector>
|
|
@ -17,16 +17,16 @@
|
|||
*/
|
||||
package com.rtbishop.look4sat.domain
|
||||
|
||||
import com.rtbishop.look4sat.model.StationPos
|
||||
import com.rtbishop.look4sat.model.GeoPos
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
interface ILocationSource {
|
||||
|
||||
val stationPosition: StateFlow<StationPos>
|
||||
val stationPosition: StateFlow<GeoPos>
|
||||
|
||||
fun setGpsPosition(): Boolean
|
||||
|
||||
fun setGeoPosition(latitude: Double, longitude: Double): Boolean
|
||||
fun setGeoPosition(latitude: Double, longitude: Double, altitude: Double = 0.0): Boolean
|
||||
|
||||
fun setQthPosition(locator: String): Boolean
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
package com.rtbishop.look4sat.domain
|
||||
|
||||
import com.rtbishop.look4sat.model.StationPos
|
||||
import com.rtbishop.look4sat.model.GeoPos
|
||||
|
||||
interface ISettingsSource {
|
||||
|
||||
|
@ -51,9 +51,9 @@ interface ISettingsSource {
|
|||
"X-Comm" to "https://celestrak.com/NORAD/elements/gp.php?GROUP=x-comm&FORMAT=csv"
|
||||
)
|
||||
|
||||
fun loadStationPosition(): StationPos
|
||||
fun loadStationPosition(): GeoPos
|
||||
|
||||
fun saveStationPosition(stationPos: StationPos)
|
||||
fun saveStationPosition(stationPos: GeoPos)
|
||||
|
||||
fun getHoursAhead(): Int
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ abstract class Satellite(val data: OrbitalData) {
|
|||
val apogee = sma * (1.0 + data.eccn) - EARTH_RADIUS
|
||||
var lin = data.incl
|
||||
if (lin >= 90.0) lin = 180.0 - lin
|
||||
acos(EARTH_RADIUS / (apogee + EARTH_RADIUS)) + lin * DEG2RAD > abs(pos.lat * DEG2RAD)
|
||||
acos(EARTH_RADIUS / (apogee + EARTH_RADIUS)) + lin * DEG2RAD > abs(pos.latitude * DEG2RAD)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,8 +146,8 @@ abstract class Satellite(val data: OrbitalData) {
|
|||
velocityVector.z - obsVel.z
|
||||
)
|
||||
magnitude(range)
|
||||
val sinLat = sin(DEG2RAD * gsPos.lat)
|
||||
val cosLat = cos(DEG2RAD * gsPos.lat)
|
||||
val sinLat = sin(DEG2RAD * gsPos.latitude)
|
||||
val cosLat = cos(DEG2RAD * gsPos.latitude)
|
||||
val sinTheta = sin(gsPosTheta)
|
||||
val cosTheta = cos(gsPosTheta)
|
||||
val topS = sinLat * cosTheta * range.x + sinLat * sinTheta * range.y - cosLat * range.z
|
||||
|
@ -173,13 +173,13 @@ abstract class Satellite(val data: OrbitalData) {
|
|||
obsVel: Vector4
|
||||
) {
|
||||
val mFactor = 7.292115E-5
|
||||
gsPosTheta = mod2PI(thetaGJD(time) + DEG2RAD * gsPos.lon)
|
||||
val c = invert(sqrt(1.0 + FLAT_FACT * (FLAT_FACT - 2) * sqr(sin(DEG2RAD * gsPos.lat))))
|
||||
gsPosTheta = mod2PI(thetaGJD(time) + DEG2RAD * gsPos.longitude)
|
||||
val c = invert(sqrt(1.0 + FLAT_FACT * (FLAT_FACT - 2) * sqr(sin(DEG2RAD * gsPos.latitude))))
|
||||
val sq = sqr(1.0 - FLAT_FACT) * c
|
||||
val achcp = (EARTH_RADIUS * c + gsPos.alt / 1000.0) * cos(DEG2RAD * gsPos.lat)
|
||||
val achcp = (EARTH_RADIUS * c + gsPos.altitude / 1000.0) * cos(DEG2RAD * gsPos.latitude)
|
||||
obsPos.setXYZ(
|
||||
achcp * cos(gsPosTheta), achcp * sin(gsPosTheta),
|
||||
(EARTH_RADIUS * sq + gsPos.alt / 1000.0) * sin(DEG2RAD * gsPos.lat)
|
||||
(EARTH_RADIUS * sq + gsPos.altitude / 1000.0) * sin(DEG2RAD * gsPos.latitude)
|
||||
)
|
||||
obsVel.setXYZ(-mFactor * obsPos.y, mFactor * obsPos.x, 0.0)
|
||||
magnitude(obsPos)
|
||||
|
|
|
@ -17,4 +17,10 @@
|
|||
*/
|
||||
package com.rtbishop.look4sat.model
|
||||
|
||||
data class GeoPos(val lat: Double, val lon: Double, val alt: Double = 0.0, val name: String? = null)
|
||||
data class GeoPos(
|
||||
val latitude: Double,
|
||||
val longitude: Double,
|
||||
val altitude: Double = 0.0,
|
||||
val qthLocator: String = "null",
|
||||
val timestamp: Long = 0L
|
||||
)
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
package com.rtbishop.look4sat.model
|
||||
|
||||
data class StationPos(
|
||||
val latitude: Double, val longitude: Double, val qthLocator: String, val timestamp: Long
|
||||
) {
|
||||
fun toGeoPos(): GeoPos = GeoPos(latitude, longitude)
|
||||
}
|
|
@ -25,11 +25,11 @@ class QthConverterTest {
|
|||
@Test
|
||||
fun `Given valid QTH returns correct POS`() {
|
||||
var result = QthConverter.qthToPosition("io91VL39FX")
|
||||
assert(result?.lat == 51.4792 && result.lon == -0.2083)
|
||||
assert(result?.latitude == 51.4792 && result.longitude == -0.2083)
|
||||
result = QthConverter.qthToPosition("JN58TD")
|
||||
assert(result?.lat == 48.1458 && result.lon == 11.6250)
|
||||
assert(result?.latitude == 48.1458 && result.longitude == 11.6250)
|
||||
result = QthConverter.qthToPosition("gf15vc")
|
||||
assert(result?.lat == -34.8958 && result.lon == -56.2083)
|
||||
assert(result?.latitude == -34.8958 && result.longitude == -56.2083)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Ładowanie…
Reference in New Issue