FT8CN/ft8cn/app/src/main/java/com/bg7yoz/ft8cn/icom/ControlUdp.java

245 wiersze
9.2 KiB
Java
Czysty Wina Historia

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package com.bg7yoz.ft8cn.icom;
/**
* 控制流的基本类。
*
* @author BGY70Z
* @date 2023-08-26
*/
import android.util.Log;
import java.net.DatagramPacket;
import java.util.Timer;
import java.util.TimerTask;
public class ControlUdp extends IcomUdpBase {
private static final String TAG = "ControlUdp";
public final String APP_NAME = "FT8CN";
//与采样率有关每20ms发送的样本数12000/50=240=F0实际字节数是16bit还要乘以2也就是480字节
public Timer tokenTimer;//续订令牌的时钟
public String userName;
public String password;
public String rigName = "";
public String audioName = "";
public byte[] rigMacAddress = new byte[6];//0xA8、0x90包中提供
public String connectionMode = "";
public boolean gotAuthOK = false;//token认证通过了
public boolean isAuthenticated = false;//登录成功
public boolean rigIsBusy = false;
public IcomCivUdp civUdp;
public AudioUdp audioUdp;
public ControlUdp(String userName, String password, String remoteIp, int remotePort) {
udpStyle = IcomUdpStyle.ControlUdp;
this.userName = userName;
this.password = password;
this.rigIp = remoteIp;
this.rigPort = remotePort;
}
@Override
public void onDataReceived(DatagramPacket packet, byte[] data) {
// 父类默认处理一下数据包:
// 控制包0x10CMD_I_AM_HERE、CMD_RETRANSMIT
// ping包0x15
// 变长包RETRANSMIT包type=IComPacketTypes.CMD_RETRANSMIT
super.onDataReceived(packet, data);
switch (data.length) {
case IComPacketTypes.CONTROL_SIZE://在父类中已经实现0x04,0x01指令
if (IComPacketTypes.ControlPacket.getType(data) == IComPacketTypes.CMD_I_AM_HERE) {
rigIp = packet.getAddress().getHostAddress();
}
//如果电台回复I'm ready,就发起login
if (IComPacketTypes.ControlPacket.getType(data) == IComPacketTypes.CMD_I_AM_READY) {
sendLoginPacket();//电台准备好了,申请登录 0x80包
startIdleTimer();//打开发送空包时钟
}
break;
case IComPacketTypes.TOKEN_SIZE://处理令牌的续订之类的事情
onReceiveTokenPacket(data);
break;
case IComPacketTypes.STATUS_SIZE://0x50电台回复我它的参数CivPort,AudioPort等
onReceiveStatusPacket(data);
break;
case IComPacketTypes.LOGIN_RESPONSE_SIZE://0x60电台回复登录的请求
onReceiveLoginResponse(data);
break;
case IComPacketTypes.CONNINFO_SIZE://电台会回复2次0x90包区别在于busy字段
onReceiveConnInfoPacket(data);
break;
case IComPacketTypes.CAP_CAPABILITIES_SIZE://0xA8数据包,返回civ地址
byte[] audioCap = IComPacketTypes.CapCapabilitiesPacket.getRadioCapPacket(data, 0);
if (audioCap != null) {
civUdp.supportTX = IComPacketTypes.RadioCapPacket.getSupportTX(audioCap);
civUdp.civAddress = IComPacketTypes.RadioCapPacket.getCivAddress(audioCap);
audioName = IComPacketTypes.RadioCapPacket.getAudioName(audioCap);
}
break;
}
}
/**
* 处理电台发送过来的connInfo0x90数据包电台发送0x90包有两次第一次busy=0,第二次busy=1。
* 在0x90数据包中取macAddress电台名称
* 这部分留给IcomControlUdp和XieGuControlUdp来处理
* @param data 0x90数据包
*/
public void onReceiveConnInfoPacket(byte[] data) {
}
/**
* 处理电台回复登录数据包
*
* @param data 0x60数据包
*/
public void onReceiveLoginResponse(byte[] data) {
if (IComPacketTypes.ControlPacket.getType(data) == 0x01) return;
connectionMode = IComPacketTypes.LoginResponsePacket.getConnection(data);
Log.d(TAG, "connection mode:" + connectionMode);
if (IComPacketTypes.LoginResponsePacket.authIsOK(data)) {//errorCode=0x00,认证成功
Log.d(TAG, "onReceiveLoginResponse: Login succeed!");
if (!isAuthenticated) {
rigToken = IComPacketTypes.LoginResponsePacket.getToken(data);
Log.d(TAG, "onReceiveLoginResponse: send token confirm 0x02");
sendTokenPacket(IComPacketTypes.TOKEN_TYPE_CONFIRM);//发送令牌确认包 0x40
startTokenTimer();//启动令牌续订时钟
isAuthenticated = true;
}
}
if (onStreamEvents != null) {//触发认证事件
onStreamEvents.OnLoginResponse(IComPacketTypes.LoginResponsePacket.authIsOK(data));
}
}
/**
* 处理电台回复我的参数。0x50数据包
*
* @param data 0x50数据包
*/
public void onReceiveStatusPacket(byte[] data) {
//if (this.authDone) return;//6100会频繁激活0x50包
if (IComPacketTypes.ControlPacket.getType(data) == 0x01) return;
if (IComPacketTypes.StatusPacket.getAuthOK(data)
&& IComPacketTypes.StatusPacket.getIsConnected(data)) {//令牌认证成功,且处于连接状态
audioUdp.rigPort = IComPacketTypes.StatusPacket.getRigAudioPort(data);
audioUdp.rigIp = rigIp;
civUdp.rigPort = IComPacketTypes.StatusPacket.getRigCivPort(data);
civUdp.rigIp = rigIp;
Log.e(TAG, String.format("onReceiveStatusPacket: Status packet 0x50: civRigPort:%d,audioRigPort:%d"
, civUdp.rigPort, audioUdp.rigPort));
//todo 6100与icom有差异
civUdp.startAreYouThereTimer();//civ端口启动连接电台
audioUdp.startAreYouThereTimer();//audio端口启动连接电台
}//else处理关闭连接
}
/**
* 处理令牌数据包
*
* @param data 0x40数据包
*/
public void onReceiveTokenPacket(byte[] data) {
//看是不是续订令牌包
if (IComPacketTypes.TokenPacket.getRequestType(data) == IComPacketTypes.TOKEN_TYPE_RENEWAL
&& IComPacketTypes.TokenPacket.getRequestReply(data) == 0x02
&& IComPacketTypes.ControlPacket.getType(data) != IComPacketTypes.CMD_RETRANSMIT) {
int response = IComPacketTypes.TokenPacket.getResponse(data);
if (response == 0x0000) {//说明续订成功了
gotAuthOK = true;
} else if (response == 0xffffffff) {
remoteId = IComPacketTypes.ControlPacket.getSentId(data);
localToken = IComPacketTypes.TokenPacket.getTokRequest(data);
rigToken = IComPacketTypes.TokenPacket.getToken(data);
sendConnectionRequest();//申请连接
} else {
Log.e(TAG, "Token renewal failed,unknow response");
}
}
}
/**
* 发送civ指令
*
* @param data 指令
*/
public void sendCivData(byte[] data) {
civUdp.sendCivData(data);
}
/**
* 发送音频数据到电台
*
* @param data 数据
*/
public void sendWaveData(float[] data) {
audioUdp.sendTxAudioData(data);
}
/**
* 发送0x90数据包向电台请求连接
*/
public void sendConnectionRequest() {
sendTrackedPacket(IComPacketTypes.ConnInfoPacket.connectRequestPacket((short) 0
, localId, remoteId, (byte) 0x01, (byte) 0x03, innerSeq, localToken, rigToken
, rigMacAddress, rigName, userName, IComPacketTypes.AUDIO_SAMPLE_RATE
, civUdp.getLocalPort(), audioUdp.getLocalPort()
, IComPacketTypes.TX_BUFFER_SIZE));
innerSeq++;
}
/**
* 发送登录数据包0x80包
*/
public void sendLoginPacket() {
sendTrackedPacket(IComPacketTypes.LoginPacket.loginPacketData((short) 0
, localId, remoteId, innerSeq, localToken, rigToken, userName, password, APP_NAME));
innerSeq++;
}
@Override
public void setOnStreamEvents(OnStreamEvents onStreamEvents) {
super.setOnStreamEvents(onStreamEvents);
audioUdp.onStreamEvents = onStreamEvents;
civUdp.onStreamEvents = onStreamEvents;
}
/**
* 启动令牌续订时钟
*/
public void startTokenTimer() {
stopTimer(tokenTimer);
Log.d(TAG, String.format("start Toke Timer: local port:%d,remote port %d", localPort, rigPort));
tokenTimer = new Timer();
tokenTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
sendTokenPacket(IComPacketTypes.TOKEN_TYPE_RENEWAL);
}
}, IComPacketTypes.TOKEN_RENEWAL_PERIOD_MS, IComPacketTypes.TOKEN_RENEWAL_PERIOD_MS);
}
public void closeAll() {
sendTrackedPacket(IComPacketTypes.TokenPacket.getTokenPacketData((short) 0
, localId, remoteId, IComPacketTypes.TOKEN_TYPE_DELETE, innerSeq, localToken, rigToken));
innerSeq++;
this.close();
civUdp.close();
audioUdp.stopTXAudio();
audioUdp.close();
civUdp.sendOpenClose(false);
}
}