kopia lustrzana https://github.com/N0BOY/FT8CN
Porównaj commity
2 Commity
e965043cbd
...
ef4f2fab92
Autor | SHA1 | Data |
---|---|---|
NØBOY | ef4f2fab92 | |
wangg | 84fe2ffdf8 |
|
@ -22,7 +22,7 @@ android {
|
|||
minSdk 23
|
||||
targetSdk 33
|
||||
versionCode 1
|
||||
versionName '0.91'
|
||||
versionName '0.92'
|
||||
|
||||
//testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
dataBinding{
|
||||
|
@ -83,13 +83,10 @@ dependencies {
|
|||
|
||||
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')//地图控件
|
||||
//implementation files('src/libs/resample-release.aar')//DS1UFX 提供的(tr)uSDX中的需要重采样库
|
||||
|
||||
}
|
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
|
@ -14,7 +14,20 @@ Please click "FAQ" if you have good suggestions or questions .
|
|||
BG7YOZ
|
||||
2022-07-01
|
||||
|
||||
2023-09-10(0.91)
|
||||
2024-01-22(0.92)
|
||||
1.增加瀑布图中消息的已通联标识。
|
||||
2.新增支持的电台型号。
|
||||
3.增加串口参数设置。
|
||||
4.新增解码的消息类型,支持全部FT8消息类型。
|
||||
5.修正串口错误提示只有中文的问题。
|
||||
6.完善SWL QSO日志记录。
|
||||
7.完善消息中带有/P或/R后缀呼号的通联程序。
|
||||
|
||||
|
||||
2023-09-14(0.91 patch 1)
|
||||
1.修正Yaesu FT-891/991 选择USB-DATA模式错误。
|
||||
|
||||
2023-09-11(0.91)
|
||||
1.修正发射非标准消息(i3=4)RR73被误发成73的问题。
|
||||
2.修正因多重解码模式下,无法及时回复上一周期解码的消息以及漏发73的问题。
|
||||
3.修正生成双方都是复合呼号的消息时,发送方的呼号可能不正确的问题。
|
||||
|
@ -308,4 +321,5 @@ BG7YOZ
|
|||
BG2EFX,提供大数据量的日志用于测试。
|
||||
DS1UFX,贡献(tr)uSDX audio over cat代码。
|
||||
BG8HT,提供某型号电台进行测试。
|
||||
UB6LUM,帮助解决某型号电台的操作模式设置。
|
||||
|
||||
|
|
|
@ -5,11 +5,11 @@ ICOM IC-7200,76,19200,0
|
|||
ICOM IC-7300,94,115200,0
|
||||
ICOM IC-7400,66,19200,0
|
||||
ICOM IC-7410,80,19200,0
|
||||
ICOM IC-746,56,19200,0
|
||||
ICOM IC-746PRO,66,19200,0
|
||||
ICOM IC-756PRO,5C,19200,0
|
||||
ICOM IC-756PRO2,64,19200,0
|
||||
ICOM IC-756PRO3,6E,19200,0
|
||||
ICOM IC-746,56,19200,22
|
||||
ICOM IC-746PRO,66,19200,22
|
||||
ICOM IC-756PRO,5C,19200,22
|
||||
ICOM IC-756PRO2,64,19200,22
|
||||
ICOM IC-756PRO3,6E,19200,22
|
||||
ICOM IC-7600,7A,19200,0
|
||||
ICOM IC-7610,98,115200,0
|
||||
ICOM IC-7700,74,19200,0
|
||||
|
@ -21,9 +21,22 @@ ICOM IC-9700,A2,115200,0
|
|||
ICOM IC-R8600,96,115200,0
|
||||
ICOM ID-52A,A6,115200,0
|
||||
ICOM IC-706MKIIG,58,19200,0
|
||||
ICOM IC-706MKII,4E,19200,0
|
||||
ICOM IC-703,68,19200,0
|
||||
ICOM IC-707(725A),3E,19200,0
|
||||
ICOM IC-718,5E,19200,0
|
||||
ICOM IC-725,28,19200,0
|
||||
ICOM IC-746PRO,66,19200,0
|
||||
ICOM IC-756PRO3,6E,19200,0
|
||||
ICOM IC-775,46,19200,0
|
||||
ICOM IC-910H,60,19200,0
|
||||
ICOM IC-78,62,19200,0
|
||||
#XIEGU(协谷) X6100(ft8cns),A4,19200,20
|
||||
XIEGU(协谷) X6100(U-DIG),A4,19200,13
|
||||
XIEGU(协谷) G90S(U-DIG),70,19200,13
|
||||
XIEGU(协谷) G90S(USB),70,19200,9
|
||||
XIEGU(协谷) G106C(U-DIG),70,19200,13
|
||||
XIEGU(协谷) G106C(USB),70,19200,9
|
||||
XIEGU(协谷) X5105,70,19200,9
|
||||
XIEGU(协谷) X108,70,19200,9
|
||||
GUOHE(国赫) Q900,00,19200,8
|
||||
|
@ -31,6 +44,7 @@ GUOHE(国赫) PMR-171,00,19200,8
|
|||
YAESU FT-450(D),00,4800,4
|
||||
YAESU FT-817,00,4800,1
|
||||
YAESU FT-818,00,4800,1
|
||||
YAESU FT-847,00,4800,21
|
||||
YAESU FT-857,00,4800,1
|
||||
YAESU FT-891/991(USB),00,4800,2
|
||||
YAESU FT-891/991(DATA-USB),00,4800,19
|
||||
|
@ -53,4 +67,5 @@ FX-4CR,00,115200,7
|
|||
Qrp Labs QDX,00,9600,7
|
||||
UA3REO Wolf SDR(DIGU),00,4800,15
|
||||
UA3REO Wolf SDR(USB),00,4800,16
|
||||
(tr)uSDX,00,115200,17
|
||||
(tr)uSDX (audio over cat),00,115200,17
|
||||
(tr)uSDX (TS-480),00,9600,7
|
|
@ -0,0 +1,5 @@
|
|||
关于串口设置
|
||||
数据位:一般可以是6位、7位或8位,多数是8位。
|
||||
停止位:一般可以是1位、1.5位或2位,多数是1位。
|
||||
校验位:校验位一般用来判断接收的数据位有无错误,一般采用奇偶校验或无校验。
|
||||
注:FT8CN默认的串口设置是数据位8、停止位1、无校验。不同的电台的串口设置可能不尽相同,具体设置请与电台的参数一致。
|
|
@ -0,0 +1,7 @@
|
|||
About Serial Connection Settings:
|
||||
Data bits: can be 6 bits, 7 bits, or 8 bits (8 bits is most common)
|
||||
Stop bits: can be 1 bit, 1.5 bits, or 2 bits (1 bit is most common)
|
||||
Parity bit: Parity bit is used to check for errors in received data and can be odd, even, or no parity.
|
||||
|
||||
Note: The default serial port settings for FT8CN are 8 data bits, 1 stop bit, and no parity.
|
||||
The serial connection settings may vary for different radios. Please ensure it matches the settings in your radio.
|
|
@ -12,6 +12,7 @@ package com.bg7yoz.ft8cn;
|
|||
*/
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
|
@ -47,6 +48,15 @@ public class Ft8Message {
|
|||
|
||||
public String extraInfo = null;
|
||||
public String maidenGrid = null;
|
||||
|
||||
public String rtty_state =null;//RTTY RU(i3=3类型)的地区名,两位字母如:CA、AL
|
||||
public int r_flag=0;//RTTY RU,EU VHF(i3=3,i3=5类型)的R标志
|
||||
public int rtty_tu;//RTTY RU(i3=3类型)的TU;标志
|
||||
public int eu_serial;//EU VHF i3=5中的序列号
|
||||
public String arrl_rac;//Field day 消息,Arrl rac
|
||||
public String arrl_class;//Field day 发射级别
|
||||
public String dx_call_to2;//DXpediton 消息中第二个接收的呼号
|
||||
|
||||
public int report = -100;//当-100时,意味着没有信号报告
|
||||
public long callFromHash10 = 0;//12位长度的哈希码
|
||||
public long callFromHash12 = 0;//12位长度的哈希码
|
||||
|
@ -80,6 +90,7 @@ public class Ft8Message {
|
|||
|
||||
|
||||
|
||||
|
||||
@NonNull
|
||||
@SuppressLint({"SimpleDateFormat", "DefaultLocale"})
|
||||
@Override
|
||||
|
@ -172,6 +183,16 @@ public class Ft8Message {
|
|||
hashList.addHash(callFromHash12, callsignFrom);
|
||||
hashList.addHash(callFromHash22, callsignFrom);
|
||||
|
||||
//rtty ru(i3=3)消息新增的
|
||||
rtty_tu = message.rtty_tu;
|
||||
rtty_state = message.rtty_state;
|
||||
r_flag =message.r_flag;
|
||||
eu_serial =message.eu_serial;
|
||||
//field day 增加的
|
||||
arrl_class = message.arrl_class;
|
||||
arrl_rac = message.arrl_rac;
|
||||
dx_call_to2 = message.dx_call_to2;
|
||||
|
||||
|
||||
//Log.d(TAG, String.format("i3:%d,n3:%d,From:%s,To:%s", i3, n3, getCallsignFrom(), getCallsignTo()));
|
||||
}
|
||||
|
@ -200,6 +221,7 @@ public class Ft8Message {
|
|||
*
|
||||
* @return String
|
||||
*/
|
||||
@SuppressLint("DefaultLocale")
|
||||
public String getMessageText() {
|
||||
|
||||
if (i3 == 0 && n3 == 0) {//说明是自由文本
|
||||
|
@ -209,6 +231,49 @@ public class Ft8Message {
|
|||
return extraInfo.toUpperCase().substring(0, 13);
|
||||
}
|
||||
}
|
||||
if (i3 == 0 && (n3 == 3 || n3 == 4)) {//说明是野外日
|
||||
return String.format("%s %s %s%d%s %s"
|
||||
,callsignTo
|
||||
,callsignFrom
|
||||
,r_flag==0?"":"R "
|
||||
,eu_serial
|
||||
,arrl_class
|
||||
,arrl_rac
|
||||
);
|
||||
}
|
||||
|
||||
if (i3 == 0 && (n3 == 1)) {//说明是DXpedition
|
||||
|
||||
return String.format("%s RR73; %s %s %s%d"
|
||||
,callsignTo
|
||||
,dx_call_to2
|
||||
,hashList.getCallsign(new long[]{callFromHash10})
|
||||
,report > 0 ? "+" : "-"
|
||||
,report
|
||||
);
|
||||
}
|
||||
|
||||
if (i3 == 3){//说明是RTTY RU消息
|
||||
return String.format("%s%s %s %s%d %s"
|
||||
,rtty_tu==0?"":"TU; "
|
||||
,callsignTo
|
||||
,callsignFrom
|
||||
,r_flag==0?"":"R "
|
||||
,report
|
||||
,rtty_state);
|
||||
}
|
||||
|
||||
if (i3 == 5){//说明是EU VHF <G4ABC> <PA9XYZ> R 570007 JO22DB
|
||||
return String.format("%s %s %s%d%04d %s"
|
||||
, callsignTo
|
||||
, callsignFrom
|
||||
, r_flag == 0?"":"R "
|
||||
, report
|
||||
, eu_serial
|
||||
, maidenGrid
|
||||
).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();
|
||||
|
@ -217,15 +282,6 @@ public class Ft8Message {
|
|||
return String.format("%s %s %s", callsignTo, callsignFrom, extraInfo).trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回解码消息带信噪比的内容
|
||||
*
|
||||
* @return 内容
|
||||
*/
|
||||
@SuppressLint("DefaultLocale")
|
||||
public String getMessageTextWithDb() {
|
||||
return String.format("%d %s %s %s", snr, callsignTo, callsignFrom, extraInfo).trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回消息的延迟时间。可能不一定对,待研究清楚解码算法后在确定
|
||||
|
@ -288,10 +344,13 @@ public class Ft8Message {
|
|||
* @return boolean
|
||||
*/
|
||||
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(""));
|
||||
//判断是不是自己,有时候,我的呼号带/P或/R,对方可能会丢掉这个后缀
|
||||
// if (GeneralVariables.myCallsign.length() == 0) return false;
|
||||
|
||||
return GeneralVariables.checkIsMyCallsign(this.callsignFrom)
|
||||
||GeneralVariables.checkIsMyCallsign(this.callsignTo);
|
||||
// return this.callsignFrom.contains(GeneralVariables.myCallsign)
|
||||
// || this.callsignTo.contains(GeneralVariables.myCallsign);
|
||||
}
|
||||
/*
|
||||
i3.n3类型 基本目的 消息范例 位字段标签
|
||||
|
@ -433,7 +492,9 @@ t71 遥感数据,最多18位十六进制数字
|
|||
case 2:
|
||||
return String.format(format, i, 0, GeneralVariables.getStringFromResource(R.string.std_msg));
|
||||
case 5:
|
||||
return String.format(format, i, 0, "EU VHF");
|
||||
case 3:
|
||||
return String.format(format, i, 0, GeneralVariables.getStringFromResource(R.string.rtty_ru_msg));
|
||||
case 4:
|
||||
return String.format(format, i, 0, GeneralVariables.getStringFromResource(R.string.none_std_msg));
|
||||
case 0:
|
||||
|
|
|
@ -6,6 +6,7 @@ package com.bg7yoz.ft8cn;
|
|||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
|
@ -18,6 +19,7 @@ import com.bg7yoz.ft8cn.html.HtmlContext;
|
|||
import com.bg7yoz.ft8cn.icom.IcomAudioUdp;
|
||||
import com.bg7yoz.ft8cn.log.QSLRecord;
|
||||
import com.bg7yoz.ft8cn.rigs.BaseRigOperation;
|
||||
import com.bg7yoz.ft8cn.serialport.UsbSerialPort;
|
||||
import com.bg7yoz.ft8cn.timer.UtcTimer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -171,6 +173,9 @@ public class GeneralVariables {
|
|||
public static int civAddress = 0xa4;//civ地址
|
||||
public static int baudRate = 19200;//波特率
|
||||
public static long band = 14074000;//载波频段
|
||||
public static int serialDataBits=8;//默认是8
|
||||
public static int serialParity = 0;//UsbSerialPort.PARITY_NONE默认是0,即:无
|
||||
public static int serialStopBits=1;//停止位的对应关系:1=1,2=3,3=1.5
|
||||
public static int instructionSet = 0;//指令集,0:icom,1:yaesu 2 代,2:yaesu 3代。
|
||||
public static int bandListIndex = -1;//电台波段的索引值
|
||||
public static MutableLiveData<Integer> mutableBandChange = new MutableLiveData<>();//波段索引值变化
|
||||
|
@ -254,6 +259,37 @@ public class GeneralVariables {
|
|||
return QSL_Callsign_list_other_band.contains(callsign);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查呼号中是不是含有我的呼号
|
||||
* @param callsign 呼号
|
||||
* @return boolean
|
||||
*/
|
||||
static public boolean checkIsMyCallsign(String callsign){
|
||||
if (GeneralVariables.myCallsign.length() == 0) return false;
|
||||
String temp = getShortCallsign(GeneralVariables.myCallsign);
|
||||
return callsign.contains(temp);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对于复合呼号,获取去掉前缀或后缀的呼号
|
||||
* @return 呼号
|
||||
*/
|
||||
static public String getShortCallsign(String callsign){
|
||||
if (callsign.contains("/")){
|
||||
String[] temp = callsign.split("/");
|
||||
int max =0;
|
||||
int max_index =0;
|
||||
for (int i = 0; i < temp.length; i++) {
|
||||
if (temp[i].length()>max){
|
||||
max = temp[i].length();
|
||||
max_index=i;
|
||||
}
|
||||
}
|
||||
return temp[max_index];
|
||||
}else{
|
||||
return callsign;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查该呼号是不是在关注的呼号列表中
|
||||
|
|
|
@ -16,7 +16,6 @@ import android.Manifest;
|
|||
import android.animation.Animator;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.AlertDialog;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
|
@ -61,12 +60,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;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
|
@ -232,30 +226,16 @@ public class MainActivity extends AppCompatActivity {
|
|||
|
||||
|
||||
//观察是不是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配置按钮
|
||||
|
@ -264,6 +244,24 @@ public class MainActivity extends AppCompatActivity {
|
|||
}
|
||||
});
|
||||
|
||||
//观察是不是xiegu radio
|
||||
mainViewModel.mutableIsXieguRadio.observe(this, new Observer<Boolean>() {
|
||||
@Override
|
||||
public void onChanged(Boolean aBoolean) {
|
||||
if (aBoolean) {
|
||||
//添加xiegu配置按钮
|
||||
floatView.addButton(R.id.xiegu_radio, "xiegu_radio", R.drawable.xiegulogo32
|
||||
, new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
navController.navigate(R.id.xieguInfoFragment);
|
||||
}
|
||||
});
|
||||
} else {//删除xiegu配置按钮
|
||||
floatView.deleteButtonByName("xiegu_radio");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//关闭串口设备列表按钮
|
||||
binding.closeSelectSerialPortImageView.setOnClickListener(new View.OnClickListener() {
|
||||
|
|
|
@ -46,11 +46,13 @@ import com.bg7yoz.ft8cn.connector.CableSerialPort;
|
|||
import com.bg7yoz.ft8cn.connector.ConnectMode;
|
||||
import com.bg7yoz.ft8cn.connector.FlexConnector;
|
||||
import com.bg7yoz.ft8cn.connector.IComWifiConnector;
|
||||
import com.bg7yoz.ft8cn.connector.X6100Connector;
|
||||
import com.bg7yoz.ft8cn.database.ControlMode;
|
||||
import com.bg7yoz.ft8cn.database.DatabaseOpr;
|
||||
import com.bg7yoz.ft8cn.database.OnAfterQueryFollowCallsigns;
|
||||
import com.bg7yoz.ft8cn.database.OperationBand;
|
||||
import com.bg7yoz.ft8cn.flex.FlexRadio;
|
||||
import com.bg7yoz.ft8cn.flex.RadioTcpClient;
|
||||
import com.bg7yoz.ft8cn.ft8listener.FT8SignalListener;
|
||||
import com.bg7yoz.ft8cn.ft8listener.OnFt8Listen;
|
||||
import com.bg7yoz.ft8cn.ft8transmit.FT8TransmitSignal;
|
||||
|
@ -76,9 +78,11 @@ import com.bg7yoz.ft8cn.rigs.KenwoodTS590Rig;
|
|||
import com.bg7yoz.ft8cn.rigs.OnRigStateChanged;
|
||||
import com.bg7yoz.ft8cn.rigs.TrUSDXRig;
|
||||
import com.bg7yoz.ft8cn.rigs.Wolf_sdr_450Rig;
|
||||
import com.bg7yoz.ft8cn.rigs.XieGu6100NetRig;
|
||||
import com.bg7yoz.ft8cn.rigs.XieGu6100Rig;
|
||||
import com.bg7yoz.ft8cn.rigs.XieGuRig;
|
||||
import com.bg7yoz.ft8cn.rigs.Yaesu2Rig;
|
||||
import com.bg7yoz.ft8cn.rigs.Yaesu2_847Rig;
|
||||
import com.bg7yoz.ft8cn.rigs.Yaesu38Rig;
|
||||
import com.bg7yoz.ft8cn.rigs.Yaesu38_450Rig;
|
||||
import com.bg7yoz.ft8cn.rigs.Yaesu39Rig;
|
||||
|
@ -89,6 +93,7 @@ import com.bg7yoz.ft8cn.timer.UtcTimer;
|
|||
import com.bg7yoz.ft8cn.ui.ToastMessage;
|
||||
import com.bg7yoz.ft8cn.wave.HamRecorder;
|
||||
import com.bg7yoz.ft8cn.wave.OnGetVoiceDataDone;
|
||||
import com.bg7yoz.ft8cn.x6100.X6100Radio;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
@ -126,6 +131,7 @@ public class MainViewModel extends ViewModel {
|
|||
public ArrayList<Ft8Message> currentMessages = null;//本周期解码的消息(用于画到频谱上)
|
||||
|
||||
public MutableLiveData<Boolean> mutableIsFlexRadio = new MutableLiveData<>();//是不是flex电台
|
||||
public MutableLiveData<Boolean> mutableIsXieguRadio = new MutableLiveData<>();//是不是flex电台
|
||||
|
||||
private final ExecutorService getQTHThreadPool = Executors.newCachedThreadPool();
|
||||
private final ExecutorService sendWaveDataThreadPool = Executors.newCachedThreadPool();
|
||||
|
@ -251,6 +257,7 @@ public class MainViewModel extends ViewModel {
|
|||
hamRecorder.startRecord();
|
||||
|
||||
mutableIsFlexRadio.setValue(false);
|
||||
mutableIsXieguRadio.setValue(false);
|
||||
|
||||
//创建用于显示时间的计时器
|
||||
utcTimer = new UtcTimer(10, false, new OnUtcTimer() {
|
||||
|
@ -494,8 +501,10 @@ public class MainViewModel extends ViewModel {
|
|||
int count = 0;
|
||||
for (Ft8Message msg : messages) {
|
||||
//与我的呼号有关,与关注的呼号有关
|
||||
if (msg.getCallsignFrom().equals(GeneralVariables.myCallsign)
|
||||
|| msg.getCallsignTo().equals(GeneralVariables.myCallsign)
|
||||
//if (msg.getCallsignFrom().equals(GeneralVariables.myCallsign)
|
||||
if (GeneralVariables.checkIsMyCallsign(msg.getCallsignFrom())
|
||||
//|| msg.getCallsignTo().equals(GeneralVariables.myCallsign)
|
||||
|| GeneralVariables.checkIsMyCallsign(msg.getCallsignTo())
|
||||
|| GeneralVariables.callsignInFollow(msg.getCallsignFrom())
|
||||
|| (GeneralVariables.callsignInFollow(msg.getCallsignTo()) && (msg.getCallsignTo() != null))
|
||||
|| (GeneralVariables.autoFollowCQ && msg.checkIsCQ())) {//是CQ,并且允许关注CQ
|
||||
|
@ -768,6 +777,54 @@ public class MainViewModel extends ViewModel {
|
|||
}, 3000);
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接到协谷Radio
|
||||
*
|
||||
* @param context context
|
||||
* @param xieguRadio X6100Radio对象
|
||||
*/
|
||||
public void connectXieguRadioRig(Context context, X6100Radio xieguRadio) {
|
||||
if (GeneralVariables.connectMode == ConnectMode.NETWORK) {
|
||||
if (baseRig != null) {
|
||||
if (baseRig.getConnector() != null) {
|
||||
baseRig.getConnector().disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
GeneralVariables.controlMode = ControlMode.CAT;//网络控制模式
|
||||
X6100Connector xieguConnector = new X6100Connector(context, xieguRadio, GeneralVariables.controlMode);
|
||||
xieguConnector.setOnWaveDataReceived(new X6100Connector.OnWaveDataReceived() {
|
||||
@Override
|
||||
public void OnDataReceived(int bufferLen, float[] buffer) {
|
||||
//Log.e(TAG,String.format("data len:%d",bufferLen));
|
||||
hamRecorder.doOnWaveDataReceived(bufferLen, buffer);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
xieguConnector.connect();
|
||||
connectRig();
|
||||
xieguConnector.setBaseRig(baseRig);
|
||||
//接收电台发回的数据
|
||||
xieguRadio.setOnReceiveDataListener(new X6100Radio.OnReceiveDataListener() {
|
||||
@Override
|
||||
public void onDataReceive(byte[] data) {
|
||||
baseRig.onReceiveData(data);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
baseRig.setOnRigStateChanged(onRigStateChanged);
|
||||
baseRig.setConnector(xieguConnector);
|
||||
|
||||
new Handler().postDelayed(new Runnable() {//连接是需要时间的,等2秒再设置频率
|
||||
@Override
|
||||
public void run() {
|
||||
setOperationBand();//设置载波频率
|
||||
}
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据指令集创建不同型号的电台
|
||||
|
@ -778,11 +835,17 @@ public class MainViewModel extends ViewModel {
|
|||
//此处判断是用什么类型的电台,ICOM,YAESU 2,YAESU 3
|
||||
switch (GeneralVariables.instructionSet) {
|
||||
case InstructionSet.ICOM:
|
||||
baseRig = new IcomRig(GeneralVariables.civAddress);
|
||||
baseRig = new IcomRig(GeneralVariables.civAddress,true);
|
||||
break;
|
||||
case InstructionSet.ICOM_756:
|
||||
baseRig = new IcomRig(GeneralVariables.civAddress,false);
|
||||
break;
|
||||
case InstructionSet.YAESU_2:
|
||||
baseRig = new Yaesu2Rig();
|
||||
break;
|
||||
case InstructionSet.YAESU_847:
|
||||
baseRig = new Yaesu2_847Rig();
|
||||
break;
|
||||
case InstructionSet.YAESU_3_9:
|
||||
baseRig = new Yaesu39Rig(false);//yaesu3代指令,9位频率,usb模式
|
||||
break;
|
||||
|
@ -819,6 +882,13 @@ public class MainViewModel extends ViewModel {
|
|||
case InstructionSet.FLEX_NETWORK:
|
||||
baseRig = new FlexNetworkRig();
|
||||
break;
|
||||
case InstructionSet.XIEGU_6100_FT8CNS:
|
||||
if (GeneralVariables.connectMode == ConnectMode.NETWORK) {//只在网络模式下工作
|
||||
baseRig = new XieGu6100NetRig(GeneralVariables.civAddress);//协谷6100ft8cns模式
|
||||
}else{//否则使用传统的模式
|
||||
baseRig = new XieGu6100Rig(GeneralVariables.civAddress);//协谷6100
|
||||
}
|
||||
break;
|
||||
case InstructionSet.XIEGU_6100:
|
||||
baseRig = new XieGu6100Rig(GeneralVariables.civAddress);//协谷6100
|
||||
break;
|
||||
|
@ -841,11 +911,11 @@ public class MainViewModel extends ViewModel {
|
|||
|
||||
if ((GeneralVariables.instructionSet == InstructionSet.FLEX_NETWORK)
|
||||
|| ((GeneralVariables.instructionSet == InstructionSet.ICOM
|
||||
||GeneralVariables.instructionSet==InstructionSet.XIEGU_6100)
|
||||
|| GeneralVariables.instructionSet==InstructionSet.XIEGU_6100
|
||||
|| GeneralVariables.instructionSet==InstructionSet.XIEGU_6100_FT8CNS)
|
||||
&& GeneralVariables.connectMode == ConnectMode.NETWORK)) {
|
||||
hamRecorder.setDataFromLan();
|
||||
} else {
|
||||
//hamRecorder.setDataFromMic();
|
||||
if (GeneralVariables.controlMode != ControlMode.CAT || baseRig == null
|
||||
|| !baseRig.supportWaveOverCAT()) {
|
||||
hamRecorder.setDataFromMic();
|
||||
|
@ -855,6 +925,7 @@ public class MainViewModel extends ViewModel {
|
|||
}
|
||||
|
||||
mutableIsFlexRadio.postValue(GeneralVariables.instructionSet == InstructionSet.FLEX_NETWORK);
|
||||
mutableIsXieguRadio.postValue(GeneralVariables.instructionSet == InstructionSet.XIEGU_6100_FT8CNS);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -91,6 +91,13 @@ public class BaseRigConnector {
|
|||
public void sendWaveData(float[] data){
|
||||
//留给网络方式发送音频流
|
||||
}
|
||||
public void sendFt8A91(byte[] a91,float baseFreq){
|
||||
//用于给x6100的ft8cns模式
|
||||
}
|
||||
|
||||
public void setRFVolume(int volume){
|
||||
//用于给x6100的ft8cns模式
|
||||
}
|
||||
|
||||
//2023-08-16 由DS1UFX提交修改(基于0.9版),用于(tr)uSDX audio over cat的支持。
|
||||
public void receiveWaveData(byte[] data){
|
||||
|
|
|
@ -14,11 +14,11 @@ import com.bg7yoz.ft8cn.serialport.util.SerialInputOutputManager;
|
|||
* @date 2023-03-20
|
||||
*/
|
||||
public class CableConnector extends BaseRigConnector {
|
||||
private static final String TAG="CableConnector";
|
||||
private static final String TAG = "CableConnector";
|
||||
|
||||
//2023-08-16 由DS1UFX提交修改(基于0.9版),用于(tr)uSDX audio over cat的支持。
|
||||
public interface OnCableDataReceived {
|
||||
void OnWaveReceived(int bufferLen,float[] buffer);
|
||||
void OnWaveReceived(int bufferLen, float[] buffer);
|
||||
}
|
||||
|
||||
private final CableSerialPort cableSerialPort;
|
||||
|
@ -26,26 +26,26 @@ public class CableConnector extends BaseRigConnector {
|
|||
private final BaseRig cableConnectedRig;
|
||||
private OnCableDataReceived onCableDataReceived;
|
||||
|
||||
public CableConnector(Context context,CableSerialPort.SerialPort serialPort, int baudRate
|
||||
//, int controlMode) {
|
||||
public CableConnector(Context context, CableSerialPort.SerialPort serialPort, int baudRate
|
||||
//, int controlMode) {
|
||||
, int controlMode, BaseRig cableConnectedRig) {
|
||||
super(controlMode);
|
||||
this.cableConnectedRig = cableConnectedRig;
|
||||
cableSerialPort= new CableSerialPort(context,serialPort,baudRate,getOnConnectorStateChanged());
|
||||
cableSerialPort.ioListener=new SerialInputOutputManager.Listener() {
|
||||
cableSerialPort = new CableSerialPort(context, serialPort, baudRate, getOnConnectorStateChanged());
|
||||
cableSerialPort.ioListener = new SerialInputOutputManager.Listener() {
|
||||
@Override
|
||||
public void onNewData(byte[] data) {
|
||||
if (getOnConnectReceiveData()!=null){
|
||||
if (getOnConnectReceiveData() != null) {
|
||||
getOnConnectReceiveData().onData(data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRunError(Exception e) {
|
||||
Log.e(TAG, "CableConnector error: "+e.getMessage() );
|
||||
getOnConnectorStateChanged().onRunError("与串口失去连接:"+e.getMessage());
|
||||
Log.e(TAG, "CableConnector error: " + e.getMessage());
|
||||
getOnConnectorStateChanged().onRunError("与串口失去连接:" + e.getMessage());
|
||||
}
|
||||
} ;
|
||||
};
|
||||
//connect();
|
||||
}
|
||||
|
||||
|
@ -58,10 +58,12 @@ public class CableConnector extends BaseRigConnector {
|
|||
@Override
|
||||
public void setPttOn(boolean on) {
|
||||
//只处理RTS和DTR
|
||||
switch (getControlMode()){
|
||||
case ControlMode.DTR: cableSerialPort.setDTR_On(on);//打开和关闭DTR
|
||||
switch (getControlMode()) {
|
||||
case ControlMode.DTR:
|
||||
cableSerialPort.setDTR_On(on);//打开和关闭DTR
|
||||
break;
|
||||
case ControlMode.RTS:cableSerialPort.setRTS_On(on);//打开和关闭RTS
|
||||
case ControlMode.RTS:
|
||||
cableSerialPort.setRTS_On(on);//打开和关闭RTS
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -87,12 +89,12 @@ public class CableConnector extends BaseRigConnector {
|
|||
// }
|
||||
|
||||
@Override
|
||||
public void receiveWaveData(float[] data){
|
||||
public void receiveWaveData(float[] data) {
|
||||
Log.i(TAG, "received wave data");
|
||||
|
||||
if (onCableDataReceived!=null){
|
||||
if (onCableDataReceived != null) {
|
||||
Log.i(TAG, "call onCableDataReceived.OnWaveReceived");
|
||||
onCableDataReceived.OnWaveReceived(data.length,data);
|
||||
onCableDataReceived.OnWaveReceived(data.length, data);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ import android.os.Build;
|
|||
import android.util.Log;
|
||||
|
||||
import com.bg7yoz.ft8cn.BuildConfig;
|
||||
import com.bg7yoz.ft8cn.GeneralVariables;
|
||||
import com.bg7yoz.ft8cn.R;
|
||||
import com.bg7yoz.ft8cn.serialport.CdcAcmSerialDriver;
|
||||
import com.bg7yoz.ft8cn.serialport.UsbSerialDriver;
|
||||
import com.bg7yoz.ft8cn.serialport.UsbSerialPort;
|
||||
|
@ -134,7 +136,7 @@ public class CableSerialPort {
|
|||
}
|
||||
if (driver == null) {
|
||||
if (onStateChanged!=null){
|
||||
onStateChanged.onRunError("无法连接串口,没有驱动或串口不存在!");
|
||||
onStateChanged.onRunError(GeneralVariables.getStringFromResource(R.string.serial_no_driver));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -164,7 +166,7 @@ public class CableSerialPort {
|
|||
}
|
||||
if (usbConnection == null) {
|
||||
if (onStateChanged!=null){
|
||||
onStateChanged.onRunError("无法连接串口,可能没有访问USB设备的权限!");
|
||||
onStateChanged.onRunError(GeneralVariables.getStringFromResource(R.string.serial_connect_no_access));
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -172,7 +174,13 @@ public class CableSerialPort {
|
|||
try {
|
||||
usbSerialPort.open(usbConnection);
|
||||
//波特率、停止位
|
||||
usbSerialPort.setParameters(baudRate, 8, 1, UsbSerialPort.PARITY_NONE);
|
||||
//usbSerialPort.setParameters(baudRate, 8, 1, UsbSerialPort.PARITY_NONE);
|
||||
Log.d(TAG,String.format("serial:baud rate:%d,data bits:%d,stop bits:%d,parity bit:%d"
|
||||
,baudRate,GeneralVariables.serialDataBits
|
||||
,GeneralVariables.serialStopBits
|
||||
,GeneralVariables.serialParity));
|
||||
usbSerialPort.setParameters(baudRate, GeneralVariables.serialDataBits
|
||||
, GeneralVariables.serialStopBits, GeneralVariables.serialParity);
|
||||
usbIoManager = new SerialInputOutputManager(usbSerialPort, new SerialInputOutputManager.Listener() {
|
||||
@Override
|
||||
public void onNewData(byte[] data) {
|
||||
|
@ -201,7 +209,8 @@ public class CableSerialPort {
|
|||
} catch (Exception e) {
|
||||
Log.e(TAG, "串口打开失败: " + e.getMessage());
|
||||
if (onStateChanged!=null){
|
||||
onStateChanged.onRunError("串口打开失败: " + e.getMessage());
|
||||
onStateChanged.onRunError(GeneralVariables.getStringFromResource(R.string.serial_connect_failed)
|
||||
+ e.getMessage());
|
||||
}
|
||||
disconnect();
|
||||
return false;
|
||||
|
|
|
@ -21,7 +21,7 @@ import java.io.DataInputStream;
|
|||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 有线连接方式的Connector,这里是指USB方式的
|
||||
* flex网络连接方式的Connector
|
||||
* @author BGY70Z
|
||||
* @date 2023-03-20
|
||||
*/
|
||||
|
@ -35,7 +35,7 @@ public class FlexConnector extends BaseRigConnector {
|
|||
public int maxRfPower;
|
||||
public int maxTunePower;
|
||||
|
||||
private static final String TAG = "CableConnector";
|
||||
private static final String TAG = "FlexConnector";
|
||||
|
||||
private FlexRadio flexRadio;
|
||||
|
||||
|
@ -108,7 +108,7 @@ public class FlexConnector extends BaseRigConnector {
|
|||
//Log.e(TAG, "onResponse: "+response.resultStatus());
|
||||
}
|
||||
|
||||
Log.e(TAG, "onResponse: command:"+response.flexCommand.toString());
|
||||
Log.d(TAG, "onResponse: command:"+response.flexCommand.toString());
|
||||
//Log.e(TAG, "onResponse: "+response.resultStatus());
|
||||
Log.e(TAG, "onResponse: "+response.rawData );
|
||||
|
||||
|
|
|
@ -0,0 +1,283 @@
|
|||
package com.bg7yoz.ft8cn.connector;
|
||||
|
||||
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.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;
|
||||
import com.bg7yoz.ft8cn.rigs.BaseRig;
|
||||
import com.bg7yoz.ft8cn.ui.ToastMessage;
|
||||
import com.bg7yoz.ft8cn.x6100.X6100Meters;
|
||||
import com.bg7yoz.ft8cn.x6100.X6100Radio;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 网络连接方式连接xiegu ft8cns
|
||||
* @author BGY70Z
|
||||
* @date 2023-12-01
|
||||
*/
|
||||
public class X6100Connector extends BaseRigConnector {
|
||||
|
||||
public interface OnWaveDataReceived{
|
||||
void OnDataReceived(int bufferLen,float[] buffer);
|
||||
}
|
||||
//public int maxRfPower;
|
||||
//public int maxTunePower;
|
||||
|
||||
private static final String TAG = "X6100Connector";
|
||||
|
||||
private X6100Radio xieguRadio;
|
||||
|
||||
private OnWaveDataReceived onWaveDataReceived;
|
||||
|
||||
private BaseRig baseRig;
|
||||
private boolean streamIsOn =false;
|
||||
|
||||
public float maxTXPower=10.0f;
|
||||
public MutableLiveData<Float> mutableMaxTxPower = new MutableLiveData<>();
|
||||
|
||||
|
||||
public X6100Connector(Context context, X6100Radio xiegRadio, int controlMode) {
|
||||
super(controlMode);
|
||||
this.xieguRadio = xiegRadio;
|
||||
setXieguRadioInterface();
|
||||
|
||||
}
|
||||
|
||||
public static short[] byteDataTo16BitData(byte[] buffer){
|
||||
short[] data=new short[buffer.length /2];
|
||||
for (int i = 0; i < buffer.length/2; i++) {
|
||||
short res = (short) ((buffer[i*2+1] & 0x00FF) | (((short) buffer[i*2]) << 8));
|
||||
data[i]=res;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 把原始的声音数据转换成16位的数组数据。
|
||||
* @param buffer 原始的声音数据(8位)
|
||||
* @return 返回16位的int格式数组
|
||||
*/
|
||||
private float[] byteDataToFloatData(byte[] buffer){
|
||||
float[] data=new float[buffer.length /2];
|
||||
for (int i = 0; i < buffer.length/2; i++) {
|
||||
int res = (buffer[i*2] & 0x000000FF) | (((int) buffer[i*2+1]) << 8);
|
||||
data[i]=res/32768.0f;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
private void setXieguRadioInterface() {
|
||||
xieguRadio.setOnReceiveStreamData(new X6100Radio.OnReceiveStreamData() {
|
||||
@Override
|
||||
public void onReceiveAudio(byte[] data) {
|
||||
if (onWaveDataReceived!=null){
|
||||
float[] waveFloat = byteDataToFloatData(data);
|
||||
onWaveDataReceived.OnDataReceived(waveFloat.length,waveFloat);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceiveIQ(byte[] data) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceiveFFT(VITA vita) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceiveMeter(X6100Meters meters) {
|
||||
maxTXPower= meters.max_power;
|
||||
mutableMaxTxPower.postValue(maxTXPower);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceiveUnKnow(byte[] data) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
//当有命令返回值时的事件
|
||||
xieguRadio.setOnCommandListener(new X6100Radio.OnCommandListener() {
|
||||
@Override
|
||||
public void onResponse(X6100Radio.XieguResponse response) {
|
||||
Log.d(TAG, String.format("onResponse(%s): %s"
|
||||
,response.xieguCommand.toString(),response.rawData ));
|
||||
if (response.xieguCommand == X6100Radio.XieguCommand.STREAM){
|
||||
if (response.resultContent.toUpperCase().contains("PORT=")){//说明流端口打开了
|
||||
streamIsOn =true;
|
||||
}
|
||||
}
|
||||
|
||||
if (response.resultCode!=0) {//只显示失败的命令
|
||||
ToastMessage.show(response.resultContent);
|
||||
Log.e(TAG, "onResponse: "+response.resultContent);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
//当有状态信息接收到时
|
||||
xieguRadio.setOnStatusListener(new X6100Radio.OnStatusListener() {
|
||||
@Override
|
||||
public void onStatus(X6100Radio.XieguResponse response) {
|
||||
//显示状态消息
|
||||
if (response.resultCode == 0){//说明是电台状态变化了
|
||||
String status[] = response.resultContent.split(" ");
|
||||
for (int i = 0; i < status.length; i++) {
|
||||
if (status[i].startsWith("active_freq")){//找出频率状态,设置频率
|
||||
String temp[]=status[i].split("=");
|
||||
if (baseRig != null) {
|
||||
baseRig.setFreq(Long.parseLong(temp[1].trim()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "onStatus: "+response.rawData );
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
xieguRadio.setOnTcpConnectStatus(new X6100Radio.OnTcpConnectStatus() {
|
||||
@SuppressLint("DefaultLocale")
|
||||
@Override
|
||||
public void onConnectSuccess(RadioTcpClient tcpClient) {
|
||||
ToastMessage.show(String.format(GeneralVariables.getStringFromResource(R.string.init_flex_operation)
|
||||
,xieguRadio.getModelName()));
|
||||
new Thread(new Runnable() {//此处使用线程方式,是防止tcp对象阻塞
|
||||
@Override
|
||||
public void run() {
|
||||
while (!streamIsOn) {//等待电台打开流端口
|
||||
xieguRadio.commandOpenStream();//设置UDP端口
|
||||
try {
|
||||
Thread.sleep(300);
|
||||
} catch (InterruptedException e) {
|
||||
Log.e(TAG, Objects.requireNonNull(e.getMessage()));
|
||||
}
|
||||
//todo 此处经常丢命令
|
||||
xieguRadio.commandGetAudioInfo();//读取6100播放的参数
|
||||
//xieguRadio.commandSubGetMeter();//查阅仪表索引编号
|
||||
xieguRadio.commandSubAllMeter();//订阅仪表流数据
|
||||
//xieguRadio.commandSetTxPower(1);//订阅仪表流数据
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectFail(RadioTcpClient tcpClient) {
|
||||
ToastMessage.show(String.format(GeneralVariables.getStringFromResource
|
||||
(R.string.xiegu_connect_failed),xieguRadio.getModelName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionClosed(RadioTcpClient tcpClient) {
|
||||
if (baseRig.getOnRigStateChanged()!=null) {
|
||||
baseRig.getOnRigStateChanged().onDisconnected();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void sendData(byte[] data) {
|
||||
xieguRadio.sendData(data);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setPttOn(boolean on) {
|
||||
xieguRadio.isPttOn=on;
|
||||
xieguRadio.commandPTTOnOff(on);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPttOn(byte[] command) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendWaveData(float[] data) {
|
||||
xieguRadio.sendWaveData(data);
|
||||
}
|
||||
|
||||
|
||||
public void setMaxTXPower(int power){
|
||||
maxTXPower=power;
|
||||
mutableMaxTxPower.postValue(maxTXPower);
|
||||
GeneralVariables.flexMaxRfPower=power;
|
||||
xieguRadio.commandSetTxPower(power);//设置发射功率
|
||||
|
||||
}
|
||||
|
||||
//传送a91数据包的方式
|
||||
@Override
|
||||
public void sendFt8A91(byte[] a91,float baseFreq){
|
||||
Log.d(TAG,String.format("A91:%s", BaseRig.byteToStr(a91)));
|
||||
//xieguRadio.commandSendA91(a91,GeneralVariables.volumePercent,baseFreq);
|
||||
xieguRadio.commandSendA91(a91,0.95f,baseFreq);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRFVolume(int volume) {
|
||||
xieguRadio.commandSetTxVol(volume);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect() {
|
||||
super.connect();
|
||||
xieguRadio.openAudio();
|
||||
xieguRadio.connect();
|
||||
xieguRadio.openStreamPort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
super.disconnect();
|
||||
xieguRadio.closeAudio();
|
||||
xieguRadio.closeStreamPort();
|
||||
xieguRadio.disConnect();
|
||||
}
|
||||
|
||||
public OnWaveDataReceived getOnWaveDataReceived() {
|
||||
return onWaveDataReceived;
|
||||
}
|
||||
|
||||
public void setOnWaveDataReceived(OnWaveDataReceived onWaveDataReceived) {
|
||||
this.onWaveDataReceived = onWaveDataReceived;
|
||||
}
|
||||
|
||||
public BaseRig getBaseRig() {
|
||||
return baseRig;
|
||||
}
|
||||
|
||||
public void setBaseRig(BaseRig baseRig) {
|
||||
this.baseRig = baseRig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return xieguRadio.isConnect();
|
||||
}
|
||||
|
||||
public X6100Radio getXieguRadio() {
|
||||
return xieguRadio;
|
||||
}
|
||||
}
|
|
@ -51,7 +51,7 @@ public class DatabaseOpr extends SQLiteOpenHelper {
|
|||
|
||||
public static DatabaseOpr getInstance(@Nullable Context context, @Nullable String databaseName) {
|
||||
if (instance == null) {
|
||||
instance = new DatabaseOpr(context, databaseName, null, 14);
|
||||
instance = new DatabaseOpr(context, databaseName, null, 15);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
@ -364,6 +364,7 @@ public class DatabaseOpr extends SQLiteOpenHelper {
|
|||
}
|
||||
|
||||
private void createSWLTables(SQLiteDatabase sqLiteDatabase) {
|
||||
//Log.e(TAG,"upgrade database.");
|
||||
if (!checkTableExists(sqLiteDatabase, "SWLMessages")) {
|
||||
sqLiteDatabase.execSQL("CREATE TABLE SWLMessages (\n" +
|
||||
"\tID INTEGER PRIMARY KEY AUTOINCREMENT,\n" +
|
||||
|
@ -384,6 +385,7 @@ public class DatabaseOpr extends SQLiteOpenHelper {
|
|||
"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" +
|
||||
|
@ -400,7 +402,11 @@ public class DatabaseOpr extends SQLiteOpenHelper {
|
|||
"\tfreq TEXT,\n" +
|
||||
"\tstation_callsign TEXT,\n" +
|
||||
"\tmy_gridsquare TEXT,\n" +
|
||||
"\toperator TEXT,\n" +
|
||||
"\tcomment TEXT)");
|
||||
}else {
|
||||
alterTable(sqLiteDatabase, "SWLQSOTable", "operator"
|
||||
, "operator TEXT");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1201,8 +1207,8 @@ public class DatabaseOpr extends SQLiteOpenHelper {
|
|||
});
|
||||
//添加记录
|
||||
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)\n" +
|
||||
"VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
|
||||
"time_on, qso_date_off, time_off, band, freq, station_callsign, my_gridsquare,operator,comment)\n" +
|
||||
"VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
|
||||
|
||||
databaseOpr.db.execSQL(querySQL, new String[]{qslRecord.getToCallsign()
|
||||
, qslRecord.getToMaidenGrid()
|
||||
|
@ -1218,6 +1224,7 @@ public class DatabaseOpr extends SQLiteOpenHelper {
|
|||
, BaseRigOperation.getFrequencyFloat(qslRecord.getBandFreq())
|
||||
, qslRecord.getMyCallsign()
|
||||
, qslRecord.getMyMaidenGrid()
|
||||
, GeneralVariables.myCallsign//我的呼号,不是双方的呼号
|
||||
, qslRecord.getComment()});
|
||||
|
||||
|
||||
|
@ -1794,6 +1801,12 @@ public class DatabaseOpr extends SQLiteOpenHelper {
|
|||
Ft8Message.hashList.addHash(FT8Package.getHash22(callsign), callsign);
|
||||
Ft8Message.hashList.addHash(FT8Package.getHash12(callsign), callsign);
|
||||
Ft8Message.hashList.addHash(FT8Package.getHash10(callsign), callsign);
|
||||
if (callsign.contains("/")) {
|
||||
String shortCallsign = GeneralVariables.getShortCallsign(callsign);
|
||||
Ft8Message.hashList.addHash(FT8Package.getHash22(shortCallsign), shortCallsign);
|
||||
Ft8Message.hashList.addHash(FT8Package.getHash12(shortCallsign), shortCallsign);
|
||||
Ft8Message.hashList.addHash(FT8Package.getHash10(shortCallsign), shortCallsign);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (name.equalsIgnoreCase("toModifier")) {
|
||||
|
@ -1899,6 +1912,16 @@ public class DatabaseOpr extends SQLiteOpenHelper {
|
|||
if (name.equalsIgnoreCase("deepMode")) {//是不是深度解码模式
|
||||
GeneralVariables.deepDecodeMode =result.equals("1");
|
||||
}
|
||||
if (name.equalsIgnoreCase("dataBits")) {//串口数据位
|
||||
GeneralVariables.serialDataBits =Integer.parseInt(result);
|
||||
}
|
||||
if (name.equalsIgnoreCase("stopBits")) {//串口停止位
|
||||
GeneralVariables.serialStopBits =Integer.parseInt(result);
|
||||
}
|
||||
if (name.equalsIgnoreCase("parityBits")) {//串口校验位
|
||||
GeneralVariables.serialParity =Integer.parseInt(result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
cursor.close();
|
||||
|
|
|
@ -5,6 +5,8 @@ package com.bg7yoz.ft8cn.flex;
|
|||
* @date 2023-03-20
|
||||
*/
|
||||
|
||||
import static com.bg7yoz.ft8cn.flex.VITA.readShortData;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
@ -73,25 +75,8 @@ public class FlexMeterList extends HashMap<Integer, FlexMeterList.FlexMeter> {
|
|||
|
||||
|
||||
|
||||
/**
|
||||
* 把字节转换成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 {
|
||||
|
|
|
@ -15,6 +15,7 @@ import androidx.annotation.NonNull;
|
|||
|
||||
import com.bg7yoz.ft8cn.GeneralVariables;
|
||||
import com.bg7yoz.ft8cn.R;
|
||||
import com.bg7yoz.ft8cn.ui.ToastMessage;
|
||||
import com.bg7yoz.ft8cn.wave.FT8Resample;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
@ -95,7 +96,7 @@ public class FlexRadio {
|
|||
private boolean allFlexRadioStatusEvent = false;
|
||||
private String clientID = "";
|
||||
private long daxAudioStreamId = 0;
|
||||
private long daxTxAudioStreamId = 0;
|
||||
private int daxTxAudioStreamId = 0;
|
||||
private long panadapterStreamId = 0;
|
||||
private final HashSet<Long> streamIdSet = new HashSet<>();
|
||||
|
||||
|
@ -251,6 +252,12 @@ public class FlexRadio {
|
|||
}
|
||||
onReceiveData(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionClosed() {
|
||||
tcpClient.disconnect();
|
||||
ToastMessage.show(GeneralVariables.getStringFromResource(R.string.tcp_connect_closed));
|
||||
}
|
||||
});
|
||||
clearBufferData();//清除一下缓存的指令数据
|
||||
tcpClient.connect(ip, port);//连接TCP
|
||||
|
@ -483,9 +490,13 @@ public class FlexRadio {
|
|||
}
|
||||
|
||||
//byte[] send = vita.audioDataToVita(packetCount, streamTxId,0x534c2d, voice);
|
||||
//daxTxAudioStreamId=0x0084000001&0x00000000ffffffff;
|
||||
//Log.e(TAG, String.format("run: daxTxAudioStreamId:0x%X",daxTxAudioStreamId) );
|
||||
byte[] send = vita.audioDataToVita(packetCount, daxTxAudioStreamId,0x534c0123, voice);
|
||||
//daxTxAudioStreamId=0x0084000001&0x00000000ffffffff;
|
||||
//Log.e(TAG, String.format("run: daxTxAudioStreamId:0x%X",daxTxAudioStreamId) );
|
||||
//daxTxAudioStreamId,0x534c0123,
|
||||
vita.streamId=daxTxAudioStreamId;
|
||||
vita.classId = 0x534c0123;
|
||||
vita.classId64 = 0x00001c2d534c0123L;
|
||||
byte[] send = vita.audioFloatDataToVita(packetCount, voice);
|
||||
packetCount++;
|
||||
try {
|
||||
//Log.e(TAG, String.format("run: send ip:%s, port:%d",ip,4993) );
|
||||
|
@ -581,7 +592,7 @@ public class FlexRadio {
|
|||
commandStr = String.format("C%d%03d|%s\n", commandSeq, command.ordinal()
|
||||
, cmdContent);
|
||||
tcpClient.sendByte(commandStr.getBytes());
|
||||
Log.e(TAG, "sendCommand: " + commandStr);
|
||||
Log.d(TAG, "sendCommand: " + commandStr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -652,7 +663,7 @@ public class FlexRadio {
|
|||
}
|
||||
if (response.daxTxStreamId != 0) {
|
||||
this.daxTxAudioStreamId = response.daxTxStreamId;
|
||||
Log.e(TAG, String.format("doReceiveLineEvent: txStreamID:0x%x", daxTxAudioStreamId));
|
||||
Log.d(TAG, String.format("doReceiveLineEvent: txStreamID:0x%x", daxTxAudioStreamId));
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -1033,7 +1044,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 int daxTxStreamId = 0;
|
||||
public long panadapterStreamId = 0;
|
||||
public FlexCommand flexCommand = FlexCommand.UNKNOW;
|
||||
public long resultValue = 0;
|
||||
|
@ -1135,12 +1146,12 @@ public class FlexRadio {
|
|||
}
|
||||
}
|
||||
|
||||
private long getStreamId(String line) {
|
||||
private int getStreamId(String line) {
|
||||
String[] lines = line.split("\\|");
|
||||
if (lines.length > 2) {
|
||||
if (lines[1].equals("0")) {
|
||||
try {
|
||||
return Long.parseLong(lines[2], 16);//stream id,16进制
|
||||
return Integer.parseInt(lines[2], 16);//stream id,16进制
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "getDaxStreamId exception: " + e.getMessage());
|
||||
|
|
|
@ -10,18 +10,18 @@ import java.util.Timer;
|
|||
import java.util.TimerTask;
|
||||
|
||||
// VITA 形成的发现消息解析器的枚举定义
|
||||
enum VitaTokens {
|
||||
nullToken ,
|
||||
ipToken,
|
||||
portToken,
|
||||
modelToken,
|
||||
serialToken,
|
||||
callsignToken,
|
||||
nameToken,
|
||||
dpVersionToken,
|
||||
versionToken,
|
||||
statusToken,
|
||||
};
|
||||
//enum VitaTokens {
|
||||
// nullToken ,
|
||||
// ipToken,
|
||||
// portToken,
|
||||
// modelToken,
|
||||
// serialToken,
|
||||
// callsignToken,
|
||||
// nameToken,
|
||||
// dpVersionToken,
|
||||
// versionToken,
|
||||
// statusToken,
|
||||
//};
|
||||
/**
|
||||
* RadioFactory 当前发现的所有收音机。
|
||||
* RadioFactory: 实例化这个类来创建一个 Radio Factory,它将为网络上发现的无线电维护FlexRadio列表flexRadios。
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.io.InputStream;
|
|||
import java.io.OutputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
@ -21,7 +22,7 @@ public class RadioTcpClient {
|
|||
private static RadioTcpClient radioTcpClient = null;
|
||||
private String ip;
|
||||
private int port;
|
||||
public static final int MAX_BUFFER_SIZE=1024 * 64;
|
||||
public static final int MAX_BUFFER_SIZE=1024 * 32;
|
||||
|
||||
private final ExecutorService sendByteThreadPool = Executors.newCachedThreadPool();
|
||||
private final SendByteRunnable sendByteRunnable=new SendByteRunnable(this);
|
||||
|
@ -115,27 +116,40 @@ public class RadioTcpClient {
|
|||
return;
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
|
||||
}catch (SocketException e){
|
||||
Log.e(TAG,"TCP Connection exception:"+e.getMessage());
|
||||
}
|
||||
catch (IOException e) {
|
||||
connectFail();
|
||||
Log.e(TAG, "SocketThread connect io exception = " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
int errorCount=0;
|
||||
//read ...
|
||||
while (isConnect() && !isStop && !isInterrupted()) {
|
||||
int size;
|
||||
try {
|
||||
byte[] buffer = new byte[MAX_BUFFER_SIZE];
|
||||
if (mInputStream == null) return;
|
||||
size = mInputStream.read(buffer);//null data -1 , zrd serial rule size default 10
|
||||
size = mInputStream.read(buffer);//null data -1 ,
|
||||
if (size > 0) {
|
||||
if (onDataReceiveListener != null) {
|
||||
byte[] temp = Arrays.copyOf(buffer, size);
|
||||
onDataReceiveListener.onDataReceive(temp);
|
||||
}
|
||||
errorCount =0;
|
||||
}else {
|
||||
errorCount ++;
|
||||
if (errorCount > 10){
|
||||
if (onDataReceiveListener!=null){
|
||||
onDataReceiveListener.onConnectionClosed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (SocketException e){
|
||||
Log.e(TAG,"Tcp Connection exception:"+e.getMessage());
|
||||
} catch (IOException e) {
|
||||
//uiHandler.sendEmptyMessage(-1);
|
||||
Log.e(TAG, "SocketThread read io exception = " + e.getMessage());
|
||||
|
@ -207,6 +221,7 @@ public class RadioTcpClient {
|
|||
void onConnectFail();
|
||||
|
||||
void onDataReceive(byte[] buffer);
|
||||
void onConnectionClosed();
|
||||
}
|
||||
|
||||
public void setOnDataReceiveListener(
|
||||
|
|
|
@ -20,7 +20,7 @@ import java.util.concurrent.Executors;
|
|||
|
||||
public class RadioUdpClient {
|
||||
private static final String TAG = "RadioUdpSocket";
|
||||
private final int MAX_BUFFER_SIZE = 1024*2;
|
||||
private final int MAX_BUFFER_SIZE = 1024*4;
|
||||
private DatagramSocket sendSocket;
|
||||
private int port;
|
||||
private boolean activated = false;
|
||||
|
@ -76,7 +76,6 @@ public class RadioUdpClient {
|
|||
if (activated) {//通过activated判断是否结束接收线程,并清空sendSocket指针
|
||||
sendSocket = new DatagramSocket(null);//绑定的端口号随机
|
||||
sendSocket.bind(new InetSocketAddress(port));
|
||||
// Log.e(TAG, "openUdpPort: "+sendSocket.getLocalPort());
|
||||
receiveData();
|
||||
}else {
|
||||
if (sendSocket!=null){
|
||||
|
@ -92,31 +91,6 @@ 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;
|
||||
|
@ -128,15 +102,18 @@ public class RadioUdpClient {
|
|||
@Override
|
||||
public void run() {
|
||||
while (client.activated) {
|
||||
|
||||
byte[] data = new byte[client.MAX_BUFFER_SIZE];
|
||||
DatagramPacket packet = new DatagramPacket(data, data.length);
|
||||
try {
|
||||
client.sendSocket.receive(packet);
|
||||
if (client.onUdpEvents != null) {
|
||||
byte[] temp = Arrays.copyOf(packet.getData(), packet.getLength());
|
||||
|
||||
client.onUdpEvents.OnReceiveData(client.sendSocket, packet, temp);
|
||||
|
||||
|
||||
}
|
||||
//Log.d(TAG, "receiveData:host port: " + packet.getPort());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "receiveData: error:" + e.getMessage());
|
||||
|
@ -152,7 +129,7 @@ public class RadioUdpClient {
|
|||
this.onUdpEvents = onUdpEvents;
|
||||
}
|
||||
|
||||
interface OnUdpEvents {
|
||||
public interface OnUdpEvents {
|
||||
void OnReceiveData(DatagramSocket socket, DatagramPacket packet, byte[] data);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package com.bg7yoz.ft8cn.flex;
|
||||
/**
|
||||
* VITA49协议的简单解包和封包操作。
|
||||
* VITA49协议的简单解包和封包操作。此库停止维护,将使用VITA49.java替换此库。
|
||||
*
|
||||
* @author BGY70Z
|
||||
* @date 2023-03-20
|
||||
|
@ -18,48 +18,10 @@ 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";
|
||||
|
@ -89,8 +51,19 @@ public class VITA {
|
|||
public static final int VS_DAX_Audio = 0x03e3;
|
||||
public static final int VS_Discovery = 0xffff;
|
||||
|
||||
//与x6100有关的id信息
|
||||
public static final long XIEGU_Discovery_Class_Id = 0x005849454755FFFFL;
|
||||
public static final int XIEGU_Discovery_Stream_Id = 0x00000800;
|
||||
public static final long XIEGU_AUDIO_CLASS_ID = 0x00584945475500A1L;
|
||||
public static final int XIEGU_PING_Stream_Id= 0x00000600;
|
||||
public static final long XIEGU_PING_CLASS_ID = 0x58494547550006L;
|
||||
public static final long XIEGU_METER_CLASS_ID = 0x58494547550007L;
|
||||
public static final int XIEGU_METER_Stream_Id= 0x00000700;
|
||||
|
||||
|
||||
|
||||
private byte[] buffer;
|
||||
public boolean streamIdPresent;//是否有流字符
|
||||
public VitaPacketType packetType;
|
||||
public boolean classIdPresent;//指示数据包中是否包含类标识符(类ID)字段
|
||||
public boolean trailerPresent;//指示数据包是否包含尾部。
|
||||
|
@ -98,145 +71,226 @@ public class VITA {
|
|||
public VitaTSF tsf;//时间戳小数部分类型
|
||||
public int packetCount;//包计数器,可以对连续的IF data packet进行计数,这些packet具有相同的Stream Identifier 和packet type。
|
||||
public int packetSize;//表示有多少32bit数在IF Data packet 里面
|
||||
|
||||
public int streamId;//流ID,32位
|
||||
//时间戳共有两部分,小数部分和整数部分,整数部分以秒为分辨率,32位,小数部分64位。
|
||||
public long integerTimestamp;//u_int32,long是64位的
|
||||
public int 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 long classId64;
|
||||
|
||||
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数据包的头部
|
||||
* @param packet 数据包
|
||||
* @return 数据包
|
||||
*/
|
||||
public byte[] setVitaPacketHeader(byte[] packet){
|
||||
if (packet.length < 28) return packet;
|
||||
packet[0] = (byte) (packetType.ordinal() << 4);//packetType
|
||||
if (classIdPresent) packet[0] |=0x08;
|
||||
if (trailerPresent) packet[0] |= 0x04;
|
||||
|
||||
|
||||
packet[1] = (byte) (packetCount & 0xf);//packet count
|
||||
packet[1] |= (byte) (tsi.ordinal() << 6);//TSI
|
||||
packet[1] |= (byte) (tsf.ordinal() << 4);//TSF
|
||||
|
||||
packet[2] = (byte) ((packetSize & 0xff00 ) >> 8 & 0xff);//packetSize 1(高8位)
|
||||
packet[3] = (byte) (packetSize & 0xff);//packetSize 2(低8位)
|
||||
|
||||
//-----Stream Identifier--No.2 word----
|
||||
packet[4] = (byte) (((streamId & 0x00ff000000) >> 24) & 0xff);
|
||||
packet[5] = (byte) (((streamId & 0x0000ff0000) >> 16) & 0xff);
|
||||
packet[6] = (byte) (((streamId & 0x000000ff00) >> 8) & 0xff);
|
||||
packet[7] = (byte) (streamId & 0x00000000ff);
|
||||
//----Class ID--No.3 words----
|
||||
|
||||
packet[8] = 0x00;
|
||||
packet[9] = (byte)(((classId64 & 0x00ff000000000000L) >> 48)& 0xff);
|
||||
packet[10] =(byte)(((classId64 & 0x0000ff0000000000L) >> 40)& 0xff);
|
||||
packet[11] =(byte)(((classId64 & 0x000000ff00000000L) >> 32)& 0xff);
|
||||
|
||||
//---Class ID--No.4 word----
|
||||
packet[12] =(byte)(((classId64 & 0x00000000ff000000L) >> 24)& 0xff);
|
||||
packet[13] =(byte)(((classId64 & 0x0000000000ff0000L) >> 16)& 0xff);
|
||||
packet[14] =(byte)(((classId64 & 0x000000000000ff00L) >> 8)& 0xff);
|
||||
packet[15] =(byte)((classId64 & 0x00000000000000ffL ));
|
||||
|
||||
//---Timestamp Int--No.5 word----
|
||||
packet[16] =(byte)(((integerTimestamp & 0xff000000) >> 24)& 0xff);
|
||||
packet[17] =(byte)(((integerTimestamp & 0x00ff0000) >> 16)& 0xff);
|
||||
packet[18] =(byte)(((integerTimestamp & 0x0000ff00) >> 8)& 0xff);
|
||||
packet[19] =(byte)(integerTimestamp & 0x000000ff);
|
||||
|
||||
//---Timestamp franc Int--No.6 No.7 word----
|
||||
packet[20] = (byte)(((fracTimeStamp & 0xff00000000000000L) >> 56)& 0xff);
|
||||
packet[21] = (byte)(((fracTimeStamp & 0x00ff000000000000L) >> 48)& 0xff);
|
||||
packet[22] = (byte)(((fracTimeStamp & 0x0000ff0000000000L) >> 40)& 0xff);
|
||||
packet[23] = (byte)(((fracTimeStamp & 0x000000ff00000000L) >> 32)& 0xff);
|
||||
//---Class ID--No.4 word----
|
||||
|
||||
packet[24] =(byte)(((fracTimeStamp & 0x00000000ff000000L) >> 24)& 0xff);
|
||||
packet[25] =(byte)(((fracTimeStamp & 0x0000000000ff0000L) >> 16)& 0xff);
|
||||
packet[26] =(byte)(((fracTimeStamp & 0x000000000000ff00L) >> 8)& 0xff);
|
||||
packet[27] =(byte)(fracTimeStamp & 0x00000000000000ffL ) ;
|
||||
return packet;
|
||||
}
|
||||
|
||||
public byte[] pingDataToVita(){
|
||||
byte[] result=new byte[28];
|
||||
return setVitaPacketHeader(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成音频流的VITA数据包
|
||||
*
|
||||
* @param count 包计数器
|
||||
* @param payload 音频流数据
|
||||
* @return vita数据包
|
||||
*/
|
||||
public byte[] audioShortDataToVita(int count, short[] payload){
|
||||
packetType = VitaPacketType.EXT_DATA_WITH_STREAM;
|
||||
classIdPresent = true;
|
||||
trailerPresent = false;//没有尾巴
|
||||
tsi = VitaTSI.TSI_OTHER;//
|
||||
tsf = VitaTSF.TSF_SAMPLE_COUNT;
|
||||
this.packetCount = count;
|
||||
this.packetSize = (payload.length / 2) + 7;
|
||||
|
||||
byte[] result=new byte[payload.length*2 + 28];
|
||||
setVitaPacketHeader(result);
|
||||
|
||||
//----HEADER--No.1 word------
|
||||
|
||||
// result[0] = (byte) (packetType.ordinal() << 4);//packetType
|
||||
// if (classIdPresent) result[0] |=0x08;
|
||||
// if (trailerPresent) result[0] |= 0x04;
|
||||
//
|
||||
//
|
||||
// result[1] = (byte) (packetCount & 0xf);//packet count
|
||||
// result[1] |= (byte) (tsi.ordinal() << 6);//TSI
|
||||
// result[1] |= (byte) (tsf.ordinal() << 4);//TSF
|
||||
//
|
||||
// result[2] = (byte) ((packetSize & 0xff00 ) >> 8 & 0xff);//packetSize 1(高8位)
|
||||
// result[3] = (byte) (packetSize & 0xff);//packetSize 2(低8位)
|
||||
//
|
||||
// //-----Stream Identifier--No.2 word----
|
||||
// result[4] = (byte) (((streamId & 0x00ff000000) >> 24) & 0xff);
|
||||
// result[5] = (byte) (((streamId & 0x0000ff0000) >> 16) & 0xff);
|
||||
// result[6] = (byte) (((streamId & 0x000000ff00) >> 8) & 0xff);
|
||||
// result[7] = (byte) (streamId & 0x00000000ff);
|
||||
// //----Class ID--No.3 words----
|
||||
//
|
||||
// result[8] = 0x00;
|
||||
// result[9] = (byte)(((classId64 & 0x00ff000000000000L) >> 48)& 0xff);
|
||||
// result[10] =(byte)(((classId64 & 0x0000ff0000000000L) >> 40)& 0xff);
|
||||
// result[11] =(byte)(((classId64 & 0x000000ff00000000L) >> 32)& 0xff);
|
||||
//
|
||||
// //---Class ID--No.4 word----
|
||||
// result[12] =(byte)(((classId64 & 0x00000000ff000000L) >> 24)& 0xff);
|
||||
// result[13] =(byte)(((classId64 & 0x0000000000ff0000L) >> 16)& 0xff);
|
||||
// result[14] =(byte)(((classId64 & 0x000000000000ff00L) >> 8)& 0xff);
|
||||
// result[15] =(byte)((classId64 & 0x00000000000000ffL ));
|
||||
//
|
||||
// //---Timestamp Int--No.5 word----
|
||||
// result[16] =(byte)(((integerTimestamp & 0xff000000) >> 24)& 0xff);
|
||||
// result[17] =(byte)(((integerTimestamp & 0x00ff0000) >> 16)& 0xff);
|
||||
// result[18] =(byte)(((integerTimestamp & 0x0000ff00) >> 8)& 0xff);
|
||||
// result[19] =(byte)(integerTimestamp & 0x000000ff);
|
||||
//
|
||||
// //---Timestamp franc Int--No.6 No.7 word----
|
||||
// result[20] = (byte)(((fracTimeStamp & 0xff00000000000000L) >> 56)& 0xff);
|
||||
// result[21] = (byte)(((fracTimeStamp & 0x00ff000000000000L) >> 48)& 0xff);
|
||||
// result[22] = (byte)(((fracTimeStamp & 0x0000ff0000000000L) >> 40)& 0xff);
|
||||
// result[23] = (byte)(((fracTimeStamp & 0x000000ff00000000L) >> 32)& 0xff);
|
||||
// //---Class ID--No.4 word----
|
||||
//
|
||||
// result[24] =(byte)(((fracTimeStamp & 0x00000000ff000000L) >> 24)& 0xff);
|
||||
// result[25] =(byte)(((fracTimeStamp & 0x0000000000ff0000L) >> 16)& 0xff);
|
||||
// result[26] =(byte)(((fracTimeStamp & 0x000000000000ff00L) >> 8)& 0xff);
|
||||
// result[27] =(byte)(fracTimeStamp & 0x00000000000000ffL ) ;
|
||||
|
||||
for (int i = 0; i < payload.length ; i++) {
|
||||
result[28+i*2]=(byte)(payload[i]&0xff) ;
|
||||
result[28+i*2+1]= (byte)((payload[i]>>8)&0xff) ;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成音频流的VITA数据包,id应当是电台create stream是赋给的
|
||||
*
|
||||
* @param stream_id streamId
|
||||
* @param data 音频流数据
|
||||
* @return vita数据包
|
||||
*/
|
||||
public byte[] audioDataToVita(int count, long stream_id, int class_id, float[] data) {
|
||||
public byte[] audioFloatDataToVita(int count, 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=?应该是这个全部音频流的总包数
|
||||
packetCount=count % 16;//模16
|
||||
tsf = VitaTSF.TSF_SAMPLE_COUNT;
|
||||
this.packetCount = count;
|
||||
this.packetSize = (data.length / 2) + 7;
|
||||
|
||||
//packetSize是以word(32位,4字节)为单位,
|
||||
//packetSize值为263居多估计以音频,还有其它的长度,263是包含7个word(28字节)的头长度。
|
||||
packetSize = (data.length) + 7;//7个word是VITA的包头
|
||||
//----以上是Header,32位,第一个word-------
|
||||
|
||||
streamId = stream_id;//第二个word,此id是电台赋给的。经常是0x40000xx。
|
||||
|
||||
oui = 0x00001c2d;//第三个word,FlexRadio Systems OUI
|
||||
//classId = 0x534c0123;//第四个word,64位
|
||||
classId = class_id;
|
||||
//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
|
||||
if (classIdPresent) result[0] |=0x08;
|
||||
if (trailerPresent) result[0] |= 0x04;
|
||||
|
||||
result[1] = (byte) 0x0000;
|
||||
result[1] |= (byte) (packetCount & 0xf);//packet count
|
||||
result[1] = (byte) (tsi.ordinal() << 6);//TSI
|
||||
|
||||
result[1] = (byte) (packetCount & 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[2] = (byte) ((packetSize & 0xff00 ) >> 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);
|
||||
result[4] = (byte) (((streamId & 0x00ff000000) >> 24) & 0xff);
|
||||
result[5] = (byte) (((streamId & 0x0000ff0000) >> 16) & 0xff);
|
||||
result[6] = (byte) (((streamId & 0x000000ff00) >> 8) & 0xff);
|
||||
result[7] = (byte) (streamId & 0x00000000ff);
|
||||
//----Class ID--No.3 words----
|
||||
|
||||
//----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] = (byte) ((classId & 0x00ff000000 >> 24) & 0xff);
|
||||
result[13] = (byte) (((classId & 0x00ff0000) >> 16) & 0xff);
|
||||
result[14] = (byte) (((classId & 0x0000ff00) >> 8) & 0xff);
|
||||
result[15] = (byte) (classId & 0x000000ff);
|
||||
result[9] = (byte)(((classId64 & 0x00ff000000000000L) >> 48)& 0xff);
|
||||
result[10] =(byte)(((classId64 & 0x0000ff0000000000L) >> 40)& 0xff);
|
||||
result[11] =(byte)(((classId64 & 0x000000ff00000000L) >> 32)& 0xff);
|
||||
|
||||
// result[12] = 0x53;
|
||||
// result[13] = 0x4c;
|
||||
// result[14] = (byte) 0x01;
|
||||
// result[15] = (byte) 0x23;
|
||||
//---Class ID--No.4 word----
|
||||
result[12] =(byte)(((classId64 & 0x00000000ff000000L) >> 24)& 0xff);
|
||||
result[13] =(byte)(((classId64 & 0x0000000000ff0000L) >> 16)& 0xff);
|
||||
result[14] =(byte)(((classId64 & 0x000000000000ff00L) >> 8)& 0xff);
|
||||
result[15] =(byte)((classId64 & 0x00000000000000ffL ));
|
||||
|
||||
//---Timestamp Int--No.5 word----
|
||||
result[16] =(byte)(((integerTimestamp & 0xff000000) >> 24)& 0xff);
|
||||
result[17] =(byte)(((integerTimestamp & 0x00ff0000) >> 16)& 0xff);
|
||||
result[18] =(byte)(((integerTimestamp & 0x0000ff00) >> 8)& 0xff);
|
||||
result[19] =(byte)(integerTimestamp & 0x000000ff);
|
||||
|
||||
//---Timestamp franc Int--No.6 No.7 word----
|
||||
result[20] = (byte)(((fracTimeStamp & 0xff00000000000000L) >> 56)& 0xff);
|
||||
result[21] = (byte)(((fracTimeStamp & 0x00ff000000000000L) >> 48)& 0xff);
|
||||
result[22] = (byte)(((fracTimeStamp & 0x0000ff0000000000L) >> 40)& 0xff);
|
||||
result[23] = (byte)(((fracTimeStamp & 0x000000ff00000000L) >> 32)& 0xff);
|
||||
//---Class ID--No.4 word----
|
||||
|
||||
result[24] =(byte)(((fracTimeStamp & 0x00000000ff000000L) >> 24)& 0xff);
|
||||
result[25] =(byte)(((fracTimeStamp & 0x0000000000ff0000L) >> 16)& 0xff);
|
||||
result[26] =(byte)(((fracTimeStamp & 0x000000000000ff00L) >> 8)& 0xff);
|
||||
result[27] =(byte)(fracTimeStamp & 0x00000000000000ffL ) ;
|
||||
|
||||
|
||||
//---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];
|
||||
|
@ -245,24 +299,32 @@ public class VITA {
|
|||
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( VitaPacketType packetType
|
||||
, VitaTSI tsi, VitaTSF tsf
|
||||
, int packetCount
|
||||
, int streamId, long classId64) {
|
||||
this.streamIdPresent = true;
|
||||
this.packetType = packetType;
|
||||
this.classIdPresent = true;
|
||||
this.trailerPresent = false;
|
||||
this.tsi = tsi;
|
||||
this.tsf = tsf;
|
||||
this.packetCount = packetCount;
|
||||
this.streamId = streamId;
|
||||
this.classId64 = classId64;
|
||||
this.isAvailable = true;
|
||||
}
|
||||
|
||||
public VITA(byte[] data) {
|
||||
this.buffer = data;
|
||||
//如果包的长度太小,或包为空,就退出计算
|
||||
|
@ -284,7 +346,7 @@ public class VITA {
|
|||
|| packetType == VitaPacketType.EXT_DATA_WITH_STREAM;
|
||||
|
||||
if (streamIdPresent) {//是否有流ID,获取流ID,32位
|
||||
streamId = ((((long) data[offset]) & 0x00ff) << 24) | ((((int) data[offset + 1]) & 0x00ff) << 16)
|
||||
streamId = ((((int) data[offset]) & 0x00ff) << 24) | ((((int) data[offset + 1]) & 0x00ff) << 16)
|
||||
| ((((int) data[offset + 2]) & 0x00ff) << 8) | ((int) data[offset + 3]) & 0x00ff;
|
||||
offset += 4;
|
||||
}
|
||||
|
@ -299,6 +361,15 @@ public class VITA {
|
|||
|
||||
classId = ((((int) data[offset + 4]) & 0x00ff) << 24) | ((((int) data[offset + 5]) & 0x00ff) << 16)
|
||||
| ((((int) data[offset + 6]) & 0x00ff) << 8) | ((int) data[offset + 7]) & 0x00ff;
|
||||
|
||||
classId64 = ((((long) data[offset + 1]) & 0x00ff) << 48)
|
||||
| ((((long) data[offset + 2]) & 0x00ff) << 40)
|
||||
| ((((long) data[offset + 3]) & 0x00ff)<<32)
|
||||
| ((((long) data[offset + 4]) & 0x00ff) << 24)
|
||||
| ((((long) data[offset + 5]) & 0x00ff) << 16)
|
||||
| ((((long) data[offset + 6]) & 0x00ff) << 8)
|
||||
| ((long) data[offset + 7]) & 0x00ff;
|
||||
//Log.d(TAG,String.format("classId64:%X",classId64));
|
||||
offset += 8;
|
||||
}
|
||||
//Log.e(TAG, "VITA: "+String.format("id: 0x%x, classIdPresent:0x%x,packetSize:%d",streamId,classId,packetSize) );
|
||||
|
@ -309,14 +380,14 @@ public class VITA {
|
|||
//小数部分的时间戳共有64位,小数部分可以在没有整数部分的情况下使用,
|
||||
//所有的时间带来都是在以一个采样数据为该reference-point 时间
|
||||
if (tsi != VitaTSI.TSI_NONE) {//32位,
|
||||
integerTimestamp = ((((long) data[offset]) & 0x00ff) << 24) | ((((int) data[offset + 1]) & 0x00ff) << 16)
|
||||
integerTimestamp = ((((int) 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 + 2]) & 0x00ff) << 40) | ((((long) data[offset + 3]) & 0x00ff) << 32)
|
||||
| ((((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;
|
||||
|
@ -353,7 +424,7 @@ public class VITA {
|
|||
*/
|
||||
public String showPayload() {
|
||||
if (payload != null) {
|
||||
return new String(payload).replace(" ", "\n");
|
||||
return new String(payload);//.replace(" ", "\n");
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
|
@ -385,9 +456,9 @@ public class VITA {
|
|||
"类低位(packetClassCode): 0x%X\n" +
|
||||
"公司标识码(oui): 0x%X\n" +
|
||||
"时间戳类型(tsi): %s\n" +
|
||||
"时间戳整数部分(integerTimestamp):%s\n" +
|
||||
"时间戳整数部分(integerTimestamp):0x%X\n" +
|
||||
"时间戳小数部分类型(tsf): %s\n" +
|
||||
"时间戳小数部分值(fracTimeStamp): %d\n" +
|
||||
"时间戳小数部分值(fracTimeStamp): 0x%X\n" +
|
||||
"负载长度(payloadLength): %d\n" +
|
||||
"是否有尾部(trailerPresent): %s\n"
|
||||
|
||||
|
@ -402,7 +473,7 @@ public class VITA {
|
|||
, packetClassCode
|
||||
, oui
|
||||
, tsi.toString()
|
||||
, timestampToDateStr(integerTimestamp * 1000)
|
||||
, integerTimestamp
|
||||
, tsf.toString()
|
||||
, fracTimeStamp
|
||||
, (payload == null ? 0 : payload.length)
|
||||
|
@ -426,15 +497,7 @@ public class VITA {
|
|||
|
||||
}
|
||||
|
||||
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();
|
||||
|
@ -443,4 +506,40 @@ public class VITA {
|
|||
}
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 把字节转换成short,做小端模式转换
|
||||
*
|
||||
* @param data 字节数据
|
||||
* @param start 起始位置
|
||||
* @return short
|
||||
*/
|
||||
public static short readShortDataBigEnd(byte[] data, int start) {
|
||||
if (data.length - start < 2) return 0;
|
||||
return (short) ((short) data[start] & 0xff
|
||||
| ((short) data[start +1] & 0xff) << 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 把字节转换成short,不做小端转换!!
|
||||
*
|
||||
* @param data 字节数据
|
||||
* @param start 起始位置
|
||||
* @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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package com.bg7yoz.ft8cn.flex;
|
||||
|
||||
public 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);
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package com.bg7yoz.ft8cn.flex;
|
||||
|
||||
//时间戳小数部分类型
|
||||
//小数部分主要有三种:
|
||||
// 一种是sample-count ,以采样周期为最小分辨率,
|
||||
// 一种是real-time以ps为最小单位,
|
||||
// 第三种是以任意选择的时间进行累加得出的,
|
||||
// 前面两种时间戳可以直接与整数部分叠加,
|
||||
// 第三种则不能保证与整数部分保持恒定关系,前两种与整数部分叠加来操作的可以在覆盖的时间范围为年
|
||||
// 小数部分的时间戳共有64位,小数部分可以在没有整数部分的情况下使用,
|
||||
// 所有的时间带来都是在以一个采样数据为该参考点(reference-point)的时间。
|
||||
public 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. 自由运行计数时间戳
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package com.bg7yoz.ft8cn.flex;
|
||||
|
||||
//时间戳的类型
|
||||
//时间戳共有两部分,小数部分和整数部分,整数部分以秒为分辨率,32位, 主要传递UTC时间或者 GPS 时间,
|
||||
//小数部分主要有三种,一种是sample-count ,以采样周期为最小分辨率,一种是real-time以ps为最小单位,第三种是以任意选择的时间进行累加得出的,前面两种时间戳可以直接与整数部分叠加,第三种则不能保证与整数部分保持恒定关系,前两种与整数部分叠加来操作的可以在覆盖的时间范围为年
|
||||
//小数部分的时间戳共有64位,小数部分可以在没有整数部分的情况下使用,
|
||||
//所有的时间带来都是在以一个采样数据为该reference-point 时间
|
||||
public enum VitaTSI {
|
||||
TSI_NONE,//No Integer-seconds Timestamp field included
|
||||
TSI_UTC,//Coordinated Universal Time(UTC)
|
||||
TSI_GPS,//GPS time
|
||||
TSI_OTHER//Other
|
||||
}
|
|
@ -141,7 +141,8 @@ public class FT8Package {
|
|||
}
|
||||
|
||||
if (fromCall.endsWith("/P") || fromCall.endsWith("/R")) {
|
||||
fromCall = message.callsignFrom.substring(0, message.callsignFrom.length() - 2);
|
||||
fromCall = fromCall.substring(0, fromCall.length() - 2);
|
||||
// fromCall = message.callsignFrom.substring(0, message.callsignFrom.length() - 2);
|
||||
}
|
||||
|
||||
//当双方都是复合呼号或非标准呼号时(带/的呼号),我的呼号变成标准呼号
|
||||
|
@ -149,6 +150,16 @@ public class FT8Package {
|
|||
fromCall = getStdCall(fromCall);//从复合呼号中提取标准呼号
|
||||
// fromCall = fromCall.substring(0, fromCall.indexOf("/"));
|
||||
}
|
||||
byte r1_p1=pack_r1_p1(message.callsignTo);
|
||||
|
||||
byte r2_p2;
|
||||
//如果双方都有后缀,但不是相同的类型后缀,则取消r1或p1标志,以发送方后缀为准
|
||||
if ((message.callsignFrom.endsWith("/R")&&message.callsignTo.endsWith("/P"))
|
||||
||(message.callsignFrom.endsWith("/P")&&message.callsignTo.endsWith("/R"))){
|
||||
r2_p2=0;
|
||||
}else {
|
||||
r2_p2 = pack_r1_p1(message.getCallsignFrom());
|
||||
}
|
||||
|
||||
|
||||
byte[] data = new byte[12];
|
||||
|
@ -156,7 +167,8 @@ public class FT8Package {
|
|||
data[1] = (byte) ((pack_c28(toCall) & 0x00ffffff) >> 12);
|
||||
data[2] = (byte) ((pack_c28(toCall) & 0x0000ffff) >> 4);
|
||||
data[3] = (byte) ((pack_c28(toCall) & 0x0000000f) << 4);
|
||||
data[3] = (byte) (data[3] | (pack_r1_p1(message.callsignTo) << 3));
|
||||
//data[3] = (byte) (data[3] | (pack_r1_p1(message.callsignTo) << 3));
|
||||
data[3] = (byte) (data[3] | (r1_p1 << 3));
|
||||
data[3] = (byte) (data[3] | (pack_c28(fromCall) & 0x00fffffff) >> 25);
|
||||
|
||||
|
||||
|
@ -166,7 +178,8 @@ public class FT8Package {
|
|||
data[7] = (byte) ((pack_c28(fromCall) & 0x0000000ff) << 7);
|
||||
|
||||
|
||||
data[7] = (byte) (data[7] | (pack_r1_p1(message.getCallsignFrom())) << 6);
|
||||
data[7] = (byte) (data[7] | (r2_p2) << 6);
|
||||
//data[7] = (byte) (data[7] | (pack_r1_p1(message.getCallsignFrom())) << 6);
|
||||
data[7] = (byte) (data[7] | (pack_R1_g15(message.extraInfo) & 0x0ffff) >> 10);
|
||||
data[8] = (byte) ((pack_R1_g15(message.extraInfo) & 0x0003fff) >> 2);
|
||||
data[9] = (byte) ((pack_R1_g15(message.extraInfo) & 0x00000ff) << 6);
|
||||
|
|
|
@ -341,7 +341,7 @@ public class FT8TransmitSignal {
|
|||
try {
|
||||
Thread.sleep(1);
|
||||
long current = System.currentTimeMillis() - now;
|
||||
if (current > 13000) {//实际发射的时长
|
||||
if (current > 13100) {//实际发射的时长
|
||||
isTransmitting = false;
|
||||
break;
|
||||
}
|
||||
|
@ -491,7 +491,8 @@ public class FT8TransmitSignal {
|
|||
if ((GeneralVariables.checkFun3(message.extraInfo)
|
||||
|| GeneralVariables.checkFun2(message.extraInfo))
|
||||
&& (message.callsignFrom.equals(toCallsign.callsign)
|
||||
&& message.callsignTo.equals(GeneralVariables.myCallsign))) {
|
||||
&& GeneralVariables.checkIsMyCallsign(message.callsignTo))) {
|
||||
//&& message.callsignTo.equals(GeneralVariables.myCallsign))) {
|
||||
receiveTargetReport = getReportFromExtraInfo(message.extraInfo);
|
||||
break;
|
||||
}
|
||||
|
@ -502,7 +503,8 @@ public class FT8TransmitSignal {
|
|||
if ((GeneralVariables.checkFun3(message.extraInfo)
|
||||
|| GeneralVariables.checkFun2(message.extraInfo))
|
||||
&& (message.callsignTo.equals(toCallsign.callsign)
|
||||
&& message.callsignFrom.equals(GeneralVariables.myCallsign))) {
|
||||
&& GeneralVariables.checkIsMyCallsign(message.callsignFrom))) {
|
||||
//&& message.callsignFrom.equals(GeneralVariables.myCallsign))) {
|
||||
sentTargetReport = getReportFromExtraInfo(message.extraInfo);
|
||||
break;
|
||||
}
|
||||
|
@ -581,7 +583,8 @@ public class FT8TransmitSignal {
|
|||
if (toCallsign == null) {
|
||||
continue;
|
||||
}
|
||||
if (ft8Message.getCallsignTo().equals(GeneralVariables.myCallsign)
|
||||
//if (ft8Message.getCallsignTo().equals(GeneralVariables.myCallsign)
|
||||
if (GeneralVariables.checkIsMyCallsign(ft8Message.getCallsignTo())
|
||||
&& checkCallsignIsCallTo(ft8Message.getCallsignFrom(), toCallsign.callsign)) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -606,7 +609,8 @@ public class FT8TransmitSignal {
|
|||
continue;
|
||||
}
|
||||
//是双方的呼叫信息
|
||||
if (ft8Message.getCallsignTo().equals(GeneralVariables.myCallsign)
|
||||
//if (ft8Message.getCallsignTo().equals(GeneralVariables.myCallsign)
|
||||
if (GeneralVariables.checkIsMyCallsign(ft8Message.getCallsignTo())
|
||||
&& checkCallsignIsCallTo(ft8Message.getCallsignFrom(), toCallsign.callsign)) {
|
||||
//--TODO ----检查起始时间是不是0,如果是0,补充起始时间。因为有的呼叫会越过第一步
|
||||
|
||||
|
@ -676,7 +680,8 @@ public class FT8TransmitSignal {
|
|||
if (isExcludeMessage(msg)) continue;//检查是不是属于排除的消息:
|
||||
if (toCallsign == null) break;
|
||||
|
||||
if (msg.getCallsignTo().equals(GeneralVariables.myCallsign)
|
||||
//if (msg.getCallsignTo().equals(GeneralVariables.myCallsign)
|
||||
if (GeneralVariables.checkIsMyCallsign(msg.getCallsignTo())
|
||||
&& msg.getCallsignFrom().equals(toCallsign.callsign)//todo 注意测试复合呼号的情况
|
||||
&& !GeneralVariables.checkFun5(msg.extraInfo)) {//cq我、不是73、发送方是我关注的目标
|
||||
//设置发射之前,确定消息的序号,避免从头开始
|
||||
|
@ -692,7 +697,8 @@ public class FT8TransmitSignal {
|
|||
for (int i = messages.size() - 1; i >= 0; i--) {//此处是检查有没有CQ我。(TO:ME,且不能是73)
|
||||
Ft8Message msg = messages.get(i);
|
||||
if (isExcludeMessage(msg)) continue;//检查是不是属于排除的消息:
|
||||
if ((msg.getCallsignTo().equals(GeneralVariables.myCallsign)
|
||||
//if ((msg.getCallsignTo().equals(GeneralVariables.myCallsign)
|
||||
if ((GeneralVariables.checkIsMyCallsign(msg.getCallsignTo())
|
||||
&& !GeneralVariables.checkFun5(msg.extraInfo))) {//cq我、不是73、
|
||||
//设置发射之前,确定消息的序号,避免从头开始
|
||||
setTransmit(new TransmitCallsign(msg.i3, msg.n3, msg.getCallsignFrom(), msg.freq_hz
|
||||
|
@ -728,7 +734,8 @@ public class FT8TransmitSignal {
|
|||
&& ((GeneralVariables.autoCallFollow && GeneralVariables.autoFollowCQ)//自动呼叫CQ
|
||||
|| GeneralVariables.callsignInFollow(msg.getCallsignFrom()))//是我关注的
|
||||
&& !GeneralVariables.checkQSLCallsign(msg.getCallsignFrom())//之前没有联通成功过
|
||||
&& !msg.callsignFrom.equals(GeneralVariables.myCallsign))) {//不是我自己
|
||||
&& !GeneralVariables.checkIsMyCallsign(msg.callsignFrom))) {//不是我自己
|
||||
//&& !msg.callsignFrom.equals(GeneralVariables.myCallsign))) {//不是我自己
|
||||
|
||||
resetTargetReport();
|
||||
setTransmit(new TransmitCallsign(msg.i3, msg.n3, msg.getCallsignFrom(), msg.freq_hz
|
||||
|
|
|
@ -122,15 +122,7 @@ public class GenerateFT8 {
|
|||
return generateFt8(msg,frequency,sample_rate,true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成FT8信号
|
||||
* @param msg 消息
|
||||
* @param frequency 频率
|
||||
* @param sample_rate 采样率
|
||||
* @param hasModifier 是否有修饰符
|
||||
* @return
|
||||
*/
|
||||
public static float[] generateFt8(Ft8Message msg, float frequency,int sample_rate,boolean hasModifier) {
|
||||
public static byte[] generateA91(Ft8Message msg,boolean hasModifier){
|
||||
if (msg.callsignFrom.length()<3){
|
||||
ToastMessage.show(GeneralVariables.getStringFromResource(R.string.callsign_error));
|
||||
return null;
|
||||
|
@ -146,6 +138,7 @@ public class GenerateFT8 {
|
|||
msg.modifier="";
|
||||
}
|
||||
|
||||
|
||||
//判定用非标准呼号i3=4的条件:
|
||||
//1.FROMCALL为非标准呼号 ,且 符合2或3
|
||||
//2.扩展消息时 网格、RR73,RRR,73
|
||||
|
@ -157,7 +150,9 @@ public class GenerateFT8 {
|
|||
if (!checkIsStandardCallsign(msg.callsignFrom)
|
||||
&& (!checkIsReport(msg.extraInfo) || msg.checkIsCQ())) {
|
||||
msg.i3 = 4;
|
||||
} else if (msg.callsignFrom.endsWith("/P")||(msg.callsignTo.endsWith("/P"))) {
|
||||
//} else if (msg.callsignFrom.endsWith("/P")||(msg.callsignTo.endsWith("/P"))) {
|
||||
} else if (msg.callsignFrom.endsWith("/P")//如果目标有/P后缀,则以目标呼号为准。如果目标没有/P后缀,则以发送方是否有/P后缀为准
|
||||
||(msg.callsignTo.endsWith("/P")&&(!msg.callsignFrom.endsWith("/P")))) {
|
||||
msg.i3 = 2;
|
||||
} else {
|
||||
msg.i3 = 1;
|
||||
|
@ -172,39 +167,62 @@ public class GenerateFT8 {
|
|||
packFreeTextTo77(msg.getMessageText(), packed);
|
||||
}
|
||||
|
||||
return generateFt8ByA91(packed,frequency,sample_rate);
|
||||
return packed;
|
||||
}
|
||||
|
||||
/*
|
||||
// 其次,将二进制消息编码为FSK音调序列,79个字节
|
||||
byte[] tones = new byte[num_tones]; // 79音调(符号)数组
|
||||
//此处是88个字节(91+7)/8,可以使用a91生成音频
|
||||
ft8_encode(packed, tones);
|
||||
/**
|
||||
* 生成FT8信号
|
||||
* @param msg 消息
|
||||
* @param frequency 频率
|
||||
* @param sample_rate 采样率
|
||||
* @param hasModifier 是否有修饰符
|
||||
* @return
|
||||
*/
|
||||
public static float[] generateFt8(Ft8Message msg, float frequency,int sample_rate,boolean hasModifier) {
|
||||
// if (msg.callsignFrom.length()<3){
|
||||
// ToastMessage.show(GeneralVariables.getStringFromResource(R.string.callsign_error));
|
||||
// return null;
|
||||
// }
|
||||
// // 首先,将文本数据打包为二进制消息,共12个字节
|
||||
// byte[] packed = new byte[FTX_LDPC_K_BYTES];
|
||||
// //把"<>"去掉
|
||||
// msg.callsignTo = msg.callsignTo.replace("<", "").replace(">", "");
|
||||
// msg.callsignFrom = msg.callsignFrom.replace("<", "").replace(">", "");
|
||||
// if (hasModifier) {
|
||||
// msg.modifier = GeneralVariables.toModifier;//修饰符
|
||||
// }else {
|
||||
// msg.modifier="";
|
||||
// }
|
||||
|
||||
// 第三,将FSK音调转换为音频信号b
|
||||
//判定用非标准呼号i3=4的条件:
|
||||
//1.FROMCALL为非标准呼号 ,且 符合2或3
|
||||
//2.扩展消息时 网格、RR73,RRR,73
|
||||
//3.CQ,QRZ,DE
|
||||
|
||||
|
||||
int num_samples = (int) (0.5f + num_tones * symbol_period * sample_rate); // 数据信号中的采样数0.5+79*0.16*12000
|
||||
|
||||
//float[] signal = new float[Ft8num_samples];
|
||||
float[] signal = new float[num_samples];
|
||||
// if (msg.i3 != 0) {//目前只支持i3=1,i3=2,i3=4,i3=0 && n3=0
|
||||
// if (!checkIsStandardCallsign(msg.callsignFrom)
|
||||
// && (!checkIsReport(msg.extraInfo) || msg.checkIsCQ())) {
|
||||
// msg.i3 = 4;
|
||||
// } else if (msg.callsignFrom.endsWith("/P")||(msg.callsignTo.endsWith("/P"))) {
|
||||
// msg.i3 = 2;
|
||||
// } else {
|
||||
// msg.i3 = 1;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (msg.i3 == 1 || msg.i3 == 2) {
|
||||
// packed = FT8Package.generatePack77_i1(msg);
|
||||
// } else if (msg.i3 == 4) {//说明是非标准呼号
|
||||
// packed = FT8Package.generatePack77_i4(msg);
|
||||
// } else {
|
||||
// packFreeTextTo77(msg.getMessageText(), packed);
|
||||
// }
|
||||
|
||||
//Ft8num_sampleFT8声音的总采样数,不是字节数。15*12000
|
||||
//for (int i = 0; i < Ft8num_samples; i++)//把数据全部静音。
|
||||
for (int i = 0; i < num_samples; i++)//把数据全部静音。
|
||||
{
|
||||
signal[i] = 0;
|
||||
}
|
||||
return generateFt8ByA91(generateA91(msg,hasModifier),frequency,sample_rate);
|
||||
//return generateFt8ByA91(packed,frequency,sample_rate);
|
||||
|
||||
// 用79个字节符号,生成FT8音频
|
||||
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;
|
||||
*/
|
||||
}
|
||||
|
||||
public static float[] generateFt8ByA91(byte[] a91, float frequency,int sample_rate){
|
||||
|
@ -218,11 +236,6 @@ 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)/2(1.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];
|
||||
|
||||
//Ft8num_sampleFT8声音的总采样数,不是字节数。15*12000
|
||||
|
|
|
@ -91,7 +91,8 @@ public class GridMarkerInfoWindow extends InfoWindow {
|
|||
|
||||
|
||||
ImageButton imageButton=(ImageButton) this.mView.findViewById(R.id.callThisImageButton);
|
||||
if (GeneralVariables.myCallsign.equals(msg.getCallsignFrom())){
|
||||
//if (GeneralVariables.myCallsign.equals(msg.getCallsignFrom())){
|
||||
if (GeneralVariables.checkIsMyCallsign(msg.getCallsignFrom())){
|
||||
imageButton.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
|
|
|
@ -222,6 +222,7 @@ public class GridTrackerMainActivity extends AppCompatActivity {
|
|||
getString(R.string.decoding_takes_milliseconds)
|
||||
, mainViewModel.ft8SignalListener.decodeTimeSec.getValue())));
|
||||
|
||||
|
||||
//画电台之间的连线
|
||||
//对CQ的电台打点
|
||||
for (Ft8Message msg : tempMsg) {
|
||||
|
@ -765,7 +766,8 @@ public class GridTrackerMainActivity extends AppCompatActivity {
|
|||
if (message != null) {
|
||||
//呼叫的目标不能是自己
|
||||
if (!message.getCallsignFrom().equals("<...>")
|
||||
&& !message.getCallsignFrom().equals(GeneralVariables.myCallsign)
|
||||
//&& !message.getCallsignFrom().equals(GeneralVariables.myCallsign)
|
||||
&& !GeneralVariables.checkIsMyCallsign(message.getCallsignFrom())
|
||||
&& !(message.i3 == 0 && message.n3 == 0)) {
|
||||
doCallNow(message);
|
||||
}
|
||||
|
|
|
@ -1297,6 +1297,7 @@ public class LogHttpServer extends NanoHTTPD {
|
|||
, GeneralVariables.getStringFromResource(R.string.html_qsl_freq)//"freq"
|
||||
, GeneralVariables.getStringFromResource(R.string.html_callsign)//"station_callsign"
|
||||
, GeneralVariables.getStringFromResource(R.string.html_qsl_grid)//"my_gridsquare"
|
||||
, "Operator"//"my_gridsquare"
|
||||
, GeneralVariables.getStringFromResource(R.string.html_comment))//"comment")
|
||||
.append("\n");
|
||||
HtmlContext.tableRowEnd(result).append("\n");
|
||||
|
@ -1317,6 +1318,7 @@ public class LogHttpServer extends NanoHTTPD {
|
|||
String freq = cursor.getString(cursor.getColumnIndex("freq"));
|
||||
String station_callsign = cursor.getString(cursor.getColumnIndex("station_callsign"));
|
||||
String my_gridsquare = cursor.getString(cursor.getColumnIndex("my_gridsquare"));
|
||||
String operator = cursor.getString(cursor.getColumnIndex("operator"));
|
||||
String comment = cursor.getString(cursor.getColumnIndex("comment"));
|
||||
|
||||
|
||||
|
@ -1336,9 +1338,9 @@ public class LogHttpServer extends NanoHTTPD {
|
|||
.replace(">", "")
|
||||
, station_callsign.replace("<", "<")
|
||||
.replace(">", ">")));
|
||||
HtmlContext.tableCell(result, my_gridsquare == null ? "" : my_gridsquare
|
||||
, comment).append("\n");
|
||||
|
||||
HtmlContext.tableCell(result, my_gridsquare == null ? "" : my_gridsquare);
|
||||
HtmlContext.tableCell(result, operator == null ? "" : operator, comment).append("\n");
|
||||
HtmlContext.tableRowEnd(result).append("\n");
|
||||
order++;
|
||||
}
|
||||
|
@ -1888,6 +1890,7 @@ public class LogHttpServer extends NanoHTTPD {
|
|||
} else {
|
||||
logStr.append("<swl:1>Y ");
|
||||
}
|
||||
|
||||
if (cursor.getString(cursor.getColumnIndex("gridsquare")) != null) {
|
||||
logStr.append(String.format("<gridsquare:%d>%s "
|
||||
, cursor.getString(cursor.getColumnIndex("gridsquare")).length()
|
||||
|
@ -1960,6 +1963,14 @@ public class LogHttpServer extends NanoHTTPD {
|
|||
, cursor.getString(cursor.getColumnIndex("my_gridsquare"))));
|
||||
}
|
||||
|
||||
if (cursor.getColumnIndex("operator") != -1) {
|
||||
if (cursor.getString(cursor.getColumnIndex("operator")) != null) {
|
||||
logStr.append(String.format("<operator:%d>%s "
|
||||
, cursor.getString(cursor.getColumnIndex("operator")).length()
|
||||
, cursor.getString(cursor.getColumnIndex("operator"))));
|
||||
}
|
||||
}
|
||||
|
||||
String comment = cursor.getString(cursor.getColumnIndex("comment"));
|
||||
|
||||
//<comment:15>Distance: 99 km <eor>
|
||||
|
|
|
@ -79,7 +79,8 @@ public class SWLQsoList {
|
|||
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())
|
||||
//if (msg.callsignFrom.equals(record.getMyCallsign())
|
||||
if (GeneralVariables.checkIsMyCallsign(msg.callsignFrom)
|
||||
&& msg.callsignTo.equals(record.getToCallsign())
|
||||
&& !foundFromReport) {//callsignFrom发出的信号报告
|
||||
int report = GeneralVariables.checkFun2_3(msg.extraInfo);
|
||||
|
@ -92,7 +93,8 @@ public class SWLQsoList {
|
|||
}
|
||||
|
||||
if (msg.callsignFrom.equals(record.getToCallsign())
|
||||
&& msg.callsignTo.equals(record.getMyCallsign())
|
||||
//&& msg.callsignTo.equals(record.getMyCallsign())
|
||||
&& GeneralVariables.checkIsMyCallsign(msg.callsignTo)
|
||||
&& !foundToReport) {//callsignTo发出的信号报告
|
||||
int report = GeneralVariables.checkFun2_3(msg.extraInfo);
|
||||
if (time_on > msg.utcTime) time_on = msg.utcTime;//取最早的时间
|
||||
|
@ -123,7 +125,8 @@ public class SWLQsoList {
|
|||
for (int i = allMessages.size() - 1; i >= 0; i--) {
|
||||
Ft8Message msg = allMessages.get(i);
|
||||
if (!foundFromGrid
|
||||
&& msg.callsignFrom.equals(record.getMyCallsign())
|
||||
//&& msg.callsignFrom.equals(record.getMyCallsign())
|
||||
&& GeneralVariables.checkIsMyCallsign(msg.callsignFrom)
|
||||
&& (msg.callsignTo.equals(record.getToCallsign()) || msg.checkIsCQ())) {//callsignFrom的网格报告
|
||||
|
||||
if (GeneralVariables.checkFun1_6(msg.extraInfo)) {
|
||||
|
@ -135,7 +138,9 @@ public class SWLQsoList {
|
|||
|
||||
if (!foundToGrid
|
||||
&& msg.callsignFrom.equals(record.getToCallsign())
|
||||
&& (msg.callsignTo.equals(record.getMyCallsign())|| msg.checkIsCQ())) {//callsignTo发出的信号报告
|
||||
//&& (msg.callsignTo.equals(record.getMyCallsign())
|
||||
&& (GeneralVariables.checkIsMyCallsign(msg.callsignTo)
|
||||
|| msg.checkIsCQ())) {//callsignTo发出的信号报告
|
||||
if (GeneralVariables.checkFun1_6(msg.extraInfo)) {
|
||||
record.setToMaidenGrid(msg.extraInfo.trim());
|
||||
foundToGrid = true;
|
||||
|
|
|
@ -139,6 +139,7 @@ public class Flex6000Rig extends BaseRig {
|
|||
}
|
||||
}
|
||||
},START_QUERY_FREQ_DELAY-500);
|
||||
|
||||
readFreqTimer.schedule(readTask(), START_QUERY_FREQ_DELAY,QUERY_FREQ_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -114,7 +114,7 @@ public class IcomCommand {
|
|||
}
|
||||
if (buffer[i] == (byte) 0xfe
|
||||
&& buffer[i + 1] == (byte) 0xfe//命令头0xfe 0xfe
|
||||
&& (buffer[i + 2] == (byte) ctrAddr || buffer[i + 2] == (byte) 0x00)//控制者地址默认E0或00
|
||||
&& (buffer[i + 2] == (byte) ctrAddr || (buffer[i + 2] == (byte) 0x00))//控制者地址默认E0或00
|
||||
&& buffer[i + 3] == (byte) rigAddr) {//电台地址,705的默认值是A4,协谷是70
|
||||
position = i;
|
||||
break;
|
||||
|
|
|
@ -29,6 +29,8 @@ public class IcomRig extends BaseRig {
|
|||
private boolean alcMaxAlert = false;
|
||||
private boolean swrAlert = false;
|
||||
private Timer meterTimer;//查询meter的Timer
|
||||
|
||||
private boolean oldVersion=false;//针对老电台,可能不支持SWR查询
|
||||
//private boolean isPttOn = false;
|
||||
|
||||
@Override
|
||||
|
@ -166,9 +168,11 @@ public class IcomRig extends BaseRig {
|
|||
|
||||
//目前只对频率和模式消息作反应
|
||||
switch (icomCommand.getCommandID()) {
|
||||
|
||||
case IcomRigConstant.CMD_SEND_FREQUENCY_DATA://获取到的是频率数据
|
||||
case IcomRigConstant.CMD_READ_OPERATING_FREQUENCY:
|
||||
//获取频率
|
||||
//ToastMessage.show(byteToStr(icomCommand.getData(false)));
|
||||
setFreq(icomCommand.getFrequency(false));
|
||||
break;
|
||||
case IcomRigConstant.CMD_SEND_MODE_DATA://获取到的是模式数据
|
||||
|
@ -211,9 +215,7 @@ public class IcomRig extends BaseRig {
|
|||
|
||||
@Override
|
||||
public void onReceiveData(byte[] data) {
|
||||
|
||||
//ToastMessage.show("--"+byteToStr(data));
|
||||
|
||||
//ToastMessage.show(byteToStr(data));
|
||||
|
||||
int commandEnd = getCommandEnd(data);
|
||||
if (commandEnd <= -1) {//这是没有指令结尾
|
||||
|
@ -259,7 +261,7 @@ public class IcomRig extends BaseRig {
|
|||
meterTimer.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (isPttOn()) {//当Ptt被按下去的时候测量
|
||||
if (isPttOn() && !oldVersion) {//当Ptt被按下去的时候测量,并且不是老版本的电台
|
||||
sendCivData(IcomRigConstant.getSWRState(ctrAddress, getCivAddress()));
|
||||
sendCivData(IcomRigConstant.getALCState(ctrAddress, getCivAddress()));
|
||||
}
|
||||
|
@ -272,8 +274,10 @@ public class IcomRig extends BaseRig {
|
|||
return BaseRigOperation.getFrequencyStr(getFreq());
|
||||
}
|
||||
|
||||
public IcomRig(int civAddress) {
|
||||
|
||||
public IcomRig(int civAddress,boolean newRig) {
|
||||
Log.d(TAG, "IcomRig: Create.");
|
||||
this.oldVersion = !newRig;
|
||||
setCivAddress(civAddress);
|
||||
startMeterTimer();
|
||||
}
|
||||
|
|
|
@ -23,6 +23,10 @@ public class InstructionSet {
|
|||
public static final int KENWOOD_TS570=18;//KENWOOD TS-570D
|
||||
public static final int YAESU_3_9_U_DIG=19;//KENWOOD TS-570D
|
||||
|
||||
public static final int XIEGU_6100_FT8CNS=20;//6100的ft8cns版
|
||||
public static final int YAESU_847=21;//Ft-847
|
||||
public static final int ICOM_756=22;//Ft-847
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -248,7 +248,8 @@ public class TrUSDXRig extends BaseRig {
|
|||
rxStreamBuffer.write(data, 0, data.length);
|
||||
if (rxStreamBuffer.size() >= 256 || force) {//8位转16位,7812Hz转12000Hz
|
||||
//byte[] resampled = rxResample.processCopy(toWaveSamples8To16(rxStreamBuffer.toByteArray()));
|
||||
float[] resampled = FT8Resample.get32Resample16(toWaveSamples8To16Int(rxStreamBuffer.toByteArray()), rxSampling, 12000);
|
||||
float[] resampled = FT8Resample.get32Resample16(
|
||||
toWaveSamples8To16Int(rxStreamBuffer.toByteArray()), rxSampling, 12000,1);
|
||||
rxStreamBuffer.reset();
|
||||
getConnector().receiveWaveData(resampled);
|
||||
}
|
||||
|
@ -280,7 +281,7 @@ public class TrUSDXRig extends BaseRig {
|
|||
// txResample.close();
|
||||
// byte[] pcm8 = toWaveSamples16To8(resampled);
|
||||
|
||||
byte[] pcm8 = FT8Resample.get8Resample32(wave, 24000, txSampling);
|
||||
byte[] pcm8 = FT8Resample.get8Resample32(wave, 24000, txSampling,1);
|
||||
|
||||
|
||||
for (int i = 0; i < pcm8.length; i++) {
|
||||
|
|
|
@ -216,6 +216,7 @@ public class XieGu6100Command {
|
|||
*/
|
||||
public long getFrequency(boolean hasSubCommand) {
|
||||
byte[] data = getData(hasSubCommand);
|
||||
if (data == null) return -1;
|
||||
if (data.length < 5) {
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
package com.bg7yoz.ft8cn.rigs;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.bg7yoz.ft8cn.Ft8Message;
|
||||
import com.bg7yoz.ft8cn.GeneralVariables;
|
||||
import com.bg7yoz.ft8cn.connector.X6100Connector;
|
||||
import com.bg7yoz.ft8cn.ft8transmit.GenerateFT8;
|
||||
|
||||
/**
|
||||
* XieGu6100的ft8cns模式,只支持网络模式,所以在设置baseRig时要做好判断
|
||||
*/
|
||||
public class XieGu6100NetRig extends BaseRig {
|
||||
private static final String TAG = "x6100RigNet";
|
||||
|
||||
//private final int ctrAddress = 0xE0;//接收地址,默认0xE0;电台回复命令有时也可以是0x00
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void setPTT(boolean on) {
|
||||
super.setPTT(on);
|
||||
if (getConnector() != null) {
|
||||
getConnector().setPttOn(on);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
if (getConnector() == null) {
|
||||
return false;
|
||||
}
|
||||
return getConnector().isConnected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUsbModeToRig() {
|
||||
if (getConnector() != null) {
|
||||
X6100Connector x6100Connector =(X6100Connector)getConnector();
|
||||
x6100Connector.getXieguRadio().commandSetMode("u-dig",1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFreqToRig() {
|
||||
if (getConnector() != null) {
|
||||
X6100Connector x6100Connector =(X6100Connector)getConnector();
|
||||
x6100Connector.getXieguRadio().commandTuneFreq(getFreq());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onReceiveData(byte[] data) {
|
||||
//命令解析都在X6100Radio中完成了,此处不需要动作了
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendWaveData(Ft8Message message) {//发送音频数据到电台,用于网络方式
|
||||
if (getConnector() != null) {//把生成的具体音频数据传递到Connector,
|
||||
//判断如果是ft8cns,就传输a19数据包
|
||||
if (GeneralVariables.instructionSet == InstructionSet.XIEGU_6100_FT8CNS){
|
||||
//Log.e(TAG,"generate A91");
|
||||
getConnector().sendFt8A91(GenerateFT8.generateA91(message,true)
|
||||
,GeneralVariables.getBaseFrequency());
|
||||
}else {//否则正常传输音频数据
|
||||
float[] data = GenerateFT8.generateFt8(message, GeneralVariables.getBaseFrequency()
|
||||
, 12000);//此处电台发射音频的采样率是12000
|
||||
getConnector().sendWaveData(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFreqFromRig() {//通过X6100Radio的状态来获取频率,此处获取频率指令不需要了
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "XIEGU X6100 series";
|
||||
}
|
||||
|
||||
public XieGu6100NetRig(int civAddress) {
|
||||
Log.d(TAG, "x6100RigNet: Create.");
|
||||
setCivAddress(civAddress);
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ import com.bg7yoz.ft8cn.Ft8Message;
|
|||
import com.bg7yoz.ft8cn.GeneralVariables;
|
||||
import com.bg7yoz.ft8cn.R;
|
||||
import com.bg7yoz.ft8cn.connector.ConnectMode;
|
||||
import com.bg7yoz.ft8cn.connector.X6100Connector;
|
||||
import com.bg7yoz.ft8cn.database.ControlMode;
|
||||
import com.bg7yoz.ft8cn.ft8transmit.GenerateFT8;
|
||||
import com.bg7yoz.ft8cn.ui.ToastMessage;
|
||||
|
@ -221,13 +222,20 @@ public class XieGu6100Rig extends BaseRig {
|
|||
@Override
|
||||
public void sendWaveData(Ft8Message message) {//发送音频数据到电台,用于网络方式
|
||||
if (getConnector() != null) {//把生成的具体音频数据传递到Connector,
|
||||
float[] data = GenerateFT8.generateFt8(message, GeneralVariables.getBaseFrequency()
|
||||
,12000);//此处icom电台发射音频的采样率是12000
|
||||
if (data==null){
|
||||
setPTT(false);
|
||||
return;
|
||||
//判断如果是ft8cns,就传输a19数据包
|
||||
if (GeneralVariables.instructionSet == InstructionSet.XIEGU_6100_FT8CNS){
|
||||
//Log.e(TAG,"generate A91");
|
||||
getConnector().sendFt8A91(GenerateFT8.generateA91(message,true)
|
||||
,GeneralVariables.getBaseFrequency());
|
||||
}else {//否则正常传输音频数据
|
||||
float[] data = GenerateFT8.generateFt8(message, GeneralVariables.getBaseFrequency()
|
||||
, 12000);//此处icom电台发射音频的采样率是12000
|
||||
if (data == null) {
|
||||
setPTT(false);
|
||||
return;
|
||||
}
|
||||
getConnector().sendWaveData(data);
|
||||
}
|
||||
getConnector().sendWaveData(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@ public class Yaesu2RigConstant {
|
|||
private static final byte[] PTT_ON = {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x08};
|
||||
private static final byte[] PTT_OFF = {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x88};
|
||||
private static final byte[] GET_METER = {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xBD};
|
||||
private static final byte[] GET_CONNECT = {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00};
|
||||
private static final byte[] GET_DISCONNECT = {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80};
|
||||
//USB模式
|
||||
private static final byte[] USB_MODE = {(byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x07};
|
||||
//DIG模式
|
||||
|
@ -61,11 +63,19 @@ public class Yaesu2RigConstant {
|
|||
public static byte[] setOperationUSBMode() {
|
||||
return DIG_MODE;
|
||||
}
|
||||
|
||||
public static byte[] setOperationUSB847Mode() {
|
||||
return USB_MODE;
|
||||
}
|
||||
public static byte[] readMeter() {
|
||||
return GET_METER;
|
||||
}
|
||||
|
||||
public static byte[] sendConnectData() {
|
||||
return GET_CONNECT;
|
||||
}
|
||||
public static byte[] sendDisconnectData() {
|
||||
return GET_DISCONNECT;
|
||||
}
|
||||
public static byte[] setOperationFreq(long freq) {
|
||||
byte[] data = new byte[]{
|
||||
(byte) (((byte) (freq % 1000000000 / 100000000) << 4) + (byte) (freq % 100000000 / 10000000))
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
package com.bg7yoz.ft8cn.rigs;
|
||||
|
||||
import static com.bg7yoz.ft8cn.GeneralVariables.QUERY_FREQ_TIMEOUT;
|
||||
import static com.bg7yoz.ft8cn.GeneralVariables.START_QUERY_FREQ_DELAY;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.bg7yoz.ft8cn.GeneralVariables;
|
||||
import com.bg7yoz.ft8cn.R;
|
||||
import com.bg7yoz.ft8cn.database.ControlMode;
|
||||
import com.bg7yoz.ft8cn.ui.ToastMessage;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
/**
|
||||
* YAESU的部分电台,回送的数据不是连续的,所以,要做一个缓冲区,接受5字节长度。满了就复位。或发送指令时,就复位。
|
||||
* ft848在连接成功后,必须发送5个0,结束后发送4个0加80
|
||||
*/
|
||||
public class Yaesu2_847Rig extends BaseRig{
|
||||
private static final String TAG="Yaesu2_847Rig";
|
||||
private Timer readFreqTimer = new Timer();
|
||||
|
||||
private int swr = 0;
|
||||
private int alc = 0;
|
||||
private boolean alcMaxAlert = false;
|
||||
private boolean swrAlert = false;
|
||||
private boolean sentConnect =false;
|
||||
|
||||
private TimerTask readTask(){
|
||||
return new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (!isConnected()){
|
||||
readFreqTimer.cancel();
|
||||
readFreqTimer.purge();
|
||||
readFreqTimer=null;
|
||||
return;
|
||||
}
|
||||
if (!sentConnect) {//发送连接头数据,5个0,只发送1次
|
||||
sendConnectData();
|
||||
sentConnect =!sentConnect;
|
||||
}
|
||||
|
||||
if (isPttOn()) {
|
||||
readMeters();
|
||||
} else {
|
||||
readFreqFromRig();
|
||||
}
|
||||
}catch (Exception e)
|
||||
{
|
||||
Log.e(TAG, "readFreq error:"+e.getMessage() );
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setPTT(boolean on) {
|
||||
super.setPTT(on);
|
||||
|
||||
if (getConnector()!=null){
|
||||
switch (getControlMode()){
|
||||
case ControlMode.CAT://以CIV指令
|
||||
getConnector().setPttOn(Yaesu2RigConstant.setPTTState(on));
|
||||
break;
|
||||
case ControlMode.RTS:
|
||||
case ControlMode.DTR:
|
||||
getConnector().setPttOn(on);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
if (getConnector()==null) {
|
||||
return false;
|
||||
}
|
||||
return getConnector().isConnected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUsbModeToRig() {
|
||||
if (getConnector()!=null){
|
||||
getConnector().sendData(Yaesu2RigConstant.setOperationUSB847Mode());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFreqToRig() {
|
||||
if (getConnector()!=null){
|
||||
getConnector().sendData(Yaesu2RigConstant.setOperationFreq(getFreq()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceiveData(byte[] data) {
|
||||
//YAESU 817的指令,返回频率是5字节的,METER是2字节的。
|
||||
//Meter是2字节的,第一字节高位功率,0-A,低位ALC 0-9,第二字节高位驻波比,0-C,0为高驻波,低位音频输入0-8
|
||||
if (data.length == 5) {//频率
|
||||
long freq = Yaesu2Command.getFrequency(data);
|
||||
if (freq > -1) {
|
||||
setFreq(freq);
|
||||
}
|
||||
} else if (data.length == 2) {//METERS
|
||||
alc = (data[0] & 0x0f);
|
||||
swr = (data[1] & 0x0f0) >> 4;
|
||||
showAlert();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取Meter RM;
|
||||
*/
|
||||
private void readMeters() {
|
||||
if (getConnector() != null) {
|
||||
getConnector().sendData(Yaesu2RigConstant.readMeter());
|
||||
}
|
||||
}
|
||||
|
||||
private void sendConnectData() {//连接电台后,要发送5个0
|
||||
if (getConnector() != null) {
|
||||
getConnector().sendData(Yaesu2RigConstant.sendConnectData());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnecting() {//断开电台前,要发送4个0加80
|
||||
if (getConnector() != null) {
|
||||
getConnector().sendData(Yaesu2RigConstant.sendDisconnectData());
|
||||
}
|
||||
super.onDisconnecting();
|
||||
}
|
||||
|
||||
private void showAlert() {
|
||||
if (swr > Yaesu2RigConstant.swr_817_alert_min) {
|
||||
if (!swrAlert) {
|
||||
swrAlert = true;
|
||||
ToastMessage.show(GeneralVariables.getStringFromResource(R.string.swr_high_alert));
|
||||
}
|
||||
} else {
|
||||
swrAlert = false;
|
||||
}
|
||||
if (alc >= Yaesu2RigConstant.alc_817_alert_max) {//网络模式下不警告ALC
|
||||
if (!alcMaxAlert) {
|
||||
alcMaxAlert = true;
|
||||
ToastMessage.show(GeneralVariables.getStringFromResource(R.string.alc_high_alert));
|
||||
}
|
||||
} else {
|
||||
alcMaxAlert = false;
|
||||
}
|
||||
|
||||
}
|
||||
@Override
|
||||
public void readFreqFromRig(){
|
||||
if (getConnector()!=null){
|
||||
//clearBuffer();//清除一下缓冲区
|
||||
getConnector().sendData(Yaesu2RigConstant.setReadOperationFreq());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "YAESU 847 series";
|
||||
}
|
||||
|
||||
public Yaesu2_847Rig() {
|
||||
readFreqTimer.schedule(readTask(),START_QUERY_FREQ_DELAY,QUERY_FREQ_TIMEOUT);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,132 +1,132 @@
|
|||
package com.bg7yoz.ft8cn.rigs;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
|
||||
public class Yaesu3RigConstant {
|
||||
private static final String TAG = "Yaesu3RigConstant";
|
||||
//LSB:0,USB:1,AM:2,CW:3,RTTY:4,FM:5,WFM:6,CW_R:7,RTTY_R:8,DV:17
|
||||
public static final int LSB = 0x01;
|
||||
public static final int USB = 0x02;
|
||||
public static final int CW = 0x03;
|
||||
public static final int FM = 0x04;
|
||||
public static final int AM = 0x05;
|
||||
public static final int RTTY = 0x06;
|
||||
public static final int CW_R = 0x07;
|
||||
public static final int DATA = 0x08;
|
||||
public static final int RTTY_R = 0x09;
|
||||
public static final int NONE = 0x0A;
|
||||
public static final int FM_N = 0x0B;
|
||||
public static final int DATA_R = 0x0C;
|
||||
public static final int AM_N = 0x0D;
|
||||
|
||||
|
||||
public static final int swr_39_alert_max=125;//相当于3.0
|
||||
public static final int alc_39_alert_max=125;//超过,在表上显示红色
|
||||
//PTT状态
|
||||
|
||||
//指令集
|
||||
private static final String PTT_ON = "MX1;";
|
||||
private static final String PTT_OFF = "MX0;";
|
||||
private static final String USB_MODE = "MD02;";
|
||||
private static final String USB_MODE_DATA = "MD09;";
|
||||
private static final String DATA_U_MODE = "MD0C;";
|
||||
private static final String READ_FREQ = "FA;";
|
||||
private static final String READ_39METER_ALC = "RM4;";//38,39的指令都是一样的
|
||||
private static final String READ_39METER_SWR = "RM6;";//38,39的指令都是一样的
|
||||
|
||||
private static final String TX_ON = "TX1;";//用于FT450 ptt
|
||||
private static final String TX_OFF = "TX0;";//用于FT450 ptt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static String getModeStr(int mode) {
|
||||
switch (mode) {
|
||||
case LSB:
|
||||
return "LSB";
|
||||
case USB:
|
||||
return "USB";
|
||||
case CW:
|
||||
return "CW";
|
||||
case FM:
|
||||
return "FM";
|
||||
case AM:
|
||||
return "AM";
|
||||
case RTTY:
|
||||
return "RTTY";
|
||||
case CW_R:
|
||||
return "CW_R";
|
||||
case DATA:
|
||||
return "DATA";
|
||||
case RTTY_R:
|
||||
return "RTTY_R";
|
||||
case NONE:
|
||||
return "NONE";
|
||||
case FM_N:
|
||||
return "FM_N";
|
||||
case DATA_R:
|
||||
return "DATA_R";
|
||||
case AM_N:
|
||||
return "AM_N";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static byte[] setPTTState(boolean on) {
|
||||
if (on) {
|
||||
return PTT_ON.getBytes();
|
||||
} else {
|
||||
return PTT_OFF.getBytes();
|
||||
}
|
||||
|
||||
}
|
||||
//针对YAESU 450的发射指令
|
||||
public static byte[] setPTT_TX_On(boolean on) {//用于FT450
|
||||
if (on) {
|
||||
return TX_ON.getBytes();
|
||||
} else {
|
||||
return TX_OFF.getBytes();
|
||||
}
|
||||
|
||||
}
|
||||
public static byte[] setOperationUSBMode() {
|
||||
return USB_MODE.getBytes();
|
||||
}
|
||||
public static byte[] setOperationUSB_Data_Mode() {
|
||||
return DATA_U_MODE.getBytes();
|
||||
}
|
||||
|
||||
public static byte[] setOperationDATA_U_Mode() {
|
||||
return DATA_U_MODE.getBytes();
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
public static byte[] setOperationFreq11Byte(long freq) {//用于KENWOOD TS590
|
||||
return String.format("FA%011d;",freq).getBytes();
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
public static byte[] setOperationFreq9Byte(long freq) {
|
||||
return String.format("FA%09d;",freq).getBytes();
|
||||
}
|
||||
@SuppressLint("DefaultLocale")
|
||||
public static byte[] setOperationFreq8Byte(long freq) {
|
||||
return String.format("FA%08d;",freq).getBytes();
|
||||
}
|
||||
public static byte[] setReadOperationFreq(){
|
||||
return READ_FREQ.getBytes();
|
||||
}
|
||||
|
||||
public static byte[] setRead39Meters_ALC(){
|
||||
return READ_39METER_ALC.getBytes();
|
||||
}
|
||||
public static byte[] setRead39Meters_SWR(){
|
||||
return READ_39METER_SWR.getBytes();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
package com.bg7yoz.ft8cn.rigs;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
|
||||
public class Yaesu3RigConstant {
|
||||
private static final String TAG = "Yaesu3RigConstant";
|
||||
//LSB:0,USB:1,AM:2,CW:3,RTTY:4,FM:5,WFM:6,CW_R:7,RTTY_R:8,DV:17
|
||||
public static final int LSB = 0x01;
|
||||
public static final int USB = 0x02;
|
||||
public static final int CW = 0x03;
|
||||
public static final int FM = 0x04;
|
||||
public static final int AM = 0x05;
|
||||
public static final int RTTY = 0x06;
|
||||
public static final int CW_R = 0x07;
|
||||
public static final int DATA = 0x08;
|
||||
public static final int RTTY_R = 0x09;
|
||||
public static final int NONE = 0x0A;
|
||||
public static final int FM_N = 0x0B;
|
||||
public static final int DATA_R = 0x0C;
|
||||
public static final int AM_N = 0x0D;
|
||||
|
||||
|
||||
public static final int swr_39_alert_max=125;//相当于3.0
|
||||
public static final int alc_39_alert_max=125;//超过,在表上显示红色
|
||||
//PTT状态
|
||||
|
||||
//指令集
|
||||
private static final String PTT_ON = "MX1;";
|
||||
private static final String PTT_OFF = "MX0;";
|
||||
private static final String USB_MODE = "MD02;";
|
||||
private static final String USB_MODE_DATA = "MD09;";
|
||||
private static final String DATA_U_MODE = "MD0C;";
|
||||
private static final String READ_FREQ = "FA;";
|
||||
private static final String READ_39METER_ALC = "RM4;";//38,39的指令都是一样的
|
||||
private static final String READ_39METER_SWR = "RM6;";//38,39的指令都是一样的
|
||||
|
||||
private static final String TX_ON = "TX1;";//用于FT450 ptt
|
||||
private static final String TX_OFF = "TX0;";//用于FT450 ptt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static String getModeStr(int mode) {
|
||||
switch (mode) {
|
||||
case LSB:
|
||||
return "LSB";
|
||||
case USB:
|
||||
return "USB";
|
||||
case CW:
|
||||
return "CW";
|
||||
case FM:
|
||||
return "FM";
|
||||
case AM:
|
||||
return "AM";
|
||||
case RTTY:
|
||||
return "RTTY";
|
||||
case CW_R:
|
||||
return "CW_R";
|
||||
case DATA:
|
||||
return "DATA";
|
||||
case RTTY_R:
|
||||
return "RTTY_R";
|
||||
case NONE:
|
||||
return "NONE";
|
||||
case FM_N:
|
||||
return "FM_N";
|
||||
case DATA_R:
|
||||
return "DATA_R";
|
||||
case AM_N:
|
||||
return "AM_N";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static byte[] setPTTState(boolean on) {
|
||||
if (on) {
|
||||
return PTT_ON.getBytes();
|
||||
} else {
|
||||
return PTT_OFF.getBytes();
|
||||
}
|
||||
|
||||
}
|
||||
//针对YAESU 450的发射指令
|
||||
public static byte[] setPTT_TX_On(boolean on) {//用于FT450
|
||||
if (on) {
|
||||
return TX_ON.getBytes();
|
||||
} else {
|
||||
return TX_OFF.getBytes();
|
||||
}
|
||||
|
||||
}
|
||||
public static byte[] setOperationUSBMode() {
|
||||
return USB_MODE.getBytes();
|
||||
}
|
||||
public static byte[] setOperationUSB_Data_Mode() {
|
||||
return DATA_U_MODE.getBytes();
|
||||
}
|
||||
|
||||
public static byte[] setOperationDATA_U_Mode() {
|
||||
return DATA_U_MODE.getBytes();
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
public static byte[] setOperationFreq11Byte(long freq) {//用于KENWOOD TS590
|
||||
return String.format("FA%011d;",freq).getBytes();
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
public static byte[] setOperationFreq9Byte(long freq) {
|
||||
return String.format("FA%09d;",freq).getBytes();
|
||||
}
|
||||
@SuppressLint("DefaultLocale")
|
||||
public static byte[] setOperationFreq8Byte(long freq) {
|
||||
return String.format("FA%08d;",freq).getBytes();
|
||||
}
|
||||
public static byte[] setReadOperationFreq(){
|
||||
return READ_FREQ.getBytes();
|
||||
}
|
||||
|
||||
public static byte[] setRead39Meters_ALC(){
|
||||
return READ_39METER_ALC.getBytes();
|
||||
}
|
||||
public static byte[] setRead39Meters_SWR(){
|
||||
return READ_39METER_SWR.getBytes();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import java.util.EnumSet;
|
|||
public interface UsbSerialPort extends Closeable {
|
||||
|
||||
/** 5 data bits. */
|
||||
//数据位
|
||||
int DATABITS_5 = 5;
|
||||
/** 6 data bits. */
|
||||
int DATABITS_6 = 6;
|
||||
|
@ -39,6 +40,7 @@ public interface UsbSerialPort extends Closeable {
|
|||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({PARITY_NONE, PARITY_ODD, PARITY_EVEN, PARITY_MARK, PARITY_SPACE})
|
||||
@interface Parity {}
|
||||
//奇偶校验位
|
||||
/** No parity. */
|
||||
int PARITY_NONE = 0;
|
||||
/** Odd parity. */
|
||||
|
|
|
@ -53,7 +53,8 @@ public class CallingListAdapter extends RecyclerView.Adapter<CallingListAdapter.
|
|||
|
||||
//添加菜单的参数i1:组,i2:id值,i3:显示顺序
|
||||
if (!ft8Message.getCallsignTo().contains("...")//目标不能是自己
|
||||
&& !ft8Message.getCallsignTo().equals(GeneralVariables.myCallsign)
|
||||
//&& !ft8Message.getCallsignTo().equals(GeneralVariables.myCallsign)
|
||||
&& !GeneralVariables.checkIsMyCallsign(ft8Message.getCallsignTo())
|
||||
&& !(ft8Message.i3==0&&ft8Message.n3==0)) {
|
||||
if (!ft8Message.checkIsCQ()) {
|
||||
if (showMode==ShowMode.CALLING_LIST) {//在消息列表中就可以显示这个菜单了
|
||||
|
@ -69,7 +70,8 @@ public class CallingListAdapter extends RecyclerView.Adapter<CallingListAdapter.
|
|||
.setActionView(view);
|
||||
}
|
||||
//说明是对我呼叫,加上回复菜单
|
||||
if (ft8Message.getCallsignTo().equals(GeneralVariables.myCallsign)) {
|
||||
//if (ft8Message.getCallsignTo().equals(GeneralVariables.myCallsign)) {
|
||||
if (GeneralVariables.checkIsMyCallsign(ft8Message.getCallsignTo())) {
|
||||
contextMenu.add(0, 4, 0, String.format(
|
||||
GeneralVariables.getStringFromResource(R.string.reply_to)
|
||||
, ft8Message.getCallsignFrom(), ft8Message.fromWhere))
|
||||
|
@ -91,7 +93,8 @@ public class CallingListAdapter extends RecyclerView.Adapter<CallingListAdapter.
|
|||
}
|
||||
|
||||
if (!ft8Message.getCallsignFrom().contains("...")
|
||||
&& !ft8Message.getCallsignFrom().equals(GeneralVariables.myCallsign)
|
||||
//&& !ft8Message.getCallsignFrom().equals(GeneralVariables.myCallsign)
|
||||
&& !GeneralVariables.checkIsMyCallsign(ft8Message.getCallsignFrom())
|
||||
&& !(ft8Message.i3==0&&ft8Message.n3==0)) {
|
||||
if (showMode==ShowMode.CALLING_LIST) {//在消息列表中就可以显示这个菜单了
|
||||
contextMenu.add(1, 2, 0, String.format(
|
||||
|
|
|
@ -230,8 +230,9 @@ public class CallingListFragment extends Fragment {
|
|||
if (message != null) {
|
||||
//呼叫的目标不能是自己
|
||||
if (!message.getCallsignFrom().equals("<...>")
|
||||
&& !message.getCallsignFrom().equals(GeneralVariables.myCallsign)
|
||||
&& !(message.i3 == 0 && message.n3 == 0)) {
|
||||
//&& !message.getCallsignFrom().equals(GeneralVariables.myCallsign)
|
||||
&& !GeneralVariables.checkIsMyCallsign(message.getCallsignFrom())
|
||||
&& !(message.i3 == 0 && (message.n3 == 0 || message.n3 == 5))) {//遥测和自由文本不能呼叫
|
||||
doCallNow(message);
|
||||
} else {
|
||||
callingListAdapter.notifyItemChanged(viewHolder.getAdapterPosition());
|
||||
|
|
|
@ -48,6 +48,9 @@ public class ConfigFragment extends Fragment {
|
|||
private FragmentConfigBinding binding;
|
||||
private BandsSpinnerAdapter bandsSpinnerAdapter;
|
||||
private BauRateSpinnerAdapter bauRateSpinnerAdapter;
|
||||
private SerialDataBitsSpinnerAdapter dataBitsSpinnerAdapter;
|
||||
private SerialParityBitsSpinnerAdapter parityBitsSpinnerAdapter;
|
||||
private SerialStopBitsSpinnerAdapter stopBitsSpinnerAdapter;
|
||||
private RigNameSpinnerAdapter rigNameSpinnerAdapter;
|
||||
private LaunchSupervisionSpinnerAdapter launchSupervisionSpinnerAdapter;
|
||||
private PttDelaySpinnerAdapter pttDelaySpinnerAdapter;
|
||||
|
@ -257,13 +260,6 @@ public class ConfigFragment extends Fragment {
|
|||
mainViewModel = MainViewModel.getInstance(this);
|
||||
binding = FragmentConfigBinding.inflate(inflater, container, false);
|
||||
|
||||
//只对中国开方问题收集
|
||||
// if (GeneralVariables.isChina) {
|
||||
// binding.faqButton.setVisibility(View.VISIBLE);
|
||||
// } else {
|
||||
// binding.faqButton.setVisibility(View.GONE);
|
||||
// }
|
||||
|
||||
|
||||
//设置时间偏移
|
||||
setUtcTimeOffsetSpinner();
|
||||
|
@ -277,6 +273,15 @@ public class ConfigFragment extends Fragment {
|
|||
//设置波特率列表
|
||||
setBauRateSpinner();
|
||||
|
||||
//设置数据位列表
|
||||
setDataBitsSpinner();
|
||||
|
||||
//设置校验位
|
||||
setParityBitsSpinner();
|
||||
|
||||
//设置停止位
|
||||
setStopBitsSpinner();
|
||||
|
||||
//设置电台名称,参数列表
|
||||
setRigNameSpinner();
|
||||
|
||||
|
@ -307,6 +312,9 @@ public class ConfigFragment extends Fragment {
|
|||
//设置无回应次数中断
|
||||
setNoReplyLimitSpinner();
|
||||
|
||||
//设置各个spinner的OnItemSelected事件
|
||||
setSpinnerOnItemSelected();
|
||||
|
||||
//显示滚动箭头
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
|
@ -387,122 +395,24 @@ public class ConfigFragment extends Fragment {
|
|||
});
|
||||
|
||||
//设置PTT延迟
|
||||
binding.pttDelayOffsetSpinner.setOnItemSelectedListener(null);
|
||||
binding.pttDelayOffsetSpinner.setSelection(GeneralVariables.pttDelay / 10);
|
||||
binding.pttDelayOffsetSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
GeneralVariables.pttDelay = i * 10;
|
||||
writeConfig("pttDelay", String.valueOf(GeneralVariables.pttDelay));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> adapterView) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
//获取操作的波段
|
||||
binding.operationBandSpinner.setOnItemSelectedListener(null);
|
||||
binding.operationBandSpinner.setSelection(GeneralVariables.bandListIndex);
|
||||
binding.operationBandSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
GeneralVariables.bandListIndex = i;
|
||||
GeneralVariables.band = OperationBand.getBandFreq(i);//把当前的频段保存下来
|
||||
|
||||
mainViewModel.databaseOpr.getAllQSLCallsigns();//通联成功的呼号读出来
|
||||
writeConfig("bandFreq", String.valueOf(GeneralVariables.band));
|
||||
if (GeneralVariables.controlMode == ControlMode.CAT//CAT、RTS、DTR模式下控制电台
|
||||
|| GeneralVariables.controlMode == ControlMode.RTS
|
||||
|| GeneralVariables.controlMode == ControlMode.DTR) {
|
||||
//如果在CAT、RTS模式下,修改电台的频率
|
||||
mainViewModel.setOperationBand();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> adapterView) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
//获取电台型号
|
||||
binding.rigNameSpinner.setOnItemSelectedListener(null);
|
||||
binding.rigNameSpinner.setSelection(GeneralVariables.modelNo);
|
||||
new Handler().postDelayed(new Runnable() {//延迟2秒修改OnItemSelectedListener
|
||||
@Override
|
||||
public void run() {
|
||||
binding.rigNameSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
GeneralVariables.modelNo = i;
|
||||
writeConfig("model", String.valueOf(i));
|
||||
setAddrAndBauRate(rigNameSpinnerAdapter.getRigName(i));
|
||||
|
||||
//指令集
|
||||
GeneralVariables.instructionSet = rigNameSpinnerAdapter.getRigName(i).instructionSet;
|
||||
writeConfig("instruction", String.valueOf(GeneralVariables.instructionSet));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> adapterView) {
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
|
||||
//串口数据位
|
||||
binding.dataBitsSpinner.setSelection(dataBitsSpinnerAdapter.getPosition(GeneralVariables.serialDataBits));
|
||||
//串口停止位
|
||||
binding.stopBitsSpinner.setSelection(stopBitsSpinnerAdapter.getPosition(GeneralVariables.serialStopBits));
|
||||
binding.parityBitsSpinner.setSelection(parityBitsSpinnerAdapter.getPosition(GeneralVariables.serialParity));
|
||||
//获取波特率
|
||||
binding.baudRateSpinner.setOnItemSelectedListener(null);
|
||||
binding.baudRateSpinner.setSelection(bauRateSpinnerAdapter.getPosition(
|
||||
GeneralVariables.baudRate));
|
||||
binding.baudRateSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
GeneralVariables.baudRate = bauRateSpinnerAdapter.getValue(i);
|
||||
writeConfig("baudRate", String.valueOf(GeneralVariables.baudRate));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> adapterView) {
|
||||
}
|
||||
});
|
||||
|
||||
//设置发射监管
|
||||
binding.launchSupervisionSpinner.setOnItemSelectedListener(null);
|
||||
binding.launchSupervisionSpinner.setSelection(launchSupervisionSpinnerAdapter
|
||||
.getPosition(GeneralVariables.launchSupervision));
|
||||
binding.launchSupervisionSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
GeneralVariables.launchSupervision = LaunchSupervisionSpinnerAdapter.getTimeOut(i);
|
||||
writeConfig("launchSupervision", String.valueOf(GeneralVariables.launchSupervision));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> adapterView) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
//设置无回应中断
|
||||
binding.noResponseCountSpinner.setOnItemSelectedListener(null);
|
||||
binding.noResponseCountSpinner.setSelection(GeneralVariables.noReplyLimit);
|
||||
binding.noResponseCountSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
GeneralVariables.noReplyLimit = i;
|
||||
writeConfig("noReplyLimit", String.valueOf(GeneralVariables.noReplyLimit));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> adapterView) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
//设置自动关注CQ
|
||||
|
@ -587,17 +497,183 @@ public class ConfigFragment extends Fragment {
|
|||
}
|
||||
});
|
||||
|
||||
//串口默认值设置复位键
|
||||
binding.serialDefaultButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
GeneralVariables.serialParity = 0;
|
||||
GeneralVariables.serialDataBits = 8;
|
||||
GeneralVariables.serialStopBits = 1;
|
||||
requireActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
binding.parityBitsSpinner.setSelection(
|
||||
parityBitsSpinnerAdapter.getPosition(GeneralVariables.serialParity));
|
||||
binding.dataBitsSpinner.setSelection(
|
||||
dataBitsSpinnerAdapter.getPosition(GeneralVariables.serialDataBits));
|
||||
binding.stopBitsSpinner.setSelection(
|
||||
stopBitsSpinnerAdapter.getPosition(GeneralVariables.serialStopBits));
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置各个spinner的OnItemSelected事件,防止在进入主界面时,重复向数据库写入配置信息
|
||||
*/
|
||||
private void setSpinnerOnItemSelected(){
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
binding.pttDelayOffsetSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
GeneralVariables.pttDelay = i * 10;
|
||||
writeConfig("pttDelay", String.valueOf(GeneralVariables.pttDelay));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> adapterView) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
binding.operationBandSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
GeneralVariables.bandListIndex = i;
|
||||
GeneralVariables.band = OperationBand.getBandFreq(i);//把当前的频段保存下来
|
||||
|
||||
mainViewModel.databaseOpr.getAllQSLCallsigns();//通联成功的呼号读出来
|
||||
writeConfig("bandFreq", String.valueOf(GeneralVariables.band));
|
||||
if (GeneralVariables.controlMode == ControlMode.CAT//CAT、RTS、DTR模式下控制电台
|
||||
|| GeneralVariables.controlMode == ControlMode.RTS
|
||||
|| GeneralVariables.controlMode == ControlMode.DTR) {
|
||||
//如果在CAT、RTS模式下,修改电台的频率
|
||||
mainViewModel.setOperationBand();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> adapterView) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
binding.rigNameSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
GeneralVariables.modelNo = i;
|
||||
writeConfig("model", String.valueOf(i));
|
||||
setAddrAndBauRate(rigNameSpinnerAdapter.getRigName(i));
|
||||
|
||||
//指令集
|
||||
GeneralVariables.instructionSet = rigNameSpinnerAdapter.getRigName(i).instructionSet;
|
||||
writeConfig("instruction", String.valueOf(GeneralVariables.instructionSet));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> adapterView) {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
binding.baudRateSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
GeneralVariables.baudRate = bauRateSpinnerAdapter.getValue(i);
|
||||
writeConfig("baudRate", String.valueOf(GeneralVariables.baudRate));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> adapterView) {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
binding.launchSupervisionSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
GeneralVariables.launchSupervision = LaunchSupervisionSpinnerAdapter.getTimeOut(i);
|
||||
writeConfig("launchSupervision", String.valueOf(GeneralVariables.launchSupervision));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> adapterView) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
binding.noResponseCountSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
GeneralVariables.noReplyLimit = i;
|
||||
writeConfig("noReplyLimit", String.valueOf(GeneralVariables.noReplyLimit));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> adapterView) {
|
||||
|
||||
}
|
||||
});
|
||||
//串口数据位
|
||||
binding.dataBitsSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
GeneralVariables.serialDataBits = dataBitsSpinnerAdapter.getValue(i);
|
||||
writeConfig("dataBits", String.valueOf(GeneralVariables.serialDataBits));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> adapterView) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
//串口停止位
|
||||
binding.stopBitsSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
GeneralVariables.serialStopBits = stopBitsSpinnerAdapter.getValue(i);
|
||||
writeConfig("stopBits", String.valueOf(GeneralVariables.serialStopBits));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> adapterView) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
//校验位
|
||||
binding.parityBitsSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
GeneralVariables.serialParity = parityBitsSpinnerAdapter.getValue(i);
|
||||
writeConfig("parityBits", String.valueOf(GeneralVariables.serialParity));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> adapterView) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置地址和波特率,指令集
|
||||
*
|
||||
* @param rigName 电台型号
|
||||
*/
|
||||
private void setAddrAndBauRate(RigNameList.RigName rigName) {
|
||||
//mainViewModel.setCivAddress(rigName.address);
|
||||
GeneralVariables.civAddress = rigName.address;
|
||||
mainViewModel.setCivAddress();
|
||||
GeneralVariables.baudRate = rigName.bauRate;
|
||||
|
@ -715,6 +791,49 @@ public class ConfigFragment extends Fragment {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置数据位列表
|
||||
*/
|
||||
private void setDataBitsSpinner(){
|
||||
dataBitsSpinnerAdapter = new SerialDataBitsSpinnerAdapter(requireContext());
|
||||
binding.dataBitsSpinner.setAdapter(dataBitsSpinnerAdapter);
|
||||
requireActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
dataBitsSpinnerAdapter.notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置校验位列表
|
||||
*/
|
||||
private void setParityBitsSpinner(){
|
||||
parityBitsSpinnerAdapter = new SerialParityBitsSpinnerAdapter(requireContext());
|
||||
binding.parityBitsSpinner.setAdapter(parityBitsSpinnerAdapter);
|
||||
requireActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
parityBitsSpinnerAdapter.notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 设置停止位列表
|
||||
*/
|
||||
private void setStopBitsSpinner(){
|
||||
stopBitsSpinnerAdapter = new SerialStopBitsSpinnerAdapter(requireContext());
|
||||
binding.stopBitsSpinner.setAdapter(stopBitsSpinnerAdapter);
|
||||
requireActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
stopBitsSpinnerAdapter.notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 设置无回应次数中断
|
||||
*/
|
||||
|
@ -771,6 +890,8 @@ public class ConfigFragment extends Fragment {
|
|||
binding.pttDelayOffsetSpinner.setSelection(GeneralVariables.pttDelay / 10);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -943,13 +1064,15 @@ public class ConfigFragment extends Fragment {
|
|||
* 设置连线的方式,可以是USB,也可以是BLUE_TOOTH
|
||||
*/
|
||||
private void setConnectMode() {
|
||||
if (GeneralVariables.controlMode == ControlMode.CAT
|
||||
if ((GeneralVariables.controlMode == ControlMode.CAT)
|
||||
//&& BluetoothConstants.checkBluetoothIsOpen()
|
||||
) {
|
||||
//此处要改成VISIBLE
|
||||
binding.connectModeLayout.setVisibility(View.VISIBLE);
|
||||
binding.serialLayout.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
binding.connectModeLayout.setVisibility(View.GONE);
|
||||
binding.serialLayout.setVisibility(View.GONE);
|
||||
}
|
||||
binding.connectModeRadioGroup.clearCheck();
|
||||
switch (GeneralVariables.connectMode) {
|
||||
|
@ -985,7 +1108,10 @@ 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.XIEGU_6100_FT8CNS) {
|
||||
new SelectXieguRadioDialog(requireContext(), mainViewModel).show();
|
||||
}
|
||||
else if(GeneralVariables.instructionSet== InstructionSet.ICOM
|
||||
||GeneralVariables.instructionSet== InstructionSet.XIEGU_6100
|
||||
||GeneralVariables.instructionSet== InstructionSet.XIEGUG90S) {
|
||||
new LoginIcomRadioDialog(requireContext(), mainViewModel).show();
|
||||
|
@ -1067,6 +1193,16 @@ public class ConfigFragment extends Fragment {
|
|||
, true).show();
|
||||
}
|
||||
});
|
||||
//设置串口参数帮助
|
||||
binding.serialHelpImageButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
new HelpDialog(requireContext(), requireActivity()
|
||||
, GeneralVariables.getStringFromResource(R.string.serial_setting_help)
|
||||
, true).show();
|
||||
}
|
||||
});
|
||||
|
||||
//显示列表方式
|
||||
binding.messageModeeHelpImageButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
|
|
|
@ -33,12 +33,6 @@ public class FlexRadioInfoFragment extends Fragment {
|
|||
}
|
||||
|
||||
|
||||
// public static FlexRadioInfoFragment newInstance(String param1, String param2) {
|
||||
// FlexRadioInfoFragment fragment = new FlexRadioInfoFragment();
|
||||
//
|
||||
// return fragment;
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
|
|
@ -467,7 +467,8 @@ public class MyCallingFragment extends Fragment {
|
|||
if (message != null) {
|
||||
//呼叫的目标不能是自己
|
||||
if (!message.getCallsignFrom().equals("<...>")
|
||||
&& !message.getCallsignFrom().equals(GeneralVariables.myCallsign)
|
||||
//&& !message.getCallsignFrom().equals(GeneralVariables.myCallsign)
|
||||
&& !GeneralVariables.checkIsMyCallsign(message.getCallsignFrom())
|
||||
&& !(message.i3 == 0 && message.n3 == 0)) {
|
||||
doCallNow(message);
|
||||
}
|
||||
|
|
|
@ -50,11 +50,17 @@ public class RigNameSpinnerAdapter extends BaseAdapter {
|
|||
view=_LayoutInflater.inflate(R.layout.rig_name_spinner_item, null);
|
||||
if (view!=null){
|
||||
TextView textView=view.findViewById(R.id.rigNameItemTextView);
|
||||
if (rigNameList.getRigNameInfo(i).startsWith("#")) {
|
||||
view.setVisibility(View.GONE);
|
||||
}
|
||||
ImageView imageView=view.findViewById(R.id.rigLogoImageView);
|
||||
if (rigNameList.getRigNameInfo(i).contains("GUOHE")){
|
||||
if (rigNameList.getRigNameInfo(i).toUpperCase().contains("GUOHE")){
|
||||
imageView.setImageDrawable(mContext.getDrawable(R.drawable.guohe_logo));
|
||||
imageView.setVisibility(View.VISIBLE);
|
||||
}else {
|
||||
}else if (rigNameList.getRigNameInfo(i).toUpperCase().contains("XIEGU")){
|
||||
imageView.setImageDrawable(mContext.getDrawable(R.drawable.xiegulogo));
|
||||
imageView.setVisibility(View.VISIBLE);
|
||||
}else {
|
||||
imageView.setVisibility(View.GONE);
|
||||
imageView.setImageDrawable(null);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,246 @@
|
|||
package com.bg7yoz.ft8cn.ui;
|
||||
/**
|
||||
* FlexRadio选择对话框。
|
||||
* @author BGY70Z
|
||||
* @date 2023-03-20
|
||||
*/
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bg7yoz.ft8cn.GeneralVariables;
|
||||
import com.bg7yoz.ft8cn.MainViewModel;
|
||||
import com.bg7yoz.ft8cn.R;
|
||||
import com.bg7yoz.ft8cn.flex.FlexRadio;
|
||||
import com.bg7yoz.ft8cn.flex.FlexRadioFactory;
|
||||
import com.bg7yoz.ft8cn.x6100.X6100Radio;
|
||||
import com.bg7yoz.ft8cn.x6100.XieguRadioFactory;
|
||||
|
||||
public class SelectXieguRadioDialog extends Dialog {
|
||||
private static final String TAG="SelectXieguRadioDialog";
|
||||
private final MainViewModel mainViewModel;
|
||||
private RecyclerView xieguRecyclerView;
|
||||
private ImageView upImage;
|
||||
private ImageView downImage;
|
||||
private XieguRadioFactory xieguRadioFactory;
|
||||
private XieguRadioAdapter xieguRadioAdapter;
|
||||
private ImageButton connectXieguImageButton;
|
||||
private EditText inputXieguAddressEdit;
|
||||
|
||||
|
||||
|
||||
public SelectXieguRadioDialog(@NonNull Context context, MainViewModel mainViewModel){
|
||||
super(context);
|
||||
this.mainViewModel = mainViewModel;
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState){
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.select_xiegu_dialog_layout);
|
||||
xieguRecyclerView=(RecyclerView) findViewById(R.id.xieguRadioListRecyclerView);
|
||||
xieguRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
upImage=(ImageView) findViewById(R.id.xieguRadioScrollUpImageView);
|
||||
downImage=(ImageView)findViewById(R.id.xieguRadioScrollDownImageView);
|
||||
inputXieguAddressEdit=(EditText)findViewById(R.id.inputXieguAddressEdit);
|
||||
connectXieguImageButton=(ImageButton) findViewById(R.id.connectXieguImageButton);
|
||||
connectXieguImageButton.setEnabled(false);
|
||||
connectXieguImageButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
ToastMessage.show(String.format(
|
||||
GeneralVariables.getStringFromResource(R.string.select_xiegu_device)
|
||||
,inputXieguAddressEdit.getText()));
|
||||
X6100Radio xieguRadio = new X6100Radio();
|
||||
xieguRadio.setRig_ip(inputXieguAddressEdit.getText().toString());
|
||||
xieguRadio.setModelName("Xiegu Rig");
|
||||
ToastMessage.show(xieguRadio.getRig_ip());
|
||||
//此处添加连接6100电台的动作
|
||||
mainViewModel.connectXieguRadioRig(GeneralVariables.getMainContext(),xieguRadio);
|
||||
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
inputXieguAddressEdit.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
connectXieguImageButton.setEnabled(!inputXieguAddressEdit.getText().toString().isEmpty());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
xieguRadioAdapter=new XieguRadioAdapter();
|
||||
xieguRecyclerView.setAdapter(xieguRadioAdapter);
|
||||
|
||||
xieguRadioFactory = XieguRadioFactory.getInstance();
|
||||
|
||||
|
||||
xieguRadioFactory.setOnXieguRadioEvents(new XieguRadioFactory.OnXieguRadioEvents() {
|
||||
|
||||
@Override
|
||||
public void onXieguRadioAdded(X6100Radio xieguRadio) {
|
||||
xieguRecyclerView.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
xieguRadioAdapter.notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onXieguRadioInvalid(X6100Radio flexRadio) {
|
||||
xieguRecyclerView.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
xieguRadioAdapter.notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
//显示滚动箭头
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
setScrollImageVisible();
|
||||
}
|
||||
}, 1000);
|
||||
xieguRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||
@Override
|
||||
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
|
||||
super.onScrolled(recyclerView, dx, dy);
|
||||
setScrollImageVisible();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
super.show();
|
||||
WindowManager.LayoutParams params = getWindow().getAttributes();
|
||||
//设置对话框的大小,以百分比0.6
|
||||
int height = getWindow().getWindowManager().getDefaultDisplay().getHeight();
|
||||
int width = getWindow().getWindowManager().getDefaultDisplay().getWidth();
|
||||
// params.height = (int) (height * 0.6);
|
||||
if (width > height) {
|
||||
params.width = (int) (width * 0.6);
|
||||
params.height = (int) (height * 0.6);
|
||||
} else {
|
||||
params.width = (int) (width * 0.8);
|
||||
params.height = (int) (height * 0.5);
|
||||
}
|
||||
getWindow().setAttributes(params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置界面的上下滚动的图标
|
||||
*/
|
||||
private void setScrollImageVisible() {
|
||||
|
||||
if (xieguRecyclerView.canScrollVertically(1)) {
|
||||
upImage.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
upImage.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (xieguRecyclerView.canScrollVertically(-1)) {
|
||||
downImage.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
downImage.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
class XieguRadioAdapter extends RecyclerView.Adapter<XieguRadioAdapter.XieguViewHolder>{
|
||||
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public XieguViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
|
||||
View view = layoutInflater.inflate(R.layout.xiegu_device_list_item, parent, false);
|
||||
final XieguViewHolder holder = new XieguViewHolder(view);
|
||||
return holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull XieguViewHolder holder, int position) {
|
||||
holder.xieguRadio=xieguRadioFactory.xieguRadios.get(position);
|
||||
holder.xieguRadioIpTextView.setText(holder.xieguRadio.getRig_ip());
|
||||
holder.xieguInfoTextView.setText(holder.xieguRadio.getMac());
|
||||
holder.xieguRadioNameTextView.setText(holder.xieguRadio.getModelName());
|
||||
|
||||
holder.xieguRadioListConstraintLayout.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
ToastMessage.show(String.format(
|
||||
GeneralVariables.getStringFromResource(R.string.select_xiegu_device)
|
||||
,holder.xieguRadio.getModelName()));
|
||||
ToastMessage.show(holder.xieguRadio.getRig_ip());
|
||||
//此处添加连接6100电台的动作
|
||||
mainViewModel.connectXieguRadioRig(GeneralVariables.getMainContext(),holder.xieguRadio);
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return xieguRadioFactory.xieguRadios.size();
|
||||
}
|
||||
|
||||
|
||||
|
||||
class XieguViewHolder extends RecyclerView.ViewHolder{
|
||||
public X6100Radio xieguRadio;
|
||||
TextView xieguRadioNameTextView,xieguRadioIpTextView,xieguInfoTextView;
|
||||
ConstraintLayout xieguRadioListConstraintLayout;
|
||||
public XieguViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
xieguRadioNameTextView=itemView.findViewById(R.id.xieguRadioNameTextView);
|
||||
xieguRadioIpTextView=itemView.findViewById(R.id.xieguRadioIpTextView);
|
||||
xieguInfoTextView=itemView.findViewById(R.id.xieguInfoTextView);
|
||||
xieguRadioListConstraintLayout=itemView.findViewById(R.id.xieguRadioListConstraintLayout);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package com.bg7yoz.ft8cn.ui;
|
||||
/**
|
||||
* 串口数据位列表界面
|
||||
* @author BGY70Z
|
||||
* @date 2024-01-03
|
||||
*/
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bg7yoz.ft8cn.R;
|
||||
|
||||
|
||||
public class SerialDataBitsSpinnerAdapter extends BaseAdapter {
|
||||
private final Context mContext;
|
||||
private final int[] dataBits= {8,7,6,5};
|
||||
public SerialDataBitsSpinnerAdapter(Context context) {
|
||||
mContext=context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return dataBits.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int i) {
|
||||
return dataBits[i];
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int i) {
|
||||
return i;
|
||||
}
|
||||
|
||||
@SuppressLint({"ViewHolder", "InflateParams"})
|
||||
@Override
|
||||
public View getView(int i, View view, ViewGroup viewGroup) {
|
||||
LayoutInflater _LayoutInflater=LayoutInflater.from(mContext);
|
||||
view=_LayoutInflater.inflate(R.layout.serial_data_bits_spinner_item, null);
|
||||
if (view!=null){
|
||||
TextView textView=(TextView)view.findViewById(R.id.serialDataBitsItemTextView);
|
||||
textView.setText(String.valueOf(dataBits[i]));
|
||||
}
|
||||
return view;
|
||||
}
|
||||
public int getPosition(int i){
|
||||
for (int j = 0; j < dataBits.length; j++) {
|
||||
if (dataBits[j]==i){
|
||||
return j;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
public int getValue(int position){
|
||||
return dataBits[position];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package com.bg7yoz.ft8cn.ui;
|
||||
/**
|
||||
* 串口校验位列表界面
|
||||
* @author BGY70Z
|
||||
* @date 2024-01-03
|
||||
*/
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bg7yoz.ft8cn.GeneralVariables;
|
||||
import com.bg7yoz.ft8cn.R;
|
||||
|
||||
|
||||
public class SerialParityBitsSpinnerAdapter extends BaseAdapter {
|
||||
private final Context mContext;
|
||||
private final int[] parityBits= {0,1,2,3,4};
|
||||
private final String[] parityStr={GeneralVariables.getStringFromResource(R.string.serial_parity_none)
|
||||
,GeneralVariables.getStringFromResource(R.string.serial_parity_odd)
|
||||
,GeneralVariables.getStringFromResource(R.string.serial_parity_even)
|
||||
,GeneralVariables.getStringFromResource(R.string.serial_parity_mark)
|
||||
,GeneralVariables.getStringFromResource(R.string.serial_parity_space)
|
||||
};
|
||||
public SerialParityBitsSpinnerAdapter(Context context) {
|
||||
mContext=context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return parityBits.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int i) {
|
||||
return parityBits[i];
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int i) {
|
||||
return i;
|
||||
}
|
||||
|
||||
@SuppressLint({"ViewHolder", "InflateParams"})
|
||||
@Override
|
||||
public View getView(int i, View view, ViewGroup viewGroup) {
|
||||
LayoutInflater _LayoutInflater=LayoutInflater.from(mContext);
|
||||
view=_LayoutInflater.inflate(R.layout.serial_parity_bits_spinner_item, null);
|
||||
if (view!=null){
|
||||
TextView textView=(TextView)view.findViewById(R.id.serialParityBitsItemTextView);
|
||||
//textView.setText(String.valueOf(parityBits[i]));
|
||||
textView.setText(parityStr[i]);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
public int getPosition(int i){
|
||||
for (int j = 0; j < parityBits.length; j++) {
|
||||
if (parityBits[j]==i){
|
||||
return j;
|
||||
}
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
public int getValue(int position){
|
||||
return parityBits[position];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package com.bg7yoz.ft8cn.ui;
|
||||
/**
|
||||
* 串口停止位列表界面
|
||||
* @author BGY70Z
|
||||
* @date 2024-01-03
|
||||
*/
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bg7yoz.ft8cn.R;
|
||||
|
||||
|
||||
public class SerialStopBitsSpinnerAdapter extends BaseAdapter {
|
||||
private final Context mContext;
|
||||
private final int[] stopBits= {1,3,2};
|
||||
private final String[] stopBitsStr= {"1","1.5","2"};
|
||||
public SerialStopBitsSpinnerAdapter(Context context) {
|
||||
mContext=context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return stopBits.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int i) {
|
||||
return stopBits[i];
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int i) {
|
||||
return i;
|
||||
}
|
||||
|
||||
@SuppressLint({"ViewHolder", "InflateParams"})
|
||||
@Override
|
||||
public View getView(int i, View view, ViewGroup viewGroup) {
|
||||
LayoutInflater _LayoutInflater=LayoutInflater.from(mContext);
|
||||
view=_LayoutInflater.inflate(R.layout.serial_stop_bits_spinner_item, null);
|
||||
if (view!=null){
|
||||
TextView textView=(TextView)view.findViewById(R.id.serialStopBitsItemTextView);
|
||||
textView.setText(stopBitsStr[i]);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
public int getPosition(int i){
|
||||
for (int j = 0; j < stopBits.length; j++) {
|
||||
if (stopBits[j]==i){
|
||||
return j;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
public int getValue(int position){
|
||||
return stopBits[position];
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ import android.annotation.SuppressLint;
|
|||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
|
@ -60,6 +61,12 @@ public class SetVolumeDialog extends Dialog {
|
|||
GeneralVariables.volumePercent=i/100f;
|
||||
GeneralVariables.mutableVolumePercent.postValue(i/100f);
|
||||
mainViewModel.databaseOpr.writeConfig("volumeValue",String.valueOf(i),null);
|
||||
if (mainViewModel.baseRig!=null){
|
||||
if (mainViewModel.baseRig.getConnector()!=null) {
|
||||
mainViewModel.baseRig.getConnector().setRFVolume(i);
|
||||
Log.e(TAG,String.format("set volume:%d",i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -62,6 +62,9 @@ public class VolumeProgress extends View {
|
|||
init();
|
||||
invalidate();
|
||||
}
|
||||
public float getPercent(){
|
||||
return mPercent;
|
||||
}
|
||||
public void reDraw(){
|
||||
setPercent(mPercent);
|
||||
}
|
||||
|
|
|
@ -12,9 +12,11 @@ import android.annotation.SuppressLint;
|
|||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.LinearGradient;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Shader;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
|
@ -24,6 +26,7 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.bg7yoz.ft8cn.Ft8Message;
|
||||
import com.bg7yoz.ft8cn.GeneralVariables;
|
||||
import com.bg7yoz.ft8cn.timer.UtcTimer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -44,7 +47,8 @@ public class WaterfallView extends View {
|
|||
private Paint touchPaint = new Paint();
|
||||
private final Paint fontPaint = new Paint();
|
||||
private final Paint messagePaint = new Paint();
|
||||
private final Paint messagePaintBack = new Paint();//消息背景
|
||||
private final Paint textLinePaint = new Paint();
|
||||
// private final Paint messagePaintBack = new Paint();//消息背景
|
||||
private final Paint utcPaint = new Paint();
|
||||
Paint linearPaint = new Paint();
|
||||
private final Paint utcPainBack = new Paint();
|
||||
|
@ -105,6 +109,14 @@ public class WaterfallView extends View {
|
|||
fontPaint.setDither(true);
|
||||
fontPaint.setTextAlign(Paint.Align.LEFT);
|
||||
|
||||
|
||||
textLinePaint.setColor(0xff00ffff);
|
||||
textLinePaint.setAntiAlias(true);
|
||||
textLinePaint.setDither(true);
|
||||
textLinePaint.setStrokeWidth(2);
|
||||
textLinePaint.setStyle(Paint.Style.FILL_AND_STROKE);
|
||||
|
||||
|
||||
// messagePaint = new Paint();
|
||||
messagePaint.setTextSize(dpToPixel(11));
|
||||
messagePaint.setColor(0xff00ffff);
|
||||
|
@ -113,16 +125,17 @@ public class WaterfallView extends View {
|
|||
messagePaint.setStrokeWidth(0);
|
||||
messagePaint.setStyle(Paint.Style.FILL_AND_STROKE);
|
||||
messagePaint.setTextAlign(Paint.Align.CENTER);
|
||||
messagePaint.setShadowLayer(10,5,5,Color.BLACK);
|
||||
|
||||
//messagePaintBack = new Paint();
|
||||
messagePaintBack.setTextSize(dpToPixel(11));
|
||||
messagePaintBack.setColor(0xff000000);//背景不透明
|
||||
messagePaintBack.setAntiAlias(true);
|
||||
messagePaintBack.setDither(true);
|
||||
messagePaintBack.setStrokeWidth(dpToPixel(3));
|
||||
messagePaintBack.setFakeBoldText(true);
|
||||
messagePaintBack.setStyle(Paint.Style.FILL_AND_STROKE);
|
||||
messagePaintBack.setTextAlign(Paint.Align.CENTER);
|
||||
// messagePaintBack.setTextSize(dpToPixel(11));
|
||||
// messagePaintBack.setColor(0xff000000);//背景不透明
|
||||
// messagePaintBack.setAntiAlias(true);
|
||||
// messagePaintBack.setDither(true);
|
||||
// messagePaintBack.setStrokeWidth(dpToPixel(3));
|
||||
// messagePaintBack.setFakeBoldText(true);
|
||||
// messagePaintBack.setStyle(Paint.Style.FILL_AND_STROKE);
|
||||
// messagePaintBack.setTextAlign(Paint.Align.CENTER);
|
||||
|
||||
//utcPaint = new Paint();
|
||||
utcPaint.setTextSize(dpToPixel(10));
|
||||
|
@ -133,6 +146,7 @@ public class WaterfallView extends View {
|
|||
utcPaint.setStyle(Paint.Style.FILL_AND_STROKE);
|
||||
utcPaint.setTextAlign(Paint.Align.LEFT);
|
||||
|
||||
|
||||
//utcPainBack = new Paint();
|
||||
utcPainBack.setTextSize(dpToPixel(10));
|
||||
utcPainBack.setColor(0xff000000);//背景不透明
|
||||
|
@ -242,27 +256,39 @@ public class WaterfallView extends View {
|
|||
//消息有3种:普通、CQ、有我
|
||||
if (drawMessage && messages != null) {
|
||||
drawMessage = false;//只画一遍
|
||||
fontPaint.setTextAlign(Paint.Align.LEFT);
|
||||
//fontPaint.setTextAlign(Paint.Align.LEFT);
|
||||
//fontPaint.setStrikeThruText(true);
|
||||
for (Ft8Message msg : messages) {
|
||||
|
||||
if (msg.inMyCall()) {//与我有关
|
||||
messagePaint.setColor(0xffffb2b2);
|
||||
textLinePaint.setColor(0xffffb2b2);
|
||||
} else if (msg.checkIsCQ()) {//CQ
|
||||
messagePaint.setColor(0xffeeee00);
|
||||
textLinePaint.setColor(0xffeeee00);
|
||||
} else {
|
||||
messagePaint.setColor(0xff00ffff);
|
||||
textLinePaint.setColor(0xff00ffff);
|
||||
}
|
||||
|
||||
Path path = new Path();
|
||||
|
||||
path.moveTo(msg.freq_hz * freq_width, pathStart);
|
||||
path.lineTo(msg.freq_hz * freq_width, pathEnd);
|
||||
|
||||
|
||||
_canvas.drawTextOnPath(msg.getMessageText(true), path
|
||||
, 0, 0, messagePaintBack);//消息背景
|
||||
|
||||
// _canvas.drawTextOnPath(msg.getMessageText(true), path
|
||||
// , 0, 0, messagePaintBack);//消息背景
|
||||
_canvas.drawTextOnPath(msg.getMessageText(true), path
|
||||
, 0, 0, messagePaint);//消息
|
||||
|
||||
if (GeneralVariables.checkQSLCallsign(msg.getCallsignFrom())) {//画删除线
|
||||
float text_len = messagePaint.measureText(msg.getMessageText(true));
|
||||
float text_start = ((pathEnd- pathStart)-text_len)/2;
|
||||
float text_high =dpToPixel(4);//messagePaint.getFontSpacing()/2;
|
||||
_canvas.drawLine(msg.freq_hz * freq_width + text_high , text_start
|
||||
, msg.freq_hz * freq_width + text_high, text_len + text_start, textLinePaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
package com.bg7yoz.ft8cn.ui;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.Observer;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.SeekBar;
|
||||
|
||||
import com.bg7yoz.ft8cn.GeneralVariables;
|
||||
import com.bg7yoz.ft8cn.MainViewModel;
|
||||
import com.bg7yoz.ft8cn.R;
|
||||
import com.bg7yoz.ft8cn.connector.X6100Connector;
|
||||
import com.bg7yoz.ft8cn.databinding.FragmentXieguInfoBinding;
|
||||
import com.bg7yoz.ft8cn.x6100.X6100Meters;
|
||||
import com.bg7yoz.ft8cn.x6100.X6100Radio;
|
||||
|
||||
|
||||
public class XieguInfoFragment extends Fragment {
|
||||
private static final String TAG ="XieguInfoFragment";
|
||||
private MainViewModel mainViewModel;
|
||||
private X6100Connector connector;
|
||||
private FragmentXieguInfoBinding binding;
|
||||
private X6100Radio xieguRadio;
|
||||
private SeekBar.OnSeekBarChangeListener onSeekBarChangeListener=null;
|
||||
|
||||
|
||||
|
||||
public XieguInfoFragment() {
|
||||
// Required empty public constructor
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mainViewModel = MainViewModel.getInstance(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
binding=FragmentXieguInfoBinding.inflate(getLayoutInflater());
|
||||
if (mainViewModel.baseRig != null){
|
||||
connector = (X6100Connector) mainViewModel.baseRig.getConnector();
|
||||
xieguRadio =connector.getXieguRadio();
|
||||
binding.xieguInfoTextView.setText(xieguRadio.getModelName());
|
||||
|
||||
//ping 值
|
||||
xieguRadio.mutablePing.observe(getViewLifecycleOwner(), new Observer<Long>() {
|
||||
@Override
|
||||
public void onChanged(Long aLong) {
|
||||
binding.xieguPingValueTextView.setText(
|
||||
String.format(GeneralVariables.getStringFromResource(R.string.xiegu_ping_value)
|
||||
,aLong));
|
||||
}
|
||||
});
|
||||
//丢包数量
|
||||
xieguRadio.mutableLossPackets.observe(getViewLifecycleOwner(), new Observer<Integer>() {
|
||||
@Override
|
||||
public void onChanged(Integer integer) {
|
||||
binding.xieguLossValueTextView.setText(String.format(
|
||||
GeneralVariables.getStringFromResource(R.string.x6100_packet_lost)
|
||||
,integer));
|
||||
}
|
||||
});
|
||||
mainViewModel.baseRig.mutableFrequency.observe(getViewLifecycleOwner(), new Observer<Long>() {
|
||||
@Override
|
||||
public void onChanged(Long aLong) {
|
||||
binding.xieguFreqValueTextView.setText(String.format(
|
||||
GeneralVariables.getStringFromResource(R.string.xiegu_band_str)
|
||||
,GeneralVariables.getBandString()));
|
||||
}
|
||||
});
|
||||
xieguRadio.mutableMeters.observe(getViewLifecycleOwner(), new Observer<X6100Meters>() {
|
||||
@Override
|
||||
public void onChanged(X6100Meters x6100Meters) {
|
||||
binding.xieguMetersValueTextView.setText(x6100Meters.toString());
|
||||
binding.xieguSMeterRulerView.setValue(x6100Meters.sMeter);
|
||||
binding.xieguSwrMeterRulerView.setValue(x6100Meters.swr);
|
||||
binding.xieguPowerMeterRulerView.setValue(x6100Meters.power);
|
||||
binding.xieguAlcMeterRulerView.setValue(x6100Meters.alc);
|
||||
binding.xieguVoltMeterRulerView.setValue(x6100Meters.volt);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
binding.xieguSMeterRulerView.initVal(-130f, -30f, 10f, 9, 3);
|
||||
binding.xieguSMeterRulerView.initLabels("S.Po", "dBm"
|
||||
, new String[]{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}
|
||||
, new String[]{"20", "40", ""});
|
||||
binding.xieguSwrMeterRulerView.initVal(1f, 3f, 8f, 4, 4);
|
||||
binding.xieguSwrMeterRulerView.initLabels("SWR", ""
|
||||
, new String[]{"1", "1.5", "2", "2.5", "3"}
|
||||
, new String[]{"", "", "", "∞"});
|
||||
|
||||
binding.xieguAlcMeterRulerView.initVal(0f, 100f, 100f, 6, 4);
|
||||
binding.xieguAlcMeterRulerView.initLabels("ALC", ""
|
||||
, new String[]{"0", "10", "20","30","40","50","60"}
|
||||
, new String[]{"70", "80", "90","100"});
|
||||
|
||||
binding.xieguPowerMeterRulerView.initVal(-0f, 5f, 10f, 5, 5);
|
||||
binding.xieguPowerMeterRulerView.initLabels("PWR", "W"
|
||||
, new String[]{"0", "1", "2", "3", "4", "5"}
|
||||
, new String[]{"6", "7", "8", "9", "10"});
|
||||
binding.xieguVoltMeterRulerView.initVal(-0f, 14f, 16f, 8, 2);
|
||||
binding.xieguVoltMeterRulerView.initLabels("Volt", "V"
|
||||
, new String[]{"0", "2", "4", "6", "8", "10","12","13","14"}
|
||||
, new String[]{ "15", "16"});
|
||||
|
||||
binding.xieguMaxPwrProgress.setValueColor(getContext().getColor(R.color.power_progress_value));
|
||||
binding.xieguMaxPwrProgress.setRadarColor(getContext().getColor(R.color.power_progress_radar_value));
|
||||
binding.xieguMaxPwrProgress.setAlarmValue(0.99f);
|
||||
|
||||
|
||||
connector.mutableMaxTxPower.observe(getViewLifecycleOwner(), new Observer<Float>() {
|
||||
@Override
|
||||
public void onChanged(Float aFloat) {
|
||||
binding.xieguMaxPwrProgress.setPercent( aFloat.floatValue() / 10f);
|
||||
binding.xiegumaxPowerSeekBar.setOnSeekBarChangeListener(null);
|
||||
binding.xiegumaxPowerSeekBar.setProgress(Math.round(aFloat.floatValue()*10));
|
||||
binding.xiegumaxPowerSeekBar.setOnSeekBarChangeListener(onSeekBarChangeListener);
|
||||
binding.xieguMaxTxPowertextView.setText(String.format(
|
||||
GeneralVariables.getStringFromResource(R.string.flex_max_tx_power), Math.round(aFloat.floatValue())));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
binding.xieguSMeterRulerView.setValue(-60f);
|
||||
binding.xieguSwrMeterRulerView.setValue(1.1f);
|
||||
binding.xieguAlcMeterRulerView.setValue(30f);
|
||||
binding.xieguPowerMeterRulerView.setValue(8f);
|
||||
binding.xieguVoltMeterRulerView.setValue(12.5f);
|
||||
|
||||
|
||||
|
||||
binding.xieguAtuOnButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
xieguRadio.commandAtuOn();
|
||||
}
|
||||
});
|
||||
binding.xieguAtuOffButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
xieguRadio.commandAtuOff();
|
||||
}
|
||||
});
|
||||
binding.xieguStartAtuButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
xieguRadio.commandAtuStart();
|
||||
}
|
||||
});
|
||||
|
||||
onSeekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
|
||||
binding.xieguMaxPwrProgress.setPercent(i * 1.0f / 100);
|
||||
|
||||
connector.setMaxTXPower(i/10);
|
||||
binding.xieguMaxTxPowertextView.setText(String.format(
|
||||
GeneralVariables.getStringFromResource(R.string.flex_max_tx_power), i/10));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
};
|
||||
binding.xiegumaxPowerSeekBar.setOnSeekBarChangeListener( onSeekBarChangeListener);
|
||||
|
||||
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
}
|
|
@ -12,18 +12,19 @@ public class FT8Resample {
|
|||
}
|
||||
|
||||
public static native short[] get16Resample16(short[] inputData, int inputRate
|
||||
, int outputRate);
|
||||
, int outputRate,int channels);
|
||||
|
||||
public static native float[] get32Resample16(short[] inputData, int inputRate
|
||||
, int outputRate);
|
||||
, int outputRate,int channels);
|
||||
public static native short[] get16Resample32(float[] inputData, int inputRate
|
||||
, int outputRate);
|
||||
, int outputRate,int channels);
|
||||
public static native float[] get32Resample32(float[] inputData, int inputRate
|
||||
, int outputRate);
|
||||
, int outputRate,int channels);
|
||||
|
||||
public static native byte[] get8Resample16(short[] inputData, int inputRate
|
||||
, int outputRate);
|
||||
, int outputRate,int channels);
|
||||
|
||||
public static native byte[] get8Resample32(float[] inputData, int inputRate
|
||||
, int outputRate);
|
||||
, int outputRate,int channels);
|
||||
|
||||
}
|
||||
|
|
|
@ -209,22 +209,24 @@ public class HamRecorder {
|
|||
onHamRecord = new OnHamRecord() {
|
||||
@Override
|
||||
public void OnReceiveData(float[] data, int size) {
|
||||
for (int i = 0; (i < size) && (dataCount < voiceData.length); i++) {
|
||||
voiceData[dataCount] = data[i];//把录音缓冲区的数据搬运到本监听器中来
|
||||
dataCount++;
|
||||
}
|
||||
if (dataCount >= (voiceData.length)) {//当数据量达到所需要的。发起回调。
|
||||
// new Thread(new Runnable() {//以新的线程运行,防止占用过多的录音时间。
|
||||
// @Override
|
||||
// public void run() {
|
||||
onGetVoiceDataDone.onGetDone(voiceData);
|
||||
// }
|
||||
// }).start();
|
||||
int remainingSize = size+dataCount-voiceData.length;//如果大于0,就是剩余的数据量,
|
||||
|
||||
for (int i = 0; (i < size) && (dataCount < voiceData.length); i++) {
|
||||
voiceData[dataCount] = data[i];//把录音缓冲区的数据搬运到本监听器中来
|
||||
dataCount++;
|
||||
}
|
||||
|
||||
if (dataCount >= (voiceData.length)) {//当数据量达到所需要的。发起回调。
|
||||
onGetVoiceDataDone.onGetDone(voiceData);
|
||||
if (afterDoneRemove) {//如果是一次性的获取数据,则在录音对象中的监听列表中删除此监听回调。
|
||||
hamRecorder.deleteVoiceDataMonitor(voiceDataMonitor);
|
||||
} else {
|
||||
dataCount = 0;//如果是循环录音,则复位计数器。
|
||||
if (remainingSize>0) {//把剩余的数据补发到后续事件上
|
||||
float[] remainingData = new float[remainingSize];
|
||||
System.arraycopy(data, size - remainingSize, remainingData, 0, remainingSize);
|
||||
OnReceiveData(remainingData,remainingSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
package com.bg7yoz.ft8cn.x6100;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.bg7yoz.ft8cn.GeneralVariables;
|
||||
import com.bg7yoz.ft8cn.R;
|
||||
import com.bg7yoz.ft8cn.flex.VITA;
|
||||
|
||||
public class X6100Meters {
|
||||
private final String TAG="X6100Meters";
|
||||
public float sMeter;
|
||||
public float power;
|
||||
public float swr;
|
||||
public float alc;
|
||||
public float volt;
|
||||
public float max_power;
|
||||
public short tx_volume;
|
||||
public short af_level;//电台的音量
|
||||
|
||||
public X6100Meters() {
|
||||
|
||||
}
|
||||
public synchronized void update(byte[] meterData){
|
||||
for (int i = 0; i < meterData.length/4; i++) {
|
||||
short index = VITA.readShortDataBigEnd(meterData,i*4);
|
||||
short value= VITA.readShortDataBigEnd(meterData,i*4+2);
|
||||
setValues(index,value);
|
||||
}
|
||||
}
|
||||
|
||||
private void setValues(short index,short value){
|
||||
switch (index){
|
||||
case 0://sMeter
|
||||
sMeter = (100.0f/255.0f)*value -130;
|
||||
//Log.e(TAG,String.format("s.Meter:%.1f",sMeter));
|
||||
break;
|
||||
case 1://power
|
||||
power = (25/255f)*value*10;
|
||||
//Log.e(TAG,String.format("power:%.1f",power));
|
||||
break;
|
||||
case 2://swr
|
||||
swr= value *1.0f;
|
||||
//Log.e(TAG,String.format("swr:%d",value));
|
||||
//ToastMessage.show(String.format("swr:%d",value));
|
||||
break;
|
||||
case 3://alc
|
||||
alc = (100.0f/255f)*value;
|
||||
//ToastMessage.show(String.format("alc:%d",value));
|
||||
//Log.e(TAG,String.format("alc:%.1f",alc));
|
||||
break;
|
||||
case 4:
|
||||
volt = value *1.0f;
|
||||
//Log.e(TAG,String.format("volt:%.1f",volt));
|
||||
break;
|
||||
case 5:
|
||||
max_power = value /25.5f;
|
||||
//GeneralVariables.flexMaxRfPower=(int)max_power;
|
||||
//Log.e(TAG,String.format("max_power:%.1f,val:%d",max_power,value));
|
||||
break;
|
||||
case 6:
|
||||
tx_volume = value;
|
||||
//Log.e(TAG,String.format("tx_volume:%d",tx_volume));
|
||||
break;
|
||||
case 7:
|
||||
af_level = value;
|
||||
//Log.e(TAG,String.format("tx_volume:%d",tx_volume));
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
//return String.format("S.Meter: %.1f dBm\nSWR: %s\nALC: %.1f\nVolt: %.1fV\nTX power: %.1f W\nMax tx power: %.1f\nTX volume:%d%%"
|
||||
return String.format(GeneralVariables.getStringFromResource(R.string.xiegu_meter_info)
|
||||
,sMeter
|
||||
,swr > 8 ? "∞" : String.format("%.1f",swr)
|
||||
,alc
|
||||
,volt
|
||||
,power
|
||||
,max_power
|
||||
,tx_volume
|
||||
);
|
||||
//"信号强度: %.1f dBm\n驻波: %s\nALC: %.1f\n电压: %.1fV\n发射功率: %.1f W\n最大发射功率: %.1f\n发射音量:%d%%"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,975 @@
|
|||
package com.bg7yoz.ft8cn.x6100;
|
||||
|
||||
import static com.bg7yoz.ft8cn.flex.VITA.XIEGU_METER_CLASS_ID;
|
||||
import static com.bg7yoz.ft8cn.flex.VITA.XIEGU_METER_Stream_Id;
|
||||
import static com.bg7yoz.ft8cn.flex.VITA.XIEGU_PING_CLASS_ID;
|
||||
import static com.bg7yoz.ft8cn.flex.VITA.XIEGU_PING_Stream_Id;
|
||||
import static com.bg7yoz.ft8cn.flex.VITA.byteToStr;
|
||||
import static com.bg7yoz.ft8cn.flex.VITA.readShortData;
|
||||
import static com.bg7yoz.ft8cn.x6100.X6100Radio.XieguResponseStyle.RESPONSE;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.AudioFormat;
|
||||
import android.media.AudioTrack;
|
||||
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.FlexRadio;
|
||||
import com.bg7yoz.ft8cn.flex.FlexResponseStyle;
|
||||
import com.bg7yoz.ft8cn.flex.RadioTcpClient;
|
||||
import com.bg7yoz.ft8cn.flex.RadioUdpClient;
|
||||
import com.bg7yoz.ft8cn.flex.VITA;
|
||||
import com.bg7yoz.ft8cn.flex.VitaPacketType;
|
||||
import com.bg7yoz.ft8cn.flex.VitaTSF;
|
||||
import com.bg7yoz.ft8cn.flex.VitaTSI;
|
||||
import com.bg7yoz.ft8cn.rigs.BaseRig;
|
||||
import com.bg7yoz.ft8cn.ui.ToastMessage;
|
||||
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
public class X6100Radio {
|
||||
public enum XieguCommand {
|
||||
UNKNOW,//未知指令
|
||||
AUDIO,//音频指令
|
||||
STREAM,//数据流指令
|
||||
SUB,//订阅仪表
|
||||
UNSUB,//取消订阅仪表
|
||||
A91,//ft8符号
|
||||
ATU,//自动天调
|
||||
TUNE,//设置频率
|
||||
MODE,//操作模式
|
||||
PTT,//PTT操作
|
||||
SET//设置操作
|
||||
}
|
||||
|
||||
public enum XieguResponseStyle {
|
||||
STATUS,//状态信息,S+HANDLE
|
||||
RESPONSE,//命令的响应,R+客户端命令序列号
|
||||
HANDLE,//电台给定的句柄,H+句柄(32位的16进制表示)
|
||||
VERSION,//版本号,V+版本号
|
||||
COMMAND,//发送命令,C+序列号|命令
|
||||
UNKNOW//未知的回复类型
|
||||
}
|
||||
|
||||
private static final String TAG = "X6100Radio";
|
||||
private static int lossCount = 0;
|
||||
private static int currentCount = -1;
|
||||
|
||||
private String modelName;//电台型号
|
||||
private String version;//电台版本号
|
||||
private String rig_ip;//电台的IP
|
||||
private String mac;//mac地址
|
||||
public boolean isPttOn = false;
|
||||
private int control_port = 7002;//电台的控制端口
|
||||
private int stream_port = 7003;//电台端流数据的端口
|
||||
private int discovery_port = 7001;//发现协议的端口
|
||||
private long lastSeen;//最后一次消息的时间
|
||||
private boolean isAvailable = true;//电台是不是有效
|
||||
private final StringBuilder buffer = new StringBuilder();//指令的缓存
|
||||
private final RadioTcpClient tcpClient = new RadioTcpClient();
|
||||
private RadioUdpClient streamClient;
|
||||
private int commandSeq = 1;//指令的序列
|
||||
private XieguCommand xieguCommand;
|
||||
private int handle = 0;
|
||||
private String commandStr;
|
||||
private int frames = 768;//每个周期的帧数
|
||||
private int period = 64;//每个周期的时长,毫秒
|
||||
|
||||
|
||||
//************************事件处理接口*******************************
|
||||
private OnReceiveDataListener onReceiveDataListener;//当前接收到的数据事件
|
||||
private OnTcpConnectStatus onTcpConnectStatus;//当TCP连接状态变化的事件
|
||||
private OnReceiveStreamData onReceiveStreamData;//当接收到流数据后的处理事件
|
||||
private OnCommandListener onCommandListener;//触发命令事件
|
||||
private OnStatusListener onStatusListener;//触发状态事件
|
||||
//*****************************************************************
|
||||
private AudioTrack audioTrack = null;
|
||||
|
||||
|
||||
///******************用于仪表信息显示*************
|
||||
public MutableLiveData<Long> mutablePing = new MutableLiveData<>();//ping值
|
||||
public MutableLiveData<Integer> mutableLossPackets = new MutableLiveData<>();//丢失的包数量
|
||||
public MutableLiveData<X6100Meters> mutableMeters = new MutableLiveData<>();
|
||||
private X6100Meters meters = new X6100Meters();
|
||||
|
||||
private boolean swrAlert = false;
|
||||
private boolean alcAlert = false;
|
||||
|
||||
|
||||
private Timer pingTimer = new Timer();
|
||||
|
||||
private TimerTask pingTask() {
|
||||
return new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
try {
|
||||
if (!streamClient.isActivated() || !isConnect()) {
|
||||
pingTimer.cancel();
|
||||
pingTimer.purge();
|
||||
pingTimer = null;
|
||||
return;
|
||||
}
|
||||
VITA vita = new VITA(VitaPacketType.EXT_DATA_WITH_STREAM
|
||||
, VitaTSI.TSI_OTHER
|
||||
, VitaTSF.TSF_REALTIME
|
||||
, 0
|
||||
, XIEGU_PING_Stream_Id
|
||||
, XIEGU_PING_CLASS_ID);
|
||||
|
||||
vita.packetCount = 0;
|
||||
vita.packetSize = 7;
|
||||
vita.integerTimestamp = 0;//0是发送包,1是接收包
|
||||
vita.fracTimeStamp = System.currentTimeMillis();
|
||||
streamClient.sendData(vita.pingDataToVita(), rig_ip, stream_port);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "ping timer error:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新最后看到的时间
|
||||
*/
|
||||
public void updateLastSeen() {
|
||||
this.lastSeen = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public X6100Radio() {
|
||||
updateLastSeen();
|
||||
}
|
||||
|
||||
public X6100Radio(String s, String ip) {
|
||||
mutableLossPackets.postValue(0);
|
||||
update(s, ip);
|
||||
}
|
||||
|
||||
public void update(String discoverStr, String ip) {
|
||||
Log.d(TAG, discoverStr);
|
||||
rig_ip = ip;
|
||||
|
||||
String[] paras = discoverStr.replace("\0", " ").split(" ");
|
||||
version = getParameterStr(paras, "ft8cn_server_version");
|
||||
modelName = getParameterStr(paras, "model");
|
||||
mac = getParameterStr(paras, "mac");
|
||||
control_port = getParameterInt(paras, "control_port");
|
||||
stream_port = getParameterInt(paras, "stream_port");
|
||||
discovery_port = getParameterInt(paras, "discovery_port");
|
||||
|
||||
updateLastSeen();
|
||||
}
|
||||
|
||||
/**
|
||||
* 到参数列表中找指定的字符类型参数
|
||||
*
|
||||
* @param parameters 参数列表
|
||||
* @param prefix 参数名前缀
|
||||
* @return 参数
|
||||
*/
|
||||
private String getParameterStr(String[] parameters, String prefix) {
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
if (parameters[i].toLowerCase().startsWith(prefix.toLowerCase() + "=")) {
|
||||
return parameters[i].substring(prefix.length() + 1);
|
||||
}
|
||||
}
|
||||
//如果没找到,返回空字符串
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 到参数列表中找指定的int类型参数
|
||||
*
|
||||
* @param parameters 参数列表
|
||||
* @param prefix 参数名前缀
|
||||
* @return 参数
|
||||
*/
|
||||
private int getParameterInt(String[] parameters, String prefix) {
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
if (parameters[i].toLowerCase().startsWith(prefix.toLowerCase() + "=")) {
|
||||
try {
|
||||
return Integer.parseInt(parameters[i].substring(prefix.length() + 1));
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "getParameterInt exception: " + e.getMessage());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
//如果没找到,返回0
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是不是 刚刚 离线,离线条件:5秒内没有收到电台的广播数据包
|
||||
*
|
||||
* @return 是否
|
||||
*/
|
||||
public boolean isInvalidNow() {
|
||||
if (isAvailable) {//如果标记在线,而大于5秒的时间没有收到数据包,就视为刚刚离线。
|
||||
isAvailable = System.currentTimeMillis() - lastSeen < 1000 * 5;//小于5秒,就视为在线
|
||||
return !isAvailable;
|
||||
} else {//如果已经标记不在线了,就不是刚刚离线的。
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public String getModelName() {
|
||||
return modelName;
|
||||
}
|
||||
|
||||
public void setModelName(String modelName) {
|
||||
this.modelName = modelName;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public String getRig_ip() {
|
||||
return rig_ip;
|
||||
}
|
||||
|
||||
public void setRig_ip(String rig_ip) {
|
||||
this.rig_ip = rig_ip;
|
||||
}
|
||||
|
||||
public String getMac() {
|
||||
return mac;
|
||||
}
|
||||
|
||||
public boolean isEqual(String madAddress) {
|
||||
return this.mac.equalsIgnoreCase(madAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接到控制电台
|
||||
*/
|
||||
public void connect() {
|
||||
this.connect(this.rig_ip, this.control_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接控制到电台,TCP
|
||||
*
|
||||
* @param ip 地址
|
||||
* @param port 端口
|
||||
*/
|
||||
public void connect(String ip, int port) {
|
||||
if (tcpClient.isConnect()) {
|
||||
tcpClient.disconnect();
|
||||
}
|
||||
//Tcp连接触发的事件
|
||||
tcpClient.setOnDataReceiveListener(new RadioTcpClient.OnDataReceiveListener() {
|
||||
@Override
|
||||
public void onConnectSuccess() {
|
||||
if (onTcpConnectStatus != null) {
|
||||
onTcpConnectStatus.onConnectSuccess(tcpClient);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectFail() {
|
||||
if (onTcpConnectStatus != null) {
|
||||
onTcpConnectStatus.onConnectFail(tcpClient);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataReceive(byte[] buffer) {
|
||||
if (onReceiveDataListener != null) {//此处把数据传递给XieGu6100NetRig
|
||||
onReceiveDataListener.onDataReceive(buffer);
|
||||
}
|
||||
onReceiveData(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionClosed() {
|
||||
tcpClient.disconnect();
|
||||
ToastMessage.show(GeneralVariables.getStringFromResource(R.string.tcp_connect_closed));
|
||||
if (onTcpConnectStatus != null){
|
||||
onTcpConnectStatus.onConnectionClosed(tcpClient);
|
||||
}
|
||||
}
|
||||
});
|
||||
clearBufferData();//清除一下缓存的指令数据
|
||||
tcpClient.connect(ip, port);//连接TCP
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭接收数据流的端口
|
||||
*/
|
||||
public synchronized void closeStreamPort() {
|
||||
if (streamClient != null) {
|
||||
if (streamClient.isActivated()) {
|
||||
try {
|
||||
streamClient.setActivated(false);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
streamClient = null;
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
public synchronized void commandAtuOn() {
|
||||
sendCommand(XieguCommand.ATU, "atu on");
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
public synchronized void commandAtuOff() {
|
||||
sendCommand(XieguCommand.ATU, "atu off");
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
public synchronized void commandAtuStart() {
|
||||
sendCommand(XieguCommand.ATU, "atu start");
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
public synchronized void commandOpenStream() {
|
||||
sendCommand(XieguCommand.STREAM, String.format("stream on %d", stream_port));
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
public synchronized void commandSendA91(byte[] a91, float vol, float freq) {
|
||||
sendCommand(XieguCommand.A91, String.format("a91 %.2f %.0f %s", vol, freq, BaseRig.byteToStr(a91)));
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
public synchronized void commandGetAudioInfo() {
|
||||
sendCommand(XieguCommand.AUDIO, "audio get all");
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
public synchronized void commandGetStreamInfo() {
|
||||
sendCommand(XieguCommand.STREAM, "stream get");
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
public synchronized void commandSubAllMeter() {
|
||||
sendCommand(XieguCommand.SUB, "sub all");
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
public synchronized void commandSubGetMeter() {
|
||||
sendCommand(XieguCommand.SUB, "sub get");
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
public synchronized void commandUnubMeter() {
|
||||
sendCommand(XieguCommand.UNSUB, "unsub");
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
public synchronized void commandTuneFreq(long freq) {
|
||||
sendCommand(XieguCommand.TUNE, String.format("tune %d", freq));
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
public synchronized void commandSetMode(String mode, int filter) {
|
||||
sendCommand(XieguCommand.TUNE, String.format("mode %s %d", mode, filter));
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
public synchronized void commandSetTxPower(int power) {
|
||||
sendCommand(XieguCommand.SET, String.format("set tx %d", power));
|
||||
}
|
||||
@SuppressLint("DefaultLocale")
|
||||
public synchronized void commandSetTxVol(int volume) {
|
||||
sendCommand(XieguCommand.SET, String.format("set tx_vol %d", volume));
|
||||
}
|
||||
|
||||
private void showAlert() {
|
||||
Log.e(TAG, String.format("ALC:%f", meters.alc));
|
||||
if (meters.swr >= 5) {
|
||||
if (!swrAlert) {
|
||||
swrAlert = true;
|
||||
ToastMessage.show(GeneralVariables.getStringFromResource(R.string.swr_high_alert));
|
||||
}
|
||||
} else {
|
||||
swrAlert = false;
|
||||
}
|
||||
|
||||
|
||||
if (meters.alc > 50 || meters.alc < 20) {
|
||||
if (!alcAlert) {
|
||||
alcAlert = true;
|
||||
if (meters.alc > 50) {
|
||||
ToastMessage.show(GeneralVariables.getStringFromResource(R.string.alc_high_alert));
|
||||
} else {
|
||||
ToastMessage.show(GeneralVariables.getStringFromResource(R.string.alc_low_alert));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
alcAlert = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开接收数据流的端口
|
||||
*/
|
||||
public void openStreamPort() {
|
||||
if (streamClient != null) {
|
||||
if (streamClient.isActivated()) {
|
||||
try {
|
||||
streamClient.setActivated(false);
|
||||
} catch (Exception e) {
|
||||
ToastMessage.show(e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RadioUdpClient.OnUdpEvents onUdpEvents = new RadioUdpClient.OnUdpEvents() {
|
||||
@SuppressLint("DefaultLocale")
|
||||
@Override
|
||||
public void OnReceiveData(DatagramSocket socket, DatagramPacket packet, byte[] data) {
|
||||
VITA vita = new VITA(data);
|
||||
if (vita.classId64 == VITA.XIEGU_AUDIO_CLASS_ID) {//音频数据
|
||||
|
||||
//判断数据包丢失情况
|
||||
int temp = lossCount;
|
||||
if (currentCount <= -1) {
|
||||
currentCount = vita.packetCount;
|
||||
}
|
||||
if (currentCount > vita.packetCount) {
|
||||
lossCount = lossCount + vita.packetCount + 16 - currentCount - 1;
|
||||
} else if (currentCount < vita.packetCount) {
|
||||
lossCount = lossCount + vita.packetCount - currentCount - 1;
|
||||
}
|
||||
currentCount = vita.packetCount;
|
||||
if (lossCount > temp) {
|
||||
Log.e(TAG, String.format("丢包数量:%d", lossCount));
|
||||
|
||||
for (int i = 0; i < (lossCount - temp); i++) {
|
||||
Log.d(TAG, String.format("补发数据,%d,size:%d", i, vita.payload.length));
|
||||
sendReceivedAudio(vita.payload);//把当前的数据补发给录音对象
|
||||
}
|
||||
mutableLossPackets.postValue(lossCount);
|
||||
}
|
||||
sendReceivedAudio(vita.payload);//把音频发给录音对象
|
||||
playReceiveAudio(vita.payload);//发送当前的音频数据
|
||||
} else if (vita.classId64 == XIEGU_PING_CLASS_ID//ping数据
|
||||
&& vita.streamId == XIEGU_PING_Stream_Id
|
||||
&& vita.integerTimestamp == 1) {//ping的回包
|
||||
mutablePing.postValue(System.currentTimeMillis() - vita.fracTimeStamp);
|
||||
} else if (vita.classId64 == XIEGU_METER_CLASS_ID//仪表数据
|
||||
&& vita.streamId == XIEGU_METER_Stream_Id) {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
meters.update(vita.payload);
|
||||
mutableMeters.postValue(meters);
|
||||
if (isPttOn) {
|
||||
showAlert();
|
||||
} else {
|
||||
alcAlert = false;
|
||||
swrAlert = false;
|
||||
}
|
||||
if (onReceiveStreamData != null){
|
||||
onReceiveStreamData.onReceiveMeter(meters);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
//此处要确定stream的udp端口
|
||||
streamClient = new RadioUdpClient(stream_port);
|
||||
streamClient.setOnUdpEvents(onUdpEvents);
|
||||
try {
|
||||
streamClient.setActivated(true);
|
||||
pingTimer.schedule(pingTask(), 1000, 1000);//启动ping计时器
|
||||
} catch (SocketException e) {
|
||||
ToastMessage.show(e.getMessage());
|
||||
e.printStackTrace();
|
||||
Log.d(TAG, "streamClient: " + e.getMessage());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 当接收到音频数据后,发送给录音对象的操作
|
||||
*
|
||||
* @param data 音频数据
|
||||
*/
|
||||
private void sendReceivedAudio(byte[] data) {
|
||||
if (onReceiveStreamData != null) {
|
||||
onReceiveStreamData.onReceiveAudio(data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 当接收到音频数据时的处理
|
||||
*
|
||||
* @param data 音频数据
|
||||
*/
|
||||
private void playReceiveAudio(byte[] data) {
|
||||
if (audioTrack != null) {//如果音频播放已经打开,就写音频流数据
|
||||
audioTrack.write(data, 0, data.length, AudioTrack.WRITE_NON_BLOCKING);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 断开与电台的连接
|
||||
*/
|
||||
public synchronized void disConnect() {
|
||||
if (tcpClient.isConnect()) {
|
||||
tcpClient.disconnect();
|
||||
}
|
||||
if (pingTimer != null) {
|
||||
pingTimer.cancel();
|
||||
pingTimer.purge();
|
||||
pingTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 电台是否连接
|
||||
*
|
||||
* @return 是否
|
||||
*/
|
||||
public boolean isConnect() {
|
||||
return tcpClient.isConnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭音频
|
||||
*/
|
||||
public void closeAudio() {
|
||||
if (audioTrack != null) {
|
||||
audioTrack.stop();
|
||||
audioTrack = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 当接收到数据时触发的事件,此处是TCP连接得到的数据
|
||||
*
|
||||
* @param data 数据
|
||||
*/
|
||||
private void onReceiveData(byte[] data) {
|
||||
|
||||
if (data.length > 4) {//判断是不是老式的icom指令
|
||||
if ((data[0] == (byte) 0xfe) && (data[1] == (byte) 0xfe)
|
||||
&& ((data[2] == (byte) 0xe0) || data[3] == (byte) 0xe0)) {
|
||||
clearBufferData();
|
||||
return;
|
||||
}
|
||||
}
|
||||
String s = new String(data);
|
||||
if (!s.contains("\n")) {//不包含换行符,说明命令行没有接受完。
|
||||
buffer.append(s);
|
||||
} else {//说明已经有命令行了。可能不止一个哦。在此部分要触发OnReceiveLine
|
||||
String[] commands = s.split("\n");
|
||||
if (commands.length > 0) {//把收到数据的第一行,追加到之前接收的命令数据上
|
||||
buffer.append(commands[0]);
|
||||
}
|
||||
|
||||
//先把缓存中的数据触发出来
|
||||
doReceiveLineEvent(buffer.toString());
|
||||
clearBufferData();
|
||||
//从第二行开始触发,最后一行不触发,最后一行要看是不是换行结尾
|
||||
for (int i = 1; i < commands.length - 1; i++) {
|
||||
doReceiveLineEvent(commands[i]);
|
||||
}
|
||||
|
||||
if (commands.length > 1) {//当数据是多行的时候,最后一行的处理
|
||||
if (s.endsWith("\n")) {//如果是以换行结尾,或者缓冲区没满(接收完全了),就触发事件
|
||||
doReceiveLineEvent(commands[commands.length - 1]);
|
||||
} else {//如果不是以换行结尾,说明指令没有接收完全
|
||||
buffer.append(commands[commands.length - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 当接收到数据行时,触发的事件。可以触发两种事件:
|
||||
* 1.行数据事件onReceiveLineListener;
|
||||
* 2.命令事件onCommandListener。
|
||||
* <p>
|
||||
* //* @param line 数据行
|
||||
*/
|
||||
private void doReceiveLineEvent(String line) {
|
||||
|
||||
XieguResponse response = new XieguResponse(line);
|
||||
//更新一下句柄
|
||||
switch (response.responseStyle) {
|
||||
case VERSION:
|
||||
this.version = response.head.substring(1);
|
||||
break;
|
||||
case HANDLE:
|
||||
this.handle = Integer.parseInt(response.head.substring(1), 16);
|
||||
break;
|
||||
case RESPONSE:
|
||||
if (XieguCommand.AUDIO == response.xieguCommand) {//是音频指令回复的信息
|
||||
setAudioInfo(response.resultContent);
|
||||
}
|
||||
if (onCommandListener != null) {
|
||||
onCommandListener.onResponse(response);
|
||||
}
|
||||
break;
|
||||
case STATUS:
|
||||
|
||||
if (response.resultCode == 0) {//说明是电台状态变化了
|
||||
String status[] = response.resultContent.split(" ");
|
||||
for (int i = 0; i < status.length; i++) {//找出ptt的状体,设置ptt
|
||||
if (status[i].startsWith("ptt")) {//判断PTT
|
||||
String temp[] = status[i].split("=");
|
||||
isPttOn = temp[1].equalsIgnoreCase("on");
|
||||
}
|
||||
|
||||
if (status[i].startsWith("play_volume")) {//判断PTT
|
||||
String temp[] = status[i].split("=");
|
||||
float vol = Integer.parseInt(temp[1].trim())*1.0f/100f;
|
||||
GeneralVariables.volumePercent = vol;
|
||||
GeneralVariables.mutableVolumePercent.postValue(vol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (onStatusListener != null) {
|
||||
onStatusListener.onStatus(response);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取电台的音频信息
|
||||
*
|
||||
* @param result 返回信息
|
||||
*/
|
||||
private void setAudioInfo(String result) {
|
||||
String[] keys = result.split(" ");
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
String[] val = keys[i].split("=");
|
||||
if (val[0].equalsIgnoreCase("period")) period = Integer.parseInt(val[1]) / 1000;
|
||||
if (val[0].equalsIgnoreCase("frames")) frames = Integer.parseInt(val[1]);
|
||||
}
|
||||
Log.d(TAG, String.format("set audio para:frames=%d,period=%d", frames, period));
|
||||
}
|
||||
|
||||
public synchronized void sendData(byte[] data) {
|
||||
tcpClient.sendByte(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 制作命令,命令序号规则:后3位是命令的种类,序号除1000,是命令的真正序号
|
||||
*
|
||||
* @param command 命令的种类
|
||||
* @param cmdContent 命令的具体内容
|
||||
*/
|
||||
@SuppressLint("DefaultLocale")
|
||||
public void sendCommand(XieguCommand command, String cmdContent) {
|
||||
if (tcpClient.isConnect()) {
|
||||
commandSeq++;
|
||||
xieguCommand = command;
|
||||
commandStr = String.format("C%05d%03d|%s\n", commandSeq, command.ordinal()
|
||||
, cmdContent);
|
||||
tcpClient.sendByte(commandStr.getBytes());
|
||||
Log.d(TAG, "sendCommand: " + commandStr);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void commandPTTOnOff(boolean on) {
|
||||
if (on) {
|
||||
sendCommand(XieguCommand.PTT, "ptt on");
|
||||
} else {
|
||||
sendCommand(XieguCommand.PTT, "ptt off");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发射的采样率为12000采样率,单声道,16位
|
||||
*
|
||||
* @param data 音频
|
||||
*/
|
||||
public void sendWaveData(float[] data) {
|
||||
Log.d(TAG, String.format("send wav data,len:%d....", data.length));
|
||||
short[] temp = new short[data.length];
|
||||
//传递过来的音频是LPCM,32 float,12000Hz
|
||||
//x6100的音频格式是LPCM 16 Int,12000Hz
|
||||
//要做一下浮点到16位int的转换
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
float x = data[i];
|
||||
if (x > 1.0)
|
||||
x = 1.0f;
|
||||
else if (x < -1.0)
|
||||
x = -1.0f;
|
||||
temp[i] = (short) (x * 32767.0);
|
||||
}
|
||||
short[] payload = new short[frames];
|
||||
|
||||
VITA vita = new VITA(VitaPacketType.EXT_DATA_WITH_STREAM
|
||||
, VitaTSI.TSI_OTHER
|
||||
, VitaTSF.TSF_SAMPLE_COUNT
|
||||
, 0
|
||||
, 0x84000001
|
||||
, 0x584945475500A1L);
|
||||
|
||||
vita.packetCount = 0;
|
||||
vita.integerTimestamp = 0;
|
||||
vita.fracTimeStamp = payload.length * 2L;
|
||||
|
||||
|
||||
try {
|
||||
int count = 0;
|
||||
int a = 0;
|
||||
while (count < temp.length) {
|
||||
long now = System.currentTimeMillis();//获取当前时间
|
||||
Arrays.fill(payload, (short) 0);//数组清零
|
||||
|
||||
if (!isPttOn) break;
|
||||
if (data.length - count > frames) {
|
||||
System.arraycopy(temp, count, payload, 0, frames);
|
||||
count = count + frames;
|
||||
} else {
|
||||
System.arraycopy(temp, count, payload, 0, temp.length - count);
|
||||
count = temp.length;
|
||||
}
|
||||
streamClient.sendData(vita.audioShortDataToVita(vita.packetCount, payload), rig_ip, stream_port);
|
||||
while (isPttOn) {
|
||||
if (System.currentTimeMillis() - now >= period) {//64毫秒一个周期
|
||||
break;
|
||||
}
|
||||
}
|
||||
a++;
|
||||
}
|
||||
|
||||
|
||||
} catch (UnknownHostException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空缓存数据
|
||||
*/
|
||||
private void clearBufferData() {
|
||||
buffer.setLength(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开音频,流方式。当收到音频流的时候,播放数据
|
||||
*/
|
||||
public void openAudio() {
|
||||
AudioAttributes attributes = new AudioAttributes.Builder()
|
||||
.setUsage(AudioAttributes.USAGE_MEDIA)
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
|
||||
.build();
|
||||
AudioFormat myFormat = new AudioFormat.Builder().setSampleRate(12000)
|
||||
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
|
||||
.setChannelMask(AudioFormat.CHANNEL_OUT_MONO).build();
|
||||
int mySession = 0;
|
||||
audioTrack = new AudioTrack(attributes, myFormat
|
||||
//, 12000 * 4, AudioTrack.MODE_STREAM
|
||||
, 768 * 2 * 4, AudioTrack.MODE_STREAM//x6100一个声音周期是64毫秒,共计768*2个字节
|
||||
, mySession);
|
||||
audioTrack.play();
|
||||
}
|
||||
|
||||
public OnTcpConnectStatus getOnTcpConnectStatus() {
|
||||
return onTcpConnectStatus;
|
||||
}
|
||||
|
||||
public void setOnTcpConnectStatus(OnTcpConnectStatus onTcpConnectStatus) {
|
||||
this.onTcpConnectStatus = onTcpConnectStatus;
|
||||
}
|
||||
|
||||
public OnReceiveStreamData getOnReceiveStreamData() {
|
||||
return onReceiveStreamData;
|
||||
}
|
||||
|
||||
public void setOnReceiveStreamData(OnReceiveStreamData onReceiveStreamData) {
|
||||
this.onReceiveStreamData = onReceiveStreamData;
|
||||
}
|
||||
|
||||
public OnStatusListener getOnStatusListener() {
|
||||
return onStatusListener;
|
||||
}
|
||||
|
||||
public void setOnStatusListener(OnStatusListener onStatusListener) {
|
||||
this.onStatusListener = onStatusListener;
|
||||
}
|
||||
|
||||
public OnCommandListener getOnCommandListener() {
|
||||
return onCommandListener;
|
||||
}
|
||||
|
||||
public void setOnCommandListener(OnCommandListener onCommandListener) {
|
||||
this.onCommandListener = onCommandListener;
|
||||
}
|
||||
|
||||
public OnReceiveDataListener getOnReceiveDataListener() {
|
||||
return onReceiveDataListener;
|
||||
}
|
||||
|
||||
public void setOnReceiveDataListener(OnReceiveDataListener onReceiveDataListener) {
|
||||
this.onReceiveDataListener = onReceiveDataListener;
|
||||
}
|
||||
|
||||
|
||||
//**************各种接口**********************
|
||||
|
||||
/**
|
||||
* 当TCP接收到数据
|
||||
*/
|
||||
public interface OnReceiveDataListener {
|
||||
void onDataReceive(byte[] data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当TCP连接状态变化
|
||||
*/
|
||||
public interface OnTcpConnectStatus {
|
||||
void onConnectSuccess(RadioTcpClient tcpClient);
|
||||
|
||||
void onConnectFail(RadioTcpClient tcpClient);
|
||||
void onConnectionClosed(RadioTcpClient tcpClient);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当接收到流数据时的事件
|
||||
*/
|
||||
public interface OnReceiveStreamData {
|
||||
void onReceiveAudio(byte[] data);//音频数据
|
||||
|
||||
void onReceiveIQ(byte[] data);//IQ数据
|
||||
|
||||
void onReceiveFFT(VITA vita);//频谱数据
|
||||
|
||||
void onReceiveMeter(X6100Meters meters);//仪表数据
|
||||
|
||||
void onReceiveUnKnow(byte[] data);//未知数据
|
||||
}
|
||||
|
||||
/**
|
||||
* 当接收到指令回复
|
||||
*/
|
||||
public interface OnCommandListener {
|
||||
void onResponse(XieguResponse response);
|
||||
}
|
||||
|
||||
public interface OnStatusListener {
|
||||
void onStatus(XieguResponse response);
|
||||
}
|
||||
//*******************************************
|
||||
|
||||
|
||||
/**
|
||||
* 电台TCP回复数据的基础类
|
||||
*/
|
||||
public static class XieguResponse {
|
||||
private static final String TAG = "XieguResponse";
|
||||
public XieguResponseStyle responseStyle;
|
||||
public String head;//消息头
|
||||
public int resultCode;//消息代码
|
||||
public String resultContent;//扩展消息,有的返回消息分为3段,取第3段消息
|
||||
public String rawData;//原始数据
|
||||
public int seq_number;//32位int,指令序号
|
||||
|
||||
public XieguCommand xieguCommand = XieguCommand.UNKNOW;
|
||||
|
||||
|
||||
public XieguResponse(String line) {
|
||||
rawData = line;
|
||||
char header;
|
||||
if (line.length() > 0) {
|
||||
header = line.toUpperCase().charAt(0);
|
||||
} else {
|
||||
header = 0;
|
||||
}
|
||||
switch (header) {
|
||||
case 'S':
|
||||
responseStyle = XieguResponseStyle.STATUS;
|
||||
getHeadAndContent(line, "\\|");//获取指令的头、值、内容
|
||||
|
||||
break;
|
||||
case 'R':
|
||||
responseStyle = RESPONSE;
|
||||
getHeadAndContent(line, "\\|");
|
||||
try {
|
||||
seq_number = Integer.parseInt(head.substring(1));//解析指令序号
|
||||
xieguCommand = XieguCommand.values()[seq_number % 1000];
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "XieguResponse parseInt seq_number exception: " + e.getMessage());
|
||||
}
|
||||
break;
|
||||
case 'H':
|
||||
responseStyle = XieguResponseStyle.HANDLE;
|
||||
head = line;
|
||||
resultContent = line;
|
||||
Log.d(TAG, "XieguResponse: handle:" + line.substring(1));
|
||||
|
||||
break;
|
||||
case 'V':
|
||||
responseStyle = XieguResponseStyle.VERSION;
|
||||
head = line;
|
||||
resultContent = line;
|
||||
break;
|
||||
|
||||
case 0:
|
||||
default:
|
||||
responseStyle = XieguResponseStyle.UNKNOW;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 分割消息的头和内容,并分别负值给head和content
|
||||
*
|
||||
* @param line 消息
|
||||
* @param split 分隔符
|
||||
*/
|
||||
private void getHeadAndContent(String line, String split) {
|
||||
String[] temp = line.split(split);
|
||||
if (temp.length > 1) {
|
||||
head = temp[0];
|
||||
|
||||
resultCode = Integer.parseInt(temp[1]);
|
||||
|
||||
} else {
|
||||
head = "";
|
||||
}
|
||||
|
||||
if (temp.length > 2) {
|
||||
resultContent = temp[2];
|
||||
} else {
|
||||
resultContent = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,187 @@
|
|||
package com.bg7yoz.ft8cn.x6100;
|
||||
|
||||
/**
|
||||
* XieguRadioFactory 当前发现的所有收音机。
|
||||
* RadioFactory: 实例化这个类来创建一个 Radio Factory,通过它发现在相同局域网内地协谷电台。
|
||||
*
|
||||
* 通过Upd协议,在7001端口的广播数据中获取vita协议数据,并解析电台信息。
|
||||
*
|
||||
* @author BGY70Z
|
||||
* @date 2023-11-29
|
||||
*/
|
||||
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.bg7yoz.ft8cn.flex.FlexRadio;
|
||||
import com.bg7yoz.ft8cn.flex.RadioUdpClient;
|
||||
import com.bg7yoz.ft8cn.flex.VITA;
|
||||
|
||||
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.SocketException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
|
||||
|
||||
|
||||
public class XieguRadioFactory {
|
||||
private static final String TAG="XieguRadioFactory";
|
||||
private static final int XIEGU_DISCOVERY_PORT =7001;
|
||||
private static XieguRadioFactory instance=null;
|
||||
private final RadioUdpClient broadcastClient ;
|
||||
private OnXieguRadioEvents onXieguRadioEvents;
|
||||
|
||||
private Timer refreshTimer=null;
|
||||
private TimerTask refreshTask=null;
|
||||
|
||||
public ArrayList<X6100Radio> xieguRadios=new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 获取电台列表实例
|
||||
* @return 电台列表实例
|
||||
*/
|
||||
public static XieguRadioFactory getInstance(){
|
||||
if (instance==null){
|
||||
instance= new XieguRadioFactory();
|
||||
}
|
||||
instance.xieguRadios.clear();
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public XieguRadioFactory() {
|
||||
broadcastClient = new RadioUdpClient(XIEGU_DISCOVERY_PORT);
|
||||
|
||||
|
||||
broadcastClient.setOnUdpEvents(new RadioUdpClient.OnUdpEvents() {
|
||||
@Override
|
||||
public void OnReceiveData(DatagramSocket socket, DatagramPacket packet, byte[] data) {
|
||||
VITA vita = new VITA(data);
|
||||
|
||||
if (vita.isAvailable//如果数据包有效
|
||||
&&vita.classId64 == VITA.XIEGU_Discovery_Class_Id
|
||||
&&vita.streamId==VITA.XIEGU_Discovery_Stream_Id){
|
||||
InetAddress address = packet.getAddress();//获取ip地址
|
||||
updateXieguRadioList(new String(vita.payload),address.getHostAddress());
|
||||
}
|
||||
}
|
||||
});
|
||||
try {
|
||||
broadcastClient.setActivated(true);
|
||||
} catch (SocketException e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "XieguRadioFactory: "+e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void startRefreshTimer(){
|
||||
if (refreshTimer==null) {
|
||||
refreshTask=new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
Log.e(TAG, "run: 检查离线" );
|
||||
checkOffLineRadios();
|
||||
}
|
||||
};
|
||||
refreshTimer=new Timer();
|
||||
refreshTimer.schedule(refreshTask, 1000, 1000);//检查电台列表中的电台是否在线(每一秒)
|
||||
}
|
||||
}
|
||||
public void cancelRefreshTimer(){
|
||||
if (refreshTimer!=null){
|
||||
refreshTimer.cancel();
|
||||
refreshTimer=null;
|
||||
refreshTask.cancel();
|
||||
refreshTask=null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从数据中查找电台的MAC地址
|
||||
* @param s 数据
|
||||
* @return mac地址
|
||||
*/
|
||||
private String getMacAddress(String s){
|
||||
String[] strings=s.split(" ");
|
||||
for (int i = 0; i <strings.length ; i++) {
|
||||
if (strings[i].toLowerCase().startsWith("mac")){
|
||||
return strings[i].substring("mac".length()+1);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 在电台列表中查找有没有指定MAC的电台
|
||||
* @param mac MAC地址
|
||||
* @return 电台实例
|
||||
*/
|
||||
public X6100Radio checkXieguRadioByMac(String mac){
|
||||
for (X6100Radio radio:xieguRadios) {
|
||||
if (radio.isEqual(mac)){
|
||||
return radio;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private synchronized void updateXieguRadioList(String s,String ip){
|
||||
String mac = getMacAddress(s);
|
||||
if (mac.equals("")) {return;}
|
||||
X6100Radio radio=checkXieguRadioByMac(mac);
|
||||
if (radio!=null){
|
||||
radio.updateLastSeen();
|
||||
}else {
|
||||
radio=new X6100Radio(s,ip);
|
||||
if (onXieguRadioEvents!=null){
|
||||
onXieguRadioEvents.onXieguRadioAdded(radio);
|
||||
}
|
||||
xieguRadios.add(radio);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查电台是不是离线,如果离线,触发离线事件
|
||||
*/
|
||||
private void checkOffLineRadios(){
|
||||
for (X6100Radio radio:xieguRadios) {
|
||||
if (radio.isInvalidNow()){
|
||||
if (onXieguRadioEvents!=null){
|
||||
onXieguRadioEvents.onXieguRadioInvalid(radio);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//***********Getter****************
|
||||
public RadioUdpClient getBroadcastClient() {
|
||||
return broadcastClient;
|
||||
}
|
||||
|
||||
public OnXieguRadioEvents getOnFlexRadioEvents() {
|
||||
return onXieguRadioEvents;
|
||||
}
|
||||
|
||||
public void setOnXieguRadioEvents(OnXieguRadioEvents onXieguRadioEvents) {
|
||||
this.onXieguRadioEvents = onXieguRadioEvents;
|
||||
}
|
||||
//*********************************
|
||||
|
||||
|
||||
/**
|
||||
* 电台列表变化的接口
|
||||
*/
|
||||
public static interface OnXieguRadioEvents{
|
||||
void onXieguRadioAdded(X6100Radio flexRadio);
|
||||
void onXieguRadioInvalid(X6100Radio flexRadio);
|
||||
}
|
||||
|
||||
}
|
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 22 KiB |
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<padding
|
||||
android:bottom="0dp"
|
||||
android:left="0dp"
|
||||
android:right="0dp"
|
||||
android:top="0dp" />
|
||||
|
||||
<stroke
|
||||
android:width="0.3dp"
|
||||
android:color="@color/calling_list_item_color" />
|
||||
|
||||
<gradient
|
||||
android:angle="90"
|
||||
android:startColor="@color/xiegu_icon_start_color"
|
||||
android:endColor="@color/xiegu_icon_end_color"
|
||||
android:type="linear" />
|
||||
|
||||
<corners android:radius="5dp" />
|
||||
|
||||
<corners
|
||||
android:bottomLeftRadius="6dp"
|
||||
android:bottomRightRadius="6dp"
|
||||
android:topLeftRadius="6dp"
|
||||
android:topRightRadius="6dp" />
|
||||
</shape>
|
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 22 KiB |
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 2.0 KiB |
|
@ -0,0 +1,293 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/xiegu_info_frameLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ui.XieguInfoFragment">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/xieguInfoMainLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline35"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintGuide_percent="0.5" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/xieguInfoTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/xiegu_name_label"
|
||||
android:textColor="@color/power_progress_value"
|
||||
android:textSize="30sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/xieguLogoImageView"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:contentDescription="@string/xiegu_name_label"
|
||||
android:src="@drawable/xiegulogo"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/xieguInfoTextView"
|
||||
app:layout_constraintEnd_toStartOf="@+id/xieguInfoTextView"
|
||||
app:layout_constraintTop_toTopOf="@+id/xieguInfoTextView" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/xieguRulersLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="124dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="@drawable/flex_meter_style"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline35"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/xieguInfoTextView">
|
||||
|
||||
<com.bg7yoz.ft8cn.ui.FlexMeterRulerView
|
||||
android:id="@+id/xieguSMeterRulerView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:background="@drawable/flex_meter_ruler_style"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.bg7yoz.ft8cn.ui.FlexMeterRulerView
|
||||
android:id="@+id/xieguAlcMeterRulerView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:background="@drawable/flex_meter_ruler_style"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/xieguSMeterRulerView" />
|
||||
|
||||
<com.bg7yoz.ft8cn.ui.FlexMeterRulerView
|
||||
android:id="@+id/xieguSwrMeterRulerView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:background="@drawable/flex_meter_ruler_style"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/xieguAlcMeterRulerView" />
|
||||
|
||||
<com.bg7yoz.ft8cn.ui.FlexMeterRulerView
|
||||
android:id="@+id/xieguPowerMeterRulerView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:background="@drawable/flex_meter_ruler_style"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/xieguSwrMeterRulerView" />
|
||||
|
||||
<com.bg7yoz.ft8cn.ui.FlexMeterRulerView
|
||||
android:id="@+id/xieguVoltMeterRulerView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:background="@drawable/flex_meter_ruler_style"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/xieguPowerMeterRulerView" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/xieguConfigLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="@drawable/flex_meter_style"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline35"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/xieguRulersLayout">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/xieguMaxTxPowertextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="@string/flex_max_tx_power"
|
||||
android:textColor="@color/power_progress_value"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.bg7yoz.ft8cn.ui.VolumeProgress
|
||||
android:id="@+id/xieguMaxPwrProgress"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="15dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:rotation="180"
|
||||
android:rotationX="180"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/xieguMaxTxPowertextView" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/xiegumaxPowerSeekBar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:background="@drawable/flex_meter_ruler_style"
|
||||
android:max="100"
|
||||
android:progress="3"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/xieguMaxPwrProgress" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/xieguAtuLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="@drawable/flex_meter_style"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline35"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/xieguConfigLayout">
|
||||
|
||||
<Button
|
||||
android:id="@+id/xieguStartAtuButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="@string/xiegu_start_atu"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/xieguAtuOffButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="@string/xiegu_atu_off"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/xieguAtuOnButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="@string/xiegu_atu_on"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/xieguAtuOffButton"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/xieguAllInfoLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="@drawable/flex_meter_style"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@+id/guideline35"
|
||||
app:layout_constraintTop_toTopOf="@+id/xieguRulersLayout">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/xieguMetersValueTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:text="@string/current_frequency"
|
||||
android:textColor="@color/power_progress_value"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/xieguFreqValueTextView" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/xieguFreqValueTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:text="@string/current_frequency"
|
||||
android:textColor="@color/power_progress_value"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/xieguPingValueTextView" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintGuide_percent="0.5" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/xieguLossValueTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/x6100_packet_lost"
|
||||
android:textColor="@color/power_progress_value"
|
||||
app:layout_constraintStart_toStartOf="@+id/guideline"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/xieguPingValueTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/xiegu_ping_value"
|
||||
android:textColor="@color/power_progress_value"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
|
@ -73,12 +73,10 @@
|
|||
android:id="@+id/gridMessageLayout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginTop="-12dp"
|
||||
android:background="@drawable/tracker_config_bar_style"
|
||||
app:layout_constraintEnd_toStartOf="@+id/trackerSampleLayout"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/trackerSampleLayout">
|
||||
app:layout_constraintStart_toStartOf="@+id/guideline32"
|
||||
app:layout_constraintTop_toBottomOf="@+id/trackerSampleLayout">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gridMessageTextView"
|
||||
|
@ -89,7 +87,7 @@
|
|||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="message"/>
|
||||
tools:text="message" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
|
|
@ -968,6 +968,130 @@
|
|||
</com.bg7yoz.ft8cn.ui.RadioGroupFt8cn>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/serialLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:background="@drawable/editor_layout_style"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/connectModeLayout">
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/serialDataBitsTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:text="@string/serial_data_bits"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/dataBitsSpinner"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/dataBitsSpinner" />
|
||||
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/dataBitsSpinner"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/editor_style"
|
||||
android:dropDownWidth="match_parent"
|
||||
android:popupBackground="@drawable/spinner_style"
|
||||
android:prompt="@string/timeOffset"
|
||||
android:spinnerMode="dropdown"
|
||||
app:layout_constraintStart_toEndOf="@+id/serialDataBitsTextView"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="TouchTargetSizeCheck,SpeakableTextPresentCheck,DuplicateSpeakableTextCheck"
|
||||
tools:listitem="@layout/serial_data_bits_spinner_item" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/parityTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/serial_parity"
|
||||
android:textColor="@color/text_view_color"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/parityBitsSpinner"
|
||||
app:layout_constraintStart_toEndOf="@+id/dataBitsSpinner"
|
||||
app:layout_constraintStart_toStartOf="@+id/serialDataBitsTextView"
|
||||
app:layout_constraintTop_toTopOf="@+id/parityBitsSpinner" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/parityBitsSpinner"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="@drawable/editor_style"
|
||||
android:dropDownWidth="match_parent"
|
||||
android:popupBackground="@drawable/spinner_style"
|
||||
android:prompt="@string/timeOffset"
|
||||
android:spinnerMode="dropdown"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/parityTextView"
|
||||
app:layout_constraintTop_toBottomOf="@+id/dataBitsSpinner"
|
||||
app:layout_constraintVertical_bias="1.0"
|
||||
tools:ignore="TouchTargetSizeCheck,SpeakableTextPresentCheck,DuplicateSpeakableTextCheck"
|
||||
tools:listitem="@layout/serial_parity_bits_spinner_item" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stopBitsTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:text="@string/serial_stop_bits"
|
||||
android:textColor="@color/text_view_color"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/serialDataBitsTextView"
|
||||
app:layout_constraintStart_toEndOf="@+id/dataBitsSpinner"
|
||||
app:layout_constraintTop_toTopOf="@+id/serialDataBitsTextView" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/stopBitsSpinner"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:background="@drawable/editor_style"
|
||||
android:dropDownWidth="match_parent"
|
||||
android:popupBackground="@drawable/spinner_style"
|
||||
android:prompt="@string/timeOffset"
|
||||
android:spinnerMode="dropdown"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/stopBitsTextView"
|
||||
app:layout_constraintStart_toEndOf="@+id/stopBitsTextView"
|
||||
app:layout_constraintTop_toTopOf="@+id/stopBitsTextView"
|
||||
tools:ignore="TouchTargetSizeCheck,SpeakableTextPresentCheck,DuplicateSpeakableTextCheck"
|
||||
tools:listitem="@layout/serial_stop_bits_spinner_item" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/serialHelpImageButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="@drawable/imagebutton_transparent_style"
|
||||
android:contentDescription="@string/help"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_baseline_info_32"
|
||||
tools:ignore="TouchTargetSizeCheck" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/serialDefaultButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:text="@string/serial_default"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/serialHelpImageButton"
|
||||
app:layout_constraintStart_toEndOf="@+id/parityBitsSpinner"
|
||||
app:layout_constraintTop_toBottomOf="@+id/dataBitsSpinner" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/civAddressLayout"
|
||||
|
@ -977,7 +1101,7 @@
|
|||
android:background="@drawable/editor_layout_style"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/connectModeLayout">
|
||||
app:layout_constraintTop_toBottomOf="@+id/serialLayout">
|
||||
|
||||
|
||||
<TextView
|
||||
|
|
|
@ -138,7 +138,7 @@
|
|||
android:layout_width="0dp"
|
||||
android:layout_height="15dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:rotation="180"
|
||||
android:rotationX="180"
|
||||
app:layout_constraintBottom_toTopOf="@+id/maxPowerSeekBar"
|
||||
|
@ -264,6 +264,8 @@
|
|||
app:layout_constraintTop_toBottomOf="@+id/tunePowerProgress" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
@ -0,0 +1,290 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/xiegu_info_frameLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ui.XieguInfoFragment">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/xieguInfoMainLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/xieguInfoTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:text="@string/xiegu_name_label"
|
||||
android:textColor="@color/power_progress_value"
|
||||
android:textSize="30sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/xieguLogoImageView"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:contentDescription="@string/xiegu_name_label"
|
||||
android:src="@drawable/xiegulogo"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/xieguInfoTextView"
|
||||
app:layout_constraintEnd_toStartOf="@+id/xieguInfoTextView"
|
||||
app:layout_constraintTop_toTopOf="@+id/xieguInfoTextView" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/xieguRulersLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="124dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="@drawable/flex_meter_style"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/xieguInfoTextView">
|
||||
|
||||
<com.bg7yoz.ft8cn.ui.FlexMeterRulerView
|
||||
android:id="@+id/xieguSMeterRulerView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:background="@drawable/flex_meter_ruler_style"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.bg7yoz.ft8cn.ui.FlexMeterRulerView
|
||||
android:id="@+id/xieguAlcMeterRulerView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:background="@drawable/flex_meter_ruler_style"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/xieguSMeterRulerView" />
|
||||
|
||||
<com.bg7yoz.ft8cn.ui.FlexMeterRulerView
|
||||
android:id="@+id/xieguSwrMeterRulerView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:background="@drawable/flex_meter_ruler_style"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/xieguAlcMeterRulerView" />
|
||||
|
||||
<com.bg7yoz.ft8cn.ui.FlexMeterRulerView
|
||||
android:id="@+id/xieguPowerMeterRulerView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:background="@drawable/flex_meter_ruler_style"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/xieguSwrMeterRulerView" />
|
||||
|
||||
<com.bg7yoz.ft8cn.ui.FlexMeterRulerView
|
||||
android:id="@+id/xieguVoltMeterRulerView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:background="@drawable/flex_meter_ruler_style"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/xieguPowerMeterRulerView" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/xieguConfigLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="@drawable/flex_meter_style"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/xieguRulersLayout">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/xieguMaxTxPowertextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="@string/flex_max_tx_power"
|
||||
android:textColor="@color/power_progress_value"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.bg7yoz.ft8cn.ui.VolumeProgress
|
||||
android:id="@+id/xieguMaxPwrProgress"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="15dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:rotation="180"
|
||||
android:rotationX="180"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/xieguMaxTxPowertextView" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/xiegumaxPowerSeekBar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:background="@drawable/flex_meter_ruler_style"
|
||||
android:max="100"
|
||||
android:progress="3"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/xieguMaxPwrProgress" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/xieguAtuLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="@drawable/flex_meter_style"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/xieguConfigLayout">
|
||||
|
||||
<Button
|
||||
android:id="@+id/xieguStartAtuButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="@string/xiegu_start_atu"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/xieguAtuOffButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="@string/xiegu_atu_off"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/xieguAtuOnButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="@string/xiegu_atu_on"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/xieguAtuOffButton"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/xieguAllInfoLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="@drawable/flex_meter_style"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/xieguAtuLayout">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/xieguMetersValueTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:text="@string/current_frequency"
|
||||
android:textColor="@color/power_progress_value"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/xieguFreqValueTextView" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/xieguFreqValueTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:text="@string/current_frequency"
|
||||
android:textColor="@color/power_progress_value"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/xieguPingValueTextView" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintGuide_percent="0.5" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/xieguLossValueTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/x6100_packet_lost"
|
||||
android:textColor="@color/power_progress_value"
|
||||
app:layout_constraintStart_toStartOf="@+id/guideline"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/xieguPingValueTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/xiegu_ping_value"
|
||||
android:textColor="@color/power_progress_value"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
|
@ -52,7 +52,7 @@
|
|||
android:layout_marginStart="8dp"
|
||||
android:text="@string/flex_ip_addr"
|
||||
android:textAlignment="textEnd"
|
||||
android:textColor="@color/text_view_color"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/inputFlexAddressEdit"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/linearLayout2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/select_bluetooth_dialog_style"
|
||||
android:minWidth="300dp"
|
||||
android:minHeight="200dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:background="@drawable/icon_background_style"
|
||||
android:contentDescription="@string/help"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ft8cn_icon" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/selectXieguHelpMessage"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="start"
|
||||
android:text="@string/pl_select_xiegu_radio"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/image" />
|
||||
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/xieguIpAddressConstraintLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:background="@drawable/editor_layout_style"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/selectXieguHelpMessage">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/xieguIpAddressText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:text="@string/flex_ip_addr"
|
||||
android:textAlignment="textEnd"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/inputXieguAddressEdit"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/inputXieguAddressEdit"
|
||||
tools:ignore="TextContrastCheck" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/inputXieguAddressEdit"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:autofillHints=""
|
||||
android:background="@drawable/editor_style"
|
||||
android:ems="10"
|
||||
android:hint="@string/pl_input_flex_ip"
|
||||
android:inputType="textPersonName|textCapCharacters"
|
||||
|
||||
android:minHeight="32dp"
|
||||
android:textColor="@color/text_view_color"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/connectXieguImageButton"
|
||||
app:layout_constraintStart_toEndOf="@+id/xieguIpAddressText"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="TextContrastCheck,TouchTargetSizeCheck,VisualLintTextFieldSize" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/connectXieguImageButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="@drawable/imagebutton_style"
|
||||
android:contentDescription="@string/connect_flex"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_baseline_link_24"
|
||||
tools:ignore="TouchTargetSizeCheck" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/xieguRadioListRecyclerView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="1dp"
|
||||
android:layout_marginEnd="1dp"
|
||||
android:layout_marginBottom="1dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="1.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/xieguIpAddressConstraintLayout"
|
||||
app:layout_constraintVertical_bias="0.0" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/xieguRadioScrollDownImageView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:contentDescription="@string/help"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/xieguRadioListRecyclerView"
|
||||
app:srcCompat="@drawable/ic_baseline_keyboard_arrow_up_24" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/xieguRadioScrollUpImageView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:contentDescription="@string/help"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/xieguRadioListRecyclerView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:srcCompat="@drawable/ic_baseline_keyboard_arrow_down_24" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/bauRateConstraintLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="1dp"
|
||||
android:layout_marginTop="1dp"
|
||||
android:layout_marginRight="1dp"
|
||||
android:layout_marginBottom="1dp"
|
||||
android:stateListAnimator="@anim/click_button"
|
||||
tools:ignore="TouchTargetSizeCheck">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/serialDataBitsItemTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:textColor="@color/text_view_color"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="@string/serial_data_bits" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/bauRateConstraintLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="1dp"
|
||||
android:layout_marginTop="1dp"
|
||||
android:layout_marginRight="1dp"
|
||||
android:layout_marginBottom="1dp"
|
||||
android:stateListAnimator="@anim/click_button"
|
||||
tools:ignore="TouchTargetSizeCheck">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/serialParityBitsItemTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:textColor="@color/text_view_color"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="@string/serial_parity" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/bauRateConstraintLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="1dp"
|
||||
android:layout_marginTop="1dp"
|
||||
android:layout_marginRight="1dp"
|
||||
android:layout_marginBottom="1dp"
|
||||
android:stateListAnimator="@anim/click_button"
|
||||
tools:ignore="TouchTargetSizeCheck">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/serialStopBitsItemTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:textColor="@color/text_view_color"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="@string/serial_stop_bits" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,80 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/xieguRadioListConstraintLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="1dp"
|
||||
android:layout_marginTop="1dp"
|
||||
android:layout_marginRight="1dp"
|
||||
android:layout_marginBottom="1dp"
|
||||
android:background="@drawable/calling_list_cell_style"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
android:stateListAnimator="@anim/click_button"
|
||||
tools:ignore="TouchTargetSizeCheck">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/xieguRadioNameTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="2dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textColor="@color/bluetooth_device_enable_color"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="@+id/guideline16"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="TextContrastCheck"
|
||||
tools:text="FLEX-6400" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/xieguInfoTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textColor="@color/help_dialog_text_color"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/xieguImageView"
|
||||
app:layout_constraintTop_toBottomOf="@+id/xieguRadioNameTextView"
|
||||
tools:ignore="TextContrastCheck"
|
||||
tools:text="1418-6579-6400-0461" />
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/xieguImageView"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:background="@drawable/xiegu_logo_style"
|
||||
android:contentDescription="@string/headset_device"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline16"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/xiegulogo"
|
||||
tools:ignore="ImageContrastCheck" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline16"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintGuide_begin="40dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/xieguRadioIpTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="3dp"
|
||||
android:textColor="@color/bluetooth_device_enable_color"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/xieguRadioNameTextView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/xieguRadioNameTextView"
|
||||
tools:ignore="TextContrastCheck"
|
||||
tools:text="192.168.168.168"
|
||||
/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -53,4 +53,9 @@
|
|||
android:name="com.bg7yoz.ft8cn.ui.FlexRadioInfoFragment"
|
||||
android:label="fragment_flex_radio_info"
|
||||
tools:layout="@layout/fragment_flex_radio_info" />
|
||||
<fragment
|
||||
android:id="@+id/xieguInfoFragment"
|
||||
android:name="com.bg7yoz.ft8cn.ui.XieguInfoFragment"
|
||||
android:label="fragment_xiegu_info"
|
||||
tools:layout="@layout/fragment_xiegu_info" />
|
||||
</navigation>
|
|
@ -491,6 +491,33 @@
|
|||
<string name="config_msg_std_mode">Standard</string>
|
||||
<string name="config_msg_simple_mode">Simple</string>
|
||||
<string name="message_mode_help">messageMode_en.txt</string>
|
||||
<string name="pl_select_xiegu_radio">Please select an available XIEGU Radio</string>
|
||||
<string name="select_xiegu_device">Select XieguRadio device : %s</string>
|
||||
<string name="xiegu_connect_failed">XieguRadio %s connect failed !</string>
|
||||
<string name="x6100_packet_lost">Loss of packets: %d</string>
|
||||
<string name="xiegu_name_label">XIEGU</string>
|
||||
<string name="xiegu_ping_value">Ping: %d ms</string>
|
||||
<string name="xiegu_start_atu">Start ATU</string>
|
||||
<string name="xiegu_atu_on">ATU On</string>
|
||||
<string name="xiegu_atu_off">ATU Off</string>
|
||||
<string name="xiegu_band_str">Band: %s</string>
|
||||
<string name="alc_low_alert">Warning ! ALC is too low .</string>
|
||||
<string name="xiegu_meter_info">S.Meter: %.1f dBm\nSWR: %s\nALC: %.1f\nVolt: %.1fV\nTX power: %.1f W\nMax tx power: %.1f\nTX volume:%d%%</string>
|
||||
<string name="serial_connect_no_access">Unable to connect to the serial port, please confirm if you have access to the USB device!</string>
|
||||
<string name="serial_connect_failed">Serial port opening failed</string>
|
||||
<string name="serial_no_driver">Unable to connect to serial port, no driver or serial port not present!</string>
|
||||
<string name="serial_data_bits">Data bit</string>
|
||||
<string name="serial_parity">Parity bit</string>
|
||||
<string name="serial_parity_none">None</string>
|
||||
<string name="serial_parity_odd">Odd</string>
|
||||
<string name="serial_parity_even">Even</string>
|
||||
<string name="serial_parity_mark">Mark</string>
|
||||
<string name="serial_parity_space">Space</string>
|
||||
<string name="serial_stop_bits">Stop bits</string>
|
||||
<string name="serial_default">Default</string>
|
||||
<string name="serial_setting_help">serial_help_en.txt</string>
|
||||
<string name="tcp_connect_closed">The network connection has been disconnected.</string>
|
||||
<string name="rtty_ru_msg">RTTY RU</string>
|
||||
|
||||
<!-- <string name="hello_blank_fragment">Hello blank fragment</string>-->
|
||||
|
||||
|
|
|
@ -491,6 +491,33 @@
|
|||
<string name="config_msg_std_mode">Standard</string>
|
||||
<string name="config_msg_simple_mode">Simple</string>
|
||||
<string name="message_mode_help">messageMode_en.txt</string>
|
||||
<string name="pl_select_xiegu_radio">Please select an available XIEGU Radio</string>
|
||||
<string name="select_xiegu_device">Select XieguRadio device : %s</string>
|
||||
<string name="xiegu_connect_failed">XieguRadio %s connect failed !</string>
|
||||
<string name="x6100_packet_lost">Loss of packets: %d</string>
|
||||
<string name="xiegu_name_label">XIEGU</string>
|
||||
<string name="xiegu_ping_value">Ping: %d ms</string>
|
||||
<string name="xiegu_start_atu">Start ATU</string>
|
||||
<string name="xiegu_atu_on">ATU On</string>
|
||||
<string name="xiegu_atu_off">ATU Off</string>
|
||||
<string name="xiegu_band_str">Band: %s</string>
|
||||
<string name="alc_low_alert">Warning ! ALC is too low .</string>
|
||||
<string name="xiegu_meter_info">S.Meter: %.1f dBm\nSWR: %s\nALC: %.1f\nVolt: %.1fV\nTX power: %.1f W\nMax tx power: %.1f\nTX volume:%d%%</string>
|
||||
<string name="serial_connect_no_access">Unable to connect to the serial port, please confirm if you have access to the USB device!</string>
|
||||
<string name="serial_connect_failed">Serial port opening failed</string>
|
||||
<string name="serial_no_driver">Unable to connect to serial port, no driver or serial port not present!</string>
|
||||
<string name="serial_data_bits">Bits de datos</string>
|
||||
<string name="serial_parity">Parity bit</string>
|
||||
<string name="serial_parity_none">None</string>
|
||||
<string name="serial_parity_odd">Odd</string>
|
||||
<string name="serial_parity_even">Even</string>
|
||||
<string name="serial_parity_mark">Mark</string>
|
||||
<string name="serial_parity_space">Space</string>
|
||||
<string name="serial_stop_bits">Stop bits</string>
|
||||
<string name="serial_default">Default</string>
|
||||
<string name="serial_setting_help">serial_help_en.txt</string>
|
||||
<string name="tcp_connect_closed">The network connection has been disconnected.</string>
|
||||
<string name="rtty_ru_msg">RTTY RU</string>
|
||||
<!-- <string name="hello_blank_fragment">Hello blank fragment</string>-->
|
||||
|
||||
</resources>
|
|
@ -768,12 +768,38 @@
|
|||
<string name="import_canceled_html">インポートが取り消されました!</string>
|
||||
<string name="import_cancel_button">インポートを取り消す</string>
|
||||
<string name="qsl_query_log_menu">Log (%s)</string>
|
||||
<string name="message_list_simple_mode">単純リストモード</string>
|
||||
<string name="message_list_simple_mode">簡易リストモード</string>
|
||||
<string name="message_list_standard_mode">標準リストモード</string>
|
||||
<string name="config_msg_mode">メッセージモード</string>
|
||||
<string name="config_msg_std_mode">標準</string>
|
||||
<string name="config_msg_simple_mode">わかりやすい</string>
|
||||
<string name="config_msg_mode">表示</string>
|
||||
<string name="config_msg_std_mode">標準表示</string>
|
||||
<string name="config_msg_simple_mode">簡易表示</string>
|
||||
<string name="message_mode_help">messageMode_en.txt</string>
|
||||
|
||||
<string name="pl_select_xiegu_radio">存在するXIEGUを選択してください</string>
|
||||
<string name="select_xiegu_device">選択されたXieguRadioデバイス: %s</string>
|
||||
<string name="xiegu_connect_failed">XieguRadio %s の接続に失敗しました</string>
|
||||
<string name="x6100_packet_lost">パケット損失:%d</string>
|
||||
<string name="xiegu_name_label">XIEGU</string>
|
||||
<string name="xiegu_ping_value">Ping: %d ms</string>
|
||||
<string name="xiegu_start_atu">ATUを起動</string>
|
||||
<string name="xiegu_atu_on">ATU On</string>
|
||||
<string name="xiegu_atu_off">ATU Off</string>
|
||||
<string name="xiegu_band_str">バンド: %s</string>
|
||||
<string name="alc_low_alert">警告!ALCが低すぎます</string>
|
||||
<string name="xiegu_meter_info">S.Meter: %.1f dBm\nSWR: %s\nALC: %.1f\nVolt: %.1fV\nTX power: %.1f W\nMax tx power: %.1f\nTX volume:%d%%</string>
|
||||
<string name="serial_connect_no_access">シリアルポートに接続できません。USBデバイスへのアクセス権限があるか確認してください!</string>
|
||||
<string name="serial_connect_failed">シリアルポートのオープンに失敗しました</string>
|
||||
<string name="serial_no_driver">シリアルポートを接続できません、ドライブがないかシリアルポートが存在しません!</string>
|
||||
<string name="serial_data_bits">Data bit</string>
|
||||
<string name="serial_parity">Parity</string>
|
||||
<string name="serial_parity_none">None</string>
|
||||
<string name="serial_parity_odd">Odd</string>
|
||||
<string name="serial_parity_even">Even</string>
|
||||
<string name="serial_parity_mark">Mark</string>
|
||||
<string name="serial_parity_space">Space</string>
|
||||
<string name="serial_stop_bits">Stop bits</string>
|
||||
<string name="serial_default">デフォルト設定</string>
|
||||
<string name="serial_setting_help">serial_help_en.txt</string>
|
||||
<string name="tcp_connect_closed">The network connection has been disconnected.</string>
|
||||
<string name="rtty_ru_msg">RTTY RU</string>
|
||||
|
||||
</resources>
|
|
@ -19,6 +19,9 @@
|
|||
<color name="tracker_line_color">#FFFFFFFF</color>
|
||||
<color name="tracker_in_my_line_color">#ffFF1E27</color>
|
||||
|
||||
<color name="xiegu_icon_start_color">#E6606060</color>
|
||||
<color name="xiegu_icon_end_color">#DF1E1E1E</color>
|
||||
|
||||
<color name="power_progress_value">#ff00ffff</color>
|
||||
<color name="power_progress_radar_value">#B552AEF8</color>
|
||||
|
||||
|
|
|
@ -490,6 +490,33 @@
|
|||
<string name="config_msg_std_mode">标准</string>
|
||||
<string name="config_msg_simple_mode">精简</string>
|
||||
<string name="message_mode_help">messageMode.txt</string>
|
||||
<string name="pl_select_xiegu_radio">请选择协谷电台设备</string>
|
||||
<string name="select_xiegu_device">选择协谷电台:%s</string>
|
||||
<string name="xiegu_connect_failed">连接Xiegu %s 失败!</string>
|
||||
<string name="x6100_packet_lost">丢包数:%d</string>
|
||||
<string name="xiegu_name_label">XIEGU</string>
|
||||
<string name="xiegu_ping_value">Ping: %d ms</string>
|
||||
<string name="xiegu_start_atu">启动ATU</string>
|
||||
<string name="xiegu_atu_on">ATU On</string>
|
||||
<string name="xiegu_atu_off">ATU Off</string>
|
||||
<string name="xiegu_band_str">频段:%s</string>
|
||||
<string name="alc_low_alert">警告!ALC太低了。</string>
|
||||
<string name="xiegu_meter_info">信号强度: %.1f dBm\n驻波: %s\nALC: %.1f\n电压: %.1fV\n发射功率: %.1f W\n最大发射功率: %.1f\n发射音量:%d%%</string>
|
||||
<string name="serial_connect_no_access">无法连接串口,请确认是否有访问USB设备的权限!</string>
|
||||
<string name="serial_connect_failed">串口打开失败!</string>
|
||||
<string name="serial_no_driver">无法连接串口,没有驱动或串口不存在!</string>
|
||||
<string name="serial_data_bits">数据位</string>
|
||||
<string name="serial_parity">校验位</string>
|
||||
<string name="serial_parity_none">无校验位</string>
|
||||
<string name="serial_parity_odd">奇数校验</string>
|
||||
<string name="serial_parity_even">偶数校验</string>
|
||||
<string name="serial_parity_mark">总为1</string>
|
||||
<string name="serial_parity_space">总为0</string>
|
||||
<string name="serial_stop_bits">停止位</string>
|
||||
<string name="serial_default">默认设置</string>
|
||||
<string name="serial_setting_help">serial_help.txt</string>
|
||||
<string name="tcp_connect_closed">网络连接已经断开!</string>
|
||||
<string name="rtty_ru_msg">RTTY RU</string>
|
||||
|
||||
|
||||
</resources>
|
|
@ -490,6 +490,33 @@
|
|||
<string name="config_msg_std_mode">標准</string>
|
||||
<string name="config_msg_simple_mode">精簡</string>
|
||||
<string name="message_mode_help">messageMode.txt</string>
|
||||
<string name="pl_select_xiegu_radio">請選擇協谷電臺設備</string>
|
||||
<string name="select_xiegu_device">選擇協谷電臺:%s</string>
|
||||
<string name="xiegu_connect_failed">連接Xiegu %s 失敗!</string>
|
||||
<string name="x6100_packet_lost">丟包數:%d</string>
|
||||
<string name="xiegu_name_label">XIEGU</string>
|
||||
<string name="xiegu_ping_value">Ping: %d ms</string>
|
||||
<string name="xiegu_start_atu">啓動ATU</string>
|
||||
<string name="xiegu_atu_on">ATU On</string>
|
||||
<string name="xiegu_atu_off">ATU Off</string>
|
||||
<string name="xiegu_band_str">頻段:%s</string>
|
||||
<string name="alc_low_alert">警告!ALC太低了。</string>
|
||||
<string name="xiegu_meter_info">信號強度: %.1f dBm\n駐波: %s\nALC: %.1f\n電壓: %.1fV\n發射功率: %.1f W\n最大發射功率: %.1f\n發射音量:%d%%</string>
|
||||
<string name="serial_connect_no_access">無法連接串口,請確認是否有訪問USB設備的權限!</string>
|
||||
<string name="serial_connect_failed">串口打開失敗!</string>
|
||||
<string name="serial_no_driver">無法連接串口,沒有驅動或串口不存在!</string>
|
||||
<string name="serial_data_bits">數據位</string>
|
||||
<string name="serial_parity">校驗位</string>
|
||||
<string name="serial_parity_none">無校驗位</string>
|
||||
<string name="serial_parity_odd">奇數校驗</string>
|
||||
<string name="serial_parity_even">偶數校驗</string>
|
||||
<string name="serial_parity_mark">總爲1</string>
|
||||
<string name="serial_parity_space">總爲0</string>
|
||||
<string name="serial_stop_bits">停止位</string>
|
||||
<string name="serial_default">默認設置</string>
|
||||
<string name="serial_setting_help">serial_help.txt</string>
|
||||
<string name="tcp_connect_closed">網路連接已經斷開!</string>
|
||||
<string name="rtty_ru_msg">RTTY RU</string>
|
||||
|
||||
|
||||
</resources>
|
|
@ -490,6 +490,31 @@
|
|||
<string name="config_msg_std_mode">標准</string>
|
||||
<string name="config_msg_simple_mode">精簡</string>
|
||||
<string name="message_mode_help">messageMode.txt</string>
|
||||
|
||||
<string name="pl_select_xiegu_radio">請選擇協谷電臺設備</string>
|
||||
<string name="select_xiegu_device">選擇協谷電臺:%s</string>
|
||||
<string name="xiegu_connect_failed">連接Xiegu %s 失敗!</string>
|
||||
<string name="x6100_packet_lost">丟包數:%d</string>
|
||||
<string name="xiegu_name_label">XIEGU</string>
|
||||
<string name="xiegu_ping_value">Ping: %d ms</string>
|
||||
<string name="xiegu_start_atu">啓動ATU</string>
|
||||
<string name="xiegu_atu_on">ATU On</string>
|
||||
<string name="xiegu_atu_off">ATU Off</string>
|
||||
<string name="xiegu_band_str">頻段:%s</string>
|
||||
<string name="alc_low_alert">警告!ALC太低了。</string>
|
||||
<string name="xiegu_meter_info">信號強度: %.1f dBm\n駐波: %s\nALC: %.1f\n電壓: %.1fV\n發射功率: %.1f W\n最大發射功率: %.1f\n發射音量:%d%%</string>
|
||||
<string name="serial_connect_no_access">無法連接串口,請確認是否有訪問USB設備的權限!</string>
|
||||
<string name="serial_connect_failed">串口打開失敗!</string>
|
||||
<string name="serial_no_driver">無法連接串口,沒有驅動或串口不存在!</string>
|
||||
<string name="serial_data_bits">數據位</string>
|
||||
<string name="serial_parity">校驗位</string>
|
||||
<string name="serial_parity_none">無校驗位</string>
|
||||
<string name="serial_parity_odd">奇數校驗</string>
|
||||
<string name="serial_parity_even">偶數校驗</string>
|
||||
<string name="serial_parity_mark">總爲1</string>
|
||||
<string name="serial_parity_space">總爲0</string>
|
||||
<string name="serial_stop_bits">停止位</string>
|
||||
<string name="serial_default">默認設置</string>
|
||||
<string name="serial_setting_help">serial_help.txt</string>
|
||||
<string name="tcp_connect_closed">網路連接已經斷開!</string>
|
||||
|
||||
</resources>
|
|
@ -490,6 +490,31 @@
|
|||
<string name="config_msg_std_mode">標准</string>
|
||||
<string name="config_msg_simple_mode">精簡</string>
|
||||
<string name="message_mode_help">messageMode.txt</string>
|
||||
|
||||
<string name="pl_select_xiegu_radio">請選擇協谷電臺設備</string>
|
||||
<string name="select_xiegu_device">選擇協谷電臺:%s</string>
|
||||
<string name="xiegu_connect_failed">連接Xiegu %s 失敗!</string>
|
||||
<string name="x6100_packet_lost">丟包數:%d</string>
|
||||
<string name="xiegu_name_label">XIEGU</string>
|
||||
<string name="xiegu_ping_value">Ping: %d ms</string>
|
||||
<string name="xiegu_start_atu">啓動ATU</string>
|
||||
<string name="xiegu_atu_on">ATU On</string>
|
||||
<string name="xiegu_atu_off">ATU Off</string>
|
||||
<string name="xiegu_band_str">頻段:%s</string>
|
||||
<string name="alc_low_alert">警告!ALC太低了。</string>
|
||||
<string name="xiegu_meter_info">信號強度: %.1f dBm\n駐波: %s\nALC: %.1f\n電壓: %.1fV\n發射功率: %.1f W\n最大發射功率: %.1f\n發射音量:%d%%</string>
|
||||
<string name="serial_connect_no_access">無法連接串口,請確認是否有訪問USB設備的權限!</string>
|
||||
<string name="serial_connect_failed">串口打開失敗!</string>
|
||||
<string name="serial_no_driver">無法連接串口,沒有驅動或串口不存在!</string>
|
||||
<string name="serial_data_bits">數據位</string>
|
||||
<string name="serial_parity">校驗位</string>
|
||||
<string name="serial_parity_none">無校驗位</string>
|
||||
<string name="serial_parity_odd">奇數校驗</string>
|
||||
<string name="serial_parity_even">偶數校驗</string>
|
||||
<string name="serial_parity_mark">總爲1</string>
|
||||
<string name="serial_parity_space">總爲0</string>
|
||||
<string name="serial_stop_bits">停止位</string>
|
||||
<string name="serial_default">默認設置</string>
|
||||
<string name="serial_setting_help">serial_help.txt</string>
|
||||
<string name="tcp_connect_closed">網路連接已經斷開!</string>
|
||||
|
||||
</resources>
|
|
@ -11,6 +11,8 @@
|
|||
<color name="tracker_sample_qso_text_color">#FF0000</color>
|
||||
<color name="tracker_sample_qsx_color">#7f0000ff</color>
|
||||
<color name="tracker_sample_qsx_text_color">#FFFFFF</color>
|
||||
<color name="xiegu_icon_start_color">#BF9C9C9C</color>
|
||||
<color name="xiegu_icon_end_color">#E1FFFFFF</color>
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -7,5 +7,6 @@
|
|||
<item name="grid_tracker_config" type="id" />
|
||||
<item name="grid_tracker_trans" type="id" />
|
||||
<item name="flex_radio" type="id" />
|
||||
<item name="xiegu_radio" type="id" />
|
||||
|
||||
</resources>
|
|
@ -99,6 +99,7 @@
|
|||
<string name="dXpedition">DXpedition</string>>
|
||||
<string name="field_day">Field Day</string>>
|
||||
<string name="telemetry">Telemetry</string>>
|
||||
<string name="rtty_ru_msg">RTTY RU</string>>
|
||||
|
||||
|
||||
<!-- 配置栏-->
|
||||
|
@ -329,8 +330,10 @@
|
|||
<string name="network_connect">Network</string>
|
||||
<string name="pl_select_flex_radio">Please select an available Flex Radio</string>
|
||||
<string name="select_flex_device">Select FlexRadio device : %s</string>
|
||||
<string name="select_xiegu_device">Select XieguRadio device : %s</string>
|
||||
<string name="only_flex_supported">Now only the FlexRadio or ICom series is supported !</string>
|
||||
<string name="flex_connect_failed">FlexRadio %s connect failed !</string>
|
||||
<string name="xiegu_connect_failed">XieguRadio %s connect failed !</string>
|
||||
<string name="init_flex_operation">%s is initializing the operation .</string>
|
||||
<string name="html_max_message_cache">Maximum number of cached messages</string>
|
||||
<string name="instruction_failed">The instruction %s failed to execute . message : %s</string>
|
||||
|
@ -359,6 +362,7 @@
|
|||
<string name="civ_stream">CI-V stream</string>
|
||||
<string name="audio_stream">Audio stream</string>
|
||||
<string name="alc_high_alert">Warning ! ALC is too high .</string>
|
||||
|
||||
<string name="swr_high_alert">Warning ! SWR is too high .</string>
|
||||
<string name="volume_percent">Signal output strength %.0f %%</string>
|
||||
<string name="signal_strength">Signal output strength</string>
|
||||
|
@ -494,6 +498,29 @@
|
|||
<string name="config_msg_std_mode">Standard</string>
|
||||
<string name="config_msg_simple_mode">Simple</string>
|
||||
<string name="message_mode_help">messageMode_en.txt</string>
|
||||
|
||||
<string name="pl_select_xiegu_radio">Please select an available XIEGU Radio</string>
|
||||
<string name="x6100_packet_lost">Loss of packets: %d</string>
|
||||
<string name="xiegu_name_label">XIEGU</string>
|
||||
<string name="xiegu_ping_value">Ping: %d ms</string>
|
||||
<string name="xiegu_start_atu">Start ATU</string>
|
||||
<string name="xiegu_atu_on">ATU On</string>
|
||||
<string name="xiegu_atu_off">ATU Off</string>
|
||||
<string name="xiegu_band_str">Band : %s</string>
|
||||
<string name="alc_low_alert">Warning ! ALC is too low .</string>
|
||||
<string name="xiegu_meter_info">S.Meter: %.1f dBm\nSWR: %s\nALC: %.1f\nVolt: %.1fV\nTX power: %.1f W\nMax tx power: %.1f\nTX volume:%d%%</string>
|
||||
<string name="serial_connect_no_access">Unable to connect to the serial port, please confirm if you have access to the USB device!</string>
|
||||
<string name="serial_connect_failed">Serial port opening failed !</string>
|
||||
<string name="serial_no_driver">Unable to connect to serial port, serial port not found!</string>
|
||||
<string name="serial_data_bits">Data bit</string>
|
||||
<string name="serial_parity">Parity bit</string>
|
||||
<string name="serial_parity_none">None</string>
|
||||
<string name="serial_parity_odd">Odd</string>
|
||||
<string name="serial_parity_even">Even</string>
|
||||
<string name="serial_parity_mark">Mark</string>
|
||||
<string name="serial_parity_space">Space</string>
|
||||
<string name="serial_stop_bits">Stop bits</string>
|
||||
<string name="serial_default">Default</string>
|
||||
<string name="serial_setting_help">serial_help_en.txt</string>
|
||||
<string name="tcp_connect_closed">The network connection has been disconnected.</string>
|
||||
|
||||
</resources>
|
Ładowanie…
Reference in New Issue