mirror of
https://github.com/SatDump/SatDump
synced 2026-08-13 17:47:30 -04:00
599 lines
No EOL
27 KiB
C++
599 lines
No EOL
27 KiB
C++
#include "identify.h"
|
|
#include "logger.h"
|
|
#include "msg/msg_headers.h"
|
|
#include "utils/string.h"
|
|
#include "xrit/fy4/fy4_headers.h"
|
|
#include "xrit/goes/goes_headers.h"
|
|
#include <cmath>
|
|
#include <cstring>
|
|
#include <ctime>
|
|
#include <exception>
|
|
#include <string>
|
|
|
|
namespace satdump
|
|
{
|
|
namespace xrit
|
|
{
|
|
////////////////////////////////////////
|
|
// Identifcation done from file data
|
|
////////////////////////////////////////
|
|
|
|
bool identifyElektroFile(XRITFileInfo &i, XRITFile &file)
|
|
{
|
|
i.filename = file.filename;
|
|
|
|
auto parts = splitString(i.filename, '-');
|
|
|
|
if (parts.size() != 8)
|
|
return false;
|
|
|
|
if ((parts[0] != "H" && parts[0] != "L") || parts[1] != "000")
|
|
return false;
|
|
|
|
int sat_num;
|
|
if (sscanf(parts[2].c_str(), "GOMS%1d", &sat_num) != 1)
|
|
return false;
|
|
|
|
std::tm timeS;
|
|
memset(&timeS, 0, sizeof(std::tm));
|
|
if (sscanf(parts[6].c_str(), "%4d%2d%2d%2d%2d", &timeS.tm_year, &timeS.tm_mon, &timeS.tm_mday, &timeS.tm_hour, &timeS.tm_min) != 5)
|
|
return false;
|
|
|
|
i.type = XRIT_ELEKTRO_MSUGS;
|
|
|
|
timeS.tm_year -= 1900;
|
|
timeS.tm_mon -= 1;
|
|
|
|
i.timestamp = timegm(&timeS);
|
|
i.instrument_id = "msu_gs";
|
|
i.instrument_name = "MSU-GS";
|
|
i.satellite_name = "ELEKTRO-L" + std::to_string(sat_num);
|
|
i.satellite_short_name = "L" + std::to_string(sat_num);
|
|
|
|
i.groupid = std::to_string(sat_num) + "_" + std::to_string((time_t)i.timestamp);
|
|
if (parts[4] != "_________")
|
|
i.seg_groupid = parts[5] + "_" + i.groupid;
|
|
|
|
if (file.all_headers.count(msg::SegmentIdentificationHeader::TYPE))
|
|
{
|
|
i.channel = std::to_string(file.getHeader<msg::SegmentIdentificationHeader>().channel_id + 1);
|
|
i.seg_groupid = i.groupid + "_" + i.channel;
|
|
}
|
|
|
|
if (file.all_headers.count(ImageStructureRecord::TYPE))
|
|
i.bit_depth = file.getHeader<ImageStructureRecord>().bit_per_pixel;
|
|
|
|
if (file.all_headers.count(ImageNavigationRecord::TYPE))
|
|
i.image_navigation_record = std::make_shared<ImageNavigationRecord>(file.getHeader<ImageNavigationRecord>());
|
|
|
|
// Adapt navigation if needed (to shift between segments!)
|
|
if (file.all_headers.count(msg::SegmentIdentificationHeader::TYPE) && file.all_headers.count(ImageStructureRecord::TYPE) && i.image_navigation_record)
|
|
{
|
|
auto segment_id_header = file.getHeader<msg::SegmentIdentificationHeader>();
|
|
auto image_structure_record = file.getHeader<ImageStructureRecord>();
|
|
i.image_navigation_record->line_offset =
|
|
i.image_navigation_record->line_offset + (segment_id_header.segment_sequence_number - segment_id_header.planned_start_segment) * image_structure_record.lines_count;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool identifyMSGFile(XRITFileInfo &i, XRITFile &file)
|
|
{
|
|
i.filename = file.filename;
|
|
|
|
auto parts = splitString(i.filename, '-');
|
|
|
|
if (parts.size() != 8)
|
|
return false;
|
|
|
|
if ((parts[0] != "H" && parts[0] != "L") || parts[1] != "000")
|
|
return false;
|
|
|
|
int sat_num;
|
|
if (sscanf(parts[2].c_str(), "MSG%1d", &sat_num) != 1)
|
|
return false;
|
|
|
|
std::tm timeS;
|
|
memset(&timeS, 0, sizeof(std::tm));
|
|
if (sscanf(parts[6].c_str(), "%4d%2d%2d%2d%2d", &timeS.tm_year, &timeS.tm_mon, &timeS.tm_mday, &timeS.tm_hour, &timeS.tm_min) != 5)
|
|
return false;
|
|
|
|
i.type = XRIT_MSG_SEVIRI;
|
|
|
|
timeS.tm_year -= 1900;
|
|
timeS.tm_mon -= 1;
|
|
|
|
i.timestamp = timegm(&timeS);
|
|
i.instrument_id = "seviri";
|
|
i.instrument_name = "SEVIRI";
|
|
i.satellite_name = "MSG-" + std::to_string(sat_num);
|
|
i.satellite_short_name = "MSG" + std::to_string(sat_num);
|
|
|
|
i.groupid = std::to_string(sat_num) + "_" + std::to_string((time_t)i.timestamp);
|
|
|
|
if (file.all_headers.count(msg::SegmentIdentificationHeader::TYPE))
|
|
{
|
|
i.channel = std::to_string(file.getHeader<msg::SegmentIdentificationHeader>().channel_id);
|
|
i.seg_groupid = i.groupid + "_" + i.channel;
|
|
}
|
|
|
|
if (file.all_headers.count(ImageStructureRecord::TYPE))
|
|
i.bit_depth = file.getHeader<ImageStructureRecord>().bit_per_pixel;
|
|
|
|
if (file.all_headers.count(ImageNavigationRecord::TYPE))
|
|
i.image_navigation_record = std::make_shared<ImageNavigationRecord>(file.getHeader<ImageNavigationRecord>());
|
|
|
|
// Adapt navigation if needed (to shift between segments!)
|
|
if (file.all_headers.count(msg::SegmentIdentificationHeader::TYPE) && file.all_headers.count(ImageStructureRecord::TYPE) && i.image_navigation_record)
|
|
{
|
|
auto segment_id_header = file.getHeader<msg::SegmentIdentificationHeader>();
|
|
auto image_structure_record = file.getHeader<ImageStructureRecord>();
|
|
i.image_navigation_record->line_offset =
|
|
i.image_navigation_record->line_offset + (segment_id_header.segment_sequence_number - segment_id_header.planned_start_segment) * image_structure_record.lines_count;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool identifyGOESFile(XRITFileInfo &i, XRITFile &file)
|
|
{
|
|
PrimaryHeader primary_header = file.getHeader<PrimaryHeader>();
|
|
|
|
if (!file.hasHeader<goes::NOAALRITHeader>())
|
|
return false;
|
|
goes::NOAALRITHeader noaa_header = file.getHeader<goes::NOAALRITHeader>();
|
|
|
|
if (primary_header.file_type_code == 0 && file.hasHeader<ImageStructureRecord>())
|
|
{
|
|
ImageStructureRecord image_structure_record = file.getHeader<ImageStructureRecord>();
|
|
i.bit_depth = image_structure_record.bit_per_pixel;
|
|
|
|
if (!file.hasHeader<TimeStampRecord>())
|
|
return false;
|
|
TimeStampRecord timestamp_record = file.getHeader<TimeStampRecord>();
|
|
std::tm *timeReadable = gmtime(×tamp_record.timestamp);
|
|
|
|
// GOES-R Data, from GOES-16 to 19.
|
|
// Once again peeked in goestools for the meso detection, sorry :-)
|
|
if (primary_header.file_type_code == 0 && (noaa_header.product_id == 16 || noaa_header.product_id == 17 || noaa_header.product_id == 18 || noaa_header.product_id == 19))
|
|
{
|
|
i.type = XRIT_GOES_ABI;
|
|
i.instrument_id = "abi";
|
|
i.instrument_name = "ABI";
|
|
i.satellite_name = "GOES-" + std::to_string(noaa_header.product_id);
|
|
i.satellite_short_name = "G" + std::to_string(noaa_header.product_id);
|
|
|
|
std::vector<std::string> cutFilename = satdump::splitString(file.filename, '-');
|
|
if (cutFilename.size() < 4)
|
|
return false;
|
|
|
|
int mode = -1;
|
|
int channel_buf = -1;
|
|
if (sscanf(cutFilename[3].c_str(), "M%dC%02d", &mode, &channel_buf) != 2 && sscanf(cutFilename[3].c_str(), "M%d_", &mode) != 1)
|
|
return false;
|
|
|
|
if (channel_buf == -1)
|
|
{
|
|
i.channel = cutFilename[2];
|
|
if (i.channel.back() == 'F' || i.channel.back() == 'C')
|
|
i.channel.pop_back();
|
|
else if (i.channel.find_last_of('M') != std::string::npos)
|
|
i.channel = i.channel.substr(0, i.channel.find_last_of('M'));
|
|
}
|
|
else
|
|
i.channel = std::to_string(channel_buf);
|
|
|
|
if (file.hasHeader<goes::AncillaryTextRecord>())
|
|
{
|
|
goes::AncillaryTextRecord ancillary_record = file.getHeader<goes::AncillaryTextRecord>();
|
|
|
|
// Parse Region
|
|
if (ancillary_record.meta.count("Region") > 0)
|
|
{
|
|
std::string regionName = ancillary_record.meta["Region"];
|
|
|
|
if (regionName == "Full Disk")
|
|
{
|
|
i.region = "Full Disk";
|
|
}
|
|
else if (regionName == "Mesoscale")
|
|
{
|
|
if (cutFilename[2] == "CMIPM1")
|
|
i.region = "Mesoscale 1";
|
|
else if (cutFilename[2] == "CMIPM2")
|
|
i.region = "Mesoscale 2";
|
|
else
|
|
i.region = "Mesoscale";
|
|
}
|
|
}
|
|
|
|
// Parse scan time
|
|
std::tm scanTimestamp = *timeReadable; // Default to CCSDS timestamp normally...
|
|
if (ancillary_record.meta.count("Time of frame start") > 0) // ...unless we have a proper scan time
|
|
{
|
|
std::string scanTime = ancillary_record.meta["Time of frame start"];
|
|
strptime(scanTime.c_str(), "%Y-%m-%dT%H:%M:%S", &scanTimestamp);
|
|
i.timestamp = timegm(&scanTimestamp);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
logger->error("No ancilliary record!");
|
|
return false;
|
|
}
|
|
|
|
// Group parsing
|
|
if (i.channel == "")
|
|
i.groupid = i.channel + "_" + std::to_string(noaa_header.product_id) + "_" + std::to_string((time_t)i.timestamp);
|
|
else
|
|
i.groupid = std::to_string(noaa_header.product_id) + "_" + std::to_string((time_t)i.timestamp);
|
|
|
|
if (file.all_headers.count(goes::SegmentIdentificationHeader::TYPE))
|
|
i.seg_groupid = i.channel + "_" + std::to_string(noaa_header.product_id) + "_" + std::to_string((time_t)i.timestamp);
|
|
}
|
|
// GOES-N Data, from GOES-13 to 15.
|
|
else if (primary_header.file_type_code == 0 && (noaa_header.product_id == 13 || noaa_header.product_id == 14 || noaa_header.product_id == 15))
|
|
{
|
|
i.type = XRIT_GOESN_IMAGER;
|
|
i.instrument_id = "goesn_imager";
|
|
i.instrument_name = "IMAGER";
|
|
i.satellite_name = "GOES-" + std::to_string(noaa_header.product_id);
|
|
i.satellite_short_name = "G" + std::to_string(noaa_header.product_id);
|
|
// lmeta.is_goesn = true;
|
|
|
|
// Parse channel
|
|
if (noaa_header.product_subid <= 10)
|
|
i.channel = "4";
|
|
else if (noaa_header.product_subid <= 20)
|
|
i.channel = "1";
|
|
else if (noaa_header.product_subid <= 30)
|
|
i.channel = "3";
|
|
|
|
// Parse Region. Had to peak in goestools again...
|
|
if (noaa_header.product_subid % 10 == 1)
|
|
i.region = "Full Disk";
|
|
else if (noaa_header.product_subid % 10 == 2)
|
|
i.region = "Northern Hemisphere";
|
|
else if (noaa_header.product_subid % 10 == 3)
|
|
i.region = "Southern Hemisphere";
|
|
else if (noaa_header.product_subid % 10 == 4)
|
|
i.region = "United States";
|
|
else
|
|
{
|
|
char buf[32];
|
|
size_t len;
|
|
int num = (noaa_header.product_subid % 10) - 5;
|
|
len = snprintf(buf, 32, "Special Interest %d", num);
|
|
i.region = std::string(buf, len);
|
|
}
|
|
|
|
// Parse scan time
|
|
goes::AncillaryTextRecord ancillary_record = file.getHeader<goes::AncillaryTextRecord>();
|
|
std::tm scanTimestamp = *timeReadable; // Default to CCSDS timestamp normally...
|
|
if (ancillary_record.meta.count("Time of frame start") > 0) // ...unless we have a proper scan time
|
|
{
|
|
std::string scanTime = ancillary_record.meta["Time of frame start"];
|
|
strptime(scanTime.c_str(), "%Y-%m-%dT%H:%M:%S", &scanTimestamp);
|
|
i.timestamp = timegm(&scanTimestamp);
|
|
}
|
|
|
|
// Group parsing
|
|
i.groupid = std::to_string(noaa_header.product_id) + "_" + std::to_string((time_t)i.timestamp);
|
|
|
|
if (file.all_headers.count(goes::SegmentIdentificationHeader::TYPE))
|
|
i.seg_groupid = i.channel + "_" + std::to_string(noaa_header.product_id) + "_" + std::to_string((time_t)i.timestamp);
|
|
}
|
|
// Himawari rebroadcast
|
|
else if (primary_header.file_type_code == 0 && noaa_header.product_id == goes::ID_HIMAWARI)
|
|
{
|
|
i.type = XRIT_GOES_ABI;
|
|
i.instrument_id = "ahi";
|
|
i.instrument_name = "AHI";
|
|
i.satellite_name = "Himawari";
|
|
i.satellite_short_name = "HIM";
|
|
i.channel = std::to_string(noaa_header.product_subid);
|
|
|
|
// Translate to real numbers
|
|
if (i.channel == "3")
|
|
i.channel = "13";
|
|
else if (i.channel == "7")
|
|
i.channel = "8";
|
|
else if (i.channel == "1")
|
|
i.channel = "3";
|
|
|
|
// Apparently the timestamp is in there for Himawari-8 data
|
|
AnnotationRecord annotation_record = file.getHeader<AnnotationRecord>();
|
|
|
|
std::vector<std::string> strParts = satdump::splitString(annotation_record.annotation_text, '_');
|
|
if (strParts.size() > 3)
|
|
{
|
|
strptime(strParts[2].c_str(), "%Y%m%d%H%M", timeReadable);
|
|
i.timestamp = timegm(timeReadable);
|
|
}
|
|
|
|
// Group parsing
|
|
i.groupid = "him_" + std::to_string((time_t)i.timestamp);
|
|
|
|
if (file.all_headers.count(goes::SegmentIdentificationHeader::TYPE))
|
|
i.seg_groupid = i.channel + "_him_" + std::to_string((time_t)i.timestamp);
|
|
}
|
|
else
|
|
return false;
|
|
|
|
//////////////////
|
|
|
|
if (file.all_headers.count(ImageNavigationRecord::TYPE))
|
|
{
|
|
i.image_navigation_record = std::make_shared<ImageNavigationRecord>(file.getHeader<ImageNavigationRecord>());
|
|
|
|
bool used_ancillary_proj = false;
|
|
|
|
if (file.hasHeader<goes::AncillaryTextRecord>())
|
|
{
|
|
goes::AncillaryTextRecord ancillary_record = file.getHeader<goes::AncillaryTextRecord>();
|
|
|
|
// On GOES-R HRIT, the projection information in the Image Navigation Header is not accurate enough. Use the data
|
|
// in the Ancillary record instead
|
|
if (ancillary_record.meta.count("perspective_point_height") > 0 && ancillary_record.meta.count("y_add_offset") > 0 && ancillary_record.meta.count("y_scale_factor") > 0)
|
|
{
|
|
used_ancillary_proj = true; // TODOREWORK????
|
|
double scale_factor = std::stod(ancillary_record.meta["y_scale_factor"]);
|
|
i.image_navigation_record->line_scalar = std::abs(std::stod(ancillary_record.meta["perspective_point_height"]) * scale_factor);
|
|
i.image_navigation_record->line_offset = -std::stod(ancillary_record.meta["y_add_offset"]) / scale_factor;
|
|
|
|
// Avoid upstream rounding errors on smaller images
|
|
if (image_structure_record.columns_count < 5424)
|
|
i.image_navigation_record->line_offset = std::ceil(i.image_navigation_record->line_offset);
|
|
}
|
|
if (ancillary_record.meta.count("perspective_point_height") > 0 && ancillary_record.meta.count("x_add_offset") > 0 && ancillary_record.meta.count("x_scale_factor") > 0)
|
|
{
|
|
double scale_factor = std::stod(ancillary_record.meta["x_scale_factor"]);
|
|
i.image_navigation_record->column_scalar = std::abs(std::stod(ancillary_record.meta["perspective_point_height"]) * scale_factor);
|
|
i.image_navigation_record->column_offset = -std::stod(ancillary_record.meta["x_add_offset"]) / scale_factor;
|
|
|
|
// Avoid upstream rounding errors on smaller images
|
|
if (image_structure_record.columns_count < 5424)
|
|
i.image_navigation_record->column_offset = std::ceil(i.image_navigation_record->column_offset);
|
|
}
|
|
}
|
|
|
|
if (file.all_headers.count(goes::SegmentIdentificationHeader::TYPE))
|
|
{
|
|
auto segment_id_header = file.getHeader<goes::SegmentIdentificationHeader>();
|
|
if (i.image_navigation_record && noaa_header.product_id != goes::ID_HIMAWARI && !used_ancillary_proj)
|
|
i.image_navigation_record->line_offset =
|
|
i.image_navigation_record->line_offset + segment_id_header.segment_sequence_number * (segment_id_header.max_row / segment_id_header.max_segment);
|
|
}
|
|
}
|
|
|
|
if (file.hasHeader<ImageDataFunctionRecord>())
|
|
i.image_data_function_record = std::make_shared<ImageDataFunctionRecord>(file.getHeader<ImageDataFunctionRecord>());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool identifyGK2AFile(XRITFileInfo &i, XRITFile &file)
|
|
{
|
|
i.filename = file.filename;
|
|
|
|
auto parts = splitString(i.filename, '_');
|
|
|
|
if (parts.size() != 7)
|
|
return false;
|
|
|
|
// Only FDs are supposed to come down. We only handle other headers in this situation.
|
|
if (parts[0] != "IMG" || parts[1] != "FD")
|
|
return false;
|
|
|
|
std::tm timeS;
|
|
memset(&timeS, 0, sizeof(std::tm));
|
|
if (sscanf((parts[4] + parts[5]).c_str(), "%4d%2d%2d%2d%2d%2d", &timeS.tm_year, &timeS.tm_mon, &timeS.tm_mday, &timeS.tm_hour, &timeS.tm_min, &timeS.tm_sec) != 6)
|
|
return false;
|
|
|
|
i.type = XRIT_GK2A_AMI;
|
|
|
|
timeS.tm_year -= 1900;
|
|
timeS.tm_mon -= 1;
|
|
|
|
i.timestamp = timegm(&timeS);
|
|
i.instrument_id = "ami";
|
|
i.instrument_name = "AMI";
|
|
i.satellite_name = "GK-2A";
|
|
i.satellite_short_name = "GK2A";
|
|
|
|
i.groupid = std::to_string((time_t)i.timestamp);
|
|
|
|
i.channel = parts[3];
|
|
i.seg_groupid = i.channel + "_" + i.groupid;
|
|
|
|
if (file.all_headers.count(ImageStructureRecord::TYPE))
|
|
i.bit_depth = file.getHeader<ImageStructureRecord>().bit_per_pixel;
|
|
|
|
// Try to parse navigation
|
|
if (file.all_headers.count(ImageNavigationRecord::TYPE))
|
|
{
|
|
i.image_navigation_record = std::make_shared<ImageNavigationRecord>(file.getHeader<ImageNavigationRecord>());
|
|
i.image_navigation_record->line_scaling_factor = -i.image_navigation_record->line_scaling_factor; // Little GK-2A Quirk...
|
|
}
|
|
|
|
// Try parse calibration
|
|
if (file.hasHeader<ImageDataFunctionRecord>())
|
|
i.image_data_function_record = std::make_shared<ImageDataFunctionRecord>(file.getHeader<ImageDataFunctionRecord>());
|
|
|
|
return true;
|
|
}
|
|
|
|
bool identifyHimawariFile(XRITFileInfo &i, XRITFile &file)
|
|
{
|
|
i.filename = file.filename;
|
|
|
|
auto parts = splitString(i.filename, '_');
|
|
|
|
if (parts.size() != 4)
|
|
return false;
|
|
|
|
// Only FDs are supposed to come down. We only handle other headers in this situation.
|
|
if (parts[0] != "IMG" || parts[1].find("DK01") == std::string::npos)
|
|
return false;
|
|
|
|
std::tm timeS;
|
|
memset(&timeS, 0, sizeof(std::tm));
|
|
if (sscanf(file.filename.substr(12, 12).c_str(), "%4d%2d%2d%2d%2d", &timeS.tm_year, &timeS.tm_mon, &timeS.tm_mday, &timeS.tm_hour, &timeS.tm_min) != 5)
|
|
return false;
|
|
|
|
i.type = XRIT_HIMAWARI_AHI;
|
|
|
|
timeS.tm_year -= 1900;
|
|
timeS.tm_mon -= 1;
|
|
|
|
i.timestamp = timegm(&timeS);
|
|
i.instrument_id = "ahi";
|
|
i.instrument_name = "AHI";
|
|
i.satellite_name = "Himawari";
|
|
i.satellite_short_name = "HIM";
|
|
|
|
i.groupid = std::to_string((time_t)i.timestamp);
|
|
|
|
std::string channel_name = file.filename.substr(4, 7);
|
|
// Parse channel number
|
|
if (channel_name == "DK01VIS")
|
|
i.channel = "3";
|
|
else if (channel_name == "DK01IR4")
|
|
i.channel = "7";
|
|
else if (channel_name == "DK01IR3")
|
|
i.channel = "8";
|
|
else if (channel_name == "DK01IR1")
|
|
i.channel = "13";
|
|
else if (channel_name == "DK01IR2")
|
|
i.channel = "15";
|
|
else if (channel_name == "DK01B04")
|
|
i.channel = "4";
|
|
else if (channel_name == "DK01B05")
|
|
i.channel = "5";
|
|
else if (channel_name == "DK01B06")
|
|
i.channel = "6";
|
|
else if (channel_name == "DK01B07")
|
|
i.channel = "7_hr";
|
|
else if (channel_name == "DK01B08")
|
|
i.channel = "8_hr";
|
|
else if (channel_name == "DK01B09")
|
|
i.channel = "9";
|
|
else if (channel_name == "DK01B10")
|
|
i.channel = "10";
|
|
else if (channel_name == "DK01B11")
|
|
i.channel = "11";
|
|
else if (channel_name == "DK01B12")
|
|
i.channel = "12";
|
|
else if (channel_name == "DK01B13")
|
|
i.channel = "13_hr";
|
|
else if (channel_name == "DK01B14")
|
|
i.channel = "14";
|
|
else if (channel_name == "DK01B15")
|
|
i.channel = "15";
|
|
else if (channel_name == "DK01B16")
|
|
i.channel = "16";
|
|
else
|
|
logger->info("Unknown AHI channel!");
|
|
|
|
i.seg_groupid = i.channel + "_" + i.groupid;
|
|
|
|
if (file.all_headers.count(ImageStructureRecord::TYPE))
|
|
i.bit_depth = file.getHeader<ImageStructureRecord>().bit_per_pixel;
|
|
|
|
// Try to parse navigation
|
|
if (file.all_headers.count(ImageNavigationRecord::TYPE))
|
|
i.image_navigation_record = std::make_shared<ImageNavigationRecord>(file.getHeader<ImageNavigationRecord>());
|
|
|
|
// Try parse calibration
|
|
if (file.hasHeader<ImageDataFunctionRecord>())
|
|
i.image_data_function_record = std::make_shared<ImageDataFunctionRecord>(file.getHeader<ImageDataFunctionRecord>());
|
|
|
|
return true;
|
|
}
|
|
|
|
bool identifyFY4File(XRITFileInfo &i, XRITFile &file)
|
|
{
|
|
i.filename = file.filename;
|
|
|
|
auto parts = splitString(i.filename, '_');
|
|
|
|
if (parts.size() != 13)
|
|
return false;
|
|
|
|
if (parts[0].find("FY4") == std::string::npos || parts[1] != "AGRI--")
|
|
return false;
|
|
|
|
std::tm timeS;
|
|
memset(&timeS, 0, sizeof(std::tm));
|
|
if (sscanf(parts[9].c_str(), "%4d%2d%2d%2d%2d%2d", &timeS.tm_year, &timeS.tm_mon, &timeS.tm_mday, &timeS.tm_hour, &timeS.tm_min, &timeS.tm_sec) != 6)
|
|
return false;
|
|
|
|
if (parts[0].size() != 5)
|
|
return false;
|
|
|
|
std::string sat_num = parts[0].substr(3, 1);
|
|
|
|
i.type = XRIT_FY4_AGRI;
|
|
|
|
timeS.tm_year -= 1900;
|
|
timeS.tm_mon -= 1;
|
|
|
|
i.timestamp = timegm(&timeS);
|
|
i.instrument_id = "agri";
|
|
i.instrument_name = "AGRI";
|
|
i.satellite_name = "FY-4" + sat_num;
|
|
i.satellite_short_name = "FY4" + sat_num;
|
|
i.region = parts[3];
|
|
|
|
i.groupid = sat_num + "_" + i.region + "_" + std::to_string((time_t)i.timestamp);
|
|
|
|
if (file.all_headers.count(fy4::ImageInformationRecord::TYPE))
|
|
i.channel = std::to_string(file.getHeader<fy4::ImageInformationRecord>().channel_number);
|
|
else
|
|
i.channel = std::to_string(std::stod(parts[7].substr(1, 4)));
|
|
|
|
i.seg_groupid = sat_num + "_" + i.region + "_" + i.channel + "_" + i.groupid;
|
|
|
|
if (file.all_headers.count(fy4::ImageInformationRecord::TYPE))
|
|
i.bit_depth = file.getHeader<fy4::ImageInformationRecord>().bit_per_pixel;
|
|
|
|
// Try to parse navigation
|
|
if (file.all_headers.count(fy4::ImageNavigationRecord::TYPE))
|
|
i.image_navigation_record_fy4 = std::make_shared<fy4::ImageNavigationRecord>(file.getHeader<fy4::ImageNavigationRecord>());
|
|
|
|
// Try parse calibration
|
|
// if (file.hasHeader<ImageDataFunctionRecord>())
|
|
// i.image_data_function_record = std::make_shared<ImageDataFunctionRecord>(file.getHeader<ImageDataFunctionRecord>());
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////
|
|
// Generic functions
|
|
////////////////////////////////////////
|
|
|
|
XRITFileInfo identifyXRITFIle(XRITFile &file)
|
|
{
|
|
XRITFileInfo i;
|
|
|
|
if (file.lrit_data.size() == 0)
|
|
return i;
|
|
|
|
if (identifyElektroFile(i, file))
|
|
return i;
|
|
else if (identifyMSGFile(i, file))
|
|
return i;
|
|
else if (identifyGOESFile(i, file))
|
|
return i;
|
|
else if (identifyGK2AFile(i, file))
|
|
return i;
|
|
else if (identifyHimawariFile(i, file))
|
|
return i;
|
|
else if (identifyFY4File(i, file))
|
|
return i;
|
|
return XRITFileInfo();
|
|
}
|
|
} // namespace xrit
|
|
} // namespace satdump
|