diff --git a/README.md b/README.md index 2e4f3ed7..6f8392a6 100644 --- a/README.md +++ b/README.md @@ -609,6 +609,11 @@ jradio has lots of built-in satellite decoders. Some of them have non standard d 60246 ru.r2cloud.jradio.catsat.CatsatBeacon + + ENSO + 58470 + ru.r2cloud.jradio.enso.EnsoBeacon + diff --git a/src/main/java/ru/r2cloud/jradio/enso/EnsoBeacon.java b/src/main/java/ru/r2cloud/jradio/enso/EnsoBeacon.java new file mode 100644 index 00000000..37bd2757 --- /dev/null +++ b/src/main/java/ru/r2cloud/jradio/enso/EnsoBeacon.java @@ -0,0 +1,106 @@ +package ru.r2cloud.jradio.enso; + +import java.io.DataInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import ru.r2cloud.jradio.ax25.Ax25Beacon; +import ru.r2cloud.jradio.fec.ccsds.UncorrectableException; +import ru.r2cloud.jradio.util.LittleEndianDataInputStream; + +public class EnsoBeacon extends Ax25Beacon { + + private int size; + private int frameType; + private long ts; + private Obdh obdh; + private Eps eps; + private Ttc ttc; + private byte[] customPayload; + private String tweet; + + @Override + public void readBeacon(DataInputStream dis) throws IOException, UncorrectableException { + size = dis.readUnsignedByte(); + frameType = dis.readUnsignedByte(); + LittleEndianDataInputStream ldis = new LittleEndianDataInputStream(dis); + ts = ldis.readUnsignedInt(); + obdh = new Obdh(ldis); + eps = new Eps(ldis); + ttc = new Ttc(ldis); + customPayload = new byte[48]; + dis.readFully(customPayload); + if (dis.available() > 0) { + byte[] tweetBytes = new byte[dis.available()]; + tweet = new String(tweetBytes, StandardCharsets.US_ASCII).trim(); + if (tweet.length() == 0) { + tweet = null; + } + } + } + + public int getSize() { + return size; + } + + public void setSize(int size) { + this.size = size; + } + + public int getFrameType() { + return frameType; + } + + public void setFrameType(int frameType) { + this.frameType = frameType; + } + + public long getTs() { + return ts; + } + + public void setTs(long ts) { + this.ts = ts; + } + + public Obdh getObdh() { + return obdh; + } + + public void setObdh(Obdh obdh) { + this.obdh = obdh; + } + + public Eps getEps() { + return eps; + } + + public void setEps(Eps eps) { + this.eps = eps; + } + + public Ttc getTtc() { + return ttc; + } + + public void setTtc(Ttc ttc) { + this.ttc = ttc; + } + + public byte[] getCustomPayload() { + return customPayload; + } + + public void setCustomPayload(byte[] customPayload) { + this.customPayload = customPayload; + } + + public String getTweet() { + return tweet; + } + + public void setTweet(String tweet) { + this.tweet = tweet; + } + +} diff --git a/src/main/java/ru/r2cloud/jradio/enso/Eps.java b/src/main/java/ru/r2cloud/jradio/enso/Eps.java new file mode 100644 index 00000000..9fa741b8 --- /dev/null +++ b/src/main/java/ru/r2cloud/jradio/enso/Eps.java @@ -0,0 +1,328 @@ +package ru.r2cloud.jradio.enso; + +import java.io.IOException; + +import ru.r2cloud.jradio.celesta.EpsMode; +import ru.r2cloud.jradio.util.LittleEndianDataInputStream; + +public class Eps { + + private EpsMode epsMode; + private int batteryVoltage; + private byte batteryTemperature; + private int minBatteryVoltage; + private int maxBatteryVoltage; + private int avgBatteryVoltage; + private int avgChargeCurrent; + private int maxChargeCurrent; + private byte zMinuFaceTemperature; + private int obdhCurrent; + private int epsCurrent; + private int ttcMicroCCurrent; + private int ttcTxCur; + private int ttcTxCurMax; + private int plCur; + private int chargeCur; + private byte tempXPlus; + private byte tempXMinus; + private byte tempYPlus; + private byte tempYMinus; + private byte tempZPlus; + private int obdhVolt; + private int ttcVolt; + private int plVolt; + private float mos1Volt; + private float mos2Volt; + private float mos3Volt; + private float refVolt; + private byte temp5vReg; + private byte temp6vReg; + private int ttcMcuVolt; + + public Eps() { + // do nothing + } + + public Eps(LittleEndianDataInputStream dis) throws IOException { + epsMode = EpsMode.valueOfCode(dis.readUnsignedByte()); + batteryVoltage = dis.readUnsignedByte() * 20; + batteryTemperature = dis.readByte(); + minBatteryVoltage = dis.readUnsignedByte() * 20; + maxBatteryVoltage = dis.readUnsignedByte() * 20; + avgBatteryVoltage = dis.readUnsignedByte() * 20; + avgChargeCurrent = dis.readUnsignedByte() * 12; + maxChargeCurrent = dis.readUnsignedByte() * 12; + zMinuFaceTemperature = dis.readByte(); + obdhCurrent = dis.readUnsignedByte(); + epsCurrent = dis.readUnsignedByte(); + ttcMicroCCurrent = dis.readUnsignedByte(); + ttcTxCur = dis.readUnsignedByte() * 5; + ttcTxCurMax = dis.readUnsignedByte() * 5; + plCur = dis.readUnsignedByte() * 5; + chargeCur = dis.readUnsignedByte() * 12; + tempXPlus = dis.readByte(); + tempXMinus = dis.readByte(); + tempYPlus = dis.readByte(); + tempYMinus = dis.readByte(); + tempZPlus = dis.readByte(); + obdhVolt = dis.readUnsignedByte() * 10 + 4000; + ttcVolt = dis.readUnsignedByte() * 10 + 4000; + plVolt = dis.readUnsignedByte() * 10 + 4000; + mos1Volt = (dis.readUnsignedByte() + 2200) * 0.805f; + mos2Volt = (dis.readUnsignedByte() + 2200) * 0.805f; + mos3Volt = (dis.readUnsignedByte() + 2200) * 0.805f; + refVolt = dis.readUnsignedShort() * 0.805f; + temp5vReg = dis.readByte(); + temp6vReg = dis.readByte(); + ttcMcuVolt = dis.readUnsignedByte() * 10 + 4000; + } + + public EpsMode getEpsMode() { + return epsMode; + } + + public void setEpsMode(EpsMode epsMode) { + this.epsMode = epsMode; + } + + public int getBatteryVoltage() { + return batteryVoltage; + } + + public void setBatteryVoltage(int batteryVoltage) { + this.batteryVoltage = batteryVoltage; + } + + public byte getBatteryTemperature() { + return batteryTemperature; + } + + public void setBatteryTemperature(byte batteryTemperature) { + this.batteryTemperature = batteryTemperature; + } + + public int getMinBatteryVoltage() { + return minBatteryVoltage; + } + + public void setMinBatteryVoltage(int minBatteryVoltage) { + this.minBatteryVoltage = minBatteryVoltage; + } + + public int getMaxBatteryVoltage() { + return maxBatteryVoltage; + } + + public void setMaxBatteryVoltage(int maxBatteryVoltage) { + this.maxBatteryVoltage = maxBatteryVoltage; + } + + public int getAvgBatteryVoltage() { + return avgBatteryVoltage; + } + + public void setAvgBatteryVoltage(int avgBatteryVoltage) { + this.avgBatteryVoltage = avgBatteryVoltage; + } + + public int getAvgChargeCurrent() { + return avgChargeCurrent; + } + + public void setAvgChargeCurrent(int avgChargeCurrent) { + this.avgChargeCurrent = avgChargeCurrent; + } + + public int getMaxChargeCurrent() { + return maxChargeCurrent; + } + + public void setMaxChargeCurrent(int maxChargeCurrent) { + this.maxChargeCurrent = maxChargeCurrent; + } + + public byte getzMinuFaceTemperature() { + return zMinuFaceTemperature; + } + + public void setzMinuFaceTemperature(byte zMinuFaceTemperature) { + this.zMinuFaceTemperature = zMinuFaceTemperature; + } + + public int getObdhCurrent() { + return obdhCurrent; + } + + public void setObdhCurrent(int obdhCurrent) { + this.obdhCurrent = obdhCurrent; + } + + public int getEpsCurrent() { + return epsCurrent; + } + + public void setEpsCurrent(int epsCurrent) { + this.epsCurrent = epsCurrent; + } + + public int getTtcMicroCCurrent() { + return ttcMicroCCurrent; + } + + public void setTtcMicroCCurrent(int ttcMicroCCurrent) { + this.ttcMicroCCurrent = ttcMicroCCurrent; + } + + public int getTtcTxCur() { + return ttcTxCur; + } + + public void setTtcTxCur(int ttcTxCur) { + this.ttcTxCur = ttcTxCur; + } + + public int getTtcTxCurMax() { + return ttcTxCurMax; + } + + public void setTtcTxCurMax(int ttcTxCurMax) { + this.ttcTxCurMax = ttcTxCurMax; + } + + public int getPlCur() { + return plCur; + } + + public void setPlCur(int plCur) { + this.plCur = plCur; + } + + public int getChargeCur() { + return chargeCur; + } + + public void setChargeCur(int chargeCur) { + this.chargeCur = chargeCur; + } + + public byte getTempXPlus() { + return tempXPlus; + } + + public void setTempXPlus(byte tempXPlus) { + this.tempXPlus = tempXPlus; + } + + public byte getTempXMinus() { + return tempXMinus; + } + + public void setTempXMinus(byte tempXMinus) { + this.tempXMinus = tempXMinus; + } + + public byte getTempYPlus() { + return tempYPlus; + } + + public void setTempYPlus(byte tempYPlus) { + this.tempYPlus = tempYPlus; + } + + public byte getTempYMinus() { + return tempYMinus; + } + + public void setTempYMinus(byte tempYMinus) { + this.tempYMinus = tempYMinus; + } + + public byte getTempZPlus() { + return tempZPlus; + } + + public void setTempZPlus(byte tempZPlus) { + this.tempZPlus = tempZPlus; + } + + public int getObdhVolt() { + return obdhVolt; + } + + public void setObdhVolt(int obdhVolt) { + this.obdhVolt = obdhVolt; + } + + public int getTtcVolt() { + return ttcVolt; + } + + public void setTtcVolt(int ttcVolt) { + this.ttcVolt = ttcVolt; + } + + public int getPlVolt() { + return plVolt; + } + + public void setPlVolt(int plVolt) { + this.plVolt = plVolt; + } + + public float getMos1Volt() { + return mos1Volt; + } + + public void setMos1Volt(float mos1Volt) { + this.mos1Volt = mos1Volt; + } + + public float getMos2Volt() { + return mos2Volt; + } + + public void setMos2Volt(float mos2Volt) { + this.mos2Volt = mos2Volt; + } + + public float getMos3Volt() { + return mos3Volt; + } + + public void setMos3Volt(float mos3Volt) { + this.mos3Volt = mos3Volt; + } + + public float getRefVolt() { + return refVolt; + } + + public void setRefVolt(float refVolt) { + this.refVolt = refVolt; + } + + public byte getTemp5vReg() { + return temp5vReg; + } + + public void setTemp5vReg(byte temp5vReg) { + this.temp5vReg = temp5vReg; + } + + public byte getTemp6vReg() { + return temp6vReg; + } + + public void setTemp6vReg(byte temp6vReg) { + this.temp6vReg = temp6vReg; + } + + public int getTtcMcuVolt() { + return ttcMcuVolt; + } + + public void setTtcMcuVolt(int ttcMcuVolt) { + this.ttcMcuVolt = ttcMcuVolt; + } + +} diff --git a/src/main/java/ru/r2cloud/jradio/enso/Obdh.java b/src/main/java/ru/r2cloud/jradio/enso/Obdh.java new file mode 100644 index 00000000..8074d85a --- /dev/null +++ b/src/main/java/ru/r2cloud/jradio/enso/Obdh.java @@ -0,0 +1,94 @@ +package ru.r2cloud.jradio.enso; + +import java.io.IOException; + +import ru.r2cloud.jradio.celesta.ObdhMode; +import ru.r2cloud.jradio.celesta.SatelliteMode; +import ru.r2cloud.jradio.util.LittleEndianDataInputStream; + +public class Obdh { + + private long timestamp; + private float temperature; + private SatelliteMode satelliteMode; + private ObdhMode obdhMode; + private long bytesToTransmit; + private int numberOfResets; + private int numberOfErrors; + + public Obdh() { + // do nothing + } + + public Obdh(LittleEndianDataInputStream dis) throws IOException { + timestamp = dis.readUnsignedInt(); + int tempRaw = dis.readUnsignedShort(); + if (tempRaw <= 4095) { + temperature = tempRaw * 0.0625f; + } else { + temperature = (tempRaw - 8192) * 0.0625f; + } + satelliteMode = SatelliteMode.valueOfCode(dis.readUnsignedByte()); + obdhMode = ObdhMode.valueOfCode(dis.readUnsignedByte()); + bytesToTransmit = dis.readUnsignedInt(); + numberOfResets = dis.readUnsignedShort(); + numberOfErrors = dis.readUnsignedShort(); + } + + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + public float getTemperature() { + return temperature; + } + + public void setTemperature(float temperature) { + this.temperature = temperature; + } + + public SatelliteMode getSatelliteMode() { + return satelliteMode; + } + + public void setSatelliteMode(SatelliteMode satelliteMode) { + this.satelliteMode = satelliteMode; + } + + public ObdhMode getObdhMode() { + return obdhMode; + } + + public void setObdhMode(ObdhMode obdhMode) { + this.obdhMode = obdhMode; + } + + public long getBytesToTransmit() { + return bytesToTransmit; + } + + public void setBytesToTransmit(long bytesToTransmit) { + this.bytesToTransmit = bytesToTransmit; + } + + public int getNumberOfResets() { + return numberOfResets; + } + + public void setNumberOfResets(int numberOfResets) { + this.numberOfResets = numberOfResets; + } + + public int getNumberOfErrors() { + return numberOfErrors; + } + + public void setNumberOfErrors(int numberOfErrors) { + this.numberOfErrors = numberOfErrors; + } + +} diff --git a/src/main/java/ru/r2cloud/jradio/enso/Ttc.java b/src/main/java/ru/r2cloud/jradio/enso/Ttc.java new file mode 100644 index 00000000..7eb02c59 --- /dev/null +++ b/src/main/java/ru/r2cloud/jradio/enso/Ttc.java @@ -0,0 +1,150 @@ +package ru.r2cloud.jradio.enso; + +import java.io.IOException; + +import ru.r2cloud.jradio.celesta.LastErrorCode; +import ru.r2cloud.jradio.celesta.LastResetCause; +import ru.r2cloud.jradio.celesta.TtcMode; +import ru.r2cloud.jradio.util.LittleEndianDataInputStream; + +public class Ttc { + + private TtcMode ttcMode; + private int numberOfTtcResets; + private LastResetCause lastResetCause; + private int numberOfReceivedValidPackets; + private int numberOfTransmittedPackets; + private int measuredTransmissionPower; + private int powerRev; + private LastErrorCode lastErrorCode; + private int powerConfiguration; + private byte powerAmplifierTemperature; + private int rssiOfLastReceivedPacket; + private int frequencyDeviationOfLastReceivedPacket; + private int beaconPeriod; + + public Ttc() { + // do nothing + } + + public Ttc(LittleEndianDataInputStream dis) throws IOException { + ttcMode = TtcMode.valueOfCode(dis.readUnsignedByte()); + numberOfTtcResets = dis.readUnsignedShort(); + lastResetCause = LastResetCause.valueOfCode(dis.readUnsignedByte()); + numberOfReceivedValidPackets = dis.readUnsignedShort(); + numberOfTransmittedPackets = dis.readUnsignedShort(); + measuredTransmissionPower = dis.readUnsignedByte() * 10; + powerRev = dis.readUnsignedByte() * 10; + lastErrorCode = LastErrorCode.valueOfCode(dis.readUnsignedByte()); + powerConfiguration = dis.readUnsignedByte(); + powerAmplifierTemperature = dis.readByte(); + rssiOfLastReceivedPacket = dis.readUnsignedByte() * -1; + frequencyDeviationOfLastReceivedPacket = dis.readUnsignedByte() * 17; + beaconPeriod = dis.readUnsignedByte(); + } + + public TtcMode getTtcMode() { + return ttcMode; + } + + public void setTtcMode(TtcMode ttcMode) { + this.ttcMode = ttcMode; + } + + public int getNumberOfTtcResets() { + return numberOfTtcResets; + } + + public void setNumberOfTtcResets(int numberOfTtcResets) { + this.numberOfTtcResets = numberOfTtcResets; + } + + public LastResetCause getLastResetCause() { + return lastResetCause; + } + + public void setLastResetCause(LastResetCause lastResetCause) { + this.lastResetCause = lastResetCause; + } + + public int getNumberOfReceivedValidPackets() { + return numberOfReceivedValidPackets; + } + + public void setNumberOfReceivedValidPackets(int numberOfReceivedValidPackets) { + this.numberOfReceivedValidPackets = numberOfReceivedValidPackets; + } + + public int getNumberOfTransmittedPackets() { + return numberOfTransmittedPackets; + } + + public void setNumberOfTransmittedPackets(int numberOfTransmittedPackets) { + this.numberOfTransmittedPackets = numberOfTransmittedPackets; + } + + public int getMeasuredTransmissionPower() { + return measuredTransmissionPower; + } + + public void setMeasuredTransmissionPower(int measuredTransmissionPower) { + this.measuredTransmissionPower = measuredTransmissionPower; + } + + public int getPowerRev() { + return powerRev; + } + + public void setPowerRev(int powerRev) { + this.powerRev = powerRev; + } + + public LastErrorCode getLastErrorCode() { + return lastErrorCode; + } + + public void setLastErrorCode(LastErrorCode lastErrorCode) { + this.lastErrorCode = lastErrorCode; + } + + public int getPowerConfiguration() { + return powerConfiguration; + } + + public void setPowerConfiguration(int powerConfiguration) { + this.powerConfiguration = powerConfiguration; + } + + public byte getPowerAmplifierTemperature() { + return powerAmplifierTemperature; + } + + public void setPowerAmplifierTemperature(byte powerAmplifierTemperature) { + this.powerAmplifierTemperature = powerAmplifierTemperature; + } + + public int getRssiOfLastReceivedPacket() { + return rssiOfLastReceivedPacket; + } + + public void setRssiOfLastReceivedPacket(int rssiOfLastReceivedPacket) { + this.rssiOfLastReceivedPacket = rssiOfLastReceivedPacket; + } + + public int getFrequencyDeviationOfLastReceivedPacket() { + return frequencyDeviationOfLastReceivedPacket; + } + + public void setFrequencyDeviationOfLastReceivedPacket(int frequencyDeviationOfLastReceivedPacket) { + this.frequencyDeviationOfLastReceivedPacket = frequencyDeviationOfLastReceivedPacket; + } + + public int getBeaconPeriod() { + return beaconPeriod; + } + + public void setBeaconPeriod(int beaconPeriod) { + this.beaconPeriod = beaconPeriod; + } + +} diff --git a/src/test/java/ru/r2cloud/jradio/enso/EnsoBeaconTest.java b/src/test/java/ru/r2cloud/jradio/enso/EnsoBeaconTest.java new file mode 100644 index 00000000..19577135 --- /dev/null +++ b/src/test/java/ru/r2cloud/jradio/enso/EnsoBeaconTest.java @@ -0,0 +1,30 @@ +package ru.r2cloud.jradio.enso; + +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 EnsoBeaconTest { + + @Test + public void testBeacon() throws Exception { + byte[] data = ViterbiTest.hexStringToByteArray("8C689694B040E08CB06C8CA486E103F08C10AB27B966A927B966D91F045538460600C900569255CBF7C5CBCA3666EA0E0D3900002839EDF1EAFFEBCA64C982E4A15409FEFFC811A40177000066039DBF66467F00001D02004A004BBF00330026010C00350031000689020202070100FE98FE70E8E9FFFE0015FFFF0000000000000000000000476F20506172697320323032342021202020202020202020"); + EnsoBeacon result = new EnsoBeacon(); + result.readBeacon(data); + AssertJson.assertObjectsEqual("EnsoBeacon.json", result); + } + + @Test + public void testPojo() { + assertThat(EnsoBeacon.class, allOf(hasValidBeanConstructor(), hasValidGettersAndSetters())); + assertThat(Obdh.class, allOf(hasValidBeanConstructor(), hasValidGettersAndSetters())); + assertThat(Eps.class, allOf(hasValidBeanConstructor(), hasValidGettersAndSetters())); + assertThat(Ttc.class, allOf(hasValidBeanConstructor(), hasValidGettersAndSetters())); + } +} diff --git a/src/test/resources/expected/EnsoBeacon.json b/src/test/resources/expected/EnsoBeacon.json new file mode 100644 index 00000000..b3af0752 --- /dev/null +++ b/src/test/resources/expected/EnsoBeacon.json @@ -0,0 +1,132 @@ +{ + "size": 140, + "frameType": 16, + "ts": 1723410347, + "obdh": { + "timestamp": 1723410345, + "temperature": -2.4375, + "satelliteMode": "MISSION", + "obdhMode": "MISSION", + "bytesToTransmit": 411192, + "numberOfResets": 201, + "numberOfErrors": 37462 + }, + "eps": { + "epsMode": "MISSION", + "batteryVoltage": 4060, + "batteryTemperature": -9, + "minBatteryVoltage": 3940, + "maxBatteryVoltage": 4060, + "avgBatteryVoltage": 4040, + "avgChargeCurrent": 648, + "maxChargeCurrent": 1224, + "zMinuFaceTemperature": -22, + "obdhCurrent": 14, + "epsCurrent": 13, + "ttcMicroCCurrent": 57, + "ttcTxCur": 0, + "ttcTxCurMax": 0, + "plCur": 200, + "chargeCur": 684, + "tempXPlus": -19, + "tempXMinus": -15, + "tempYPlus": -22, + "tempYMinus": -1, + "tempZPlus": -21, + "obdhVolt": 6020, + "ttcVolt": 5000, + "plVolt": 6010, + "mos1Volt": 1875.65, + "mos2Volt": 1954.54, + "mos3Volt": 1900.605, + "refVolt": 1922.34, + "temp5vReg": -2, + "temp6vReg": -1, + "ttcMcuVolt": 6000 + }, + "ttc": { + "ttcMode": "BEACON", + "numberOfTtcResets": 420, + "lastResetCause": "SOFTWARE_RESET", + "numberOfReceivedValidPackets": 0, + "numberOfTransmittedPackets": 870, + "measuredTransmissionPower": 1570, + "powerRev": 1910, + "lastErrorCode": "OBC_TEMP_HW_ERROR", + "powerConfiguration": 70, + "powerAmplifierTemperature": 127, + "rssiOfLastReceivedPacket": 0, + "frequencyDeviationOfLastReceivedPacket": 0, + "beaconPeriod": 29 + }, + "customPayload": [ + 2, + 0, + 74, + 0, + 75, + -65, + 0, + 51, + 0, + 38, + 1, + 12, + 0, + 53, + 0, + 49, + 0, + 6, + -119, + 2, + 2, + 2, + 7, + 1, + 0, + -2, + -104, + -2, + 112, + -24, + -23, + -1, + -2, + 0, + 21, + -1, + -1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "header": { + "destinationAddress": { + "callsign": "F4KJX", + "ssid": 0, + "extensionBit": 0 + }, + "sourceAddress": { + "callsign": "FX6FRC", + "ssid": 0, + "extensionBit": 1 + }, + "frameType": "U", + "sendSequenceNumber": 0, + "receiveSequenceNumber": 0, + "uControlType": "UI", + "pid": 240 + }, + "beginSample": 0, + "beginMillis": 0, + "endSample": 0 +} \ No newline at end of file