From 6a2a4f9f9e60c80bc6be391979b0f4e8282ada57 Mon Sep 17 00:00:00 2001 From: Arty Bishop Date: Sun, 30 Jul 2023 12:48:47 +0100 Subject: [PATCH] Added GeoConverter.kt and various minor fixes --- app/src/main/AndroidManifest.xml | 3 +- .../com/rtbishop/look4sat/MainContainer.kt | 20 +++++--- .../look4sat/data/repository/DatabaseRepo.kt | 1 + .../rtbishop/look4sat/data/ExampleUnitTest.kt | 12 +++++ domain/build.gradle.kts | 1 + .../look4sat/domain/utility}/DataParser.kt | 2 +- .../look4sat/domain/utility/Extensions.kt | 22 +++----- .../look4sat/domain/utility/GeoConverter.kt | 50 +++++++++++++++++++ .../look4sat/domain}/DataParserTest.kt | 38 +++++++------- 9 files changed, 108 insertions(+), 41 deletions(-) create mode 100644 data/src/test/java/com/rtbishop/look4sat/data/ExampleUnitTest.kt rename {data/src/main/java/com/rtbishop/look4sat/data/repository => domain/src/main/java/com/rtbishop/look4sat/domain/utility}/DataParser.kt (99%) create mode 100644 domain/src/main/java/com/rtbishop/look4sat/domain/utility/GeoConverter.kt rename {data/src/test/java/com/rtbishop/look4sat/data => domain/src/test/java/com/rtbishop/look4sat/domain}/DataParserTest.kt (69%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1bcd7b01..fa667c0c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -9,6 +9,7 @@ android:name=".MainApplication" android:allowBackup="false" android:dataExtractionRules="@xml/data_extraction_rules" + android:enableOnBackInvokedCallback="true" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" @@ -16,7 +17,7 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.Look4Sat.Main" - tools:targetApi="31"> + tools:targetApi="33"> println("Look4Sat: $error") } + private val remoteSource = provideRemoteSource() val mainScope = CoroutineScope(SupervisorJob() + Dispatchers.Default + mainHandler) val settingsRepo = provideSettingsRepo() val databaseRepo = provideDatabaseRepo() @@ -57,11 +61,15 @@ class MainContainer(private val context: Context) { return SensorSource(sensorManager, sensor) } + private fun provideRemoteSource(): IDataSource { + val cache = Cache(context.cacheDir, 1000 * 1000 * 10L) + val httpClient = OkHttpClient.Builder().cache(cache).build() + return DataSource(context.contentResolver, httpClient, Dispatchers.IO) + } + private fun provideDatabaseRepo(): IDatabaseRepo { - val httpClient = OkHttpClient.Builder().build() val dataParser = DataParser(Dispatchers.Default) - val dataSource = DataSource(context.contentResolver, httpClient, Dispatchers.IO) - return DatabaseRepo(Dispatchers.Default, dataParser, dataSource, localStorage, settingsRepo) + return DatabaseRepo(Dispatchers.Default, dataParser, remoteSource, localStorage, settingsRepo) } private fun provideSatelliteRepo(): ISatelliteRepo { diff --git a/data/src/main/java/com/rtbishop/look4sat/data/repository/DatabaseRepo.kt b/data/src/main/java/com/rtbishop/look4sat/data/repository/DatabaseRepo.kt index 4a343a78..3068e24d 100644 --- a/data/src/main/java/com/rtbishop/look4sat/data/repository/DatabaseRepo.kt +++ b/data/src/main/java/com/rtbishop/look4sat/data/repository/DatabaseRepo.kt @@ -24,6 +24,7 @@ import com.rtbishop.look4sat.domain.repository.IDatabaseRepo import com.rtbishop.look4sat.domain.repository.ISettingsRepo import com.rtbishop.look4sat.domain.source.IDataSource import com.rtbishop.look4sat.domain.source.ILocalStorage +import com.rtbishop.look4sat.domain.utility.DataParser import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.async import kotlinx.coroutines.withContext diff --git a/data/src/test/java/com/rtbishop/look4sat/data/ExampleUnitTest.kt b/data/src/test/java/com/rtbishop/look4sat/data/ExampleUnitTest.kt new file mode 100644 index 00000000..ced6c914 --- /dev/null +++ b/data/src/test/java/com/rtbishop/look4sat/data/ExampleUnitTest.kt @@ -0,0 +1,12 @@ +package com.rtbishop.look4sat.data + +import org.junit.Assert +import org.junit.Test + +class ExampleUnitTest { + + @Test + fun addition_isCorrect() { + Assert.assertEquals(4, 2 + 2) + } +} diff --git a/domain/build.gradle.kts b/domain/build.gradle.kts index 3736e48f..1e4b7f46 100644 --- a/domain/build.gradle.kts +++ b/domain/build.gradle.kts @@ -8,6 +8,7 @@ kotlin { dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") + implementation("org.json:json:20230618") testImplementation("junit:junit:4.13.2") testImplementation("io.mockk:mockk:1.13.5") diff --git a/data/src/main/java/com/rtbishop/look4sat/data/repository/DataParser.kt b/domain/src/main/java/com/rtbishop/look4sat/domain/utility/DataParser.kt similarity index 99% rename from data/src/main/java/com/rtbishop/look4sat/data/repository/DataParser.kt rename to domain/src/main/java/com/rtbishop/look4sat/domain/utility/DataParser.kt index fa825fad..61fbc83d 100644 --- a/data/src/main/java/com/rtbishop/look4sat/data/repository/DataParser.kt +++ b/domain/src/main/java/com/rtbishop/look4sat/domain/utility/DataParser.kt @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.rtbishop.look4sat.data.repository +package com.rtbishop.look4sat.domain.utility import com.rtbishop.look4sat.domain.model.SatRadio import com.rtbishop.look4sat.domain.predict.OrbitalData diff --git a/domain/src/main/java/com/rtbishop/look4sat/domain/utility/Extensions.kt b/domain/src/main/java/com/rtbishop/look4sat/domain/utility/Extensions.kt index c40fc2e4..0bd63eac 100644 --- a/domain/src/main/java/com/rtbishop/look4sat/domain/utility/Extensions.kt +++ b/domain/src/main/java/com/rtbishop/look4sat/domain/utility/Extensions.kt @@ -17,8 +17,6 @@ */ package com.rtbishop.look4sat.domain.utility -import com.rtbishop.look4sat.domain.predict.DEG2RAD -import com.rtbishop.look4sat.domain.predict.RAD2DEG import java.util.concurrent.TimeUnit fun Long.toTimerString(): String { @@ -41,10 +39,6 @@ fun Double.round(decimals: Int): Double { return kotlin.math.round(this * multiplier) / multiplier } -fun Double.toDegrees(): Double = this * RAD2DEG - -fun Double.toRadians(): Double = this * DEG2RAD - //fun String.getHash(type: String = "SHA-256"): String { // val hexChars = "0123456789ABCDEF" // val bytes = MessageDigest.getInstance(type).digest(this.toByteArray()) @@ -63,12 +57,12 @@ fun Double.toRadians(): Double = this * DEG2RAD // return pattern.matcher(this).matches() //} -fun String.isValidIPv4(): Boolean { - val ip4 = "^((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])(\\.(?!\$)|\$)){4}\$" - return this.matches(ip4.toRegex()) -} +//fun String.isValidIPv4(): Boolean { +// val ip4 = "^((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])(\\.(?!\$)|\$)){4}\$" +// return this.matches(ip4.toRegex()) +//} -fun String.isValidPort(): Boolean { - val port = "([1-9]|[1-9]\\d{1,3}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])" - return this.matches(port.toRegex()) && this.toInt() in 1024..65535 -} +//fun String.isValidPort(): Boolean { +// val port = "([1-9]|[1-9]\\d{1,3}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])" +// return this.matches(port.toRegex()) && this.toInt() in 1024..65535 +//} diff --git a/domain/src/main/java/com/rtbishop/look4sat/domain/utility/GeoConverter.kt b/domain/src/main/java/com/rtbishop/look4sat/domain/utility/GeoConverter.kt new file mode 100644 index 00000000..5b136204 --- /dev/null +++ b/domain/src/main/java/com/rtbishop/look4sat/domain/utility/GeoConverter.kt @@ -0,0 +1,50 @@ +package com.rtbishop.look4sat.domain.utility + +import com.rtbishop.look4sat.domain.predict.DEG2RAD +import com.rtbishop.look4sat.domain.predict.PI +import com.rtbishop.look4sat.domain.predict.RAD2DEG +import kotlin.math.ln +import kotlin.math.max +import kotlin.math.min +import kotlin.math.sin + +private const val MIN_LATITUDE = -85.05112877980658 +private const val MAX_LATITUDE = 85.05112877980658 +private const val MIN_LONGITUDE = -180.0 +private const val MAX_LONGITUDE = 180.0 + +fun Double.toDegrees(): Double = this * RAD2DEG + +fun Double.toRadians(): Double = this * DEG2RAD + +fun Double.latToY01(): Double { + val sinus = sin(clipLat(this) * PI / MAX_LONGITUDE) + return 0.5 - ln((1 + sinus) / (1 - sinus)) / (4 * PI) +} + +fun Double.lonToX01(): Double { + return (clipLon(this) - MIN_LONGITUDE) / (MAX_LONGITUDE - MIN_LONGITUDE) +} + +//fun Double.y01ToLat(): Double { +// return 90 - 360 * atan(exp((this - 0.5) * 2 * PI)) / PI +//} + +//fun Double.x01ToLon(): Double { +// return MIN_LONGITUDE + (MAX_LONGITUDE - MIN_LONGITUDE) * this +//} + +private fun clipLat(latitude: Double): Double { + return clip(latitude, MIN_LATITUDE, MAX_LATITUDE) +} + +private fun clipLon(longitude: Double): Double { + var result = longitude + while (result < MIN_LONGITUDE) result += 360.0 + while (result > MAX_LONGITUDE) result -= 360.0 + return clip(result, MIN_LONGITUDE, MAX_LONGITUDE) +} + +private fun clip(currentValue: Double, minValue: Double, maxValue: Double): Double { + return min(max(currentValue, minValue), maxValue) +} diff --git a/data/src/test/java/com/rtbishop/look4sat/data/DataParserTest.kt b/domain/src/test/java/com/rtbishop/look4sat/domain/DataParserTest.kt similarity index 69% rename from data/src/test/java/com/rtbishop/look4sat/data/DataParserTest.kt rename to domain/src/test/java/com/rtbishop/look4sat/domain/DataParserTest.kt index 74ffb14b..15338746 100644 --- a/data/src/test/java/com/rtbishop/look4sat/data/DataParserTest.kt +++ b/domain/src/test/java/com/rtbishop/look4sat/domain/DataParserTest.kt @@ -15,9 +15,9 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.rtbishop.look4sat.data +package com.rtbishop.look4sat.domain -import com.rtbishop.look4sat.data.repository.DataParser +import com.rtbishop.look4sat.domain.utility.DataParser import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.runTest @@ -49,12 +49,12 @@ class DataParserTest { 1 25544U 98067A 21320.51955234 .00001288 00000+0 31985-4 0 9990 2 25544 51.6447 309.4881 0004694 203.6966 299.8876 15.48582035312205 """.trimIndent().byteInputStream() -// private val validJSONStream = """ -// [{"uuid":"UzPz4gcsNBPKPKAFPmer7g","description":"Upper side band (drifting)","alive":true,"type":"Transmitter","uplink_low":null,"uplink_high":null,"uplink_drift":null,"downlink_low":136658500,"downlink_high":null,"downlink_drift":null,"mode":"USB","mode_id":9,"uplink_mode":null,"invert":false,"baud":null,"sat_id":"SCHX-0895-2361-9925-0309","norad_cat_id":965,"status":"active","updated":"2019-04-18T05:39:53.343316Z","citation":"CITATION NEEDED - https://xkcd.com/285/","service":"Unknown","coordination":"","coordination_url":""}] -// """.trimIndent().byteInputStream() -// private val invalidJSONStream = """ -// [{"description":"Upper side band (drifting)","alive":true,"type":"Transmitter","uplink_low":null,"uplink_high":null,"uplink_drift":null,"downlink_low":136658500,"downlink_high":null,"downlink_drift":null,"mode":"USB","mode_id":9,"uplink_mode":null,"invert":false,"baud":null,"sat_id":"SCHX-0895-2361-9925-0309","norad_cat_id":965,"status":"active","updated":"2019-04-18T05:39:53.343316Z","citation":"CITATION NEEDED - https://xkcd.com/285/","service":"Unknown","coordination":"","coordination_url":""}] -// """.trimIndent().byteInputStream() + private val validJSONStream = """ + [{"uuid":"UzPz4gcsNBPKPKAFPmer7g","description":"Upper side band (drifting)","alive":true,"type":"Transmitter","uplink_low":null,"uplink_high":null,"uplink_drift":null,"downlink_low":136658500,"downlink_high":null,"downlink_drift":null,"mode":"USB","mode_id":9,"uplink_mode":null,"invert":false,"baud":null,"sat_id":"SCHX-0895-2361-9925-0309","norad_cat_id":965,"status":"active","updated":"2019-04-18T05:39:53.343316Z","citation":"CITATION NEEDED - https://xkcd.com/285/","service":"Unknown","coordination":"","coordination_url":""}] + """.trimIndent().byteInputStream() + private val invalidJSONStream = """ + [{"description":"Upper side band (drifting)","alive":true,"type":"Transmitter","uplink_low":null,"uplink_high":null,"uplink_drift":null,"downlink_low":136658500,"downlink_high":null,"downlink_drift":null,"mode":"USB","mode_id":9,"uplink_mode":null,"invert":false,"baud":null,"sat_id":"SCHX-0895-2361-9925-0309","norad_cat_id":965,"status":"active","updated":"2019-04-18T05:39:53.343316Z","citation":"CITATION NEEDED - https://xkcd.com/285/","service":"Unknown","coordination":"","coordination_url":""}] + """.trimIndent().byteInputStream() @Test fun `Given valid CSV stream returns valid data`() = runTest(testDispatcher) { @@ -87,15 +87,15 @@ class DataParserTest { assert(csvResult == tleResult) } -// @Test -// fun `Given valid JSON stream returns valid data`() = runTest(testDispatcher) { -// val parsedList = dataParser.parseJSONStream(validJSONStream) -// assert(parsedList[0].downlink == 136658500L) -// } -// -// @Test -// fun `Given invalid JSON stream returns empty list`() = runTest(testDispatcher) { -// val parsedList = dataParser.parseJSONStream(invalidJSONStream) -// assert(parsedList.isEmpty()) -// } + @Test + fun `Given valid JSON stream returns valid data`() = runTest(testDispatcher) { + val parsedList = dataParser.parseJSONStream(validJSONStream) + assert(parsedList[0].downlink == 136658500L) + } + + @Test + fun `Given invalid JSON stream returns empty list`() = runTest(testDispatcher) { + val parsedList = dataParser.parseJSONStream(invalidJSONStream) + assert(parsedList.isEmpty()) + } }