diff --git a/FT8CN Quick Guide v0.89.pdf b/FT8CN Quick Guide v0.89.pdf
new file mode 100644
index 0000000..f887d8b
Binary files /dev/null and b/FT8CN Quick Guide v0.89.pdf differ
diff --git a/ft8CN/.gitignore b/ft8CN/.gitignore
old mode 100755
new mode 100644
diff --git a/ft8CN/.idea/.gitignore b/ft8CN/.idea/.gitignore
old mode 100755
new mode 100644
diff --git a/ft8CN/.idea/.name b/ft8CN/.idea/.name
old mode 100755
new mode 100644
diff --git a/ft8CN/.idea/compiler.xml b/ft8CN/.idea/compiler.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/.idea/gradle.xml b/ft8CN/.idea/gradle.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/.idea/inspectionProfiles/Project_Default.xml b/ft8CN/.idea/inspectionProfiles/Project_Default.xml
old mode 100755
new mode 100644
index 0b687d1..211837e
--- a/ft8CN/.idea/inspectionProfiles/Project_Default.xml
+++ b/ft8CN/.idea/inspectionProfiles/Project_Default.xml
@@ -6,5 +6,8 @@
+
+
+
\ No newline at end of file
diff --git a/ft8CN/.idea/misc.xml b/ft8CN/.idea/misc.xml
old mode 100755
new mode 100644
index 12e56e5..4e1908a
--- a/ft8CN/.idea/misc.xml
+++ b/ft8CN/.idea/misc.xml
@@ -172,4 +172,11 @@
+
+
+
\ No newline at end of file
diff --git a/ft8CN/.idea/render.experimental.xml b/ft8CN/.idea/render.experimental.xml
old mode 100755
new mode 100644
diff --git a/ft8CN/app/build.gradle b/ft8CN/app/build.gradle
index 94c2598..302da94 100644
--- a/ft8CN/app/build.gradle
+++ b/ft8CN/app/build.gradle
@@ -22,7 +22,7 @@ android {
minSdk 23
targetSdk 33
versionCode 1
- versionName '0.89'
+ versionName '0.9'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
dataBinding{
@@ -80,13 +80,10 @@ dependencies {
implementation 'androidx.navigation:navigation-ui:2.4.1'
implementation 'com.google.android.gms:play-services-maps:18.0.2'
-// implementation 'com.google.code.gson:gson:2.7'
implementation 'commons-net:commons-net:3.6'//用于时间同步
implementation 'com.google.guava:guava:31.1-jre'//用于HashTable(多key的HashMap)
- //testImplementation 'junit:junit:4.13.2'
- //androidTestImplementation 'androidx.test.ext:junit:1.1.3'
- //androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+
implementation files('src/libs/MPAndroidChartv_3.1.0.jar')
implementation files('src/libs/nanohttpd-2.2.0.jar')
diff --git a/ft8CN/app/libs/arm64-v8a/libft8cn.so b/ft8CN/app/libs/arm64-v8a/libft8cn.so
index 6304cb8..eb92da8 100644
Binary files a/ft8CN/app/libs/arm64-v8a/libft8cn.so and b/ft8CN/app/libs/arm64-v8a/libft8cn.so differ
diff --git a/ft8CN/app/libs/armeabi-v7a/libft8cn.so b/ft8CN/app/libs/armeabi-v7a/libft8cn.so
index 3683d6e..a1e919a 100644
Binary files a/ft8CN/app/libs/armeabi-v7a/libft8cn.so and b/ft8CN/app/libs/armeabi-v7a/libft8cn.so differ
diff --git a/ft8CN/app/libs/x86/libft8cn.so b/ft8CN/app/libs/x86/libft8cn.so
index c054f3b..4a16858 100644
Binary files a/ft8CN/app/libs/x86/libft8cn.so and b/ft8CN/app/libs/x86/libft8cn.so differ
diff --git a/ft8CN/app/libs/x86_64/libft8cn.so b/ft8CN/app/libs/x86_64/libft8cn.so
index 75d7d58..05a379d 100644
Binary files a/ft8CN/app/libs/x86_64/libft8cn.so and b/ft8CN/app/libs/x86_64/libft8cn.so differ
diff --git a/ft8CN/app/src/main/AndroidManifest.xml b/ft8CN/app/src/main/AndroidManifest.xml
index fe4d34c..3181675 100644
--- a/ft8CN/app/src/main/AndroidManifest.xml
+++ b/ft8CN/app/src/main/AndroidManifest.xml
@@ -28,6 +28,7 @@
android:theme="@style/Theme.Ft8CN">
{
SQLiteDatabase db;
-
HashMap grids = new HashMap<>();
OnGetQsoGrids onGetQsoGrids;
@@ -1534,7 +1578,7 @@ public class DatabaseOpr extends SQLiteOpenHelper {
limitStr="limit 100 offset "+offset;
}
String querySQL = "select q.[call] as callsign ,q.gridsquare as grid" +
- ",q.band||\"(\"||q.freq||\" Mhz)\" as band \n" +
+ ",q.band||\"(\"||q.freq||\" MHz)\" as band \n" +
",q.qso_date as last_time ,q.mode ,q.isQSL,q.isLotW_QSL\n" +
"from QSLTable q inner join QSLTable q2 ON q.id =q2.id \n" +
"where (q.[call] like ?)\n" +
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/database/OperationBand.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/database/OperationBand.java
index e22d0d6..f2fc067 100644
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/database/OperationBand.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/database/OperationBand.java
@@ -147,7 +147,7 @@ public class OperationBand {
}
@SuppressLint("DefaultLocale")
public String getBandInfo(){
- return String.format("%s %.3f Mhz (%s)"
+ return String.format("%s %.3f MHz (%s)"
,marked?"*":" "
,(float)(band/1000000f)
,waveLength);
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/FlexRadio.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/FlexRadio.java
index ecddb5e..630b050 100644
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/FlexRadio.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/FlexRadio.java
@@ -34,7 +34,7 @@ public class FlexRadio {
private static final String TAG = "FlexRadio";
- private static int streamPort = 7051;
+ public static int streamPort = 7051;
private int flexStreamPort = 4993;
public boolean isPttOn = false;
public long streamTxId = 0x084000000;
@@ -443,15 +443,14 @@ public class FlexRadio {
* @param data 音频
*/
public void sendWaveData(float[] data) {
- float[] temp = new float[data.length * 4];
- for (int i = 0; i < data.length; i++) {//转成立体声,12000采样率转24000采样率
- temp[i * 4] = data[i];
- temp[i * 4 + 1] = data[i];
- temp[i * 4 + 2] = data[i];
- temp[i * 4 + 3] = data[i];
+ float[] temp = new float[data.length * 2];
+ for (int i = 0; i < data.length; i++) {//转成立体声,24000采样率
+ temp[i * 2] = data[i];
+ temp[i * 2 + 1] = data[i];
}
//port=4991;
//streamTxId=0x084000001;
+ //每5毫秒一个包?立体声,共256个float
Log.e(TAG, String.format("sendWaveData: streamid:0x%x,ip:%s,port:%d",streamTxId,ip, port) );
new Thread(new Runnable() {
@Override
@@ -463,9 +462,14 @@ public class FlexRadio {
int packetCount=0;
while (count temp.length) break;
@@ -479,14 +483,14 @@ public class FlexRadio {
throw new RuntimeException(e);
}
if (count>temp.length) break;
- }
+ //}
while (isPttOn) {
- if (System.currentTimeMillis() - now >= 41) {//40毫秒一个周期,每个周期3个包,每个包64个float。
+ if (System.currentTimeMillis() - now >= 5) {//5毫秒一个周期,每个周期256个float。
break;
}
}
if (!isPttOn){
- Log.e(TAG, String.format("count:%d,temp.length:%d",count,temp.length ));
+ // Log.e(TAG, String.format("count:%d,temp.length:%d",count,temp.length ));
}
}
@@ -531,6 +535,7 @@ public class FlexRadio {
}
return s.toString();
}
+ @SuppressLint("DefaultLocale")
public static String floatToStr(float[] data) {
StringBuilder s = new StringBuilder();
for (int i = 0; i < data.length; i++) {
@@ -815,7 +820,7 @@ public class FlexRadio {
@SuppressLint("DefaultLocale")
public synchronized void commandStreamCreateDaxTx(int channel) {
//sendCommand(FlexCommand.STREAM_CREATE_DAX_TX, String.format("stream create type=dax_tx dax_channel=%d", channel));
- //sendCommand(FlexCommand.STREAM_CREATE_DAX_TX, String.format("stream create type=dax_tx compression=none"));
+// sendCommand(FlexCommand.STREAM_CREATE_DAX_TX, String.format("stream create type=dax_tx compression=none"));
sendCommand(FlexCommand.STREAM_CREATE_DAX_TX, String.format("stream create type=remote_audio_tx"));
}
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/RadioUdpClient.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/RadioUdpClient.java
index 3e0d84f..d15b888 100644
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/RadioUdpClient.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/flex/RadioUdpClient.java
@@ -43,18 +43,6 @@ public class RadioUdpClient {
sendDataRunnable.address=address;
sendDataRunnable.port=port;
sendDataThreadPool.execute(sendDataRunnable);
-// new Thread(new Runnable() {
-// @Override
-// public void run() {
-// DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
-// try {
-// sendSocket.send(packet);
-// } catch (IOException e) {
-// e.printStackTrace();
-// Log.e(TAG, "run: " + e.getMessage());
-// }
-// }
-// }).start();
}
private static class SendDataRunnable implements Runnable{
@@ -88,7 +76,7 @@ public class RadioUdpClient {
if (activated) {//通过activated判断是否结束接收线程,并清空sendSocket指针
sendSocket = new DatagramSocket(null);//绑定的端口号随机
sendSocket.bind(new InetSocketAddress(port));
- Log.e(TAG, "openUdpPort: "+sendSocket.getLocalPort());
+ // Log.e(TAG, "openUdpPort: "+sendSocket.getLocalPort());
receiveData();
}else {
if (sendSocket!=null){
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8listener/FT8SignalListener.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8listener/FT8SignalListener.java
index 850d04d..45f600e 100644
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8listener/FT8SignalListener.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/ft8listener/FT8SignalListener.java
@@ -111,6 +111,13 @@ public class FT8SignalListener {
public void decodeFt8(long utc, float[] voiceData) {
+ //此处是测试用代码-------------------------
+// String fileName = getCacheFileName("test_01.wav");
+// Log.e(TAG, "onClick: fileName:" + fileName);
+// WaveFileReader reader = new WaveFileReader(fileName);
+// int data[][] = reader.getData();
+ //----------------------------------------------------------
+
new Thread(new Runnable() {
@Override
public void run() {
@@ -119,17 +126,20 @@ public class FT8SignalListener {
onFt8Listen.beforeListen(utc);
}
-
+// float[] tempData = ints2floats(data);
///读入音频数据,并做预处理
//其实这种方式要注意一个问题,在一个周期之内,必须解码完毕,否则新的解码又要开始了
long ft8Decoder = InitDecoder(utc, FT8Common.SAMPLE_RATE
, voiceData.length, true);
+// , tempData.length, true);
DecoderMonitorPressFloat(voiceData, ft8Decoder);//读入音频数据
+// DecoderMonitorPressFloat(tempData, ft8Decoder);//读入音频数据
ArrayList allMsg = new ArrayList<>();
+// ArrayList msgs = runDecode(utc, voiceData,false);
ArrayList msgs = runDecode(ft8Decoder, utc, false);
addMsgToList(allMsg, msgs);
timeSec = System.currentTimeMillis() - time;
@@ -140,6 +150,7 @@ public class FT8SignalListener {
if (GeneralVariables.deepDecodeMode) {//进入深度解码模式
+ //float[] newSignal=tempData;
msgs = runDecode(ft8Decoder, utc, true);
addMsgToList(allMsg, msgs);
timeSec = System.currentTimeMillis() - time;
@@ -274,14 +285,34 @@ public class FT8SignalListener {
super.finalize();
}
-
+ public OnWaveDataListener getOnWaveDataListener() {
+ return onWaveDataListener;
+ }
public void setOnWaveDataListener(OnWaveDataListener onWaveDataListener) {
this.onWaveDataListener = onWaveDataListener;
}
+ public String getCacheFileName(String fileName) {
+ return GeneralVariables.getMainContext().getCacheDir() + "/" + fileName;
+ }
+ public float[] ints2floats(int data[][]) {
+ float temp[] = new float[data[0].length];
+ for (int i = 0; i < data[0].length; i++) {
+ temp[i] = data[0][i] / 32768.0f;
+ }
+ return temp;
+ }
+
+ public int[] floats2ints(float data[]) {
+ int temp[] = new int[data.length];
+ for (int i = 0; i < data.length; i++) {
+ temp[i] = (int) (data[i] * 32767.0f);
+ }
+ return temp;
+ }
/**
* 解码的第一步,初始化解码器,获取解码器的地址。
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/grid_tracker/GridOsmMapView.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/grid_tracker/GridOsmMapView.java
index eff74c3..75b694f 100644
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/grid_tracker/GridOsmMapView.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/grid_tracker/GridOsmMapView.java
@@ -280,8 +280,10 @@ public class GridOsmMapView {
* @return 图层
*/
public GridPolygon getGridPolygon(String grid) {
- for (GridPolygon polygon : gridPolygons) {
- if (polygon.grid.equals(grid)) return polygon;
+ synchronized (gridPolygons) {
+ for (GridPolygon polygon : gridPolygons) {
+ if (polygon.grid.equals(grid)) return polygon;
+ }
}
return null;
}
@@ -380,13 +382,18 @@ public class GridOsmMapView {
*/
public synchronized GridPolygon addGridPolygon(String grid, GridMode gridMode) {
if (gridMapView == null) return null;
- //here, we create a polygon using polygon class, note that you need 4 points in order to make a rectangle
- GridPolygon polygon = new GridPolygon(context, gridMapView, grid, gridMode);
+ if (gridMapView.getRepository()==null) return null;
+ try {//当日志量过多时,会出现闪退的问题,在此处做一个异常捕获,防止闪退
- gridPolygons.add(polygon);
- gridMapView.getOverlays().add(polygon);
+ GridPolygon polygon = new GridPolygon(context, gridMapView, grid, gridMode);
+ gridPolygons.add(polygon);
+ gridMapView.getOverlays().add(polygon);
+ return polygon;
- return polygon;
+ } catch (Exception e) {
+ //throw new RuntimeException(e);
+ }
+ return null;
}
/**
@@ -689,40 +696,40 @@ public class GridOsmMapView {
this.grid = grid;
this.gridMode = gridMode;
this.context = context;
- //infoWindow=new BasicInfoWindow(R.layout.tracker_grid_info_win,mapView);
- //this.details = details;
setTitle(grid);
- //setSubDescription(details);
setStrokeWidth(3f);
setStrokeColor(this.context.getColor(R.color.osm_grid_out_line_color));
- //setSnippet("445534343");
updateGridMode();
ArrayList pts = LatLngs2GeoPoints(MaidenheadGrid.gridToPolygon(grid));
setPoints(pts);
-
setVisible(true);
}
- public void updateGridMode() {
- switch (gridMode) {
- case QSL:
- setFillColor(this.context.getColor(R.color.tracker_sample_qsl_color));
- break;
- case QSO:
- setFillColor(this.context.getColor(R.color.tracker_sample_qso_color));
- break;
- case QSX:
- setFillColor(this.context.getColor(R.color.tracker_sample_qsx_color));
- break;
+ public synchronized void updateGridMode() {
+ synchronized (this) {//防止闪退
+ switch (gridMode) {
+ case QSL:
+ this.mFillPaint.setColor(this.context.getColor(R.color.tracker_sample_qsl_color));
+ //setFillColor(this.context.getColor(R.color.tracker_sample_qsl_color));
+ break;
+ case QSO:
+ this.mFillPaint.setColor(this.context.getColor(R.color.tracker_sample_qso_color));
+ //setFillColor(this.context.getColor(R.color.tracker_sample_qso_color));
+ break;
+ case QSX:
+ this.mFillPaint.setColor(this.context.getColor(R.color.tracker_sample_qsx_color));
+ //setFillColor(this.context.getColor(R.color.tracker_sample_qsx_color));
+ break;
+ }
}
}
- public void upgradeGridMode(GridMode mode) {
+ public synchronized void upgradeGridMode(GridMode mode) {
if (mode.ordinal() > gridMode.ordinal()) {
gridMode = mode;
updateGridMode();
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/grid_tracker/GridTrackerMainActivity.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/grid_tracker/GridTrackerMainActivity.java
index baf5424..da2b4bc 100644
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/grid_tracker/GridTrackerMainActivity.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/grid_tracker/GridTrackerMainActivity.java
@@ -10,6 +10,7 @@ import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Canvas;
@@ -21,6 +22,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.util.AttributeSet;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
@@ -28,6 +30,7 @@ import android.view.WindowManager;
import android.view.animation.AnimationUtils;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.content.ContextCompat;
@@ -127,11 +130,11 @@ public class GridTrackerMainActivity extends AppCompatActivity {
//设置深色模式
getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_YES);
- //setContentView(R.layout.activity_grid_tracker_main);
//全屏
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN
, WindowManager.LayoutParams.FLAG_FULLSCREEN);
- setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);//设定为横屏
+
+
mainViewModel = MainViewModel.getInstance(this);
binding = ActivityGridTrackerMainBinding.inflate(getLayoutInflater());
@@ -187,30 +190,6 @@ public class GridTrackerMainActivity extends AppCompatActivity {
@SuppressLint({"DefaultLocale", "NotifyDataSetChanged"})
@Override
public void onChanged(Integer integer) {
-// callingListAdapter.notifyDataSetChanged();
- //当列表下部稍微多出一些,自动上移
-// if (callMessagesRecyclerView.computeVerticalScrollRange()
-// - callMessagesRecyclerView.computeVerticalScrollExtent()
-// - callMessagesRecyclerView.computeVerticalScrollOffset() < 500) {
-// callMessagesRecyclerView.scrollToPosition(callingListAdapter.getItemCount() - 1);
-// }
-// if (mainViewModel.currentMessages != null) {
-//
-// ToastMessage.show(String.format(GeneralVariables.getStringFromResource(
-// R.string.tracker_decoded_new)
-// , mainViewModel.currentDecodeCount)
-// + " " + String.format(
-// getString(R.string.decoding_takes_milliseconds)
-// , mainViewModel.ft8SignalListener.decodeTimeSec.getValue()));
-// //画电台之间的连线
-// //对CQ的电台打点
-// gridOsmMapView.clearLines();
-// gridOsmMapView.clearMarkers();
-// for (Ft8Message msg : mainViewModel.currentMessages) {
-// drawMessage(msg);//在地图上画每一个消息
-// }
-// gridOsmMapView.showInfoWindows();
-// }
}
});
mainViewModel.mutableIsDecoding.observe(this, new Observer() {
@@ -329,7 +308,6 @@ public class GridTrackerMainActivity extends AppCompatActivity {
//获取曾经通联过的网格
mainViewModel.databaseOpr.getQsoGridQuery(new DatabaseOpr.OnGetQsoGrids() {
- //ConcurrentHashMap
@Override
public void onAfterQuery(HashMap grids) {
for (Map.Entry entry : grids.entrySet()) {
@@ -377,6 +355,7 @@ public class GridTrackerMainActivity extends AppCompatActivity {
setContentView(binding.getRoot());
}
+
/**
* 在地图上画消息,包括收发消息和CQ消息
*
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/html/LogHttpServer.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/html/LogHttpServer.java
index 49f32a4..860dbb2 100644
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/html/LogHttpServer.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/html/LogHttpServer.java
@@ -18,6 +18,7 @@ import com.bg7yoz.ft8cn.MainViewModel;
import com.bg7yoz.ft8cn.R;
import com.bg7yoz.ft8cn.connector.CableSerialPort;
import com.bg7yoz.ft8cn.connector.ConnectMode;
+import com.bg7yoz.ft8cn.database.AfterInsertQSLData;
import com.bg7yoz.ft8cn.database.ControlMode;
import com.bg7yoz.ft8cn.database.RigNameList;
import com.bg7yoz.ft8cn.log.LogFileImport;
@@ -42,6 +43,8 @@ public class LogHttpServer extends NanoHTTPD {
public static int DEFAULT_PORT = 7050;
private static final String TAG = "LOG HTTP";
+ private ImportTaskList importTaskList = new ImportTaskList();//导如日志的任务列表
+
public LogHttpServer(MainViewModel viewModel, int port) {
super(port);
@@ -100,6 +103,10 @@ public class LogHttpServer extends NanoHTTPD {
msg = HTML_STRING(showQSLTable());
} else if (uri.equalsIgnoreCase("IMPORTLOG")) {
msg = HTML_STRING(showImportLog());
+ } else if (uri.equalsIgnoreCase("GETIMPORTTASK")) {//这个是用户实时获取导入状态的URI
+ msg = HTML_STRING(makeGetImportTaskHTML(session));
+ } else if (uri.equalsIgnoreCase("CANCELTASK")) {//这个是用户取消导入的URI
+ msg = HTML_STRING(doCancelImport(session));
} else if (uri.equalsIgnoreCase("IMPORTLOGDATA")) {
msg = HTML_STRING(doImportLogFile(session));
} else if (uri.equalsIgnoreCase("SHOWALLQSL")) {
@@ -159,35 +166,27 @@ public class LogHttpServer extends NanoHTTPD {
//Map header = session.getHeaders();
try {
session.parseBody(files);
+
+ Log.e(TAG, "doImportLogFile: information:" + files.toString());
String param = files.get("file1");//这个是post或put文件的key
- LogFileImport logFileImport = new LogFileImport(param);
- ArrayList> recordList = logFileImport.getLogRecords();
- int importCount = 0;
- int recordCount = 0;
- for (HashMap record : recordList) {
- QSLRecord qslRecord = new QSLRecord(record);
- recordCount++;
- if (mainViewModel.databaseOpr.doInsertQSLData(qslRecord)) {
- importCount++;
+ ImportTaskList.ImportTask task = importTaskList.addTask(param.hashCode());//生成一个新的任务
+
+ LogFileImport logFileImport = new LogFileImport(task, param);
+
+
+ //把提交的数据放到一个独立的线程运行,防止WEB页面停留太久
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ doImportADI(task, logFileImport);
}
- }
+ }).start();
- StringBuilder temp = new StringBuilder();
- temp.append(String.format(GeneralVariables.getStringFromResource(R.string.html_import_count) + "
"
- , recordCount, importCount, logFileImport.getErrorCount()));
- if (logFileImport.getErrorCount() > 0) {
- temp.append("");
- temp.append(String.format(" | %d malformed logs |
\n", logFileImport.getErrorCount()));
- for (int key : logFileImport.getErrorLines().keySet()) {
- temp.append(String.format("%d | %s |
\n"
- , key, logFileImport.getErrorLines().get(key)));
- }
+ //重定向,跳转到实时导入信息界面
+ return String.format("\n"
+ , param.hashCode());
- temp.append("
");
- }
- mainViewModel.databaseOpr.getQslDxccToMap();//更新一下已经通联的分区
- return temp.toString();
} catch (IOException | ResponseException e) {
e.printStackTrace();
return String.format(GeneralVariables.getStringFromResource(R.string.html_import_failed)
@@ -197,6 +196,93 @@ public class LogHttpServer extends NanoHTTPD {
return GeneralVariables.getStringFromResource(R.string.html_illegal_command);
}
+
+ private String makeGetImportTaskHTML(IHTTPSession session) {
+ String script = "";
+ script = "\n\n";
+ Map pars = session.getParms();
+ if (pars.get("session") != null) {
+ String s = Objects.requireNonNull(pars.get("session"));
+ int id = Integer.parseInt(s);
+ if (!importTaskList.checkTaskIsRunning(id)) {//如果任务停止,就没有必要刷新了
+ script = "";
+ }
+ return script + importTaskList.getTaskHTML(id);
+ }
+
+ return script;
+ }
+
+ @SuppressLint("DefaultLocale")
+ private String doCancelImport(IHTTPSession session) {
+ Map pars = session.getParms();
+ Log.e(TAG, "doCancelImport: " + pars.toString());
+ if (pars.get("session") != null) {
+ String s = Objects.requireNonNull(pars.get("session"));
+ int id = Integer.parseInt(s);
+ importTaskList.cancelTask(id);
+ return String.format("\n"
+ , id);
+ }
+ return "";
+ }
+
+ @SuppressLint("DefaultLocale")
+ private void doImportADI(ImportTaskList.ImportTask task, LogFileImport logFileImport) {
+ task.setStatus(ImportTaskList.ImportState.IMPORTING);
+ ArrayList> recordList = logFileImport.getLogRecords();//以正则表达式:[<][Ee][Oo][Rr][>]分行
+ task.importedCount = 0;
+ task.count = recordList.size();//总行数
+ for (HashMap record : recordList) {
+ if (task.status == ImportTaskList.ImportState.CANCELED) break;//检查是不是取消导入
+
+ QSLRecord qslRecord = new QSLRecord(record);
+ task.processCount++;
+ if (mainViewModel.databaseOpr.doInsertQSLData(qslRecord, new AfterInsertQSLData() {
+ @Override
+ public void doAfterInsert(boolean isInvalid, boolean isNewQSL) {
+ if (isInvalid) {
+ task.invalidCount++;
+ return;
+ }
+ if (isNewQSL) {
+ task.newCount++;
+ } else {
+ task.updateCount++;
+ }
+ }
+ })) {
+ task.importedCount++;
+ }
+ }
+
+
+ //此处是显示错误的数据
+ StringBuilder temp = new StringBuilder();
+ if (logFileImport.getErrorCount() > 0) {
+ temp.append("");
+ temp.append(String.format(" | %d malformed logs |
\n", logFileImport.getErrorCount()));
+ for (int key : logFileImport.getErrorLines().keySet()) {
+ temp.append(String.format("%d | %s |
\n"
+ , key, logFileImport.getErrorLines().get(key)));
+ }
+
+ temp.append("
");
+ }
+
+ task.errorMsg = temp.toString();
+ if (task.status!= ImportTaskList.ImportState.CANCELED) {
+ task.setStatus(ImportTaskList.ImportState.FINISHED);
+ }
+ mainViewModel.databaseOpr.getQslDxccToMap();//更新一下已经通联的分区
+ }
+
+
/**
* 获取配置信息
*
@@ -1014,10 +1100,11 @@ public class LogHttpServer extends NanoHTTPD {
/**
* 把swo的QSO日志导出到文件
+ *
* @param exportFile 文件名
- * @param callsign 呼号
+ * @param callsign 呼号
* @param start_date 起始日期
- * @param end_date 结束日期
+ * @param end_date 结束日期
* @return 数据
*/
@SuppressLint("Range")
@@ -1078,6 +1165,7 @@ public class LogHttpServer extends NanoHTTPD {
/**
* 查询SWL日志
+ *
* @param session 会话
* @return html
*/
@@ -1263,10 +1351,11 @@ public class LogHttpServer extends NanoHTTPD {
/**
* 把QSO日志导出到文件
+ *
* @param exportFile 文件名
- * @param callsign 呼号
+ * @param callsign 呼号
* @param start_date 起始日期
- * @param end_date 结束日期
+ * @param end_date 结束日期
* @return 数据
*/
@SuppressLint("Range")
@@ -1317,6 +1406,7 @@ public class LogHttpServer extends NanoHTTPD {
/**
* 查询QSO日志
+ *
* @param session 会话
* @return html
*/
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/log/LogFileImport.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/log/LogFileImport.java
index 45253bc..d040203 100644
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/log/LogFileImport.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/log/LogFileImport.java
@@ -2,6 +2,8 @@ package com.bg7yoz.ft8cn.log;
import android.util.Log;
+import com.bg7yoz.ft8cn.html.ImportTaskList;
+
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
@@ -22,6 +24,7 @@ public class LogFileImport {
private static final String TAG = "LogFileImport";
private final String fileContext;
private final HashMap errorLines=new HashMap<>();
+ private ImportTaskList.ImportTask importTask;
/**
* 构建函数,需要文件名,如果在读取文件时出错,会回抛异常
@@ -29,7 +32,8 @@ public class LogFileImport {
* @param logFileName 日志文件名
* @throws IOException 回抛异常
*/
- public LogFileImport(String logFileName) throws IOException {
+ public LogFileImport(ImportTaskList.ImportTask task, String logFileName) throws IOException {
+ importTask=task;
FileInputStream logFileStream = new FileInputStream(logFileName);
byte[] bytes = new byte[logFileStream.available()];
logFileStream.read(bytes);
@@ -99,6 +103,7 @@ public class LogFileImport {
records.add(record);//保存记录
}catch (Exception e){
errorLines.put(count,s.replace("<","<"));//把错误的内容保存下来。
+ importTask.readErrorCount=errorLines.size();
}
}
return records;
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/log/QSLRecord.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/log/QSLRecord.java
index 1c8ec5c..974690c 100644
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/log/QSLRecord.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/log/QSLRecord.java
@@ -48,6 +48,9 @@ public class QSLRecord {
public boolean saved = false;//是否被保存到数据库中
+ public boolean isInvalid=false;//是否解析出错
+ public String errorMSG="";//如果解析出错,错误的消息
+
/**
* 用于SWL QSO记录,记录SWL QSO的条件是收听到双方的信号报告
*
@@ -137,6 +140,8 @@ public class QSLRecord {
float freq = Float.parseFloat(Objects.requireNonNull(map.get("FREQ")));
bandFreq = Math.round(freq * 1000000);
} catch (NumberFormatException e) {
+ isInvalid=true;
+ errorMSG="freq:"+e.getMessage();
e.printStackTrace();
Log.e(TAG, "QSLRecord: freq" + e.getMessage());
}
@@ -193,6 +198,8 @@ public class QSLRecord {
try {//要把float转成Long
receivedReport = Integer.parseInt(Objects.requireNonNull(map.get("RST_RCVD").trim()));
} catch (NumberFormatException e) {
+ isInvalid=true;
+ errorMSG="RST_RCVD:"+e.getMessage();
e.printStackTrace();
Log.e(TAG, "QSLRecord: RST_RCVD:" + e.getMessage());
}
@@ -204,6 +211,8 @@ public class QSLRecord {
try {//要把float转成Long
sendReport = Integer.parseInt(Objects.requireNonNull(map.get("RST_SENT").trim()));
} catch (NumberFormatException e) {
+ isInvalid=true;
+ errorMSG="RST_SENT:"+e.getMessage();
e.printStackTrace();
Log.e(TAG, "QSLRecord: RST_SENT:" + e.getMessage());
}
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/BaseRigOperation.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/BaseRigOperation.java
index f4174f6..796fb00 100644
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/BaseRigOperation.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/BaseRigOperation.java
@@ -10,7 +10,7 @@ import android.annotation.SuppressLint;
public class BaseRigOperation {
@SuppressLint("DefaultLocale")
public static String getFrequencyStr(long freq) {
- return String.format("%d.%03dMhz", freq / 1000000, (freq % 1000000) / 1000);
+ return String.format("%d.%03dMHz", freq / 1000000, (freq % 1000000) / 1000);
}
/**
* 检查是不是在WSPR2的频段内
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/FlexNetworkRig.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/FlexNetworkRig.java
index a0cb8ed..1c9a0a8 100644
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/FlexNetworkRig.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/FlexNetworkRig.java
@@ -4,15 +4,18 @@ import android.annotation.SuppressLint;
import android.util.Log;
import com.bg7yoz.ft8cn.Ft8Message;
+import com.bg7yoz.ft8cn.GeneralVariables;
import com.bg7yoz.ft8cn.connector.FlexConnector;
import com.bg7yoz.ft8cn.flex.FlexCommand;
import com.bg7yoz.ft8cn.flex.FlexRadio;
+import com.bg7yoz.ft8cn.ft8transmit.GenerateFT8;
-public class FlexNetworkRig extends BaseRig{
- private static final String TAG="FlexNetworkRig";
+public class FlexNetworkRig extends BaseRig {
+ private static final String TAG = "FlexNetworkRig";
private int commandSeq = 1;//指令的序列
private FlexCommand flexCommand;
private String commandStr;
+
//private final int ctrAddress=0xE0;//接收地址,默认0xE0;电台回复命令有时也可以是0x00
//private byte[] dataBuffer=new byte[0];//数据缓冲区
@SuppressLint("DefaultLocale")
@@ -31,10 +34,12 @@ public class FlexNetworkRig extends BaseRig{
public synchronized void commandSliceTune(int sliceOder, String freq) {
sendCommand(FlexCommand.SLICE_TUNE, String.format("slice t %d %s", sliceOder, freq));
}
+
@SuppressLint("DefaultLocale")
public synchronized void commandSliceSetMode(int sliceOder, FlexRadio.FlexMode mode) {
sendCommand(FlexCommand.SLICE_SET_TX_ANT, String.format("slice s %d mode=%s", sliceOder, mode.toString()));
}
+
@Override
public void setPTT(boolean on) {
getConnector().setPttOn(on);
@@ -43,7 +48,7 @@ public class FlexNetworkRig extends BaseRig{
@Override
public boolean isConnected() {
- if (getConnector()==null) {
+ if (getConnector() == null) {
return false;
}
return getConnector().isConnected();
@@ -51,7 +56,7 @@ public class FlexNetworkRig extends BaseRig{
@Override
public void setUsbModeToRig() {
- if (getConnector()!=null){
+ if (getConnector() != null) {
commandSliceSetMode(0, FlexRadio.FlexMode.DIGU);//设置操作模式
}
}
@@ -59,13 +64,12 @@ public class FlexNetworkRig extends BaseRig{
@SuppressLint("DefaultLocale")
@Override
public void setFreqToRig() {
- if (getConnector()!=null){
- commandSliceTune(0,String.format("%.3f", getFreq()/1000000f));
+ if (getConnector() != null) {
+ commandSliceTune(0, String.format("%.3f", getFreq() / 1000000f));
}
}
-
@Override
public void onReceiveData(byte[] data) {
//ToastMessage.show("--"+byteToStr(data));
@@ -74,18 +78,23 @@ public class FlexNetworkRig extends BaseRig{
@Override
public void readFreqFromRig() {
- if (getConnector()!=null){
+ if (getConnector() != null) {
//getConnector().sendData(IcomRigConstant.setReadFreq(ctrAddress, getCivAddress()));
}
}
@Override
public void sendWaveData(Ft8Message message) {
-// Log.e(TAG, "sendWaveData: "+data.length );
-// if (getConnector()!=null){
-// getConnector().sendWaveData(data);
-// }
+ if (getConnector() != null) {
+ float[] data = GenerateFT8.generateFt8(message, GeneralVariables.getBaseFrequency()
+ , 24000);//flex音频的采样率是24000,todo 此处可改为动态设置24000,48000
+ if (data == null) {
+ setPTT(false);
+ return;
+ }
+ getConnector().sendWaveData(data);
+ }
}
@Override
diff --git a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/InstructionSet.java b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/InstructionSet.java
index b012935..46ad6b2 100644
--- a/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/InstructionSet.java
+++ b/ft8CN/app/src/main/java/com/bg7yoz/ft8cn/rigs/InstructionSet.java
@@ -16,4 +16,7 @@ public class InstructionSet {
public static final int FLEX_NETWORK=12;//FLEX网络模式连接;
public static final int XIEGU_6100=13;//协谷X6100;
public static final int KENWOOD_TS2000=14;//建武TS2000,发射指令是TX0;
+ public static final int WOLF_SDR_DIGU=15;//UA3REO Wolf SDR,DIG-U模式,兼容YAESU 450D;
+ public static final int WOLF_SDR_USB=16;//UA3REO Wolf SDR,USB模式,兼容YAESU 450D;
+
}
diff --git a/ft8CN/app/src/main/res/values-el/strings.xml b/ft8CN/app/src/main/res/values-el/strings.xml
index f970a2a..9f570ab 100644
--- a/ft8CN/app/src/main/res/values-el/strings.xml
+++ b/ft8CN/app/src/main/res/values-el/strings.xml
@@ -472,7 +472,18 @@
Λειτουργία πολλαπλής αποκωδικοποίησης
Παρακαλώ χρησιμοποιήστε τον web browser των άλλων συσκευών για εξαγωγή στο παρασκήνιο στο ίδιο LAN που είναι αυτή η συσκευή.\nPlease connect to a valid Wi-Fi
decode_help_en.txt
+ Το έργο δεν υπάρχει!
+ Εισαγωγή...
+ Τελειώσει!
+ Error lines: %d
+ Append QSLs: %d
+ Update QSLs: %d
+ Invalid QSLs: %d
+ "Progress: "
+ Validated QSL: %d
+ Canceled!
+ Stop
-
+
\ No newline at end of file
diff --git a/ft8CN/app/src/main/res/values-es/strings.xml b/ft8CN/app/src/main/res/values-es/strings.xml
index 9b6ec62..c3b0487 100644
--- a/ft8CN/app/src/main/res/values-es/strings.xml
+++ b/ft8CN/app/src/main/res/values-es/strings.xml
@@ -166,7 +166,7 @@
Confirmación manual (%s)
- Gird:%s
+ Grid:%s
Hora de inicio : %s
Hora de finalización: %s
Rst recibido : %s
@@ -472,6 +472,17 @@
Modo de decodificación múltiple
Utilice el navegador web de otros dispositivos para exportar desde el fondo en la misma LAN que este dispositivo.\nPlease connect to a valid Wi-Fi
decode_help_en.txt
-
+ ¡La tarea no existe!
+ Importador...
+ ¡Terminado!
+ Error lines: %d
+ Append QSLs: %d
+ Update QSLs: %d
+ Invalid QSLs: %d
+ "Progress: "
+ Validated QSL: %d
+ Canceled!
+ Stop
+
\ No newline at end of file
diff --git a/ft8CN/app/src/main/res/values-ja/strings.xml b/ft8CN/app/src/main/res/values-ja/strings.xml
index 0600511..a0d6300 100644
--- a/ft8CN/app/src/main/res/values-ja/strings.xml
+++ b/ft8CN/app/src/main/res/values-ja/strings.xml
@@ -755,6 +755,17 @@
ディープ
このデバイスと同じLANに接続し、ほかのデバイスでウェブブラウザーを利用してエクスポートしてください。\nブラウザーのアドレスバーに次の内容を入力してください:\n有効なWiFiに接続してください。
decode_help_en.txt
+ タスクが存在しません!
+ インポート中…
+ インポート済み!
+ エラー行: %d
+ 新QSL数: %d
+ 変更されたQSL数: %d
+ 無効QSL数: %d
+ 進捗:
+ 確認済みQSL: %d
+ インポートが取り消されました!
+ インポートを取り消す
\ No newline at end of file
diff --git a/ft8CN/app/src/main/res/values-zh-rCN/strings.xml b/ft8CN/app/src/main/res/values-zh-rCN/strings.xml
index 0c20174..1392434 100644
--- a/ft8CN/app/src/main/res/values-zh-rCN/strings.xml
+++ b/ft8CN/app/src/main/res/values-zh-rCN/strings.xml
@@ -211,6 +211,7 @@
通联开始时间
通联开始日期
通联结束时间
+ 通联结束日期
模式
网格
频段
@@ -470,6 +471,17 @@
多次解码
请在与本机相同的局域网下,用其它设备的网页浏览器从后台导出。\n在浏览器的地址栏中输入如下内容:\n无法获取合适的IP地址,请连接到一个有效的Wifi。
decode_help.txt
+ 任务不存在!
+ 正在导入...
+ 导入结束!
+ 错误行数:%d
+ 新增QSL:%d
+ 更新QSL:%d
+ 无效的QSL:%d
+ 进度:
+ 有效的QSL:%d
+ 导入被取消!
+ 停止导入
\ No newline at end of file
diff --git a/ft8CN/app/src/main/res/values-zh-rHK/strings.xml b/ft8CN/app/src/main/res/values-zh-rHK/strings.xml
index d4a9220..57abf83 100644
--- a/ft8CN/app/src/main/res/values-zh-rHK/strings.xml
+++ b/ft8CN/app/src/main/res/values-zh-rHK/strings.xml
@@ -151,7 +151,7 @@
呼叫 %s(%s)
- %.0f仟米
+ %.0f km
已確認
@@ -471,6 +471,17 @@
多次解碼
請在與本機相同的局域網下,用其它設備的網頁瀏覽器從後台導出。\n在瀏覽器的地址欄中輸入如下內容:\n無法獲取合適的IP地址,請連接到一個有效的Wifi。
decode_help.txt
+ 任務不存在!
+ 正在匯入...
+ 匯入結束!
+ 錯誤行數:%d
+ 新增QSL:%d
+ 更新QSL:%d
+ 無效的QSL:%d
+ 進程:
+ 有效的QSL:%d
+ 匯入被取消!
+ 終止匯入
\ No newline at end of file
diff --git a/ft8CN/app/src/main/res/values-zh-rMO/strings.xml b/ft8CN/app/src/main/res/values-zh-rMO/strings.xml
index 1d36dab..bbf972b 100644
--- a/ft8CN/app/src/main/res/values-zh-rMO/strings.xml
+++ b/ft8CN/app/src/main/res/values-zh-rMO/strings.xml
@@ -151,7 +151,7 @@
呼叫 %s(%s)
- %.0f仟米
+ %.0f km
已確認
@@ -471,6 +471,17 @@
多次解碼
請在與本機相同的局域網下,用其它設備的網頁瀏覽器從後台導出。\n在瀏覽器的地址欄中輸入如下內容:\n無法獲取合適的IP地址,請連接到一個有效的Wifi。
decode_help.txt
+ 任務不存在!
+ 正在匯入...
+ 匯入結束!
+ 錯誤行數:%d
+ 新增QSL:%d
+ 更新QSL:%d
+ 無效的QSL:%d
+ 進程:
+ 有效的QSL:%d
+ 匯入被取消!
+ 終止匯入
\ No newline at end of file
diff --git a/ft8CN/app/src/main/res/values-zh-rTW/strings.xml b/ft8CN/app/src/main/res/values-zh-rTW/strings.xml
index b052a6b..28bd6ae 100644
--- a/ft8CN/app/src/main/res/values-zh-rTW/strings.xml
+++ b/ft8CN/app/src/main/res/values-zh-rTW/strings.xml
@@ -151,7 +151,7 @@
呼叫 %s(%s)
- %.0f仟米
+ %.0f km
已確認
@@ -471,6 +471,17 @@
多次解碼
請在與本機相同的局域網下,用其它設備的網頁瀏覽器從後台導出。\n在瀏覽器的地址欄中輸入如下內容:\n無法獲取合適的IP地址,請連接到一個有效的Wifi。
decode_help.txt
+ 任務不存在!
+ 正在匯入...
+ 匯入結束!
+ 錯誤行數:%d
+ 新增QSL:%d
+ 更新QSL:%d
+ 無效的QSL:%d
+ 進程:
+ 有效的QSL:%d
+ 匯入被取消!
+ 終止匯入
\ No newline at end of file
diff --git a/ft8CN/app/src/main/res/values/strings.xml b/ft8CN/app/src/main/res/values/strings.xml
index 8671633..e6e7dc4 100644
--- a/ft8CN/app/src/main/res/values/strings.xml
+++ b/ft8CN/app/src/main/res/values/strings.xml
@@ -169,7 +169,7 @@
Manual confirmation (%s)
- Gird:%s
+ Grid:%s
Start time : %s
End time : %s
Rst received : %s
@@ -475,6 +475,17 @@
Fast decode
Deep decode
Export from WebUI via web browser on another device under the same LAN \n Please connect to a valid Wi-Fi
+ Task not exist!
+ Importing...
+ Finished !
+ Error lines: %d
+ Add QSLs: %d
+ Update QSLs: %d
+ Invalid QSLs: %d
+ "Progress: "
+ Validated QSL: %d
+ Canceled!
+ Stop
\ No newline at end of file
diff --git a/ft8CN/build.gradle b/ft8CN/build.gradle
index 5908a23..dc6bfbf 100644
--- a/ft8CN/build.gradle
+++ b/ft8CN/build.gradle
@@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
- id 'com.android.application' version '7.1.2' apply false
- id 'com.android.library' version '7.1.2' apply false
+ id 'com.android.application' version '7.4.1' apply false
+ id 'com.android.library' version '7.4.1' apply false
}
ext {
var3 = 'G:\\coding\\ft8CN\\ft8CN.jks'
diff --git a/ft8CN/gradle/wrapper/gradle-wrapper.properties b/ft8CN/gradle/wrapper/gradle-wrapper.properties
index e0c50f2..2ec761c 100644
--- a/ft8CN/gradle/wrapper/gradle-wrapper.properties
+++ b/ft8CN/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Sat Jul 08 16:47:34 CST 2023
+#Fri Nov 11 20:54:19 CST 2022
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
diff --git a/ft8cn/app/src/main/java/com/bg7yoz/ft8cn/database/AfterInsertQSLData.java b/ft8cn/app/src/main/java/com/bg7yoz/ft8cn/database/AfterInsertQSLData.java
new file mode 100644
index 0000000..869930f
--- /dev/null
+++ b/ft8cn/app/src/main/java/com/bg7yoz/ft8cn/database/AfterInsertQSLData.java
@@ -0,0 +1,5 @@
+package com.bg7yoz.ft8cn.database;
+
+public interface AfterInsertQSLData {
+ void doAfterInsert(boolean isInvalid,boolean isNewQSL);
+}
diff --git a/ft8cn/app/src/main/java/com/bg7yoz/ft8cn/html/ImportTaskList.java b/ft8cn/app/src/main/java/com/bg7yoz/ft8cn/html/ImportTaskList.java
new file mode 100644
index 0000000..409b5df
--- /dev/null
+++ b/ft8cn/app/src/main/java/com/bg7yoz/ft8cn/html/ImportTaskList.java
@@ -0,0 +1,134 @@
+package com.bg7yoz.ft8cn.html;
+
+import android.annotation.SuppressLint;
+
+import com.bg7yoz.ft8cn.GeneralVariables;
+import com.bg7yoz.ft8cn.R;
+
+import java.util.HashMap;
+
+public class ImportTaskList extends HashMap {
+
+ /**
+ * 获取上传的任务,以session为key
+ *
+ * @param session session
+ * @return 任务的HTML
+ */
+ public String getTaskHTML(int session) {
+ ImportTask task = this.get(session);
+ if (task == null) {
+ return GeneralVariables.getStringFromResource(R.string.null_task_html);
+ }
+ return task.getHtml();
+ }
+ public void cancelTask(int session){
+ ImportTask task = this.get(session);
+ if (task != null) {
+ task.setStatus(ImportState.CANCELED);
+ }
+ }
+
+ /**
+ * 检查任务是不是在运行。
+ *
+ * @param session 任务ID
+ * @return false 没有任务或任务结束
+ */
+ public boolean checkTaskIsRunning(int session) {
+ ImportTask task = this.get(session);
+ if (task == null) {
+ return false;
+ } else {
+ return task.status == ImportState.STARTING || task.status == ImportState.IMPORTING;
+ }
+ }
+
+ /**
+ * 添加任务到列表,要确保线程安全
+ *
+ * @param session session
+ * @param task 任务
+ */
+ public synchronized ImportTask addTask(int session, ImportTask task) {
+ this.put(session, task);
+ return task;
+ }
+
+ public ImportTask addTask(int session) {
+ return addTask(session, new ImportTask(session));
+ }
+
+
+ enum ImportState {
+ STARTING, IMPORTING, FINISHED, CANCELED
+ }
+
+ public static class ImportTask {
+
+
+ int session;//session,用于记录上传会话,是一个hash
+ public int count = 0;//解析出总的数据量
+ public int importedCount = 0;//导入的数量
+ public int readErrorCount = 0;//读取数据错误数量
+ public int processCount = 0;
+ public int updateCount = 0;//更新的数量
+ public int invalidCount = 0;//无效的QSL
+ public int newCount = 0;//新导入的数量
+ public ImportState status = ImportState.STARTING;//状态:0:开始,1:运行,2:结束,3:取消
+ String message = "";//任务消息描述
+ String errorMsg = "";
+
+ @SuppressLint("DefaultLocale")
+ public String getHtml() {
+ String htmlHeader = "\n";
+ String htmlEnder = "
\n";
+ String progress = String.format("%s %.1f%%(%d/%d)\n", GeneralVariables.getStringFromResource(R.string.import_progress_html)
+ , count == 0 ? 0 : processCount * 100f / count, processCount, count);
+ String cell = "%s |
\n";
+ String errorHtml = status == ImportState.FINISHED || status == ImportState.CANCELED ? errorMsg : "";
+ String doCancelButton = status == ImportState.FINISHED || status == ImportState.CANCELED ? ""
+ : String.format("
"
+ , session,GeneralVariables.getStringFromResource(R.string.import_cancel_button));
+ return htmlHeader
+ + String.format(cell, progress)
+ + String.format(cell, String.format(GeneralVariables.getStringFromResource(R.string.import_read_error_count_html), readErrorCount))
+ + String.format(cell, String.format(GeneralVariables.getStringFromResource(R.string.import_new_count_html), newCount))
+ + String.format(cell, String.format(GeneralVariables.getStringFromResource(R.string.import_update_count_html), updateCount))
+ + String.format(cell, String.format(GeneralVariables.getStringFromResource(R.string.import_invalid_count_html), invalidCount))
+ + String.format(cell, String.format(GeneralVariables.getStringFromResource(R.string.import_readed_html), importedCount))
+ + String.format(cell, message)
+ + String.format(cell, errorHtml)
+ + htmlEnder
+ + doCancelButton;
+ }
+
+ public ImportTask(int session) {
+ this.session = session;
+ }
+
+ public void setStatus(ImportState status) {
+ this.status = status;
+ setStateMSG(status);
+ }
+
+ private void setStateMSG(ImportState state) {
+ switch (state) {
+ case IMPORTING:
+ this.message = String.format("%s"
+ ,GeneralVariables.getStringFromResource(R.string.log_importing_html));
+ break;
+ case FINISHED:
+ this.message = String.format("%s"
+ ,GeneralVariables.getStringFromResource(R.string.log_import_finished_html));
+ break;
+ case CANCELED:
+ this.message = String.format("%s"
+ ,GeneralVariables.getStringFromResource(R.string.import_canceled_html));
+ break;
+ }
+ }
+
+
+ }
+}
diff --git a/ft8cn/app/src/main/java/com/bg7yoz/ft8cn/rigs/Wolf_sdr_450Rig.java b/ft8cn/app/src/main/java/com/bg7yoz/ft8cn/rigs/Wolf_sdr_450Rig.java
new file mode 100644
index 0000000..924967a
--- /dev/null
+++ b/ft8cn/app/src/main/java/com/bg7yoz/ft8cn/rigs/Wolf_sdr_450Rig.java
@@ -0,0 +1,201 @@
+package com.bg7yoz.ft8cn.rigs;
+
+import static com.bg7yoz.ft8cn.GeneralVariables.QUERY_FREQ_TIMEOUT;
+import static com.bg7yoz.ft8cn.GeneralVariables.START_QUERY_FREQ_DELAY;
+
+import android.util.Log;
+
+import com.bg7yoz.ft8cn.GeneralVariables;
+import com.bg7yoz.ft8cn.R;
+import com.bg7yoz.ft8cn.database.ControlMode;
+import com.bg7yoz.ft8cn.ui.ToastMessage;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * wolf 的cat指令集兼容yaesu 450d,但是有的ham在实际测试中发现,450d默认是dig-u,此模式在wolf上测试无法满功率发射,
+ * 而采用usb模式就可以满功率发射,故增加一个usb模式的选项
+ * 在创建rig时,用布尔参数是否时USB模式
+ */
+public class Wolf_sdr_450Rig extends BaseRig {
+ private static final String TAG = "Wolf_sdr_450Rig";
+ private final StringBuilder buffer = new StringBuilder();
+ private int swr = 0;
+ private int alc = 0;
+ private boolean alcMaxAlert = false;
+ private boolean swrAlert = false;
+
+ private Timer readFreqTimer = new Timer();
+ private boolean isUsbMode=true;
+
+ private TimerTask readTask() {
+ return new TimerTask() {
+ @Override
+ public void run() {
+ try {
+ if (!isConnected()) {
+ readFreqTimer.cancel();
+ readFreqTimer.purge();
+ readFreqTimer = null;
+ return;
+ }
+ if (isPttOn()) {
+ readMeters();
+ } else {
+ readFreqFromRig();
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "readFreq error:" + e.getMessage());
+ }
+ }
+ };
+ }
+ /**
+ * 读取Meter RM;
+ */
+ private void readMeters() {
+ if (getConnector() != null) {
+ clearBufferData();//清空一下缓存
+ getConnector().sendData(Yaesu3RigConstant.setRead39Meters_ALC());
+ getConnector().sendData(Yaesu3RigConstant.setRead39Meters_SWR());
+ }
+ }
+
+ private void showAlert() {
+ if (swr >= Yaesu3RigConstant.swr_39_alert_max) {
+ if (!swrAlert) {
+ swrAlert = true;
+ ToastMessage.show(GeneralVariables.getStringFromResource(R.string.swr_high_alert));
+ }
+ } else {
+ swrAlert = false;
+ }
+ if (alc > Yaesu3RigConstant.alc_39_alert_max) {//网络模式下不警告ALC
+ if (!alcMaxAlert) {
+ alcMaxAlert = true;
+ ToastMessage.show(GeneralVariables.getStringFromResource(R.string.alc_high_alert));
+ }
+ } else {
+ alcMaxAlert = false;
+ }
+
+ }
+
+ /**
+ * 清空缓存数据
+ */
+ private void clearBufferData() {
+ buffer.setLength(0);
+ }
+
+ @Override
+ public void setPTT(boolean on) {
+ super.setPTT(on);
+ if (getConnector() != null) {
+ switch (getControlMode()) {
+ case ControlMode.CAT://以CIV指令
+ getConnector().setPttOn(Yaesu3RigConstant.setPTT_TX_On(on));//针对YAESU 450指令
+ break;
+ case ControlMode.RTS:
+ case ControlMode.DTR:
+ getConnector().setPttOn(on);
+ break;
+ }
+ }
+ }
+
+ @Override
+ public boolean isConnected() {
+ if (getConnector() == null) {
+ return false;
+ }
+ return getConnector().isConnected();
+ }
+
+ @Override
+ public void setUsbModeToRig() {
+ if (getConnector() != null) {
+ //getConnector().sendData(Yaesu3RigConstant.setOperationDATA_U_Mode());
+ //getConnector().sendData(Yaesu3RigConstant.setOperationUSB_Data_Mode());
+ if (isUsbMode) {//usb模式
+ getConnector().sendData(Yaesu3RigConstant.setOperationUSBMode());
+ }else {//dig-u模式
+ getConnector().sendData(Yaesu3RigConstant.setOperationDATA_U_Mode());
+ }
+ }
+ }
+
+ @Override
+ public void setFreqToRig() {
+ if (getConnector() != null) {
+ getConnector().sendData(Yaesu3RigConstant.setOperationFreq8Byte(getFreq()));
+ }
+ }
+
+ @Override
+ public void onReceiveData(byte[] data) {
+ String s = new String(data);
+ //ToastMessage.showDebug("39 YAESU 读数据:"+new String(Yaesu3RigConstant.setReadOperationFreq()));
+
+ if (!s.contains(";")) {
+ buffer.append(s);
+ if (buffer.length()>1000) clearBufferData();
+ //return;//说明数据还没接收完。
+ } else {
+ if (s.indexOf(";") > 0) {//说明接到结束的数据了,并且不是第一个字符是;
+ buffer.append(s.substring(0, s.indexOf(";")));
+ }
+
+ //开始分析数据
+ Yaesu3Command yaesu3Command = Yaesu3Command.getCommand(buffer.toString());
+ clearBufferData();//清一下缓存
+ //要把剩下的数据放到缓存里
+ buffer.append(s.substring(s.indexOf(";") + 1));
+
+ if (yaesu3Command == null) {
+ return;
+ }
+ //long tempFreq = Yaesu3Command.getFrequency(yaesu3Command);
+ //if (tempFreq != 0) {//如果tempFreq==0,说明频率不正常
+ // setFreq(Yaesu3Command.getFrequency(yaesu3Command));
+ //}
+
+ if (yaesu3Command.getCommandID().equalsIgnoreCase("FA")
+ || yaesu3Command.getCommandID().equalsIgnoreCase("FB")) {
+ long tempFreq = Yaesu3Command.getFrequency(yaesu3Command);
+ if (tempFreq != 0) {//如果tempFreq==0,说明频率不正常
+ setFreq(Yaesu3Command.getFrequency(yaesu3Command));
+ }
+ } else if (yaesu3Command.getCommandID().equalsIgnoreCase("RM")) {//METER
+ if (Yaesu3Command.isSWRMeter38(yaesu3Command)) {
+ swr = Yaesu3Command.getALCOrSWR38(yaesu3Command);
+ }
+ if (Yaesu3Command.isALCMeter38(yaesu3Command)) {
+ alc = Yaesu3Command.getALCOrSWR38(yaesu3Command);
+ }
+ showAlert();
+ }
+
+ }
+
+ }
+
+ @Override
+ public void readFreqFromRig() {
+ if (getConnector() != null) {
+ clearBufferData();//清空一下缓存
+ getConnector().sendData(Yaesu3RigConstant.setReadOperationFreq());
+ }
+ }
+
+ @Override
+ public String getName() {
+ return "WOLF SDR";
+ }
+
+ public Wolf_sdr_450Rig(boolean usbMode) {
+ isUsbMode=usbMode;
+ readFreqTimer.schedule(readTask(), START_QUERY_FREQ_DELAY,QUERY_FREQ_TIMEOUT);
+ }
+}