diff --git a/codec2talkie/build.gradle b/codec2talkie/build.gradle index 5515542..558b477 100644 --- a/codec2talkie/build.gradle +++ b/codec2talkie/build.gradle @@ -10,8 +10,8 @@ android { applicationId "com.radio.codec2talkie" minSdkVersion 23 targetSdkVersion 30 - versionCode 152 - versionName "1.52" + versionCode 153 + versionName "1.53" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/MainActivity.java b/codec2talkie/src/main/java/com/radio/codec2talkie/MainActivity.java index f23969e..37adf9a 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/MainActivity.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/MainActivity.java @@ -491,12 +491,18 @@ public class MainActivity extends AppCompatActivity implements ServiceConnection status += getString(R.string.voax25_label); } + // Lora aprs text packets + boolean textPacketsEnabled = SettingsWrapper.isTextPacketsEnabled(_sharedPreferences); + if (textPacketsEnabled) { + status += getString(R.string.text_packets_label); + } // Digirepeater boolean isDigirepeaterEnabled = _sharedPreferences.getBoolean(PreferenceKeys.AX25_DIGIREPEATER_ENABLED, false); if (isDigirepeaterEnabled) { status += getString(R.string.digirepeater_label); } + // APRSIS boolean aprsisEnabled = SettingsWrapper.isAprsIsEnabled(_sharedPreferences); if (aprsisEnabled) { status += getString(R.string.aprsis_label); diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/Ax25.java b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/Ax25.java index d20a04e..0763685 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/Ax25.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/Ax25.java @@ -15,6 +15,8 @@ import com.radio.codec2talkie.settings.SettingsWrapper; import com.radio.codec2talkie.transport.Transport; import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; public class Ax25 implements Protocol { @@ -26,6 +28,7 @@ public class Ax25 implements Protocol { private String _digipath; private boolean _isVoax25Enabled; private boolean _isDigiRepeaterEnabled; + private boolean _useTextPackets; private ProtocolCallback _parentProtocolCallback; @@ -44,6 +47,7 @@ public class Ax25 implements Protocol { // NOTE, may need to pass through sendData/sendAudio _digipath = sharedPreferences.getString(PreferenceKeys.AX25_DIGIPATH, "").toUpperCase(); _isVoax25Enabled = SettingsWrapper.isVoax25Enabled(sharedPreferences); + _useTextPackets = SettingsWrapper.isTextPacketsEnabled(sharedPreferences); _isDigiRepeaterEnabled = sharedPreferences.getBoolean(PreferenceKeys.AX25_DIGIREPEATER_ENABLED, false); } @@ -93,7 +97,7 @@ public class Ax25 implements Protocol { ax25Packet.digipath = path == null ? _digipath : path; ax25Packet.isAudio = false; ax25Packet.rawData = dataPacket; - byte[] ax25Frame = ax25Packet.toBinary(); + byte[] ax25Frame = _useTextPackets ? ax25Packet.toTextBinary() : ax25Packet.toBinary(); if (ax25Frame == null) { Log.e(TAG, "Cannot convert AX.25 data packet to binary"); _parentProtocolCallback.onProtocolTxError(); diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/ax25/AX25Packet.java b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/ax25/AX25Packet.java index 4905f70..1c2c33d 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/ax25/AX25Packet.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/ax25/AX25Packet.java @@ -2,11 +2,14 @@ package com.radio.codec2talkie.protocol.ax25; import androidx.annotation.NonNull; +import com.radio.codec2talkie.protocol.aprs.tools.AprsIsData; import com.radio.codec2talkie.tools.DebugTools; import com.radio.codec2talkie.tools.TextTools; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; public class AX25Packet { @@ -28,6 +31,21 @@ public class AX25Packet { public void fromBinary(byte[] data) { isValid = false; if (data == null) return; + // lora text packet with 0x3c,0xff,0x01 prefix + if (data.length > 3 && data[0] == (byte)0x3c && data[1] == (byte)0xff && data[2] == (byte)0x01) { + String rawText = new String(Arrays.copyOfRange(data, 3, data.length), StandardCharsets.US_ASCII); + AprsIsData textPacket = AprsIsData.fromString(rawText); + if (textPacket != null) { + src = textPacket.src; + dst = textPacket.dst; + digipath = textPacket.rawDigipath; + rawData = textPacket.data.getBytes(StandardCharsets.US_ASCII); + isAudio = false; + isValid = true; + return; + } + } + // binary packet ByteBuffer buffer = ByteBuffer.wrap(data); try { // dst @@ -81,6 +99,18 @@ public class AX25Packet { } } + public byte[] toTextBinary() { + byte[] packetContent = toString().getBytes(StandardCharsets.US_ASCII); + // lora aprs prefix 0x3c,0xff,0x01 + ByteBuffer textPacketBuffer = ByteBuffer.allocateDirect(packetContent.length + 3); + textPacketBuffer.put((byte)0x3c).put((byte)0xff).put((byte)0x01); + textPacketBuffer.put(packetContent); + textPacketBuffer.flip(); + byte[] ax25Frame = new byte[textPacketBuffer.remaining()]; + textPacketBuffer.get(ax25Frame); + return ax25Frame; + } + public byte[] toBinary() { ByteBuffer buffer = ByteBuffer.allocate(MaximumSize); String[] rptCallsigns = new String[] {}; diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/settings/PreferenceKeys.java b/codec2talkie/src/main/java/com/radio/codec2talkie/settings/PreferenceKeys.java index e3b26c3..5e0312d 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/settings/PreferenceKeys.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/settings/PreferenceKeys.java @@ -67,6 +67,7 @@ public final class PreferenceKeys { public static String APP_AUDIO_DESTINATION = "app_audio_destination"; public static String AX25_VOAX25_ENABLE = "aprs_voax25_enable"; + public static String AX25_TEXT_PACKETS_ENABLE = "aprs_text_packets_enable"; public static String AX25_CALLSIGN = "aprs_callsign"; public static String AX25_SSID = "aprs_ssid"; public static String AX25_DIGIPATH = "aprs_digipath"; diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/settings/SettingsWrapper.java b/codec2talkie/src/main/java/com/radio/codec2talkie/settings/SettingsWrapper.java index 4229c64..c16a12e 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/settings/SettingsWrapper.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/settings/SettingsWrapper.java @@ -93,6 +93,10 @@ public class SettingsWrapper { !isFreeDvSoundModemModulation(sharedPreferences); // no voax25 in freedv } + public static boolean isTextPacketsEnabled(SharedPreferences sharedPreferences) { + return sharedPreferences.getBoolean(PreferenceKeys.AX25_TEXT_PACKETS_ENABLE, false); + } + public static boolean isAprsIsEnabled(SharedPreferences sharedPreferences) { return sharedPreferences.getBoolean(PreferenceKeys.APRS_IS_ENABLE, false); } diff --git a/codec2talkie/src/main/res/values/strings.xml b/codec2talkie/src/main/res/values/strings.xml index a93ecd5..9059b2b 100644 --- a/codec2talkie/src/main/res/values/strings.xml +++ b/codec2talkie/src/main/res/values/strings.xml @@ -232,6 +232,7 @@ Send position + 🔔 View log @@ -366,4 +367,6 @@ Rotate map with compass Show range circles Show moving stations + Enable text packets + Send lora aprs compatible text packets (0x3c,0xff,0x01 prefix) \ No newline at end of file diff --git a/codec2talkie/src/main/res/xml/preferences.xml b/codec2talkie/src/main/res/xml/preferences.xml index 326d95a..f26a660 100644 --- a/codec2talkie/src/main/res/xml/preferences.xml +++ b/codec2talkie/src/main/res/xml/preferences.xml @@ -215,6 +215,14 @@ app:defaultValue="true"> + + +