Skip to content

Commit 61e3fc9

Browse files
committed
Merge remote-tracking branch 'upstream/main'
2 parents cc2a0e5 + 0c21ac5 commit 61e3fc9

File tree

6 files changed

+223
-73
lines changed

6 files changed

+223
-73
lines changed

android-src/KV4PHT/app/src/main/java/com/vagell/kv4pht/radio/RxStreamParser.java renamed to android-src/KV4PHT/app/src/main/java/com/vagell/kv4pht/radio/ESP32DataStreamParser.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import java.io.ByteArrayOutputStream;
66
import java.util.function.BiConsumer;
77

8-
public class RxStreamParser {
8+
public class ESP32DataStreamParser {
99

1010
private int matchedDelimiterTokens = 0;
1111
private byte command;
@@ -15,7 +15,7 @@ public class RxStreamParser {
1515

1616
private final BiConsumer<Byte, byte[]> onCommand;
1717

18-
public RxStreamParser(BiConsumer<Byte, byte[]> onCommand) {
18+
public ESP32DataStreamParser(BiConsumer<Byte, byte[]> onCommand) {
1919
this.onCommand = onCommand;
2020
}
2121

@@ -32,16 +32,24 @@ public byte[] extractAudioAndHandleCommands(byte[] newData) {
3232
}
3333
} else if (matchedDelimiterTokens == COMMAND_DELIMITER.length) {
3434
command = b;
35+
// Log.d("DEBUG", "command: " + command);
3536
matchedDelimiterTokens++;
3637
} else if (matchedDelimiterTokens == COMMAND_DELIMITER.length + 1) {
3738
commandParamLen = b;
3839
commandParams.reset();
3940
matchedDelimiterTokens++;
41+
42+
if (commandParamLen == 0) { // If this command has no params...
43+
lookaheadBuffer.reset();
44+
onCommand.accept(command, commandParams.toByteArray());
45+
resetParser(audioOut);
46+
}
4047
} else {
4148
commandParams.write(b);
4249
matchedDelimiterTokens++;
4350
lookaheadBuffer.reset();
4451
if (commandParams.size() == commandParamLen) {
52+
// Log.d("DEBUG", "commandParams: " + commandParams);
4553
onCommand.accept(command, commandParams.toByteArray());
4654
resetParser(audioOut);
4755
}

android-src/KV4PHT/app/src/main/java/com/vagell/kv4pht/radio/RadioAudioService.java

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,10 @@ public class RadioAudioService extends Service {
152152
// Delimiter must match ESP32 code
153153
static final byte[] COMMAND_DELIMITER = new byte[] {(byte)0xDE, (byte)0xAD, (byte)0xBE, (byte)0xEF, (byte)0xDE, (byte)0xAD, (byte)0xBE, (byte)0xEF};
154154
private static final byte COMMAND_SMETER_REPORT = 0x53; // Ascii "S"
155+
private static final byte COMMAND_PHYS_PTT_DOWN = 0x44; // Ascii "D"
156+
private static final byte COMMAND_PHYS_PTT_UP = 0x55; // Ascii "U"
155157

156-
private final RxStreamParser rxStreamParser = new RxStreamParser(this::handleParsedCommand);
158+
private final ESP32DataStreamParser esp32DataStreamParser = new ESP32DataStreamParser(this::handleParsedCommand);
157159

158160
// AFSK modem
159161
private Afsk1200Modulator afskModulator = null;
@@ -183,7 +185,7 @@ public class RadioAudioService extends Service {
183185
private static float max70cmTxFreq = 450.0f; // US 70cm band upper limit, in MHz (will be overwritten by user setting)
184186
private static final float UHF_MAX_FREQ = 470.0f; // DRA818U upper limit, in MHz
185187

186-
private String activeFrequencyStr = String.format(java.util.Locale.US, "%.4f", min2mTxFreq); // 4 decimal places, in MHz
188+
private String activeFrequencyStr = null;
187189
private int squelch = 0;
188190
private String callsign = null;
189191
private int consecutiveSilenceBytes = 0; // To determine when to move scan after silence
@@ -303,8 +305,8 @@ public void setBandwidth(String bandwidth) {
303305
public void setMinRadioFreq(float newMinFreq) {
304306
minRadioFreq = newMinFreq;
305307

306-
// Detect if we're moving from VHF to UHF, and move fix active frequency to within band.
307-
if (activeFrequencyStr != null && Float.parseFloat(activeFrequencyStr) < minRadioFreq) {
308+
// Detect if we're moving from VHF to UHF, and move active frequency to within band.
309+
if (mode != MODE_STARTUP && activeFrequencyStr != null && Float.parseFloat(activeFrequencyStr) < minRadioFreq) {
308310
tuneToFreq(String.format(java.util.Locale.US, "%.4f", min70cmTxFreq), squelch, true);
309311
callbacks.forceTunedToFreq(activeFrequencyStr);
310312
}
@@ -313,8 +315,8 @@ public void setMinRadioFreq(float newMinFreq) {
313315
public void setMaxRadioFreq(float newMaxFreq) {
314316
maxRadioFreq = newMaxFreq;
315317

316-
// Detect if we're moving from UHF to VHF, and move fix active frequency to within band.
317-
if (activeFrequencyStr != null && Float.parseFloat(activeFrequencyStr) > maxRadioFreq) {
318+
// Detect if we're moving from UHF to VHF, and move active frequency to within band.
319+
if (mode != MODE_STARTUP && activeFrequencyStr != null && Float.parseFloat(activeFrequencyStr) > maxRadioFreq) {
318320
tuneToFreq(String.format(java.util.Locale.US, "%.4f", min2mTxFreq), squelch, true);
319321
callbacks.forceTunedToFreq(activeFrequencyStr);
320322
}
@@ -479,6 +481,8 @@ public interface RadioAudioServiceCallbacks {
479481
public void sentAprsBeacon(double latitude, double longitude);
480482
public void unknownLocation();
481483
public void forceTunedToFreq(String newFreqStr);
484+
public void forcedPttStart();
485+
public void forcedPttEnd();
482486
}
483487

484488
public void setCallbacks(RadioAudioServiceCallbacks callbacks) {
@@ -1283,6 +1287,10 @@ private void handleESP32Data(byte[] data) {
12831287
} */
12841288
// Log.d("DEBUG", "Num bytes from ESP32: " + data.length);
12851289

1290+
// Handle and remove any commands (e.g. S-meter updates, physical PTT up/down)
1291+
// which may be intermixed with audio bytes.
1292+
data = esp32DataStreamParser.extractAudioAndHandleCommands(data);
1293+
12861294
if (mode == MODE_STARTUP) {
12871295
try {
12881296
// TODO rework this to use same command-handling as s-meter updates (below)
@@ -1334,9 +1342,6 @@ private void handleESP32Data(byte[] data) {
13341342
}
13351343

13361344
if (mode == MODE_RX || mode == MODE_SCAN) {
1337-
// Handle and remove any commands (e.g. S-meter updates) embedded in the audio.
1338-
data = rxStreamParser.extractAudioAndHandleCommands(data);
1339-
13401345
if (prebufferComplete && audioTrack != null) {
13411346
synchronized (audioTrack) {
13421347
if (afskDemodulator != null) { // Avoid race condition at app start.
@@ -1390,13 +1395,8 @@ private void handleESP32Data(byte[] data) {
13901395
}
13911396
}
13921397
} else if (mode == MODE_TX) {
1393-
// Print any data we get in MODE_TX (we're not expecting any, this is either leftover rx bytes or debug info).
1394-
/* try {
1395-
String dataStr = new String(data, "UTF-8");
1396-
Log.d("DEBUG", "Unexpected data from ESP32 during MODE_TX: " + dataStr);
1397-
} catch (UnsupportedEncodingException e) {
1398-
throw new RuntimeException(e);
1399-
} */
1398+
// We only expect physical PTT up and down commands in this mode, but it's already
1399+
// been handled by esp32DataStreamParser() earlier in this method.
14001400
}
14011401

14021402
if (mode == MODE_BAD_FIRMWARE) {
@@ -1443,6 +1443,16 @@ private void handleParsedCommand(byte cmd, byte[] param) {
14431443

14441444
callbacks.sMeterUpdate(sMeter9Value);
14451445
}
1446+
} else if (cmd == COMMAND_PHYS_PTT_DOWN) {
1447+
if (mode == MODE_RX) { // Note that people can't hit PTT in the middle of a scan.
1448+
startPtt();
1449+
callbacks.forcedPttStart();
1450+
}
1451+
} else if (cmd == COMMAND_PHYS_PTT_UP) {
1452+
if (mode == MODE_TX) {
1453+
endPtt();
1454+
callbacks.forcedPttEnd();
1455+
}
14461456
} else {
14471457
Log.d("DEBUG", "Unknown cmd received from ESP32: 0x" + Integer.toHexString(cmd & 0xFF) +
14481458
" paramLen=" + param.length);

android-src/KV4PHT/app/src/main/java/com/vagell/kv4pht/ui/MainActivity.java

Lines changed: 45 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ public class MainActivity extends AppCompatActivity {
121121
private static final String ACTION_USB_PERMISSION = "com.vagell.kv4pht.USB_PERMISSION";
122122

123123
// Radio params and related settings
124-
private String activeFrequencyStr = "144.0000";
124+
private String activeFrequencyStr = "0.0000";
125125
private int squelch = 0;
126126
private String callsign = null;
127127
private boolean stickyPTT = false;
@@ -209,7 +209,7 @@ public void run() {
209209

210210
if (radioAudioService != null) {
211211
radioAudioService.tuneToFreq(freq, squelch, false); // Stay on the same freq as the now-deleted memory
212-
tuneToFreqUi(freq);
212+
tuneToFreqUi(freq, false);
213213
}
214214

215215
viewModel.setCallback(null);
@@ -489,7 +489,17 @@ public void unknownLocation() {
489489
public void forceTunedToFreq(String newFreqStr) {
490490
// This is called when RadioAudioService is changing bands, and we need
491491
// to reflect that in the UI.
492-
tuneToFreqUi(newFreqStr);
492+
tuneToFreqUi(newFreqStr, true);
493+
}
494+
495+
@Override
496+
public void forcedPttStart() { // When user pushes physical PTT.
497+
startPttUi(false);
498+
}
499+
500+
@Override
501+
public void forcedPttEnd() { // When user releases physical PTT.
502+
endPttUi();
493503
}
494504
};
495505

@@ -1004,32 +1014,6 @@ public void run() {
10041014
selectMemoryGroup(lastGroupSetting.value);
10051015
}
10061016

1007-
if (lastMemoryId != null && !lastMemoryId.value.equals("-1")) {
1008-
activeMemoryId = Integer.parseInt(lastMemoryId.value);
1009-
1010-
if (radioAudioService != null) {
1011-
radioAudioService.setActiveMemoryId(activeMemoryId);
1012-
}
1013-
} else {
1014-
activeMemoryId = -1;
1015-
if (lastFreq != null) {
1016-
activeFrequencyStr = lastFreq.value;
1017-
} else {
1018-
activeFrequencyStr = "144.0000";
1019-
}
1020-
1021-
if (radioAudioService != null) {
1022-
radioAudioService.setActiveMemoryId(activeMemoryId);
1023-
radioAudioService.setActiveFrequencyStr(activeFrequencyStr);
1024-
}
1025-
}
1026-
1027-
if (bandwidthSetting != null) {
1028-
if (radioAudioService != null) {
1029-
radioAudioService.setBandwidth(bandwidthSetting.value);
1030-
}
1031-
}
1032-
10331017
if (min2mTxFreqSetting != null) {
10341018
int min2mTxFreq = Integer.parseInt(min2mTxFreqSetting.value);
10351019
if (radioAudioService != null) {
@@ -1058,6 +1042,27 @@ public void run() {
10581042
}
10591043
}
10601044

1045+
if (lastMemoryId != null && !lastMemoryId.value.equals("-1")) {
1046+
activeMemoryId = Integer.parseInt(lastMemoryId.value);
1047+
1048+
if (radioAudioService != null) {
1049+
radioAudioService.setActiveMemoryId(activeMemoryId);
1050+
}
1051+
} else {
1052+
activeMemoryId = -1;
1053+
if (lastFreq != null) {
1054+
activeFrequencyStr = lastFreq.value;
1055+
} else {
1056+
activeFrequencyStr = "0.0000"; // Will force radioAudioService to use minimum freq in current band
1057+
}
1058+
}
1059+
1060+
if (bandwidthSetting != null) {
1061+
if (radioAudioService != null) {
1062+
radioAudioService.setBandwidth(bandwidthSetting.value);
1063+
}
1064+
}
1065+
10611066
if (micGainBoostSetting != null) {
10621067
String micGainBoost = micGainBoostSetting.value;
10631068
if (radioAudioService != null) {
@@ -1076,7 +1081,7 @@ public void run() {
10761081
} else {
10771082
if (radioAudioService != null) {
10781083
radioAudioService.tuneToFreq(activeFrequencyStr, squelch, radioAudioService.getMode() == RadioAudioService.MODE_RX);
1079-
tuneToFreqUi(activeFrequencyStr);
1084+
tuneToFreqUi(activeFrequencyStr, radioAudioService.getMode() == RadioAudioService.MODE_RX);
10801085
}
10811086
}
10821087
if (radioAudioService != null) {
@@ -1270,7 +1275,7 @@ public void onClick(View v) {
12701275
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
12711276
if (radioAudioService != null) {
12721277
radioAudioService.tuneToFreq(activeFrequencyField.getText().toString(), squelch, false);
1273-
tuneToFreqUi(RadioAudioService.makeSafeHamFreq(activeFrequencyField.getText().toString())); // Fixes any invalid freq user may have entered.
1278+
tuneToFreqUi(RadioAudioService.makeSafeHamFreq(activeFrequencyField.getText().toString()), false); // Fixes any invalid freq user may have entered.
12741279
}
12751280

12761281
hideKeyboard();
@@ -1357,13 +1362,20 @@ private void hideKeyboard() {
13571362
* Updates the UI to represent that we've tuned to the given frequency. Does not actually
13581363
* interact with the radio (use RadioAudioService for that).
13591364
*/
1360-
private void tuneToFreqUi(String frequencyStr) {
1365+
private void tuneToFreqUi(String frequencyStr, boolean wasForced) {
13611366
final Context ctx = this;
13621367
activeFrequencyStr = radioAudioService.validateFrequency(frequencyStr);
13631368
activeMemoryId = -1;
13641369

1370+
showMemoryName("Simplex");
1371+
showFrequency(activeFrequencyStr);
1372+
1373+
// Unhighlight all memory rows, since this is a simplex frequency.
1374+
viewModel.highlightMemory(null);
1375+
memoriesAdapter.notifyDataSetChanged();
1376+
13651377
// Save most recent freq so we can restore it on app restart
1366-
if (threadPoolExecutor == null) {
1378+
if (threadPoolExecutor == null || wasForced) { // wasForced means user didn't actually type in the frequency (we shouldn't save it)
13671379
return;
13681380
}
13691381
threadPoolExecutor.execute(new Runnable() {
@@ -1391,13 +1403,6 @@ public void run() {
13911403
}
13921404
}
13931405
});
1394-
1395-
showMemoryName("Simplex");
1396-
showFrequency(activeFrequencyStr);
1397-
1398-
// Unhighlight all memory rows, since this is a simplex frequency.
1399-
viewModel.highlightMemory(null);
1400-
memoriesAdapter.notifyDataSetChanged();
14011406
}
14021407

14031408
/**
@@ -1452,7 +1457,6 @@ private void showFrequency(String frequency) {
14521457
runOnUiThread(new Runnable() {
14531458
@Override
14541459
public void run() {
1455-
Log.d("DEBUG", "showFrequency: " + frequency);
14561460
EditText activeFrequencyField = findViewById(R.id.activeFrequency);
14571461
activeFrequencyField.setText(frequency);
14581462
activeFrequencyStr = frequency;

0 commit comments

Comments
 (0)