diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/app/AppWorker.java b/codec2talkie/src/main/java/com/radio/codec2talkie/app/AppWorker.java index b73b167..d51096c 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/app/AppWorker.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/app/AppWorker.java @@ -492,16 +492,11 @@ public class AppWorker extends Thread { @Override public void run() { Log.i(TAG, "Starting message loop"); - try { - sleep(2000); - } catch (InterruptedException e) { - e.printStackTrace(); - } android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO); Looper.prepare(); sendStatusUpdate(AppMessage.EV_CONNECTED, null); - //_systemAudioPlayer.play(); + _systemAudioPlayer.play(); try { _protocol.initialize(_transport, _context, _protocolCallback); diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/Hdlc.java b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/Hdlc.java new file mode 100644 index 0000000..9b0e96a --- /dev/null +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/Hdlc.java @@ -0,0 +1,110 @@ +package com.radio.codec2talkie.protocol; + +import android.content.Context; + +import com.radio.codec2talkie.protocol.message.TextMessage; +import com.radio.codec2talkie.protocol.position.Position; +import com.radio.codec2talkie.tools.BitTools; +import com.radio.codec2talkie.tools.ChecksumTools; +import com.radio.codec2talkie.transport.Transport; + +import java.io.IOException; +import java.nio.ByteBuffer; + +public class Hdlc implements Protocol { + + protected Transport _transport; + private ProtocolCallback _parentProtocolCallback; + + public Hdlc() { + } + + @Override + public void initialize(Transport transport, Context context, ProtocolCallback protocolCallback) throws IOException { + _transport = transport; + _parentProtocolCallback = protocolCallback; + } + + @Override + public int getPcmAudioBufferSize() { + throw new UnsupportedOperationException(); + } + + @Override + public void sendPcmAudio(String src, String dst, int codec, short[] pcmFrame) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void sendCompressedAudio(String src, String dst, int codec, byte[] frame) throws IOException { + _transport.write(hdlcEncode(frame)); + } + + @Override + public void sendTextMessage(TextMessage textMessage) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void sendData(String src, String dst, byte[] dataPacket) throws IOException { + _transport.write(hdlcEncode(dataPacket)); + } + + @Override + public boolean receive() throws IOException { + return false; + } + + @Override + public void sendPosition(Position position) { + throw new UnsupportedOperationException(); + } + + @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); + + // TODO, read from settings + int prefixLen = 30; + int suffixLen = 5; + + // 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); + byte[] dataBytesAsBits = BitTools.convertToHDLCBitArray(data, true); + + // add preamble + ByteBuffer hdlcBitBuffer = ByteBuffer.allocate(dataBytesAsBits.length + 8*prefixLen + 8*suffixLen); + hdlcBitBuffer.put(genPreamble(prefixLen)); + hdlcBitBuffer.put(dataBytesAsBits); + hdlcBitBuffer.put(genPreamble(suffixLen)); + + // return + hdlcBitBuffer.flip(); + byte[] r = new byte[hdlcBitBuffer.remaining()]; + hdlcBitBuffer.get(r); + return r; + } +} diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/ProtocolFactory.java b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/ProtocolFactory.java index 9206a06..baa5846 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/ProtocolFactory.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/ProtocolFactory.java @@ -11,6 +11,7 @@ public class ProtocolFactory { public enum ProtocolType { RAW("RAW"), + HDLC("HDLC"), KISS("KISS"), KISS_BUFFERED("KISS BUF"), KISS_PARROT("KISS RPT"); @@ -32,7 +33,9 @@ public class ProtocolFactory { ProtocolFactory.ProtocolType protocolType; - if (sharedPreferences.getBoolean(PreferenceKeys.KISS_ENABLED, true)) { + if (sharedPreferences.getBoolean(PreferenceKeys.PORTS_SOUND_MODEM_ENABLED, true)) { + protocolType = ProtocolFactory.ProtocolType.HDLC; + } else if (sharedPreferences.getBoolean(PreferenceKeys.KISS_ENABLED, true)) { if (sharedPreferences.getBoolean(PreferenceKeys.KISS_PARROT, false)) { protocolType = ProtocolFactory.ProtocolType.KISS_PARROT; } @@ -70,6 +73,9 @@ public class ProtocolFactory { case KISS_PARROT: proto = new KissParrot(); break; + case HDLC: + proto = new Hdlc(); + break; case RAW: default: proto = new Raw(); diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/tools/BitTools.java b/codec2talkie/src/main/java/com/radio/codec2talkie/tools/BitTools.java new file mode 100644 index 0000000..bb4dfd5 --- /dev/null +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/tools/BitTools.java @@ -0,0 +1,48 @@ +package com.radio.codec2talkie.tools; + +import java.nio.ByteBuffer; + +public class BitTools { + + public static byte[] convertToNRZI(byte[] bitsAsBytes) { + ByteBuffer buffer = ByteBuffer.allocate(bitsAsBytes.length); + byte last = 0; + for (byte bitAsByte : bitsAsBytes) { + if (bitAsByte == 0) { + last = last == 0 ? (byte) 1 : (byte) 0; + } + buffer.put(last); + } + buffer.flip(); + byte[] r = new byte[buffer.remaining()]; + buffer.get(r); + return r; + } + + public static byte[] convertToHDLCBitArray(byte[] data, boolean shouldBitStuff) { + ByteBuffer bitBuffer = ByteBuffer.allocate(2 * data.length * 8); + + int bitStuffCnt = 5; + int cntOnes = 0; + for (int i = 0; i < 8 * data.length; i++) { + int b = ((int) data[i / 8]) & 0xff; + // HDLC transmits least significant bit first + if ((b & (1 << (i % 8))) > 0) { + bitBuffer.put((byte) 1); + if (shouldBitStuff) + cntOnes += 1; + } else { + bitBuffer.put((byte) 0); + cntOnes = 0; + } + if (cntOnes == bitStuffCnt) { + bitBuffer.put((byte) 0); + cntOnes = 0; + } + } + bitBuffer.flip(); + byte[] r = new byte[bitBuffer.remaining()]; + bitBuffer.get(r); + return r; + } +} diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/transport/SoundModem.java b/codec2talkie/src/main/java/com/radio/codec2talkie/transport/SoundModem.java index 38a84d1..621ed32 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/transport/SoundModem.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/transport/SoundModem.java @@ -11,6 +11,7 @@ import android.util.Log; import androidx.preference.PreferenceManager; +import com.radio.codec2talkie.tools.BitTools; import com.radio.codec2talkie.tools.ChecksumTools; import com.ustadmobile.codec2.Codec2; @@ -45,6 +46,7 @@ public class SoundModem implements Transport { _context = context; _sharedPreferences = PreferenceManager.getDefaultSharedPreferences(_context); + // TODO, read from settings //_fskModem = Codec2.fskCreate(AUDIO_SAMPLE_SIZE, 300, 1600, 200); _fskModem = Codec2.fskCreate(AUDIO_SAMPLE_SIZE, 1200, 1200, 1000); @@ -102,102 +104,9 @@ public class SoundModem implements Transport { return 0; } - public static byte[] toHdlcByteBitArray(byte[] data, boolean shouldBitStuff) { - StringBuilder s = new StringBuilder(); - StringBuilder s2 = new StringBuilder(); - ByteBuffer bitBuffer = ByteBuffer.allocate(512*8); - - int cntOnes = 0; - for (int i = 0; i < 8 * data.length; i++) { - int b = ((int)data[i / 8]) & 0xff; - // HDLC transmits least significant bit first - if ((b & (1 << (i % 8))) > 0) { - bitBuffer.put((byte)1); - s.append('1'); - if (shouldBitStuff) - cntOnes += 1; - } else { - bitBuffer.put((byte)0); - s.append(0); - cntOnes = 0; - } - if (shouldBitStuff && cntOnes == 5) { - bitBuffer.put((byte)0); - s.append('0'); - s2.append(' '); - cntOnes = 0; - } - if (i % 8 == 3) s.append(':'); - if (i % 8 == 7) { - s.append(' '); - s2.append(String.format("%02x ", b)); - } - } - - Log.i(TAG, s2.toString()); - Log.i(TAG, s.toString()); - - bitBuffer.flip(); - byte[] r = new byte[bitBuffer.remaining()]; - bitBuffer.get(r); - return r; - } - - public byte[] genPreamble(int count) { - byte[] preamble = new byte[count]; - for (int i = 0; i < count; i++) - preamble[i] = (byte)0x7e; - return toHdlcByteBitArray(preamble, false); - } - - public byte[] hdlcEncode(byte[] dataSrc) { - ByteBuffer buffer = ByteBuffer.allocate(512); - - buffer.put(dataSrc); - int fcs = ChecksumTools.calculateFcs(dataSrc); - //least significant byte first - buffer.put((byte)(fcs & 0xff)); - buffer.put((byte)((fcs >> 8) & 0xff)); - - buffer.flip(); - byte[] data = new byte[buffer.remaining()]; - buffer.get(data); - - //Log.i(TAG, String.format("checksum: %x", fcs)); - Log.i(TAG, "" + Arrays.toString(data)); - - byte[] dataBytesAsBits = toHdlcByteBitArray(data, true); - Log.i(TAG, "write() " + data.length + " " + 8 * data.length + " " + dataBytesAsBits.length + " " + _playbackBitBuffer.length); - - ByteBuffer hdlcBitBuffer = ByteBuffer.allocate(512*8); - hdlcBitBuffer.put(genPreamble(30)); - hdlcBitBuffer.put(dataBytesAsBits); - hdlcBitBuffer.put(genPreamble(5)); - - hdlcBitBuffer.flip(); - byte[] r = new byte[hdlcBitBuffer.remaining()]; - hdlcBitBuffer.get(r); - return r; - } - - public byte[] toNrzi(byte[] data) { - ByteBuffer buffer = ByteBuffer.allocate(data.length); - byte last = 0; - for (int i = 0; i < data.length; i++) { - if (data[i] == 0) { - last = last == 0 ? (byte)1 : (byte)0; - } - buffer.put(last); - } - buffer.flip(); - byte[] r = new byte[buffer.remaining()]; - buffer.get(r); - return r; - } - @Override - public int write(byte[] dataSrc) throws IOException { - byte[] dataBytesAsBits = toNrzi(hdlcEncode(dataSrc)); + public int write(byte[] srcDataBytesAsBits) throws IOException { + byte[] dataBytesAsBits = BitTools.convertToNRZI(srcDataBytesAsBits); int j = 0; StringBuilder ss = new StringBuilder(); @@ -206,8 +115,8 @@ public class SoundModem implements Transport { Log.i(TAG, "-- " + i + " " + j); Codec2.fskModulate(_fskModem, _playbackAudioBuffer, _playbackBitBuffer); StringBuilder s = new StringBuilder(); - for (int x = 0; x < _playbackAudioBuffer.length; x++) { - s.append(_playbackAudioBuffer[x]); + for (short value : _playbackAudioBuffer) { + s.append(value); s.append(' '); } Log.i(TAG, s.toString()); diff --git a/libcodec2-android/src/main/cpp/Codec2JNI.cpp b/libcodec2-android/src/main/cpp/Codec2JNI.cpp index 10061cf..e582b78 100644 --- a/libcodec2-android/src/main/cpp/Codec2JNI.cpp +++ b/libcodec2-android/src/main/cpp/Codec2JNI.cpp @@ -137,7 +137,6 @@ namespace Java_com_ustadmobile_codec2_Codec2 { con->buf[i] = v; } env->ReleaseShortArrayElements(inputBuffer, jbuf, 0); - //env->DeleteLocalRef(inputBuffer); codec2_encode(con->c2, con->bits, con->buf); @@ -146,7 +145,6 @@ namespace Java_com_ustadmobile_codec2_Codec2 { jbits[i] = con->bits[i]; } env->ReleaseCharArrayElements(outputBits, jbits, 0); - //env->DeleteLocalRef(outputBits); return 0; } @@ -160,7 +158,6 @@ namespace Java_com_ustadmobile_codec2_Codec2 { conFsk->modBits[i] = v; } env->ReleaseByteArrayElements(inputBits, jbuf, 0); - //env->DeleteLocalRef(inputBits); fsk_mod(conFsk->fsk, conFsk->modBuf, conFsk->modBits, inputBitsSize); @@ -169,7 +166,6 @@ namespace Java_com_ustadmobile_codec2_Codec2 { jOutBuf[i] = (int16_t)(conFsk->modBuf[i] * FDMDV_SCALE); } env->ReleaseShortArrayElements(outputSamples, jOutBuf, 0); - //env->DeleteLocalRef(outputSamples); return 0; }