FT8CN/ft8cn/app/src/main/java/com/bg7yoz/ft8cn/ft8signal/FT8Package.java

377 wiersze
14 KiB
Java
Czysty Zwykły widok Historia

package com.bg7yoz.ft8cn.ft8signal;
/**
* FT8
*
* @author BGY70Z
* @date 2023-03-20
*/
import android.util.Log;
import com.bg7yoz.ft8cn.Ft8Message;
import com.bg7yoz.ft8cn.ft8transmit.GenerateFT8;
public class FT8Package {
private static final String TAG = "FT8Package";
public static final int NTOKENS = 2063592;
public static final int MAX22 = 4194304;
public static final int MAXGRID4 = 32400;
private static final String A1 = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String A2 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String A3 = "0123456789";
private static final String A4 = " ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String A5 = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ/";
static {
System.loadLibrary("ft8cn");
}
/**
* i3=477
* @param message
* @return
*/
public static byte[] generatePack77_i4(Ft8Message message) {
String toCall = message.callsignTo.replace("<", "").replace(">", "");
String fromCall = message.callsignFrom.replace("<", "").replace(">", "");
int hash12;
if (message.checkIsCQ()) {//如果是CQ就把自己的呼号的哈希加上
hash12 = getHash12(fromCall);
} else {
hash12 = getHash12(toCall);
}
if (fromCall.length() > 11) {//非标准呼号的长度不等长于11位
fromCall = fromCall.substring(0, 11);
}
byte[] data = new byte[10];
long n58 = 0;
for (int i = 0; i < fromCall.length(); i++) {
n58 = n58 * 38 + A5.indexOf(fromCall.charAt(i));
}
//n58=3479529522318088L;
data[0] = (byte) ((hash12 & 0x00000fff) >> 4);
data[1] = (byte) ((hash12 & 0x0000000f) << 4);
data[1] = (byte) (data[1] | ((n58 & 0x0fff_ffff_ffff_ffffL) >> 54));
data[2] = (byte) (((n58 & 0x00ff_ffff_ffff_ffffL) >> 54 - 8));
data[3] = (byte) (((n58 & 0x0000_ffff_ffff_ffffL) >> 54 - 8 - 8));
data[4] = (byte) (((n58 & 0x0000_00ff_ffff_ffffL) >> 54 - 8 - 8 - 8));
data[5] = (byte) (((n58 & 0x0000_0000_ffff_ffffL) >> 54 - 8 - 8 - 8 - 8));
data[6] = (byte) (((n58 & 0x0000_0000_00ff_ffffL) >> 54 - 8 - 8 - 8 - 8 - 8));
data[7] = (byte) (((n58 & 0x0000_0000_0000_ffffL) >> 54 - 48));
data[8] = (byte) (((n58 & 0x0000_0000_0000_00ffL) << 2));
//RRR=1,RR73=2,73=3,""=0
if (message.checkIsCQ()) {
data[9] = (byte) 0x60;
} else {
data[9] = (byte) 0x20;
switch (message.extraInfo) {
case "RRR": //r2=1
data[8] = (byte) (data[8] & 0xfe);
data[9] = (byte) (data[9] | 0x80);
break;
case "RR73": //r2=2
data[8] = (byte) (data[8] | 0x01);
//data[9] = (byte) (data[9] | 0x00);//data[9]无需改变
break;
case "73": //r2=3
data[8] = (byte) (data[8] | 0x01);
data[9] = (byte) (data[9] | 0x80);
break;
}
}
return data;
}
/**
* /
*
* /FT8
* @param compoundCallsign
* @return
*/
public static String getStdCall(String compoundCallsign) {
if (!compoundCallsign.contains("/")) return compoundCallsign;
String[] callsigns = compoundCallsign.split("/");
for (String callsign : callsigns) {//用正则表达式提取标准呼号
//FT8的认定标准业余呼号由一个或两个字符的前缀组成其中至少一个必须是字母后跟一个十进制数字和最多三个字母的后缀。
if (callsign.matches("[A-Z0-9]?[A-Z0-9][0-9][A-Z][A-Z0-9]?[A-Z]?")) {
return callsign;
}
}
//当无法提取标准呼号时,取最长的字段
int len = 0;
int index = 0;
for (int i = 0; i < callsigns.length; i++) {
if (callsigns[i].length() > len) {
len = callsigns[i].length();
index = i;
}
}
return callsigns[index];
}
/**
* i1=1,i1=2FT8EU VHF
* i1=1/Ri1=2/P
*
*
* @param message
* @return packet77
*/
public static byte[] generatePack77_i1(Ft8Message message) {
String toCall = message.callsignTo.replace("<", "").replace(">", "");
String fromCall = message.callsignFrom.replace("<", "").replace(">", "");
if (message.checkIsCQ() && message.modifier != null) {//把修饰符加上
if (message.modifier.length() > 0) {
toCall = toCall + " " + message.modifier;
}
}
//如果以/P 或/R结尾的呼号要把这个/P /R去掉
if (toCall.endsWith("/P") || toCall.endsWith("/R")) {
toCall = toCall.substring(0, toCall.length() - 2);
}
if (fromCall.endsWith("/P") || fromCall.endsWith("/R")) {
fromCall = fromCall.substring(0, fromCall.length() - 2);
// fromCall = message.callsignFrom.substring(0, message.callsignFrom.length() - 2);
}
//当双方都是复合呼号或非标准呼号时(带/的呼号),我的呼号变成标准呼号
if ((toCall.contains("/")) && fromCall.contains("/")) {
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];
data[0] = (byte) ((pack_c28(toCall) & 0x0fffffff) >> 20);
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] | (r1_p1 << 3));
data[3] = (byte) (data[3] | (pack_c28(fromCall) & 0x00fffffff) >> 25);
data[4] = (byte) ((pack_c28(fromCall) & 0x003ffffff) >> 25 - 8);
data[5] = (byte) ((pack_c28(fromCall) & 0x00003ffff) >> 25 - 8 - 8);
data[6] = (byte) ((pack_c28(fromCall) & 0x0000003ff) >> 25 - 8 - 8 - 8);
data[7] = (byte) ((pack_c28(fromCall) & 0x0000000ff) << 7);
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);
data[9] = (byte) (data[9] | (message.i3 & 0x3) << 3);
return data;
}
/**
* R1+g1516R1R-17:R1=1,-17:R1=0
*
* @param grid4
* @return R1+g15
*/
public static int pack_R1_g15(String grid4) {
if (grid4 == null)// 只有两个呼号,没有信号报告和网格
{
return MAXGRID4 + 1;
}
if (grid4.length() == 0) {// 只有两个呼号,没有信号报告和网格
return MAXGRID4 + 1;
}
// 特殊的报告RRR,RR73,73
if (grid4.equals("RRR"))
return MAXGRID4 + 2;
if (grid4.equals("RR73"))
return MAXGRID4 + 3;
if (grid4.equals("73"))
return MAXGRID4 + 4;
// 检查是不是标准的4字符网格
if (grid4.matches("[A-Z][A-Z][0-9][0-9]")) {
int igrid4 = grid4.charAt(0) - 'A';
igrid4 = igrid4 * 18 + (grid4.charAt(1) - 'A');
igrid4 = igrid4 * 10 + (grid4.charAt(2) - '0');
igrid4 = igrid4 * 10 + (grid4.charAt(3) - '0');
return igrid4;
}
// 检查是不是信号报告: +dd / -dd / R+dd / R-dd
// 信号报告在-30到99dB之间
// 信号报告的正则:[R]?[+-][0-9]{1,2}
String s = grid4;
if (grid4.charAt(0) == 'R') {
s = grid4.substring(1);
int irpt = 35 + Integer.parseInt(s);
return (MAXGRID4 + irpt) | 0x8000; // R1 = 1
} else {
int irpt = 35 + Integer.parseInt(grid4);
return (MAXGRID4 + irpt); // R1 = 0
}
}
public static byte pack_r1_p1(String callsign) {
String s = callsign.substring(callsign.length() - 2);
if (s.equals("/R") || s.equals("/P")) {
return 1;
} else {
return 0;
}
}
/**
* c28,/R/Phash22+2063592;
*
* @param callsign
* @return c28
*/
public static int pack_c28(String callsign) {
//byte[] data=new byte[]{(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00};
switch (callsign) {
case "DE":
return 0;
case "QRZ":
return 1;
case "CQ":
return 2;
}
//判断是否有修饰符000-999,A-Z,AA-ZZ,AAA-ZZZ,AAAA-ZZZZ
if (callsign.startsWith("CQ ") && callsign.length() > 3) {
String temp = callsign.substring(3).trim().toUpperCase();
if (temp.matches("[0-9]{3}")) {
int i = Integer.parseInt(temp);
return i + 3;
}
if (temp.matches("[A-Z]{1,4}")) {
int a0 = 0;
int a1 = 0;
int a2 = 0;
int a3 = 0;
if (temp.length() == 1) {//A-Z
a0 = (int) temp.charAt(0) - 65;
return a0 + 1004;
}
if (temp.length() == 2) {//AA-ZZ
a0 = (int) temp.charAt(0) - 65;
a1 = (int) temp.charAt(1) - 65;
return a0 * 27 + a1 + 1031;
}
if (temp.length() == 3) {//AAA-ZZZ
a0 = (int) temp.charAt(0) - 65;
a1 = (int) temp.charAt(1) - 65;
a2 = (int) temp.charAt(2) - 65;
return a0 * 27 * 27 + a1 * 27 + a2 + 1760;
}
if (temp.length() == 4) {//AAAA-ZZZZ
a0 = (int) temp.charAt(0) - 65;
a1 = (int) temp.charAt(1) - 65;
a2 = (int) temp.charAt(2) - 65;
a3 = (int) temp.charAt(3) - 65;
return a0 * 27 * 27 * 27 + a1 * 27 * 27 + a2 * 27 + a3 + 21443;
}
}
}
//格式化成标准的呼号。6位、第3位带数字
//c6也可以是非标准呼号。大于6位的都是非标准呼号
String c6 = formatCallsign(callsign);
//判断是不是标准呼号
if (!GenerateFT8.checkIsStandardCallsign(callsign)) {//生成HASH22+2063592
return NTOKENS + getHash22(callsign);
}
//6位呼号取值
int i0, i1, i2, i3, i4, i5;
i0 = A1.indexOf(c6.substring(0, 1));
i1 = A2.indexOf(c6.substring(1, 2));
i2 = A3.indexOf(c6.substring(2, 3));
i3 = A4.indexOf(c6.substring(3, 4));
i4 = A4.indexOf(c6.substring(4, 5));
i5 = A4.indexOf(c6.substring(5, 6));
int n28 = i0;
n28 = n28 * 36 + i1;
n28 = n28 * 10 + i2;
n28 = n28 * 27 + i3;
n28 = n28 * 27 + i4;
n28 = n28 * 27 + i5;
return NTOKENS + MAX22 + n28;
}
/**
*
* 61~2+13
*
* 1.Swaziland: 3DA0XYZ -> 3D0XYZ
* 2.Guinea: 3XA0XYZ -> QA0XYZ
* 3.2A0XYZ->" A0XYZ"
* 4.3BA2BI->"BA2BI "
*
* @param callsign
* @return C28int
*/
private static String formatCallsign(String callsign) {
String c6 = callsign;
// 解决斯威士兰Swaziland的呼号前缀问题: 3DA0XYZ -> 3D0XYZ
if (callsign.length() > 3 && callsign.substring(0, 4).equals("3DA0") && callsign.length() <= 7) {
c6 = "3D0" + callsign.substring(4);
// 解决几内亚Guinea呼号前缀问题: 3XA0XYZ -> QA0XYZ
} else if (callsign.length() > 3 && callsign.substring(0, 3).matches("3X[A-Z]") && callsign.length() <= 7) {
c6 = "Q" + callsign.substring(2);
} else {
// 第2位是数字,第3位是字母的要在前面补充空格A0XYZ -> " A0XYZ",A6开头的除外
if (callsign.substring(0, 3).matches("[A-Z][0-9][A-Z]")) {
c6 = " " + callsign;
}
}
if (c6.length() < 6) {//如果长度不足6位结尾补充空格
for (int i = 0; i < 6 - c6.length() + 1; i++) {
c6 = c6 + " ";
}
}
return c6;
}
public static native int getHash12(String callsign);
public static native int getHash10(String callsign);
public static native int getHash22(String callsign);
}