- Added QSO log importing WebUI
- Fixed map crash when importing large amount of logs (10k+)
- Optimized database structure to improve log importing speed and stability
- Fixed typo in translation
- Added support for UA3REO Wolf SDR
- Added support for GUOHE PMR-171
pull/64/head
wangg 2023-08-15 00:42:44 -04:00
rodzic 9ad039fae3
commit db5c112690
45 zmienionych plików z 778 dodań i 150 usunięć

Plik binarny nie jest wyświetlany.

0
ft8CN/.gitignore vendored 100755 → 100644
Wyświetl plik

0
ft8CN/.idea/.gitignore vendored 100755 → 100644
Wyświetl plik

0
ft8CN/.idea/.name 100755 → 100644
Wyświetl plik

0
ft8CN/.idea/compiler.xml 100755 → 100644
Wyświetl plik

0
ft8CN/.idea/gradle.xml 100755 → 100644
Wyświetl plik

Wyświetl plik

@ -6,5 +6,8 @@
<option name="m_reportAllNonLibraryCalls" value="false" />
<option name="callCheckString" value="java.io.File,.*,java.io.InputStream,read|skip|available|markSupported,java.io.Reader,read|skip|ready|markSupported,java.lang.AbstractStringBuilder,capacity|codePointAt|codePointBefore|codePointCount|indexOf|lastIndexOf|offsetByCodePoints|substring|subSequence,java.lang.Boolean,.*,java.lang.Byte,.*,java.lang.Character,.*,java.lang.Double,.*,java.lang.Float,.*,java.lang.Integer,.*,java.lang.Long,.*,java.lang.Math,.*,java.lang.Object,equals|hashCode|toString,java.lang.Short,.*,java.lang.StrictMath,.*,java.lang.String,.*,java.lang.Thread,interrupted,java.math.BigDecimal,.*,java.math.BigInteger,.*,java.net.InetAddress,.*,java.net.URI,.*,java.nio.channels.AsynchronousChannelGroup,.*,java.util.Arrays,.*,java.util.Collections,(?!addAll).*,java.util.List,of,java.util.Map,of|ofEntries|entry,java.util.Set,of,java.util.UUID,.*,java.util.concurrent.CountDownLatch,await|getCount,java.util.concurrent.ExecutorService,awaitTermination|isShutdown|isTerminated,java.util.concurrent.ForkJoinPool,awaitQuiescence,java.util.concurrent.Semaphore,tryAcquire|availablePermits|isFair|hasQueuedThreads|getQueueLength|getQueuedThreads,java.util.concurrent.locks.Condition,await|awaitNanos|awaitUntil,java.util.concurrent.locks.Lock,tryLock|newCondition,java.util.regex.Matcher,pattern|toMatchResult|start|end|group|groupCount|matches|find|lookingAt|quoteReplacement|replaceAll|replaceFirst|regionStart|regionEnd|hasTransparentBounds|hasAnchoringBounds|hitEnd|requireEnd,java.util.regex.Pattern,.*,java.util.stream.BaseStream,.*,java.util.stream.DoubleStream,.*,java.util.stream.IntStream,.*,java.util.stream.LongStream,.*,java.util.stream.Stream,.*" />
</inspection_tool>
<inspection_tool class="JavadocDeclaration" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ADDITIONAL_TAGS" value="date" />
</inspection_tool>
</profile>
</component>

7
ft8CN/.idea/misc.xml 100755 → 100644
Wyświetl plik

@ -172,4 +172,11 @@
<component name="ProjectType">
<option name="id" value="Android" />
</component>
<component name="VisualizationToolProject">
<option name="state">
<ProjectState>
<option name="scale" value="0.027632950990615225" />
</ProjectState>
</option>
</component>
</project>

Wyświetl plik

Wyświetl plik

