Separated satellite map logic from the passes screen

pull/49/head
Arty Bishop 2021-03-21 19:53:59 +00:00
rodzic 332b6f666c
commit f04e54d59b
3 zmienionych plików z 77 dodań i 90 usunięć

Wyświetl plik

@ -18,10 +18,11 @@
package com.rtbishop.look4sat.data.model
import com.github.amsacode.predict4java.Position
import com.github.amsacode.predict4java.Satellite
import org.osmdroid.views.overlay.Overlay
class SelectedSat(
val pass: SatPass,
val pass: Satellite,
val catNum: Int,
val name: String,
val range: Double,

Wyświetl plik

@ -26,9 +26,8 @@ import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import com.github.amsacode.predict4java.Position
import com.github.amsacode.predict4java.Satellite
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.data.model.Result
import com.rtbishop.look4sat.data.model.SatPass
import com.rtbishop.look4sat.data.model.SelectedSat
import com.rtbishop.look4sat.data.repository.PrefsRepo
import com.rtbishop.look4sat.databinding.FragmentMapBinding
@ -77,18 +76,13 @@ class MapFragment : Fragment(R.layout.fragment_map) {
overlays.addAll(Array(4) { FolderOverlay() })
}
}
binding.fabPrev.setOnClickListener { mapViewModel.scrollSelection(true) }
binding.fabNext.setOnClickListener { mapViewModel.scrollSelection(false) }
setupObservers()
}
private fun setupObservers() {
mapViewModel.getGSP().observe(viewLifecycleOwner, { setupPosOverlay(it) })
mapViewModel.passes.observe(viewLifecycleOwner, {
if (it is Result.Success && it.data.isNotEmpty()) {
mapViewModel.setPasses(it.data)
binding.fabPrev.setOnClickListener { mapViewModel.scrollSelection(true) }
binding.fabNext.setOnClickListener { mapViewModel.scrollSelection(false) }
}
})
mapViewModel.stationPosition.observe(viewLifecycleOwner, { setupPosOverlay(it) })
mapViewModel.getSelectedSat().observe(viewLifecycleOwner, { setSelectedSatDetails(it) })
mapViewModel.getSatMarkers().observe(viewLifecycleOwner, { setMarkers(it) })
}
@ -122,7 +116,7 @@ class MapFragment : Fragment(R.layout.fragment_map) {
}
}
private fun setMarkers(map: Map<SatPass, Position>) {
private fun setMarkers(map: Map<Satellite, Position>) {
binding.apply {
val markers = FolderOverlay()
map.entries.forEach {
@ -185,7 +179,7 @@ class MapFragment : Fragment(R.layout.fragment_map) {
tintedMatrix.preConcat(negativeMatrix)
return ColorMatrixColorFilter(tintedMatrix)
}
private fun getMinZoom(screenHeight: Int): Double {
val minZoom = MapView.getTileSystem().getLatitudeZoom(maxLat, minLat, screenHeight)
Timber.d("Min zoom level for this screen: $minZoom")

Wyświetl plik

@ -22,31 +22,26 @@ import android.graphics.Paint
import androidx.lifecycle.*
import com.github.amsacode.predict4java.Position
import com.github.amsacode.predict4java.SatPos
import com.rtbishop.look4sat.data.model.SatPass
import com.github.amsacode.predict4java.Satellite
import com.rtbishop.look4sat.data.model.SelectedSat
import com.rtbishop.look4sat.data.repository.PassesRepo
import com.rtbishop.look4sat.data.repository.PrefsRepo
import com.rtbishop.look4sat.utility.QthConverter
import com.rtbishop.look4sat.data.repository.SatelliteRepo
import com.rtbishop.look4sat.utility.*
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.collect
import org.osmdroid.util.GeoPoint
import org.osmdroid.views.MapView
import org.osmdroid.views.overlay.FolderOverlay
import org.osmdroid.views.overlay.Overlay
import org.osmdroid.views.overlay.Polygon
import org.osmdroid.views.overlay.Polyline
import java.util.*
import javax.inject.Inject
import kotlin.math.pow
import kotlin.math.sqrt
@HiltViewModel
class MapViewModel @Inject constructor(
private val prefsRepo: PrefsRepo,
passesRepo: PassesRepo,
prefsRepo: PrefsRepo,
private val satelliteRepo: SatelliteRepo,
private val qthConverter: QthConverter
) : ViewModel() {
@ -64,82 +59,103 @@ class MapViewModel @Inject constructor(
color = Color.parseColor("#26FFE082")
isAntiAlias = true
}
private var filteredPasses = listOf<SatPass>()
private lateinit var selectedPass: SatPass
private val _gsp = MutableLiveData(getStationPosition())
fun getGSP(): LiveData<Position> = _gsp
private val gsp = prefsRepo.getStationPosition()
private var filteredSats = listOf<Satellite>()
private lateinit var selectedSat: Satellite
private val _selectedSat = MutableLiveData<SelectedSat>()
fun getSelectedSat(): LiveData<SelectedSat> = _selectedSat
private val _satMarkers = MutableLiveData<Map<SatPass, Position>>()
fun getSatMarkers(): LiveData<Map<SatPass, Position>> = _satMarkers
private val _satMarkers = MutableLiveData<Map<Satellite, Position>>()
fun getSatMarkers(): LiveData<Map<Satellite, Position>> = _satMarkers
val passes = passesRepo.passes.asLiveData()
val stationPosition = liveData {
emit(Position(gsp.latitude.toOsmLat(), gsp.longitude.toOsmLon()))
}
fun setPasses(passesList: List<SatPass>) {
filteredPasses = passesList.distinctBy { it.tle }
selectedPass = filteredPasses[0]
init {
viewModelScope.launch {
while (true) {
dateNow.time = System.currentTimeMillis()
_selectedSat.value = getDataForSelSatellite(selectedPass)
_satMarkers.value = getDataForAllSatellites(filteredPasses)
delay(2000)
satelliteRepo.getSelectedSatellitesFlow().collect { satellites ->
if (satellites.isNotEmpty()) {
filteredSats = satellites
selectedSat = satellites.first()
try {
while (true) {
ensureActive()
dateNow.time = System.currentTimeMillis()
_selectedSat.value = getDataForSelSatellite(selectedSat)
_satMarkers.value = getDataForAllSatellites(filteredSats)
delay(2000)
}
} catch (e: Exception) {
}
}
}
}
}
fun scrollSelection(decrement: Boolean) {
val index = filteredPasses.indexOf(selectedPass)
if (decrement) {
if (index > 0) selectSatellite(filteredPasses[index - 1])
else selectSatellite(filteredPasses[filteredPasses.size - 1])
} else {
if (index < filteredPasses.size - 1) selectSatellite(filteredPasses[index + 1])
else selectSatellite(filteredPasses[0])
if (filteredSats.isNotEmpty()) {
val index = filteredSats.indexOf(selectedSat)
if (decrement) {
if (index > 0) selectSatellite(filteredSats[index - 1])
else selectSatellite(filteredSats[filteredSats.size - 1])
} else {
if (index < filteredSats.size - 1) selectSatellite(filteredSats[index + 1])
else selectSatellite(filteredSats[0])
}
}
}
fun selectSatellite(satPass: SatPass) {
selectedPass = satPass
viewModelScope.launch { _selectedSat.value = getDataForSelSatellite(selectedPass) }
fun selectSatellite(satellite: Satellite) {
selectedSat = satellite
viewModelScope.launch { _selectedSat.value = getDataForSelSatellite(selectedSat) }
}
private suspend fun getDataForSelSatellite(pass: SatPass): SelectedSat =
private suspend fun getDataForSelSatellite(satellite: Satellite): SelectedSat =
withContext(Dispatchers.Default) {
val satPos = pass.predictor.getSatPos(dateNow)
val osmPos = getOsmPosition(satPos.latitude, satPos.longitude, true)
val satPos = satellite.getPredictor(gsp).getSatPos(dateNow)
val osmPos = Position(
Math.toDegrees(satPos.latitude).toOsmLat(),
Math.toDegrees(satPos.longitude).toOsmLon()
)
val qthLoc = qthConverter.locationToQTH(osmPos.lat, osmPos.lon) ?: "-- --"
val velocity = getSatVelocity(satPos.altitude)
val velocity = satPos.altitude.getOrbitalVelocity()
val coverage = satPos.rangeCircleRadiusKm * 2
val footprint = getSatFootprint(satPos)
val track = getSatTrack(pass)
val track = getSatTrack(satellite)
return@withContext SelectedSat(
pass, pass.tle.catnum, pass.tle.name, satPos.range,
satellite, satellite.tle.catnum, satellite.tle.name, satPos.range,
satPos.altitude, velocity, qthLoc, osmPos, coverage, footprint, track
)
}
private suspend fun getDataForAllSatellites(passes: List<SatPass>): Map<SatPass, Position> =
private suspend fun getDataForAllSatellites(satellites: List<Satellite>): Map<Satellite, Position> =
withContext(Dispatchers.Default) {
val passesMap = mutableMapOf<SatPass, Position>()
passes.forEach {
val satPos = it.predictor.getSatPos(dateNow)
passesMap[it] = getOsmPosition(satPos.latitude, satPos.longitude, true)
val passesMap = mutableMapOf<Satellite, Position>()
satellites.forEach { satellite ->
val satPos = satellite.getPredictor(gsp).getSatPos(dateNow)
passesMap[satellite] = Position(
Math.toDegrees(satPos.latitude).toOsmLat(),
Math.toDegrees(satPos.longitude).toOsmLon()
)
}
return@withContext passesMap
}
private fun getSatTrack(pass: SatPass): Overlay {
val period = (24 * 60 / pass.tle.meanmo).toInt()
val positions = pass.predictor.getPositions(dateNow, 20, 0, period * 3)
private fun getSatTrack(satellite: Satellite): Overlay {
val positions = satellite.getPredictor(gsp).getPositions(dateNow, 20, 5, 2)
val trackOverlay = FolderOverlay()
val trackPoints = mutableListOf<GeoPoint>()
var oldLon = 0.0
positions.forEach {
val osmPos = getOsmPosition(it.latitude, it.longitude, true)
val osmPos = Position(
Math.toDegrees(it.latitude).toOsmLat(),
Math.toDegrees(it.longitude).toOsmLon()
)
if (oldLon < -170.0 && osmPos.lon > 170.0 || oldLon > 170.0 && osmPos.lon < -170.0) {
val currentPoints = mutableListOf<GeoPoint>()
currentPoints.addAll(trackPoints)
@ -164,7 +180,7 @@ class MapViewModel @Inject constructor(
private fun getSatFootprint(satPos: SatPos): Overlay {
val rangePoints = mutableListOf<GeoPoint>()
satPos.rangeCircle.forEach {
val osmPos = getOsmPosition(it.lat, it.lon, false)
val osmPos = Position(it.lat.toOsmLat(), it.lon.toOsmLon())
rangePoints.add(GeoPoint(osmPos.lat, osmPos.lon))
}
return Polygon().apply {
@ -173,28 +189,4 @@ class MapViewModel @Inject constructor(
points = rangePoints
}
}
private fun getStationPosition(): Position {
val stationPosition = prefsRepo.getStationPosition()
return getOsmPosition(stationPosition.latitude, stationPosition.longitude, false)
}
private fun getSatVelocity(altitude: Double): Double {
val earthG = 6.674 * 10.0.pow(-11)
val earthM = 5.98 * 10.0.pow(24)
val radius = 6.37 * 10.0.pow(6) + altitude * 10.0.pow(3)
return sqrt(earthG * earthM / radius) / 1000
}
private fun getOsmPosition(lat: Double, lon: Double, inRadians: Boolean): Position {
return if (inRadians) {
val osmLat = MapView.getTileSystem().cleanLatitude(Math.toDegrees(lat))
val osmLon = MapView.getTileSystem().cleanLongitude(Math.toDegrees(lon))
Position(osmLat, osmLon)
} else {
val osmLat = MapView.getTileSystem().cleanLatitude(lat)
val osmLon = MapView.getTileSystem().cleanLongitude(lon)
Position(osmLat, osmLon)
}
}
}