Fully extracted satellite data calculation to Predictor class

This commit is contained in:
Arty Bishop committed 2021-09-18 12:41:41 +01:00
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,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)
}
@@ -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
}
}
@@ -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()
}