Fragments and adapters refactoring

This commit is contained in:
Arty Bishop committed 2021-03-21 21:16:52 +00:00
1 parent f04e54d59b
commit 37c5ebbf2e
10 files changed
+141 -127

No files matched your search

@@ -18,11 +18,11 @@
package com.rtbishop.look4sat.data.model package com.rtbishop.look4sat.data.model
import com.github.amsacode.predict4java.SatPassTime import com.github.amsacode.predict4java.SatPassTime
import com.github.amsacode.predict4java.TLE import com.github.amsacode.predict4java.Satellite
import com.rtbishop.look4sat.utility.PassPredictor import com.rtbishop.look4sat.utility.PassPredictor
class SatPass( class SatPass(
val tle: TLE, val satellite: Satellite,
val predictor: PassPredictor, val predictor: PassPredictor,
val pass: SatPassTime, val pass: SatPassTime,
var progress: Int = 0 var progress: Int = 0
@@ -35,7 +35,8 @@ class PassesRepo @Inject constructor(
private val prefsRepo: PrefsRepo, private val prefsRepo: PrefsRepo,
@DefaultDispatcher private val defaultDispatcher: CoroutineDispatcher @DefaultDispatcher private val defaultDispatcher: CoroutineDispatcher
) { ) {
private val _passes = MutableStateFlow<Result<MutableList<SatPass>>>(Result.InProgress) private val _passes =
MutableStateFlow<Result<MutableList<SatPass>>>(Result.Success(mutableListOf()))
val passes: StateFlow<Result<MutableList<SatPass>>> = _passes val passes: StateFlow<Result<MutableList<SatPass>>> = _passes
private var selectedSatellites = emptyList<Satellite>() private var selectedSatellites = emptyList<Satellite>()
@@ -61,7 +62,7 @@ class PassesRepo @Inject constructor(
private fun getPasses(satellite: Satellite, refDate: Date): MutableList<SatPass> { private fun getPasses(satellite: Satellite, refDate: Date): MutableList<SatPass> {
val predictor = satellite.getPredictor(prefsRepo.getStationPosition()) val predictor = satellite.getPredictor(prefsRepo.getStationPosition())
val passes = predictor.getPasses(refDate, prefsRepo.getHoursAhead(), true) val passes = predictor.getPasses(refDate, prefsRepo.getHoursAhead(), true)
val passList = passes.map { SatPass(satellite.tle, predictor, it) } val passList = passes.map { SatPass(satellite, predictor, it) }
return passList as MutableList<SatPass> return passList as MutableList<SatPass>
} }
@@ -46,10 +46,8 @@ class EntriesFragment : Fragment(R.layout.fragment_entries), EntriesAdapter.Entr
registerForActivityResult(ActivityResultContracts.GetContent()) { uri -> registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
uri?.let { viewModel.importSatDataFromFile(uri) } uri?.let { viewModel.importSatDataFromFile(uri) }
} }
private val entriesAdapter = EntriesAdapter().apply {
setEntriesClickListener(this@EntriesFragment)
}
private var binding: FragmentEntriesBinding? = null private var binding: FragmentEntriesBinding? = null
private var entriesAdapter: EntriesAdapter? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@@ -60,6 +58,9 @@ class EntriesFragment : Fragment(R.layout.fragment_entries), EntriesAdapter.Entr
} }
private fun setupComponents() { private fun setupComponents() {
entriesAdapter = EntriesAdapter().apply {
setEntriesClickListener(this@EntriesFragment)
}
binding?.apply { binding?.apply {
entriesRecycler.apply { entriesRecycler.apply {
setHasFixedSize(true) setHasFixedSize(true)
@@ -70,7 +71,7 @@ class EntriesFragment : Fragment(R.layout.fragment_entries), EntriesAdapter.Entr
} }
importWeb.setOnClickListener { showImportFromWebDialog() } importWeb.setOnClickListener { showImportFromWebDialog() }
importFile.setOnClickListener { filePicker.launch("*/*") } importFile.setOnClickListener { filePicker.launch("*/*") }
selectAll.setOnClickListener { entriesAdapter.selectAllItems() } selectAll.setOnClickListener { entriesAdapter?.selectAllItems() }
searchBar.addTextChangedListener { query -> filterByQuery(query) } searchBar.addTextChangedListener { query -> filterByQuery(query) }
searchBar.clearFocus() searchBar.clearFocus()
} }
@@ -83,7 +84,7 @@ class EntriesFragment : Fragment(R.layout.fragment_entries), EntriesAdapter.Entr
if (result.data.isEmpty()) { if (result.data.isEmpty()) {
setEmpty() setEmpty()
} else { } else {
entriesAdapter.submitAllItems(result.data) entriesAdapter?.submitAllItems(result.data)
setLoaded() setLoaded()
} }
} }
@@ -134,7 +135,7 @@ class EntriesFragment : Fragment(R.layout.fragment_entries), EntriesAdapter.Entr
} }
private fun filterByQuery(it: Editable?) { private fun filterByQuery(it: Editable?) {
entriesAdapter.filterItems(it.toString()) entriesAdapter?.filterItems(it.toString())
binding?.entriesRecycler?.scrollToPosition(0) binding?.entriesRecycler?.scrollToPosition(0)
} }
@@ -147,6 +148,7 @@ class EntriesFragment : Fragment(R.layout.fragment_entries), EntriesAdapter.Entr
} }
override fun onDestroyView() { override fun onDestroyView() {
entriesAdapter = null
binding = null binding = null
super.onDestroyView() super.onDestroyView()
} }
@@ -17,7 +17,6 @@
*/ */
package com.rtbishop.look4sat.ui.passesScreen package com.rtbishop.look4sat.ui.passesScreen
import android.content.Context
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
@@ -31,22 +30,9 @@ import com.rtbishop.look4sat.utility.navigateSafe
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
class PassesAdapter(context: Context, private val shouldUseUTC: Boolean = false) : class PassesAdapter(private val shouldUseUTC: Boolean = false) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() { RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private val currentDate = Date()
private val satIdFormat = context.getString(R.string.pass_satId)
private val azFormat = context.getString(R.string.pat_azimuth)
private val altFormat = context.getString(R.string.pat_altitude)
private val elevFormat = context.getString(R.string.pat_elevation)
private val aosAzFormat = context.getString(R.string.pass_aosAz)
private val maxElFormat = context.getString(R.string.pass_maxEl)
private val losAzFormat = context.getString(R.string.pass_losAz)
private val startTimeFormat = context.getString(R.string.pass_startTime)
private val endTimeFormat = context.getString(R.string.pass_endTime)
private val timeZoneUTC = TimeZone.getTimeZone("UTC")
private val startFormat = SimpleDateFormat(startTimeFormat, Locale.getDefault())
private val endFormat = SimpleDateFormat(endTimeFormat, Locale.getDefault())
private var satPassList: MutableList<SatPass> = mutableListOf() private var satPassList: MutableList<SatPass> = mutableListOf()
fun setList(list: MutableList<SatPass>) { fun setList(list: MutableList<SatPass>) {
@@ -55,11 +41,10 @@ class PassesAdapter(context: Context, private val shouldUseUTC: Boolean = false)
} }
fun tickPasses(timeNow: Long) { fun tickPasses(timeNow: Long) {
currentDate.time = timeNow
val iterator = satPassList.listIterator() val iterator = satPassList.listIterator()
while (iterator.hasNext()) { while (iterator.hasNext()) {
val satPass = iterator.next() val satPass = iterator.next()
if (!satPass.tle.isDeepspace) { if (!satPass.satellite.tle.isDeepspace) {
if (satPass.progress < 100) { if (satPass.progress < 100) {
val timeStart = satPass.pass.startTime.time val timeStart = satPass.pass.startTime.time
if (timeNow > timeStart) { if (timeNow > timeStart) {
@@ -84,42 +69,49 @@ class PassesAdapter(context: Context, private val shouldUseUTC: Boolean = false)
} }
override fun getItemViewType(position: Int): Int { override fun getItemViewType(position: Int): Int {
return if (satPassList[position].tle.isDeepspace) 1 return if (satPassList[position].satellite.tle.isDeepspace) 1
else 0 else 0
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return if (viewType == 0) { return if (viewType == 0) {
val bindingLeo = ItemPassLeoBinding SatPassLeoHolder.from(parent)
.inflate(LayoutInflater.from(parent.context), parent, false)
SatPassLeoHolder(bindingLeo)
} else { } else {
val bindingGeo = ItemPassGeoBinding SatPassGeoHolder.from(parent)
.inflate(LayoutInflater.from(parent.context), parent, false)
SatPassGeoHolder(bindingGeo)
} }
} }
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder.itemViewType == 0) { if (holder.itemViewType == 0) {
(holder as SatPassLeoHolder).bind(satPassList[position]) (holder as SatPassLeoHolder).bind(satPassList[position], position, shouldUseUTC)
} else { } else {
(holder as SatPassGeoHolder).bind(satPassList[position]) (holder as SatPassGeoHolder).bind(satPassList[position], position)
} }
} }
inner class SatPassLeoHolder(private val binding: ItemPassLeoBinding) : class SatPassLeoHolder private constructor(private val binding: ItemPassLeoBinding) :
RecyclerView.ViewHolder(binding.root) { RecyclerView.ViewHolder(binding.root) {
fun bind(satPass: SatPass) { private val satIdFormat = itemView.context.getString(R.string.pass_satId)
val satPos = satPass.predictor.getSatPos(currentDate) private val altFormat = itemView.context.getString(R.string.pat_altitude)
private val aosAzFormat = itemView.context.getString(R.string.pass_aosAz)
private val maxElFormat = itemView.context.getString(R.string.pass_maxEl)
private val losAzFormat = itemView.context.getString(R.string.pass_losAz)
private val startTimeFormat = itemView.context.getString(R.string.pass_startTime)
private val endTimeFormat = itemView.context.getString(R.string.pass_endTime)
private val timeZoneUTC = TimeZone.getTimeZone("UTC")
private val startFormat = SimpleDateFormat(startTimeFormat, Locale.getDefault())
private val endFormat = SimpleDateFormat(endTimeFormat, Locale.getDefault())
fun bind(satPass: SatPass, position: Int, shouldUseUTC: Boolean) {
val satPos = satPass.predictor.getSatPos(Date())
binding.apply { binding.apply {
if (shouldUseUTC) { if (shouldUseUTC) {
startFormat.timeZone = timeZoneUTC startFormat.timeZone = timeZoneUTC
endFormat.timeZone = timeZoneUTC endFormat.timeZone = timeZoneUTC
} }
passLeoSatName.text = satPass.tle.name passLeoSatName.text = satPass.satellite.tle.name
passLeoSatId.text = String.format(satIdFormat, satPass.tle.catnum) passLeoSatId.text = String.format(satIdFormat, satPass.satellite.tle.catnum)
passLeoAosAz.text = String.format(aosAzFormat, satPass.pass.aosAzimuth) passLeoAosAz.text = String.format(aosAzFormat, satPass.pass.aosAzimuth)
passLeoMaxEl.text = String.format(maxElFormat, satPass.pass.maxEl) passLeoMaxEl.text = String.format(maxElFormat, satPass.pass.maxEl)
passLeoLosAz.text = String.format(losAzFormat, satPass.pass.losAzimuth) passLeoLosAz.text = String.format(losAzFormat, satPass.pass.losAzimuth)
@@ -131,30 +123,49 @@ class PassesAdapter(context: Context, private val shouldUseUTC: Boolean = false)
itemView.setOnClickListener { itemView.setOnClickListener {
if (satPass.progress < 100) { if (satPass.progress < 100) {
val bundle = bundleOf("index" to satPassList.indexOf(satPass)) val bundle = bundleOf("index" to position)
itemView.findNavController().navigateSafe(R.id.action_passes_to_polar, bundle) itemView.findNavController().navigateSafe(R.id.action_passes_to_polar, bundle)
} }
} }
} }
companion object {
fun from(parent: ViewGroup): SatPassLeoHolder {
val inflater = LayoutInflater.from(parent.context)
return SatPassLeoHolder(ItemPassLeoBinding.inflate(inflater, parent, false))
}
}
} }
inner class SatPassGeoHolder(private val binding: ItemPassGeoBinding) : class SatPassGeoHolder private constructor(private val binding: ItemPassGeoBinding) :
RecyclerView.ViewHolder(binding.root) { RecyclerView.ViewHolder(binding.root) {
fun bind(satPass: SatPass) { private val satIdFormat = itemView.context.getString(R.string.pass_satId)
val satPos = satPass.predictor.getSatPos(currentDate) private val azFormat = itemView.context.getString(R.string.pat_azimuth)
private val altFormat = itemView.context.getString(R.string.pat_altitude)
private val elevFormat = itemView.context.getString(R.string.pat_elevation)
fun bind(satPass: SatPass, position: Int) {
val satPos = satPass.predictor.getSatPos(Date())
binding.apply { binding.apply {
passGeoSatName.text = satPass.tle.name passGeoSatName.text = satPass.satellite.tle.name
passGeoSatId.text = String.format(satIdFormat, satPass.tle.catnum) passGeoSatId.text = String.format(satIdFormat, satPass.satellite.tle.catnum)
passGeoAz.text = String.format(azFormat, Math.toDegrees(satPos.azimuth)) passGeoAz.text = String.format(azFormat, Math.toDegrees(satPos.azimuth))
passGeoAlt.text = String.format(altFormat, satPos.altitude) passGeoAlt.text = String.format(altFormat, satPos.altitude)
passGeoEl.text = String.format(elevFormat, Math.toDegrees(satPos.elevation)) passGeoEl.text = String.format(elevFormat, Math.toDegrees(satPos.elevation))
} }
itemView.setOnClickListener { itemView.setOnClickListener {
val bundle = bundleOf("index" to satPassList.indexOf(satPass)) val bundle = bundleOf("index" to position)
itemView.findNavController().navigateSafe(R.id.action_passes_to_polar, bundle) itemView.findNavController().navigateSafe(R.id.action_passes_to_polar, bundle)
} }
} }
companion object {
fun from(parent: ViewGroup): SatPassGeoHolder {
val inflater = LayoutInflater.from(parent.context)
return SatPassGeoHolder(ItemPassGeoBinding.inflate(inflater, parent, false))
}
}
} }
} }
@@ -37,10 +37,10 @@ import java.util.*
@AndroidEntryPoint @AndroidEntryPoint
class PassesFragment : Fragment(R.layout.fragment_passes) { class PassesFragment : Fragment(R.layout.fragment_passes) {
private var binding: FragmentPassesBinding? = null
private var passesAdapter: PassesAdapter? = null
private val viewModel: PassesViewModel by viewModels() private val viewModel: PassesViewModel by viewModels()
private var passes = mutableListOf<SatPass>() private var passes = mutableListOf<SatPass>()
private var binding: FragmentPassesBinding? = null
private var passesAdapter: PassesAdapter? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@@ -84,7 +84,7 @@ class PassesFragment : Fragment(R.layout.fragment_passes) {
} }
private fun setupComponents() { private fun setupComponents() {
passesAdapter = PassesAdapter(requireContext(), viewModel.shouldUseUTC()) passesAdapter = PassesAdapter(viewModel.shouldUseUTC())
binding?.apply { binding?.apply {
passesRecycler.apply { passesRecycler.apply {
setHasFixedSize(true) setHasFixedSize(true)
@@ -32,23 +32,18 @@ import androidx.recyclerview.widget.SimpleItemAnimator
import com.rtbishop.look4sat.R import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.data.model.Result import com.rtbishop.look4sat.data.model.Result
import com.rtbishop.look4sat.data.model.SatPass import com.rtbishop.look4sat.data.model.SatPass
import com.rtbishop.look4sat.data.repository.PrefsRepo
import com.rtbishop.look4sat.databinding.FragmentPolarBinding import com.rtbishop.look4sat.databinding.FragmentPolarBinding
import com.rtbishop.look4sat.utility.RecyclerDivider import com.rtbishop.look4sat.utility.RecyclerDivider
import com.rtbishop.look4sat.utility.formatForTimer import com.rtbishop.look4sat.utility.formatForTimer
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.FlowPreview
import java.util.* import java.util.*
import javax.inject.Inject
import kotlin.math.round import kotlin.math.round
@FlowPreview @FlowPreview
@AndroidEntryPoint @AndroidEntryPoint
class PolarFragment : Fragment(R.layout.fragment_polar), SensorEventListener { class PolarFragment : Fragment(R.layout.fragment_polar), SensorEventListener {
@Inject
lateinit var prefsRepo: PrefsRepo
private lateinit var transmitterAdapter: TransAdapter private lateinit var transmitterAdapter: TransAdapter
private lateinit var binding: FragmentPolarBinding private lateinit var binding: FragmentPolarBinding
private lateinit var satPass: SatPass private lateinit var satPass: SatPass
@@ -61,13 +56,13 @@ class PolarFragment : Fragment(R.layout.fragment_polar), SensorEventListener {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
binding = FragmentPolarBinding.bind(view) binding = FragmentPolarBinding.bind(view)
sensorManager = requireContext().getSystemService(Context.SENSOR_SERVICE) as SensorManager sensorManager = requireContext().getSystemService(Context.SENSOR_SERVICE) as SensorManager
magneticDeclination = prefsRepo.getMagDeclination() magneticDeclination = viewModel.getMagDeclination()
observePasses() observePasses()
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
if (prefsRepo.shouldUseCompass()) { if (viewModel.shouldUseCompass()) {
sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR).also { sensor -> sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR).also { sensor ->
sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_UI) sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_UI)
} }
@@ -76,7 +71,7 @@ class PolarFragment : Fragment(R.layout.fragment_polar), SensorEventListener {
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
if (prefsRepo.shouldUseCompass()) { if (viewModel.shouldUseCompass()) {
sensorManager.unregisterListener(this) sensorManager.unregisterListener(this)
} }
} }
@@ -98,7 +93,7 @@ class PolarFragment : Fragment(R.layout.fragment_polar), SensorEventListener {
} }
private fun observePasses() { private fun observePasses() {
viewModel.passes.observe(viewLifecycleOwner, { result -> viewModel.getPasses().observe(viewLifecycleOwner, { result ->
if (result is Result.Success) { if (result is Result.Success) {
satPass = result.data[requireArguments().getInt("index")] satPass = result.data[requireArguments().getInt("index")]
polarView = PolarView(requireContext()).apply { setPass(satPass) } polarView = PolarView(requireContext()).apply { setPass(satPass) }
@@ -109,26 +104,27 @@ class PolarFragment : Fragment(R.layout.fragment_polar), SensorEventListener {
} }
private fun observeTransmitters() { private fun observeTransmitters() {
viewModel.getTransmittersForSat(satPass.tle.catnum).observe(viewLifecycleOwner, { list -> viewModel.getTransmittersForSat(satPass.satellite.tle.catnum)
transmitterAdapter = TransAdapter(requireContext(), satPass) .observe(viewLifecycleOwner, { list ->
if (list.isNotEmpty()) { transmitterAdapter = TransAdapter(satPass)
transmitterAdapter.setData(list) if (list.isNotEmpty()) {
binding.recycler.apply { transmitterAdapter.setData(list)
setHasFixedSize(true) binding.recycler.apply {
adapter = transmitterAdapter setHasFixedSize(true)
isVerticalScrollBarEnabled = false adapter = transmitterAdapter
layoutManager = LinearLayoutManager(context) isVerticalScrollBarEnabled = false
(itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false layoutManager = LinearLayoutManager(context)
addItemDecoration(RecyclerDivider(R.drawable.rec_divider_dark)) (itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
visibility = View.VISIBLE addItemDecoration(RecyclerDivider(R.drawable.rec_divider_dark))
visibility = View.VISIBLE
}
binding.noTransMsg.visibility = View.INVISIBLE
} else {
binding.recycler.visibility = View.INVISIBLE
binding.noTransMsg.visibility = View.VISIBLE
} }
binding.noTransMsg.visibility = View.INVISIBLE observeTimer()
} else { })
binding.recycler.visibility = View.INVISIBLE
binding.noTransMsg.visibility = View.VISIBLE
}
observeTimer()
})
} }
private fun observeTimer() { private fun observeTimer() {
@@ -151,7 +147,7 @@ class PolarFragment : Fragment(R.layout.fragment_polar), SensorEventListener {
binding.distance.text = String.format(polarRng, satPos.range) binding.distance.text = String.format(polarRng, satPos.range)
binding.altitude.text = String.format(polarAlt, satPos.altitude) binding.altitude.text = String.format(polarAlt, satPos.altitude)
if (!satPass.tle.isDeepspace) { if (!satPass.satellite.tle.isDeepspace) {
if (dateNow.before(satPass.pass.startTime)) { if (dateNow.before(satPass.pass.startTime)) {
val millisBeforeStart = satPass.pass.startTime.time.minus(timeNow) val millisBeforeStart = satPass.pass.startTime.time.minus(timeNow)
binding.polarTimer.text = millisBeforeStart.formatForTimer() binding.polarTimer.text = millisBeforeStart.formatForTimer()
@@ -72,7 +72,7 @@ class PolarView(context: Context) : View(context) {
canvas.translate(polarCenter, polarCenter) canvas.translate(polarCenter, polarCenter)
drawRadarView(canvas) drawRadarView(canvas)
drawRadarText(canvas) drawRadarText(canvas)
if (!satPass.tle.isDeepspace) drawPassTrajectory(canvas, satPass) if (!satPass.satellite.tle.isDeepspace) drawPassTrajectory(canvas, satPass)
drawSatellite(canvas, satPass) drawSatellite(canvas, satPass)
} }
@@ -30,12 +30,10 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
class PolarViewModel @Inject constructor( class PolarViewModel @Inject constructor(
private val prefsRepo: PrefsRepo, private val prefsRepo: PrefsRepo,
passesRepo: PassesRepo, private val passesRepo: PassesRepo,
private val satelliteRepo: SatelliteRepo private val satelliteRepo: SatelliteRepo
) : ViewModel() { ) : ViewModel() {
val passes = passesRepo.passes.asLiveData()
fun getAppTimer() = liveData { fun getAppTimer() = liveData {
while (true) { while (true) {
emit(System.currentTimeMillis()) emit(System.currentTimeMillis())
@@ -43,5 +41,15 @@ class PolarViewModel @Inject constructor(
} }
} }
fun getPasses() = passesRepo.passes.asLiveData()
fun shouldUseCompass(): Boolean {
return prefsRepo.shouldUseCompass()
}
fun getMagDeclination(): Float {
return prefsRepo.getMagDeclination()
}
fun getTransmittersForSat(satId: Int) = satelliteRepo.getSatTransmitters(satId).asLiveData() fun getTransmittersForSat(satId: Int) = satelliteRepo.getSatTransmitters(satId).asLiveData()
} }
@@ -17,7 +17,6 @@
*/ */
package com.rtbishop.look4sat.ui.polarScreen package com.rtbishop.look4sat.ui.polarScreen
import android.content.Context
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@@ -25,19 +24,12 @@ import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.data.model.SatPass import com.rtbishop.look4sat.data.model.SatPass
import com.rtbishop.look4sat.data.model.SatTrans import com.rtbishop.look4sat.data.model.SatTrans
import com.rtbishop.look4sat.databinding.ItemTransBinding import com.rtbishop.look4sat.databinding.ItemTransBinding
import com.rtbishop.look4sat.utility.PassPredictor
import com.rtbishop.look4sat.utility.getDopplerFreq
import java.util.* import java.util.*
class TransAdapter(context: Context, private val satPass: SatPass) : class TransAdapter(private val pass: SatPass) : RecyclerView.Adapter<TransAdapter.TransHolder>() {
RecyclerView.Adapter<TransAdapter.TransHolder>() {
private val divider = 1000000f
private val strNo = context.getString(R.string.btn_no)
private val strYes = context.getString(R.string.btn_yes)
private val mode = context.getString(R.string.trans_mode)
private val formatLink = context.getString(R.string.trans_link_low)
private val formatLinkNull = context.getString(R.string.trans_no_link)
private val isInverted = context.getString(R.string.trans_inverted)
private val predictor = satPass.predictor
private var transmittersList = emptyList<SatTrans>() private var transmittersList = emptyList<SatTrans>()
fun setData(list: List<SatTrans>) { fun setData(list: List<SatTrans>) {
@@ -46,7 +38,7 @@ class TransAdapter(context: Context, private val satPass: SatPass) :
} }
fun tickTransmitters() { fun tickTransmitters() {
if (!satPass.tle.isDeepspace) { if (!pass.satellite.tle.isDeepspace) {
val iterator = transmittersList.listIterator() val iterator = transmittersList.listIterator()
while (iterator.hasNext()) { while (iterator.hasNext()) {
val trans = iterator.next() val trans = iterator.next()
@@ -61,23 +53,29 @@ class TransAdapter(context: Context, private val satPass: SatPass) :
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TransHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TransHolder {
val binding = ItemTransBinding return TransHolder.from(parent)
.inflate(LayoutInflater.from(parent.context), parent, false)
return TransHolder(binding)
} }
override fun onBindViewHolder(holder: TransHolder, position: Int) { override fun onBindViewHolder(holder: TransHolder, position: Int) {
holder.bind(transmittersList[position]) holder.bind(transmittersList[position], pass)
} }
inner class TransHolder(private val binding: ItemTransBinding) : class TransHolder private constructor(private val binding: ItemTransBinding) :
RecyclerView.ViewHolder(binding.root) { RecyclerView.ViewHolder(binding.root) {
fun bind(satTrans: SatTrans) { private val divider = 1000000f
private val strNo = itemView.context.getString(R.string.btn_no)
private val strYes = itemView.context.getString(R.string.btn_yes)
private val mode = itemView.context.getString(R.string.trans_mode)
private val formatLink = itemView.context.getString(R.string.trans_link_low)
private val formatLinkNull = itemView.context.getString(R.string.trans_no_link)
private val isInverted = itemView.context.getString(R.string.trans_inverted)
fun bind(satTrans: SatTrans, satPass: SatPass) {
binding.description.text = satTrans.description binding.description.text = satTrans.description
if (satPass.tle.isDeepspace) setRegularFreq(satTrans) if (satPass.satellite.tle.isDeepspace) setRegularFreq(satTrans)
else setDopplerFreq(satTrans) else setDopplerFreq(satTrans, satPass.predictor)
if (satTrans.mode != null) binding.mode.text = String.format(mode, satTrans.mode) if (satTrans.mode != null) binding.mode.text = String.format(mode, satTrans.mode)
else binding.mode.text = String.format(mode, strNo) else binding.mode.text = String.format(mode, strNo)
@@ -98,18 +96,27 @@ class TransAdapter(context: Context, private val satPass: SatPass) :
} else binding.uplink.text = formatLinkNull } else binding.uplink.text = formatLinkNull
} }
private fun setDopplerFreq(satTrans: SatTrans) { private fun setDopplerFreq(satTrans: SatTrans, predictor: PassPredictor) {
val dateNow = Date() val dateNow = Date()
if (satTrans.downlinkLow != null) { if (satTrans.downlinkLow != null) {
val downFreq = predictor.getDownlinkFreq(satTrans.downlinkLow, dateNow) / divider val rangeRate = predictor.getSatPos(dateNow).rangeRate
binding.downlink.text = String.format(Locale.ENGLISH, formatLink, downFreq) val freq = satTrans.downlinkLow.getDopplerFreq(rangeRate, true) / divider
binding.downlink.text = String.format(Locale.ENGLISH, formatLink, freq)
} else binding.downlink.text = formatLinkNull } else binding.downlink.text = formatLinkNull
if (satTrans.uplinkLow != null) { if (satTrans.uplinkLow != null) {
val upFreq = predictor.getUplinkFreq(satTrans.uplinkLow, dateNow) / divider val rangeRate = predictor.getSatPos(dateNow).rangeRate
binding.uplink.text = String.format(Locale.ENGLISH, formatLink, upFreq) val freq = satTrans.uplinkLow.getDopplerFreq(rangeRate, false) / divider
binding.uplink.text = String.format(Locale.ENGLISH, formatLink, freq)
} else binding.uplink.text = formatLinkNull } else binding.uplink.text = formatLinkNull
} }
companion object {
fun from(parent: ViewGroup): TransHolder {
val inflater = LayoutInflater.from(parent.context)
return TransHolder(ItemTransBinding.inflate(inflater, parent, false))
}
}
} }
} }
@@ -24,18 +24,8 @@ import com.github.amsacode.predict4java.Satellite
import java.util.* import java.util.*
class PassPredictor(private val satellite: Satellite, private val qth: GroundStationPosition) { 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 oneQuarterOrbitMin = (24.0 * 60.0 / satellite.tle.meanmo / 4.0).toInt()
private val speedOfLight = 2.99792458E8
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(date: Date): SatPos { fun getSatPos(date: Date): SatPos {
return satellite.getPosition(qth, date) return satellite.getPosition(qth, date)
@@ -81,10 +71,9 @@ class PassPredictor(private val satellite: Satellite, private val qth: GroundSta
val satPos = getSatPos(refDate) val satPos = getSatPos(refDate)
val startDate = Date(refDate.time - 24 * 60L * 60L * 1000L) val startDate = Date(refDate.time - 24 * 60L * 60L * 1000L)
val endDate = 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 azimuth = Math.toDegrees(satPos.azimuth).toInt()
val losAzimuth = (satPos.azimuth / (2.0 * Math.PI) * 360.0).toInt() val maxEl = Math.toDegrees(satPos.elevation)
val maxEl = satPos.elevation / (2.0 * Math.PI) * 360.0 return SatPassTime(startDate, endDate, "pole", azimuth, azimuth, maxEl)
return SatPassTime(startDate, endDate, "pole", aosAzimuth, losAzimuth, maxEl)
} }
private fun nextNearEarthPass(refDate: Date, windBack: Boolean = false): SatPassTime { private fun nextNearEarthPass(refDate: Date, windBack: Boolean = false): SatPassTime {
@@ -127,7 +116,7 @@ class PassPredictor(private val satellite: Satellite, private val qth: GroundSta
} while (satPos.elevation < 0.0) } while (satPos.elevation < 0.0)
val startDate = satPos.time val startDate = satPos.time
val aosAzimuth = (satPos.azimuth / (2.0 * Math.PI) * 360.0).toInt() val aosAzimuth = Math.toDegrees(satPos.azimuth).toInt()
// find when sat goes below // find when sat goes below
do { do {
@@ -147,8 +136,8 @@ class PassPredictor(private val satellite: Satellite, private val qth: GroundSta
} while (satPos.elevation > 0.0) } while (satPos.elevation > 0.0)
val endDate = satPos.time val endDate = satPos.time
val losAzimuth = (satPos.azimuth / (2.0 * Math.PI) * 360.0).toInt() val losAzimuth = Math.toDegrees(satPos.azimuth).toInt()
val maxEl = maxElevation / (2.0 * Math.PI) * 360.0 val maxEl = Math.toDegrees(maxElevation)
return SatPassTime(startDate, endDate, "pole", aosAzimuth, losAzimuth, maxEl) return SatPassTime(startDate, endDate, "pole", aosAzimuth, losAzimuth, maxEl)
} }
} }