FT8CN/ft8cn/app/src/main/java/com/bg7yoz/ft8cn/ft8transmit/GenerateFT8.java

270 wiersze
9.6 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.ft8transmit;
/**
* 生成FT8音频信号的类。音频数据是32位的浮点数组。
* @author BGY70Z
* @date 2023-03-20
*/
import android.util.Log;
import com.bg7yoz.ft8cn.Ft8Message;
import com.bg7yoz.ft8cn.GeneralVariables;
import com.bg7yoz.ft8cn.R;
import com.bg7yoz.ft8cn.ft8signal.FT8Package;
import com.bg7yoz.ft8cn.ui.ToastMessage;
public class GenerateFT8 {
private static final String TAG = "GenerateFT8";
private static final int FTX_LDPC_K = 91;
public static final int FTX_LDPC_K_BYTES = (FTX_LDPC_K + 7) / 8;
private static final int FT8_NN = 79;
private static final float FT8_SYMBOL_PERIOD = 0.160f;
private static final float FT8_SYMBOL_BT = 2.0f;
private static final float FT8_SLOT_TIME = 15.0f;
private static final int Ft8num_samples = 15 * 12000;
private static final float M_PI = 3.14159265358979323846f;
public static final int num_tones = FT8_NN;//符号数量FT8是79个FT4是105个。
public static final float symbol_period = FT8_SYMBOL_PERIOD;//FT8_SYMBOL_PERIOD=0.160f
private static final float symbol_bt = FT8_SYMBOL_BT;//FT8_SYMBOL_BT=2.0f
private static final float slot_time = FT8_SLOT_TIME;//FT8_SLOT_TIME=15f
//public static int sample_rate = 48000;//采样率
//public static int sample_rate = 12000;//采样率
static {
System.loadLibrary("ft8cn");
}
public static int checkI3ByCallsign(String callsign) {
String substring = callsign.substring(callsign.length() - 2);
if (substring.equals("/P")) {
if (callsign.length() <= 8) {
return 2;//i3=2消息
} else {
return 4;//说明时非标准呼号
}
}
if (substring.equals("/R")) {
if (callsign.length() <= 8) {
return 1;//i3=2消息
} else {
return 4;//说明时非标准呼号
}
}
if (callsign.contains("/")) {//除了/P /R以外其余的都是非标准呼号
return 4;
}
if (callsign.length() > 6) {//呼号大于6位也是非标准呼号
return 4;
}
if (callsign.length() == 0) {//没有呼号,就是自由文本
return 0;
}
return 1;
}
public static String byteToBinString(byte[] data) {
if (data == null) {
return "";
}
StringBuilder string = new StringBuilder();
for (int i = 0; i < data.length; i++) {
string.append(String.format(",%8s", Integer.toBinaryString(data[i] & 0xff)).replace(" ", "0"));
}
return string.toString();
}
public static String byteToHexString(byte[] data) {
StringBuilder string = new StringBuilder();
for (int i = 0; i < data.length; i++) {
string.append(String.format(",%02X", data[i]));
}
return string.toString();
}
/**
* 检查是不是标准呼号
*
* @param callsign 呼号
* @return 是不是
*/
public static boolean checkIsStandardCallsign(String callsign) {
String temp;
if (callsign.endsWith("/P") || callsign.endsWith("/R")){
temp=callsign.substring(0,callsign.length()-2);
}else {
temp=callsign;
}
//FT8的认定标准业余呼号由一个或两个字符的前缀组成其中至少一个必须是字母后跟一个十进制数字和最多三个字母的后缀。
return temp.matches("[A-Z0-9]?[A-Z0-9][0-9][A-Z][A-Z0-9]?[A-Z]?");
}
/**
* 检查是不是信号报告
*
* @param extraInfo 扩展消息
* @return 是不是
*/
private static boolean checkIsReport(String extraInfo) {
if (extraInfo.equals("73") || extraInfo.equals("RRR")
|| extraInfo.equals("RR73")||extraInfo.equals("")) {
return false;
}
return !extraInfo.trim().matches("[A-Z][A-Z][0-9][0-9]");
}
public static float[] generateFt8(Ft8Message msg, float frequency,int sample_rate){
return generateFt8(msg,frequency,sample_rate,true);
}
public static byte[] generateA91(Ft8Message msg,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="";
}
//判定用非标准呼号i3=4的条件
//1.FROMCALL为非标准呼号 ,且 符合2或3
//2.扩展消息时 网格、RR73,RRR,73
//3.CQ,QRZ,DE
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"))) {
} else if (msg.callsignFrom.endsWith("/P")//如果目标有/P后缀则以目标呼号为准。如果目标没有/P后缀则以发送方是否有/P后缀为准
||(msg.callsignTo.endsWith("/P")&&(!msg.callsignFrom.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);
}
return packed;
}
/**
* 生成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="";
// }
//判定用非标准呼号i3=4的条件
//1.FROMCALL为非标准呼号 ,且 符合2或3
//2.扩展消息时 网格、RR73,RRR,73
//3.CQ,QRZ,DE
// 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);
// }
return generateFt8ByA91(generateA91(msg,hasModifier),frequency,sample_rate);
//return generateFt8ByA91(packed,frequency,sample_rate);
}
public static float[] generateFt8ByA91(byte[] a91, float frequency,int sample_rate){
byte[] tones = new byte[num_tones]; // 79音调符号数组
//此处是12个字节91+7/8可以使用a91生成音频
ft8_encode(a91, tones);
// 第三将FSK音调转换为音频信号b
int num_samples = (int) (0.5f + num_tones * symbol_period * sample_rate); // 数据信号中的采样数0.5+79*0.16*12000
float[] signal = new float[num_samples];
//Ft8num_sampleFT8声音的总采样数不是字节数。15*12000
//for (int i = 0; i < Ft8num_samples; i++)//把数据全部静音。
for (int i = 0; i < num_samples; i++)//把数据全部静音。
{
signal[i] = 0;
}
// 用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;
}
private static native int packFreeTextTo77(String msg, byte[] c77);
private static native int pack77(String msg, byte[] c77);
private static native void ft8_encode(byte[] payload, byte[] tones);
private static native void synth_gfsk(byte[] symbols, int n_sym, float f0,
float symbol_bt, float symbol_period,
int signal_rate, float[] signal, int offset);
}