Fully extracted satellite data calculation to Predictor class
This commit is contained in:
1 parent
e6d0671816
commit
f295b3b224
8 files changed
+109
-72
No files matched your search
@@ -0,0 +1,5 @@
|
||||
package com.rtbishop.look4sat.presentation.satPassInfoScreen
|
||||
|
||||
import com.rtbishop.look4sat.domain.SatPos
|
||||
|
||||
data class PassData(val position: SatPos, val positions: List<SatPos> = emptyList())
|
||||
+28
-34
@@ -28,11 +28,11 @@ import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.data.PreferencesSource
|
||||
import com.rtbishop.look4sat.databinding.FragmentPolarBinding
|
||||
import com.rtbishop.look4sat.domain.SatPass
|
||||
import com.rtbishop.look4sat.domain.SatPos
|
||||
import com.rtbishop.look4sat.utility.RecyclerDivider
|
||||
import com.rtbishop.look4sat.utility.navigateSafe
|
||||
import com.rtbishop.look4sat.utility.toTimerString
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
@@ -69,49 +69,43 @@ class PassInfoFragment : Fragment(R.layout.fragment_polar) {
|
||||
viewModel.disableSensor()
|
||||
}
|
||||
|
||||
private fun setupObservers(satTransAdapter: SatTransAdapter, binding: FragmentPolarBinding) {
|
||||
private fun setupObservers(transAdapter: SatTransAdapter, binding: FragmentPolarBinding) {
|
||||
val catNum = requireArguments().getInt("catNum")
|
||||
val aosTime = requireArguments().getLong("aosTime")
|
||||
val stationPos = preferences.loadStationPosition()
|
||||
viewModel.getPass(catNum, aosTime).observe(viewLifecycleOwner) { pass ->
|
||||
passInfoView = PassInfoView(requireContext()).apply {
|
||||
setShowAim(preferences.shouldUseCompass())
|
||||
setPass(pass)
|
||||
setStationPos(stationPos)
|
||||
setScanning(preferences.shouldShowSweep())
|
||||
}
|
||||
binding.frame.addView(passInfoView)
|
||||
observeTransmitters(pass, satTransAdapter, binding)
|
||||
viewModel.passData.observe(viewLifecycleOwner, { passData ->
|
||||
passInfoView?.setPosition(passData.position)
|
||||
passInfoView?.setPositions(passData.positions)
|
||||
setPassText(pass, passData.position, binding)
|
||||
})
|
||||
viewModel.transmitters.observe(viewLifecycleOwner, { list ->
|
||||
if (list.isNotEmpty()) {
|
||||
transAdapter.submitList(list)
|
||||
binding.recycler.visibility = View.VISIBLE
|
||||
binding.noTransMsg.visibility = View.INVISIBLE
|
||||
} else {
|
||||
binding.recycler.visibility = View.INVISIBLE
|
||||
binding.noTransMsg.visibility = View.VISIBLE
|
||||
}
|
||||
passInfoView?.invalidate()
|
||||
})
|
||||
viewModel.orientation.observe(viewLifecycleOwner, { orientation ->
|
||||
passInfoView?.setOrientation(
|
||||
orientation.first,
|
||||
orientation.second,
|
||||
orientation.third
|
||||
)
|
||||
})
|
||||
}
|
||||
viewModel.orientation.observe(viewLifecycleOwner, { orientation ->
|
||||
passInfoView?.setOrientation(orientation.first, orientation.second, orientation.third)
|
||||
})
|
||||
}
|
||||
|
||||
private fun observeTransmitters(
|
||||
satPass: SatPass,
|
||||
satTransAdapter: SatTransAdapter,
|
||||
binding: FragmentPolarBinding
|
||||
) {
|
||||
viewModel.transmitters.observe(viewLifecycleOwner, { list ->
|
||||
if (list.isNotEmpty()) {
|
||||
satTransAdapter.submitList(list)
|
||||
binding.recycler.visibility = View.VISIBLE
|
||||
binding.noTransMsg.visibility = View.INVISIBLE
|
||||
} else {
|
||||
binding.recycler.visibility = View.INVISIBLE
|
||||
binding.noTransMsg.visibility = View.VISIBLE
|
||||
}
|
||||
setPassText(satPass, binding)
|
||||
passInfoView?.invalidate()
|
||||
})
|
||||
}
|
||||
|
||||
private fun setPassText(satPass: SatPass, binding: FragmentPolarBinding) {
|
||||
val dateNow = Date()
|
||||
private fun setPassText(satPass: SatPass, satPos: SatPos, binding: FragmentPolarBinding) {
|
||||
val timeNow = System.currentTimeMillis()
|
||||
val stationPos = preferences.loadStationPosition()
|
||||
val satPos = satPass.satellite.getPosition(stationPos, dateNow.time)
|
||||
val polarAz = getString(R.string.pat_azimuth)
|
||||
val polarEl = getString(R.string.pat_elevation)
|
||||
val polarRng = getString(R.string.pat_distance)
|
||||
@@ -123,13 +117,13 @@ class PassInfoFragment : Fragment(R.layout.fragment_polar) {
|
||||
binding.satName.text = satPass.name
|
||||
|
||||
if (!satPass.isDeepSpace) {
|
||||
if (dateNow.time < satPass.aosTime) {
|
||||
if (timeNow < satPass.aosTime) {
|
||||
val millisBeforeStart = satPass.aosTime.minus(timeNow)
|
||||
binding.polarTimer.text = millisBeforeStart.toTimerString()
|
||||
} else {
|
||||
val millisBeforeEnd = satPass.losTime.minus(timeNow)
|
||||
binding.polarTimer.text = millisBeforeEnd.toTimerString()
|
||||
if (dateNow.time > satPass.losTime) {
|
||||
if (timeNow > satPass.losTime) {
|
||||
binding.polarTimer.text = 0L.toTimerString()
|
||||
findNavController().navigateSafe(R.id.action_polar_to_passes)
|
||||
}
|
||||
|
||||
+21
-25
@@ -22,23 +22,21 @@ import android.graphics.*
|
||||
import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.domain.GeoPos
|
||||
import com.rtbishop.look4sat.domain.SatPass
|
||||
import java.util.*
|
||||
import com.rtbishop.look4sat.domain.SatPos
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.min
|
||||
import kotlin.math.sin
|
||||
|
||||
class PassInfoView(context: Context) : View(context) {
|
||||
|
||||
private lateinit var satPass: SatPass
|
||||
private val defaultColor = ContextCompat.getColor(context, R.color.themeLight)
|
||||
private val scale = resources.displayMetrics.density
|
||||
private val radarWidth = resources.displayMetrics.widthPixels
|
||||
private val radarRadius = radarWidth * 0.48f
|
||||
private val piDiv2 = Math.PI / 2.0
|
||||
private val strokeSize = scale * 2f
|
||||
private var stationPos: GeoPos = GeoPos(0.0, 0.0, 0.0)
|
||||
private var position: SatPos? = null
|
||||
private var positions: List<SatPos> = emptyList()
|
||||
|
||||
private var radarColor = ContextCompat.getColor(context, R.color.greyLight)
|
||||
private var radarCircleNum = 3
|
||||
@@ -103,12 +101,12 @@ class PassInfoView(context: Context) : View(context) {
|
||||
shouldShowSweep = isScanning
|
||||
}
|
||||
|
||||
fun setPass(satPass: SatPass) {
|
||||
this.satPass = satPass
|
||||
fun setPosition(position: SatPos) {
|
||||
this.position = position
|
||||
}
|
||||
|
||||
fun setStationPos(stationPos: GeoPos) {
|
||||
this.stationPos = stationPos
|
||||
fun setPositions(positions: List<SatPos>) {
|
||||
this.positions = positions
|
||||
}
|
||||
|
||||
fun setShowAim(showAim: Boolean) {
|
||||
@@ -131,8 +129,8 @@ class PassInfoView(context: Context) : View(context) {
|
||||
val cx = paddingLeft + radarWidth / 2f
|
||||
val cy = paddingTop + radarHeight / 2f
|
||||
|
||||
if (!satPass.isDeepSpace && !isTrackCreated) {
|
||||
createPassTrajectory(satPass)
|
||||
if (positions.isNotEmpty() && !isTrackCreated) {
|
||||
createPassTrajectory()
|
||||
createPassTrajectoryArrow()
|
||||
isTrackCreated = true
|
||||
}
|
||||
@@ -143,12 +141,12 @@ class PassInfoView(context: Context) : View(context) {
|
||||
drawRadarText(canvas, cx, radarRadius)
|
||||
|
||||
canvas.translate(cx, cy)
|
||||
if (!satPass.isDeepSpace) {
|
||||
if (positions.isNotEmpty()) {
|
||||
canvas.drawPath(trackPath, trackPaint)
|
||||
canvas.drawPath(trackPath, arrowPaint)
|
||||
}
|
||||
if (shouldShowBeacons) {
|
||||
drawSatellite(canvas, satPass)
|
||||
drawSatellite(canvas)
|
||||
}
|
||||
if (shouldShowAim) {
|
||||
drawCrosshair(canvas, azimuth, pitch)
|
||||
@@ -179,12 +177,13 @@ class PassInfoView(context: Context) : View(context) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun drawSatellite(canvas: Canvas, satPass: SatPass) {
|
||||
val satPos = satPass.satellite.getPosition(stationPos, Date().time)
|
||||
if (satPos.elevation > 0) {
|
||||
val satX = sph2CartX(satPos.azimuth, satPos.elevation, radarRadius.toDouble())
|
||||
val satY = sph2CartY(satPos.azimuth, satPos.elevation, radarRadius.toDouble())
|
||||
canvas.drawCircle(satX, -satY, beaconSize, beaconPaint)
|
||||
private fun drawSatellite(canvas: Canvas) {
|
||||
position?.let { satPos ->
|
||||
if (satPos.elevation > 0) {
|
||||
val satX = sph2CartX(satPos.azimuth, satPos.elevation, radarRadius.toDouble())
|
||||
val satY = sph2CartY(satPos.azimuth, satPos.elevation, radarRadius.toDouble())
|
||||
canvas.drawCircle(satX, -satY, beaconSize, beaconPaint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,18 +208,15 @@ class PassInfoView(context: Context) : View(context) {
|
||||
return (radius * sin(piDiv2 - azimuth)).toFloat()
|
||||
}
|
||||
|
||||
private fun createPassTrajectory(satPass: SatPass) {
|
||||
var currentTime = satPass.aosTime
|
||||
while (currentTime < satPass.losTime) {
|
||||
val satPos = satPass.satellite.getPosition(stationPos, currentTime)
|
||||
private fun createPassTrajectory() {
|
||||
positions.forEachIndexed { index, satPos ->
|
||||
val passX = sph2CartX(satPos.azimuth, satPos.elevation, radarRadius.toDouble())
|
||||
val passY = sph2CartY(satPos.azimuth, satPos.elevation, radarRadius.toDouble())
|
||||
if (currentTime == satPass.aosTime) {
|
||||
if (index == 0) {
|
||||
trackPath.moveTo(passX, -passY)
|
||||
} else {
|
||||
trackPath.lineTo(passX, -passY)
|
||||
}
|
||||
currentTime += 15000
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+25
-1
@@ -18,14 +18,19 @@
|
||||
package com.rtbishop.look4sat.presentation.satPassInfoScreen
|
||||
|
||||
import androidx.lifecycle.*
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.data.PreferencesSource
|
||||
import com.rtbishop.look4sat.data.SatelliteRepo
|
||||
import com.rtbishop.look4sat.domain.Predictor
|
||||
import com.rtbishop.look4sat.domain.SatPass
|
||||
import com.rtbishop.look4sat.domain.SatPos
|
||||
import com.rtbishop.look4sat.domain.Transmitter
|
||||
import com.rtbishop.look4sat.framework.OrientationProvider
|
||||
import com.rtbishop.look4sat.injection.IoDispatcher
|
||||
import com.rtbishop.look4sat.utility.navigateSafe
|
||||
import com.rtbishop.look4sat.utility.round
|
||||
import com.rtbishop.look4sat.utility.toTimerString
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.collect
|
||||
@@ -44,8 +49,10 @@ class PassInfoViewModel @Inject constructor(
|
||||
) : ViewModel(), OrientationProvider.OrientationListener {
|
||||
|
||||
private val stationPos = preferences.loadStationPosition()
|
||||
private val _passData = MutableLiveData<PassData>()
|
||||
private val _transmitters = MutableLiveData<List<Transmitter>>()
|
||||
private val _orientation = MutableLiveData<Triple<Float, Float, Float>>()
|
||||
val passData: LiveData<PassData> = _passData
|
||||
val transmitters: LiveData<List<Transmitter>> = _transmitters
|
||||
val orientation: LiveData<Triple<Float, Float, Float>> = _orientation
|
||||
|
||||
@@ -53,9 +60,10 @@ class PassInfoViewModel @Inject constructor(
|
||||
predictor.passes.collect { passes ->
|
||||
val pass = passes.find { it.catNum == catNum && it.aosTime == aosTime }
|
||||
pass?.let { satPass ->
|
||||
emit(satPass)
|
||||
sendPassData(satPass)
|
||||
processTransmitters(satPass)
|
||||
initRotatorControl(satPass)
|
||||
emit(satPass)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,6 +80,22 @@ class PassInfoViewModel @Inject constructor(
|
||||
_orientation.value = Triple(azimuth + preferences.getMagDeclination(), pitch, roll)
|
||||
}
|
||||
|
||||
private fun sendPassData(satPass: SatPass) {
|
||||
viewModelScope.launch {
|
||||
var track: List<SatPos> = emptyList()
|
||||
if (!satPass.isDeepSpace) {
|
||||
val startDate = Date(satPass.aosTime)
|
||||
val endDate = Date(satPass.losTime)
|
||||
track = predictor.getSatTrack(satPass.satellite, stationPos, startDate, endDate)
|
||||
}
|
||||
while (isActive) {
|
||||
val pos = predictor.getSatPos(satPass.satellite, stationPos, Date())
|
||||
_passData.postValue(PassData(pos, track))
|
||||
delay(1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initRotatorControl(satPass: SatPass) {
|
||||
viewModelScope.launch {
|
||||
val rotatorPrefs = preferences.getRotatorServer()
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.rtbishop.look4sat.utility
|
||||
|
||||
import android.app.Activity
|
||||
|
||||
class CustomHandler(private val activity: Activity) : Thread.UncaughtExceptionHandler {
|
||||
|
||||
private val rootHandler = Thread.getDefaultUncaughtExceptionHandler()
|
||||
|
||||
init {
|
||||
Thread.setDefaultUncaughtExceptionHandler(this)
|
||||
}
|
||||
|
||||
override fun uncaughtException(thread: Thread, throwable: Throwable) {
|
||||
// Custom exception handling, use intents instead of dialogs
|
||||
// Process.killProcess(Process.myPid())
|
||||
// exitProcess(0)
|
||||
}
|
||||
}
|
||||
@@ -90,7 +90,7 @@ class DeepSpaceSat(params: TLE) : Satellite(params) {
|
||||
deep = DeepSpaceCalculator(dsv)
|
||||
}
|
||||
|
||||
fun calculateSDP4(tSince: Double) {
|
||||
internal fun calculateSDP4(tSince: Double) {
|
||||
synchronized(this) {
|
||||
val temp = DoubleArray(12)
|
||||
val xmdf = params.xmo + dsv.xmdot * tSince
|
||||
|
||||
@@ -135,7 +135,7 @@ class NearEarthSat(params: TLE) : Satellite(params) {
|
||||
}
|
||||
}
|
||||
|
||||
fun calculateSGP4(tSince: Double) {
|
||||
internal fun calculateSGP4(tSince: Double) {
|
||||
synchronized(this) {
|
||||
val temp = DoubleArray(9)
|
||||
val xmdf = params.xmo + xmdot * tSince
|
||||
|
||||
@@ -42,7 +42,7 @@ abstract class Satellite(val params: TLE) {
|
||||
var qoms24 = 0.0
|
||||
var s4 = 0.0
|
||||
|
||||
fun willBeSeen(pos: GeoPos): Boolean {
|
||||
internal fun willBeSeen(pos: GeoPos): Boolean {
|
||||
return if (params.meanmo < 1e-8) false
|
||||
else {
|
||||
val sma = 331.25 * exp(ln(1440.0 / params.meanmo) * (2.0 / 3.0))
|
||||
@@ -53,7 +53,7 @@ abstract class Satellite(val params: TLE) {
|
||||
}
|
||||
}
|
||||
|
||||
fun getPosition(pos: GeoPos, time: Long): SatPos {
|
||||
internal fun getPosition(pos: GeoPos, time: Long): SatPos {
|
||||
val satPos = SatPos()
|
||||
// Date/time at which the position and velocity were calculated
|
||||
val julUTC = calcCurrentDaynum(time) + 2444238.5
|
||||
@@ -89,7 +89,7 @@ abstract class Satellite(val params: TLE) {
|
||||
return julianDateOfYear(year) + day
|
||||
}
|
||||
|
||||
fun julianDateOfYear(theYear: Double): Double {
|
||||
internal fun julianDateOfYear(theYear: Double): Double {
|
||||
val aYear = theYear - 1
|
||||
var i = floor(aYear / 100).toLong()
|
||||
val a = i
|
||||
@@ -207,7 +207,7 @@ abstract class Satellite(val params: TLE) {
|
||||
}
|
||||
}
|
||||
|
||||
fun calculatePosAndVel(
|
||||
internal fun calculatePosAndVel(
|
||||
rk: Double, uk: Double, xnodek: Double,
|
||||
xinck: Double, rdotk: Double, rfdotk: Double
|
||||
) {
|
||||
@@ -234,7 +234,7 @@ abstract class Satellite(val params: TLE) {
|
||||
velocity.z = rdotk * uz + rfdotk * vz
|
||||
}
|
||||
|
||||
class Vector4 {
|
||||
internal class Vector4 {
|
||||
var w = 0.0
|
||||
var x = 0.0
|
||||
var y = 0.0
|
||||
@@ -253,16 +253,16 @@ abstract class Satellite(val params: TLE) {
|
||||
}
|
||||
}
|
||||
|
||||
fun sqr(arg: Double): Double {
|
||||
internal fun sqr(arg: Double): Double {
|
||||
return arg * arg
|
||||
}
|
||||
|
||||
fun invert(value: Double): Double {
|
||||
internal fun invert(value: Double): Double {
|
||||
return 1.0 / value
|
||||
}
|
||||
|
||||
// Calculates the modulus of 2 * PI
|
||||
fun mod2PI(value: Double): Double {
|
||||
internal fun mod2PI(value: Double): Double {
|
||||
var retVal = value
|
||||
val i = (retVal / twoPi).toInt()
|
||||
retVal -= i * twoPi
|
||||
@@ -271,7 +271,7 @@ abstract class Satellite(val params: TLE) {
|
||||
}
|
||||
|
||||
// Solves Keplers' Equation
|
||||
fun converge(temp: DoubleArray, axn: Double, ayn: Double, capu: Double) {
|
||||
internal fun converge(temp: DoubleArray, axn: Double, ayn: Double, capu: Double) {
|
||||
var converged = false
|
||||
var i = 0
|
||||
do {
|
||||
@@ -287,7 +287,7 @@ abstract class Satellite(val params: TLE) {
|
||||
}
|
||||
|
||||
// Sets perigee and checks and adjusts the calculation if the perigee is less tan 156KM
|
||||
fun setPerigee(perigee: Double) {
|
||||
internal fun setPerigee(perigee: Double) {
|
||||
this.perigee = perigee
|
||||
checkPerigee()
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user