kopia lustrzana https://github.com/sh123/codec2_talkie
Demodulator bug fixing
rodzic
b96ff124ae
commit
2b0f1609b1
|
@ -2,30 +2,47 @@ package com.radio.codec2talkie.protocol;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Debug;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import com.radio.codec2talkie.protocol.message.TextMessage;
|
import com.radio.codec2talkie.protocol.message.TextMessage;
|
||||||
import com.radio.codec2talkie.protocol.position.Position;
|
import com.radio.codec2talkie.protocol.position.Position;
|
||||||
import com.radio.codec2talkie.settings.PreferenceKeys;
|
import com.radio.codec2talkie.settings.PreferenceKeys;
|
||||||
import com.radio.codec2talkie.tools.BitTools;
|
import com.radio.codec2talkie.tools.BitTools;
|
||||||
import com.radio.codec2talkie.tools.ChecksumTools;
|
import com.radio.codec2talkie.tools.ChecksumTools;
|
||||||
|
import com.radio.codec2talkie.tools.DebugTools;
|
||||||
|
import com.radio.codec2talkie.transport.SoundModem;
|
||||||
import com.radio.codec2talkie.transport.Transport;
|
import com.radio.codec2talkie.transport.Transport;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class Hdlc implements Protocol {
|
public class Hdlc implements Protocol {
|
||||||
|
private static final String TAG = Hdlc.class.getSimpleName();
|
||||||
|
|
||||||
|
private static final int RX_BUFFER_SIZE = 8192;
|
||||||
|
|
||||||
protected Transport _transport;
|
protected Transport _transport;
|
||||||
private ProtocolCallback _parentProtocolCallback;
|
private ProtocolCallback _parentProtocolCallback;
|
||||||
|
|
||||||
|
protected final byte[] _rxDataBuffer;
|
||||||
|
protected final ByteBuffer _currentFrameBuffer;
|
||||||
|
|
||||||
private final int _prefixSymCount;
|
private final int _prefixSymCount;
|
||||||
|
|
||||||
|
private int _readByte = 0;
|
||||||
|
private int _prevHdlc = 0;
|
||||||
|
|
||||||
public Hdlc(SharedPreferences sharedPreferences) {
|
public Hdlc(SharedPreferences sharedPreferences) {
|
||||||
double preambleLenSec = Integer.parseInt(sharedPreferences.getString(PreferenceKeys.PORTS_SOUND_MODEM_PREAMBLE, "200")) / 1000.0;
|
double preambleLenSec = Integer.parseInt(sharedPreferences.getString(PreferenceKeys.PORTS_SOUND_MODEM_PREAMBLE, "200")) / 1000.0;
|
||||||
String modemType = sharedPreferences.getString(PreferenceKeys.PORTS_SOUND_MODEM_TYPE, "1200");
|
String modemType = sharedPreferences.getString(PreferenceKeys.PORTS_SOUND_MODEM_TYPE, "1200");
|
||||||
// FIXME if more modulation schemes
|
// FIXME if more modulation schemes
|
||||||
int modemSpeed = modemType.equals("300") ? 300 : 1200;
|
int modemSpeed = modemType.equals("300") ? 300 : 1200;
|
||||||
_prefixSymCount = (int) (preambleLenSec * modemSpeed / 8);
|
_prefixSymCount = (int) (preambleLenSec * modemSpeed / 8);
|
||||||
|
|
||||||
|
_rxDataBuffer = new byte[RX_BUFFER_SIZE];
|
||||||
|
_currentFrameBuffer = ByteBuffer.allocate(RX_BUFFER_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -61,6 +78,38 @@ public class Hdlc implements Protocol {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean receive() throws IOException {
|
public boolean receive() throws IOException {
|
||||||
|
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) {
|
||||||
|
Log.i(TAG, "HDLC " + _prevHdlc/8);
|
||||||
|
int pos = _currentFrameBuffer.position();
|
||||||
|
if (pos >= 7) {
|
||||||
|
_currentFrameBuffer.position(_currentFrameBuffer.position() - 7);
|
||||||
|
} else {
|
||||||
|
_currentFrameBuffer.position(0);
|
||||||
|
}
|
||||||
|
_currentFrameBuffer.flip();
|
||||||
|
byte[] packetBits = new byte[_currentFrameBuffer.remaining()];
|
||||||
|
_currentFrameBuffer.get(packetBits);
|
||||||
|
byte[] packetBytes = BitTools.convertFromHDLCBitArray(packetBits);
|
||||||
|
if (packetBytes != null) {
|
||||||
|
Log.i(TAG, DebugTools.byteBitsToString(packetBits));
|
||||||
|
Log.i(TAG, DebugTools.bytesToHex(packetBytes));
|
||||||
|
}
|
||||||
|
_currentFrameBuffer.clear();
|
||||||
|
_readByte = 0;
|
||||||
|
_prevHdlc = 0;
|
||||||
|
} else {
|
||||||
|
_currentFrameBuffer.put(bit);
|
||||||
|
_prevHdlc++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package com.radio.codec2talkie.tools;
|
package com.radio.codec2talkie.tools;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
public class BitTools {
|
public class BitTools {
|
||||||
|
@ -19,6 +21,25 @@ public class BitTools {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static byte[] convertFromNRZI(byte[] bitsAsBytes, byte prevLastBit) {
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(bitsAsBytes.length);
|
||||||
|
byte last = prevLastBit;
|
||||||
|
for (byte bitAsByte : bitsAsBytes) {
|
||||||
|
// no transition -> 1
|
||||||
|
if (last == bitAsByte) {
|
||||||
|
buffer.put((byte) 1);
|
||||||
|
// transition -> 0
|
||||||
|
} else {
|
||||||
|
buffer.put((byte) 0);
|
||||||
|
}
|
||||||
|
last = bitAsByte;
|
||||||
|
}
|
||||||
|
buffer.flip();
|
||||||
|
byte[] r = new byte[buffer.remaining()];
|
||||||
|
buffer.get(r);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
public static byte[] convertToHDLCBitArray(byte[] data, boolean shouldBitStuff) {
|
public static byte[] convertToHDLCBitArray(byte[] data, boolean shouldBitStuff) {
|
||||||
ByteBuffer bitBuffer = ByteBuffer.allocate(2 * data.length * 8);
|
ByteBuffer bitBuffer = ByteBuffer.allocate(2 * data.length * 8);
|
||||||
|
|
||||||
|
@ -40,9 +61,57 @@ public class BitTools {
|
||||||
cntOnes = 0;
|
cntOnes = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// return
|
||||||
bitBuffer.flip();
|
bitBuffer.flip();
|
||||||
byte[] r = new byte[bitBuffer.remaining()];
|
byte[] r = new byte[bitBuffer.remaining()];
|
||||||
bitBuffer.get(r);
|
bitBuffer.get(r);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static byte[] convertFromHDLCBitArray(byte[] dataBitsAsBytes) {
|
||||||
|
ByteBuffer byteBuffer = ByteBuffer.allocate(dataBitsAsBytes.length / 8);
|
||||||
|
|
||||||
|
int currentByte = 0;
|
||||||
|
int cntOnes = 0;
|
||||||
|
int bitStuffCnt = 5;
|
||||||
|
boolean skipNext = false;
|
||||||
|
StringBuffer s = new StringBuffer();
|
||||||
|
int cntBits = 0;
|
||||||
|
for (int i = 0; i < dataBitsAsBytes.length; i++) {
|
||||||
|
byte currentBit = dataBitsAsBytes[i];
|
||||||
|
if (skipNext) {
|
||||||
|
// cannot have 6 consecutive 1, non-HDLC data
|
||||||
|
if (currentBit == 1) return null;
|
||||||
|
s.append(String.format("[%d]", currentBit));
|
||||||
|
skipNext = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
currentByte >>= 1;
|
||||||
|
s.append(String.format("%d", currentBit));
|
||||||
|
if (currentBit == 1) {
|
||||||
|
currentByte |= (1 << 7);
|
||||||
|
cntOnes++;
|
||||||
|
} else {
|
||||||
|
cntOnes = 0;
|
||||||
|
}
|
||||||
|
if (i % 8 == 7) {
|
||||||
|
s.append(' ');
|
||||||
|
byteBuffer.put((byte)(currentByte & 0xff));
|
||||||
|
currentByte = 0;
|
||||||
|
}
|
||||||
|
// 5 ones, skip next
|
||||||
|
if (cntOnes == bitStuffCnt) {
|
||||||
|
skipNext = true;
|
||||||
|
cntOnes = 0;
|
||||||
|
}
|
||||||
|
cntBits++;
|
||||||
|
}
|
||||||
|
//if (cntBits % 8 != 0) return null;
|
||||||
|
Log.i("----", s.toString());
|
||||||
|
// return
|
||||||
|
byteBuffer.flip();
|
||||||
|
byte[] r = new byte[byteBuffer.remaining()];
|
||||||
|
byteBuffer.get(r);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import android.media.AudioFormat;
|
||||||
import android.media.AudioRecord;
|
import android.media.AudioRecord;
|
||||||
import android.media.AudioTrack;
|
import android.media.AudioTrack;
|
||||||
import android.media.MediaRecorder;
|
import android.media.MediaRecorder;
|
||||||
|
import android.os.Debug;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
@ -17,9 +18,11 @@ import com.radio.codec2talkie.tools.DebugTools;
|
||||||
import com.ustadmobile.codec2.Codec2;
|
import com.ustadmobile.codec2.Codec2;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.BufferOverflowException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class SoundModem implements Transport {
|
public class SoundModem implements Transport, Runnable {
|
||||||
|
|
||||||
private static final String TAG = SoundModem.class.getSimpleName();
|
private static final String TAG = SoundModem.class.getSimpleName();
|
||||||
|
|
||||||
|
@ -36,10 +39,13 @@ public class SoundModem implements Transport {
|
||||||
private final short[] _playbackAudioBuffer;
|
private final short[] _playbackAudioBuffer;
|
||||||
private final byte[] _playbackBitBuffer;
|
private final byte[] _playbackBitBuffer;
|
||||||
private final int _samplesPerSymbol;
|
private final int _samplesPerSymbol;
|
||||||
|
private final ByteBuffer _bitBuffer;
|
||||||
|
|
||||||
private final Context _context;
|
private final Context _context;
|
||||||
private final SharedPreferences _sharedPreferences;
|
private final SharedPreferences _sharedPreferences;
|
||||||
|
|
||||||
|
private boolean _isRunning = true;
|
||||||
|
|
||||||
private final long _fskModem;
|
private final long _fskModem;
|
||||||
|
|
||||||
public SoundModem(Context context) {
|
public SoundModem(Context context) {
|
||||||
|
@ -59,8 +65,11 @@ public class SoundModem implements Transport {
|
||||||
_playbackAudioBuffer = new short[Codec2.fskModSamplesBufSize(_fskModem)];
|
_playbackAudioBuffer = new short[Codec2.fskModSamplesBufSize(_fskModem)];
|
||||||
_playbackBitBuffer = new byte[Codec2.fskModBitsBufSize(_fskModem)];
|
_playbackBitBuffer = new byte[Codec2.fskModBitsBufSize(_fskModem)];
|
||||||
_samplesPerSymbol = Codec2.fskSamplesPerSymbol(_fskModem);
|
_samplesPerSymbol = Codec2.fskSamplesPerSymbol(_fskModem);
|
||||||
|
_bitBuffer = ByteBuffer.allocate(10 * _recordBitBuffer.length);
|
||||||
|
|
||||||
constructSystemAudioDevices();
|
constructSystemAudioDevices();
|
||||||
|
|
||||||
|
new Thread(this).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void constructSystemAudioDevices() {
|
private void constructSystemAudioDevices() {
|
||||||
|
@ -81,6 +90,7 @@ public class SoundModem implements Transport {
|
||||||
AUDIO_SAMPLE_SIZE,
|
AUDIO_SAMPLE_SIZE,
|
||||||
AudioFormat.CHANNEL_OUT_MONO,
|
AudioFormat.CHANNEL_OUT_MONO,
|
||||||
AudioFormat.ENCODING_PCM_16BIT);
|
AudioFormat.ENCODING_PCM_16BIT);
|
||||||
|
_systemAudioRecorder.startRecording();
|
||||||
|
|
||||||
int usage = AudioAttributes.USAGE_MEDIA;
|
int usage = AudioAttributes.USAGE_MEDIA;
|
||||||
_systemAudioPlayer = new AudioTrack.Builder()
|
_systemAudioPlayer = new AudioTrack.Builder()
|
||||||
|
@ -107,6 +117,16 @@ public class SoundModem implements Transport {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read(byte[] data) throws IOException {
|
public int read(byte[] data) throws IOException {
|
||||||
|
synchronized (_bitBuffer) {
|
||||||
|
if (_bitBuffer.position() > 0) {
|
||||||
|
_bitBuffer.flip();
|
||||||
|
int len = _bitBuffer.remaining();
|
||||||
|
_bitBuffer.get(data, 0, len);
|
||||||
|
//Log.i(TAG, "-- " + DebugTools.byteBitsToString(data));
|
||||||
|
_bitBuffer.compact();
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,6 +153,36 @@ public class SoundModem implements Transport {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
|
Log.i(TAG, "close()");
|
||||||
|
_isRunning = false;
|
||||||
|
_systemAudioRecorder.stop();
|
||||||
|
_systemAudioPlayer.stop();
|
||||||
|
_systemAudioRecorder.release();
|
||||||
|
_systemAudioPlayer.release();
|
||||||
Codec2.fskDestroy(_fskModem);
|
Codec2.fskDestroy(_fskModem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
byte prevLastBit = 0;
|
||||||
|
while (_isRunning) {
|
||||||
|
// TODO, take readCnt into account, do not read if playback is active
|
||||||
|
int readCnt = _systemAudioRecorder.read(_recordAudioBuffer, 0, Codec2.fskNin(_fskModem));
|
||||||
|
//Log.i(TAG, DebugTools.shortsToHex(_recordAudioBuffer));
|
||||||
|
//Log.i(TAG, readCnt + " " + _recordAudioBuffer.length + " " + Codec2.fskNin(_fskModem));
|
||||||
|
Codec2.fskDemodulate(_fskModem, _recordAudioBuffer, _recordBitBuffer);
|
||||||
|
|
||||||
|
//Log.i(TAG, "-- " + DebugTools.byteBitsToString(_recordBitBuffer));
|
||||||
|
//Log.i(TAG, "== " + DebugTools.byteBitsToString(BitTools.convertFromNRZI(_recordBitBuffer, prevLastBit)));
|
||||||
|
synchronized (_bitBuffer) {
|
||||||
|
try {
|
||||||
|
_bitBuffer.put(BitTools.convertFromNRZI(_recordBitBuffer, prevLastBit));
|
||||||
|
prevLastBit = _recordBitBuffer[_recordBitBuffer.length - 1];
|
||||||
|
} catch (BufferOverflowException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
_bitBuffer.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,6 +74,9 @@ namespace Java_com_ustadmobile_codec2_Codec2 {
|
||||||
conFsk->demodBits = (uint8_t*)malloc(sizeof(uint8_t) * fsk->Nbits);
|
conFsk->demodBits = (uint8_t*)malloc(sizeof(uint8_t) * fsk->Nbits);
|
||||||
conFsk->demodBuf = (int16_t*)malloc(sizeof(short) * (fsk->N + 2 * fsk->Ts));
|
conFsk->demodBuf = (int16_t*)malloc(sizeof(short) * (fsk->N + 2 * fsk->Ts));
|
||||||
|
|
||||||
|
fsk_set_freq_est_limits(fsk, -sampleFrequency / 2, sampleFrequency / 2);
|
||||||
|
fsk_set_freq_est_alg(fsk, 1);
|
||||||
|
|
||||||
auto pv = (unsigned long) conFsk;
|
auto pv = (unsigned long) conFsk;
|
||||||
return pv;
|
return pv;
|
||||||
}
|
}
|
||||||
|
@ -113,6 +116,11 @@ namespace Java_com_ustadmobile_codec2_Codec2 {
|
||||||
return conFsk->Ts;
|
return conFsk->Ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static jint fskNin(JNIEnv * env, jclass clazz, jlong n) {
|
||||||
|
ContextFsk *conFsk = getContextFsk(n);
|
||||||
|
return fsk_nin(conFsk->fsk);
|
||||||
|
}
|
||||||
|
|
||||||
static jint destroy(JNIEnv *env, jclass clazz, jlong n) {
|
static jint destroy(JNIEnv *env, jclass clazz, jlong n) {
|
||||||
Context *con = getContext(n);
|
Context *con = getContext(n);
|
||||||
codec2_destroy(con->c2);
|
codec2_destroy(con->c2);
|
||||||
|
@ -201,7 +209,8 @@ namespace Java_com_ustadmobile_codec2_Codec2 {
|
||||||
{"fskModSamplesBufSize","(J)I", (void *) fskModSamplesBufSize},
|
{"fskModSamplesBufSize","(J)I", (void *) fskModSamplesBufSize},
|
||||||
{"fskDemodSamplesBufSize","(J)I", (void *) fskDemodSamplesBufSize},
|
{"fskDemodSamplesBufSize","(J)I", (void *) fskDemodSamplesBufSize},
|
||||||
{"fskModBitsBufSize", "(J)I", (void *) fskModBitsBufSize},
|
{"fskModBitsBufSize", "(J)I", (void *) fskModBitsBufSize},
|
||||||
{"fskSamplesPerSymbol","(J)I", (void *) fskSamplesPerSymbol}
|
{"fskSamplesPerSymbol","(J)I", (void *) fskSamplesPerSymbol},
|
||||||
|
{"fskNin", "(J)I", (void *) fskNin}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ public class Codec2 {
|
||||||
public native static int fskDemodSamplesBufSize(long conFsk);
|
public native static int fskDemodSamplesBufSize(long conFsk);
|
||||||
public native static int fskModBitsBufSize(long conFsk);
|
public native static int fskModBitsBufSize(long conFsk);
|
||||||
public native static int fskSamplesPerSymbol(long conFsk);
|
public native static int fskSamplesPerSymbol(long conFsk);
|
||||||
|
public native static int fskNin(long conFsk);
|
||||||
|
|
||||||
public native static long fskModulate(long conFsk, short[] outputSamples, byte[] inputBits);
|
public native static long fskModulate(long conFsk, short[] outputSamples, byte[] inputBits);
|
||||||
public native static long fskDemodulate(long conFsk, short[] inputSamples, byte[] outputBits);
|
public native static long fskDemodulate(long conFsk, short[] inputSamples, byte[] outputBits);
|
||||||
|
|
Ładowanie…
Reference in New Issue