mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
fix(lotw): only collect satellite QSO grids (PROP_MODE=SAT); keep both grids of VUCC_GRIDS pairs
This commit is contained in:
1 parent
72d5e13acf
commit
9ed2aebc9d
3 files changed
+115
-7
No files matched your search
+25
-6
@@ -94,14 +94,33 @@ class LoTWRepository : ILoTWRepository {
|
|||||||
// (LoTW writes it lowercase). Match case-insensitively to be safe.
|
// (LoTW writes it lowercase). Match case-insensitively to be safe.
|
||||||
if (!body.contains("<eoh>", ignoreCase = true)) return null
|
if (!body.contains("<eoh>", ignoreCase = true)) return null
|
||||||
val grids = mutableSetOf<String>()
|
val grids = mutableSetOf<String>()
|
||||||
|
// ADIF fields of one QSO record span multiple lines and are terminated by
|
||||||
|
// <EOR>. Satellite QSOs carry <PROP_MODE:3>SAT (plus <SAT_NAME>); ground
|
||||||
|
// QSOs omit it. Grid fields (GRIDSQUARE / VUCC_GRIDS) must only be
|
||||||
|
// collected for records whose PROP_MODE is SAT, otherwise the map mixes
|
||||||
|
// in terrestrial contacts.
|
||||||
|
var propMode: String? = null
|
||||||
for (raw in body.lineSequence()) {
|
for (raw in body.lineSequence()) {
|
||||||
val line = raw.trim()
|
val line = raw.trim()
|
||||||
val value = when {
|
when {
|
||||||
line.startsWith("<GRIDSQUARE:") -> line.substringAfter('>')
|
line.equals("<EOR>", ignoreCase = true) -> propMode = null
|
||||||
line.startsWith("<VUCC_GRIDS:") -> line.substringAfter('>')
|
line.startsWith("<PROP_MODE:") -> {
|
||||||
else -> continue
|
propMode = line.substringAfter('>').substringBefore("E<").trim().uppercase()
|
||||||
}.substringBefore("E<").trim().uppercase()
|
}
|
||||||
if (value.length >= 4) grids.add(value.take(4))
|
line.startsWith("<GRIDSQUARE:") || line.startsWith("<VUCC_GRIDS:") -> {
|
||||||
|
if (propMode == "SAT") {
|
||||||
|
// VUCC_GRIDS holds a comma-separated PAIR of grids
|
||||||
|
// ("EN52en,EN53fa") for contacts spanning two squares —
|
||||||
|
// split on ',' and take the 4-char field of each, or the
|
||||||
|
// second grid is silently dropped.
|
||||||
|
val value = line.substringAfter('>').substringBefore("E<")
|
||||||
|
value.split(',').forEach { grid ->
|
||||||
|
val field = grid.trim().uppercase()
|
||||||
|
if (field.length >= 4) grids.add(field.take(4))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return grids
|
return grids
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-1
@@ -214,7 +214,10 @@ private class FakeSettingsRepo(dataSources: DataSourcesSettings = defaultDataSou
|
|||||||
)
|
)
|
||||||
|
|
||||||
override val otherSettings: StateFlow<OtherSettings> = MutableStateFlow(
|
override val otherSettings: StateFlow<OtherSettings> = MutableStateFlow(
|
||||||
OtherSettings(false, false, false, false, false, false, false, false)
|
OtherSettings(
|
||||||
|
false, false, false, false, false, false, false,
|
||||||
|
shouldSeeWarning = false, shouldSeeWhatsNew = false
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
override val dataSourcesSettings: MutableStateFlow<DataSourcesSettings> = MutableStateFlow(dataSources)
|
override val dataSourcesSettings: MutableStateFlow<DataSourcesSettings> = MutableStateFlow(dataSources)
|
||||||
@@ -270,6 +273,24 @@ private class FakeSettingsRepo(dataSources: DataSourcesSettings = defaultDataSou
|
|||||||
override fun getAmSatCallsign(): String = ""
|
override fun getAmSatCallsign(): String = ""
|
||||||
|
|
||||||
override fun setAmSatCallsign(callsign: String) = Unit
|
override fun setAmSatCallsign(callsign: String) = Unit
|
||||||
|
|
||||||
|
override val wavelogSettings: StateFlow<com.rtbishop.look4sat.core.domain.model.WavelogSettings> =
|
||||||
|
MutableStateFlow(com.rtbishop.look4sat.core.domain.model.WavelogSettings())
|
||||||
|
|
||||||
|
override fun updateWavelogSettings(settings: com.rtbishop.look4sat.core.domain.model.WavelogSettings) = Unit
|
||||||
|
|
||||||
|
override fun getWorkedGrids(): Set<String> = emptySet()
|
||||||
|
|
||||||
|
private val workedGrids = MutableStateFlow(emptySet<String>())
|
||||||
|
|
||||||
|
override fun setWorkedGrids(grids: Set<String>) {
|
||||||
|
workedGrids.value = grids
|
||||||
|
}
|
||||||
|
|
||||||
|
override val lotwSettings: StateFlow<com.rtbishop.look4sat.core.domain.model.LoTWSettings> =
|
||||||
|
MutableStateFlow(com.rtbishop.look4sat.core.domain.model.LoTWSettings())
|
||||||
|
|
||||||
|
override fun updateLoTWSettings(settings: com.rtbishop.look4sat.core.domain.model.LoTWSettings) = Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun defaultDataSourcesSettings(): DataSourcesSettings {
|
private fun defaultDataSourcesSettings(): DataSourcesSettings {
|
||||||
|
|||||||
+68
@@ -0,0 +1,68 @@
|
|||||||
|
package com.rtbishop.look4sat.core.data.repository
|
||||||
|
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertNull
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class LoTWRepositoryTest {
|
||||||
|
|
||||||
|
private val repo = LoTWRepository()
|
||||||
|
|
||||||
|
private fun report(vararg records: String): String =
|
||||||
|
buildString {
|
||||||
|
append("<ADIF_VERS:5>3.1.4\n")
|
||||||
|
append("<PROGRAMID:8>look4sat\n")
|
||||||
|
append("<eoh>\n")
|
||||||
|
records.forEach { append(it).append("\n") }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun parseRejectsBodyWithoutEoh() {
|
||||||
|
assertNull(repo.parseConfirmedGrids("<HTML>Username/password incorrect</HTML>"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun parseCollectsOnlySatelliteGrids() {
|
||||||
|
// Satellite QSO: PROP_MODE SAT, confirmed, opponent grid.
|
||||||
|
val satQso = "<CALL:6>BA7OPF\n" +
|
||||||
|
"<QSO_DATE:8>20260820\n" +
|
||||||
|
"<PROP_MODE:3>SAT\n" +
|
||||||
|
"<SAT_NAME:5>FO-29\n" +
|
||||||
|
"<GRIDSQUARE:4>NL47\n" +
|
||||||
|
"<EOR>\n"
|
||||||
|
// Ground QSO: same grid but no PROP_MODE -> must be excluded.
|
||||||
|
val groundQso = "<CALL:6>BA7OPF\n" +
|
||||||
|
"<QSO_DATE:8>20260821\n" +
|
||||||
|
"<GRIDSQUARE:4>NL47\n" +
|
||||||
|
"<EOR>\n"
|
||||||
|
val result = repo.parseConfirmedGrids(report(satQso, groundQso))
|
||||||
|
assertEquals(setOf("NL47"), result)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun parseSupportsVuccGridPairsAndSixCharGrids() {
|
||||||
|
val satQso = "<PROP_MODE:3>SAT\n" +
|
||||||
|
"<SAT_NAME:5>SO-50\n" +
|
||||||
|
"<GRIDSQUARE:6>OM60IL\n" +
|
||||||
|
"<VUCC_GRIDS:11>EN52en,EN53fa\n" +
|
||||||
|
"<EOR>\n"
|
||||||
|
val result = repo.parseConfirmedGrids(report(satQso))
|
||||||
|
assertEquals(setOf("OM60", "EN52", "EN53"), result)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun parsePropModeDoesNotLeakAcrossRecords() {
|
||||||
|
// PROP_MODE SAT record first, then a ground QSO with a grid — the second
|
||||||
|
// record must not inherit the satellite flag after its own <EOR>.
|
||||||
|
val satQso = "<PROP_MODE:3>SAT\n<GRIDSQUARE:4>OL62\n<EOR>\n"
|
||||||
|
val groundQso = "<GRIDSQUARE:4>PM00\n<EOR>\n"
|
||||||
|
val result = repo.parseConfirmedGrids(report(satQso, groundQso))
|
||||||
|
assertEquals(setOf("OL62"), result)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun parseReturnsEmptySetForReportWithoutGrids() {
|
||||||
|
val satQso = "<PROP_MODE:3>SAT\n<SAT_NAME:5>AO-07\n<EOR>\n"
|
||||||
|
assertEquals(emptySet<String>(), repo.parseConfirmedGrids(report(satQso)))
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user