Added multiple fixes to RadarScreen and components

pull/153/head
Arty Bishop 2024-12-17 09:35:33 +00:00
rodzic 33bcc138f7
commit 1657fad715
16 zmienionych plików z 304 dodań i 184 usunięć

Wyświetl plik

@ -16,6 +16,7 @@ import com.rtbishop.look4sat.data.repository.SensorsRepo
import com.rtbishop.look4sat.data.repository.SettingsRepo
import com.rtbishop.look4sat.data.source.LocalSource
import com.rtbishop.look4sat.data.source.RemoteSource
import com.rtbishop.look4sat.data.usecase.AddToCalendar
import com.rtbishop.look4sat.domain.repository.IDatabaseRepo
import com.rtbishop.look4sat.domain.repository.ISatelliteRepo
import com.rtbishop.look4sat.domain.repository.ISelectionRepo
@ -23,6 +24,7 @@ import com.rtbishop.look4sat.domain.repository.ISensorsRepo
import com.rtbishop.look4sat.domain.repository.ISettingsRepo
import com.rtbishop.look4sat.domain.source.ILocalSource
import com.rtbishop.look4sat.domain.source.IRemoteSource
import com.rtbishop.look4sat.domain.usecase.IAddToCalendar
import com.rtbishop.look4sat.domain.utility.DataParser
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
@ -41,6 +43,10 @@ class MainContainer(private val context: Context) {
val satelliteRepo = provideSatelliteRepo()
val databaseRepo = provideDatabaseRepo()
fun provideAddToCalendar(): IAddToCalendar {
return AddToCalendar(context)
}
fun provideBluetoothReporter(): BluetoothReporter {
val manager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
return BluetoothReporter(manager, CoroutineScope(Dispatchers.IO))

Wyświetl plik

@ -16,7 +16,6 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.presentation.info.infoDestination
import com.rtbishop.look4sat.presentation.map.mapDestination
@ -42,14 +41,9 @@ fun MainScreen() {
val routeWithParams = "${Screen.Radar.route}?catNum=${catNum}&aosTime=${aosTime}"
outerNavController.navigate(routeWithParams)
}
val radarRoute = "${Screen.Radar.route}?catNum={catNum}&aosTime={aosTime}"
val radarArgs = listOf(
navArgument("catNum") { defaultValue = 0 },
navArgument("aosTime") { defaultValue = 0L }
)
NavHost(navController = outerNavController, startDestination = Screen.Main.route) {
mainDestination(navigateToRadar)
radarDestination(radarRoute, radarArgs)
radarDestination { outerNavController.navigateUp() }
}
}

Wyświetl plik

@ -80,14 +80,14 @@ fun RowScope.TimerBar(timeString: String, isTimeAos: Boolean) {
horizontalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier.fillMaxSize()
) {
Text(text = "AOS", fontSize = 16.sp, color = aosColor)
Text(text = "AOS", fontSize = 16.sp, fontWeight = FontWeight.Medium, color = aosColor)
Text(
text = timeString,
fontSize = 32.sp,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.primary
)
Text(text = "LOS", fontSize = 16.sp, color = losColor)
Text(text = "LOS", fontSize = 16.sp, fontWeight = FontWeight.Medium, color = losColor)
}
}
}

Wyświetl plik

