Merge pull request #92 from N0BOY/Dev

v0.92 release
release v0.92
NØBOY 2024-01-23 23:10:21 -08:00 zatwierdzone przez GitHub
commit ef4f2fab92
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
93 zmienionych plików z 5026 dodań i 696 usunięć

Wyświetl plik

@ -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'//HashTablekeyHashMap
//testImplementation 'junit:junit:4.13.2'
//androidTestImplementation 'androidx.test.ext:junit:1.1.3'
//androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
implementation files('src/libs/MPAndroidChartv_3.1.0.jar')
implementation files('src/libs/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.

Wyświetl plik

@ -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=4RR73被误发成73的问题。
2.修正因多重解码模式下无法及时回复上一周期解码的消息以及漏发73的问题。
3.修正生成双方都是复合呼号的消息时,发送方的呼号可能不正确的问题。
@ -308,4 +321,5 @@ BG7YOZ
BG2EFX提供大数据量的日志用于测试。
DS1UFX贡献(tr)uSDX audio over cat代码。
BG8HT提供某型号电台进行测试。
UB6LUM帮助解决某型号电台的操作模式设置。

Wyświetl plik

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

Wyświetl plik

@ -0,0 +1,5 @@
关于串口设置
数据位一般可以是6位、7位或8位多数是8位。
停止位一般可以是1位、1.5位或2位多数是1位。
校验位:校验位一般用来判断接收的数据位有无错误,一般采用奇偶校验或无校验。
FT8CN默认的串口设置是数据位8、停止位1、无校验。不同的电台的串口设置可能不尽相同具体设置请与电台的参数一致。

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -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:icom1: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;
}
}
/**
*

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -21,7 +21,7 @@ import java.io.DataInputStream;
import java.io.IOException;
/**
* 线ConnectorUSB
* flexConnector
* @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 );

Wyświetl plik

@ -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 16int
*/
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;
}
}

Wyświetl plik

@ -51,7 +51,7 @@ public class DatabaseOpr extends SQLiteOpenHelper {
public static DatabaseOpr getInstance(@Nullable Context context, @Nullable String databaseName) {
if (instance == null) {
instance = new DatabaseOpr(context, databaseName, null, 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();

Wyświetl plik

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

Wyświetl plik

@ -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 id16进制
return Integer.parseInt(lines[2], 16);//stream id16进制
} catch (NumberFormatException e) {
e.printStackTrace();
Log.e(TAG, "getDaxStreamId exception: " + e.getMessage());

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -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;//流ID32位
//时间戳共有两部分小数部分和整数部分整数部分以秒为分辨率32位小数部分64位。
public long integerTimestamp;//u_int32long是64位的
public int integerTimestamp;//u_int32long是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;//流ID32位FLEX应当是0x0800
/*
VITA28VITA
/**
* 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;
}
/**
* VITAidcreate 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是以word32位4字节为单位
//packetSize值为263居多估计以音频还有其它的长度,263是包含7个word28字节的头长度。
packetSize = (data.length) + 7;//7个word是VITA的包头
//----以上是Header,32位第一个word-------
streamId = stream_id;//第二个word,此id是电台赋给的。经常是0x40000xx。
oui = 0x00001c2d;//第三个word,FlexRadio Systems OUI
//classId = 0x534c0123;//第四个word64位
classId = class_id;
//classId = 0x534c03e3;//第四个word64位
//integerTimestamp =0;// System.currentTimeMillis() / 1000;//第五个word,时间戳的整数部分,以秒为单位。应该是取当前时间
//fracTimeStamp = 0;//第六七个word,时间戳的小数部分64位此处为0。
//fracTimeStamp = frac;//第六七个word,时间戳的小数部分64位此处为0。
byte temp = 0;
if (classIdPresent) {
temp = 0x08;
}
if (trailerPresent) {
temp |= 0x04;
}
//----HEADER--No.1 word------
// result[0]=0x18;
result[0] = (byte) (packetType.ordinal() << 4);//packetType
result[0] |= temp;//其实就是0011 1000,0x38//CTRR,classIdPresent、trailerPresent、R、R
result[0] |= 0x03c0;//CTRR,classIdPresent、trailerPresent、R、R
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默认263words
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+28byte[] result=new byte[data.length+28];
streamIdPresent=true;
streamIdPresent=packetType==VitaPacketType.IF_DATA_WITH_STREAM
||packetType==VitaPacketType.EXT_DATA_WITH_STREAM;
streamId:0x4000008,STREAM_CREATE_DAX_TX
classIdPresent:0x534c03e3,packetSize:263
integerTimestamp=now/1000;
fracTimeStamp=0;
*/
return result;
}
public VITA() {
}
public VITA( 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,获取流ID32位
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);
}
}

Wyświetl plik

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

Wyświetl plik

@ -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. 自由运行计数时间戳
}

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -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/21.18秒的样本数)
//int num_total_samples = num_silence + num_samples + num_silence; // 填充信号中的样本数2.36秒+12.64秒=15秒的样本数
//float[] signal = new float[Ft8num_samples];
float[] signal = new float[num_samples];
//Ft8num_sampleFT8声音的总采样数不是字节数。15*12000

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -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("<", "&lt;")
.replace(">", "&gt;")));
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>

Wyświetl plik

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

Wyświetl plik

@ -139,6 +139,7 @@ public class Flex6000Rig extends BaseRig {
}
}
},START_QUERY_FREQ_DELAY-500);
readFreqTimer.schedule(readTask(), START_QUERY_FREQ_DELAY,QUERY_FREQ_TIMEOUT);
}
}

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -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;
/**
* XieGu6100ft8cnsbaseRig
*/
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);
}
}

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -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;
/**
* YAESU5
* ft848504080
*/
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-C0为高驻波低位音频输入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);
}
}

Wyświetl plik

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

Wyświetl plik

@ -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. */

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -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();
}
/**
* spinnerOnItemSelected
*/
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 {
* 线USBBLUE_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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -62,6 +62,9 @@ public class VolumeProgress extends View {
init();
invalidate();
}
public float getPercent(){
return mPercent;
}
public void reDraw(){
setPercent(mPercent);
}

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -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%%"
}
}

Wyświetl plik

@ -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);
}
/**
* 31000
*
* @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 float12000Hz
//x6100的音频格式是LPCM 16 Int12000Hz
//要做一下浮点到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;
}
}
/**
* headcontent
*
* @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 = "";
}
}
}
}

Wyświetl plik

@ -0,0 +1,187 @@
package com.bg7yoz.ft8cn.x6100;
/**
* XieguRadioFactory
* RadioFactory: Radio Factory
*
* Upd7001广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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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