Cleaned up the code in PassPredictor class

This commit is contained in:
Arty Bishop committed 2021-02-27 14:47:26 +00:00
1 parent 7be9965352
commit bfeeff5372
3 files changed
+96 -197

No files matched your search

@@ -21,12 +21,13 @@ package com.rtbishop.look4sat
import android.net.Uri import android.net.Uri
import androidx.lifecycle.* import androidx.lifecycle.*
import com.github.amsacode.predict4java.SatelliteFactory
import com.rtbishop.look4sat.data.* import com.rtbishop.look4sat.data.*
import com.rtbishop.look4sat.repo.EntriesRepo import com.rtbishop.look4sat.repo.EntriesRepo
import com.rtbishop.look4sat.repo.SourcesRepo import com.rtbishop.look4sat.repo.SourcesRepo
import com.rtbishop.look4sat.repo.TransmittersRepo import com.rtbishop.look4sat.repo.TransmittersRepo
import com.rtbishop.look4sat.utility.PassPredictor
import com.rtbishop.look4sat.utility.PrefsManager import com.rtbishop.look4sat.utility.PrefsManager
import com.rtbishop.look4sat.utility.getPredictor
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@@ -128,7 +129,7 @@ class SharedViewModel @Inject constructor(
private fun getPasses(entry: SatEntry, dateNow: Date): MutableList<SatPass> { private fun getPasses(entry: SatEntry, dateNow: Date): MutableList<SatPass> {
val gsp = prefsManager.getStationPosition() val gsp = prefsManager.getStationPosition()
val predictor = PassPredictor(entry.tle, gsp) val predictor = SatelliteFactory.createSatellite(entry.tle).getPredictor(gsp)
val hoursAhead = prefsManager.getPassPrefs().hoursAhead val hoursAhead = prefsManager.getPassPrefs().hoursAhead
val passes = predictor.getPasses(dateNow, hoursAhead, true) val passes = predictor.getPasses(dateNow, hoursAhead, true)
val passList = passes.map { SatPass(entry.tle, predictor, it) } val passList = passes.map { SatPass(entry.tle, predictor, it) }
@@ -19,6 +19,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
package com.rtbishop.look4sat.utility package com.rtbishop.look4sat.utility
import com.github.amsacode.predict4java.GroundStationPosition
import com.github.amsacode.predict4java.Satellite
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
fun Long.formatForTimer(): String { fun Long.formatForTimer(): String {
@@ -34,3 +36,7 @@ fun Double.round(decimals: Int): Double {
repeat(decimals) { multiplier *= 10 } repeat(decimals) { multiplier *= 10 }
return kotlin.math.round(this * multiplier) / multiplier return kotlin.math.round(this * multiplier) / multiplier
} }
fun Satellite.getPredictor(stationPosition: GroundStationPosition): PassPredictor {
return PassPredictor(this, stationPosition)
}
@@ -1,249 +1,141 @@
/**
* predict4java: An SDP4 / SGP4 library for satellite orbit predictions
*
* Copyright (C) 2004-2010 David A. B. Johnson, G4DPZ.
*
* This class is a Java port of one of the core elements of
* the Predict program, Copyright John A. Magliacane,
* KD2BD 1991-2003: http://www.qsl.net/kd2bd/predict.html
*
* Dr. T.S. Kelso is the author of the SGP4/SDP4 orbital models,
* originally written in Fortran and Pascal, and released into the
* public domain through his website (http://www.celestrak.com/).
* Neoklis Kyriazis, 5B4AZ, later re-wrote Dr. Kelso's code in C,
* and released it under the GNU GPL in 2002.
* PREDICT's core is based on 5B4AZ's code translation efforts.
*
* Author: David A. B. Johnson, G4DPZ <dave></dave>@g4dpz.me.uk>
*
* Comments, questions and bugreports should be submitted via
* http://sourceforge.net/projects/websat/
* More details can be found at the project home page:
*
* http://websat.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, visit http://www.fsf.org/
*/
package com.rtbishop.look4sat.utility package com.rtbishop.look4sat.utility
import com.github.amsacode.predict4java.* import com.github.amsacode.predict4java.GroundStationPosition
import com.github.amsacode.predict4java.SatPassTime
import com.github.amsacode.predict4java.SatPos
import com.github.amsacode.predict4java.Satellite
import java.util.* import java.util.*
class PassPredictor(private val tle: TLE, private val qth: GroundStationPosition) { class PassPredictor(private val satellite: Satellite, private val qth: GroundStationPosition) {
private val sat: Satellite = SatelliteFactory.createSatellite(tle) private val oneQuarterOrbitMin = (24.0 * 60.0 / satellite.tle.meanmo / 4.0).toInt()
private val speedOfLight = 2.99792458E8 private val speedOfLight = 2.99792458E8
private val timeZone: TimeZone = TimeZone.getTimeZone("UTC") private val polePassed = "none"
private var iterationCount = 0
fun getDownlinkFreq(freq: Long, date: Date): Long { fun getDownlinkFreq(freq: Long, date: Date): Long {
val rangeRate = getSatPos(date).rangeRate val rangeRate = getSatPos(date).rangeRate
return (freq.toDouble() * (speedOfLight - rangeRate * 1000.0) / speedOfLight).toLong() return (freq.toDouble() * (speedOfLight - rangeRate * 1000.0) / speedOfLight).toLong()
} }
fun getUplinkFreq(freq: Long, date: Date): Long { fun getUplinkFreq(freq: Long, date: Date): Long {
val rangeRate = getSatPos(date).rangeRate val rangeRate = getSatPos(date).rangeRate
return (freq.toDouble() * (speedOfLight + rangeRate * 1000.0) / speedOfLight).toLong() return (freq.toDouble() * (speedOfLight + rangeRate * 1000.0) / speedOfLight).toLong()
} }
fun getSatPos(time: Date): SatPos { fun getSatPos(date: Date): SatPos {
iterationCount++ return satellite.getPosition(qth, date)
return sat.getPosition(qth, time)
} }
fun getPositions( fun getPositions(refDate: Date, stepSeconds: Int, minBefore: Int, minAfter: Int): List<SatPos> {
referenceDate: Date, val positions = mutableListOf<SatPos>()
incrementSeconds: Int, val endDate = Date(refDate.time + minAfter * 60L * 1000L)
minutesBefore: Int, val startDate = Date(refDate.time - minBefore * 60L * 1000L)
minutesAfter: Int var currentDate = startDate
): List<SatPos> {
val positions: MutableList<SatPos> = ArrayList() while (currentDate.before(endDate)) {
val endDateDate = Date(referenceDate.time + minutesAfter * 60L * 1000L) positions.add(getSatPos(currentDate))
var trackDate = Date(referenceDate.time - minutesBefore * 60L * 1000L) currentDate = Date(currentDate.time + stepSeconds * 1000)
while (trackDate.before(endDateDate)) {
positions.add(getSatPos(trackDate))
trackDate = Date(trackDate.time + incrementSeconds * 1000)
} }
return positions return positions
} }
fun getPasses(start: Date, hoursAhead: Int, windBack: Boolean): List<SatPassTime> { fun getPasses(refDate: Date, hoursAhead: Int, windBack: Boolean): List<SatPassTime> {
iterationCount = 0 val passes = mutableListOf<SatPassTime>()
val endDate = Date(refDate.time + hoursAhead * 60L * 60L * 1000L)
val satellite = SatelliteFactory.createSatellite(tle) var startDate = refDate
val passes: MutableList<SatPassTime> = ArrayList() var shouldWindBack = windBack
val trackEndDate = Date(start.time + hoursAhead * 60L * 60L * 1000L) var lastAosDate: Date
var trackStartDate = start
var windBackTime = windBack
var lastAOS: Date
var count = 0 var count = 0
if (satellite.willBeSeen(qth)) { if (satellite.willBeSeen(qth)) {
if (tle.isDeepspace) passes.add(nextGeoSatPass(start)) if (satellite.tle.isDeepspace) {
else { passes.add(nextDeepSpacePass(refDate))
} else {
do { do {
if (count > 0) windBackTime = false if (count > 0) shouldWindBack = false
val pass = nextSatPass(trackStartDate, windBackTime) val pass = nextNearEarthPass(startDate, shouldWindBack)
lastAOS = pass.startTime lastAosDate = pass.startTime
passes.add(pass) passes.add(pass)
trackStartDate = startDate = Date(pass.endTime.time + (oneQuarterOrbitMin * 3) * 60L * 1000L)
Date(pass.endTime.time + threeQuarterOrbitMinutes() * 60L * 1000L)
count++ count++
} while (lastAOS < trackEndDate) } while (lastAosDate < endDate)
} }
} }
return passes return passes
} }
private fun nextGeoSatPass(date: Date): SatPassTime { private fun nextDeepSpacePass(refDate: Date): SatPassTime {
val aosAzimuth: Int val satPos = getSatPos(refDate)
val losAzimuth: Int val startDate = Date(refDate.time - 24 * 60L * 60L * 1000L)
val tca: Date val endDate = Date(refDate.time + 24 * 60L * 60L * 1000L)
val polePassed: String val aosAzimuth = (satPos.azimuth / (2.0 * Math.PI) * 360.0).toInt()
val cal = Calendar.getInstance(timeZone).apply { val losAzimuth = (satPos.azimuth / (2.0 * Math.PI) * 360.0).toInt()
clear() val maxEl = satPos.elevation / (2.0 * Math.PI) * 360.0
timeInMillis = date.time return SatPassTime(startDate, endDate, polePassed, aosAzimuth, losAzimuth, maxEl)
}
val satPos = getSatPos(cal.time)
aosAzimuth = (satPos.azimuth / (2.0 * Math.PI) * 360.0).toInt()
losAzimuth = (satPos.azimuth / (2.0 * Math.PI) * 360.0).toInt()
tca = satPos.time
polePassed = getPolePassed(satPos, satPos)
cal.add(Calendar.HOUR, -12)
val startDate = cal.time
cal.add(Calendar.HOUR, 24)
val endDate = cal.time
return SatPassTime(
startDate, endDate, tca, polePassed, aosAzimuth,
losAzimuth, satPos.elevation / (2.0 * Math.PI) * 360.0
)
} }
private fun nextSatPass(date: Date, windBack: Boolean = false): SatPassTime { private fun nextNearEarthPass(refDate: Date, windBack: Boolean = false): SatPassTime {
val aosAzimuth: Int val calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")).apply {
val losAzimuth: Int clear()
timeInMillis = refDate.time
}
var maxElevation = 0.0 var maxElevation = 0.0
var elevation: Double var elevation: Double
var prevPos: SatPos
var polePassed = "none"
// get the current position
val cal = Calendar.getInstance(timeZone).apply {
clear()
timeInMillis = date.time
}
// wind back time 1/4 of an orbit // wind back time 1/4 of an orbit
if (windBack) { if (windBack) calendar.add(Calendar.MINUTE, -oneQuarterOrbitMin)
val meanMotion = tle.meanmo var satPos = getSatPos(calendar.time)
cal.add(Calendar.MINUTE, (-24.0 * 60.0 / meanMotion / 4.0).toInt())
}
var satPos = getSatPos(cal.time)
// test for the elevation being above the horizon // test for the elevation being above the horizon
if (satPos.elevation > 0.0) { if (satPos.elevation > 0.0) {
// move time forward in 30 second intervals until the sat goes below the horizon // move time forward in 30 second intervals until the sat goes below the horizon
do { do {
satPos = getPosition(cal, 60) calendar.add(Calendar.SECOND, 30)
satPos = getSatPos(calendar.time)
} while (satPos.elevation > 0.0) } while (satPos.elevation > 0.0)
// move time forward 3/4 orbit // move time forward 3/4 orbit
cal.add(Calendar.MINUTE, threeQuarterOrbitMinutes()) calendar.add(Calendar.MINUTE, oneQuarterOrbitMin * 3)
} }
// now find the next time it comes above the horizon // find the next time it comes above the horizon
do { do {
satPos = getPosition(cal, 60) calendar.add(Calendar.SECOND, 60)
satPos = getSatPos(calendar.time)
elevation = satPos.elevation elevation = satPos.elevation
if (elevation > maxElevation) { if (elevation > maxElevation) maxElevation = elevation
maxElevation = elevation
}
} while (satPos.elevation < 0.0) } while (satPos.elevation < 0.0)
// refine it to 5 seconds // refine it to 3 seconds
cal.add(Calendar.SECOND, -60) calendar.add(Calendar.SECOND, -60)
do { do {
satPos = getPosition(cal, 5) calendar.add(Calendar.SECOND, 3)
satPos = getSatPos(calendar.time)
elevation = satPos.elevation elevation = satPos.elevation
if (elevation > maxElevation) { if (elevation > maxElevation) maxElevation = elevation
maxElevation = elevation
}
prevPos = satPos
} while (satPos.elevation < 0.0) } while (satPos.elevation < 0.0)
val startDate = satPos.time val startDate = satPos.time
aosAzimuth = (satPos.azimuth / (2.0 * Math.PI) * 360.0).toInt() val aosAzimuth = (satPos.azimuth / (2.0 * Math.PI) * 360.0).toInt()
// now find when it goes below // now find when it goes below
do { do {
satPos = getPosition(cal, 30) calendar.add(Calendar.SECOND, 30)
val currPolePassed = getPolePassed(prevPos, satPos) satPos = getSatPos(calendar.time)
if (currPolePassed != "none") {
polePassed = currPolePassed
}
elevation = satPos.elevation elevation = satPos.elevation
if (elevation > maxElevation) { if (elevation > maxElevation) maxElevation = elevation
maxElevation = elevation
}
prevPos = satPos
} while (satPos.elevation > 0.0) } while (satPos.elevation > 0.0)
// refine it to 5 seconds // refine it to 3 seconds
cal.add(Calendar.SECOND, -30) calendar.add(Calendar.SECOND, -30)
do { do {
satPos = getPosition(cal, 5) calendar.add(Calendar.SECOND, 3)
satPos = getSatPos(calendar.time)
elevation = satPos.elevation elevation = satPos.elevation
if (elevation > maxElevation) { if (elevation > maxElevation) maxElevation = elevation
maxElevation = elevation
}
} while (satPos.elevation > 0.0) } while (satPos.elevation > 0.0)
val endDate = satPos.time val endDate = satPos.time
losAzimuth = (satPos.azimuth / (2.0 * Math.PI) * 360.0).toInt() val losAzimuth = (satPos.azimuth / (2.0 * Math.PI) * 360.0).toInt()
val tca = Date(startDate.time + (endDate.time - startDate.time) / 2) val maxEl = maxElevation / (2.0 * Math.PI) * 360.0
return SatPassTime(startDate, endDate, polePassed, aosAzimuth, losAzimuth, maxEl)
return SatPassTime(
startDate, endDate, tca, polePassed, aosAzimuth,
losAzimuth, maxElevation / (2.0 * Math.PI) * 360.0
)
}
private fun getPosition(cal: Calendar, offSet: Int): SatPos {
cal.add(Calendar.SECOND, offSet)
return getSatPos(cal.time)
}
private fun getPolePassed(prevPos: SatPos, satPos: SatPos): String {
var polePassed = "none"
val north = "north"
val south = "south"
val az1 = Math.toDegrees(prevPos.azimuth)
val az2 = Math.toDegrees(satPos.azimuth)
if (az1 > az2) {
if (az1 > 350 && az2 < 10) polePassed = north
else if (az1 > 180 && az2 < 180) polePassed = south
} else {
if (az1 < 10 && az2 > 350) polePassed = north
else if (az1 < 180 && az2 > 180) polePassed = south
}
return polePassed
}
private fun threeQuarterOrbitMinutes(): Int {
return (24.0 * 60.0 / tle.meanmo * 0.75).toInt()
} }
} }