@ -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'//HashTablekeyHashMap
//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')

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -28,6 +28,7 @@
android:theme="@style/Theme.Ft8CN">
<activity
android:name=".grid_tracker.GridTrackerMainActivity"
android:screenOrientation="sensorLandscape"
android:exported="false">
<meta-data
android:name="android.app.lib_name"

Wyświetl plik

@ -14,6 +14,13 @@ Please click "FAQ" if you have good suggestions or questions .
BG7YOZ
2022-07-01
2023-08-07(0.90)
1.增加日志导入时Web界面交互模式。
2.修正当日志数据量过大时,地图崩溃的问题。
3.优化数据库结构,提升日志数据导入、更新速度(更新此版本前,建议备份日志以防不测)。
4.修正部分单词拼写错误。
5.增加电台UA3REO Wolf SDR。
6.增加电台GUOHE(国赫) PMR-171。
2023-07-08(0.89)
1.增加多重解码功能,在多重解码模式下,提高解码深度,尝试解码叠加的信号。
@ -265,7 +272,8 @@ BG7YOZ
BG7YXN提供某型号电台用于测试。
BG7YRB对呼号规则运算提供帮助。
BG8KAH提供设备用于测试。
BA7LVG、JE6WUD完成日文的翻译校对工作。
BA7LVG完成日文的翻译校对工作。
JE6WUD完成日文的翻译校对工作。
BG6RI帮助解决日志的信号报告问题。
SV1EEX完成希腊文、西班牙文UI的翻译工作。
VR2VRC帮助修正历史呼号读取规则。
@ -284,4 +292,5 @@ BG7YOZ
BH4FTI发现并协助对一些BUG进行调试。
BG8BXMM哥为FT8CN的使用做推广抖音和B站上有很多他的教学视频。
BG7MFQ为FT8CN的使用做推广帮助测试。
BG2EFX提供大数据量的日志用于测试。

Wyświetl plik

@ -26,6 +26,7 @@ XIEGU(协谷) X6100/G90S(USB),70,19200,9
XIEGU(协谷) X5105,70,19200,9
XIEGU(协谷) X108,70,19200,9
GUOHE(国赫) Q900,00,19200,8
GUOHE(国赫) PMR-171,00,19200,8
YAESU FT-450(D),00,4800,4
YAESU FT-817,00,4800,1
YAESU FT-818,00,4800,1
@ -47,4 +48,6 @@ Elecraft K3S\K3\KX3\KX2,00,38400,10
mcHF-QRP sdr,00,4800,1
FlexRadio 6000 series,00,00,12
FX-4CR,00,115200,7
Qrp Labs QDX,00,9600,7
Qrp Labs QDX,00,9600,7
UA3REO Wolf SDR(DIGU),00,4800,15
UA3REO Wolf SDR(USB),00,4800,16

Wyświetl plik

@ -71,6 +71,7 @@ import com.bg7yoz.ft8cn.rigs.KenwoodKT90Rig;
import com.bg7yoz.ft8cn.rigs.KenwoodTS2000Rig;
import com.bg7yoz.ft8cn.rigs.KenwoodTS590Rig;
import com.bg7yoz.ft8cn.rigs.OnRigStateChanged;
import com.bg7yoz.ft8cn.rigs.Wolf_sdr_450Rig;
import com.bg7yoz.ft8cn.rigs.XieGu6100Rig;
import com.bg7yoz.ft8cn.rigs.XieGuRig;
import com.bg7yoz.ft8cn.rigs.Yaesu2Rig;
@ -767,6 +768,12 @@ public class MainViewModel extends ViewModel {
case InstructionSet.KENWOOD_TS2000:
baseRig = new KenwoodTS2000Rig();//建伍TS2000
break;
case InstructionSet.WOLF_SDR_DIGU:
baseRig = new Wolf_sdr_450Rig(false);
break;
case InstructionSet.WOLF_SDR_USB:
baseRig = new Wolf_sdr_450Rig(true);
break;
}
mutableIsFlexRadio.postValue(GeneralVariables.instructionSet == InstructionSet.FLEX_NETWORK);

