Start new LRIT system work, convert ELEKTRO

This commit is contained in:
Aang23 2022-11-14 14:46:46 +01:00
parent a86d578827
commit b03d24fa70
16 changed files with 618 additions and 716 deletions

View file

@ -22,16 +22,16 @@ namespace Util
{
/**
* thread safe pointer class
**/
* thread safe pointer class
**/
template <class T>
class CSmartPtr
{
private:
/**
* Holds address of heap memory and maintains counter of references to it.
**/
* Holds address of heap memory and maintains counter of references to it.
**/
class CCounted
{
private:
@ -40,8 +40,8 @@ namespace Util
public:
/**
* Stores address of heap memory, initializes number of references.
**/
* Stores address of heap memory, initializes number of references.
**/
CCounted(T *i_Ptr)
{
m_Ptr = i_Ptr;
@ -49,30 +49,30 @@ namespace Util
}
/**
* Increments number of references.
**/
* Increments number of references.
**/
void Use()
{
m_NumReferences++;
}
/**
* Decrements number of references,deletes itself when last reference dismisses.
**/
* Decrements number of references,deletes itself when last reference dismisses.
**/
void Dismiss()
{
if (0 == --m_NumReferences)
{
delete m_Ptr;
// delete this;
delete[] m_Ptr; // ORIGINAL WAS delete
// delete this;
}
}
/**
* Returns heap memory address and set sets own copy of the address to NULL to prevent deletion.
* @returns
* Heap memory address
**/
* Returns heap memory address and set sets own copy of the address to NULL to prevent deletion.
* @returns
* Heap memory address
**/
T *Release()
{
/** Return the heap memory address.**/
@ -82,10 +82,10 @@ namespace Util
}
/**
* Operators to access the heap memory.
* @returns
* Address or value of dynamic object
**/
* Operators to access the heap memory.
* @returns
* Address or value of dynamic object
**/
operator T &() const { return *m_Ptr; }
operator T *() const { return m_Ptr; }
T *operator->() const { return m_Ptr; }
@ -95,8 +95,8 @@ namespace Util
public:
/**
* constructor
**/
* constructor
**/
explicit CSmartPtr(
T *i_Ptr = NULL /** Start address of heap memory.**/
)
@ -106,8 +106,8 @@ namespace Util
}
/**
* copy constructor
**/
* copy constructor
**/
CSmartPtr(
const CSmartPtr<T> &i_Src /** Reference CSmartPtr.**/
)
@ -117,18 +117,18 @@ namespace Util
}
/**
* destructor
**/
* destructor
**/
~CSmartPtr()
{
m_Counted->Dismiss();
}
/**
* Assignment operator.
* @returns
* Reference to itself.
**/
* Assignment operator.
* @returns
* Reference to itself.
**/
CSmartPtr<T> &operator=(
T *i_Ptr /** Start address of heap memory.**/
)
@ -140,10 +140,10 @@ namespace Util
}
/**
* Assignment operator.
* @returns
* Reference to itself.
**/
* Assignment operator.
* @returns
* Reference to itself.
**/
CSmartPtr<T> &operator=(
const CSmartPtr<T> &i_Src /** Reference CSmartPtr.**/
)
@ -154,10 +154,10 @@ namespace Util
return *this;
}
/**
*compare < operator. Will call the operator for the data itself.
* @returns
* true if this object is'less than' (in terms of the defined data-operator)
**/
*compare < operator. Will call the operator for the data itself.
* @returns
* true if this object is'less than' (in terms of the defined data-operator)
**/
bool operator<(
const CSmartPtr<T> &i_Src /** Reference CSmartPtr.**/
)
@ -166,27 +166,27 @@ namespace Util
}
/**
* Operators and functions to access the heap memory.
* @returns
* Address or value of dynamic object.
**/
* Operators and functions to access the heap memory.
* @returns
* Address or value of dynamic object.
**/
operator T &() const { return m_Counted->operator T &(); }
operator T *() const { return m_Counted->operator T *(); }
T *operator->() const { return m_Counted->operator->(); }
T *Get() const { return m_Counted->operator->(); }
/**
*Only for easier conversion of former auto_ptr's.
*
**/
*Only for easier conversion of former auto_ptr's.
*
**/
T *get() const { return m_Counted->operator->(); }
/**
* Returns heap memory address and set sets own copy of the address to NULL to prevent deletion.
* @returns
* Heap memory address.
**/
* Returns heap memory address and set sets own copy of the address to NULL to prevent deletion.
* @returns
* Heap memory address.
**/
T *Release()
{
@ -194,13 +194,13 @@ namespace Util
}
/**
* Casts a Util::CSmartPtr<F> object to a Util:CSmartPtr<T> object.
* Note that this will actually release the original and set it to NULL.
* @returns
* Util::CSmartPtr<T>.
* - the cast operation is not possible because the classes are not suitably related, or
* - the original CSmartPtr contains a NULL pointer.
**/
* Casts a Util::CSmartPtr<F> object to a Util:CSmartPtr<T> object.
* Note that this will actually release the original and set it to NULL.
* @returns
* Util::CSmartPtr<T>.
* - the cast operation is not possible because the classes are not suitably related, or
* - the original CSmartPtr contains a NULL pointer.
**/
template <class F>
inline static Util::CSmartPtr<T> Cast(
Util::CSmartPtr<F> i_From)
@ -213,16 +213,16 @@ namespace Util
};
/**
* Casts a Util::CSmartPtr<CFrom> object to a Util:CSmartPtr<CTo> object.
* Note that this will actually release the original and set it to NULL.
* @returns
* Util::CSmartPtr<T>.
* - the cast operation is not possible because the classes are not suitably related, or
* - the original CSmartPtr contains a NULL pointer.
* NOTE:
* Function exists only for backward compatibility.
* Rather use the CSmartPtr<CTo>::Cast() function directly!
**/
* Casts a Util::CSmartPtr<CFrom> object to a Util:CSmartPtr<CTo> object.
* Note that this will actually release the original and set it to NULL.
* @returns
* Util::CSmartPtr<T>.
* - the cast operation is not possible because the classes are not suitably related, or
* - the original CSmartPtr contains a NULL pointer.
* NOTE:
* Function exists only for backward compatibility.
* Rather use the CSmartPtr<CTo>::Cast() function directly!
**/
template <class CTo, class CFrom>
inline Util::CSmartPtr<CTo> SmartPtrCast(
Util::CSmartPtr<CFrom> i_From)

