From 307ad8b8b224ca366299d8e4741f08446913cfc1 Mon Sep 17 00:00:00 2001 From: dernasherbrezon Date: Fri, 9 Aug 2024 23:19:46 +0200 Subject: [PATCH] add support for GRBBETA --- README.md | 5 + .../r2cloud/jradio/grbbeta/GRBBetaBeacon.java | 82 ++++++++++ .../ru/r2cloud/jradio/grbbeta/TrxBeacon.java | 151 ++++++++++++++++++ .../jradio/grbbeta/GRBBetaBeaconTest.java | 37 +++++ .../resources/expected/GRBBetaBeaconCsp.json | 36 +++++ .../resources/expected/GRBBetaBeaconTrx.json | 36 +++++ 6 files changed, 347 insertions(+) create mode 100644 src/main/java/ru/r2cloud/jradio/grbbeta/GRBBetaBeacon.java create mode 100644 src/main/java/ru/r2cloud/jradio/grbbeta/TrxBeacon.java create mode 100644 src/test/java/ru/r2cloud/jradio/grbbeta/GRBBetaBeaconTest.java create mode 100644 src/test/resources/expected/GRBBetaBeaconCsp.json create mode 100644 src/test/resources/expected/GRBBetaBeaconTrx.json diff --git a/README.md b/README.md index f6311c01..b73183bd 100644 --- a/README.md +++ b/README.md @@ -599,6 +599,11 @@ jradio has lots of built-in satellite decoders. Some of them have non standard d 52191, 52192, 52188 ru.r2cloud.jradio.suchai2.Suchai2Beacon + + GRBBETA + 60237 + ru.r2cloud.jradio.grbbeta.GRBBetaBeacon + diff --git a/src/main/java/ru/r2cloud/jradio/grbbeta/GRBBetaBeacon.java b/src/main/java/ru/r2cloud/jradio/grbbeta/GRBBetaBeacon.java new file mode 100644 index 00000000..04a69f5b --- /dev/null +++ b/src/main/java/ru/r2cloud/jradio/grbbeta/GRBBetaBeacon.java @@ -0,0 +1,82 @@ +package ru.r2cloud.jradio.grbbeta; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import ru.r2cloud.jradio.Beacon; +import ru.r2cloud.jradio.csp.Header; +import ru.r2cloud.jradio.fec.ccsds.UncorrectableException; + +public class GRBBetaBeacon extends Beacon { + + private ru.r2cloud.jradio.ax25.Header ax25Header; + private Header cspHeader; + private TrxBeacon trx; + + private byte[] unknownPayload; + private String unknownMessage; + + @Override + public void readBeacon(byte[] data) throws IOException, UncorrectableException { + try { + DataInputStream dis = new DataInputStream(new ByteArrayInputStream(data)); + ax25Header = new ru.r2cloud.jradio.ax25.Header(dis); + byte[] bodyBytes = new byte[dis.available()]; + dis.readFully(bodyBytes); + String body = new String(bodyBytes, StandardCharsets.US_ASCII); + if (body.startsWith("U,")) { + trx = new TrxBeacon(body); + } else { + unknownMessage = body; + } + } catch (Exception e) { + DataInputStream dis = new DataInputStream(new ByteArrayInputStream(data)); + cspHeader = new Header(dis); + unknownPayload = new byte[dis.available()]; + dis.readFully(unknownPayload); + } + } + + public ru.r2cloud.jradio.ax25.Header getAx25Header() { + return ax25Header; + } + + public void setAx25Header(ru.r2cloud.jradio.ax25.Header ax25Header) { + this.ax25Header = ax25Header; + } + + public Header getCspHeader() { + return cspHeader; + } + + public void setCspHeader(Header cspHeader) { + this.cspHeader = cspHeader; + } + + public TrxBeacon getTrx() { + return trx; + } + + public void setTrx(TrxBeacon trx) { + this.trx = trx; + } + + public byte[] getUnknownPayload() { + return unknownPayload; + } + + public void setUnknownPayload(byte[] unknownPayload) { + this.unknownPayload = unknownPayload; + } + + public String getUnknownMessage() { + return unknownMessage; + } + + public void setUnknownMessage(String unknownMessage) { + this.unknownMessage = unknownMessage; + } + +} diff --git a/src/main/java/ru/r2cloud/jradio/grbbeta/TrxBeacon.java b/src/main/java/ru/r2cloud/jradio/grbbeta/TrxBeacon.java new file mode 100644 index 00000000..558cb2af --- /dev/null +++ b/src/main/java/ru/r2cloud/jradio/grbbeta/TrxBeacon.java @@ -0,0 +1,151 @@ +package ru.r2cloud.jradio.grbbeta; + +import java.util.regex.Pattern; + +public class TrxBeacon { + private static final Pattern COMMA = Pattern.compile(","); + + private long uptimeSinceReset; + private long uptime; + private long radioBootCount; + private long rfSegmentResetCount; + private float radioMcuTemperature; + private float rfChipTemperature; + private float rfPowerAmplifierTemperature; + private long digipeaterMessageCount; + private String lastDigipeaterCallsign; + private long rxDataPackets; + private long txDataPackets; + private float actualRssi; + private float rssiPreamble; + + public TrxBeacon() { + // do nothing + } + + public TrxBeacon(String message) { + String[] parts = COMMA.split(message.trim()); + int i = 1; + uptimeSinceReset = Long.valueOf(parts[i++]); + uptime = Long.valueOf(parts[i++]); + radioBootCount = Long.valueOf(parts[i++]); + rfSegmentResetCount = Long.valueOf(parts[i++]); + radioMcuTemperature = Long.valueOf(parts[i++]) * 0.01f; + rfChipTemperature = Long.valueOf(parts[i++]) * 0.01f; + rfPowerAmplifierTemperature = Long.valueOf(parts[i++]) * 0.01f; + digipeaterMessageCount = Long.valueOf(parts[i++]); + lastDigipeaterCallsign = parts[i++].trim(); + if (lastDigipeaterCallsign.length() == 0) { + lastDigipeaterCallsign = null; + } + rxDataPackets = Long.valueOf(parts[i++]); + txDataPackets = Long.valueOf(parts[i++]); + actualRssi = Long.valueOf(parts[i++]) / 2.0f - 134; + rssiPreamble = Long.valueOf(parts[i++]) / 2.0f - 134; + } + + public long getUptimeSinceReset() { + return uptimeSinceReset; + } + + public void setUptimeSinceReset(long uptimeSinceReset) { + this.uptimeSinceReset = uptimeSinceReset; + } + + public long getUptime() { + return uptime; + } + + public void setUptime(long uptime) { + this.uptime = uptime; + } + + public long getRadioBootCount() { + return radioBootCount; + } + + public void setRadioBootCount(long radioBootCount) { + this.radioBootCount = radioBootCount; + } + + public long getRfSegmentResetCount() { + return rfSegmentResetCount; + } + + public void setRfSegmentResetCount(long rfSegmentResetCount) { + this.rfSegmentResetCount = rfSegmentResetCount; + } + + public float getRadioMcuTemperature() { + return radioMcuTemperature; + } + + public void setRadioMcuTemperature(float radioMcuTemperature) { + this.radioMcuTemperature = radioMcuTemperature; + } + + public float getRfChipTemperature() { + return rfChipTemperature; + } + + public void setRfChipTemperature(float rfChipTemperature) { + this.rfChipTemperature = rfChipTemperature; + } + + public float getRfPowerAmplifierTemperature() { + return rfPowerAmplifierTemperature; + } + + public void setRfPowerAmplifierTemperature(float rfPowerAmplifierTemperature) { + this.rfPowerAmplifierTemperature = rfPowerAmplifierTemperature; + } + + public long getDigipeaterMessageCount() { + return digipeaterMessageCount; + } + + public void setDigipeaterMessageCount(long digipeaterMessageCount) { + this.digipeaterMessageCount = digipeaterMessageCount; + } + + public String getLastDigipeaterCallsign() { + return lastDigipeaterCallsign; + } + + public void setLastDigipeaterCallsign(String lastDigipeaterCallsign) { + this.lastDigipeaterCallsign = lastDigipeaterCallsign; + } + + public long getRxDataPackets() { + return rxDataPackets; + } + + public void setRxDataPackets(long rxDataPackets) { + this.rxDataPackets = rxDataPackets; + } + + public long getTxDataPackets() { + return txDataPackets; + } + + public void setTxDataPackets(long txDataPackets) { + this.txDataPackets = txDataPackets; + } + + public float getActualRssi() { + return actualRssi; + } + + public void setActualRssi(float actualRssi) { + this.actualRssi = actualRssi; + } + + public float getRssiPreamble() { + return rssiPreamble; + } + + public void setRssiPreamble(float rssiPreamble) { + this.rssiPreamble = rssiPreamble; + } + +} diff --git a/src/test/java/ru/r2cloud/jradio/grbbeta/GRBBetaBeaconTest.java b/src/test/java/ru/r2cloud/jradio/grbbeta/GRBBetaBeaconTest.java new file mode 100644 index 00000000..714ba3ba --- /dev/null +++ b/src/test/java/ru/r2cloud/jradio/grbbeta/GRBBetaBeaconTest.java @@ -0,0 +1,37 @@ +package ru.r2cloud.jradio.grbbeta; + +import static com.google.code.beanmatchers.BeanMatchers.hasValidBeanConstructor; +import static com.google.code.beanmatchers.BeanMatchers.hasValidGettersAndSetters; +import static org.hamcrest.CoreMatchers.allOf; +import static org.hamcrest.MatcherAssert.assertThat; + +import org.junit.Test; + +import ru.r2cloud.jradio.AssertJson; +import ru.r2cloud.jradio.fec.ViterbiTest; + +public class GRBBetaBeaconTest { + + @Test + public void testBeacon() throws Exception { + byte[] data = ViterbiTest.hexStringToByteArray("86A240404040609082648EA4846103F0552C323633373233312C323635393030372C322C302C313237352C313237342C313231382C302C2C313830382C3237393833322C37352C393200"); + GRBBetaBeacon result = new GRBBetaBeacon(); + result.readBeacon(data); + AssertJson.assertObjectsEqual("GRBBetaBeaconTrx.json", result); + } + + @Test + public void testCspBeacon() throws Exception { + byte[] data = ViterbiTest.hexStringToByteArray("4050EA3EB97297B4EAEC190B3621EE6447C69D532D"); + GRBBetaBeacon result = new GRBBetaBeacon(); + result.readBeacon(data); + AssertJson.assertObjectsEqual("GRBBetaBeaconCsp.json", result); + } + + @Test + public void testPojo() { + assertThat(GRBBetaBeacon.class, allOf(hasValidBeanConstructor(), hasValidGettersAndSetters())); + assertThat(TrxBeacon.class, allOf(hasValidBeanConstructor(), hasValidGettersAndSetters())); + } + +} diff --git a/src/test/resources/expected/GRBBetaBeaconCsp.json b/src/test/resources/expected/GRBBetaBeaconCsp.json new file mode 100644 index 00000000..687ad795 --- /dev/null +++ b/src/test/resources/expected/GRBBetaBeaconCsp.json @@ -0,0 +1,36 @@ +{ + "cspHeader": { + "priority": "CSP_PRIO_HIGH", + "source": 0, + "destination": 5, + "sourcePort": 42, + "destinationPort": 3, + "ffrag": true, + "fhmac": true, + "fxtea": true, + "frdp": true, + "fcrc32": false + }, + "unknownPayload": [ + -71, + 114, + -105, + -76, + -22, + -20, + 25, + 11, + 54, + 33, + -18, + 100, + 71, + -58, + -99, + 83, + 45 + ], + "beginSample": 0, + "beginMillis": 0, + "endSample": 0 +} \ No newline at end of file diff --git a/src/test/resources/expected/GRBBetaBeaconTrx.json b/src/test/resources/expected/GRBBetaBeaconTrx.json new file mode 100644 index 00000000..2cb0162b --- /dev/null +++ b/src/test/resources/expected/GRBBetaBeaconTrx.json @@ -0,0 +1,36 @@ +{ + "ax25Header": { + "destinationAddress": { + "callsign": "CQ", + "ssid": 0, + "extensionBit": 0 + }, + "sourceAddress": { + "callsign": "HA2GRB", + "ssid": 0, + "extensionBit": 1 + }, + "frameType": "U", + "sendSequenceNumber": 0, + "receiveSequenceNumber": 0, + "uControlType": "UI", + "pid": 240 + }, + "trx": { + "uptimeSinceReset": 2637231, + "uptime": 2659007, + "radioBootCount": 2, + "rfSegmentResetCount": 0, + "radioMcuTemperature": 12.75, + "rfChipTemperature": 12.74, + "rfPowerAmplifierTemperature": 12.179999, + "digipeaterMessageCount": 0, + "rxDataPackets": 1808, + "txDataPackets": 279832, + "actualRssi": -96.5, + "rssiPreamble": -88.0 + }, + "beginSample": 0, + "beginMillis": 0, + "endSample": 0 +} \ No newline at end of file