@@ -11,7 +11,7 @@ find_package(PNG) | |||
# 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(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); | |||
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 |
@@ -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_flipImage(apt_image_t *img, int width, int offset); | |||
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_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', | |||
Channel_A='a', | |||
Channel_B='b', | |||
Distribution='d' | |||
Distribution='d', | |||
Visible='v' | |||
}; | |||
enum effects { | |||
Crop_Telemetry='t', | |||
@@ -310,8 +310,8 @@ int apt_cropNoise(apt_image_t *img){ | |||
return startCrop; | |||
} | |||
// --- Temperature Calibration --- // | |||
#include "satcal.h" | |||
// --- Visible and Temperature Calibration --- // | |||
#include "calibration.h" | |||
typedef struct { | |||
float Nbb; | |||
@@ -321,71 +321,78 @@ typedef struct { | |||
} tempparam_t; | |||
// 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 | |||
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 | |||
T -= 273.15; | |||
Te -= 273.15; | |||
// 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 | |||
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 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 <errno.h> | |||
#include <time.h> | |||
#include "libs/argparse.h" | |||
#include "argparse/argparse.h" | |||
#include "common.h" | |||
#include "apt.h" | |||
@@ -222,10 +222,24 @@ static int processAudio(char *filename, options_t *opts){ | |||
} | |||
// 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); | |||
} | |||
// 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 | |||
if (CONTAINS(opts->type, MCIR)) | |||
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; |