View file

@ -1,44 +0,0 @@
#pragma once
#include <cstdint>
namespace elektro
{
namespace lrit
{
uint16_t crc_table[] = {
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0,
};
}
}

View file

@ -1,4 +1,4 @@
#include "lrit_data_decoder.h"
#include "lrit_data.h"
#include "resources.h"
#include "imgui/imgui_image.h"
#include "logger.h"

View file

@ -1,4 +1,4 @@
#include "lrit_data_decoder.h"
#include "lrit_data.h"
#include "resources.h"
#include "imgui/imgui_image.h"
#include "logger.h"

View file

@ -16,6 +16,13 @@ namespace elektro
{
namespace lrit
{
enum lrit_image_status
{
RECEIVING,
SAVING,
IDLE
};
class SegmentedLRITImageDecoder
{
private:
@ -33,13 +40,6 @@ namespace elektro
std::string image_id = "";
};
enum lrit_image_status
{
RECEIVING,
SAVING,
IDLE
};
class ELEKTRO221Composer
{
private:
@ -100,47 +100,5 @@ namespace elektro
unsigned int textureID = 0;
uint32_t *textureBuffer;
};
class LRITDataDecoder
{
private:
const std::string directory;
bool file_in_progress;
std::vector<uint8_t> lrit_data;
bool is_jpeg_compressed, is_wt_compressed;
std::string current_filename;
std::vector<uint8_t> decompression_buffer;
std::map<int, int> all_headers;
SegmentedLRITImageDecoder segmentedDecoder;
bool header_parsed = false;
void processLRITHeader(ccsds::CCSDSPacket &pkt);
void parseHeader();
void processLRITData(ccsds::CCSDSPacket &pkt);
void finalizeLRITData();
public: // Other things
std::shared_ptr<ELEKTRO221Composer> elektro_221_composer_full_disk;
std::shared_ptr<ELEKTRO321Composer> elektro_321_composer_full_disk;
public:
LRITDataDecoder(std::string dir);
~LRITDataDecoder();
void save();
void work(ccsds::CCSDSPacket &packet);
lrit_image_status imageStatus;
int img_width, img_height;
public:
// UI Stuff
bool hasToUpdate = false;
unsigned int textureID = 0;
uint32_t *textureBuffer;
};
} // namespace atms
} // namespace jpss

View file

@ -1,289 +0,0 @@
#pragma once
#include <cstdint>
#include <cstring>
#include <vector>
#include "common/utils.h"
namespace elektro
{
namespace lrit
{
struct PrimaryHeader
{
static constexpr int TYPE = 0;
uint8_t type;
uint16_t record_length;
uint8_t file_type_code;
uint32_t total_header_length;
uint64_t data_field_length;
PrimaryHeader(uint8_t *data)
{
type = data[0];
record_length = data[1] << 8 | data[2];
file_type_code = data[3];
total_header_length = (uint32_t)data[4] << 24 |
(uint32_t)data[5] << 16 |
(uint32_t)data[6] << 8 |
(uint32_t)data[7];
data_field_length = (uint64_t)data[8] << 56 |
(uint64_t)data[9] << 48 |
(uint64_t)data[10] << 40 |
(uint64_t)data[11] << 32 |
(uint64_t)data[12] << 24 |
(uint64_t)data[13] << 16 |
(uint64_t)data[14] << 8 |
(uint64_t)data[15];
}
};
struct ImageStructureRecord
{
static constexpr int TYPE = 1;
uint8_t type;
uint16_t record_length;
uint8_t bit_per_pixel;
uint16_t columns_count;
uint16_t lines_count;
uint8_t compression_flag;
ImageStructureRecord(uint8_t *data)
{
type = data[0];
record_length = data[1] << 8 | data[2];
bit_per_pixel = data[3];
columns_count = data[4] << 8 | data[5];
lines_count = data[6] << 8 | data[7];
compression_flag = data[8];
}
};
struct ImageNavigationRecord
{
static constexpr int TYPE = 2;
uint8_t type;
uint16_t record_length;
char projection_name[32];
uint32_t column_scaling_factor;
uint32_t line_scaling_factor;
uint32_t column_offset;
uint32_t line_offset;
ImageNavigationRecord(uint8_t *data)
{
type = data[0];
record_length = data[1] << 8 | data[2];
std::memcpy(projection_name, &data[3], 32);
column_scaling_factor = (uint32_t)data[36] << 24 |
(uint32_t)data[37] << 16 |
(uint32_t)data[38] << 8 |
(uint32_t)data[39];
line_scaling_factor = (uint32_t)data[40] << 24 |
(uint32_t)data[41] << 16 |
(uint32_t)data[42] << 8 |
(uint32_t)data[43];
column_offset = (uint32_t)data[44] << 24 |
(uint32_t)data[45] << 16 |
(uint32_t)data[46] << 8 |
(uint32_t)data[47];
line_offset = (uint32_t)data[48] << 24 |
(uint32_t)data[49] << 16 |
(uint32_t)data[50] << 8 |
(uint32_t)data[51];
}
};
struct ImageDataFunctionRecord
{
static constexpr int TYPE = 3;
uint8_t type;
uint16_t record_length;
std::string data_definition;
ImageDataFunctionRecord(uint8_t *data)
{
type = data[0];
record_length = data[1] << 8 | data[2];
data_definition.insert(data_definition.end(), &data[3], &data[record_length]);
}
};
struct AnnotationRecord
{
static constexpr int TYPE = 4;
uint8_t type;
uint16_t record_length;
std::string annotation_text;
AnnotationRecord(uint8_t *data)
{
type = data[0];
record_length = data[1] << 8 | data[2];
annotation_text.insert(annotation_text.end(), &data[3], &data[record_length]);
}
};
struct TimeStampRecord
{
static constexpr int TYPE = 5;
uint8_t type;
uint16_t record_length;
uint16_t days;
uint32_t milliseconds_of_day;
time_t timestamp;
TimeStampRecord(uint8_t *data)
{
type = data[0];
record_length = data[1] << 8 | data[2];
uint16_t days = data[3] << 8 | data[4];
uint32_t milliseconds_of_day = data[5] << 24 | data[6] << 16 | data[7] << 8 | data[8];
timestamp = (days - 4383) * 86400 + milliseconds_of_day;
}
};
struct AncillaryTextRecord
{
static constexpr int TYPE = 6;
uint8_t type;
uint16_t record_length;
std::string ancillary_text;
std::map<std::string, std::string> meta;
AncillaryTextRecord(uint8_t *data)
{
type = data[0];
record_length = data[1] << 8 | data[2];
ancillary_text.insert(ancillary_text.end(), &data[3], &data[record_length]);
// I will admit I needed to peek in goestools to figure that one out
// A bit hard without having live data...
std::vector<std::string> fields = splitString(ancillary_text, ';');
for (std::string &field : fields)
{
std::vector<std::string> values = splitString(field, '=');
if (values.size() == 2)
{
values[0] = values[0].substr(0, values[0].find_last_not_of(' ') + 1);
values[1] = values[1].substr(values[1].find_first_not_of(' '));
meta.insert({values[0], values[1]});
}
}
}
};
struct KeyHeader
{
static constexpr int TYPE = 7;
uint8_t type;
uint16_t record_length;
uint32_t key;
KeyHeader(uint8_t *data)
{
type = data[0];
record_length = data[1] << 8 | data[2];
key = data[3] << 24 | data[4] << 16 | data[5] << 8 | data[6];
}
};
struct SegmentIdentificationHeader
{
static constexpr int TYPE = 128;
uint8_t type;
uint16_t record_length;
uint16_t sc_id;
uint8_t channel_id;
uint16_t segment_sequence_number;
uint16_t planned_start_segment;
uint16_t planned_end_segment;
uint8_t compression;
SegmentIdentificationHeader(uint8_t *data)
{
type = data[0];
record_length = data[1] << 8 | data[2];
sc_id = data[3] << 8 | data[4];
channel_id = data[5];
segment_sequence_number = data[6] << 8 | data[7];
planned_start_segment = data[8] << 8 | data[9];
planned_end_segment = data[10] << 8 | data[11];
compression = data[12];
}
};
/*struct NOAALRITHeader
{
static constexpr int TYPE = 129;
uint8_t type;
uint16_t record_length;
char agency_signature[4];
uint16_t product_id;
uint16_t product_subid;
uint16_t parameter;
uint8_t noaa_specific_compression;
NOAALRITHeader(uint8_t *data)
{
type = data[0];
record_length = data[1] << 8 | data[2];
std::memcpy(agency_signature, &data[3], 4);
product_id = data[7] << 8 | data[8];
product_subid = data[9] << 8 | data[10];
parameter = data[11] << 8 | data[12];
noaa_specific_compression = data[13];
}
};*/
struct HeaderStructureRecord
{
static constexpr int TYPE = 130;
uint8_t type;
uint16_t record_length;
std::string header_structure;
HeaderStructureRecord(uint8_t *data)
{
type = data[0];
record_length = data[1] << 8 | data[2];
header_structure.insert(header_structure.end(), &data[3], &data[record_length]);
}
};
/*struct RiceCompressionHeader
{
static constexpr int TYPE = 131;
uint8_t type;
uint16_t record_length;
uint16_t flags;
uint8_t pixels_per_block;
uint8_t scanlines_per_packet;
RiceCompressionHeader(uint8_t *data)
{
type = data[0];
record_length = data[1] << 8 | data[2];
flags = data[3] << 8 | data[4];
pixels_per_block = data[5];
scanlines_per_packet = data[6];
}
};*/
}
}

