FT8CN/ft8cn/app/src/main/java/com/bg7yoz/ft8cn/x6100/X6100Radio.java

976 wiersze
32 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.x6100;
import static com.bg7yoz.ft8cn.flex.VITA.XIEGU_METER_CLASS_ID;
import static com.bg7yoz.ft8cn.flex.VITA.XIEGU_METER_Stream_Id;
import static com.bg7yoz.ft8cn.flex.VITA.XIEGU_PING_CLASS_ID;
import static com.bg7yoz.ft8cn.flex.VITA.XIEGU_PING_Stream_Id;
import static com.bg7yoz.ft8cn.flex.VITA.byteToStr;
import static com.bg7yoz.ft8cn.flex.VITA.readShortData;
import static com.bg7yoz.ft8cn.x6100.X6100Radio.XieguResponseStyle.RESPONSE;
import android.annotation.SuppressLint;
import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioTrack;
import android.util.Log;
import androidx.lifecycle.MutableLiveData;
import com.bg7yoz.ft8cn.GeneralVariables;
import com.bg7yoz.ft8cn.R;
import com.bg7yoz.ft8cn.flex.FlexCommand;
import com.bg7yoz.ft8cn.flex.FlexRadio;
import com.bg7yoz.ft8cn.flex.FlexResponseStyle;
import com.bg7yoz.ft8cn.flex.RadioTcpClient;
import com.bg7yoz.ft8cn.flex.RadioUdpClient;
import com.bg7yoz.ft8cn.flex.VITA;
import com.bg7yoz.ft8cn.flex.VitaPacketType;
import com.bg7yoz.ft8cn.flex.VitaTSF;
import com.bg7yoz.ft8cn.flex.VitaTSI;
import com.bg7yoz.ft8cn.rigs.BaseRig;
import com.bg7yoz.ft8cn.ui.ToastMessage;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Timer;
import java.util.TimerTask;
public class X6100Radio {
public enum XieguCommand {
UNKNOW,//未知指令
AUDIO,//音频指令
STREAM,//数据流指令
SUB,//订阅仪表
UNSUB,//取消订阅仪表
A91,//ft8符号
ATU,//自动天调
TUNE,//设置频率
MODE,//操作模式
PTT,//PTT操作
SET//设置操作
}
public enum XieguResponseStyle {
STATUS,//状态信息S+HANDLE
RESPONSE,//命令的响应R+客户端命令序列号
HANDLE,//电台给定的句柄H+句柄32位的16进制表示
VERSION,//版本号V+版本号
COMMAND,//发送命令C+序列号|命令
UNKNOW//未知的回复类型
}
private static final String TAG = "X6100Radio";
private static int lossCount = 0;
private static int currentCount = -1;
private String modelName;//电台型号
private String version;//电台版本号
private String rig_ip;//电台的IP
private String mac;//mac地址
public boolean isPttOn = false;
private int control_port = 7002;//电台的控制端口
private int stream_port = 7003;//电台端流数据的端口
private int discovery_port = 7001;//发现协议的端口
private long lastSeen;//最后一次消息的时间
private boolean isAvailable = true;//电台是不是有效
private final StringBuilder buffer = new StringBuilder();//指令的缓存
private final RadioTcpClient tcpClient = new RadioTcpClient();
private RadioUdpClient streamClient;
private int commandSeq = 1;//指令的序列
private XieguCommand xieguCommand;
private int handle = 0;
private String commandStr;
private int frames = 768;//每个周期的帧数
private int period = 64;//每个周期的时长,毫秒
//************************事件处理接口*******************************
private OnReceiveDataListener onReceiveDataListener;//当前接收到的数据事件
private OnTcpConnectStatus onTcpConnectStatus;//当TCP连接状态变化的事件
private OnReceiveStreamData onReceiveStreamData;//当接收到流数据后的处理事件
private OnCommandListener onCommandListener;//触发命令事件
private OnStatusListener onStatusListener;//触发状态事件
//*****************************************************************
private AudioTrack audioTrack = null;
///******************用于仪表信息显示*************
public MutableLiveData<Long> mutablePing = new MutableLiveData<>();//ping值
public MutableLiveData<Integer> mutableLossPackets = new MutableLiveData<>();//丢失的包数量
public MutableLiveData<X6100Meters> mutableMeters = new MutableLiveData<>();
private X6100Meters meters = new X6100Meters();
private boolean swrAlert = false;
private boolean alcAlert = false;
private Timer pingTimer = new Timer();
private TimerTask pingTask() {
return new TimerTask() {
@Override
public void run() {
try {
if (!streamClient.isActivated() || !isConnect()) {
pingTimer.cancel();
pingTimer.purge();
pingTimer = null;
return;
}
VITA vita = new VITA(VitaPacketType.EXT_DATA_WITH_STREAM
, VitaTSI.TSI_OTHER
, VitaTSF.TSF_REALTIME
, 0
, XIEGU_PING_Stream_Id
, XIEGU_PING_CLASS_ID);
vita.packetCount = 0;
vita.packetSize = 7;
vita.integerTimestamp = 0;//0是发送包1是接收包
vita.fracTimeStamp = System.currentTimeMillis();
streamClient.sendData(vita.pingDataToVita(), rig_ip, stream_port);
} catch (Exception e) {
Log.e(TAG, "ping timer error:" + e.getMessage());
}
}
};
}
/**
* 更新最后看到的时间
*/
public void updateLastSeen() {
this.lastSeen = System.currentTimeMillis();
}
public X6100Radio() {
updateLastSeen();
}
public X6100Radio(String s, String ip) {
mutableLossPackets.postValue(0);
update(s, ip);
}
public void update(String discoverStr, String ip) {
Log.d(TAG, discoverStr);
rig_ip = ip;
String[] paras = discoverStr.replace("\0", " ").split(" ");
version = getParameterStr(paras, "ft8cn_server_version");
modelName = getParameterStr(paras, "model");
mac = getParameterStr(paras, "mac");
control_port = getParameterInt(paras, "control_port");
stream_port = getParameterInt(paras, "stream_port");
discovery_port = getParameterInt(paras, "discovery_port");
updateLastSeen();
}
/**
* 到参数列表中找指定的字符类型参数
*
* @param parameters 参数列表
* @param prefix 参数名前缀
* @return 参数
*/
private String getParameterStr(String[] parameters, String prefix) {
for (int i = 0; i < parameters.length; i++) {
if (parameters[i].toLowerCase().startsWith(prefix.toLowerCase() + "=")) {
return parameters[i].substring(prefix.length() + 1);
}
}
//如果没找到,返回空字符串
return "";
}
/**
* 到参数列表中找指定的int类型参数
*
* @param parameters 参数列表
* @param prefix 参数名前缀
* @return 参数
*/
private int getParameterInt(String[] parameters, String prefix) {
for (int i = 0; i < parameters.length; i++) {
if (parameters[i].toLowerCase().startsWith(prefix.toLowerCase() + "=")) {
try {
return Integer.parseInt(parameters[i].substring(prefix.length() + 1));
} catch (NumberFormatException e) {
e.printStackTrace();
Log.e(TAG, "getParameterInt exception: " + e.getMessage());
return 0;
}
}
}
//如果没找到返回0
return 0;
}
/**
* 检查是不是 刚刚 离线离线条件5秒内没有收到电台的广播数据包
*
* @return 是否
*/
public boolean isInvalidNow() {
if (isAvailable) {//如果标记在线而大于5秒的时间没有收到数据包就视为刚刚离线。
isAvailable = System.currentTimeMillis() - lastSeen < 1000 * 5;//小于5秒就视为在线
return !isAvailable;
} else {//如果已经标记不在线了,就不是刚刚离线的。
return false;
}
}
public String getModelName() {
return modelName;
}
public void setModelName(String modelName) {
this.modelName = modelName;
}
public String getVersion() {
return version;
}
public String getRig_ip() {
return rig_ip;
}
public void setRig_ip(String rig_ip) {
this.rig_ip = rig_ip;
}
public String getMac() {
return mac;
}
public boolean isEqual(String madAddress) {
return this.mac.equalsIgnoreCase(madAddress);
}
/**
* 连接到控制电台
*/
public void connect() {
this.connect(this.rig_ip, this.control_port);
}
/**
* 连接控制到电台TCP
*
* @param ip 地址
* @param port 端口
*/
public void connect(String ip, int port) {
if (tcpClient.isConnect()) {
tcpClient.disconnect();
}
//Tcp连接触发的事件
tcpClient.setOnDataReceiveListener(new RadioTcpClient.OnDataReceiveListener() {
@Override
public void onConnectSuccess() {
if (onTcpConnectStatus != null) {
onTcpConnectStatus.onConnectSuccess(tcpClient);
}
}
@Override
public void onConnectFail() {
if (onTcpConnectStatus != null) {
onTcpConnectStatus.onConnectFail(tcpClient);
}
}
@Override
public void onDataReceive(byte[] buffer) {
if (onReceiveDataListener != null) {//此处把数据传递给XieGu6100NetRig
onReceiveDataListener.onDataReceive(buffer);
}
onReceiveData(buffer);
}
@Override
public void onConnectionClosed() {
tcpClient.disconnect();
ToastMessage.show(GeneralVariables.getStringFromResource(R.string.tcp_connect_closed));
if (onTcpConnectStatus != null){
onTcpConnectStatus.onConnectionClosed(tcpClient);
}
}
});
clearBufferData();//清除一下缓存的指令数据
tcpClient.connect(ip, port);//连接TCP
}
/**
* 关闭接收数据流的端口
*/
public synchronized void closeStreamPort() {
if (streamClient != null) {
if (streamClient.isActivated()) {
try {
streamClient.setActivated(false);
} catch (Exception e) {
e.printStackTrace();
}
}
}
streamClient = null;
}
@SuppressLint("DefaultLocale")
public synchronized void commandAtuOn() {
sendCommand(XieguCommand.ATU, "atu on");
}
@SuppressLint("DefaultLocale")
public synchronized void commandAtuOff() {
sendCommand(XieguCommand.ATU, "atu off");
}
@SuppressLint("DefaultLocale")
public synchronized void commandAtuStart() {
sendCommand(XieguCommand.ATU, "atu start");
}
@SuppressLint("DefaultLocale")
public synchronized void commandOpenStream() {
sendCommand(XieguCommand.STREAM, String.format("stream on %d", stream_port));
}
@SuppressLint("DefaultLocale")
public synchronized void commandSendA91(byte[] a91, float vol, float freq) {
sendCommand(XieguCommand.A91, String.format("a91 %.2f %.0f %s", vol, freq, BaseRig.byteToStr(a91)));
}
@SuppressLint("DefaultLocale")
public synchronized void commandGetAudioInfo() {
sendCommand(XieguCommand.AUDIO, "audio get all");
}
@SuppressLint("DefaultLocale")
public synchronized void commandGetStreamInfo() {
sendCommand(XieguCommand.STREAM, "stream get");
}
@SuppressLint("DefaultLocale")
public synchronized void commandSubAllMeter() {
sendCommand(XieguCommand.SUB, "sub all");
}
@SuppressLint("DefaultLocale")
public synchronized void commandSubGetMeter() {
sendCommand(XieguCommand.SUB, "sub get");
}
@SuppressLint("DefaultLocale")
public synchronized void commandUnubMeter() {
sendCommand(XieguCommand.UNSUB, "unsub");
}
@SuppressLint("DefaultLocale")
public synchronized void commandTuneFreq(long freq) {
sendCommand(XieguCommand.TUNE, String.format("tune %d", freq));
}
@SuppressLint("DefaultLocale")
public synchronized void commandSetMode(String mode, int filter) {
sendCommand(XieguCommand.TUNE, String.format("mode %s %d", mode, filter));
}
@SuppressLint("DefaultLocale")
public synchronized void commandSetTxPower(int power) {
sendCommand(XieguCommand.SET, String.format("set tx %d", power));
}
@SuppressLint("DefaultLocale")
public synchronized void commandSetTxVol(int volume) {
sendCommand(XieguCommand.SET, String.format("set tx_vol %d", volume));
}
private void showAlert() {
Log.e(TAG, String.format("ALC:%f", meters.alc));
if (meters.swr >= 5) {
if (!swrAlert) {
swrAlert = true;
ToastMessage.show(GeneralVariables.getStringFromResource(R.string.swr_high_alert));
}
} else {
swrAlert = false;
}
if (meters.alc > 50 || meters.alc < 20) {
if (!alcAlert) {
alcAlert = true;
if (meters.alc > 50) {
ToastMessage.show(GeneralVariables.getStringFromResource(R.string.alc_high_alert));
} else {
ToastMessage.show(GeneralVariables.getStringFromResource(R.string.alc_low_alert));
}
}
} else {
alcAlert = false;
}
}
/**
* 打开接收数据流的端口
*/
public void openStreamPort() {
if (streamClient != null) {
if (streamClient.isActivated()) {
try {
streamClient.setActivated(false);
} catch (Exception e) {
ToastMessage.show(e.getMessage());
e.printStackTrace();
}
}
}
RadioUdpClient.OnUdpEvents onUdpEvents = new RadioUdpClient.OnUdpEvents() {
@SuppressLint("DefaultLocale")
@Override
public void OnReceiveData(DatagramSocket socket, DatagramPacket packet, byte[] data) {
VITA vita = new VITA(data);
if (vita.classId64 == VITA.XIEGU_AUDIO_CLASS_ID) {//音频数据
//判断数据包丢失情况
int temp = lossCount;
if (currentCount <= -1) {
currentCount = vita.packetCount;
}
if (currentCount > vita.packetCount) {
lossCount = lossCount + vita.packetCount + 16 - currentCount - 1;
} else if (currentCount < vita.packetCount) {
lossCount = lossCount + vita.packetCount - currentCount - 1;
}
currentCount = vita.packetCount;
if (lossCount > temp) {
Log.e(TAG, String.format("丢包数量:%d", lossCount));
for (int i = 0; i < (lossCount - temp); i++) {
Log.d(TAG, String.format("补发数据,%d,size:%d", i, vita.payload.length));
sendReceivedAudio(vita.payload);//把当前的数据补发给录音对象
}
mutableLossPackets.postValue(lossCount);
}
sendReceivedAudio(vita.payload);//把音频发给录音对象
playReceiveAudio(vita.payload);//发送当前的音频数据
} else if (vita.classId64 == XIEGU_PING_CLASS_ID//ping数据
&& vita.streamId == XIEGU_PING_Stream_Id
&& vita.integerTimestamp == 1) {//ping的回包
mutablePing.postValue(System.currentTimeMillis() - vita.fracTimeStamp);
} else if (vita.classId64 == XIEGU_METER_CLASS_ID//仪表数据
&& vita.streamId == XIEGU_METER_Stream_Id) {
new Thread(new Runnable() {
@Override
public void run() {
meters.update(vita.payload);
mutableMeters.postValue(meters);
if (isPttOn) {
showAlert();
} else {
alcAlert = false;
swrAlert = false;
}
if (onReceiveStreamData != null){
onReceiveStreamData.onReceiveMeter(meters);
}
}
}).start();
}
}
};
//此处要确定stream的udp端口
streamClient = new RadioUdpClient(stream_port);
streamClient.setOnUdpEvents(onUdpEvents);
try {
streamClient.setActivated(true);
pingTimer.schedule(pingTask(), 1000, 1000);//启动ping计时器
} catch (SocketException e) {
ToastMessage.show(e.getMessage());
e.printStackTrace();
Log.d(TAG, "streamClient: " + e.getMessage());
}
}
/**
* 当接收到音频数据后,发送给录音对象的操作
*
* @param data 音频数据
*/
private void sendReceivedAudio(byte[] data) {
if (onReceiveStreamData != null) {
onReceiveStreamData.onReceiveAudio(data);
}
}
/**
* 当接收到音频数据时的处理
*
* @param data 音频数据
*/
private void playReceiveAudio(byte[] data) {
if (audioTrack != null) {//如果音频播放已经打开,就写音频流数据
audioTrack.write(data, 0, data.length, AudioTrack.WRITE_NON_BLOCKING);
}
}
/**
* 断开与电台的连接
*/
public synchronized void disConnect() {
if (tcpClient.isConnect()) {
tcpClient.disconnect();
}
if (pingTimer != null) {
pingTimer.cancel();
pingTimer.purge();
pingTimer = null;
}
}
/**
* 电台是否连接
*
* @return 是否
*/
public boolean isConnect() {
return tcpClient.isConnect();
}
/**
* 关闭音频
*/
public void closeAudio() {
if (audioTrack != null) {
audioTrack.stop();
audioTrack = null;
}
}
/**
* 当接收到数据时触发的事件此处是TCP连接得到的数据
*
* @param data 数据
*/
private void onReceiveData(byte[] data) {
if (data.length > 4) {//判断是不是老式的icom指令
if ((data[0] == (byte) 0xfe) && (data[1] == (byte) 0xfe)
&& ((data[2] == (byte) 0xe0) || data[3] == (byte) 0xe0)) {
clearBufferData();
return;
}
}
String s = new String(data);
if (!s.contains("\n")) {//不包含换行符,说明命令行没有接受完。
buffer.append(s);
} else {//说明已经有命令行了。可能不止一个哦。在此部分要触发OnReceiveLine
String[] commands = s.split("\n");
if (commands.length > 0) {//把收到数据的第一行,追加到之前接收的命令数据上
buffer.append(commands[0]);
}
//先把缓存中的数据触发出来
doReceiveLineEvent(buffer.toString());
clearBufferData();
//从第二行开始触发,最后一行不触发,最后一行要看是不是换行结尾
for (int i = 1; i < commands.length - 1; i++) {
doReceiveLineEvent(commands[i]);
}
if (commands.length > 1) {//当数据是多行的时候,最后一行的处理
if (s.endsWith("\n")) {//如果是以换行结尾,或者缓冲区没满(接收完全了),就触发事件
doReceiveLineEvent(commands[commands.length - 1]);
} else {//如果不是以换行结尾,说明指令没有接收完全
buffer.append(commands[commands.length - 1]);
}
}
}
}
/**
* 当接收到数据行时,触发的事件。可以触发两种事件:
* 1.行数据事件onReceiveLineListener
* 2.命令事件onCommandListener。
* <p>
* //* @param line 数据行
*/
private void doReceiveLineEvent(String line) {
XieguResponse response = new XieguResponse(line);
//更新一下句柄
switch (response.responseStyle) {
case VERSION:
this.version = response.head.substring(1);
break;
case HANDLE:
this.handle = Integer.parseInt(response.head.substring(1), 16);
break;
case RESPONSE:
if (XieguCommand.AUDIO == response.xieguCommand) {//是音频指令回复的信息
setAudioInfo(response.resultContent);
}
if (onCommandListener != null) {
onCommandListener.onResponse(response);
}
break;
case STATUS:
if (response.resultCode == 0) {//说明是电台状态变化了
String status[] = response.resultContent.split(" ");
for (int i = 0; i < status.length; i++) {//找出ptt的状体设置ptt
if (status[i].startsWith("ptt")) {//判断PTT
String temp[] = status[i].split("=");
isPttOn = temp[1].equalsIgnoreCase("on");
}
if (status[i].startsWith("play_volume")) {//判断PTT
String temp[] = status[i].split("=");
float vol = Integer.parseInt(temp[1].trim())*1.0f/100f;
GeneralVariables.volumePercent = vol;
GeneralVariables.mutableVolumePercent.postValue(vol);
}
}
}
if (onStatusListener != null) {
onStatusListener.onStatus(response);
}
break;
}
}
/**
* 获取电台的音频信息
*
* @param result 返回信息
*/
private void setAudioInfo(String result) {
String[] keys = result.split(" ");
for (int i = 0; i < keys.length; i++) {
String[] val = keys[i].split("=");
if (val[0].equalsIgnoreCase("period")) period = Integer.parseInt(val[1]) / 1000;
if (val[0].equalsIgnoreCase("frames")) frames = Integer.parseInt(val[1]);
}
Log.d(TAG, String.format("set audio para:frames=%d,period=%d", frames, period));
}
public synchronized void sendData(byte[] data) {
tcpClient.sendByte(data);
}
/**
* 制作命令命令序号规则后3位是命令的种类序号除1000是命令的真正序号
*
* @param command 命令的种类
* @param cmdContent 命令的具体内容
*/
@SuppressLint("DefaultLocale")
public void sendCommand(XieguCommand command, String cmdContent) {
if (tcpClient.isConnect()) {
commandSeq++;
xieguCommand = command;
commandStr = String.format("C%05d%03d|%s\n", commandSeq, command.ordinal()
, cmdContent);
tcpClient.sendByte(commandStr.getBytes());
Log.d(TAG, "sendCommand: " + commandStr);
}
}
public synchronized void commandPTTOnOff(boolean on) {
if (on) {
sendCommand(XieguCommand.PTT, "ptt on");
} else {
sendCommand(XieguCommand.PTT, "ptt off");
}
}
/**
* 发射的采样率为12000采样率单声道,16位
*
* @param data 音频
*/
public void sendWaveData(float[] data) {
Log.d(TAG, String.format("send wav data,len:%d....", data.length));
short[] temp = new short[data.length];
//传递过来的音频是LPCM,32 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;
}
}
/**
* 分割消息的头和内容并分别负值给head和content
*
* @param line 消息
* @param split 分隔符
*/
private void getHeadAndContent(String line, String split) {
String[] temp = line.split(split);
if (temp.length > 1) {
head = temp[0];
resultCode = Integer.parseInt(temp[1]);
} else {
head = "";
}
if (temp.length > 2) {
resultContent = temp[2];
} else {
resultContent = "";
}
}
}
}