sh123 2022-08-20 15:52:10 +03:00
rodzic fd9847f621
commit e71a53c5be
12 zmienionych plików z 182 dodań i 23 usunięć

Wyświetl plik

@ -9,7 +9,6 @@ import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@ -64,7 +63,7 @@ public class AppWorker extends Thread {
private short[] _recordAudioBuffer;
// callbacks
private final Handler _onPlayerStateChanged;
private final Handler _onWorkerStateChanged;
private Handler _onMessageReceived;
private final Timer _processPeriodicTimer;
@ -80,8 +79,8 @@ public class AppWorker extends Thread {
private final SharedPreferences _sharedPreferences;
public AppWorker(TransportFactory.TransportType transportType,
Handler onPlayerStateChanged, Context context) throws IOException {
_onPlayerStateChanged = onPlayerStateChanged;
Handler onWorkerStateChanged, Context context) throws IOException {
_onWorkerStateChanged = onWorkerStateChanged;
_context = context;
_sharedPreferences = PreferenceManager.getDefaultSharedPreferences(_context);
@ -220,7 +219,7 @@ public class AppWorker extends Thread {
if (note != null) {
msg.obj = note;
}
_onPlayerStateChanged.sendMessage(msg);
_onWorkerStateChanged.sendMessage(msg);
}
if (newStatus != AppMessage.EV_LISTENING) {
restartListening();
@ -232,21 +231,21 @@ public class AppWorker extends Thread {
msg.what = AppMessage.EV_RX_RADIO_LEVEL.toInt();
msg.arg1 = rssi;
msg.arg2 = snr;
_onPlayerStateChanged.sendMessage(msg);
_onWorkerStateChanged.sendMessage(msg);
}
private void sendRxAudioLevelUpdate(short [] pcmAudioSamples) {
Message msg = Message.obtain();
msg.what = AppMessage.EV_RX_LEVEL.toInt();
msg.arg1 = AudioTools.getSampleLevelDb(pcmAudioSamples);
_onPlayerStateChanged.sendMessage(msg);
_onWorkerStateChanged.sendMessage(msg);
}
private void sendTxAudioLevelUpdate(short [] pcmAudioSamples) {
Message msg = Message.obtain();
msg.what = AppMessage.EV_TX_LEVEL.toInt();
msg.arg1 = AudioTools.getSampleLevelDb(pcmAudioSamples);
_onPlayerStateChanged.sendMessage(msg);
_onWorkerStateChanged.sendMessage(msg);
}
private void recordAndSendAudioFrame() throws IOException {

Wyświetl plik

@ -22,7 +22,6 @@ import com.radio.codec2talkie.R;
import com.radio.codec2talkie.settings.PreferenceKeys;
import java.io.IOException;
import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.net.Socket;
@ -31,11 +30,11 @@ public class TcpIpConnectActivity extends AppCompatActivity {
private final int TCP_IP_CONNECTED = 1;
private final int TCP_IP_FAILED = 2;
private final int DEFAULT_MAX_RETRIES = 5;
private final int DEFAULT_RETRY_DELAY_MS = 5000;
private static final int DEFAULT_MAX_RETRIES = 5;
private static final int DEFAULT_RETRY_DELAY_MS = 5000;
private final String DEFAULT_ADDRESS = "127.0.0.1";
private final String DEFAULT_PORT = "8081";
private static final String DEFAULT_ADDRESS = "127.0.0.1";
private static final String DEFAULT_PORT = "8081";
private String _address;
private String _port;
@ -57,7 +56,7 @@ public class TcpIpConnectActivity extends AppCompatActivity {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
_address = sharedPreferences.getString(PreferenceKeys.PORTS_TCP_IP_ADDRESS, DEFAULT_ADDRESS);
_port = sharedPreferences.getString(PreferenceKeys.PORTS_TCP_IP_PORT, DEFAULT_PORT);
_maxRetries = Integer.parseInt(sharedPreferences.getString(PreferenceKeys.PORTS_TCP_IP_RETRY_COUNT, String.valueOf(DEFAULT_MAX_RETRIES)));;
_maxRetries = Integer.parseInt(sharedPreferences.getString(PreferenceKeys.PORTS_TCP_IP_RETRY_COUNT, String.valueOf(DEFAULT_MAX_RETRIES)));
_retryDelayMs = Integer.parseInt(sharedPreferences.getString(PreferenceKeys.PORTS_TCP_IP_RETRY_DELAY, String.valueOf(DEFAULT_RETRY_DELAY_MS)));
ProgressBar progressBarTcpIp = findViewById(R.id.progressBarTcpIp);

Wyświetl plik

@ -143,7 +143,7 @@ public class Aprs implements Protocol {
protected void onReceiveData(String src, String dst, String path, byte[] data) {
if (data.length == 0) return;
AprsDataType dataType = new AprsDataType((char)data[0]);
AprsData aprsData = AprsDataFactory.fromBinary(src, dst, data);
AprsData aprsData = AprsDataFactory.fromBinary(src, dst, path, data);
if (aprsData != null && aprsData.isValid()) {
if (dataType.isTextMessage()) {
TextMessage textMessage = aprsData.toTextMessage();

Wyświetl plik

@ -2,18 +2,36 @@ package com.radio.codec2talkie.protocol;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import androidx.preference.PreferenceManager;
import com.radio.codec2talkie.app.AppMessage;
import com.radio.codec2talkie.protocol.message.TextMessage;
import com.radio.codec2talkie.protocol.position.Position;
import com.radio.codec2talkie.settings.PreferenceKeys;
import com.radio.codec2talkie.tools.DebugTools;
import com.radio.codec2talkie.tools.TextTools;
import com.radio.codec2talkie.transport.TcpIp;
import com.radio.codec2talkie.transport.Transport;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;
public class AprsIs implements Protocol {
public class AprsIs implements Protocol, Runnable {
private static final String TAG = AprsIs.class.getSimpleName();
private final Protocol _childProtocol;
@ -27,11 +45,20 @@ public class AprsIs implements Protocol {
private boolean _isRxGateEnabled;
private boolean _isTxGateEnabled;
private String _callsign;
private String _ssid;
private int _filterRadius;
private String _filter;
private final ByteBuffer _rxQueue;
private final ByteBuffer _txQueue;
protected boolean _isRunning = true;
public AprsIs(Protocol childProtocol) {
_childProtocol = childProtocol;
_rxQueue = ByteBuffer.allocate(4096);
_txQueue = ByteBuffer.allocate(4096);
}
@Override
@ -43,12 +70,16 @@ public class AprsIs implements Protocol {
_isRxGateEnabled = sharedPreferences.getBoolean(PreferenceKeys.APRS_IS_ENABLE_RX_GATE, false);
_isTxGateEnabled = sharedPreferences.getBoolean(PreferenceKeys.APRS_IS_ENABLE_TX_GATE, false);
_isSelfEnabled = sharedPreferences.getBoolean(PreferenceKeys.APRS_IS_ENABLE_SELF, false);
_callsign = sharedPreferences.getString(PreferenceKeys.AX25_CALLSIGN, "N0CALL");
_ssid = sharedPreferences.getString(PreferenceKeys.AX25_SSID, "0");
_passcode = sharedPreferences.getString(PreferenceKeys.APRS_IS_CODE, "");
_server = sharedPreferences.getString(PreferenceKeys.APRS_IS_TCPIP_SERVER, "euro.aprs2.net");
_filterRadius = Integer.parseInt(sharedPreferences.getString(PreferenceKeys.APRS_IS_RADIUS, "10"));
_filter = sharedPreferences.getString(PreferenceKeys.APRS_IS_FILTER, "");
Log.i(TAG, "AprsIs RX gate: " + _isTxGateEnabled + ", TX gate: " + _isTxGateEnabled + ", server: " + _server);
new Thread(this).start();
}
@Override
@ -81,7 +112,17 @@ public class AprsIs implements Protocol {
@Override
public boolean receive() throws IOException {
return _childProtocol.receive();
String line = "";
synchronized (_rxQueue) {
line = TextTools.getString(_rxQueue);
}
if (line.length() > 0) {
if (_isTxGateEnabled) {
// TODO, forward APRS-IS data to radio
}
_parentProtocolCallback.onReceiveLog(line);
}
return _childProtocol.receive() || line.length() > 0;
}
ProtocolCallback _protocolCallback = new ProtocolCallback() {
@ -176,6 +217,101 @@ public class AprsIs implements Protocol {
@Override
public void close() {
Log.i(TAG, "close()");
_isRunning = false;
_childProtocol.close();
}
private String getLoginCommand() {
String cmd = "user " + _callsign + "-" + _ssid + " pass " + _passcode + " vers " + "C2T 1.0";
if (_filterRadius > 0) {
cmd += " filter m/" + _filterRadius;
}
if (_filter.length() > 0) {
if (!cmd.contains("filter")) {
cmd += " filter ";
}
cmd += " " + _filter;
}
cmd += "\n";
return cmd;
}
@Override
public void run() {
Socket socket;
boolean isConnected = false;
TcpIp tcpIp = null;
byte[] buf = new byte[4096];
Log.i(TAG, "Started APRS-IS thread");
while (_isRunning) {
// connect
if (!isConnected) {
socket = new Socket();
try {
socket.connect(new InetSocketAddress(_server, 14580));
tcpIp = new TcpIp(socket, "aprsis");
String loginCmd = getLoginCommand();
Log.i(TAG, "Login command " + loginCmd);
tcpIp.write(loginCmd.getBytes());
Log.i(TAG, "Connected to " + _server);
} catch (IOException e) {
Log.w(TAG, "Failed to connect");
e.printStackTrace();
try {
Thread.sleep(10000);
} catch (InterruptedException interruptedException) {
interruptedException.printStackTrace();
}
isConnected = false;
continue;
}
isConnected = true;
}
// read data
int bytesRead = 0;
try {
// # aprsc 2.1.11-g80df3b4 20 Aug 2022 11:33:40 GMT T2FINLAND 85.188.1.129:14580
// # logresp N0CALL unverified, server T2GYOR<0xd><0xa>
bytesRead = tcpIp.read(buf);
} catch (IOException e) {
// TODO, notify parent
Log.w(TAG, "Lost connection on receive");
e.printStackTrace();
isConnected = false;
continue;
}
if (bytesRead > 0 && buf[0] != '#') {
synchronized (_rxQueue) {
_rxQueue.put(Arrays.copyOf(buf, bytesRead));
}
}
// write data
synchronized (_txQueue) {
String line = TextTools.getString(_txQueue);
if (line.length() > 0) {
Log.v(TAG, line);
try {
tcpIp.write(line.getBytes());
} catch (IOException e) {
// TODO, notify parent
Log.w(TAG, "Lost connection on transmit");
e.printStackTrace();
isConnected = false;
}
}
}
}
if (tcpIp != null) {
try {
tcpIp.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Log.i(TAG, "Stopped APRS-IS thread");
}
}

Wyświetl plik

@ -8,7 +8,7 @@ public interface AprsData {
void fromTextMessage(TextMessage textMessage);
Position toPosition();
TextMessage toTextMessage();
void fromBinary(String srcCallsign, String dstCallsign, byte[] infoData);
void fromBinary(String srcCallsign, String dstCallsign, String digipath, byte[] infoData);
byte[] toBinary();
boolean isValid();
}

Wyświetl plik

@ -20,14 +20,14 @@ public class AprsDataFactory {
return null;
}
public static AprsData fromBinary(String srcCallsign, String dstCallsign, byte[] infoData) {
public static AprsData fromBinary(String srcCallsign, String dstCallsign, String digipath, byte[] infoData) {
ByteBuffer buffer = ByteBuffer.wrap(infoData);
AprsDataType dataType = new AprsDataType((char)buffer.get());
AprsData aprsData = create(dataType);
if (aprsData == null) return null;
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
aprsData.fromBinary(srcCallsign, dstCallsign, data);
aprsData.fromBinary(srcCallsign, dstCallsign, digipath, data);
return aprsData;
}
}

Wyświetl plik

@ -48,11 +48,12 @@ public class AprsDataPositionReport implements AprsData {
}
@Override
public void fromBinary(String srcCallsign, String dstCallsign, byte[] infoData) {
public void fromBinary(String srcCallsign, String dstCallsign, String digipath, byte[] infoData) {
_isValid = false;
_position = new Position();
_position.srcCallsign = srcCallsign;
_position.dstCallsign = dstCallsign;
_position.digipath = digipath;
_position.status = "";
_position.comment = "";
_position.privacyLevel = 0;

Wyświetl plik

@ -122,12 +122,13 @@ public class AprsDataPositionReportMicE implements AprsData {
}
@Override
public void fromBinary(String srcCallsign, String dstCallsign, byte[] infoData) {
public void fromBinary(String srcCallsign, String dstCallsign, String digipath, byte[] infoData) {
_isValid = false;
_position = new Position();
_dstCallsign = dstCallsign;
_position.srcCallsign = srcCallsign;
_position.dstCallsign = dstCallsign;
_position.digipath = digipath;
_position.privacyLevel = 0;
_position.status = "";
_position.comment = "";

Wyświetl plik

@ -9,6 +9,7 @@ public class AprsDataTextMessage implements AprsData {
public String srcCallsign;
public String dstCallsign;
public String digipath;
public String textMessage;
private boolean _isValid;
@ -22,6 +23,7 @@ public class AprsDataTextMessage implements AprsData {
public void fromTextMessage(TextMessage textMessage) {
this.dstCallsign = textMessage.dst;
this.textMessage = textMessage.text;
this.digipath = textMessage.digipath;
_isValid = true;
}
@ -35,14 +37,16 @@ public class AprsDataTextMessage implements AprsData {
TextMessage textMessage = new TextMessage();
textMessage.src = this.srcCallsign;
textMessage.dst = this.dstCallsign;
textMessage.digipath = this.digipath;
textMessage.text = this.textMessage;
return textMessage;
}
@Override
public void fromBinary(String srcCallsign, String dstCallsign, byte[] infoData) {
public void fromBinary(String srcCallsign, String dstCallsign, String digipath, byte[] infoData) {
_isValid = false;
if (infoData.length < 10) return;
this.digipath = digipath;
this.srcCallsign = srcCallsign;
ByteBuffer buffer = ByteBuffer.wrap(infoData);
// callsign, trim ending spaces

Wyświetl plik

@ -5,6 +5,7 @@ import com.radio.codec2talkie.storage.message.MessageItem;
public class TextMessage {
public String src;
public String dst;
public String digipath;
public String text;
public MessageItem toMessageItem(boolean isTransmit) {

Wyświetl plik

@ -9,6 +9,7 @@ public class Position {
public long timestampEpochMs;
public String srcCallsign;
public String dstCallsign;
public String digipath;
public double latitude;
public double longitude;
public String maidenHead;

Wyświetl plik

@ -2,6 +2,7 @@ package com.radio.codec2talkie.tools;
import android.util.Log;
import java.nio.ByteBuffer;
import java.util.Arrays;
public class TextTools {
@ -35,4 +36,20 @@ public class TextTools {
if (i == data.length) return data;
return Arrays.copyOf(data, i);
}
public static String getString(ByteBuffer byteBuffer) {
StringBuilder result = new StringBuilder();
if (byteBuffer.position() > 0) {
byteBuffer.flip();
while (byteBuffer.hasRemaining()) {
char c = (char)byteBuffer.get();
if (c == '\n') {
break;
}
result.append(c);
}
byteBuffer.compact();
}
return result.toString();
}
}