Even more restructuring and code cleanup
This commit is contained in:
1 parent
a807cfd8f6
commit
f8e7bcdf39
12 files changed
+222
-217
No files matched your search
@@ -8,21 +8,21 @@ import android.location.LocationManager
|
||||
import androidx.room.Room
|
||||
import com.rtbishop.look4sat.data.DataParser
|
||||
import com.rtbishop.look4sat.data.DatabaseRepo
|
||||
import com.rtbishop.look4sat.data.ISensorSource
|
||||
import com.rtbishop.look4sat.data.SatelliteRepo
|
||||
import com.rtbishop.look4sat.data.SelectionRepo
|
||||
import com.rtbishop.look4sat.domain.IDatabaseRepo
|
||||
import com.rtbishop.look4sat.domain.ISatelliteRepo
|
||||
import com.rtbishop.look4sat.domain.ISelectionRepo
|
||||
import com.rtbishop.look4sat.domain.ISensorsRepo
|
||||
import com.rtbishop.look4sat.domain.ISettingsRepo
|
||||
import com.rtbishop.look4sat.framework.BluetoothReporter
|
||||
import com.rtbishop.look4sat.framework.NetworkReporter
|
||||
import com.rtbishop.look4sat.framework.SensorsRepo
|
||||
import com.rtbishop.look4sat.framework.SettingsRepo
|
||||
import com.rtbishop.look4sat.framework.data.DataSource
|
||||
import com.rtbishop.look4sat.framework.data.LocalStorage
|
||||
import com.rtbishop.look4sat.framework.data.MIGRATION_1_2
|
||||
import com.rtbishop.look4sat.framework.data.MainDatabase
|
||||
import com.rtbishop.look4sat.framework.data.SensorSource
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -40,7 +40,6 @@ class MainContainer(private val context: Context) {
|
||||
val databaseRepo = provideDatabaseRepo()
|
||||
val satelliteRepo = provideSatelliteRepo()
|
||||
val selectionRepo = provideSelectionRepo()
|
||||
val sensorsRepo = provideSensorsRepo()
|
||||
|
||||
fun provideBluetoothReporter(): BluetoothReporter {
|
||||
val btManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
|
||||
@@ -51,6 +50,12 @@ class MainContainer(private val context: Context) {
|
||||
return NetworkReporter(CoroutineScope(Dispatchers.IO))
|
||||
}
|
||||
|
||||
fun provideSensorSource(): ISensorSource {
|
||||
val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
|
||||
val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)
|
||||
return SensorSource(sensorManager, sensor)
|
||||
}
|
||||
|
||||
private fun provideDatabaseRepo(): IDatabaseRepo {
|
||||
val dataParser = DataParser(Dispatchers.Default)
|
||||
val dataSource = DataSource(context.contentResolver, Dispatchers.IO)
|
||||
@@ -58,19 +63,13 @@ class MainContainer(private val context: Context) {
|
||||
}
|
||||
|
||||
private fun provideSatelliteRepo(): ISatelliteRepo {
|
||||
return SatelliteRepo(Dispatchers.Default, localStorage)
|
||||
return SatelliteRepo(Dispatchers.Default, localStorage, settingsRepo)
|
||||
}
|
||||
|
||||
private fun provideSelectionRepo(): ISelectionRepo {
|
||||
return SelectionRepo(Dispatchers.Default, localStorage, settingsRepo)
|
||||
}
|
||||
|
||||
private fun provideSensorsRepo(): ISensorsRepo {
|
||||
val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
|
||||
val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)
|
||||
return SensorsRepo(sensorManager, sensor)
|
||||
}
|
||||
|
||||
private fun provideSettingsRepo(): ISettingsRepo {
|
||||
val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||
val preferences = context.getSharedPreferences("default", Context.MODE_PRIVATE)
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.rtbishop.look4sat.domain.ISettingsRepo
|
||||
import com.rtbishop.look4sat.model.DatabaseState
|
||||
import com.rtbishop.look4sat.model.GeoPos
|
||||
import com.rtbishop.look4sat.model.OtherSettings
|
||||
import com.rtbishop.look4sat.model.PassesSettings
|
||||
import com.rtbishop.look4sat.utility.QthConverter
|
||||
import com.rtbishop.look4sat.utility.round
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -99,15 +100,6 @@ class SettingsRepo(
|
||||
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())
|
||||
@@ -126,8 +118,51 @@ class SettingsRepo(
|
||||
saveStationPosition(GeoPos(newLat, newLon, newAlt, locator, timestamp))
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region # Database update settings
|
||||
|
||||
private val _databaseState = MutableStateFlow(loadDatabaseState())
|
||||
override val databaseState: StateFlow<DatabaseState> = _databaseState
|
||||
|
||||
override fun saveDatabaseState(state: DatabaseState) = preferences.edit {
|
||||
putInt(keyDataEntries, state.entriesTotal)
|
||||
putInt(keyDataRadios, state.radiosTotal)
|
||||
putLong(keyDataTimestamp, state.timestamp)
|
||||
_databaseState.value = state
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
private fun loadDatabaseState(): DatabaseState {
|
||||
val entriesTotal = preferences.getInt(keyDataEntries, 0)
|
||||
val radiosTotal = preferences.getInt(keyDataRadios, 0)
|
||||
val timestamp = preferences.getLong(keyDataTimestamp, 0L)
|
||||
return DatabaseState(entriesTotal, radiosTotal, timestamp)
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region # Entries selection settings
|
||||
|
||||
private val _satelliteSelection = MutableStateFlow(loadEntriesSelection())
|
||||
override val satelliteSelection: StateFlow<List<Int>> = _satelliteSelection
|
||||
|
||||
@@ -141,49 +176,36 @@ class SettingsRepo(
|
||||
return catNums?.map { catnum -> catnum.toInt() }?.sorted() ?: emptyList()
|
||||
}
|
||||
|
||||
override fun saveSatType(type: String, catnums: List<Int>) {
|
||||
val stringList = catnums.map { catnum -> catnum.toString() }
|
||||
preferences.edit { putStringSet("type$type", stringList.toSet()) }
|
||||
//endregion
|
||||
|
||||
//region # Passes filter settings
|
||||
|
||||
private val _passesSettings = MutableStateFlow(loadPassesSettings())
|
||||
override val passesSettings: StateFlow<PassesSettings> = _passesSettings
|
||||
|
||||
override fun savePassesSettings(settings: PassesSettings) = preferences.edit {
|
||||
putInt(keyHoursAhead, settings.hoursAhead)
|
||||
putDouble(keyMinElevation, settings.minElevation)
|
||||
_passesSettings.value = settings
|
||||
}
|
||||
|
||||
override fun loadSatType(type: String): List<Int> {
|
||||
val catnums =
|
||||
preferences.getStringSet("type$type", emptySet())?.map { catnum -> catnum.toInt() }
|
||||
return catnums ?: emptyList()
|
||||
override fun saveModesSelection(modes: List<String>) {
|
||||
preferences.edit { putStringSet(keyModes, modes.toSet()) }
|
||||
}
|
||||
|
||||
override fun getHoursAhead(): Int {
|
||||
return preferences.getInt(keyHoursAhead, 24)
|
||||
override fun loadModesSelection(): List<String> {
|
||||
return preferences.getStringSet(keyModes, null)?.toList()?.sorted() ?: emptyList()
|
||||
}
|
||||
|
||||
override fun setHoursAhead(hoursAhead: Int) {
|
||||
preferences.edit { putInt(keyHoursAhead, hoursAhead) }
|
||||
private fun loadPassesSettings(): PassesSettings {
|
||||
val hoursAhead = preferences.getInt(keyHoursAhead, 24)
|
||||
val minElevation = preferences.getDouble(keyMinElevation, 16.0)
|
||||
return PassesSettings(hoursAhead, minElevation)
|
||||
}
|
||||
|
||||
override fun getMinElevation(): Double {
|
||||
return preferences.getDouble(keyMinElevation, 16.0)
|
||||
}
|
||||
//endregion
|
||||
|
||||
override fun setMinElevation(minElevation: Double) {
|
||||
preferences.edit { putDouble(keyMinElevation, minElevation) }
|
||||
}
|
||||
|
||||
private val _databaseState = MutableStateFlow(loadDatabaseState())
|
||||
override val databaseState: StateFlow<DatabaseState> = _databaseState
|
||||
|
||||
override fun saveDatabaseState(state: DatabaseState) = preferences.edit {
|
||||
putInt(keyDataEntries, state.entriesTotal)
|
||||
putInt(keyDataRadios, state.radiosTotal)
|
||||
putLong(keyDataTimestamp, state.timestamp)
|
||||
_databaseState.value = state
|
||||
}
|
||||
|
||||
private fun loadDatabaseState(): DatabaseState {
|
||||
val entriesTotal = preferences.getInt(keyDataEntries, 0)
|
||||
val radiosTotal = preferences.getInt(keyDataRadios, 0)
|
||||
val timestamp = preferences.getLong(keyDataTimestamp, 0L)
|
||||
return DatabaseState(entriesTotal, radiosTotal, timestamp)
|
||||
}
|
||||
//region # Other settings
|
||||
|
||||
private val _otherSettings = MutableStateFlow(loadOtherSettings())
|
||||
override val otherSettings: StateFlow<OtherSettings> = _otherSettings
|
||||
@@ -216,13 +238,9 @@ class SettingsRepo(
|
||||
return OtherSettings(utcState, updateState, sweepState, sensorState)
|
||||
}
|
||||
|
||||
override fun saveModesSelection(modes: List<String>) {
|
||||
preferences.edit { putStringSet(keyModes, modes.toSet()) }
|
||||
}
|
||||
//endregion
|
||||
|
||||
override fun loadModesSelection(): List<String> {
|
||||
return preferences.getStringSet(keyModes, null)?.toList()?.sorted() ?: emptyList()
|
||||
}
|
||||
//region # Undefined settings
|
||||
|
||||
override fun getRotatorEnabled(): Boolean {
|
||||
return preferences.getBoolean(keyRotator, false)
|
||||
@@ -280,6 +298,8 @@ class SettingsRepo(
|
||||
preferences.edit { putString(keyBTFormat, value) }
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
private fun SharedPreferences.getDouble(key: String, default: Double): Double {
|
||||
return Double.fromBits(getLong(key, default.toRawBits()))
|
||||
}
|
||||
|
||||
+10
-12
@@ -15,30 +15,30 @@
|
||||
* 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
|
||||
package com.rtbishop.look4sat.framework.data
|
||||
|
||||
import android.hardware.Sensor
|
||||
import android.hardware.SensorEvent
|
||||
import android.hardware.SensorEventListener
|
||||
import android.hardware.SensorManager
|
||||
import com.rtbishop.look4sat.domain.ISensorsRepo
|
||||
import com.rtbishop.look4sat.data.ISensorSource
|
||||
import com.rtbishop.look4sat.domain.RAD2DEG
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlin.math.round
|
||||
|
||||
class SensorsRepo(private val sensorManager: SensorManager, private val sensor: Sensor?) :
|
||||
SensorEventListener, ISensorsRepo {
|
||||
|
||||
private val rotationMatrix = FloatArray(9)
|
||||
private val orientationValues = FloatArray(3)
|
||||
private var sensorAccuracy: Int = SensorManager.SENSOR_STATUS_UNRELIABLE
|
||||
class SensorSource(private val sensorManager: SensorManager, private val sensor: Sensor?) :
|
||||
SensorEventListener, ISensorSource {
|
||||
|
||||
private val _orientation = MutableStateFlow(Pair(0f, 0f))
|
||||
private val rotationMatrix = FloatArray(9)
|
||||
private val orientationValues = FloatArray(3)
|
||||
private var sensorAccuracy = SensorManager.SENSOR_STATUS_UNRELIABLE
|
||||
|
||||
override val orientation: StateFlow<Pair<Float, Float>> = _orientation
|
||||
|
||||
override fun enableSensor() {
|
||||
sensor?.let { sensorManager.registerListener(this, it, 16000) }
|
||||
sensor?.let { sensorManager.registerListener(this, it, 8000) }
|
||||
}
|
||||
|
||||
override fun disableSensor() = sensorManager.unregisterListener(this)
|
||||
@@ -57,12 +57,10 @@ class SensorsRepo(private val sensorManager: SensorManager, private val sensor:
|
||||
SensorManager.getRotationMatrixFromVector(rotationMatrix, rotationVector)
|
||||
SensorManager.getOrientation(rotationMatrix, orientationValues)
|
||||
val azimuth = (orientationValues[0] * RAD2DEG).toFloat()
|
||||
val pitch = (orientationValues[1] * RAD2DEG).toFloat()
|
||||
// val roll = (orientationValues[2] * RAD2DEG).toFloat()
|
||||
val pitch = (orientationValues[1] * RAD2DEG).toFloat() // roll [2]
|
||||
val magneticAzimuth = (azimuth + 360f) % 360f
|
||||
val roundedAzimuth = round(magneticAzimuth * 10) / 10
|
||||
val roundedPitch = round(pitch * 10) / 10
|
||||
// val roundedRoll = round(roll * 10) / 10
|
||||
_orientation.value = Pair(roundedAzimuth, roundedPitch)
|
||||
}
|
||||
}
|
||||
@@ -49,8 +49,8 @@ class MapViewModel(
|
||||
) : ViewModel() {
|
||||
|
||||
private val stationPos = settingsRepo.stationPosition.value
|
||||
private var allPasses = satelliteRepo.getPasses()
|
||||
private var allSatellites = listOf<Satellite>()
|
||||
private var allPasses = satelliteRepo.passes.value
|
||||
private var allSatellites = satelliteRepo.satellites.value
|
||||
private var dataUpdateJob: Job? = null
|
||||
private var dataUpdateRate = 1000L
|
||||
private var selectedSatellite: Satellite? = null
|
||||
@@ -87,18 +87,12 @@ class MapViewModel(
|
||||
}
|
||||
|
||||
fun selectDefaultSatellite(catnum: Int) {
|
||||
viewModelScope.launch {
|
||||
val selectedIds = settingsRepo.satelliteSelection.value
|
||||
satelliteRepo.getEntriesWithIds(selectedIds).also { satellites ->
|
||||
if (satellites.isNotEmpty()) {
|
||||
allSatellites = satellites
|
||||
if (catnum == -1) {
|
||||
allPasses.find { pass -> pass.progress < 100 && !pass.isDeepSpace }
|
||||
?.let { pass -> selectSatellite(pass.satellite) }
|
||||
} else {
|
||||
satellites.find { it.data.catnum == catnum }?.let { selectSatellite(it) }
|
||||
}
|
||||
}
|
||||
if (allSatellites.isNotEmpty()) {
|
||||
if (catnum == -1) {
|
||||
allPasses.find { pass -> pass.progress < 100 && !pass.isDeepSpace }
|
||||
?.let { pass -> selectSatellite(pass.satellite) }
|
||||
} else {
|
||||
allSatellites.find { it.data.catnum == catnum }?.let { selectSatellite(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,10 +53,9 @@ fun PassesScreen(navToRadar: (Int, Long) -> Unit) {
|
||||
val showDialog = rememberSaveable { mutableStateOf(false) }
|
||||
val toggleDialog = { showDialog.value = showDialog.value.not() }
|
||||
if (showDialog.value) {
|
||||
FilterDialog(viewModel.getHoursAhead(),
|
||||
viewModel.getMinElevation(),
|
||||
{ toggleDialog() }) { hours, elev ->
|
||||
viewModel.calculatePasses(hours, elev)
|
||||
val (hoursAhead, minElevation) = viewModel.getFilterSettings()
|
||||
FilterDialog(hoursAhead, minElevation, { toggleDialog() }) { newHours, newElevation ->
|
||||
viewModel.calculatePasses(System.currentTimeMillis(), newHours, newElevation)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.rtbishop.look4sat.MainApplication
|
||||
import com.rtbishop.look4sat.domain.ISatelliteRepo
|
||||
import com.rtbishop.look4sat.domain.ISettingsRepo
|
||||
import com.rtbishop.look4sat.model.DataState
|
||||
import com.rtbishop.look4sat.model.PassesSettings
|
||||
import com.rtbishop.look4sat.model.SatPass
|
||||
import com.rtbishop.look4sat.utility.toTimerString
|
||||
import kotlinx.coroutines.Job
|
||||
@@ -47,13 +48,14 @@ class PassesViewModel(
|
||||
val passes: StateFlow<DataState<List<SatPass>>> = _passes
|
||||
val timerText: StateFlow<Triple<String, String, String>?> = _timerText
|
||||
|
||||
fun getHoursAhead() = settingsRepo.getHoursAhead()
|
||||
fun getFilterSettings() = settingsRepo.passesSettings.value
|
||||
|
||||
fun getMinElevation() = settingsRepo.getMinElevation()
|
||||
fun shouldUseUTC() = settingsRepo.otherSettings.value.utcState
|
||||
|
||||
init {
|
||||
viewModelScope.launch { satelliteRepo.initRepository() }
|
||||
viewModelScope.launch {
|
||||
satelliteRepo.calculatedPasses.collect { passes ->
|
||||
satelliteRepo.passes.collect { passes ->
|
||||
passesProcessing?.cancelAndJoin()
|
||||
passesProcessing = viewModelScope.launch {
|
||||
while (isActive) {
|
||||
@@ -84,32 +86,20 @@ class PassesViewModel(
|
||||
}
|
||||
}
|
||||
}
|
||||
viewModelScope.launch {
|
||||
settingsRepo.satelliteSelection.collect { calculatePasses() }
|
||||
}
|
||||
}
|
||||
|
||||
fun shouldUseUTC() = settingsRepo.otherSettings.value.utcState
|
||||
fun calculatePasses() = viewModelScope.launch {
|
||||
_passes.emit(DataState.Loading)
|
||||
passesProcessing?.cancelAndJoin()
|
||||
val (hoursAhead, minElevation) = settingsRepo.passesSettings.value
|
||||
satelliteRepo.calculatePasses(System.currentTimeMillis(), hoursAhead, minElevation)
|
||||
}
|
||||
|
||||
fun calculatePasses(
|
||||
hoursAhead: Int = 1,
|
||||
minElevation: Double = settingsRepo.getMinElevation(),
|
||||
timeRef: Long = System.currentTimeMillis()
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
_passes.emit(DataState.Loading)
|
||||
// _timerText.postValue(timerDefaultText)
|
||||
passesProcessing?.cancelAndJoin()
|
||||
// selection?.let { items -> settingsRepository.saveEntriesSelection(items) }
|
||||
settingsRepo.setHoursAhead(hoursAhead)
|
||||
settingsRepo.setMinElevation(minElevation)
|
||||
val stationPos = settingsRepo.stationPosition.value
|
||||
val selectedIds = settingsRepo.satelliteSelection.value
|
||||
val satellites = satelliteRepo.getEntriesWithIds(selectedIds)
|
||||
satelliteRepo.calculatePasses(
|
||||
satellites, stationPos, timeRef, hoursAhead, minElevation
|
||||
)
|
||||
}
|
||||
fun calculatePasses(timeRef: Long, hoursAhead: Int, minElev: Double) = viewModelScope.launch {
|
||||
_passes.emit(DataState.Loading)
|
||||
passesProcessing?.cancelAndJoin()
|
||||
settingsRepo.savePassesSettings(PassesSettings(hoursAhead, minElev))
|
||||
satelliteRepo.calculatePasses(timeRef, hoursAhead, minElev)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -28,8 +28,8 @@ import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.initializer
|
||||
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||
import com.rtbishop.look4sat.MainApplication
|
||||
import com.rtbishop.look4sat.data.ISensorSource
|
||||
import com.rtbishop.look4sat.domain.ISatelliteRepo
|
||||
import com.rtbishop.look4sat.domain.ISensorsRepo
|
||||
import com.rtbishop.look4sat.domain.ISettingsRepo
|
||||
import com.rtbishop.look4sat.domain.Satellite
|
||||
import com.rtbishop.look4sat.framework.BluetoothReporter
|
||||
@@ -51,20 +51,20 @@ class RadarViewModel(
|
||||
private val networkReporter: NetworkReporter,
|
||||
private val satelliteRepo: ISatelliteRepo,
|
||||
private val settingsRepo: ISettingsRepo,
|
||||
private val sensorsRepo: ISensorsRepo
|
||||
private val sensorSource: ISensorSource
|
||||
) : ViewModel() {
|
||||
|
||||
private val stationPos = settingsRepo.stationPosition.value
|
||||
private val magDeclination = getMagDeclination(stationPos)
|
||||
val transmitters = mutableStateOf<List<SatRadio>>(emptyList())
|
||||
val radarData = mutableStateOf<RadarData?>(null)
|
||||
val orientation = mutableStateOf(sensorsRepo.orientation.value)
|
||||
val orientation = mutableStateOf(sensorSource.orientation.value)
|
||||
|
||||
init {
|
||||
if (settingsRepo.otherSettings.value.sensorState) {
|
||||
viewModelScope.launch {
|
||||
sensorsRepo.enableSensor()
|
||||
sensorsRepo.orientation.collect { data ->
|
||||
sensorSource.enableSensor()
|
||||
sensorSource.orientation.collect { data ->
|
||||
orientation.value = Pair(data.first + magDeclination, data.second)
|
||||
}
|
||||
}
|
||||
@@ -72,29 +72,28 @@ class RadarViewModel(
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
sensorsRepo.disableSensor()
|
||||
sensorSource.disableSensor()
|
||||
super.onCleared()
|
||||
}
|
||||
|
||||
fun getPass() = flow {
|
||||
val catNum = savedStateHandle.get<Int>("catNum") ?: 0
|
||||
val aosTime = savedStateHandle.get<Long>("aosTime") ?: 0L
|
||||
satelliteRepo.calculatedPasses.collect { passes ->
|
||||
val pass = passes.find { pass -> pass.catNum == catNum && pass.aosTime == aosTime }
|
||||
val currentPass = pass ?: passes.firstOrNull()
|
||||
currentPass?.let { satPass ->
|
||||
emit(satPass)
|
||||
val transmitters = satelliteRepo.getRadiosWithId(satPass.catNum)
|
||||
viewModelScope.launch {
|
||||
while (isActive) {
|
||||
val timeNow = System.currentTimeMillis()
|
||||
val satPos =
|
||||
satelliteRepo.getPosition(satPass.satellite, stationPos, timeNow)
|
||||
sendPassData(satPass, satPos, satPass.satellite)
|
||||
sendPassDataBT(satPos)
|
||||
processRadios(transmitters, satPass.satellite, timeNow)
|
||||
delay(1000)
|
||||
}
|
||||
val passes = satelliteRepo.passes.value
|
||||
val pass = passes.find { pass -> pass.catNum == catNum && pass.aosTime == aosTime }
|
||||
val currentPass = pass ?: passes.firstOrNull()
|
||||
currentPass?.let { satPass ->
|
||||
emit(satPass)
|
||||
val transmitters = satelliteRepo.getRadiosWithId(satPass.catNum)
|
||||
viewModelScope.launch {
|
||||
while (isActive) {
|
||||
val timeNow = System.currentTimeMillis()
|
||||
val satPos =
|
||||
satelliteRepo.getPosition(satPass.satellite, stationPos, timeNow)
|
||||
sendPassData(satPass, satPos, satPass.satellite)
|
||||
sendPassDataBT(satPos)
|
||||
processRadios(transmitters, satPass.satellite, timeNow)
|
||||
delay(1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -165,7 +164,7 @@ class RadarViewModel(
|
||||
container.provideNetworkReporter(),
|
||||
container.satelliteRepo,
|
||||
container.settingsRepo,
|
||||
container.sensorsRepo
|
||||
container.provideSensorSource()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
package com.rtbishop.look4sat.domain
|
||||
package com.rtbishop.look4sat.data
|
||||
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
interface ISensorsRepo {
|
||||
interface ISensorSource {
|
||||
|
||||
val orientation: StateFlow<Pair<Float, Float>>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package com.rtbishop.look4sat.data
|
||||
|
||||
import com.rtbishop.look4sat.domain.ISatelliteRepo
|
||||
import com.rtbishop.look4sat.domain.ISettingsRepo
|
||||
import com.rtbishop.look4sat.domain.Satellite
|
||||
import com.rtbishop.look4sat.model.GeoPos
|
||||
import com.rtbishop.look4sat.model.SatPass
|
||||
@@ -26,25 +27,33 @@ import com.rtbishop.look4sat.model.SatRadio
|
||||
import com.rtbishop.look4sat.utility.round
|
||||
import com.rtbishop.look4sat.utility.toDegrees
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class SatelliteRepo(
|
||||
private val dispatcher: CoroutineDispatcher,
|
||||
private val localStorage: ILocalStorage
|
||||
private val localStorage: ILocalStorage,
|
||||
private val settingsRepo: ISettingsRepo
|
||||
) : ISatelliteRepo {
|
||||
|
||||
private var passes = listOf<SatPass>()
|
||||
private val _calculatedPasses = MutableSharedFlow<List<SatPass>>(replay = 1)
|
||||
override val calculatedPasses: SharedFlow<List<SatPass>> = _calculatedPasses
|
||||
private val _passes = MutableStateFlow<List<SatPass>>(emptyList())
|
||||
override val passes: StateFlow<List<SatPass>> = _passes
|
||||
|
||||
override fun getPasses(): List<SatPass> = passes
|
||||
|
||||
override suspend fun getEntriesWithIds(ids: List<Int>) = localStorage.getEntriesWithIds(ids)
|
||||
private val _satellites = MutableStateFlow<List<Satellite>>(emptyList())
|
||||
override val satellites: StateFlow<List<Satellite>> = _satellites
|
||||
|
||||
override suspend fun getRadiosWithId(id: Int) = localStorage.getRadiosWithId(id)
|
||||
|
||||
override suspend fun initRepository() = withContext(dispatcher) {
|
||||
settingsRepo.satelliteSelection.collect { selectedIds ->
|
||||
_satellites.update { localStorage.getEntriesWithIds(selectedIds) }
|
||||
val (hoursAhead, minElevation) = settingsRepo.passesSettings.value
|
||||
calculatePasses(System.currentTimeMillis(), hoursAhead, minElevation)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getPosition(sat: Satellite, pos: GeoPos, time: Long): SatPos {
|
||||
return withContext(dispatcher) { sat.getPosition(pos, time) }
|
||||
}
|
||||
@@ -70,12 +79,8 @@ class SatelliteRepo(
|
||||
val satPos = sat.getPosition(pos, time)
|
||||
val copiedList = radios.map { it.copy() }
|
||||
copiedList.forEach { transmitter ->
|
||||
transmitter.downlink?.let {
|
||||
transmitter.downlink = satPos.getDownlinkFreq(it)
|
||||
}
|
||||
transmitter.uplink?.let {
|
||||
transmitter.uplink = satPos.getUplinkFreq(it)
|
||||
}
|
||||
transmitter.downlink?.let { transmitter.downlink = satPos.getDownlinkFreq(it) }
|
||||
transmitter.uplink?.let { transmitter.uplink = satPos.getUplinkFreq(it) }
|
||||
}
|
||||
copiedList.map { it.copy() }
|
||||
}
|
||||
@@ -97,23 +102,18 @@ class SatelliteRepo(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun calculatePasses(
|
||||
satList: List<Satellite>, pos: GeoPos, time: Long, hoursAhead: Int, minElevation: Double
|
||||
) {
|
||||
if (satList.isEmpty()) {
|
||||
val newPasses = emptyList<SatPass>()
|
||||
passes = newPasses
|
||||
_calculatedPasses.emit(newPasses)
|
||||
} else {
|
||||
override suspend fun calculatePasses(time: Long, hoursAhead: Int, minElevation: Double) {
|
||||
if (_satellites.value.isNotEmpty()) {
|
||||
withContext(dispatcher) {
|
||||
val allPasses = mutableListOf<SatPass>()
|
||||
satList.forEach { satellite ->
|
||||
allPasses.addAll(satellite.getPasses(pos, time, hoursAhead))
|
||||
val stationPos = settingsRepo.stationPosition.value
|
||||
_satellites.value.forEach { satellite ->
|
||||
allPasses.addAll(satellite.getPasses(stationPos, time, hoursAhead))
|
||||
}
|
||||
val newPasses = allPasses.filter(time, hoursAhead, minElevation)
|
||||
passes = newPasses
|
||||
_calculatedPasses.emit(newPasses)
|
||||
_passes.update { allPasses.filter(time, hoursAhead, minElevation) }
|
||||
}
|
||||
} else {
|
||||
_passes.update { emptyList() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,8 +151,7 @@ class SatelliteRepo(
|
||||
private fun getGeoPass(sat: Satellite, pos: GeoPos, time: Long): SatPass {
|
||||
val satPos = sat.getPosition(pos, time)
|
||||
val aos = time - 24 * 60L * 60L * 1000L
|
||||
val los = time + 24 * 60L * 60L * 1000L
|
||||
// val tca = (aos + los) / 2
|
||||
val los = time + 24 * 60L * 60L * 1000L // val tca = (aos + los) / 2
|
||||
val az = satPos.azimuth.toDegrees().round(1)
|
||||
val elev = satPos.elevation.toDegrees().round(1)
|
||||
val alt = satPos.altitude
|
||||
@@ -164,8 +163,7 @@ class SatelliteRepo(
|
||||
var calendarTimeMillis = time
|
||||
var elevation: Double
|
||||
var maxElevation = 0.0
|
||||
var alt = 0.0
|
||||
// var tcaAz = 0.0
|
||||
var alt = 0.0 // var tcaAz = 0.0
|
||||
// rewind 1/4 of an orbit
|
||||
if (rewind) calendarTimeMillis += -quarterOrbitMin * 60L * 1000L
|
||||
|
||||
@@ -187,8 +185,7 @@ class SatelliteRepo(
|
||||
elevation = satPos.elevation
|
||||
if (elevation > maxElevation) {
|
||||
maxElevation = elevation
|
||||
alt = satPos.altitude
|
||||
// tcaAz = satPos.azimuth.toDegrees()
|
||||
alt = satPos.altitude // tcaAz = satPos.azimuth.toDegrees()
|
||||
}
|
||||
} while (satPos.elevation < 0.0)
|
||||
|
||||
@@ -200,8 +197,7 @@ class SatelliteRepo(
|
||||
elevation = satPos.elevation
|
||||
if (elevation > maxElevation) {
|
||||
maxElevation = elevation
|
||||
alt = satPos.altitude
|
||||
// tcaAz = satPos.azimuth.toDegrees()
|
||||
alt = satPos.altitude // tcaAz = satPos.azimuth.toDegrees()
|
||||
}
|
||||
} while (satPos.elevation < 0.0)
|
||||
|
||||
@@ -215,8 +211,7 @@ class SatelliteRepo(
|
||||
elevation = satPos.elevation
|
||||
if (elevation > maxElevation) {
|
||||
maxElevation = elevation
|
||||
alt = satPos.altitude
|
||||
// tcaAz = satPos.azimuth.toDegrees()
|
||||
alt = satPos.altitude // tcaAz = satPos.azimuth.toDegrees()
|
||||
}
|
||||
} while (satPos.elevation > 0.0)
|
||||
|
||||
@@ -228,14 +223,12 @@ class SatelliteRepo(
|
||||
elevation = satPos.elevation
|
||||
if (elevation > maxElevation) {
|
||||
maxElevation = elevation
|
||||
alt = satPos.altitude
|
||||
// tcaAz = satPos.azimuth.toDegrees()
|
||||
alt = satPos.altitude // tcaAz = satPos.azimuth.toDegrees()
|
||||
}
|
||||
} while (satPos.elevation > 0.0)
|
||||
|
||||
val los = satPos.time
|
||||
val los = satPos.time // val tca = (aos + los) / 2
|
||||
val losAz = satPos.azimuth.toDegrees().round(1)
|
||||
// val tca = (aos + los) / 2
|
||||
val elev = maxElevation.toDegrees().round(1)
|
||||
return SatPass(aos, aosAz, los, losAz, alt.toInt(), elev, sat)
|
||||
}
|
||||
|
||||
@@ -4,36 +4,27 @@ import com.rtbishop.look4sat.model.GeoPos
|
||||
import com.rtbishop.look4sat.model.SatPass
|
||||
import com.rtbishop.look4sat.model.SatPos
|
||||
import com.rtbishop.look4sat.model.SatRadio
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
interface ISatelliteRepo {
|
||||
|
||||
val calculatedPasses: SharedFlow<List<SatPass>>
|
||||
val passes: StateFlow<List<SatPass>>
|
||||
|
||||
fun getPasses(): List<SatPass>
|
||||
|
||||
suspend fun getEntriesWithIds(ids: List<Int>): List<Satellite>
|
||||
val satellites: StateFlow<List<Satellite>>
|
||||
|
||||
suspend fun getRadiosWithId(id: Int): List<SatRadio>
|
||||
|
||||
suspend fun initRepository()
|
||||
|
||||
suspend fun getPosition(sat: Satellite, pos: GeoPos, time: Long): SatPos
|
||||
|
||||
suspend fun getTrack(sat: Satellite, pos: GeoPos, start: Long, end: Long): List<SatPos>
|
||||
|
||||
suspend fun processRadios(
|
||||
sat: Satellite,
|
||||
pos: GeoPos,
|
||||
radios: List<SatRadio>,
|
||||
time: Long
|
||||
sat: Satellite, pos: GeoPos, radios: List<SatRadio>, time: Long
|
||||
): List<SatRadio>
|
||||
|
||||
suspend fun processPasses(passList: List<SatPass>, time: Long): List<SatPass>
|
||||
|
||||
suspend fun calculatePasses(
|
||||
satList: List<Satellite>,
|
||||
pos: GeoPos,
|
||||
time: Long,
|
||||
hoursAhead: Int = 8,
|
||||
minElevation: Double = 16.0
|
||||
)
|
||||
suspend fun calculatePasses(time: Long, hoursAhead: Int = 8, minElevation: Double = 16.0)
|
||||
}
|
||||
@@ -20,12 +20,12 @@ package com.rtbishop.look4sat.domain
|
||||
import com.rtbishop.look4sat.model.DatabaseState
|
||||
import com.rtbishop.look4sat.model.GeoPos
|
||||
import com.rtbishop.look4sat.model.OtherSettings
|
||||
import com.rtbishop.look4sat.model.PassesSettings
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
interface ISettingsRepo {
|
||||
|
||||
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",
|
||||
@@ -56,9 +56,9 @@ interface ISettingsRepo {
|
||||
"X-Comm" to "https://celestrak.com/NORAD/elements/gp.php?GROUP=x-comm&FORMAT=csv"
|
||||
)
|
||||
|
||||
val stationPosition: StateFlow<GeoPos>
|
||||
//region # Station position settings
|
||||
|
||||
val satelliteSelection: StateFlow<List<Int>>
|
||||
val stationPosition: StateFlow<GeoPos>
|
||||
|
||||
fun setGpsPosition(): Boolean
|
||||
|
||||
@@ -66,18 +66,42 @@ interface ISettingsRepo {
|
||||
|
||||
fun setQthPosition(locator: String): Boolean
|
||||
|
||||
fun getHoursAhead(): Int
|
||||
//endregion
|
||||
|
||||
fun setHoursAhead(hoursAhead: Int)
|
||||
|
||||
fun getMinElevation(): Double
|
||||
|
||||
fun setMinElevation(minElevation: Double)
|
||||
//region # Database update settings
|
||||
|
||||
val databaseState: StateFlow<DatabaseState>
|
||||
|
||||
fun saveDatabaseState(state: DatabaseState)
|
||||
|
||||
fun saveSatType(type: String, catnums: List<Int>)
|
||||
|
||||
fun loadSatType(type: String): List<Int>
|
||||
|
||||
//endregion
|
||||
|
||||
//region # Entries selection settings
|
||||
|
||||
val satelliteSelection: StateFlow<List<Int>>
|
||||
|
||||
fun saveEntriesSelection(catnums: List<Int>)
|
||||
|
||||
//endregion
|
||||
|
||||
//region # Passes filter settings
|
||||
|
||||
val passesSettings: StateFlow<PassesSettings>
|
||||
|
||||
fun savePassesSettings(settings: PassesSettings)
|
||||
|
||||
fun saveModesSelection(modes: List<String>)
|
||||
|
||||
fun loadModesSelection(): List<String>
|
||||
|
||||
//endregion
|
||||
|
||||
//region # Other settings
|
||||
|
||||
val otherSettings: StateFlow<OtherSettings>
|
||||
|
||||
fun toggleUtc(value: Boolean)
|
||||
@@ -88,15 +112,9 @@ interface ISettingsRepo {
|
||||
|
||||
fun toggleSensor(value: Boolean)
|
||||
|
||||
fun saveModesSelection(modes: List<String>)
|
||||
//endregion
|
||||
|
||||
fun loadModesSelection(): List<String>
|
||||
|
||||
fun saveEntriesSelection(catnums: List<Int>)
|
||||
|
||||
fun saveSatType(type: String, catnums: List<Int>)
|
||||
|
||||
fun loadSatType(type: String): List<Int>
|
||||
//region # Undefined settings
|
||||
|
||||
fun getRotatorEnabled(): Boolean
|
||||
|
||||
@@ -125,4 +143,6 @@ interface ISettingsRepo {
|
||||
fun getBTFormat(): String
|
||||
|
||||
fun setBTFormat(value: String)
|
||||
|
||||
//endregion
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
package com.rtbishop.look4sat.model
|
||||
|
||||
data class DatabaseState(val entriesTotal: Int, val radiosTotal: Int, val timestamp: Long)
|
||||
|
||||
data class PassesSettings(val hoursAhead: Int, val minElevation: Double)
|
||||
|
||||
data class OtherSettings(
|
||||
val utcState: Boolean,
|
||||
val updateState: Boolean,
|
||||
val sweepState: Boolean,
|
||||
val sensorState: Boolean
|
||||
)
|
||||
|
||||
data class DatabaseState(val entriesTotal: Int, val radiosTotal: Int, val timestamp: Long)
|
||||
Reference in new issue
Block a user