CCSDSPack
C++ Library for CCSDS Space Packet manipulation. i.e. generation, extraction, analisys and more
Loading...
Searching...
No Matches
CCSDSUtils.cpp
Go to the documentation of this file.
1#include "CCSDSUtils.h"
2#include <cstddef>
3#include <iostream>
4#include <iomanip>
5#include <fstream>
6
7//###########################################################################
8#define VERBOSE 1
9
10
11std::string getBinaryString(const uint32_t value, const int bits) {
12 std::string binaryString;
13 // Calculate the minimum number of bits required to represent in groups of 4
14 const int paddedBits = ((bits + 3) / 4) * 4; // Round up to the nearest multiple of 4
15
16 for (int i = paddedBits - 1; i >= 0; --i) {
17 binaryString += ((value >> i) & 1) ? '1' : '0';
18
19 // Add a space after every 4 bits, except at the end
20 if (i % 4 == 0 && i != 0) {
21 binaryString += ' ';
22 }
23 }
24 return binaryString;
25}
26
27std::string getBitsSpaces(const int num) {
28 std::string spaces;
29
30 for (int i = num - 1; i >= 0; --i) {
31 spaces += ' ';
32 }
33
34 return spaces;
35}
36
37void printBufferData(const std::vector<uint8_t> &buffer, const int limitBytes) {
38 std::cout << "[ ";
39 if (buffer.size() > limitBytes) {
40 for (size_t i= 0 ; i < static_cast<int>(limitBytes / 2); i++) {
41 std::cout << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>(buffer[i]) << " ";
42 }
43 std::cout << "... ";
44 for (size_t i = buffer.size() - static_cast<int>(limitBytes / 2) ; i < buffer.size(); i++) {
45 std::cout << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>(buffer[i]) << " ";
46 }
47 } else {
48 for (const unsigned char i: buffer) {
49 std::cout << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>(i) << " ";
50 }
51 }
52 std::cout << "]" << std::endl;
53}
54
55void printData(CCSDS::DataField dataField) {
56 const auto dataFieldHeader = dataField.getDataFieldHeaderBytes();
57 auto applicationData = dataField.getApplicationData();
58 const uint16_t maxSize = (applicationData.size() > dataFieldHeader.size())
59 ? applicationData.size()
60 : dataFieldHeader.size();
61
62 std::cout << " [CCSDS DATA] Data Field Length : " << applicationData.size() + dataFieldHeader.size() << " bytes" << std::endl;
63 std::cout << " [CCSDS DATA] Secondary Header Present : [ " << (dataField.getDataFieldHeaderFlag()
64 ? "True"
65 : "False") << " ]" << std::endl;
66 if (!dataFieldHeader.empty()) {
67 std::cout << " [CCSDS DATA] Secondary Header [Hex] : " << getBitsSpaces(
68 (maxSize - static_cast<uint16_t>(dataFieldHeader.size())) * 4);
69 printBufferData(dataFieldHeader);
70 }
71
72 std::cout << " [CCSDS DATA] Application Data [Hex] : ";
73 printBufferData(applicationData);
74 std::cout << std::endl;
75}
76
78 std::cout << " [CCSDS HEADER] Full Primary Header [Hex] : [ " << getBitsSpaces(17 - 12) << "0x" << std::hex <<
79 header.getFullHeader() << " ]" << std::endl;
80 std::cout << std::endl;
81
82 std::cout << " [CCSDS HEADER] Version Number : [ " << getBitsSpaces(19 - 4) <<
83 getBinaryString(header.getVersionNumber(), 3) << " ]";
84 std::cout << " - [Dec] : "<< std::dec << static_cast<int>( header.getVersionNumber()) << std::endl;
85
86 std::cout << " [CCSDS HEADER] Type : [ " << getBitsSpaces(19 - 4) <<
87 getBinaryString(header.getType(), 1) << " ]";
88 std::cout << " - [Dec] : "<< std::dec << static_cast<int>( header.getType()) << std::endl;
89
90 std::cout << " [CCSDS HEADER] APID : [ " << getBitsSpaces(17 - 12) <<
91 getBinaryString(header.getAPID(), 11) << " ]";
92 std::cout << " - [Dec] : " << std::dec << header.getAPID() << std::endl;
93
94 std::cout << " [CCSDS HEADER] Data Field Header Flag : [ " << getBitsSpaces(19 - 4) << getBinaryString(
95 header.getDataFieldHeaderFlag(), 1) << " ]";
96 std::cout << " - : " << (header.getDataFieldHeaderFlag() ? "True" : "False") << std::endl;
97
98 std::cout << " [CCSDS HEADER] Sequence Flags : [ " << getBitsSpaces(19 - 4) <<
99 getBinaryString(header.getSequenceFlags(), 2) << " ]";
100 std::cout << " - : ";
101 switch(header.getSequenceFlags()) {
102 case 0:
103 std::cout << "CONTINUING_SEGMENT";
104 break;
105 case 1:
106 std::cout << "FIRST_SEGMENT";
107 break;
108 case 2:
109 std::cout << "LAST_SEGMENT";
110 break;
111 case 3:
112 std::cout << "UNSEGMENTED";
113 break;
114 default:
115 break;
116 }
117 std::cout << std::endl;
118
119 std::cout << " [CCSDS HEADER] Sequence Count : [ " << getBitsSpaces(0) << getBinaryString(
120 header.getSequenceCount(), 14) << " ]";
121 std::cout << " - [Dec] : "<< std::dec << header.getSequenceCount() << std::endl;
122
123 std::cout << " [CCSDS HEADER] DataLength : [ " << getBinaryString(header.getDataLength(), 16) <<
124 " ]";
125 std::cout << " - [Dec] : "<< std::dec << header.getDataLength() << std::endl;
126
127 std::cout << std::endl;
128}
129
130
132 CCSDS::Header header;
133 FORWARD_RESULT(header.deserialize({packet.getPrimaryHeaderBytes()}));
134 printHeader(header);
135 return true;
136}
137
139 auto dataField = packet.getDataField();
140
141 printData(dataField);
142 std::cout << "[ CCSDSPack ] CRC-16 [Hex] : [ " << "0x" << std::hex << packet.getCRC() << " ]";
143 std::cout << " - [Dec] : "<< std::dec << packet.getCRC() << std::endl;
144
145}
146
147uint16_t crc16(
148 const std::vector<uint8_t> &data, const uint16_t polynomial, const uint16_t initialValue,
149 const uint16_t finalXorValue) {
150 uint16_t crc = initialValue;
151
152 for (const auto &byte: data) {
153 crc ^= static_cast<uint16_t>(byte) << 8; // Align byte with MSB of 16-bit CRC
154 for (int i = 0; i < 8; ++i) {
155 // Process each bit
156 if (crc & 0x8000) {
157 // Check if MSB is set
158 crc = (crc << 1) ^ polynomial; // Shift and XOR with polynomial
159 } else {
160 crc = crc << 1; // Shift only
161 }
162 }
163 }
164
165 return crc ^ finalXorValue; // Apply final XOR
166}
167
169 printPrimaryHeader(packet);
170 printDataField(packet);
171}
172
174 std::cout << "[ CCSDS Manager ] Number of Packets : " << manager.getTotalPackets() << std::endl;
175 std::cout << "[ CCSDS Manager ] Sync Pattern Enabled : " << (manager.getSyncPatternEnable() ? "True" : "False") << std::endl;
176 std::cout << "[ CCSDS Manager ] Sync Pattern : 0x" << std::hex << manager.getSyncPattern() << std::dec << std::endl;
177
178 auto templatePacket = manager.getTemplate();
179 std::cout << "[ CCSDS Manager ] Template : " << std::endl ;
180 printPrimaryHeader(templatePacket);
181
182 int idx = 1;
183 for (auto &packet: manager.getPacketsReference()) {
184 std::cout << "__________________________________________________________________________________________________________________" << std::endl;
185 std::cout << "[ CCSDS Manager ] Printing Packet [ " << idx << " ]:" << std::endl;
186 std::cout << "[ CCSDS Manager ] Packet Length : " << packet.getFullPacketLength() << " bytes" << std::endl;
187 std::cout << "[ CCSDS Manager ] Data ";
188 printBufferData(packet.serialize(), 20);
189 printPacket(packet);
190 idx++;
191 }
192}
193
194CCSDS::ResultBool writeBinaryFile(const std::vector<uint8_t>& data, const std::string& filename) {
195 RET_IF_ERR_MSG(filename.empty(),CCSDS::ErrorCode::FILE_WRITE_ERROR, "No filename provided");
196 RET_IF_ERR_MSG(data.empty(),CCSDS::ErrorCode::FILE_WRITE_ERROR, "No data provided");
197 std::ofstream out(filename, std::ios::binary);
198
199 RET_IF_ERR_MSG(!out,CCSDS::ErrorCode::FILE_WRITE_ERROR, "Failed to open file for writing");
200
201 // Write the entire vector data to the file in one go
202 out.write(reinterpret_cast<const char*>(data.data()), static_cast<std::streamsize>(data.size()));
203 RET_IF_ERR_MSG(!out,CCSDS::ErrorCode::FILE_WRITE_ERROR, "Failed to write the data to the file");
204
205 return true;
206}
207
208CCSDS::ResultBuffer readBinaryFile(const std::string& filename) {
209 RET_IF_ERR_MSG(filename.empty(),CCSDS::ErrorCode::FILE_READ_ERROR, "No filename provided");
210
211 std::ifstream in(filename, std::ios::binary | std::ios::ate);
212 RET_IF_ERR_MSG(!in,CCSDS::ErrorCode::FILE_READ_ERROR, "Failed to open file for reading");
213
214 // Get the file size using the 'ate' flag (seeks to the end automatically)
215 const std::streamsize size = in.tellg();
216 in.seekg(0, std::ios::beg);
217
218 // Read the entire file content into the vector
219 std::vector<uint8_t> data(size);
220 in.read(reinterpret_cast<char*>(data.data()), size);
221
222 RET_IF_ERR_MSG(!in,CCSDS::ErrorCode::FILE_READ_ERROR, "Failed to read the entire file");
223 return data;
224}
225
226bool fileExists(const std::string &fileName) {
227 auto exp = readBinaryFile(fileName);
228 if ( exp.has_value()) {
229 return true;
230 }
231 return false;
232}
233
234bool stringEndsWith(const std::string& str, const std::string& suffix) {
235 return str.size() >= suffix.size() &&
236 str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
237}
238
239/* Class methods*/
240
241CCSDS::ResultBool Config::load(const std::string &filename) {
242 std::ifstream file(filename);
243 RET_IF_ERR_MSG(!file.is_open(),CCSDS::ErrorCode::CONFIG_FILE_ERROR, "Failed to open configuration file");
244
245 std::string line;
246 while (std::getline(file, line)) {
247 if (line.empty() || line.front() == '#') continue;
248
249 auto [key, type, valueStr] = parseLine(line);
250 RET_IF_ERR_MSG(key.empty() || type.empty(),CCSDS::ErrorCode::CONFIG_FILE_ERROR, "Failed to parse configuration file");
251 ConfigValue value;
252 if (type == "string") {
253 value = valueStr;
254 } else if (type == "int") {
255 int base = 10;
256 if (valueStr.size() > 2 && valueStr.substr(0, 2) == "0x") {
257 valueStr = valueStr.substr(2);
258 base = 16;
259 }
260 value = std::stoi(valueStr, nullptr, base);
261 } else if (type == "float") {
262 value = std::stof(valueStr);
263 } else if (type == "bool") {
264 value = (valueStr == "true" || valueStr == "1");
265 } else if (type == "bytes") {
266 ASSIGN_CP( value, parseBytes(valueStr) );
267 } else {
268 return CCSDS::Error{CCSDS::ErrorCode::CONFIG_FILE_ERROR, " unknown type: " + type};
269 }
270
271 values[key] = value;
272 }
273
274 return true;
275}
276
277bool Config::isKey(const std::string &key) const {
278 if (values.find(key) != values.end()) {
279 return true;
280 }
281 return false;
282}
283
284std::tuple<std::string, std::string, std::string> Config::parseLine(const std::string& line) {
285 auto colonPos = line.find(':');
286 auto equalPos = line.find('=');
287
288 if (colonPos == std::string::npos || equalPos == std::string::npos || equalPos < colonPos)
289 return {"", "", ""};
290
291 std::string key = line.substr(0, colonPos);
292 std::string type = line.substr(colonPos + 1, equalPos - colonPos - 1);
293 std::string value = line.substr(equalPos + 1);
294
295 if (!value.empty() && value.front() == '"' && value.back() == '"')
296 value = value.substr(1, value.size() - 2);
297
298 return {key, type, value};
299}
300
301CCSDS::ResultBuffer Config::parseBytes(const std::string &valueStr) {
302 std::vector<uint8_t> result;
303 RET_IF_ERR_MSG(valueStr.front() != '[' || valueStr.back() != ']', CCSDS::ErrorCode::CONFIG_FILE_ERROR, "Config: Invalid buffer formatting []");
304
305 std::string inner = valueStr.substr(1, valueStr.size() - 2);
306 std::stringstream ss(inner);
307 std::string token;
308
309 while (std::getline(ss, token, ',')) {
310 // Trim whitespace
311 token.erase(std::remove_if(token.begin(), token.end(), ::isspace), token.end());
312
313 try {
314 int base = 10;
315 if (token.size() > 2 && token.substr(0, 2) == "0x") {
316 token = token.substr(2);
317 base = 16;
318 }
319 result.push_back(static_cast<uint8_t>(std::stoi(token, nullptr, base)));
320 } catch (...) {
321 return CCSDS::Error{CCSDS::ErrorCode::CONFIG_FILE_ERROR, "Invalid byte value: " + token};
322 }
323 }
324 return result;
325}
#define RET_IF_ERR_MSG(condition, errorCode, message)
Macro to return an error with an error message if a condition is met.
#define FORWARD_RESULT(result)
Macro to return a result as-is (for functions returning Result<T>).
#define ASSIGN_CP(var, result)
Macro to assign a result value to a variable or return an error by copy.
bool fileExists(const std::string &fileName)
filesystem check fore file existence prepared for both windows and linux.
std::string getBinaryString(const uint32_t value, const int bits)
Converts a given value to its binary representation as a string, with spaces every 4 bits.
uint16_t crc16(const std::vector< uint8_t > &data, const uint16_t polynomial, const uint16_t initialValue, const uint16_t finalXorValue)
Computes the CRC-16 checksum for a given data vector with configurable parameters.
void printPacket(CCSDS::Packet &packet)
Prints to console a CCSDS Packets, breaking it down to Primary header and Data field.
bool stringEndsWith(const std::string &str, const std::string &suffix)
Tests if str ends with suffix.
CCSDS::ResultBool printPrimaryHeader(CCSDS::Packet &packet)
Prints to console the primary header of a provided CCSDS packet.
std::string getBitsSpaces(const int num)
Generates a string of spaces for formatting binary outputs.
void printData(CCSDS::DataField dataField)
Prints the data field details, including the secondary header and application data.
CCSDS::ResultBuffer readBinaryFile(const std::string &filename)
Read a specified binary file and return its contents as a buffer.
void printHeader(CCSDS::Header &header)
Prints the header fields and their binary or hexadecimal representations.
CCSDS::ResultBool writeBinaryFile(const std::vector< uint8_t > &data, const std::string &filename)
This function takes in a buffer of data and a file name.
void printBufferData(const std::vector< uint8_t > &buffer, const int limitBytes)
Prints to console the HEX data from the bytes vector.
void printPackets(CCSDS::Manager &manager)
Prints to console a series of CCSDS Packets contained in the manager.
void printDataField(CCSDS::Packet &packet)
Prints the data field and the CRC-16 checksum of the packet.
Represents the data field of a CCSDS packet.
std::vector< uint8_t > getDataFieldHeaderBytes()
Retrieves the secondary header data as a vector of bytes.
std::vector< uint8_t > getApplicationData()
Retrieves the application data from the data field.
bool getDataFieldHeaderFlag() const
retrieves true if a known secondary header has been set
Represents an error with both an error code and a message.
Definition CCSDSResult.h:43
Manages the decomposition and manipulation of CCSDS primary headers.
Definition CCSDSHeader.h:80
uint8_t getVersionNumber() const
3 bits
Definition CCSDSHeader.h:84
ResultBool deserialize(const std::vector< uint8_t > &data)
Sets the header data from a 64-bit integer representation.
uint16_t getAPID() const
11 bits
Definition CCSDSHeader.h:87
uint8_t getType() const
1 bits
Definition CCSDSHeader.h:85
uint8_t getSequenceFlags() const
2 bits
Definition CCSDSHeader.h:88
uint8_t getDataFieldHeaderFlag() const
1 bits
Definition CCSDSHeader.h:86
uint16_t getSequenceCount() const
14 bits
Definition CCSDSHeader.h:89
uint16_t getDataLength() const
16 bits
Definition CCSDSHeader.h:90
uint64_t getFullHeader()
Computes and retrieves the full header as a 64-bit value.
Manages CCSDS packets and their templates.
std::vector< Packet > & getPacketsReference()
Returns a reference to the packets vector.
uint32_t getSyncPattern() const
returns the currently set sync pattern.
uint16_t getTotalPackets() const
Retrieves the total number of packets managed.
Packet getTemplate()
Retrieves the packet template.
bool getSyncPatternEnable() const
returns the current settings of the sync pattern enable
Represents a CCSDS (Consultative Committee for Space Data Systems) packet.
Definition CCSDSPacket.h:60
uint16_t getCRC()
Computes and retrieves the CRC-16 checksum of the packet.
CCSDS::DataField & getDataField()
returns the CCSDS packet's DataField.
Encapsulates a result that can hold either a value or an Error.
Definition CCSDSResult.h:81
std::variant< std::string, int, float, bool, std::vector< uint8_t > > ConfigValue
Definition CCSDSUtils.h:147
bool isKey(const std::string &key) const
static CCSDS::ResultBuffer parseBytes(const std::string &valueStr)
Parse string "[1,2,3]" into vector<uint8_t>
std::unordered_map< std::string, ConfigValue > values
Definition CCSDSUtils.h:163
CCSDS::ResultBool load(const std::string &filename)
Load config file.
CCSDS::Result< T > get(const std::string &key) const
Get value by key and type.
Definition CCSDSUtils.h:154
static std::tuple< std::string, std::string, std::string > parseLine(const std::string &line)
Parse a single line from config.
@ FILE_READ_ERROR
Reading from file failure.
Definition CCSDSResult.h:31
@ CONFIG_FILE_ERROR
Definition CCSDSResult.h:33
@ FILE_WRITE_ERROR
Writing to file failure.
Definition CCSDSResult.h:32