From 8aedde6f9c35944486bfe420a7c4fe0cc5f8450f Mon Sep 17 00:00:00 2001 From: sh123 Date: Sun, 14 Aug 2022 15:57:05 +0300 Subject: [PATCH] APRS transmit over FreeDV data --- .../radio/codec2talkie/protocol/Freedv.java | 29 +++++++++++++++++-- .../protocol/ProtocolFactory.java | 18 +++++++----- .../settings/SettingsWrapper.java | 3 +- .../transport/SoundModemBase.java | 11 ++++--- libcodec2-android/src/main/cpp/Codec2JNI.cpp | 14 ++++----- 5 files changed, 52 insertions(+), 23 deletions(-) diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/Freedv.java b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/Freedv.java index 8e7a186..6ed4271 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/Freedv.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/Freedv.java @@ -6,7 +6,6 @@ import android.util.Log; import androidx.preference.PreferenceManager; -import com.radio.codec2talkie.app.AppWorker; import com.radio.codec2talkie.protocol.message.TextMessage; import com.radio.codec2talkie.protocol.position.Position; import com.radio.codec2talkie.settings.PreferenceKeys; @@ -25,10 +24,14 @@ public class Freedv implements Protocol { private Transport _transport; private long _freedv; + private long _freedvData; private short[] _modemTxBuffer; private short[] _speechRxBuffer; + private short[] _dataSamplesBuffer; + private byte[] _dataTxBuffer; + public Freedv() { } @@ -48,6 +51,10 @@ public class Freedv implements Protocol { _freedv = Codec2.freedvCreate(mode, isSquelchEnabled, squelchSnr); _modemTxBuffer = new short[Codec2.freedvGetNomModemSamples(_freedv)]; _speechRxBuffer = new short[Codec2.freedvGetMaxSpeechSamples(_freedv)]; + + _freedvData = Codec2.freedvCreate(dataMode, isSquelchEnabled, squelchSnr); + _dataTxBuffer = new byte[Codec2.freedvGetBitsPerModemFrame(_freedvData) / 8]; + _dataSamplesBuffer = new short[Codec2.freedvGetNTxSamples(_freedvData)]; } @Override @@ -75,7 +82,24 @@ public class Freedv implements Protocol { @Override public void sendData(String src, String dst, byte[] dataPacket) throws IOException { - // TODO, send as data + Log.v(TAG, "sendData() " + dataPacket.length); + if (dataPacket.length > _dataTxBuffer.length - 2) { + Log.e(TAG, "Too large packet " + dataPacket.length + " > " + _dataTxBuffer.length); + return; + } + long cnt = Codec2.freedvRawDataPreambleTx(_freedvData, _dataSamplesBuffer); + Log.v(TAG, "sendData() write preamble " + cnt); + _transport.write(Arrays.copyOf(_dataSamplesBuffer, (int) cnt)); + + Arrays.fill(_dataTxBuffer, (byte) 0); + System.arraycopy(dataPacket, 0, _dataTxBuffer, 0, dataPacket.length); + Codec2.freedvRawDataTx(_freedvData, _dataSamplesBuffer, _dataTxBuffer); + Log.v(TAG, "sendData() write data " + _dataSamplesBuffer.length); + _transport.write(_dataSamplesBuffer); + + cnt = Codec2.freedvRawDataPostambleTx(_freedvData, _dataSamplesBuffer); + Log.v(TAG, "sendData() write postamble " + cnt); + _transport.write(Arrays.copyOf(_dataSamplesBuffer, (int) cnt)); } @Override @@ -109,6 +133,7 @@ public class Freedv implements Protocol { @Override public void close() { + Codec2.freedvDestroy(_freedvData); Codec2.freedvDestroy(_freedv); } } 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 67a7435..4e25497 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/ProtocolFactory.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/ProtocolFactory.java @@ -68,7 +68,9 @@ public class ProtocolFactory { boolean scramblingEnabled = SettingsWrapper.isKissScramblerEnabled(sharedPreferences); String scramblingKey = SettingsWrapper.getKissScramblerKey(sharedPreferences); boolean aprsEnabled = SettingsWrapper.isAprsEnabled(sharedPreferences); + boolean freedvEnabled = SettingsWrapper.isFreeDvSoundModemModulation(sharedPreferences); + // "root" protocol Protocol proto; switch (protocolType) { case KISS: @@ -84,8 +86,8 @@ public class ProtocolFactory { proto = new Hdlc(sharedPreferences); break; case FREEDV: - // standalone - return new Freedv(); + proto = new Freedv(); + break; case RAW: default: proto = new Raw(); @@ -98,12 +100,14 @@ public class ProtocolFactory { if (aprsEnabled) { proto = new Ax25(proto); } - if (recordingEnabled) { - proto = new Recorder(proto, codec2ModeId); - } + if (!freedvEnabled) { + if (recordingEnabled) { + proto = new Recorder(proto, codec2ModeId); + } - proto = new AudioFrameAggregator(proto, codec2ModeId); - proto = new AudioCodec2(proto, codec2ModeId); + proto = new AudioFrameAggregator(proto, codec2ModeId); + proto = new AudioCodec2(proto, codec2ModeId); + } if (aprsEnabled) { proto = new Aprs(proto); 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 fbcc899..ee68656 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/settings/SettingsWrapper.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/settings/SettingsWrapper.java @@ -85,7 +85,6 @@ public class SettingsWrapper { } public static boolean isAprsEnabled(SharedPreferences sharedPreferences) { - return sharedPreferences.getBoolean(PreferenceKeys.APRS_ENABLED, false) && - !isFreeDvSoundModemModulation(sharedPreferences); // no aprs when in freedv + return sharedPreferences.getBoolean(PreferenceKeys.APRS_ENABLED, false); } } diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/transport/SoundModemBase.java b/codec2talkie/src/main/java/com/radio/codec2talkie/transport/SoundModemBase.java index aaaf9a7..ae2039c 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/transport/SoundModemBase.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/transport/SoundModemBase.java @@ -73,7 +73,7 @@ public class SoundModemBase implements Runnable { } private void constructSystemAudioDevices(boolean disableRx, int sampleRate) { - int audioRecorderMinBufferSize = AudioRecord.getMinBufferSize( + int audioRecorderMinBufferSize = 10 * AudioRecord.getMinBufferSize( sampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT); @@ -84,12 +84,13 @@ public class SoundModemBase implements Runnable { sampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, - 10*audioRecorderMinBufferSize); + audioRecorderMinBufferSize); - int audioPlayerMinBufferSize = AudioTrack.getMinBufferSize( + int audioPlayerMinBufferSize = 10 * AudioTrack.getMinBufferSize( sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT); + if (!disableRx) _systemAudioRecorder.startRecording(); @@ -105,9 +106,11 @@ public class SoundModemBase implements Runnable { .setChannelMask(AudioFormat.CHANNEL_OUT_MONO) .build()) .setTransferMode(AudioTrack.MODE_STREAM) - .setBufferSizeInBytes(10*audioPlayerMinBufferSize) + .setBufferSizeInBytes(audioPlayerMinBufferSize) .build(); _systemAudioPlayer.setVolume(AudioTrack.getMaxVolume()); + + Log.i(TAG, "Play buffer size " + audioPlayerMinBufferSize + ", recorder " + audioRecorderMinBufferSize); } protected int read(short[] sampleBuffer, int samplesToRead) { diff --git a/libcodec2-android/src/main/cpp/Codec2JNI.cpp b/libcodec2-android/src/main/cpp/Codec2JNI.cpp index 88266c9..ec59640 100644 --- a/libcodec2-android/src/main/cpp/Codec2JNI.cpp +++ b/libcodec2-android/src/main/cpp/Codec2JNI.cpp @@ -112,7 +112,7 @@ namespace Java_com_ustadmobile_codec2_Codec2 { conFreedv->rawData = static_cast(malloc( freedv_get_bits_per_modem_frame(conFreedv->freeDv) / 8)); conFreedv->rawDataSamples = static_cast(malloc( - freedv_get_n_tx_modem_samples(conFreedv->freeDv))); + freedv_get_n_tx_modem_samples(conFreedv->freeDv) * sizeof(short))); // squelch freedv_set_squelch_en(conFreedv->freeDv, isSquelchEnabled); freedv_set_snr_squelch_thresh(conFreedv->freeDv, squelchSnr); @@ -278,23 +278,21 @@ namespace Java_com_ustadmobile_codec2_Codec2 { conFreedv->rawData[cntBytes-1] = crc16 & 0xff; freedv_rawdatatx(conFreedv->freeDv, conFreedv->rawDataSamples, conFreedv->rawData); int cntSamples = freedv_get_n_tx_modem_samples(conFreedv->freeDv); - env->SetShortArrayRegion(outputModemSamples, 0, cntSamples, conFreedv->modemSamples); + env->SetShortArrayRegion(outputModemSamples, 0, cntSamples, conFreedv->rawDataSamples); return cntSamples; } static jlong freedvRawDataPreambleTx(JNIEnv *env, jclass clazz, jlong n, jshortArray outputModemSamples) { ContextFreedv *conFreedv = getContextFreedv(n); - freedv_rawdatapreambletx(conFreedv->freeDv, conFreedv->rawDataSamples); - int cntSamples = freedv_get_n_tx_modem_samples(conFreedv->freeDv); - env->SetShortArrayRegion(outputModemSamples, 0, cntSamples, conFreedv->modemSamples); + int cntSamples = freedv_rawdatapreambletx(conFreedv->freeDv, conFreedv->rawDataSamples); + env->SetShortArrayRegion(outputModemSamples, 0, cntSamples, conFreedv->rawDataSamples); return cntSamples; } static jlong freedvRawDataPostambleTx(JNIEnv *env, jclass clazz, jlong n, jshortArray outputModemSamples) { ContextFreedv *conFreedv = getContextFreedv(n); - freedv_rawdatapostambletx(conFreedv->freeDv, conFreedv->rawDataSamples); - int cntSamples = freedv_get_n_tx_modem_samples(conFreedv->freeDv); - env->SetShortArrayRegion(outputModemSamples, 0, cntSamples, conFreedv->modemSamples); + int cntSamples = freedv_rawdatapostambletx(conFreedv->freeDv, conFreedv->rawDataSamples); + env->SetShortArrayRegion(outputModemSamples, 0, cntSamples, conFreedv->rawDataSamples); return cntSamples; }