mirror of
https://github.com/rt-bishop/Look4Sat.git
synced 2026-10-04 19:57:27 +00:00
Further classes movement and cleanup
This commit is contained in:
1 parent
542cfcfd50
commit
622d4e3632
27 files changed
+684
-578
No files matched your search
@@ -19,7 +19,7 @@ package com.rtbishop.look4sat
|
|||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import com.rtbishop.look4sat.domain.IDataRepository
|
import com.rtbishop.look4sat.domain.IDataRepository
|
||||||
import com.rtbishop.look4sat.domain.ISettingsSource
|
import com.rtbishop.look4sat.domain.ISettingsRepository
|
||||||
import dagger.hilt.android.HiltAndroidApp
|
import dagger.hilt.android.HiltAndroidApp
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
@@ -33,7 +33,7 @@ class MainApplication : Application() {
|
|||||||
lateinit var dataRepository: IDataRepository
|
lateinit var dataRepository: IDataRepository
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var settingsSource: ISettingsSource
|
lateinit var settingsRepository: ISettingsRepository
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
@@ -44,8 +44,8 @@ class MainApplication : Application() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun checkAutoUpdate() {
|
private fun checkAutoUpdate() {
|
||||||
if (settingsSource.isUpdateEnabled()) {
|
if (settingsRepository.isUpdateEnabled()) {
|
||||||
val timeDelta = System.currentTimeMillis() - settingsSource.getLastUpdateTime()
|
val timeDelta = System.currentTimeMillis() - settingsRepository.getLastUpdateTime()
|
||||||
if (timeDelta > 172800000) { // 48 hours in ms
|
if (timeDelta > 172800000) { // 48 hours in ms
|
||||||
val sdf = SimpleDateFormat("d MMM yyyy - HH:mm:ss", Locale.getDefault())
|
val sdf = SimpleDateFormat("d MMM yyyy - HH:mm:ss", Locale.getDefault())
|
||||||
Timber.d("Started periodic data update on ${sdf.format(Date())}")
|
Timber.d("Started periodic data update on ${sdf.format(Date())}")
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import android.location.LocationManager
|
|||||||
import androidx.room.Room
|
import androidx.room.Room
|
||||||
import com.rtbishop.look4sat.data.DataParser
|
import com.rtbishop.look4sat.data.DataParser
|
||||||
import com.rtbishop.look4sat.data.DataRepository
|
import com.rtbishop.look4sat.data.DataRepository
|
||||||
|
import com.rtbishop.look4sat.data.SatelliteRepository
|
||||||
import com.rtbishop.look4sat.domain.*
|
import com.rtbishop.look4sat.domain.*
|
||||||
import com.rtbishop.look4sat.framework.*
|
import com.rtbishop.look4sat.framework.*
|
||||||
import com.rtbishop.look4sat.framework.data.*
|
import com.rtbishop.look4sat.framework.data.*
|
||||||
@@ -55,12 +56,14 @@ object MainModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideNetworkReporter(): NetworkReporter = NetworkReporter(CoroutineScope(Dispatchers.IO))
|
fun provideNetworkReporter(): NetworkReporter {
|
||||||
|
return NetworkReporter(CoroutineScope(Dispatchers.IO))
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideDataRepository(
|
fun provideDataRepository(
|
||||||
@ApplicationContext context: Context, settings: ISettingsSource
|
@ApplicationContext context: Context, settings: ISettingsRepository
|
||||||
): IDataRepository {
|
): IDataRepository {
|
||||||
val db = Room.databaseBuilder(context, MainDatabase::class.java, "Look4SatDb")
|
val db = Room.databaseBuilder(context, MainDatabase::class.java, "Look4SatDb")
|
||||||
.addMigrations(MIGRATION_1_2).fallbackToDestructiveMigration().build()
|
.addMigrations(MIGRATION_1_2).fallbackToDestructiveMigration().build()
|
||||||
@@ -75,28 +78,23 @@ object MainModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideLocationSource(
|
fun provideSensorRepository(@ApplicationContext context: Context): ISensorRepository {
|
||||||
@ApplicationContext context: Context, settings: ISettingsSource
|
|
||||||
): ILocationSource {
|
|
||||||
val manager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
|
||||||
return LocationSource(manager, settings)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
fun provideOrientationSource(@ApplicationContext context: Context): OrientationSource {
|
|
||||||
val manager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
|
val manager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
|
||||||
val sensor = manager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)
|
val sensor = manager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)
|
||||||
return OrientationSource(manager, sensor)
|
return SensorRepository(manager, sensor)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideSatelliteManager(): ISatelliteManager = SatelliteManager(Dispatchers.Default)
|
fun provideSatelliteRepository(): ISatelliteRepository {
|
||||||
|
return SatelliteRepository(Dispatchers.Default)
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideSettingsSource(@ApplicationContext context: Context): ISettingsSource {
|
fun provideSettingsRepository(@ApplicationContext context: Context): ISettingsRepository {
|
||||||
return SettingsSource(context.getSharedPreferences("default", Context.MODE_PRIVATE))
|
val manager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||||
|
val preferences = context.getSharedPreferences("default", Context.MODE_PRIVATE)
|
||||||
|
return SettingsRepository(manager, preferences)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
/*
|
|
||||||
* Look4Sat. Amateur radio satellite tracker and pass predictor.
|
|
||||||
* Copyright (C) 2019-2022 Arty Bishop (bishop.arty@gmail.com)
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package com.rtbishop.look4sat.framework
|
|
||||||
|
|
||||||
import android.location.Criteria
|
|
||||||
import android.location.LocationManager
|
|
||||||
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.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 {
|
|
||||||
|
|
||||||
private val _stationPosition = MutableStateFlow(settings.loadStationPosition())
|
|
||||||
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
|
|
||||||
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, altitude: Double): Boolean {
|
|
||||||
val newLongitude = if (longitude > 180.0) longitude - 180 else longitude
|
|
||||||
val locator = QthConverter.positionToQth(latitude, newLongitude) ?: return false
|
|
||||||
saveGeoPos(latitude, newLongitude, altitude, locator)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setQthPosition(locator: String): Boolean {
|
|
||||||
val position = QthConverter.qthToPosition(locator) ?: return false
|
|
||||||
saveGeoPos(position.latitude, position.longitude, 0.0, locator)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun saveGeoPos(latitude: Double, longitude: Double, altitude: Double, locator: String) {
|
|
||||||
val newLat = latitude.round(4)
|
|
||||||
val newLon = longitude.round(4)
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+7
-7
@@ -21,27 +21,27 @@ import android.hardware.Sensor
|
|||||||
import android.hardware.SensorEvent
|
import android.hardware.SensorEvent
|
||||||
import android.hardware.SensorEventListener
|
import android.hardware.SensorEventListener
|
||||||
import android.hardware.SensorManager
|
import android.hardware.SensorManager
|
||||||
|
import com.rtbishop.look4sat.domain.ISensorRepository
|
||||||
import com.rtbishop.look4sat.domain.RAD2DEG
|
import com.rtbishop.look4sat.domain.RAD2DEG
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlin.math.round
|
import kotlin.math.round
|
||||||
|
|
||||||
class OrientationSource(
|
class SensorRepository(private val manager: SensorManager, private val sensor: Sensor?) :
|
||||||
private val manager: SensorManager, private val sensor: Sensor?
|
SensorEventListener, ISensorRepository {
|
||||||
) : SensorEventListener {
|
|
||||||
|
|
||||||
private val rotationMatrix = FloatArray(9)
|
private val rotationMatrix = FloatArray(9)
|
||||||
private val orientationValues = FloatArray(3)
|
private val orientationValues = FloatArray(3)
|
||||||
private var sensorAccuracy: Int = SensorManager.SENSOR_STATUS_UNRELIABLE
|
private var sensorAccuracy: Int = SensorManager.SENSOR_STATUS_UNRELIABLE
|
||||||
|
|
||||||
private val _orientation = MutableStateFlow(Pair(0f, 0f))
|
private val _orientation = MutableStateFlow(Pair(0f, 0f))
|
||||||
val orientation: StateFlow<Pair<Float, Float>> = _orientation
|
override val orientation: StateFlow<Pair<Float, Float>> = _orientation
|
||||||
|
|
||||||
fun startListening() = sensor?.let {
|
override fun enableSensor() {
|
||||||
manager.registerListener(this, it, 16000)
|
sensor?.let { manager.registerListener(this, it, 16000) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stopListening() = manager.unregisterListener(this)
|
override fun disableSensor() = manager.unregisterListener(this)
|
||||||
|
|
||||||
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
|
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
|
||||||
sensorAccuracy = accuracy
|
sensorAccuracy = accuracy
|
||||||
@@ -0,0 +1,277 @@
|
|||||||
|
/*
|
||||||
|
* Look4Sat. Amateur radio satellite tracker and pass predictor.
|
||||||
|
* Copyright (C) 2019-2022 Arty Bishop (bishop.arty@gmail.com)
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.rtbishop.look4sat.framework
|
||||||
|
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.location.Criteria
|
||||||
|
import android.location.LocationManager
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import androidx.core.location.LocationManagerCompat
|
||||||
|
import androidx.core.os.CancellationSignal
|
||||||
|
import com.rtbishop.look4sat.domain.ISettingsRepository
|
||||||
|
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 SettingsRepository(
|
||||||
|
private val manager: LocationManager, private val preferences: SharedPreferences
|
||||||
|
) : ISettingsRepository {
|
||||||
|
|
||||||
|
private val keyModes = "satModes"
|
||||||
|
private val keyCompass = "compass"
|
||||||
|
private val keyRadarSweep = "radarSweep"
|
||||||
|
private val keyTimeUTC = "timeUTC"
|
||||||
|
private val keyLastUpdateTime = "lastUpdateTime"
|
||||||
|
private val keyAutoUpdateEnabled = "autoUpdateEnabled"
|
||||||
|
private val keyHoursAhead = "hoursAhead"
|
||||||
|
private val keyMinElevation = "minElevation"
|
||||||
|
private val keyRotator = "isRotatorEnabled"
|
||||||
|
private val keyRotatorAddress = "rotatorAddress"
|
||||||
|
private val keyRotatorPort = "rotatorPort"
|
||||||
|
private val keyBTEnabled = "isBTEnabled"
|
||||||
|
private val keyBTDeviceName = "BTDeviceName"
|
||||||
|
private val keyBTDeviceAddr = "BTDeviceAddr"
|
||||||
|
private val keyBTFormat = "BTFormat"
|
||||||
|
private val keyLatitude = "stationLat"
|
||||||
|
private val keyLongitude = "stationLon"
|
||||||
|
private val keyAltitude = "stationAlt"
|
||||||
|
private val keyLocator = "stationQTH"
|
||||||
|
private val keyLocTimestamp = "locTimestamp"
|
||||||
|
private val keySelection = "selection"
|
||||||
|
|
||||||
|
//region # Station position settings
|
||||||
|
|
||||||
|
private val _stationPosition = MutableStateFlow(loadStationPosition())
|
||||||
|
private val defaultProvider = LocationManager.PASSIVE_PROVIDER
|
||||||
|
private val executor = Executors.newSingleThreadExecutor()
|
||||||
|
private val timeoutSignal = CancellationSignal().apply {
|
||||||
|
setOnCancelListener { _stationPosition.value = loadStationPosition() }
|
||||||
|
}
|
||||||
|
override val stationPosition: StateFlow<GeoPos> = _stationPosition
|
||||||
|
|
||||||
|
override fun setGpsPosition(): Boolean {
|
||||||
|
if (!LocationManagerCompat.isLocationEnabled(manager)) return false
|
||||||
|
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, altitude: Double): Boolean {
|
||||||
|
val newLongitude = if (longitude > 180.0) longitude - 180 else longitude
|
||||||
|
val locator = QthConverter.positionToQth(latitude, newLongitude) ?: return false
|
||||||
|
saveGeoPos(latitude, newLongitude, altitude, locator)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setQthPosition(locator: String): Boolean {
|
||||||
|
val position = QthConverter.qthToPosition(locator) ?: return false
|
||||||
|
saveGeoPos(position.latitude, position.longitude, 0.0, locator)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadStationPosition(): GeoPos {
|
||||||
|
val latitude = (preferences.getString(keyLatitude, null) ?: "0.0").toDouble()
|
||||||
|
val longitude = (preferences.getString(keyLongitude, null) ?: "0.0").toDouble()
|
||||||
|
val altitude = (preferences.getString(keyAltitude, null) ?: "0.0").toDouble()
|
||||||
|
val qthLocator = preferences.getString(keyLocator, null) ?: "null"
|
||||||
|
val timestamp = preferences.getLong(keyLocTimestamp, 0L)
|
||||||
|
return GeoPos(latitude, longitude, altitude, qthLocator, timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveStationPosition(stationPos: GeoPos) = preferences.edit {
|
||||||
|
putString(keyLatitude, stationPos.latitude.toString())
|
||||||
|
putString(keyLongitude, stationPos.longitude.toString())
|
||||||
|
putString(keyAltitude, stationPos.altitude.toString())
|
||||||
|
putString(keyLocator, stationPos.qthLocator)
|
||||||
|
putLong(keyLocTimestamp, stationPos.timestamp)
|
||||||
|
_stationPosition.value = stationPos
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveGeoPos(latitude: Double, longitude: Double, altitude: Double, locator: String) {
|
||||||
|
val newLat = latitude.round(4)
|
||||||
|
val newLon = longitude.round(4)
|
||||||
|
val newAlt = altitude.round(1)
|
||||||
|
val timestamp = System.currentTimeMillis()
|
||||||
|
Timber.d("Received new Position($newLat, $newLon, $newAlt) & Locator $locator")
|
||||||
|
saveStationPosition(GeoPos(newLat, newLon, newAlt, locator, timestamp))
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
override fun saveEntriesSelection(catnums: List<Int>) {
|
||||||
|
val stringList = catnums.map { catnum -> catnum.toString() }
|
||||||
|
preferences.edit { putStringSet(keySelection, stringList.toSet()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun loadEntriesSelection(): List<Int> {
|
||||||
|
val catnums =
|
||||||
|
preferences.getStringSet(keySelection, emptySet())?.map { catnum -> catnum.toInt() }
|
||||||
|
return catnums?.sorted() ?: emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun saveSatType(type: String, catnums: List<Int>) {
|
||||||
|
val stringList = catnums.map { catnum -> catnum.toString() }
|
||||||
|
preferences.edit { putStringSet("type$type", stringList.toSet()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun loadSatType(type: String): List<Int> {
|
||||||
|
val catnums =
|
||||||
|
preferences.getStringSet("type$type", emptySet())?.map { catnum -> catnum.toInt() }
|
||||||
|
return catnums ?: emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getHoursAhead(): Int {
|
||||||
|
return preferences.getInt(keyHoursAhead, 24)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setHoursAhead(hoursAhead: Int) {
|
||||||
|
preferences.edit { putInt(keyHoursAhead, hoursAhead) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getMinElevation(): Double {
|
||||||
|
return preferences.getDouble(keyMinElevation, 16.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setMinElevation(minElevation: Double) {
|
||||||
|
preferences.edit { putDouble(keyMinElevation, minElevation) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isUtcEnabled(): Boolean {
|
||||||
|
return preferences.getBoolean(keyTimeUTC, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setUtcState(value: Boolean) {
|
||||||
|
preferences.edit { putBoolean(keyTimeUTC, value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getLastUpdateTime(): Long {
|
||||||
|
return preferences.getLong(keyLastUpdateTime, 0L)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setLastUpdateTime(updateTime: Long) {
|
||||||
|
preferences.edit { putLong(keyLastUpdateTime, updateTime) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isUpdateEnabled(): Boolean {
|
||||||
|
return preferences.getBoolean(keyAutoUpdateEnabled, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setUpdateState(value: Boolean) {
|
||||||
|
preferences.edit { putBoolean(keyAutoUpdateEnabled, value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isSensorEnabled(): Boolean {
|
||||||
|
return preferences.getBoolean(keyCompass, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setSensorState(value: Boolean) {
|
||||||
|
preferences.edit { putBoolean(keyCompass, value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isSweepEnabled(): Boolean {
|
||||||
|
return preferences.getBoolean(keyRadarSweep, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setSweepState(value: Boolean) {
|
||||||
|
preferences.edit { putBoolean(keyRadarSweep, value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun saveModesSelection(modes: List<String>) {
|
||||||
|
preferences.edit { putStringSet(keyModes, modes.toSet()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun loadModesSelection(): List<String> {
|
||||||
|
return preferences.getStringSet(keyModes, null)?.toList()?.sorted() ?: emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getRotatorEnabled(): Boolean {
|
||||||
|
return preferences.getBoolean(keyRotator, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setRotatorEnabled(value: Boolean) {
|
||||||
|
preferences.edit { putBoolean(keyRotator, value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getRotatorServer(): String {
|
||||||
|
return preferences.getString(keyRotatorAddress, null) ?: "127.0.0.1"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setRotatorServer(value: String) {
|
||||||
|
preferences.edit { putString(keyRotatorAddress, value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getRotatorPort(): String {
|
||||||
|
return preferences.getString(keyRotatorPort, null) ?: "4533"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setRotatorPort(value: String) {
|
||||||
|
preferences.edit { putString(keyRotatorPort, value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getBTEnabled(): Boolean {
|
||||||
|
return preferences.getBoolean(keyBTEnabled, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setBTEnabled(value: Boolean) {
|
||||||
|
preferences.edit { putBoolean(keyBTEnabled, value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getBTDeviceAddr(): String {
|
||||||
|
return preferences.getString(keyBTDeviceAddr, null) ?: "00:0C:BF:13:80:5D"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setBTDeviceAddr(value: String) {
|
||||||
|
preferences.edit { putString(keyBTDeviceAddr, value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getBTDeviceName(): String {
|
||||||
|
return preferences.getString(keyBTDeviceName, null) ?: "Default"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setBTDeviceName(value: String) {
|
||||||
|
preferences.edit { putString(keyBTDeviceName, value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getBTFormat(): String {
|
||||||
|
return preferences.getString(keyBTFormat, null) ?: "W\$AZ \$EL"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setBTFormat(value: String) {
|
||||||
|
preferences.edit { putString(keyBTFormat, value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun SharedPreferences.getDouble(key: String, default: Double): Double {
|
||||||
|
return Double.fromBits(getLong(key, default.toRawBits()))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun SharedPreferences.Editor.putDouble(key: String, double: Double) {
|
||||||
|
putLong(key, double.toRawBits())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,238 +0,0 @@
|
|||||||
/*
|
|
||||||
* Look4Sat. Amateur radio satellite tracker and pass predictor.
|
|
||||||
* Copyright (C) 2019-2022 Arty Bishop (bishop.arty@gmail.com)
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
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.GeoPos
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.onCompletion
|
|
||||||
|
|
||||||
class SettingsSource(private val prefs: SharedPreferences) : ISettingsSource {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val keyModes = "satModes"
|
|
||||||
const val keyCompass = "compass"
|
|
||||||
const val keyRadarSweep = "radarSweep"
|
|
||||||
const val keyTimeUTC = "timeUTC"
|
|
||||||
const val keyLastUpdateTime = "lastUpdateTime"
|
|
||||||
const val keyAutoUpdateEnabled = "autoUpdateEnabled"
|
|
||||||
const val keyHoursAhead = "hoursAhead"
|
|
||||||
const val keyMinElevation = "minElevation"
|
|
||||||
const val keyRotator = "isRotatorEnabled"
|
|
||||||
const val keyRotatorAddress = "rotatorAddress"
|
|
||||||
const val keyRotatorPort = "rotatorPort"
|
|
||||||
const val keyBTEnabled = "isBTEnabled"
|
|
||||||
const val keyBTDeviceName = "BTDeviceName"
|
|
||||||
const val keyBTDeviceAddr = "BTDeviceAddr"
|
|
||||||
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(): 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 GeoPos(latitude, longitude, altitude, qthLocator, timestamp)
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun saveEntriesSelection(catnums: List<Int>) {
|
|
||||||
val stringList = catnums.map { catnum -> catnum.toString() }
|
|
||||||
prefs.edit { putStringSet(keySelection, stringList.toSet()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun loadEntriesSelection(): List<Int> {
|
|
||||||
val catnums = prefs.getStringSet(keySelection, emptySet())?.map { catnum -> catnum.toInt() }
|
|
||||||
return catnums?.sorted() ?: emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun saveSatType(type: String, catnums: List<Int>) {
|
|
||||||
val stringList = catnums.map { catnum -> catnum.toString() }
|
|
||||||
prefs.edit { putStringSet("type$type", stringList.toSet()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun loadSatType(type: String): List<Int> {
|
|
||||||
val catnums = prefs.getStringSet("type$type", emptySet())?.map { catnum -> catnum.toInt() }
|
|
||||||
return catnums ?: emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getHoursAhead(): Int {
|
|
||||||
return prefs.getInt(keyHoursAhead, 24)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setHoursAhead(hoursAhead: Int) {
|
|
||||||
prefs.edit { putInt(keyHoursAhead, hoursAhead) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getMinElevation(): Double {
|
|
||||||
return prefs.getDouble(keyMinElevation, 16.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setMinElevation(minElevation: Double) {
|
|
||||||
prefs.edit { putDouble(keyMinElevation, minElevation) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isUtcEnabled(): Boolean {
|
|
||||||
return prefs.getBoolean(keyTimeUTC, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setUtcState(value: Boolean) {
|
|
||||||
prefs.edit { putBoolean(keyTimeUTC, value) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getLastUpdateTime(): Long {
|
|
||||||
return prefs.getLong(keyLastUpdateTime, 0L)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setLastUpdateTime(updateTime: Long) {
|
|
||||||
prefs.edit { putLong(keyLastUpdateTime, updateTime) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isUpdateEnabled(): Boolean {
|
|
||||||
return prefs.getBoolean(keyAutoUpdateEnabled, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setUpdateState(value: Boolean) {
|
|
||||||
prefs.edit { putBoolean(keyAutoUpdateEnabled, value) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isSensorEnabled(): Boolean {
|
|
||||||
return prefs.getBoolean(keyCompass, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setSensorState(value: Boolean) {
|
|
||||||
prefs.edit { putBoolean(keyCompass, value) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isSweepEnabled(): Boolean {
|
|
||||||
return prefs.getBoolean(keyRadarSweep, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setSweepState(value: Boolean) {
|
|
||||||
prefs.edit { putBoolean(keyRadarSweep, value) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun saveModesSelection(modes: List<String>) {
|
|
||||||
prefs.edit { putStringSet(keyModes, modes.toSet()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun loadModesSelection(): List<String> {
|
|
||||||
return prefs.getStringSet(keyModes, null)?.toList()?.sorted() ?: emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getRotatorEnabled(): Boolean {
|
|
||||||
return prefs.getBoolean(keyRotator, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setRotatorEnabled(value: Boolean) {
|
|
||||||
prefs.edit { putBoolean(keyRotator, value) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getRotatorServer(): String {
|
|
||||||
return prefs.getString(keyRotatorAddress, null) ?: "127.0.0.1"
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setRotatorServer(value: String) {
|
|
||||||
prefs.edit { putString(keyRotatorAddress, value) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getRotatorPort(): String {
|
|
||||||
return prefs.getString(keyRotatorPort, null) ?: "4533"
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setRotatorPort(value: String) {
|
|
||||||
prefs.edit { putString(keyRotatorPort, value) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getBTEnabled(): Boolean {
|
|
||||||
return prefs.getBoolean(keyBTEnabled, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setBTEnabled(value: Boolean) {
|
|
||||||
prefs.edit { putBoolean(keyBTEnabled, value) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getBTDeviceAddr(): String {
|
|
||||||
return prefs.getString(keyBTDeviceAddr, null) ?: "00:0C:BF:13:80:5D"
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setBTDeviceAddr(value: String) {
|
|
||||||
prefs.edit { putString(keyBTDeviceAddr, value) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getBTDeviceName(): String {
|
|
||||||
return prefs.getString(keyBTDeviceName, null) ?: "Default"
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setBTDeviceName(value: String) {
|
|
||||||
prefs.edit { putString(keyBTDeviceName, value) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getBTFormat(): String {
|
|
||||||
return prefs.getString(keyBTFormat, null) ?: "W\$AZ \$EL"
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setBTFormat(value: String) {
|
|
||||||
prefs.edit { putString(keyBTFormat, value) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun SharedPreferences.getDouble(key: String, default: Double): Double {
|
|
||||||
return Double.fromBits(getLong(key, default.toRawBits()))
|
|
||||||
}
|
|
||||||
|
|
||||||
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}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -20,11 +20,9 @@ package com.rtbishop.look4sat.presentation.entries
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.rtbishop.look4sat.domain.IDataRepository
|
import com.rtbishop.look4sat.domain.IDataRepository
|
||||||
import com.rtbishop.look4sat.domain.ILocationSource
|
import com.rtbishop.look4sat.domain.ISatelliteRepository
|
||||||
import com.rtbishop.look4sat.domain.ISatelliteManager
|
import com.rtbishop.look4sat.domain.ISettingsRepository
|
||||||
import com.rtbishop.look4sat.domain.ISettingsSource
|
|
||||||
import com.rtbishop.look4sat.model.DataState
|
import com.rtbishop.look4sat.model.DataState
|
||||||
import com.rtbishop.look4sat.model.GeoPos
|
|
||||||
import com.rtbishop.look4sat.model.SatItem
|
import com.rtbishop.look4sat.model.SatItem
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
@@ -37,10 +35,9 @@ import javax.inject.Inject
|
|||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class EntriesViewModel @Inject constructor(
|
class EntriesViewModel @Inject constructor(
|
||||||
private val satelliteManager: ISatelliteManager,
|
private val dataRepository: IDataRepository,
|
||||||
private val repository: IDataRepository,
|
private val satelliteRepository: ISatelliteRepository,
|
||||||
private val settings: ISettingsSource,
|
private val settingsRepository: ISettingsRepository
|
||||||
private val locationSource: ILocationSource
|
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val satType = MutableStateFlow("All")
|
private val satType = MutableStateFlow("All")
|
||||||
@@ -53,12 +50,12 @@ class EntriesViewModel @Inject constructor(
|
|||||||
itemsWithType.map { items -> filterByQuery(items, query) }
|
itemsWithType.map { items -> filterByQuery(items, query) }
|
||||||
}
|
}
|
||||||
val satData = itemsWithQuery.map { items -> DataState.Success(items) }
|
val satData = itemsWithQuery.map { items -> DataState.Success(items) }
|
||||||
val satTypes: List<String> = settings.sourcesMap.keys.sorted()
|
val satTypes = dataRepository.getSatelliteTypes()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
delay(250)
|
delay(250)
|
||||||
itemsFromRepo.value = loadEntriesWithSelection()
|
itemsFromRepo.value = dataRepository.getEntriesWithSelection()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,15 +75,14 @@ class EntriesViewModel @Inject constructor(
|
|||||||
|
|
||||||
fun saveSelection() = viewModelScope.launch {
|
fun saveSelection() = viewModelScope.launch {
|
||||||
val newSelection = itemsFromRepo.value.filter { it.isSelected }.map { it.catnum }
|
val newSelection = itemsFromRepo.value.filter { it.isSelected }.map { it.catnum }
|
||||||
settings.saveEntriesSelection(newSelection)
|
settingsRepository.saveEntriesSelection(newSelection)
|
||||||
val selectedIds = settings.loadEntriesSelection()
|
val selectedIds = settingsRepository.loadEntriesSelection()
|
||||||
val satellites = repository.getEntriesWithIds(selectedIds)
|
val satellites = dataRepository.getEntriesWithIds(selectedIds)
|
||||||
val stationPos = locationSource.stationPosition.value
|
val stationPos = settingsRepository.stationPosition.value
|
||||||
val geoPos = GeoPos(stationPos.latitude, stationPos.longitude)
|
|
||||||
val timeRef = System.currentTimeMillis()
|
val timeRef = System.currentTimeMillis()
|
||||||
val hoursAhead = settings.getHoursAhead()
|
val hoursAhead = settingsRepository.getHoursAhead()
|
||||||
val minElev = settings.getMinElevation()
|
val minElev = settingsRepository.getMinElevation()
|
||||||
satelliteManager.calculatePasses(satellites, geoPos, timeRef, hoursAhead, minElev)
|
satelliteRepository.calculatePasses(satellites, stationPos, timeRef, hoursAhead, minElev)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateSelection(catNums: List<Int>, isSelected: Boolean) {
|
fun updateSelection(catNums: List<Int>, isSelected: Boolean) {
|
||||||
@@ -99,14 +95,9 @@ class EntriesViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun loadEntriesWithSelection(): List<SatItem> {
|
|
||||||
val selectedIds = settings.loadEntriesSelection()
|
|
||||||
return repository.getEntriesWithModes().onEach { it.isSelected = it.catnum in selectedIds }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun filterByType(items: List<SatItem>, type: String): List<SatItem> {
|
private fun filterByType(items: List<SatItem>, type: String): List<SatItem> {
|
||||||
if (type == "All") return items
|
if (type == "All") return items
|
||||||
val catnums = settings.loadSatType(type)
|
val catnums = settingsRepository.loadSatType(type)
|
||||||
if (catnums.isEmpty()) return items
|
if (catnums.isEmpty()) return items
|
||||||
return items.filter { item -> item.catnum in catnums }
|
return items.filter { item -> item.catnum in catnums }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,10 @@ package com.rtbishop.look4sat.presentation.map
|
|||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.rtbishop.look4sat.domain.*
|
import com.rtbishop.look4sat.domain.IDataRepository
|
||||||
|
import com.rtbishop.look4sat.domain.ISatelliteRepository
|
||||||
|
import com.rtbishop.look4sat.domain.ISettingsRepository
|
||||||
|
import com.rtbishop.look4sat.domain.Satellite
|
||||||
import com.rtbishop.look4sat.model.GeoPos
|
import com.rtbishop.look4sat.model.GeoPos
|
||||||
import com.rtbishop.look4sat.model.SatPos
|
import com.rtbishop.look4sat.model.SatPos
|
||||||
import com.rtbishop.look4sat.utility.QthConverter
|
import com.rtbishop.look4sat.utility.QthConverter
|
||||||
@@ -38,15 +41,14 @@ import kotlin.math.min
|
|||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class MapViewModel @Inject constructor(
|
class MapViewModel @Inject constructor(
|
||||||
private val repository: IDataRepository,
|
private val dataRepository: IDataRepository,
|
||||||
private val satelliteManager: ISatelliteManager,
|
private val satelliteRepository: ISatelliteRepository,
|
||||||
private val settings: ISettingsSource,
|
private val settingsRepository: ISettingsRepository
|
||||||
private val locationSource: ILocationSource
|
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val stationPosNew = locationSource.stationPosition.value
|
private val stationPosNew = settingsRepository.stationPosition.value
|
||||||
private val stationPosition = GeoPos(stationPosNew.latitude, stationPosNew.longitude)
|
private val stationPosition = GeoPos(stationPosNew.latitude, stationPosNew.longitude)
|
||||||
private var allPasses = satelliteManager.getPasses()
|
private var allPasses = satelliteRepository.getPasses()
|
||||||
private var allSatellites = listOf<Satellite>()
|
private var allSatellites = listOf<Satellite>()
|
||||||
private var dataUpdateJob: Job? = null
|
private var dataUpdateJob: Job? = null
|
||||||
private var dataUpdateRate = 1000L
|
private var dataUpdateRate = 1000L
|
||||||
@@ -85,8 +87,8 @@ class MapViewModel @Inject constructor(
|
|||||||
|
|
||||||
fun selectDefaultSatellite(catnum: Int) {
|
fun selectDefaultSatellite(catnum: Int) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val selectedIds = settings.loadEntriesSelection()
|
val selectedIds = settingsRepository.loadEntriesSelection()
|
||||||
repository.getEntriesWithIds(selectedIds).also { satellites ->
|
dataRepository.getEntriesWithIds(selectedIds).also { satellites ->
|
||||||
if (satellites.isNotEmpty()) {
|
if (satellites.isNotEmpty()) {
|
||||||
allSatellites = satellites
|
allSatellites = satellites
|
||||||
if (catnum == -1) {
|
if (catnum == -1) {
|
||||||
@@ -123,7 +125,7 @@ class MapViewModel @Inject constructor(
|
|||||||
val currentTrack = mutableListOf<GeoPos>()
|
val currentTrack = mutableListOf<GeoPos>()
|
||||||
val endDate = Date(date.time + (satellite.data.orbitalPeriod * 2.4 * 60000L).toLong())
|
val endDate = Date(date.time + (satellite.data.orbitalPeriod * 2.4 * 60000L).toLong())
|
||||||
var oldLongitude = 0.0
|
var oldLongitude = 0.0
|
||||||
satelliteManager.getTrack(satellite, pos, date.time, endDate.time).forEach { satPos ->
|
satelliteRepository.getTrack(satellite, pos, date.time, endDate.time).forEach { satPos ->
|
||||||
val osmLat = clipLat(satPos.latitude.toDegrees())
|
val osmLat = clipLat(satPos.latitude.toDegrees())
|
||||||
val osmLon = clipLon(satPos.longitude.toDegrees())
|
val osmLon = clipLon(satPos.longitude.toDegrees())
|
||||||
val currentPosition = GeoPos(osmLat, osmLon)
|
val currentPosition = GeoPos(osmLat, osmLon)
|
||||||
@@ -150,7 +152,7 @@ class MapViewModel @Inject constructor(
|
|||||||
private suspend fun getPositions(satellites: List<Satellite>, pos: GeoPos, date: Date) {
|
private suspend fun getPositions(satellites: List<Satellite>, pos: GeoPos, date: Date) {
|
||||||
val positions = mutableMapOf<Satellite, GeoPos>()
|
val positions = mutableMapOf<Satellite, GeoPos>()
|
||||||
satellites.forEach { satellite ->
|
satellites.forEach { satellite ->
|
||||||
val satPos = satelliteManager.getPosition(satellite, pos, date.time)
|
val satPos = satelliteRepository.getPosition(satellite, pos, date.time)
|
||||||
val osmLat = clipLat(satPos.latitude.toDegrees())
|
val osmLat = clipLat(satPos.latitude.toDegrees())
|
||||||
val osmLon = clipLon(satPos.longitude.toDegrees())
|
val osmLon = clipLon(satPos.longitude.toDegrees())
|
||||||
positions[satellite] = GeoPos(osmLat, osmLon)
|
positions[satellite] = GeoPos(osmLat, osmLon)
|
||||||
@@ -159,7 +161,7 @@ class MapViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getSatFootprint(satellite: Satellite, pos: GeoPos, date: Date) {
|
private suspend fun getSatFootprint(satellite: Satellite, pos: GeoPos, date: Date) {
|
||||||
val satPos = satelliteManager.getPosition(satellite, pos, date.time)
|
val satPos = satelliteRepository.getPosition(satellite, pos, date.time)
|
||||||
_footprint.emit(satPos)
|
_footprint.emit(satPos)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,7 +179,7 @@ class MapViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val satPos = satelliteManager.getPosition(sat, pos, date.time)
|
val satPos = satelliteRepository.getPosition(sat, pos, date.time)
|
||||||
val azimuth = satPos.azimuth.toDegrees()
|
val azimuth = satPos.azimuth.toDegrees()
|
||||||
val elevation = satPos.elevation.toDegrees()
|
val elevation = satPos.elevation.toDegrees()
|
||||||
val osmLat = clipLat(satPos.latitude.toDegrees())
|
val osmLat = clipLat(satPos.latitude.toDegrees())
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||||||
import androidx.compose.ui.unit.*
|
import androidx.compose.ui.unit.*
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import com.rtbishop.look4sat.R
|
import com.rtbishop.look4sat.R
|
||||||
import com.rtbishop.look4sat.domain.NearEarthSat
|
import com.rtbishop.look4sat.domain.NearEarthSatellite
|
||||||
import com.rtbishop.look4sat.model.DataState
|
import com.rtbishop.look4sat.model.DataState
|
||||||
import com.rtbishop.look4sat.model.OrbitalData
|
import com.rtbishop.look4sat.model.OrbitalData
|
||||||
import com.rtbishop.look4sat.model.SatPass
|
import com.rtbishop.look4sat.model.SatPass
|
||||||
@@ -100,7 +100,7 @@ private fun PassPreview() {
|
|||||||
val data = OrbitalData(
|
val data = OrbitalData(
|
||||||
"Satellite", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 45000, 0.0
|
"Satellite", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 45000, 0.0
|
||||||
)
|
)
|
||||||
val satellite = NearEarthSat(data)
|
val satellite = NearEarthSatellite(data)
|
||||||
val pass = SatPass(1L, 25.0, 10L, 75.0, 850, 45.0, satellite, 0.5f)
|
val pass = SatPass(1L, 25.0, 10L, 75.0, 850, 45.0, satellite, 0.5f)
|
||||||
MainTheme { Pass(pass = pass, { _,_ -> }) }
|
MainTheme { Pass(pass = pass, { _,_ -> }) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,11 +20,9 @@ package com.rtbishop.look4sat.presentation.passes
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.rtbishop.look4sat.domain.IDataRepository
|
import com.rtbishop.look4sat.domain.IDataRepository
|
||||||
import com.rtbishop.look4sat.domain.ILocationSource
|
import com.rtbishop.look4sat.domain.ISatelliteRepository
|
||||||
import com.rtbishop.look4sat.domain.ISatelliteManager
|
import com.rtbishop.look4sat.domain.ISettingsRepository
|
||||||
import com.rtbishop.look4sat.domain.ISettingsSource
|
|
||||||
import com.rtbishop.look4sat.model.DataState
|
import com.rtbishop.look4sat.model.DataState
|
||||||
import com.rtbishop.look4sat.model.GeoPos
|
|
||||||
import com.rtbishop.look4sat.model.SatPass
|
import com.rtbishop.look4sat.model.SatPass
|
||||||
import com.rtbishop.look4sat.utility.toTimerString
|
import com.rtbishop.look4sat.utility.toTimerString
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
@@ -35,10 +33,9 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class PassesViewModel @Inject constructor(
|
class PassesViewModel @Inject constructor(
|
||||||
private val satelliteManager: ISatelliteManager,
|
private val dataRepository: IDataRepository,
|
||||||
private val repository: IDataRepository,
|
private val satelliteRepository: ISatelliteRepository,
|
||||||
private val settings: ISettingsSource,
|
private val settingsRepository: ISettingsRepository
|
||||||
private val locationSource: ILocationSource
|
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _passes = MutableStateFlow<DataState<List<SatPass>>>(DataState.Loading)
|
private val _passes = MutableStateFlow<DataState<List<SatPass>>>(DataState.Loading)
|
||||||
@@ -48,18 +45,18 @@ class PassesViewModel @Inject constructor(
|
|||||||
val passes: StateFlow<DataState<List<SatPass>>> = _passes
|
val passes: StateFlow<DataState<List<SatPass>>> = _passes
|
||||||
val timerText: StateFlow<Triple<String, String, String>?> = _timerText
|
val timerText: StateFlow<Triple<String, String, String>?> = _timerText
|
||||||
|
|
||||||
fun getHoursAhead() = settings.getHoursAhead()
|
fun getHoursAhead() = settingsRepository.getHoursAhead()
|
||||||
|
|
||||||
fun getMinElevation() = settings.getMinElevation()
|
fun getMinElevation() = settingsRepository.getMinElevation()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
satelliteManager.calculatedPasses.collect { passes ->
|
satelliteRepository.calculatedPasses.collect { passes ->
|
||||||
passesProcessing?.cancelAndJoin()
|
passesProcessing?.cancelAndJoin()
|
||||||
passesProcessing = viewModelScope.launch {
|
passesProcessing = viewModelScope.launch {
|
||||||
while (isActive) {
|
while (isActive) {
|
||||||
val timeNow = System.currentTimeMillis()
|
val timeNow = System.currentTimeMillis()
|
||||||
val newPasses = satelliteManager.processPasses(passes, timeNow)
|
val newPasses = satelliteRepository.processPasses(passes, timeNow)
|
||||||
|
|
||||||
if (newPasses.isNotEmpty()) {
|
if (newPasses.isNotEmpty()) {
|
||||||
try {
|
try {
|
||||||
@@ -86,18 +83,18 @@ class PassesViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
repository.updateState.collect { updateState ->
|
dataRepository.updateState.collect { updateState ->
|
||||||
if (updateState is DataState.Success) calculatePasses()
|
if (updateState is DataState.Success) calculatePasses()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
calculatePasses()
|
calculatePasses()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun shouldUseUTC() = settings.isUtcEnabled()
|
fun shouldUseUTC() = settingsRepository.isUtcEnabled()
|
||||||
|
|
||||||
fun calculatePasses(
|
fun calculatePasses(
|
||||||
hoursAhead: Int = 1,
|
hoursAhead: Int = 1,
|
||||||
minElevation: Double = settings.getMinElevation(),
|
minElevation: Double = settingsRepository.getMinElevation(),
|
||||||
timeRef: Long = System.currentTimeMillis(),
|
timeRef: Long = System.currentTimeMillis(),
|
||||||
selection: List<Int>? = null
|
selection: List<Int>? = null
|
||||||
) {
|
) {
|
||||||
@@ -105,15 +102,14 @@ class PassesViewModel @Inject constructor(
|
|||||||
_passes.emit(DataState.Loading)
|
_passes.emit(DataState.Loading)
|
||||||
// _timerText.postValue(timerDefaultText)
|
// _timerText.postValue(timerDefaultText)
|
||||||
passesProcessing?.cancelAndJoin()
|
passesProcessing?.cancelAndJoin()
|
||||||
selection?.let { items -> settings.saveEntriesSelection(items) }
|
selection?.let { items -> settingsRepository.saveEntriesSelection(items) }
|
||||||
settings.setHoursAhead(hoursAhead)
|
settingsRepository.setHoursAhead(hoursAhead)
|
||||||
settings.setMinElevation(minElevation)
|
settingsRepository.setMinElevation(minElevation)
|
||||||
val stationPos = locationSource.stationPosition.value
|
val stationPos = settingsRepository.stationPosition.value
|
||||||
val geoPos = GeoPos(stationPos.latitude, stationPos.longitude)
|
val selectedIds = settingsRepository.loadEntriesSelection()
|
||||||
val selectedIds = settings.loadEntriesSelection()
|
val satellites = dataRepository.getEntriesWithIds(selectedIds)
|
||||||
val satellites = repository.getEntriesWithIds(selectedIds)
|
satelliteRepository.calculatePasses(
|
||||||
satelliteManager.calculatePasses(
|
satellites, stationPos, timeRef, hoursAhead, minElevation
|
||||||
satellites, geoPos, timeRef, hoursAhead, minElevation
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import androidx.lifecycle.viewModelScope
|
|||||||
import com.rtbishop.look4sat.domain.*
|
import com.rtbishop.look4sat.domain.*
|
||||||
import com.rtbishop.look4sat.framework.BluetoothReporter
|
import com.rtbishop.look4sat.framework.BluetoothReporter
|
||||||
import com.rtbishop.look4sat.framework.NetworkReporter
|
import com.rtbishop.look4sat.framework.NetworkReporter
|
||||||
import com.rtbishop.look4sat.framework.OrientationSource
|
|
||||||
import com.rtbishop.look4sat.model.GeoPos
|
import com.rtbishop.look4sat.model.GeoPos
|
||||||
import com.rtbishop.look4sat.model.SatPass
|
import com.rtbishop.look4sat.model.SatPass
|
||||||
import com.rtbishop.look4sat.model.SatPos
|
import com.rtbishop.look4sat.model.SatPos
|
||||||
@@ -42,27 +41,26 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class RadarViewModel @Inject constructor(
|
class RadarViewModel @Inject constructor(
|
||||||
private val savedStateHandle: SavedStateHandle,
|
|
||||||
private val orientationSource: OrientationSource,
|
|
||||||
private val reporter: NetworkReporter,
|
|
||||||
private val bluetoothReporter: BluetoothReporter,
|
private val bluetoothReporter: BluetoothReporter,
|
||||||
private val satManager: ISatelliteManager,
|
private val networkReporter: NetworkReporter,
|
||||||
private val repository: IDataRepository,
|
private val savedStateHandle: SavedStateHandle,
|
||||||
private val settings: ISettingsSource,
|
private val dataRepository: IDataRepository,
|
||||||
private val locationSource: ILocationSource
|
private val satelliteRepository: ISatelliteRepository,
|
||||||
|
private val sensorRepository: ISensorRepository,
|
||||||
|
private val settingsRepository: ISettingsRepository
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val stationPos = locationSource.stationPosition.value
|
private val stationPos = settingsRepository.stationPosition.value
|
||||||
private val magDeclination = getMagDeclination(stationPos)
|
private val magDeclination = getMagDeclination(stationPos)
|
||||||
val transmitters = mutableStateOf<List<SatRadio>>(emptyList())
|
val transmitters = mutableStateOf<List<SatRadio>>(emptyList())
|
||||||
val radarData = mutableStateOf<RadarData?>(null)
|
val radarData = mutableStateOf<RadarData?>(null)
|
||||||
val orientation = mutableStateOf(orientationSource.orientation.value)
|
val orientation = mutableStateOf(sensorRepository.orientation.value)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (settings.isSensorEnabled()) {
|
if (settingsRepository.isSensorEnabled()) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
orientationSource.startListening()
|
sensorRepository.enableSensor()
|
||||||
orientationSource.orientation.collect { data ->
|
sensorRepository.orientation.collect { data ->
|
||||||
orientation.value = Pair(data.first + magDeclination, data.second)
|
orientation.value = Pair(data.first + magDeclination, data.second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,23 +68,24 @@ class RadarViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
orientationSource.stopListening()
|
sensorRepository.disableSensor()
|
||||||
super.onCleared()
|
super.onCleared()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPass() = flow {
|
fun getPass() = flow {
|
||||||
val catNum = savedStateHandle.get<Int>("catNum") ?: 0
|
val catNum = savedStateHandle.get<Int>("catNum") ?: 0
|
||||||
val aosTime = savedStateHandle.get<Long>("aosTime") ?: 0L
|
val aosTime = savedStateHandle.get<Long>("aosTime") ?: 0L
|
||||||
satManager.calculatedPasses.collect { passes ->
|
satelliteRepository.calculatedPasses.collect { passes ->
|
||||||
val pass = passes.find { pass -> pass.catNum == catNum && pass.aosTime == aosTime }
|
val pass = passes.find { pass -> pass.catNum == catNum && pass.aosTime == aosTime }
|
||||||
val currentPass = pass ?: passes.firstOrNull()
|
val currentPass = pass ?: passes.firstOrNull()
|
||||||
currentPass?.let { satPass ->
|
currentPass?.let { satPass ->
|
||||||
emit(satPass)
|
emit(satPass)
|
||||||
val transmitters = repository.getRadiosWithId(satPass.catNum)
|
val transmitters = dataRepository.getRadiosWithId(satPass.catNum)
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
while (isActive) {
|
while (isActive) {
|
||||||
val timeNow = System.currentTimeMillis()
|
val timeNow = System.currentTimeMillis()
|
||||||
val satPos = satManager.getPosition(satPass.satellite, stationPos, timeNow)
|
val satPos =
|
||||||
|
satelliteRepository.getPosition(satPass.satellite, stationPos, timeNow)
|
||||||
sendPassData(satPass, satPos, satPass.satellite)
|
sendPassData(satPass, satPos, satPass.satellite)
|
||||||
sendPassDataBT(satPos)
|
sendPassDataBT(satPos)
|
||||||
processRadios(transmitters, satPass.satellite, timeNow)
|
processRadios(transmitters, satPass.satellite, timeNow)
|
||||||
@@ -97,9 +96,9 @@ class RadarViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getUseCompass(): Boolean = settings.isSensorEnabled()
|
fun getUseCompass(): Boolean = settingsRepository.isSensorEnabled()
|
||||||
|
|
||||||
fun getShowSweep(): Boolean = settings.isSweepEnabled()
|
fun getShowSweep(): Boolean = settingsRepository.isSweepEnabled()
|
||||||
|
|
||||||
private fun getMagDeclination(geoPos: GeoPos, time: Long = System.currentTimeMillis()): Float {
|
private fun getMagDeclination(geoPos: GeoPos, time: Long = System.currentTimeMillis()): Float {
|
||||||
val latitude = geoPos.latitude.toFloat()
|
val latitude = geoPos.latitude.toFloat()
|
||||||
@@ -111,14 +110,16 @@ class RadarViewModel @Inject constructor(
|
|||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
var track: List<SatPos> = emptyList()
|
var track: List<SatPos> = emptyList()
|
||||||
if (!satPass.isDeepSpace) {
|
if (!satPass.isDeepSpace) {
|
||||||
track = satManager.getTrack(satellite, stationPos, satPass.aosTime, satPass.losTime)
|
track = satelliteRepository.getTrack(
|
||||||
|
satellite, stationPos, satPass.aosTime, satPass.losTime
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if (settings.getRotatorEnabled()) {
|
if (settingsRepository.getRotatorEnabled()) {
|
||||||
val server = settings.getRotatorServer()
|
val server = settingsRepository.getRotatorServer()
|
||||||
val port = settings.getRotatorPort().toInt()
|
val port = settingsRepository.getRotatorPort().toInt()
|
||||||
val azimuth = satPos.azimuth.toDegrees().round(1)
|
val azimuth = satPos.azimuth.toDegrees().round(1)
|
||||||
val elevation = satPos.elevation.toDegrees().round(1)
|
val elevation = satPos.elevation.toDegrees().round(1)
|
||||||
reporter.reportRotation(server, port, azimuth, elevation)
|
networkReporter.reportRotation(server, port, azimuth, elevation)
|
||||||
}
|
}
|
||||||
radarData.value = RadarData(satPos, track)
|
radarData.value = RadarData(satPos, track)
|
||||||
}
|
}
|
||||||
@@ -126,10 +127,10 @@ class RadarViewModel @Inject constructor(
|
|||||||
|
|
||||||
private fun sendPassDataBT(satPos: SatPos) {
|
private fun sendPassDataBT(satPos: SatPos) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
if (settings.getBTEnabled()) {
|
if (settingsRepository.getBTEnabled()) {
|
||||||
val btDevice = settings.getBTDeviceAddr()
|
val btDevice = settingsRepository.getBTDeviceAddr()
|
||||||
if (bluetoothReporter.isConnected()) {
|
if (bluetoothReporter.isConnected()) {
|
||||||
val format = settings.getBTFormat()
|
val format = settingsRepository.getBTFormat()
|
||||||
val azimuth = satPos.azimuth.toDegrees().round(0).toInt()
|
val azimuth = satPos.azimuth.toDegrees().round(0).toInt()
|
||||||
val elevation = satPos.elevation.toDegrees().round(0).toInt()
|
val elevation = satPos.elevation.toDegrees().round(0).toInt()
|
||||||
bluetoothReporter.reportRotation(format, azimuth, elevation)
|
bluetoothReporter.reportRotation(format, azimuth, elevation)
|
||||||
@@ -144,7 +145,7 @@ class RadarViewModel @Inject constructor(
|
|||||||
private fun processRadios(radios: List<SatRadio>, satellite: Satellite, time: Long) {
|
private fun processRadios(radios: List<SatRadio>, satellite: Satellite, time: Long) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
delay(125)
|
delay(125)
|
||||||
val list = satManager.processRadios(satellite, stationPos, radios, time)
|
val list = satelliteRepository.processRadios(satellite, stationPos, radios, time)
|
||||||
transmitters.value = list
|
transmitters.value = list
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.*
|
|||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -165,9 +166,8 @@ private fun CardAbout(version: String, modifier: Modifier = Modifier) {
|
|||||||
@Composable
|
@Composable
|
||||||
private fun LocationCardPreview() = MainTheme {
|
private fun LocationCardPreview() = MainTheme {
|
||||||
val stationPos = GeoPos(0.0, 0.0, 0.0, "IO91vl", 0L)
|
val stationPos = GeoPos(0.0, 0.0, 0.0, "IO91vl", 0L)
|
||||||
LocationCard(
|
val settings = LocationSettings(true, stationPos, 0, {}, { _, _ -> }, {}, {})
|
||||||
settings = LocationSettings(true, stationPos, {}, { _, _ -> }, { }),
|
LocationCard(settings = settings, setGpsLoc = {}, togglePosDialog = {}) {}
|
||||||
setGpsLoc = {}, togglePosDialog = {}) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -231,14 +231,21 @@ private fun LocationCard(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (settings.messageResId != 0) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val errorString = stringResource(id = settings.messageResId)
|
||||||
|
LaunchedEffect(key1 = settings.messageResId) {
|
||||||
|
showToast(context, errorString)
|
||||||
|
settings.dismissMessage()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
@Composable
|
@Composable
|
||||||
private fun DataCardPreview() = MainTheme {
|
private fun DataCardPreview() = MainTheme {
|
||||||
DataCard(settings = DataSettings(
|
val settings = DataSettings(true, 0L, 5000, 2500, {}, {}, {})
|
||||||
true, 0L, 5000, 2500, {}, { }, {})
|
DataCard(settings = settings) {}
|
||||||
) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -335,10 +342,10 @@ private fun DataCard(settings: DataSettings, updateFromFile: () -> Unit) {
|
|||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
@Composable
|
@Composable
|
||||||
private fun OtherCardPreview() = MainTheme {
|
private fun OtherCardPreview() = MainTheme {
|
||||||
OtherCard(settings = OtherSettings(
|
val settings = OtherSettings(
|
||||||
getUtc = false, getUpdate = true, getSweep = true, getSensor = true,
|
getUtc = false, getUpdate = true, getSweep = true, getSensor = true,
|
||||||
setUtc = {}, setUpdate = {}, setSweep = {}, setSensor = {})
|
setUtc = {}, setUpdate = {}, setSweep = {}, setSensor = {})
|
||||||
)
|
OtherCard(settings = settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
+3
-1
@@ -5,9 +5,11 @@ import com.rtbishop.look4sat.model.GeoPos
|
|||||||
data class LocationSettings(
|
data class LocationSettings(
|
||||||
val isUpdating: Boolean,
|
val isUpdating: Boolean,
|
||||||
val stationPos: GeoPos,
|
val stationPos: GeoPos,
|
||||||
|
val messageResId: Int,
|
||||||
val setGpsLoc: () -> Unit,
|
val setGpsLoc: () -> Unit,
|
||||||
val setManualLoc: (Double, Double) -> Unit,
|
val setManualLoc: (Double, Double) -> Unit,
|
||||||
val setQthLoc: (String) -> Unit
|
val setQthLoc: (String) -> Unit,
|
||||||
|
val dismissMessage: () -> Unit
|
||||||
)
|
)
|
||||||
|
|
||||||
data class DataSettings(
|
data class DataSettings(
|
||||||
|
|||||||
+61
-28
@@ -20,9 +20,9 @@ package com.rtbishop.look4sat.presentation.settings
|
|||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.rtbishop.look4sat.R
|
||||||
import com.rtbishop.look4sat.domain.IDataRepository
|
import com.rtbishop.look4sat.domain.IDataRepository
|
||||||
import com.rtbishop.look4sat.domain.ILocationSource
|
import com.rtbishop.look4sat.domain.ISettingsRepository
|
||||||
import com.rtbishop.look4sat.domain.ISettingsSource
|
|
||||||
import com.rtbishop.look4sat.model.DataState
|
import com.rtbishop.look4sat.model.DataState
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
@@ -31,31 +31,32 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class SettingsViewModel @Inject constructor(
|
class SettingsViewModel @Inject constructor(
|
||||||
private val locationSource: ILocationSource,
|
private val dataRepository: IDataRepository,
|
||||||
private val repository: IDataRepository,
|
private val settingsRepository: ISettingsRepository
|
||||||
private val settings: ISettingsSource
|
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val defaultLocationSettings = LocationSettings(false,
|
private val defaultLocationSettings = LocationSettings(false,
|
||||||
locationSource.stationPosition.value,
|
settingsRepository.stationPosition.value,
|
||||||
{ updateStationPosition() },
|
0,
|
||||||
{ lat, lon -> locationSource.setGeoPosition(lat, lon) },
|
{ setGpsPosition() },
|
||||||
{ locationSource.setQthPosition(it) })
|
{ latitude, longitude -> setGeoPosition(latitude, longitude) },
|
||||||
|
{ locator -> setQthPosition(locator) },
|
||||||
|
{ dismissLocationMessage() })
|
||||||
val locationSettings = mutableStateOf(defaultLocationSettings)
|
val locationSettings = mutableStateOf(defaultLocationSettings)
|
||||||
|
|
||||||
private val defaultDataSettings = DataSettings(false,
|
private val defaultDataSettings = DataSettings(false,
|
||||||
settings.getLastUpdateTime(),
|
settingsRepository.getLastUpdateTime(),
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
{ repository.updateFromWeb() },
|
{ dataRepository.updateFromWeb() },
|
||||||
{ repository.updateFromFile(it) },
|
{ dataRepository.updateFromFile(it) },
|
||||||
{ repository.clearAllData() })
|
{ dataRepository.clearAllData() })
|
||||||
val dataSettings = mutableStateOf(defaultDataSettings)
|
val dataSettings = mutableStateOf(defaultDataSettings)
|
||||||
|
|
||||||
private val defaultOtherSettings = OtherSettings(settings.isUtcEnabled(),
|
private val defaultOtherSettings = OtherSettings(settingsRepository.isUtcEnabled(),
|
||||||
settings.isUpdateEnabled(),
|
settingsRepository.isUpdateEnabled(),
|
||||||
settings.isSweepEnabled(),
|
settingsRepository.isSweepEnabled(),
|
||||||
settings.isSensorEnabled(),
|
settingsRepository.isSensorEnabled(),
|
||||||
{ setUtc(it) },
|
{ setUtc(it) },
|
||||||
{ setUpdate(it) },
|
{ setUpdate(it) },
|
||||||
{ setSweep(it) },
|
{ setSweep(it) },
|
||||||
@@ -64,49 +65,81 @@ class SettingsViewModel @Inject constructor(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
locationSource.stationPosition.collect { stationPos ->
|
settingsRepository.stationPosition.collect { stationPos ->
|
||||||
locationSettings.value = locationSettings.value.copy(
|
locationSettings.value = locationSettings.value.copy(
|
||||||
isUpdating = false, stationPos = stationPos
|
isUpdating = false, stationPos = stationPos
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
repository.updateState.collect {
|
dataRepository.updateState.collect {
|
||||||
val isUpdating = it is DataState.Loading
|
val isUpdating = it is DataState.Loading
|
||||||
dataSettings.value = dataSettings.value.copy(
|
dataSettings.value = dataSettings.value.copy(
|
||||||
isUpdating = isUpdating, lastUpdated = settings.getLastUpdateTime()
|
isUpdating = isUpdating, lastUpdated = settingsRepository.getLastUpdateTime()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
combine(repository.getEntriesTotal(), repository.getRadiosTotal()) { sats, radios ->
|
combine(
|
||||||
|
dataRepository.getEntriesTotal(), dataRepository.getRadiosTotal()
|
||||||
|
) { sats, radios ->
|
||||||
dataSettings.value = dataSettings.value.copy(satsTotal = sats, radiosTotal = radios)
|
dataSettings.value = dataSettings.value.copy(satsTotal = sats, radiosTotal = radios)
|
||||||
}.collect {}
|
}.collect {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateStationPosition() {
|
private fun setGpsPosition() {
|
||||||
val isSuccessful = locationSource.setGpsPosition()
|
if (settingsRepository.setGpsPosition()) {
|
||||||
locationSettings.value = locationSettings.value.copy(isUpdating = isSuccessful)
|
val messageResId = R.string.location_success
|
||||||
|
locationSettings.value =
|
||||||
|
locationSettings.value.copy(isUpdating = true, messageResId = messageResId)
|
||||||
|
} else {
|
||||||
|
val errorResId = R.string.location_gps_error
|
||||||
|
locationSettings.value = locationSettings.value.copy(messageResId = errorResId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setGeoPosition(latitude: Double, longitude: Double) {
|
||||||
|
if (settingsRepository.setGeoPosition(latitude, longitude)) {
|
||||||
|
val messageResId = R.string.location_success
|
||||||
|
locationSettings.value = locationSettings.value.copy(messageResId = messageResId)
|
||||||
|
} else {
|
||||||
|
val errorResId = R.string.location_manual_error
|
||||||
|
locationSettings.value = locationSettings.value.copy(messageResId = errorResId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setQthPosition(locator: String) {
|
||||||
|
if (settingsRepository.setQthPosition(locator)) {
|
||||||
|
val messageResId = R.string.location_success
|
||||||
|
locationSettings.value = locationSettings.value.copy(messageResId = messageResId)
|
||||||
|
} else {
|
||||||
|
val errorResId = R.string.location_qth_error
|
||||||
|
locationSettings.value = locationSettings.value.copy(messageResId = errorResId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun dismissLocationMessage() {
|
||||||
|
locationSettings.value = locationSettings.value.copy(messageResId = 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setUtc(value: Boolean) {
|
private fun setUtc(value: Boolean) {
|
||||||
settings.setUtcState(value)
|
settingsRepository.setUtcState(value)
|
||||||
otherSettings.value = otherSettings.value.copy(getUtc = value)
|
otherSettings.value = otherSettings.value.copy(getUtc = value)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setUpdate(value: Boolean) {
|
private fun setUpdate(value: Boolean) {
|
||||||
settings.setUpdateState(value)
|
settingsRepository.setUpdateState(value)
|
||||||
otherSettings.value = otherSettings.value.copy(getUpdate = value)
|
otherSettings.value = otherSettings.value.copy(getUpdate = value)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setSweep(value: Boolean) {
|
private fun setSweep(value: Boolean) {
|
||||||
settings.setSweepState(value)
|
settingsRepository.setSweepState(value)
|
||||||
otherSettings.value = otherSettings.value.copy(getSweep = value)
|
otherSettings.value = otherSettings.value.copy(getSweep = value)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setSensor(value: Boolean) {
|
private fun setSensor(value: Boolean) {
|
||||||
settings.setSensorState(value)
|
settingsRepository.setSensorState(value)
|
||||||
otherSettings.value = otherSettings.value.copy(getSensor = value)
|
otherSettings.value = otherSettings.value.copy(getSensor = value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,56 +26,52 @@ import org.json.JSONObject
|
|||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
class DataParser(private val defaultDispatcher: CoroutineDispatcher) {
|
class DataParser(private val dispatcher: CoroutineDispatcher) {
|
||||||
|
|
||||||
suspend fun parseCSVStream(csvStream: InputStream): List<OrbitalData> =
|
suspend fun parseCSVStream(stream: InputStream): List<OrbitalData> = withContext(dispatcher) {
|
||||||
withContext(defaultDispatcher) {
|
val parsedItems = mutableListOf<OrbitalData>()
|
||||||
val parsedItems = mutableListOf<OrbitalData>()
|
stream.bufferedReader().useLines { lines ->
|
||||||
csvStream.bufferedReader().useLines { lines ->
|
lines.forEachIndexed { index, line ->
|
||||||
lines.forEachIndexed { index, line ->
|
if (index != 0) {
|
||||||
if (index != 0) {
|
val values = line.split(",")
|
||||||
val values = line.split(",")
|
parseCSV(values)?.let { tle -> parsedItems.add(tle) }
|
||||||
parseCSV(values)?.let { tle -> parsedItems.add(tle) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return@withContext parsedItems
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun parseTLEStream(stream: InputStream): List<OrbitalData> = withContext(dispatcher) {
|
||||||
|
val tleStrings = mutableListOf(String(), String(), String())
|
||||||
|
val parsedItems = mutableListOf<OrbitalData>()
|
||||||
|
var lineIndex = 0
|
||||||
|
stream.bufferedReader().forEachLine { line ->
|
||||||
|
tleStrings[lineIndex] = line
|
||||||
|
if (lineIndex < 2) {
|
||||||
|
lineIndex++
|
||||||
|
} else {
|
||||||
|
val isLineOneValid = tleStrings[1].substring(0, 1) == "1"
|
||||||
|
val isLineTwoValid = tleStrings[2].substring(0, 1) == "2"
|
||||||
|
if (!isLineOneValid && !isLineTwoValid) return@forEachLine
|
||||||
|
parseTLE(tleStrings)?.let { tle -> parsedItems.add(tle) }
|
||||||
|
lineIndex = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return@withContext parsedItems
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun parseJSONStream(stream: InputStream): List<SatRadio> = withContext(dispatcher) {
|
||||||
|
val parsedItems = mutableListOf<SatRadio>()
|
||||||
|
try {
|
||||||
|
val jsonArray = JSONArray(stream.bufferedReader().readText())
|
||||||
|
for (index in 0 until jsonArray.length()) {
|
||||||
|
val jsonObject = jsonArray.getJSONObject(index)
|
||||||
|
parseJSON(jsonObject)?.let { parsedItems.add(it) }
|
||||||
|
}
|
||||||
return@withContext parsedItems
|
return@withContext parsedItems
|
||||||
}
|
} catch (exception: Exception) {
|
||||||
|
|
||||||
suspend fun parseTLEStream(tleStream: InputStream): List<OrbitalData> =
|
|
||||||
withContext(defaultDispatcher) {
|
|
||||||
val tleStrings = mutableListOf(String(), String(), String())
|
|
||||||
val parsedItems = mutableListOf<OrbitalData>()
|
|
||||||
var lineIndex = 0
|
|
||||||
tleStream.bufferedReader().forEachLine { line ->
|
|
||||||
tleStrings[lineIndex] = line
|
|
||||||
if (lineIndex < 2) {
|
|
||||||
lineIndex++
|
|
||||||
} else {
|
|
||||||
val isLineOneValid = tleStrings[1].substring(0, 1) == "1"
|
|
||||||
val isLineTwoValid = tleStrings[2].substring(0, 1) == "2"
|
|
||||||
if (!isLineOneValid && !isLineTwoValid) return@forEachLine
|
|
||||||
parseTLE(tleStrings)?.let { tle -> parsedItems.add(tle) }
|
|
||||||
lineIndex = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return@withContext parsedItems
|
return@withContext parsedItems
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun parseJSONStream(jsonStream: InputStream): List<SatRadio> {
|
|
||||||
return withContext(defaultDispatcher) {
|
|
||||||
val parsedItems = mutableListOf<SatRadio>()
|
|
||||||
try {
|
|
||||||
val jsonArray = JSONArray(jsonStream.bufferedReader().readText())
|
|
||||||
for (index in 0 until jsonArray.length()) {
|
|
||||||
val jsonObject = jsonArray.getJSONObject(index)
|
|
||||||
parseJSON(jsonObject)?.let { parsedItems.add(it) }
|
|
||||||
}
|
|
||||||
return@withContext parsedItems
|
|
||||||
} catch (exception: Exception) {
|
|
||||||
return@withContext parsedItems
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseCSV(values: List<String>): OrbitalData? {
|
private fun parseCSV(values: List<String>): OrbitalData? {
|
||||||
@@ -120,8 +116,9 @@ class DataParser(private val defaultDispatcher: CoroutineDispatcher) {
|
|||||||
val argper: Double = tle[2].substring(34, 42).toDouble()
|
val argper: Double = tle[2].substring(34, 42).toDouble()
|
||||||
val meanan: Double = tle[2].substring(43, 51).toDouble()
|
val meanan: Double = tle[2].substring(43, 51).toDouble()
|
||||||
val catnum: Int = tle[1].substring(2, 7).trim().toInt()
|
val catnum: Int = tle[1].substring(2, 7).trim().toInt()
|
||||||
val bstar: Double = 1.0e-5 * tle[1].substring(53, 59).toDouble() /
|
val bstar: Double = 1.0e-5 * tle[1].substring(53, 59).toDouble() / 10.0.pow(
|
||||||
10.0.pow(tle[1].substring(60, 61).toDouble())
|
tle[1].substring(60, 61).toDouble()
|
||||||
|
)
|
||||||
return OrbitalData(name, epoch, meanmo, eccn, incl, raan, argper, meanan, catnum, bstar)
|
return OrbitalData(name, epoch, meanmo, eccn, incl, raan, argper, meanan, catnum, bstar)
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
return null
|
return null
|
||||||
@@ -154,7 +151,7 @@ class DataParser(private val defaultDispatcher: CoroutineDispatcher) {
|
|||||||
var dayOfYear = dayOfMonth
|
var dayOfYear = dayOfMonth
|
||||||
// If leap year increment Feb days
|
// If leap year increment Feb days
|
||||||
if (((year / 4 == 0) && (year / 100 != 0)) || (year / 400 == 0)) daysArray[1]++
|
if (((year / 4 == 0) && (year / 100 != 0)) || (year / 400 == 0)) daysArray[1]++
|
||||||
for (i in 0 until month - 1) { dayOfYear += daysArray[i] }
|
for (i in 0 until month - 1) dayOfYear += daysArray[i]
|
||||||
return dayOfYear
|
return dayOfYear
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -18,9 +18,10 @@
|
|||||||
package com.rtbishop.look4sat.data
|
package com.rtbishop.look4sat.data
|
||||||
|
|
||||||
import com.rtbishop.look4sat.domain.IDataRepository
|
import com.rtbishop.look4sat.domain.IDataRepository
|
||||||
import com.rtbishop.look4sat.domain.ISettingsSource
|
import com.rtbishop.look4sat.domain.ISettingsRepository
|
||||||
import com.rtbishop.look4sat.model.DataState
|
import com.rtbishop.look4sat.model.DataState
|
||||||
import com.rtbishop.look4sat.model.SatEntry
|
import com.rtbishop.look4sat.model.SatEntry
|
||||||
|
import com.rtbishop.look4sat.model.SatItem
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
@@ -34,7 +35,7 @@ class DataRepository(
|
|||||||
private val radioSource: IRadiosStorage,
|
private val radioSource: IRadiosStorage,
|
||||||
private val remoteSource: INetworkSource,
|
private val remoteSource: INetworkSource,
|
||||||
private val repositoryScope: CoroutineScope,
|
private val repositoryScope: CoroutineScope,
|
||||||
private val settingsManager: ISettingsSource
|
private val settingsRepository: ISettingsRepository
|
||||||
) : IDataRepository {
|
) : IDataRepository {
|
||||||
|
|
||||||
private val exceptionHandler = CoroutineExceptionHandler { _, exception ->
|
private val exceptionHandler = CoroutineExceptionHandler { _, exception ->
|
||||||
@@ -48,10 +49,17 @@ class DataRepository(
|
|||||||
|
|
||||||
override fun getRadiosTotal() = radioSource.getRadiosTotal()
|
override fun getRadiosTotal() = radioSource.getRadiosTotal()
|
||||||
|
|
||||||
|
override fun getSatelliteTypes() = remoteSource.satelliteSourcesMap.keys.sorted()
|
||||||
|
|
||||||
override suspend fun getEntriesWithModes() = entrySource.getEntriesWithModes()
|
override suspend fun getEntriesWithModes() = entrySource.getEntriesWithModes()
|
||||||
|
|
||||||
override suspend fun getEntriesWithIds(ids: List<Int>) = entrySource.getEntriesWithIds(ids)
|
override suspend fun getEntriesWithIds(ids: List<Int>) = entrySource.getEntriesWithIds(ids)
|
||||||
|
|
||||||
|
override suspend fun getEntriesWithSelection(): List<SatItem> {
|
||||||
|
val selectedIds = settingsRepository.loadEntriesSelection()
|
||||||
|
return getEntriesWithModes().onEach { it.isSelected = it.catnum in selectedIds }
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun getRadiosWithId(id: Int) = radioSource.getRadiosWithId(id)
|
override suspend fun getRadiosWithId(id: Int) = radioSource.getRadiosWithId(id)
|
||||||
|
|
||||||
override fun updateFromFile(uri: String) {
|
override fun updateFromFile(uri: String) {
|
||||||
@@ -69,7 +77,7 @@ class DataRepository(
|
|||||||
_updateState.value = DataState.Loading
|
_updateState.value = DataState.Loading
|
||||||
repositoryScope.launch(exceptionHandler) {
|
repositoryScope.launch(exceptionHandler) {
|
||||||
val importedEntries = mutableListOf<SatEntry>()
|
val importedEntries = mutableListOf<SatEntry>()
|
||||||
val sourcesMap = settingsManager.sourcesMap
|
val sourcesMap = remoteSource.satelliteSourcesMap
|
||||||
val jobsMap = sourcesMap.mapValues { async { remoteSource.getDataStream(it.value) } }
|
val jobsMap = sourcesMap.mapValues { async { remoteSource.getDataStream(it.value) } }
|
||||||
jobsMap.mapValues { job -> job.value.await() }.forEach { entry ->
|
jobsMap.mapValues { job -> job.value.await() }.forEach { entry ->
|
||||||
entry.value?.let { stream ->
|
entry.value?.let { stream ->
|
||||||
@@ -78,7 +86,7 @@ class DataRepository(
|
|||||||
// parse tle stream
|
// parse tle stream
|
||||||
val satellites = importSatellites(stream)
|
val satellites = importSatellites(stream)
|
||||||
val catnums = satellites.map { it.data.catnum }
|
val catnums = satellites.map { it.data.catnum }
|
||||||
settingsManager.saveSatType(type, catnums)
|
settingsRepository.saveSatType(type, catnums)
|
||||||
importedEntries.addAll(satellites)
|
importedEntries.addAll(satellites)
|
||||||
}
|
}
|
||||||
"McCants", "Classified" -> {
|
"McCants", "Classified" -> {
|
||||||
@@ -86,7 +94,7 @@ class DataRepository(
|
|||||||
val unzipped = ZipInputStream(stream).apply { nextEntry }
|
val unzipped = ZipInputStream(stream).apply { nextEntry }
|
||||||
val satellites = importSatellites(unzipped)
|
val satellites = importSatellites(unzipped)
|
||||||
val catnums = satellites.map { it.data.catnum }
|
val catnums = satellites.map { it.data.catnum }
|
||||||
settingsManager.saveSatType(type, catnums)
|
settingsRepository.saveSatType(type, catnums)
|
||||||
importedEntries.addAll(satellites)
|
importedEntries.addAll(satellites)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
@@ -94,7 +102,7 @@ class DataRepository(
|
|||||||
val parsed = dataParser.parseCSVStream(stream)
|
val parsed = dataParser.parseCSVStream(stream)
|
||||||
val satellites = parsed.map { data -> SatEntry(data) }
|
val satellites = parsed.map { data -> SatEntry(data) }
|
||||||
val catnums = satellites.map { it.data.catnum }
|
val catnums = satellites.map { it.data.catnum }
|
||||||
settingsManager.saveSatType(type, catnums)
|
settingsRepository.saveSatType(type, catnums)
|
||||||
importedEntries.addAll(satellites)
|
importedEntries.addAll(satellites)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -104,7 +112,7 @@ class DataRepository(
|
|||||||
setUpdateSuccessful()
|
setUpdateSuccessful()
|
||||||
}
|
}
|
||||||
repositoryScope.launch(exceptionHandler) {
|
repositoryScope.launch(exceptionHandler) {
|
||||||
remoteSource.getDataStream(remoteSource.radioApi)?.let { stream ->
|
remoteSource.getDataStream(remoteSource.radioSourceUrl)?.let { stream ->
|
||||||
radioSource.insertRadios(dataParser.parseJSONStream(stream))
|
radioSource.insertRadios(dataParser.parseJSONStream(stream))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -125,7 +133,7 @@ class DataRepository(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setUpdateSuccessful(updateTime: Long = System.currentTimeMillis()) {
|
private fun setUpdateSuccessful(updateTime: Long = System.currentTimeMillis()) {
|
||||||
settingsManager.setLastUpdateTime(updateTime)
|
settingsRepository.setLastUpdateTime(updateTime)
|
||||||
_updateState.value = DataState.Success(updateTime)
|
_updateState.value = DataState.Success(updateTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,36 @@ import java.io.InputStream
|
|||||||
|
|
||||||
interface INetworkSource {
|
interface INetworkSource {
|
||||||
|
|
||||||
val radioApi: String get() = "https://db.satnogs.org/api/transmitters/?format=json&status=active"
|
val radioSourceUrl: String get() = "https://db.satnogs.org/api/transmitters/?format=json"
|
||||||
|
val satelliteSourcesMap: Map<String, String>
|
||||||
|
get() = mapOf(
|
||||||
|
"All" to "https://celestrak.com/NORAD/elements/gp.php?GROUP=active&FORMAT=csv",
|
||||||
|
"Amsat" to "https://amsat.org/tle/current/nasabare.txt",
|
||||||
|
"Amateur" to "https://celestrak.com/NORAD/elements/gp.php?GROUP=amateur&FORMAT=csv",
|
||||||
|
"Classified" to "https://www.prismnet.com/~mmccants/tles/classfd.zip",
|
||||||
|
"Cubesat" to "https://celestrak.com/NORAD/elements/gp.php?GROUP=cubesat&FORMAT=csv",
|
||||||
|
"Education" to "https://celestrak.com/NORAD/elements/gp.php?GROUP=education&FORMAT=csv",
|
||||||
|
"Engineer" to "https://celestrak.com/NORAD/elements/gp.php?GROUP=engineering&FORMAT=csv",
|
||||||
|
"Geostationary" to "https://celestrak.com/NORAD/elements/gp.php?GROUP=geo&FORMAT=csv",
|
||||||
|
"Globalstar" to "https://celestrak.com/NORAD/elements/gp.php?GROUP=globalstar&FORMAT=csv",
|
||||||
|
"GNSS" to "https://celestrak.com/NORAD/elements/gp.php?GROUP=gnss&FORMAT=csv",
|
||||||
|
"Intelsat" to "https://celestrak.com/NORAD/elements/gp.php?GROUP=intelsat&FORMAT=csv",
|
||||||
|
"Iridium" to "https://celestrak.com/NORAD/elements/gp.php?GROUP=iridium-NEXT&FORMAT=csv",
|
||||||
|
"McCants" to "https://www.prismnet.com/~mmccants/tles/inttles.zip",
|
||||||
|
"Military" to "https://celestrak.com/NORAD/elements/gp.php?GROUP=military&FORMAT=csv",
|
||||||
|
"New" to "https://celestrak.com/NORAD/elements/gp.php?GROUP=last-30-days&FORMAT=csv",
|
||||||
|
"OneWeb" to "https://celestrak.com/NORAD/elements/gp.php?GROUP=oneweb&FORMAT=csv",
|
||||||
|
"Orbcomm" to "https://celestrak.com/NORAD/elements/gp.php?GROUP=orbcomm&FORMAT=csv",
|
||||||
|
"R4UAB" to "https://r4uab.ru/satonline.txt",
|
||||||
|
"Resource" to "https://celestrak.com/NORAD/elements/gp.php?GROUP=resource&FORMAT=csv",
|
||||||
|
"SatNOGS" to "https://celestrak.com/NORAD/elements/gp.php?GROUP=satnogs&FORMAT=csv",
|
||||||
|
"Science" to "https://celestrak.com/NORAD/elements/gp.php?GROUP=science&FORMAT=csv",
|
||||||
|
"Spire" to "https://celestrak.com/NORAD/elements/gp.php?GROUP=spire&FORMAT=csv",
|
||||||
|
"Starlink" to "https://celestrak.com/NORAD/elements/gp.php?GROUP=starlink&FORMAT=csv",
|
||||||
|
"Swarm" to "https://celestrak.com/NORAD/elements/gp.php?GROUP=swarm&FORMAT=csv",
|
||||||
|
"Weather" to "https://celestrak.com/NORAD/elements/gp.php?GROUP=weather&FORMAT=csv",
|
||||||
|
"X-Comm" to "https://celestrak.com/NORAD/elements/gp.php?GROUP=x-comm&FORMAT=csv"
|
||||||
|
)
|
||||||
|
|
||||||
suspend fun getDataStream(url: String): InputStream?
|
suspend fun getDataStream(url: String): InputStream?
|
||||||
}
|
}
|
||||||
+9
-7
@@ -15,8 +15,10 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package com.rtbishop.look4sat.domain
|
package com.rtbishop.look4sat.data
|
||||||
|
|
||||||
|
import com.rtbishop.look4sat.domain.ISatelliteRepository
|
||||||
|
import com.rtbishop.look4sat.domain.Satellite
|
||||||
import com.rtbishop.look4sat.model.GeoPos
|
import com.rtbishop.look4sat.model.GeoPos
|
||||||
import com.rtbishop.look4sat.model.SatPass
|
import com.rtbishop.look4sat.model.SatPass
|
||||||
import com.rtbishop.look4sat.model.SatPos
|
import com.rtbishop.look4sat.model.SatPos
|
||||||
@@ -28,7 +30,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow
|
|||||||
import kotlinx.coroutines.flow.SharedFlow
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
class SatelliteManager(private val defaultDispatcher: CoroutineDispatcher) : ISatelliteManager {
|
class SatelliteRepository(private val dispatcher: CoroutineDispatcher) : ISatelliteRepository {
|
||||||
|
|
||||||
private var passes = listOf<SatPass>()
|
private var passes = listOf<SatPass>()
|
||||||
private val _calculatedPasses = MutableSharedFlow<List<SatPass>>(replay = 1)
|
private val _calculatedPasses = MutableSharedFlow<List<SatPass>>(replay = 1)
|
||||||
@@ -37,13 +39,13 @@ class SatelliteManager(private val defaultDispatcher: CoroutineDispatcher) : ISa
|
|||||||
override fun getPasses(): List<SatPass> = passes
|
override fun getPasses(): List<SatPass> = passes
|
||||||
|
|
||||||
override suspend fun getPosition(sat: Satellite, pos: GeoPos, time: Long): SatPos {
|
override suspend fun getPosition(sat: Satellite, pos: GeoPos, time: Long): SatPos {
|
||||||
return withContext(defaultDispatcher) { sat.getPosition(pos, time) }
|
return withContext(dispatcher) { sat.getPosition(pos, time) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getTrack(
|
override suspend fun getTrack(
|
||||||
sat: Satellite, pos: GeoPos, start: Long, end: Long
|
sat: Satellite, pos: GeoPos, start: Long, end: Long
|
||||||
): List<SatPos> {
|
): List<SatPos> {
|
||||||
return withContext(defaultDispatcher) {
|
return withContext(dispatcher) {
|
||||||
val positions = mutableListOf<SatPos>()
|
val positions = mutableListOf<SatPos>()
|
||||||
var currentTime = start
|
var currentTime = start
|
||||||
while (currentTime < end) {
|
while (currentTime < end) {
|
||||||
@@ -57,7 +59,7 @@ class SatelliteManager(private val defaultDispatcher: CoroutineDispatcher) : ISa
|
|||||||
override suspend fun processRadios(
|
override suspend fun processRadios(
|
||||||
sat: Satellite, pos: GeoPos, radios: List<SatRadio>, time: Long
|
sat: Satellite, pos: GeoPos, radios: List<SatRadio>, time: Long
|
||||||
): List<SatRadio> {
|
): List<SatRadio> {
|
||||||
return withContext(defaultDispatcher) {
|
return withContext(dispatcher) {
|
||||||
val satPos = sat.getPosition(pos, time)
|
val satPos = sat.getPosition(pos, time)
|
||||||
val copiedList = radios.map { it.copy() }
|
val copiedList = radios.map { it.copy() }
|
||||||
copiedList.forEach { transmitter ->
|
copiedList.forEach { transmitter ->
|
||||||
@@ -73,7 +75,7 @@ class SatelliteManager(private val defaultDispatcher: CoroutineDispatcher) : ISa
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun processPasses(passList: List<SatPass>, time: Long): List<SatPass> {
|
override suspend fun processPasses(passList: List<SatPass>, time: Long): List<SatPass> {
|
||||||
return withContext(defaultDispatcher) {
|
return withContext(dispatcher) {
|
||||||
passList.forEach { pass ->
|
passList.forEach { pass ->
|
||||||
if (!pass.isDeepSpace) {
|
if (!pass.isDeepSpace) {
|
||||||
val timeStart = pass.aosTime
|
val timeStart = pass.aosTime
|
||||||
@@ -96,7 +98,7 @@ class SatelliteManager(private val defaultDispatcher: CoroutineDispatcher) : ISa
|
|||||||
passes = newPasses
|
passes = newPasses
|
||||||
_calculatedPasses.emit(newPasses)
|
_calculatedPasses.emit(newPasses)
|
||||||
} else {
|
} else {
|
||||||
withContext(defaultDispatcher) {
|
withContext(dispatcher) {
|
||||||
val allPasses = mutableListOf<SatPass>()
|
val allPasses = mutableListOf<SatPass>()
|
||||||
satList.forEach { satellite ->
|
satList.forEach { satellite ->
|
||||||
allPasses.addAll(satellite.getPasses(pos, time, hoursAhead))
|
allPasses.addAll(satellite.getPasses(pos, time, hoursAhead))
|
||||||
+1
-1
@@ -20,7 +20,7 @@ package com.rtbishop.look4sat.domain
|
|||||||
import com.rtbishop.look4sat.model.OrbitalData
|
import com.rtbishop.look4sat.model.OrbitalData
|
||||||
import kotlin.math.*
|
import kotlin.math.*
|
||||||
|
|
||||||
class DeepSpaceSat(data: OrbitalData) : Satellite(data) {
|
class DeepSpaceSatellite(data: OrbitalData) : Satellite(data) {
|
||||||
|
|
||||||
private val c1: Double
|
private val c1: Double
|
||||||
private val c4: Double
|
private val c4: Double
|
||||||
@@ -31,10 +31,14 @@ interface IDataRepository {
|
|||||||
|
|
||||||
fun getRadiosTotal(): Flow<Int>
|
fun getRadiosTotal(): Flow<Int>
|
||||||
|
|
||||||
|
fun getSatelliteTypes(): List<String>
|
||||||
|
|
||||||
suspend fun getEntriesWithModes(): List<SatItem>
|
suspend fun getEntriesWithModes(): List<SatItem>
|
||||||
|
|
||||||
suspend fun getEntriesWithIds(ids: List<Int>): List<Satellite>
|
suspend fun getEntriesWithIds(ids: List<Int>): List<Satellite>
|
||||||
|
|
||||||
|
suspend fun getEntriesWithSelection(): List<SatItem>
|
||||||
|
|
||||||
suspend fun getRadiosWithId(id: Int): List<SatRadio>
|
suspend fun getRadiosWithId(id: Int): List<SatRadio>
|
||||||
|
|
||||||
fun updateFromFile(uri: String)
|
fun updateFromFile(uri: String)
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
* Look4Sat. Amateur radio satellite tracker and pass predictor.
|
|
||||||
* Copyright (C) 2019-2022 Arty Bishop (bishop.arty@gmail.com)
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package com.rtbishop.look4sat.domain
|
|
||||||
|
|
||||||
import com.rtbishop.look4sat.model.GeoPos
|
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
|
|
||||||
interface ILocationSource {
|
|
||||||
|
|
||||||
val stationPosition: StateFlow<GeoPos>
|
|
||||||
|
|
||||||
fun setGpsPosition(): Boolean
|
|
||||||
|
|
||||||
fun setGeoPosition(latitude: Double, longitude: Double, altitude: Double = 0.0): Boolean
|
|
||||||
|
|
||||||
fun setQthPosition(locator: String): Boolean
|
|
||||||
}
|
|
||||||
+1
-1
@@ -6,7 +6,7 @@ import com.rtbishop.look4sat.model.SatPos
|
|||||||
import com.rtbishop.look4sat.model.SatRadio
|
import com.rtbishop.look4sat.model.SatRadio
|
||||||
import kotlinx.coroutines.flow.SharedFlow
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
|
|
||||||
interface ISatelliteManager {
|
interface ISatelliteRepository {
|
||||||
|
|
||||||
val calculatedPasses: SharedFlow<List<SatPass>>
|
val calculatedPasses: SharedFlow<List<SatPass>>
|
||||||
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.rtbishop.look4sat.domain
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
|
interface ISensorRepository {
|
||||||
|
|
||||||
|
val orientation: StateFlow<Pair<Float, Float>>
|
||||||
|
|
||||||
|
fun enableSensor()
|
||||||
|
|
||||||
|
fun disableSensor()
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* Look4Sat. Amateur radio satellite tracker and pass predictor.
|
||||||
|
* Copyright (C) 2019-2022 Arty Bishop (bishop.arty@gmail.com)
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.rtbishop.look4sat.domain
|
||||||
|
|
||||||
|
import com.rtbishop.look4sat.model.GeoPos
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
|
interface ISettingsRepository {
|
||||||
|
|
||||||
|
val stationPosition: StateFlow<GeoPos>
|
||||||
|
|
||||||
|
fun setGpsPosition(): Boolean
|
||||||
|
|
||||||
|
fun setGeoPosition(latitude: Double, longitude: Double, altitude: Double = 0.0): Boolean
|
||||||
|
|
||||||
|
fun setQthPosition(locator: String): Boolean
|
||||||
|
|
||||||
|
fun getHoursAhead(): Int
|
||||||
|
|
||||||
|
fun setHoursAhead(hoursAhead: Int)
|
||||||
|
|
||||||
|
fun getMinElevation(): Double
|
||||||
|
|
||||||
|
fun setMinElevation(minElevation: Double)
|
||||||
|
|
||||||
|
fun isUtcEnabled(): Boolean
|
||||||
|
|
||||||
|
fun setUtcState(value: Boolean)
|
||||||
|
|
||||||
|
fun getLastUpdateTime(): Long
|
||||||
|
|
||||||
|
fun setLastUpdateTime(updateTime: Long)
|
||||||
|
|
||||||
|
fun isUpdateEnabled(): Boolean
|
||||||
|
|
||||||
|
fun setUpdateState(value: Boolean)
|
||||||
|
|
||||||
|
fun isSensorEnabled(): Boolean
|
||||||
|
|
||||||
|
fun setSensorState(value: Boolean)
|
||||||
|
|
||||||
|
fun isSweepEnabled(): Boolean
|
||||||
|
|
||||||
|
fun setSweepState(value: Boolean)
|
||||||
|
|
||||||
|
fun saveModesSelection(modes: List<String>)
|
||||||
|
|
||||||
|
fun loadModesSelection(): List<String>
|
||||||
|
|
||||||
|
fun saveEntriesSelection(catnums: List<Int>)
|
||||||
|
|
||||||
|
fun loadEntriesSelection(): List<Int>
|
||||||
|
|
||||||
|
fun saveSatType(type: String, catnums: List<Int>)
|
||||||
|
|
||||||
|
fun loadSatType(type: String): List<Int>
|
||||||
|
|
||||||
|
fun getRotatorEnabled(): Boolean
|
||||||
|
|
||||||
|
fun setRotatorEnabled(value: Boolean)
|
||||||
|
|
||||||
|
fun getRotatorServer(): String
|
||||||
|
|
||||||
|
fun setRotatorServer(value: String)
|
||||||
|
|
||||||
|
fun getRotatorPort(): String
|
||||||
|
|
||||||
|
fun setRotatorPort(value: String)
|
||||||
|
|
||||||
|
fun getBTEnabled(): Boolean
|
||||||
|
|
||||||
|
fun setBTEnabled(value: Boolean)
|
||||||
|
|
||||||
|
fun getBTDeviceAddr(): String
|
||||||
|
|
||||||
|
fun setBTDeviceAddr(value: String)
|
||||||
|
|
||||||
|
fun getBTDeviceName(): String
|
||||||
|
|
||||||
|
fun setBTDeviceName(value: String)
|
||||||
|
|
||||||
|
fun getBTFormat(): String
|
||||||
|
|
||||||
|
fun setBTFormat(value: String)
|
||||||
|
}
|
||||||
+1
-1
@@ -20,7 +20,7 @@ package com.rtbishop.look4sat.domain
|
|||||||
import com.rtbishop.look4sat.model.OrbitalData
|
import com.rtbishop.look4sat.model.OrbitalData
|
||||||
import kotlin.math.*
|
import kotlin.math.*
|
||||||
|
|
||||||
class NearEarthSat(data: OrbitalData) : Satellite(data) {
|
class NearEarthSatellite(data: OrbitalData) : Satellite(data) {
|
||||||
|
|
||||||
private val aodp: Double
|
private val aodp: Double
|
||||||
private val aycof: Double
|
private val aycof: Double
|
||||||
@@ -110,8 +110,8 @@ abstract class Satellite(val data: OrbitalData) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun calculateSDP4orSGP4(tsince: Double) {
|
private fun calculateSDP4orSGP4(tsince: Double) {
|
||||||
if (data.isDeepSpace) (this as DeepSpaceSat).calculateSDP4(tsince)
|
if (data.isDeepSpace) (this as DeepSpaceSatellite).calculateSDP4(tsince)
|
||||||
else (this as NearEarthSat).calculateSGP4(tsince)
|
else (this as NearEarthSatellite).calculateSGP4(tsince)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Converts the sat position and velocity vectors to km and km/sec
|
// Converts the sat position and velocity vectors to km and km/sec
|
||||||
|
|||||||
@@ -39,5 +39,6 @@ data class OrbitalData(
|
|||||||
// Space objects are classified as NearEarth (period < 225 min) or DeepSpace (period >= 225 min)
|
// Space objects are classified as NearEarth (period < 225 min) or DeepSpace (period >= 225 min)
|
||||||
val isDeepSpace: Boolean = orbitalPeriod >= 225.0
|
val isDeepSpace: Boolean = orbitalPeriod >= 225.0
|
||||||
) {
|
) {
|
||||||
fun getSatellite(): Satellite = if (isDeepSpace) DeepSpaceSat(this) else NearEarthSat(this)
|
fun getSatellite(): Satellite =
|
||||||
|
if (isDeepSpace) DeepSpaceSatellite(this) else NearEarthSatellite(this)
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user