mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-05 03:57:24 +00:00
refine: AOS binary-search refinement, getRadios signature cleanup
Refine AOS crossing to ~500ms via binary search instead of linear stepping; simplify getRadios to take satPos; tidy radar view/viewmodel.
This commit is contained in:
1 parent
1813e1bfa4
commit
9d0d971500
4 files changed
+38
-33
No files matched your search
+19
-21
@@ -95,14 +95,8 @@ class SatelliteRepo(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getRadios(
|
override suspend fun getRadios(satPos: OrbitalPos, radios: List<SatRadio>): List<SatRadio> {
|
||||||
sat: OrbitalObject,
|
|
||||||
pos: GeoPos,
|
|
||||||
radios: List<SatRadio>,
|
|
||||||
time: Long
|
|
||||||
): List<SatRadio> {
|
|
||||||
return withContext(dispatcher) {
|
return withContext(dispatcher) {
|
||||||
val satPos = sat.getPosition(pos, time)
|
|
||||||
radios.map { transmitter ->
|
radios.map { transmitter ->
|
||||||
transmitter.copy(
|
transmitter.copy(
|
||||||
downlinkLow = transmitter.downlinkLow?.let { satPos.getDownlinkFreq(it) },
|
downlinkLow = transmitter.downlinkLow?.let { satPos.getDownlinkFreq(it) },
|
||||||
@@ -242,13 +236,16 @@ class SatelliteRepo(
|
|||||||
if (elevation > maxElevation) maxElevation = elevation
|
if (elevation > maxElevation) maxElevation = elevation
|
||||||
} while (elevation < 0.0)
|
} while (elevation < 0.0)
|
||||||
|
|
||||||
// refine AOS to ~500ms precision
|
// refine AOS to ~500ms precision via binary search.
|
||||||
calendarTimeMillis -= 60L * 1000L
|
// Elevation is monotonic across the horizon crossing, so binary search
|
||||||
do {
|
// finds the crossing in ~8 SGP4 calls instead of up to 120 linear steps.
|
||||||
calendarTimeMillis += 500L
|
var aosLo = calendarTimeMillis - 60L * 1000L // elevation < 0 (below horizon)
|
||||||
elevation = sat.getElevation(pos, calendarTimeMillis)
|
var aosHi = calendarTimeMillis // elevation >= 0 (above horizon)
|
||||||
if (elevation > maxElevation) maxElevation = elevation
|
while (aosHi - aosLo > 500L) {
|
||||||
} while (elevation < 0.0)
|
val mid = (aosLo + aosHi) / 2
|
||||||
|
if (sat.getElevation(pos, mid) < 0.0) aosLo = mid else aosHi = mid
|
||||||
|
}
|
||||||
|
calendarTimeMillis = aosHi
|
||||||
|
|
||||||
// Get full position for AOS data (azimuth, altitude)
|
// Get full position for AOS data (azimuth, altitude)
|
||||||
val aosPos = sat.getFullPosition(pos, calendarTimeMillis)
|
val aosPos = sat.getFullPosition(pos, calendarTimeMillis)
|
||||||
@@ -262,13 +259,14 @@ class SatelliteRepo(
|
|||||||
if (elevation > maxElevation) maxElevation = elevation
|
if (elevation > maxElevation) maxElevation = elevation
|
||||||
} while (elevation > 0.0)
|
} while (elevation > 0.0)
|
||||||
|
|
||||||
// refine LOS to ~500ms precision
|
// refine LOS to ~500ms precision via binary search (same monotonic argument)
|
||||||
calendarTimeMillis -= 30L * 1000L
|
var losLo = calendarTimeMillis - 30L * 1000L // elevation > 0 (above horizon)
|
||||||
do {
|
var losHi = calendarTimeMillis // elevation <= 0 (below horizon)
|
||||||
calendarTimeMillis += 500L
|
while (losHi - losLo > 500L) {
|
||||||
elevation = sat.getElevation(pos, calendarTimeMillis)
|
val mid = (losLo + losHi) / 2
|
||||||
if (elevation > maxElevation) maxElevation = elevation
|
if (sat.getElevation(pos, mid) > 0.0) losLo = mid else losHi = mid
|
||||||
} while (elevation > 0.0)
|
}
|
||||||
|
calendarTimeMillis = losHi
|
||||||
|
|
||||||
// Get full position for LOS data (azimuth, altitude)
|
// Get full position for LOS data (azimuth, altitude)
|
||||||
val losPos = sat.getFullPosition(pos, calendarTimeMillis)
|
val losPos = sat.getFullPosition(pos, calendarTimeMillis)
|
||||||
|
|||||||
+1
-1
@@ -61,7 +61,7 @@ interface ISatelliteRepo {
|
|||||||
suspend fun getTrack(sat: OrbitalObject, pos: GeoPos, start: Long, end: Long): List<OrbitalPos>
|
suspend fun getTrack(sat: OrbitalObject, pos: GeoPos, start: Long, end: Long): List<OrbitalPos>
|
||||||
|
|
||||||
/** Get Doppler-shifted radio frequencies for a satellite at the given time. */
|
/** Get Doppler-shifted radio frequencies for a satellite at the given time. */
|
||||||
suspend fun getRadios(sat: OrbitalObject, pos: GeoPos, radios: List<SatRadio>, time: Long): List<SatRadio>
|
suspend fun getRadios(satPos: OrbitalPos, radios: List<SatRadio>): List<SatRadio>
|
||||||
|
|
||||||
/** Fetch radio transceivers for a satellite by its catalog number. */
|
/** Fetch radio transceivers for a satellite by its catalog number. */
|
||||||
suspend fun getRadiosWithId(id: Int): List<SatRadio>
|
suspend fun getRadiosWithId(id: Int): List<SatRadio>
|
||||||
|
|||||||
@@ -94,14 +94,21 @@ fun RadarViewCompose(
|
|||||||
animationSpec = infiniteRepeatable(tween(1000)),
|
animationSpec = infiniteRepeatable(tween(1000)),
|
||||||
label = "animScale"
|
label = "animScale"
|
||||||
)
|
)
|
||||||
// Drive the sweep from the animation framework to eliminate state mutation inside the draw block
|
// Drive the sweep from the animation framework to eliminate state mutation inside the draw block.
|
||||||
val sweepTransition = rememberInfiniteTransition(label = "sweep")
|
// Only create the infinite transition when the sweep is enabled — otherwise the canvas would
|
||||||
val sweepDegrees by sweepTransition.animateFloat(
|
// be invalidated every frame for an effect that isn't visible, wasting GPU and battery.
|
||||||
initialValue = 0f,
|
val sweepDegrees: Float
|
||||||
targetValue = 360f,
|
if (shouldShowSweep) {
|
||||||
animationSpec = infiniteRepeatable(tween(SWEEP_DURATION_MS, easing = LinearEasing)),
|
val sweepTransition = rememberInfiniteTransition(label = "sweep")
|
||||||
label = "sweepDegrees"
|
sweepDegrees by sweepTransition.animateFloat(
|
||||||
)
|
initialValue = 0f,
|
||||||
|
targetValue = 360f,
|
||||||
|
animationSpec = infiniteRepeatable(tween(SWEEP_DURATION_MS, easing = LinearEasing)),
|
||||||
|
label = "sweepDegrees"
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
sweepDegrees = 0f
|
||||||
|
}
|
||||||
val measurer = rememberTextMeasurer()
|
val measurer = rememberTextMeasurer()
|
||||||
val sunPainter = painterResource(R.drawable.ic_sun)
|
val sunPainter = painterResource(R.drawable.ic_sun)
|
||||||
val moonPainter = painterResource(R.drawable.ic_moon)
|
val moonPainter = painterResource(R.drawable.ic_moon)
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ class RadarViewModel(
|
|||||||
orbitalPos = pos, sunPosition = cachedSunPos, moonPosition = cachedMoonPos
|
orbitalPos = pos, sunPosition = cachedSunPos, moonPosition = cachedMoonPos
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
processRadios(allRadios, pass.orbitalObject, timeNow)
|
processRadios(allRadios, pos)
|
||||||
sendPassData(pos)
|
sendPassData(pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,8 +342,8 @@ class RadarViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun processRadios(radios: List<SatRadio>, orbitalObject: OrbitalObject, time: Long) {
|
private suspend fun processRadios(radios: List<SatRadio>, satPos: OrbitalPos) {
|
||||||
val transmitters = satelliteRepo.getRadios(orbitalObject, stationPos, radios, time)
|
val transmitters = satelliteRepo.getRadios(satPos, radios)
|
||||||
_uiState.update { state ->
|
_uiState.update { state ->
|
||||||
val freq = if (state.transceivers.selectedUuid != null) {
|
val freq = if (state.transceivers.selectedUuid != null) {
|
||||||
val selectedRadio = transmitters.firstOrNull { it.uuid == state.transceivers.selectedUuid }
|
val selectedRadio = transmitters.firstOrNull { it.uuid == state.transceivers.selectedUuid }
|
||||||
|
|||||||
Reference in new issue
Block a user