Remove PTT-aware logic, which is not necessary

I was setting the Tx frequency only when we were
in Tx mode, but the Icom 705 will accept the
command to set the Tx frequency even when in Rx mode, so
there is no need to check the PTT state before sending the command.
This commit is contained in:
Rui Oliveira committed 2026-07-24 20:09:43 +01:00
1 parent 58fb688593
commit 77dfb7bb05
4 files changed
+16 -86

No files matched your search

@@ -151,37 +151,18 @@ class Ic705Controller(
} }
override suspend fun pttOn(): Boolean = withContext(Dispatchers.IO) { override suspend fun pttOn(): Boolean = withContext(Dispatchers.IO) {
Log.w(tag, "pttOn: PTT control not used for IC-705 (radio is master)") Log.w(tag, "pttOn: not used for IC-705")
true true
} }
override suspend fun pttOff(): Boolean = withContext(Dispatchers.IO) { override suspend fun pttOff(): Boolean = withContext(Dispatchers.IO) {
Log.w(tag, "pttOff: PTT control not used for IC-705 (radio is master)") Log.w(tag, "pttOff: not used for IC-705")
true true
} }
// ── IRadioController – IC-705 extended operations ─────────────────────── // ── IRadioController – IC-705 extended operations ───────────────────────
/** /** Select VFO-A (main/RX) or VFO-B (sub/TX). */
* Read the current PTT (TX/RX) status from the radio.
* Returns **true** if the radio is currently transmitting, **false** if
* receiving, **null** on error.
*/
override suspend fun readPttStatus(): Boolean? = withContext(Dispatchers.IO) {
ioMutex.withLock {
val cmd = IcomCivProtocol.buildReadTxStatusCommand()
Log.d(tag, "CMD readPTT → ${IcomCivProtocol.toHex(cmd)}")
val payload = sendAndReadResponse(cmd, IcomCivProtocol.CMD_TX_STATUS) ?: return@withContext null
IcomCivProtocol.parseTxStatus(payload).also {
Log.d(tag, "PTT status: ${if (it == true) "TX" else if (it == false) "RX" else "unknown"}")
}
}
}
/**
* Select VFO-A (main) or VFO-B (sub).
* @param vfoA true → VFO-A, false → VFO-B
*/
override suspend fun setVfo(vfoA: Boolean): Boolean = withContext(Dispatchers.IO) { override suspend fun setVfo(vfoA: Boolean): Boolean = withContext(Dispatchers.IO) {
val cmd = if (vfoA) IcomCivProtocol.buildSelectVfoACommand() val cmd = if (vfoA) IcomCivProtocol.buildSelectVfoACommand()
else IcomCivProtocol.buildSelectVfoBCommand() else IcomCivProtocol.buildSelectVfoBCommand()
@@ -212,7 +193,7 @@ class Ic705Controller(
/** /**
* Set TX VFO frequency via CMD 0x25 sub 0x01 (unselected VFO). * Set TX VFO frequency via CMD 0x25 sub 0x01 (unselected VFO).
* Used in split mode while PTT is pressed. * Sent every tracking cycle in split mode alongside [setWorkingFrequency].
*/ */
override suspend fun setTxVfoFrequency(frequencyHz: Long): Boolean = withContext(Dispatchers.IO) { override suspend fun setTxVfoFrequency(frequencyHz: Long): Boolean = withContext(Dispatchers.IO) {
Log.d(tag, "setTxVfoFrequency (0x25/01): ${frequencyHz}Hz") Log.d(tag, "setTxVfoFrequency (0x25/01): ${frequencyHz}Hz")
@@ -57,9 +57,7 @@ object IcomCivProtocol {
const val CMD_CTCSS_TONE: Byte = 0x1B const val CMD_CTCSS_TONE: Byte = 0x1B
/** Read/write misc settings (used for enabling CTCSS encode). */ /** Read/write misc settings (used for enabling CTCSS encode). */
const val CMD_MISC_SETTING: Byte = 0x16 const val CMD_MISC_SETTING: Byte = 0x16
/** Read/write TX-inhibit / transmit status. */ /** Read/write selected-VFO frequency (cmd 0x25). */
const val CMD_TX_STATUS: Byte = 0x1C
/** Read/write selected-VFO frequency (cmd 0x25, sub 0x00). */
const val CMD_SELECTED_VFO_FREQ: Byte = 0x25 const val CMD_SELECTED_VFO_FREQ: Byte = 0x25
// ── Sub-command bytes ────────────────────────────────────────────────── // ── Sub-command bytes ──────────────────────────────────────────────────
@@ -71,8 +69,6 @@ object IcomCivProtocol {
const val SUB_SPLIT_OFF: Byte = 0x00 const val SUB_SPLIT_OFF: Byte = 0x00
/** Sub for CMD_DUPLEX_SPLIT: SPLIT ON. */ /** Sub for CMD_DUPLEX_SPLIT: SPLIT ON. */
const val SUB_SPLIT_ON: Byte = 0x01 const val SUB_SPLIT_ON: Byte = 0x01
/** Sub for CMD_TX_STATUS: query TX/RX state. */
const val SUB_TX_STATE: Byte = 0x00
/** Sub for CMD_SELECTED_VFO_FREQ: selected (active) VFO frequency. */ /** Sub for CMD_SELECTED_VFO_FREQ: selected (active) VFO frequency. */
const val SUB_SELECTED_VFO: Byte = 0x00 const val SUB_SELECTED_VFO: Byte = 0x00
/** Sub for CMD_SELECTED_VFO_FREQ: unselected (inactive / TX in split) VFO frequency. */ /** Sub for CMD_SELECTED_VFO_FREQ: unselected (inactive / TX in split) VFO frequency. */
@@ -200,9 +196,6 @@ object IcomCivProtocol {
return frame(CMD_DUPLEX_SPLIT, sub) return frame(CMD_DUPLEX_SPLIT, sub)
} }
/** Query TX/RX status (CMD 0x1C sub 0x00). Radio replies with 0x00=RX, 0x01=TX. */
fun buildReadTxStatusCommand(): ByteArray = frame(CMD_TX_STATUS, SUB_TX_STATE)
/** /**
* Enable/disable CTCSS encode (CMD 0x16 sub 0x42). * Enable/disable CTCSS encode (CMD 0x16 sub 0x42).
* 0x01 = CTCSS encoder ON, 0x00 = OFF. * 0x01 = CTCSS encoder ON, 0x00 = OFF.
@@ -293,26 +286,6 @@ object IcomCivProtocol {
return freqHz to mode return freqHz to mode
} }
/**
* Parse TX status from a CMD_TX_STATUS reply payload.
*
* The IC-705 responds to a `1C 00` query with:
* FE FE E0 A4 1C 00 <status> FD
* After [parseResponse] strips the frame header the payload is:
* payload[0] = 0x00 (sub-command echo)
* payload[1] = status byte (0x00 = RX, 0x01 = TX)
*
* Returns true if transmitting, false if receiving, null on error.
*/
fun parseTxStatus(payload: ByteArray): Boolean? {
if (payload.size < 2) return null
return when (payload[1]) {
0x00.toByte() -> false // RX
0x01.toByte() -> true // TX
else -> null
}
}
/** Hex dump of bytes, useful for debug logging. */ /** Hex dump of bytes, useful for debug logging. */
fun toHex(bytes: ByteArray): String = fun toHex(bytes: ByteArray): String =
bytes.joinToString(" ") { String.format(Locale.US, "%02X", it.toInt() and 0xFF) } bytes.joinToString(" ") { String.format(Locale.US, "%02X", it.toInt() and 0xFF) }
@@ -380,33 +380,17 @@ class RadioTrackingService(
val rxRadioFreq = rxBaseCalc?.let { pos.getDownlinkFreq(it) } val rxRadioFreq = rxBaseCalc?.let { pos.getDownlinkFreq(it) }
if (radio.isConnected) { if (radio.isConnected) {
// Query PTT state — don't assume silence; filter broadcast noise // Update both VFOs every cycle — no PTT polling needed.
val isPtt = radio.readPttStatus() // 0x25/00 = active (RX) VFO, 0x25/01 = inactive (TX) VFO.
Log.d(tag, "Split loop: isPTT=$isPtt txFreq=$txRadioFreq rxFreq=$rxRadioFreq") if (rxRadioFreq != null) {
Log.d(tag, "Split loop RX (0x25/00): ${rxRadioFreq}Hz")
when (isPtt) { radio.setWorkingFrequency(rxRadioFreq)
true -> {
// PTT ON → radio's active VFO is VFO-B (TX)
// Use 0x25/01 (unselected VFO) to target VFO-B directly
if (txRadioFreq != null) {
Log.d(tag, "Split loop TX update (0x25/01): ${txRadioFreq}Hz")
radio.setTxVfoFrequency(txRadioFreq)
}
} }
false -> { if (txRadioFreq != null) {
// PTT OFF → radio's active VFO is VFO-A (RX) Log.d(tag, "Split loop TX (0x25/01): ${txRadioFreq}Hz")
// Use 0x25/00 (selected/active VFO) to update RX radio.setTxVfoFrequency(txRadioFreq)
if (rxRadioFreq != null) {
Log.d(tag, "Split loop RX update (0x25/00): ${rxRadioFreq}Hz")
radio.setWorkingFrequency(rxRadioFreq)
}
}
null -> {
// Could not read PTT (broadcast noise race) — skip this tick
Log.d(tag, "Split loop: PTT read failed, skipping tick")
} }
} }
}
_state.update { _state.update {
it.copy( it.copy(
@@ -41,13 +41,6 @@ interface IRadioController {
// ── Extended operations (IC-705 / CI-V) ────────────────────────────── // ── Extended operations (IC-705 / CI-V) ──────────────────────────────
/**
* Read the current PTT status from the radio.
* Returns true if transmitting, false if receiving, null on error.
* Default: not supported.
*/
suspend fun readPttStatus(): Boolean? = null
/** /**
* Select the active VFO. * Select the active VFO.
* @param vfoA true → VFO-A (main/RX), false → VFO-B (sub/TX in split). * @param vfoA true → VFO-A (main/RX), false → VFO-B (sub/TX in split).
@@ -62,15 +55,14 @@ interface IRadioController {
suspend fun setSplitMode(enabled: Boolean): Boolean = false suspend fun setSplitMode(enabled: Boolean): Boolean = false
/** /**
* Set the frequency of the currently active VFO (CMD 0x25 sub 0x00). * Set the frequency of the currently active VFO (IC-705: CMD 0x25 sub 0x00).
* Default: delegates to [setFrequency]. * Default: delegates to [setFrequency].
*/ */
suspend fun setWorkingFrequency(frequencyHz: Long): Boolean = setFrequency(frequencyHz) suspend fun setWorkingFrequency(frequencyHz: Long): Boolean = setFrequency(frequencyHz)
/** /**
* Set the frequency of the inactive (TX) VFO via CMD 0x25 sub 0x01. * Set the frequency of the inactive/TX VFO (IC-705: CMD 0x25 sub 0x01).
* Used in split mode when PTT is pressed: the radio has made VFO-B * Sent every tracking cycle alongside [setWorkingFrequency] in split mode.
* the active VFO, so sub 0x01 targets it directly.
* Default: delegates to [setWorkingFrequency]. * Default: delegates to [setWorkingFrequency].
*/ */
suspend fun setTxVfoFrequency(frequencyHz: Long): Boolean = setWorkingFrequency(frequencyHz) suspend fun setTxVfoFrequency(frequencyHz: Long): Boolean = setWorkingFrequency(frequencyHz)