From bfeeff5372098e509d4bb3712f8f1206710e02c8 Mon Sep 17 00:00:00 2001 From: Arty Bishop Date: Sat, 27 Feb 2021 14:47:26 +0000 Subject: [PATCH] Cleaned up the code in PassPredictor class --- .../com/rtbishop/look4sat/SharedViewModel.kt | 5 +- .../rtbishop/look4sat/utility/Extensions.kt | 6 + .../look4sat/utility/PassPredictor.kt | 282 ++++++------------ 3 files changed, 96 insertions(+), 197 deletions(-) diff --git a/app/src/main/java/com/rtbishop/look4sat/SharedViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/SharedViewModel.kt index e6e77c8f..158ae3da 100644 --- a/app/src/main/java/com/rtbishop/look4sat/SharedViewModel.kt +++ b/app/src/main/java/com/rtbishop/look4sat/SharedViewModel.kt @@ -21,12 +21,13 @@ package com.rtbishop.look4sat import android.net.Uri import androidx.lifecycle.* +import com.github.amsacode.predict4java.SatelliteFactory import com.rtbishop.look4sat.data.* import com.rtbishop.look4sat.repo.EntriesRepo import com.rtbishop.look4sat.repo.SourcesRepo import com.rtbishop.look4sat.repo.TransmittersRepo -import com.rtbishop.look4sat.utility.PassPredictor import com.rtbishop.look4sat.utility.PrefsManager +import com.rtbishop.look4sat.utility.getPredictor import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay @@ -128,7 +129,7 @@ class SharedViewModel @Inject constructor( private fun getPasses(entry: SatEntry, dateNow: Date): MutableList { val gsp = prefsManager.getStationPosition() - val predictor = PassPredictor(entry.tle, gsp) + val predictor = SatelliteFactory.createSatellite(entry.tle).getPredictor(gsp) val hoursAhead = prefsManager.getPassPrefs().hoursAhead val passes = predictor.getPasses(dateNow, hoursAhead, true) val passList = passes.map { SatPass(entry.tle, predictor, it) } diff --git a/app/src/main/java/com/rtbishop/look4sat/utility/Extensions.kt b/app/src/main/java/com/rtbishop/look4sat/utility/Extensions.kt index fa728247..7709d3a0 100644 --- a/app/src/main/java/com/rtbishop/look4sat/utility/Extensions.kt +++ b/app/src/main/java/com/rtbishop/look4sat/utility/Extensions.kt @@ -19,6 +19,8 @@ with this program; if not, write to the Free Software Foundation, Inc., package com.rtbishop.look4sat.utility +import com.github.amsacode.predict4java.GroundStationPosition +import com.github.amsacode.predict4java.Satellite import java.util.concurrent.TimeUnit fun Long.formatForTimer(): String { @@ -34,3 +36,7 @@ fun Double.round(decimals: Int): Double { repeat(decimals) { multiplier *= 10 } return kotlin.math.round(this * multiplier) / multiplier } + +fun Satellite.getPredictor(stationPosition: GroundStationPosition): PassPredictor { + return PassPredictor(this, stationPosition) +} diff --git a/app/src/main/java/com/rtbishop/look4sat/utility/PassPredictor.kt b/app/src/main/java/com/rtbishop/look4sat/utility/PassPredictor.kt index b8a800ff..6442ab95 100644 --- a/app/src/main/java/com/rtbishop/look4sat/utility/PassPredictor.kt +++ b/app/src/main/java/com/rtbishop/look4sat/utility/PassPredictor.kt @@ -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 @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 -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.* -class PassPredictor(private val tle: TLE, private val qth: GroundStationPosition) { - private val sat: Satellite = SatelliteFactory.createSatellite(tle) +class PassPredictor(private val satellite: Satellite, private val qth: GroundStationPosition) { + private val oneQuarterOrbitMin = (24.0 * 60.0 / satellite.tle.meanmo / 4.0).toInt() private val speedOfLight = 2.99792458E8 - private val timeZone: TimeZone = TimeZone.getTimeZone("UTC") - private var iterationCount = 0 - + private val polePassed = "none" + fun getDownlinkFreq(freq: Long, date: Date): Long { val rangeRate = getSatPos(date).rangeRate return (freq.toDouble() * (speedOfLight - rangeRate * 1000.0) / speedOfLight).toLong() } - + fun getUplinkFreq(freq: Long, date: Date): Long { val rangeRate = getSatPos(date).rangeRate return (freq.toDouble() * (speedOfLight + rangeRate * 1000.0) / speedOfLight).toLong() } - - fun getSatPos(time: Date): SatPos { - iterationCount++ - return sat.getPosition(qth, time) + + fun getSatPos(date: Date): SatPos { + return satellite.getPosition(qth, date) } - - fun getPositions( - referenceDate: Date, - incrementSeconds: Int, - minutesBefore: Int, - minutesAfter: Int - ): List { - val positions: MutableList = ArrayList() - val endDateDate = Date(referenceDate.time + minutesAfter * 60L * 1000L) - var trackDate = Date(referenceDate.time - minutesBefore * 60L * 1000L) - - while (trackDate.before(endDateDate)) { - positions.add(getSatPos(trackDate)) - trackDate = Date(trackDate.time + incrementSeconds * 1000) + + fun getPositions(refDate: Date, stepSeconds: Int, minBefore: Int, minAfter: Int): List { + val positions = mutableListOf() + val endDate = Date(refDate.time + minAfter * 60L * 1000L) + val startDate = Date(refDate.time - minBefore * 60L * 1000L) + var currentDate = startDate + + while (currentDate.before(endDate)) { + positions.add(getSatPos(currentDate)) + currentDate = Date(currentDate.time + stepSeconds * 1000) } - return positions } - - fun getPasses(start: Date, hoursAhead: Int, windBack: Boolean): List { - iterationCount = 0 - - val satellite = SatelliteFactory.createSatellite(tle) - val passes: MutableList = ArrayList() - val trackEndDate = Date(start.time + hoursAhead * 60L * 60L * 1000L) - var trackStartDate = start - var windBackTime = windBack - var lastAOS: Date + + fun getPasses(refDate: Date, hoursAhead: Int, windBack: Boolean): List { + val passes = mutableListOf() + val endDate = Date(refDate.time + hoursAhead * 60L * 60L * 1000L) + var startDate = refDate + var shouldWindBack = windBack + var lastAosDate: Date var count = 0 - + if (satellite.willBeSeen(qth)) { - if (tle.isDeepspace) passes.add(nextGeoSatPass(start)) - else { + if (satellite.tle.isDeepspace) { + passes.add(nextDeepSpacePass(refDate)) + } else { do { - if (count > 0) windBackTime = false - val pass = nextSatPass(trackStartDate, windBackTime) - lastAOS = pass.startTime + if (count > 0) shouldWindBack = false + val pass = nextNearEarthPass(startDate, shouldWindBack) + lastAosDate = pass.startTime passes.add(pass) - trackStartDate = - Date(pass.endTime.time + threeQuarterOrbitMinutes() * 60L * 1000L) + startDate = Date(pass.endTime.time + (oneQuarterOrbitMin * 3) * 60L * 1000L) count++ - } while (lastAOS < trackEndDate) + } while (lastAosDate < endDate) } } - return passes } - - private fun nextGeoSatPass(date: Date): SatPassTime { - val aosAzimuth: Int - val losAzimuth: Int - val tca: Date - val polePassed: String - val cal = Calendar.getInstance(timeZone).apply { - clear() - timeInMillis = date.time - } - 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 nextDeepSpacePass(refDate: Date): SatPassTime { + val satPos = getSatPos(refDate) + val startDate = Date(refDate.time - 24 * 60L * 60L * 1000L) + val endDate = Date(refDate.time + 24 * 60L * 60L * 1000L) + val aosAzimuth = (satPos.azimuth / (2.0 * Math.PI) * 360.0).toInt() + val losAzimuth = (satPos.azimuth / (2.0 * Math.PI) * 360.0).toInt() + val maxEl = satPos.elevation / (2.0 * Math.PI) * 360.0 + return SatPassTime(startDate, endDate, polePassed, aosAzimuth, losAzimuth, maxEl) } - - private fun nextSatPass(date: Date, windBack: Boolean = false): SatPassTime { - val aosAzimuth: Int - val losAzimuth: Int + + private fun nextNearEarthPass(refDate: Date, windBack: Boolean = false): SatPassTime { + val calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")).apply { + clear() + timeInMillis = refDate.time + } var maxElevation = 0.0 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 - if (windBack) { - val meanMotion = tle.meanmo - cal.add(Calendar.MINUTE, (-24.0 * 60.0 / meanMotion / 4.0).toInt()) - } - var satPos = getSatPos(cal.time) - + if (windBack) calendar.add(Calendar.MINUTE, -oneQuarterOrbitMin) + var satPos = getSatPos(calendar.time) + // test for the elevation being above the horizon if (satPos.elevation > 0.0) { // move time forward in 30 second intervals until the sat goes below the horizon do { - satPos = getPosition(cal, 60) + calendar.add(Calendar.SECOND, 30) + satPos = getSatPos(calendar.time) } while (satPos.elevation > 0.0) // 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 { - satPos = getPosition(cal, 60) + calendar.add(Calendar.SECOND, 60) + satPos = getSatPos(calendar.time) elevation = satPos.elevation - if (elevation > maxElevation) { - maxElevation = elevation - } + if (elevation > maxElevation) maxElevation = elevation } while (satPos.elevation < 0.0) - - // refine it to 5 seconds - cal.add(Calendar.SECOND, -60) + + // refine it to 3 seconds + calendar.add(Calendar.SECOND, -60) do { - satPos = getPosition(cal, 5) + calendar.add(Calendar.SECOND, 3) + satPos = getSatPos(calendar.time) elevation = satPos.elevation - if (elevation > maxElevation) { - maxElevation = elevation - } - prevPos = satPos + if (elevation > maxElevation) maxElevation = elevation } while (satPos.elevation < 0.0) + 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 do { - satPos = getPosition(cal, 30) - val currPolePassed = getPolePassed(prevPos, satPos) - if (currPolePassed != "none") { - polePassed = currPolePassed - } + calendar.add(Calendar.SECOND, 30) + satPos = getSatPos(calendar.time) elevation = satPos.elevation - if (elevation > maxElevation) { - maxElevation = elevation - } - prevPos = satPos + if (elevation > maxElevation) maxElevation = elevation } while (satPos.elevation > 0.0) - - // refine it to 5 seconds - cal.add(Calendar.SECOND, -30) + + // refine it to 3 seconds + calendar.add(Calendar.SECOND, -30) do { - satPos = getPosition(cal, 5) + calendar.add(Calendar.SECOND, 3) + satPos = getSatPos(calendar.time) elevation = satPos.elevation - if (elevation > maxElevation) { - maxElevation = elevation - } + if (elevation > maxElevation) maxElevation = elevation } while (satPos.elevation > 0.0) - + val endDate = satPos.time - losAzimuth = (satPos.azimuth / (2.0 * Math.PI) * 360.0).toInt() - val tca = Date(startDate.time + (endDate.time - startDate.time) / 2) - - 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() + val losAzimuth = (satPos.azimuth / (2.0 * Math.PI) * 360.0).toInt() + val maxEl = maxElevation / (2.0 * Math.PI) * 360.0 + return SatPassTime(startDate, endDate, polePassed, aosAzimuth, losAzimuth, maxEl) } }