kopia lustrzana https://github.com/N0BOY/FT8CN
Porównaj commity
50 Commity
Autor | SHA1 | Data |
---|---|---|
NØBOY | ef4f2fab92 | |
wangg | 84fe2ffdf8 | |
NØBOY | e965043cbd | |
wangg | 822a353abe | |
NØBOY | 89500cb162 | |
wangg | 48e9c9066c | |
wangg | 069205d44a | |
wangg | ffed0a8898 | |
NØBOY | 68e1c79b6a | |
Sunguk Lee | 1f09f61e8d | |
Sunguk Lee | 8bbcc2be6f | |
Sunguk Lee | ca08aa3479 | |
Sunguk Lee | 40d4c06a65 | |
Sunguk Lee | 262704af00 | |
Sunguk Lee | 8f5975b107 | |
Sunguk Lee | c731f6e507 | |
Sunguk Lee | 98fc73ba75 | |
Sunguk Lee | a37a5307f7 | |
Sunguk Lee | ff4db69b61 | |
Sunguk Lee | aecd8f03c5 | |
Sunguk Lee | b905f92cf3 | |
NØBOY | 40d3e95e7d | |
wangg | 824b4bdd00 | |
wangg | d6e50da2e0 | |
NØBOY | ed6847e481 | |
wangg | a1e5c8c4eb | |
NØBOY | ded02df37d | |
NØBOY | 7d046a0a5e | |
NØBOY | ae6ce5a2c6 | |
wangg | db5c112690 | |
NØBOY | 0b78e47b4b | |
wangg | 9ad039fae3 | |
NØBOY | 292430322f | |
wangg | cb1d1284c1 | |
NØBOY | 0eae0821d9 | |
wangg | 65c38571b7 | |
wangg | f2562bc7ae | |
NØBOY | 3a6d07f1aa | |
wangg | cb0d88dc73 | |
NØBOY | ceef6e9e76 | |
NØBOY | 82b9517316 | |
wangg | 42e652f679 | |
wangg | 92acc33514 | |
NØBOY | bd295531fb | |
NØBOY | 6d099c0f78 | |
NØBOY | a42cc77db1 | |
wangg | d94e5ddcd9 | |
wangg | 03760ab94a | |
NØBOY | 295ff4d1f2 | |
wangg | 1dfca70dcb |
|
@ -0,0 +1,5 @@
|
|||
|
||||
ft8CN/app/.gitignore 2
|
||||
ft8CN/.gitignore 2
|
||||
.FT8CN软件设计初衷及使用说明0.88版.pdf.icloud
|
||||
ft8CN/app/src/main/assets/.nightUSGS4Layer.sqlite.icloud
|
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
75
README.md
75
README.md
|
@ -1,8 +1,67 @@
|
|||
# FT8CN
|
||||
Developed by BG7YOZ and hosted by N0BOY
|
||||
|
||||
Run FT8 natively on Android
|
||||
|
||||
Check [Releases](https://github.com/N0BOY/FT8CN/releases) to download the latest apk file.
|
||||
|
||||
(More info coming up soon, check back in a while)
|
||||
# FT8CN
|
||||
Developed by BG7YOZ and hosted by N0BOY
|
||||
|
||||
Run FT8 natively on Android
|
||||
|
||||
Check [Releases](https://github.com/N0BOY/FT8CN/releases) to download the latest apk file.
|
||||
|
||||
```
|
||||
免责声明:
|
||||
FT8CN旨在研究的目的,学习如何对FT8信号进行解码、发射等操作,不对使用者操作本APP所产生的后果负责。
|
||||
在中华人民共和国境内,使用FT8CN请遵守《中华人民共和国无线电管理条例》等相关规定。
|
||||
考虑到手机的性能和续航的限制,对信号的处理采用轻量化的运算,未做深度解码等处理。
|
||||
如有好的建议或问题可以提交到到”有问题要吐槽“。
|
||||
|
||||
Disclaimer
|
||||
FT8CN aims to learn how to decode, transmit FT8 signal for research purposes, which is not responsible for the consequences caused by the user's operation.
|
||||
Please comply with local laws and regulations when using FT8CN.
|
||||
Considering the performance and endurance limitations of the mobile phone, the processing of the signal adopts lightweight operations instead of deep decoding and other processing.
|
||||
Please click "FAQ" if you have good suggestions or questions .
|
||||
|
||||
BG7YOZ
|
||||
2022-07-01
|
||||
|
||||
致敬:
|
||||
Steve Franke(K9AN)、Bill Somerville(G4WJS)、Joe Taylor(K1JT),提出FT8和FT4协议(FT是Franke和Taylor的首字母),并在论文《The FT4 and FT8 Communication Protocols》详细介绍了FT4和FT8的设计初衷和在WSJT-X中的具体实现细节,成为完成本APP的根本指南。
|
||||
Karlis Goba(YL3JG)在代码的具体实现上提供了参考。
|
||||
鸣谢:
|
||||
BG7YOY,在FT8CN开发阶段为我在无线电基本理论上作出指导,并为FT8CN设计了图标
|
||||
BG4IGX,在我刚刚入门业余无线电时为我在具体实践上作出指导。抖音上您可以搜到很多他的教学视频
|
||||
BD7MXN,帮助我对部分电台的连接控制做了一些测试,并提出改进建议
|
||||
BH2RSJ,帮助我建立了一个FT8CN测试群,为测试和后续改进提出了很多宝贵意见
|
||||
BH7ACO,帮助解决了某电台的驱动和相关的配置参数
|
||||
BG7IKK,帮助解决了只支持通过RTS控制PTT发射的电台的测试
|
||||
BI1NIZ,帮助注册账号,用于收集问题反馈和FAQ的功能
|
||||
BD3OOX以及石家庄业余无线电俱乐部,FT8CN的呼号地区归属数据提取至JTDX石家庄版,使呼号定位可以精确到中国的省级
|
||||
VR2UPU(BD7MJO),在FT8的开发和使用经验上提供指导,并在多语言方面给予帮助
|
||||
BA2BI,在业余无线电的基础知识和通联的日志处理方面上给予帮助和指导
|
||||
BI3QXJ,在对某品牌系列电台的指令集上给予专业性的指导
|
||||
BG6TQD,在对某型号电台的指令集测试上给予帮助
|
||||
BG5CSS,提供某型号电台用于测试
|
||||
BG7YXN,提供某型号电台用于测试
|
||||
BG7YRB,对呼号规则运算提供帮助
|
||||
BG8KAH,提供设备用于测试
|
||||
BA7LVG、JE6WUD,完成日文的翻译校对工作
|
||||
BG6RI,帮助解决日志的信号报告问题
|
||||
SV1EEX,完成希腊文、西班牙文UI的翻译工作
|
||||
VR2VRC,帮助修正历史呼号读取规则
|
||||
BA7NQ,提供设备用于测试
|
||||
BD7MYM,对某型号的电台测试给予指导
|
||||
NØBOY,帮助提供Github源,以及翻译工作
|
||||
BG5JNT,帮助修正非标准呼号的识别问题
|
||||
BH3NEK,协助对某型号电台进行测试
|
||||
BG2ALB,协助对某型号电台进行测试
|
||||
BG6DRU,协助对某型号电台进行测试
|
||||
BG7NQF,提供某型号电台的隐藏指令,对一些设备做兼容性测试
|
||||
BH2VSQ,协助对某型号电台进行测试
|
||||
BG7YBW,协助对部分功能进行测试
|
||||
BH1RNN,协助对部分功能进行测试
|
||||
BG7BSM,协助对一些BUG进行调试
|
||||
BH4FTI,发现并协助对一些BUG进行调试
|
||||
BG8BXM(M哥),为FT8CN的使用做推广,抖音和B站上有很多他的教学视频
|
||||
BG7MFQ,为FT8CN的使用做推广,帮助测试
|
||||
BG2EFX,提供大数据量的日志用于测试
|
||||
DS1UFX,贡献(tr)uSDX audio over CAT代码
|
||||
BG8HT,提供某型号电台进行测试
|
||||
UB6LUM,帮助解决某型号电台的操作模式设置
|
||||
```
|
||||
|
|
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
|
@ -1,26 +0,0 @@
|
|||
package com.bg7yoz.ft8cn;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
assertEquals("com.bg7yoz.ft8cn", appContext.getPackageName());
|
||||
}
|
||||
}
|
Plik binarny nie jest wyświetlany.
|
@ -1,75 +0,0 @@
|
|||
package com.bg7yoz.ft8cn.connector;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import com.bg7yoz.ft8cn.database.ControlMode;
|
||||
import com.bg7yoz.ft8cn.serialport.util.SerialInputOutputManager;
|
||||
|
||||
/**
|
||||
* 有线连接方式的Connector,这里是指USB方式的,继承于BaseRigConnector
|
||||
*
|
||||
* @author BG7YOZ
|
||||
* @date 2023-03-20
|
||||
*/
|
||||
public class CableConnector extends BaseRigConnector {
|
||||
private static final String TAG="CableConnector";
|
||||
|
||||
private final CableSerialPort cableSerialPort;
|
||||
|
||||
|
||||
public CableConnector(Context context,CableSerialPort.SerialPort serialPort, int baudRate
|
||||
, int controlMode) {
|
||||
super(controlMode);
|
||||
cableSerialPort= new CableSerialPort(context,serialPort,baudRate,getOnConnectorStateChanged());
|
||||
cableSerialPort.ioListener=new SerialInputOutputManager.Listener() {
|
||||
@Override
|
||||
public void onNewData(byte[] data) {
|
||||
if (getOnConnectReceiveData()!=null){
|
||||
getOnConnectReceiveData().onData(data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRunError(Exception e) {
|
||||
Log.e(TAG, "CableConnector error: "+e.getMessage() );
|
||||
getOnConnectorStateChanged().onRunError("与串口失去连接:"+e.getMessage());
|
||||
}
|
||||
} ;
|
||||
//connect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void sendData(byte[] data) {
|
||||
cableSerialPort.sendData(data);
|
||||
}
|
||||
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPttOn(byte[] command) {
|
||||
cableSerialPort.sendData(command);//以CAT指令发送PTT
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect() {
|
||||
super.connect();
|
||||
cableSerialPort.connect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
super.disconnect();
|
||||
cableSerialPort.disconnect();
|
||||
}
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
package com.bg7yoz.ft8cn.connector;
|
||||
/**
|
||||
* ICom网络方式的连接器。
|
||||
* 注:ICom网络方式的音频数据包是Int类型,需要转换成Float类型
|
||||
*
|
||||
* @author BGY70Z
|
||||
* @date 2023-03-20
|
||||
*/
|
||||
|
||||
import com.bg7yoz.ft8cn.icom.IComWifiRig;
|
||||
|
||||
public class IComWifiConnector extends BaseRigConnector{
|
||||
private static final String TAG = "IComWifiConnector";
|
||||
public interface OnWifiDataReceived{
|
||||
void OnWaveReceived(int bufferLen,float[] buffer);
|
||||
void OnCivReceived(byte[] data);
|
||||
}
|
||||
|
||||
private IComWifiRig iComWifiRig;
|
||||
private OnWifiDataReceived onWifiDataReceived;
|
||||
|
||||
|
||||
public IComWifiConnector(int controlMode,IComWifiRig iComWifiRig) {
|
||||
super(controlMode);
|
||||
this.iComWifiRig=iComWifiRig;
|
||||
|
||||
this.iComWifiRig.setOnIComDataEvents(new IComWifiRig.OnIComDataEvents() {
|
||||
@Override
|
||||
public void onReceivedCivData(byte[] data) {
|
||||
if (getOnConnectReceiveData()!=null){
|
||||
getOnConnectReceiveData().onData(data);
|
||||
}
|
||||
if (onWifiDataReceived!=null) {
|
||||
onWifiDataReceived.OnCivReceived(data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedWaveData(byte[] data) {//接收音频数据事件,把音频数据转换成float格式的。
|
||||
if (onWifiDataReceived!=null){
|
||||
float[] waveFloat=new float[data.length/2];
|
||||
for (int i = 0; i <waveFloat.length ; i++) {
|
||||
waveFloat[i]=readShortBigEndianData(data,i*2)/32768.0f;
|
||||
}
|
||||
onWifiDataReceived.OnWaveReceived(waveFloat.length,waveFloat);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendWaveData(float[] data) {
|
||||
if (iComWifiRig.opened) {
|
||||
iComWifiRig.sendWaveData(data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect() {
|
||||
super.connect();
|
||||
iComWifiRig.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
super.disconnect();
|
||||
iComWifiRig.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendData(byte[] data) {
|
||||
iComWifiRig.sendCivData(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPttOn(byte[] command) {
|
||||
iComWifiRig.sendCivData(command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPttOn(boolean on) {
|
||||
if (iComWifiRig.opened){
|
||||
iComWifiRig.setPttOn(on);
|
||||
}
|
||||
}
|
||||
public OnWifiDataReceived getOnWifiDataReceived() {
|
||||
return onWifiDataReceived;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return iComWifiRig.opened;
|
||||
}
|
||||
|
||||
public void setOnWifiDataReceived(OnWifiDataReceived onDataReceived) {
|
||||
this.onWifiDataReceived = onDataReceived;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从流数据中读取小端模式的Short
|
||||
*
|
||||
* @param data 流数据
|
||||
* @param start 起始点
|
||||
* @return Int16
|
||||
*/
|
||||
public static short readShortBigEndianData(byte[] data, int start) {
|
||||
if (data.length - start < 2) return 0;
|
||||
return (short) ((short) data[start] & 0xff
|
||||
| ((short) data[start + 1] & 0xff) << 8);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
package com.bg7yoz.ft8cn.flex;
|
||||
/**
|
||||
* flexRadio仪表处理
|
||||
* @author BG7YOZ
|
||||
*/
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class FlexMeters extends HashMap<Integer, FlexMeters.FlexMeter> {
|
||||
private static final String TAG="FlexMeters";
|
||||
public FlexMeters(String content) {
|
||||
String[] 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(".")));
|
||||
FlexMeter meter;
|
||||
if (this.containsKey(index)){
|
||||
meter=this.get(index);
|
||||
}else {
|
||||
meter=new FlexMeter();
|
||||
this.put(index,meter);
|
||||
}
|
||||
|
||||
if (val[0].toLowerCase().contains(".src")) {
|
||||
meter.src = val[1];
|
||||
}
|
||||
if (val[0].toLowerCase().contains(".num")) {
|
||||
meter.num = val[1];
|
||||
}
|
||||
if (val[0].toLowerCase().contains(".nam")) {
|
||||
meter.nam = val[1];
|
||||
}
|
||||
if (val[0].toLowerCase().contains(".low")) {
|
||||
meter.low = val[1];
|
||||
}
|
||||
if (val[0].toLowerCase().contains(".hi")) {
|
||||
meter.hi = val[1];
|
||||
}
|
||||
if (val[0].toLowerCase().contains(".desc")) {
|
||||
meter.desc = val[1];
|
||||
}
|
||||
if (val[0].toLowerCase().contains(".unit")) {
|
||||
meter.unit = val[1];
|
||||
}
|
||||
if (val[0].toLowerCase().contains(".fps")) {
|
||||
meter.fps = val[1];
|
||||
}
|
||||
if (val[0].toLowerCase().contains(".peak")) {
|
||||
meter.peak = val[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void getAllMeters(){
|
||||
for (int key:this.keySet()) {
|
||||
//s.append(String.format("%d->%s\n",key,get(key)));
|
||||
Log.e(TAG, "getAllMeters: "+String.format("ID:%d FIELDS:%s\n",key,get(key)) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class FlexMeter {
|
||||
public String src;
|
||||
public String num;
|
||||
public String nam;
|
||||
public String low;
|
||||
public String hi;
|
||||
public String desc;
|
||||
public String unit;
|
||||
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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,437 +0,0 @@
|
|||
package com.bg7yoz.ft8cn.flex;
|
||||
/**
|
||||
* 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;
|
||||
|
||||
enum VitaPacketType {
|
||||
IF_DATA,//IF Data packet without Stream Identifier
|
||||
IF_DATA_WITH_STREAM,//IF Data packet with Stream Identifier
|
||||
EXT_DATA,//Extension Data packet without Stream Identifier
|
||||
EXT_DATA_WITH_STREAM,//Extension Data packet with Stream Identifier
|
||||
IF_CONTEXT,//IF Context packet(see Section 7)
|
||||
EXT_CONTEXT//Extension Context packet(see Section 7);
|
||||
};
|
||||
|
||||
//时间戳的类型
|
||||
//时间戳共有两部分,小数部分和整数部分,整数部分以秒为分辨率,32位, 主要传递UTC时间或者 GPS 时间,
|
||||
//小数部分主要有三种,一种是sample-count ,以采样周期为最小分辨率,一种是real-time以ps为最小单位,第三种是以任意选择的时间进行累加得出的,前面两种时间戳可以直接与整数部分叠加,第三种则不能保证与整数部分保持恒定关系,前两种与整数部分叠加来操作的可以在覆盖的时间范围为年
|
||||
//小数部分的时间戳共有64位,小数部分可以在没有整数部分的情况下使用,
|
||||
//所有的时间带来都是在以一个采样数据为该reference-point 时间
|
||||
enum VitaTSI {
|
||||
TSI_NONE,//No Integer-seconds Timestamp field included
|
||||
TSI_UTC,//Coordinated Universal Time(UTC)
|
||||
TSI_GPS,//GPS time
|
||||
TSI_OTHER//Other
|
||||
};
|
||||
|
||||
//时间戳小数部分类型
|
||||
//小数部分主要有三种:
|
||||
// 一种是sample-count ,以采样周期为最小分辨率,
|
||||
// 一种是real-time以ps为最小单位,
|
||||
// 第三种是以任意选择的时间进行累加得出的,
|
||||
// 前面两种时间戳可以直接与整数部分叠加,
|
||||
// 第三种则不能保证与整数部分保持恒定关系,前两种与整数部分叠加来操作的可以在覆盖的时间范围为年
|
||||
// 小数部分的时间戳共有64位,小数部分可以在没有整数部分的情况下使用,
|
||||
// 所有的时间带来都是在以一个采样数据为该参考点(reference-point)的时间。
|
||||
enum VitaTSF {
|
||||
TSF_NONE,//No Fractional-seconds Timestamp field included. 不包括分数秒时间戳字段
|
||||
TSF_SAMPLE_COUNT,//Sample Count Timestamp. 样本计数时间戳
|
||||
TSF_REALTIME,//Real Time(Picoseconds) Timestamp. 实时(皮秒)时间戳
|
||||
TSF_FREERUN,//Free Running Count Timestamp. 自由运行计数时间戳
|
||||
};
|
||||
|
||||
public class VITA {
|
||||
private static final String TAG = "VITA";
|
||||
|
||||
// 最小有效的VITA包长度
|
||||
private static final int VITAmin = 28;
|
||||
|
||||
public static final int FRS_OUI = 0x12cd;
|
||||
//public static final int VITA_PORT = 4991;
|
||||
|
||||
public static final int FLEX_CLASS_ID = 0x534C;
|
||||
public static final int FLEX_DAX_AUDIO_CLASS_ID = 0x534C03E3;
|
||||
public static final int FLEX_DAX_IQ_CLASS_ID = 0x534C00E3;
|
||||
public static final int FLEX_FFT_CLASS_ID = 0x534C8003;
|
||||
public static final int FLEX_METER_CLASS_ID = 0x534C8002;
|
||||
public static final int FLEX_Discovery_stream_ID = 0x800;
|
||||
|
||||
|
||||
public static final int VS_Meter = 0x8002;
|
||||
public static final int VS_PAN_FFT = 0x8003;
|
||||
public static final int VS_Waterfall = 0x8004;
|
||||
public static final int VS_Opus = 0x8005;
|
||||
public static final int DAX_IQ_24Khz = 0x00e3;
|
||||
public static final int DAX_IQ_48Khz = 0x00e4;
|
||||
public static final int DAX_IQ_96Khz = 0x00e5;
|
||||
public static final int DAX_IQ_192KHz = 0x00e6;
|
||||
public static final int VS_DAX_Audio = 0x03e3;
|
||||
public static final int VS_Discovery = 0xffff;
|
||||
|
||||
|
||||
private byte[] buffer;
|
||||
public VitaPacketType packetType;
|
||||
public boolean classIdPresent;//指示数据包中是否包含类标识符(类ID)字段
|
||||
public boolean trailerPresent;//指示数据包是否包含尾部。
|
||||
public VitaTSI tsi;//时间戳的类型。
|
||||
public VitaTSF tsf;//时间戳小数部分类型
|
||||
public int packetCount;//包计数器,可以对连续的IF data packet进行计数,这些packet具有相同的Stream Identifier 和packet type。
|
||||
public int packetSize;//表示有多少32bit数在IF Data packet 里面
|
||||
|
||||
//时间戳共有两部分,小数部分和整数部分,整数部分以秒为分辨率,32位,小数部分64位。
|
||||
public long integerTimestamp;//u_int32,long是64位的
|
||||
public long fracTimeStamp;
|
||||
public long oui;
|
||||
public int informationClassCode;//无用了,用classId代替
|
||||
public int packetClassCode;//无用了,用classId代替
|
||||
public int classId;//FLEX应该是0x534CFFF,是informationClassCode与packetClassCode合并的
|
||||
public byte[] payload = null;
|
||||
public long trailer;
|
||||
public boolean isAvailable = false;//电台对象是否有效。
|
||||
|
||||
public boolean streamIdPresent;//是否有流字符
|
||||
|
||||
//用来区分不同的 packet stream 。
|
||||
//stream ID 不是必须的,如果仅有一个数据包在单一数据链路传递的话就可以不用要,
|
||||
//如果 packet stream想用同一 stream ID 的话那每一个packet都得有,
|
||||
//在系统内部,不同的packet stream 之间的 Stream ID是不同的。
|
||||
//如果要用到 data-context 配对,那么IF data packet需要 Stream ID
|
||||
public long streamId;//流ID,32位,FLEX应当是0x0800
|
||||
|
||||
/*
|
||||
VITA前28个字节是VITA头
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* 生成音频流的VITA数据包,id应当是电台create 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是以word(32位,4字节)为单位,
|
||||
//packetSize值为263居多估计以音频,还有其它的长度,263是包含7个word(28字节)的头长度。
|
||||
packetSize = (data.length ) + 7;//7个word是VITA的包头
|
||||
//----以上是Header,32位,第一个word-------
|
||||
|
||||
streamId = id;//第二个word,此id是电台赋给的。经常是0x40000xx。
|
||||
|
||||
oui = 0x00001c2d;//第三个word,FlexRadio Systems OUI
|
||||
classId = 0x534c0123;//第四个word,64位
|
||||
//classId = 0x534c03e3;//第四个word,64位
|
||||
|
||||
//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默认263(words)
|
||||
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的长度+28字节:byte[] 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;
|
||||
|
||||
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;//定位
|
||||
//检查是否有流字符
|
||||
streamIdPresent = packetType == VitaPacketType.IF_DATA_WITH_STREAM
|
||||
|| packetType == VitaPacketType.EXT_DATA_WITH_STREAM;
|
||||
|
||||
if (streamIdPresent) {//是否有流ID,获取流ID,32位
|
||||
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) {
|
||||
//只取24位,前8位保留
|
||||
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;
|
||||
|
||||
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为最小单位,第三种是以任意选择的时间进行累加得出的,前面两种时间戳可以直接与整数部分叠加,第三种则不能保证与整数部分保持恒定关系,前两种与整数部分叠加来操作的可以在覆盖的时间范围为年
|
||||
//小数部分的时间戳共有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;
|
||||
}
|
||||
//获取时间戳的小数部分,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 (trailerPresent) {
|
||||
trailer = ((((int) data[data.length - 2]) & 0x00ff) << 8) | ((int) data[data.length - 1]) & 0x00ff;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取payload的长度,如果没有数据,payload长度为0;
|
||||
*
|
||||
* @return payload长度
|
||||
*/
|
||||
public int getPayloadLength() {
|
||||
if (buffer == null) {
|
||||
return 0;
|
||||
} else {
|
||||
return buffer.length;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示扩展数据
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public String showPayload() {
|
||||
if (payload != null) {
|
||||
return new String(payload).replace(" ", "\n");
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public String showPayloadHex() {
|
||||
if (payload != null) {
|
||||
return byteToStr(payload);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示VITA 49的包头信息
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@SuppressLint("DefaultLocale")
|
||||
public String showHeadStr() {
|
||||
return String.format("包类型(packetType): %s\n" +
|
||||
"包数量(packetCount): %d\n" +
|
||||
"包大小(packetSize): %d\n" +
|
||||
"是否有流ID(streamIdPresent): %s\n" +
|
||||
"流ID(streamId): 0x%X\n" +
|
||||
"是否有类ID(classIdPresent): %s\n" +
|
||||
"类ID(classId): 0x%X\n" +
|
||||
"类高位(informationClassCode): 0x%X\n" +
|
||||
"类低位(packetClassCode): 0x%X\n" +
|
||||
"公司标识码(oui): 0x%X\n" +
|
||||
"时间戳类型(tsi): %s\n" +
|
||||
"时间戳整数部分(integerTimestamp):%s\n" +
|
||||
"时间戳小数部分类型(tsf): %s\n" +
|
||||
"时间戳小数部分值(fracTimeStamp): %d\n" +
|
||||
"负载长度(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 ? "是" : "否"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示VITA 49 包数据
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@SuppressLint("DefaultLocale")
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s负载(payload):\n%s\n"
|
||||
, showHeadStr()
|
||||
, (payload == null ? "" : new String(payload))
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
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");
|
||||
String sd = sdf.format(new Date(timestamp)); // 时间戳转换日期
|
||||
//System.out.println(sd);
|
||||
return sd;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
Plik diff jest za duży
Load Diff
|
@ -1,5 +0,0 @@
|
|||
<vector android:height="36dp" android:tint="#B8B8B8"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="36dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2L18,7L6,7v12zM8.46,11.88l1.41,-1.41L12,12.59l2.12,-2.12 1.41,1.41L13.41,14l2.12,2.12 -1.41,1.41L12,15.41l-2.12,2.12 -1.41,-1.41L10.59,14l-2.13,-2.12zM15.5,4l-1,-1h-5l-1,1L5,4v2h14L19,4z"/>
|
||||
</vector>
|
|
@ -1,5 +0,0 @@
|
|||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:tint="#FFFFFF" android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M7.41,8.59L12,13.17l4.59,-4.58L18,10l-6,6 -6,-6 1.41,-1.41z"/>
|
||||
</vector>
|
|
@ -1,5 +0,0 @@
|
|||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:tint="#FFFFFF" android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M7.41,15.41L12,10.83l4.59,4.58L18,14l-6,-6 -6,6z"/>
|
||||
</vector>
|
Plik binarny nie jest wyświetlany.
Przed Szerokość: | Wysokość: | Rozmiar: 1.5 KiB |
Plik binarny nie jest wyświetlany.
Przed Szerokość: | Wysokość: | Rozmiar: 6.9 KiB |
Plik binarny nie jest wyświetlany.
Przed Szerokość: | Wysokość: | Rozmiar: 4.6 KiB |
Plik binarny nie jest wyświetlany.
Przed Szerokość: | Wysokość: | Rozmiar: 13 KiB |
|
@ -1,5 +0,0 @@
|
|||
<vector android:autoMirrored="true" android:height="36dp"
|
||||
android:tint="#B8B8B8" android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="36dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M19,3h-4.18C14.4,1.84 13.3,1 12,1c-1.3,0 -2.4,0.84 -2.82,2L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM12,3c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM14,17L7,17v-2h7v2zM17,13L7,13v-2h10v2zM17,9L7,9L7,7h10v2z"/>
|
||||
</vector>
|
|
@ -1,5 +0,0 @@
|
|||
<vector android:autoMirrored="true" android:height="36dp"
|
||||
android:tint="#B8B8B8" android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="36dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M19,3h-4.18C14.4,1.84 13.3,1 12,1c-1.3,0 -2.4,0.84 -2.82,2L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM12,3c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM12,7c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zM18,19L6,19v-1.4c0,-2 4,-3.1 6,-3.1s6,1.1 6,3.1L18,19z"/>
|
||||
</vector>
|
|
@ -1,6 +0,0 @@
|
|||
<vector android:autoMirrored="true" android:height="36dp" android:tint="#5A5A5A"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="36dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillAlpha="0.8" android:fillColor="@android:color/white" android:pathData="M4,9c-1.1,0 -2,0.9 -2,2v2c0,1.1 0.9,2 2,2h1v4h2v-4h1l5,3L13,6L8,9L4,9zM15.5,12c0,-1.33 -0.58,-2.53 -1.5,-3.35v6.69c0.92,-0.81 1.5,-2.01 1.5,-3.34z"/>
|
||||
<!-- <path android:fillAlpha="1" android:fillColor="@android:color/white" android:pathData="M 7 4 L 7 20 L 9 20 L 9 4 L 7 4 M 17 4 L 15 4 L 15 20 L 17 20 L 17 4"/>-->
|
||||
</vector>
|
|
@ -1,5 +0,0 @@
|
|||
<vector android:height="48dp" android:tint="#B8B8B8"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
|
||||
</vector>
|
|
@ -1,5 +0,0 @@
|
|||
<vector android:autoMirrored="true" android:height="32dp"
|
||||
android:tint="#B8B8B8" android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="32dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
|
||||
</vector>
|
|
@ -1,5 +0,0 @@
|
|||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:tint="#B8B8B8" android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M12,1c-4.97,0 -9,4.03 -9,9v7c0,1.66 1.34,3 3,3h3v-8H5v-2c0,-3.87 3.13,-7 7,-7s7,3.13 7,7v2h-4v8h4v1h-7v2h6c1.66,0 3,-1.34 3,-3V10c0,-4.97 -4.03,-9 -9,-9z"/>
|
||||
</vector>
|
|
@ -1,5 +0,0 @@
|
|||
<vector android:autoMirrored="true" android:height="36dp"
|
||||
android:tint="#B8B8B8" android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="36dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M3.9,12c0,-1.71 1.39,-3.1 3.1,-3.1h4L11,7L7,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5h4v-1.9L7,15.1c-1.71,0 -3.1,-1.39 -3.1,-3.1zM8,13h8v-2L8,11v2zM17,7h-4v1.9h4c1.71,0 3.1,1.39 3.1,3.1s-1.39,3.1 -3.1,3.1h-4L13,17h4c2.76,0 5,-2.24 5,-5s-2.24,-5 -5,-5z"/>
|
||||
</vector>
|
|
@ -1,5 +0,0 @@
|
|||
<vector android:autoMirrored="true" android:height="36dp"
|
||||
android:tint="#B8B8B8" android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="36dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M19,5v14L5,19L5,5h14m1.1,-2L3.9,3c-0.5,0 -0.9,0.4 -0.9,0.9v16.2c0,0.4 0.4,0.9 0.9,0.9h16.2c0.4,0 0.9,-0.5 0.9,-0.9L21,3.9c0,-0.5 -0.5,-0.9 -0.9,-0.9zM11,7h6v2h-6L11,7zM11,11h6v2h-6v-2zM11,15h6v2h-6zM7,7h2v2L7,9zM7,11h2v2L7,13zM7,15h2v2L7,17z"/>
|
||||
</vector>
|
|
@ -1,5 +0,0 @@
|
|||
<vector android:autoMirrored="true" android:height="32dp"
|
||||
android:tint="#CB89F4" android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="32dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M12,2C8.14,2 5,5.14 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.86 -3.14,-7 -7,-7zM12.88,15.75h-1.75L11.13,14h1.75v1.75zM12.88,12.88h-1.75c0,-2.84 2.62,-2.62 2.62,-4.38 0,-0.96 -0.79,-1.75 -1.75,-1.75s-1.75,0.79 -1.75,1.75L8.5,8.5C8.5,6.57 10.07,5 12,5s3.5,1.57 3.5,3.5c0,2.19 -2.62,2.41 -2.62,4.38z"/>
|
||||
</vector>
|
|
@ -1,5 +0,0 @@
|
|||
<vector android:autoMirrored="true" android:height="36dp"
|
||||
android:tint="#B8B8B8" android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="36dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M19,12v7L5,19v-7L3,12v7c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2zM13,12.67l2.59,-2.58L17,11.5l-5,5 -5,-5 1.41,-1.41L11,12.67L11,3h2z"/>
|
||||
</vector>
|
|
@ -1,5 +0,0 @@
|
|||
<vector android:height="36dp" android:tint="#B8B8B8"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="36dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M4,9c-1.1,0 -2,0.9 -2,2v2c0,1.1 0.9,2 2,2h1v4h2v-4h1l5,3L13,6L8,9L4,9zM15.5,12c0,-1.33 -0.58,-2.53 -1.5,-3.35v6.69c0.92,-0.81 1.5,-2.01 1.5,-3.34z"/>
|
||||
</vector>
|
Plik binarny nie jest wyświetlany.
Przed Szerokość: | Wysokość: | Rozmiar: 2.8 KiB |
Plik binarny nie jest wyświetlany.
Przed Szerokość: | Wysokość: | Rozmiar: 759 B |
|
@ -1,17 +0,0 @@
|
|||
package com.bg7yoz.ft8cn;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test
|
||||
public void addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
|
@ -6,5 +6,8 @@
|
|||
<option name="m_reportAllNonLibraryCalls" value="false" />
|
||||
<option name="callCheckString" value="java.io.File,.*,java.io.InputStream,read|skip|available|markSupported,java.io.Reader,read|skip|ready|markSupported,java.lang.AbstractStringBuilder,capacity|codePointAt|codePointBefore|codePointCount|indexOf|lastIndexOf|offsetByCodePoints|substring|subSequence,java.lang.Boolean,.*,java.lang.Byte,.*,java.lang.Character,.*,java.lang.Double,.*,java.lang.Float,.*,java.lang.Integer,.*,java.lang.Long,.*,java.lang.Math,.*,java.lang.Object,equals|hashCode|toString,java.lang.Short,.*,java.lang.StrictMath,.*,java.lang.String,.*,java.lang.Thread,interrupted,java.math.BigDecimal,.*,java.math.BigInteger,.*,java.net.InetAddress,.*,java.net.URI,.*,java.nio.channels.AsynchronousChannelGroup,.*,java.util.Arrays,.*,java.util.Collections,(?!addAll).*,java.util.List,of,java.util.Map,of|ofEntries|entry,java.util.Set,of,java.util.UUID,.*,java.util.concurrent.CountDownLatch,await|getCount,java.util.concurrent.ExecutorService,awaitTermination|isShutdown|isTerminated,java.util.concurrent.ForkJoinPool,awaitQuiescence,java.util.concurrent.Semaphore,tryAcquire|availablePermits|isFair|hasQueuedThreads|getQueueLength|getQueuedThreads,java.util.concurrent.locks.Condition,await|awaitNanos|awaitUntil,java.util.concurrent.locks.Lock,tryLock|newCondition,java.util.regex.Matcher,pattern|toMatchResult|start|end|group|groupCount|matches|find|lookingAt|quoteReplacement|replaceAll|replaceFirst|regionStart|regionEnd|hasTransparentBounds|hasAnchoringBounds|hitEnd|requireEnd,java.util.regex.Pattern,.*,java.util.stream.BaseStream,.*,java.util.stream.DoubleStream,.*,java.util.stream.IntStream,.*,java.util.stream.LongStream,.*,java.util.stream.Stream,.*" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="JavadocDeclaration" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ADDITIONAL_TAGS" value="date" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
|
@ -172,4 +172,11 @@
|
|||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
<component name="VisualizationToolProject">
|
||||
<option name="state">
|
||||
<ProjectState>
|
||||
<option name="scale" value="0.027632950990615225" />
|
||||
</ProjectState>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
|
@ -5,7 +5,7 @@ plugins {
|
|||
id 'com.android.application'
|
||||
}
|
||||
|
||||
def currentTime = getCurrentTime();
|
||||
def currentTime = getCurrentTime()
|
||||
|
||||
static def getCurrentTime() {
|
||||
DateFormat df = new SimpleDateFormat("yyyy-MM-dd")
|
||||
|
@ -22,9 +22,9 @@ android {
|
|||
minSdk 23
|
||||
targetSdk 33
|
||||
versionCode 1
|
||||
versionName '0.87'
|
||||
versionName '0.92'
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
//testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
dataBinding{
|
||||
enabled true
|
||||
}
|
||||
|
@ -35,14 +35,6 @@ android {
|
|||
|
||||
}
|
||||
}
|
||||
ndk{
|
||||
abiFilters 'armeabi-v7a','arm64-v8a','x86','x86_64'
|
||||
}
|
||||
sourceSets{
|
||||
main{
|
||||
jniLibs.srcDirs=['libs']
|
||||
}
|
||||
}
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
|
||||
|
@ -56,11 +48,6 @@ android {
|
|||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
buildConfigField("String", "apkBuildTime", "\"${currentTime}\"")
|
||||
|
||||
// true - 打开混淆
|
||||
//minifyEnabled true
|
||||
// true - 打开资源压缩
|
||||
//shrinkResources true
|
||||
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
|
@ -73,30 +60,33 @@ android {
|
|||
// version '3.18.1'
|
||||
// }
|
||||
// }
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
jniLibs.srcDirs = ['libs']
|
||||
}
|
||||
}
|
||||
namespace 'com.bg7yoz.ft8cn'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation 'androidx.appcompat:appcompat:1.4.1'
|
||||
implementation 'com.google.android.material:material:1.5.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
|
||||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1'
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'com.google.android.material:material:1.8.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
implementation 'androidx.lifecycle:lifecycle-livedata:2.5.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.5.1'
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
implementation 'androidx.navigation:navigation-fragment:2.4.1'
|
||||
implementation 'androidx.navigation:navigation-ui:2.4.1'
|
||||
implementation 'com.google.android.gms:play-services-maps:18.0.2'
|
||||
implementation 'androidx.navigation:navigation-fragment:2.5.3'
|
||||
implementation 'androidx.navigation:navigation-ui:2.5.3'
|
||||
implementation 'com.google.android.gms:play-services-maps:18.1.0'
|
||||
|
||||
// implementation 'com.google.code.gson:gson:2.7'
|
||||
implementation 'commons-net:commons-net:3.6'//用于时间同步
|
||||
implementation 'com.google.guava:guava:31.1-jre'//用于HashTable(多key的HashMap)
|
||||
|
||||
//testImplementation 'junit:junit:4.13.2'
|
||||
//androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||
//androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||
|
||||
implementation files('src/libs/MPAndroidChartv_3.1.0.jar')
|
||||
implementation files('src/libs/commons-net-3.6.jar')////用于时间同步
|
||||
implementation files('src/libs/nanohttpd-2.2.0.jar')
|
||||
implementation files('src/libs/osmdroid-android-6.1.14.aar')//地图控件
|
||||
|
||||
}
|
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.
|
@ -28,6 +28,7 @@
|
|||
android:theme="@style/Theme.Ft8CN">
|
||||
<activity
|
||||
android:name=".grid_tracker.GridTrackerMainActivity"
|
||||
android:screenOrientation="sensorLandscape"
|
||||
android:exported="false">
|
||||
<meta-data
|
||||
android:name="android.app.lib_name"
|
|
@ -17,6 +17,10 @@ import android.view.ViewGroup;
|
|||
import android.widget.AdapterView;
|
||||
import android.widget.CompoundButton;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.Observer;
|
||||
|
||||
import com.bg7yoz.ft8cn.FAQActivity;
|
||||
import com.bg7yoz.ft8cn.Ft8Message;
|
||||
import com.bg7yoz.ft8cn.GeneralVariables;
|
||||
|
@ -34,10 +38,6 @@ import com.bg7yoz.ft8cn.timer.UtcTimer;
|
|||
|
||||
import java.io.IOException;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.Observer;
|
||||
|
||||
/**
|
||||
* A simple {@link Fragment} subclass.
|
||||
* create an instance of this fragment.
|
||||
|
@ -280,6 +280,18 @@ public class ConfigFragment extends Fragment {
|
|||
//设置电台名称,参数列表
|
||||
setRigNameSpinner();
|
||||
|
||||
//设置解码模式
|
||||
setDecodeMode();
|
||||
|
||||
//设置音频输出的位数
|
||||
setAudioOutputBitsMode();
|
||||
|
||||
//设置音频输出采样率
|
||||
setAudioOutputRateMode();
|
||||
|
||||
//设置显示消息模式
|
||||
setMessageMode();
|
||||
|
||||
//设置控制模式 VOX CAT
|
||||
setControlMode();
|
||||
|
||||
|
@ -762,6 +774,112 @@ public class ConfigFragment extends Fragment {
|
|||
}
|
||||
|
||||
|
||||
private void setDecodeMode() {
|
||||
binding.decodeModeRadioGroup.clearCheck();
|
||||
binding.fastDecodeRadioButton.setChecked(!GeneralVariables.deepDecodeMode);
|
||||
binding.deepDecodeRadioButton.setChecked(GeneralVariables.deepDecodeMode);
|
||||
|
||||
View.OnClickListener listener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
int buttonId = binding.decodeModeRadioGroup.getCheckedRadioButtonId();
|
||||
GeneralVariables.deepDecodeMode= buttonId ==binding.deepDecodeRadioButton.getId();
|
||||
writeConfig("deepMode", GeneralVariables.deepDecodeMode? "1" : "0");
|
||||
}
|
||||
};
|
||||
|
||||
binding.fastDecodeRadioButton.setOnClickListener(listener);
|
||||
binding.deepDecodeRadioButton.setOnClickListener(listener);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置音频输出的位数
|
||||
*/
|
||||
private void setAudioOutputBitsMode() {
|
||||
//binding.controlModeRadioGroup.setOnCheckedChangeListener(null);
|
||||
binding.audioBitsRadioGroup.clearCheck();
|
||||
binding.audio32BitsRadioButton.setChecked(GeneralVariables.audioOutput32Bit);
|
||||
binding.audio16BitsRadioButton.setChecked(!GeneralVariables.audioOutput32Bit);
|
||||
|
||||
|
||||
|
||||
View.OnClickListener listener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
int buttonId = binding.audioBitsRadioGroup.getCheckedRadioButtonId();
|
||||
GeneralVariables.audioOutput32Bit= buttonId ==binding.audio32BitsRadioButton.getId();
|
||||
writeConfig("audioBits", GeneralVariables.audioOutput32Bit? "1" : "0");
|
||||
}
|
||||
};
|
||||
|
||||
binding.audio32BitsRadioButton.setOnClickListener(listener);
|
||||
binding.audio16BitsRadioButton.setOnClickListener(listener);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出音频的采样率设置
|
||||
*/
|
||||
private void setAudioOutputRateMode() {
|
||||
binding.audioRateRadioGroup.clearCheck();
|
||||
binding.audio12kRadioButton.setChecked(GeneralVariables.audioSampleRate==12000);
|
||||
binding.audio24kRadioButton.setChecked(GeneralVariables.audioSampleRate==24000);
|
||||
binding.audio48kRadioButton.setChecked(GeneralVariables.audioSampleRate==48000);
|
||||
|
||||
|
||||
|
||||
View.OnClickListener listener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (binding.audio12kRadioButton.isChecked()) GeneralVariables.audioSampleRate=12000;
|
||||
if (binding.audio24kRadioButton.isChecked()) GeneralVariables.audioSampleRate=24000;
|
||||
if (binding.audio48kRadioButton.isChecked()) GeneralVariables.audioSampleRate=48000;
|
||||
writeConfig("audioRate", String.valueOf(GeneralVariables.audioSampleRate));
|
||||
}
|
||||
};
|
||||
|
||||
binding.audio12kRadioButton.setOnClickListener(listener);
|
||||
binding.audio24kRadioButton.setOnClickListener(listener);
|
||||
binding.audio48kRadioButton.setOnClickListener(listener);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 设置消息列表显示模式
|
||||
*/
|
||||
private void setMessageMode() {
|
||||
binding.messageModeRadioGroup.clearCheck();
|
||||
if (GeneralVariables.simpleCallItemMode){
|
||||
binding.msgSimpleRadioButton.setChecked(true);
|
||||
}else {
|
||||
binding.msgStandardRadioButton.setChecked(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
View.OnClickListener listener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
int buttonId = binding.messageModeRadioGroup.getCheckedRadioButtonId();
|
||||
GeneralVariables.simpleCallItemMode=
|
||||
binding.messageModeRadioGroup.getCheckedRadioButtonId()
|
||||
==binding.msgSimpleRadioButton.getId();
|
||||
|
||||
writeConfig("msgMode", GeneralVariables.simpleCallItemMode?"1":"0");
|
||||
}
|
||||
};
|
||||
|
||||
binding.msgStandardRadioButton.setOnClickListener(listener);
|
||||
binding.msgSimpleRadioButton.setOnClickListener(listener);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 设置控制模式VOX CAT
|
||||
*/
|
||||
|
@ -867,7 +985,9 @@ public class ConfigFragment extends Fragment {
|
|||
//打开网络电台列表对话框
|
||||
if (GeneralVariables.instructionSet== InstructionSet.FLEX_NETWORK) {
|
||||
new SelectFlexRadioDialog(requireContext(), mainViewModel).show();
|
||||
}else if(GeneralVariables.instructionSet== InstructionSet.ICOM) {
|
||||
}else if(GeneralVariables.instructionSet== InstructionSet.ICOM
|
||||
||GeneralVariables.instructionSet== InstructionSet.XIEGU_6100
|
||||
||GeneralVariables.instructionSet== InstructionSet.XIEGUG90S) {
|
||||
new LoginIcomRadioDialog(requireContext(), mainViewModel).show();
|
||||
}else {
|
||||
ToastMessage.show(GeneralVariables.getStringFromResource(R.string.only_flex_supported));
|
||||
|
@ -897,68 +1017,66 @@ public class ConfigFragment extends Fragment {
|
|||
binding.callsignHelpImageButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (GeneralVariables.isChina) {
|
||||
new HelpDialog(requireContext(), requireActivity(), "callsign.txt", true).show();
|
||||
} else {
|
||||
new HelpDialog(requireContext(), requireActivity(), "callsign_en.txt", true).show();
|
||||
}
|
||||
new HelpDialog(requireContext(), requireActivity()
|
||||
, GeneralVariables.getStringFromResource(R.string.callsign_help)
|
||||
, true).show();
|
||||
}
|
||||
});
|
||||
//梅登海德网格的帮助
|
||||
binding.maidenGridImageButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (GeneralVariables.isChina) {
|
||||
new HelpDialog(requireContext(), requireActivity(), "maidenhead.txt", true).show();
|
||||
} else {
|
||||
new HelpDialog(requireContext(), requireActivity(), "maidenhead_en.txt", true).show();
|
||||
}
|
||||
new HelpDialog(requireContext(), requireActivity()
|
||||
, GeneralVariables.getStringFromResource(R.string.maidenhead_help)
|
||||
, true).show();
|
||||
}
|
||||
});
|
||||
//发射频率的帮助
|
||||
binding.frequencyImageButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (GeneralVariables.isChina) {
|
||||
new HelpDialog(requireContext(), requireActivity(), "frequency.txt", true).show();
|
||||
} else {
|
||||
new HelpDialog(requireContext(), requireActivity(), "frequency_en.txt", true).show();
|
||||
}
|
||||
new HelpDialog(requireContext(), requireActivity()
|
||||
, GeneralVariables.getStringFromResource(R.string.frequency_help)
|
||||
, true).show();
|
||||
}
|
||||
});
|
||||
//延迟发射帮助
|
||||
binding.transDelayImageButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (GeneralVariables.isChina) {
|
||||
new HelpDialog(requireContext(), requireActivity(), "transDelay.txt", true).show();
|
||||
} else {
|
||||
new HelpDialog(requireContext(), requireActivity(), "transDelay_en.txt", true).show();
|
||||
}
|
||||
new HelpDialog(requireContext(), requireActivity()
|
||||
, GeneralVariables.getStringFromResource(R.string.transDelay_help)
|
||||
, true).show();
|
||||
}
|
||||
});
|
||||
//时间偏移帮助
|
||||
binding.timeOffsetImageButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (GeneralVariables.isChina) {
|
||||
new HelpDialog(requireContext(), requireActivity(), "timeoffset.txt", true).show();
|
||||
} else {
|
||||
new HelpDialog(requireContext(), requireActivity(), "timeoffset_en.txt", true).show();
|
||||
}
|
||||
new HelpDialog(requireContext(), requireActivity()
|
||||
, GeneralVariables.getStringFromResource(R.string.timeoffset_help)
|
||||
, true).show();
|
||||
}
|
||||
});
|
||||
//PTT延时帮助
|
||||
binding.pttDelayImageButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (GeneralVariables.isChina) {
|
||||
new HelpDialog(requireContext(), requireActivity(), "pttdelay.txt", true).show();
|
||||
} else {
|
||||
new HelpDialog(requireContext(), requireActivity(), "pttdelay_en.txt", true).show();
|
||||
}
|
||||
new HelpDialog(requireContext(), requireActivity()
|
||||
, GeneralVariables.getStringFromResource(R.string.pttdelay_help)
|
||||
, true).show();
|
||||
}
|
||||
});
|
||||
//显示列表方式
|
||||
binding.messageModeeHelpImageButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
new HelpDialog(requireContext(),requireActivity()
|
||||
,GeneralVariables.getStringFromResource(R.string.message_mode_help)
|
||||
,true).show();
|
||||
}
|
||||
});
|
||||
|
||||
//设置ABOUT
|
||||
binding.aboutButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
|
@ -970,110 +1088,110 @@ public class ConfigFragment extends Fragment {
|
|||
binding.operationHelpImageButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (GeneralVariables.isChina) {
|
||||
new HelpDialog(requireContext(), requireActivity(), "operationBand.txt", true).show();
|
||||
} else {
|
||||
new HelpDialog(requireContext(), requireActivity(), "operationBand_en.txt", true).show();
|
||||
}
|
||||
new HelpDialog(requireContext(), requireActivity()
|
||||
, GeneralVariables.getStringFromResource(R.string.operationBand_help)
|
||||
, true).show();
|
||||
}
|
||||
});
|
||||
//设置操作模式
|
||||
binding.controlModeHelpImageButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (GeneralVariables.isChina) {
|
||||
new HelpDialog(requireContext(), requireActivity(), "controlMode.txt", true).show();
|
||||
} else {
|
||||
new HelpDialog(requireContext(), requireActivity(), "controlMode_en.txt", true).show();
|
||||
}
|
||||
new HelpDialog(requireContext(), requireActivity()
|
||||
, GeneralVariables.getStringFromResource(R.string.controlMode_help)
|
||||
, true).show();
|
||||
}
|
||||
});
|
||||
//设置CI-V地址和波特率帮助
|
||||
binding.baudRateHelpImageButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (GeneralVariables.isChina) {
|
||||
new HelpDialog(requireContext(), requireActivity(), "civ_help.txt", true).show();
|
||||
} else {
|
||||
new HelpDialog(requireContext(), requireActivity(), "civ_help_en.txt", true).show();
|
||||
}
|
||||
new HelpDialog(requireContext(), requireActivity()
|
||||
, GeneralVariables.getStringFromResource(R.string.civ_help)
|
||||
, true).show();
|
||||
}
|
||||
});
|
||||
//电台型号列表
|
||||
binding.rigNameHelpImageButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (GeneralVariables.isChina) {
|
||||
new HelpDialog(requireContext(), requireActivity(), "rig_model_help.txt", true).show();
|
||||
} else {
|
||||
new HelpDialog(requireContext(), requireActivity(), "rig_model_help_en.txt", true).show();
|
||||
}
|
||||
new HelpDialog(requireContext(), requireActivity()
|
||||
, GeneralVariables.getStringFromResource(R.string.rig_model_help)
|
||||
, true).show();
|
||||
}
|
||||
});
|
||||
//发射监管
|
||||
binding.launchSupervisionImageButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (GeneralVariables.isChina) {
|
||||
new HelpDialog(requireContext(), requireActivity(), "launch_supervision_help.txt", true).show();
|
||||
} else {
|
||||
new HelpDialog(requireContext(), requireActivity(), "launch_supervision_en.txt", true).show();
|
||||
}
|
||||
new HelpDialog(requireContext(), requireActivity()
|
||||
, GeneralVariables.getStringFromResource(R.string.launch_supervision_help)
|
||||
, true).show();
|
||||
}
|
||||
});
|
||||
//无回应次数
|
||||
binding.noResponseCountButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (GeneralVariables.isChina) {
|
||||
new HelpDialog(requireContext(), requireActivity(), "no_response_help.txt", true).show();
|
||||
} else {
|
||||
new HelpDialog(requireContext(), requireActivity(), "no_response_help_en.txt", true).show();
|
||||
}
|
||||
new HelpDialog(requireContext(), requireActivity()
|
||||
, GeneralVariables.getStringFromResource(R.string.no_response_help)
|
||||
, true).show();
|
||||
}
|
||||
});
|
||||
//自动呼叫
|
||||
binding.autoFollowCountButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (GeneralVariables.isChina) {
|
||||
new HelpDialog(requireContext(), requireActivity(), "auto_follow_help.txt", true).show();
|
||||
} else {
|
||||
new HelpDialog(requireContext(), requireActivity(), "auto_follow_help_en.txt", true).show();
|
||||
}
|
||||
new HelpDialog(requireContext(), requireActivity()
|
||||
, GeneralVariables.getStringFromResource(R.string.auto_follow_help)
|
||||
, true).show();
|
||||
}
|
||||
});
|
||||
//连接模式
|
||||
binding.connectModeHelpImageButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (GeneralVariables.isChina) {
|
||||
new HelpDialog(requireContext(), requireActivity(), "connectMode.txt", true).show();
|
||||
} else {
|
||||
new HelpDialog(requireContext(), requireActivity(), "connectMode_en.txt", true).show();
|
||||
}
|
||||
new HelpDialog(requireContext(), requireActivity()
|
||||
, GeneralVariables.getStringFromResource(R.string.connectMode_help)
|
||||
, true).show();
|
||||
}
|
||||
});
|
||||
//排除选项
|
||||
binding.excludedHelpButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (GeneralVariables.isChina) {
|
||||
new HelpDialog(requireContext(), requireActivity(), "excludeCallsign.txt", true).show();
|
||||
} else {
|
||||
new HelpDialog(requireContext(), requireActivity(), "excludeCallsign_en.txt", true).show();
|
||||
}
|
||||
new HelpDialog(requireContext(), requireActivity()
|
||||
, GeneralVariables.getStringFromResource(R.string.excludeCallsign_help)
|
||||
, true).show();
|
||||
}
|
||||
});
|
||||
|
||||
binding.swlHelpButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (GeneralVariables.isChina) {
|
||||
new HelpDialog(requireContext(), requireActivity(), "swlMode.txt", true).show();
|
||||
} else {
|
||||
new HelpDialog(requireContext(), requireActivity(), "swlMode_en.txt", true).show();
|
||||
}
|
||||
new HelpDialog(requireContext(), requireActivity()
|
||||
, GeneralVariables.getStringFromResource(R.string.swlMode_help)
|
||||
, true).show();
|
||||
}
|
||||
});
|
||||
|
||||
//解码模式
|
||||
binding.decodeModeHelpButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
new HelpDialog(requireContext(),requireActivity()
|
||||
,GeneralVariables.getStringFromResource(R.string.deep_mode_help)
|
||||
,true).show();
|
||||
}
|
||||
});
|
||||
|
||||
//音频输出帮助
|
||||
binding.audioOutputImageButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
new HelpDialog(requireContext(), requireActivity()
|
||||
, GeneralVariables.getStringFromResource(R.string.audio_output_help)
|
||||
, true).show();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1081,11 +1199,9 @@ public class ConfigFragment extends Fragment {
|
|||
binding.clearCacheHelpButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (GeneralVariables.isChina) {
|
||||
new HelpDialog(requireContext(), requireActivity(), "clear_cache_data.txt", true).show();
|
||||
} else {
|
||||
new HelpDialog(requireContext(), requireActivity(), "clear_cache_data_en.txt", true).show();
|
||||
}
|
||||
new HelpDialog(requireContext(), requireActivity()
|
||||
, GeneralVariables.getStringFromResource(R.string.clear_cache_data_help)
|
||||
, true).show();
|
||||
}
|
||||
});
|
||||
binding.clearFollowButton.setOnClickListener(new View.OnClickListener() {
|
|
@ -0,0 +1,5 @@
|
|||
关于音频输出设置
|
||||
|
||||
采样位深:也称采样精度,FT8CN只有16位整型和32位浮点可选。采样位数是表示声音强度量化后的精细程度,它的数值越大,波动幅度的分辨率也就越高,所发出声音的能力越强。
|
||||
|
||||
采样率:也称取样频率, 指每秒钟取得声音样本的次数。采样频率越高,声音的质量也就越好,但占的资源也多。
|
|
@ -0,0 +1,5 @@
|
|||
Audio Output Setting
|
||||
|
||||
Bit depth: Choose 16-bit int or 32-bit float. Bit depth dictates the number of possible amplitude values of audio sample. A higher bit depth will produce a higher resolution audio.
|
||||
|
||||
Sample rate: Sample rate refers to the number of samples that are present within one second of digital audio. Higher sample rate provides more accurate audio waveform, but consume more system resources.
|
|
@ -0,0 +1,4 @@
|
|||
关于解码模式
|
||||
快速解码:沿用原来FT8CN的解码,对接收到的音频做一次性的解码。
|
||||
多次解码:在快速解码的基础上,再做多次的解码,增加迭代的次数,并尝试解码频率有叠加的信号。
|
||||
注:多次解码增加的运算量,总的解码时间会变长,会缩短设备的续航时间。
|
|
@ -0,0 +1,4 @@
|
|||
Decode mode:
|
||||
Fast decode: Default mode, one decoding pass on received audio.
|
||||
Deep decode: Recursive decoding that process received audio in multiple passes .
|
||||
* Note: Deep decoding requires longer decoding time and more battery consumption.
|
|
@ -0,0 +1,3 @@
|
|||
消息模式,是指显示消息列表的方式。
|
||||
标准列表模式,就是FT8CN传统的FT8消息显示模式,包含FT8消息的要素外,还包括双方呼号归属地、距离、未通联的分区标志、消息类型等。
|
||||
精简列表模式,就是简化的FT8消息显示模式,只包含必要的FT8消息。
|
|
@ -0,0 +1,5 @@
|
|||
"Message mode" refers to how messages are displayed.
|
||||
|
||||
Standard list mode is the default FT8 display mode. In addition to FT8 messages, it includes callsign, location, distance, worked zone indicator, message type, etc.
|
||||
|
||||
Simple list mode is a simplified FT8 message display mode that contains only essential FT8 messages.
|
Some files were not shown because too many files have changed in this diff Show More
Ładowanie…
Reference in New Issue