diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 7ac24c7..a0de2a1 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -1,17 +1,19 @@ + diff --git a/.idea/misc.xml b/.idea/misc.xml index e0d5b93..4912fd3 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -29,7 +29,7 @@ - + diff --git a/.idea/modules.xml b/.idea/modules.xml index 80afbb3..125e41d 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,8 +2,11 @@ - - + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml deleted file mode 100644 index 7f68460..0000000 --- a/.idea/runConfigurations.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 1d25c1d..aff8b4f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,11 +1,11 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 26 + compileSdkVersion 28 defaultConfig { applicationId "sound.xwh.communication" - minSdkVersion 15 - targetSdkVersion 26 + minSdkVersion 16 + targetSdkVersion 28 versionCode 1 versionName "1.0" } @@ -19,5 +19,13 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'com.android.support:appcompat-v7:26.1.0' + implementation 'com.android.support:appcompat-v7:28.0.0' + implementation 'com.android.support.constraint:constraint-layout:2.0.4' +} + +android { + packagingOptions + { + exclude'META-INF/DEPENDENCIES' + } } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2d38189..ead3894 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,23 +1,37 @@ - + - + + + + + + + + - - - + android:allowBackup="true" + android:icon="@mipmap/ic_launcher" + android:label="@string/app_name" + android:roundIcon="@mipmap/ic_launcher_round" + android:supportsRtl="true" + android:usesCleartextTraffic="true" + android:theme="@style/AppTheme"> + + + + + + - - - - + + + + \ No newline at end of file diff --git a/app/src/main/java/xwh/sound/CodeBook.java b/app/src/main/java/xwh/sound/CodeBook.java index 8eefcaa..775360a 100644 --- a/app/src/main/java/xwh/sound/CodeBook.java +++ b/app/src/main/java/xwh/sound/CodeBook.java @@ -1,5 +1,5 @@ package xwh.sound; - +import android.util.Log; /** * 编码字典表 * 用声音频率来表达不同的字符,理想的做法是每个字符对应一段指定频率的声音。但是声音容易被干扰,只能采用划分大的频率段的方式进行编码。 @@ -13,25 +13,37 @@ public class CodeBook { - public static final int CODE_BOOK_LENGTH_CONTENT = 8; // 内容编码长度 (0-7为内容) - public static final int DUPLICATE_INDEX_1 = CODE_BOOK_LENGTH_CONTENT; // 重复标记1 - public static final int DUPLICATE_INDEX_2 = DUPLICATE_INDEX_1 +1; // 重复标记2 - public static final int START_INDEX = DUPLICATE_INDEX_2 + 1; // 开始标记 - public static final int END_INDEX = START_INDEX + 1; // 结束标记 + + public static int CODE_BOOK_LENGTH_CONTENT = 8; // 内容编码长度 (0-7为内容) + public static int DUPLICATE_INDEX_1 = CODE_BOOK_LENGTH_CONTENT; // 重复标记1 + public static int DUPLICATE_INDEX_2 = DUPLICATE_INDEX_1 +1; // 重复标记2 + public static int END_INDEX = DUPLICATE_INDEX_2 + 1; // 开始标记 + public static int START_INDEX = END_INDEX + 1; // 结束标记 + + public static int SEP_INDEX = START_INDEX + 1; // 分割符 + + //public static int freqDistance = MainActivity.FREQ_DISTANCE; // 两个频率之间的间距 + public static int START_INDEX_HAMMING = 4; + public static int END_INDEX_HAMMING = 6; + public static int DUPLICATE_INDEX_1_HAMMING = 5; + public static int DUPLICATE_INDEX_2_HAMMING = 7; + //public static int BASE_FREQ = MainActivity.BASE_FREQ; + //public static int START_FREQ_HAMMING = BASE_FREQ + freqDistance * START_INDEX_HAMMING; + //public static int END_FREQ_HAMMING = BASE_FREQ + freqDistance * END_INDEX_HAMMING; + //public static int DUP1_FREQ_HAMMING = BASE_FREQ + freqDistance * DUPLICATE_INDEX_1_HAMMING; + //public static int DUP2_FREQ_HAMMING = BASE_FREQ + freqDistance * DUPLICATE_INDEX_2_HAMMING; + /** * 两个book字典码来组成下面每个字符的编码 */ public final static String CONTENT_CODE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; // Base64编码 - - public static int[] freqsWave = new int[12]; // 将声音频率划分成12段,每一段表示一个字典码。 - - public static int freqDistance = 500; // 两个频率之间的间距 + public static int[] freqsWave = new int[13]; // 将声音频率划分成12段,每一段表示一个字典码。 static { for(int i=0; i= 0; i--) { + res += (((value >> i) & 0x1) ^ ((value >> (i + 1)) & 0x1)) << i; + } + return res; + } + + private static int _inv_gray(int value, int bitNum) { + value = value & ((1<= 0; i--) { + res += (((value >> i) & 0x1) ^ ((res >> (i + 1)) & 0x1)) << i; + } + //Log.i("_inv_gray", "before: " + value + " after: " + res); + return res; + } /** * 从码库里面找到一个最相近的 @@ -52,7 +99,7 @@ public static int encode(int index) { public static int decode(int fre) { int index = -1; - if ( fre + freqDistance > freqsWave[0]) { // 太小的不要 + if ( fre + MainActivity.FREQ_DISTANCE > freqsWave[0]) { // 太小的不要 int min = Integer.MAX_VALUE; for(int i=0; i= START_INDEX_HAMMING) { + // return index; + //} + //index = _gray(index, 2); + if (index > DUPLICATE_INDEX_2_HAMMING) index = DUPLICATE_INDEX_2_HAMMING; + return index; + } } diff --git a/app/src/main/java/xwh/sound/Decoder.java b/app/src/main/java/xwh/sound/Decoder.java index 1488f82..c3fd468 100644 --- a/app/src/main/java/xwh/sound/Decoder.java +++ b/app/src/main/java/xwh/sound/Decoder.java @@ -5,8 +5,12 @@ import android.util.Base64; import android.util.Log; +import java.io.UnsupportedEncodingException; import java.util.ArrayList; +import java.util.Deque; +import java.util.LinkedList; import java.util.List; +import java.util.Queue; import xwh.sound.utils.FFT; @@ -16,7 +20,7 @@ public class Decoder { - public static final String TAG = "Decoder"; + public static final String TAG = "Decoder"; private final static int UP = 1; private final static int DOWN = 2; @@ -27,19 +31,27 @@ public class Decoder { private int countEndCode = 0; private boolean startDecode; private ArrayList codeIndexs; + private Deque codeQueue; + private Deque debugQueue; + private int queueTop; + private int lastIndex; private Handler mHandler; private long lastStartTime; private static final int TIMEOUT = 10000; - private static final int COUNT_STEP_SIZE = 10; + private static final int COUNT_STEP_SIZE = 20; private FFT fft = new FFT(); public Decoder(Handler handler) { this.mHandler = handler; codeIndexs = new ArrayList<>(); + codeQueue = new LinkedList(); + debugQueue = new LinkedList(); + queueTop = -1; + lastIndex = -1; } public void countFreq(short[] datas, int sampleStep) { @@ -62,19 +74,16 @@ public void countFreq(short[] datas, int sampleStep) { /** * 对一个录音Buffer进行频率统计 */ - public int countFreq1(short[] datas, int sampleStep) { + public int countFreq1(short[] datas, int sampleStep) throws UnsupportedEncodingException { int itemStep = datas.length / COUNT_STEP_SIZE; - int waveState = -1; int stepCount = 0; int bufferFreqCount = 0; int currentFreq = 0; for(short sample : datas) { - if (waveState == -1) { waveState = sample > 0 ? UP : DOWN; } - // 根据波形上下计算频率 if (waveState == UP) { if (sample < -10) { @@ -87,8 +96,6 @@ public int countFreq1(short[] datas, int sampleStep) { waveCount++; } } - - /** * 并不是每个buffer取一次频率,而是在一段buffer中获取小段,每个小段进行解码 */ @@ -97,43 +104,28 @@ public int countFreq1(short[] datas, int sampleStep) { //waveCount = waveCount / 2; // 一上一下表示一个波形,所以要除以2 bufferFreqCount += waveCount; currentFreq = waveCount * COUNT_STEP_SIZE * sampleStep / 2; // 这里根据每小段得出频率(一秒内波形次数) - - decodeFre(currentFreq); + if (MainActivity.METHOD == 1) + decodeFre(currentFreq); + if (MainActivity.METHOD == 2) + decodeFre_74hamming(currentFreq); + if (MainActivity.METHOD == 3) + decodeFre_SeqHamming(currentFreq); stepCount = 0; waveCount = 0; } - } - - if (mHandler != null) { - - /*if (listBufferFreq.size() >= sampleStep) { - listBufferFreq.remove(0); - } - listBufferFreq.add(bufferFreqCount); - - int allCount = 0; - for(int bCount : listBufferFreq) { // 累计最近1s采样得到频率 - allCount += bCount; - } - - int freq = allCount / 2; // 一上一下表示一个波形,所以要除以2*/ - - //int freq = currentFreq; int freq = bufferFreqCount * sampleStep / 2; // 一上一下表示一个波形,所以要除以2 - Message msg = mHandler.obtainMessage(); msg.what = MainActivity.MSG_CURRENT_FREQ; msg.obj = freq + ""; mHandler.sendMessage(msg); + if (freq > MainActivity.BASE_FREQ + MainActivity.FREQ_DISTANCE * CodeBook.START_INDEX_HAMMING) { - - Log.i("Record", "fre:" + freq); - + //Log.i("Record", "fre:" + freq); + } } - return currentFreq; } @@ -142,20 +134,14 @@ public int countFreq1(short[] datas, int sampleStep) { * @param fre */ public void decodeFre(int fre) { - int codeIndex = CodeBook.decode(fre); - if (codeIndex != -1) { - - Log.i("Record", "waveCount:" + waveCount + ", fre:" + fre + ", decode:" + codeIndex); - + //Log.i("Record", "waveCount:" + waveCount + ", fre:" + fre + ", decode:" + codeIndex); if (codeIndex == CodeBook.START_INDEX) { countEndCode = 0; - if (startDecode) { return; } - countStartCode++; if (countStartCode >= 2) { countStartCode = 0; @@ -165,27 +151,65 @@ public void decodeFre(int fre) { } } else if (startDecode) { countStartCode = 0; - if (System.currentTimeMillis() - lastStartTime > TIMEOUT) { startDecode = false; // 可能上一次结束码丢失,超时 return; } - if (codeIndex == CodeBook.END_INDEX) { countEndCode++; if (countEndCode >= 2) { countEndCode = 0; startDecode = false; List cleanIndexs = cleanCodeIndexs(codeIndexs); - Log.i("Record", "clearCodeIndexs:" + cleanIndexs); - if(cleanIndexs.size() > 0) { showResult(cleanIndexs); } - } + } else { + countEndCode = 0; + codeIndexs.add(codeIndex); + } + } else { + countStartCode = 0; + countEndCode = 0; + } + } + } + public void decodeFre_SeqHamming(int fre) { + int codeIndex = CodeBook.decode(fre); + if (codeIndex != -1) { + //Log.i("Record", "waveCount:" + waveCount + ", fre:" + fre + ", decode:" + codeIndex); + if (codeIndex == CodeBook.START_INDEX) { + countEndCode = 0; + if (startDecode) { + return; + } + countStartCode++; + if (countStartCode >= 2) { + countStartCode = 0; + startDecode = true; + codeIndexs.clear(); + lastStartTime = System.currentTimeMillis(); + } + } else if (startDecode) { + countStartCode = 0; + if (System.currentTimeMillis() - lastStartTime > TIMEOUT) { + startDecode = false; // 可能上一次结束码丢失,超时 + return; + } + if (codeIndex == CodeBook.END_INDEX) { + countEndCode++; + if (countEndCode >= 2) { + countEndCode = 0; + startDecode = false; + List cleanIndexs = cleanCodeIndexs(codeIndexs); + Log.i("Record", "clearCodeIndexs:" + cleanIndexs); + if(cleanIndexs.size() > 0) { + showResult_SeqHamming(cleanIndexs); + } + } } else { countEndCode = 0; codeIndexs.add(codeIndex); @@ -194,8 +218,90 @@ public void decodeFre(int fre) { countStartCode = 0; countEndCode = 0; } + } + } + public void decodeFre_74hamming(int fre) throws UnsupportedEncodingException { + int codeIndex = CodeBook.decode_hamming(fre); + if (codeIndex == -1) { + return; + } else if (codeIndex == CodeBook.START_INDEX_HAMMING) { + if (startDecode) { + return; + } + countStartCode++; + if (countStartCode >= 2) { + countEndCode = 0; + debugQueue.clear(); + codeQueue.clear(); + startDecode = true; + codeIndexs.clear(); + lastStartTime = System.currentTimeMillis(); + } + } else if (startDecode) { + countStartCode = 0; + if (System.currentTimeMillis() - lastStartTime > TIMEOUT) { + startDecode = false; + return; + } + if (codeIndex == CodeBook.END_INDEX_HAMMING) { + countEndCode++; + if (countEndCode >= 2) { + countEndCode = 0; + startDecode = false; + List cleanIndexs = dummyCodeIndexs(codeIndexs); + + Log.i("Record", "clearCodeIndexs:" + cleanIndexs); + Log.i("Record", "fracs: " + debugQueue.toString()); + + if (cleanIndexs.size() > 0) { + showResult_74hamming(cleanIndexs); + } + } + } else { + countEndCode = 0; + if (lastIndex != -1 && codeIndex == lastIndex) { + return; + } else { + lastIndex = codeIndex; + if (queueTop != -1 && (codeIndex == CodeBook.DUPLICATE_INDEX_2_HAMMING || codeIndex == CodeBook.DUPLICATE_INDEX_1_HAMMING)) { + codeQueue.add(queueTop); + debugQueue.add(queueTop); + } else { + codeQueue.add(codeIndex); + debugQueue.add(codeIndex); + queueTop = codeIndex; + } + } + if (codeQueue.size() >= 7) { + int c = 0; + int s = 0; + for (int i = 0; i < 7 && !codeQueue.isEmpty(); ++i) { + s += (codeQueue.pollFirst() << (2 * i)); + } + int [] code = new int [7]; + for (int i = 0; i < 2; ++i) { + int curr7bit = (s >> (i * 7)) & 0x7f; + for (int j = 0; j < 7; ++j) { + code[j] = (curr7bit >> j) & 0x1; + } + int ch1 = code[0] ^ code[2] ^ code[4] ^ code[6]; + int ch2 = code[1] ^ code[2] ^ code[5] ^ code[6]; + int ch3 = code[3] ^ code[4] ^ code[5] ^ code[6]; + int errorBit = ch1 + (ch2 << 1) + (ch3 << 2); + if (errorBit != 0) { + code[errorBit-1] = 1 - code[errorBit-1]; + } + int curr4bit = code[2] + (code[4] << 1) + (code[5] << 2) + (code[6] << 3); + c += (curr4bit << (4 * i)); + } + codeIndexs.add(c); + } + } + } else { + countEndCode = 0; + countStartCode = 0; } } @@ -232,50 +338,55 @@ private List cleanCodeIndexs(ArrayList codeIndexs) { return list; } + private List dummyCodeIndexs(ArrayList codeIndexs) { + List list = new ArrayList<>(codeIndexs); + return list; + } + private String showResult(List indexs) { - if (indexs.size() < 2) { - return null; - } - - // 还原相邻相同字符 - for (int i=indexs.size()-1; i>0; i--) { - if (indexs.get(i) == CodeBook.DUPLICATE_INDEX_1 || indexs.get(i) == CodeBook.DUPLICATE_INDEX_2) { - int temp = i -1; - while(temp>0 && (indexs.get(temp) == CodeBook.DUPLICATE_INDEX_1 || indexs.get(temp) == CodeBook.DUPLICATE_INDEX_2)) { - temp --; - continue; - } - - indexs.set(i, indexs.get(temp)); - } - } - - int crcContent0 = indexs.get(indexs.size() -2); + if (indexs.size() < 2) { + return null; + } + + // 还原相邻相同字符 + for (int i=indexs.size()-1; i>0; i--) { + if (indexs.get(i) == CodeBook.DUPLICATE_INDEX_1 || indexs.get(i) == CodeBook.DUPLICATE_INDEX_2) { + int temp = i -1; + while(temp>0 && (indexs.get(temp) == CodeBook.DUPLICATE_INDEX_1 || indexs.get(temp) == CodeBook.DUPLICATE_INDEX_2)) { + temp --; + continue; + } + + indexs.set(i, indexs.get(temp)); + } + } + + int crcContent0 = indexs.get(indexs.size() -2); int crcContent1 = indexs.get(indexs.size() -1); - int[] crc = Utils.crc(indexs, 0, indexs.size() -3); + int[] crc = Utils.crc(indexs, 0, indexs.size() -3); boolean crcResult = (crc[0] == crcContent0 && crc[1] == crcContent1); - StringBuilder mTextBuilder = new StringBuilder(); - for(int i=0; i indexs) { return text; - } + } + + private String showResult_SeqHamming(List raw_indexs) { + List indexs = new ArrayList(); + if (raw_indexs.size() < 2) { + return null; + } + // 还原相邻相同字符 + while (raw_indexs.get(0) == CodeBook.DUPLICATE_INDEX_2 || raw_indexs.get(0) == CodeBook.DUPLICATE_INDEX_1) { + raw_indexs.remove(0); + } + int temp = raw_indexs.size(); + for (int i=raw_indexs.size()-1; i>=0; i--) { + if (raw_indexs.get(i) == CodeBook.DUPLICATE_INDEX_1 || raw_indexs.get(i) == CodeBook.DUPLICATE_INDEX_2) { + if (temp >= i) { + temp = i -1; + } + while(temp>0 && (raw_indexs.get(temp) == CodeBook.DUPLICATE_INDEX_1 || raw_indexs.get(temp) == CodeBook.DUPLICATE_INDEX_2)) { + temp --; + } + if (temp < 0) temp = 0; + raw_indexs.set(i, raw_indexs.get(temp)); + } + } + // 截取字符段进行译码 + for (int i = 0; i < raw_indexs.size(); ++i) { + while (i < raw_indexs.size() && raw_indexs.get(i) == CodeBook.SEP_INDEX) { + i++; + } + int j = i; + while (j < raw_indexs.size() && raw_indexs.get(j) != CodeBook.SEP_INDEX) { + j++; + } + if (j > i) { + List sublist = new ArrayList<>(raw_indexs.subList(i, j)); + int[] corrected_units = correct(sublist); + for (int k = 0; k < 4; ++k) { + indexs.add(corrected_units[k]); + } + } + i = j; + } + StringBuilder mTextBuilder = new StringBuilder(); + for(int i=0; i "+ text); + return text; + } + + // 译码及纠错 + private int[] correct(List frag) { + Log.d(TAG, "correct: " + frag); + int[] indexs = new int[4]; + if (frag.size() < 7) { + Log.d(TAG, "Loss of units"); + if (frag.size() < 6) { + while (frag.size() < 7) { + frag.add(0); + } + process_complete_pack(frag, indexs); + } else { + for (int i = 0; i <= 6; ++i) { + frag.add(i, 0); + int error_pos = process_complete_pack(frag, indexs); + if (error_pos == -1 || error_pos == i) { + Log.d(TAG, "correct success: " + error_pos + " = " + i); + return indexs; + } + frag.remove(i); + } + } + } else if (frag.size() > 7) { + Log.d(TAG, "Redundant of units"); + if (frag.size() > 8) { + frag = frag.subList(0, 7); + process_complete_pack(frag, indexs); + } else { + for (int i = 0; i < 8; ++i) { + int tmp = frag.get(i); + frag.remove(i); + int error_pos = process_complete_pack(frag, indexs); + if (error_pos == -1) { + Log.d(TAG, "correct success: " + error_pos + " now i = " + i); + return indexs; + } + frag.add(i, tmp); + } + } + } + else { + process_complete_pack(frag, indexs); + } + return indexs; + } + + private int process_complete_pack(List _frag, int[] indexs) { + Log.d(TAG, "process: " + _frag); + List frag = new ArrayList(_frag); + int pos = 0; + int chk1 = (frag.get(0) + frag.get(2) + frag.get(4) + frag.get(6)) % CodeBook.CODE_BOOK_LENGTH_CONTENT; + int chk2 = (frag.get(1) + frag.get(2) + frag.get(5) + frag.get(6)) % CodeBook.CODE_BOOK_LENGTH_CONTENT; + int chk3 = (frag.get(3) + frag.get(4) + frag.get(5) + frag.get(6)) % CodeBook.CODE_BOOK_LENGTH_CONTENT; + int offset = 0; + if (chk1 != 0) { + pos += 1; + offset = chk1; + } + if (chk2 != 0) { + pos += 2; + offset = chk2; + } + if (chk3 != 0) { + pos += 4; + offset = chk3; + } + pos -= 1; + if (pos != -1) { + int error_value = frag.get(pos); + frag.set(pos, (error_value + CodeBook.CODE_BOOK_LENGTH_CONTENT - offset) % CodeBook.CODE_BOOK_LENGTH_CONTENT); + } + Log.d(TAG, "processed: " + frag + "corrected: " + pos); + indexs[0] = frag.get(2); + indexs[1] = frag.get(4); + indexs[2] = frag.get(5); + indexs[3] = frag.get(6); + return pos; + } + + private String showResult_SeqHamming(List raw_indexs) { + List indexs = new ArrayList(); + if (raw_indexs.size() < 2) { + return null; + } + // 还原相邻相同字符 + while (raw_indexs.get(0) == CodeBook.DUPLICATE_INDEX_2 || raw_indexs.get(0) == CodeBook.DUPLICATE_INDEX_1) { + raw_indexs.remove(0); + } + int temp = raw_indexs.size(); + for (int i=raw_indexs.size()-1; i>=0; i--) { + if (raw_indexs.get(i) == CodeBook.DUPLICATE_INDEX_1 || raw_indexs.get(i) == CodeBook.DUPLICATE_INDEX_2) { + if (temp >= i) { + temp = i -1; + } + while(temp>0 && (raw_indexs.get(temp) == CodeBook.DUPLICATE_INDEX_1 || raw_indexs.get(temp) == CodeBook.DUPLICATE_INDEX_2)) { + temp --; + } + if (temp < 0) temp = 0; + raw_indexs.set(i, raw_indexs.get(temp)); + } + } + // 截取字符段进行译码 + for (int i = 0; i < raw_indexs.size(); ++i) { + while (i < raw_indexs.size() && raw_indexs.get(i) == CodeBook.SEP_INDEX) { + i++; + } + int j = i; + while (j < raw_indexs.size() && raw_indexs.get(j) != CodeBook.SEP_INDEX) { + j++; + } + if (j > i) { + List sublist = new ArrayList<>(raw_indexs.subList(i, j)); + int[] corrected_units = correct(sublist); + for (int k = 0; k < 4; ++k) { + indexs.add(corrected_units[k]); + } + } + i = j; + } + StringBuilder mTextBuilder = new StringBuilder(); + for(int i=0; i "+ text); + return text; + } + + // 译码及纠错 + private int[] correct(List frag) { + Log.d(TAG, "correct: " + frag); + int[] indexs = new int[4]; + if (frag.size() < 7) { + Log.d(TAG, "Loss of units"); + if (frag.size() < 6) { + while (frag.size() < 7) { + frag.add(0); + } + process_complete_pack(frag, indexs); + } else { + for (int i = 0; i <= 6; ++i) { + frag.add(i, 0); + int error_pos = process_complete_pack(frag, indexs); + if (error_pos == -1 || error_pos == i) { + Log.d(TAG, "correct success: " + error_pos + " = " + i); + return indexs; + } + frag.remove(i); + } + } + } else if (frag.size() > 7) { + Log.d(TAG, "Redundant of units"); + if (frag.size() > 8) { + frag = frag.subList(0, 7); + process_complete_pack(frag, indexs); + } else { + for (int i = 0; i < 8; ++i) { + int tmp = frag.get(i); + frag.remove(i); + int error_pos = process_complete_pack(frag, indexs); + if (error_pos == -1) { + Log.d(TAG, "correct success: " + error_pos + " now i = " + i); + return indexs; + } + frag.add(i, tmp); + } + } + } + else { + process_complete_pack(frag, indexs); + } + return indexs; + } + + private int process_complete_pack(List _frag, int[] indexs) { + Log.d(TAG, "process: " + _frag); + List frag = new ArrayList(_frag); + int pos = 0; + int chk1 = (frag.get(0) + frag.get(2) + frag.get(4) + frag.get(6)) % CodeBook.CODE_BOOK_LENGTH_CONTENT; + int chk2 = (frag.get(1) + frag.get(2) + frag.get(5) + frag.get(6)) % CodeBook.CODE_BOOK_LENGTH_CONTENT; + int chk3 = (frag.get(3) + frag.get(4) + frag.get(5) + frag.get(6)) % CodeBook.CODE_BOOK_LENGTH_CONTENT; + int offset = 0; + if (chk1 != 0) { + pos += 1; + offset = chk1; + } + if (chk2 != 0) { + pos += 2; + offset = chk2; + } + if (chk3 != 0) { + pos += 4; + offset = chk3; + } + pos -= 1; + if (pos != -1) { + int error_value = frag.get(pos); + frag.set(pos, (error_value + CodeBook.CODE_BOOK_LENGTH_CONTENT - offset) % CodeBook.CODE_BOOK_LENGTH_CONTENT); + } + Log.d(TAG, "processed: " + frag + "corrected: " + pos); + indexs[0] = frag.get(2); + indexs[1] = frag.get(4); + indexs[2] = frag.get(5); + indexs[3] = frag.get(6); + return pos; + } + + private String showResult_74hamming(List codeIndexs) throws UnsupportedEncodingException { + byte [] bytes = new byte[codeIndexs.size()]; + for (int i = 0; i < codeIndexs.size(); ++i) { + bytes[i] = codeIndexs.get(i).byteValue(); + } + String re = new String(bytes, "UTF-8"); + String text = null; + try { + text = new String(Base64.decode(re, Base64.NO_WRAP | Base64.NO_PADDING)); + } catch (Exception e) { + e.printStackTrace(); + } + + if (mHandler != null) { + Message msg = mHandler.obtainMessage(); + msg.what = MainActivity.MSG_RESULT; + msg.obj = text; + mHandler.sendMessage(msg); + } + + Log.d(TAG, "showResult:" + "____" + codeIndexs +"____"+ text); + + return text; + + } -} +} \ No newline at end of file diff --git a/app/src/main/java/xwh/sound/Encoder.java b/app/src/main/java/xwh/sound/Encoder.java index 613d525..4446c61 100644 --- a/app/src/main/java/xwh/sound/Encoder.java +++ b/app/src/main/java/xwh/sound/Encoder.java @@ -3,7 +3,9 @@ import android.text.TextUtils; import android.util.Log; +import java.io.UnsupportedEncodingException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -16,40 +18,143 @@ public class Encoder { public static List convertTextToCodes(String text) { List mCodes = new ArrayList<>(); if (!TextUtils.isEmpty(text)) { - mCodes.add(CodeBook.START_INDEX); //开始 mCodes.add(CodeBook.START_INDEX); //开始(防止丢失,重复添加两组) int len = text.length(); for (int i = 0; i < len; ++i) { // 内容 char ch = text.charAt(i); - int[] indexs = Utils.char2Indexs(ch); if (indexs != null) { mCodes.add(indexs[0]); mCodes.add(indexs[1]); } else { - //ret = false; Log.d(TAG, "invalidate char:" + ch); - // break; } } // 对内容进行crc校验 int[] crc = Utils.crc(mCodes, 2, mCodes.size()-1); mCodes.add(crc[0]); mCodes.add(crc[1]); - mCodes.add(CodeBook.END_INDEX); //结束 mCodes.add(CodeBook.END_INDEX); //结束(防止丢失,重复添加两组) - // 替换内容相邻相同字符 for (int i=mCodes.size()-3; i>2; i--) { if (mCodes.get(i) == mCodes.get(i-1)) { mCodes.set(i, i%2 == 0 ? CodeBook.DUPLICATE_INDEX_1 : CodeBook.DUPLICATE_INDEX_2); } } + } + return mCodes; + } + public static List convertTextToCode_SeqHamming(String text) { + List mCodes = new ArrayList<>(); + Log.d(TAG, text); + if (!TextUtils.isEmpty(text)) { + mCodes.add(CodeBook.START_INDEX); //开始 + mCodes.add(CodeBook.START_INDEX); //开始(防止丢失,重复添加两组) + int len = text.length(); + if (len % 2 == 1) { + if (text.endsWith("0")) { + text = text.concat("1"); + } else { + text = text.concat("0"); + } + } else { + if (text.endsWith("0")) { + text = text.concat("11"); + } else { + text = text.concat("00"); + } + } + Log.d(TAG, text); + len = text.length(); + for (int i = 0; i < len; i+=2) { // 内容 + char ch1 = text.charAt(i); + char ch2 = text.charAt(i+1); + int[] indexs1 = Utils.char2Indexs(ch1); + int[] indexs2 = Utils.char2Indexs(ch2); + int chk1; + int chk2; + int chk3; + if (indexs1 != null && indexs2 != null) { + chk1 = (CodeBook.CODE_BOOK_LENGTH_CONTENT - ((indexs1[0] + indexs1[1] + indexs2[1]) % CodeBook.CODE_BOOK_LENGTH_CONTENT)) % CodeBook.CODE_BOOK_LENGTH_CONTENT; + chk2 = (CodeBook.CODE_BOOK_LENGTH_CONTENT - ((indexs1[0] + indexs2[0] + indexs2[1]) % CodeBook.CODE_BOOK_LENGTH_CONTENT)) % CodeBook.CODE_BOOK_LENGTH_CONTENT; + chk3 = (CodeBook.CODE_BOOK_LENGTH_CONTENT - ((indexs1[1] + indexs2[0] + indexs2[1]) % CodeBook.CODE_BOOK_LENGTH_CONTENT)) % CodeBook.CODE_BOOK_LENGTH_CONTENT; + mCodes.add(chk1); + mCodes.add(chk2); + mCodes.add(indexs1[0]); + mCodes.add(chk3); + mCodes.add(indexs1[1]); + mCodes.add(indexs2[0]); + mCodes.add(indexs2[1]); + mCodes.add(CodeBook.SEP_INDEX); + mCodes.add(CodeBook.SEP_INDEX); + } else { + Log.d(TAG, "invalidate char:" + ch1 + " or " + ch2); + } + } + mCodes.add(CodeBook.END_INDEX); //结束 + mCodes.add(CodeBook.END_INDEX); //结束(防止丢失,重复添加两组) + Log.d(TAG, mCodes.toString()); + // 替换内容相邻相同字符 + for (int i=mCodes.size()-3; i>2; i--) { + if (mCodes.get(i) == mCodes.get(i-1) && mCodes.get(i) != CodeBook.SEP_INDEX) { + mCodes.set(i, i%2 == 0 ? CodeBook.DUPLICATE_INDEX_1 : CodeBook.DUPLICATE_INDEX_2); + } + } } + return mCodes; + } + public static List convertTextToCode_74hamming(String text) throws UnsupportedEncodingException { + byte[] bytes = text.getBytes("UTF-8"); + List mCodes = new ArrayList<>(); + mCodes.add(CodeBook.START_INDEX_HAMMING); + mCodes.add(CodeBook.START_INDEX_HAMMING); + int[] temp = new int[14]; + StringBuilder binaryString = new StringBuilder(); + // little endian + for (byte b: bytes) { + int cb = b; + Log.i("byte", String.valueOf(cb)); + for (int i = 0; i < 2; ++i) { + int curr = (b >> (i * 4)) & 0xf; + Log.i("curr", String.valueOf(curr)); + int d1 = curr & 1; + int d2 = (curr >> 1) & 1; + int d3 = (curr >> 2) & 1; + int d4 = (curr >> 3) & 1; + temp[2 + i * 7] = d1; + temp[4 + i * 7] = d2; + temp[5 + i * 7] = d3; + temp[6 + i * 7] = d4; + //chk bits + temp[0 + i * 7] = (d1 ^ d2 ^ d4); + temp[1 + i * 7] = (d1 ^ d3 ^ d4); + temp[3 + i * 7] = (d2 ^ d3 ^ d4); + } + Log.i("bitarray", Arrays.toString(temp)); + int last_code = -1; + boolean odd = true; + for (int i = 0; i < 7; ++i) { + int code = temp[i*2] + (temp[i*2+1] << 1); + if (code != last_code) { + odd = true; + mCodes.add(code); + last_code = code; + } else if (odd) { + mCodes.add(CodeBook.DUPLICATE_INDEX_1_HAMMING); + odd = false; + } else { + mCodes.add(CodeBook.DUPLICATE_INDEX_2_HAMMING); + odd = true; + } + } + } + mCodes.add(CodeBook.END_INDEX_HAMMING); + mCodes.add(CodeBook.END_INDEX_HAMMING); + Log.i("mCodes", "before: " + text + " after: " + mCodes); return mCodes; } } diff --git a/app/src/main/java/xwh/sound/MainActivity.java b/app/src/main/java/xwh/sound/MainActivity.java index 327a6b3..0313ba4 100644 --- a/app/src/main/java/xwh/sound/MainActivity.java +++ b/app/src/main/java/xwh/sound/MainActivity.java @@ -1,6 +1,8 @@ package xwh.sound; +import android.annotation.SuppressLint; import android.content.Context; +import android.content.Intent; import android.media.AudioManager; import android.os.Bundle; import android.os.Handler; @@ -9,37 +11,75 @@ import android.util.Base64; import android.util.Log; import android.view.View; +import android.webkit.WebView; import android.widget.Button; import android.widget.EditText; +import android.widget.RadioButton; +import android.widget.RadioGroup; import android.widget.TextView; import android.widget.Toast; - +import android.webkit.WebViewClient; + +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; +import java.util.LinkedList; +import java.util.Queue; + + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; public class MainActivity extends AppCompatActivity { public static final int MSG_RESULT = 1; public static final int MSG_CURRENT_FREQ = 2; - + public static int BASE_FREQ; + public static int FREQ_DISTANCE; + public static int METHOD; private EditText inputHz; + private EditText BaseFreq; + private EditText FreqDistance; private int[] cc = {0, 262, 294, 330, 349, 392, 440, 494}; private TextView recordResult; + private TextView Message; private TextView currentFreq; private Record record; - + private RadioGroup encodeMethod; + private RadioButton method1; + private RadioButton method2; + private RadioButton method3; private Handler mHandler; private Button btRecord; + private Queue url_queue = new LinkedList(); + + @SuppressLint("SetJavaScriptEnabled") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); inputHz = this.findViewById(R.id.input_hz); + BaseFreq = this.findViewById(R.id.baseFreq); + FreqDistance = this.findViewById(R.id.freqDistance); recordResult = this.findViewById(R.id.record_result); + Message = this.findViewById(R.id.message); currentFreq = this.findViewById(R.id.text_current_freq); + encodeMethod = this.findViewById(R.id.method); + method1 = this.findViewById(R.id.encoding1); + method2 = this.findViewById(R.id.encoding2); + method3 = this.findViewById(R.id.encoding3); mHandler = new Handler() { @Override @@ -47,6 +87,7 @@ public void handleMessage(Message msg) { String re = (String) msg.obj; if (msg.what == MSG_RESULT) { recordResult.append(re + "\n"); + url_queue.add(re); } else if (msg.what == MSG_CURRENT_FREQ) { currentFreq.setText(re + "HZ"); } @@ -82,7 +123,11 @@ public void run() { //PCMPlayer.start(CodeBook.freqsWave[0], 2000); - testBase64(); + try { + testBase64(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } //testCodes(); @@ -100,6 +145,37 @@ public void run() { } }); + findViewById(R.id.bt_play).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + String encode_method = ""; + if(method1.isChecked()) { + encode_method = "Raw"; + METHOD = 1; + } + else if(method2.isChecked()) { + encode_method = "74Hamming"; + METHOD = 2; + } + else if(method3.isChecked()) { + encode_method = "SeqHamming"; + METHOD =3; + } + Message.setText(""); + BASE_FREQ = Integer.parseInt(BaseFreq.getText().toString()); + FREQ_DISTANCE = Integer.parseInt(FreqDistance.getText().toString()); + for(int i=0; i 0){ + String name = url_queue.poll(); + Runnable networkTask = () -> { + try{ + URL url = new URL(name); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + if (conn.getResponseCode() == 200) { + Intent intent = new Intent(MainActivity.this,webpage.class); + intent.putExtra("context", name); + startActivity(intent); + } + }catch(Exception e){ + e.printStackTrace(); + } + }; + new Thread(networkTask).start(); + } + } } - - } - }).start(); - + }).start(); + } } }); } - private void testBase64() { + + + private void testBase64() throws UnsupportedEncodingException { String test = inputHz.getText().toString(); String str = Base64.encodeToString(test.getBytes(), Base64.NO_WRAP | Base64.NO_PADDING); - List codes = Encoder.convertTextToCodes(str); + List codes = Encoder.convertTextToCodes(str);; + if (METHOD == 2) + codes = Encoder.convertTextToCode_74hamming(str); + if (METHOD == 3) + codes = Encoder.convertTextToCode_SeqHamming(str); Log.d("Encode", "encodeArray:" + codes); PCMPlayer.getInstance().start(codes, 50); } + + private void testCodes() { List codes = new ArrayList<>(); codes.add(CodeBook.START_INDEX); diff --git a/app/src/main/java/xwh/sound/PCMPlayer.java b/app/src/main/java/xwh/sound/PCMPlayer.java index 756aec4..b4c08df 100644 --- a/app/src/main/java/xwh/sound/PCMPlayer.java +++ b/app/src/main/java/xwh/sound/PCMPlayer.java @@ -3,6 +3,7 @@ import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioTrack; +import android.util.Log; import java.util.List; @@ -16,8 +17,8 @@ public class PCMPlayer { public final static int DEFAULT_SAMPLE_RATE = 44100; private AudioTrack mAudioTrack; - private static PCMPlayer instance; + public static PCMPlayer getInstance() { if (instance == null) { instance = new PCMPlayer(); @@ -29,7 +30,6 @@ private void init() { int minBufSize = AudioTrack.getMinBufferSize(DEFAULT_SAMPLE_RATE, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT); - mAudioTrack=new AudioTrack(AudioManager.STREAM_MUSIC, DEFAULT_SAMPLE_RATE, AudioFormat.CHANNEL_OUT_MONO, // CHANNEL_CONFIGURATION_MONO, @@ -50,7 +50,6 @@ public byte[] sin(byte[] wave, int start, int freq, int length) { if(freq % 2 == 1) { // 偶数(奇数会有噪音,暂时没找到原因) freq += 1; } - for (int i = 0; i < length; i+=2) { int index = i+start; if (freq == 0) { @@ -74,23 +73,18 @@ public void start(int hz, int during){ if (isPlaying()) { return; } - int length = DEFAULT_SAMPLE_RATE * during / 1000 * 2; - //生成正弦波 byte[] wave = new byte[length]; - if(hz>0){ if (mAudioTrack == null) { init(); } mAudioTrack.play(); - sin(wave, 0, hz, length); }else{ sin(wave, 0, 0, length); } - mAudioTrack.write(wave, 0, length); } @@ -104,30 +98,25 @@ public void start(List codeIndexs, int during){ if (isPlaying()) { return; } - if (mAudioTrack == null) { init(); } mAudioTrack.play(); - int lengthItem = DEFAULT_SAMPLE_RATE * during / 1000 * 2; - byte[] waveAll = new byte[lengthItem * codeIndexs.size()]; - for(int i=0; i minBufferSize) { - buffer = new byte[mBufferSize]; - mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, DEFAULT_SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, mBufferSize); - mAudioRecord.startRecording(); - - short[] waveDatas = new short[mBufferSize/2]; - while (isRecording()) { readSize = mAudioRecord.read(buffer, 0, mBufferSize); if (AudioRecord.ERROR_INVALID_OPERATION != readSize) { short sh; - for (int i = 0; i < mBufferSize; i+=2) { - short sh1 = buffer[i]; sh1 &= 0xff; short sh2 = buffer[i+1]; sh2 <<= 8; sh = (short) ((sh1) | (sh2)); // 16Bit,两个字节一个采样值。 - waveDatas[i/2] = sh; - } - mDecoder.countFreq1(waveDatas, SAMPLE_STEP); - } - } } - } - - public void stopRecord() { if (mAudioRecord != null) { mAudioRecord.stop(); diff --git a/app/src/main/java/xwh/sound/webpage.java b/app/src/main/java/xwh/sound/webpage.java new file mode 100644 index 0000000..5fee3ec --- /dev/null +++ b/app/src/main/java/xwh/sound/webpage.java @@ -0,0 +1,47 @@ +package xwh.sound; + +import android.annotation.SuppressLint; +import android.content.Intent; +import android.net.http.SslError; +import android.os.Handler; +import android.os.Message; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.webkit.SslErrorHandler; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.TextView; +import android.app.Activity; + + +public class webpage extends Activity { + + + private WebView web; + @SuppressLint("SetJavaScriptEnabled") + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_webpage); + Intent intent=getIntent(); + String name=intent.getStringExtra("context"); + + web = this.findViewById(R.id.web_view); + + + + web.getSettings().setJavaScriptEnabled(true); + web.loadUrl(name); + web.setWebViewClient(new WebViewClient(){ + @Override + public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { + handler.proceed(); + } + }); +// web.setWebViewClient(new WebViewClient()); + + + + + } +} \ No newline at end of file diff --git a/app/src/main/network_security_config.xml b/app/src/main/network_security_config.xml new file mode 100644 index 0000000..1bc6506 --- /dev/null +++ b/app/src/main/network_security_config.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index fc8f2ca..8cfdd9a 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -11,13 +11,98 @@ android:id="@+id/input_hz" android:layout_width="match_parent" android:layout_height="wrap_content" - android:text=""/> + android:text="" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +