Merge branch 'master' into legacy

legacy
sh123 2022-07-23 17:26:08 +03:00
commit 5043aa9d06
11 zmienionych plików z 166 dodań i 30 usunięć

Wyświetl plik

@ -167,7 +167,7 @@ public class AppWorker extends Thread {
Message msg = new Message();
msg.what = AppMessage.CMD_SEND_LOCATION_TO_TNC.toInt();
msg.obj = position;
Log.i(TAG, String.format("Position sent: %f %f %f %f %f",
Log.i(TAG, String.format("Position sent: lat: %f, lon: %f, course: %f, speed: %f, alt: %f",
position.latitude, position.longitude,
position.bearingDegrees, position.speedMetersPerSecond, position.altitudeMeters));
_onMessageReceived.sendMessage(msg);
@ -226,9 +226,10 @@ public class AppWorker extends Thread {
private final ProtocolCallback _protocolCallback = new ProtocolCallback() {
@Override
protected void onReceivePosition(Position position) {
Log.i(TAG, String.format("Position received: %f %f %f %f %f",
Log.i(TAG, String.format("Position received: lat: %f, lon: %f, course: %f, speed: %f, alt: %f, sym: %s, status: %s, comment: %s",
position.latitude, position.longitude,
position.bearingDegrees, position.speedMetersPerSecond, position.altitudeMeters));
position.bearingDegrees, position.speedMetersPerSecond, position.altitudeMeters,
position.symbolCode, position.status, position.comment));
// TODO, store to database
}

Wyświetl plik

@ -145,11 +145,10 @@ public class Aprs implements Protocol {
protected void onReceiveData(String src, String dst, byte[] data) {
if (data.length == 0) return;
AprsDataType dataType = new AprsDataType((char)data[0]);
AprsData aprsData = AprsDataFactory.fromBinary(data);
AprsData aprsData = AprsDataFactory.fromBinary(src, dst, data);
if (aprsData != null && aprsData.isValid()) {
if (dataType.isTextMessage()) {
TextMessage textMessage = aprsData.toTextMessage();
textMessage.src = src;
_parentProtocolCallback.onReceiveTextMessage(textMessage);
return;
} else if (dataType.isPositionReport()) {

Wyświetl plik

@ -8,7 +8,7 @@ public interface AprsData {
void fromTextMessage(TextMessage textMessage);
Position toPosition();
TextMessage toTextMessage();
void fromBinary(byte[] infoData);
void fromBinary(String srcCallsign, String dstCallsign, byte[] infoData);
byte[] toBinary();
boolean isValid();
}

Wyświetl plik

@ -22,14 +22,14 @@ public class AprsDataFactory {
return null;
}
public static AprsData fromBinary(byte[] infoData) {
public static AprsData fromBinary(String srcCallsign, String dstCallsign, byte[] infoData) {
ByteBuffer buffer = ByteBuffer.wrap(infoData);
AprsDataType dataType = new AprsDataType((char)buffer.get());
AprsData aprsData = create(dataType);
if (aprsData == null) return null;
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
aprsData.fromBinary(data);
aprsData.fromBinary(srcCallsign, dstCallsign, data);
return aprsData;
}
}

Wyświetl plik

@ -4,6 +4,7 @@ import com.radio.codec2talkie.protocol.aprs.tools.AprsTools;
import com.radio.codec2talkie.protocol.message.TextMessage;
import com.radio.codec2talkie.protocol.position.Position;
import com.radio.codec2talkie.tools.MathTools;
import com.radio.codec2talkie.tools.TextTools;
import com.radio.codec2talkie.tools.UnitTools;
import java.nio.ByteBuffer;
@ -43,9 +44,13 @@ public class AprsDataPositionReport implements AprsData {
}
@Override
public void fromBinary(byte[] infoData) {
public void fromBinary(String srcCallsign, String dstCallsign, byte[] infoData) {
_isValid = false;
_position = new Position();
_position.srcCallsign = srcCallsign;
_position.dstCallsign = dstCallsign;
_position.status = "";
_position.privacyLevel = 0;
if ((infoData[0] == '/' || infoData[0] == '\\') && fromCompressedBinary(infoData)) {
_position.isCompressed = true;
_isValid = true;
@ -201,18 +206,23 @@ public class AprsDataPositionReport implements AprsData {
byte[] tail = new byte[buffer.remaining()];
buffer.get(tail);
String strTail = new String(tail);
Pattern latLonPattern = Pattern.compile("^(\\d{4}[.]\\d{2})(N|S)([/\\\\])(\\d{5}[.]\\d{2})(E|W)(\\S)(.+)$");
Pattern latLonPattern = Pattern.compile("^([\\d ]{4}[.][\\d ]{2})(N|S)([/\\\\])([\\d ]{5}[.][\\d ]{2})(E|W)(\\S)(.+)$");
Matcher latLonMatcher = latLonPattern.matcher(strTail);
if (!latLonMatcher.matches()) return false;
String lat = latLonMatcher.group(1);
String latSuffix = latLonMatcher.group(2);
if (lat == null || latSuffix == null) return false;
_position.privacyLevel = TextTools.countChars(lat, ' ');
// NOTE, ambiguity, replace with 0
lat = lat.replace(' ', '0');
_position.latitude = UnitTools.nmeaToDecimal(lat, latSuffix);
String table = latLonMatcher.group(3);
String lon = latLonMatcher.group(4);
String lonSuffix = latLonMatcher.group(5);
if (lon == null || lonSuffix == null) return false;
// NOTE, ambiguity, replace with 0
lon = lon.replace(' ', '0');
_position.longitude = UnitTools.nmeaToDecimal(lon, lonSuffix);
String symbol = latLonMatcher.group(6);
_position.symbolCode = String.format("%s%s", table, symbol);

Wyświetl plik

@ -6,7 +6,9 @@ import com.radio.codec2talkie.protocol.position.Position;
import com.radio.codec2talkie.tools.UnitTools;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
public class AprsDataPositionReportMicE implements AprsData {
@ -37,6 +39,28 @@ public class AprsDataPositionReportMicE implements AprsData {
put("emergency", 0b000);
}};
private final Map<Integer, String> _miceMessageReverseTypeMapStd = new HashMap<Integer, String>() {{
put(0b000, "emergency");
put(0b111, "off_duty");
put(0b110, "en_route");
put(0b101, "in_service");
put(0b100, "returning");
put(0b011, "committed");
put(0b010, "special");
put(0b001, "priority");
}};
private final Map<Integer, String> _miceMessageReverseTypeMapCustom = new HashMap<Integer, String>() {{
put(0b000, "emergency");
put(0b111, "custom_0");
put(0b110, "custom_1");
put(0b101, "custom_2");
put(0b100, "custom_3");
put(0b011, "custom_4");
put(0b010, "custom_5");
put(0b001, "custom_6");
}};
@Override
public void fromPosition(Position position) {
_isValid = false;
@ -85,18 +109,106 @@ public class AprsDataPositionReportMicE implements AprsData {
}
@Override
public void fromBinary(byte[] infoData) {
public void fromBinary(String srcCallsign, String dstCallsign, byte[] infoData) {
_isValid = false;
// TODO, implement fromBinary, needs dst callsign
// ByteBuffer buffer = ByteBuffer.wrap(infoData);
_position = new Position();
_dstCallsign = dstCallsign;
_position.srcCallsign = srcCallsign;
_position.dstCallsign = dstCallsign;
_position.privacyLevel = 0;
_position.status = "";
_position.comment = "";
if (srcCallsign == null || dstCallsign == null) return;
if (dstCallsign.length() < 6 || infoData.length < 8) return;
// read latitude
// read symbol table
boolean isCustom = false;
char ns = 'S';
char we = 'E';
int longOffset = 0;
int messageId = 0;
byte[] dstCallsignBuf = dstCallsign.getBytes();
StringBuilder latitude = new StringBuilder();
for (int i = 0; i < 6; i++) {
char c = (char) dstCallsignBuf[i];
if (c >= 'A' && c <= 'L') {
if (i < 3) {
isCustom = true;
if (c != 'L') messageId |= 1;
}
if (c == 'K' || c == 'L') {
// NOTE, using 0 instead of ' ' for position ambiguity
c = '0';
_position.privacyLevel += 1;
} else
c = (char) (c - 17);
} else if (c >= 'P' && c <= 'Z') {
if (i < 3) {
messageId |= 1;
} else if (i == 3) {
ns = 'N';
} else if (i == 4) {
longOffset = 100;
} else {
we = 'W';
}
if (c == 'Z') {
// NOTE, using 0 instead of ' ' for position ambiguity
c = '0';
_position.privacyLevel += 1;
} else
c = (char) (c - 32);
}
if (i < 2) messageId <<= 1;
if (i == 4) latitude.append('.');
latitude.append(c);
}
_position.latitude = UnitTools.nmeaToDecimal(latitude.toString(), Character.toString(ns));
_position.status = isCustom
? _miceMessageReverseTypeMapCustom.get(messageId)
: _miceMessageReverseTypeMapStd.get(messageId);
// read longitude
// read symbol
int d = ((int)infoData[0] - 28) + longOffset;
if (d >= 180 && d <= 189) d -= 80;
else if (d >= 190) d -= 190;
int m = ((int)infoData[1] - 28);
if (m >= 60) m -= 60;
int h = ((int)infoData[2] - 28);
String longitude = String.format(Locale.US, "%03d%d.%d", d, m, h);
_position.longitude = UnitTools.nmeaToDecimal(longitude, Character.toString(we));
// read course/speed
// read altitude (could be anywhere inside the comment)
int sp = 10 * ((int)infoData[3] - 28);
int dcSp = ((int)infoData[4] - 28) / 10;
int dcSe = (((int)infoData[4] - 28) % 10) * 100;
int se = (int)infoData[5] - 28;
int speed = sp + dcSp;
if (speed >= 800) speed -= 800;
int course = dcSe + se;
if (course >= 400) course -= 400;
_position.hasBearing = true;
_position.bearingDegrees = course;
_position.hasSpeed = true;
_position.speedMetersPerSecond = UnitTools.knotsToMetersPerSecond(speed);
// read symbol table + symbol code
_position.symbolCode = String.format(Locale.US, "%c%c", infoData[7], infoData[6]);
// read comment until the end
// _isValid = true;
_position.comment = new String(Arrays.copyOfRange(infoData, 8, infoData.length));
_isValid = true;
}
@Override
@ -191,7 +303,7 @@ public class AprsDataPositionReportMicE implements AprsData {
lonMinHun += 28;
buffer.put(lonMinHun);
// encode speed/cou8rse
// encode speed/course
long speed = UnitTools.metersPerSecondToKnots(position.speedMetersPerSecond);
byte speedHun = (byte)((speed / 10) + 28);
buffer.put(speedHun);

Wyświetl plik

@ -7,6 +7,7 @@ import java.nio.ByteBuffer;
public class AprsDataTextMessage implements AprsData {
public String srcCallsign;
public String dstCallsign;
public String textMessage;
@ -32,20 +33,22 @@ public class AprsDataTextMessage implements AprsData {
@Override
public TextMessage toTextMessage() {
TextMessage textMessage = new TextMessage();
textMessage.src = this.srcCallsign;
textMessage.dst = this.dstCallsign;
textMessage.text = this.textMessage;
return textMessage;
}
@Override
public void fromBinary(byte[] infoData) {
public void fromBinary(String srcCallsign, String dstCallsign, byte[] infoData) {
_isValid = false;
if (infoData.length < 10) return;
this.srcCallsign = srcCallsign;
ByteBuffer buffer = ByteBuffer.wrap(infoData);
// callsign, trim ending spaces
byte[] callsign = new byte[9];
buffer.get(callsign);
dstCallsign = new String(callsign).replaceAll("\\s+$", "");
this.dstCallsign = new String(callsign).replaceAll("\\s+$", "");
// ':' separator
byte b = buffer.get();
if (b != ':') return;

Wyświetl plik

@ -9,8 +9,8 @@ public class Position {
public double latitude;
public double longitude;
public double altitudeMeters;
public float bearingDegrees;
public float speedMetersPerSecond;
public double bearingDegrees;
public double speedMetersPerSecond;
public String status;
public String comment;
public String symbolCode;

Wyświetl plik

@ -4,4 +4,15 @@ public class TextTools {
public static String addZeroWidthSpaces(String text) {
return text.replaceAll(".(?!$)", "$0\u200b");
}
public static int countChars(String text, char ch) {
int count = 0;
for (int i = 0; i < text.length(); i++) {
if (text.charAt(i) == ch) {
count++;
}
}
return count;
}
}

Wyświetl plik

@ -9,7 +9,7 @@ public class UnitTools {
double degreesIntegral = Math.abs(degrees) - degreesFractional;
degreesFractional *= 60;
degreesIntegral *= 100;
long nmeaDouble = (long)Math.round((degreesIntegral + degreesFractional) * 100.0);
long nmeaDouble = Math.round((degreesIntegral + degreesFractional) * 100.0);
return String.format(
Locale.US,
isLatitude ? "%06d%c" : "%07d%c",
@ -37,22 +37,22 @@ public class UnitTools {
}
public static long metersToFeet(double meters) {
return (long)(meters * 3.2808);
return (long)(meters * 3.28084);
}
public static double feetToMeters(long feet) {
return feet * 0.3048;
return feet / 3.28084;
}
public static long metersPerSecondToKnots(float metersPerSecond) {
public static long metersPerSecondToKnots(double metersPerSecond) {
return (long)(metersPerSecond / 0.514444);
}
public static float knotsToMetersPerSecond(long knots) {
return (float) (knots * 0.5144444);
public static double knotsToMetersPerSecond(long knots) {
return knots * 0.5144444;
}
public static double metersPerSecondToMilesPerHour(float metersPerSecond) {
public static double metersPerSecondToMilesPerHour(double metersPerSecond) {
return metersPerSecond * 2.23693629;
}

Wyświetl plik

@ -99,7 +99,7 @@ public class Smart implements Tracker {
private boolean isCornerPeggingTriggered(Position newPosition, Position oldPosition) {
long timeDifferenceSeconds = UnitTools.millisToSeconds(newPosition.timestampEpochMs - oldPosition.timestampEpochMs);
float turnAngleDegrees = Math.abs(newPosition.bearingDegrees - oldPosition.bearingDegrees) % 360;
double turnAngleDegrees = Math.abs(newPosition.bearingDegrees - oldPosition.bearingDegrees) % 360;
turnAngleDegrees = turnAngleDegrees <= 180 ? turnAngleDegrees : 360 - turnAngleDegrees;
if (!newPosition.hasBearing || newPosition.speedMetersPerSecond == 0)