View file

@ -1,4 +1,4 @@
#include "lrit_data_decoder.h"
#include "lrit_data.h"
#include <cstring>
namespace elektro

View file

@ -0,0 +1,38 @@
#pragma once
#include <cstdint>
#include <cstring>
#include <vector>
#include "common/utils.h"
namespace elektro
{
namespace lrit
{
struct SegmentIdentificationHeader
{
static constexpr int TYPE = 128;
uint8_t type;
uint16_t record_length;
uint16_t sc_id;
uint8_t channel_id;
uint16_t segment_sequence_number;
uint16_t planned_start_segment;
uint16_t planned_end_segment;
uint8_t compression;
SegmentIdentificationHeader(uint8_t *data)
{
type = data[0];
record_length = data[1] << 8 | data[2];
sc_id = data[3] << 8 | data[4];
channel_id = data[5];
segment_sequence_number = data[6] << 8 | data[7];
planned_start_segment = data[8] << 8 | data[9];
planned_end_segment = data[10] << 8 | data[11];
compression = data[12];
}
};
}
}

View file

@ -1,16 +1,11 @@
#include "module_elektro_lrit_data_decoder.h"
#include <fstream>
#include "common/ccsds/ccsds_1_0_1024/demuxer.h"
#include "common/ccsds/ccsds_1_0_1024/vcdu.h"
#include "logger.h"
#include <filesystem>
#include "imgui/imgui.h"
#include "imgui/imgui_image.h"
#define BUFFER_SIZE 8192
// Return filesize
size_t getFilesize(std::string filepath);
#include "common/utils.h"
#include "common/lrit/lrit_demux.h"
namespace elektro
{
@ -34,9 +29,9 @@ namespace elektro
ELEKTROLRITDataDecoderModule::~ELEKTROLRITDataDecoderModule()
{
for (std::pair<int, std::shared_ptr<LRITDataDecoder>> decMap : decoders)
for (auto &decMap : all_wip_images)
{
std::shared_ptr<LRITDataDecoder> dec = decMap.second;
auto &dec = decMap.second;
if (dec->textureID > 0)
{
@ -70,7 +65,40 @@ namespace elektro
logger->info("Demultiplexing and deframing...");
std::map<int, std::shared_ptr<ccsds::ccsds_1_0_1024::Demuxer>> demuxers;
// std::map<int, std::shared_ptr<ccsds::ccsds_1_0_1024::Demuxer>> demuxers;
::lrit::LRITDemux lrit_demux;
this->directory = directory;
lrit_demux.onParseHeader =
[](::lrit::LRITFile &file) -> void
{
// Check if this is image data
if (file.hasHeader<::lrit::ImageStructureRecord>())
{
::lrit::ImageStructureRecord image_structure_record = file.getHeader<::lrit::ImageStructureRecord>(); //(&lrit_data[all_headers[ImageStructureRecord::TYPE]]);
logger->debug("This is image data. Size " + std::to_string(image_structure_record.columns_count) + "x" + std::to_string(image_structure_record.lines_count));
if (image_structure_record.compression_flag == 2 /* Progressive JPEG */)
{
logger->debug("JPEG Compression is used, decompressing...");
file.custom_flags.insert_or_assign(JPEG_COMPRESSED, true);
file.custom_flags.insert_or_assign(WT_COMPRESSED, false);
}
else if (image_structure_record.compression_flag == 1 /* Wavelet */)
{
logger->debug("Wavelet Compression is used, decompressing...");
file.custom_flags.insert_or_assign(JPEG_COMPRESSED, false);
file.custom_flags.insert_or_assign(WT_COMPRESSED, true);
}
else
{
file.custom_flags.insert_or_assign(JPEG_COMPRESSED, false);
file.custom_flags.insert_or_assign(WT_COMPRESSED, false);
}
}
};
while (input_data_type == DATA_FILE ? !data_in.eof() : input_active.load())
{
@ -80,33 +108,10 @@ namespace elektro
else
input_fifo->read((uint8_t *)&cadu, 1024);
// Parse this transport frame
ccsds::ccsds_1_0_1024::VCDU vcdu = ccsds::ccsds_1_0_1024::parseVCDU(cadu);
std::vector<::lrit::LRITFile> files = lrit_demux.work(cadu);
if (vcdu.vcid == 63)
continue;
if (demuxers.count(vcdu.vcid) <= 0)
demuxers.emplace(std::pair<int, std::shared_ptr<ccsds::ccsds_1_0_1024::Demuxer>>(vcdu.vcid, std::make_shared<ccsds::ccsds_1_0_1024::Demuxer>(884, false)));
// Demux
std::vector<ccsds::CCSDSPacket> ccsdsFrames = demuxers[vcdu.vcid]->work(cadu);
// Push into processor (filtering APID 103 and 104)
for (ccsds::CCSDSPacket &pkt : ccsdsFrames)
{
if (pkt.header.apid != 2047)
{
if (decoders.count(vcdu.vcid) <= 0)
{
decoders.emplace(std::pair<int, std::shared_ptr<LRITDataDecoder>>(vcdu.vcid, std::make_shared<LRITDataDecoder>(directory)));
decoders[vcdu.vcid]->elektro_221_composer_full_disk = elektro_221_composer_full_disk;
decoders[vcdu.vcid]->elektro_321_composer_full_disk = elektro_321_composer_full_disk;
}
decoders[vcdu.vcid]->work(pkt);
}
}
for (auto &file : files)
processLRITFile(file);
if (input_data_type == DATA_FILE)
progress = data_in.tellg();
@ -120,8 +125,19 @@ namespace elektro
data_in.close();
for (const std::pair<const int, std::shared_ptr<LRITDataDecoder>> &dec : decoders)
dec.second->save();
for (auto &segmentedDecoder : segmentedDecoders)
{
if (segmentedDecoder.second.image_id != "")
{
logger->info("Writing image " + directory + "/IMAGES/" + segmentedDecoder.second.image_id + ".png" + "...");
segmentedDecoder.second.image.save_png(std::string(directory + "/IMAGES/" + segmentedDecoder.second.image_id + ".png").c_str());
}
}
if (elektro_221_composer_full_disk->hasData)
elektro_221_composer_full_disk->save(directory);
if (elektro_321_composer_full_disk->hasData)
elektro_321_composer_full_disk->save(directory);
}
void ELEKTROLRITDataDecoderModule::drawUI(bool window)
@ -132,9 +148,9 @@ namespace elektro
{
bool hasImage = false;
for (std::pair<int, std::shared_ptr<LRITDataDecoder>> decMap : decoders)
for (auto &decMap : all_wip_images)
{
std::shared_ptr<LRITDataDecoder> dec = decMap.second;
auto &dec = decMap.second;
if (dec->textureID == 0)
{
@ -152,7 +168,7 @@ namespace elektro
hasImage = true;
if (ImGui::BeginTabItem(std::string("VCID " + std::to_string(decMap.first)).c_str()))
if (ImGui::BeginTabItem(std::string("Ch " + std::to_string(decMap.first)).c_str()))
{
ImGui::Image((void *)(intptr_t)dec->textureID, {200 * ui_scale, 200 * ui_scale});
ImGui::SameLine();

View file

@ -1,7 +1,9 @@
#pragma once
#include "core/module.h"
#include "data/lrit_data_decoder.h"
#include "data/lrit_data.h"
#include "common/lrit/lrit_file.h"
namespace elektro
{
@ -13,10 +15,32 @@ namespace elektro
std::atomic<size_t> filesize;
std::atomic<size_t> progress;
std::map<int, std::shared_ptr<LRITDataDecoder>> decoders;
std::shared_ptr<ELEKTRO221Composer> elektro_221_composer_full_disk;
std::shared_ptr<ELEKTRO321Composer> elektro_321_composer_full_disk;
std::shared_ptr<ELEKTRO321Composer> elektro_321_composer_full_disk;
std::map<int, SegmentedLRITImageDecoder> segmentedDecoders;
std::string directory;
enum CustomFileParams
{
JPEG_COMPRESSED,
WT_COMPRESSED,
};
struct wip_images
{
lrit_image_status imageStatus = RECEIVING;
int img_width, img_height;
// UI Stuff
bool hasToUpdate = false;
unsigned int textureID = 0;
uint32_t *textureBuffer;
};
std::map<int, std::unique_ptr<wip_images>> all_wip_images;
void processLRITFile(::lrit::LRITFile &file);
public:
ELEKTROLRITDataDecoderModule(std::string input_file, std::string output_file_hint, nlohmann::json parameters);

View file

@ -1,18 +1,9 @@
#include "lrit_data_decoder.h"
#include "module_elektro_lrit_data_decoder.h"
#include "logger.h"
#include <fstream>
#include "lrit_header.h"
#include "crc_table.h"
#include <sstream>
#include <string>
#include <iomanip>
#include <filesystem>
#include <algorithm>
#include "common/utils.h"
#include "libs/others/strptime.h"
#include "imgui/imgui_image.h"
#include "lrit_header.h"
#include "common/image/jpeg_utils.h"
#include "resources.h"
#include "DecompWT/CompressWT.h"
#include "DecompWT/CompressT4.h"
@ -44,181 +35,28 @@ namespace elektro
return utc_filename;
}
// CRC Implementation from LRIT-Missin-Specific-Document.pdf
uint16_t computeCRC(const uint8_t *data, int size)
void ELEKTROLRITDataDecoderModule::processLRITFile(::lrit::LRITFile &file)
{
uint16_t crc = 0xffff;
for (int i = 0; i < size; i++)
crc = (crc << 8) ^ crc_table[(crc >> 8) ^ (uint16_t)data[i]];
return crc;
}
std::string current_filename = file.filename;
LRITDataDecoder::LRITDataDecoder(std::string dir) : directory(dir)
{
file_in_progress = false;
imageStatus = IDLE;
img_height = 0;
img_width = 0;
}
LRITDataDecoder::~LRITDataDecoder()
{
}
void LRITDataDecoder::work(ccsds::CCSDSPacket &packet)
{
if (packet.header.sequence_flag == 1 || packet.header.sequence_flag == 3)
{
if (file_in_progress)
finalizeLRITData();
lrit_data.clear();
// Check CRC
uint16_t crc = packet.payload.data()[packet.payload.size() - 2] << 8 | packet.payload.data()[packet.payload.size() - 1];
if (crc == computeCRC(packet.payload.data(), packet.payload.size() - 2))
{
processLRITHeader(packet);
header_parsed = false;
file_in_progress = true;
}
else
{
logger->error("CRC is invalid... Skipping.");
file_in_progress = false;
}
}
else if (packet.header.sequence_flag == 0)
{
if (file_in_progress)
{
processLRITData(packet);
}
}
else if (packet.header.sequence_flag == 2)
{
if (file_in_progress)
{
processLRITData(packet);
finalizeLRITData();
file_in_progress = false;
}
}
if (file_in_progress && !header_parsed)
{
PrimaryHeader primary_header(&lrit_data[0]);
if (lrit_data.size() >= primary_header.total_header_length)
{
parseHeader();
header_parsed = true;
}
}
}
void LRITDataDecoder::processLRITHeader(ccsds::CCSDSPacket &pkt)
{
lrit_data.insert(lrit_data.end(), &pkt.payload.data()[10], &pkt.payload.data()[pkt.payload.size() - 2]);
}
void LRITDataDecoder::parseHeader()
{
PrimaryHeader primary_header(&lrit_data[0]);
// Get all other headers
all_headers.clear();
for (uint32_t i = 0; i < primary_header.total_header_length;)
{
uint8_t type = lrit_data[i];
uint16_t record_length = lrit_data[i + 1] << 8 | lrit_data[i + 2];
if (record_length == 0)
break;
all_headers.emplace(std::pair<int, int>(type, i));
i += record_length;
}
// Check if this has a filename
if (all_headers.count(AnnotationRecord::TYPE) > 0)
{
AnnotationRecord annotation_record(&lrit_data[all_headers[AnnotationRecord::TYPE]]);
current_filename = std::string(annotation_record.annotation_text.data());
std::replace(current_filename.begin(), current_filename.end(), '/', '_'); // Safety
std::replace(current_filename.begin(), current_filename.end(), '\\', '_'); // Safety
for (char &c : current_filename) // Strip invalid chars
{
if (c < 33)
c = '_';
}
logger->info("New LRIT file : " + current_filename);
// Check if this is image data
if (all_headers.count(ImageStructureRecord::TYPE) > 0)
{
ImageStructureRecord image_structure_record(&lrit_data[all_headers[ImageStructureRecord::TYPE]]);
logger->debug("This is image data. Size " + std::to_string(image_structure_record.columns_count) + "x" + std::to_string(image_structure_record.lines_count));
if (image_structure_record.compression_flag == 2 /* Progressive JPEG */)
{
logger->debug("JPEG Compression is used, decompressing...");
is_jpeg_compressed = true;
is_wt_compressed = false;
}
else if (image_structure_record.compression_flag == 1 /* Wavelet */)
{
logger->debug("Wavelet Compression is used, decompressing...");
is_jpeg_compressed = false;
is_wt_compressed = true;
}
else
{
is_jpeg_compressed = false;
is_wt_compressed = false;
}
}
if (all_headers.count(KeyHeader::TYPE) > 0)
{
KeyHeader key_header(&lrit_data[all_headers[KeyHeader::TYPE]]);
logger->debug("This is encrypted!");
}
}
}
void LRITDataDecoder::processLRITData(ccsds::CCSDSPacket &pkt)
{
// File may be encrypted so we cannot process them before the fact...
lrit_data.insert(lrit_data.end(), &pkt.payload.data()[0], &pkt.payload.data()[pkt.payload.size() - 2]);
}
void LRITDataDecoder::finalizeLRITData()
{
PrimaryHeader primary_header(&lrit_data[0]);
::lrit::PrimaryHeader primary_header = file.getHeader<::lrit::PrimaryHeader>();
// Check if this is image data, and if so also write it as an image
if (primary_header.file_type_code == 0 && all_headers.count(ImageStructureRecord::TYPE) > 0)
if (primary_header.file_type_code == 0 && file.hasHeader<::lrit::ImageStructureRecord>())
{
if (!std::filesystem::exists(directory + "/IMAGES"))
std::filesystem::create_directory(directory + "/IMAGES");
ImageStructureRecord image_structure_record(&lrit_data[all_headers[ImageStructureRecord::TYPE]]);
::lrit::ImageStructureRecord image_structure_record = file.getHeader<::lrit::ImageStructureRecord>();
if (is_jpeg_compressed) // Is this Jpeg-Compressed? Decompress
if (file.custom_flags[JPEG_COMPRESSED]) // Is this Jpeg-Compressed? Decompress
{
logger->info("Decompressing JPEG...");
image::Image<uint8_t> img = image::decompress_jpeg(&lrit_data[primary_header.total_header_length], lrit_data.size() - primary_header.total_header_length, true);
lrit_data.erase(lrit_data.begin() + primary_header.total_header_length, lrit_data.end());
lrit_data.insert(lrit_data.end(), (uint8_t *)&img[0], (uint8_t *)&img[img.height() * img.width()]);
image::Image<uint8_t> img = image::decompress_jpeg(&file.lrit_data[primary_header.total_header_length], file.lrit_data.size() - primary_header.total_header_length, true);
file.lrit_data.erase(file.lrit_data.begin() + primary_header.total_header_length, file.lrit_data.end());
file.lrit_data.insert(file.lrit_data.end(), (uint8_t *)&img[0], (uint8_t *)&img[img.height() * img.width()]);
}
else if (is_wt_compressed) // Is this Wavelet-Compressed? Decompress. We know this will always be 10-bits
else if (file.custom_flags[WT_COMPRESSED]) // Is this Wavelet-Compressed? Decompress. We know this will always be 10-bits
{
/*
This could be better... Sometimes I should maybe adapt the DecompWT code to be cleaner for this purpose...
@ -227,9 +65,9 @@ namespace elektro
int compression_type = 3; // We assume Wavelet
if (all_headers.count(SegmentIdentificationHeader::TYPE) > 0) // But if we have a header with better info, use it
if (file.all_headers.count(SegmentIdentificationHeader::TYPE) > 0) // But if we have a header with better info, use it
{
SegmentIdentificationHeader segment_id_header(&lrit_data[all_headers[SegmentIdentificationHeader::TYPE]]);
SegmentIdentificationHeader segment_id_header = file.getHeader<SegmentIdentificationHeader>();
compression_type = segment_id_header.compression;
}
@ -241,12 +79,12 @@ namespace elektro
// Decompress
{
// We need to copy over that memory to its own buffer
uint8_t *compressedData = new uint8_t[lrit_data.size() - primary_header.total_header_length];
std::memcpy(compressedData, &lrit_data[primary_header.total_header_length], lrit_data.size() - primary_header.total_header_length);
uint8_t *compressedData = new uint8_t[file.lrit_data.size() - primary_header.total_header_length];
std::memcpy(compressedData, &file.lrit_data[primary_header.total_header_length], file.lrit_data.size() - primary_header.total_header_length);
// Images object
Util::CDataFieldCompressedImage compressedImage(compressedData,
(lrit_data.size() - primary_header.total_header_length) * 8,
(file.lrit_data.size() - primary_header.total_header_length) * 8,
image_structure_record.bit_per_pixel,
image_structure_record.columns_count,
image_structure_record.lines_count);
@ -278,7 +116,7 @@ namespace elektro
if (out_pixels == image_structure_record.columns_count * image_structure_record.lines_count) // Matches?
{
// Empty current LRIT file
lrit_data.erase(lrit_data.begin() + primary_header.total_header_length, lrit_data.end());
file.lrit_data.erase(file.lrit_data.begin() + primary_header.total_header_length, file.lrit_data.end());
if (image_structure_record.bit_per_pixel == 10)
{
@ -290,21 +128,21 @@ namespace elektro
uint16_t v3 = ((image_ptr[2] % 16) << 6) | (image_ptr[3] >> 2);
uint16_t v4 = ((image_ptr[3] % 4) << 8) | image_ptr[4];
lrit_data.push_back(v1 >> 2);
lrit_data.push_back(v2 >> 2);
lrit_data.push_back(v3 >> 2);
lrit_data.push_back(v4 >> 2);
file.lrit_data.push_back(v1 >> 2);
file.lrit_data.push_back(v2 >> 2);
file.lrit_data.push_back(v3 >> 2);
file.lrit_data.push_back(v4 >> 2);
image_ptr += 5;
}
// Fill remaining if required
for (int i = 0; i < buf_size % 5; i++)
lrit_data.push_back(0);
file.lrit_data.push_back(0);
}
else if (image_structure_record.bit_per_pixel == 8) // Just in case
{
lrit_data.insert(lrit_data.end(), image_ptr, &image_ptr[buf_size]);
file.lrit_data.insert(file.lrit_data.end(), image_ptr, &image_ptr[buf_size]);
}
else
{
@ -318,11 +156,11 @@ namespace elektro
out_pixels = image_structure_record.lines_count * image_structure_record.columns_count;
// Empty current LRIT file
lrit_data.erase(lrit_data.begin() + primary_header.total_header_length, lrit_data.end());
file.lrit_data.erase(file.lrit_data.begin() + primary_header.total_header_length, file.lrit_data.end());
// Fill with 0s
for (int i = 0; i < out_pixels; i++)
lrit_data.push_back(0);
file.lrit_data.push_back(0);
}
// Free up memory if necessary
@ -330,10 +168,9 @@ namespace elektro
// delete[] image_ptr;
}
if (all_headers.count(SegmentIdentificationHeader::TYPE) > 0)
if (file.all_headers.count(SegmentIdentificationHeader::TYPE) > 0)
{
imageStatus = RECEIVING;
SegmentIdentificationHeader segment_id_header(&lrit_data[all_headers[SegmentIdentificationHeader::TYPE]]);
SegmentIdentificationHeader segment_id_header = file.getHeader<SegmentIdentificationHeader>();
std::vector<std::string> header_parts = splitString(current_filename, '_');
@ -389,13 +226,23 @@ namespace elektro
}
}
if (all_wip_images.count(channel) == 0)
all_wip_images.insert({channel, std::make_unique<wip_images>()});
std::unique_ptr<wip_images> &wip_img = all_wip_images[channel];
if (segmentedDecoders.count(channel) == 0)
segmentedDecoders.insert({channel, SegmentedLRITImageDecoder()});
SegmentedLRITImageDecoder &segmentedDecoder = segmentedDecoders[channel];
if (segmentedDecoder.image_id != image_id)
{
if (segmentedDecoder.image_id != "")
{
current_filename = image_id;
imageStatus = SAVING;
wip_img->imageStatus = SAVING;
logger->info("Writing image " + directory + "/IMAGES/" + current_filename + ".png" + "...");
segmentedDecoder.image.save_png(std::string(directory + "/IMAGES/" + current_filename + ".png").c_str());
@ -404,7 +251,7 @@ namespace elektro
if (elektro_321_composer_full_disk->hasData)
elektro_321_composer_full_disk->save(directory);
imageStatus = RECEIVING;
wip_img->imageStatus = RECEIVING;
}
segmentedDecoder = SegmentedLRITImageDecoder(segment_id_header.planned_end_segment,
@ -414,7 +261,7 @@ namespace elektro
}
int seg_number = segment_id_header.segment_sequence_number - 1;
segmentedDecoder.pushSegment(&lrit_data[primary_header.total_header_length], seg_number);
segmentedDecoder.pushSegment(&file.lrit_data[primary_header.total_header_length], seg_number);
// Composite?
if (channel == 1)
@ -433,22 +280,22 @@ namespace elektro
}
// If the UI is active, update texture
if (textureID > 0)
if (wip_img->textureID > 0)
{
// Downscale image
img_height = 1000;
img_width = 1000;
wip_img->img_height = 1000;
wip_img->img_width = 1000;
image::Image<uint8_t> imageScaled = segmentedDecoder.image;
imageScaled.resize(img_width, img_height);
uchar_to_rgba(imageScaled.data(), textureBuffer, img_height * img_width);
hasToUpdate = true;
imageScaled.resize(wip_img->img_width, wip_img->img_height);
uchar_to_rgba(imageScaled.data(), wip_img->textureBuffer, wip_img->img_height * wip_img->img_width);
wip_img->hasToUpdate = true;
}
if (segmentedDecoder.isComplete())
{
current_filename = image_id;
imageStatus = SAVING;
wip_img->imageStatus = SAVING;
logger->info("Writing image " + directory + "/IMAGES/" + current_filename + ".png" + "...");
segmentedDecoder.image.save_png(std::string(directory + "/IMAGES/" + current_filename + ".png").c_str());
@ -458,14 +305,14 @@ namespace elektro
elektro_321_composer_full_disk->save(directory);
segmentedDecoder = SegmentedLRITImageDecoder();
imageStatus = IDLE;
wip_img->imageStatus = IDLE;
}
}
else
{
// Write raw image dats
logger->info("Writing image " + directory + "/IMAGES/" + current_filename + ".png" + "...");
image::Image<uint8_t> image(&lrit_data[primary_header.total_header_length], image_structure_record.columns_count, image_structure_record.lines_count, 1);
image::Image<uint8_t> image(&file.lrit_data[primary_header.total_header_length], image_structure_record.columns_count, image_structure_record.lines_count, 1);
image.save_png(std::string(directory + "/IMAGES/" + current_filename + ".png").c_str());
}
}
@ -474,24 +321,13 @@ namespace elektro
if (!std::filesystem::exists(directory + "/LRIT"))
std::filesystem::create_directory(directory + "/LRIT");
logger->info("Writing file " + directory + "/LRIT/" + current_filename + "...");
logger->info("Writing file " + directory + "/LRIT/" + file.filename + "...");
// Write file out
std::ofstream file(directory + "/LRIT/" + current_filename, std::ios::binary);
file.write((char *)lrit_data.data(), lrit_data.size());
file.close();
std::ofstream fileo(directory + "/LRIT/" + file.filename, std::ios::binary);
fileo.write((char *)file.lrit_data.data(), file.lrit_data.size());
fileo.close();
}
}
void LRITDataDecoder::save()
{
if (segmentedDecoder.image_id != "")
{
finalizeLRITData();
logger->info("Writing image " + directory + "/IMAGES/" + current_filename + ".png" + "...");
segmentedDecoder.image.save_png(std::string(directory + "/IMAGES/" + current_filename + ".png").c_str());
}
}
} // namespace atms
} // namespace jpss
} // namespace avhrr
} // namespace metop

View file

@ -0,0 +1,41 @@
#pragma once
#include <cstdint>
namespace lrit
{
uint16_t crc_table[] = {
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0,
};
}

View file

@ -0,0 +1,129 @@
#include "lrit_demux.h"
#include "common/ccsds/ccsds_1_0_1024/vcdu.h"
#include "logger.h"
#include "crc_table.h"
namespace lrit
{
LRITDemux::LRITDemux(int mpdu_size)
: d_mpdu_size(mpdu_size)
{
}
LRITDemux::~LRITDemux()
{
}
// CRC Implementation from LRIT-Mission-Specific-Document.pdf
uint16_t computeCRC(const uint8_t *data, int size)
{
uint16_t crc = 0xffff;
for (int i = 0; i < size; i++)
crc = (crc << 8) ^ crc_table[(crc >> 8) ^ (uint16_t)data[i]];
return crc;
}
std::vector<LRITFile> LRITDemux::work(uint8_t *cadu)
{
files.clear();
ccsds::ccsds_1_0_1024::VCDU vcdu = ccsds::ccsds_1_0_1024::parseVCDU(cadu);
if (vcdu.vcid == 63) // Skip filler
return files;
if (demuxers.count(vcdu.vcid) <= 0) // Add new demux if required
{
demuxers.emplace(std::pair<int, std::unique_ptr<ccsds::ccsds_1_0_1024::Demuxer>>(vcdu.vcid, std::make_unique<ccsds::ccsds_1_0_1024::Demuxer>(d_mpdu_size, false)));
wip_files.insert({vcdu.vcid, std::map<int, LRITFile>()});
}
// Demux
std::vector<ccsds::CCSDSPacket> ccsdsFrames = demuxers[vcdu.vcid]->work(cadu);
for (ccsds::CCSDSPacket &pkt : ccsdsFrames)
{
if (pkt.header.apid == 2047) // Skip filler
return files;
if (wip_files[vcdu.vcid].count(pkt.header.apid) == 0) // One file per APID
wip_files[vcdu.vcid].insert({pkt.header.apid, LRITFile()});
LRITFile &current_file = wip_files[vcdu.vcid][pkt.header.apid];
if (pkt.header.sequence_flag == 1 || pkt.header.sequence_flag == 3)
{
if (current_file.file_in_progress)
finalizeLRITData(current_file);
current_file.lrit_data.clear();
// Check CRC
uint16_t crc = pkt.payload.data()[pkt.payload.size() - 2] << 8 | pkt.payload.data()[pkt.payload.size() - 1];
if (crc == computeCRC(pkt.payload.data(), pkt.payload.size() - 2))
{
processLRITHeader(current_file, pkt);
current_file.header_parsed = false;
current_file.file_in_progress = true;
}
else
{
logger->error("LRIT CRC is invalid... Skipping.");
current_file.file_in_progress = false;
}
}
else if (pkt.header.sequence_flag == 0)
{
if (current_file.file_in_progress)
processLRITData(current_file, pkt);
}
else if (pkt.header.sequence_flag == 2)
{
if (current_file.file_in_progress)
{
processLRITData(current_file, pkt);
finalizeLRITData(current_file);
current_file.file_in_progress = false;
}
}
if (current_file.file_in_progress && !current_file.header_parsed)
{
PrimaryHeader primary_header = current_file.getHeader<PrimaryHeader>();
if (current_file.lrit_data.size() >= primary_header.total_header_length)
{
parseHeader(current_file);
current_file.header_parsed = true;
}
}
}
return files;
}
void LRITDemux::processLRITHeader(LRITFile &file, ccsds::CCSDSPacket &pkt)
{
file.lrit_data.insert(file.lrit_data.end(), &pkt.payload.data()[10], &pkt.payload.data()[pkt.payload.size() - 2]);
}
void LRITDemux::parseHeader(LRITFile &file)
{
file.parseHeaders();
logger->info("New LRIT file : " + file.filename);
onParseHeader(file);
}
void LRITDemux::processLRITData(LRITFile &file, ccsds::CCSDSPacket &pkt)
{
if (onProcessData(file, pkt))
file.lrit_data.insert(file.lrit_data.end(), &pkt.payload.data()[0], &pkt.payload.data()[pkt.payload.size() - 2]);
}
void LRITDemux::finalizeLRITData(LRITFile &file)
{
files.push_back(file);
}
};

View file

@ -0,0 +1,40 @@
#pragma once
#include "lrit_file.h"
#include "common/ccsds/ccsds_1_0_1024/demuxer.h"
#include <memory>
#include <functional>
namespace lrit
{
class LRITDemux
{
private:
const int d_mpdu_size;
private:
std::map<int, std::unique_ptr<ccsds::ccsds_1_0_1024::Demuxer>> demuxers;
std::map<int, std::map<int, LRITFile>> wip_files;
std::vector<LRITFile> files;
private:
void processLRITHeader(LRITFile &file, ccsds::CCSDSPacket &pkt);
void parseHeader(LRITFile &file);
void processLRITData(LRITFile &file, ccsds::CCSDSPacket &pkt);
void finalizeLRITData(LRITFile &file);
public:
std::function<void(LRITFile &)> onParseHeader =
[](LRITFile &) -> void {};
std::function<bool(LRITFile &, ccsds::CCSDSPacket &)> onProcessData =
[](LRITFile &, ccsds::CCSDSPacket &) -> bool
{ return true; };
public:
LRITDemux(int mpdu_size = 884);
~LRITDemux();
std::vector<LRITFile> work(uint8_t *cadu);
};
};

View file

@ -0,0 +1,44 @@
#include "lrit_file.h"
#include <algorithm>
namespace lrit
{
void LRITFile::parseHeaders()
{
PrimaryHeader primary_header = getHeader<PrimaryHeader>();
// Get all other headers
all_headers.clear();
for (uint32_t i = 0; i < primary_header.total_header_length;)
{
uint8_t type = lrit_data[i];
uint16_t record_length = lrit_data[i + 1] << 8 | lrit_data[i + 2];
if (record_length == 0)
break;
all_headers.emplace(std::pair<int, int>(type, i));
i += record_length;
}
// Check if this has a filename
if (all_headers.count(AnnotationRecord::TYPE) > 0)
{
AnnotationRecord annotation_record = getHeader<AnnotationRecord>();
filename = std::string(annotation_record.annotation_text.data());
std::replace(filename.begin(), filename.end(), '/', '_'); // Safety
std::replace(filename.begin(), filename.end(), '\\', '_'); // Safety
for (char &c : filename) // Strip invalid chars
{
if (c < 33)
c = '_';
}
}
total_header_length = primary_header.total_header_length;
}
}

View file

@ -0,0 +1,109 @@
#pragma once
#include <cstdint>
#include <vector>
#include <map>
#include <string>
namespace lrit
{
////////////////////////////////////////
struct PrimaryHeader
{
static constexpr int TYPE = 0;
uint8_t type;
uint16_t record_length;
uint8_t file_type_code;
uint32_t total_header_length;
uint64_t data_field_length;
PrimaryHeader(uint8_t *data)
{
type = data[0];
record_length = data[1] << 8 | data[2];
file_type_code = data[3];
total_header_length = (uint32_t)data[4] << 24 |
(uint32_t)data[5] << 16 |
(uint32_t)data[6] << 8 |
(uint32_t)data[7];
data_field_length = (uint64_t)data[8] << 56 |
(uint64_t)data[9] << 48 |
(uint64_t)data[10] << 40 |
(uint64_t)data[11] << 32 |
(uint64_t)data[12] << 24 |
(uint64_t)data[13] << 16 |
(uint64_t)data[14] << 8 |
(uint64_t)data[15];
}
};
struct AnnotationRecord
{
static constexpr int TYPE = 4;
uint8_t type;
uint16_t record_length;
std::string annotation_text;
AnnotationRecord(uint8_t *data)
{
type = data[0];
record_length = data[1] << 8 | data[2];
annotation_text.insert(annotation_text.end(), &data[3], &data[record_length]);
}
};
struct ImageStructureRecord
{
static constexpr int TYPE = 1;
uint8_t type;
uint16_t record_length;
uint8_t bit_per_pixel;
uint16_t columns_count;
uint16_t lines_count;
uint8_t compression_flag;
ImageStructureRecord(uint8_t *data)
{
type = data[0];
record_length = data[1] << 8 | data[2];
bit_per_pixel = data[3];
columns_count = data[4] << 8 | data[5];
lines_count = data[6] << 8 | data[7];
compression_flag = data[8];
}
};
////////////////////////////////////////
struct LRITFile
{
bool file_in_progress = false;
bool header_parsed = false;
std::map<int, int> custom_flags;
std::string filename;
int total_header_length;
std::map<int, int> all_headers;
std::vector<uint8_t> lrit_data;
template <typename T>
T getHeader()
{
if constexpr (std::is_same_v<T, PrimaryHeader>)
return PrimaryHeader(&lrit_data[0]);
else
return T(&lrit_data[all_headers[T::TYPE]]);
}
template <typename T>
bool hasHeader()
{
return all_headers.count(T::TYPE);
}
void parseHeaders();
};
};