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

245 wiersze
9.2 KiB
Java
Czysty Zwykły widok Historia

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;
}
}
/**
* connInfo0x900x90busy=0,busy=1
* 0x90macAddress
* IcomControlUdpXieGuControlUdp
* @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);
}
}