@@ -0,0 +1,4 @@ | |||||
Language: Cpp | |||||
BasedOnStyle: Google | |||||
IndentWidth: 4 | |||||
ColumnLimit: 130 # Conservative estimate considering the size of modern displays |
@@ -17,6 +17,7 @@ | |||||
*/ | */ | ||||
#include "algebra.h" | #include "algebra.h" | ||||
#include <math.h> | #include <math.h> | ||||
// Find the best linear equation to estimate the value of the | // Find the best linear equation to estimate the value of the | ||||
@@ -39,21 +40,17 @@ linear_t linear_regression(const float *independent, const float *dependent, siz | |||||
a_numerator += (independent[i] - independent_mean) * (dependent[i] - dependent_mean); | a_numerator += (independent[i] - independent_mean) * (dependent[i] - dependent_mean); | ||||
a_denominator += powf(independent[i] - independent_mean, 2.0f); | a_denominator += powf(independent[i] - independent_mean, 2.0f); | ||||
} | } | ||||
a = a_numerator/a_denominator; | |||||
a = a_numerator / a_denominator; | |||||
} | } | ||||
// We can now solve for the y-intercept since we know the slope | // We can now solve for the y-intercept since we know the slope | ||||
// and the centoid, which the line must pass through | // and the centoid, which the line must pass through | ||||
float b = dependent_mean - a * independent_mean; | float b = dependent_mean - a * independent_mean; | ||||
//printf("y(x) = %fx + %f\n", a, b); | |||||
// printf("y(x) = %fx + %f\n", a, b); | |||||
return (linear_t){a, b}; | return (linear_t){a, b}; | ||||
} | } | ||||
float linear_calc(float x, linear_t line) { | |||||
return x*line.a + line.b; | |||||
} | |||||
float linear_calc(float x, linear_t line) { return x * line.a + line.b; } | |||||
float quadratic_calc(float x, quadratic_t quadratic) { | |||||
return x*x*quadratic.a + x*quadratic.b + quadratic.c; | |||||
} | |||||
float quadratic_calc(float x, quadratic_t quadratic) { return x * x * quadratic.a + x * quadratic.b + quadratic.c; } |
@@ -25,9 +25,9 @@ | |||||
extern "C" { | extern "C" { | ||||
#endif | #endif | ||||
#if defined (__GNUC__) && (__GNUC__ >= 4) | |||||
#if defined(__GNUC__) && (__GNUC__ >= 4) | |||||
#define APT_API __attribute__((visibility("default"))) | #define APT_API __attribute__((visibility("default"))) | ||||
#elif defined (_MSC_VER) | |||||
#elif defined(_MSC_VER) | |||||
#ifdef APT_API_EXPORT | #ifdef APT_API_EXPORT | ||||
#define APT_API __declspec(dllexport) | #define APT_API __declspec(dllexport) | ||||
#elif APT_API_STATIC | #elif APT_API_STATIC | ||||
@@ -44,20 +44,20 @@ extern "C" { | |||||
// Width in pixels of sync | // Width in pixels of sync | ||||
#define APT_SYNC_WIDTH 39 | #define APT_SYNC_WIDTH 39 | ||||
// Width in pixels of space | // Width in pixels of space | ||||
#define APT_SPC_WIDTH 47 | |||||
#define APT_SPC_WIDTH 47 | |||||
// Width in pixels of telemetry | // Width in pixels of telemetry | ||||
#define APT_TELE_WIDTH 45 | #define APT_TELE_WIDTH 45 | ||||
// Width in pixels of a single channel image | // Width in pixels of a single channel image | ||||
#define APT_CH_WIDTH 909 | |||||
#define APT_FRAME_LEN 128 | |||||
#define APT_CH_OFFSET (APT_SYNC_WIDTH+APT_SPC_WIDTH+APT_CH_WIDTH+APT_TELE_WIDTH) | |||||
#define APT_CH_WIDTH 909 | |||||
#define APT_FRAME_LEN 128 | |||||
#define APT_CH_OFFSET (APT_SYNC_WIDTH + APT_SPC_WIDTH + APT_CH_WIDTH + APT_TELE_WIDTH) | |||||
// Width in pixels of full frame, including sync, space, images and telemetry | // Width in pixels of full frame, including sync, space, images and telemetry | ||||
#define APT_IMG_WIDTH 2080 | |||||
#define APT_IMG_WIDTH 2080 | |||||
// Offset in pixels to channel A | // Offset in pixels to channel A | ||||
#define APT_CHA_OFFSET (APT_SYNC_WIDTH+APT_SPC_WIDTH) | |||||
#define APT_CHA_OFFSET (APT_SYNC_WIDTH + APT_SPC_WIDTH) | |||||
// Offset in pixels to channel B | // Offset in pixels to channel B | ||||
#define APT_CHB_OFFSET (APT_SYNC_WIDTH+APT_SPC_WIDTH+APT_CH_WIDTH+APT_TELE_WIDTH+APT_SYNC_WIDTH+APT_SPC_WIDTH) | |||||
#define APT_TOTAL_TELE (APT_SYNC_WIDTH+APT_SPC_WIDTH+APT_TELE_WIDTH+APT_SYNC_WIDTH+APT_SPC_WIDTH+APT_TELE_WIDTH) | |||||
#define APT_CHB_OFFSET (APT_SYNC_WIDTH + APT_SPC_WIDTH + APT_CH_WIDTH + APT_TELE_WIDTH + APT_SYNC_WIDTH + APT_SPC_WIDTH) | |||||
#define APT_TOTAL_TELE (APT_SYNC_WIDTH + APT_SPC_WIDTH + APT_TELE_WIDTH + APT_SYNC_WIDTH + APT_SPC_WIDTH + APT_TELE_WIDTH) | |||||
// Number of rows required for apt_calibrate | // Number of rows required for apt_calibrate | ||||
#define APT_CALIBRATION_ROWS 192 | #define APT_CALIBRATION_ROWS 192 | ||||
@@ -69,7 +69,15 @@ extern "C" { | |||||
// Channel 3B: mid-infrared (3.55-3.93 um) | // Channel 3B: mid-infrared (3.55-3.93 um) | ||||
// Channel 4: thermal-infrared (10.3-11.3 um) | // Channel 4: thermal-infrared (10.3-11.3 um) | ||||
// Channel 5: thermal-infrared (11.5-12.5 um) | // Channel 5: thermal-infrared (11.5-12.5 um) | ||||
typedef enum apt_channel {APT_CHANNEL_UNKNOWN, APT_CHANNEL_1, APT_CHANNEL_2, APT_CHANNEL_3A, APT_CHANNEL_4, APT_CHANNEL_5, APT_CHANNEL_3B} apt_channel_t; | |||||
typedef enum apt_channel { | |||||
APT_CHANNEL_UNKNOWN, | |||||
APT_CHANNEL_1, | |||||
APT_CHANNEL_2, | |||||
APT_CHANNEL_3A, | |||||
APT_CHANNEL_4, | |||||
APT_CHANNEL_5, | |||||
APT_CHANNEL_3B | |||||
} apt_channel_t; | |||||
// Width in elements of apt_image_t.prow arrays | // Width in elements of apt_image_t.prow arrays | ||||
#define APT_PROW_WIDTH 2150 | #define APT_PROW_WIDTH 2150 | ||||
@@ -79,16 +87,16 @@ typedef enum apt_channel {APT_CHANNEL_UNKNOWN, APT_CHANNEL_1, APT_CHANNEL_2, APT | |||||
typedef int (*apt_getsamples_t)(void *context, float *samples, int count); | typedef int (*apt_getsamples_t)(void *context, float *samples, int count); | ||||
typedef struct { | typedef struct { | ||||
float *prow[APT_MAX_HEIGHT]; // Row buffers | |||||
int nrow; // Number of rows | |||||
int zenith; // Row in image where satellite reaches peak elevation | |||||
apt_channel_t chA, chB; // ID of each channel | |||||
char name[256]; // Stripped filename | |||||
char *palette; // Filename of palette | |||||
float *prow[APT_MAX_HEIGHT]; // Row buffers | |||||
int nrow; // Number of rows | |||||
int zenith; // Row in image where satellite reaches peak elevation | |||||
apt_channel_t chA, chB; // ID of each channel | |||||
char name[256]; // Stripped filename | |||||
char *palette; // Filename of palette | |||||
} apt_image_t; | } apt_image_t; | ||||
typedef struct { | typedef struct { | ||||
float r, g, b; | |||||
float r, g, b; | |||||
} apt_rgb_t; | } apt_rgb_t; | ||||
int APT_API apt_init(double sample_rate); | int APT_API apt_init(double sample_rate); | ||||
@@ -96,7 +104,7 @@ int APT_API apt_getpixelrow(float *pixelv, int nrow, int *zenith, int reset, apt | |||||
void APT_API apt_histogramEqualise(float **prow, int nrow, int offset, int width); | void APT_API apt_histogramEqualise(float **prow, int nrow, int offset, int width); | ||||
void APT_API apt_linearEnhance(float **prow, int nrow, int offset, int width); | void APT_API apt_linearEnhance(float **prow, int nrow, int offset, int width); | ||||
apt_channel_t APT_API apt_calibrate(float **prow, int nrow, int offset, int width) ; | |||||
apt_channel_t APT_API apt_calibrate(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_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); | ||||
@@ -108,8 +116,8 @@ void APT_API apt_calibrate_visible(int satnum, apt_image_t *img, int offset, int | |||||
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); | ||||
extern char APT_API apt_TempPalette[256*3]; | |||||
extern char APT_API apt_PrecipPalette[58*3]; | |||||
extern char APT_API apt_TempPalette[256 * 3]; | |||||
extern char APT_API apt_PrecipPalette[58 * 3]; | |||||
#ifdef __cplusplus | #ifdef __cplusplus | ||||
} | } | ||||
@@ -17,104 +17,85 @@ | |||||
*/ | */ | ||||
#include "calibration.h" | #include "calibration.h" | ||||
#include "util.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 | |||||
} | |||||
} | |||||
}; | |||||
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) { | calibration_t get_calibration(int satid) { | ||||
switch (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]; | |||||
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]; | |||||
} | } | ||||
} | } |
@@ -25,7 +25,7 @@ typedef struct { | |||||
// Quadratics for calculating PRT temperature | // Quadratics for calculating PRT temperature | ||||
quadratic_t prt[4]; | quadratic_t prt[4]; | ||||
// Visible calibration coefficients | // Visible calibration coefficients | ||||
struct { | struct { | ||||
linear_t low; | linear_t low; | ||||
@@ -34,8 +34,8 @@ typedef struct { | |||||
} visible[2]; | } visible[2]; | ||||
// Radiance coefficients | // Radiance coefficients | ||||
struct { | |||||
float vc, A, B; | |||||
struct { | |||||
float vc, A, B; | |||||
} rad[3]; | } rad[3]; | ||||
// Non linear correction coefficients | // Non linear correction coefficients | ||||
@@ -1,4 +1,4 @@ | |||||
/* | |||||
/* | |||||
* This file is part of Aptdec. | * This file is part of Aptdec. | ||||
* Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019-2022 | * Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019-2022 | ||||
* | * | ||||
@@ -17,67 +17,61 @@ | |||||
* | * | ||||
*/ | */ | ||||
#include "apt.h" | |||||
#include "color.h" | #include "color.h" | ||||
apt_rgb_t apt_applyPalette(char *palette, int val){ | |||||
return (apt_rgb_t){ | |||||
palette[(int)CLIP(val, 0, 255)*3 + 0], | |||||
palette[(int)CLIP(val, 0, 255)*3 + 1], | |||||
palette[(int)CLIP(val, 0, 255)*3 + 2] | |||||
}; | |||||
#include "apt.h" | |||||
apt_rgb_t apt_applyPalette(char *palette, int val) { | |||||
return (apt_rgb_t){palette[(int)CLIP(val, 0, 255) * 3 + 0], palette[(int)CLIP(val, 0, 255) * 3 + 1], | |||||
palette[(int)CLIP(val, 0, 255) * 3 + 2]}; | |||||
} | } | ||||
apt_rgb_t apt_RGBcomposite(apt_rgb_t top, float top_a, apt_rgb_t bottom, float bottom_a){ | |||||
return (apt_rgb_t){ | |||||
MCOMPOSITE(top.r, top_a, bottom.r, bottom_a), | |||||
MCOMPOSITE(top.g, top_a, bottom.g, bottom_a), | |||||
MCOMPOSITE(top.b, top_a, bottom.b, bottom_a) | |||||
}; | |||||
apt_rgb_t apt_RGBcomposite(apt_rgb_t top, float top_a, apt_rgb_t bottom, float bottom_a) { | |||||
return (apt_rgb_t){MCOMPOSITE(top.r, top_a, bottom.r, bottom_a), MCOMPOSITE(top.g, top_a, bottom.g, bottom_a), | |||||
MCOMPOSITE(top.b, top_a, bottom.b, bottom_a)}; | |||||
} | } | ||||
// The "I totally didn't just steal this from WXtoImg" palette | // The "I totally didn't just steal this from WXtoImg" palette | ||||
char apt_TempPalette[256*3] = { | |||||
"\x45\x0\x8f\x46\x0\x91\x47\x0\x92\x48\x0\x94\x49\x0\x96\x4a\x0\x98\x4b\x0\x9b\x4d\x0\x9d" | |||||
"\x4e\x0\xa0\x50\x0\xa2\x51\x0\xa5\x52\x0\xa7\x54\x0\xaa\x56\x0\xae\x57\x0\xb1" | |||||
"\x58\x0\xb4\x5a\x0\xb7\x5c\x0\xba\x5e\x0\xbd\x5f\x0\xc0\x61\x0\xc4\x64\x0\xc8" | |||||
"\x66\x0\xcb\x68\x0\xce\x69\x0\xd1\x68\x0\xd4\x65\x0\xd7\x63\x0\xda\x61\x0\xdd" | |||||
"\x5d\x0\xe1\x5b\x0\xe4\x59\x0\xe6\x56\x0\xe9\x53\x0\xeb\x50\x0\xee\x4d\x0\xf0" | |||||
"\x49\x0\xf3\x47\x0\xfc\x43\x0\xfa\x31\x0\xbf\x20\x0\x89\x20\x0\x92\x1e\x0\x95" | |||||
"\x1b\x0\x97\x19\x0\x9a\x17\x0\x9c\x15\x0\x9e\x12\x0\xa0\xf\x0\xa3\xf\x2\xa5" | |||||
"\xe\x6\xa8\xe\xa\xab\xe\xd\xad\xe\x11\xb1\xd\x15\xb4\xd\x18\xb7\xd\x1c\xba" | |||||
"\xb\x21\xbd\xa\x25\xc0\xa\x29\xc3\x9\x2d\xc6\x8\x33\xca\x7\x36\xcd\x7\x3b\xd0" | |||||
"\x7\x41\xd3\x5\x45\xd6\x4\x4b\xd9\x4\x50\xdc\x3\x55\xde\x2\x5d\xe2\x1\x61\xe5" | |||||
"\x0\x66\xe7\x0\x6c\xea\x0\x72\xec\x0\x78\xee\x0\x7d\xf0\x0\x82\xf3\x0\x8d\xfc" | |||||
"\x0\x90\xfa\x0\x71\xbf\x0\x54\x89\x0\x5c\x91\x0\x61\x94\x0\x64\x96\x0\x68\x97" | |||||
"\x0\x6d\x99\x0\x71\x9b\x0\x75\x9d\x0\x79\x9f\x0\x7e\xa0\x0\x82\xa2\x0\x87\xa4" | |||||
"\x0\x8c\xa6\x0\x92\xaa\x0\x96\xac\x0\x9c\xae\x0\xa2\xb1\x0\xa6\xb3\x0\xaa\xb5" | |||||
"\x0\xad\xb7\x0\xb1\xba\x0\xb6\xbe\x0\xba\xc0\x0\xbe\xc2\x0\xc2\xc5\x0\xc6\xc6" | |||||
"\x0\xca\xc9\x0\xcc\xca\x0\xcf\xcb\x0\xd2\xcc\x0\xd4\xcc\x0\xd6\xcc\x0\xd9\xcb" | |||||
"\x0\xdb\xcb\x0\xde\xcb\x0\xe0\xcb\x0\xe2\xcc\x0\xea\xd2\x0\xea\xcf\x0\xb9\xa4" | |||||
"\x0\x8e\x7a\x1\x94\x7c\x4\x97\x79\x7\x99\x75\x9\x9b\x71\xd\x9d\x6b\x10\x9f\x67" | |||||
"\x12\xa1\x63\x15\xa3\x5f\x17\xa5\x59\x1a\xa8\x55\x1d\xaa\x50\x20\xac\x4b\x24\xaf\x45" | |||||
"\x28\xb2\x41\x2b\xb5\x3b\x2e\xb8\x35\x31\xba\x30\x34\xbd\x2b\x39\xbf\x24\x3f\xc1\x17" | |||||
"\x49\xc5\x8\x4f\xc8\x1\x4f\xca\x0\x4e\xcd\x0\x4e\xcf\x0\x4f\xd2\x0\x54\xd5\x0" | |||||
"\x5d\xd8\x0\x68\xdb\x0\x6e\xdd\x0\x74\xdf\x0\x7a\xe2\x0\x7f\xe4\x0\x85\xe7\x0" | |||||
"\x8b\xe9\x0\x8f\xeb\x0\x9b\xf3\x0\x9e\xf2\x0\x7e\xbb\x0\x60\x8a\x0\x68\x92\x0" | |||||
"\x6d\x95\x0\x71\x96\x0\x75\x98\x0\x7b\x9a\x0\x7f\x9d\x0\x83\x9f\x0\x87\xa1\x0" | |||||
"\x8c\xa2\x0\x8f\xa5\x0\x92\xa7\x0\x96\xa9\x0\x9a\xad\x0\x9d\xb0\x0\xa1\xb2\x0" | |||||
"\xa5\xb5\x0\xa9\xb7\x0\xad\xba\x0\xb2\xbd\x0\xb6\xbf\x0\xbb\xc3\x0\xbf\xc6\x0" | |||||
"\xc3\xc8\x0\xc8\xcb\x0\xcc\xce\x0\xd0\xd1\x0\xd3\xd2\x0\xd5\xd4\x0\xd9\xd4\x0" | |||||
"\xdc\xd4\x0\xde\xd5\x0\xe1\xd5\x0\xe3\xd5\x0\xe6\xd4\x0\xe8\xd1\x0\xea\xce\x0" | |||||
"\xf2\xcf\x0\xf2\xca\x0\xbb\x99\x0\x8a\x6e\x0\x92\x72\x0\x95\x72\x0\x97\x71\x0" | |||||
"\x9a\x70\x0\x9c\x6e\x0\x9e\x6d\x0\xa0\x6b\x0\xa3\x6a\x0\xa5\x68\x0\xa8\x67\x0" | |||||
"\xab\x66\x0\xae\x65\x0\xb2\x63\x0\xb4\x61\x0\xb7\x5f\x0\xba\x5d\x0\xbd\x5c\x0" | |||||
"\xc0\x59\x0\xc3\x57\x0\xc6\x54\x0\xca\x50\x0\xcd\x4d\x0\xd0\x4a\x0\xd3\x47\x0" | |||||
"\xd6\x43\x0\xd9\x40\x0\xdc\x3d\x0\xde\x39\x0\xe2\x33\x0\xe5\x2f\x0\xe7\x2c\x0" | |||||
"\xea\x28\x0\xec\x23\x0\xef\x1f\x0\xf1\x1a\x0\xf3\x14\x0\xfb\xf\x0\xfa\xd\x0" | |||||
"\xc1\x5\x0\x8e\x0\x0\x97\x0\x0\x9b\x0\x0\x9e\x0\x0\xa1\x0\x0\xa5\x0\x0" | |||||
"\xa9\x0\x0\xad\x0\x0\xb1\x0\x0\xb6\x0\x0\xba\x0\x0\xbd\x0\x0\xc2\x0\x0" | |||||
"\xc8\x0\x0\xcc\x0\x0\xcc\x0\x0" | |||||
}; | |||||
char apt_TempPalette[256 * 3] = { | |||||
"\x45\x0\x8f\x46\x0\x91\x47\x0\x92\x48\x0\x94\x49\x0\x96\x4a\x0\x98\x4b\x0\x9b\x4d\x0\x9d" | |||||
"\x4e\x0\xa0\x50\x0\xa2\x51\x0\xa5\x52\x0\xa7\x54\x0\xaa\x56\x0\xae\x57\x0\xb1" | |||||
"\x58\x0\xb4\x5a\x0\xb7\x5c\x0\xba\x5e\x0\xbd\x5f\x0\xc0\x61\x0\xc4\x64\x0\xc8" | |||||
"\x66\x0\xcb\x68\x0\xce\x69\x0\xd1\x68\x0\xd4\x65\x0\xd7\x63\x0\xda\x61\x0\xdd" | |||||
"\x5d\x0\xe1\x5b\x0\xe4\x59\x0\xe6\x56\x0\xe9\x53\x0\xeb\x50\x0\xee\x4d\x0\xf0" | |||||
"\x49\x0\xf3\x47\x0\xfc\x43\x0\xfa\x31\x0\xbf\x20\x0\x89\x20\x0\x92\x1e\x0\x95" | |||||
"\x1b\x0\x97\x19\x0\x9a\x17\x0\x9c\x15\x0\x9e\x12\x0\xa0\xf\x0\xa3\xf\x2\xa5" | |||||
"\xe\x6\xa8\xe\xa\xab\xe\xd\xad\xe\x11\xb1\xd\x15\xb4\xd\x18\xb7\xd\x1c\xba" | |||||
"\xb\x21\xbd\xa\x25\xc0\xa\x29\xc3\x9\x2d\xc6\x8\x33\xca\x7\x36\xcd\x7\x3b\xd0" | |||||
"\x7\x41\xd3\x5\x45\xd6\x4\x4b\xd9\x4\x50\xdc\x3\x55\xde\x2\x5d\xe2\x1\x61\xe5" | |||||
"\x0\x66\xe7\x0\x6c\xea\x0\x72\xec\x0\x78\xee\x0\x7d\xf0\x0\x82\xf3\x0\x8d\xfc" | |||||
"\x0\x90\xfa\x0\x71\xbf\x0\x54\x89\x0\x5c\x91\x0\x61\x94\x0\x64\x96\x0\x68\x97" | |||||
"\x0\x6d\x99\x0\x71\x9b\x0\x75\x9d\x0\x79\x9f\x0\x7e\xa0\x0\x82\xa2\x0\x87\xa4" | |||||
"\x0\x8c\xa6\x0\x92\xaa\x0\x96\xac\x0\x9c\xae\x0\xa2\xb1\x0\xa6\xb3\x0\xaa\xb5" | |||||
"\x0\xad\xb7\x0\xb1\xba\x0\xb6\xbe\x0\xba\xc0\x0\xbe\xc2\x0\xc2\xc5\x0\xc6\xc6" | |||||
"\x0\xca\xc9\x0\xcc\xca\x0\xcf\xcb\x0\xd2\xcc\x0\xd4\xcc\x0\xd6\xcc\x0\xd9\xcb" | |||||
"\x0\xdb\xcb\x0\xde\xcb\x0\xe0\xcb\x0\xe2\xcc\x0\xea\xd2\x0\xea\xcf\x0\xb9\xa4" | |||||
"\x0\x8e\x7a\x1\x94\x7c\x4\x97\x79\x7\x99\x75\x9\x9b\x71\xd\x9d\x6b\x10\x9f\x67" | |||||
"\x12\xa1\x63\x15\xa3\x5f\x17\xa5\x59\x1a\xa8\x55\x1d\xaa\x50\x20\xac\x4b\x24\xaf\x45" | |||||
"\x28\xb2\x41\x2b\xb5\x3b\x2e\xb8\x35\x31\xba\x30\x34\xbd\x2b\x39\xbf\x24\x3f\xc1\x17" | |||||
"\x49\xc5\x8\x4f\xc8\x1\x4f\xca\x0\x4e\xcd\x0\x4e\xcf\x0\x4f\xd2\x0\x54\xd5\x0" | |||||
"\x5d\xd8\x0\x68\xdb\x0\x6e\xdd\x0\x74\xdf\x0\x7a\xe2\x0\x7f\xe4\x0\x85\xe7\x0" | |||||
"\x8b\xe9\x0\x8f\xeb\x0\x9b\xf3\x0\x9e\xf2\x0\x7e\xbb\x0\x60\x8a\x0\x68\x92\x0" | |||||
"\x6d\x95\x0\x71\x96\x0\x75\x98\x0\x7b\x9a\x0\x7f\x9d\x0\x83\x9f\x0\x87\xa1\x0" | |||||
"\x8c\xa2\x0\x8f\xa5\x0\x92\xa7\x0\x96\xa9\x0\x9a\xad\x0\x9d\xb0\x0\xa1\xb2\x0" | |||||
"\xa5\xb5\x0\xa9\xb7\x0\xad\xba\x0\xb2\xbd\x0\xb6\xbf\x0\xbb\xc3\x0\xbf\xc6\x0" | |||||
"\xc3\xc8\x0\xc8\xcb\x0\xcc\xce\x0\xd0\xd1\x0\xd3\xd2\x0\xd5\xd4\x0\xd9\xd4\x0" | |||||
"\xdc\xd4\x0\xde\xd5\x0\xe1\xd5\x0\xe3\xd5\x0\xe6\xd4\x0\xe8\xd1\x0\xea\xce\x0" | |||||
"\xf2\xcf\x0\xf2\xca\x0\xbb\x99\x0\x8a\x6e\x0\x92\x72\x0\x95\x72\x0\x97\x71\x0" | |||||
"\x9a\x70\x0\x9c\x6e\x0\x9e\x6d\x0\xa0\x6b\x0\xa3\x6a\x0\xa5\x68\x0\xa8\x67\x0" | |||||
"\xab\x66\x0\xae\x65\x0\xb2\x63\x0\xb4\x61\x0\xb7\x5f\x0\xba\x5d\x0\xbd\x5c\x0" | |||||
"\xc0\x59\x0\xc3\x57\x0\xc6\x54\x0\xca\x50\x0\xcd\x4d\x0\xd0\x4a\x0\xd3\x47\x0" | |||||
"\xd6\x43\x0\xd9\x40\x0\xdc\x3d\x0\xde\x39\x0\xe2\x33\x0\xe5\x2f\x0\xe7\x2c\x0" | |||||
"\xea\x28\x0\xec\x23\x0\xef\x1f\x0\xf1\x1a\x0\xf3\x14\x0\xfb\xf\x0\xfa\xd\x0" | |||||
"\xc1\x5\x0\x8e\x0\x0\x97\x0\x0\x9b\x0\x0\x9e\x0\x0\xa1\x0\x0\xa5\x0\x0" | |||||
"\xa9\x0\x0\xad\x0\x0\xb1\x0\x0\xb6\x0\x0\xba\x0\x0\xbd\x0\x0\xc2\x0\x0" | |||||
"\xc8\x0\x0\xcc\x0\x0\xcc\x0\x0"}; | |||||
char apt_PrecipPalette[58*3] = { | |||||
char apt_PrecipPalette[58 * 3] = { | |||||
"\x8\x89\x41\x0\xc5\x44\x0\xd1\x2c\x0\xe3\x1c\x0\xf9\x6\x14\xff\x0\x3e\xff\x0\x5d\xff\x0" | "\x8\x89\x41\x0\xc5\x44\x0\xd1\x2c\x0\xe3\x1c\x0\xf9\x6\x14\xff\x0\x3e\xff\x0\x5d\xff\x0" | ||||
"\x80\xff\x0\xab\xff\x0\xcd\xfe\x0\xf8\xff\x0\xff\xe6\x0\xff\xb8\x0\xff\x98\x0" | "\x80\xff\x0\xab\xff\x0\xcd\xfe\x0\xf8\xff\x0\xff\xe6\x0\xff\xb8\x0\xff\x98\x0" | ||||
"\xff\x75\x0\xff\x49\x0\xfe\x26\x0\xff\x4\x0\xdf\x0\x0\xa8\x0\x0\x87\x0\x0" | "\xff\x75\x0\xff\x49\x0\xfe\x26\x0\xff\x4\x0\xdf\x0\x0\xa8\x0\x0\x87\x0\x0" | ||||
@@ -86,5 +80,4 @@ char apt_PrecipPalette[58*3] = { | |||||
"\xc0\xc0\xc0\xce\xce\xce\xdc\xdc\xdc\xef\xef\xef\xfa\xfa\xfa\xff\xff\xff\xff\xff\xff" | "\xc0\xc0\xc0\xce\xce\xce\xdc\xdc\xdc\xef\xef\xef\xfa\xfa\xfa\xff\xff\xff\xff\xff\xff" | ||||
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" | "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" | ||||
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" | "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" | ||||
"\xff\xff\xff" | |||||
}; | |||||
"\xff\xff\xff"}; |
@@ -1,3 +1,3 @@ | |||||
#include "common.h" | #include "common.h" | ||||
#define MCOMPOSITE(m1, a1, m2, a2) (m1*a1 + m2*a2*(1-a1)) | |||||
#define MCOMPOSITE(m1, a1, m2, a2) (m1 * a1 + m2 * a2 * (1 - a1)) |
@@ -1,4 +1,4 @@ | |||||
/* | |||||
/* | |||||
* This file is part of Aptdec. | * This file is part of Aptdec. | ||||
* Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019-2022 | * Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019-2022 | ||||
* | * | ||||
@@ -22,39 +22,39 @@ | |||||
// Useful macros | // Useful macros | ||||
#define CLIP(v, lo, hi) (v > hi ? hi : (v > lo ? v : lo)) | #define CLIP(v, lo, hi) (v > hi ? hi : (v > lo ? v : lo)) | ||||
#define CONTAINS(str, char) (strchr(str, (int) char) != NULL) | |||||
#define CONTAINS(str, char) (strchr(str, (int)char) != NULL) | |||||
// Typedefs | // Typedefs | ||||
#ifndef STRUCTS_DEFINED | #ifndef STRUCTS_DEFINED | ||||
#define STRUCTS_DEFINED | #define STRUCTS_DEFINED | ||||
typedef struct { | typedef struct { | ||||
char *type; // Output image type | |||||
char *effects; // Effects on the image | |||||
int satnum; // The satellite number | |||||
char *path; // Output directory | |||||
int realtime; // Realtime decoding | |||||
char *filename; // Output filename | |||||
char *palette; // Filename of palette | |||||
float gamma; // Gamma | |||||
char *type; // Output image type | |||||
char *effects; // Effects on the image | |||||
int satnum; // The satellite number | |||||
char *path; // Output directory | |||||
int realtime; // Realtime decoding | |||||
char *filename; // Output filename | |||||
char *palette; // Filename of palette | |||||
float gamma; // Gamma | |||||
} options_t; | } options_t; | ||||
enum imagetypes { | enum imagetypes { | ||||
Raw_Image='r', | |||||
Palleted='p', | |||||
Temperature='t', | |||||
Channel_A='a', | |||||
Channel_B='b', | |||||
Distribution='d', | |||||
Visible='v' | |||||
Raw_Image = 'r', | |||||
Palleted = 'p', | |||||
Temperature = 't', | |||||
Channel_A = 'a', | |||||
Channel_B = 'b', | |||||
Distribution = 'd', | |||||
Visible = 'v' | |||||
}; | }; | ||||
enum effects { | enum effects { | ||||
Crop_Telemetry='t', | |||||
Histogram_Equalise='h', | |||||
Denoise='d', | |||||
Precipitation_Overlay='p', | |||||
Flip_Image='f', | |||||
Linear_Equalise='l', | |||||
Crop_Noise='c' | |||||
Crop_Telemetry = 't', | |||||
Histogram_Equalise = 'h', | |||||
Denoise = 'd', | |||||
Precipitation_Overlay = 'p', | |||||
Flip_Image = 'f', | |||||
Linear_Equalise = 'l', | |||||
Crop_Noise = 'c' | |||||
}; | }; | ||||
#endif | #endif |
@@ -16,10 +16,10 @@ | |||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | * along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
*/ | */ | ||||
#include <stdlib.h> | |||||
#include <math.h> | |||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdlib.h> | |||||
#include <string.h> | #include <string.h> | ||||
#include <math.h> | |||||
#include "apt.h" | #include "apt.h" | ||||
#include "filter.h" | #include "filter.h" | ||||
@@ -34,7 +34,7 @@ | |||||
#define MAX_CARRIER_OFFSET 20.0 | #define MAX_CARRIER_OFFSET 20.0 | ||||
#define RSMULT 15 | #define RSMULT 15 | ||||
#define Fi (APT_IMG_WIDTH*2 * RSMULT) | |||||
#define Fi (APT_IMG_WIDTH * 2 * RSMULT) | |||||
static float _sample_rate; | static float _sample_rate; | ||||
@@ -47,218 +47,212 @@ static float pll_beta; | |||||
// Initalise and configure PLL | // Initalise and configure PLL | ||||
int apt_init(double sample_rate) { | int apt_init(double sample_rate) { | ||||
if(sample_rate > Fi) return 1; | |||||
if(sample_rate < APT_IMG_WIDTH*2) return -1; | |||||
_sample_rate = sample_rate; | |||||
if (sample_rate > Fi) return 1; | |||||
if (sample_rate < APT_IMG_WIDTH * 2) return -1; | |||||
_sample_rate = sample_rate; | |||||
// Pll configuration | |||||
pll_alpha = 50 / _sample_rate; | |||||
pll_beta = pll_alpha * pll_alpha / 2.0; | |||||
oscillator_freq = CARRIER_FREQ/sample_rate; | |||||
// Pll configuration | |||||
pll_alpha = 50 / _sample_rate; | |||||
pll_beta = pll_alpha * pll_alpha / 2.0; | |||||
oscillator_freq = CARRIER_FREQ / sample_rate; | |||||
return 0; | |||||
return 0; | |||||
} | } | ||||
static float pll(complexf_t in) { | static float pll(complexf_t in) { | ||||
static float oscillator_phase = 0.0; | |||||
static float oscillator_phase = 0.0; | |||||
// Internal oscillator | |||||
// Internal oscillator | |||||
#ifdef _MSC_VER | #ifdef _MSC_VER | ||||
complexf_t osc = _FCbuild(cos(oscillator_phase), -sin(oscillator_phase)); | |||||
complexf_t osc = _FCbuild(cos(oscillator_phase), -sin(oscillator_phase)); | |||||
in = _FCmulcc(in, osc); | in = _FCmulcc(in, osc); | ||||
#else | #else | ||||
complexf_t osc = cos(oscillator_phase) + -sin(oscillator_phase)*I; | |||||
complexf_t osc = cos(oscillator_phase) + -sin(oscillator_phase) * I; | |||||
in *= osc; | in *= osc; | ||||
#endif | #endif | ||||
// Error detector | |||||
float error = cargf(in); | |||||
// Error detector | |||||
float error = cargf(in); | |||||
// Adjust frequency and phase | |||||
oscillator_freq += pll_beta*error; | |||||
oscillator_freq = clamp_half(oscillator_freq, (CARRIER_FREQ + MAX_CARRIER_OFFSET) / _sample_rate); | |||||
oscillator_phase += M_TAUf * (pll_alpha*error + oscillator_freq); | |||||
oscillator_phase = remainderf(oscillator_phase, M_TAUf); | |||||
// Adjust frequency and phase | |||||
oscillator_freq += pll_beta * error; | |||||
oscillator_freq = clamp_half(oscillator_freq, (CARRIER_FREQ + MAX_CARRIER_OFFSET) / _sample_rate); | |||||
oscillator_phase += M_TAUf * (pll_alpha * error + oscillator_freq); | |||||
oscillator_phase = remainderf(oscillator_phase, M_TAUf); | |||||
return crealf(in); | |||||
return crealf(in); | |||||
} | } | ||||
// Convert samples into pixels | // Convert samples into pixels | ||||
static int getamp(float *ampbuff, int count, apt_getsamples_t getsamples, void *context) { | static int getamp(float *ampbuff, int count, apt_getsamples_t getsamples, void *context) { | ||||
static float inbuff[BLKIN]; | |||||
static int idxin = 0; | |||||
static int nin = 0; | |||||
for (int n = 0; n < count; n++) { | |||||
float I2, Q; | |||||
// Get some more samples when needed | |||||
if (nin < HILBERT_FILTER_SIZE * 2 + 2) { | |||||
// Number of samples read | |||||
int res; | |||||
memmove(inbuff, &(inbuff[idxin]), nin * sizeof(float)); | |||||
idxin = 0; | |||||
// Read some samples | |||||
res = getsamples(context, &(inbuff[nin]), BLKIN - nin); | |||||
nin += res; | |||||
// Make sure there is enough samples to continue | |||||
if (nin < HILBERT_FILTER_SIZE * 2 + 2) | |||||
return n; | |||||
} | |||||
// Process read samples into a brightness value | |||||
complexf_t sample = hilbert_transform(&inbuff[idxin], hilbert_filter, HILBERT_FILTER_SIZE); | |||||
ampbuff[n] = pll(sample); | |||||
// Increment current sample | |||||
idxin++; | |||||
nin--; | |||||
} | |||||
return count; | |||||
static float inbuff[BLKIN]; | |||||
static int idxin = 0; | |||||
static int nin = 0; | |||||
for (int n = 0; n < count; n++) { | |||||
float I2, Q; | |||||
// Get some more samples when needed | |||||
if (nin < HILBERT_FILTER_SIZE * 2 + 2) { | |||||
// Number of samples read | |||||
int res; | |||||
memmove(inbuff, &(inbuff[idxin]), nin * sizeof(float)); | |||||
idxin = 0; | |||||
// Read some samples | |||||
res = getsamples(context, &(inbuff[nin]), BLKIN - nin); | |||||
nin += res; | |||||
// Make sure there is enough samples to continue | |||||
if (nin < HILBERT_FILTER_SIZE * 2 + 2) return n; | |||||
} | |||||
// Process read samples into a brightness value | |||||
complexf_t sample = hilbert_transform(&inbuff[idxin], hilbert_filter, HILBERT_FILTER_SIZE); | |||||
ampbuff[n] = pll(sample); | |||||
// Increment current sample | |||||
idxin++; | |||||
nin--; | |||||
} | |||||
return count; | |||||
} | } | ||||
// Sub-pixel offsetting | |||||
// Sub-pixel offsetting | |||||
int getpixelv(float *pvbuff, int count, apt_getsamples_t getsamples, void *context) { | int getpixelv(float *pvbuff, int count, apt_getsamples_t getsamples, void *context) { | ||||
// Amplitude buffer | |||||
static float ampbuff[BLKAMP]; | |||||
static int nam = 0; | |||||
static int idxam = 0; | |||||
// Amplitude buffer | |||||
static float ampbuff[BLKAMP]; | |||||
static int nam = 0; | |||||
static int idxam = 0; | |||||
float mult; | |||||
float mult; | |||||
// Gaussian resampling factor | |||||
mult = (float) Fi / _sample_rate * FreqLine; | |||||
int m = (int)(LOW_PASS_SIZE / mult + 1); | |||||
// Gaussian resampling factor | |||||
mult = (float)Fi / _sample_rate * FreqLine; | |||||
int m = (int)(LOW_PASS_SIZE / mult + 1); | |||||
for (int n = 0; n < count; n++) { | |||||
int shift; | |||||
for (int n = 0; n < count; n++) { | |||||
int shift; | |||||
if (nam < m) { | |||||
int res; | |||||
memmove(ampbuff, &(ampbuff[idxam]), nam * sizeof(float)); | |||||
idxam = 0; | |||||
res = getamp(&(ampbuff[nam]), BLKAMP - nam, getsamples, context); | |||||
nam += res; | |||||
if (nam < m) | |||||
return n; | |||||
} | |||||
if (nam < m) { | |||||
int res; | |||||
memmove(ampbuff, &(ampbuff[idxam]), nam * sizeof(float)); | |||||
idxam = 0; | |||||
res = getamp(&(ampbuff[nam]), BLKAMP - nam, getsamples, context); | |||||
nam += res; | |||||
if (nam < m) return n; | |||||
} | |||||
pvbuff[n] = interpolating_convolve(&(ampbuff[idxam]), low_pass, LOW_PASS_SIZE, offset, mult) * mult * 256.0; | |||||
pvbuff[n] = interpolating_convolve(&(ampbuff[idxam]), low_pass, LOW_PASS_SIZE, offset, mult) * mult * 256.0; | |||||
shift = ((int) floor((RSMULT - offset) / mult)) + 1; | |||||
offset = shift * mult + offset - RSMULT; | |||||
shift = ((int)floor((RSMULT - offset) / mult)) + 1; | |||||
offset = shift * mult + offset - RSMULT; | |||||
idxam += shift; | |||||
nam -= shift; | |||||
} | |||||
idxam += shift; | |||||
nam -= shift; | |||||
} | |||||
return count; | |||||
return count; | |||||
} | } | ||||
// Get an entire row of pixels, aligned with sync markers | // Get an entire row of pixels, aligned with sync markers | ||||
int apt_getpixelrow(float *pixelv, int nrow, int *zenith, int reset, apt_getsamples_t getsamples, void *context) { | int apt_getpixelrow(float *pixelv, int nrow, int *zenith, int reset, apt_getsamples_t getsamples, void *context) { | ||||
static float pixels[APT_IMG_WIDTH + SYNC_PATTERN_SIZE]; | |||||
static size_t npv; | |||||
static int synced = 0; | |||||
static float max = 0.0; | |||||
static float minDoppler = 1000000000, previous = 0; | |||||
if(reset) synced = 0; | |||||
float corr, ecorr, lcorr; | |||||
int res; | |||||
// Move the row buffer into the the image buffer | |||||
if (npv > 0) | |||||
memmove(pixelv, pixels, npv * sizeof(float)); | |||||
// Get the sync line | |||||
if (npv < SYNC_PATTERN_SIZE + 2) { | |||||
res = getpixelv(&(pixelv[npv]), SYNC_PATTERN_SIZE + 2 - npv, getsamples, context); | |||||
npv += res; | |||||
if (npv < SYNC_PATTERN_SIZE + 2) | |||||
return 0; | |||||
} | |||||
// Calculate the frequency offset | |||||
ecorr = convolve(pixelv, sync_pattern, SYNC_PATTERN_SIZE); | |||||
corr = convolve(&pixelv[1], sync_pattern, SYNC_PATTERN_SIZE - 1); | |||||
lcorr = convolve(&pixelv[2], sync_pattern, SYNC_PATTERN_SIZE - 2); | |||||
FreqLine = 1.0+((ecorr-lcorr) / corr / APT_IMG_WIDTH / 4.0); | |||||
float val = fabs(lcorr - ecorr)*0.25 + previous*0.75; | |||||
if(val < minDoppler && nrow > 10){ | |||||
minDoppler = val; | |||||
*zenith = nrow; | |||||
} | |||||
previous = fabs(lcorr - ecorr); | |||||
// The point in which the pixel offset is recalculated | |||||
if (corr < 0.75 * max) { | |||||
synced = 0; | |||||
FreqLine = 1.0; | |||||
} | |||||
max = corr; | |||||
if (synced < 8) { | |||||
int mshift; | |||||
static float pixels[APT_IMG_WIDTH + SYNC_PATTERN_SIZE]; | |||||
static size_t npv; | |||||
static int synced = 0; | |||||
static float max = 0.0; | |||||
static float minDoppler = 1000000000, previous = 0; | |||||
if (reset) synced = 0; | |||||
float corr, ecorr, lcorr; | |||||
int res; | |||||
// Move the row buffer into the the image buffer | |||||
if (npv > 0) memmove(pixelv, pixels, npv * sizeof(float)); | |||||
// Get the sync line | |||||
if (npv < SYNC_PATTERN_SIZE + 2) { | |||||
res = getpixelv(&(pixelv[npv]), SYNC_PATTERN_SIZE + 2 - npv, getsamples, context); | |||||
npv += res; | |||||
if (npv < SYNC_PATTERN_SIZE + 2) return 0; | |||||
} | |||||
// Calculate the frequency offset | |||||
ecorr = convolve(pixelv, sync_pattern, SYNC_PATTERN_SIZE); | |||||
corr = convolve(&pixelv[1], sync_pattern, SYNC_PATTERN_SIZE - 1); | |||||
lcorr = convolve(&pixelv[2], sync_pattern, SYNC_PATTERN_SIZE - 2); | |||||
FreqLine = 1.0 + ((ecorr - lcorr) / corr / APT_IMG_WIDTH / 4.0); | |||||
float val = fabs(lcorr - ecorr) * 0.25 + previous * 0.75; | |||||
if (val < minDoppler && nrow > 10) { | |||||
minDoppler = val; | |||||
*zenith = nrow; | |||||
} | |||||
previous = fabs(lcorr - ecorr); | |||||
// The point in which the pixel offset is recalculated | |||||
if (corr < 0.75 * max) { | |||||
synced = 0; | |||||
FreqLine = 1.0; | |||||
} | |||||
max = corr; | |||||
if (synced < 8) { | |||||
int mshift; | |||||
static int lastmshift; | static int lastmshift; | ||||
if (npv < APT_IMG_WIDTH + SYNC_PATTERN_SIZE) { | |||||
res = getpixelv(&(pixelv[npv]), APT_IMG_WIDTH + SYNC_PATTERN_SIZE - npv, getsamples, context); | |||||
npv += res; | |||||
if (npv < APT_IMG_WIDTH + SYNC_PATTERN_SIZE) | |||||
return 0; | |||||
} | |||||
// Test every possible position until we get the best result | |||||
mshift = 0; | |||||
for (int shift = 0; shift < APT_IMG_WIDTH; shift++) { | |||||
float corr; | |||||
corr = convolve(&(pixelv[shift + 1]), sync_pattern, SYNC_PATTERN_SIZE); | |||||
if (corr > max) { | |||||
mshift = shift; | |||||
max = corr; | |||||
} | |||||
} | |||||
// Stop rows dissapearing into the void | |||||
int mshiftOrig = mshift; | |||||
if(abs(lastmshift-mshift) > 3 && nrow != 0){ | |||||
mshift = 0; | |||||
} | |||||
lastmshift = mshiftOrig; | |||||
// If we are already as aligned as we can get, just continue | |||||
if (mshift == 0) { | |||||
synced++; | |||||
} else { | |||||
memmove(pixelv, &(pixelv[mshift]), (npv - mshift) * sizeof(float)); | |||||
npv -= mshift; | |||||
synced = 0; | |||||
FreqLine = 1.0; | |||||
} | |||||
} | |||||
// Get the rest of this row | |||||
if (npv < APT_IMG_WIDTH) { | |||||
res = getpixelv(&(pixelv[npv]), APT_IMG_WIDTH - npv, getsamples, context); | |||||
npv += res; | |||||
if (npv < APT_IMG_WIDTH) | |||||
return 0; | |||||
} | |||||
// Move the sync lines into the output buffer with the calculated offset | |||||
if (npv == APT_IMG_WIDTH) { | |||||
npv = 0; | |||||
} else { | |||||
memmove(pixels, &(pixelv[APT_IMG_WIDTH]), (npv - APT_IMG_WIDTH) * sizeof(float)); | |||||
npv -= APT_IMG_WIDTH; | |||||
} | |||||
return 1; | |||||
if (npv < APT_IMG_WIDTH + SYNC_PATTERN_SIZE) { | |||||
res = getpixelv(&(pixelv[npv]), APT_IMG_WIDTH + SYNC_PATTERN_SIZE - npv, getsamples, context); | |||||
npv += res; | |||||
if (npv < APT_IMG_WIDTH + SYNC_PATTERN_SIZE) return 0; | |||||
} | |||||
// Test every possible position until we get the best result | |||||
mshift = 0; | |||||
for (int shift = 0; shift < APT_IMG_WIDTH; shift++) { | |||||
float corr; | |||||
corr = convolve(&(pixelv[shift + 1]), sync_pattern, SYNC_PATTERN_SIZE); | |||||
if (corr > max) { | |||||
mshift = shift; | |||||
max = corr; | |||||
} | |||||
} | |||||
// Stop rows dissapearing into the void | |||||
int mshiftOrig = mshift; | |||||
if (abs(lastmshift - mshift) > 3 && nrow != 0) { | |||||
mshift = 0; | |||||
} | |||||
lastmshift = mshiftOrig; | |||||
// If we are already as aligned as we can get, just continue | |||||
if (mshift == 0) { | |||||
synced++; | |||||
} else { | |||||
memmove(pixelv, &(pixelv[mshift]), (npv - mshift) * sizeof(float)); | |||||
npv -= mshift; | |||||
synced = 0; | |||||
FreqLine = 1.0; | |||||
} | |||||
} | |||||
// Get the rest of this row | |||||
if (npv < APT_IMG_WIDTH) { | |||||
res = getpixelv(&(pixelv[npv]), APT_IMG_WIDTH - npv, getsamples, context); | |||||
npv += res; | |||||
if (npv < APT_IMG_WIDTH) return 0; | |||||
} | |||||
// Move the sync lines into the output buffer with the calculated offset | |||||
if (npv == APT_IMG_WIDTH) { | |||||
npv = 0; | |||||
} else { | |||||
memmove(pixels, &(pixelv[APT_IMG_WIDTH]), (npv - APT_IMG_WIDTH) * sizeof(float)); | |||||
npv -= APT_IMG_WIDTH; | |||||
} | |||||
return 1; | |||||
} | } |
@@ -16,45 +16,47 @@ | |||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | * along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
*/ | */ | ||||
#include <math.h> | |||||
#include "filter.h" | #include "filter.h" | ||||
#include <math.h> | |||||
#include "util.h" | #include "util.h" | ||||
float convolve(const float *in, const float *taps, size_t len) { | float convolve(const float *in, const float *taps, size_t len) { | ||||
float sum = 0.0; | |||||
for (size_t i = 0; i < len; i++) { | |||||
sum += in[i] * taps[i]; | |||||
} | |||||
float sum = 0.0; | |||||
for (size_t i = 0; i < len; i++) { | |||||
sum += in[i] * taps[i]; | |||||
} | |||||
return sum; | |||||
return sum; | |||||
} | } | ||||
complexf_t hilbert_transform(const float *in, const float *taps, size_t len) { | complexf_t hilbert_transform(const float *in, const float *taps, size_t len) { | ||||
float i = 0.0; | |||||
float q = 0.0; | |||||
float i = 0.0; | |||||
float q = 0.0; | |||||
for (size_t k = 0; k < len; k++) { | |||||
q += in[2*k] * taps[k]; | |||||
i += in[2*k]; | |||||
} | |||||
for (size_t k = 0; k < len; k++) { | |||||
q += in[2 * k] * taps[k]; | |||||
i += in[2 * k]; | |||||
} | |||||
i = in[len-1] - (i / len); | |||||
i = in[len - 1] - (i / len); | |||||
#ifdef _MSC_VER | #ifdef _MSC_VER | ||||
return _FCbuild(i, q); | |||||
return _FCbuild(i, q); | |||||
#else | #else | ||||
return i + q*I; | |||||
return i + q * I; | |||||
#endif | #endif | ||||
} | } | ||||
float interpolating_convolve(const float *in, const float *taps, size_t len, float offset, float delta) { | float interpolating_convolve(const float *in, const float *taps, size_t len, float offset, float delta) { | ||||
float out = 0.0; | |||||
float n = offset; | |||||
float out = 0.0; | |||||
float n = offset; | |||||
for (size_t i = 0; i < (len-1)/delta-1; n += delta, i++) { | |||||
int k = (int)floor(n); | |||||
float alpha = n - k; | |||||
for (size_t i = 0; i < (len - 1) / delta - 1; n += delta, i++) { | |||||
int k = (int)floor(n); | |||||
float alpha = n - k; | |||||
out += in[i] * (taps[k] * (1.0f-alpha) + taps[k + 1] * alpha); | |||||
} | |||||
return out; | |||||
out += in[i] * (taps[k] * (1.0f - alpha) + taps[k + 1] * alpha); | |||||
} | |||||
return out; | |||||
} | } |
@@ -16,8 +16,8 @@ | |||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | * along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
*/ | */ | ||||
#include <stddef.h> | |||||
#include <complex.h> | #include <complex.h> | ||||
#include <stddef.h> | |||||
#ifdef _MSC_VER | #ifdef _MSC_VER | ||||
typedef _Fcomplex complexf_t; | typedef _Fcomplex complexf_t; | ||||
@@ -25,6 +25,6 @@ typedef _Fcomplex complexf_t; | |||||
typedef complex float complexf_t; | typedef complex float complexf_t; | ||||
#endif | #endif | ||||
float convolve(const float *in, const float *taps, size_t len); | |||||
float convolve(const float *in, const float *taps, size_t len); | |||||
complexf_t hilbert_transform(const float *in, const float *taps, size_t len); | complexf_t hilbert_transform(const float *in, const float *taps, size_t len); | ||||
float interpolating_convolve(const float *in, const float *taps, size_t len, float offset, float delta); | |||||
float interpolating_convolve(const float *in, const float *taps, size_t len, float offset, float delta); |
@@ -1,4 +1,4 @@ | |||||
/* | |||||
/* | |||||
* This file is part of Aptdec. | * This file is part of Aptdec. | ||||
* Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019-2022 | * Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019-2022 | ||||
* | * | ||||
@@ -17,382 +17,372 @@ | |||||
* | * | ||||
*/ | */ | ||||
#include <stdio.h> | |||||
#include <string.h> | |||||
#include "image.h" | |||||
#include <math.h> | #include <math.h> | ||||
#include <stdio.h> | |||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <string.h> | |||||
#include "apt.h" | |||||
#include "algebra.h" | #include "algebra.h" | ||||
#include "image.h" | |||||
#include "apt.h" | |||||
#include "util.h" | #include "util.h" | ||||
static linear_t compute_regression(float *wedges) { | static linear_t compute_regression(float *wedges) { | ||||
// { 0.106, 0.215, 0.324, 0.433, 0.542, 0.652, 0.78, 0.87, 0.0 } | |||||
const float teleramp[9] = { 31.07, 63.02, 94.96, 126.9, 158.86, 191.1, 228.62, 255.0, 0.0 }; | |||||
// { 0.106, 0.215, 0.324, 0.433, 0.542, 0.652, 0.78, 0.87, 0.0 } | |||||
const float teleramp[9] = {31.07, 63.02, 94.96, 126.9, 158.86, 191.1, 228.62, 255.0, 0.0}; | |||||
return linear_regression(wedges, teleramp, 9); | |||||
return linear_regression(wedges, teleramp, 9); | |||||
} | } | ||||
static float tele[16]; | static float tele[16]; | ||||
static float Cs; | static float Cs; | ||||
void apt_histogramEqualise(float **prow, int nrow, int offset, int width){ | |||||
// Plot histogram | |||||
int histogram[256] = { 0 }; | |||||
for(int y = 0; y < nrow; y++) | |||||
for(int x = 0; x < width; x++) | |||||
histogram[(int)CLIP(prow[y][x+offset], 0, 255)]++; | |||||
// Calculate cumulative frequency | |||||
long sum = 0, cf[256] = { 0 }; | |||||
for(int i = 0; i < 255; i++){ | |||||
sum += histogram[i]; | |||||
cf[i] = sum; | |||||
} | |||||
// Apply histogram | |||||
int area = nrow * width; | |||||
for(int y = 0; y < nrow; y++){ | |||||
for(int x = 0; x < width; x++){ | |||||
int k = (int)prow[y][x+offset]; | |||||
prow[y][x+offset] = (256.0f/area) * cf[k]; | |||||
} | |||||
} | |||||
void apt_histogramEqualise(float **prow, int nrow, int offset, int width) { | |||||
// Plot histogram | |||||
int histogram[256] = {0}; | |||||
for (int y = 0; y < nrow; y++) | |||||
for (int x = 0; x < width; x++) histogram[(int)CLIP(prow[y][x + offset], 0, 255)]++; | |||||
// Calculate cumulative frequency | |||||
long sum = 0, cf[256] = {0}; | |||||
for (int i = 0; i < 255; i++) { | |||||
sum += histogram[i]; | |||||
cf[i] = sum; | |||||
} | |||||
// Apply histogram | |||||
int area = nrow * width; | |||||
for (int y = 0; y < nrow; y++) { | |||||
for (int x = 0; x < width; x++) { | |||||
int k = (int)prow[y][x + offset]; | |||||
prow[y][x + offset] = (256.0f / area) * cf[k]; | |||||
} | |||||
} | |||||
} | } | ||||
void apt_linearEnhance(float **prow, int nrow, int offset, int width){ | |||||
// Plot histogram | |||||
int histogram[256] = { 0 }; | |||||
for(int y = 0; y < nrow; y++) | |||||
for(int x = 0; x < width; x++) | |||||
histogram[(int)CLIP(prow[y][x+offset], 0, 255)]++; | |||||
// Find min/max points | |||||
int min = -1, max = -1; | |||||
for(int i = 5; i < 250; i++){ | |||||
if(histogram[i]/width/(nrow/255.0) > 0.1){ | |||||
if(min == -1) min = i; | |||||
max = i; | |||||
} | |||||
} | |||||
// Stretch the brightness into the new range | |||||
for(int y = 0; y < nrow; y++){ | |||||
for(int x = 0; x < width; x++){ | |||||
prow[y][x+offset] = (prow[y][x+offset]-min) / (max-min) * 255.0f; | |||||
prow[y][x+offset] = CLIP(prow[y][x+offset], 0.0f, 255.0f); | |||||
} | |||||
} | |||||
void apt_linearEnhance(float **prow, int nrow, int offset, int width) { | |||||
// Plot histogram | |||||
int histogram[256] = {0}; | |||||
for (int y = 0; y < nrow; y++) | |||||
for (int x = 0; x < width; x++) histogram[(int)CLIP(prow[y][x + offset], 0, 255)]++; | |||||
// Find min/max points | |||||
int min = -1, max = -1; | |||||
for (int i = 5; i < 250; i++) { | |||||
if (histogram[i] / width / (nrow / 255.0) > 0.1) { | |||||
if (min == -1) min = i; | |||||
max = i; | |||||
} | |||||
} | |||||
// Stretch the brightness into the new range | |||||
for (int y = 0; y < nrow; y++) { | |||||
for (int x = 0; x < width; x++) { | |||||
prow[y][x + offset] = (prow[y][x + offset] - min) / (max - min) * 255.0f; | |||||
prow[y][x + offset] = CLIP(prow[y][x + offset], 0.0f, 255.0f); | |||||
} | |||||
} | |||||
} | } | ||||
// Brightness calibrate, including telemetry | // Brightness calibrate, including telemetry | ||||
void calibrateImage(float **prow, int nrow, int offset, int width, linear_t regr){ | |||||
offset -= APT_SYNC_WIDTH+APT_SPC_WIDTH; | |||||
for (int y = 0; y < nrow; y++) { | |||||
for (int x = 0; x < width+APT_SYNC_WIDTH+APT_SPC_WIDTH+APT_TELE_WIDTH; x++) { | |||||
float pv = linear_calc(prow[y][x + offset], regr); | |||||
prow[y][x + offset] = CLIP(pv, 0, 255); | |||||
} | |||||
} | |||||
void calibrateImage(float **prow, int nrow, int offset, int width, linear_t regr) { | |||||
offset -= APT_SYNC_WIDTH + APT_SPC_WIDTH; | |||||
for (int y = 0; y < nrow; y++) { | |||||
for (int x = 0; x < width + APT_SYNC_WIDTH + APT_SPC_WIDTH + APT_TELE_WIDTH; x++) { | |||||
float pv = linear_calc(prow[y][x + offset], regr); | |||||
prow[y][x + offset] = CLIP(pv, 0, 255); | |||||
} | |||||
} | |||||
} | } | ||||
float teleNoise(float *wedges){ | |||||
float pattern[9] = { 31.07, 63.02, 94.96, 126.9, 158.86, 191.1, 228.62, 255.0, 0.0 }; | |||||
float noise = 0; | |||||
for(int i = 0; i < 9; i++) | |||||
noise += fabs(wedges[i] - pattern[i]); | |||||
float teleNoise(float *wedges) { | |||||
float pattern[9] = {31.07, 63.02, 94.96, 126.9, 158.86, 191.1, 228.62, 255.0, 0.0}; | |||||
float noise = 0; | |||||
for (int i = 0; i < 9; i++) noise += fabs(wedges[i] - pattern[i]); | |||||
return noise; | |||||
return noise; | |||||
} | } | ||||
// Get telemetry data for thermal calibration | // Get telemetry data for thermal calibration | ||||
apt_channel_t apt_calibrate(float **prow, int nrow, int offset, int width) { | apt_channel_t apt_calibrate(float **prow, int nrow, int offset, int width) { | ||||
float teleline[APT_MAX_HEIGHT] = { 0.0 }; | |||||
float wedge[16]; | |||||
linear_t regr[APT_MAX_HEIGHT/APT_FRAME_LEN + 1]; | |||||
int telestart, mtelestart = 0; | |||||
int channel = -1; | |||||
// The minimum rows required to decode a full frame | |||||
if (nrow < APT_CALIBRATION_ROWS) { | |||||
error_noexit("Telemetry decoding error, not enough rows"); | |||||
return APT_CHANNEL_UNKNOWN; | |||||
} | |||||
// Calculate average of a row of telemetry | |||||
for (int y = 0; y < nrow; y++) { | |||||
for (int x = 3; x < 43; x++) | |||||
teleline[y] += prow[y][x + offset + width]; | |||||
teleline[y] /= 40.0; | |||||
} | |||||
/* Wedge 7 is white and 8 is black, this will have the largest | |||||
* difference in brightness, this can be used to find the current | |||||
* position within the telemetry. | |||||
*/ | |||||
float max = 0.0f; | |||||
for (int n = nrow / 3 - 64; n < 2 * nrow / 3 - 64; n++) { | |||||
float df; | |||||
// (sum 4px below) - (sum 4px above) | |||||
df = (float)((teleline[n - 4] + teleline[n - 3] + teleline[n - 2] + teleline[n - 1]) - | |||||
(teleline[n + 0] + teleline[n + 1] + teleline[n + 2] + teleline[n + 3])); | |||||
// Find the maximum difference | |||||
if (df > max) { | |||||
mtelestart = n; | |||||
max = df; | |||||
} | |||||
} | |||||
telestart = (mtelestart + 64) % APT_FRAME_LEN; | |||||
// Make sure that theres at least one full frame in the image | |||||
if (nrow < telestart + APT_FRAME_LEN) { | |||||
error_noexit("Telemetry decoding error, not enough rows"); | |||||
return APT_CHANNEL_UNKNOWN; | |||||
} | |||||
// Find the least noisy frame | |||||
float minNoise = -1; | |||||
int bestFrame = -1; | |||||
for (int n = telestart, k = 0; n < nrow - APT_FRAME_LEN; n += APT_FRAME_LEN, k++) { | |||||
int j; | |||||
for (j = 0; j < 16; j++) { | |||||
int i; | |||||
wedge[j] = 0.0; | |||||
for (i = 1; i < 7; i++) | |||||
wedge[j] += teleline[n + j * 8 + i]; | |||||
wedge[j] /= 6; | |||||
} | |||||
float noise = teleNoise(wedge); | |||||
if(noise < minNoise || minNoise == -1){ | |||||
minNoise = noise; | |||||
bestFrame = k; | |||||
// Compute & apply regression on the wedges | |||||
regr[k] = compute_regression(wedge); | |||||
for (int j = 0; j < 16; j++) | |||||
tele[j] = linear_calc(wedge[j], regr[k]); | |||||
/* Compare the channel ID wedge to the reference | |||||
* wedges, the wedge with the closest match will | |||||
* be the channel ID | |||||
*/ | |||||
float min = -1; | |||||
for (int j = 0; j < 6; j++) { | |||||
float df = (float)(tele[15] - tele[j]); | |||||
df *= df; | |||||
if (df < min || min == -1) { | |||||
channel = j; | |||||
min = df; | |||||
} | |||||
} | |||||
// Find the brightness of the minute marker, I don't really know what for | |||||
Cs = 0.0; | |||||
int i, j = n; | |||||
for (i = 0, j = n; j < n + APT_FRAME_LEN; j++) { | |||||
float csline = 0.0; | |||||
for (int l = 3; l < 43; l++) | |||||
csline += prow[n][l + offset - APT_SPC_WIDTH]; | |||||
csline /= 40.0; | |||||
if (csline > 50.0) { | |||||
Cs += csline; | |||||
i++; | |||||
} | |||||
} | |||||
Cs = linear_calc((Cs / i), regr[k]); | |||||
} | |||||
} | |||||
if(bestFrame == -1){ | |||||
error_noexit("Something has gone very wrong, please file a bug report"); | |||||
return APT_CHANNEL_UNKNOWN; | |||||
} | |||||
calibrateImage(prow, nrow, offset, width, regr[bestFrame]); | |||||
return (apt_channel_t)(channel + 1); | |||||
float teleline[APT_MAX_HEIGHT] = {0.0}; | |||||
float wedge[16]; | |||||
linear_t regr[APT_MAX_HEIGHT / APT_FRAME_LEN + 1]; | |||||
int telestart, mtelestart = 0; | |||||
int channel = -1; | |||||
// The minimum rows required to decode a full frame | |||||
if (nrow < APT_CALIBRATION_ROWS) { | |||||
error_noexit("Telemetry decoding error, not enough rows"); | |||||
return APT_CHANNEL_UNKNOWN; | |||||
} | |||||
// Calculate average of a row of telemetry | |||||
for (int y = 0; y < nrow; y++) { | |||||
for (int x = 3; x < 43; x++) teleline[y] += prow[y][x + offset + width]; | |||||
teleline[y] /= 40.0; | |||||
} | |||||
/* Wedge 7 is white and 8 is black, this will have the largest | |||||
* difference in brightness, this can be used to find the current | |||||
* position within the telemetry. | |||||
*/ | |||||
float max = 0.0f; | |||||
for (int n = nrow / 3 - 64; n < 2 * nrow / 3 - 64; n++) { | |||||
float df; | |||||
// (sum 4px below) - (sum 4px above) | |||||
df = (float)((teleline[n - 4] + teleline[n - 3] + teleline[n - 2] + teleline[n - 1]) - | |||||
(teleline[n + 0] + teleline[n + 1] + teleline[n + 2] + teleline[n + 3])); | |||||
// Find the maximum difference | |||||
if (df > max) { | |||||
mtelestart = n; | |||||
max = df; | |||||
} | |||||
} | |||||
telestart = (mtelestart + 64) % APT_FRAME_LEN; | |||||
// Make sure that theres at least one full frame in the image | |||||
if (nrow < telestart + APT_FRAME_LEN) { | |||||
error_noexit("Telemetry decoding error, not enough rows"); | |||||
return APT_CHANNEL_UNKNOWN; | |||||
} | |||||
// Find the least noisy frame | |||||
float minNoise = -1; | |||||
int bestFrame = -1; | |||||
for (int n = telestart, k = 0; n < nrow - APT_FRAME_LEN; n += APT_FRAME_LEN, k++) { | |||||
int j; | |||||
for (j = 0; j < 16; j++) { | |||||
int i; | |||||
wedge[j] = 0.0; | |||||
for (i = 1; i < 7; i++) wedge[j] += teleline[n + j * 8 + i]; | |||||
wedge[j] /= 6; | |||||
} | |||||
float noise = teleNoise(wedge); | |||||
if (noise < minNoise || minNoise == -1) { | |||||
minNoise = noise; | |||||
bestFrame = k; | |||||
// Compute & apply regression on the wedges | |||||
regr[k] = compute_regression(wedge); | |||||
for (int j = 0; j < 16; j++) tele[j] = linear_calc(wedge[j], regr[k]); | |||||
/* Compare the channel ID wedge to the reference | |||||
* wedges, the wedge with the closest match will | |||||
* be the channel ID | |||||
*/ | |||||
float min = -1; | |||||
for (int j = 0; j < 6; j++) { | |||||
float df = (float)(tele[15] - tele[j]); | |||||
df *= df; | |||||
if (df < min || min == -1) { | |||||
channel = j; | |||||
min = df; | |||||
} | |||||
} | |||||
// Find the brightness of the minute marker, I don't really know what for | |||||
Cs = 0.0; | |||||
int i, j = n; | |||||
for (i = 0, j = n; j < n + APT_FRAME_LEN; j++) { | |||||
float csline = 0.0; | |||||
for (int l = 3; l < 43; l++) csline += prow[n][l + offset - APT_SPC_WIDTH]; | |||||
csline /= 40.0; | |||||
if (csline > 50.0) { | |||||
Cs += csline; | |||||
i++; | |||||
} | |||||
} | |||||
Cs = linear_calc((Cs / i), regr[k]); | |||||
} | |||||
} | |||||
if (bestFrame == -1) { | |||||
error_noexit("Something has gone very wrong, please file a bug report"); | |||||
return APT_CHANNEL_UNKNOWN; | |||||
} | |||||
calibrateImage(prow, nrow, offset, width, regr[bestFrame]); | |||||
return (apt_channel_t)(channel + 1); | |||||
} | } | ||||
extern float quick_select(float arr[], int n); | extern float quick_select(float arr[], int n); | ||||
// Biased median denoise, pretyt ugly | // Biased median denoise, pretyt ugly | ||||
#define TRIG_LEVEL 40 | #define TRIG_LEVEL 40 | ||||
void apt_denoise(float **prow, int nrow, int offset, int width){ | |||||
for(int y = 2; y < nrow-2; y++){ | |||||
for(int x = offset+1; x < offset+width-1; x++){ | |||||
if(prow[y][x+1] - prow[y][x] > TRIG_LEVEL || | |||||
prow[y][x-1] - prow[y][x] > TRIG_LEVEL || | |||||
prow[y+1][x] - prow[y][x] > TRIG_LEVEL || | |||||
prow[y-1][x] - prow[y][x] > TRIG_LEVEL){ | |||||
prow[y][x] = quick_select((float[]){ | |||||
prow[y+2][x-1], prow[y+2][x], prow[y+2][x+1], | |||||
prow[y+1][x-1], prow[y+1][x], prow[y+1][x+1], | |||||
prow[y-1][x-1], prow[y-1][x], prow[y-1][x+1], | |||||
prow[y-2][x-1], prow[y-2][x], prow[y-2][x+1] | |||||
}, 12); | |||||
} | |||||
} | |||||
} | |||||
void apt_denoise(float **prow, int nrow, int offset, int width) { | |||||
for (int y = 2; y < nrow - 2; y++) { | |||||
for (int x = offset + 1; x < offset + width - 1; x++) { | |||||
if (prow[y][x + 1] - prow[y][x] > TRIG_LEVEL || prow[y][x - 1] - prow[y][x] > TRIG_LEVEL || | |||||
prow[y + 1][x] - prow[y][x] > TRIG_LEVEL || prow[y - 1][x] - prow[y][x] > TRIG_LEVEL) { | |||||
prow[y][x] = quick_select((float[]){prow[y + 2][x - 1], prow[y + 2][x], prow[y + 2][x + 1], prow[y + 1][x - 1], | |||||
prow[y + 1][x], prow[y + 1][x + 1], prow[y - 1][x - 1], prow[y - 1][x], | |||||
prow[y - 1][x + 1], prow[y - 2][x - 1], prow[y - 2][x], prow[y - 2][x + 1]}, | |||||
12); | |||||
} | |||||
} | |||||
} | |||||
} | } | ||||
#undef TRIG_LEVEL | #undef TRIG_LEVEL | ||||
// Flips a channel, for northbound passes | // Flips a channel, for northbound passes | ||||
void apt_flipImage(apt_image_t *img, int width, int offset){ | |||||
for(int y = 1; y < img->nrow; y++){ | |||||
for(int x = 1; x < ceil(width / 2.0); x++){ | |||||
// Flip top-left & bottom-right | |||||
float buffer = img->prow[img->nrow - y][offset + x]; | |||||
img->prow[img->nrow - y][offset + x] = img->prow[y][offset + (width - x)]; | |||||
img->prow[y][offset + (width - x)] = buffer; | |||||
} | |||||
} | |||||
void apt_flipImage(apt_image_t *img, int width, int offset) { | |||||
for (int y = 1; y < img->nrow; y++) { | |||||
for (int x = 1; x < ceil(width / 2.0); x++) { | |||||
// Flip top-left & bottom-right | |||||
float buffer = img->prow[img->nrow - y][offset + x]; | |||||
img->prow[img->nrow - y][offset + x] = img->prow[y][offset + (width - x)]; | |||||
img->prow[y][offset + (width - x)] = buffer; | |||||
} | |||||
} | |||||
} | } | ||||
// Calculate crop to reomve noise from the start and end of an image | // Calculate crop to reomve noise from the start and end of an image | ||||
int apt_cropNoise(apt_image_t *img){ | |||||
#define NOISE_THRESH 180.0 | |||||
// Average value of minute marker | |||||
float spc_rows[APT_MAX_HEIGHT] = { 0.0 }; | |||||
int startCrop = 0; int endCrop = img->nrow; | |||||
for(int y = 0; y < img->nrow; y++) { | |||||
for(int x = 0; x < APT_SPC_WIDTH; x++) { | |||||
spc_rows[y] += img->prow[y][x + (APT_CHB_OFFSET - APT_SPC_WIDTH)]; | |||||
} | |||||
spc_rows[y] /= APT_SPC_WIDTH; | |||||
// Skip minute markings | |||||
if(spc_rows[y] < 10) { | |||||
spc_rows[y] = spc_rows[y-1]; | |||||
} | |||||
} | |||||
// 3 row average | |||||
for(int y = 0; y < img->nrow; y++){ | |||||
spc_rows[y] = (spc_rows[y+1] + spc_rows[y+2] + spc_rows[y+3])/3; | |||||
//img.prow[y][0] = spc_rows[y]; | |||||
} | |||||
// Find ends | |||||
for(int y = 0; y < img->nrow-1; y++) { | |||||
if(spc_rows[y] > NOISE_THRESH){ | |||||
endCrop = y; | |||||
} | |||||
} | |||||
for(int y = img->nrow; y > 0; y--) { | |||||
if(spc_rows[y] > NOISE_THRESH) { | |||||
startCrop = y; | |||||
} | |||||
} | |||||
//printf("Crop rows: %i -> %i\n", startCrop, endCrop); | |||||
// Remove the noisy rows at start | |||||
for(int y = 0; y < img->nrow-startCrop; y++) { | |||||
memmove(img->prow[y], img->prow[y+startCrop], sizeof(float)*APT_PROW_WIDTH); | |||||
} | |||||
// Ignore the noisy rows at the end | |||||
img->nrow = (endCrop - startCrop); | |||||
return startCrop; | |||||
int apt_cropNoise(apt_image_t *img) { | |||||
#define NOISE_THRESH 180.0 | |||||
// Average value of minute marker | |||||
float spc_rows[APT_MAX_HEIGHT] = {0.0}; | |||||
int startCrop = 0; | |||||
int endCrop = img->nrow; | |||||
for (int y = 0; y < img->nrow; y++) { | |||||
for (int x = 0; x < APT_SPC_WIDTH; x++) { | |||||
spc_rows[y] += img->prow[y][x + (APT_CHB_OFFSET - APT_SPC_WIDTH)]; | |||||
} | |||||
spc_rows[y] /= APT_SPC_WIDTH; | |||||
// Skip minute markings | |||||
if (spc_rows[y] < 10) { | |||||
spc_rows[y] = spc_rows[y - 1]; | |||||
} | |||||
} | |||||
// 3 row average | |||||
for (int y = 0; y < img->nrow; y++) { | |||||
spc_rows[y] = (spc_rows[y + 1] + spc_rows[y + 2] + spc_rows[y + 3]) / 3; | |||||
// img.prow[y][0] = spc_rows[y]; | |||||
} | |||||
// Find ends | |||||
for (int y = 0; y < img->nrow - 1; y++) { | |||||
if (spc_rows[y] > NOISE_THRESH) { | |||||
endCrop = y; | |||||
} | |||||
} | |||||
for (int y = img->nrow; y > 0; y--) { | |||||
if (spc_rows[y] > NOISE_THRESH) { | |||||
startCrop = y; | |||||
} | |||||
} | |||||
// printf("Crop rows: %i -> %i\n", startCrop, endCrop); | |||||
// Remove the noisy rows at start | |||||
for (int y = 0; y < img->nrow - startCrop; y++) { | |||||
memmove(img->prow[y], img->prow[y + startCrop], sizeof(float) * APT_PROW_WIDTH); | |||||
} | |||||
// Ignore the noisy rows at the end | |||||
img->nrow = (endCrop - startCrop); | |||||
return startCrop; | |||||
} | } | ||||
// --- Visible and Temperature Calibration --- // | // --- Visible and Temperature Calibration --- // | ||||
#include "calibration.h" | #include "calibration.h" | ||||
typedef struct { | typedef struct { | ||||
float Nbb; | |||||
float Cs; | |||||
float Cb; | |||||
int ch; | |||||
float Nbb; | |||||
float Cs; | |||||
float Cb; | |||||
int ch; | |||||
} tempparam_t; | } tempparam_t; | ||||
// IR channel temperature compensation | // IR channel temperature compensation | ||||
tempparam_t tempcomp(float t[16], int ch, int satnum) { | tempparam_t tempcomp(float t[16], int ch, int satnum) { | ||||
tempparam_t tpr; | |||||
tpr.ch = ch - 4; | |||||
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; | |||||
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]); | |||||
} | |||||
// 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]); | |||||
} | |||||
float Tbb = (T[0]+T[1]+T[2]+T[3]) / 4.0; // Blackbody temperature | |||||
float Tbbstar = A + Tbb*B; // Effective blackbody temperature | |||||
float Tbb = (T[0] + T[1] + T[2] + T[3]) / 4.0; // Blackbody temperature | |||||
float Tbbstar = A + Tbb * B; // Effective blackbody temperature | |||||
tpr.Nbb = C1 * pow(Vc, 3) / (expf(C2 * Vc / Tbbstar) - 1.0f); // Blackbody radiance | |||||
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; | |||||
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 tpr) { | 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; | |||||
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 | |||||
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 | |||||
Te -= 273.15; | |||||
// Rescale to 0-255 for -100°C to +60°C | |||||
return (Te + 100.0) / 160.0 * 255.0; | |||||
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; | |||||
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 | |||||
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 | |||||
Te -= 273.15; | |||||
// Rescale to 0-255 for -100°C to +60°C | |||||
return (Te + 100.0) / 160.0 * 255.0; | |||||
} | } | ||||
// Temperature calibration wrapper | // Temperature calibration wrapper | ||||
void apt_calibrate_thermal(int satnum, apt_image_t *img, int offset, int width) { | void apt_calibrate_thermal(int satnum, apt_image_t *img, int offset, int width) { | ||||
tempparam_t temp = tempcomp(tele, img->chB, satnum); | |||||
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); | |||||
} | |||||
} | |||||
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) { | 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; | |||||
} | |||||
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; | |||||
} | |||||
} | } | ||||
void apt_calibrate_visible(int satnum, apt_image_t *img, int offset, int width) { | 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] = clamp(calibrate_pixel(img->prow[y][x + offset], channel, calibration), 255.0f, 0.0f); | |||||
} | |||||
} | |||||
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] = clamp(calibrate_pixel(img->prow[y][x + offset], channel, calibration), 255.0f, 0.0f); | |||||
} | |||||
} | |||||
} | } |
@@ -1,4 +1,4 @@ | |||||
/* | |||||
/* | |||||
* This file is part of Aptdec. | * This file is part of Aptdec. | ||||
* Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019-2022 | * Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019-2022 | ||||
* | * | ||||
@@ -17,26 +17,25 @@ | |||||
* | * | ||||
*/ | */ | ||||
#include <stdlib.h> | |||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdlib.h> | |||||
#include <string.h> | #include <string.h> | ||||
#ifndef _MSC_VER | #ifndef _MSC_VER | ||||
#include <libgen.h> | #include <libgen.h> | ||||
#else | #else | ||||
#include <windows.h> | #include <windows.h> | ||||
#endif | #endif | ||||
#include <errno.h> | |||||
#include <math.h> | #include <math.h> | ||||
#include <sndfile.h> | #include <sndfile.h> | ||||
#include <errno.h> | |||||
#include <time.h> | #include <time.h> | ||||
#include "argparse/argparse.h" | |||||
#include "common.h" | |||||
#include "apt.h" | #include "apt.h" | ||||
#include "pngio.h" | |||||
#include "image.h" | |||||
#include "argparse/argparse.h" | |||||
#include "color.h" | #include "color.h" | ||||
#include "common.h" | |||||
#include "image.h" | |||||
#include "pngio.h" | |||||
#include "util.h" | #include "util.h" | ||||
// Audio file | // Audio file | ||||
@@ -51,279 +50,267 @@ static int processAudio(char *filename, options_t *opts); | |||||
#ifdef _MSC_VER | #ifdef _MSC_VER | ||||
// Functions not supported by MSVC | // Functions not supported by MSVC | ||||
static char *dirname(char *path) | |||||
{ | |||||
static char dir[MAX_PATH]; | |||||
_splitpath(path, NULL, dir, NULL, NULL); | |||||
return dir; | |||||
static char *dirname(char *path) { | |||||
static char dir[MAX_PATH]; | |||||
_splitpath(path, NULL, dir, NULL, NULL); | |||||
return dir; | |||||
} | } | ||||
static char *basename(char *path) | |||||
{ | |||||
static char base[MAX_PATH]; | |||||
_splitpath(path, NULL, NULL, base, NULL); | |||||
return base; | |||||
static char *basename(char *path) { | |||||
static char base[MAX_PATH]; | |||||
_splitpath(path, NULL, NULL, base, NULL); | |||||
return base; | |||||
} | } | ||||
#endif | #endif | ||||
int main(int argc, const char **argv) { | int main(int argc, const char **argv) { | ||||
options_t opts = { | |||||
.type = "r", | |||||
.effects = "", | |||||
.satnum = 19, | |||||
.path = ".", | |||||
.realtime = 0, | |||||
.filename = "", | |||||
.palette = "", | |||||
.gamma = 1.0 | |||||
}; | |||||
static const char *const usages[] = { | |||||
"aptdec [options] [[--] sources]", | |||||
"aptdec [sources]", | |||||
NULL, | |||||
}; | |||||
struct argparse_option options[] = { | |||||
OPT_HELP(), | |||||
OPT_GROUP("Image options"), | |||||
OPT_STRING('i', "image", &opts.type, "set output image type (see the README for a list)", NULL, 0, 0), | |||||
OPT_STRING('e', "effect", &opts.effects, "add an effect (see the README for a list)", NULL, 0, 0), | |||||
OPT_FLOAT('g', "gamma", &opts.gamma, "gamma adjustment (1.0 = off)", NULL, 0, 0), | |||||
OPT_GROUP("Satellite options"), | |||||
OPT_INTEGER('s', "satellite", &opts.satnum, "satellite ID, must be between 15 and 19", NULL, 0, 0), | |||||
OPT_GROUP("Paths"), | |||||
OPT_STRING('p', "palette", &opts.palette, "path to a palette", NULL, 0, 0), | |||||
OPT_STRING('o', "filename", &opts.filename, "filename of the output image", NULL, 0, 0), | |||||
OPT_STRING('d', "output", &opts.path, "output directory (must exist first)", NULL, 0, 0), | |||||
OPT_GROUP("Misc"), | |||||
OPT_BOOLEAN('r', "realtime", &opts.realtime, "decode in realtime", NULL, 0, 0), | |||||
OPT_END(), | |||||
}; | |||||
struct argparse argparse; | |||||
argparse_init(&argparse, options, usages, 0); | |||||
argparse_describe(&argparse, "\nA lightweight FOSS NOAA APT satellite imagery decoder.", "\nSee `README.md` for a full description of command line arguments and `LICENSE` for licensing conditions."); | |||||
argc = argparse_parse(&argparse, argc, argv); | |||||
if(argc == 0){ | |||||
argparse_usage(&argparse); | |||||
} | |||||
// Actually decode the files | |||||
for (int i = 0; i < argc; i++) { | |||||
char *filename = strdup(argv[i]); | |||||
processAudio(filename, &opts); | |||||
} | |||||
return 0; | |||||
options_t opts = { | |||||
.type = "r", .effects = "", .satnum = 19, .path = ".", .realtime = 0, .filename = "", .palette = "", .gamma = 1.0}; | |||||
static const char *const usages[] = { | |||||
"aptdec [options] [[--] sources]", | |||||
"aptdec [sources]", | |||||
NULL, | |||||
}; | |||||
struct argparse_option options[] = { | |||||
OPT_HELP(), | |||||
OPT_GROUP("Image options"), | |||||
OPT_STRING('i', "image", &opts.type, "set output image type (see the README for a list)", NULL, 0, 0), | |||||
OPT_STRING('e', "effect", &opts.effects, "add an effect (see the README for a list)", NULL, 0, 0), | |||||
OPT_FLOAT('g', "gamma", &opts.gamma, "gamma adjustment (1.0 = off)", NULL, 0, 0), | |||||
OPT_GROUP("Satellite options"), | |||||
OPT_INTEGER('s', "satellite", &opts.satnum, "satellite ID, must be between 15 and 19", NULL, 0, 0), | |||||
OPT_GROUP("Paths"), | |||||
OPT_STRING('p', "palette", &opts.palette, "path to a palette", NULL, 0, 0), | |||||
OPT_STRING('o', "filename", &opts.filename, "filename of the output image", NULL, 0, 0), | |||||
OPT_STRING('d', "output", &opts.path, "output directory (must exist first)", NULL, 0, 0), | |||||
OPT_GROUP("Misc"), | |||||
OPT_BOOLEAN('r', "realtime", &opts.realtime, "decode in realtime", NULL, 0, 0), | |||||
OPT_END(), | |||||
}; | |||||
struct argparse argparse; | |||||
argparse_init(&argparse, options, usages, 0); | |||||
argparse_describe( | |||||
&argparse, "\nA lightweight FOSS NOAA APT satellite imagery decoder.", | |||||
"\nSee `README.md` for a full description of command line arguments and `LICENSE` for licensing conditions."); | |||||
argc = argparse_parse(&argparse, argc, argv); | |||||
if (argc == 0) { | |||||
argparse_usage(&argparse); | |||||
} | |||||
// Actually decode the files | |||||
for (int i = 0; i < argc; i++) { | |||||
char *filename = strdup(argv[i]); | |||||
processAudio(filename, &opts); | |||||
} | |||||
return 0; | |||||
} | } | ||||
static int processAudio(char *filename, options_t *opts){ | |||||
// Image info struct | |||||
apt_image_t img; | |||||
// Mapping between wedge value and channel ID | |||||
static struct { | |||||
char *id[7]; | |||||
char *name[7]; | |||||
} ch = { | |||||
{ "?", "1", "2", "3A", "4", "5", "3B" }, | |||||
{ "unknown", "visble", "near-infrared", "near-infrared", "thermal-infrared", "thermal-infrared", "mid-infrared" } | |||||
}; | |||||
// Buffer for image channel | |||||
char desc[60]; | |||||
// Parse file path | |||||
char path[256], extension[32]; | |||||
strcpy(path, filename); | |||||
strcpy(path, dirname(path)); | |||||
sscanf(basename(filename), "%255[^.].%31s", img.name, extension); | |||||
if(opts->realtime){ | |||||
// Set output filename to current time when in realtime mode | |||||
time_t t; | |||||
time(&t); | |||||
strncpy(img.name, ctime(&t), 24); | |||||
// Init a row writer | |||||
initWriter(opts, &img, APT_IMG_WIDTH, APT_MAX_HEIGHT, "Unprocessed realtime image", "r"); | |||||
} | |||||
if(strcmp(extension, "png") == 0){ | |||||
// Read PNG into image buffer | |||||
printf("Reading %s\n", filename); | |||||
if(readRawImage(filename, img.prow, &img.nrow) == 0){ | |||||
exit(EPERM); | |||||
} | |||||
}else{ | |||||
// Attempt to open the audio file | |||||
if (initsnd(filename) == 0) | |||||
exit(EPERM); | |||||
// Build image | |||||
// TODO: multithreading, would require some sort of input buffer | |||||
for (img.nrow = 0; img.nrow < APT_MAX_HEIGHT; img.nrow++) { | |||||
// Allocate memory for this row | |||||
img.prow[img.nrow] = (float *) malloc(sizeof(float) * APT_PROW_WIDTH); | |||||
// Write into memory and break the loop when there are no more samples to read | |||||
if (apt_getpixelrow(img.prow[img.nrow], img.nrow, &img.zenith, (img.nrow == 0), getsamples, NULL) == 0) | |||||
break; | |||||
if(opts->realtime) pushRow(img.prow[img.nrow], APT_IMG_WIDTH); | |||||
fprintf(stderr, "Row: %d\r", img.nrow); | |||||
fflush(stderr); | |||||
} | |||||
// Close stream | |||||
sf_close(audioFile); | |||||
} | |||||
if(opts->realtime) closeWriter(); | |||||
printf("Total rows: %d\n", img.nrow); | |||||
// Calibrate | |||||
img.chA = apt_calibrate(img.prow, img.nrow, APT_CHA_OFFSET, APT_CH_WIDTH); | |||||
img.chB = apt_calibrate(img.prow, img.nrow, APT_CHB_OFFSET, APT_CH_WIDTH); | |||||
printf("Channel A: %s (%s)\n", ch.id[img.chA], ch.name[img.chA]); | |||||
printf("Channel B: %s (%s)\n", ch.id[img.chB], ch.name[img.chB]); | |||||
// Crop noise from start and end of image | |||||
if(CONTAINS(opts->effects, Crop_Noise)){ | |||||
img.zenith -= apt_cropNoise(&img); | |||||
} | |||||
// Denoise | |||||
if(CONTAINS(opts->effects, Denoise)){ | |||||
apt_denoise(img.prow, img.nrow, APT_CHA_OFFSET, APT_CH_WIDTH); | |||||
apt_denoise(img.prow, img.nrow, APT_CHB_OFFSET, APT_CH_WIDTH); | |||||
} | |||||
// Flip, for northbound passes | |||||
if(CONTAINS(opts->effects, Flip_Image)){ | |||||
apt_flipImage(&img, APT_CH_WIDTH, APT_CHA_OFFSET); | |||||
apt_flipImage(&img, APT_CH_WIDTH, APT_CHB_OFFSET); | |||||
} | |||||
// Temperature | |||||
if (CONTAINS(opts->type, Temperature) && img.chB >= 4) { | |||||
// 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 temperature calibration | |||||
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); | |||||
} | |||||
// Visible | |||||
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, NULL); | |||||
} | |||||
// Linear equalise | |||||
if(CONTAINS(opts->effects, Linear_Equalise)){ | |||||
apt_linearEnhance(img.prow, img.nrow, APT_CHA_OFFSET, APT_CH_WIDTH); | |||||
apt_linearEnhance(img.prow, img.nrow, APT_CHB_OFFSET, APT_CH_WIDTH); | |||||
} | |||||
// Histogram equalise | |||||
if(CONTAINS(opts->effects, Histogram_Equalise)){ | |||||
apt_histogramEqualise(img.prow, img.nrow, APT_CHA_OFFSET, APT_CH_WIDTH); | |||||
apt_histogramEqualise(img.prow, img.nrow, APT_CHB_OFFSET, APT_CH_WIDTH); | |||||
} | |||||
// Raw image | |||||
if (CONTAINS(opts->type, Raw_Image)) { | |||||
sprintf(desc, "%s (%s) & %s (%s)", ch.id[img.chA], ch.name[img.chA], ch.id[img.chB], ch.name[img.chB]); | |||||
ImageOut(opts, &img, 0, APT_IMG_WIDTH, desc, Raw_Image, NULL); | |||||
} | |||||
// Palette image | |||||
if (CONTAINS(opts->type, Palleted)) { | |||||
img.palette = opts->palette; | |||||
strcpy(desc, "Palette composite"); | |||||
ImageOut(opts, &img, APT_CHA_OFFSET, APT_CH_WIDTH, desc, Palleted, NULL); | |||||
} | |||||
// Channel A | |||||
if (CONTAINS(opts->type, Channel_A)) { | |||||
sprintf(desc, "%s (%s)", ch.id[img.chA], ch.name[img.chA]); | |||||
ImageOut(opts, &img, APT_CHA_OFFSET, APT_CH_WIDTH, desc, Channel_A, NULL); | |||||
} | |||||
// Channel B | |||||
if (CONTAINS(opts->type, Channel_B)) { | |||||
sprintf(desc, "%s (%s)", ch.id[img.chB], ch.name[img.chB]); | |||||
ImageOut(opts, &img, APT_CHB_OFFSET, APT_CH_WIDTH, desc, Channel_B, NULL); | |||||
} | |||||
return 1; | |||||
static int processAudio(char *filename, options_t *opts) { | |||||
// Image info struct | |||||
apt_image_t img; | |||||
// Mapping between wedge value and channel ID | |||||
static struct { | |||||
char *id[7]; | |||||
char *name[7]; | |||||
} ch = {{"?", "1", "2", "3A", "4", "5", "3B"}, | |||||
{"unknown", "visble", "near-infrared", "near-infrared", "thermal-infrared", "thermal-infrared", "mid-infrared"}}; | |||||
// Buffer for image channel | |||||
char desc[60]; | |||||
// Parse file path | |||||
char path[256], extension[32]; | |||||
strcpy(path, filename); | |||||
strcpy(path, dirname(path)); | |||||
sscanf(basename(filename), "%255[^.].%31s", img.name, extension); | |||||
if (opts->realtime) { | |||||
// Set output filename to current time when in realtime mode | |||||
time_t t; | |||||
time(&t); | |||||
strncpy(img.name, ctime(&t), 24); | |||||
// Init a row writer | |||||
initWriter(opts, &img, APT_IMG_WIDTH, APT_MAX_HEIGHT, "Unprocessed realtime image", "r"); | |||||
} | |||||
if (strcmp(extension, "png") == 0) { | |||||
// Read PNG into image buffer | |||||
printf("Reading %s\n", filename); | |||||
if (readRawImage(filename, img.prow, &img.nrow) == 0) { | |||||
exit(EPERM); | |||||
} | |||||
} else { | |||||
// Attempt to open the audio file | |||||
if (initsnd(filename) == 0) exit(EPERM); | |||||
// Build image | |||||
// TODO: multithreading, would require some sort of input buffer | |||||
for (img.nrow = 0; img.nrow < APT_MAX_HEIGHT; img.nrow++) { | |||||
// Allocate memory for this row | |||||
img.prow[img.nrow] = (float *)malloc(sizeof(float) * APT_PROW_WIDTH); | |||||
// Write into memory and break the loop when there are no more samples to read | |||||
if (apt_getpixelrow(img.prow[img.nrow], img.nrow, &img.zenith, (img.nrow == 0), getsamples, NULL) == 0) break; | |||||
if (opts->realtime) pushRow(img.prow[img.nrow], APT_IMG_WIDTH); | |||||
fprintf(stderr, "Row: %d\r", img.nrow); | |||||
fflush(stderr); | |||||
} | |||||
// Close stream | |||||
sf_close(audioFile); | |||||
} | |||||
if (opts->realtime) closeWriter(); | |||||
printf("Total rows: %d\n", img.nrow); | |||||
// Calibrate | |||||
img.chA = apt_calibrate(img.prow, img.nrow, APT_CHA_OFFSET, APT_CH_WIDTH); | |||||
img.chB = apt_calibrate(img.prow, img.nrow, APT_CHB_OFFSET, APT_CH_WIDTH); | |||||
printf("Channel A: %s (%s)\n", ch.id[img.chA], ch.name[img.chA]); | |||||
printf("Channel B: %s (%s)\n", ch.id[img.chB], ch.name[img.chB]); | |||||
// Crop noise from start and end of image | |||||
if (CONTAINS(opts->effects, Crop_Noise)) { | |||||
img.zenith -= apt_cropNoise(&img); | |||||
} | |||||
// Denoise | |||||
if (CONTAINS(opts->effects, Denoise)) { | |||||
apt_denoise(img.prow, img.nrow, APT_CHA_OFFSET, APT_CH_WIDTH); | |||||
apt_denoise(img.prow, img.nrow, APT_CHB_OFFSET, APT_CH_WIDTH); | |||||
} | |||||
// Flip, for northbound passes | |||||
if (CONTAINS(opts->effects, Flip_Image)) { | |||||
apt_flipImage(&img, APT_CH_WIDTH, APT_CHA_OFFSET); | |||||
apt_flipImage(&img, APT_CH_WIDTH, APT_CHB_OFFSET); | |||||
} | |||||
// Temperature | |||||
if (CONTAINS(opts->type, Temperature) && img.chB >= 4) { | |||||
// 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 temperature calibration | |||||
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); | |||||
} | |||||
// Visible | |||||
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, NULL); | |||||
} | |||||
// Linear equalise | |||||
if (CONTAINS(opts->effects, Linear_Equalise)) { | |||||
apt_linearEnhance(img.prow, img.nrow, APT_CHA_OFFSET, APT_CH_WIDTH); | |||||
apt_linearEnhance(img.prow, img.nrow, APT_CHB_OFFSET, APT_CH_WIDTH); | |||||
} | |||||
// Histogram equalise | |||||
if (CONTAINS(opts->effects, Histogram_Equalise)) { | |||||
apt_histogramEqualise(img.prow, img.nrow, APT_CHA_OFFSET, APT_CH_WIDTH); | |||||
apt_histogramEqualise(img.prow, img.nrow, APT_CHB_OFFSET, APT_CH_WIDTH); | |||||
} | |||||
// Raw image | |||||
if (CONTAINS(opts->type, Raw_Image)) { | |||||
sprintf(desc, "%s (%s) & %s (%s)", ch.id[img.chA], ch.name[img.chA], ch.id[img.chB], ch.name[img.chB]); | |||||
ImageOut(opts, &img, 0, APT_IMG_WIDTH, desc, Raw_Image, NULL); | |||||
} | |||||
// Palette image | |||||
if (CONTAINS(opts->type, Palleted)) { | |||||
img.palette = opts->palette; | |||||
strcpy(desc, "Palette composite"); | |||||
ImageOut(opts, &img, APT_CHA_OFFSET, APT_CH_WIDTH, desc, Palleted, NULL); | |||||
} | |||||
// Channel A | |||||
if (CONTAINS(opts->type, Channel_A)) { | |||||
sprintf(desc, "%s (%s)", ch.id[img.chA], ch.name[img.chA]); | |||||
ImageOut(opts, &img, APT_CHA_OFFSET, APT_CH_WIDTH, desc, Channel_A, NULL); | |||||
} | |||||
// Channel B | |||||
if (CONTAINS(opts->type, Channel_B)) { | |||||
sprintf(desc, "%s (%s)", ch.id[img.chB], ch.name[img.chB]); | |||||
ImageOut(opts, &img, APT_CHB_OFFSET, APT_CH_WIDTH, desc, Channel_B, NULL); | |||||
} | |||||
return 1; | |||||
} | } | ||||
float *samplebuf; | float *samplebuf; | ||||
static int initsnd(char *filename) { | static int initsnd(char *filename) { | ||||
SF_INFO infwav; | |||||
int res; | |||||
// Open audio file | |||||
infwav.format = 0; | |||||
audioFile = sf_open(filename, SFM_READ, &infwav); | |||||
if (audioFile == NULL) { | |||||
error_noexit("Could not file"); | |||||
return 0; | |||||
} | |||||
res = apt_init(infwav.samplerate); | |||||
printf("Input file: %s\n", filename); | |||||
if(res < 0) { | |||||
error_noexit("Input sample rate too low"); | |||||
return 0; | |||||
}else if(res > 0) { | |||||
error_noexit("Input sample rate too high"); | |||||
return 0; | |||||
} | |||||
printf("Input sample rate: %d\n", infwav.samplerate); | |||||
channels = infwav.channels; | |||||
samplebuf = (float *)malloc(sizeof(float) * 32768 * channels); | |||||
return 1; | |||||
SF_INFO infwav; | |||||
int res; | |||||
// Open audio file | |||||
infwav.format = 0; | |||||
audioFile = sf_open(filename, SFM_READ, &infwav); | |||||
if (audioFile == NULL) { | |||||
error_noexit("Could not file"); | |||||
return 0; | |||||
} | |||||
res = apt_init(infwav.samplerate); | |||||
printf("Input file: %s\n", filename); | |||||
if (res < 0) { | |||||
error_noexit("Input sample rate too low"); | |||||
return 0; | |||||
} else if (res > 0) { | |||||
error_noexit("Input sample rate too high"); | |||||
return 0; | |||||
} | |||||
printf("Input sample rate: %d\n", infwav.samplerate); | |||||
channels = infwav.channels; | |||||
samplebuf = (float *)malloc(sizeof(float) * 32768 * channels); | |||||
return 1; | |||||
} | } | ||||
// Read samples from the audio file | // Read samples from the audio file | ||||
int getsamples(void *context, float *samples, int nb) { | int getsamples(void *context, float *samples, int nb) { | ||||
(void) context; | |||||
if (channels == 1){ | |||||
return (int)sf_read_float(audioFile, samples, nb); | |||||
} else if (channels == 2) { | |||||
// Stereo channels are interleaved | |||||
int samplesRead = (int)sf_read_float(audioFile, samplebuf, nb * channels); | |||||
for(int i = 0; i < nb; i++) { | |||||
samples[i] = samplebuf[i * channels]; | |||||
} | |||||
return samplesRead / channels; | |||||
} else { | |||||
printf("Only mono and stereo input files are supported\n"); | |||||
exit(1); | |||||
} | |||||
(void)context; | |||||
if (channels == 1) { | |||||
return (int)sf_read_float(audioFile, samples, nb); | |||||
} else if (channels == 2) { | |||||
// Stereo channels are interleaved | |||||
int samplesRead = (int)sf_read_float(audioFile, samplebuf, nb * channels); | |||||
for (int i = 0; i < nb; i++) { | |||||
samples[i] = samplebuf[i * channels]; | |||||
} | |||||
return samplesRead / channels; | |||||
} else { | |||||
printf("Only mono and stereo input files are supported\n"); | |||||
exit(1); | |||||
} | |||||
} | } |
@@ -1,4 +1,4 @@ | |||||
/* | |||||
/* | |||||
* This file is part of Aptdec. | * This file is part of Aptdec. | ||||
* Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019-2022 | * Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019-2022 | ||||
* | * | ||||
@@ -17,337 +17,333 @@ | |||||
* | * | ||||
*/ | */ | ||||
#include "pngio.h" | |||||
#include <math.h> | |||||
#include <png.h> | #include <png.h> | ||||
#include <stdlib.h> | |||||
#include <stdint.h> | |||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdlib.h> | |||||
#include <string.h> | #include <string.h> | ||||
#include <stdint.h> | |||||
#include <math.h> | |||||
#include "util.h" | |||||
#include "pngio.h" | |||||
#include "util.h" | |||||
int readRawImage(char *filename, float **prow, int *nrow) { | int readRawImage(char *filename, float **prow, int *nrow) { | ||||
FILE *fp = fopen(filename, "rb"); | |||||
FILE *fp = fopen(filename, "rb"); | |||||
printf("%s", filename); | printf("%s", filename); | ||||
if(!fp) { | |||||
error_noexit("Cannot open image"); | |||||
return 0; | |||||
} | |||||
// Create reader | |||||
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | |||||
if(!png) { | |||||
fclose(fp); | |||||
return 0; | |||||
} | |||||
png_infop info = png_create_info_struct(png); | |||||
if(!info) { | |||||
fclose(fp); | |||||
return 0; | |||||
} | |||||
png_init_io(png, fp); | |||||
// Read info from header | |||||
png_read_info(png, info); | |||||
int width = png_get_image_width(png, info); | |||||
int height = png_get_image_height(png, info); | |||||
png_byte color_type = png_get_color_type(png, info); | |||||
png_byte bit_depth = png_get_bit_depth(png, info); | |||||
// Check the image | |||||
if(width != APT_IMG_WIDTH){ | |||||
error_noexit("Raw image must be 2080px wide"); | |||||
return 0; | |||||
}else if(bit_depth != 8){ | |||||
error_noexit("Raw image must have 8 bit color"); | |||||
return 0; | |||||
}else if(color_type != PNG_COLOR_TYPE_GRAY){ | |||||
error_noexit("Raw image must be grayscale"); | |||||
return 0; | |||||
} | |||||
// Create row buffers | |||||
png_bytep *PNGrows = NULL; | |||||
PNGrows = (png_bytep *) malloc(sizeof(png_bytep) * height); | |||||
for(int y = 0; y < height; y++) PNGrows[y] = (png_byte *) | |||||
malloc(png_get_rowbytes(png, info)); | |||||
// Read image | |||||
png_read_image(png, PNGrows); | |||||
// Tidy up | |||||
fclose(fp); | |||||
png_destroy_read_struct(&png, &info, NULL); | |||||
// Put into prow | |||||
*nrow = height; | |||||
for(int y = 0; y < height; y++) { | |||||
prow[y] = (float *) malloc(sizeof(float) * width); | |||||
for(int x = 0; x < width; x++) | |||||
prow[y][x] = (float)PNGrows[y][x]; | |||||
} | |||||
return 1; | |||||
if (!fp) { | |||||
error_noexit("Cannot open image"); | |||||
return 0; | |||||
} | |||||
// Create reader | |||||
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | |||||
if (!png) { | |||||
fclose(fp); | |||||
return 0; | |||||
} | |||||
png_infop info = png_create_info_struct(png); | |||||
if (!info) { | |||||
fclose(fp); | |||||
return 0; | |||||
} | |||||
png_init_io(png, fp); | |||||
// Read info from header | |||||
png_read_info(png, info); | |||||
int width = png_get_image_width(png, info); | |||||
int height = png_get_image_height(png, info); | |||||
png_byte color_type = png_get_color_type(png, info); | |||||
png_byte bit_depth = png_get_bit_depth(png, info); | |||||
// Check the image | |||||
if (width != APT_IMG_WIDTH) { | |||||
error_noexit("Raw image must be 2080px wide"); | |||||
return 0; | |||||
} else if (bit_depth != 8) { | |||||
error_noexit("Raw image must have 8 bit color"); | |||||
return 0; | |||||
} else if (color_type != PNG_COLOR_TYPE_GRAY) { | |||||
error_noexit("Raw image must be grayscale"); | |||||
return 0; | |||||
} | |||||
// Create row buffers | |||||
png_bytep *PNGrows = NULL; | |||||
PNGrows = (png_bytep *)malloc(sizeof(png_bytep) * height); | |||||
for (int y = 0; y < height; y++) PNGrows[y] = (png_byte *)malloc(png_get_rowbytes(png, info)); | |||||
// Read image | |||||
png_read_image(png, PNGrows); | |||||
// Tidy up | |||||
fclose(fp); | |||||
png_destroy_read_struct(&png, &info, NULL); | |||||
// Put into prow | |||||
*nrow = height; | |||||
for (int y = 0; y < height; y++) { | |||||
prow[y] = (float *)malloc(sizeof(float) * width); | |||||
for (int x = 0; x < width; x++) prow[y][x] = (float)PNGrows[y][x]; | |||||
} | |||||
return 1; | |||||
} | } | ||||
int readPalette(char *filename, apt_rgb_t **pixels) { | int readPalette(char *filename, apt_rgb_t **pixels) { | ||||
FILE *fp = fopen(filename, "rb"); | |||||
if(!fp) { | |||||
error_noexit("Cannot open palette"); | |||||
return 0; | |||||
} | |||||
// Create reader | |||||
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | |||||
if(!png) { | |||||
fclose(fp); | |||||
return 0; | |||||
} | |||||
png_infop info = png_create_info_struct(png); | |||||
if(!info) { | |||||
fclose(fp); | |||||
return 0; | |||||
} | |||||
png_init_io(png, fp); | |||||
// Read info from header | |||||
png_read_info(png, info); | |||||
int width = png_get_image_width(png, info); | |||||
int height = png_get_image_height(png, info); | |||||
png_byte color_type = png_get_color_type(png, info); | |||||
png_byte bit_depth = png_get_bit_depth(png, info); | |||||
// Check the image | |||||
if(width != 256 && height != 256){ | |||||
error_noexit("Palette must be 256x256"); | |||||
return 0; | |||||
}else if(bit_depth != 8){ | |||||
error_noexit("Palette must be 8 bit color"); | |||||
return 0; | |||||
}else if(color_type != PNG_COLOR_TYPE_RGB){ | |||||
error_noexit("Palette must be RGB"); | |||||
return 0; | |||||
} | |||||
// Create row buffers | |||||
png_bytep *PNGrows = NULL; | |||||
PNGrows = (png_bytep *) malloc(sizeof(png_bytep) * height); | |||||
for(int y = 0; y < height; y++) | |||||
PNGrows[y] = (png_byte *) malloc(png_get_rowbytes(png, info)); | |||||
// Read image | |||||
png_read_image(png, PNGrows); | |||||
// Tidy up | |||||
fclose(fp); | |||||
png_destroy_read_struct(&png, &info, NULL); | |||||
// Put into crow | |||||
for(int y = 0; y < height; y++) { | |||||
pixels[y] = (apt_rgb_t *) malloc(sizeof(apt_rgb_t) * width); | |||||
for(int x = 0; x < width; x++) | |||||
pixels[y][x] = (apt_rgb_t){ | |||||
PNGrows[y][x*3], | |||||
PNGrows[y][x*3 + 1], | |||||
PNGrows[y][x*3 + 2] | |||||
}; | |||||
} | |||||
return 1; | |||||
FILE *fp = fopen(filename, "rb"); | |||||
if (!fp) { | |||||
error_noexit("Cannot open palette"); | |||||
return 0; | |||||
} | |||||
// Create reader | |||||
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | |||||
if (!png) { | |||||
fclose(fp); | |||||
return 0; | |||||
} | |||||
png_infop info = png_create_info_struct(png); | |||||
if (!info) { | |||||
fclose(fp); | |||||
return 0; | |||||
} | |||||
png_init_io(png, fp); | |||||
// Read info from header | |||||
png_read_info(png, info); | |||||
int width = png_get_image_width(png, info); | |||||
int height = png_get_image_height(png, info); | |||||
png_byte color_type = png_get_color_type(png, info); | |||||
png_byte bit_depth = png_get_bit_depth(png, info); | |||||
// Check the image | |||||
if (width != 256 && height != 256) { | |||||
error_noexit("Palette must be 256x256"); | |||||
return 0; | |||||
} else if (bit_depth != 8) { | |||||
error_noexit("Palette must be 8 bit color"); | |||||
return 0; | |||||
} else if (color_type != PNG_COLOR_TYPE_RGB) { | |||||
error_noexit("Palette must be RGB"); | |||||
return 0; | |||||
} | |||||
// Create row buffers | |||||
png_bytep *PNGrows = NULL; | |||||
PNGrows = (png_bytep *)malloc(sizeof(png_bytep) * height); | |||||
for (int y = 0; y < height; y++) PNGrows[y] = (png_byte *)malloc(png_get_rowbytes(png, info)); | |||||
// Read image | |||||
png_read_image(png, PNGrows); | |||||
// Tidy up | |||||
fclose(fp); | |||||
png_destroy_read_struct(&png, &info, NULL); | |||||
// Put into crow | |||||
for (int y = 0; y < height; y++) { | |||||
pixels[y] = (apt_rgb_t *)malloc(sizeof(apt_rgb_t) * width); | |||||
for (int x = 0; x < width; x++) | |||||
pixels[y][x] = (apt_rgb_t){PNGrows[y][x * 3], PNGrows[y][x * 3 + 1], PNGrows[y][x * 3 + 2]}; | |||||
} | |||||
return 1; | |||||
} | } | ||||
void prow2crow(float **prow, int nrow, char *palette, apt_rgb_t **crow){ | |||||
for(int y = 0; y < nrow; y++){ | |||||
crow[y] = (apt_rgb_t *) malloc(sizeof(apt_rgb_t) * APT_IMG_WIDTH); | |||||
for(int x = 0; x < APT_IMG_WIDTH; x++){ | |||||
if(palette == NULL) | |||||
crow[y][x].r = crow[y][x].g = crow[y][x].b = prow[y][x]; | |||||
else | |||||
crow[y][x] = apt_applyPalette(palette, prow[y][x]); | |||||
} | |||||
} | |||||
void prow2crow(float **prow, int nrow, char *palette, apt_rgb_t **crow) { | |||||
for (int y = 0; y < nrow; y++) { | |||||
crow[y] = (apt_rgb_t *)malloc(sizeof(apt_rgb_t) * APT_IMG_WIDTH); | |||||
for (int x = 0; x < APT_IMG_WIDTH; x++) { | |||||
if (palette == NULL) | |||||
crow[y][x].r = crow[y][x].g = crow[y][x].b = prow[y][x]; | |||||
else | |||||
crow[y][x] = apt_applyPalette(palette, prow[y][x]); | |||||
} | |||||
} | |||||
} | } | ||||
int applyUserPalette(float **prow, int nrow, char *filename, apt_rgb_t **crow){ | |||||
apt_rgb_t *pal_row[256]; | |||||
if(!readPalette(filename, pal_row)){ | |||||
error_noexit("Could not read palette"); | |||||
return 0; | |||||
} | |||||
for(int y = 0; y < nrow; y++){ | |||||
for(int x = 0; x < APT_CH_WIDTH; x++){ | |||||
int cha = CLIP(prow[y][x + APT_CHA_OFFSET], 0, 255); | |||||
int chb = CLIP(prow[y][x + APT_CHB_OFFSET], 0, 255); | |||||
crow[y][x + APT_CHA_OFFSET] = pal_row[chb][cha]; | |||||
} | |||||
} | |||||
return 1; | |||||
int applyUserPalette(float **prow, int nrow, char *filename, apt_rgb_t **crow) { | |||||
apt_rgb_t *pal_row[256]; | |||||
if (!readPalette(filename, pal_row)) { | |||||
error_noexit("Could not read palette"); | |||||
return 0; | |||||
} | |||||
for (int y = 0; y < nrow; y++) { | |||||
for (int x = 0; x < APT_CH_WIDTH; x++) { | |||||
int cha = CLIP(prow[y][x + APT_CHA_OFFSET], 0, 255); | |||||
int chb = CLIP(prow[y][x + APT_CHB_OFFSET], 0, 255); | |||||
crow[y][x + APT_CHA_OFFSET] = pal_row[chb][cha]; | |||||
} | |||||
} | |||||
return 1; | |||||
} | } | ||||
int ImageOut(options_t *opts, apt_image_t *img, int offset, int width, char *desc, char chid, char *palette){ | |||||
char outName[512]; | |||||
if(opts->filename == NULL || opts->filename[0] == '\0'){ | |||||
sprintf(outName, "%s/%s-%c.png", opts->path, img->name, chid); | |||||
}else{ | |||||
sprintf(outName, "%s/%s", opts->path, opts->filename); | |||||
} | |||||
png_text meta[] = { | |||||
{PNG_TEXT_COMPRESSION_NONE, "Software", VERSION, sizeof(VERSION)}, | |||||
{PNG_TEXT_COMPRESSION_NONE, "Channel", desc, sizeof(desc)}, | |||||
{PNG_TEXT_COMPRESSION_NONE, "Description", "NOAA satellite image", 20} | |||||
}; | |||||
// Parse image type | |||||
int greyscale = 1; | |||||
switch (chid){ | |||||
case Palleted: | |||||
greyscale = 0; | |||||
break; | |||||
case Temperature: | |||||
greyscale = 0; | |||||
break; | |||||
case Raw_Image: break; | |||||
case Channel_A: break; | |||||
case Channel_B: break; | |||||
} | |||||
// Parse effects | |||||
int crop_telemetry = 0; | |||||
for(unsigned long int i = 0; i < strlen(opts->effects); i++){ | |||||
switch (opts->effects[i]) { | |||||
case Crop_Telemetry: | |||||
if (width == 2080) { | |||||
width -= APT_TOTAL_TELE; | |||||
offset += APT_SYNC_WIDTH + APT_SPC_WIDTH; | |||||
crop_telemetry = 1; | |||||
} | |||||
break; | |||||
case Precipitation_Overlay: | |||||
greyscale = 0; | |||||
break; | |||||
case Flip_Image: break; | |||||
case Denoise: break; | |||||
case Histogram_Equalise: break; | |||||
case Linear_Equalise: break; | |||||
case Crop_Noise: break; | |||||
default: { | |||||
char text[100]; | |||||
sprintf(text, "Unrecognised effect, \"%c\"", opts->effects[i]); | |||||
warning(text); | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
FILE *pngfile; | |||||
// Create writer | |||||
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | |||||
if (!png_ptr) { | |||||
png_destroy_write_struct(&png_ptr, (png_infopp) NULL); | |||||
error_noexit("Could not create a PNG writer"); | |||||
return 0; | |||||
} | |||||
png_infop info_ptr = png_create_info_struct(png_ptr); | |||||
if (!info_ptr) { | |||||
png_destroy_write_struct(&png_ptr, (png_infopp) NULL); | |||||
error_noexit("Could not create a PNG writer"); | |||||
return 0; | |||||
} | |||||
if(greyscale){ | |||||
// Greyscale image | |||||
png_set_IHDR(png_ptr, info_ptr, width, img->nrow, | |||||
8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, | |||||
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); | |||||
}else{ | |||||
// 8 bit RGB image | |||||
png_set_IHDR(png_ptr, info_ptr, width, img->nrow, | |||||
8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, | |||||
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); | |||||
} | |||||
png_set_text(png_ptr, info_ptr, meta, 3); | |||||
png_set_pHYs(png_ptr, info_ptr, 3636, 3636, PNG_RESOLUTION_METER); | |||||
// Init I/O | |||||
pngfile = fopen(outName, "wb"); | |||||
if (!pngfile) { | |||||
error_noexit("Could not open PNG for writing"); | |||||
return 1; | |||||
} | |||||
png_init_io(png_ptr, pngfile); | |||||
png_write_info(png_ptr, info_ptr); | |||||
// Move prow into crow, crow ~ color rows, if required | |||||
apt_rgb_t *crow[APT_MAX_HEIGHT]; | |||||
if(!greyscale){ | |||||
prow2crow(img->prow, img->nrow, palette, crow); | |||||
} | |||||
// Apply a user provided color palette | |||||
if(chid == Palleted){ | |||||
applyUserPalette(img->prow, img->nrow, opts->palette, crow); | |||||
} | |||||
// Precipitation overlay | |||||
if(CONTAINS(opts->effects, Precipitation_Overlay)){ | |||||
for(int y = 0; y < img->nrow; y++){ | |||||
for(int x = 0; x < APT_CH_WIDTH; x++){ | |||||
if(img->prow[y][x + APT_CHB_OFFSET] >= 198) | |||||
crow[y][x + APT_CHB_OFFSET] = crow[y][x + APT_CHA_OFFSET] = apt_applyPalette(apt_PrecipPalette, img->prow[y][x + APT_CHB_OFFSET]-198); | |||||
} | |||||
} | |||||
} | |||||
printf("Writing %s", outName); | |||||
// Float power macro (for gamma adjustment) | |||||
#define POWF(a, b) (b == 1.0 ? a : exp(b * log(a))) | |||||
float a = POWF(255, opts->gamma)/255; | |||||
// Build image | |||||
for (int y = 0; y < img->nrow; y++) { | |||||
png_color pix[APT_IMG_WIDTH]; // Color | |||||
png_byte mpix[APT_IMG_WIDTH]; // Mono | |||||
int skip = 0; | |||||
for (int x = 0; x < width; x++) { | |||||
if(crop_telemetry && x == APT_CH_WIDTH) | |||||
skip += APT_TELE_WIDTH + APT_SYNC_WIDTH + APT_SPC_WIDTH; | |||||
if(greyscale){ | |||||
mpix[x] = POWF(img->prow[y][x + skip + offset], opts->gamma)/a; | |||||
}else{ | |||||
pix[x] = (png_color){ | |||||
POWF(crow[y][x + skip + offset].r, opts->gamma)/a, | |||||
POWF(crow[y][x + skip + offset].g, opts->gamma)/a, | |||||
POWF(crow[y][x + skip + offset].b, opts->gamma)/a | |||||
}; | |||||
} | |||||
} | |||||
if(greyscale){ | |||||
png_write_row(png_ptr, (png_bytep) mpix); | |||||
}else{ | |||||
png_write_row(png_ptr, (png_bytep) pix); | |||||
} | |||||
} | |||||
// Tidy up | |||||
png_write_end(png_ptr, info_ptr); | |||||
fclose(pngfile); | |||||
printf("\nDone\n"); | |||||
png_destroy_write_struct(&png_ptr, &info_ptr); | |||||
return 1; | |||||
int ImageOut(options_t *opts, apt_image_t *img, int offset, int width, char *desc, char chid, char *palette) { | |||||
char outName[512]; | |||||
if (opts->filename == NULL || opts->filename[0] == '\0') { | |||||
sprintf(outName, "%s/%s-%c.png", opts->path, img->name, chid); | |||||
} else { | |||||
sprintf(outName, "%s/%s", opts->path, opts->filename); | |||||
} | |||||
png_text meta[] = {{PNG_TEXT_COMPRESSION_NONE, "Software", VERSION, sizeof(VERSION)}, | |||||
{PNG_TEXT_COMPRESSION_NONE, "Channel", desc, sizeof(desc)}, | |||||
{PNG_TEXT_COMPRESSION_NONE, "Description", "NOAA satellite image", 20}}; | |||||
// Parse image type | |||||
int greyscale = 1; | |||||
switch (chid) { | |||||
case Palleted: | |||||
greyscale = 0; | |||||
break; | |||||
case Temperature: | |||||
greyscale = 0; | |||||
break; | |||||
case Raw_Image: | |||||
break; | |||||
case Channel_A: | |||||
break; | |||||
case Channel_B: | |||||
break; | |||||
} | |||||
// Parse effects | |||||
int crop_telemetry = 0; | |||||
for (unsigned long int i = 0; i < strlen(opts->effects); i++) { | |||||
switch (opts->effects[i]) { | |||||
case Crop_Telemetry: | |||||
if (width == 2080) { | |||||
width -= APT_TOTAL_TELE; | |||||
offset += APT_SYNC_WIDTH + APT_SPC_WIDTH; | |||||
crop_telemetry = 1; | |||||
} | |||||
break; | |||||
case Precipitation_Overlay: | |||||
greyscale = 0; | |||||
break; | |||||
case Flip_Image: | |||||
break; | |||||
case Denoise: | |||||
break; | |||||
case Histogram_Equalise: | |||||
break; | |||||
case Linear_Equalise: | |||||
break; | |||||
case Crop_Noise: | |||||
break; | |||||
default: { | |||||
char text[100]; | |||||
sprintf(text, "Unrecognised effect, \"%c\"", opts->effects[i]); | |||||
warning(text); | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
FILE *pngfile; | |||||
// Create writer | |||||
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | |||||
if (!png_ptr) { | |||||
png_destroy_write_struct(&png_ptr, (png_infopp)NULL); | |||||
error_noexit("Could not create a PNG writer"); | |||||
return 0; | |||||
} | |||||
png_infop info_ptr = png_create_info_struct(png_ptr); | |||||
if (!info_ptr) { | |||||
png_destroy_write_struct(&png_ptr, (png_infopp)NULL); | |||||
error_noexit("Could not create a PNG writer"); | |||||
return 0; | |||||
} | |||||
if (greyscale) { | |||||
// Greyscale image | |||||
png_set_IHDR(png_ptr, info_ptr, width, img->nrow, 8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, | |||||
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); | |||||
} else { | |||||
// 8 bit RGB image | |||||
png_set_IHDR(png_ptr, info_ptr, width, img->nrow, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, | |||||
PNG_FILTER_TYPE_DEFAULT); | |||||
} | |||||
png_set_text(png_ptr, info_ptr, meta, 3); | |||||
png_set_pHYs(png_ptr, info_ptr, 3636, 3636, PNG_RESOLUTION_METER); | |||||
// Init I/O | |||||
pngfile = fopen(outName, "wb"); | |||||
if (!pngfile) { | |||||
error_noexit("Could not open PNG for writing"); | |||||
return 1; | |||||
} | |||||
png_init_io(png_ptr, pngfile); | |||||
png_write_info(png_ptr, info_ptr); | |||||
// Move prow into crow, crow ~ color rows, if required | |||||
apt_rgb_t *crow[APT_MAX_HEIGHT]; | |||||
if (!greyscale) { | |||||
prow2crow(img->prow, img->nrow, palette, crow); | |||||
} | |||||
// Apply a user provided color palette | |||||
if (chid == Palleted) { | |||||
applyUserPalette(img->prow, img->nrow, opts->palette, crow); | |||||
} | |||||
// Precipitation overlay | |||||
if (CONTAINS(opts->effects, Precipitation_Overlay)) { | |||||
for (int y = 0; y < img->nrow; y++) { | |||||
for (int x = 0; x < APT_CH_WIDTH; x++) { | |||||
if (img->prow[y][x + APT_CHB_OFFSET] >= 198) | |||||
crow[y][x + APT_CHB_OFFSET] = crow[y][x + APT_CHA_OFFSET] = | |||||
apt_applyPalette(apt_PrecipPalette, img->prow[y][x + APT_CHB_OFFSET] - 198); | |||||
} | |||||
} | |||||
} | |||||
printf("Writing %s", outName); | |||||
// Float power macro (for gamma adjustment) | |||||
#define POWF(a, b) (b == 1.0 ? a : exp(b * log(a))) | |||||
float a = POWF(255, opts->gamma) / 255; | |||||
// Build image | |||||
for (int y = 0; y < img->nrow; y++) { | |||||
png_color pix[APT_IMG_WIDTH]; // Color | |||||
png_byte mpix[APT_IMG_WIDTH]; // Mono | |||||
int skip = 0; | |||||
for (int x = 0; x < width; x++) { | |||||
if (crop_telemetry && x == APT_CH_WIDTH) skip += APT_TELE_WIDTH + APT_SYNC_WIDTH + APT_SPC_WIDTH; | |||||
if (greyscale) { | |||||
mpix[x] = POWF(img->prow[y][x + skip + offset], opts->gamma) / a; | |||||
} else { | |||||
pix[x] = (png_color){POWF(crow[y][x + skip + offset].r, opts->gamma) / a, | |||||
POWF(crow[y][x + skip + offset].g, opts->gamma) / a, | |||||
POWF(crow[y][x + skip + offset].b, opts->gamma) / a}; | |||||
} | |||||
} | |||||
if (greyscale) { | |||||
png_write_row(png_ptr, (png_bytep)mpix); | |||||
} else { | |||||
png_write_row(png_ptr, (png_bytep)pix); | |||||
} | |||||
} | |||||
// Tidy up | |||||
png_write_end(png_ptr, info_ptr); | |||||
fclose(pngfile); | |||||
printf("\nDone\n"); | |||||
png_destroy_write_struct(&png_ptr, &info_ptr); | |||||
return 1; | |||||
} | } | ||||
// TODO: clean up everthing below this comment | // TODO: clean up everthing below this comment | ||||
@@ -355,65 +351,61 @@ png_structp rt_png_ptr; | |||||
png_infop rt_info_ptr; | png_infop rt_info_ptr; | ||||
FILE *rt_pngfile; | FILE *rt_pngfile; | ||||
int initWriter(options_t *opts, apt_image_t *img, int width, int height, char *desc, char *chid){ | |||||
char outName[384]; | |||||
sprintf(outName, "%s/%s-%s.png", opts->path, img->name, chid); | |||||
png_text meta[] = { | |||||
{PNG_TEXT_COMPRESSION_NONE, "Software", VERSION, sizeof(VERSION)}, | |||||
{PNG_TEXT_COMPRESSION_NONE, "Channel", desc, sizeof(desc)}, | |||||
{PNG_TEXT_COMPRESSION_NONE, "Description", "NOAA satellite image", 20} | |||||
}; | |||||
// Create writer | |||||
rt_png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | |||||
if (!rt_png_ptr) { | |||||
png_destroy_write_struct(&rt_png_ptr, (png_infopp) NULL); | |||||
error_noexit("Could not create a PNG writer"); | |||||
return 0; | |||||
} | |||||
rt_info_ptr = png_create_info_struct(rt_png_ptr); | |||||
if (!rt_info_ptr) { | |||||
png_destroy_write_struct(&rt_png_ptr, (png_infopp) NULL); | |||||
error_noexit("Could not create a PNG writer"); | |||||
return 0; | |||||
} | |||||
// Greyscale image | |||||
png_set_IHDR(rt_png_ptr, rt_info_ptr, width, height, | |||||
8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, | |||||
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); | |||||
png_set_text(rt_png_ptr, rt_info_ptr, meta, 3); | |||||
// Channel = 25cm wide | |||||
png_set_pHYs(rt_png_ptr, rt_info_ptr, 3636, 3636, PNG_RESOLUTION_METER); | |||||
// Init I/O | |||||
rt_pngfile = fopen(outName, "wb"); | |||||
if (!rt_pngfile) { | |||||
error_noexit("Could not open PNG for writing"); | |||||
return 0; | |||||
} | |||||
png_init_io(rt_png_ptr, rt_pngfile); | |||||
png_write_info(rt_png_ptr, rt_info_ptr); | |||||
// Turn off compression | |||||
png_set_compression_level(rt_png_ptr, 0); | |||||
return 1; | |||||
int initWriter(options_t *opts, apt_image_t *img, int width, int height, char *desc, char *chid) { | |||||
char outName[384]; | |||||
sprintf(outName, "%s/%s-%s.png", opts->path, img->name, chid); | |||||
png_text meta[] = {{PNG_TEXT_COMPRESSION_NONE, "Software", VERSION, sizeof(VERSION)}, | |||||
{PNG_TEXT_COMPRESSION_NONE, "Channel", desc, sizeof(desc)}, | |||||
{PNG_TEXT_COMPRESSION_NONE, "Description", "NOAA satellite image", 20}}; | |||||
// Create writer | |||||
rt_png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | |||||
if (!rt_png_ptr) { | |||||
png_destroy_write_struct(&rt_png_ptr, (png_infopp)NULL); | |||||
error_noexit("Could not create a PNG writer"); | |||||
return 0; | |||||
} | |||||
rt_info_ptr = png_create_info_struct(rt_png_ptr); | |||||
if (!rt_info_ptr) { | |||||
png_destroy_write_struct(&rt_png_ptr, (png_infopp)NULL); | |||||
error_noexit("Could not create a PNG writer"); | |||||
return 0; | |||||
} | |||||
// Greyscale image | |||||
png_set_IHDR(rt_png_ptr, rt_info_ptr, width, height, 8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, | |||||
PNG_FILTER_TYPE_DEFAULT); | |||||
png_set_text(rt_png_ptr, rt_info_ptr, meta, 3); | |||||
// Channel = 25cm wide | |||||
png_set_pHYs(rt_png_ptr, rt_info_ptr, 3636, 3636, PNG_RESOLUTION_METER); | |||||
// Init I/O | |||||
rt_pngfile = fopen(outName, "wb"); | |||||
if (!rt_pngfile) { | |||||
error_noexit("Could not open PNG for writing"); | |||||
return 0; | |||||
} | |||||
png_init_io(rt_png_ptr, rt_pngfile); | |||||
png_write_info(rt_png_ptr, rt_info_ptr); | |||||
// Turn off compression | |||||
png_set_compression_level(rt_png_ptr, 0); | |||||
return 1; | |||||
} | } | ||||
void pushRow(float *row, int width){ | |||||
png_byte pix[APT_IMG_WIDTH]; | |||||
for(int i = 0; i < width; i++) | |||||
pix[i] = row[i]; | |||||
void pushRow(float *row, int width) { | |||||
png_byte pix[APT_IMG_WIDTH]; | |||||
for (int i = 0; i < width; i++) pix[i] = row[i]; | |||||
png_write_row(rt_png_ptr, (png_bytep) pix); | |||||
png_write_row(rt_png_ptr, (png_bytep)pix); | |||||
} | } | ||||
void closeWriter(){ | |||||
png_write_end(rt_png_ptr, rt_info_ptr); | |||||
fclose(rt_pngfile); | |||||
png_destroy_write_struct(&rt_png_ptr, &rt_info_ptr); | |||||
void closeWriter() { | |||||
png_write_end(rt_png_ptr, rt_info_ptr); | |||||
fclose(rt_pngfile); | |||||
png_destroy_write_struct(&rt_png_ptr, &rt_info_ptr); | |||||
} | } |
@@ -16,77 +16,65 @@ | |||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | * along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
*/ | */ | ||||
static const float hilbert_filter[] = { 0.0205361, 0.0219524, 0.0235785, 0.0254648, 0.0276791, 0.0303152, | |||||
0.0335063, 0.0374482, 0.0424413, 0.0489708, 0.0578745, 0.0707355, 0.0909457, 0.127324, 0.212207, 0.63662, | |||||
-0.63662, -0.212207, -0.127324, -0.0909457, -0.0707355, -0.0578745, -0.0489708, -0.0424413, -0.0374482, | |||||
-0.0335063, -0.0303152, -0.0276791, -0.0254648, -0.0235785, -0.0219524, -0.0205361 }; | |||||
#define HILBERT_FILTER_SIZE (sizeof(hilbert_filter)/sizeof(hilbert_filter[0])) | |||||
static const float hilbert_filter[] = {0.0205361, 0.0219524, 0.0235785, 0.0254648, 0.0276791, 0.0303152, 0.0335063, | |||||
0.0374482, 0.0424413, 0.0489708, 0.0578745, 0.0707355, 0.0909457, 0.127324, | |||||
0.212207, 0.63662, -0.63662, -0.212207, -0.127324, -0.0909457, -0.0707355, | |||||
-0.0578745, -0.0489708, -0.0424413, -0.0374482, -0.0335063, -0.0303152, -0.0276791, | |||||
-0.0254648, -0.0235785, -0.0219524, -0.0205361}; | |||||
#define HILBERT_FILTER_SIZE (sizeof(hilbert_filter) / sizeof(hilbert_filter[0])) | |||||
static const float low_pass[] = { -3.37279e-04, -8.80292e-06, -3.96418e-04, -1.78544e-04, -5.27511e-04, | |||||
-3.75376e-04, -6.95337e-04, -5.93148e-04, -8.79730e-04, -8.15327e-04, -1.05669e-03, -1.01377e-03, | |||||
-1.19836e-03, -1.15443e-03, -1.26937e-03, -1.20955e-03, -1.23904e-03, -1.15302e-03, -1.08660e-03, | |||||
-9.64235e-04, -8.02450e-04, -6.46202e-04, -3.95376e-04, -2.18096e-04, 1.11906e-04, 2.89567e-04, | |||||
6.67167e-04, 8.19039e-04, 1.21725e-03, 1.30556e-03, 1.69365e-03, 1.68588e-03, 2.03277e-03, 1.90159e-03, | |||||
2.18455e-03, 1.90833e-03, 2.12100e-03, 1.69052e-03, 1.77484e-03, 1.42542e-03, 1.18292e-03, 8.66979e-04, | |||||
5.54161e-04, 2.15793e-04, -1.11623e-04, -4.35173e-04, -7.27194e-04, -9.91551e-04, -1.20407e-03, | |||||
-1.37032e-03, -1.46991e-03, -1.51120e-03, -1.48008e-03, -1.39047e-03, -1.23115e-03, -1.02128e-03, | |||||
-7.60099e-04, -4.68008e-04, -1.46339e-04, 1.80867e-04, 5.11244e-04, 8.19243e-04, 1.09739e-03, 1.32668e-03, | |||||
1.50632e-03, 1.61522e-03, 1.66246e-03, 1.62390e-03, 1.52430e-03, 1.34273e-03, 1.10736e-03, 8.10335e-04, | |||||
4.76814e-04, 1.13622e-04, -2.64150e-04, -6.26595e-04, -9.95436e-04, -1.27846e-03, -1.54080e-03, | |||||
-1.74292e-03, -1.86141e-03, -1.89318e-03, -1.83969e-03, -1.69770e-03, -1.47938e-03, -1.18696e-03, | |||||
-8.37003e-04, -4.39507e-04, -1.56907e-05, 4.19904e-04, 8.43172e-04, 1.23827e-03, 1.58411e-03, | |||||
1.86382e-03, 2.06312e-03, 2.17177e-03, 2.18121e-03, 2.08906e-03, 1.89772e-03, 1.61153e-03, | |||||
1.24507e-03, 8.13976e-04, 3.29944e-04, -1.74591e-04, -6.83619e-04, -1.17826e-03, -1.61659e-03, | |||||
-2.00403e-03, -2.29070e-03, -2.49179e-03, -2.56546e-03, -2.53448e-03, -2.37032e-03, -2.10060e-03, | |||||
-1.72140e-03, -1.24542e-03, -7.15425e-04, -1.24964e-04, 4.83736e-04, 1.08328e-03, 1.64530e-03, | |||||
2.14503e-03, 2.55400e-03, 2.85589e-03, 3.02785e-03, 3.06271e-03, 2.95067e-03, 2.69770e-03, | |||||
2.30599e-03, 1.79763e-03, 1.18587e-03, 5.04003e-04, -2.23591e-04, -9.57591e-04, -1.66939e-03, | |||||
-2.31717e-03, -2.87636e-03, -3.31209e-03, -3.60506e-03, -3.73609e-03, -3.69208e-03, -3.44913e-03, | |||||
-3.06572e-03, -2.50229e-03, -1.80630e-03, -1.00532e-03, -1.22305e-04, 7.83910e-04, 1.69402e-03, | |||||
2.53826e-03, 3.30312e-03, 3.91841e-03, 4.38017e-03, 4.63546e-03, 4.68091e-03, 4.50037e-03, | |||||
4.09614e-03, 3.47811e-03, 2.67306e-03, 1.70418e-03, 6.20542e-04, -5.36994e-04, -1.70981e-03, | |||||
-2.84712e-03, -3.88827e-03, -4.78659e-03, -5.48593e-03, -5.95049e-03, -6.14483e-03, -6.05118e-03, | |||||
-5.65829e-03, -4.97525e-03, -4.01796e-03, -2.82224e-03, -1.43003e-03, 1.00410e-04, 1.71169e-03, | |||||
3.31983e-03, 4.87796e-03, 6.23237e-03, 7.31013e-03, 8.20642e-03, 8.67374e-03, 8.77681e-03, | |||||
8.43444e-03, 7.66794e-03, 6.46827e-03, 4.87294e-03, 2.92923e-03, 6.98913e-04, -1.72126e-03, | |||||
-4.24785e-03, -6.75380e-03, -9.13309e-03, -1.12532e-02, -1.30038e-02, -1.42633e-02, -1.49338e-02, | |||||
-1.49145e-02, -1.41484e-02, -1.25761e-02, -1.01870e-02, -6.97432e-03, -2.97910e-03, 1.75386e-03, | |||||
7.11899e-03, 1.30225e-02, 1.93173e-02, 2.58685e-02, 3.24965e-02, 3.90469e-02, 4.53316e-02, | |||||
5.11931e-02, 5.64604e-02, 6.09924e-02, 6.46584e-02, 6.73547e-02, 6.90049e-02, 6.97096e-02, | |||||
6.90049e-02, 6.73547e-02, 6.46584e-02, 6.09924e-02, 5.64604e-02, 5.11931e-02, 4.53316e-02, | |||||
3.90469e-02, 3.24965e-02, 2.58685e-02, 1.93173e-02, 1.30225e-02, 7.11899e-03, 1.75386e-03, | |||||
-2.97910e-03, -6.97432e-03, -1.01870e-02, -1.25761e-02, -1.41484e-02, -1.49145e-02, -1.49338e-02, | |||||
-1.42633e-02, -1.30038e-02, -1.12532e-02, -9.13309e-03, -6.75380e-03, -4.24785e-03, -1.72126e-03, | |||||
6.98913e-04, 2.92923e-03, 4.87294e-03, 6.46827e-03, 7.66794e-03, 8.43444e-03, 8.77681e-03, | |||||
8.67374e-03, 8.20642e-03, 7.31013e-03, 6.23237e-03, 4.87796e-03, 3.31983e-03, 1.71169e-03, | |||||
1.00410e-04, -1.43003e-03, -2.82224e-03, -4.01796e-03, -4.97525e-03, -5.65829e-03, -6.05118e-03, | |||||
-6.14483e-03, -5.95049e-03, -5.48593e-03, -4.78659e-03, -3.88827e-03, -2.84712e-03, -1.70981e-03, | |||||
-5.36994e-04, 6.20542e-04, 1.70418e-03, 2.67306e-03, 3.47811e-03, 4.09614e-03, 4.50037e-03, | |||||
4.68091e-03, 4.63546e-03, 4.38017e-03, 3.91841e-03, 3.30312e-03, 2.53826e-03, 1.69402e-03, | |||||
7.83910e-04, -1.22305e-04, -1.00532e-03, -1.80630e-03, -2.50229e-03, -3.06572e-03, -3.44913e-03, | |||||
-3.69208e-03, -3.73609e-03, -3.60506e-03, -3.31209e-03, -2.87636e-03, -2.31717e-03, -1.66939e-03, | |||||
-9.57591e-04, -2.23591e-04, 5.04003e-04, 1.18587e-03, 1.79763e-03, 2.30599e-03, 2.69770e-03, | |||||
2.95067e-03, 3.06271e-03, 3.02785e-03, 2.85589e-03, 2.55400e-03, 2.14503e-03, 1.64530e-03, | |||||
1.08328e-03, 4.83736e-04, -1.24964e-04, -7.15425e-04, -1.24542e-03, -1.72140e-03, -2.10060e-03, | |||||
-2.37032e-03, -2.53448e-03, -2.56546e-03, -2.49179e-03, -2.29070e-03, -2.00403e-03, -1.61659e-03, | |||||
-1.17826e-03, -6.83619e-04, -1.74591e-04, 3.29944e-04, 8.13976e-04, 1.24507e-03, 1.61153e-03, | |||||
1.89772e-03, 2.08906e-03, 2.18121e-03, 2.17177e-03, 2.06312e-03, 1.86382e-03, 1.58411e-03, | |||||
1.23827e-03, 8.43172e-04, 4.19904e-04, -1.56907e-05, -4.39507e-04, -8.37003e-04, -1.18696e-03, | |||||
-1.47938e-03, -1.69770e-03, -1.83969e-03, -1.89318e-03, -1.86141e-03, -1.74292e-03, -1.54080e-03, | |||||
-1.27846e-03, -9.95436e-04, -6.26595e-04, -2.64150e-04, 1.13622e-04, 4.76814e-04, 8.10335e-04, | |||||
1.10736e-03, 1.34273e-03, 1.52430e-03, 1.62390e-03, 1.66246e-03, 1.61522e-03, 1.50632e-03, | |||||
1.32668e-03, 1.09739e-03, 8.19243e-04, 5.11244e-04, 1.80867e-04, -1.46339e-04, -4.68008e-04, | |||||
-7.60099e-04, -1.02128e-03, -1.23115e-03, -1.39047e-03, -1.48008e-03, -1.51120e-03, -1.46991e-03, | |||||
-1.37032e-03, -1.20407e-03, -9.91551e-04, -7.27194e-04, -4.35173e-04, -1.11623e-04, 2.15793e-04, | |||||
5.54161e-04, 8.66979e-04, 1.18292e-03, 1.42542e-03, 1.77484e-03, 1.69052e-03, 2.12100e-03, | |||||
1.90833e-03, 2.18455e-03, 1.90159e-03, 2.03277e-03, 1.68588e-03, 1.69365e-03, 1.30556e-03, | |||||
1.21725e-03, 8.19039e-04, 6.67167e-04, 2.89567e-04, 1.11906e-04, -2.18096e-04, -3.95376e-04, | |||||
-6.46202e-04, -8.02450e-04, -9.64235e-04, -1.08660e-03, -1.15302e-03, -1.23904e-03, -1.20955e-03, | |||||
-1.26937e-03, -1.15443e-03, -1.19836e-03, -1.01377e-03, -1.05669e-03, -8.15327e-04, -8.79730e-04, | |||||
-5.93148e-04, -6.95337e-04, -3.75376e-04, -5.27511e-04, -1.78544e-04, -3.96418e-04, -8.80292e-06, | |||||
-3.37279e-04 }; | |||||
#define LOW_PASS_SIZE (sizeof(low_pass)/sizeof(low_pass[0])) | |||||
static const float low_pass[] = { | |||||
-3.37279e-04, -8.80292e-06, -3.96418e-04, -1.78544e-04, -5.27511e-04, -3.75376e-04, -6.95337e-04, -5.93148e-04, -8.79730e-04, | |||||
-8.15327e-04, -1.05669e-03, -1.01377e-03, -1.19836e-03, -1.15443e-03, -1.26937e-03, -1.20955e-03, -1.23904e-03, -1.15302e-03, | |||||
-1.08660e-03, -9.64235e-04, -8.02450e-04, -6.46202e-04, -3.95376e-04, -2.18096e-04, 1.11906e-04, 2.89567e-04, 6.67167e-04, | |||||
8.19039e-04, 1.21725e-03, 1.30556e-03, 1.69365e-03, 1.68588e-03, 2.03277e-03, 1.90159e-03, 2.18455e-03, 1.90833e-03, | |||||
2.12100e-03, 1.69052e-03, 1.77484e-03, 1.42542e-03, 1.18292e-03, 8.66979e-04, 5.54161e-04, 2.15793e-04, -1.11623e-04, | |||||
-4.35173e-04, -7.27194e-04, -9.91551e-04, -1.20407e-03, -1.37032e-03, -1.46991e-03, -1.51120e-03, -1.48008e-03, -1.39047e-03, | |||||
-1.23115e-03, -1.02128e-03, -7.60099e-04, -4.68008e-04, -1.46339e-04, 1.80867e-04, 5.11244e-04, 8.19243e-04, 1.09739e-03, | |||||
1.32668e-03, 1.50632e-03, 1.61522e-03, 1.66246e-03, 1.62390e-03, 1.52430e-03, 1.34273e-03, 1.10736e-03, 8.10335e-04, | |||||
4.76814e-04, 1.13622e-04, -2.64150e-04, -6.26595e-04, -9.95436e-04, -1.27846e-03, -1.54080e-03, -1.74292e-03, -1.86141e-03, | |||||
-1.89318e-03, -1.83969e-03, -1.69770e-03, -1.47938e-03, -1.18696e-03, -8.37003e-04, -4.39507e-04, -1.56907e-05, 4.19904e-04, | |||||
8.43172e-04, 1.23827e-03, 1.58411e-03, 1.86382e-03, 2.06312e-03, 2.17177e-03, 2.18121e-03, 2.08906e-03, 1.89772e-03, | |||||
1.61153e-03, 1.24507e-03, 8.13976e-04, 3.29944e-04, -1.74591e-04, -6.83619e-04, -1.17826e-03, -1.61659e-03, -2.00403e-03, | |||||
-2.29070e-03, -2.49179e-03, -2.56546e-03, -2.53448e-03, -2.37032e-03, -2.10060e-03, -1.72140e-03, -1.24542e-03, -7.15425e-04, | |||||
-1.24964e-04, 4.83736e-04, 1.08328e-03, 1.64530e-03, 2.14503e-03, 2.55400e-03, 2.85589e-03, 3.02785e-03, 3.06271e-03, | |||||
2.95067e-03, 2.69770e-03, 2.30599e-03, 1.79763e-03, 1.18587e-03, 5.04003e-04, -2.23591e-04, -9.57591e-04, -1.66939e-03, | |||||
-2.31717e-03, -2.87636e-03, -3.31209e-03, -3.60506e-03, -3.73609e-03, -3.69208e-03, -3.44913e-03, -3.06572e-03, -2.50229e-03, | |||||
-1.80630e-03, -1.00532e-03, -1.22305e-04, 7.83910e-04, 1.69402e-03, 2.53826e-03, 3.30312e-03, 3.91841e-03, 4.38017e-03, | |||||
4.63546e-03, 4.68091e-03, 4.50037e-03, 4.09614e-03, 3.47811e-03, 2.67306e-03, 1.70418e-03, 6.20542e-04, -5.36994e-04, | |||||
-1.70981e-03, -2.84712e-03, -3.88827e-03, -4.78659e-03, -5.48593e-03, -5.95049e-03, -6.14483e-03, -6.05118e-03, -5.65829e-03, | |||||
-4.97525e-03, -4.01796e-03, -2.82224e-03, -1.43003e-03, 1.00410e-04, 1.71169e-03, 3.31983e-03, 4.87796e-03, 6.23237e-03, | |||||
7.31013e-03, 8.20642e-03, 8.67374e-03, 8.77681e-03, 8.43444e-03, 7.66794e-03, 6.46827e-03, 4.87294e-03, 2.92923e-03, | |||||
6.98913e-04, -1.72126e-03, -4.24785e-03, -6.75380e-03, -9.13309e-03, -1.12532e-02, -1.30038e-02, -1.42633e-02, -1.49338e-02, | |||||
-1.49145e-02, -1.41484e-02, -1.25761e-02, -1.01870e-02, -6.97432e-03, -2.97910e-03, 1.75386e-03, 7.11899e-03, 1.30225e-02, | |||||
1.93173e-02, 2.58685e-02, 3.24965e-02, 3.90469e-02, 4.53316e-02, 5.11931e-02, 5.64604e-02, 6.09924e-02, 6.46584e-02, | |||||
6.73547e-02, 6.90049e-02, 6.97096e-02, 6.90049e-02, 6.73547e-02, 6.46584e-02, 6.09924e-02, 5.64604e-02, 5.11931e-02, | |||||
4.53316e-02, 3.90469e-02, 3.24965e-02, 2.58685e-02, 1.93173e-02, 1.30225e-02, 7.11899e-03, 1.75386e-03, -2.97910e-03, | |||||
-6.97432e-03, -1.01870e-02, -1.25761e-02, -1.41484e-02, -1.49145e-02, -1.49338e-02, -1.42633e-02, -1.30038e-02, -1.12532e-02, | |||||
-9.13309e-03, -6.75380e-03, -4.24785e-03, -1.72126e-03, 6.98913e-04, 2.92923e-03, 4.87294e-03, 6.46827e-03, 7.66794e-03, | |||||
8.43444e-03, 8.77681e-03, 8.67374e-03, 8.20642e-03, 7.31013e-03, 6.23237e-03, 4.87796e-03, 3.31983e-03, 1.71169e-03, | |||||
1.00410e-04, -1.43003e-03, -2.82224e-03, -4.01796e-03, -4.97525e-03, -5.65829e-03, -6.05118e-03, -6.14483e-03, -5.95049e-03, | |||||
-5.48593e-03, -4.78659e-03, -3.88827e-03, -2.84712e-03, -1.70981e-03, -5.36994e-04, 6.20542e-04, 1.70418e-03, 2.67306e-03, | |||||
3.47811e-03, 4.09614e-03, 4.50037e-03, 4.68091e-03, 4.63546e-03, 4.38017e-03, 3.91841e-03, 3.30312e-03, 2.53826e-03, | |||||
1.69402e-03, 7.83910e-04, -1.22305e-04, -1.00532e-03, -1.80630e-03, -2.50229e-03, -3.06572e-03, -3.44913e-03, -3.69208e-03, | |||||
-3.73609e-03, -3.60506e-03, -3.31209e-03, -2.87636e-03, -2.31717e-03, -1.66939e-03, -9.57591e-04, -2.23591e-04, 5.04003e-04, | |||||
1.18587e-03, 1.79763e-03, 2.30599e-03, 2.69770e-03, 2.95067e-03, 3.06271e-03, 3.02785e-03, 2.85589e-03, 2.55400e-03, | |||||
2.14503e-03, 1.64530e-03, 1.08328e-03, 4.83736e-04, -1.24964e-04, -7.15425e-04, -1.24542e-03, -1.72140e-03, -2.10060e-03, | |||||
-2.37032e-03, -2.53448e-03, -2.56546e-03, -2.49179e-03, -2.29070e-03, -2.00403e-03, -1.61659e-03, -1.17826e-03, -6.83619e-04, | |||||
-1.74591e-04, 3.29944e-04, 8.13976e-04, 1.24507e-03, 1.61153e-03, 1.89772e-03, 2.08906e-03, 2.18121e-03, 2.17177e-03, | |||||
2.06312e-03, 1.86382e-03, 1.58411e-03, 1.23827e-03, 8.43172e-04, 4.19904e-04, -1.56907e-05, -4.39507e-04, -8.37003e-04, | |||||
-1.18696e-03, -1.47938e-03, -1.69770e-03, -1.83969e-03, -1.89318e-03, -1.86141e-03, -1.74292e-03, -1.54080e-03, -1.27846e-03, | |||||
-9.95436e-04, -6.26595e-04, -2.64150e-04, 1.13622e-04, 4.76814e-04, 8.10335e-04, 1.10736e-03, 1.34273e-03, 1.52430e-03, | |||||
1.62390e-03, 1.66246e-03, 1.61522e-03, 1.50632e-03, 1.32668e-03, 1.09739e-03, 8.19243e-04, 5.11244e-04, 1.80867e-04, | |||||
-1.46339e-04, -4.68008e-04, -7.60099e-04, -1.02128e-03, -1.23115e-03, -1.39047e-03, -1.48008e-03, -1.51120e-03, -1.46991e-03, | |||||
-1.37032e-03, -1.20407e-03, -9.91551e-04, -7.27194e-04, -4.35173e-04, -1.11623e-04, 2.15793e-04, 5.54161e-04, 8.66979e-04, | |||||
1.18292e-03, 1.42542e-03, 1.77484e-03, 1.69052e-03, 2.12100e-03, 1.90833e-03, 2.18455e-03, 1.90159e-03, 2.03277e-03, | |||||
1.68588e-03, 1.69365e-03, 1.30556e-03, 1.21725e-03, 8.19039e-04, 6.67167e-04, 2.89567e-04, 1.11906e-04, -2.18096e-04, | |||||
-3.95376e-04, -6.46202e-04, -8.02450e-04, -9.64235e-04, -1.08660e-03, -1.15302e-03, -1.23904e-03, -1.20955e-03, -1.26937e-03, | |||||
-1.15443e-03, -1.19836e-03, -1.01377e-03, -1.05669e-03, -8.15327e-04, -8.79730e-04, -5.93148e-04, -6.95337e-04, -3.75376e-04, | |||||
-5.27511e-04, -1.78544e-04, -3.96418e-04, -8.80292e-06, -3.37279e-04}; | |||||
#define LOW_PASS_SIZE (sizeof(low_pass) / sizeof(low_pass[0])) | |||||
static const float sync_pattern[] = { -14, -14, -14, 18, 18, -14, -14, 18, 18, -14, -14, 18, 18, -14, -14, | |||||
18, 18, -14, -14, 18, 18, -14, -14, 18, 18, -14, -14, 18, 18, -14, -14, -14 }; | |||||
#define SYNC_PATTERN_SIZE (sizeof(sync_pattern)/sizeof(sync_pattern[0])) | |||||
static const float sync_pattern[] = {-14, -14, -14, 18, 18, -14, -14, 18, 18, -14, -14, 18, 18, -14, -14, 18, | |||||
18, -14, -14, 18, 18, -14, -14, 18, 18, -14, -14, 18, 18, -14, -14, -14}; | |||||
#define SYNC_PATTERN_SIZE (sizeof(sync_pattern) / sizeof(sync_pattern[0])) |
@@ -46,6 +46,4 @@ float clamp(float x, float hi, float lo) { | |||||
return x; | return x; | ||||
} | } | ||||
float clamp_half(float x, float hi) { | |||||
return clamp(x, hi, -hi); | |||||
} | |||||
float clamp_half(float x, float hi) { return clamp(x, hi, -hi); } |
@@ -20,8 +20,8 @@ | |||||
#define M_PIf 3.14159265358979323846f | #define M_PIf 3.14159265358979323846f | ||||
#define M_TAUf (M_PIf * 2.0f) | #define M_TAUf (M_PIf * 2.0f) | ||||
#define MIN(a,b) (((a) < (b)) ? (a) : (b)) | |||||
#define MAX(a,b) (((a) > (b)) ? (a) : (b)) | |||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b)) | |||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b)) | |||||
#ifndef UTIL_H | #ifndef UTIL_H | ||||
#define UTIL_H | #define UTIL_H | ||||