satdump/plugins/gvar_extended/main.cpp

389 lines
No EOL
17 KiB
C++

#include "core/plugin.h"
#include "logger.h"
#include "settings.h"
#include "resources.h"
#include <iostream>
#include <fstream>
#include <vector>
#include <iomanip>
#include "libs/predict/predict.h"
#include "goes/gvar/module_gvar_image_decoder.h"
//#include "common/tracking/tle.h"
//#include "common/geodetic/projection/geo_projection.h"
//#include "common/map/map_drawer.h"
#define FIXED_FLOAT(x) std::fixed << std::setprecision(3) << (x)
// geodetic::projection::GEOSProjection proj_geos;
class GVARExtended : public satdump::Plugin
{
private:
static std::string misc_preview_text;
static std::vector<std::array<float, 3>> points;
static std::vector<std::string> names;
static void satdumpStartedHandler(const satdump::SatDumpStartedEvent &)
{
/*
if (global_cfg.contains("gvar_extended"))
{
if (global_cfg["gvar_extended"].contains("preview_misc_text"))
misc_preview_text = global_cfg["gvar_extended"]["preview_misc_text"].get<std::string>();
else
misc_preview_text = "SatDump | GVAR";
if (global_cfg["gvar_extended"].contains("temperature_points"))
{
points.clear();
for (int i = 0; i < (int)global_cfg["gvar_extended"]["temperature_points"].size(); i++)
{
points.push_back({global_cfg["gvar_extended"]["temperature_points"][i]["lat"].get<float>(),
global_cfg["gvar_extended"]["temperature_points"][i]["lon"].get<float>(),
global_cfg["gvar_extended"]["temperature_points"][i]["radius"].get<float>()});
names.push_back(global_cfg["gvar_extended"]["temperature_points"][i]["name"].get<std::string>());
}
}
}*/
}
static void gvarSaveChannelImagesHandler(const goes::gvar::events::GVARSaveChannelImagesEvent &evt)
{
logger->info("Preview...");
image::Image<uint8_t> preview(1300, 948, 1);
image::Image<uint8_t> previewImage;
// Preview generation
{
image::Image<uint8_t> channel1_8bit(1040, 948, 1);
image::Image<uint8_t> channel2_8bit(260, 237, 1);
image::Image<uint8_t> channel3_8bit(260, 237, 1);
image::Image<uint8_t> channel4_8bit(260, 237, 1);
image::Image<uint8_t> channel5_8bit(260, 237, 1);
image::Image<uint16_t> resized5 = evt.images.image5;
resized5.resize(1040, 948);
for (int i = 0; i < (int)channel1_8bit.size(); i++)
{
channel1_8bit[i] = resized5[i] / 256;
}
image::Image<uint16_t> buff2 = evt.images.image1,
buff3 = evt.images.image2,
buff4 = evt.images.image3,
buff5 = evt.images.image4;
buff2.resize(260, 237);
buff3.resize(260, 237);
buff4.resize(260, 237);
buff5.resize(260, 237);
for (int i = 0; i < (int)channel2_8bit.size(); i++)
{
channel2_8bit[i] = buff2[i] / 256;
channel3_8bit[i] = buff3[i] / 256;
channel4_8bit[i] = buff4[i] / 256;
channel5_8bit[i] = buff5[i] / 256;
}
channel1_8bit.simple_despeckle();
channel2_8bit.simple_despeckle();
channel3_8bit.simple_despeckle();
channel4_8bit.simple_despeckle();
channel5_8bit.simple_despeckle();
preview.draw_image(0, channel1_8bit, 0, 0);
preview.draw_image(0, channel2_8bit, 1040, 0);
preview.draw_image(0, channel3_8bit, 1040, 237);
preview.draw_image(0, channel4_8bit, 1040, 474);
preview.draw_image(0, channel5_8bit, 1040, 711);
}
// Overlay the preview
{
std::string sat_name = evt.images.sat_number == 13 ? "EWS-G1 / GOES-13" : ("GOES-" + std::to_string(evt.images.sat_number));
std::string date_time = (evt.timeReadable->tm_mday > 9 ? std::to_string(evt.timeReadable->tm_mday) : "0" + std::to_string(evt.timeReadable->tm_mday)) + '/' +
(evt.timeReadable->tm_mon + 1 > 9 ? std::to_string(evt.timeReadable->tm_mon + 1) : "0" + std::to_string(evt.timeReadable->tm_mon + 1)) + '/' +
std::to_string(evt.timeReadable->tm_year + 1900) + ' ' +
(evt.timeReadable->tm_hour > 9 ? std::to_string(evt.timeReadable->tm_hour) : "0" + std::to_string(evt.timeReadable->tm_hour)) + ':' +
(evt.timeReadable->tm_min > 9 ? std::to_string(evt.timeReadable->tm_min) : "0" + std::to_string(evt.timeReadable->tm_min)) + " UTC";
int offsetX, offsetY, bar_height;
// set ratios for calculating bar size
float bar_ratio = 0.02;
bar_height = preview.width() * bar_ratio;
offsetX = 5; // preview.width() * offsetXratio;
offsetY = 1; // preview.width() * offsetYratio;
unsigned char color = 255;
image::Image<uint8_t> imgtext = image::generate_text_image(sat_name.c_str(), &color, bar_height, offsetX, offsetY);
image::Image<uint8_t> imgtext1 = image::generate_text_image(date_time.c_str(), &color, bar_height, offsetX, offsetY);
image::Image<uint8_t> imgtext2 = image::generate_text_image(misc_preview_text.c_str(), &color, bar_height, offsetX, offsetY);
previewImage = image::Image<uint8_t>(preview.width(), preview.height() + 2 * bar_height, 1);
previewImage.fill(0);
previewImage.draw_image(0, imgtext, 0, 0);
previewImage.draw_image(0, imgtext1, previewImage.width() - imgtext1.width(), 0);
previewImage.draw_image(0, imgtext2, 0, bar_height + preview.height());
previewImage.draw_image(0, preview, 0, bar_height);
}
previewImage.save_img(std::string(evt.directory + "/preview").c_str());
/*
// calibrated temperature measurement based on NOAA LUTs (https://www.ospo.noaa.gov/Operations/GOES/calibration/gvar-conversion.html)
if (evt.images.image1.width() == 5206 || evt.images.image1.width() == 5209)
{
std::string filename = "goes/gvar/goes" + std::to_string(evt.images.sat_number) + "_gvar_lut.txt";
if (resources::resourceExists(filename) && points.size() > 0)
{
std::ifstream input(resources::getResourcePath(filename).c_str());
std::array<std::array<float, 1024>, 4> LUTs = readLUTValues(input);
input.close();
std::ofstream output(evt.directory + "/temperatures.txt");
logger->info("Temperatures... temperatures.txt");
image::Image<uint16_t> im1 = cropIR(evt.images.image1);
image::Image<uint16_t> im2 = cropIR(evt.images.image2);
image::Image<uint16_t> im3 = cropIR(evt.images.image3);
image::Image<uint16_t> im4 = cropIR(evt.images.image4);
std::array<image::Image<uint16_t>, 4> channels = {im1, im2, im3, im4};
geodetic::projection::GEOProjector proj(61.5, 35782.466981, 18990, 18956, 1.1737, 1.1753, 0, -40, 1);
for (int j = 0; j < (int)points.size(); j++)
{
int x, y;
proj.forward(points[j][1], points[j][0], x, y);
x /= 4;
y /= 4;
output << "Temperature measurements for point [" + std::to_string(x) + ", " + std::to_string(y) + "] (" + names[j] + ") with r = " + std::to_string(points[j][2]) << '\n'
<< '\n';
logger->info("Temperature measurements for point [" + std::to_string(x) + ", " + std::to_string(y) + "] (" + names[j] + ") with r = " + std::to_string(points[j][2]));
for (int i = 0; i < 4; i++)
{
output << " Channel " + std::to_string(i + 2) + ": " << FIXED_FLOAT(LUTs[i][getAVG(channels[i], x, y, points[j][2]) >> 6])
<< " K (";
if (LUTs[i][getAVG(channels[i], x, y, points[j][2]) >> 6] - 273.15 < 10)
{
if (LUTs[i][getAVG(channels[i], x, y, points[j][2]) >> 6] - 273.15 <= -10)
{
output << FIXED_FLOAT(LUTs[i][getAVG(channels[i], x, y, points[j][2]) >> 6] - 273.15);
}
else if (LUTs[i][getAVG(channels[i], x, y, points[j][2]) >> 6] - 273.15 > 0)
{
output << " " << FIXED_FLOAT(LUTs[i][getAVG(channels[i], x, y, points[j][2]) >> 6] - 273.15);
}
else
{
output << " " << FIXED_FLOAT(LUTs[i][getAVG(channels[i], x, y, points[j][2]) >> 6] - 273.15);
}
}
else
{
output << " " << FIXED_FLOAT(LUTs[i][getAVG(channels[i], x, y, points[j][2]) >> 6] - 273.15);
}
output << " °C)";
output << '\n';
logger->info("channel " + std::to_string(i + 2) + ": " +
std::to_string(LUTs[i][getAVG(channels[i], x, y, points[j][2]) >> 6]) +
" K (" + std::to_string(LUTs[i][getAVG(channels[i], x, y, points[j][2]) >> 6] - 273.15) + " °C)");
}
output << '\n'
<< '\n';
}
output.close();
}
else
{
logger->warn("goes/gvar/goes" + std::to_string(evt.images.sat_number) + "_gvar_lut.txt LUT is missing! Temperature measurement will not be performed.");
}
}
else
{
logger->info("Image is not a FD, temperature measurement will not be performed.");
}
logger->info("Generating mapped crops..");
// mapped crops of europe. IR and VIS
image::Image<uint16_t> mapProj = cropIR(evt.images.image3);
drawMapOverlay(evt.images.sat_number, evt.timeUTC, mapProj);
mapProj.crop(500, 50, 500 + 1560, 50 + 890);
logger->info("Europe IR crop..");
mapProj.to8bits().save_img(std::string(evt.directory + "/europe_IR").c_str());
mapProj = cropVIS(evt.images.image5);
drawMapOverlay(evt.images.sat_number, evt.timeUTC, mapProj);
mapProj.crop(1348, 240, 1348 + 5928, 240 + 4120);
logger->info("Europe VIS crop.. europe_VIS");
mapProj.to8bits().save_img(std::string(evt.directory + "/europe_VIS").c_str());
mapProj.clear();
*/
}
static void gvarSaveFalceColorHandler(const goes::gvar::events::GVARSaveFCImageEvent &evt)
{
if (evt.sat_number == 13)
{
logger->info("Europe crop...");
image::Image<uint8_t> crop = evt.false_color_image;
if (crop.width() == 20836)
crop.crop(3198, 240, 3198 + 5928, 240 + 4120);
else
crop.crop(1348, 240, 1348 + 5928, 240 + 4120);
crop.save_img(std::string(evt.directory + "/europe").c_str());
}
}
static std::array<std::array<float, 1024>, 4> readLUTValues(std::ifstream &LUT)
{
std::array<std::array<float, 1024>, 4> values;
std::string tmp;
// skip first 7 lines
for (int i = 0; i < 7; i++)
{
std::getline(LUT, tmp);
}
// read LUTs
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 1024; j++)
{
std::getline(LUT, tmp);
values[i][j] = std::stof(tmp.substr(39, 7));
}
if (i != 3)
{
// skip det2 for first 3 channels (no det2 for ch6)
for (int j = 0; j < 1030; j++)
{
std::getline(LUT, tmp);
}
}
}
return values;
}
static unsigned short getAVG(image::Image<uint16_t> &image, int x, int y, int r)
{
uint64_t sum = 0;
for (int i = 0; i < std::pow(r * 2 + 1, 2); i++)
{
sum += image[(y - r + i / (2 * r + 1)) * image.width() + (x - r + i % (2 * r + 1))];
}
return sum / std::pow(r * 2 + 1, 2);
}
static image::Image<uint16_t> cropIR(image::Image<uint16_t> input)
{
image::Image<uint16_t> output(4749, input.height(), 1);
if (input.width() == 5206)
{
output.draw_image(0, input, 0, 0);
}
else if (input.width() == 5209)
{
output.draw_image(0, input, -463, 0);
}
else
{
logger->warn("Wrong IR image size (" + std::to_string(input.width()) + "), it will not be cropped");
}
return output;
}
static image::Image<uint16_t> cropVIS(image::Image<uint16_t> input)
{
image::Image<uint16_t> output(18990, input.height(), 1);
if (input.width() == 20824)
{
output.draw_image(0, input, 0, 0);
}
else if (input.width() == 20836)
{
output.draw_image(0, input, -1852, 0);
}
else
{
logger->warn("Wrong VIS image size (" + std::to_string(input.width()) + "), it will not be cropped");
}
return output;
}
static int getNORADFromSatNumber(int number)
{
if (number == 13)
return 29155;
else if (number == 14)
return 35491;
else if (number == 15)
return 36411;
else
return 0;
};
static predict_position getSatellitePosition(int number, time_t time)
{
// tle::TLE goes_tle = tle::getTLEfromNORAD(getNORADFromSatNumber(number));
// predict_orbital_elements_t *goes_object = predict_parse_tle(goes_tle.line1.c_str(), goes_tle.line2.c_str());
predict_position goes_position;
// predict_orbit(goes_object, &goes_position, predict_to_julian(time));
// predict_destroy_orbital_elements(goes_object);
return goes_position;
}
// Expect cropped IR
static void drawMapOverlay(int number, time_t time, image::Image<uint16_t> &image)
{
/*geodetic::projection::GEOProjector proj(61.5, 35782.466981, 18990, 18956, 1.1737, 1.1753, 0, -40, 1);
unsigned short color[3] = {65535, 65535, 65535};
map::drawProjectedMapShapefile({resources::getResourcePath("maps/ne_10m_admin_0_countries.shp")}, image, color, [&proj, &image](float lat, float lon, int map_height, int map_width) -> std::pair<int, int>
{
int image_x, image_y;
proj.forward(lon, lat, image_x, image_y);
if (image.width() == 18990)
{
return {image_x, image_y};
}
else
{
if (image_x == -1 && image_y == -1)
{
return {-1, -1};
}
else
{
return {image_x / 4, image_y / 4};
}
} });*/
}
public:
std::string getID()
{
return "gvar_extended";
}
void init()
{
satdump::eventBus->register_handler<satdump::SatDumpStartedEvent>(satdumpStartedHandler);
satdump::eventBus->register_handler<goes::gvar::events::GVARSaveChannelImagesEvent>(gvarSaveChannelImagesHandler);
satdump::eventBus->register_handler<goes::gvar::events::GVARSaveFCImageEvent>(gvarSaveFalceColorHandler);
}
};
std::string GVARExtended::misc_preview_text = "SatDump | GVAR";
std::vector<std::array<float, 3>> GVARExtended::points = {{}};
std::vector<std::string> GVARExtended::names = {};
PLUGIN_LOADER(GVARExtended)