mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-05 12:07:25 +00:00
Cleaned up the code in PassPredictor class
This commit is contained in:
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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user