Wyświetl plik

@ -164,6 +164,9 @@ public class FlexConnector extends BaseRigConnector {
flexRadio.commandSetDaxAudio(1, 0, true);//打开DAX
//todo 防止流的端口没有释放,把端口变换一下?
//FlexRadio.streamPort++;
flexRadio.commandUdpPort();//设置UDP端口
@ -176,7 +179,7 @@ public class FlexConnector extends BaseRigConnector {
flexRadio.commandMeterList();//列一下仪表
//flexRadio.commandSubMeterAll();//显示全部仪表消息
//flexRadio.commandSubMeterAll();//此处订阅指令放到了接收响应部分
setMaxRfPower(maxRfPower);//设置发射功率
setMaxTunePower(maxTunePower);//设置调谐功率
@ -258,9 +261,8 @@ public class FlexConnector extends BaseRigConnector {
@Override
public void sendWaveData(float[] data) {
Log.e(TAG, "sendWaveData: flexConnector:"+data.length );
//Log.e(TAG, "sendWaveData: flexConnector:"+data.length );
flexRadio.sendWaveData(data);
//super.sendWaveData(data);
}
@Override

Wyświetl plik

@ -51,7 +51,7 @@ public class DatabaseOpr extends SQLiteOpenHelper {
public static DatabaseOpr getInstance(@Nullable Context context, @Nullable String databaseName) {
if (instance == null) {
instance = new DatabaseOpr(context, databaseName, null, 13);
instance = new DatabaseOpr(context, databaseName, null, 14);
}
return instance;
}
@ -93,6 +93,8 @@ public class DatabaseOpr extends SQLiteOpenHelper {
//创建SWL相关的表
createSWLTables(sqLiteDatabase);
//创建索引
createIndex(sqLiteDatabase);
}
@ -116,6 +118,9 @@ public class DatabaseOpr extends SQLiteOpenHelper {
//创建SWL相关的表
createSWLTables(sqLiteDatabase);
//创建索引
createIndex(sqLiteDatabase);
//删除DXCC呼号列表中的等号
//deleteDxccPrefixEqual(sqLiteDatabase);
}
@ -173,6 +178,21 @@ public class DatabaseOpr extends SQLiteOpenHelper {
return false;
}
/**
*
* @param db
* @param indexName
* @return
*/
private boolean checkIndexExists(SQLiteDatabase db, String indexName) {
Cursor cursor = db.rawQuery("select * from sqlite_master where type = 'index' and name = ?"
, new String[]{indexName});
if (cursor.moveToNext()) {
cursor.close();
return true;
}
return false;
}
private void deleteDxccPrefixEqual(SQLiteDatabase db) {
db.execSQL("DELETE from dxcc_prefix where prefix LIKE \"=%\"");
}
@ -385,6 +405,20 @@ public class DatabaseOpr extends SQLiteOpenHelper {
}
/**
*
* @param sqLiteDatabase
*/
private void createIndex(SQLiteDatabase sqLiteDatabase) {
if (!checkIndexExists(sqLiteDatabase, "QslCallsigns_callsign_IDX")) {
sqLiteDatabase.execSQL("CREATE INDEX QslCallsigns_callsign_IDX ON QslCallsigns (callsign,startTime,finishTime,mode)");
}
if (!checkIndexExists(sqLiteDatabase, "QSLTable_call_IDX")) {
sqLiteDatabase.execSQL("CREATE INDEX QSLTable_call_IDX ON QSLTable (\"call\",qso_date,time_on,mode)");
}
}
public void loadItuDataFromFile(SQLiteDatabase db) {
AssetManager assetManager = context.getAssets();
InputStream inputStream;
@ -818,8 +852,11 @@ public class DatabaseOpr extends SQLiteOpenHelper {
}
@SuppressLint("Range")
public boolean doInsertQSLData(QSLRecord record) {
public boolean doInsertQSLData(QSLRecord record,AfterInsertQSLData afterInsertQSLData) {
if (record.getToCallsign() == null) {
if (afterInsertQSLData!=null){
afterInsertQSLData.doAfterInsert(true,true);//说明是无效的QSL
}
return false;
}
@ -892,6 +929,10 @@ public class DatabaseOpr extends SQLiteOpenHelper {
, record.getMyCallsign()
, record.getMyMaidenGrid()
, record.getComment()});
if (afterInsertQSLData!=null){
afterInsertQSLData.doAfterInsert(false,true);//说明是新的QSL
}
} else {
if (record.isQSL) {
db.execSQL("UPDATE QSLTable SET isQSL=? " +
@ -949,6 +990,10 @@ public class DatabaseOpr extends SQLiteOpenHelper {
, record.getTime_on()
, record.getMode()});
}
if (afterInsertQSLData!=null){
afterInsertQSLData.doAfterInsert(false,false);//说明已经存在需要更新的QSL
}
}
return true;
}
@ -1198,7 +1243,7 @@ public class DatabaseOpr extends SQLiteOpenHelper {
@SuppressLint("Range")
@Override
protected Void doInBackground(Void... voids) {
databaseOpr.doInsertQSLData(qslRecord);//添加日志和通联成功的呼号
databaseOpr.doInsertQSLData(qslRecord,null);//添加日志和通联成功的呼号
return null;
}
}
@ -1273,7 +1318,7 @@ public class DatabaseOpr extends SQLiteOpenHelper {
while (cursor.moveToNext()) {
long s = cursor.getLong(cursor.getColumnIndex("BAND")); //获取频段
int total = cursor.getInt(cursor.getColumnIndex("c")); //获取数量
callsigns.add(String.format("%.3fMhz \t %d", s / 1000000f, total));
callsigns.add(String.format("%.3fMHz \t %d", s / 1000000f, total));
sum = sum + total;
}
callsigns.add(String.format("-----------Total %d -----------", sum));
@ -1387,7 +1432,6 @@ public class DatabaseOpr extends SQLiteOpenHelper {
static class GetQsoGrids extends AsyncTask<Void, Void, Void> {
SQLiteDatabase db;
HashMap<String, Boolean> 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" +

Wyświetl plik

@ -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);

Wyświetl plik

@ -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){
long now = System.currentTimeMillis() - 1;//获取当前时间
float[] voice=new float[64];
for (int j = 0; j <3 ; j++) {
for (int i = 0; i < 64; i++) {
float[] voice=new float[256];//因为是立体声240*2
//for (int j = 0; j <3 ; j++) {
for (int i = 0; i < voice.length; i++) {
voice[i] = temp[count];
count++;
if (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"));
}

Wyświetl plik

@ -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){

Wyświetl plik

@ -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<Ft8Message> allMsg = new ArrayList<>();
// ArrayList<Ft8Message> msgs = runDecode(utc, voiceData,false);
ArrayList<Ft8Message> 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;
}
/**
*

Wyświetl plik

@ -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<GeoPoint> 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();

Wyświetl plik

@ -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<Boolean>() {
@ -329,7 +308,6 @@ public class GridTrackerMainActivity extends AppCompatActivity {
//获取曾经通联过的网格
mainViewModel.databaseOpr.getQsoGridQuery(new DatabaseOpr.OnGetQsoGrids() {
//ConcurrentHashMap
@Override
public void onAfterQuery(HashMap<String, Boolean> grids) {
for (Map.Entry<String, Boolean> entry : grids.entrySet()) {
@ -377,6 +355,7 @@ public class GridTrackerMainActivity extends AppCompatActivity {
setContentView(binding.getRoot());
}
/**
* CQ
*

Wyświetl plik

@ -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<String, String> 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<HashMap<String, String>> recordList = logFileImport.getLogRecords();
int importCount = 0;
int recordCount = 0;
for (HashMap<String, String> 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) + "<br>"
, recordCount, importCount, logFileImport.getErrorCount()));
if (logFileImport.getErrorCount() > 0) {
temp.append("<table>");
temp.append(String.format("<tr><th></th><th>%d malformed logs</th></tr>\n", logFileImport.getErrorCount()));
for (int key : logFileImport.getErrorLines().keySet()) {
temp.append(String.format("<tr><td><pre>%d</pre></td><td><pre >%s</pre></td></tr>\n"
, key, logFileImport.getErrorLines().get(key)));
}
//重定向,跳转到实时导入信息界面
return String.format("<head>\n<meta http-equiv=\"Refresh\" content=\"0; URL=getImportTask?session=%d\" /></head><body></body>"
, param.hashCode());
temp.append("</table>");
}
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<script language=\"JavaScript\">\n" +
"function refreshTask(){\n" +
"window.location.reload();\n" +
"}\n" +
"setTimeout('refreshTask()',1000);\n" +
" </script>\n";
Map<String, String> 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<String, String> 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("<head>\n<meta http-equiv=\"Refresh\" content=\"0; URL=getImportTask?session=%d\" /></head><body></body>"
, id);
}
return "";
}
@SuppressLint("DefaultLocale")
private void doImportADI(ImportTaskList.ImportTask task, LogFileImport logFileImport) {
task.setStatus(ImportTaskList.ImportState.IMPORTING);
ArrayList<HashMap<String, String>> recordList = logFileImport.getLogRecords();//以正则表达式:[<][Ee][Oo][Rr][>]分行
task.importedCount = 0;
task.count = recordList.size();//总行数
for (HashMap<String, String> 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("<table>");
temp.append(String.format("<tr><th></th><th>%d malformed logs</th></tr>\n", logFileImport.getErrorCount()));
for (int key : logFileImport.getErrorLines().keySet()) {
temp.append(String.format("<tr><td><pre>%d</pre></td><td><pre >%s</pre></td></tr>\n"
, key, logFileImport.getErrorLines().get(key)));
}
temp.append("</table>");
}
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 {
/**
* swoQSO
*
* @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
*/

Wyświetl plik

@ -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<Integer,String> 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("<","&lt;"));//把错误的内容保存下来。
importTask.readErrorCount=errorLines.size();
}
}
return records;

Wyświetl plik

@ -48,6 +48,9 @@ public class QSLRecord {
public boolean saved = false;//是否被保存到数据库中
public boolean isInvalid=false;//是否解析出错
public String errorMSG="";//如果解析出错,错误的消息
/**
* SWL QSOSWL 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());
}

Wyświetl plik

@ -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

Wyświetl plik

@ -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音频的采样率是24000todo 此处可改为动态设置2400048000
if (data == null) {
setPTT(false);
return;
}
getConnector().sendWaveData(data);
}
}
@Override

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -472,7 +472,18 @@
<string name="deep_mode">Λειτουργία πολλαπλής αποκωδικοποίησης</string>
<string name="export_null">Παρακαλώ χρησιμοποιήστε τον web browser των άλλων συσκευών για εξαγωγή στο παρασκήνιο στο ίδιο LAN που είναι αυτή η συσκευή.\nPlease connect to a valid Wi-Fi</string>
<string name="deep_mode_help">decode_help_en.txt</string>
<string name="null_task_html">Το έργο δεν υπάρχει!</string>
<string name="log_importing_html">Εισαγωγή...</string>
<string name="log_import_finished_html">Τελειώσει!</string>
<string name="import_read_error_count_html">Error lines: %d</string>
<string name="import_new_count_html">Append QSLs: %d</string>
<string name="import_update_count_html">Update QSLs: %d</string>
<string name="import_invalid_count_html">Invalid QSLs: %d</string>
<string name="import_progress_html">"Progress: "</string>
<string name="import_readed_html">Validated QSL: %d</string>
<string name="import_canceled_html">Canceled!</string>
<string name="import_cancel_button">Stop</string>
<!-- <string name="hello_blank_fragment">Hello blank fragment</string>-->
<!-- <string name="hello_blank_fragment">Hello blank fragment</string>-->
</resources>

Wyświetl plik

@ -166,7 +166,7 @@
<string name="log_manual_confirmation">Confirmación manual (%s)</string>
<!-- QSL日志列表-->
<string name="qsl_grid">Gird:%s</string>
<string name="qsl_grid">Grid:%s</string>
<string name="qsl_start_time">Hora de inicio : %s</string>
<string name="qsl_end_time">Hora de finalización: %s</string>
<string name="qsl_rst_rcvd">Rst recibido : %s</string>
@ -472,6 +472,17 @@
<string name="deep_mode">Modo de decodificación múltiple</string>
<string name="export_null">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</string>
<string name="deep_mode_help">decode_help_en.txt</string>
<!-- <string name="hello_blank_fragment">Hello blank fragment</string>-->
<string name="null_task_html">¡La tarea no existe!</string>
<string name="log_importing_html">Importador...</string>
<string name="log_import_finished_html">¡Terminado!</string>
<string name="import_read_error_count_html">Error lines: %d</string>
<string name="import_new_count_html">Append QSLs: %d</string>
<string name="import_update_count_html">Update QSLs: %d</string>
<string name="import_invalid_count_html">Invalid QSLs: %d</string>
<string name="import_progress_html">"Progress: "</string>
<string name="import_readed_html">Validated QSL: %d</string>
<string name="import_canceled_html">Canceled!</string>
<string name="import_cancel_button">Stop</string>
<!-- <string name="hello_blank_fragment">Hello blank fragment</string>-->
</resources>

Wyświetl plik

@ -755,6 +755,17 @@
<string name="deep_mode">ディープ</string>
<string name="export_null">このデバイスと同じLANに接続し、ほかのデバイスでウェブブラウザーを利用してエクスポートしてください。\nブラウザーのアドレスバーに次の内容を入力してください\n有効なWiFiに接続してください。</string>
<string name="deep_mode_help">decode_help_en.txt</string>
<string name="null_task_html">タスクが存在しません!</string>
<string name="log_importing_html">インポート中…</string>
<string name="log_import_finished_html">インポート済み!</string>
<string name="import_read_error_count_html">エラー行: %d</string>
<string name="import_new_count_html">新QSL数: %d</string>
<string name="import_update_count_html">変更されたQSL数: %d</string>
<string name="import_invalid_count_html">無効QSL数: %d</string>
<string name="import_progress_html">進捗:</string>
<string name="import_readed_html">確認済みQSL: %d</string>
<string name="import_canceled_html">インポートが取り消されました!</string>
<string name="import_cancel_button">インポートを取り消す</string>
</resources>

Wyświetl plik

@ -211,6 +211,7 @@
<string name="html_qsl_start_time">通联开始时间</string>
<string name="html_qsl_start_day">通联开始日期</string>
<string name="html_qsl_end_time">通联结束时间</string>
<string name="html_qsl_end_date">通联结束日期</string>
<string name="html_qsl_mode">模式</string>
<string name="html_qsl_grid">网格</string>
<string name="html_qsl_band">频段</string>
@ -470,6 +471,17 @@
<string name="deep_mode">多次解码</string>
<string name="export_null">请在与本机相同的局域网下,用其它设备的网页浏览器从后台导出。\n在浏览器的地址栏中输入如下内容\n无法获取合适的IP地址请连接到一个有效的Wifi。</string>
<string name="deep_mode_help">decode_help.txt</string>
<string name="null_task_html">任务不存在!</string>
<string name="log_importing_html">正在导入...</string>
<string name="log_import_finished_html">导入结束!</string>
<string name="import_read_error_count_html">错误行数:%d</string>
<string name="import_new_count_html">新增QSL%d</string>
<string name="import_update_count_html">更新QSL%d</string>
<string name="import_invalid_count_html">无效的QSL%d</string>
<string name="import_progress_html">进度:</string>
<string name="import_readed_html">有效的QSL%d</string>
<string name="import_canceled_html">导入被取消!</string>
<string name="import_cancel_button">停止导入</string>
</resources>

Wyświetl plik

@ -151,7 +151,7 @@
<string name="calling">呼叫 %s(%s)</string>
<!-- 網格距離計算-->
<string name="distance">%.0f仟米</string>
<string name="distance">%.0f km</string>
<!-- 日誌列表-->
<string name="confirmed">已確認</string>
@ -471,6 +471,17 @@
<string name="deep_mode">多次解碼</string>
<string name="export_null">請在與本機相同的局域網下,用其它設備的網頁瀏覽器從後台導出。\n在瀏覽器的地址欄中輸入如下內容\n無法獲取合適的IP地址請連接到一個有效的Wifi。</string>
<string name="deep_mode_help">decode_help.txt</string>
<string name="null_task_html">任務不存在!</string>
<string name="log_importing_html">正在匯入...</string>
<string name="log_import_finished_html">匯入結束!</string>
<string name="import_read_error_count_html">錯誤行數:%d</string>
<string name="import_new_count_html">新增QSL%d</string>
<string name="import_update_count_html">更新QSL%d</string>
<string name="import_invalid_count_html">無效的QSL%d</string>
<string name="import_progress_html">進程:</string>
<string name="import_readed_html">有效的QSL%d</string>
<string name="import_canceled_html">匯入被取消!</string>
<string name="import_cancel_button">終止匯入</string>
</resources>

Wyświetl plik

@ -151,7 +151,7 @@
<string name="calling">呼叫 %s(%s)</string>
<!-- 網格距離計算-->
<string name="distance">%.0f仟米</string>
<string name="distance">%.0f km</string>
<!-- 日誌列表-->
<string name="confirmed">已確認</string>
@ -471,6 +471,17 @@
<string name="deep_mode">多次解碼</string>
<string name="export_null">請在與本機相同的局域網下,用其它設備的網頁瀏覽器從後台導出。\n在瀏覽器的地址欄中輸入如下內容\n無法獲取合適的IP地址請連接到一個有效的Wifi。</string>
<string name="deep_mode_help">decode_help.txt</string>
<string name="null_task_html">任務不存在!</string>
<string name="log_importing_html">正在匯入...</string>
<string name="log_import_finished_html">匯入結束!</string>
<string name="import_read_error_count_html">錯誤行數:%d</string>
<string name="import_new_count_html">新增QSL%d</string>
<string name="import_update_count_html">更新QSL%d</string>
<string name="import_invalid_count_html">無效的QSL%d</string>
<string name="import_progress_html">進程:</string>
<string name="import_readed_html">有效的QSL%d</string>
<string name="import_canceled_html">匯入被取消!</string>
<string name="import_cancel_button">終止匯入</string>
</resources>

Wyświetl plik

@ -151,7 +151,7 @@
<string name="calling">呼叫 %s(%s)</string>
<!-- 網格距離計算-->
<string name="distance">%.0f仟米</string>
<string name="distance">%.0f km</string>
<!-- 日誌列表-->
<string name="confirmed">已確認</string>
@ -471,6 +471,17 @@
<string name="deep_mode">多次解碼</string>
<string name="export_null">請在與本機相同的局域網下,用其它設備的網頁瀏覽器從後台導出。\n在瀏覽器的地址欄中輸入如下內容\n無法獲取合適的IP地址請連接到一個有效的Wifi。</string>
<string name="deep_mode_help">decode_help.txt</string>
<string name="null_task_html">任務不存在!</string>
<string name="log_importing_html">正在匯入...</string>
<string name="log_import_finished_html">匯入結束!</string>
<string name="import_read_error_count_html">錯誤行數:%d</string>
<string name="import_new_count_html">新增QSL%d</string>
<string name="import_update_count_html">更新QSL%d</string>
<string name="import_invalid_count_html">無效的QSL%d</string>
<string name="import_progress_html">進程:</string>
<string name="import_readed_html">有效的QSL%d</string>
<string name="import_canceled_html">匯入被取消!</string>
<string name="import_cancel_button">終止匯入</string>
</resources>

Wyświetl plik

@ -169,7 +169,7 @@
<string name="log_manual_confirmation">Manual confirmation (%s)</string>
<!-- QSL日志列表-->
<string name="qsl_grid">Gird:%s</string>
<string name="qsl_grid">Grid:%s</string>
<string name="qsl_start_time">Start time : %s</string>
<string name="qsl_end_time">End time : %s</string>
<string name="qsl_rst_rcvd">Rst received : %s</string>
@ -475,6 +475,17 @@
<string name="fast_mode">Fast decode</string>
<string name="deep_mode">Deep decode</string>
<string name="export_null">Export from WebUI via web browser on another device under the same LAN \n Please connect to a valid Wi-Fi</string>
<string name="null_task_html">Task not exist!</string>
<string name="log_importing_html">Importing...</string>
<string name="log_import_finished_html">Finished !</string>
<string name="import_read_error_count_html">Error lines: %d</string>
<string name="import_new_count_html">Add QSLs: %d</string>
<string name="import_update_count_html">Update QSLs: %d</string>
<string name="import_invalid_count_html">Invalid QSLs: %d</string>
<string name="import_progress_html">"Progress: "</string>
<string name="import_readed_html">Validated QSL: %d</string>
<string name="import_canceled_html">Canceled!</string>
<string name="import_cancel_button">Stop</string>
</resources>

Wyświetl plik

@ -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'

Wyświetl plik

@ -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

Wyświetl plik

@ -0,0 +1,5 @@
package com.bg7yoz.ft8cn.database;
public interface AfterInsertQSLData {
void doAfterInsert(boolean isInvalid,boolean isNewQSL);
}

Wyświetl plik

@ -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<Integer, ImportTaskList.ImportTask> {
/**
* sessionkey
*
* @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 = "<table bgcolor=#a1a1a1 border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">\n";
String htmlEnder = "</table>\n";
String progress = String.format("<FONT COLOR=\"BLUE\">%s %.1f%%(%d/%d)</FONT>\n", GeneralVariables.getStringFromResource(R.string.import_progress_html)
, count == 0 ? 0 : processCount * 100f / count, processCount, count);
String cell = "<tr><td>%s</td></tr>\n";
String errorHtml = status == ImportState.FINISHED || status == ImportState.CANCELED ? errorMsg : "";
String doCancelButton = status == ImportState.FINISHED || status == ImportState.CANCELED ? ""
: String.format("<br><a href=\"cancelTask?session=%d\"><button>%s</button></a><br>"
, 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("<FONT COLOR=\"BLUE\"><B>%s</B></FONT>"
,GeneralVariables.getStringFromResource(R.string.log_importing_html));
break;
case FINISHED:
this.message = String.format("<FONT COLOR=\"GREEN\"><B>%s</B></FONT>"
,GeneralVariables.getStringFromResource(R.string.log_import_finished_html));
break;
case CANCELED:
this.message = String.format("<FONT COLOR=\"RED\"><B>%s</B></FONT>"
,GeneralVariables.getStringFromResource(R.string.import_canceled_html));
break;
}
}
}
}

Wyświetl plik

@ -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 catyaesu 450d,ham450ddig-u,wolf
* usbusb
* rigUSB
*/
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);
}
}