Added DeepSpace passes filter, fixed refresh issue (#208)

This commit is contained in:
Arty Bishop authored and Arty Bishop committed 2026-04-18 15:56:28 +01:00
1 parent d2b3184084
commit 8265b73d75
10 files changed
+112 -68

No files matched your search

@@ -31,6 +31,7 @@ import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
@@ -45,6 +46,9 @@ class SatelliteRepo(
private val _passes = MutableStateFlow<List<OrbitalPass>>(emptyList())
override val passes: StateFlow<List<OrbitalPass>> = _passes
private val _isCalculating = MutableStateFlow(false)
override val isCalculating: StateFlow<Boolean> = _isCalculating
private val _satellites = MutableStateFlow<List<OrbitalObject>>(emptyList())
override val satellites: StateFlow<List<OrbitalObject>> = _satellites
@@ -53,7 +57,7 @@ class SatelliteRepo(
override suspend fun initRepository() = withContext(dispatcher) {
settingsRepo.selectedIds.collect { selectedIds ->
_satellites.update { localStorage.getEntriesWithIds(selectedIds) }
val (hoursAhead, minElevation, modes) = settingsRepo.passesSettings.value
val (_, hoursAhead, minElevation, modes) = settingsRepo.passesSettings.value
calculatePasses(System.currentTimeMillis(), hoursAhead, minElevation, modes)
}
}
@@ -95,41 +99,40 @@ class SatelliteRepo(
}
override suspend fun calculatePasses(time: Long, hoursAhead: Int, minElevation: Double, modes: List<String>) {
_isCalculating.value = true
// Normalize to the start of the current minute so that coarse 60-second stepping
// in getLeoPass always begins from the same phase, producing stable AOS/LOS times
val normalizedTime = time / 60_000L * 60_000L
val currentSatellites = _satellites.value
if (currentSatellites.isNotEmpty()) {
withContext(dispatcher) {
val idsWithModes = localStorage.getIdsWithModes(modes)
val stationPos = settingsRepo.stationPosition.value
val filteredSatellites = if (idsWithModes.isEmpty()) {
currentSatellites
} else {
currentSatellites.filter { it.data.catnum in idsWithModes }
}
// Compute passes for each satellite in parallel
val passLists = coroutineScope {
filteredSatellites.map { satellite ->
async { satellite.getPasses(stationPos, normalizedTime, hoursAhead) }
}.awaitAll()
}
// Flatten and filter in a single pass
val timeFuture = normalizedTime + (hoursAhead * 60L * 60L * 1000L)
val newPasses = ArrayList<OrbitalPass>()
for (list in passLists) {
for (pass in list) {
if (pass.losTime > time && pass.aosTime < timeFuture && pass.maxElevation > minElevation) {
newPasses.add(pass)
}
withContext(dispatcher) {
val idsWithModes = localStorage.getIdsWithModes(modes)
val stationPos = settingsRepo.stationPosition.value
val filteredSatellites = if (idsWithModes.isEmpty()) {
currentSatellites
} else {
currentSatellites.filter { it.data.catnum in idsWithModes }
}
// Compute passes for each satellite in parallel
val passLists = coroutineScope {
filteredSatellites.map { satellite ->
async { satellite.getPasses(stationPos, normalizedTime, hoursAhead) }
}.awaitAll()
}
// Flatten and filter in a single pass
val timeFuture = normalizedTime + (hoursAhead * 60L * 60L * 1000L)
val newPasses = ArrayList<OrbitalPass>()
for (list in passLists) {
for (pass in list) {
if (pass.losTime > time && pass.aosTime < timeFuture && pass.maxElevation > minElevation) {
newPasses.add(pass)
}
}
newPasses.sortBy { it.aosTime }
_passes.update { newPasses }
}
} else {
_passes.update { emptyList() }
newPasses.sortBy { it.aosTime }
delay(1000) // Simulate loading time for better UX
_passes.update { newPasses }
}
_isCalculating.value = false
}
private fun OrbitalObject.getPasses(pos: GeoPos, time: Long, hours: Int): List<OrbitalPass> {
@@ -51,6 +51,7 @@ class SettingsRepo(
private val keyBluetoothFrequencyState = "bluetoothFrequencyState"
private val keyBluetoothFrequencyAddress = "bluetoothFrequencyAddress"
private val keyBluetoothFrequencyFormat = "bluetoothFrequencyFormat"
private val keyFilterShowDeepSpace = "filterShowDeepSpace"
private val keyFilterHoursAhead = "filterHoursAhead"
private val keyFilterMinElevation = "filterMinElevation"
private val keyNumberOfRadios = "numberOfRadios"
@@ -121,6 +122,7 @@ class SettingsRepo(
override val passesSettings: StateFlow<PassesSettings> = _passesSettings
override fun setPassesSettings(settings: PassesSettings) = preferences.edit {
putBoolean(keyFilterShowDeepSpace, settings.showDeepSpace)
putInt(keyFilterHoursAhead, settings.hoursAhead)
putLong(keyFilterMinElevation, settings.minElevation.toRawBits())
putString(keySelectedModes, settings.selectedModes.joinToString(separatorComma))
@@ -128,11 +130,12 @@ class SettingsRepo(
}
private fun getPassesSettings(): PassesSettings {
val showDeepSpace = preferences.getBoolean(keyFilterShowDeepSpace, true)
val hoursAhead = preferences.getInt(keyFilterHoursAhead, 24)
val minElevation = Double.fromBits(preferences.getLong(keyFilterMinElevation, 16.0.toRawBits()))
val selectedModesString = preferences.getString(keySelectedModes, null)
val selectedModes = selectedModesString?.split(separatorComma)?.sorted() ?: emptyList()
return PassesSettings(hoursAhead, minElevation, selectedModes)
return PassesSettings(showDeepSpace, hoursAhead, minElevation, selectedModes)
}
//endregion
@@ -24,6 +24,7 @@ data class DatabaseState(
)
data class PassesSettings(
val showDeepSpace: Boolean = true,
val hoursAhead: Int,
val minElevation: Double,
val selectedModes: List<String>
@@ -31,6 +31,9 @@ interface ISatelliteRepo {
/** Raw calculated passes (without live progress). Updated on selection/filter change. */
val passes: StateFlow<List<OrbitalPass>>
/** Whether the repo is currently calculating passes. */
val isCalculating: StateFlow<Boolean>
/** Load satellite objects from DB based on the current selection. */
suspend fun initRepository()
@@ -32,6 +32,7 @@
<string name="pass_filter_title">Filter passes</string>
<string name="pass_filter_elev">Minimal elevation</string>
<string name="pass_filter_hours">Hours ahead</string>
<string name="pass_filter_deep_space">DeepSpace (period >225min)</string>
<string name="pass_time_placeholder" translatable="false"> -- : -- : -- </string>
<string name="pass_modes_title">Select modulation type</string>
<string name="pass_satId" translatable="false">%05d</string>