-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposed SatnogsTransferService to perform deduplication on Satnogs Telemetry API Result Field #62
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,23 @@ | ||
package org.oresat.uniclogs.links; | ||
|
||
import com.fasterxml.jackson.databind.JsonNode; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import org.yamcs.ConfigurationException; | ||
import org.yamcs.YConfiguration; | ||
import org.yamcs.YamcsServer; | ||
import org.yamcs.tctm.AbstractTmDataLink; | ||
import org.oresat.uniclogs.services.SatnogsTransferService; | ||
import java.io.BufferedReader; | ||
import java.io.IOException; | ||
import java.io.InputStreamReader; | ||
import java.net.HttpURLConnection; | ||
import java.net.URL; | ||
|
||
|
||
public class SatnogsTmLink extends AbstractTmDataLink { | ||
|
||
SatnogsTransferService tsfrService; | ||
|
||
@Override | ||
protected Status connectionStatus() { | ||
// TODO Auto-generated method stub | ||
|
@@ -12,14 +26,54 @@ protected Status connectionStatus() { | |
|
||
@Override | ||
protected void doStart() { | ||
// TODO Auto-generated method stub | ||
|
||
|
||
try { | ||
|
||
URL urlArtifacts = new URL("https://db.satnogs.org/api/telemetry/?format=json&satellite=98867"); | ||
HttpURLConnection conArtifacts = (HttpURLConnection) urlArtifacts.openConnection(); | ||
conArtifacts.setRequestProperty("Authorization", "Bearer " + System.getenv("SATNOGSDB_AUTH_TOKEN")); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any environment variables should be managed via a higher level and not-baked into the code like this. We've been going back-and-forth on the best way to do this, but ultimately, we're thinking it may have to be set as an ENV variable name set in For now I'd say do this. |
||
conArtifacts.setRequestMethod("GET"); | ||
conArtifacts.setRequestProperty("contentType", "application/json; charset=utf-8"); | ||
|
||
BufferedReader inArtifacts = new BufferedReader( | ||
new InputStreamReader(conArtifacts.getInputStream())); | ||
String inputLineArtifacts; | ||
StringBuilder contentArtifacts = new StringBuilder(); | ||
while ((inputLineArtifacts = inArtifacts.readLine()) != null) { | ||
contentArtifacts.append(inputLineArtifacts); | ||
} | ||
inArtifacts.close(); | ||
|
||
ObjectMapper objectMapper = new ObjectMapper(); | ||
JsonNode jsonNode = objectMapper.readTree(String.valueOf(contentArtifacts)); | ||
|
||
// Access specific fields from the JSON structure | ||
String contentArtifactsSet = jsonNode.get("results").asText(); | ||
|
||
tsfrService.getDeduplication(contentArtifactsSet); | ||
|
||
} catch (IOException e) { | ||
this.log.error(e.getMessage()); | ||
} | ||
} | ||
|
||
@Override | ||
protected void doStop() { | ||
// TODO Auto-generated method stub | ||
|
||
|
||
} | ||
|
||
public SatnogsTmLink(String instanceName, YConfiguration config) { | ||
String envName = config.getString("envService"); | ||
this.tsfrService = YamcsServer.getServer().getInstance(instanceName).getService(SatnogsTransferService.class, envName); | ||
|
||
if (this.tsfrService == null) { | ||
throw new ConfigurationException("Service " + envName + " does not exist or is not of class SatnogsTransferService."); | ||
} | ||
} | ||
|
||
public SatnogsTmLink(String instanceName) { | ||
this(instanceName, YConfiguration.emptyConfig()); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package org.oresat.uniclogs.services; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This file looks like a carbon copy of There may have been a misunderstanding of what the service file is for that needs more in-person explanation. But the elements that Anyways, the long-short of it is that nearly everything that's in
|
||
|
||
import org.yamcs.AbstractYamcsService; | ||
import org.yamcs.InitException; | ||
import org.yamcs.YConfiguration; | ||
import org.yamcs.utils.ByteArrayUtils; | ||
import org.yamcs.yarch.Bucket; | ||
import org.yamcs.yarch.YarchDatabase; | ||
import org.yamcs.yarch.YarchDatabaseInstance; | ||
import java.util.HashSet; | ||
import java.util.Set; | ||
|
||
import java.io.IOException; | ||
|
||
public class SatnogsTransferService extends AbstractYamcsService { | ||
static final String sequenceNumberId = "seqNum"; | ||
static final String hmacKeyId = "hmacKey"; | ||
String hmacEnvVar; | ||
byte[] hmacKey; | ||
Integer seqNum; | ||
Bucket db; | ||
|
||
private byte[] loadHmacFromEnv(String varName) { | ||
return System.getenv(varName).getBytes(); | ||
} | ||
|
||
private Set<String> processedData; | ||
// processedData set keeps track of unique data | ||
|
||
@Override | ||
protected void doStart() { | ||
try { | ||
this.hmacKey = this.loadHmacKey(); | ||
this.seqNum = this.loadSeqNum(); | ||
this.log.info("Loaded Sequence Number: " + this.seqNum); | ||
processedData = new HashSet<>(); | ||
} catch (IOException e) { | ||
this.log.error(e.getMessage()); | ||
} | ||
this.notifyStarted(); | ||
} | ||
|
||
@Override | ||
protected void doStop() { | ||
try { | ||
this.saveSeqNum(this.seqNum); | ||
this.saveHmacKey(this.hmacKey); | ||
} catch (IOException e) { | ||
this.log.error(e.getMessage()); | ||
} | ||
this.notifyStopped(); | ||
} | ||
|
||
public Integer getSeqNum() { | ||
Integer num = this.seqNum; | ||
this.seqNum++; | ||
return num; | ||
} | ||
|
||
public Boolean getDeduplication(String contentArtifactsSet) { | ||
// Check if the data is unique (not already processed) | ||
if (!processedData.contains(contentArtifactsSet)){ | ||
System.out.println("Processing unique data: " + contentArtifactsSet); | ||
// Add the processed data to the set | ||
this.log.info("DB Satnogs Artifacts: " + processedData.add(contentArtifactsSet)); | ||
return true; | ||
} else { | ||
System.out.println("Duplicate data received: " + contentArtifactsSet); | ||
// Handle duplicate data (If the text is present, it won't be added and the method returns false) | ||
return false; | ||
} | ||
} | ||
|
||
public byte[] getHmacKey() { | ||
return this.hmacKey; | ||
} | ||
|
||
@Override | ||
public void init(String yamcsInstance, String serviceName, YConfiguration config) throws InitException { | ||
super.init(yamcsInstance, serviceName, config); | ||
this.hmacEnvVar = config.getString("hmacEnvVar"); | ||
log.info(this.hmacEnvVar); | ||
YarchDatabaseInstance instanceDb = YarchDatabase.getInstance(yamcsInstance); | ||
try { | ||
this.db = instanceDb.getBucket("env"); | ||
if (this.db == null) { | ||
this.log.info(String.format("Env not found for %s, creating new env...", yamcsInstance)); | ||
this.db = instanceDb.createBucket("env"); | ||
} | ||
} catch (IOException e) { | ||
throw new InitException(e.getMessage()); | ||
} | ||
} | ||
|
||
private Integer loadSeqNum() throws IOException { | ||
byte[] numBytes = db.getObject(sequenceNumberId); | ||
if (numBytes == null) { | ||
this.log.info(String.format("Sequence Number not found for %s, Sequence Number set to 1", yamcsInstance)); | ||
this.saveSeqNum(1); | ||
return 1; | ||
} | ||
return ByteArrayUtils.decodeInt(db.getObject(sequenceNumberId), 0); | ||
} | ||
|
||
private byte[] loadHmacKey() throws IOException { | ||
byte[] hmac = this.db.getObject(hmacKeyId); | ||
if (hmac == null) { | ||
this.log.info(String.format("Hmac Key not in %s. Loading from env var: %s", yamcsInstance, this.hmacEnvVar)); | ||
hmac = this.loadHmacFromEnv(this.hmacEnvVar); | ||
this.saveHmacKey(hmac); | ||
} | ||
return hmac; | ||
} | ||
|
||
private void saveSeqNum(Integer seq) throws IOException { | ||
db.putObject(sequenceNumberId, "Integer", null, ByteArrayUtils.encodeInt(seq)); | ||
} | ||
|
||
private void saveHmacKey(byte[] hmacKey) throws IOException { | ||
db.putObject(hmacKeyId, "bytes", null, hmacKey); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package org.oresat.uniclogs.tctm; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure why this exists. SatNOGS is a receive-only network, meaning we will never send commands to it and it has no capability to do commanding. This plus anything else to do with commanding in this PR can be axed. |
||
import org.oresat.uniclogs.services.SatnogsTransferService; | ||
import org.yamcs.ConfigurationException; | ||
import org.yamcs.YConfiguration; | ||
import org.yamcs.YamcsServer; | ||
import org.yamcs.cmdhistory.CommandHistoryPublisher; | ||
import org.yamcs.commanding.PreparedCommand; | ||
import org.yamcs.tctm.CommandPostprocessor; | ||
|
||
|
||
public class SatnogsCommandPostprocessor implements CommandPostprocessor { | ||
protected CommandHistoryPublisher cmdHistory; | ||
SatnogsTransferService env; | ||
|
||
@Override | ||
public byte[] process(PreparedCommand pc) { | ||
Integer seqNum = this.env.getSeqNum(); | ||
byte[] hmacKey = this.env.getHmacKey(); | ||
|
||
EDLPacket cmd = new EDLPacket(pc.getBinary(), seqNum, hmacKey); | ||
byte[] cmdBin = cmd.getBinary(); | ||
|
||
pc.setBinary(cmdBin); | ||
this.cmdHistory.publish(pc.getCommandId(), "edl-seqnum", seqNum); | ||
this.cmdHistory.publish(pc.getCommandId(), PreparedCommand.CNAME_BINARY, cmdBin); | ||
return cmdBin; | ||
} | ||
|
||
@Override | ||
public void setCommandHistoryPublisher(CommandHistoryPublisher commandHistoryListener) { | ||
this.cmdHistory = commandHistoryListener; | ||
} | ||
|
||
public SatnogsCommandPostprocessor(String instanceName, YConfiguration config) { | ||
String envName = config.getString("envService"); | ||
this.env = YamcsServer.getServer().getInstance(instanceName).getService(SatnogsTransferService.class, envName); | ||
|
||
if (this.env == null) { | ||
throw new ConfigurationException("Service " + envName + " does not exist or is not of class SatnogsTransferService."); | ||
} | ||
} | ||
|
||
public SatnogsCommandPostprocessor(String instanceName) { | ||
this(instanceName, YConfiguration.emptyConfig()); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package org.oresat.uniclogs.tctm; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be culled as SatNOGS does not produce a unique TLM frame that we are processing in the Mission Database. |
||
|
||
import java.util.Arrays; | ||
|
||
import org.apache.commons.codec.digest.HmacAlgorithms; | ||
import org.apache.commons.codec.digest.HmacUtils; | ||
import org.yamcs.TmPacket; | ||
import org.yamcs.xtce.util.HexUtils; | ||
|
||
|
||
public class SatnogsPacket extends Packet { | ||
private static final Integer SEQ_NUM_OFFSET = 7; | ||
|
||
public SatnogsPacket(byte[] packet, Integer seqNum, byte[] hmacSecret) { | ||
//sequence number offset of 7 | ||
super(packet, packet.length+36, seqNum, SEQ_NUM_OFFSET); | ||
|
||
// set sequence number in packet | ||
this.encodeSeqNum(); | ||
|
||
|
||
// set frame length in packet: C = (Total Number of Octets in the Transfer Frame) − 1 | ||
// CRC adds 4, HMAC adds 32 -> (size + (36 - 1)) | ||
this.encodeFrameLength(35, 4); | ||
this.addHmac(hmacSecret); | ||
|
||
// Add CRC data to packet | ||
this.encodeCrc(); | ||
|
||
} | ||
|
||
private void addHmac(byte[] hmacSecret) { | ||
byte[] hmac = new HmacUtils(HmacAlgorithms.HMAC_SHA_256, hmacSecret).hmac(this.data.array()); | ||
log.info(String.format("Hmac bytes: %s", Arrays.toString(hmac))); | ||
this.data.put(hmac); | ||
log.info(String.format("HMAC_SHA_256 (%s) added to packet (seqNum: %d).", HexUtils.hex(hmac), this.sequenceNumber)); | ||
} | ||
|
||
protected void encodeCrc() { | ||
int crc = this.calcCrc(this.data.array()); | ||
log.info(String.format("CRC_32 (%d) added to packet (seqNum: %d).", crc, this.sequenceNumber)); | ||
this.data.putInt(crc); | ||
} | ||
|
||
public SatnogsPacket(TmPacket tmPacket) { | ||
super(tmPacket.getPacket(), tmPacket.getPacket().length, getSequenceNumber(tmPacket.getPacket(), SEQ_NUM_OFFSET), SEQ_NUM_OFFSET); | ||
} | ||
|
||
@Override | ||
boolean validCrc() { | ||
return crc32(0, this.data.array().length); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package org.oresat.uniclogs.tctm; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be culled as SatNOGS does not produce a unique TLM frame that we are processing in the Mission Database. |
||
|
||
import java.util.Date; | ||
|
||
import org.yamcs.TmPacket; | ||
import org.yamcs.YConfiguration; | ||
import org.yamcs.tctm.AbstractPacketPreprocessor; | ||
|
||
public class SatnogsPacketPreprocessor extends AbstractPacketPreprocessor { | ||
|
||
public SatnogsPacketPreprocessor(String yamcsInstance, YConfiguration config) { | ||
super(yamcsInstance, config); | ||
} | ||
|
||
public SatnogsPacketPreprocessor(String yamcsInstance) { | ||
super(yamcsInstance, YConfiguration.emptyConfig()); | ||
} | ||
|
||
@Override | ||
public TmPacket process(TmPacket tmPacket) { | ||
SatnogsPacket packet = new SatnogsPacket(tmPacket); | ||
tmPacket.setSequenceCount(packet.getSeqNum()); | ||
if (!packet.validCrc()) { | ||
tmPacket.setInvalid(); | ||
this.eventProducer.sendWarning("PACKET_CORRUPT", "Satnogs Response Packet Corrupted"); | ||
} | ||
tmPacket.setGenerationTime(new Date().getTime()); | ||
tmPacket.setLocalGenTimeFlag(); | ||
return tmPacket; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ realtime: | |
services: | ||
- class: org.yamcs.StreamTmPacketProvider | ||
args: | ||
streams: ["edl_tm_realtime", "beacon_tm_realtime", "tm_dump"] | ||
streams: ["edl_tm_realtime", "beacon_tm_realtime", "tm_dump", "satnogs_tm_realtime"] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Satnogs Telemetry should be on the same caedance of beacon telemetry and thus should use the |
||
- class: org.yamcs.StreamTcCommandReleaser | ||
- class: org.yamcs.tctm.StreamParameterProvider | ||
- class: org.yamcs.algorithms.AlgorithmManager | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please pull the parameters, (so far it's just the
satellite
param in the URI, but we may need more down the line) so that they are configurable inyamcs.oresat*.yml
.See this piece of code on how to do this and the config it corresponds to.