Merge pull request #22 from N0BOY/dev

Update to ver 0.87
pull/35/head^2 v0.87
NØBOY 2023-03-24 09:44:14 -07:00 zatwierdzone przez GitHub
commit 356f5f90f0
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
202 zmienionych plików z 6185 dodań i 1199 usunięć

Wyświetl plik

@ -15,14 +15,14 @@ static def getCurrentTime() {
android {
compileSdk 32
compileSdk 33
defaultConfig {
applicationId "com.bg7yoz.ft8cn"
minSdk 23
targetSdk 32
targetSdk 33
versionCode 1
versionName '0.86'
versionName '0.87'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
dataBinding{
@ -35,7 +35,6 @@ android {
}
}
ndk{
abiFilters 'armeabi-v7a','arm64-v8a','x86','x86_64'
}
@ -90,11 +89,12 @@ dependencies {
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'
//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.

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -6,4 +6,4 @@ FT8CN在数据库中保存有您关注的呼号、通联过程中的消息
清空“关注的呼号”,关注的呼号是在平时通联是您关注的呼号,在该呼号出现时,会自动添加的呼叫列表中,方便自动呼叫。清空关注的呼号,是把所有关注的呼号清空。
清空“缓存的QSO消息”缓存的QSO消息是您在通联呼叫时保存下来的通联过程该消息没有在FT8CN界面中显示可以在后台中查看保存该数据的目的是方便查看您通联的过程做为调试的一部分。如果您对该数据不感兴趣可以定期清空
清空“解码的消息”FT8CN可以保存所有解码的消息这些消息暂时没有在FT8CN界面中显示可以在后台中查看和查询

Wyświetl plik

@ -1,10 +1,12 @@
About Empty the Cache:
FT8CN keeps the tracked callsign list and QSO decoding log in a cache file. You can permanently remove them using empty cache option. Note that clearing cache won't delete any QSO log.
Clear Cache Data:
FT8CN keeps a list of followed callsign and decoded messages in the cache file. You can permanently delete those using empty cache option.
Clear Tracked callsign list:
Tracked callsign list contains all your favorite callsigns. When a tracked callsign shows up, it will be added to the calling message for quick or auto call.
Note that clearing cache won't delete any QSO log.
Note that clearing tracked callsign list will remove all saved favorite callsigns.
List of followed callsign:
This list contains all your followed callsigns. When a followed callsign shows up, it will be added to the calling message for quick-call or auto-call.
Note that clearing the list of tracked callsign will remove all saved favorite callsigns.
Clean "cached QSO decoding":
FT8CN saves all decoded entries during your QSO. It doesn't display on the UI, but can be accessed for debugging and troubleshooting. You can clean it regularly if not interested in any debugging.
FT8CN can save all decoded messages. You review them from Web UI. (Not on main UI yet)

Wyświetl plik

@ -14,6 +14,17 @@ Please click "FAQ" if you have good suggestions or questions .
BG7YOZ
2022-07-01
2023-03-10(0.87)
1.增加查询的通联日志结果在地图中定位显示功能。
2.增加FlexRadio仪表显示和参数设置(目前暂不支持发射)。
3.增加时间自动同步功能服务器是Microsoft NTP
4.增加SWL模式对解码消息以及QSO有保存和导出的功能(SWL的QSO认定标准至少要有双方的报告以及结束语73、RR73、RRR)。
5.丰富后台数据的查询功能。
6.修正后台查询“呼号与网格映射表”中距离计算的错误。
7.针对协谷G90S未来的新固件调整电台型号选项。
8.解决解码消息较多时界面会卡顿的问题。
9.优化日志查询性能。
2023-02-06(0.86)
1.提高日志导入的健壮性,反馈格式错误的日志信息。
2.修正在推算SNR时偶尔会出现数组下标越界造成闪退的问题。
@ -251,7 +262,6 @@ BG7YOZ
BH1RNN协助对部分功能进行测试。
BG7BSM协助对一些BUG进行调试。
BH4FTI发现并协助对一些BUG进行调试。
BH4FTI发现并协助对一些BUG进行调试。
BG8BXMM哥为FT8CN的使用做推广抖音和B站上有很多他的教学视频。
BG7MFQ为FT8CN的使用做推广帮助测试。

Wyświetl plik

@ -20,10 +20,10 @@ ICOM IC-9100,7C,19200,0
ICOM IC-9700,A2,115200,0
ICOM IC-R8600,96,115200,0
ICOM ID-52A,A6,115200,0
XIEGU(协谷) X6100,70,19200,13
ICOM IC-706MKIIG,58,19200,0
XIEGU(协谷) X6100/G90S(U-DIG),70,19200,13
XIEGU(协谷) X6100/G90S(USB),70,19200,9
XIEGU(协谷) X5105,70,19200,9
XIEGU(协谷) G90S,70,19200,9
XIEGU(协谷) X108,70,19200,9
GUOHE(国赫) Q900,00,19200,8
YAESU FT-450(D),00,4800,4

Wyświetl plik

@ -0,0 +1,7 @@
SWL是英文Shortwave Listener的简称即短波收听爱好者。
SWL模式是仅仅收听其他业余无线电爱好者的通联而不进行无线电发射的模式。
针对SWLFT8CN增加了“保存解码消息”和“保存SWL记录”功能。
保存解码消息就是把FT8CN所有解码的消息都保存下来。
保存SWL记录就是把守听到的其他业余无线电台通联的QSO日志保存下来。在FT8CN中认定成功的SWL QSO标准是有结束语73、RR73、RRR且有双方的信号报告双方的网格报告不是必须项。
保存的“解码消息”和“SWL记录”可以在后台需要在同网段的局域网用浏览器访问做查询以及导出操作。
保存的“解码消息”和“SWL记录”的删除操作在”设置界面“的”清空缓存“中操作。

Wyświetl plik

@ -0,0 +1,7 @@
SWL stands for Shortwave Listener, whom may not hold an amateur radio licence to transmit but listen and report amateur radio QSOs. FT8CN supports "Save decoded messages" and "Save SWL QSO" for SWL.
Save decoded messages: FT8CN will save all decoded messages.
Save SWL QSO: FT8CN can save QSO log as SWL when both stations provide signal report and 73/RR73/RRR (Gridsqure is optional).
You can look up and export saved decoded messages or SWL QSO log via Web UI (under the same LAN).
You can delete them using "Clear Cache Data" in settings.

Wyświetl plik

@ -1,3 +1,3 @@
时间偏移是指本APP在每个周期相对于系统时钟的偏移值。
FT8的时钟周期是15秒所有电台的时钟都是以UTC时间为基准以每分钟的第0秒、15秒、30秒、45秒为周期的起始时间。
一般来说手机会自动同步时间如果您的设备与UTC时间不同步需要设定偏移值。
如果可以访问网络FT8CN会自动同步时间如果不能访问网络可以手动设定偏移值。

Wyświetl plik

@ -1,3 +1,3 @@
Time offset refers to the offset value of the APP relative to the system clock in each cycle.
The clock period of FT8 is 15 seconds. The clocks of all RIGs are based on UTC time, with the 0th, 15th, 30th, and 45th seconds of each minute as the starting time of the cycle.
In general, the phone will automatically sync the time. If your device is out of sync with UTC time, you need to set the offset value.
Local clock offset:
In FT8, all stations start TX/RX at each 0/15/45/60 seconds, which require accurate time synchronization.
FT8CN will automatically sync time via internet. When internet is not available, you can adjust the local clock by changing this local clock offset.

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn;
/**
* WebView
* @author BGY70Z
* @date 2023-03-20
*/
import android.annotation.SuppressLint;
import android.os.Bundle;

Wyświetl plik

@ -1,5 +1,10 @@
package com.bg7yoz.ft8cn;
/**
* FT8
* @author BGY70Z
* @date 2023-03-20
*/
public final class FT8Common {
public static final int FT8_MODE=0;
public static final int FT4_MODE=1;

Wyświetl plik

@ -7,8 +7,8 @@ package com.bg7yoz.ft8cn;
* 1.便GetString
* -----2022.5.13---
* 2.i3,n3
* BG7YOZ
* 2022.5.6
* @author BG7YOZ
* @date 2022.5.6
*/
import android.annotation.SuppressLint;
@ -42,7 +42,7 @@ public class Ft8Message {
public String callsignFrom = null;//发起呼叫的呼号
public String callsignTo = null;//接收呼叫的呼号
public String modifier=null;//目标呼号的修饰符 如CQ POTA BG7YOZ OL50中的POTA
public String modifier = null;//目标呼号的修饰符 如CQ POTA BG7YOZ OL50中的POTA
public String extraInfo = null;
public String maidenGrid = null;
@ -53,26 +53,26 @@ public class Ft8Message {
public long callToHash10 = 0;//12位长度的哈希码
public long callToHash12 = 0;//12位长度的哈希码
public long callToHash22 = 0;//12位长度的哈希码
public boolean isCallMe = false;//是不是CALL我的消息
//private boolean isCallMe = false;//是不是CALL我的消息
public long band;//载波频率
public String fromWhere = null;//用于显示地址
public String toWhere = null;//用于显示地址
public boolean isQSL_Callsign=false;//是不是通联过的呼号
public boolean isQSL_Callsign = false;//是不是通联过的呼号
public static MessageHashMap hashList = new MessageHashMap();
public boolean fromDxcc=false;
public boolean fromItu=false;
public boolean fromCq=false;
public boolean toDxcc=false;
public boolean toItu=false;
public boolean toCq=false;
public boolean fromDxcc = false;
public boolean fromItu = false;
public boolean fromCq = false;
public boolean toDxcc = false;
public boolean toItu = false;
public boolean toCq = false;
public LatLng fromLatLng=null;
public LatLng toLatLng=null;
public LatLng fromLatLng = null;
public LatLng toLatLng = null;
@NonNull
@ -93,20 +93,22 @@ public class Ft8Message {
this.signalFormat = signalFormat;
}
public Ft8Message(String callTo,String callFrom,String extraInfo) {
public Ft8Message(String callTo, String callFrom, String extraInfo) {
//如果是自由文本callTo=CQ,callFrom=MyCall,extraInfo=freeText
this.callsignTo=callTo.toUpperCase();
this.callsignFrom=callFrom.toUpperCase();
this.extraInfo=extraInfo.toUpperCase();
this.callsignTo = callTo.toUpperCase();
this.callsignFrom = callFrom.toUpperCase();
this.extraInfo = extraInfo.toUpperCase();
}
public Ft8Message(int i3,int n3,String callTo,String callFrom,String extraInfo) {
this.callsignTo=callTo;
this.callsignFrom=callFrom;
this.extraInfo=extraInfo;
this.i3=i3;
this.n3=n3;
this.utcTime=UtcTimer.getSystemTime();//用于显示TX
public Ft8Message(int i3, int n3, String callTo, String callFrom, String extraInfo) {
this.callsignTo = callTo;
this.callsignFrom = callFrom;
this.extraInfo = extraInfo;
this.i3 = i3;
this.n3 = n3;
this.utcTime = UtcTimer.getSystemTime();//用于显示TX
}
/**
*
*
@ -137,10 +139,10 @@ public class Ft8Message {
} else {
callsignTo = message.callsignTo;
}
if (message.i3==4){
hashList.addHash(FT8Package.getHash22(message.callsignFrom),message.callsignFrom);
hashList.addHash(FT8Package.getHash12(message.callsignFrom),message.callsignFrom);
hashList.addHash(FT8Package.getHash10(message.callsignFrom),message.callsignFrom);
if (message.i3 == 4) {
hashList.addHash(FT8Package.getHash22(message.callsignFrom), message.callsignFrom);
hashList.addHash(FT8Package.getHash12(message.callsignFrom), message.callsignFrom);
hashList.addHash(FT8Package.getHash10(message.callsignFrom), message.callsignFrom);
}
extraInfo = message.extraInfo;
@ -187,16 +189,16 @@ public class Ft8Message {
*/
public String getMessageText() {
if (i3==0&&n3==0){//说明是自由文本
if (extraInfo.length()<13){
return String.format("%-13s",extraInfo.toUpperCase());
}else {
return extraInfo.toUpperCase().substring(0,13);
if (i3 == 0 && n3 == 0) {//说明是自由文本
if (extraInfo.length() < 13) {
return String.format("%-13s", extraInfo.toUpperCase());
} else {
return extraInfo.toUpperCase().substring(0, 13);
}
}
if (modifier!=null&&checkIsCQ()){//修饰符
if (modifier.matches("[0-9]{3}|[A-Z]{1,4}")){
return String.format("%s %s %s %s", callsignTo,modifier, callsignFrom, extraInfo).trim();
if (modifier != null && checkIsCQ()) {//修饰符
if (modifier.matches("[0-9]{3}|[A-Z]{1,4}")) {
return String.format("%s %s %s %s", callsignTo, modifier, callsignFrom, extraInfo).trim();
}
}
return String.format("%s %s %s", callsignTo, callsignFrom, extraInfo).trim();
@ -204,11 +206,12 @@ public class Ft8Message {
/**
*
*
* @return
*/
@SuppressLint("DefaultLocale")
public String getMessageTextWithDb(){
return String.format("%d %s %s %s",snr, callsignTo, callsignFrom, extraInfo).trim();
public String getMessageTextWithDb() {
return String.format("%d %s %s %s", snr, callsignTo, callsignFrom, extraInfo).trim();
}
/**
@ -269,11 +272,13 @@ public class Ft8Message {
/**
* mycall
*
* @param mycall
* @return boolean
*/
public boolean inMyCall(String mycall) {
return (this.callsignFrom.contains(mycall) || this.callsignTo.contains(mycall)) && (!mycall.equals(""));
public boolean inMyCall() {
if (GeneralVariables.myCallsign.length() == 0) return false;
return this.callsignFrom.contains(GeneralVariables.myCallsign)
|| this.callsignTo.contains(GeneralVariables.myCallsign);
//return (this.callsignFrom.contains(mycall) || this.callsignTo.contains(mycall)) && (!mycall.equals(""));
}
/*
i3.n3
@ -327,7 +332,7 @@ t71 遥感数据最多18位十六进制数字
if (callsignFrom == null) {
return "";
}
return callsignFrom.replace("<","").replace(">","");
return callsignFrom.replace("<", "").replace(">", "");
}
/**
@ -346,7 +351,7 @@ t71 遥感数据最多18位十六进制数字
|| callsignTo.substring(0, 3).equals("QRZ")) {
return "";
}
return callsignTo.replace("<","").replace(">","");
return callsignTo.replace("<", "").replace(">", "");
}
/**
@ -356,20 +361,21 @@ t71 遥感数据最多18位十六进制数字
*/
public String getMaidenheadGrid(DatabaseOpr db) {
if (i3 != 1 && i3 != 2) {//一般只有i3=1或i3=2标准消息甚高频消息才有网格
return GeneralVariables.getGridByCallsign(callsignFrom,db);//到对应表中找一下网格
return GeneralVariables.getGridByCallsign(callsignFrom, db);//到对应表中找一下网格
} else {
String[] msg = getMessageText().split(" ");
if (msg.length < 1) {
return GeneralVariables.getGridByCallsign(callsignFrom,db);//到对应表中找一下网格
return GeneralVariables.getGridByCallsign(callsignFrom, db);//到对应表中找一下网格
}
String s = msg[msg.length - 1];
if (MaidenheadGrid.checkMaidenhead(s)) {
return s;
} else {//不是网格信息,就可能是信号报告
return GeneralVariables.getGridByCallsign(callsignFrom,db);//到对应表中找一下网格
return GeneralVariables.getGridByCallsign(callsignFrom, db);//到对应表中找一下网格
}
}
}
public String getToMaidenheadGrid(DatabaseOpr db) {
if (checkIsCQ()) return "";
return GeneralVariables.getGridByCallsign(callsignTo, db);
@ -392,39 +398,50 @@ t71 遥感数据最多18位十六进制数字
/**
* i3.n3
*
* @return String
* @return
*/
public String getCommandInfo() {
return getCommandInfoByI3N3(i3, n3);
}
/**
* i3.n3
*
* @param i i3
* @param n n3
* @return
*/
@SuppressLint("DefaultLocale")
public String getCommandInfo() {
public static String getCommandInfoByI3N3(int i, int n) {
String format = "%d.%d:%s";
switch (i3) {
switch (i) {
case 1:
case 2:
return String.format(format, i3, 0, GeneralVariables.getStringFromResource(R.string.std_msg));
return String.format(format, i, 0, GeneralVariables.getStringFromResource(R.string.std_msg));
case 5:
case 3:
case 4:
return String.format(format, i3, 0, GeneralVariables.getStringFromResource(R.string.none_std_msg));
return String.format(format, i, 0, GeneralVariables.getStringFromResource(R.string.none_std_msg));
case 0:
switch (n3) {
switch (n) {
case 0:
return String.format(format, i3, n3, GeneralVariables.getStringFromResource(R.string.free_text));
return String.format(format, i, n, GeneralVariables.getStringFromResource(R.string.free_text));
case 1:
return String.format(format, i3, n3, GeneralVariables.getStringFromResource(R.string.dXpedition));
return String.format(format, i, n, GeneralVariables.getStringFromResource(R.string.dXpedition));
case 3:
case 4:
return String.format(format, i3, n3, GeneralVariables.getStringFromResource(R.string.field_day));
return String.format(format, i, n, GeneralVariables.getStringFromResource(R.string.field_day));
case 5:
return String.format(format, i3, n3, GeneralVariables.getStringFromResource(R.string.telemetry));
return String.format(format, i, n, GeneralVariables.getStringFromResource(R.string.telemetry));
}
}
return "";
}
//获取发送者的传输对象
public TransmitCallsign getFromCallTransmitCallsign() {
return new TransmitCallsign(this.i3,this.n3,this.callsignFrom, freq_hz
return new TransmitCallsign(this.i3, this.n3, this.callsignFrom, freq_hz
, this.getSequence()
, snr);
}
@ -432,9 +449,9 @@ t71 遥感数据最多18位十六进制数字
//获取发送者的传输对象,注意!!!与发送者的时序是相反的!!!
public TransmitCallsign getToCallTransmitCallsign() {
if (report == -100) {//如果消息中没有信号报告就用发送方的SNR代替
return new TransmitCallsign(this.i3,this.n3,this.callsignTo, freq_hz, (this.getSequence() + 1) % 2, snr);
return new TransmitCallsign(this.i3, this.n3, this.callsignTo, freq_hz, (this.getSequence() + 1) % 2, snr);
} else {
return new TransmitCallsign(this.i3,this.n3,this.callsignTo, freq_hz, (this.getSequence() + 1) % 2, report);
return new TransmitCallsign(this.i3, this.n3, this.callsignTo, freq_hz, (this.getSequence() + 1) % 2, report);
}
}

Wyświetl plik

@ -1,10 +1,12 @@
package com.bg7yoz.ft8cn;
/**
* mainContext
* mainContext
*/
import android.annotation.SuppressLint;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.MutableLiveData;
import com.bg7yoz.ft8cn.callsign.CallsignDatabase;
@ -12,15 +14,14 @@ import com.bg7yoz.ft8cn.connector.ConnectMode;
import com.bg7yoz.ft8cn.database.ControlMode;
import com.bg7yoz.ft8cn.database.DatabaseOpr;
import com.bg7yoz.ft8cn.ft8transmit.QslRecordList;
import com.bg7yoz.ft8cn.log.QSLRecord;
import com.bg7yoz.ft8cn.rigs.BaseRigOperation;
import com.bg7yoz.ft8cn.timer.UtcTimer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@ -29,10 +30,15 @@ public class GeneralVariables {
public static String VERSION = BuildConfig.VERSION_NAME;//版本号"0.62Beta 4";
public static String BUILD_DATE = BuildConfig.apkBuildTime;//编译的时间
public static int MESSAGE_COUNT = 3000;//消息的最大缓存数量
public static boolean saveSWLMessage=false;//保存解码消息开关
public static boolean saveSWL_QSO=false;//保存解码消息消息中的QSO开关
public static MutableLiveData<Float> mutableVolumePercent = new MutableLiveData<>();
public static float volumePercent = 0.5f;//播放音频的音量,是百分比
public static int flexMaxRfPower=10;//flex电台的最大发射功率
public static int flexMaxTunePower=10;//flex电台的最大调谐功率
private Context mainContext;
public static CallsignDatabase callsignDatabase = null;
@ -42,7 +48,7 @@ public class GeneralVariables {
public static boolean isChina = true;//语言是不是中国
public static boolean isTraditionalChinese = true;//语言是不是繁体中文
public static double maxDist = 0;//最远距离
//public static double maxDist = 0;//最远距离
//各已经通联的分区列表
public static final Map<String, String> dxccMap = new HashMap<>();
@ -99,7 +105,7 @@ public class GeneralVariables {
if (i == 0) {
calls.append(key);
} else {
calls.append("," + key);
calls.append(",").append(key);
}
i++;
}
@ -366,6 +372,43 @@ public class GeneralVariables {
}
/**
* report
* @param extraInfo
* @return ,-100
*/
public static int checkFun2_3(String extraInfo){
if (extraInfo.equals("73")) return -100;
if (extraInfo.matches("[R]?[+-]?[0-9]{1,2}")){
try {
return Integer.parseInt(extraInfo.replace("R",""));
} catch (Exception e) {
return -100;
}
}
return -100;
}
/**
* report
* @param extraInfo
* @return
*/
public static boolean checkFun1_6(String extraInfo){
return extraInfo.trim().matches("[A-Z][A-Z][0-9][0-9]")
&& !extraInfo.trim().equals("RR73");
}
/**
* RRRRR7373
* @param extraInfo
* @return
*/
public static boolean checkFun4_5(String extraInfo){
return extraInfo.trim().equals("RR73")
|| extraInfo.trim().equals("RRR")
||extraInfo.trim().equals("73");
}
/**
* String.xml
*
@ -517,7 +560,7 @@ public class GeneralVariables {
}
public static synchronized void deleteArrayListMore(ArrayList list) {
public static synchronized void deleteArrayListMore(ArrayList<Ft8Message> list) {
if (list.size() > GeneralVariables.MESSAGE_COUNT) {
while (list.size() > GeneralVariables.MESSAGE_COUNT) {
list.remove(0);

Wyświetl plik

@ -6,8 +6,9 @@ package com.bg7yoz.ft8cn;
* 1.MainViewModelMainViewModel
* 2.
* 3.Fragment
* BG7YOZ
* 2022.5.6
* 4.USB
* @author BG7YOZ
* @date 2022.5.6
*/
@ -60,6 +61,7 @@ import com.bg7yoz.ft8cn.ui.ToastMessage;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
@ -132,8 +134,6 @@ public class MainActivity extends AppCompatActivity {
setContentView(binding.getRoot());
ToastMessage.getInstance();
registerBluetoothReceiver();//注册蓝牙动作改变的广播
if (mainViewModel.isBTConnected()) {
@ -195,6 +195,7 @@ public class MainActivity extends AppCompatActivity {
//清空缓存中的文件
//deleteFolderFile(this.getCacheDir().getPath());
//Log.e(TAG, this.getCacheDir().getPath());
//用于Fragment的导航。
NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.fragmentContainerView);
@ -217,18 +218,53 @@ public class MainActivity extends AppCompatActivity {
binding.welcomTextView.setText(String.format(getString(R.string.version_info)
, GeneralVariables.VERSION, GeneralVariables.BUILD_DATE));
floatView = new FloatView(this, 32);
if (!animatorRunned) {
animationImage();
animatorRunned = true;
} else {
binding.initDataLayout.setVisibility(View.GONE);
InitFloatView();
}
//初始化数据
InitData();
//观察是不是flex radio
mainViewModel.mutableIsFlexRadio.observe(this, new Observer<Boolean>() {
@Override
public void onChanged(Boolean aBoolean) {
//if (floatView==null) return;
if (aBoolean) {
//添加flex配置按钮
floatView.addButton(R.id.flex_radio, "flex_radio", R.drawable.flex_icon
, new View.OnClickListener() {
@Override
public void onClick(View view) {
navController.navigate(R.id.flexRadioInfoFragment);
// if (mainViewModel.baseRig != null) {
// if (mainViewModel.baseRig.isConnected()) {
// ToastMessage.show("flex connected");
// }else {
// ToastMessage.show("flex disconnected");
// }
// }else {
// ToastMessage.show("rig is null");
// }
}
});
} else {//删除flex配置按钮
floatView.deleteButtonByName("flex_radio");
}
}
});
//关闭串口设备列表按钮
binding.closeSelectSerialPortImageView.setOnClickListener(new View.OnClickListener() {
@Override
@ -281,7 +317,7 @@ public class MainActivity extends AppCompatActivity {
*/
private void InitFloatView() {
floatView = new FloatView(this, 32);
//floatView = new FloatView(this, 32);
binding.container.addView(floatView);
floatView.setButtonMargin(0);
@ -332,6 +368,15 @@ public class MainActivity extends AppCompatActivity {
}
});
// floatView.addButton(R.id.flex_radio, "flex_radio", R.drawable.flex_icon
// , new View.OnClickListener() {
// @Override
// public void onClick(View view) {
// navController.navigate(R.id.flexRadioInfoFragment);
// }
// });
floatView.initLocation();
}

Wyświetl plik

@ -5,21 +5,21 @@ package com.bg7yoz.ft8cn;
* 1.decoded_countermutable_Decoded_Counter
* 2.Ft8MessageArrayListft8MessagesmutableFt8MessageList
* 3.UTC15UtcTimer
* runRecodetestFt8--------TO DO------------
* runRecodetestFt8--------TODO------------
* 4.UTCtimerSecUtcTimer100
*
* <p>
* 5.getInstanceMainViewModel
* 6.HamAudioRecorder----TO DO---
*
* <p>
* 7.JNICft8cncppCMakeLists.txtdecode_ft8.cpp
*
* <p>
* -----2022.5.9-----
*
*
* 30014.77
*
* BG7YOZ
* 2022.5.6
* <p>
* @author BG7YOZ
* @date 2022.5.6
*/
import static com.bg7yoz.ft8cn.GeneralVariables.getStringFromResource;
@ -59,7 +59,9 @@ import com.bg7yoz.ft8cn.ft8transmit.OnDoTransmitted;
import com.bg7yoz.ft8cn.ft8transmit.OnTransmitSuccess;
import com.bg7yoz.ft8cn.html.LogHttpServer;
import com.bg7yoz.ft8cn.icom.IComWifiRig;
import com.bg7yoz.ft8cn.log.QSLCallsignRecord;
import com.bg7yoz.ft8cn.log.QSLRecord;
import com.bg7yoz.ft8cn.log.SWLQsoList;
import com.bg7yoz.ft8cn.rigs.BaseRig;
import com.bg7yoz.ft8cn.rigs.BaseRigOperation;
import com.bg7yoz.ft8cn.rigs.ElecraftRig;
@ -121,6 +123,8 @@ public class MainViewModel extends ViewModel {
public MutableLiveData<Boolean> mutableIsDecoding = new MutableLiveData<>();//会触发频谱图中的标记动作
public ArrayList<Ft8Message> currentMessages = null;//本周期解码的消息(用于画到频谱上)
public MutableLiveData<Boolean> mutableIsFlexRadio=new MutableLiveData<>();//是不是flex电台
private final ExecutorService getQTHThreadPool = Executors.newCachedThreadPool();
private final ExecutorService sendWaveDataThreadPool = Executors.newCachedThreadPool();
private final GetQTHRunnable getQTHRunnable=new GetQTHRunnable(this);
@ -136,6 +140,8 @@ public class MainViewModel extends ViewModel {
//控制电台的方式
public OperationBand operationBand = null;
private SWLQsoList swlQsoList=new SWLQsoList();//用于记录SWL的QSO对象对SWL QSO做判断防止重复。
public MutableLiveData<ArrayList<CableSerialPort.SerialPort>> mutableSerialPorts = new MutableLiveData<>();
private ArrayList<CableSerialPort.SerialPort> serialPorts;//串口列表
@ -193,6 +199,8 @@ public class MainViewModel extends ViewModel {
public String queryKey = "";//查询的关键字
public int queryFilter = 0;//过滤0全部1确认2未确认
public MutableLiveData<Integer> mutableQueryFilter = new MutableLiveData<>();
public ArrayList<QSLCallsignRecord> callsignRecords=new ArrayList<>();
//public ArrayList<QSLRecordStr> qslRecords=new ArrayList<>();
//********************************************
//关注呼号的列表
//public ArrayList<String> followCallsign = new ArrayList<>();
@ -240,6 +248,7 @@ public class MainViewModel extends ViewModel {
hamRecorder = new HamRecorder(null);
hamRecorder.startRecord();
mutableIsFlexRadio.setValue(false);
//创建用于显示时间的计时器
utcTimer = new UtcTimer(10, false, new OnUtcTimer() {
@ -257,6 +266,8 @@ public class MainViewModel extends ViewModel {
});
utcTimer.start();//启动计时器
//同步一下时间。microsoft的NTP服务器
UtcTimer.syncTime(null);
mutableFt8MessageList.setValue(ft8Messages);
@ -270,13 +281,10 @@ public class MainViewModel extends ViewModel {
@Override
public void afterDecode(long utc, float time_sec, int sequential, ArrayList<Ft8Message> messages) {
for (Ft8Message msg : messages) {
msg.isCallMe = msg.inMyCall(GeneralVariables.myCallsign);
}
synchronized (ft8Messages) {
ft8Messages.addAll(messages);//添加消息到列表
}
GeneralVariables.deleteArrayListMore(ft8Messages);//删除多余的消息
GeneralVariables.deleteArrayListMore(ft8Messages);//删除多余的消息,FT8CN限定的可展示消息的总数量
mutableFt8MessageList.postValue(ft8Messages);//触发添加消息的动作,让界面能观察到
mutableTimerOffset.postValue(time_sec);//本次时间偏移量
@ -292,8 +300,21 @@ public class MainViewModel extends ViewModel {
currentMessages = messages;//保存本次解码的消息
getQTHRunnable.messages=messages;
getQTHThreadPool.execute(getQTHRunnable);//用线程池的方式查询
getQTHThreadPool.execute(getQTHRunnable);//用线程池的方式查询归属地
if (GeneralVariables.saveSWLMessage) {
databaseOpr.writeMessage(messages);//把SWL消息写到数据库
}
//检查QSO of SWL,并保存到SWLQSOTable中的通联列表qsoList中
if (GeneralVariables.saveSWL_QSO){
swlQsoList.findSwlQso(messages, ft8Messages, new SWLQsoList.OnFoundSwlQso() {
@Override
public void doFound(QSLRecord record) {
databaseOpr.addSWL_QSO(record);//把SWL QSO保存到数据库
ToastMessage.show(record.swlQSOInfo());
}
});
}
//从列表中查找呼号和网格对应关系,并添加到表中
getCallsignAndGrid(messages);
}
@ -345,14 +366,14 @@ public class MainViewModel extends ViewModel {
}
@Override
public void onAfterGenerate(short[] data) {
public void onAfterGenerate(float[] data) {
if (GeneralVariables.connectMode == ConnectMode.NETWORK) {
if (baseRig != null) {
if (baseRig.isConnected()) {
sendWaveDataRunnable.baseRig=baseRig;
sendWaveDataRunnable.data=data;
//以线程池的方式执行网络数据包发送
sendWaveDataThreadPool.execute(sendWaveDataRunnable);
}
}
}
@ -361,7 +382,6 @@ public class MainViewModel extends ViewModel {
@Override
public void doAfterTransmit(QSLRecord qslRecord) {
databaseOpr.addQSL_Callsign(qslRecord);//两个操作把呼号和QSL记录下来
if (qslRecord.getToCallsign() != null) {//把通联成功的分区加入到分区列表
GeneralVariables.callsignDatabase.getCallsignInformation(qslRecord.getToCallsign()
, new OnAfterQueryCallsignLocation() {
@ -373,7 +393,6 @@ public class MainViewModel extends ViewModel {
}
});
}
}
});
@ -437,7 +456,6 @@ public class MainViewModel extends ViewModel {
public void clearTransmittingMessage() {
GeneralVariables.transmitMessages.clear();
mutableTransmitMessagesCount.postValue(0);
//mutableTransmitMessages.postValue(GeneralVariables.transmitMessages);
}
@ -448,7 +466,7 @@ public class MainViewModel extends ViewModel {
*/
private void getCallsignAndGrid(ArrayList<Ft8Message> messages) {
for (Ft8Message msg : messages) {
if (GeneralVariables.checkFun1(msg.extraInfo)) {
if (GeneralVariables.checkFun1(msg.extraInfo)) {//检查是不是网格
//如果内存表中没有,或不一致,就写入数据库中
if (!GeneralVariables.getCallsignHasGrid(msg.getCallsignFrom(), msg.maidenGrid)) {
databaseOpr.addCallsignQTH(msg.getCallsignFrom(), msg.maidenGrid);//写数据库
@ -663,7 +681,7 @@ public class MainViewModel extends ViewModel {
baseRig.setOnRigStateChanged(onRigStateChanged);
baseRig.setConnector(flexConnector);
//
new Handler().postDelayed(new Runnable() {//蓝牙连接是需要时间的等2秒再设置频率
new Handler().postDelayed(new Runnable() {//连接是需要时间的等2秒再设置频率
@Override
public void run() {
setOperationBand();//设置载波频率
@ -713,7 +731,7 @@ public class MainViewModel extends ViewModel {
case InstructionSet.GUOHE_Q900:
baseRig = new GuoHeQ900Rig();//国赫Q900
break;
case InstructionSet.XIEGUG90S:
case InstructionSet.XIEGUG90S://协谷USB模式
baseRig = new XieGuRig(GeneralVariables.civAddress);//协谷G90S
break;
case InstructionSet.ELECRAFT:
@ -729,6 +747,9 @@ public class MainViewModel extends ViewModel {
baseRig = new XieGu6100Rig(GeneralVariables.civAddress);//协谷6100
break;
}
mutableIsFlexRadio.postValue(GeneralVariables.instructionSet==InstructionSet.FLEX_NETWORK);
}
@ -774,7 +795,6 @@ public class MainViewModel extends ViewModel {
.getSystemService(Context.AUDIO_SERVICE);
if (audioManager == null) return;
if (audioManager.isBluetoothScoOn()) {
//audioManager.setMode(AudioManager.MODE_NORMAL);
audioManager.setBluetoothScoOn(false);
audioManager.stopBluetoothSco();
audioManager.setSpeakerphoneOn(true);//退出耳机模式
@ -861,11 +881,11 @@ public class MainViewModel extends ViewModel {
}
private static class SendWaveDataRunnable implements Runnable{
BaseRig baseRig;
short[] data;
float[] data;
@Override
public void run() {
if (baseRig!=null&&data!=null){
baseRig.sendWaveData(data);
baseRig.sendWaveData(data);//实际生成的数据是12.64+0.04,0.04是生成的0数据
}
}
}

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn;
/**
*
* @author BGY70Z
* @date 2023-03-20
*/
import android.util.Log;
@ -12,8 +17,11 @@ public class MessageHashMap extends HashMap<Long,String> {
*
* @param hashCode
* @param callsign
* @return false
*/
public synchronized void addHash(long hashCode, String callsign) {
//if (callsign.length()<2){return;}
//if (){return;}
if (callsign.equals("CQ")||callsign.equals("QRZ")||callsign.equals("DE")){
return;
}
@ -27,7 +35,12 @@ public class MessageHashMap extends HashMap<Long,String> {
//检查是否存在这个hash码
public boolean checkHash(long hashCode) {
return get(hashCode)!=null;
// for (HashStruct hash : this) {
// if (hash.hashCode == hashCode) {
// return true;
// }
// }
// return false;
}
//通过哈希码查呼号

Wyświetl plik

@ -7,14 +7,18 @@ import android.os.ParcelUuid;
import com.bg7yoz.ft8cn.BuildConfig;
/**
*
*/
public class BluetoothConstants {
// values have to be globally unique
static final String INTENT_ACTION_DISCONNECT = BuildConfig.APPLICATION_ID + ".Disconnect";
static final String NOTIFICATION_CHANNEL = BuildConfig.APPLICATION_ID + ".Channel";
static final String INTENT_CLASS_MAIN_ACTIVITY = BuildConfig.APPLICATION_ID + ".MainActivity";
// values have to be unique within each app
static final int NOTIFY_MANAGER_START_FOREGROUND_SERVICE = 1001;

Wyświetl plik

@ -1,5 +1,10 @@
package com.bg7yoz.ft8cn.bluetooth;
/**
*
* BG7YOZ
* 2023-03
*/
public interface BluetoothSerialListener {
void onSerialConnect ();
void onSerialConnectError (Exception e);

Wyświetl plik

@ -13,7 +13,11 @@ import java.io.IOException;
import java.util.LinkedList;
import java.util.Queue;
/**
*
* BG7YOZ
* 2023-03
*/
public class BluetoothSerialService extends Service implements BluetoothSerialListener {
public class SerialBinder extends Binder {
@ -118,11 +122,6 @@ public class BluetoothSerialService extends Service implements BluetoothSerialLi
if (connected){
disconnect();
}
//if(connected)
// createNotification();
// items already in event queue (posted before detach() to mainLooper) will end up in queue1
// items occurring later, will be moved directly to queue2
// detach() and mainLooper.post run in the main thread, so all items are caught
listener = null;
}

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.bluetooth;
/**
* SOCKET
* BG7YOZ
* 2023-03
*/
import android.annotation.SuppressLint;
import android.app.Activity;

Wyświetl plik

@ -1,7 +1,7 @@
package com.bg7yoz.ft8cn.bluetooth;
/**
* 广
* @author bg7yoz
* @writer bg7yoz
* @date 2022-07-22
*/
@ -92,4 +92,18 @@ public class BluetoothStateBroadcastReceive extends BroadcastReceiver {
}
}
// static final int PROFILE_HEADSET = 0;
// static final int PROFILE_A2DP = 1;
// static final int PROFILE_OPP = 2;
// static final int PROFILE_HID = 3;
// static final int PROFILE_PANU = 4;
// static final int PROFILE_NAP = 5;
// static final int PROFILE_A2DP_SINK = 6;
//
// private boolean checkBluetoothClass(BluetoothClass bluetoothClass,int proFile){
// if (proFile==PROFILE_A2DP){
// bluetoothClass.hasService(BluetoothClass.Service.RENDER);
// return true;
// }
// }
}

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.callsign;
/**
* CTY.DAT
* @author BG7YOZ
* 2023-03-20
*/
import android.annotation.SuppressLint;
import android.content.ContentValues;
@ -182,6 +187,29 @@ public class CallsignDatabase extends SQLiteOpenHelper {
@SuppressLint("Range")
@Override
protected Void doInBackground(Void... voids) {
// String querySQL = "select a.*,b.* from callsigns as a left join countries as b on a.countryId =b.id \n" +
// "WHERE (SUBSTR(?,1,LENGTH(callsign))=callsign) OR (callsign=\"=\"||?)\n" +
// "order by LENGTH(callsign) desc\n" +
// "LIMIT 1";
//
// Cursor cursor = db.rawQuery(querySQL, new String[]{sqlParameter.toUpperCase(), sqlParameter.toUpperCase()});
// if (cursor.moveToFirst()) {
// CallsignInfo callsignInfo = new CallsignInfo();
// callsignInfo.CallSign = sqlParameter.toUpperCase();
// callsignInfo.CountryNameEn = cursor.getString(cursor.getColumnIndex("CountryNameEn"));
// callsignInfo.CountryNameCN = cursor.getString(cursor.getColumnIndex("CountryNameCN"));
// callsignInfo.CQZone = cursor.getInt(cursor.getColumnIndex("CQZone"));
// callsignInfo.ITUZone = cursor.getInt(cursor.getColumnIndex("ITUZone"));
// callsignInfo.Continent = cursor.getString(cursor.getColumnIndex("Continent"));
// callsignInfo.Latitude = cursor.getFloat(cursor.getColumnIndex("Latitude"));
// callsignInfo.Longitude = cursor.getFloat(cursor.getColumnIndex("Longitude"));
// callsignInfo.GMT_offset = cursor.getFloat(cursor.getColumnIndex("GMT_offset"));
// callsignInfo.DXCC = cursor.getString(cursor.getColumnIndex("DXCC"));
// if (afterQueryCallsignLocation!=null){
// afterQueryCallsignLocation.doOnAfterQueryCallsignLocation(callsignInfo);
// }
// }
// cursor.close();
CallsignInfo callsignInfo = getCallsignInfo(db, sqlParameter);
if (callsignInfo != null && afterQueryCallsignLocation != null) {
afterQueryCallsignLocation.doOnAfterQueryCallsignLocation(callsignInfo);

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.callsign;
/**
* CTY.DAT
* @author BG7YOZ
* @date 2023-03-20
*/
import android.content.Context;
import android.content.res.AssetManager;

Wyświetl plik

@ -1,4 +1,10 @@
package com.bg7yoz.ft8cn.callsign;
/**
*
*
* @author BG7YOZ
* @date 2023-03-20
*/
import android.annotation.SuppressLint;
import android.util.Log;

Wyświetl plik

@ -1,5 +1,12 @@
package com.bg7yoz.ft8cn.callsign;
/**
*
*
* @author BG7YOZ
* @date 2023-03-20
*
*/
public interface OnAfterQueryCallsignLocation {
void doOnAfterQueryCallsignLocation(CallsignInfo callsignInfo);
}

Wyświetl plik

@ -1,5 +1,10 @@
package com.bg7yoz.ft8cn.connector;
/**
* USB线FLEXICOM
*
* @author BG7YOZ
* @date 2023-03-20
*/
import com.bg7yoz.ft8cn.rigs.OnConnectReceiveData;
import com.bg7yoz.ft8cn.rigs.OnRigStateChanged;
@ -69,7 +74,7 @@ public class BaseRigConnector {
onConnectReceiveData=receiveData;
}
public void sendWaveData(short[] data){
public void sendWaveData(float[] data){
//留给网络方式发送音频流
}

Wyświetl plik

@ -1,4 +1,10 @@
package com.bg7yoz.ft8cn.connector;
/**
* Connector,BaseRigConnector
*
* @author BG7YOZ
* @date 2023-03-20
*/
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;

Wyświetl plik

@ -7,7 +7,10 @@ import com.bg7yoz.ft8cn.database.ControlMode;
import com.bg7yoz.ft8cn.serialport.util.SerialInputOutputManager;
/**
* 线ConnectorUSB
* 线ConnectorUSBBaseRigConnector
*
* @author BG7YOZ
* @date 2023-03-20
*/
public class CableConnector extends BaseRigConnector {
private static final String TAG="CableConnector";

Wyświetl plik

@ -1,4 +1,10 @@
package com.bg7yoz.ft8cn.connector;
/**
* USBUSBserialportCDCCH34xCP21xxFTDI
*
* @author BGY70Z
* @date 2023-03-20
*/
import android.annotation.SuppressLint;
import android.app.Activity;

Wyświetl plik

@ -1,5 +1,10 @@
package com.bg7yoz.ft8cn.connector;
/**
*
* @author BGY70Z
* @date 2023-03-20
*/
public class ConnectMode {
public static final int USB_CABLE=0;
public static final int BLUE_TOOTH=1;

Wyświetl plik

@ -4,10 +4,13 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.util.Log;
import androidx.lifecycle.MutableLiveData;
import com.bg7yoz.ft8cn.GeneralVariables;
import com.bg7yoz.ft8cn.R;
import com.bg7yoz.ft8cn.flex.FlexCommand;
import com.bg7yoz.ft8cn.flex.FlexMeters;
import com.bg7yoz.ft8cn.flex.FlexMeterInfos;
import com.bg7yoz.ft8cn.flex.FlexMeterList;
import com.bg7yoz.ft8cn.flex.FlexRadio;
import com.bg7yoz.ft8cn.flex.RadioTcpClient;
import com.bg7yoz.ft8cn.flex.VITA;
@ -18,14 +21,19 @@ import java.io.DataInputStream;
import java.io.IOException;
/**
* FlexConnector,...
* @author BG7YOZ
* 线ConnectorUSB
* @author BGY70Z
* @date 2023-03-20
*/
public class FlexConnector extends BaseRigConnector {
public MutableLiveData<FlexMeterList> mutableMeterList=new MutableLiveData<>();
public FlexMeterInfos flexMeterInfos =new FlexMeterInfos("");
public FlexMeterList meterList=new FlexMeterList();
public interface OnWaveDataReceived{
void OnDataReceived(int bufferLen,float[] buffer);
}
public int maxRfPower;
public int maxTunePower;
private static final String TAG = "CableConnector";
@ -37,8 +45,8 @@ public class FlexConnector extends BaseRigConnector {
public FlexConnector(Context context, FlexRadio flexRadio, int controlMode) {
super(controlMode);
this.flexRadio = flexRadio;
maxTunePower=GeneralVariables.flexMaxTunePower;
maxRfPower=GeneralVariables.flexMaxRfPower;
setFlexRadioInterface();
//connect();
}
@ -77,17 +85,11 @@ public class FlexConnector extends BaseRigConnector {
@Override
public void onReceiveMeter(VITA vita) {
//mutableVita.postValue(vita.showHeadStr()+"\n"+vita.showPayloadHex());
// float val;
// byte data[]=new byte[4];
// data[0]=vita.payload[0];
// data[1]=vita.payload[1];
// data[2]=vita.payload[2];
// data[3]=vita.payload[3];
////
// ToastMessage.show(String.format("%s, %d,%e",vita.showPayloadHex()
// ,byteDataTo16BitData(data)[1]
// ,Float.intBitsToFloat(byteDataTo16BitData(data)[1])));
//Log.e(TAG, "onReceiveMeter: "+vita.showPayloadHex() );
meterList.setMeters(vita.payload,flexMeterInfos);
mutableMeterList.postValue(meterList);
}
@Override
@ -111,13 +113,15 @@ public class FlexConnector extends BaseRigConnector {
Log.e(TAG, "onResponse: "+response.rawData );
if (response.flexCommand== FlexCommand.METER_LIST){
Log.e(TAG, "onResponse: meter exContent:"
+response.exContent.substring(response.exContent.indexOf("meter ")+"meter ".length()));
FlexMeters flexMeters=new FlexMeters(response.exContent);
flexMeters.getAllMeters();
//FlexMeters flexMeters=new FlexMeters(response.exContent);
flexMeterInfos.setMeterInfos(response.exContent);
flexRadio.commandSubMeterAll();//显示全部仪表消息
//flexMeters.getAllMeters();
//Log.e(TAG, "onResponse: ----->>>"+flexMeters.getAllMeters() );
}
if (response.flexCommand==FlexCommand.STREAM_CREATE_DAX_TX){
flexRadio.streamTxId=response.daxTxStreamId;
}
// if (response.flexCommand== FlexCommand.METER_LIST){
// Log.e(TAG, "onResponse: ."+response.rawData.replace("#","\n") );
@ -150,28 +154,35 @@ public class FlexConnector extends BaseRigConnector {
flexRadio.commandSubDaxAll();//注册全部DAX流
//flexRadio.commandSubMeterAll();//显示全部仪表消息
flexRadio.commandClientSetEnforceNetWorkGui();//对网络MTU做设置
//flexRadio.commandSliceList();//列slice
flexRadio.commandSliceCreate();//创建slice
flexRadio.commandMeterList();//列一下仪表
//flexRadio.commandSubMeterById(5);
//flexRadio.commandSliceTune(0, "21.074");//设置默认频率
flexRadio.commandSliceTune(0,String.format("%.3f",GeneralVariables.band/1000000f));
flexRadio.commandSetDaxAudio(1, 0, true);//打开DAX
flexRadio.commandUdpPort();//设置UDP端口
flexRadio.commandStreamCreateDaxRx(1);//创建流数据到DAX通道1
flexRadio.commandStreamCreateDaxTx(1);//创建流数据到DAX通道1
//TODO 是否设置??? dax tx T 或者 dax tx 1
flexRadio.commandSliceTune(0,String.format("%.3f",GeneralVariables.band/1000000f));
flexRadio.commandSliceSetMode(0, FlexRadio.FlexMode.DIGU);//设置操作模式
flexRadio.commandSetFilter(0, 0, 3000);//设置滤波为3000HZ
flexRadio.commandSetRfPower(0);//设置发射功率
Log.e(TAG, "onConnectSuccess: ------Meter List" );
ToastMessage.show("------");
flexRadio.commandMeterList();//列一下仪表
//flexRadio.commandSubMeterAll();//显示全部仪表消息
setMaxRfPower(maxRfPower);//设置发射功率
setMaxTunePower(maxTunePower);//设置调谐功率
//flexRadio.commandSubMeterById(5);//列指定的仪表
//flexRadio.commandSliceSetNR(0, true);
//flexRadio.commandSliceSetNB(0, true);
@ -203,7 +214,30 @@ public class FlexConnector extends BaseRigConnector {
});
}
public void setMaxRfPower(int power){
maxRfPower=power;
GeneralVariables.flexMaxRfPower=power;
flexRadio.commandSetRfPower(maxRfPower);//设置发射功率
}
public void setMaxTunePower(int power){
maxTunePower=power;
GeneralVariables.flexMaxTunePower=power;
flexRadio.commandSetTunePower(maxTunePower);//设置调谐功率
}
public void startATU(){
flexRadio.commandStartATU();
}
public void tuneOnOff(boolean on){
flexRadio.commandTuneTransmitOnOff(on);
}
public void subAllMeters(){
if (flexMeterInfos.size()==0) {
flexRadio.commandMeterList();//列一下仪表
flexRadio.commandSubMeterAll();//显示全部仪表消息
}
}
@Override
public void sendData(byte[] data) {
@ -213,13 +247,8 @@ public class FlexConnector extends BaseRigConnector {
@Override
public void setPttOn(boolean on) {
//只处理RTS和DTR
// switch (getControlMode()){
// case ControlMode.DTR: cableSerialPort.setDTR_On(on);//打开和关闭DTR
// break;
// case ControlMode.RTS:cableSerialPort.setRTS_On(on);//打开和关闭RTS
// break;
// }
flexRadio.isPttOn=on;
flexRadio.commandPTTOnOff(on);
}
@Override
@ -227,6 +256,13 @@ public class FlexConnector extends BaseRigConnector {
//cableSerialPort.sendData(command);//以CAT指令发送PTT
}
@Override
public void sendWaveData(float[] data) {
Log.e(TAG, "sendWaveData: flexConnector:"+data.length );
flexRadio.sendWaveData(data);
//super.sendWaveData(data);
}
@Override
public void connect() {
super.connect();
@ -252,7 +288,7 @@ public class FlexConnector extends BaseRigConnector {
}
/**
* ,24000hz12000
* ,24000hz12000
* @param bytes
* @return
*/
@ -263,13 +299,12 @@ public class FlexConnector extends BaseRigConnector {
try {
float f1,f2;
f1=dis.readFloat();
f2=dis.readFloat();//放弃一个声道
dis.readFloat();//放弃一个声道
f2=dis.readFloat();
floats[i] = Math.max(f1,f2);//取最大值
dis.readFloat();//放弃1个声道
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "getFloat: ------>>" + e.getMessage());
break;
}
}

Wyświetl plik

@ -1,4 +1,11 @@
package com.bg7yoz.ft8cn.connector;
/**
* ICom
* IComIntFloat
*
* @author BGY70Z
* @date 2023-03-20
*/
import com.bg7yoz.ft8cn.icom.IComWifiRig;
@ -42,7 +49,7 @@ public class IComWifiConnector extends BaseRigConnector{
}
@Override
public void sendWaveData(short[] data) {
public void sendWaveData(float[] data) {
if (iComWifiRig.opened) {
iComWifiRig.sendWaveData(data);
}

Wyświetl plik

@ -1,5 +1,10 @@
package com.bg7yoz.ft8cn.connector;
/**
*
* @author BGY70Z
* @date 2023-03-20
*/
public interface OnConnectorStateChanged {
void onDisconnected();
void onConnected();

Wyświetl plik

@ -1,4 +1,11 @@
package com.bg7yoz.ft8cn.count;
/**
*
*
*
* @author BGY70Z
* @date 2023-03-20
*/
import android.annotation.SuppressLint;
import android.database.Cursor;

Wyświetl plik

@ -1,4 +1,10 @@
package com.bg7yoz.ft8cn.count;
/**
*
*
* @author BGY70Z
* @date 2023-03-20
*/
import android.annotation.SuppressLint;
import android.os.Bundle;

Wyświetl plik

@ -1,5 +1,10 @@
package com.bg7yoz.ft8cn.count;
/**
* Adapter
*
* @author BGY70Z
* @date 2023-03-20
*/
import android.content.Context;
import android.view.LayoutInflater;

Wyświetl plik

@ -1,5 +1,10 @@
package com.bg7yoz.ft8cn.database;
/**
*
* @author BGY70Z
* @date 2023-03-20
*/
public class ControlMode {
public static final int VOX=0;
public static final int CAT=1;

Wyświetl plik

@ -1,4 +1,13 @@
package com.bg7yoz.ft8cn.database;
/**
* HTTP
* onUpgrade
*
*
* @author BGY70Z
* @date 2023-03-20
*
*/
import android.annotation.SuppressLint;
import android.content.Context;
@ -20,6 +29,7 @@ import com.bg7yoz.ft8cn.log.QSLCallsignRecord;
import com.bg7yoz.ft8cn.log.QSLRecord;
import com.bg7yoz.ft8cn.log.QSLRecordStr;
import com.bg7yoz.ft8cn.rigs.BaseRigOperation;
import com.bg7yoz.ft8cn.timer.UtcTimer;
import org.jetbrains.annotations.Nullable;
import org.json.JSONArray;
@ -33,6 +43,7 @@ import java.util.HashMap;
public class DatabaseOpr extends SQLiteOpenHelper {
private static final String TAG = "DatabaseOpr";
@SuppressLint("StaticFieldLeak")
private static DatabaseOpr instance;
private final Context context;
private SQLiteDatabase db;
@ -40,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, 12);
instance = new DatabaseOpr(context, databaseName, null, 13);
}
return instance;
}
@ -79,6 +90,9 @@ public class DatabaseOpr extends SQLiteOpenHelper {
//创建呼号与网格对应关系表
createCallsignQTHTables(sqLiteDatabase);
//创建SWL相关的表
createSWLTables(sqLiteDatabase);
}
@ -99,6 +113,9 @@ public class DatabaseOpr extends SQLiteOpenHelper {
//创建呼号与网格对应关系表
createCallsignQTHTables(sqLiteDatabase);
//创建SWL相关的表
createSWLTables(sqLiteDatabase);
//删除DXCC呼号列表中的等号
//deleteDxccPrefixEqual(sqLiteDatabase);
}
@ -155,9 +172,11 @@ public class DatabaseOpr extends SQLiteOpenHelper {
}
return false;
}
private void deleteDxccPrefixEqual(SQLiteDatabase db){
private void deleteDxccPrefixEqual(SQLiteDatabase db) {
db.execSQL("DELETE from dxcc_prefix where prefix LIKE \"=%\"");
}
/**
*
*/
@ -324,6 +343,47 @@ public class DatabaseOpr extends SQLiteOpenHelper {
}
}
private void createSWLTables(SQLiteDatabase sqLiteDatabase) {
if (!checkTableExists(sqLiteDatabase, "SWLMessages")) {
sqLiteDatabase.execSQL("CREATE TABLE SWLMessages (\n" +
"\tID INTEGER PRIMARY KEY AUTOINCREMENT,\n" +
"\tI3 INTEGER,\n" +
"\tN3 INTEGER,\n" +
"\tProtocol TEXT,\n" +
"\tUTC TEXT,\n" +
"\tSNR INTEGER,\n" +
"\tTIME_SEC REAL,\n" +
"\tFREQ INTEGER,\n" +
"\tCALL_TO TEXT,\n" +
"\tCALL_FROM TEXT,\n" +
"\tEXTRAL TEXT,\n" +
"\tREPORT INTEGER,\n" +
"\tBAND INTEGER\n" +
")");
sqLiteDatabase.execSQL("CREATE INDEX SWLMessages_CALL_TO_IDX " +
"ON SWLMessages (CALL_TO,CALL_FROM)");
sqLiteDatabase.execSQL("CREATE INDEX SWLMessages_UTC_IDX ON SWLMessages (UTC)");
}
if (!checkTableExists(sqLiteDatabase, "SWLQSOTable")) {
sqLiteDatabase.execSQL("CREATE TABLE SWLQSOTable (\n" +
"\tid INTEGER PRIMARY KEY AUTOINCREMENT,\n" +
"\t\"call\" TEXT,\n" +
"\tgridsquare TEXT,\n" +
"\tmode TEXT,\n" +
"\trst_sent TEXT,\n" +
"\trst_rcvd TEXT,\n" +
"\tqso_date TEXT,\n" +
"\ttime_on TEXT,\n" +
"\tqso_date_off TEXT,\n" +
"\ttime_off TEXT,\n" +
"\tband TEXT,\n" +
"\tfreq TEXT,\n" +
"\tstation_callsign TEXT,\n" +
"\tmy_gridsquare TEXT,\n" +
"\tcomment TEXT)");
}
}
public void loadItuDataFromFile(SQLiteDatabase db) {
AssetManager assetManager = context.getAssets();
@ -424,7 +484,7 @@ public class DatabaseOpr extends SQLiteOpenHelper {
dxcc.prefix.add(prefix.getString(j));
}
dxccObjects.add(dxcc);
Log.e(TAG, "loadDataFromFile: id:" + dxcc.id + " dxcc:" + dxcc.dxcc);
//Log.e(TAG, "loadDataFromFile: id:" + dxcc.id + " dxcc:" + dxcc.dxcc);
}
inputStream.close();
@ -465,8 +525,8 @@ public class DatabaseOpr extends SQLiteOpenHelper {
new WriteConfig(db, KeyName, Value, onAfterWriteConfig).execute();
}
public void writeMessage(ArrayList<Ft8Message> messages, String callSign) {
new WriteMessages(db, messages, callSign).execute();
public void writeMessage(ArrayList<Ft8Message> messages) {
new WriteMessages(db, messages).execute();
}
/**
@ -478,10 +538,23 @@ public class DatabaseOpr extends SQLiteOpenHelper {
new GetFollowCallSigns(db, onAffterQueryFollowCallsigns).execute();
}
public void getMessageLogTotal(OnAfterQueryFollowCallsigns onAffterQueryFollowCallsigns) {
new GetMessageLogTotal(db, onAffterQueryFollowCallsigns).execute();
/**
* SWL MESSAGEBAND
* @param onAfterQueryFollowCallsigns
*/
public void getMessageLogTotal(OnAfterQueryFollowCallsigns onAfterQueryFollowCallsigns) {
new GetMessageLogTotal(db, onAfterQueryFollowCallsigns).execute();
}
/**
* SWL QSO
* @param onAfterQueryFollowCallsigns
*/
public void getSWLQsoLogTotal(OnAfterQueryFollowCallsigns onAfterQueryFollowCallsigns) {
new GetSWLQsoTotal(db, onAfterQueryFollowCallsigns).execute();
}
/**
*
*
@ -510,12 +583,24 @@ public class DatabaseOpr extends SQLiteOpenHelper {
new Thread(new Runnable() {
@Override
public void run() {
db.execSQL("delete from Messages ");
db.execSQL("delete from SWLMessages ");
}
}).start();
}
/**
* SWL QSO
*/
public void clearSWLQsoData() {
new Thread(new Runnable() {
@Override
public void run() {
db.execSQL("delete from SWLQSOTable ");
}
}).start();
}
/**
*
*
*
* @param qslRecord
*/
@ -523,6 +608,14 @@ public class DatabaseOpr extends SQLiteOpenHelper {
new AddQSL_Info(this, qslRecord).execute();
}
/**
* SWLQSOSWLQSO
* @param qslRecord
*/
public void addSWL_QSO(QSLRecord qslRecord) {
new Add_SWL_QSO_Info(this, qslRecord).execute();
}
//删除数据库中关注的呼号
public void deleteFollowCallsign(String callsign) {
new DeleteFollowCallsign(db, callsign).execute();
@ -547,8 +640,8 @@ public class DatabaseOpr extends SQLiteOpenHelper {
* @param callsign
* @param onQueryQSLCallsign
*/
public void getQSLCallsignsByCallsign(String callsign, int filter, OnQueryQSLCallsign onQueryQSLCallsign) {
new GetQLSCallsignByCallsign(db, callsign, filter, onQueryQSLCallsign).execute();
public void getQSLCallsignsByCallsign(boolean showAll,int offset,String callsign, int filter, OnQueryQSLCallsign onQueryQSLCallsign) {
new GetQLSCallsignByCallsign(showAll,offset,db, callsign, filter, onQueryQSLCallsign).execute();
}
/**
@ -567,8 +660,8 @@ public class DatabaseOpr extends SQLiteOpenHelper {
* @param callsign
* @param onQueryQSLRecordCallsign
*/
public void getQSLRecordByCallsign(String callsign, int filter, OnQueryQSLRecordCallsign onQueryQSLRecordCallsign) {
new GetQSLByCallsign(db, callsign, filter, onQueryQSLRecordCallsign).execute();
public void getQSLRecordByCallsign(boolean showAll,int offset,String callsign, int filter, OnQueryQSLRecordCallsign onQueryQSLRecordCallsign) {
new GetQSLByCallsign(showAll,offset,db, callsign, filter, onQueryQSLRecordCallsign).execute();
}
/**
@ -977,26 +1070,24 @@ public class DatabaseOpr extends SQLiteOpenHelper {
static class WriteMessages extends AsyncTask<Void, Void, Void> {
private final SQLiteDatabase db;
private ArrayList<Ft8Message> messages;
private String myCallsign;
public WriteMessages(SQLiteDatabase db, ArrayList<Ft8Message> messages, String callsign) {
public WriteMessages(SQLiteDatabase db, ArrayList<Ft8Message> messages) {
this.db = db;
this.messages = messages;
this.myCallsign = callsign;
}
@Override
protected Void doInBackground(Void... voids) {
String sql = "INSERT INTO Messages(I3,N3,Protocol,UTC,SNR,TIME_SEC,FREQ,CALL_FROM" +
",CALL_TO,EXTRAL,REPORT,BAND)" +
String sql = "INSERT INTO SWLMessages(I3,N3,Protocol,UTC,SNR,TIME_SEC,FREQ,CALL_FROM" +
",CALL_TO,EXTRAL,REPORT,BAND)\n" +
"VALUES(?,?,?,?,?,?,?,?,?,?,?,?)";
for (Ft8Message message : messages) {//只对与我有关的消息做保存
if (message.callsignTo.equals(myCallsign) || message.callsignFrom.equals(myCallsign)) {
db.execSQL(sql, new Object[]{message.i3, message.n3, "FT8", message.utcTime
, message.snr, message.time_sec, Math.round(message.freq_hz)
, message.callsignFrom, message.callsignTo, message.extraInfo
, message.report, message.band});
}
db.execSQL(sql, new Object[]{message.i3, message.n3, "FT8"
,UtcTimer.getDatetimeYYYYMMDD_HHMMSS(message.utcTime)
, message.snr, message.time_sec, Math.round(message.freq_hz)
, message.callsignFrom, message.callsignTo, message.extraInfo
, message.report, message.band});
}
return null;
}
@ -1045,6 +1136,43 @@ public class DatabaseOpr extends SQLiteOpenHelper {
}
}
static class Add_SWL_QSO_Info extends AsyncTask<Void, Void, Void>{
private final DatabaseOpr databaseOpr;
private QSLRecord qslRecord;
public Add_SWL_QSO_Info(DatabaseOpr opr, QSLRecord qslRecord) {
this.databaseOpr = opr;
this.qslRecord = qslRecord;
}
@SuppressLint("Range")
@Override
protected Void doInBackground(Void... voids) {
String querySQL;
querySQL = "INSERT INTO SWLQSOTable(call, gridsquare, mode, rst_sent, rst_rcvd, qso_date, " +
"time_on, qso_date_off, time_off, band, freq, station_callsign, my_gridsquare," +
"comment)VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
databaseOpr.db.execSQL(querySQL, new String[]{qslRecord.getToCallsign()
, qslRecord.getToMaidenGrid()
, qslRecord.getMode()
, String.valueOf(qslRecord.getSendReport())
, String.valueOf(qslRecord.getReceivedReport())
, qslRecord.getQso_date()
, qslRecord.getTime_on()
, qslRecord.getQso_date_off()
, qslRecord.getTime_off()
, qslRecord.getBandLength()//波长//RigOperationConstant.getMeterFromFreq(qslRecord.getBandFreq())
, BaseRigOperation.getFrequencyFloat(qslRecord.getBandFreq())
, qslRecord.getMyCallsign()
, qslRecord.getMyMaidenGrid()
, qslRecord.getComment()});
return null;
}
}
/**
* QSL
*/
@ -1128,7 +1256,7 @@ public class DatabaseOpr extends SQLiteOpenHelper {
@Override
@SuppressLint({"Range", "DefaultLocale"})
protected Void doInBackground(Void... voids) {
String querySQL = "SELECT BAND ,count(*) as c from Messages m group by BAND order by BAND ";
String querySQL = "SELECT BAND ,count(*) as c from SWLMessages m group by BAND order by BAND ";
Cursor cursor = db.rawQuery(querySQL, new String[]{});
ArrayList<String> callsigns = new ArrayList<>();
callsigns.add(GeneralVariables.getStringFromResource(R.string.band_total));
@ -1137,7 +1265,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));
@ -1150,6 +1278,43 @@ public class DatabaseOpr extends SQLiteOpenHelper {
}
static class GetSWLQsoTotal extends AsyncTask<Void, Void, Void> {
private final SQLiteDatabase db;
private final OnAfterQueryFollowCallsigns onAffterQueryFollowCallsigns;
public GetSWLQsoTotal(SQLiteDatabase db, OnAfterQueryFollowCallsigns onAffterQueryFollowCallsigns) {
this.db = db;
this.onAffterQueryFollowCallsigns = onAffterQueryFollowCallsigns;
}
@Override
@SuppressLint({"Range", "DefaultLocale"})
protected Void doInBackground(Void... voids) {
String querySQL = "select count(*) as c,substr(qso_date_off,1,6) as t \n" +
"from SWLQSOTable s\n" +
"group by substr(qso_date_off,1,6)";
Cursor cursor = db.rawQuery(querySQL, new String[]{});
ArrayList<String> callsigns = new ArrayList<>();
//callsigns.add(GeneralVariables.getStringFromResource(R.string.band_total));
callsigns.add("---------------------------------------");
int sum = 0;
while (cursor.moveToNext()) {
String date = cursor.getString(cursor.getColumnIndex("t")); //获取频段
int total = cursor.getInt(cursor.getColumnIndex("c")); //获取数量
callsigns.add(String.format("%s \t %d ", date, total));
sum = sum + total;
}
callsigns.add(String.format("-----------Total %d -----------", sum));
cursor.close();
if (onAffterQueryFollowCallsigns != null) {
onAffterQueryFollowCallsigns.doOnAfterQueryFollowCallsigns(callsigns);
}
return null;
}
}
/**
*
*/
@ -1248,12 +1413,16 @@ public class DatabaseOpr extends SQLiteOpenHelper {
}
static class GetQSLByCallsign extends AsyncTask<Void, Void, Void> {
boolean showAll;
int offset;
SQLiteDatabase db;
String callsign;
int filter;
OnQueryQSLRecordCallsign onQueryQSLRecordCallsign;
public GetQSLByCallsign(SQLiteDatabase db, String callsign, int queryFilter, OnQueryQSLRecordCallsign onQueryQSLRecordCallsign) {
public GetQSLByCallsign(boolean showAll,int offset,SQLiteDatabase db, String callsign, int queryFilter, OnQueryQSLRecordCallsign onQueryQSLRecordCallsign) {
this.showAll=showAll;
this.offset=offset;
this.db = db;
this.callsign = callsign;
this.filter = queryFilter;
@ -1274,9 +1443,14 @@ public class DatabaseOpr extends SQLiteOpenHelper {
default:
filterStr = "";
}
String limitStr="";
if (!showAll){
limitStr="limit 100 offset "+offset;
}
String querySQL = "select * from QSLTable where ([call] like ?) \n" +
filterStr +
" order by ID desc";
" order by ID desc\n"+
limitStr;
Cursor cursor = db.rawQuery(querySQL, new String[]{"%" + callsign + "%"});
ArrayList<QSLRecordStr> records = new ArrayList<>();
while (cursor.moveToNext()) {
@ -1320,8 +1494,12 @@ public class DatabaseOpr extends SQLiteOpenHelper {
String callsign;
int filter;
OnQueryQSLCallsign onQueryQSLCallsign;
int offset;
boolean showAll;
public GetQLSCallsignByCallsign(SQLiteDatabase db, String callsign, int queryFilter, OnQueryQSLCallsign onQueryQSLCallsign) {
public GetQLSCallsignByCallsign(boolean showAll,int offset,SQLiteDatabase db, String callsign, int queryFilter, OnQueryQSLCallsign onQueryQSLCallsign) {
this.showAll=showAll;
this.offset=offset;
this.db = db;
this.callsign = callsign;
this.filter = queryFilter;
@ -1342,6 +1520,10 @@ public class DatabaseOpr extends SQLiteOpenHelper {
default:
filterStr = "";
}
String limitStr="";
if (!showAll){
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.qso_date as last_time ,q.mode ,q.isQSL,q.isLotW_QSL\n" +
@ -1351,7 +1533,8 @@ public class DatabaseOpr extends SQLiteOpenHelper {
"group by q.[call] ,q.gridsquare,q.freq ,q.qso_date,q.band\n" +
",q.mode,q.isQSL,q.isLotW_QSL\n" +
"HAVING q.qso_date =MAX(q2.qso_date) \n" +
"order by q.qso_date desc";
"order by q.qso_date desc\n"+
limitStr;
Cursor cursor = db.rawQuery(querySQL, new String[]{"%" + callsign + "%"});
@ -1515,9 +1698,6 @@ public class DatabaseOpr extends SQLiteOpenHelper {
}
}
/**
*
*/
static class GetAllConfigParameter extends AsyncTask<Void, Void, Void> {
private final SQLiteDatabase db;
private OnAfterQueryConfig onAfterQueryConfig;
@ -1539,9 +1719,131 @@ public class DatabaseOpr extends SQLiteOpenHelper {
return result;
}
@SuppressLint("Range")
@Override
protected Void doInBackground(Void... voids) {
//TODO 此处代码效率较低已经在0.87中解决。
String querySQL = "select keyName,Value from config ";
Cursor cursor = db.rawQuery(querySQL, null);
while (cursor.moveToNext()) {
@SuppressLint("Range")
//String result = "";
String result = cursor.getString(cursor.getColumnIndex("Value"));
String name = cursor.getString(cursor.getColumnIndex("KeyName"));
if (name.equalsIgnoreCase("grid")) {
GeneralVariables.setMyMaidenheadGrid(result);
}
if (name.equalsIgnoreCase("callsign")) {
GeneralVariables.myCallsign = result;
String callsign = GeneralVariables.myCallsign;
if (callsign.length() > 0) {
Ft8Message.hashList.addHash(FT8Package.getHash22(callsign), callsign);
Ft8Message.hashList.addHash(FT8Package.getHash12(callsign), callsign);
Ft8Message.hashList.addHash(FT8Package.getHash10(callsign), callsign);
}
}
if (name.equalsIgnoreCase("toModifier")) {
GeneralVariables.toModifier = result;
}
if (name.equalsIgnoreCase("freq")) {
float freq = 1000;
try {
freq = Float.parseFloat(result);
} catch (Exception e) {
Log.e(TAG, "doInBackground: " + e.getMessage());
}
//GeneralVariables.setBaseFrequency(result.equals("") ? 1000 : Float.parseFloat(result));
GeneralVariables.setBaseFrequency(freq);
}
if (name.equalsIgnoreCase("synFreq")) {
GeneralVariables.synFrequency = !(result.equals("") || result.equals("0"));
}
if (name.equalsIgnoreCase("transDelay")) {
if (result.matches("^\\d{1,4}$")) {//正则表达式1-4位长度的数字
GeneralVariables.transmitDelay = Integer.parseInt(result);
} else {
GeneralVariables.transmitDelay = FT8Common.FT8_TRANSMIT_DELAY;
}
}
if (name.equalsIgnoreCase("civ")) {
GeneralVariables.civAddress = result.equals("") ? 0xa4 : Integer.parseInt(result, 16);
}
if (name.equalsIgnoreCase("baudRate")) {
GeneralVariables.baudRate = result.equals("") ? 19200 : Integer.parseInt(result);
}
if (name.equalsIgnoreCase("bandFreq")) {
//--todo---把波段的索引数改成频率。用bandFreq值
GeneralVariables.band = result.equals("") ? 14074000 : Long.valueOf(result);
GeneralVariables.bandListIndex = OperationBand.getIndexByFreq(GeneralVariables.band);
}
if (name.equalsIgnoreCase("ctrMode")) {
GeneralVariables.controlMode = result.equals("") ? ControlMode.VOX : Integer.parseInt(result);
}
if (name.equalsIgnoreCase("model")) {//电台型号
GeneralVariables.modelNo = result.equals("") ? 0 : Integer.parseInt(result);
}
if (name.equalsIgnoreCase("instruction")) {//指令集
GeneralVariables.instructionSet = result.equals("") ? 0 : Integer.parseInt(result);
}
if (name.equalsIgnoreCase("launchSupervision")) {//发射监管
GeneralVariables.launchSupervision = result.equals("") ?
GeneralVariables.DEFAULT_LAUNCH_SUPERVISION : Integer.parseInt(result);
}
if (name.equalsIgnoreCase("noReplyLimit")) {//
GeneralVariables.noReplyLimit = result.equals("") ? 0 : Integer.parseInt(result);
}
if (name.equalsIgnoreCase("autoFollowCQ")) {//自动关注CQ
GeneralVariables.autoFollowCQ = (result.equals("") || result.equals("1"));
}
if (name.equalsIgnoreCase("autoCallFollow")) {//自动呼叫关注
GeneralVariables.autoCallFollow = (result.equals("") || result.equals("1"));
}
if (name.equalsIgnoreCase("pttDelay")) {//ptt延时设置
GeneralVariables.pttDelay = result.equals("") ? 100 : Integer.parseInt(result);
}
if (name.equalsIgnoreCase("icomIp")) {//IcomIp地址
GeneralVariables.icomIp = result.equals("") ? "255.255.255.255" : result;
}
if (name.equalsIgnoreCase("icomPort")) {//Icom端口
GeneralVariables.icomUdpPort = result.equals("") ? 50001 : Integer.parseInt(result);
}
if (name.equalsIgnoreCase("icomUserName")) {//Icom用户名
GeneralVariables.icomUserName = result.equals("") ? "ic705" : result;
}
if (name.equalsIgnoreCase("icomPassword")) {//Icom密码
GeneralVariables.icomPassword = result;
}
if (name.equalsIgnoreCase("volumeValue")) {//输出音量大小
GeneralVariables.volumePercent = result.equals("") ? 1.0f : Float.parseFloat(result) / 100f;
}
if (name.equalsIgnoreCase("excludedCallsigns")) {//排除的呼号
GeneralVariables.addExcludedCallsigns(result);
}
if (name.equalsIgnoreCase("flexMaxRfPower")) {//指令集
GeneralVariables.flexMaxRfPower = result.equals("") ? 10 : Integer.parseInt(result);
}
if (name.equalsIgnoreCase("flexMaxTunePower")) {//指令集
GeneralVariables.flexMaxTunePower = result.equals("") ? 10 : Integer.parseInt(result);
}
if (name.equalsIgnoreCase("saveSWL")) {//保存解码信息
GeneralVariables.saveSWLMessage = result.equals("1");
}
if (name.equalsIgnoreCase("saveSWLQSO")) {//保存解码信息
GeneralVariables.saveSWL_QSO = result.equals("1");
}
}
cursor.close();
GetAllQSLCallsign.get(db);//获取通联过的呼号
if (onAfterQueryConfig != null) {
onAfterQueryConfig.doOnAfterQueryConfig(null, null);
}
/*
String result = "";
GeneralVariables.setMyMaidenheadGrid(getConfigByKey("grid"));
@ -1626,7 +1928,7 @@ public class DatabaseOpr extends SQLiteOpenHelper {
GeneralVariables.icomPassword = result;
//输出音量大小
result = getConfigByKey("volumeValue");//IcomIp地址
result = getConfigByKey("volumeValue");
GeneralVariables.volumePercent = result.equals("") ? 1.0f : Float.parseFloat(result) / 100f;
//排除的呼号
@ -1639,6 +1941,8 @@ public class DatabaseOpr extends SQLiteOpenHelper {
onAfterQueryConfig.doOnAfterQueryConfig(null, null);
}
*/
return null;
}
}

Wyświetl plik

@ -1,4 +1,10 @@
package com.bg7yoz.ft8cn.database;
/**
* DXCC3dxccListdxcc_griddxcc_prefixDXCC
*
* @author BGY70Z
* @date 2023-03-20
*/
import android.database.sqlite.SQLiteDatabase;

Wyświetl plik

@ -1,5 +1,10 @@
package com.bg7yoz.ft8cn.database;
/**
*
* @author BGY70Z
* @date 2023-03-20
*/
public interface OnAfterQueryConfig {
void doOnBeforeQueryConfig(String KeyName);
void doOnAfterQueryConfig(String KeyName,String Value);

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.database;
/**
*
* @author BGY70Z
* @date 2023-03-20
*/
import java.util.ArrayList;

Wyświetl plik

@ -1,5 +1,10 @@
package com.bg7yoz.ft8cn.database;
/**
*
* @author BGY70Z
* @date 2023-03-20
*/
public interface OnAfterWriteConfig {
void doOnAfterWriteConfig(boolean writeDone);
}

Wyświetl plik

@ -1,5 +1,10 @@
package com.bg7yoz.ft8cn.database;
/**
*
* @author BGY70Z
* @date 2023-03-20
*/
public interface OnGetCallsign {
void doOnAfterGetCallSign(boolean exists);
}

Wyświetl plik

@ -13,6 +13,8 @@ import java.util.ArrayList;
/**
* assets/bands.txt
* @author BGY70Z
* @date 2023-03-20
*/
public class OperationBand {

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.database;
/**
* rigaddress.txt
* @author BGY70Z
* @date 2023-03-20
*/
import android.content.Context;
import android.content.res.AssetManager;

Wyświetl plik

@ -1,5 +1,10 @@
package com.bg7yoz.ft8cn.flex;
/**
* Flex
* @author BGY70Z
* @date 2023-03-20
*/
public enum FlexCommand {
UNKNOW,
CLIENT_GUI,
@ -53,5 +58,9 @@ public enum FlexCommand {
SUB_DAX_ALL,
DISPLAY_PAN,
TRANSMIT_POWER
TRANSMIT_POWER,
TRANSMIT_MAX_POWER,
AUT_TUNE_MAX_POWER,
ATU,
PTT_ON
}

Wyświetl plik

@ -0,0 +1,149 @@
package com.bg7yoz.ft8cn.flex;
import java.util.HashMap;
/**
* MeterIDFlexMeterIDHash
* @author BGY70Z
* @date 2023-03-20
*/
public class FlexMeterInfos extends HashMap<Integer, FlexMeterInfos.FlexMeterInfo> {
private static final String TAG = "FlexMeters";
public int sMeterId = -1;
public int tempCId = -1;
public int swrId = -1;
public int pwrId = -1;
public int alcId = -1;
public FlexMeterInfos(String content) {
setMeterInfos(content);
}
/**
* METERIDmeter
*
* @param content
*/
public synchronized void setMeterInfos(String content) {
String[] temp;
if (content.length() == 0) return;
temp = content.substring(content.indexOf("meter ") + "meter ".length()).split("#");
for (int i = 0; i < temp.length; i++) {
String[] val = temp[i].split("=");
if (val.length == 2) {
if (val[0].contains(".")) {
int index = Integer.parseInt(val[0].substring(0, val[0].indexOf(".")));
FlexMeterInfo meterInfo;
meterInfo = this.get(index);
if (meterInfo == null) {
meterInfo = new FlexMeterInfo();
this.put(index, meterInfo);
}
if (val[0].toLowerCase().contains(".src")) {
meterInfo.src = val[1];
}
if (val[0].toLowerCase().contains(".num")) {
meterInfo.num = val[1];
}
if (val[0].toLowerCase().contains(".nam")) {
meterInfo.nam = val[1];
//为了方便MeterList快速查询
if (val[1].toUpperCase().contains("LEVEL")) {
sMeterId = index;
} else if (val[1].toUpperCase().contains("PATEMP")) {
tempCId = index;
} else if (val[1].toUpperCase().contains("SWR")) {
swrId = index;
} else if (val[1].toUpperCase().contains("FWDPWR")) {
pwrId = index;
} else if (val[1].toUpperCase().contains("ALC")) {
alcId = index;
}
}
if (val[0].toLowerCase().contains(".low")) {
meterInfo.low = Float.parseFloat(val[1]);
}
if (val[0].toLowerCase().contains(".hi")) {
meterInfo.hi = Float.parseFloat(val[1]);
}
if (val[0].toLowerCase().contains(".desc")) {
meterInfo.desc = val[1];
}
if (val[0].toLowerCase().contains(".unit")) {
String s = val[1].toUpperCase();
if (s.contains("DB")) {
meterInfo.unit = FlexMeterType.dBm;
} else if (s.contains("SWR")) {
meterInfo.unit = FlexMeterType.swr;
} else if (s.contains("DEG")) {
meterInfo.unit = FlexMeterType.Temperature;
} else if (s.contains("VOLT")) {
meterInfo.unit = FlexMeterType.volt;
} else {
meterInfo.unit = FlexMeterType.other;
}
}
if (val[0].toLowerCase().contains(".fps")) {
meterInfo.fps = val[1];
}
if (val[0].toLowerCase().contains(".peak")) {
meterInfo.peak = val[1];
}
}
}
}
// sMeterId=getMeterId("LEVEL");
// tempCId=getMeterId("PATEMP");
// swrId=getMeterId("SWR");
// pwrId=getMeterId("FWDPWR");
// alcId=getMeterId("ALC");
}
/**
* Meterid,-1
*
* @return id
*/
private int getMeterId(String s) {
for (int key : this.keySet()) {
if (get(key).nam.equalsIgnoreCase(s)) {
return key;
}
}
return -1;
}
public static class FlexMeterInfo {
public String src;
public String num;
public String nam;
public float low;
public float hi;
public String desc;
public FlexMeterType unit = FlexMeterType.other;
public String fps;
public String peak;
@Override
public String toString() {
return "{" +
"src='" + src + '\'' +
", num='" + num + '\'' +
", nam='" + nam + '\'' +
", low='" + low + '\'' +
", hi='" + hi + '\'' +
", desc='" + desc + '\'' +
", unit='" + unit + '\'' +
", fps='" + fps + '\'' +
", peak='" + peak + '\'' +
'}';
}
}
}

Wyświetl plik

@ -0,0 +1,112 @@
package com.bg7yoz.ft8cn.flex;
/**
* MeterMeter232ID+VALUE
* @author BGY70Z
* @date 2023-03-20
*/
import android.annotation.SuppressLint;
import java.util.HashMap;
public class FlexMeterList extends HashMap<Integer, FlexMeterList.FlexMeter> {
public float sMeterVal=-150;//-150~10
public float tempCVal=0;//0~100
public float alcVal=-150;//-150~20
public float swrVal=1;//1~999
public float pwrVal=0;//0~100W
public synchronized void setMeters(byte[] data, FlexMeterInfos infos) {
for (int i = 0; i < data.length / 4; i++) {
int val = readShortData(data, i * 4);
FlexMeter meter = get(val);
if (meter == null) {
if (infos.get(val) == null) continue;
meter = new FlexMeter();
meter.name = infos.get(val).nam;
meter.desc = infos.get(val).desc;
meter.type = infos.get(val).unit;
meter.id = val;
}
switch (meter.type) {
case dBm:
case swr:
meter.value = readShortData(data, i * 4 + 2) / 128f;
if (meter.name.contains("PWR")){//把dBm转换成功率值
meter.value=(float) Math.pow(10,meter.value/10f)/1000f;
}
//节省资源,提前赋值
if (meter.id==infos.sMeterId) sMeterVal=meter.value;
if (meter.id==infos.swrId) swrVal=meter.value;
if (meter.id==infos.pwrId) pwrVal=meter.value;
if (meter.id==infos.alcId) alcVal=meter.value;
break;
case volt:
meter.value = readShortData(data, i * 4 + 2) / 256f;
break;
case Temperature:
meter.value = readShortData(data, i * 4 + 2) / 64f;
//节省资源,提前赋值
if (meter.id==infos.tempCId) tempCVal=meter.value;
break;
case other:
default:
meter.value = readShortData(data, i * 4 + 2);
}
put(val, meter);
}
}
public synchronized String getMeters(){
StringBuilder temp=new StringBuilder();
int i=0;
for (int key:this.keySet()) {
i++;
temp.append(String.format("%-35s",get(key).toString()));
if (i%2==0){
temp.append("\n");
}
}
return temp.toString();
}
/**
* short
*
* @param data
* @return short
*/
public static short readShortData(byte[] data, int start) {
if (data.length - start < 2) return 0;
return (short) ((short) data[start + 1] & 0xff
| ((short) data[start] & 0xff) << 8);
}
public static float readShortFloat(byte[] data, int start) {
if (data.length - start < 2) return 0.0f;
int accum = 0;
accum = accum | (data[start] & 0xff) << 0;
accum = accum | (data[start + 1] & 0xff) << 8;
return Float.intBitsToFloat(accum);
}
public static class FlexMeter {
public int id;
public float value;
public String name;
public String desc;
public FlexMeterType type=FlexMeterType.other;
@SuppressLint("DefaultLocale")
@Override
public String toString() {
return String.format("%02d.%s : %.1f",id,name,value);
}
}
}

Wyświetl plik

@ -0,0 +1,10 @@
package com.bg7yoz.ft8cn.flex;
/**
* meter
* @author BGY70Z
* @date 2023-03-20
*/
public enum FlexMeterType {
dBm,swr,Temperature,volt,other
}

Wyświetl plik

@ -1,7 +1,8 @@
package com.bg7yoz.ft8cn.flex;
/**
* @AUTHOR BG7YOZ
* Flex使TCP使UDP
* @author BGY70Z
* @date 2023-03-20
*/
import android.annotation.SuppressLint;
@ -21,17 +22,22 @@ import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.HashSet;
public class FlexRadio {
public enum FlexMode { LSB, USB, AM, CW, DIGL, DIGU, SAM, FM, NFM, DFM, RTTY, RAW, ARQ,UNKNOW }
public enum AntMode { ANT1, ANT2, RX_A, XVTA,UNKNOW }
public enum FlexMode {LSB, USB, AM, CW, DIGL, DIGU, SAM, FM, NFM, DFM, RTTY, RAW, ARQ, UNKNOW}
public enum AntMode {ANT1, ANT2, RX_A, XVTA, UNKNOW}
private static final String TAG = "FlexRadio";
private static int streamPort = 7051;
private int flexStreamPort = 4993;
public boolean isPttOn = false;
public long streamTxId = 0x084000000;
public static int getStreamPort() {//获取用于流传输的UDP端口防止重复采用自增方式
return ++streamPort;
@ -88,6 +94,7 @@ public class FlexRadio {
private boolean allFlexRadioStatusEvent = false;
private String clientID = "";
private long daxAudioStreamId = 0;
private long daxTxAudioStreamId = 0;
private long panadapterStreamId = 0;
private final HashSet<Long> streamIdSet = new HashSet<>();
@ -361,14 +368,16 @@ public class FlexRadio {
RadioUdpClient.OnUdpEvents onUdpEvents = new RadioUdpClient.OnUdpEvents() {
@Override
public void OnReceiveData(DatagramSocket socket, DatagramPacket packet, byte[] data) {
if (flexStreamPort != packet.getPort()) flexStreamPort = packet.getPort();
VITA vita = new VITA(data);
addStreamIdToSet(vita.streamId);
//Log.e(TAG, String.format("OnReceiveData: stream id:0x%x,class id:0x%x",vita.streamId,vita.classId) );
switch (vita.classId) {
case VITA.FLEX_DAX_AUDIO_CLASS_ID://音频数据
//Log.e(TAG, String.format("FLEX_DAX_AUDIO_CLASS_ID stream id:0x%x",vita.streamId ));
doReceiveAudio(vita.payload);
//Log.e(TAG, "OnReceiveData: audio:"+vita.payload.length );
break;
case VITA.FLEX_DAX_IQ_CLASS_ID://IQ数据
doReceiveIQ(vita.payload);
@ -378,6 +387,7 @@ public class FlexRadio {
//Log.e(TAG, String.format("OnReceiveData: FFT:%d,STREAM ID:0x%x",vita.payload.length,vita.streamId));
break;
case VITA.FLEX_METER_CLASS_ID://仪表数据
//Log.e(TAG, String.format("FLEX_METER_CLASS_ID: stream id:0x%x",vita.streamId ));
doReceiveMeter(vita);
//Log.e(TAG, String.format("OnReceiveData: METER class id:0x%x,stream id:0x%x,length:%d\n%s"
// ,vita.classId,vita.streamId,vita.payload.length,vita.showPayload() ));
@ -428,6 +438,106 @@ public class FlexRadio {
}
}
/**
* flexRadio1200024000
* @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];
}
//port=4991;
//streamTxId=0x084000001;
Log.e(TAG, String.format("sendWaveData: streamid:0x%x,ip:%s,port:%d",streamTxId,ip, port) );
new Thread(new Runnable() {
@Override
public void run() {
VITA vita = new VITA();
int count = 0;
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++) {
voice[i] = temp[count];
count++;
if (count > temp.length) break;
}
byte[] send = vita.audioDataToVita(packetCount, streamTxId, voice);
packetCount++;
try {
streamClient.sendData(send, ip, port);
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
if (count>temp.length) break;
}
while (isPttOn) {
if (System.currentTimeMillis() - now >= 41) {//40毫秒一个周期,每个周期3个包每个包64个float。
break;
}
}
if (!isPttOn){
Log.e(TAG, String.format("count%d,temp.length:%d",count,temp.length ));
}
}
// for (int i = 0; i < (temp.length / (24 * 2 * 40)); i++) {//40毫秒的数据量
// if (!isPttOn) return;
// long now = System.currentTimeMillis() - 1;//获取当前时间
//
// float[] voice = new float[24 * 2 * 10];
// for (int j = 0; j < 24 * 2 *10; j++) {
// voice[j] = temp[i * 24 * 2 * 10 + j];
// }
// //Log.e(TAG, "sendWaveData: "+floatToStr(voice) );
// //streamTxId=0x84000001;
// byte[] send = vita.audioDataToVita(count, streamTxId, voice);
// count++;
//
// try {
// streamClient.sendData(send, ip, port);
// } catch (UnknownHostException e) {
// throw new RuntimeException(e);
// }
//
// while (isPttOn) {
// if (System.currentTimeMillis() - now >= 41) {//40毫秒一个周期,每个周期3个包每个包64个float。
// break;
// }
// }
// }
}
}).start();
//设置发送音频包
//streamClient.sendData();
}
public static String byteToStr(byte[] data) {
StringBuilder s = new StringBuilder();
for (int i = 0; i < data.length; i++) {
s.append(String.format("%02x ", data[i] & 0xff));
}
return s.toString();
}
public static String floatToStr(float[] data) {
StringBuilder s = new StringBuilder();
for (int i = 0; i < data.length; i++) {
s.append(String.format("%f ", data[i]));
}
return s.toString();
}
/**
*
*
@ -524,6 +634,11 @@ public class FlexRadio {
if (response.panadapterStreamId != 0) {
this.panadapterStreamId = response.panadapterStreamId;
}
if (response.daxTxStreamId != 0) {
this.daxTxAudioStreamId = response.daxTxStreamId;
Log.e(TAG, String.format("doReceiveLineEvent: txStreamID:0x%x", daxTxAudioStreamId));
}
break;
}
@ -697,6 +812,13 @@ public class FlexRadio {
sendCommand(FlexCommand.STREAM_CREATE_DAX_RX, String.format("stream create type=dax_rx dax_channel=%d", channel));
}
@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=remote_audio_tx"));
}
public synchronized void commandRemoveDaxStream() {
sendCommand(FlexCommand.STREAM_REMOVE, String.format("stream remove 0x%x", getDaxAudioStreamId()));
}
@ -712,6 +834,11 @@ public class FlexRadio {
sendCommand(FlexCommand.FILT_SET, String.format("filt %d %d %d", sliceOrder, filt_low, filt_high));
}
@SuppressLint("DefaultLocale")
public synchronized void commandStartATU() {
sendCommand(FlexCommand.FILT_SET, "atu start");
}
public synchronized void commandGetInfo() {
sendCommand(FlexCommand.INFO, "info");
}
@ -780,8 +907,32 @@ public class FlexRadio {
sendCommand(FlexCommand.SUB_DAX_ALL, "sub dax all");
}
public synchronized void commandSetRfPower(int power){
sendCommand(FlexCommand.TRANSMIT_POWER,String.format("transmit set rfpower=%d",power));
@SuppressLint("DefaultLocale")
public synchronized void commandSetRfPower(int power) {
sendCommand(FlexCommand.TRANSMIT_MAX_POWER, String.format("transmit set max_power_level=%d", power));
sendCommand(FlexCommand.TRANSMIT_POWER, String.format("transmit set rfpower=%d", power));
//sendCommand(FlexCommand.TRANSMIT_MAX_POWER,"info");
}
@SuppressLint("DefaultLocale")
public synchronized void commandSetTunePower(int power) {
sendCommand(FlexCommand.AUT_TUNE_MAX_POWER, String.format("transmit set tunepower=%d", power));
}
public synchronized void commandPTTOnOff(boolean on) {
if (on) {
sendCommand(FlexCommand.PTT_ON, "xmit 1");
} else {
sendCommand(FlexCommand.PTT_ON, "xmit 0");
}
}
public synchronized void commandTuneTransmitOnOff(boolean on) {
if (on) {
sendCommand(FlexCommand.PTT_ON, "transmit tune on");
} else {
sendCommand(FlexCommand.PTT_ON, "transmit tune off");
}
}
@ -864,6 +1015,7 @@ public class FlexRadio {
public String version;//版本信息
public int message_num;//消息号32位16进制。其中位24-25包含消息的严重性0=信息1=警告2=错误3=致命错误)
public long daxStreamId = 0;
public long daxTxStreamId = 0;
public long panadapterStreamId = 0;
public FlexCommand flexCommand = FlexCommand.UNKNOW;
public long resultValue = 0;
@ -901,6 +1053,9 @@ public class FlexRadio {
case PANADAPTER_CREATE:
this.panadapterStreamId = getStreamId(line);
break;
case STREAM_CREATE_DAX_TX:
this.daxTxStreamId = getStreamId(line);
break;
}
resultValue = Integer.parseInt(content, 16);//取命令的返回值
@ -962,12 +1117,12 @@ public class FlexRadio {
}
}
private int getStreamId(String line) {
private long getStreamId(String line) {
String[] lines = line.split("\\|");
if (lines.length > 2) {
if (lines[1].equals("0")) {
try {
return Integer.parseInt(lines[2], 16);//stream id16进制
return Long.parseLong(lines[2], 16);//stream id16进制
} catch (NumberFormatException e) {
e.printStackTrace();
Log.e(TAG, "getDaxStreamId exception: " + e.getMessage());
@ -994,10 +1149,10 @@ public class FlexRadio {
}
if (temp.length>2){
exContent=temp[2];
}else {
exContent="";
if (temp.length > 2) {
exContent = temp[2];
} else {
exContent = "";
}
}

Wyświetl plik

@ -1,9 +1,5 @@
package com.bg7yoz.ft8cn.flex;
/**
* @author BG7YOZ
*/
import android.util.Log;
import java.net.DatagramPacket;
@ -31,6 +27,8 @@ enum VitaTokens {
* RadioFactory: Radio Factory线FlexRadioflexRadios
*
* Upd4992广vitaflexRadios
* @author BGY70Z
* @date 2023-03-20
*/
public class FlexRadioFactory {
private static final String TAG="FlexRadioFactory";

Wyświetl plik

@ -1,5 +1,10 @@
package com.bg7yoz.ft8cn.flex;
/**
* Flex
* @author BGY70Z
* @date 2023-03-20
*/
public enum FlexResponseStyle {
STATUS,//状态信息S+HANDLE
RESPONSE,//命令的响应R+客户端命令序列号

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.flex;
/**
* TcpFlex
* @author BGY70Z
* @date 2023-03-20
*/
import android.util.Log;

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.flex;
/**
* udp
* @author BGY70Z
* @date 2023-03-20
*/
import android.util.Log;
@ -29,18 +34,33 @@ public class RadioUdpClient {
this.port = port;
}
public synchronized void sendData(byte[] data, String ip) throws UnknownHostException {
public synchronized void sendData(byte[] data, String ip,int port) throws UnknownHostException {
if (!activated) return;
//Log.e(TAG, "sendData: "+byteToStr(data) );
//Log.e(TAG, String.format("sendData: ip: %s,port:%d ",ip,port) );
InetAddress address = InetAddress.getByName(ip);
sendDataRunnable.data=data;
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{
byte[] data;
InetAddress address;
int port;
RadioUdpClient client;
public SendDataRunnable(RadioUdpClient client) {
@ -49,7 +69,7 @@ public class RadioUdpClient {
@Override
public void run() {
DatagramPacket packet = new DatagramPacket(data, data.length, address, client.port);
DatagramPacket packet = new DatagramPacket(data, data.length, address,port);
try {
client.sendSocket.send(packet);
} catch (IOException e) {
@ -84,6 +104,31 @@ public class RadioUdpClient {
private void receiveData() {
receiveThreadPool.execute(receiveRunnable);
// new Thread(new Runnable() {
// @Override
// public void run() {
// while (activated) {
// byte[] data = new byte[MAX_BUFFER_SIZE];
// DatagramPacket packet = new DatagramPacket(data, data.length);
// try {
// sendSocket.receive(packet);
// if (onUdpEvents != null) {
// byte[] temp = Arrays.copyOf(packet.getData(), packet.getLength());
// onUdpEvents.OnReceiveData(sendSocket, packet, temp);
// }
// //Log.d(TAG, "receiveData:host ip: " + packet.getAddress().getHostName());
// } catch (IOException e) {
// e.printStackTrace();
// Log.e(TAG, "receiveData: error:" + e.getMessage());
// }
//
// }
// Log.e(TAG, "udpClient: is exit!");
// sendSocket.close();
// sendSocket = null;
// }
// }).start();
}
private static class ReceiveRunnable implements Runnable{
RadioUdpClient client;

Wyświetl plik

@ -1,14 +1,25 @@
package com.bg7yoz.ft8cn.flex;
/**
* VITA
* @author BG7YOZ
* VITA49
* @author BGY70Z
* @date 2023-03-20
*/
/*
public static intVH_PKT_TYPE(x) ((x & 0xF0000000) >> 28)
public static intVH_C(x) ((x & 0x08000000) >> 26)
public static intVH_T(x) ((x & 0x04000000) >> 25)
public static intVH_TSI(x) ((x & 0x00c00000) >> 21)
public static intVH_TSF(x) ((x & 0x00300000) >> 19)
public static intVH_PKT_CNT(x) ((x & 0x000f0000) >> 16)
public static intVH_PKT_SIZE(x) (x & 0x0000ffff)
*/
// Enumerates for field values
import android.annotation.SuppressLint;
import android.util.Log;
import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;
import java.util.Date;
@ -23,9 +34,7 @@ enum VitaPacketType {
//时间戳的类型
//时间戳共有两部分小数部分和整数部分整数部分以秒为分辨率32位 主要传递UTC时间或者 GPS 时间,
//小数部分主要有三种一种是sample-count 以采样周期为最小分辨率一种是real-time以ps为最小单位
// 第三种是以任意选择的时间进行累加得出的,前面两种时间戳可以直接与整数部分叠加,
// 第三种则不能保证与整数部分保持恒定关系,前两种与整数部分叠加来操作的可以在覆盖的时间范围为年
//小数部分主要有三种一种是sample-count 以采样周期为最小分辨率一种是real-time以ps为最小单位第三种是以任意选择的时间进行累加得出的前面两种时间戳可以直接与整数部分叠加第三种则不能保证与整数部分保持恒定关系前两种与整数部分叠加来操作的可以在覆盖的时间范围为年
//小数部分的时间戳共有64位小数部分可以在没有整数部分的情况下使用
//所有的时间带来都是在以一个采样数据为该reference-point 时间
enum VitaTSI {
@ -34,6 +43,7 @@ enum VitaTSI {
TSI_GPS,//GPS time
TSI_OTHER//Other
};
//时间戳小数部分类型
//小数部分主要有三种:
// 一种是sample-count ,以采样周期为最小分辨率,
@ -51,7 +61,7 @@ enum VitaTSF {
};
public class VITA {
private static final String TAG="VITA";
private static final String TAG = "VITA";
// 最小有效的VITA包长度
private static final int VITAmin = 28;
@ -97,7 +107,7 @@ public class VITA {
public int classId;//FLEX应该是0x534CFFF是informationClassCode与packetClassCode合并的
public byte[] payload = null;
public long trailer;
public boolean isAvailable=false;//电台对象是否有效。
public boolean isAvailable = false;//电台对象是否有效。
public boolean streamIdPresent;//是否有流字符
@ -113,113 +123,248 @@ public class VITA {
*/
/**
* VITAidcreate stream
*
* @param id streamId
* @param data
* @return vita
*/
public byte[] audioDataToVita(int count,long id, float[] data) {
byte[] result = new byte[data.length*4 + 28];//一个float占用4个字节28字节是包头的长度7个word
//packetType = VitaPacketType.EXT_DATA_WITH_STREAM;
packetType = VitaPacketType.IF_DATA_WITH_STREAM;
classIdPresent = true;
trailerPresent = false;//没有尾巴
tsi = VitaTSI.TSI_NONE;//
// tsi = VitaTSI.TSI_OTHER;//
//tsf = VitaTSF.TSF_SAMPLE_COUNT;//--TODO---查一下这个数字是不是变化
tsf = VitaTSF.TSF_NONE;//--TODO---查一下这个数字是不是变化
//packetCount动态变化
//packetCount=?应该是这个全部音频流的总包数
//packetSize是以word32位4字节为单位
//packetSize值为263居多估计以音频还有其它的长度,263是包含7个word28字节的头长度。
packetSize = (data.length ) + 7;//7个word是VITA的包头
//----以上是Header,32位第一个word-------
streamId = id;//第二个word,此id是电台赋给的。经常是0x40000xx。
oui = 0x00001c2d;//第三个word,FlexRadio Systems OUI
classId = 0x534c0123;//第四个word64位
//classId = 0x534c03e3;//第四个word64位
//integerTimestamp =0;// System.currentTimeMillis() / 1000;//第五个word,时间戳的整数部分,以秒为单位。应该是取当前时间
//fracTimeStamp = 0;//第六七个word,时间戳的小数部分64位此处为0。
//fracTimeStamp = frac;//第六七个word,时间戳的小数部分64位此处为0。
byte temp = 0;
if (classIdPresent) {
temp = 0x08;
}
if (trailerPresent) {
temp |= 0x04;
}
//----HEADER--No.1 word------
// result[0]=0x18;
result[0] = (byte) (packetType.ordinal() << 4);//packetType
result[0] |= temp;//其实就是0011 1000,0x38//CTRR,classIdPresent、trailerPresent、R、R
result[0] |= 0x03c0;//CTRR,classIdPresent、trailerPresent、R、R
result[1]=(byte) 0xd0;
result[1]|=(byte)(count&0xf);//packet count
result[1] = (byte) (tsi.ordinal() << 6);//TSI
result[1] |= (byte) (tsf.ordinal() << 4);//TSF
result[1] |= (byte) (packetCount & 0xff);//packetCount
//packetSize默认263words
result[2] = (byte) ((packetSize >> 8) & 0xff);//packetSize 1高8位
result[3] = (byte) (packetSize & 0xff);//packetSize 2低8位
//-----Stream Identifier--No.2 word----
//streamId=id;//最后两位应当是Dax编号
result[4] = (byte) ((streamId& 0x00ff000000 >> 24) & 0xff);
result[5] = (byte) (((streamId & 0x00ff0000) >> 16) & 0xff);
result[6] = (byte) (((streamId & 0x0000ff00) >> 8) & 0xff);
result[7] = (byte) (streamId & 0x000000ff);
//----OUI--No.3 words----
//OUI = 0x001C2D
result[8] = 0x00;
result[9] = 0x00;
result[10] = 0x1c;
result[11] = 0x2d;
//---Class Identifier--No.4 word----
//class id=0x534c0123
result[12] = 0x53;
result[13] = 0x4c;
result[14] = (byte) 0x01;
result[15] = (byte) 0x23;
//---Timestamp--No.5 word----
//integerTimestamp=0x01020304
// result[16] = (byte) 0x01;
// result[17] = (byte) 0x02;
// result[18] = (byte) 0x03;
// result[19] = (byte) 0x04;
//---FracTimeStamp No.5~6 words----
//fracTimeStamp=0x10200300506070c0
// result[20] = 0x10;
// result[21] = 0x20;
// result[22] = 0x03;
// result[23] = 0x00;
// result[24] = 0x50;
// result[25] = 0x60;
// result[26] = 0x70;
// result[27] = (byte) 0xc0;
// result[20] = (byte) ((fracTimeStamp >> 56) & 0x000000ff);
// result[21] = (byte) ((fracTimeStamp >> 48) & 0x000000ff);
// result[22] = (byte) ((fracTimeStamp >> 40) & 0x000000ff);
// result[23] = (byte) ((fracTimeStamp >> 32) & 0x000000ff);
//
// result[24] = (byte) ((fracTimeStamp >> 24) & 0x000000ff);
// result[25] = (byte) ((fracTimeStamp >> 16) & 0x000000ff);
// result[26] = (byte) ((fracTimeStamp >> 8) & 0x000000ff);
// result[27] = (byte) (fracTimeStamp & 0x000000ff);
for (int i = 0; i < data.length; i++) {
byte[] bytes=ByteBuffer.allocate(4).putFloat(data[i]).array();//float转byte[]
result[i*4+28]= bytes[0];
result[i*4+29]= bytes[1];
result[i*4+30]= bytes[2];
result[i*4+31]= bytes[3];
}
/*
payload+28byte[] result=new byte[data.length+28];
streamIdPresent=true;
streamIdPresent=packetType==VitaPacketType.IF_DATA_WITH_STREAM
||packetType==VitaPacketType.EXT_DATA_WITH_STREAM;
streamId:0x4000008,STREAM_CREATE_DAX_TX
classIdPresent:0x534c03e3,packetSize:263
integerTimestamp=now/1000;
fracTimeStamp=0;
*/
return result;
}
public VITA() {
}
public VITA(byte[] data) {
this.buffer = data;
//如果包的长度太小,或包为空,就退出计算
if (data == null) return;
if (data.length<VITAmin)return;
if (data.length < VITAmin) return;
isAvailable=true;//数据长度达到28个字节说明是有效的。
packetType=VitaPacketType.values()[(data[0]>>4)&0x0f];
classIdPresent=(data[0]&0x8)==0x8;//指示数据包中是否包含类标识符类ID字段
trailerPresent=(data[0]&0x4)==0x4;//指示数据包是否包含尾部。
tsi=VitaTSI.values()[(data[1]>>6)&0x3];//如果有时间戳的话指示时间戳的整数部分是啥类型的
tsf=VitaTSF.values()[(data[1]>>4)&0x3];
packetCount=data[1]&0x0f;
packetSize=((((int)data[2])&0x00ff)<<8)|((int)data[3])&0x00ff;
isAvailable = true;//数据长度达到28个字节说明是有效的。
packetType = VitaPacketType.values()[(data[0] >> 4) & 0x0f];
classIdPresent = (data[0] & 0x8) == 0x8;//指示数据包中是否包含类标识符类ID字段
trailerPresent = (data[0] & 0x4) == 0x4;//指示数据包是否包含尾部。
tsi = VitaTSI.values()[(data[1] >> 6) & 0x3];//如果有时间戳的话指示时间戳的整数部分是啥类型的
tsf = VitaTSF.values()[(data[1] >> 4) & 0x3];
packetCount = data[1] & 0x0f;
packetSize = ((((int) data[2]) & 0x00ff) << 8) | ((int) data[3]) & 0x00ff;
int offset=4;//定位
int offset = 4;//定位
//检查是否有流字符
streamIdPresent=packetType==VitaPacketType.IF_DATA_WITH_STREAM
||packetType==VitaPacketType.EXT_DATA_WITH_STREAM;
if (streamIdPresent){//是否有流ID,获取流ID32位
streamId=((((long)data[offset])&0x00ff)<<24)|((((int)data[offset+1])&0x00ff)<<16)
|((((int)data[offset+2])&0x00ff)<<8)|((int)data[offset+3])&0x00ff;
offset+=4;
streamIdPresent = packetType == VitaPacketType.IF_DATA_WITH_STREAM
|| packetType == VitaPacketType.EXT_DATA_WITH_STREAM;
if (streamIdPresent) {//是否有流ID,获取流ID32位
streamId = ((((long) data[offset]) & 0x00ff) << 24) | ((((int) data[offset + 1]) & 0x00ff) << 16)
| ((((int) data[offset + 2]) & 0x00ff) << 8) | ((int) data[offset + 3]) & 0x00ff;
offset += 4;
}
if (classIdPresent){
if (classIdPresent) {
//只取24位前8位保留
oui=((((int)data[offset+1])&0x00ff)<<16)
|((((int)data[offset+2])&0x00ff)<<8)|((int)data[offset+3])&0x00ff;
oui = ((((int) data[offset + 1]) & 0x00ff) << 16)
| ((((int) data[offset + 2]) & 0x00ff) << 8) | ((int) data[offset + 3]) & 0x00ff;
informationClassCode=((((int)data[offset+4])&0x00ff)<<8)|((int)data[offset+5])&0x00ff;
packetClassCode=((((int)data[offset+6])&0x00ff)<<8)|((int)data[offset+7])&0x00ff;
informationClassCode = ((((int) data[offset + 4]) & 0x00ff) << 8) | ((int) data[offset + 5]) & 0x00ff;
packetClassCode = ((((int) data[offset + 6]) & 0x00ff) << 8) | ((int) data[offset + 7]) & 0x00ff;
classId=((((int)data[offset+4])&0x00ff)<<24)|((((int)data[offset+5])&0x00ff)<<16)
|((((int)data[offset+6])&0x00ff)<<8)|((int)data[offset+7])&0x00ff;
offset+=8;
classId = ((((int) data[offset + 4]) & 0x00ff) << 24) | ((((int) data[offset + 5]) & 0x00ff) << 16)
| ((((int) data[offset + 6]) & 0x00ff) << 8) | ((int) data[offset + 7]) & 0x00ff;
offset += 8;
}
//Log.e(TAG, "VITA: "+String.format("id: 0x%x, classIdPresent:0x%x,packetSize:%d",streamId,classId,packetSize) );
//获取时间戳,以秒为单位的时间戳32位。
//时间戳共有两部分小数部分和整数部分整数部分以秒为分辨率32位 主要传递UTC时间或者 GPS 时间,
//小数部分主要有三种一种是sample-count 以采样周期为最小分辨率一种是real-time以ps为最小单位
// 第三种是以任意选择的时间进行累加得出的,前面两种时间戳可以直接与整数部分叠加,
// 第三种则不能保证与整数部分保持恒定关系,前两种与整数部分叠加来操作的可以在覆盖的时间范围为年
//小数部分主要有三种一种是sample-count 以采样周期为最小分辨率一种是real-time以ps为最小单位第三种是以任意选择的时间进行累加得出的前面两种时间戳可以直接与整数部分叠加第三种则不能保证与整数部分保持恒定关系前两种与整数部分叠加来操作的可以在覆盖的时间范围为年
//小数部分的时间戳共有64位小数部分可以在没有整数部分的情况下使用
//所有的时间带来都是在以一个采样数据为该reference-point 时间
if (tsi!=VitaTSI.TSI_NONE){//32位,
integerTimestamp=((((long)data[offset])&0x00ff)<<24)|((((int)data[offset+1])&0x00ff)<<16)
|((((int)data[offset+2])&0x00ff)<<8)|((int)data[offset+3])&0x00ff;
offset+=4;
if (tsi != VitaTSI.TSI_NONE) {//32位,
integerTimestamp = ((((long) data[offset]) & 0x00ff) << 24) | ((((int) data[offset + 1]) & 0x00ff) << 16)
| ((((int) data[offset + 2]) & 0x00ff) << 8) | ((int) data[offset + 3]) & 0x00ff;
offset += 4;
}
//获取时间戳的小数部分64位。
if (tsf != VitaTSF.TSF_NONE) {
fracTimeStamp = ((((long) data[offset]) & 0x00ff) << 56) | ((((long) data[offset + 1]) & 0x00ff) << 48)
| ((((long) data[offset + 2]) & 0x00ff) << 36) | ((int) data[offset + 3]) & 0x00ff
| ((((long) data[offset + 4]) & 0x00ff) << 24) | ((((int) data[offset + 5]) & 0x00ff) << 16)
| ((((int) data[offset + 6]) & 0x00ff) << 8) | ((int) data[offset + 7]) & 0x00ff;
offset += 8;
}
//获取时间戳的小数部分64位。
if (tsf!=VitaTSF.TSF_NONE){
fracTimeStamp=((((long)data[offset])&0x00ff)<<56)|((((long)data[offset+1])&0x00ff)<<48)
|((((long)data[offset+2])&0x00ff)<<36)|((int)data[offset+3])&0x00ff
|((((long)data[offset+4])&0x00ff)<<24)|((((int)data[offset+5])&0x00ff)<<16)
|((((int)data[offset+6])&0x00ff)<<8)|((int)data[offset+7])&0x00ff;
offset+=8;
}
//Log.e(TAG, String.format("VITA: data length:%d,offset:%d",data.length,offset) );
if (offset<data.length){
payload=new byte[data.length-offset-(trailerPresent?2:0)];//如果有尾部就减去一个word的位置
System.arraycopy(data,offset,payload,0,payload.length);
if (offset < data.length) {
payload = new byte[data.length - offset - (trailerPresent ? 2 : 0)];//如果有尾部就减去一个word的位置
System.arraycopy(data, offset, payload, 0, payload.length);
}
if (trailerPresent){
trailer=((((int)data[data.length-2])&0x00ff)<<8)|((int)data[data.length-1])&0x00ff;
if (trailerPresent) {
trailer = ((((int) data[data.length - 2]) & 0x00ff) << 8) | ((int) data[data.length - 1]) & 0x00ff;
}
}
/**
* payloadpayload0
*
* @return payload
*/
public int getPayloadLength(){
if (buffer==null){
public int getPayloadLength() {
if (buffer == null) {
return 0;
}else {
} else {
return buffer.length;
}
}
/**
*
*
* @return string
*/
public String showPayload(){
if (payload!=null) {
return new String(payload).replace(" ","\n");
}else {
public String showPayload() {
if (payload != null) {
return new String(payload).replace(" ", "\n");
} else {
return "";
}
}
public String showPayloadHex(){
if (payload!=null){
public String showPayloadHex() {
if (payload != null) {
return byteToStr(payload);
}else {
} else {
return "";
}
}
/**
* VITA 49
*
* @return string
*/
@SuppressLint("DefaultLocale")
public String showHeadStr(){
public String showHeadStr() {
return String.format("包类型(packetType): %s\n" +
"包数量(packetCount): %d\n" +
"包大小(packetSize): %d\n" +
@ -227,8 +372,8 @@ public class VITA {
"流ID(streamId): 0x%X\n" +
"是否有类ID(classIdPresent): %s\n" +
"类ID(classId): 0x%X\n" +
"类高位(informationClassCode): 0x%X\n"+
"类低位(packetClassCode): 0x%X\n"+
"类高位(informationClassCode): 0x%X\n" +
"类低位(packetClassCode): 0x%X\n" +
"公司标识码(oui): 0x%X\n" +
"时间戳类型(tsi): %s\n" +
"时间戳整数部分(integerTimestamp):%s\n" +
@ -237,47 +382,46 @@ public class VITA {
"负载长度(payloadLength): %d\n" +
"是否有尾部(trailerPresent): %s\n"
,packetType.toString()
,packetCount
,packetSize
,streamIdPresent?"是":"否"
,streamId
,classIdPresent?"是":"否"
,classId
,informationClassCode
,packetClassCode
,oui
,tsi.toString()
,timestampToDateStr(integerTimestamp*1000)
,tsf.toString()
,fracTimeStamp
,(payload==null?0:payload.length)
,trailerPresent?"是":"否"
, packetType.toString()
, packetCount
, packetSize
, streamIdPresent ? "是" : "否"
, streamId
, classIdPresent ? "是" : "否"
, classId
, informationClassCode
, packetClassCode
, oui
, tsi.toString()
, timestampToDateStr(integerTimestamp * 1000)
, tsf.toString()
, fracTimeStamp
, (payload == null ? 0 : payload.length)
, trailerPresent ? "是" : "否"
);
}
/**
* VITA 49
*
* @return string
*/
@SuppressLint("DefaultLocale")
@Override
public String toString() {
return String.format("%s负载(payload):\n%s\n"
,showHeadStr()
,(payload==null?"":new String(payload))
, showHeadStr()
, (payload == null ? "" : new String(payload))
);
}
public static String timestampToDateStr(Long timestamp){
public static String timestampToDateStr(Long timestamp) {
//final String DATETIME_CONVENTIONAL_CN = "yyyy-MM-dd HH:mm:ss";
//SimpleDateFormat sdf = new SimpleDateFormat(DATETIME_CONVENTIONAL_CN);
@SuppressLint("SimpleDateFormat")
SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String sd = sdf.format(new Date(timestamp)); // 时间戳转换日期
//System.out.println(sd);
return sd;

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.floatview;
/**
* FloatButton
* @author BGY70Z
* @date 2023-03-20
*/
import android.annotation.SuppressLint;
import android.content.Context;
@ -18,10 +23,7 @@ import androidx.constraintlayout.widget.Constraints;
import java.util.ArrayList;
/**
*
* @author BG7YOZ
*/
public class FloatView extends ConstraintLayout {
private static final String TAG = "FloatView";
@ -76,13 +78,19 @@ public class FloatView extends ConstraintLayout {
}
public FloatViewButton addButton(String name, int imageResourceId, OnClickListener onClickListener) {
FloatViewButton floatViewButton = addButton(View.generateViewId(), imageResourceId, onClickListener);
FloatViewButton floatViewButton=getButtonByName(name);
if (floatViewButton==null){
floatViewButton =addButton(View.generateViewId(), imageResourceId, onClickListener);
}
floatViewButton.setName(name);
return floatViewButton;
}
public FloatViewButton addButton(int id, String name, int imageResourceId, OnClickListener onClickListener) {
FloatViewButton floatViewButton = addButton(id, imageResourceId, onClickListener);
FloatViewButton floatViewButton=getButtonByName(name);
if (floatViewButton==null){
floatViewButton = addButton(id, imageResourceId, onClickListener);
}
floatViewButton.setName(name);
return floatViewButton;
}

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.floatview;
/**
* FloatButton
* @author BGY70Z
* @date 2023-03-20
*/
import android.annotation.SuppressLint;
import android.content.Context;

Wyświetl plik

@ -1,4 +1,10 @@
package com.bg7yoz.ft8cn.ft8listener;
/**
* UtcTimerOnWaveDataListener
*
* @author BGY70Z
* @date 2023-03-20
*/
import android.util.Log;
@ -18,7 +24,7 @@ public class FT8SignalListener {
private static final String TAG = "FT8SignalListener";
private final UtcTimer utcTimer;
//private HamRecorder hamRecorder;
private OnFt8Listen onFt8Listen;//当开始监听,解码结束后触发的事件
private final OnFt8Listen onFt8Listen;//当开始监听,解码结束后触发的事件
//private long band;
public MutableLiveData<Long> decodeTimeSec = new MutableLiveData<>();//解码的时长
private OnWaveDataListener onWaveDataListener;
@ -74,7 +80,7 @@ public class FT8SignalListener {
/**
*
*
* @return
* @return int
*/
public int time_Offset() {
return utcTimer.getTime_sec() + UtcTimer.delay;
@ -99,91 +105,77 @@ public class FT8SignalListener {
}
});
}
//
// hamRecorder.getVoiceData(FT8Common.FT8_SLOT_TIME_MILLISECOND, true
// , new OnGetVoiceDataDone() {
// @Override
// public void onGetDone(float[] data) {
// Log.d(TAG, "开始解码...");
// //testFt8(utc, HamRecorder.byteDataTo16BitData(data));
// //decodeFt8(utc, HamRecorder.getFloatFromBytes(data));
// decodeFt8(utc, data);
// }
// });
}
public void decodeFt8(long utc, float[] voiceData) {
// new Thread(new Runnable() {
// @Override
// public void run() {
long time = System.currentTimeMillis();
if (onFt8Listen != null) {
onFt8Listen.beforeListen(utc);
}
ArrayList<Ft8Message> ft8Messages = new ArrayList<>();
//long ft8Decoder;
Ft8Message ft8Message = new Ft8Message(FT8Common.FT8_MODE);
ft8Message.utcTime = utc;
ft8Message.band = GeneralVariables.band;
//此处改为reset,是因为在cpp部分改为一个变量不是以指针申请新内存的方式处理了。
//其实这种方式要注意一个问题,在一个周期之内,必须解码完毕,否则新的解码又要开始了
long ft8Decoder = InitDecoder(ft8Message.utcTime, FT8Common.SAMPLE_RATE
, voiceData.length, true);
//DecoderFt8Reset(ft8Decoder, ft8Message.utcTime, voiceData.length);
DecoderMonitorPressFloat(voiceData, ft8Decoder);
int num_candidates = DecoderFt8FindSync(ft8Decoder);
float dt = 0;
int dtAverage = 0;
for (int idx = 0; idx < num_candidates; ++idx) {
try {//做一下解码失败保护
if (DecoderFt8Analysis(idx, ft8Decoder, ft8Message)) {
if (ft8Message.isValid) {
Ft8Message msg = new Ft8Message(ft8Message);//此处使用msg是因为有的哈希呼号会把<...>替换掉
if (checkMessageSame(ft8Messages, msg)) {
continue;
}
dt += ft8Message.time_sec;
dtAverage++;
//ft8Messages.add(new Ft8Message(ft8Message));
ft8Messages.add(msg);
}
new Thread(new Runnable() {
@Override
public void run() {
long time = System.currentTimeMillis();
if (onFt8Listen != null) {
onFt8Listen.beforeListen(utc);
}
} catch (Exception e) {
Log.e(TAG, "run: " + e.getMessage());
ArrayList<Ft8Message> ft8Messages = new ArrayList<>();
Ft8Message ft8Message = new Ft8Message(FT8Common.FT8_MODE);
ft8Message.utcTime = utc;
ft8Message.band = GeneralVariables.band;
//此处改为reset,是因为在cpp部分改为一个变量不是以指针申请新内存的方式处理了。
//其实这种方式要注意一个问题,在一个周期之内,必须解码完毕,否则新的解码又要开始了
long ft8Decoder = InitDecoder(ft8Message.utcTime, FT8Common.SAMPLE_RATE
, voiceData.length, true);
//DecoderFt8Reset(ft8Decoder, ft8Message.utcTime, voiceData.length);
DecoderMonitorPressFloat(voiceData, ft8Decoder);
int num_candidates = DecoderFt8FindSync(ft8Decoder);
float dt = 0;
int dtAverage = 0;
for (int idx = 0; idx < num_candidates; ++idx) {
try {//做一下解码失败保护
if (DecoderFt8Analysis(idx, ft8Decoder, ft8Message)) {
if (ft8Message.isValid) {
Ft8Message msg = new Ft8Message(ft8Message);//此处使用msg是因为有的哈希呼号会把<...>替换掉
if (checkMessageSame(ft8Messages, msg)) {
continue;
}
dt += ft8Message.time_sec;
dtAverage++;
//ft8Messages.add(new Ft8Message(ft8Message));
ft8Messages.add(msg);
}
}
} catch (Exception e) {
Log.e(TAG, "run: " + e.getMessage());
}
}
float time_sec = 0f;
if (dtAverage != 0) {
time_sec = dt / dtAverage;
//utcTimer.setTime_sec(Math.round(time_sec * 1000));
} else {//当检测不到时,不要偏移时间
utcTimer.setTime_sec(Math.round(0));
}
//移到finalize() 方法中调用了
DeleteDecoder(ft8Decoder);
if (onFt8Listen != null) {
onFt8Listen.afterDecode(utc, time_sec, UtcTimer.sequential(utc), ft8Messages);
}
decodeTimeSec.postValue(System.currentTimeMillis() - time);//解码耗时
Log.d(TAG, String.format("解码耗时:%d毫秒", System.currentTimeMillis() - time));
}
}
float time_sec = 0f;
if (dtAverage != 0) {
time_sec = dt / dtAverage;
//utcTimer.setTime_sec(Math.round(time_sec * 1000));
} else {//当检测不到时,不要偏移时间
utcTimer.setTime_sec(Math.round(0));
}
//移到finalize() 方法中调用了
DeleteDecoder(ft8Decoder);
if (onFt8Listen != null) {
onFt8Listen.afterDecode(utc, time_sec, UtcTimer.sequential(utc), ft8Messages);
}
decodeTimeSec.postValue(System.currentTimeMillis() - time);//解码耗时
db.writeMessage(ft8Messages, GeneralVariables.myCallsign);//把消息写到数据库
Log.d(TAG, String.format("解码耗时:%d毫秒", System.currentTimeMillis() - time));
// }
// }).start();
}).start();
}
@ -192,7 +184,7 @@ public class FT8SignalListener {
*
* @param ft8Messages
* @param ft8Message
* @return
* @return boolean
*/
private boolean checkMessageSame(ArrayList<Ft8Message> ft8Messages, Ft8Message ft8Message) {
for (Ft8Message msg : ft8Messages) {
@ -256,7 +248,7 @@ public class FT8SignalListener {
* @param idx
* @param decoder
* @param ft8Message
* @return
* @return boolean
*/
public native boolean DecoderFt8Analysis(int idx, long decoder, Ft8Message ft8Message);

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.ft8listener;
/**
* afterDecode
* @author BGY70Z
* @date 2023-03-20
*/
import com.bg7yoz.ft8cn.Ft8Message;

Wyświetl plik

@ -1,14 +1,15 @@
package com.bg7yoz.ft8cn.ft8signal;
/**
* FT8
* @author BGY70Z
* @date 2023-03-20
*/
import android.util.Log;
import com.bg7yoz.ft8cn.Ft8Message;
import com.bg7yoz.ft8cn.ft8transmit.GenerateFT8;
/**
* FT8
* @author BG7YOZ
*/
public class FT8Package {
private static final String TAG = "FT8Package";
public static final int NTOKENS = 2063592;
@ -370,6 +371,7 @@ public class FT8Package {
public static native int getHash12(String callsign);
public static native int getHash10(String callsign);
public static native int getHash22(String callsign);

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.ft8transmit;
/**
*
* @author BGY70Z
* @date 2023-03-20
*/
import android.annotation.SuppressLint;
import android.media.AudioAttributes;
@ -178,7 +183,66 @@ public class FT8TransmitSignal {
}
Log.d(TAG, "doTransmit: 开始发射...");
doTransmitThreadPool.execute(doTransmitRunnable);
// new Thread(new Runnable() {
// @SuppressLint("DefaultLocale")
// @Override
// public void run() {
// //--todo----
// //此处可能要修改,维护一个列表。把每个呼号,网格,时间,波段,记录下来
// if (functionOrder == 1 || functionOrder == 2) {//当消息处于1或2时说明开始了通联
// messageStartTime = UtcTimer.getSystemTime();
// }
// if (messageStartTime == 0) {//如果起始时间没有,就取现在的
// messageStartTime = UtcTimer.getSystemTime();
// }
//
// //用于显示将要发射的消息内容
// Ft8Message msg;
// if (transmitFreeText){
// msg=new Ft8Message("CQ",GeneralVariables.myCallsign,freeText);
// msg.i3=0;
// msg.n3=0;
// }else {
// msg = getFunctionCommand(functionOrder);
// }
//
// if (onDoTransmitted != null) {
// //此处用于处理PTT等事件
// onDoTransmitted.onBeforeTransmit(msg, functionOrder);
// }
// //short[] buffer = new short[FT8Common.SAMPLE_RATE * FT8Common.FT8_SLOT_TIME];
// //79个符号每个符号0.16秒采样率12000
// short[] buffer = new short[(int) (0.5f +
// GenerateFT8.num_tones * GenerateFT8.symbol_period
// * GenerateFT8.sample_rate)]; // 数据信号中的采样数0.5+79*0.16*12000];
//
//
// isTransmitting = true;
// mutableIsTransmitting.postValue(true);
//
//
// mutableTransmittingMessage.postValue(String.format(" (%.0fHz) %s"
// , GeneralVariables.getBaseFrequency()
// , msg.getMessageText()));
// if (!GenerateFT8.generateFt8(msg
// , GeneralVariables.getBaseFrequency(), buffer)) {
// return;
// }
// ;
// //电台动作可能有要有个延迟时间,所以时间并不一定完全准确
// try {//给电台一个100毫秒的响应时间
// Thread.sleep(GeneralVariables.pttDelay);//给PTT指令后电台一个响应时间默认100毫秒
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//
// if (onDoTransmitted != null) {//处理音频数据可以给ICOM的网络模式发送
// onDoTransmitted.onAfterGenerate(buffer);
// }
// //播放音频
// playFT8Signal(buffer);
// }
// }).start();
mutableFunctions.postValue(functionList);
}
@ -302,7 +366,7 @@ public class FT8TransmitSignal {
}
private void playFT8Signal(short[] buffer) {
private void playFT8Signal(float[] buffer) {
//todo--实现网络发送模式
if (GeneralVariables.connectMode == ConnectMode.NETWORK) {//网络方式就不播放音频了
@ -329,16 +393,16 @@ public class FT8TransmitSignal {
Log.d(TAG, "playFT8Signal: 准备声卡播放....");
attributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
//.setLegacyStreamType(AudioManager.STREAM_VOICE_CALL)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build();
myFormat = new AudioFormat.Builder().setSampleRate(FT8Common.SAMPLE_RATE)
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
//.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setEncoding(AudioFormat.ENCODING_PCM_FLOAT)
.setChannelMask(AudioFormat.CHANNEL_OUT_MONO).build();
int mySession = 0;
audioTrack = new AudioTrack(attributes, myFormat
, 12000 * 15 * 2, AudioTrack.MODE_STATIC
, 12000 * 15 * 4, AudioTrack.MODE_STATIC
, mySession);
@ -943,9 +1007,9 @@ public class FT8TransmitSignal {
}
//short[] buffer = new short[FT8Common.SAMPLE_RATE * FT8Common.FT8_SLOT_TIME];
//79个符号每个符号0.16秒采样率12000
short[] buffer = new short[(int) (0.5f +
GenerateFT8.num_tones * GenerateFT8.symbol_period
* GenerateFT8.sample_rate)]; // 数据信号中的采样数0.5+79*0.16*12000];
// short[] buffer = new short[(int) (0.5f +
// GenerateFT8.num_tones * GenerateFT8.symbol_period
// * GenerateFT8.sample_rate)]; // 数据信号中的采样数0.5+79*0.16*12000];
transmitSignal.isTransmitting = true;
@ -955,8 +1019,9 @@ public class FT8TransmitSignal {
transmitSignal.mutableTransmittingMessage.postValue(String.format(" (%.0fHz) %s"
, GeneralVariables.getBaseFrequency()
, msg.getMessageText()));
if (!GenerateFT8.generateFt8(msg
, GeneralVariables.getBaseFrequency(), buffer)) {
//生成信号
float[] buffer=GenerateFT8.generateFt8(msg, GeneralVariables.getBaseFrequency());
if (buffer==null) {
return;
}
;

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.ft8transmit;
/**
* FT86
* @author BGY70Z
* @date 2023-03-20
*/
import com.bg7yoz.ft8cn.Ft8Message;

Wyświetl plik

@ -1,9 +1,12 @@
package com.bg7yoz.ft8cn.ft8transmit;
/**
* FT8
* @author BG7YOZ
* FT832
* @author BGY70Z
* @date 2023-03-20
*/
import android.util.Log;
import com.bg7yoz.ft8cn.Ft8Message;
import com.bg7yoz.ft8cn.GeneralVariables;
import com.bg7yoz.ft8cn.R;
@ -25,6 +28,7 @@ public class GenerateFT8 {
public static final float symbol_period = FT8_SYMBOL_PERIOD;//FT8_SYMBOL_PERIOD=0.160f
private static final float symbol_bt = FT8_SYMBOL_BT;//FT8_SYMBOL_BT=2.0f
private static final float slot_time = FT8_SLOT_TIME;//FT8_SLOT_TIME=15f
//public static int sample_rate = 48000;//采样率
public static int sample_rate = 12000;//采样率
@ -117,10 +121,10 @@ public class GenerateFT8 {
}
public static boolean generateFt8(Ft8Message msg, float frequency, short[] buffer) {
public static float[] generateFt8(Ft8Message msg, float frequency) {
if (msg.callsignFrom.length()<3){
ToastMessage.show(GeneralVariables.getStringFromResource(R.string.callsign_error));
return false;
return null;
}
// 首先,将文本数据打包为二进制消息,共12个字节
byte[] packed = new byte[FTX_LDPC_K_BYTES];
@ -156,6 +160,7 @@ public class GenerateFT8 {
packFreeTextTo77(msg.getMessageText(), packed);
}
// 其次将二进制消息编码为FSK音调序列,79个字节
byte[] tones = new byte[num_tones]; // 79音调符号数组
ft8_encode(packed, tones);
@ -164,21 +169,31 @@ public class GenerateFT8 {
int num_samples = (int) (0.5f + num_tones * symbol_period * sample_rate); // 数据信号中的采样数0.5+79*0.16*12000
//int num_silence = (int) ((slot_time * sample_rate - num_samples) / 2); // 两端填充静音到15秒15*12000-num_samples/21.18秒的样本数)
//int num_total_samples = num_silence + num_samples + num_silence; // 填充信号中的样本数2.36秒+12.64秒=15秒的样本数
//float[] signal = new float[Ft8num_samples];
float[] signal = new float[num_samples];
synth_gfsk(tones, num_tones, frequency, symbol_bt, symbol_period, sample_rate, signal, 0);
for (int i = 0; i < num_samples; i++) {
float x = signal[i];
if (x > 1.0)
x = 1.0f;
else if (x < -1.0)
x = -1.0f;
buffer[i] = (short) (0.5 + (x * 32767.0));
//Ft8num_sampleFT8声音的总采样数不是字节数。15*12000
//for (int i = 0; i < Ft8num_samples; i++)//把数据全部静音。
for (int i = 0; i < num_samples; i++)//把数据全部静音。
{
signal[i] = 0;
}
return true;
}
synth_gfsk(tones, num_tones, frequency, symbol_bt, symbol_period, sample_rate, signal, 0);
for (int i = 0; i < num_samples; i++)//把数据全部静音。
{
if (signal[i]>1.0||signal[i]<-1.0){
Log.e(TAG, "generateFt8: "+signal[i] );
}
}
return signal;
}
private static native int packFreeTextTo77(String msg, byte[] c77);

Wyświetl plik

@ -1,9 +1,14 @@
package com.bg7yoz.ft8cn.ft8transmit;
/**
*
* @author BGY70Z
* @date 2023-03-20
*/
import com.bg7yoz.ft8cn.Ft8Message;
public interface OnDoTransmitted {
void onBeforeTransmit(Ft8Message message,int functionOder);
void onAfterTransmit(Ft8Message message, int functionOder);
void onAfterGenerate(short[] data);
void onAfterGenerate(float[] data);
}

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.ft8transmit;
/**
*
* @author BGY70Z
* @date 2023-03-20
*/
import com.bg7yoz.ft8cn.log.QSLRecord;

Wyświetl plik

@ -1,5 +1,10 @@
package com.bg7yoz.ft8cn.ft8transmit;
/**
* QSO
* @author BGY70Z
* @date 2023-03-20
*/
public class QSLRecord {
private long startTime;//起始时间
private long endTime;//结束时间

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.ft8transmit;
/**
*
* @author BGY70Z
* @date 2023-03-20
*/
import com.bg7yoz.ft8cn.log.QSLRecord;
@ -41,7 +46,14 @@ public class QslRecordList extends ArrayList<QSLRecord> {
*/
public QSLRecord addQSLRecord(QSLRecord record){
if (record.getToCallsign().equals("CQ")) return null;
//清除已经保存过的通联记录
//for (int i = this.size()-1; i >=0 ; i--) {
// if (this.get(i).getToCallsign().equals(record.getToCallsign())){
// if (this.get(i).saved){
// this.remove(i);
// }
// }
//}
//找一下看有没有已经在列表中,但还没有保存的记录
QSLRecord oldRecord= getRecordByCallsign(record.getToCallsign());
if (oldRecord==null){

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.ft8transmit;
/**
*
* @author BGY70Z
* @date 2023-03-20
*/
import android.annotation.SuppressLint;

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.grid_tracker;
/**
* 线
* @author BGY70Z
* @date 2023-03-20
*/
import android.annotation.SuppressLint;
import android.graphics.Paint;
@ -20,15 +25,7 @@ import org.osmdroid.views.overlay.infowindow.InfoWindow;
public class GridInfoWindow extends InfoWindow {
public static final int UNDEFINED_RES_ID = 0;
// static int mTitleId = 0;
// static int mDescriptionId = 0;
// static int mSubDescriptionId = 0;
// static int fromDxccImageId = 0;
// static int fromItuImageId = 0;
// static int fromCqImageId = 0;
// static int toDxccImageId = 0;
// static int toItuImageId = 0;
// static int toCqImageId = 0;
private final TextView titleView;
private final TextView descriptionView;
private final TextView subDescriptionView;
@ -68,7 +65,7 @@ public class GridInfoWindow extends InfoWindow {
boolean otherBandIsQso = GeneralVariables.checkQSLCallsign_OtherBand(msg.getCallsignFrom());
//是否有与我呼号有关的消息
if (msg.inMyCall(GeneralVariables.myCallsign)) {
if (msg.inMyCall()) {
layout.setBackground(mView.getResources().getDrawable(R.drawable.tracker_new_cq_info_win_style));
titleView.setTextColor(mapView.getResources().getColor(
R.color.message_in_my_call_text_color));
@ -102,7 +99,7 @@ public class GridInfoWindow extends InfoWindow {
}
if (this.mView == null) {
Log.e("GridInfoWindow", "Error trapped, BasicInfoWindow.open, mView is null!");
Log.w("OsmDroid", "Error trapped, BasicInfoWindow.open, mView is null!");
} else {
titleView.setText(title);
String snippet = overlay.getSnippet();

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.grid_tracker;
/**
* Marker()
* @author BGY70Z
* @date 2023-03-20
*/
import android.annotation.SuppressLint;
import android.graphics.Paint;
@ -67,7 +72,7 @@ public class GridMarkerInfoWindow extends InfoWindow {
boolean otherBandIsQso = GeneralVariables.checkQSLCallsign_OtherBand(msg.getCallsignFrom());
//是否有与我呼号有关的消息
if (msg.inMyCall(GeneralVariables.myCallsign)) {
if (msg.inMyCall()) {
layout.setBackground(mView.getResources().getDrawable(R.drawable.tracker_new_cq_info_win_style));
titleView.setTextColor(mapView.getResources().getColor(
R.color.message_in_my_call_text_color));

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.grid_tracker;
/**
* OsmMapView线sqlite线nightUSGS4Layer
* @author BGY70Z
* @date 2023-03-20
*/
import static java.lang.Math.PI;
import static java.lang.Math.asin;
@ -22,6 +27,7 @@ import com.bg7yoz.ft8cn.GeneralVariables;
import com.bg7yoz.ft8cn.MainViewModel;
import com.bg7yoz.ft8cn.R;
import com.bg7yoz.ft8cn.database.DatabaseOpr;
import com.bg7yoz.ft8cn.log.QSLRecordStr;
import com.bg7yoz.ft8cn.maidenhead.MaidenheadGrid;
import com.google.android.gms.maps.model.LatLng;
@ -75,8 +81,8 @@ public class GridOsmMapView {
// private final ArrayList<OverlayItem> markerItems = new ArrayList<>();
private final ArrayList<GridPolyLine> gridLines = new ArrayList<>();
private GridPolyLine selectedLine = null;
private static final int TIME_OUT=3;
private int selectLineTimeOut=TIME_OUT;//被选择的画线,停留的周期数
private static final int TIME_OUT = 3;
private int selectLineTimeOut = TIME_OUT;//被选择的画线,停留的周期数
private final ArrayList<GridPolygon> gridPolygons = new ArrayList<>();
private final ArrayList<GridMarker> gridMarkers = new ArrayList<>();
@ -127,12 +133,13 @@ public class GridOsmMapView {
/**
* 线
*
* @param line 线
*/
public void zoomToLineBound(GridPolyLine line) {
BoundingBox boundingBox = new BoundingBox();
selectedLine = line;
selectLineTimeOut=TIME_OUT;
selectLineTimeOut = TIME_OUT;
line.getOutlinePaint().setColor(gridMapView.getResources().getColor(
R.color.tracker_select_line_color));
line.getOutlinePaint().setStrokeWidth(6);
@ -143,15 +150,14 @@ public class GridOsmMapView {
GeoPoint westSouthPoint = new GeoPoint(line.getActualPoints().get(1).getLatitude()
, line.getActualPoints().get(1).getLongitude());
if (Math.abs(westSouthPoint.getLongitude()-eastNorthPoint.getLongitude())>180){
if (eastNorthPoint.getLongitude() > westSouthPoint.getLongitude())
{
if (Math.abs(westSouthPoint.getLongitude() - eastNorthPoint.getLongitude()) > 180) {
if (eastNorthPoint.getLongitude() > westSouthPoint.getLongitude()) {
double temp = westSouthPoint.getLongitude();
westSouthPoint.setLongitude(eastNorthPoint.getLongitude());
eastNorthPoint.setLongitude(temp);
}
}else {
} else {
if (eastNorthPoint.getLongitude() < westSouthPoint.getLongitude()) {
double temp = westSouthPoint.getLongitude();
westSouthPoint.setLongitude(eastNorthPoint.getLongitude());
@ -174,6 +180,7 @@ public class GridOsmMapView {
/**
* CQ
*
* @param marker CQ
* @param offset
*/
@ -182,7 +189,7 @@ public class GridOsmMapView {
if (offset) {
geoPoint.setLongitude(geoPoint.getLongitude() - 40f);
}
gridMapView.getController().animateTo(geoPoint,2.5,500l);
gridMapView.getController().animateTo(geoPoint, 2.5, 500L);
}
@ -206,27 +213,29 @@ public class GridOsmMapView {
gridMapView.invalidate();
}
public GridPolyLine getSelectedLine(){
public GridPolyLine getSelectedLine() {
return selectedLine;
}
public void clearSelectedLines(){
if (selectedLine!=null){
public void clearSelectedLines() {
if (selectedLine != null) {
selectedLine.closeInfoWindow();
gridMapView.getOverlays().remove(selectedLine);
selectedLine=null;
selectedLine = null;
}
}
/**
* 线
*/
public synchronized void clearLines() {
boolean isOpening=false;
boolean isOpening = false;
if (selectedLine !=null){
if (selectedLine != null) {
selectLineTimeOut--;
isOpening=selectedLine.isInfoWindowOpen();
isOpening = selectedLine.isInfoWindowOpen();
selectedLine.closeInfoWindow();
gridMapView.getOverlays().remove(selectedLine);
}
@ -235,7 +244,7 @@ public class GridOsmMapView {
gridMapView.getOverlays().remove(line);
}
gridLines.clear();
if (selectedLine != null&& selectLineTimeOut>0) {
if (selectedLine != null && selectLineTimeOut > 0) {
gridMapView.getOverlays().add(selectedLine);
if (isOpening) selectedLine.showInfoWindow();
}
@ -295,6 +304,54 @@ public class GridOsmMapView {
return gridPolygon;
}
/**
*
*
* @param recordStr
* @return
*/
public GridPolygon upgradeGridInfo(QSLRecordStr recordStr) {
GridPolygon gridPolygon = getGridPolygon(recordStr.getGridsquare());
if (gridPolygon == null) {
if (recordStr.isQSL) {
gridPolygon = addGridPolygon(recordStr.getGridsquare(), GridMode.QSL);
} else {
gridPolygon = addGridPolygon(recordStr.getGridsquare(), GridMode.QSO);
}
}
gridPolygon.setSnippet(String.format(String.format("%s %s",
String.format(GeneralVariables.getStringFromResource(R.string.qsl_freq)
, recordStr.getFreq()),
String.format(GeneralVariables.getStringFromResource(R.string.qsl_band)
, recordStr.getBand()))));
gridPolygon.setSubDescription(String.format("%s\n%s\n%s %s\n%s %s",
String.format(GeneralVariables.getStringFromResource(R.string.qsl_start_time)
, recordStr.getTime_on()),
String.format(GeneralVariables.getStringFromResource(R.string.qsl_end_time)
, recordStr.getTime_off()),
String.format(GeneralVariables.getStringFromResource(R.string.qsl_rst_rcvd)
, recordStr.getRst_rcvd()),
String.format(GeneralVariables.getStringFromResource(R.string.qsl_rst_sent)
, recordStr.getRst_sent()),
String.format(GeneralVariables.getStringFromResource(R.string.qsl_mode)
, recordStr.getMode()),
recordStr.getComment()
));
gridPolygon.setTitle(String.format("%s--%s", recordStr.getCall(), recordStr.getStation_callsign()));//显示消息内容
gridPolygon.setInfoWindow(new GridRecordInfoWindow(R.layout.tracker_record_info_win, gridMapView));
return gridPolygon;
}
/**
*
*/
public void mapUpdate(){
gridMapView.invalidate();
}
/**
* falsetrue
*
@ -385,6 +442,23 @@ public class GridOsmMapView {
return line;
}
public synchronized GridPolyLine drawLine(QSLRecordStr recordStr) {
LatLng fromLatLng = MaidenheadGrid.gridToLatLng(recordStr.getGridsquare());
LatLng toLatLng = MaidenheadGrid.gridToLatLng(recordStr.getMy_gridsquare());
if (fromLatLng == null) {
//todo 把呼号转为国家的经纬度
return null;
//fromLatLng = message.fromLatLng;
}
if (toLatLng == null) {
//todo 把呼号转为国家的经纬度
return null;
//toLatLng = message.toLatLng;
}
final GridPolyLine line = new GridPolyLine(gridMapView, fromLatLng, toLatLng, recordStr);
return line;
}
/**
* 线
@ -468,8 +542,48 @@ public class GridOsmMapView {
//public String fromGrid;
//public String toGrid;
public Ft8Message msg;
public QSLRecordStr recorder;
//public boolean marked = false;
@SuppressLint("DefaultLocale")
public GridPolyLine(MapView mapView, LatLng fromLatLng, LatLng toLatLng, QSLRecordStr recordStr) {
super(mapView);
this.recorder = recordStr;
setSnippet(String.format(String.format("%s %s",
String.format(GeneralVariables.getStringFromResource(R.string.qsl_freq)
, recordStr.getFreq()),
String.format(GeneralVariables.getStringFromResource(R.string.qsl_band)
, recordStr.getBand()))));
setSubDescription(String.format("%s\n%s\n%s %s\n%s %s",
String.format(GeneralVariables.getStringFromResource(R.string.qsl_start_time)
, recordStr.getTime_on()),
String.format(GeneralVariables.getStringFromResource(R.string.qsl_end_time)
, recordStr.getTime_off()),
String.format(GeneralVariables.getStringFromResource(R.string.qsl_rst_rcvd)
, recordStr.getRst_rcvd()),
String.format(GeneralVariables.getStringFromResource(R.string.qsl_rst_sent)
, recordStr.getRst_sent()),
String.format(GeneralVariables.getStringFromResource(R.string.qsl_mode)
, recordStr.getMode()),
recordStr.getComment()
));
setTitle(String.format("%s--%s", recordStr.getCall(), recordStr.getStation_callsign()));//显示消息内容
this.mOutlinePaint = getStrokePaint(
mapView.getResources().getColor(
R.color.tracker_history_line_color), 3);
List<GeoPoint> pts = new ArrayList<>();
pts.add(GridOsmMapView.LatLng2GeoPoint(fromLatLng));
pts.add(GridOsmMapView.LatLng2GeoPoint(toLatLng));
setPoints(pts);
setGeodesic(true);
setInfoWindow(new GridRecordInfoWindow(R.layout.tracker_record_info_win, mapView));
mapView.getOverlayManager().add(this);
}
@SuppressLint("DefaultLocale")
public GridPolyLine(MapView mapView, LatLng fromLatLng, LatLng toLatLng, Ft8Message msg) {
super(mapView);
@ -480,11 +594,11 @@ public class GridOsmMapView {
, msg.snr, msg.time_sec
, MaidenheadGrid.getDistLatLngStr(fromLatLng, toLatLng)));
setTitle(msg.getMessageText());//显示消息内容
if (msg.inMyCall(GeneralVariables.myCallsign)) {
if (msg.inMyCall()) {
this.mOutlinePaint = getStrokePaint(
mapView.getResources().getColor(
R.color.tracker_in_my_line_color), 3);
}else {
} else {
this.mOutlinePaint = getStrokePaint(mapView.getResources().getColor(
R.color.tracker_line_color), 3);
}
@ -551,10 +665,16 @@ public class GridOsmMapView {
}
public void showNewInfo() {
if ((msg.fromDxcc || msg.fromItu || msg.fromCq)
&& !GeneralVariables.checkQSLCallsign(msg.callsignFrom)) {
if (msg != null) {
if ((msg.fromDxcc || msg.fromItu || msg.fromCq)
&& !GeneralVariables.checkQSLCallsign(msg.callsignFrom)) {
showInfoWindow();
}
}
if (recorder != null) {
showInfoWindow();
}
}
}
@ -617,7 +737,8 @@ public class GridOsmMapView {
private final Ft8Message msg;
@SuppressLint({"UseCompatLoadingForDrawables", "DefaultLocale"})
public GridMarker(Context context, MainViewModel mainViewModel, MapView mapView, String grid, Ft8Message msg) {
public GridMarker(Context context, MainViewModel mainViewModel, MapView mapView
, String grid, Ft8Message msg) {
super(mapView);
this.grid = grid;
this.context = context;
@ -798,13 +919,13 @@ public class GridOsmMapView {
/**
* 线
*/
public void setGrayLine(){
double[] lats= computeDayNightTerminator(System.currentTimeMillis());
LatLng[] grayLine=new LatLng[lats.length*3];
public void setGrayLine() {
double[] lats = computeDayNightTerminator(System.currentTimeMillis());
LatLng[] grayLine = new LatLng[lats.length * 3];
for (int i = 0; i < lats.length; i++) {
grayLine[i]=new LatLng((lats[i]-90),i);
grayLine[lats.length+i]=new LatLng((lats[i]-90),i);
grayLine[lats.length*2+i]=new LatLng((lats[i]-90),i);
grayLine[i] = new LatLng((lats[i] - 90), i);
grayLine[lats.length + i] = new LatLng((lats[i] - 90), i);
grayLine[lats.length * 2 + i] = new LatLng((lats[i] - 90), i);
}
Polyline line = new Polyline(gridMapView);
@ -812,7 +933,7 @@ public class GridOsmMapView {
line.setColor(context.getColor(R.color.tracker_gray_line_color));
List<GeoPoint> pts = new ArrayList<>();
for (int i = 0; i <grayLine.length ; i++) {
for (int i = 0; i < grayLine.length; i++) {
pts.add(GridOsmMapView.LatLng2GeoPoint(grayLine[i]));
}
line.setInfoWindow(null);

Wyświetl plik

@ -0,0 +1,89 @@
package com.bg7yoz.ft8cn.grid_tracker;
/**
*
* @author BGY70Z
* @date 2023-03-20
*/
import android.annotation.SuppressLint;
import android.graphics.Paint;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.bg7yoz.ft8cn.Ft8Message;
import com.bg7yoz.ft8cn.GeneralVariables;
import com.bg7yoz.ft8cn.R;
import org.osmdroid.views.MapView;
import org.osmdroid.views.overlay.OverlayWithIW;
import org.osmdroid.views.overlay.infowindow.InfoWindow;
public class GridRecordInfoWindow extends InfoWindow {
public static final int UNDEFINED_RES_ID = 0;
// static int mTitleId = 0;
// static int mDescriptionId = 0;
// static int mSubDescriptionId = 0;
// static int fromDxccImageId = 0;
// static int fromItuImageId = 0;
// static int fromCqImageId = 0;
// static int toDxccImageId = 0;
// static int toItuImageId = 0;
// static int toCqImageId = 0;
private final TextView titleView;
private final TextView descriptionView;
private final TextView subDescriptionView;
@SuppressLint("UseCompatLoadingForDrawables")
public GridRecordInfoWindow(int layoutResId, MapView mapView) {
super(layoutResId, mapView);
//setResIds(mapView.getContext());
titleView = (TextView) this.mView.findViewById(R.id.tracker_rec_info_bubble_title);
descriptionView = (TextView) this.mView.findViewById(R.id.tracker_rec_info_bubble_description);
subDescriptionView = (TextView) this.mView.findViewById(R.id.tracker_rec_info_bubble_subdescription);
ConstraintLayout layout=(ConstraintLayout) mView.findViewById(R.id.trackerGridRecInfoConstraintLayout);
this.mView.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent e) {
if (e.getAction() == 1) {
GridRecordInfoWindow.this.close();
}
return true;
}
});
}
@Override
public void onOpen(Object item) {
OverlayWithIW overlay = (OverlayWithIW) item;
String title = overlay.getTitle();
if (title == null) {
title = "";
}
if (this.mView == null) {
Log.w("OsmDroid", "Error trapped, BasicInfoWindow.open, mView is null!");
} else {
titleView.setText(title);
String snippet = overlay.getSnippet();
//Spanned snippetHtml = Html.fromHtml(snippet);
descriptionView.setText(snippet);
String subDesc = overlay.getSubDescription();
subDescriptionView.setText(subDesc);
}
}
@Override
public void onClose() {
}
}

Wyświetl plik

@ -1,9 +1,15 @@
package com.bg7yoz.ft8cn.grid_tracker;
/**
*
* @author BGY70Z
* @date 2023-03-20
*/
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Canvas;
import android.graphics.Color;
@ -12,6 +18,7 @@ import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
@ -22,6 +29,7 @@ import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
@ -37,12 +45,15 @@ import com.bg7yoz.ft8cn.databinding.ActivityGridTrackerMainBinding;
import com.bg7yoz.ft8cn.floatview.FloatView;
import com.bg7yoz.ft8cn.floatview.FloatViewButton;
import com.bg7yoz.ft8cn.ft8transmit.TransmitCallsign;
import com.bg7yoz.ft8cn.log.OnQueryQSLRecordCallsign;
import com.bg7yoz.ft8cn.log.QSLRecordStr;
import com.bg7yoz.ft8cn.timer.UtcTimer;
import com.bg7yoz.ft8cn.ui.CallingListAdapter;
import com.bg7yoz.ft8cn.ui.FreqDialog;
import com.bg7yoz.ft8cn.ui.SetVolumeDialog;
import com.bg7yoz.ft8cn.ui.ToastMessage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@ -62,9 +73,12 @@ public class GridTrackerMainActivity extends AppCompatActivity {
private CallingListAdapter callingListAdapter;
private boolean messageListIsClose = false;
private boolean configBarIsClose = false;
private QSLRecordStr qlsRecorder = null;//用于历史显示消息
private MutableLiveData<ArrayList<QSLRecordStr>> qslRecordList = new MutableLiveData<>();
@SuppressLint("NotifyDataSetChanged")
protected void onCreateDelay() {
protected void doAfterCreate() {
//设置消息列表
callingListAdapter.notifyDataSetChanged();
callMessagesRecyclerView.scrollToPosition(callingListAdapter.getItemCount() - 1);
@ -72,12 +86,38 @@ public class GridTrackerMainActivity extends AppCompatActivity {
setTipsRadioGroupClickerListener();//显示模式Group radio动作
setShowTipsSwitchClickerListener();//显示提示开关动作
readConfig();
//读取调用本activity的参数如果不为空说明要画参数中的消息
//画在日志界面中被选择的消息
Intent intentGet = getIntent();
qlsRecorder = (QSLRecordStr) intentGet.getSerializableExtra("qslList");
if (qlsRecorder != null) {
GridOsmMapView.GridPolyLine line = drawMessage(qlsRecorder);//在地图上画每一个消息
if (line != null) {
line.showInfoWindow();
}
}
//画日志界面查询出的全部消息
String queryKey = intentGet.getStringExtra("qslAll");
int queryFilter=intentGet.getIntExtra("queryFilter",0);
if (queryKey != null) {
ToastMessage.show(GeneralVariables.getStringFromResource(R.string.tracker_query_qso_info));
mainViewModel.databaseOpr.getQSLRecordByCallsign(true, 0, queryKey, queryFilter
, new OnQueryQSLRecordCallsign() {
@Override
public void afterQuery(ArrayList<QSLRecordStr> records) {
qslRecordList.postValue(records);
}
});
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//禁止休眠
getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
@ -95,7 +135,7 @@ public class GridTrackerMainActivity extends AppCompatActivity {
gridOsmMapView = new GridOsmMapView(getBaseContext(), binding.osmMap, mainViewModel);
//---todo ---转移1
callMessagesRecyclerView = binding.callMessagesRecyclerView;
callingListAdapter = new CallingListAdapter(this, mainViewModel
, mainViewModel.ft8Messages, CallingListAdapter.ShowMode.TRACKER);
@ -165,15 +205,7 @@ public class GridTrackerMainActivity extends AppCompatActivity {
gridOsmMapView.clearLines();
gridOsmMapView.clearMarkers();
for (Ft8Message msg : mainViewModel.currentMessages) {
gridOsmMapView.upgradeGridInfo(
msg.getMaidenheadGrid(mainViewModel.databaseOpr), msg.getMessageText()
, String.format("%d dBm , %.1f ms", msg.snr, msg.time_sec));
gridOsmMapView.drawLine(msg, mainViewModel.databaseOpr);
if (msg.checkIsCQ()) {
gridOsmMapView.addGridMarker(
msg.getMaidenheadGrid(mainViewModel.databaseOpr)
, msg);
}
drawMessage(msg);//在地图上画每一个消息
}
gridOsmMapView.showInfoWindows();
}
@ -283,18 +315,52 @@ public class GridTrackerMainActivity extends AppCompatActivity {
});
gridOsmMapView.initMap(GeneralVariables.getMyMaidenhead4Grid(), true);
qslRecordList.observe(this, new Observer<ArrayList<QSLRecordStr>>() {
@Override
public void onChanged(ArrayList<QSLRecordStr> qslRecordStrs) {
for (QSLRecordStr record : qslRecordStrs) {
drawMessage(record);//在地图上画每一个消息
}
gridOsmMapView.mapUpdate();
}
});
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
onCreateDelay();
//onCreateDelay();
closeMessages();
closeConfigBar();
doAfterCreate();
}
}, 1000);
setContentView(binding.getRoot());
}
/**
* CQ
*
* @param msg
*/
@SuppressLint("DefaultLocale")
private void drawMessage(Ft8Message msg) {
gridOsmMapView.upgradeGridInfo(
msg.getMaidenheadGrid(mainViewModel.databaseOpr), msg.getMessageText()
, String.format("%d dBm , %.1f ms", msg.snr, msg.time_sec));
gridOsmMapView.drawLine(msg, mainViewModel.databaseOpr);
if (msg.checkIsCQ()) {
gridOsmMapView.addGridMarker(
msg.getMaidenheadGrid(mainViewModel.databaseOpr)
, msg);
}
}
private GridOsmMapView.GridPolyLine drawMessage(QSLRecordStr recordStr) {
gridOsmMapView.upgradeGridInfo(recordStr);
return gridOsmMapView.drawLine(recordStr);
}
private void setShowTipsSwitchClickerListener() {
binding.trackerShowQsxSwitch.setOnClickListener(new View.OnClickListener() {
@ -685,9 +751,7 @@ public class GridTrackerMainActivity extends AppCompatActivity {
@Override
public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView
, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY
, int actionState, boolean isCurrentlyActive) {
public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
Ft8Message message = callingListAdapter.getMessageByViewHolder(viewHolder);
//制作呼叫背景的图标显示

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.html;
/**
* Http
* @author BGY70Z
* @date 2023-03-20
*/
import android.database.Cursor;
@ -73,8 +78,12 @@ public class HtmlContext {
+GeneralVariables.getStringFromResource(R.string.html_trace_callsign_and_grid_correspondence_table)
+"</a></td></tr>" +
"<tr><td class=\"default\" colspan=\"15\"><a href=/message>"
+GeneralVariables.getStringFromResource(R.string.html_query_communication_message)
+GeneralVariables.getStringFromResource(R.string.html_query_swl_message)
+"</a></td></tr>" +
"<tr><td class=\"default\" colspan=\"15\"><a href=/QSOSWLMSG>"
+GeneralVariables.getStringFromResource(R.string.html_query_qso_swl)
+"</a></td></tr>" +
"<tr><td class=\"default\" colspan=\"15\"><a href=/config>"
+GeneralVariables.getStringFromResource(R.string.html_query_configuration_information)
+"</a></td></tr>" +
@ -130,7 +139,7 @@ public class HtmlContext {
order++;
}
result.append("</table>");
cursor.close();
return result.toString();
}

Wyświetl plik

@ -1,8 +1,9 @@
package com.bg7yoz.ft8cn.icom;
/**
* iCom
* @author BG7YOZ
* ICom
* @author BGY70Z
* @date 2023-03-20
*/
public class IComPacketTypes {
private static final String TAG = "IComPacketTypes";

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.icom;
/**
* WIFI
* @author BGY70Z
* @date 2023-03-20
*/
import android.media.AudioAttributes;
import android.media.AudioFormat;
@ -99,7 +104,7 @@ public class IComWifiRig {
controlUdp.civUdp.sendCivData(data);
}
public void sendWaveData(short[] data){//发送音频数据到电台
public void sendWaveData(float[] data){//发送音频数据到电台
controlUdp.sendWaveData(data);
}

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.icom;
/**
* ICom
* @author BGY70Z
* @date 2023-03-20
*/
import android.util.Log;
@ -31,10 +36,59 @@ public class IcomAudioUdp extends IcomUdpBase {
}
public void sendTxAudioData(short[] audioData) {
doTXAudioRunnable.audioData=audioData;
doTXThreadPool.execute(doTXAudioRunnable);
public void sendTxAudioData(float[] audioData) {
if (audioData==null) return;
short[] temp=new short[audioData.length];
//要做一下浮点到16位int的转换
for (int i = 0; i < audioData.length; i++) {
float x = audioData[i];
if (x > 1.0)
x = 1.0f;
else if (x < -1.0)
x = -1.0f;
temp[i] = (short) (0.5 + (x * 32767.0));
}
doTXAudioRunnable.audioData=temp;
doTXThreadPool.execute(doTXAudioRunnable);
// new Thread(new Runnable() {
// @Override
// public void run() {
// final int partialLen = IComPacketTypes.TX_BUFFER_SIZE * 2;//数据包的长度
// //要转换一下到BYTE,小端模式
//
// //byte[] data = new byte[audioData.length * 2 + partialLen * 4];//多出一点空声音放在前后各20ms*2共80ms
// //先播放是给出空的声音for i 循环做了一个判断是给前面的空声音for j循环做得判断是让后面发送空声音
// byte[] audioPacket = new byte[partialLen];
// for (int i = 0; i < (audioData.length / IComPacketTypes.TX_BUFFER_SIZE) + 8; i++) {//多出6个周期前面3个后面3个多
// if (!isPttOn) break;
// long now = System.currentTimeMillis() - 1;//获取当前时间
//
// sendTrackedPacket(IComPacketTypes.AudioPacket.getTxAudioPacket(audioPacket
// , (short) 0, localId, remoteId, innerSeq));
// innerSeq++;
//
// Arrays.fill(audioPacket,(byte)0x00);
// if (i>=3) {//让前两个空数据发送出去
// for (int j = 0; j < IComPacketTypes.TX_BUFFER_SIZE; j++) {
// if ((i-3) * IComPacketTypes.TX_BUFFER_SIZE + j < audioData.length) {
// System.arraycopy(IComPacketTypes.shortToBigEndian((short)
// (audioData[(i-3) * IComPacketTypes.TX_BUFFER_SIZE + j]
// * GeneralVariables.volumePercent))
// , 0, audioPacket, j * 2, 2);
// }
// }
// }
// while (isPttOn) {
// if (System.currentTimeMillis() - now >= 21) {//20毫秒一个周期
// break;
// }
// }
// }
// Log.e(TAG, "run: 音频发送完毕!!" );
// Thread.currentThread().interrupt();
// }
// }).start();
}
private static class DoTXAudioRunnable implements Runnable{
IcomAudioUdp icomAudioUdp;
@ -68,7 +122,7 @@ public class IcomAudioUdp extends IcomUdpBase {
if ((i-3) * IComPacketTypes.TX_BUFFER_SIZE + j < audioData.length) {
System.arraycopy(IComPacketTypes.shortToBigEndian((short)
(audioData[(i-3) * IComPacketTypes.TX_BUFFER_SIZE + j]
* GeneralVariables.volumePercent))
* GeneralVariables.volumePercent))//乘以信号量的比率
, 0, audioPacket, j * 2, 2);
}
}

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.icom;
/**
* IComCIV
* @author BGY70Z
* @date 2023-03-20
*/
import android.util.Log;

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.icom;
/**
* ICom
* @author BGY70Z
* @date 2023-03-20
*/
import android.util.Log;
@ -180,7 +185,7 @@ public class IcomControlUdp extends IcomUdpBase {
*
* @param data
*/
public void sendWaveData(short[] data){
public void sendWaveData(float[] data){
audioUdp.sendTxAudioData(data);
}

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.icom;
/**
* ICom
* @author BGY70Z
* @date 2023-03-20
*/
import java.util.ArrayList;

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.icom;
/**
* udp
* @author BGY70Z
* @date 2023-03-20
*/
import android.util.Log;

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.icom;
/**
* udp
* @author BGY70Z
* @date 2023-03-20
*/
import android.util.Log;
@ -43,7 +48,23 @@ public class IcomUdpClient {
sendDataRunnable.data=data;
sendDataRunnable.port=port;
sendDataThreadPool.execute(sendDataRunnable);
// new Thread(new Runnable() {
// @Override
// public void run() {
// DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
// synchronized (this) {
// try {
// sendSocket.send(packet);
// } catch (IOException e) {
// e.printStackTrace();
// Log.e(TAG, "IComUdpClient: " + e.getMessage());
// if (onUdpEvents!=null){
// onUdpEvents.OnUdpSendIOException(e);
// }
// }
// }
// }
// }).start();
}
private static class SendDataRunnable implements Runnable{
byte[] data;
@ -107,6 +128,31 @@ public class IcomUdpClient {
private void receiveData() {
doReceiveThreadPool.execute(doReceiveRunnable);
// new Thread(new Runnable() {
// @Override
// public void run() {
// while (activated) {
// byte[] data = new byte[MAX_BUFFER_SIZE];
// DatagramPacket packet = new DatagramPacket(data, data.length);
// try {
// sendSocket.receive(packet);
// if (onUdpEvents != null) {
// byte[] temp = Arrays.copyOf(packet.getData(), packet.getLength());
// onUdpEvents.OnReceiveData(sendSocket, packet, temp);
// }
// //Log.d(TAG, "receiveData:host ip: " + packet.getAddress().getHostName());
// } catch (IOException e) {
// e.printStackTrace();
// Log.e(TAG, "receiveData: error:" + e.getMessage());
// }
//
// }
// Log.e(TAG, "udpClient: is exit!");
// sendSocket.close();
// sendSocket = null;
// }
// }).start();
}
public void setOnUdpEvents(OnUdpEvents onUdpEvents) {

Wyświetl plik

@ -0,0 +1,117 @@
package com.bg7yoz.ft8cn.log;
/**
* SWL QSO 2String KEYHashMap
* guava:31.1-jre
*
* BG7YOZ
* 2023-03-20
*
*/
import androidx.annotation.Nullable;
import com.google.common.collect.Table;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
public class HashTable implements Table {
@Override
public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) {
return false;
}
@Override
public boolean containsRow(@Nullable Object rowKey) {
return false;
}
@Override
public boolean containsColumn(@Nullable Object columnKey) {
return false;
}
@Override
public boolean containsValue(@Nullable Object value) {
return false;
}
@Nullable
@Override
public @org.checkerframework.checker.nullness.qual.Nullable Object get(@Nullable Object rowKey, @Nullable Object columnKey) {
return null;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public int size() {
return 0;
}
@Override
public void clear() {
}
@Nullable
@Override
public @org.checkerframework.checker.nullness.qual.Nullable Object put(Object rowKey, Object columnKey, Object value) {
return null;
}
@Override
public void putAll(Table table) {
}
@Nullable
@Override
public @org.checkerframework.checker.nullness.qual.Nullable Object remove(@Nullable Object rowKey, @Nullable Object columnKey) {
return null;
}
@Override
public Map row(Object rowKey) {
return null;
}
@Override
public Map column(Object columnKey) {
return null;
}
@Override
public Set<Cell> cellSet() {
return null;
}
@Override
public Set rowKeySet() {
return null;
}
@Override
public Set columnKeySet() {
return null;
}
@Override
public Collection values() {
return null;
}
@Override
public Map rowMap() {
return null;
}
@Override
public Map columnMap() {
return null;
}
}

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.log;
/**
*
* @author BGY70Z
* @date 2023-03-20
*/
import android.annotation.SuppressLint;
import android.content.Context;
@ -22,7 +27,7 @@ import com.bg7yoz.ft8cn.maidenhead.MaidenheadGrid;
import java.util.ArrayList;
public class LogCallsignAdapter extends RecyclerView.Adapter<LogCallsignAdapter.LogCallsignItemHolder> {
private ArrayList<QSLCallsignRecord> callsignRecords=new ArrayList<>();
//private ArrayList<QSLCallsignRecord> callsignRecords=new ArrayList<>();
private final MainViewModel mainViewModel;
private final Context context;
@ -48,7 +53,7 @@ public class LogCallsignAdapter extends RecyclerView.Adapter<LogCallsignAdapter.
* @return
*/
public QSLCallsignRecord getRecord(int position){
return callsignRecords.get(position);
return mainViewModel.callsignRecords.get(position);
}
/**
*
@ -56,10 +61,18 @@ public class LogCallsignAdapter extends RecyclerView.Adapter<LogCallsignAdapter.
*/
@SuppressLint("NotifyDataSetChanged")
public void setQSLCallsignList(ArrayList<QSLCallsignRecord> records){
callsignRecords=records;
mainViewModel.callsignRecords.addAll(records);
//mainViewModel.callsignRecords=records;
notifyDataSetChanged();
}
/**
*
*/
public void clearRecords(){
mainViewModel.callsignRecords.clear();
}
@SuppressLint("SetTextI18n")
@Override
public void onBindViewHolder(@NonNull LogCallsignItemHolder holder, int position) {
@ -68,7 +81,7 @@ public class LogCallsignAdapter extends RecyclerView.Adapter<LogCallsignAdapter.
}else {
holder.logCallSignQSLHolderConstraintLayout.setBackgroundResource(R.drawable.calling_list_cell_1_style);
}
holder.record=callsignRecords.get(position);
holder.record=mainViewModel.callsignRecords.get(position);
if (holder.record.isQSL||holder.record.isLotW_QSL){
@ -147,7 +160,7 @@ public class LogCallsignAdapter extends RecyclerView.Adapter<LogCallsignAdapter.
@Override
public int getItemCount() {
return callsignRecords.size();
return mainViewModel.callsignRecords.size();
}
static class LogCallsignItemHolder extends RecyclerView.ViewHolder{

Wyświetl plik

@ -13,7 +13,9 @@ import java.util.HashMap;
* getFileContext
* getLogBody<eoh>
* getLogRecordsHashMapHashMapKeyvalue
* @author BG7YOZ
*
* @author BGY70Z
* @date 2023-03-20
*/
public class LogFileImport {

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.log;
/**
*
* @author BGY70Z
* @date 2023-03-20
*/
import android.annotation.SuppressLint;
import android.content.Context;
@ -21,13 +26,13 @@ import com.bg7yoz.ft8cn.callsign.OnAfterQueryCallsignLocation;
import java.util.ArrayList;
public class LogQSLAdapter extends RecyclerView.Adapter<LogQSLAdapter.LogQSLItemHolder> {
private ArrayList<QSLRecordStr> records=new ArrayList<>();
private ArrayList<QSLRecordStr> qslRecords=new ArrayList<>();
private final MainViewModel mainViewModel;
private final Context context;
public LogQSLAdapter(Context context,MainViewModel mainViewModel) {
public LogQSLAdapter(Context context, MainViewModel mainViewModel) {
this.mainViewModel = mainViewModel;
this.context=context;
this.context = context;
}
@NonNull
@ -40,96 +45,105 @@ public class LogQSLAdapter extends RecyclerView.Adapter<LogQSLAdapter.LogQSLItem
@SuppressLint("NotifyDataSetChanged")
public void setQSLList(ArrayList<QSLRecordStr> list){
records=list;
public void setQSLList(ArrayList<QSLRecordStr> list) {
qslRecords.addAll(list);
notifyDataSetChanged();
}
/**
*
* @param position
*
*/
public void deleteRecord(int position){
mainViewModel.databaseOpr.deleteQSLByID(records.get(position).id);
records.remove(position);
public void clearRecords(){
qslRecords.clear();
}
public QSLRecordStr getRecord(int position){
return records.get(position);
/**
*
*
* @param position
*/
public void deleteRecord(int position) {
mainViewModel.databaseOpr.deleteQSLByID(qslRecords.get(position).id);
qslRecords.remove(position);
}
public QSLRecordStr getRecord(int position) {
return qslRecords.get(position);
}
/**
*
*
* @param position
* @param b
* @param b
*/
public void setRecordIsQSL(int position,boolean b){
records.get(position).isQSL=b;
mainViewModel.databaseOpr.setQSLTableIsQSL(b,records.get(position).id);
public void setRecordIsQSL(int position, boolean b) {
qslRecords.get(position).isQSL = b;
mainViewModel.databaseOpr.setQSLTableIsQSL(b, qslRecords.get(position).id);
}
@SuppressLint({"DefaultLocale", "SetTextI18n"})
@Override
public void onBindViewHolder(@NonNull LogQSLItemHolder holder, int position) {
holder.record=records.get(position);
holder.record = qslRecords.get(position);
if ((position%2)==0){
if ((position % 2) == 0) {
holder.logQSLHolderConstraintLayout.setBackgroundResource(R.drawable.calling_list_cell_0_style);
}else {
} else {
holder.logQSLHolderConstraintLayout.setBackgroundResource(R.drawable.calling_list_cell_1_style);
}
holder.logQSLCallsignTextView.setText(holder.record.getCall());
if (!holder.record.getGridsquare().equals("")) {
holder.logQSOGridTextView.setText(String.format(GeneralVariables.getStringFromResource(R.string.qsl_grid)
, holder.record.getGridsquare()));
}else {
} else {
holder.logQSOGridTextView.setText("");
}
holder.logQSLMyCallsignTextView.setText(holder.record.getStation_callsign());
if (!holder.record.getMy_gridsquare().equals("")) {
holder.logQSLMyGridTextView.setText(String.format(GeneralVariables.getStringFromResource(R.string.qsl_grid)
, holder.record.getMy_gridsquare()));
}else {
} else {
holder.logQSLMyGridTextView.setText("");
}
holder.logQSOStartTimeTextView.setText(String.format(GeneralVariables.getStringFromResource(R.string.qsl_start_time)
, holder.record.getTime_on()));
holder.logQSOEndTimeTextView.setText(String.format(GeneralVariables.getStringFromResource(R.string.qsl_end_time)
,holder.record.getTime_off()));
, holder.record.getTime_off()));
holder.logQSOReceiveTextView.setText(String.format(GeneralVariables.getStringFromResource(R.string.qsl_rst_rcvd)
,holder.record.getRst_rcvd()));
, holder.record.getRst_rcvd()));
holder.logQSOSendTextView.setText(String.format(GeneralVariables.getStringFromResource(R.string.qsl_rst_sent)
,holder.record.getRst_sent()));
, holder.record.getRst_sent()));
holder.logQSLBandTextView.setText(String.format(GeneralVariables.getStringFromResource(R.string.qsl_band)
,holder.record.getBand()));
, holder.record.getBand()));
holder.logQSLFreqTextView.setText(String.format(GeneralVariables.getStringFromResource(R.string.qsl_freq)
,holder.record.getFreq()));
, holder.record.getFreq()));
holder.logQSOModeTextView.setText(String.format(GeneralVariables.getStringFromResource(R.string.qsl_mode)
,holder.record.getMode()));
, holder.record.getMode()));
holder.logQSOcCommentTextView.setText(holder.record.getComment());
if (holder.record.isLotW_QSL){
if (holder.record.isLotW_QSL) {
holder.logIsQSLTextView.setText(GeneralVariables.getStringFromResource(R.string.qsl_lotw_confirmation));
holder.logIsQSLTextView.setTextColor(context.getResources().getColor(
R.color.is_qsl_text_color));
}else if(holder.record.isQSL){
} else if (holder.record.isQSL) {
holder.logIsQSLTextView.setText(GeneralVariables.getStringFromResource(R.string.qsl_manual_confirmation));
holder.logIsQSLTextView.setTextColor(context.getResources().getColor(
R.color.is_qsl_text_color));
}else
{
} else {
holder.logIsQSLTextView.setText(GeneralVariables.getStringFromResource(R.string.qsl_unconfirmed));
holder.logIsQSLTextView.setTextColor(context.getResources().getColor(
R.color.is_not_qsl_text_color));
}
//查呼号的位置
if (holder.record.where==null){
if (holder.record.where == null) {
setQueryHolderCallsign(holder);
}else if (holder.record.where.equals("")){
} else if (holder.record.where.equals("")) {
setQueryHolderCallsign(holder);
}else {
} else {
holder.logQSLWhereTextView.setText(holder.record.where);
}
}
@ -146,7 +160,7 @@ public class LogQSLAdapter extends RecyclerView.Adapter<LogQSLAdapter.LogQSLItem
if (GeneralVariables.isChina) {
holder.logQSLWhereTextView.setText(callsignInfo.CountryNameCN);
holder.record.where = callsignInfo.CountryNameCN;
}else {
} else {
holder.logQSLWhereTextView.setText(callsignInfo.CountryNameEn);
holder.record.where = callsignInfo.CountryNameEn;
}
@ -158,37 +172,37 @@ public class LogQSLAdapter extends RecyclerView.Adapter<LogQSLAdapter.LogQSLItem
}
@Override
public int getItemCount() {
return records.size();
return qslRecords.size();
}
public ArrayList<QSLRecordStr> getRecords() {
return qslRecords;
}
static class LogQSLItemHolder extends RecyclerView.ViewHolder {
QSLRecordStr record;
ConstraintLayout logQSLHolderConstraintLayout;
TextView logQSLCallsignTextView,logQSOGridTextView,logQSOStartTimeTextView
,logQSOEndTimeTextView,logQSOReceiveTextView,logQSOSendTextView
,logQSLBandTextView,logQSLFreqTextView,logQSOModeTextView
,logQSOcCommentTextView,logQSLMyCallsignTextView
,logQSLMyGridTextView,logQSLWhereTextView,logIsQSLTextView;
TextView logQSLCallsignTextView, logQSOGridTextView, logQSOStartTimeTextView, logQSOEndTimeTextView, logQSOReceiveTextView, logQSOSendTextView, logQSLBandTextView, logQSLFreqTextView, logQSOModeTextView, logQSOcCommentTextView, logQSLMyCallsignTextView, logQSLMyGridTextView, logQSLWhereTextView, logIsQSLTextView;
public LogQSLItemHolder(@NonNull View itemView) {
super(itemView);
logQSLHolderConstraintLayout=itemView.findViewById(R.id.logQSLHolderConstraintLayout) ;
logQSLCallsignTextView=itemView.findViewById(R.id.logQSLCallsignTextView) ;
logQSOGridTextView=itemView.findViewById(R.id.logQSOGridTextView) ;
logQSOStartTimeTextView=itemView.findViewById(R.id.logQSOStartTimeTextView) ;
logQSOEndTimeTextView=itemView.findViewById(R.id.logQSOEndTimeTextView) ;
logQSOReceiveTextView=itemView.findViewById(R.id.logQSOReceiveTextView) ;
logQSOSendTextView=itemView.findViewById(R.id.logQSOSendTextView) ;
logQSLBandTextView=itemView.findViewById(R.id.logQSLBandTextView) ;
logQSLFreqTextView=itemView.findViewById(R.id.logQSLFreqTextView) ;
logQSOModeTextView=itemView.findViewById(R.id.logQSOModeTextView) ;
logQSOcCommentTextView=itemView.findViewById(R.id.logQSOcCommentTextView) ;
logQSLMyCallsignTextView=itemView.findViewById(R.id.logQSLMyCallsignTextView) ;
logQSLMyGridTextView=itemView.findViewById(R.id.logQSLMyGridTextView) ;
logQSLWhereTextView=itemView.findViewById(R.id.logQSLWhereTextView) ;
logIsQSLTextView=itemView.findViewById(R.id.logIsQSLTextView) ;
logQSLHolderConstraintLayout = itemView.findViewById(R.id.logQSLHolderConstraintLayout);
logQSLCallsignTextView = itemView.findViewById(R.id.logQSLCallsignTextView);
logQSOGridTextView = itemView.findViewById(R.id.logQSOGridTextView);
logQSOStartTimeTextView = itemView.findViewById(R.id.logQSOStartTimeTextView);
logQSOEndTimeTextView = itemView.findViewById(R.id.logQSOEndTimeTextView);
logQSOReceiveTextView = itemView.findViewById(R.id.logQSOReceiveTextView);
logQSOSendTextView = itemView.findViewById(R.id.logQSOSendTextView);
logQSLBandTextView = itemView.findViewById(R.id.logQSLBandTextView);
logQSLFreqTextView = itemView.findViewById(R.id.logQSLFreqTextView);
logQSOModeTextView = itemView.findViewById(R.id.logQSOModeTextView);
logQSOcCommentTextView = itemView.findViewById(R.id.logQSOcCommentTextView);
logQSLMyCallsignTextView = itemView.findViewById(R.id.logQSLMyCallsignTextView);
logQSLMyGridTextView = itemView.findViewById(R.id.logQSLMyGridTextView);
logQSLWhereTextView = itemView.findViewById(R.id.logQSLWhereTextView);
logIsQSLTextView = itemView.findViewById(R.id.logIsQSLTextView);
itemView.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
@Override
@ -196,18 +210,25 @@ public class LogQSLAdapter extends RecyclerView.Adapter<LogQSLAdapter.LogQSLItem
, ContextMenu.ContextMenuInfo contextMenuInfo) {
view.setTag(getAdapterPosition());
//添加菜单的参数i1:组i2:id值i3:显示顺序
if (record.isQSL){
contextMenu.add(0,0,0
,String.format(GeneralVariables.getStringFromResource(R.string.qsl_cancel_confirmation)
,record.getCall())).setActionView(view);
}else {
contextMenu.add(0,1,0
,String.format(GeneralVariables.getStringFromResource(R.string.qsl_manual_confirmation_s)
,record.getCall())).setActionView(view);
if (record.isQSL) {
contextMenu.add(0, 0, 0
, String.format(GeneralVariables.getStringFromResource(R.string.qsl_cancel_confirmation)
, record.getCall())).setActionView(view);
} else {
contextMenu.add(0, 1, 0
, String.format(GeneralVariables.getStringFromResource(R.string.qsl_manual_confirmation_s)
, record.getCall())).setActionView(view);
}
contextMenu.add(0, 2, 0
, String.format(GeneralVariables.getStringFromResource(R.string.qsl_qrz_confirmation_s)
, record.getCall())).setActionView(view);
if (record.getGridsquare() != null && !record.getGridsquare().equals("")
&& record.getMy_gridsquare() != null && !record.getMy_gridsquare().equals("")) {
contextMenu.add(0, 3, 0
, GeneralVariables.getStringFromResource(R.string.log_menu_location))
.setActionView(view);
}
contextMenu.add(0,2,0
,String.format(GeneralVariables.getStringFromResource(R.string.qsl_qrz_confirmation_s)
,record.getCall())).setActionView(view);
}
});
}

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.log;
/**
*
* @author BGY70Z
* @date 2023-03-20
*/
import java.util.ArrayList;

Wyświetl plik

@ -1,4 +1,9 @@
package com.bg7yoz.ft8cn.log;
/**
*
* @author BGY70Z
* @date 2023-03-20
*/
import java.util.ArrayList;

Wyświetl plik

@ -1,5 +1,10 @@
package com.bg7yoz.ft8cn.log;
/**
*
* @author BGY70Z
* @date 2023-03-20
*/
public class QSLCallsignRecord {
private String callsign;
private String mode;

Wyświetl plik

@ -1,8 +1,8 @@
package com.bg7yoz.ft8cn.log;
import android.util.Log;
import com.bg7yoz.ft8cn.Ft8Message;
import com.bg7yoz.ft8cn.GeneralVariables;
import com.bg7yoz.ft8cn.R;
import com.bg7yoz.ft8cn.maidenhead.MaidenheadGrid;
@ -17,11 +17,12 @@ import java.util.Objects;
* isLotW_import使JTDXFT8CN
* isLotW_QSL
* isQSL
* @author BG7YOZ
* @author BGY70Z
* @date 2023-03-20
*/
public class QSLRecord {
private static final String TAG = "QSLRecord";
public long id=-1;
public long id = -1;
//private long startTime;//起始时间
private String qso_date;
private String time_on;
@ -29,7 +30,7 @@ public class QSLRecord {
private String time_off;
//private long endTime;//结束时间
private String myCallsign;//我的呼号
private final String myCallsign;//我的呼号
private String myMaidenGrid;//我的网格
private String toCallsign;//对方的呼号
private String toMaidenGrid;//对方的网格
@ -44,7 +45,24 @@ public class QSLRecord {
public boolean isLotW_import = false;//是否是从外部数据导入的,此项需要在数据库中比对才能设定
public boolean isLotW_QSL = false;//是否是lotw确认的
public boolean saved=false;//是否被保存到数据库中
public boolean saved = false;//是否被保存到数据库中
/**
* SWL QSOSWL QSO
* @param msg FT8
*/
public QSLRecord(Ft8Message msg) {
this.qso_date_off = UtcTimer.getYYYYMMDD(msg.utcTime);
this.time_off = UtcTimer.getTimeHHMMSS(msg.utcTime);
this.myCallsign = msg.callsignFrom;
this.toCallsign = msg.callsignTo;
wavFrequency=Math.round(msg.freq_hz);
sendReport=-100;
receivedReport=-100;
bandLength=BaseRigOperation.getMeterFromFreq(GeneralVariables.band);//获取波长
bandFreq=GeneralVariables.band;
comment="SWL By FT8CN";
}
/**
*
@ -87,7 +105,7 @@ public class QSLRecord {
String.format("Distance: %s, QSO by FT8CN", distance);
}
public void update(QSLRecord record){
public void update(QSLRecord record) {
this.qso_date_off = record.qso_date_off;
this.time_off = record.time_off;
this.toMaidenGrid = record.toMaidenGrid;
@ -96,19 +114,19 @@ public class QSLRecord {
}
public QSLRecord(HashMap<String, String> map) {
isLotW_import=true;//说明是外部导入的数据
isLotW_import = true;//说明是外部导入的数据
if (map.containsKey("CALL")) {//对方呼号
toCallsign = map.get("CALL");
}
if (map.containsKey("STATION_CALLSIGN")) {//我的呼号
myCallsign = map.get("STATION_CALLSIGN");
}else {
myCallsign="";
} else {
myCallsign = "";
}
if (map.containsKey("BAND")) {//载波波长
bandLength = map.get("BAND");
}else {
bandLength="";
} else {
bandLength = "";
}
if (map.containsKey("FREQ")) {//载波频率
@ -122,8 +140,8 @@ public class QSLRecord {
}
if (map.containsKey("MODE")) {//模式
mode = map.get("MODE");
}else {
mode="";
} else {
mode = "";
}
if (map.containsKey("QSO_DATE")) {//通联日期
qso_date = map.get("QSO_DATE");
@ -132,8 +150,8 @@ public class QSLRecord {
}
if (map.containsKey("TIME_ON")) {//通联起始时间
time_on = map.get("TIME_ON");
}else {
time_on="";
} else {
time_on = "";
}
if (map.containsKey("QSO_DATE_OFF")) {//通联结束日期此字段只在JTDX中有。
qso_date_off = map.get("QSO_DATE_OFF");
@ -142,8 +160,8 @@ public class QSLRecord {
}
if (map.containsKey("TIME_OFF")) {//通联结束时间n1mm、Log32、JTDX有Lotw没有
time_off = map.get("TIME_OFF");
}else {
time_off="";
} else {
time_off = "";
}
if (map.containsKey("QSL_RCVD")) {//通联互认lotw中有。
isLotW_QSL = Objects.requireNonNull(map.get("QSL_RCVD")).equalsIgnoreCase("Y");
@ -157,18 +175,17 @@ public class QSLRecord {
if (map.containsKey("MY_GRIDSQUARE")) {//我的网格lotw,log32有lotw根据设置不同也可能没有N1MM没有网格
myMaidenGrid = map.get("MY_GRIDSQUARE");
}else {
myMaidenGrid="";
} else {
myMaidenGrid = "";
}
if (map.containsKey("GRIDSQUARE")) {//对方的网格lotw,log32有lotw根据设置不同也可能没有N1MM没有网格
toMaidenGrid = map.get("GRIDSQUARE");
}else {
toMaidenGrid="";
} else {
toMaidenGrid = "";
}
if (map.containsKey("RST_RCVD")) {//接收到的报告。信号报告n1mm,log32,jtdx有Lotw没有
try {//要把float转成Long
receivedReport = Integer.parseInt(Objects.requireNonNull(map.get("RST_RCVD").trim()));
@ -176,8 +193,8 @@ public class QSLRecord {
e.printStackTrace();
Log.e(TAG, "QSLRecord: RST_RCVD:" + e.getMessage());
}
}else {
receivedReport=-120;
} else {
receivedReport = -120;
}
if (map.containsKey("RST_SENT")) {//接收到的报告。信号报告n1mm,log32,jtdx有Lotw没有
@ -187,8 +204,8 @@ public class QSLRecord {
e.printStackTrace();
Log.e(TAG, "QSLRecord: RST_SENT:" + e.getMessage());
}
}else {
sendReport=-120;
} else {
sendReport = -120;
}
if (map.containsKey("COMMENT")) {//注释JTDX中有
comment = map.get("COMMENT");
@ -200,6 +217,13 @@ public class QSLRecord {
}
/**
* SWL QSO
* @return
*/
public String swlQSOInfo(){
return String.format("QSO of SWL:%s<--%s",toCallsign,myCallsign);
}
@Override
public String toString() {
return "QSLRecord{" +
@ -225,8 +249,9 @@ public class QSLRecord {
", comment='" + comment + '\'' +
'}';
}
public String toHtmlString() {
String ss=saved?"<font color=red>, saved=true</font>":", saved=false";
String ss = saved ? "<font color=red>, saved=true</font>" : ", saved=false";
return "QSLRecord{" +
"id=" + id +
", qso_date='" + qso_date + '\'' +
@ -246,10 +271,11 @@ public class QSLRecord {
", isQSL=" + isQSL +
", isLotW_import=" + isLotW_import +
", isLotW_QSL=" + isLotW_QSL +
ss+
ss +
", comment='" + comment + '\'' +
'}';
}
public String getBandLength() {
return bandLength;
}
@ -283,6 +309,10 @@ public class QSLRecord {
return myMaidenGrid;
}
public void setMyMaidenGrid(String myMaidenGrid) {
this.myMaidenGrid = myMaidenGrid;
}
public int getSendReport() {
return sendReport;
}
@ -331,4 +361,12 @@ public class QSLRecord {
public void setReceivedReport(int receivedReport) {
this.receivedReport = receivedReport;
}
public void setQso_date(String qso_date) {
this.qso_date = qso_date;
}
public void setTime_on(String time_on) {
this.time_on = time_on;
}
}

Wyświetl plik

@ -1,10 +1,18 @@
package com.bg7yoz.ft8cn.log;
import android.os.Parcel;
import android.os.Parcelable;
import com.bg7yoz.ft8cn.Ft8Message;
import java.io.Serializable;
/**
* ADAPTER
* @author BG7YOZ
* @author BGY70Z
* @date 2023-03-20
*/
public class QSLRecordStr {
public class QSLRecordStr implements Serializable {
public int id;
private String call="";
private String gridsquare="";

Wyświetl plik

@ -0,0 +1,159 @@
package com.bg7yoz.ft8cn.log;
import com.bg7yoz.ft8cn.Ft8Message;
import com.bg7yoz.ft8cn.GeneralVariables;
import com.bg7yoz.ft8cn.timer.UtcTimer;
import java.util.ArrayList;
/**
* SWLQSO
* QSOFT863
* 1.CQ C1 grid
* 2.C1 C2 grid
* ------------
* 3.C2 C1 report
* 4.C1 C2 r-report
* ------------
* 5.C2 C1 RR73(RRR)
* 6.C1 C2 73
* ------------
* <p>
* QSO
* RR73RRR73
* swlQsoListkeyHashMap,QSO
* C1C2station_callsigncall
*
* @author BG7YOZ
* @date 2023-03-07
*/
public class SWLQsoList {
private static final String TAG = "SWLQsoList";
//通联成功的列表防止重复两个KEY顺序分别是station_callsign和callBoolean=true,已经QSO
private final HashTable qsoList =new HashTable();
public SWLQsoList() {
}
/**
* QSO
*
* @param newMessages FT8
* @param allMessages FT8
* @param onFoundSwlQso
*/
public void findSwlQso(ArrayList<Ft8Message> newMessages, ArrayList<Ft8Message> allMessages
, OnFoundSwlQso onFoundSwlQso) {
for (int i = 0; i < newMessages.size(); i++) {
Ft8Message msg = newMessages.get(i);
if (msg.inMyCall()) continue;//对包含我自己的消息不处理
if (GeneralVariables.checkFun4_5(msg.extraInfo)//结束标识RRR、RR73、73
&& !qsoList.contains(msg.callsignFrom, msg.callsignTo)) {//没有QSO记录
QSLRecord qslRecord = new QSLRecord(msg);
if (checkPart2(allMessages, qslRecord)) {//找双方的信号报告一个基本的QSO必须有双方的信号报告
checkPart1(allMessages, qslRecord);//找双方的网格报告顺便更新time_on的时间
if (onFoundSwlQso != null) {//触发回调,用于记录到数据库
qsoList.put(msg.callsignFrom, msg.callsignTo, true);//把QSO记录保存下来
onFoundSwlQso.doFound(qslRecord);//触发找到QSO的动作
}
}
}
}
}
/**
* 2便QSLRecord
*
* @param allMessages
* @param record QSLRecord
* @return 02
*/
private boolean checkPart2(ArrayList<Ft8Message> allMessages, QSLRecord record) {
boolean foundFromReport = false;
boolean foundToReport = false;
long time_on = System.currentTimeMillis();//先把当前的时间作为最早时间
for (int i = allMessages.size() - 1; i >= 0; i--) {
Ft8Message msg = allMessages.get(i);
if (msg.callsignFrom.equals(record.getMyCallsign())
&& msg.callsignTo.equals(record.getToCallsign())
&& !foundFromReport) {//callsignFrom发出的信号报告
int report = GeneralVariables.checkFun2_3(msg.extraInfo);
if (time_on > msg.utcTime) time_on = msg.utcTime;//取最早的时间
if (report != -100) {
record.setSendReport(report);
foundFromReport = true;
}
}
if (msg.callsignFrom.equals(record.getToCallsign())
&& msg.callsignTo.equals(record.getMyCallsign())
&& !foundToReport) {//callsignTo发出的信号报告
int report = GeneralVariables.checkFun2_3(msg.extraInfo);
if (time_on > msg.utcTime) time_on = msg.utcTime;//取最早的时间
if (report != -100) {
record.setReceivedReport(report);
foundToReport = true;
}
}
if (foundToReport && foundFromReport) {//如果双方的信号报告都找到了,就退出循环
record.setQso_date(UtcTimer.getYYYYMMDD(time_on));
record.setTime_on(UtcTimer.getTimeHHMMSS(time_on));
break;
}
}
return foundToReport && foundFromReport;//双方的信号报告都有才算一个QSO
}
/**
* 2便QSLRecord
*
* @param allMessages
* @param record QSLRecord
*/
private void checkPart1(ArrayList<Ft8Message> allMessages, QSLRecord record) {
boolean foundFromGrid = false;
boolean foundToGrid = false;
long time_on = System.currentTimeMillis();//先把当前的时间作为最早时间
for (int i = allMessages.size() - 1; i >= 0; i--) {
Ft8Message msg = allMessages.get(i);
if (!foundFromGrid
&& msg.callsignFrom.equals(record.getMyCallsign())
&& (msg.callsignTo.equals(record.getToCallsign()) || msg.checkIsCQ())) {//callsignFrom的网格报告
if (GeneralVariables.checkFun1_6(msg.extraInfo)) {
record.setMyMaidenGrid(msg.extraInfo.trim());
foundFromGrid = true;
}
if (time_on > msg.utcTime) time_on = msg.utcTime;//取最早的时间
}
if (!foundToGrid
&& msg.callsignFrom.equals(record.getToCallsign())
&& (msg.callsignTo.equals(record.getMyCallsign())|| msg.checkIsCQ())) {//callsignTo发出的信号报告
if (GeneralVariables.checkFun1_6(msg.extraInfo)) {
record.setToMaidenGrid(msg.extraInfo.trim());
foundToGrid = true;
}
if (time_on > msg.utcTime) time_on = msg.utcTime;//取最早的时间
}
if (foundToGrid && foundFromGrid) {//如果双方的信号报告都找到了,就退出循环
break;
}
}
if (foundFromGrid || foundToGrid) {//发现网格报告,至少一个方向的
record.setQso_date(UtcTimer.getYYYYMMDD(time_on));
record.setTime_on(UtcTimer.getTimeHHMMSS(time_on));
}
}
public interface OnFoundSwlQso {
void doFound(QSLRecord record);
}
}

Wyświetl plik

@ -1,5 +1,9 @@
package com.bg7yoz.ft8cn.maidenhead;
/**
*
* @author BGY70Z
* @date 2023-03-20
*/
import android.annotation.SuppressLint;
import android.content.Context;
@ -12,11 +16,6 @@ import com.google.android.gms.maps.model.LatLng;
import java.util.List;
/**
*
*
* @author BG7YOZ
*/
public class MaidenheadGrid {
private static final String TAG = "MaidenheadGrid";
private static final double EARTH_RADIUS = 6371393; // 平均半径,单位m不是赤道半径。赤道为6378左右
@ -221,6 +220,10 @@ public class MaidenheadGrid {
latLngs[2] = new LatLng(lat2,lng2);
latLngs[3] = new LatLng(lat2,lng1);
// Log.e(TAG, "gridToPolygon: latLng0"+latLngs[0].toString() );
// Log.e(TAG, "gridToPolygon: latLng1"+latLngs[1].toString() );
// Log.e(TAG, "gridToPolygon: latLng2"+latLngs[2].toString() );
// Log.e(TAG, "gridToPolygon: latLng3"+latLngs[3].toString() );
return latLngs;

Some files were not shown because too many files have changed in this diff Show More