@@ -11,7 +11,7 @@ find_package(PNG) | |||||
# libsndfile | # libsndfile | ||||
find_package(LibSndFile) | find_package(LibSndFile) | ||||
set(LIB_C_SOURCE_FILES src/color.c src/dsp.c src/filter.c src/image.c src/algebra.c src/libs/median.c src/util.c) | |||||
set(LIB_C_SOURCE_FILES src/color.c src/dsp.c src/filter.c src/image.c src/algebra.c src/libs/median.c src/util.c src/calibration.c) | |||||
set(EXE_C_SOURCE_FILES src/main.c src/pngio.c src/argparse/argparse.c src/util.c) | set(EXE_C_SOURCE_FILES src/main.c src/pngio.c src/argparse/argparse.c src/util.c) | ||||
set(LIB_C_HEADER_FILES src/apt.h) | set(LIB_C_HEADER_FILES src/apt.h) | ||||
@@ -33,6 +33,6 @@ typedef struct { | |||||
linear_t linear_regression(const float *independent, const float *dependent, size_t len); | linear_t linear_regression(const float *independent, const float *dependent, size_t len); | ||||
float linear_calc(float x, linear_t line); | float linear_calc(float x, linear_t line); | ||||
float quadratic_calc(float x, quadratic_t line); | |||||
float quadratic_calc(float x, quadratic_t quadratic); | |||||
#endif | #endif |
@@ -100,7 +100,10 @@ apt_channel_t APT_API apt_calibrate(float **prow, int nrow, int offset, int widt | |||||
void APT_API apt_denoise(float **prow, int nrow, int offset, int width); | void APT_API apt_denoise(float **prow, int nrow, int offset, int width); | ||||
void APT_API apt_flipImage(apt_image_t *img, int width, int offset); | void APT_API apt_flipImage(apt_image_t *img, int width, int offset); | ||||
int APT_API apt_cropNoise(apt_image_t *img); | int APT_API apt_cropNoise(apt_image_t *img); | ||||
void APT_API apt_temperature(int satnum, apt_image_t *img, int offset, int width); | |||||
void APT_API apt_calibrate_thermal(int satnum, apt_image_t *img, int offset, int width); | |||||
void APT_API apt_calibrate_visible(int satnum, apt_image_t *img, int offset, int width); | |||||
// Moved to apt_calibrate_thermal | |||||
#define apt_temperature apt_calibrate_thermal | |||||
apt_rgb_t APT_API apt_applyPalette(char *palette, int val); | apt_rgb_t APT_API apt_applyPalette(char *palette, int val); | ||||
apt_rgb_t APT_API apt_RGBcomposite(apt_rgb_t top, float top_a, apt_rgb_t bottom, float bottom_a); | apt_rgb_t APT_API apt_RGBcomposite(apt_rgb_t top, float top_a, apt_rgb_t bottom, float bottom_a); | ||||
@@ -0,0 +1,120 @@ | |||||
/* | |||||
* aptdec - A lightweight FOSS (NOAA) APT decoder | |||||
* Copyright (C) 2019-2022 Xerbo (xerbo@protonmail.com) | |||||
* | |||||
* This program is free software; you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation; either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | |||||
*/ | |||||
#include "calibration.h" | |||||
#include "util.h" | |||||
const calibration_t calibration[3] = { | |||||
{ | |||||
.name = "NOAA-15", | |||||
.prt = { | |||||
{ 1.36328e-06f, 0.051045f, 276.60157f }, // PRT 1 | |||||
{ 1.47266e-06f, 0.050909f, 276.62531f }, // PRT 2 | |||||
{ 1.47656e-06f, 0.050907f, 276.67413f }, // PRT 3 | |||||
{ 1.47656e-06f, 0.050966f, 276.59258f } // PRT 4 | |||||
}, | |||||
.visible = { | |||||
{ | |||||
.low = { 0.0568f, -2.1874f }, | |||||
.high = { 0.1633f, -54.9928f }, | |||||
.cutoff = 496.0f | |||||
}, { | |||||
.low = { 0.0596f, -2.4096f }, | |||||
.high = { 0.1629f, -55.2436f }, | |||||
.cutoff = 511.0f | |||||
} | |||||
}, | |||||
.rad = { | |||||
{ 925.4075f, 0.337810f, 0.998719f }, // Channel 4 | |||||
{ 839.8979f, 0.304558f, 0.999024f }, // Channel 5 | |||||
{ 2695.9743f, 1.621256f, 0.998015f } // Channel 3B | |||||
}, | |||||
.cor = { | |||||
{ -4.50f, { 0.0004524f, -0.0932f, 4.76f } }, // Channel 4 | |||||
{ -3.61f, { 0.0002811f, -0.0659f, 3.83f } }, // Channel 5 | |||||
{ 0.0f, { 0.0f, 0.0f , 0.0f } } // Channel 3B | |||||
} | |||||
}, { | |||||
.name = "NOAA-18", | |||||
.prt = { | |||||
{ 1.657e-06f, 0.05090f, 276.601f }, // PRT 1 | |||||
{ 1.482e-06f, 0.05101f, 276.683f }, // PRT 2 | |||||
{ 1.313e-06f, 0.05117f, 276.565f }, // PRT 3 | |||||
{ 1.484e-06f, 0.05103f, 276.615f } // PRT 4 | |||||
}, | |||||
.visible = { | |||||
{ | |||||
.low = { 0.06174f, -2.434f }, | |||||
.high = { 0.1841f, -63.31f }, | |||||
.cutoff = 501.54f | |||||
}, { | |||||
.low = { 0.07514f, -2.960f }, | |||||
.high = { 0.2254f, -78.55f }, | |||||
.cutoff = 500.40f | |||||
} | |||||
}, | |||||
.rad = { | |||||
{ 928.1460f, 0.436645f, 0.998607f }, // Channel 4 | |||||
{ 833.2532f, 0.253179f, 0.999057f }, // Channel 5 | |||||
{ 2659.7952f, 1.698704f, 0.996960f } // Channel 3B | |||||
}, | |||||
.cor = { | |||||
{ -5.53f, { 0.00052337f, -0.11069f, 5.82f } }, // Channel 4 | |||||
{ -2.22f, { 0.00017715f, -0.04360f, 2.67f } }, // Channel 5 | |||||
{ 0.0f, { 0.0f, 0.0f, 0.0f } } // Channel 3B | |||||
} | |||||
}, { | |||||
.name = "NOAA-19", | |||||
.prt = { | |||||
{ 1.405783e-06f, 0.051111f, 276.6067f }, // PRT 1 | |||||
{ 1.496037e-06f, 0.051090f, 276.6119f }, // PRT 2 | |||||
{ 1.496990e-06f, 0.051033f, 276.6311f }, // PRT 3 | |||||
{ 1.493110e-06f, 0.051058f, 276.6268f } // PRT 4 | |||||
}, | |||||
.visible = { | |||||
{ | |||||
.low = { 0.05555f, -2.159f }, | |||||
.high = { 0.1639f, -56.33f }, | |||||
.cutoff = 496.43f | |||||
}, { | |||||
.low = { 0.06614f, -2.565f }, | |||||
.high = { 0.1970f, -68.01f }, | |||||
.cutoff = 500.37f | |||||
} | |||||
}, | |||||
.rad = { | |||||
{ 928.9f, 0.53959f, 0.998534f }, // Channel 4 | |||||
{ 831.9f, 0.36064f, 0.998913f }, // Channel 5 | |||||
{ 2670.0f, 1.67396f, 0.997364f } // Channel 3B | |||||
}, | |||||
.cor = { | |||||
{ -5.49f, { 0.00054668f, -0.11187f, 5.70f } }, // Channel 4 | |||||
{ -3.39f, { 0.00024985f, -0.05991f, 3.58f } }, // Channel 5 | |||||
{ 0.0f, { 0.0f, 0.0f, 0.0f } } // Channel 3B | |||||
} | |||||
} | |||||
}; | |||||
calibration_t get_calibration(int satid) { | |||||
switch (satid) { | |||||
case 15: return calibration[0]; | |||||
case 18: return calibration[1]; | |||||
case 19: return calibration[2]; | |||||
default: error("Invalid satid in get_calibration()"); /* following is only to shut up the compiler */ return calibration[0]; | |||||
} | |||||
} |
@@ -0,0 +1,55 @@ | |||||
/* | |||||
* aptdec - A lightweight FOSS (NOAA) APT decoder | |||||
* Copyright (C) 2019-2022 Xerbo (xerbo@protonmail.com) | |||||
* | |||||
* This program is free software; you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation; either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | |||||
*/ | |||||
#ifndef APTDEC_CALIBRATION_H | |||||
#define APTDEC_CALIBRATION_H | |||||
#include "algebra.h" | |||||
typedef struct { | |||||
char *name; | |||||
// Quadratics for calculating PRT temperature | |||||
quadratic_t prt[4]; | |||||
// Visible calibration coefficients | |||||
struct { | |||||
linear_t low; | |||||
linear_t high; | |||||
float cutoff; | |||||
} visible[2]; | |||||
// Radiance coefficients | |||||
struct { | |||||
float vc, A, B; | |||||
} rad[3]; | |||||
// Non linear correction coefficients | |||||
struct { | |||||
float Ns; | |||||
quadratic_t quadratic; | |||||
} cor[3]; | |||||
} calibration_t; | |||||
// First radiation constant (mW/(m2-sr-cm-4)) | |||||
static const float C1 = 1.1910427e-5f; | |||||
// Second radiation constant (cm-K) | |||||
static const float C2 = 1.4387752f; | |||||
calibration_t get_calibration(int satid); | |||||
#endif |
@@ -47,7 +47,8 @@ enum imagetypes { | |||||
Temperature='t', | Temperature='t', | ||||
Channel_A='a', | Channel_A='a', | ||||
Channel_B='b', | Channel_B='b', | ||||
Distribution='d' | |||||
Distribution='d', | |||||
Visible='v' | |||||
}; | }; | ||||
enum effects { | enum effects { | ||||
Crop_Telemetry='t', | Crop_Telemetry='t', | ||||
@@ -310,8 +310,8 @@ int apt_cropNoise(apt_image_t *img){ | |||||
return startCrop; | return startCrop; | ||||
} | } | ||||
// --- Temperature Calibration --- // | |||||
#include "satcal.h" | |||||
// --- Visible and Temperature Calibration --- // | |||||
#include "calibration.h" | |||||
typedef struct { | typedef struct { | ||||
float Nbb; | float Nbb; | ||||
@@ -321,71 +321,78 @@ typedef struct { | |||||
} tempparam_t; | } tempparam_t; | ||||
// IR channel temperature compensation | // IR channel temperature compensation | ||||
static void tempcomp(float t[16], int ch, int satnum, tempparam_t *tpr) { | |||||
float Tbb, T[4]; | |||||
float C; | |||||
tpr->ch = ch - 4; | |||||
// Compute equivalent T blackbody temperature | |||||
for (int n = 0; n < 4; n++) { | |||||
float d0, d1, d2; | |||||
C = t[9 + n] * 4.0; | |||||
d0 = satcal[satnum].d[n][0]; | |||||
d1 = satcal[satnum].d[n][1]; | |||||
d2 = satcal[satnum].d[n][2]; | |||||
T[n] = d0; | |||||
T[n] += d1 * C; | |||||
C *= C; | |||||
T[n] += d2 * C; | |||||
tempparam_t tempcomp(float t[16], int ch, int satnum) { | |||||
tempparam_t tpr; | |||||
tpr.ch = ch - 4; | |||||
const calibration_t calibration = get_calibration(satnum); | |||||
const float Vc = calibration.rad[tpr.ch].vc; | |||||
const float A = calibration.rad[tpr.ch].A; | |||||
const float B = calibration.rad[tpr.ch].B; | |||||
// Compute PRT temperature | |||||
float T[4]; | |||||
for (size_t n = 0; n < 4; n++) { | |||||
T[n] = quadratic_calc(t[n+9] * 4.0, calibration.prt[n]); | |||||
} | } | ||||
Tbb = (T[0] + T[1] + T[2] + T[3]) / 4.0; | |||||
Tbb = satcal[satnum].rad[tpr->ch].A + satcal[satnum].rad[tpr->ch].B * Tbb; | |||||
// Compute blackbody radiance temperature | |||||
C = satcal[satnum].rad[tpr->ch].vc; | |||||
tpr->Nbb = c1 * C * C * C / (expm1(c2 * C / Tbb)); | |||||
float Tbb = (T[0]+T[1]+T[2]+T[3]) / 4.0; // Blackbody temperature | |||||
float Tbbstar = A + Tbb*B; // Effective blackbody temperature | |||||
// Store blackbody count and space | |||||
tpr->Cs = Cs * 4.0; | |||||
tpr->Cb = t[14] * 4.0; | |||||
tpr.Nbb = C1 * pow(Vc, 3) / (expf(C2 * Vc / Tbbstar) - 1.0f); // Blackbody radiance | |||||
tpr.Cs = 246.4 * 4.0; // FIXME | |||||
tpr.Cb = t[14] * 4.0; | |||||
return tpr; | |||||
} | } | ||||
// IR channel temperature calibration | // IR channel temperature calibration | ||||
static float tempcal(float Ce, int satnum, tempparam_t * rgpr) { | |||||
float Nl, Nc, Ns, Ne; | |||||
float T, vc; | |||||
Ns = satcal[satnum].cor[rgpr->ch].Ns; | |||||
Nl = Ns + (rgpr->Nbb - Ns) * (rgpr->Cs - Ce * 4.0) / (rgpr->Cs - rgpr->Cb); | |||||
Nc = satcal[satnum].cor[rgpr->ch].b[0] + | |||||
satcal[satnum].cor[rgpr->ch].b[1] * Nl + | |||||
satcal[satnum].cor[rgpr->ch].b[2] * Nl * Nl; | |||||
static float tempcal(float Ce, int satnum, tempparam_t tpr) { | |||||
const calibration_t calibration = get_calibration(satnum); | |||||
const float Ns = calibration.cor[tpr.ch].Ns; | |||||
const float Vc = calibration.rad[tpr.ch].vc; | |||||
const float A = calibration.rad[tpr.ch].A; | |||||
const float B = calibration.rad[tpr.ch].B; | |||||
Ne = Nl + Nc; | |||||
float Nl = Ns + (tpr.Nbb - Ns) * (tpr.Cs - Ce * 4.0) / (tpr.Cs - tpr.Cb); // Linear radiance estimate | |||||
float Nc = quadratic_calc(Nl, calibration.cor[tpr.ch].quadratic); // Non-linear correction | |||||
float Ne = Nl + Nc; // Corrected radiance | |||||
vc = satcal[satnum].rad[rgpr->ch].vc; | |||||
T = c2 * vc / log(c1 * vc * vc * vc / Ne + 1.0); | |||||
T = (T - satcal[satnum].rad[rgpr->ch].A) / satcal[satnum].rad[rgpr->ch].B; | |||||
float Testar = C2 * Vc / logf(C1 * powf(Vc, 3) / Ne + 1.0); // Equivlent black body temperature | |||||
float Te = (Testar - A) / B; // Temperature (kelvin) | |||||
// Convert to celsius | // Convert to celsius | ||||
T -= 273.15; | |||||
Te -= 273.15; | |||||
// Rescale to 0-255 for -100°C to +60°C | // Rescale to 0-255 for -100°C to +60°C | ||||
T = (T + 100.0) / 160.0 * 255.0; | |||||
return T; | |||||
return (Te + 100.0) / 160.0 * 255.0; | |||||
} | } | ||||
// Temperature calibration wrapper | // Temperature calibration wrapper | ||||
void apt_temperature(int satnum, apt_image_t *img, int offset, int width){ | |||||
tempparam_t temp; | |||||
void apt_calibrate_thermal(int satnum, apt_image_t *img, int offset, int width) { | |||||
tempparam_t temp = tempcomp(tele, img->chB, satnum); | |||||
for (int y = 0; y < img->nrow; y++) { | |||||
for (int x = 0; x < width; x++) { | |||||
img->prow[y][x + offset] = (float)tempcal(img->prow[y][x + offset], satnum, temp); | |||||
} | |||||
} | |||||
} | |||||
float calibrate_pixel(float value, int channel, calibration_t cal) { | |||||
if (value > cal.visible[channel].cutoff) { | |||||
return linear_calc(value*4.0f, cal.visible[channel].high) * 255.0f/100.0f; | |||||
} else { | |||||
return linear_calc(value*4.0f, cal.visible[channel].low) * 255.0f/100.0f; | |||||
} | |||||
} | |||||
tempcomp(tele, img->chB, satnum - 15, &temp); | |||||
void apt_calibrate_visible(int satnum, apt_image_t *img, int offset, int width) { | |||||
const calibration_t calibration = get_calibration(satnum); | |||||
int channel = img->chA-1; | |||||
for (int y = 0; y < img->nrow; y++) { | for (int y = 0; y < img->nrow; y++) { | ||||
for (int x = 0; x < width; x++) { | for (int x = 0; x < width; x++) { | ||||
img->prow[y][x + offset] = (float)tempcal(img->prow[y][x + offset], satnum - 15, &temp); | |||||
img->prow[y][x + offset] = clamp(calibrate_pixel(img->prow[y][x + offset], channel, calibration), 255.0f, 0.0f); | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -1,384 +0,0 @@ | |||||
/** | |||||
* Copyright (C) 2012-2015 Yecheng Fu <cofyc.jackson at gmail dot com> | |||||
* All rights reserved. | |||||
* | |||||
* Use of this source code is governed by a MIT-style license that can be found | |||||
* in the LICENSE file. | |||||
*/ | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
#include <assert.h> | |||||
#include <errno.h> | |||||
#include "argparse.h" | |||||
#define OPT_UNSET 1 | |||||
#define OPT_LONG (1 << 1) | |||||
static const char * | |||||
prefix_skip(const char *str, const char *prefix) | |||||
{ | |||||
size_t len = strlen(prefix); | |||||
return strncmp(str, prefix, len) ? NULL : str + len; | |||||
} | |||||
static int | |||||
prefix_cmp(const char *str, const char *prefix) | |||||
{ | |||||
for (;; str++, prefix++) | |||||
if (!*prefix) { | |||||
return 0; | |||||
} else if (*str != *prefix) { | |||||
return (unsigned char)*prefix - (unsigned char)*str; | |||||
} | |||||
} | |||||
static void | |||||
argparse_error(struct argparse *self, const struct argparse_option *opt, | |||||
const char *reason, int flags) | |||||
{ | |||||
(void)self; | |||||
if (flags & OPT_LONG) { | |||||
fprintf(stderr, "error: option `--%s` %s\n", opt->long_name, reason); | |||||
} else { | |||||
fprintf(stderr, "error: option `-%c` %s\n", opt->short_name, reason); | |||||
} | |||||
exit(1); | |||||
} | |||||
static int | |||||
argparse_getvalue(struct argparse *self, const struct argparse_option *opt, | |||||
int flags) | |||||
{ | |||||
const char *s = NULL; | |||||
if (!opt->value) | |||||
goto skipped; | |||||
switch (opt->type) { | |||||
case ARGPARSE_OPT_BOOLEAN: | |||||
if (flags & OPT_UNSET) { | |||||
*(int *)opt->value = *(int *)opt->value - 1; | |||||
} else { | |||||
*(int *)opt->value = *(int *)opt->value + 1; | |||||
} | |||||
if (*(int *)opt->value < 0) { | |||||
*(int *)opt->value = 0; | |||||
} | |||||
break; | |||||
case ARGPARSE_OPT_BIT: | |||||
if (flags & OPT_UNSET) { | |||||
*(int *)opt->value &= ~opt->data; | |||||
} else { | |||||
*(int *)opt->value |= opt->data; | |||||
} | |||||
break; | |||||
case ARGPARSE_OPT_STRING: | |||||
if (self->optvalue) { | |||||
*(const char **)opt->value = self->optvalue; | |||||
self->optvalue = NULL; | |||||
} else if (self->argc > 1) { | |||||
self->argc--; | |||||
*(const char **)opt->value = *++self->argv; | |||||
} else { | |||||
argparse_error(self, opt, "requires a value", flags); | |||||
} | |||||
break; | |||||
case ARGPARSE_OPT_INTEGER: | |||||
errno = 0; | |||||
if (self->optvalue) { | |||||
*(int *)opt->value = strtol(self->optvalue, (char **)&s, 0); | |||||
self->optvalue = NULL; | |||||
} else if (self->argc > 1) { | |||||
self->argc--; | |||||
*(int *)opt->value = strtol(*++self->argv, (char **)&s, 0); | |||||
} else { | |||||
argparse_error(self, opt, "requires a value", flags); | |||||
} | |||||
if (errno) | |||||
argparse_error(self, opt, strerror(errno), flags); | |||||
if (s[0] != '\0') | |||||
argparse_error(self, opt, "expects an integer value", flags); | |||||
break; | |||||
case ARGPARSE_OPT_FLOAT: | |||||
errno = 0; | |||||
if (self->optvalue) { | |||||
*(float *)opt->value = strtof(self->optvalue, (char **)&s); | |||||
self->optvalue = NULL; | |||||
} else if (self->argc > 1) { | |||||
self->argc--; | |||||
*(float *)opt->value = strtof(*++self->argv, (char **)&s); | |||||
} else { | |||||
argparse_error(self, opt, "requires a value", flags); | |||||
} | |||||
if (errno) | |||||
argparse_error(self, opt, strerror(errno), flags); | |||||
if (s[0] != '\0') | |||||
argparse_error(self, opt, "expects a numerical value", flags); | |||||
break; | |||||
default: | |||||
assert(0); | |||||
} | |||||
skipped: | |||||
if (opt->callback) { | |||||
return opt->callback(self, opt); | |||||
} | |||||
return 0; | |||||
} | |||||
static void | |||||
argparse_options_check(const struct argparse_option *options) | |||||
{ | |||||
for (; options->type != ARGPARSE_OPT_END; options++) { | |||||
switch (options->type) { | |||||
case ARGPARSE_OPT_END: | |||||
case ARGPARSE_OPT_BOOLEAN: | |||||
case ARGPARSE_OPT_BIT: | |||||
case ARGPARSE_OPT_INTEGER: | |||||
case ARGPARSE_OPT_FLOAT: | |||||
case ARGPARSE_OPT_STRING: | |||||
case ARGPARSE_OPT_GROUP: | |||||
continue; | |||||
default: | |||||
fprintf(stderr, "wrong option type: %d", options->type); | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
static int | |||||
argparse_short_opt(struct argparse *self, const struct argparse_option *options) | |||||
{ | |||||
for (; options->type != ARGPARSE_OPT_END; options++) { | |||||
if (options->short_name == *self->optvalue) { | |||||
self->optvalue = self->optvalue[1] ? self->optvalue + 1 : NULL; | |||||
return argparse_getvalue(self, options, 0); | |||||
} | |||||
} | |||||
return -2; | |||||
} | |||||
static int | |||||
argparse_long_opt(struct argparse *self, const struct argparse_option *options) | |||||
{ | |||||
for (; options->type != ARGPARSE_OPT_END; options++) { | |||||
const char *rest; | |||||
int opt_flags = 0; | |||||
if (!options->long_name) | |||||
continue; | |||||
rest = prefix_skip(self->argv[0] + 2, options->long_name); | |||||
if (!rest) { | |||||
// negation disabled? | |||||
if (options->flags & OPT_NONEG) { | |||||
continue; | |||||
} | |||||
// only OPT_BOOLEAN/OPT_BIT supports negation | |||||
if (options->type != ARGPARSE_OPT_BOOLEAN && options->type != | |||||
ARGPARSE_OPT_BIT) { | |||||
continue; | |||||
} | |||||
if (prefix_cmp(self->argv[0] + 2, "no-")) { | |||||
continue; | |||||
} | |||||
rest = prefix_skip(self->argv[0] + 2 + 3, options->long_name); | |||||
if (!rest) | |||||
continue; | |||||
opt_flags |= OPT_UNSET; | |||||
} | |||||
if (*rest) { | |||||
if (*rest != '=') | |||||
continue; | |||||
self->optvalue = rest + 1; | |||||
} | |||||
return argparse_getvalue(self, options, opt_flags | OPT_LONG); | |||||
} | |||||
return -2; | |||||
} | |||||
int | |||||
argparse_init(struct argparse *self, struct argparse_option *options, | |||||
const char *const *usages, int flags) | |||||
{ | |||||
memset(self, 0, sizeof(*self)); | |||||
self->options = options; | |||||
self->usages = usages; | |||||
self->flags = flags; | |||||
self->description = NULL; | |||||
self->epilog = NULL; | |||||
return 0; | |||||
} | |||||
void | |||||
argparse_describe(struct argparse *self, const char *description, | |||||
const char *epilog) | |||||
{ | |||||
self->description = description; | |||||
self->epilog = epilog; | |||||
} | |||||
int | |||||
argparse_parse(struct argparse *self, int argc, const char **argv) | |||||
{ | |||||
self->argc = argc - 1; | |||||
self->argv = argv + 1; | |||||
self->out = argv; | |||||
argparse_options_check(self->options); | |||||
for (; self->argc; self->argc--, self->argv++) { | |||||
const char *arg = self->argv[0]; | |||||
if (arg[0] != '-' || !arg[1]) { | |||||
if (self->flags & ARGPARSE_STOP_AT_NON_OPTION) { | |||||
goto end; | |||||
} | |||||
// if it's not option or is a single char '-', copy verbatim | |||||
self->out[self->cpidx++] = self->argv[0]; | |||||
continue; | |||||
} | |||||
// short option | |||||
if (arg[1] != '-') { | |||||
self->optvalue = arg + 1; | |||||
switch (argparse_short_opt(self, self->options)) { | |||||
case -1: | |||||
break; | |||||
case -2: | |||||
goto unknown; | |||||
} | |||||
while (self->optvalue) { | |||||
switch (argparse_short_opt(self, self->options)) { | |||||
case -1: | |||||
break; | |||||
case -2: | |||||
goto unknown; | |||||
} | |||||
} | |||||
continue; | |||||
} | |||||
// if '--' presents | |||||
if (!arg[2]) { | |||||
self->argc--; | |||||
self->argv++; | |||||
break; | |||||
} | |||||
// long option | |||||
switch (argparse_long_opt(self, self->options)) { | |||||
case -1: | |||||
break; | |||||
case -2: | |||||
goto unknown; | |||||
} | |||||
continue; | |||||
unknown: | |||||
fprintf(stderr, "error: unknown option `%s`\n", self->argv[0]); | |||||
argparse_usage(self); | |||||
exit(1); | |||||
} | |||||
end: | |||||
memmove(self->out + self->cpidx, self->argv, | |||||
self->argc * sizeof(*self->out)); | |||||
self->out[self->cpidx + self->argc] = NULL; | |||||
return self->cpidx + self->argc; | |||||
} | |||||
void | |||||
argparse_usage(struct argparse *self) | |||||
{ | |||||
if (self->usages) { | |||||
fprintf(stdout, "Usage: %s\n", *self->usages++); | |||||
while (*self->usages && **self->usages) | |||||
fprintf(stdout, " or: %s\n", *self->usages++); | |||||
} else { | |||||
fprintf(stdout, "Usage:\n"); | |||||
} | |||||
// print description | |||||
if (self->description) | |||||
fprintf(stdout, "%s\n", self->description); | |||||
fputc('\n', stdout); | |||||
const struct argparse_option *options; | |||||
// figure out best width | |||||
size_t usage_opts_width = 0; | |||||
size_t len; | |||||
options = self->options; | |||||
for (; options->type != ARGPARSE_OPT_END; options++) { | |||||
len = 0; | |||||
if ((options)->short_name) { | |||||
len += 2; | |||||
} | |||||
if ((options)->short_name && (options)->long_name) { | |||||
len += 2; // separator ", " | |||||
} | |||||
if ((options)->long_name) { | |||||
len += strlen((options)->long_name) + 2; | |||||
} | |||||
if (options->type == ARGPARSE_OPT_INTEGER) { | |||||
len += strlen("=<int>"); | |||||
} | |||||
if (options->type == ARGPARSE_OPT_FLOAT) { | |||||
len += strlen("=<flt>"); | |||||
} else if (options->type == ARGPARSE_OPT_STRING) { | |||||
len += strlen("=<str>"); | |||||
} | |||||
len = (len + 3) - ((len + 3) & 3); | |||||
if (usage_opts_width < len) { | |||||
usage_opts_width = len; | |||||
} | |||||
} | |||||
usage_opts_width += 4; // 4 spaces prefix | |||||
options = self->options; | |||||
for (; options->type != ARGPARSE_OPT_END; options++) { | |||||
size_t pos = 0; | |||||
int pad = 0; | |||||
if (options->type == ARGPARSE_OPT_GROUP) { | |||||
fputc('\n', stdout); | |||||
fprintf(stdout, "%s", options->help); | |||||
fputc('\n', stdout); | |||||
continue; | |||||
} | |||||
pos = fprintf(stdout, " "); | |||||
if (options->short_name) { | |||||
pos += fprintf(stdout, "-%c", options->short_name); | |||||
} | |||||
if (options->long_name && options->short_name) { | |||||
pos += fprintf(stdout, ", "); | |||||
} | |||||
if (options->long_name) { | |||||
pos += fprintf(stdout, "--%s", options->long_name); | |||||
} | |||||
if (options->type == ARGPARSE_OPT_INTEGER) { | |||||
pos += fprintf(stdout, "=<int>"); | |||||
} else if (options->type == ARGPARSE_OPT_FLOAT) { | |||||
pos += fprintf(stdout, "=<flt>"); | |||||
} else if (options->type == ARGPARSE_OPT_STRING) { | |||||
pos += fprintf(stdout, "=<str>"); | |||||
} | |||||
if (pos <= usage_opts_width) { | |||||
pad = usage_opts_width - pos; | |||||
} else { | |||||
fputc('\n', stdout); | |||||
pad = usage_opts_width; | |||||
} | |||||
fprintf(stdout, "%*s%s\n", pad + 2, "", options->help); | |||||
} | |||||
// print epilog | |||||
if (self->epilog) | |||||
fprintf(stdout, "%s\n", self->epilog); | |||||
} | |||||
int | |||||
argparse_help_cb(struct argparse *self, const struct argparse_option *option) | |||||
{ | |||||
(void)option; | |||||
argparse_usage(self); | |||||
exit(0); | |||||
} |
@@ -1,130 +0,0 @@ | |||||
/** | |||||
* Copyright (C) 2012-2015 Yecheng Fu <cofyc.jackson at gmail dot com> | |||||
* All rights reserved. | |||||
* | |||||
* Use of this source code is governed by a MIT-style license that can be found | |||||
* in the LICENSE file. | |||||
*/ | |||||
#ifndef ARGPARSE_H | |||||
#define ARGPARSE_H | |||||
/* For c++ compatibility */ | |||||
#ifdef __cplusplus | |||||
extern "C" { | |||||
#endif | |||||
#include <stdint.h> | |||||
struct argparse; | |||||
struct argparse_option; | |||||
typedef int argparse_callback (struct argparse *self, | |||||
const struct argparse_option *option); | |||||
enum argparse_flag { | |||||
ARGPARSE_STOP_AT_NON_OPTION = 1, | |||||
}; | |||||
enum argparse_option_type { | |||||
/* special */ | |||||
ARGPARSE_OPT_END, | |||||
ARGPARSE_OPT_GROUP, | |||||
/* options with no arguments */ | |||||
ARGPARSE_OPT_BOOLEAN, | |||||
ARGPARSE_OPT_BIT, | |||||
/* options with arguments (optional or required) */ | |||||
ARGPARSE_OPT_INTEGER, | |||||
ARGPARSE_OPT_FLOAT, | |||||
ARGPARSE_OPT_STRING, | |||||
}; | |||||
enum argparse_option_flags { | |||||
OPT_NONEG = 1, /* disable negation */ | |||||
}; | |||||
/** | |||||
* argparse option | |||||
* | |||||
* `type`: | |||||
* holds the type of the option, you must have an ARGPARSE_OPT_END last in your | |||||
* array. | |||||
* | |||||
* `short_name`: | |||||
* the character to use as a short option name, '\0' if none. | |||||
* | |||||
* `long_name`: | |||||
* the long option name, without the leading dash, NULL if none. | |||||
* | |||||
* `value`: | |||||
* stores pointer to the value to be filled. | |||||
* | |||||
* `help`: | |||||
* the short help message associated to what the option does. | |||||
* Must never be NULL (except for ARGPARSE_OPT_END). | |||||
* | |||||
* `callback`: | |||||
* function is called when corresponding argument is parsed. | |||||
* | |||||
* `data`: | |||||
* associated data. Callbacks can use it like they want. | |||||
* | |||||
* `flags`: | |||||
* option flags. | |||||
*/ | |||||
struct argparse_option { | |||||
enum argparse_option_type type; | |||||
const char short_name; | |||||
const char *long_name; | |||||
void *value; | |||||
const char *help; | |||||
argparse_callback *callback; | |||||
intptr_t data; | |||||
int flags; | |||||
}; | |||||
/** | |||||
* argpparse | |||||
*/ | |||||
struct argparse { | |||||
// user supplied | |||||
const struct argparse_option *options; | |||||
const char *const *usages; | |||||
int flags; | |||||
const char *description; // a description after usage | |||||
const char *epilog; // a description at the end | |||||
// internal context | |||||
int argc; | |||||
const char **argv; | |||||
const char **out; | |||||
int cpidx; | |||||
const char *optvalue; // current option value | |||||
}; | |||||
// built-in callbacks | |||||
int argparse_help_cb(struct argparse *self, | |||||
const struct argparse_option *option); | |||||
// built-in option macros | |||||
#define OPT_END() { ARGPARSE_OPT_END, 0, NULL, NULL, 0, NULL, 0, 0 } | |||||
#define OPT_BOOLEAN(...) { ARGPARSE_OPT_BOOLEAN, __VA_ARGS__ } | |||||
#define OPT_BIT(...) { ARGPARSE_OPT_BIT, __VA_ARGS__ } | |||||
#define OPT_INTEGER(...) { ARGPARSE_OPT_INTEGER, __VA_ARGS__ } | |||||
#define OPT_FLOAT(...) { ARGPARSE_OPT_FLOAT, __VA_ARGS__ } | |||||
#define OPT_STRING(...) { ARGPARSE_OPT_STRING, __VA_ARGS__ } | |||||
#define OPT_GROUP(h) { ARGPARSE_OPT_GROUP, 0, NULL, NULL, h, NULL, 0, 0 } | |||||
#define OPT_HELP() OPT_BOOLEAN('h', "help", NULL, \ | |||||
"show this help message and exit", \ | |||||
argparse_help_cb, 0, OPT_NONEG) | |||||
int argparse_init(struct argparse *self, struct argparse_option *options, | |||||
const char *const *usages, int flags); | |||||
void argparse_describe(struct argparse *self, const char *description, | |||||
const char *epilog); | |||||
int argparse_parse(struct argparse *self, int argc, const char **argv); | |||||
void argparse_usage(struct argparse *self); | |||||
#ifdef __cplusplus | |||||
} | |||||
#endif | |||||
#endif |
@@ -29,7 +29,7 @@ | |||||
#include <sndfile.h> | #include <sndfile.h> | ||||
#include <errno.h> | #include <errno.h> | ||||
#include <time.h> | #include <time.h> | ||||
#include "libs/argparse.h" | |||||
#include "argparse/argparse.h" | |||||
#include "common.h" | #include "common.h" | ||||
#include "apt.h" | #include "apt.h" | ||||
@@ -222,10 +222,24 @@ static int processAudio(char *filename, options_t *opts){ | |||||
} | } | ||||
// Perform temperature calibration | // Perform temperature calibration | ||||
apt_temperature(opts->satnum, &tmpimg, APT_CHB_OFFSET, APT_CH_WIDTH); | |||||
apt_calibrate_thermal(opts->satnum, &tmpimg, APT_CHB_OFFSET, APT_CH_WIDTH); | |||||
ImageOut(opts, &tmpimg, APT_CHB_OFFSET, APT_CH_WIDTH, "Temperature", Temperature, (char *)apt_TempPalette); | ImageOut(opts, &tmpimg, APT_CHB_OFFSET, APT_CH_WIDTH, "Temperature", Temperature, (char *)apt_TempPalette); | ||||
} | } | ||||
// Temperature | |||||
if (CONTAINS(opts->type, Visible) && img.chA <= 2) { | |||||
// Create another buffer as to not modify the orignal | |||||
apt_image_t tmpimg = img; | |||||
for(int i = 0; i < img.nrow; i++){ | |||||
tmpimg.prow[i] = (float *) malloc(sizeof(float) * APT_PROW_WIDTH); | |||||
memcpy(tmpimg.prow[i], img.prow[i], sizeof(float) * APT_PROW_WIDTH); | |||||
} | |||||
// Perform visible calibration | |||||
apt_calibrate_visible(opts->satnum, &tmpimg, APT_CHA_OFFSET, APT_CH_WIDTH); | |||||
ImageOut(opts, &tmpimg, APT_CHA_OFFSET, APT_CH_WIDTH, "Visible", Visible, (char *)apt_TempPalette); | |||||
} | |||||
// MCIR | // MCIR | ||||
if (CONTAINS(opts->type, MCIR)) | if (CONTAINS(opts->type, MCIR)) | ||||
ImageOut(opts, &img, APT_CHA_OFFSET, APT_CH_WIDTH, "MCIR", MCIR, NULL); | ImageOut(opts, &img, APT_CHA_OFFSET, APT_CH_WIDTH, "MCIR", MCIR, NULL); | ||||
@@ -1,125 +0,0 @@ | |||||
/* | |||||
* This file is part of Aptdec. | |||||
* Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019-2022 | |||||
* | |||||
* Aptdec is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation, either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | |||||
* | |||||
*/ | |||||
const struct { | |||||
float d[4][3]; | |||||
struct { | |||||
float vc, A, B; | |||||
} rad[3]; | |||||
struct { | |||||
float Ns; | |||||
float b[3]; | |||||
} cor[3]; | |||||
/* Calibration corficcients taken from the NOAA KLM satellite user guide | |||||
* https://web.archive.org/web/20141220021557/https://www.ncdc.noaa.gov/oa/pod-guide/ncdc/docs/klm/tables.htm | |||||
*/ | |||||
} satcal[] = { | |||||
{ // NOAA 15 | |||||
{ // PRT coefficient d0, d1, d2 | |||||
{276.60157f, 0.051045f, 1.36328E-06f}, | |||||
{276.62531f, 0.050909f, 1.47266E-06f}, | |||||
{276.67413f, 0.050907f, 1.47656E-06f}, | |||||
{276.59258f, 0.050966f, 1.47656E-06f} | |||||
}, | |||||
{ // Channel radiance coefficient vc, A, B | |||||
{925.4075f, 0.337810f, 0.998719f}, // Channel 4 | |||||
{839.8979f, 0.304558f, 0.999024f}, // Channel 5 | |||||
{2695.9743f, 1.621256f, 0.998015f} // Channel 3B | |||||
}, | |||||
{ // Nonlinear radiance correction Ns, b0, b1, b2 | |||||
{-4.50f, {4.76f, -0.0932f, 0.0004524f}}, // Channel 4 | |||||
{-3.61f, {3.83f, -0.0659f, 0.0002811f}}, // Channel 5 | |||||
{0.0f, {0.0f, 0.0f, 0.0f}} // Channel 3B | |||||
} | |||||
}, | |||||
{ // NOAA 16 | |||||
{ // PRT coeff d0, d1, d2 | |||||
{276.355f, 5.562E-02f, -1.590E-05f}, | |||||
{276.142f, 5.605E-02f, -1.707E-05f}, | |||||
{275.996f, 5.486E-02f, -1.223E-05f}, | |||||
{276.132f, 5.494E-02f, -1.344E-05f} | |||||
}, | |||||
{ // Channel radiance coefficient vc, A, B | |||||
{917.2289f, 0.332380f, 0.998522f}, // Channel 4 | |||||
{838.1255f, 0.674623f, 0.998363f}, // Channel 5 | |||||
{2700.1148f, 1.592459f, 0.998147f} // Channel 3B | |||||
}, | |||||
{ // Nonlinear radiance correction Ns, b0, b1, b2 | |||||
{-2.467f, {2.96f, -0.05411f, 0.00024532f}}, // Channel 4 | |||||
{-2.009f, {2.25f, -0.03665f, 0.00014854f}}, // Channel 5 | |||||
{0.0f, {0.0f, 0.0f, 0.0f}} // Channel 3B | |||||
} | |||||
}, | |||||
{ // NOAA 17 | |||||
{ // PRT coefficient d0, d1, d2 | |||||
{276.628f, 0.05098f, 1.371e-06f}, | |||||
{276.538f, 0.05098f, 1.371e-06f}, | |||||
{276.761f, 0.05097f, 1.369e-06f}, | |||||
{276.660f, 0.05100f, 1.348e-06f} | |||||
}, | |||||
{ // Channel radiance coefficient vc, A, B | |||||
{926.2947f, 0.271683f, 0.998794f}, // Channel 4 | |||||
{839.8246f, 0.309180f, 0.999012f}, // Channel 5 | |||||
{2669.3554f, 1.702380f, 0.997378f} // Channel 3B | |||||
}, | |||||
{ // Nonlinear radiance correction Ns, b0, b1, b2 | |||||
{-8.55f, {8.22f, -0.15795f, 0.00075579f}}, // Channel 4 | |||||
{-3.97f, {4.31f, -0.07318f, 0.00030976f}}, // Channel 5 | |||||
{0.0f, {0.0f, 0.0f, 0.0f}} // Channel 3B | |||||
} | |||||
}, | |||||
{ // NOAA 18 | |||||
{ // PRT coefficient d0, d1, d2 | |||||
{276.601f, 0.05090f, 1.657e-06f}, | |||||
{276.683f, 0.05101f, 1.482e-06f}, | |||||
{276.565f, 0.05117f, 1.313e-06f}, | |||||
{276.615f, 0.05103f, 1.484e-06f} | |||||
}, | |||||
{ // Channel radiance coefficient vc, A, B | |||||
{928.1460f, 0.436645f, 0.998607f}, // Channel 4 | |||||
{833.2532f, 0.253179f, 0.999057f}, // Channel 5 | |||||
{2659.7952f, 1.698704f, 0.996960f} // Channel 3B | |||||
}, | |||||
{ // Nonlinear radiance correction Ns, b0, b1, b2 | |||||
{-5.53f, {5.82f, -0.11069f, 0.00052337f}}, // Channel 4 | |||||
{-2.22f, {2.67f, -0.04360f, 0.00017715f}}, // Channel 5 | |||||
{0.0f, {0.0f, 0.0f, 0.0f}} // Channel 3B | |||||
} | |||||
}, | |||||
{ // NOAA 19 | |||||
{ // PRT coefficient d0, d1, d2 | |||||
{276.6067f, 0.051111f, 1.405783E-06f}, | |||||
{276.6119f, 0.051090f, 1.496037E-06f}, | |||||
{276.6311f, 0.051033f, 1.496990E-06f}, | |||||
{276.6268f, 0.051058f, 1.493110E-06f} | |||||
}, | |||||
{ // Channel radiance coefficient vc, A, B | |||||
{928.9f, 0.53959f, 0.998534f}, // Channel 4 | |||||
{831.9f, 0.36064f, 0.998913f}, // Channel 5 | |||||
{2670.0f, 1.67396f, 0.997364f} // Channel 3B | |||||
}, | |||||
{ // Nonlinear radiance correction Ns, b0, b1, b2 | |||||
{-5.49f, {5.70f, -0.11187f, 0.00054668f}}, // Channel 4 | |||||
{-3.39f, {3.58f, -0.05991f, 0.00024985f}}, // Channel 5 | |||||
{0.0f, {0.0f, 0.0f, 0.0f}} // Channel 3B | |||||
} | |||||
}}; | |||||
const float c1 = 1.1910427e-5f; | |||||
const float c2 = 1.4387752f; |