2022-07-31 11:05:35 +00:00
|
|
|
package com.radio.codec2talkie.protocol;
|
|
|
|
|
|
|
|
import android.content.Context;
|
2022-07-31 12:01:27 +00:00
|
|
|
import android.content.SharedPreferences;
|
2022-08-01 19:22:00 +00:00
|
|
|
import android.util.Log;
|
2022-07-31 11:05:35 +00:00
|
|
|
|
|
|
|
import com.radio.codec2talkie.protocol.message.TextMessage;
|
|
|
|
import com.radio.codec2talkie.protocol.position.Position;
|
2022-07-31 12:01:27 +00:00
|
|
|
import com.radio.codec2talkie.settings.PreferenceKeys;
|
2022-08-11 14:18:47 +00:00
|
|
|
import com.radio.codec2talkie.settings.SettingsWrapper;
|
2022-07-31 11:05:35 +00:00
|
|
|
import com.radio.codec2talkie.tools.BitTools;
|
|
|
|
import com.radio.codec2talkie.tools.ChecksumTools;
|
2022-08-01 19:22:00 +00:00
|
|
|
import com.radio.codec2talkie.tools.DebugTools;
|
2022-07-31 11:05:35 +00:00
|
|
|
import com.radio.codec2talkie.transport.Transport;
|
|
|
|
|
|
|
|
import java.io.IOException;
|
2022-08-06 12:02:22 +00:00
|
|
|
import java.nio.BufferOverflowException;
|
2022-07-31 11:05:35 +00:00
|
|
|
import java.nio.ByteBuffer;
|
2022-08-01 19:22:00 +00:00
|
|
|
import java.util.Arrays;
|
2022-07-31 11:05:35 +00:00
|
|
|
|
|
|
|
public class Hdlc implements Protocol {
|
2022-08-01 19:22:00 +00:00
|
|
|
private static final String TAG = Hdlc.class.getSimpleName();
|
|
|
|
|
|
|
|
private static final int RX_BUFFER_SIZE = 8192;
|
2022-07-31 11:05:35 +00:00
|
|
|
|
|
|
|
protected Transport _transport;
|
|
|
|
private ProtocolCallback _parentProtocolCallback;
|
|
|
|
|
2022-08-01 19:22:00 +00:00
|
|
|
protected final byte[] _rxDataBuffer;
|
|
|
|
protected final ByteBuffer _currentFrameBuffer;
|
|
|
|
|
2022-07-31 12:01:27 +00:00
|
|
|
private final int _prefixSymCount;
|
|
|
|
|
2022-08-01 19:22:00 +00:00
|
|
|
private int _readByte = 0;
|
|
|
|
|
2022-07-31 12:01:27 +00:00
|
|
|
public Hdlc(SharedPreferences sharedPreferences) {
|
|
|
|
double preambleLenSec = Integer.parseInt(sharedPreferences.getString(PreferenceKeys.PORTS_SOUND_MODEM_PREAMBLE, "200")) / 1000.0;
|
2022-08-11 14:18:47 +00:00
|
|
|
int modemSpeed = SettingsWrapper.getFskSpeed(sharedPreferences);
|
2022-07-31 12:01:27 +00:00
|
|
|
_prefixSymCount = (int) (preambleLenSec * modemSpeed / 8);
|
2022-08-01 19:22:00 +00:00
|
|
|
|
|
|
|
_rxDataBuffer = new byte[RX_BUFFER_SIZE];
|
|
|
|
_currentFrameBuffer = ByteBuffer.allocate(RX_BUFFER_SIZE);
|
2022-07-31 11:05:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void initialize(Transport transport, Context context, ProtocolCallback protocolCallback) throws IOException {
|
|
|
|
_transport = transport;
|
|
|
|
_parentProtocolCallback = protocolCallback;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2022-08-13 17:48:45 +00:00
|
|
|
public int getPcmAudioRecordBufferSize() {
|
|
|
|
Log.w(TAG, "getPcmAudioBufferSize() is not supported");
|
|
|
|
return -1;
|
2022-07-31 11:05:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2023-12-09 12:27:49 +00:00
|
|
|
public void sendPcmAudio(String src, String dst, short[] pcmFrame) throws IOException {
|
2022-08-13 17:48:45 +00:00
|
|
|
Log.w(TAG, "sendPcmAudio() is not supported");
|
2022-07-31 11:05:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2023-12-09 12:27:49 +00:00
|
|
|
public void sendCompressedAudio(String src, String dst, byte[] frame) throws IOException {
|
2022-07-31 11:05:35 +00:00
|
|
|
_transport.write(hdlcEncode(frame));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void sendTextMessage(TextMessage textMessage) throws IOException {
|
2022-08-13 17:48:45 +00:00
|
|
|
Log.w(TAG, "sendTextMessage() is not supported");
|
2022-07-31 11:05:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2022-08-19 16:53:43 +00:00
|
|
|
public void sendData(String src, String dst, String path, byte[] dataPacket) throws IOException {
|
2022-07-31 11:05:35 +00:00
|
|
|
_transport.write(hdlcEncode(dataPacket));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean receive() throws IOException {
|
2022-08-01 19:22:00 +00:00
|
|
|
int bitsRead = _transport.read(_rxDataBuffer);
|
|
|
|
if (bitsRead > 0) {
|
|
|
|
byte[] data = Arrays.copyOf(_rxDataBuffer, bitsRead);
|
|
|
|
for (byte bit : data) {
|
|
|
|
_readByte <<= 1;
|
|
|
|
_readByte |= bit;
|
|
|
|
_readByte &= 0xff;
|
|
|
|
if (_readByte == 0x7e) {
|
2022-08-02 14:26:06 +00:00
|
|
|
//Log.i(TAG, "HDLC " + _prevHdlc/8);
|
2022-08-01 19:22:00 +00:00
|
|
|
int pos = _currentFrameBuffer.position();
|
2022-08-02 10:48:57 +00:00
|
|
|
|
|
|
|
// shift/flush back previous 8 - 1 bits
|
2022-08-01 19:22:00 +00:00
|
|
|
if (pos >= 7) {
|
|
|
|
_currentFrameBuffer.position(_currentFrameBuffer.position() - 7);
|
|
|
|
} else {
|
|
|
|
_currentFrameBuffer.position(0);
|
|
|
|
}
|
2022-08-02 10:48:57 +00:00
|
|
|
// get packet bits between 0x7e
|
2022-08-01 19:22:00 +00:00
|
|
|
_currentFrameBuffer.flip();
|
|
|
|
byte[] packetBits = new byte[_currentFrameBuffer.remaining()];
|
|
|
|
_currentFrameBuffer.get(packetBits);
|
2022-08-02 10:48:57 +00:00
|
|
|
|
|
|
|
// get bytes from bits
|
2022-08-01 19:22:00 +00:00
|
|
|
byte[] packetBytes = BitTools.convertFromHDLCBitArray(packetBits);
|
|
|
|
if (packetBytes != null) {
|
2022-08-02 14:26:06 +00:00
|
|
|
//Log.i(TAG, DebugTools.byteBitsToString(packetBits));
|
|
|
|
//Log.i(TAG, DebugTools.bytesToHex(packetBytes));
|
2022-08-02 10:48:57 +00:00
|
|
|
if (packetBytes.length > 2) {
|
|
|
|
byte[] contentBytes = Arrays.copyOf(packetBytes, packetBytes.length - 2);
|
|
|
|
int calculatedCrc = ChecksumTools.calculateFcs(contentBytes);
|
|
|
|
int packetCrc = ((int)packetBytes[packetBytes.length - 2] & 0xff) | (((int)packetBytes[packetBytes.length - 1] & 0xff) << 8);
|
2022-08-02 14:26:06 +00:00
|
|
|
//Log.i(TAG, "checksum: " + calculatedCrc + " " + packetCrc);
|
2022-08-02 10:48:57 +00:00
|
|
|
if (calculatedCrc == packetCrc) {
|
2022-08-03 19:03:23 +00:00
|
|
|
//Log.v(TAG, DebugTools.byteBitsToString(packetBits));
|
2022-08-14 16:52:42 +00:00
|
|
|
//Log.i(TAG, "RX: " + DebugTools.bytesToHex(packetBytes));
|
2023-12-09 12:27:49 +00:00
|
|
|
// NOTE, default data is compressed audio, upper layer should distinguish
|
|
|
|
_parentProtocolCallback.onReceiveCompressedAudio(null, null, contentBytes);
|
2022-08-02 10:48:57 +00:00
|
|
|
}
|
|
|
|
}
|
2022-08-01 19:22:00 +00:00
|
|
|
}
|
|
|
|
_currentFrameBuffer.clear();
|
|
|
|
_readByte = 0;
|
|
|
|
} else {
|
2022-08-06 12:02:22 +00:00
|
|
|
try {
|
|
|
|
_currentFrameBuffer.put(bit);
|
|
|
|
} catch (BufferOverflowException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
_currentFrameBuffer.clear();
|
|
|
|
}
|
2022-08-01 19:22:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-07-31 11:05:35 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void sendPosition(Position position) {
|
2022-08-13 17:48:45 +00:00
|
|
|
Log.w(TAG, "sendPosition() is not supported");
|
2022-07-31 11:05:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void flush() {
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void close() {
|
|
|
|
}
|
|
|
|
|
|
|
|
public byte[] genPreamble(int count) {
|
|
|
|
byte[] preamble = new byte[count];
|
|
|
|
for (int i = 0; i < count; i++)
|
|
|
|
preamble[i] = (byte)0x7e;
|
|
|
|
return BitTools.convertToHDLCBitArray(preamble, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
public byte[] hdlcEncode(byte[] dataSrc) {
|
|
|
|
ByteBuffer buffer = ByteBuffer.allocate(dataSrc.length + 2);
|
|
|
|
|
|
|
|
// include checksum
|
|
|
|
buffer.put(dataSrc);
|
|
|
|
int fcs = ChecksumTools.calculateFcs(dataSrc);
|
|
|
|
// least significant byte first
|
|
|
|
buffer.put((byte)(fcs & 0xff));
|
|
|
|
buffer.put((byte)((fcs >> 8) & 0xff));
|
|
|
|
|
|
|
|
// convert to bits
|
|
|
|
buffer.flip();
|
|
|
|
byte[] data = new byte[buffer.remaining()];
|
|
|
|
buffer.get(data);
|
2022-08-03 19:03:23 +00:00
|
|
|
//Log.i(TAG, "TX: " + DebugTools.bytesToHex(data));
|
2022-08-02 10:48:57 +00:00
|
|
|
|
2022-07-31 11:05:35 +00:00
|
|
|
byte[] dataBytesAsBits = BitTools.convertToHDLCBitArray(data, true);
|
|
|
|
|
|
|
|
// add preamble
|
2022-08-02 14:26:06 +00:00
|
|
|
ByteBuffer hdlcBitBuffer = ByteBuffer.allocate(dataBytesAsBits.length + 8*_prefixSymCount + 8*10);
|
2022-07-31 12:01:27 +00:00
|
|
|
hdlcBitBuffer.put(genPreamble(_prefixSymCount));
|
2022-07-31 11:05:35 +00:00
|
|
|
hdlcBitBuffer.put(dataBytesAsBits);
|
2022-08-09 13:09:28 +00:00
|
|
|
hdlcBitBuffer.put(genPreamble(2));
|
2022-07-31 11:05:35 +00:00
|
|
|
|
|
|
|
// return
|
|
|
|
hdlcBitBuffer.flip();
|
|
|
|
byte[] r = new byte[hdlcBitBuffer.remaining()];
|
|
|
|
hdlcBitBuffer.get(r);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
}
|