@ -34,6 +34,11 @@ import com.rtbishop.look4sat.domain.predict.GeoPos
import com.rtbishop.look4sat.domain.predict.OrbitalObject
import com.rtbishop.look4sat.domain.predict.OrbitalPos
import com.rtbishop.look4sat.presentation.Screen
import com.rtbishop.look4sat.presentation.components.CardIcon
import com.rtbishop.look4sat.presentation.components.NextPassRow
import com.rtbishop.look4sat.presentation.components.TimerBar
import com.rtbishop.look4sat.presentation.components.TimerRow
import com.rtbishop.look4sat.presentation.components.getDefaultPass
import org.osmdroid.tileprovider.tilesource.XYTileSource
import org.osmdroid.util.GeoPoint
import org.osmdroid.views.CustomZoomButtonsController
@ -78,6 +83,12 @@ private fun MapScreen() {
val footprint = viewModel.footprint.collectAsState(initial = null)
val positionClick = { orbitalObject: OrbitalObject -> viewModel.selectSatellite(orbitalObject) }
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
TimerRow {
CardIcon(onClick = {}, iconId = R.drawable.ic_filter)
TimerBar(timeString = "88:88:88", isTimeAos = true)
CardIcon(onClick = {}, iconId = R.drawable.ic_satellite)
}
NextPassRow(pass = getDefaultPass())
ElevatedCard(modifier = Modifier.fillMaxSize()) {
MapView(modifier = Modifier.fillMaxSize(), isLightTheme = viewModel.stateOfLightTheme) { mapView ->
stationPos.value?.let { setStationPosition(it, mapView) }

Wyświetl plik

@ -1,27 +0,0 @@
/*
* Look4Sat. Amateur radio satellite tracker and pass predictor.
* Copyright (C) 2019-2022 Arty Bishop (bishop.arty@gmail.com)
*
* 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 3 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, see <https://www.gnu.org/licenses/>.
*/
package com.rtbishop.look4sat.presentation.radar
import com.rtbishop.look4sat.domain.predict.OrbitalPass
import com.rtbishop.look4sat.domain.predict.OrbitalPos
data class RadarData(
val orbitalPass: OrbitalPass,
val orbitalPos: OrbitalPos,
val satTrack: List<OrbitalPos> = emptyList()
)

Wyświetl plik

@ -1,9 +1,13 @@
package com.rtbishop.look4sat.presentation.radar
import androidx.compose.foundation.MarqueeSpacing
import androidx.compose.foundation.background
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
@ -16,57 +20,77 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NamedNavArgument
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.composable
import androidx.navigation.navArgument
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.domain.model.SatRadio
import com.rtbishop.look4sat.presentation.MainTheme
import com.rtbishop.look4sat.presentation.Screen
import com.rtbishop.look4sat.presentation.components.CardIcon
import com.rtbishop.look4sat.presentation.components.NextPassRow
import com.rtbishop.look4sat.presentation.components.TimerBar
import com.rtbishop.look4sat.presentation.components.TimerRow
import com.rtbishop.look4sat.presentation.components.getDefaultPass
fun NavGraphBuilder.radarDestination(radarRoute: String, radarArgs: List<NamedNavArgument>) {
composable(radarRoute, radarArgs) { RadarScreen() }
fun NavGraphBuilder.radarDestination(navigateBack: () -> Unit) {
val radarRoute = "${Screen.Radar.route}?catNum={catNum}&aosTime={aosTime}"
val radarArgs = listOf(
navArgument("catNum") { defaultValue = 0 },
navArgument("aosTime") { defaultValue = 0L }
)
composable(radarRoute, radarArgs) {
val viewModel = viewModel(RadarViewModel::class.java, factory = RadarViewModel.Factory)
val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
RadarScreen(uiState, navigateBack)
}
}
@Composable
private fun RadarScreen() {
val viewModel = viewModel(RadarViewModel::class.java, factory = RadarViewModel.Factory)
val currentPass = viewModel.getPass().collectAsState(null).value
val id = currentPass?.catNum ?: 99999
val name = currentPass?.name ?: "Satellite"
private fun RadarScreen(uiState: RadarState, navigateBack: () -> Unit) {
val addToCalendar: () -> Unit = {
uiState.currentPass?.let { pass ->
uiState.sendAction(RadarAction.AddToCalendar(pass.name, pass.aosTime, pass.losTime))
}
}
Scaffold { innerPadding ->
val paddingMod = Modifier.padding(innerPadding)
Column(
modifier = Modifier.padding(innerPadding),
modifier = paddingMod.padding(6.dp),
verticalArrangement = Arrangement.spacedBy(6.dp)
) {
// TimerBarNew(id, name, "88:88:88", R.drawable.ic_notifications) {}
ElevatedCard(
modifier = Modifier
.fillMaxSize()
.weight(1f)
) {
viewModel.radarData.value?.let { data ->
TimerRow {
CardIcon(onClick = navigateBack, iconId = R.drawable.ic_back)
TimerBar(timeString = uiState.currentTime, isTimeAos = uiState.isCurrentTimeAos)
CardIcon(onClick = addToCalendar, iconId = R.drawable.ic_calendar)
}
NextPassRow(pass = uiState.currentPass ?: getDefaultPass())
ElevatedCard(modifier = Modifier.aspectRatio(1f)) {
uiState.orbitalPos?.let { item ->
RadarViewCompose(
item = data.orbitalPos,
items = data.satTrack,
azimElev = viewModel.orientation.value
item = item,
items = uiState.satTrack,
azimElev = uiState.orientationValues,
shouldShowSweep = uiState.shouldShowSweep,
shouldUseCompass = uiState.shouldUseCompass
)
}
}
ElevatedCard(
modifier = Modifier
.fillMaxSize()
.weight(1f)
) {
TransmittersList(transmitters = viewModel.transmitters.value)
ElevatedCard(modifier = Modifier.fillMaxSize()) {
TransmittersList(transmitters = uiState.transmitters)
}
}
}
@ -81,6 +105,16 @@ private fun TransmittersList(transmitters: List<SatRadio>) {
}
}
@Preview
@Composable
private fun TransmitterItemPreview() {
val transmitter = SatRadio(
"", "Extremely powerful transmitter", true, 10000000000L, 10000000000L,
null, 10000000000L, 10000000000L, "FSK AX.100 Mode 5", true, 0
)
MainTheme { TransmitterItem(transmitter) }
}
@Composable
private fun TransmitterItem(radio: SatRadio) {
Surface(color = MaterialTheme.colorScheme.background) {
@ -96,55 +130,80 @@ private fun TransmitterItem(radio: SatRadio) {
modifier = Modifier.fillMaxWidth()
) {
Icon(
painter = painterResource(id = R.drawable.ic_arrow),
contentDescription = null, modifier = Modifier.rotate(180f)
painter = painterResource(id = R.drawable.ic_back),
tint = Color.Green,
contentDescription = null, modifier = Modifier.rotate(-90f)
)
Text(
text = radio.info,
modifier = Modifier.weight(1f),
textAlign = TextAlign.Center
text = if (radio.isInverted) "INVERTED: ${radio.info} " else "${radio.info} ",
textAlign = TextAlign.Center,
modifier = Modifier
.basicMarquee(
iterations = Int.MAX_VALUE,
spacing = MarqueeSpacing(0.dp)
)
.weight(1f)
)
Icon(
painter = painterResource(id = R.drawable.ic_arrow),
contentDescription = null
)
}
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
) {
Text(
text = stringResource(id = R.string.radio_downlink, radio.downlinkLow ?: 0L),
textAlign = TextAlign.Center,
fontSize = 15.sp,
modifier = Modifier.weight(0.5f)
)
Text(
text = stringResource(id = R.string.radio_uplink, radio.uplinkLow ?: 0L),
textAlign = TextAlign.Center,
fontSize = 15.sp,
modifier = Modifier.weight(0.5f)
)
}
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.fillMaxWidth()
) {
Text(
text = radio.downlinkMode ?: "",
fontSize = 15.sp
)
Text(
text = radio.isInverted.toString(),
fontSize = 15.sp
painter = painterResource(id = R.drawable.ic_back),
tint = Color.Red,
contentDescription = null, modifier = Modifier.rotate(90f)
)
}
FrequencyRow(satRadio = radio)
}
}
}
}
@Composable
private fun FrequencyRow(satRadio: SatRadio) {
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) {
ModeText(satRadio.downlinkMode)
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(6.dp),
modifier = Modifier.weight(0.35f)
) {
FrequencyText(satRadio.downlinkHigh)
FrequencyText(satRadio.downlinkLow)
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(6.dp),
modifier = Modifier.weight(0.35f)
) {
FrequencyText(satRadio.uplinkHigh)
FrequencyText(satRadio.uplinkLow)
}
ModeText(satRadio.uplinkMode)
}
}
@Composable
private fun RowScope.ModeText(mode: String?) {
Text(
text = mode ?: "- - : - -",
textAlign = TextAlign.Center,
fontSize = 15.sp,
modifier = Modifier.weight(0.15f)
)
}
@Composable
private fun FrequencyText(frequency: Long?) {
val noLinkText = stringResource(R.string.radio_no_link)
val freqValue = frequency?.let { it / 1000000f }
val freqText = freqValue?.let { stringResource(id = R.string.radio_link_low, it) } ?: noLinkText
Text(
text = freqText,
textAlign = TextAlign.Center,
fontSize = 21.sp,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.primary
)
}
//private val divider = 1000000f
//radioDownlink.text = String.format(Locale.ENGLISH, link, downlink / divider)
//radioUplink.text = String.format(Locale.ENGLISH, link, uplink / divider)

Wyświetl plik

@ -0,0 +1,22 @@
package com.rtbishop.look4sat.presentation.radar
import com.rtbishop.look4sat.domain.model.SatRadio
import com.rtbishop.look4sat.domain.predict.OrbitalPass
import com.rtbishop.look4sat.domain.predict.OrbitalPos
data class RadarState(
val currentPass: OrbitalPass?,
val currentTime: String,
val isCurrentTimeAos: Boolean,
val orientationValues: Pair<Float, Float>,
val orbitalPos: OrbitalPos?,
val satTrack: List<OrbitalPos>,
val shouldShowSweep: Boolean,
val shouldUseCompass: Boolean,
val transmitters: List<SatRadio>,
val sendAction: (RadarAction) -> Unit
)
sealed class RadarAction {
data class AddToCalendar(val name: String, val aosTime: Long, val losTime: Long) : RadarAction()
}

Wyświetl plik

@ -1,6 +1,5 @@
package com.rtbishop.look4sat.presentation.radar
import android.media.AudioAttributes
import android.media.SoundPool
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.infiniteRepeatable
@ -10,8 +9,6 @@ import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
@ -37,20 +34,20 @@ import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.drawText
import androidx.compose.ui.text.rememberTextMeasurer
import androidx.compose.ui.unit.sp
import androidx.core.view.HapticFeedbackConstantsCompat
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.domain.predict.OrbitalPos
import com.rtbishop.look4sat.domain.predict.PI_2
import com.rtbishop.look4sat.domain.utility.toRadians
import kotlin.math.abs
import kotlin.math.cos
import kotlin.math.min
import kotlin.math.pow
import kotlin.math.sin
import kotlin.math.sqrt
@Composable
fun RadarViewCompose(item: OrbitalPos, items: List<OrbitalPos>, azimElev: Pair<Float, Float>) {
fun RadarViewCompose(
item: OrbitalPos,
items: List<OrbitalPos>,
azimElev: Pair<Float, Float>,
shouldShowSweep: Boolean,
shouldUseCompass: Boolean
) {
val context = LocalContext.current
val view = LocalView.current
val radarColor = MaterialTheme.colorScheme.secondary
@ -138,8 +135,8 @@ fun RadarViewCompose(item: OrbitalPos, items: List<OrbitalPos>, azimElev: Pair<F
trackEffect.value = createTrackEffect(trackPath.value)
trackCreated.value = true
}
rotate(-azimElev.first) {
drawSweep(center, sweepDegrees.floatValue, radius, trackColor)
rotate(if (shouldUseCompass) -azimElev.first else 0f) {
if (shouldShowSweep) drawSweep(center, sweepDegrees.floatValue, radius, trackColor)
drawRadar(radius, radarColor, strokeWidth, 3)
drawInfo(radius, trackColor, measurer, 3)
translate(center.x, center.y) {

Wyświetl plik

@ -17,9 +17,6 @@
*/
package com.rtbishop.look4sat.presentation.radar
import android.hardware.GeomagneticField
import android.util.Log
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
@ -31,17 +28,20 @@ import com.rtbishop.look4sat.MainApplication
import com.rtbishop.look4sat.data.framework.BluetoothReporter
import com.rtbishop.look4sat.data.framework.NetworkReporter
import com.rtbishop.look4sat.domain.model.SatRadio
import com.rtbishop.look4sat.domain.predict.GeoPos
import com.rtbishop.look4sat.domain.predict.OrbitalObject
import com.rtbishop.look4sat.domain.predict.OrbitalPass
import com.rtbishop.look4sat.domain.predict.OrbitalPos
import com.rtbishop.look4sat.domain.repository.ISatelliteRepo
import com.rtbishop.look4sat.domain.repository.ISensorsRepo
import com.rtbishop.look4sat.domain.repository.ISettingsRepo
import com.rtbishop.look4sat.domain.usecase.IAddToCalendar
import com.rtbishop.look4sat.domain.utility.round
import com.rtbishop.look4sat.domain.utility.toDegrees
import com.rtbishop.look4sat.domain.utility.toTimerString
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
@ -51,22 +51,61 @@ class RadarViewModel(
private val networkReporter: NetworkReporter,
private val satelliteRepo: ISatelliteRepo,
private val settingsRepo: ISettingsRepo,
private val sensorsRepo: ISensorsRepo
private val sensorsRepo: ISensorsRepo,
private val addToCalendar: IAddToCalendar
) : ViewModel() {
private val _uiState = MutableStateFlow(
RadarState(
currentPass = null,
currentTime = "00:00:00",
isCurrentTimeAos = true,
orientationValues = sensorsRepo.orientation.value,
orbitalPos = null,
satTrack = emptyList(),
shouldShowSweep = settingsRepo.otherSettings.value.stateOfSweep,
shouldUseCompass = settingsRepo.otherSettings.value.stateOfSensors,
transmitters = emptyList(),
sendAction = ::handleAction,
)
)
private val stationPos = settingsRepo.stationPosition.value
private val magDeclination = getMagDeclination(stationPos)
val stateOfLightTheme = settingsRepo.otherSettings.value.stateOfLightTheme
val transmitters = mutableStateOf<List<SatRadio>>(emptyList())
val radarData = mutableStateOf<RadarData?>(null)
val orientation = mutableStateOf(sensorsRepo.orientation.value)
private val magDeclination = sensorsRepo.getMagDeclination(stationPos)
val uiState: StateFlow<RadarState> = _uiState
init {
if (settingsRepo.otherSettings.value.stateOfSensors) {
viewModelScope.launch {
sensorsRepo.enableSensor()
sensorsRepo.orientation.collect { data ->
orientation.value = Pair(data.first + magDeclination, data.second)
val orientationValues = Pair(data.first + magDeclination, data.second)
_uiState.update { it.copy(orientationValues = orientationValues) }
}
}
}
viewModelScope.launch {
val catNum = savedStateHandle.get<Int>("catNum") ?: 0
val aosTime = savedStateHandle.get<Long>("aosTime") ?: 0L
val passes = satelliteRepo.passes.value
val pass = passes.find { pass -> pass.catNum == catNum && pass.aosTime == aosTime }
val currentPass = pass ?: passes.firstOrNull()
currentPass?.let { satPass ->
_uiState.update { it.copy(currentPass = satPass) }
val transmitters = satelliteRepo.getRadiosWithId(satPass.catNum)
while (isActive) {
val timeNow = System.currentTimeMillis()
val pos = satelliteRepo.getPosition(satPass.orbitalObject, stationPos, timeNow)
if (satPass.aosTime > timeNow) {
val time = satPass.aosTime.minus(timeNow).toTimerString()
_uiState.update { it.copy(currentTime = time, isCurrentTimeAos = true) }
} else {
val time = satPass.losTime.minus(timeNow).toTimerString()
_uiState.update { it.copy(currentTime = time, isCurrentTimeAos = false) }
}
sendPassData(satPass, pos, satPass.orbitalObject)
sendPassDataBT(pos)
processRadios(transmitters, satPass.orbitalObject, timeNow)
delay(1000)
}
}
}
@ -77,56 +116,29 @@ class RadarViewModel(
super.onCleared()
}
fun getPass() = flow {
val catNum = savedStateHandle.get<Int>("catNum") ?: 0
val aosTime = savedStateHandle.get<Long>("aosTime") ?: 0L
val passes = satelliteRepo.passes.value
val pass = passes.find { pass -> pass.catNum == catNum && pass.aosTime == aosTime }
val currentPass = pass ?: passes.firstOrNull()
currentPass?.let { satPass ->
emit(satPass)
val transmitters = satelliteRepo.getRadiosWithId(satPass.catNum)
viewModelScope.launch {
while (isActive) {
val timeNow = System.currentTimeMillis()
val satPos =
satelliteRepo.getPosition(satPass.orbitalObject, stationPos, timeNow)
sendPassData(satPass, satPos, satPass.orbitalObject)
sendPassDataBT(satPos)
processRadios(transmitters, satPass.orbitalObject, timeNow)
delay(1000)
}
}
private fun handleAction(action: RadarAction) {
when (action) {
is RadarAction.AddToCalendar -> addToCalendar(action.name, action.aosTime, action.losTime)
}
}
// fun getUseCompass(): Boolean = settingsRepo.otherSettings.value.sensorState
//
// fun getShowSweep(): Boolean = settingsRepo.otherSettings.value.sweepState
private fun getMagDeclination(geoPos: GeoPos, time: Long = System.currentTimeMillis()): Float {
val latitude = geoPos.latitude.toFloat()
val longitude = geoPos.longitude.toFloat()
return GeomagneticField(latitude, longitude, geoPos.altitude.toFloat(), time).declination
}
private fun sendPassData(orbitalPass: OrbitalPass, orbitalPos: OrbitalPos, orbitalObject: OrbitalObject) {
viewModelScope.launch {
var track: List<OrbitalPos> = emptyList()
if (!orbitalPass.isDeepSpace) {
track = satelliteRepo.getTrack(
orbitalObject, stationPos, orbitalPass.aosTime, orbitalPass.losTime
)
}
if (settingsRepo.getRotatorState()) {
val server = settingsRepo.getRotatorAddress()
val port = settingsRepo.getRotatorPort().toInt()
val azimuth = orbitalPos.azimuth.toDegrees().round(2)
val elevation = orbitalPos.elevation.toDegrees().round(2)
networkReporter.reportRotation(server, port, azimuth, elevation)
}
radarData.value = RadarData(orbitalPass, orbitalPos, track)
private suspend fun sendPassData(orbitalPass: OrbitalPass, orbitalPos: OrbitalPos, orbitalObject: OrbitalObject) {
var track: List<OrbitalPos> = emptyList()
if (!orbitalPass.isDeepSpace) {
track = satelliteRepo.getTrack(
orbitalObject, stationPos, orbitalPass.aosTime, orbitalPass.losTime
)
}
_uiState.update { it.copy(orbitalPos = orbitalPos, satTrack = track) }
// viewModelScope.launch {
// if (settingsRepo.getRotatorState()) {
// val server = settingsRepo.getRotatorAddress()
// val port = settingsRepo.getRotatorPort().toInt()
// val azimuth = orbitalPos.azimuth.toDegrees().round(2)
// val elevation = orbitalPos.elevation.toDegrees().round(2)
// networkReporter.reportRotation(server, port, azimuth, elevation)
// }
// }
}
private fun sendPassDataBT(orbitalPos: OrbitalPos) {
@ -139,19 +151,16 @@ class RadarViewModel(
val elevation = orbitalPos.elevation.toDegrees().round(0).toInt()
bluetoothReporter.reportRotation(format, azimuth, elevation)
} else if (!bluetoothReporter.isConnecting()) {
Log.i("BTReporter", "BTReporter: Attempting to connect...")
// Log.i("BTReporter", "BTReporter: Attempting to connect...")
bluetoothReporter.connectBTDevice(btDevice)
}
}
}
}
private fun processRadios(radios: List<SatRadio>, orbitalObject: OrbitalObject, time: Long) {
viewModelScope.launch {
delay(125)
val list = satelliteRepo.getRadios(orbitalObject, stationPos, radios, time)
transmitters.value = list
}
private suspend fun processRadios(radios: List<SatRadio>, orbitalObject: OrbitalObject, time: Long) {
val transmitters = satelliteRepo.getRadios(orbitalObject, stationPos, radios, time)
_uiState.update { it.copy(transmitters = transmitters) }
}
companion object {
@ -165,7 +174,8 @@ class RadarViewModel(
container.provideNetworkReporter(),
container.satelliteRepo,
container.settingsRepo,
container.provideSensorsRepo()
container.provideSensorsRepo(),
container.provideAddToCalendar()
)
}
}

Wyświetl plik

@ -178,7 +178,7 @@ private fun MiddleBar(
private fun TypeCard(types: List<String>, onClick: () -> Unit, modifier: Modifier = Modifier) {
val typesText = if (types.isEmpty()) "All" else types.joinToString(", ")
ElevatedCard(modifier = modifier) {
Row(horizontalArrangement = Arrangement.Start,
Row(horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.height(48.dp)
@ -194,9 +194,8 @@ private fun TypeCard(types: List<String>, onClick: () -> Unit, modifier: Modifie
text = "Types: $typesText",
fontSize = 16.sp,
fontWeight = FontWeight.Medium,
modifier = modifier
.basicMarquee(iterations = Int.MAX_VALUE, spacing = MarqueeSpacing(0.dp))
.padding(start = 12.dp, end = 6.dp, bottom = 2.dp)
modifier = Modifier
.basicMarquee(iterations = Int.MAX_VALUE, spacing = MarqueeSpacing(16.dp))
)
}
}

Wyświetl plik

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z" />
</vector>

Wyświetl plik

@ -17,15 +17,17 @@
*/
package com.rtbishop.look4sat.data.repository
import android.hardware.GeomagneticField
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import com.rtbishop.look4sat.domain.predict.GeoPos
import com.rtbishop.look4sat.domain.predict.RAD2DEG
import com.rtbishop.look4sat.domain.repository.ISensorsRepo
import kotlin.math.round
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlin.math.round
class SensorsRepo(private val sensorManager: SensorManager, private val sensor: Sensor?) :
SensorEventListener, ISensorsRepo {
@ -37,6 +39,12 @@ class SensorsRepo(private val sensorManager: SensorManager, private val sensor:
override val orientation: StateFlow<Pair<Float, Float>> = _orientation
override fun getMagDeclination(geoPos: GeoPos, time: Long): Float {
val latitude = geoPos.latitude.toFloat()
val longitude = geoPos.longitude.toFloat()
return GeomagneticField(latitude, longitude, geoPos.altitude.toFloat(), time).declination
}
override fun enableSensor() {
sensor?.let { sensorManager.registerListener(this, it, 8000) }
}

Wyświetl plik

@ -0,0 +1,24 @@
package com.rtbishop.look4sat.data.usecase
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.provider.CalendarContract
import com.rtbishop.look4sat.domain.usecase.IAddToCalendar
class AddToCalendar(private val context: Context) : IAddToCalendar {
override fun invoke(name: String, aosTime: Long, losTime: Long) {
val intent = Intent(Intent.ACTION_INSERT).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
setData(CalendarContract.Events.CONTENT_URI)
putExtra(CalendarContract.Events.TITLE, name)
putExtra(CalendarContract.Events.DESCRIPTION, "Look4Sat Pass")
putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, aosTime)
putExtra(CalendarContract.EXTRA_EVENT_END_TIME, losTime)
}
try {
context.startActivity(intent)
} catch (_: ActivityNotFoundException) {
}
}
}

Wyświetl plik

@ -1,9 +1,11 @@
package com.rtbishop.look4sat.domain.repository
import com.rtbishop.look4sat.domain.predict.GeoPos
import kotlinx.coroutines.flow.StateFlow
interface ISensorsRepo {
val orientation: StateFlow<Pair<Float, Float>>
fun getMagDeclination(geoPos: GeoPos, time: Long = System.currentTimeMillis()): Float
fun enableSensor()
fun disableSensor()
}

Wyświetl plik

@ -0,0 +1,5 @@
package com.rtbishop.look4sat.domain.usecase
interface IAddToCalendar {
operator fun invoke(name: String, aosTime: Long, losTime: Long)
}

Wyświetl plik

@ -17,6 +17,7 @@
*/
package com.rtbishop.look4sat.domain.utility
import java.util.Locale
import java.util.concurrent.TimeUnit
fun Long.toTimerString(): String {
@ -24,7 +25,7 @@ fun Long.toTimerString(): String {
val hours = TimeUnit.MILLISECONDS.toHours(this)
val minutes = TimeUnit.MILLISECONDS.toMinutes(this) % 60
val seconds = TimeUnit.MILLISECONDS.toSeconds(this) % 60
return String.format(format, hours, minutes, seconds)
return String.format(Locale.ENGLISH, format, hours, minutes, seconds)
}
fun Float.round(decimals: Int): Float {