@@ -44,6 +44,11 @@ if(PNG_FOUND AND SNDFILE_LIBRARIES AND SNDFILE_INCLUDE_DIR) | |||
target_compile_options(aptdec-cli PRIVATE -Wall -Wextra -pedantic -Wno-missing-field-initializers) | |||
endif() | |||
if (USE_ADDRESS_SANITIZER) | |||
target_compile_options(aptdec-cli PRIVATE -fsanitize=address) | |||
target_link_options(aptdec-cli PRIVATE -fsanitize=address) | |||
endif() | |||
install(TARGETS aptdec-cli RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) | |||
file(GLOB LUTS luts/*.png) | |||
install(FILES ${LUTS} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/aptdec/luts) | |||
@@ -75,6 +80,10 @@ else() | |||
target_link_libraries(aptdec PRIVATE m) | |||
target_compile_options(aptdec PRIVATE -Wall -Wextra -pedantic -Wno-missing-field-initializers) | |||
endif() | |||
if (USE_ADDRESS_SANITIZER) | |||
target_compile_options(aptdec PRIVATE -fsanitize=address) | |||
target_link_options(aptdec PRIVATE -fsanitize=address) | |||
endif() | |||
install(TARGETS aptdec PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) | |||
@@ -67,7 +67,6 @@ static int freq_from_filename(const char *filename); | |||
static int satid_from_freq(int freq); | |||
static size_t callback(float *samples, size_t count, void *context); | |||
static void write_line(writer_t *png, float *row); | |||
apt_image_t strip(apt_image_t img); | |||
int array_contains(char **array, char *value, size_t n); | |||
static volatile int sigint_stop = 0; | |||
@@ -183,7 +182,7 @@ static int process_file(const char *path, options_t *opts) { | |||
if (opts->realtime) { | |||
char filename[269] = { 0 }; | |||
sprintf(filename, "%s-decoding.png", name); | |||
realtime_png = writer_init(filename, APT_REGION_FULL, APTDEC_MAX_HEIGHT, PNG_COLOR_TYPE_GRAY, "Unknown"); | |||
realtime_png = writer_init(filename, APTDEC_REGION_FULL, APTDEC_MAX_HEIGHT, PNG_COLOR_TYPE_GRAY, "Unknown"); | |||
// Capture Ctrl+C | |||
signal(SIGINT, sigint_handler); | |||
@@ -209,10 +208,10 @@ static int process_file(const char *path, options_t *opts) { | |||
} | |||
// Decode image | |||
float *data = calloc(APT_IMG_WIDTH * (APTDEC_MAX_HEIGHT+1), sizeof(float)); | |||
float *data = calloc(APTDEC_IMG_WIDTH * (APTDEC_MAX_HEIGHT+1), sizeof(float)); | |||
size_t rows; | |||
for (rows = 0; rows < APTDEC_MAX_HEIGHT; rows++) { | |||
float *row = &data[rows * APT_IMG_WIDTH]; | |||
float *row = &data[rows * APTDEC_IMG_WIDTH]; | |||
// Break the loop when there are no more samples or the process has been sent SIGINT | |||
if (aptdec_getrow(aptdec, row, callback, &audioFile) == 0 || sigint_stop) { | |||
@@ -220,7 +219,10 @@ static int process_file(const char *path, options_t *opts) { | |||
} | |||
if (opts->realtime) { | |||
#pragma GCC diagnostic push | |||
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" | |||
write_line(realtime_png, row); | |||
#pragma GCC diagnostic pop | |||
} | |||
fprintf(stderr, "Row: %zu/%zu\r", rows+1, audioFile.info.frames/audioFile.info.samplerate * 2); | |||
@@ -245,7 +247,7 @@ static int process_file(const char *path, options_t *opts) { | |||
// Normalize | |||
int error; | |||
apt_image_t img = apt_normalize(data, rows, opts->satellite, &error); | |||
aptdec_image_t img = aptdec_normalize(data, rows, opts->satellite, &error); | |||
free(data); | |||
if (error) { | |||
error_noexit("Normalization failed"); | |||
@@ -274,13 +276,13 @@ static int process_file(const char *path, options_t *opts) { | |||
for (size_t i = 0; i < effects_len; i++) { | |||
if (strcmp(effects[i], "crop") == 0) { | |||
apt_crop(&img); | |||
aptdec_crop(&img); | |||
} else if (strcmp(effects[i], "denoise") == 0) { | |||
apt_denoise(&img, APT_REGION_CHA); | |||
apt_denoise(&img, APT_REGION_CHB); | |||
aptdec_denoise(&img, APTDEC_REGION_CHA); | |||
aptdec_denoise(&img, APTDEC_REGION_CHB); | |||
} else if (strcmp(effects[i], "flip") == 0) { | |||
apt_flip(&img, APT_REGION_CHA); | |||
apt_flip(&img, APT_REGION_CHB); | |||
aptdec_flip(&img, APTDEC_REGION_CHA); | |||
aptdec_flip(&img, APTDEC_REGION_CHB); | |||
} | |||
} | |||
@@ -300,11 +302,11 @@ static int process_file(const char *path, options_t *opts) { | |||
sprintf(description, "Calibrated thermal image, channel %s - %s", channel_name[img.ch[1]], channel_desc[img.ch[1]]); | |||
// Perform visible calibration | |||
apt_image_t _img = apt_image_clone(img); | |||
apt_calibrate_thermal(&_img, APT_REGION_CHA); | |||
aptdec_image_t _img = aptdec_image_clone(img); | |||
aptdec_calibrate_thermal(&_img, APTDEC_REGION_CHA); | |||
writer_t *writer = writer_init(filename, APT_REGION_CHB, img.rows, PNG_COLOR_TYPE_RGB, description); | |||
writer_write_image_gradient(writer, &_img, temperature_gradient); | |||
writer_t *writer = writer_init(filename, APTDEC_REGION_CHB, img.rows, PNG_COLOR_TYPE_RGB, description); | |||
writer_write_image_gradient(writer, &_img, aptdec_temperature_gradient); | |||
writer_free(writer); | |||
free(_img.data); | |||
@@ -319,10 +321,10 @@ static int process_file(const char *path, options_t *opts) { | |||
sprintf(description, "Calibrated visible image, channel %s - %s", channel_name[img.ch[0]], channel_desc[img.ch[0]]); | |||
// Perform visible calibration | |||
apt_image_t _img = apt_image_clone(img); | |||
apt_calibrate_visible(&_img, APT_REGION_CHA); | |||
aptdec_image_t _img = aptdec_image_clone(img); | |||
aptdec_calibrate_visible(&_img, APTDEC_REGION_CHA); | |||
writer_t *writer = writer_init(filename, APT_REGION_CHA, img.rows, PNG_COLOR_TYPE_GRAY, description); | |||
writer_t *writer = writer_init(filename, APTDEC_REGION_CHA, img.rows, PNG_COLOR_TYPE_GRAY, description); | |||
writer_write_image(writer, &_img); | |||
writer_free(writer); | |||
@@ -335,11 +337,11 @@ static int process_file(const char *path, options_t *opts) { | |||
for (size_t i = 0; i < effects_len; i++) { | |||
if (strcmp(effects[i], "stretch") == 0) { | |||
apt_stretch(&img, APT_REGION_CHA); | |||
apt_stretch(&img, APT_REGION_CHB); | |||
aptdec_stretch(&img, APTDEC_REGION_CHA); | |||
aptdec_stretch(&img, APTDEC_REGION_CHB); | |||
} else if (strcmp(effects[i], "equalize") == 0) { | |||
apt_equalize(&img, APT_REGION_CHA); | |||
apt_equalize(&img, APT_REGION_CHB); | |||
aptdec_equalize(&img, APTDEC_REGION_CHA); | |||
aptdec_equalize(&img, APTDEC_REGION_CHB); | |||
} | |||
} | |||
@@ -365,12 +367,13 @@ static int process_file(const char *path, options_t *opts) { | |||
writer_t *writer; | |||
if (array_contains(effects, "strip", effects_len)) { | |||
writer = writer_init(filename, (apt_region_t){0, APT_CH_WIDTH*2}, img.rows, PNG_COLOR_TYPE_GRAY, description); | |||
apt_image_t _img = strip(img); | |||
writer = writer_init(filename, (aptdec_region_t){0, APTDEC_CH_WIDTH*2}, img.rows, PNG_COLOR_TYPE_GRAY, description); | |||
aptdec_image_t _img = aptdec_image_clone(img); | |||
aptdec_strip(&_img); | |||
writer_write_image(writer, &_img); | |||
free(_img.data); | |||
} else { | |||
writer = writer_init(filename, APT_REGION_FULL, img.rows, PNG_COLOR_TYPE_GRAY, description); | |||
writer = writer_init(filename, APTDEC_REGION_FULL, img.rows, PNG_COLOR_TYPE_GRAY, description); | |||
writer_write_image(writer, &img); | |||
} | |||
writer_free(writer); | |||
@@ -389,7 +392,7 @@ static int process_file(const char *path, options_t *opts) { | |||
png_colorp lut = calloc(256*256, sizeof(png_color)); | |||
if (read_lut(opts->lut, lut)) { | |||
writer_t *writer = writer_init(filename, APT_REGION_CHA, img.rows, PNG_COLOR_TYPE_RGB, description); | |||
writer_t *writer = writer_init(filename, APTDEC_REGION_CHA, img.rows, PNG_COLOR_TYPE_RGB, description); | |||
writer_write_image_lut(writer, &img, lut); | |||
writer_free(writer); | |||
} | |||
@@ -405,12 +408,13 @@ static int process_file(const char *path, options_t *opts) { | |||
writer_t *writer; | |||
if (array_contains(effects, "strip", effects_len)) { | |||
writer = writer_init(filename, (apt_region_t){0, APT_CH_WIDTH}, img.rows, PNG_COLOR_TYPE_GRAY, description); | |||
apt_image_t _img = strip(img); | |||
writer = writer_init(filename, (aptdec_region_t){0, APTDEC_CH_WIDTH}, img.rows, PNG_COLOR_TYPE_GRAY, description); | |||
aptdec_image_t _img = aptdec_image_clone(img); | |||
aptdec_strip(&_img); | |||
writer_write_image(writer, &_img); | |||
free(_img.data); | |||
} else { | |||
writer = writer_init(filename, APT_REGION_CHA_FULL, img.rows, PNG_COLOR_TYPE_GRAY, description); | |||
writer = writer_init(filename, APTDEC_REGION_CHA_FULL, img.rows, PNG_COLOR_TYPE_GRAY, description); | |||
writer_write_image(writer, &img); | |||
} | |||
writer_free(writer); | |||
@@ -422,12 +426,13 @@ static int process_file(const char *path, options_t *opts) { | |||
writer_t *writer; | |||
if (array_contains(effects, "strip", effects_len)) { | |||
writer = writer_init(filename, (apt_region_t){APT_CH_WIDTH, APT_CH_WIDTH}, img.rows, PNG_COLOR_TYPE_GRAY, description); | |||
apt_image_t _img = strip(img); | |||
writer = writer_init(filename, (aptdec_region_t){APTDEC_CH_WIDTH, APTDEC_CH_WIDTH}, img.rows, PNG_COLOR_TYPE_GRAY, description); | |||
aptdec_image_t _img = aptdec_image_clone(img); | |||
aptdec_strip(&_img); | |||
writer_write_image(writer, &_img); | |||
free(_img.data); | |||
} else { | |||
writer = writer_init(filename, APT_REGION_CHB_FULL, img.rows, PNG_COLOR_TYPE_GRAY, description); | |||
writer = writer_init(filename, APTDEC_REGION_CHB_FULL, img.rows, PNG_COLOR_TYPE_GRAY, description); | |||
writer_write_image(writer, &img); | |||
} | |||
writer_free(writer); | |||
@@ -500,31 +505,17 @@ static size_t callback(float *samples, size_t count, void *context) { | |||
static void write_line(writer_t *png, float *row) { | |||
float min = FLT_MAX; | |||
float max = FLT_MIN; | |||
for (int i = 0; i < APT_IMG_WIDTH; i++) { | |||
for (int i = 0; i < APTDEC_IMG_WIDTH; i++) { | |||
if (row[i] < min) min = row[i]; | |||
if (row[i] > max) max = row[i]; | |||
} | |||
png_byte pixels[APT_IMG_WIDTH]; | |||
for (int i = 0; i < APT_IMG_WIDTH; i++) { | |||
png_byte pixels[APTDEC_IMG_WIDTH]; | |||
for (int i = 0; i < APTDEC_IMG_WIDTH; i++) { | |||
pixels[i] = clamp_int(roundf((row[i]-min) / (max-min) * 255.0f), 0, 255); | |||
} | |||
#pragma GCC diagnostic push | |||
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" | |||
png_write_row(png->png, pixels); | |||
#pragma GCC diagnostic pop | |||
} | |||
apt_image_t strip(apt_image_t img) { | |||
uint8_t *data = calloc(img.rows * APT_IMG_WIDTH, sizeof(uint8_t)); | |||
for (size_t y = 0; y < img.rows; y++) { | |||
memcpy(&data[y*APT_IMG_WIDTH], &img.data[y*APT_IMG_WIDTH + APT_CHA_OFFSET], APT_CH_WIDTH); | |||
memcpy(&data[y*APT_IMG_WIDTH + APT_CH_WIDTH], &img.data[y*APT_IMG_WIDTH + APT_CHB_OFFSET], APT_CH_WIDTH); | |||
} | |||
img.data = data; | |||
return img; | |||
} | |||
int array_contains(char **array, char *value, size_t n) { | |||
@@ -24,7 +24,7 @@ | |||
#include "util.h" | |||
writer_t *writer_init(const char *filename, apt_region_t region, uint32_t height, int color, char *channel) { | |||
writer_t *writer_init(const char *filename, aptdec_region_t region, uint32_t height, int color, char *channel) { | |||
writer_t *png = calloc(1, sizeof(writer_t)); | |||
png->region = region; | |||
@@ -64,17 +64,17 @@ writer_t *writer_init(const char *filename, apt_region_t region, uint32_t height | |||
return png; | |||
} | |||
void writer_write_image(writer_t *png, const apt_image_t *img) { | |||
void writer_write_image(writer_t *png, const aptdec_image_t *img) { | |||
for (size_t y = 0; y < img->rows; y++) { | |||
png_write_row(png->png, &img->data[y*APT_IMG_WIDTH + png->region.offset]); | |||
png_write_row(png->png, &img->data[y*APTDEC_IMG_WIDTH + png->region.offset]); | |||
} | |||
} | |||
void writer_write_image_gradient(writer_t *png, const apt_image_t *img, const uint32_t *gradient) { | |||
void writer_write_image_gradient(writer_t *png, const aptdec_image_t *img, const uint32_t *gradient) { | |||
for (size_t y = 0; y < img->rows; y++) { | |||
png_color pixels[APT_IMG_WIDTH]; | |||
for (size_t x = 0; x < APT_IMG_WIDTH; x++) { | |||
apt_rgb_t pixel = apt_gradient(gradient, img->data[y*APT_IMG_WIDTH + x + png->region.offset]); | |||
png_color pixels[APTDEC_IMG_WIDTH]; | |||
for (size_t x = 0; x < APTDEC_IMG_WIDTH; x++) { | |||
aptdec_rgb_t pixel = aptdec_gradient(gradient, img->data[y*APTDEC_IMG_WIDTH + x + png->region.offset]); | |||
pixels[x] = (png_color){ pixel.r, pixel.g, pixel.b }; | |||
} | |||
@@ -82,12 +82,12 @@ void writer_write_image_gradient(writer_t *png, const apt_image_t *img, const ui | |||
} | |||
} | |||
void writer_write_image_lut(writer_t *png, const apt_image_t *img, const png_colorp lut) { | |||
void writer_write_image_lut(writer_t *png, const aptdec_image_t *img, const png_colorp lut) { | |||
for (size_t y = 0; y < img->rows; y++) { | |||
png_color pixels[APT_CH_WIDTH]; | |||
for (size_t x = 0; x < APT_CH_WIDTH; x++) { | |||
uint8_t a = img->data[y*APT_IMG_WIDTH + x + APT_CHA_OFFSET]; | |||
uint8_t b = img->data[y*APT_IMG_WIDTH + x + APT_CHB_OFFSET]; | |||
png_color pixels[APTDEC_CH_WIDTH]; | |||
for (size_t x = 0; x < APTDEC_CH_WIDTH; x++) { | |||
uint8_t a = img->data[y*APTDEC_IMG_WIDTH + x + APTDEC_CHA_OFFSET]; | |||
uint8_t b = img->data[y*APTDEC_IMG_WIDTH + x + APTDEC_CHB_OFFSET]; | |||
pixels[x] = lut[b*256 + a]; | |||
} | |||
@@ -26,15 +26,15 @@ typedef struct { | |||
png_structp png; | |||
png_infop info; | |||
FILE *file; | |||
apt_region_t region; | |||
aptdec_region_t region; | |||
} writer_t; | |||
writer_t *writer_init(const char *filename, apt_region_t region, uint32_t height, int color, char *channel); | |||
writer_t *writer_init(const char *filename, aptdec_region_t region, uint32_t height, int color, char *channel); | |||
void writer_free(writer_t *png); | |||
void writer_write_image(writer_t *png, const apt_image_t *img); | |||
void writer_write_image_gradient(writer_t *png, const apt_image_t *img, const uint32_t *gradient); | |||
void writer_write_image_lut(writer_t *png, const apt_image_t *img, const png_colorp lut); | |||
void writer_write_image(writer_t *png, const aptdec_image_t *img); | |||
void writer_write_image_gradient(writer_t *png, const aptdec_image_t *img, const uint32_t *gradient); | |||
void writer_write_image_lut(writer_t *png, const aptdec_image_t *img, const png_colorp lut); | |||
int read_lut(const char *filename, png_colorp out); | |||
@@ -112,7 +112,7 @@ const calibration_t calibration[3] = { | |||
} | |||
}; | |||
calibration_t get_calibration(apt_satellite_t satid) { | |||
calibration_t get_calibration(aptdec_satellite_t satid) { | |||
switch (satid) { | |||
case NOAA15: return calibration[0]; | |||
case NOAA18: return calibration[1]; | |||
@@ -52,6 +52,6 @@ static const float C1 = 1.1910427e-5f; | |||
// Second radiation constant (cm-K) | |||
static const float C2 = 1.4387752f; | |||
calibration_t get_calibration(apt_satellite_t satid); | |||
calibration_t get_calibration(aptdec_satellite_t satid); | |||
#endif |
@@ -23,16 +23,16 @@ | |||
// clang-format off | |||
apt_rgb_t apt_gradient(const uint32_t *gradient, uint8_t val) { | |||
return (apt_rgb_t) { | |||
aptdec_rgb_t aptdec_gradient(const uint32_t *gradient, uint8_t val) { | |||
return (aptdec_rgb_t) { | |||
(gradient[val] & 0x00FF0000) >> 16, | |||
(gradient[val] & 0x0000FF00) >> 8, | |||
(gradient[val] & 0x000000FF) | |||
}; | |||
} | |||
apt_rgb_t apt_composite_rgb(apt_rgb_t top, float top_alpha, apt_rgb_t bottom, float bottom_alpha) { | |||
return (apt_rgb_t) { | |||
aptdec_rgb_t aptdec_composite_rgb(aptdec_rgb_t top, float top_alpha, aptdec_rgb_t bottom, float bottom_alpha) { | |||
return (aptdec_rgb_t) { | |||
MCOMPOSITE(top.r, top_alpha, bottom.r, bottom_alpha), | |||
MCOMPOSITE(top.g, top_alpha, bottom.g, bottom_alpha), | |||
MCOMPOSITE(top.b, top_alpha, bottom.b, bottom_alpha) | |||
@@ -40,7 +40,7 @@ apt_rgb_t apt_composite_rgb(apt_rgb_t top, float top_alpha, apt_rgb_t bottom, fl | |||
} | |||
// Taken from WXtoImg | |||
const uint32_t temperature_gradient[256] = { | |||
const uint32_t aptdec_temperature_gradient[256] = { | |||
0x45008F, 0x460091, 0x470092, 0x480094, 0x490096, 0x4A0098, 0x4B009B, | |||
0x4D009D, 0x4E00A0, 0x5000A2, 0x5100A5, 0x5200A7, 0x5400AA, 0x5600AE, | |||
0x5700B1, 0x5800B4, 0x5A00B7, 0x5C00BA, 0x5E00BD, 0x5F00C0, 0x6100C4, | |||
@@ -81,7 +81,7 @@ const uint32_t temperature_gradient[256] = { | |||
}; | |||
// Taken from WXtoImg | |||
const uint32_t precipitation_gradient[58] = { | |||
const uint32_t aptdec_precipitation_gradient[58] = { | |||
0x088941, 0x00C544, 0x00D12C, 0x00E31C, 0x00F906, 0x14FF00, 0x3EFF00, | |||
0x5DFF00, 0x80FF00, 0xABFF00, 0xCDFE00, 0xF8FF00, 0xFFE600, 0xFFB800, | |||
0xFF9800, 0xFF7500, 0xFF4900, 0xFE2600, 0xFF0400, 0xDF0000, 0xA80000, | |||
@@ -54,7 +54,7 @@ typedef struct { | |||
size_t ntaps; | |||
} fir_t; | |||
struct aptdec_t { | |||
struct aptdec { | |||
float sample_rate; | |||
float sync_frequency; | |||
@@ -62,7 +62,7 @@ struct aptdec_t { | |||
fir_t *hilbert; | |||
float low_pass[LOW_PASS_SIZE]; | |||
float row_buffer[APT_IMG_WIDTH + SYNC_SIZE + 2]; | |||
float row_buffer[APTDEC_IMG_WIDTH + SYNC_SIZE + 2]; | |||
float interpolator_buffer[APTDEC_BUFFER_SIZE]; | |||
size_t interpolator_n; | |||
@@ -100,15 +100,15 @@ pll_t *pll_init(float alpha, float beta, float min_freq, float max_freq, float s | |||
} | |||
aptdec_t *aptdec_init(float sample_rate) { | |||
if (sample_rate > 96000 || sample_rate < (CARRIER_FREQ + APT_IMG_WIDTH) * 2.0f) { | |||
if (sample_rate > 96000 || sample_rate < (CARRIER_FREQ + APTDEC_IMG_WIDTH) * 2.0f) { | |||
return NULL; | |||
} | |||
aptdec_t *apt = calloc(1, sizeof(aptdec_t)); | |||
apt->sample_rate = sample_rate; | |||
apt->sync_frequency = 1.0f; | |||
apt->interpolator_n = APTDEC_BUFFER_SIZE; | |||
apt->interpolator_offset = 0.0f; | |||
aptdec_t *aptdec = calloc(1, sizeof(aptdec_t)); | |||
aptdec->sample_rate = sample_rate; | |||
aptdec->sync_frequency = 1.0f; | |||
aptdec->interpolator_n = APTDEC_BUFFER_SIZE; | |||
aptdec->interpolator_offset = 0.0f; | |||
// PLL configuration | |||
// https://www.trondeau.com/blog/2011/8/13/control-loop-gain-values.html | |||
@@ -116,30 +116,30 @@ aptdec_t *aptdec_init(float sample_rate) { | |||
float bw = 0.005f; | |||
float alpha = (4.0f * damp * bw) / (1.0f + 2.0f * damp * bw + bw * bw); | |||
float beta = (4.0f * bw * bw) / (1.0f + 2.0f * damp * bw + bw * bw); | |||
apt->pll = pll_init(alpha, beta, CARRIER_FREQ-MAX_CARRIER_OFFSET, CARRIER_FREQ+MAX_CARRIER_OFFSET, sample_rate); | |||
if (apt->pll == NULL) { | |||
free(apt); | |||
aptdec->pll = pll_init(alpha, beta, CARRIER_FREQ-MAX_CARRIER_OFFSET, CARRIER_FREQ+MAX_CARRIER_OFFSET, sample_rate); | |||
if (aptdec->pll == NULL) { | |||
free(aptdec); | |||
return NULL; | |||
} | |||
// Hilbert transform | |||
apt->hilbert = fir_init(APTDEC_BUFFER_SIZE, 31); | |||
if (apt->hilbert == NULL) { | |||
free(apt->pll); | |||
free(apt); | |||
aptdec->hilbert = fir_init(APTDEC_BUFFER_SIZE, 31); | |||
if (aptdec->hilbert == NULL) { | |||
free(aptdec->pll); | |||
free(aptdec); | |||
return NULL; | |||
} | |||
design_hilbert(apt->hilbert->taps, apt->hilbert->ntaps); | |||
design_hilbert(aptdec->hilbert->taps, aptdec->hilbert->ntaps); | |||
design_low_pass(apt->low_pass, apt->sample_rate, (2080.0f + CARRIER_FREQ) / 2.0f, LOW_PASS_SIZE); | |||
design_low_pass(aptdec->low_pass, aptdec->sample_rate, (2080.0f + CARRIER_FREQ) / 2.0f, LOW_PASS_SIZE); | |||
return apt; | |||
return aptdec; | |||
} | |||
void aptdec_free(aptdec_t *apt) { | |||
fir_free(apt->hilbert); | |||
free(apt->pll); | |||
free(apt); | |||
void aptdec_free(aptdec_t *aptdec) { | |||
fir_free(aptdec->hilbert); | |||
free(aptdec->pll); | |||
free(aptdec); | |||
} | |||
static complexf_t pll_work(pll_t *pll, complexf_t in) { | |||
@@ -159,52 +159,52 @@ static complexf_t pll_work(pll_t *pll, complexf_t in) { | |||
return in; | |||
} | |||
static int am_demod(aptdec_t *apt, float *out, size_t count, aptdec_callback_t callback, void *context) { | |||
size_t read = callback(&apt->hilbert->ring_buffer[apt->hilbert->ntaps], count, context); | |||
static int am_demod(aptdec_t *aptdec, float *out, size_t count, aptdec_callback_t callback, void *context) { | |||
size_t read = callback(&aptdec->hilbert->ring_buffer[aptdec->hilbert->ntaps], count, context); | |||
for (size_t i = 0; i < read; i++) { | |||
complexf_t sample = hilbert_transform(&apt->hilbert->ring_buffer[i], apt->hilbert->taps, apt->hilbert->ntaps); | |||
out[i] = crealf(pll_work(apt->pll, sample)); | |||
complexf_t sample = hilbert_transform(&aptdec->hilbert->ring_buffer[i], aptdec->hilbert->taps, aptdec->hilbert->ntaps); | |||
out[i] = crealf(pll_work(aptdec->pll, sample)); | |||
} | |||
memcpy(apt->hilbert->ring_buffer, &apt->hilbert->ring_buffer[read], apt->hilbert->ntaps*sizeof(float)); | |||
memcpy(aptdec->hilbert->ring_buffer, &aptdec->hilbert->ring_buffer[read], aptdec->hilbert->ntaps*sizeof(float)); | |||
return read; | |||
} | |||
static int get_pixels(aptdec_t *apt, float *out, size_t count, aptdec_callback_t callback, void *context) { | |||
float ratio = apt->sample_rate / (4160.0f * apt->sync_frequency); | |||
static int get_pixels(aptdec_t *aptdec, float *out, size_t count, aptdec_callback_t callback, void *context) { | |||
float ratio = aptdec->sample_rate / (4160.0f * aptdec->sync_frequency); | |||
for (size_t i = 0; i < count; i++) { | |||
// Get more samples if there are less than `LOW_PASS_SIZE` available | |||
if (apt->interpolator_n + LOW_PASS_SIZE > APTDEC_BUFFER_SIZE) { | |||
memcpy(apt->interpolator_buffer, &apt->interpolator_buffer[apt->interpolator_n], (APTDEC_BUFFER_SIZE-apt->interpolator_n) * sizeof(float)); | |||
if (aptdec->interpolator_n + LOW_PASS_SIZE > APTDEC_BUFFER_SIZE) { | |||
memcpy(aptdec->interpolator_buffer, &aptdec->interpolator_buffer[aptdec->interpolator_n], (APTDEC_BUFFER_SIZE-aptdec->interpolator_n) * sizeof(float)); | |||
size_t read = am_demod(apt, &apt->interpolator_buffer[APTDEC_BUFFER_SIZE-apt->interpolator_n], apt->interpolator_n, callback, context); | |||
if (read != apt->interpolator_n) { | |||
size_t read = am_demod(aptdec, &aptdec->interpolator_buffer[APTDEC_BUFFER_SIZE-aptdec->interpolator_n], aptdec->interpolator_n, callback, context); | |||
if (read != aptdec->interpolator_n) { | |||
return i; | |||
} | |||
apt->interpolator_n = 0; | |||
aptdec->interpolator_n = 0; | |||
} | |||
out[i] = interpolating_convolve(&apt->interpolator_buffer[apt->interpolator_n], apt->low_pass, LOW_PASS_SIZE, apt->interpolator_offset); | |||
out[i] = interpolating_convolve(&aptdec->interpolator_buffer[aptdec->interpolator_n], aptdec->low_pass, LOW_PASS_SIZE, aptdec->interpolator_offset); | |||
// Do not question the sacred code | |||
int shift = ceilf(ratio - apt->interpolator_offset); | |||
apt->interpolator_offset = shift + apt->interpolator_offset - ratio; | |||
apt->interpolator_n += shift; | |||
int shift = ceilf(ratio - aptdec->interpolator_offset); | |||
aptdec->interpolator_offset = shift + aptdec->interpolator_offset - ratio; | |||
aptdec->interpolator_n += shift; | |||
} | |||
return count; | |||
} | |||
// Get an entire row of pixels, aligned with sync markers | |||
int aptdec_getrow(aptdec_t *apt, float *row, aptdec_callback_t callback, void *context) { | |||
int aptdec_getrow(aptdec_t *aptdec, float *row, aptdec_callback_t callback, void *context) { | |||
// Wrap the circular buffer | |||
memcpy(apt->row_buffer, &apt->row_buffer[APT_IMG_WIDTH], (SYNC_SIZE + 2) * sizeof(float)); | |||
memcpy(aptdec->row_buffer, &aptdec->row_buffer[APTDEC_IMG_WIDTH], (SYNC_SIZE + 2) * sizeof(float)); | |||
// Get a lines worth (APT_IMG_WIDTH) of samples | |||
if (get_pixels(apt, &apt->row_buffer[SYNC_SIZE + 2], APT_IMG_WIDTH, callback, context) != APT_IMG_WIDTH) { | |||
// Get a lines worth (APTDEC_IMG_WIDTH) of samples | |||
if (get_pixels(aptdec, &aptdec->row_buffer[SYNC_SIZE + 2], APTDEC_IMG_WIDTH, callback, context) != APTDEC_IMG_WIDTH) { | |||
return 0; | |||
} | |||
@@ -214,10 +214,10 @@ int aptdec_getrow(aptdec_t *apt, float *row, aptdec_callback_t callback, void *c | |||
float right = FLT_MIN; | |||
size_t phase = 0; | |||
for (size_t i = 0; i < APT_IMG_WIDTH; i++) { | |||
float _left = convolve(&apt->row_buffer[i + 0], sync_pattern, SYNC_SIZE); | |||
float _middle = convolve(&apt->row_buffer[i + 1], sync_pattern, SYNC_SIZE); | |||
float _right = convolve(&apt->row_buffer[i + 2], sync_pattern, SYNC_SIZE); | |||
for (size_t i = 0; i < APTDEC_IMG_WIDTH; i++) { | |||
float _left = convolve(&aptdec->row_buffer[i + 0], sync_pattern, SYNC_SIZE); | |||
float _middle = convolve(&aptdec->row_buffer[i + 1], sync_pattern, SYNC_SIZE); | |||
float _right = convolve(&aptdec->row_buffer[i + 2], sync_pattern, SYNC_SIZE); | |||
if (_middle > middle) { | |||
left = _left; | |||
middle = _middle; | |||
@@ -228,11 +228,11 @@ int aptdec_getrow(aptdec_t *apt, float *row, aptdec_callback_t callback, void *c | |||
// Frequency | |||
float bias = (left / middle) - (right / middle); | |||
apt->sync_frequency = 1.0f + bias / APT_IMG_WIDTH / 4.0f; | |||
aptdec->sync_frequency = 1.0f + bias / APTDEC_IMG_WIDTH / 4.0f; | |||
// Phase | |||
memcpy(&row[APT_IMG_WIDTH], &apt->row_buffer[phase], (APT_IMG_WIDTH - phase) * sizeof(float)); | |||
memcpy(&row[APT_IMG_WIDTH - phase], apt->row_buffer, phase * sizeof(float)); | |||
memcpy(&row[APTDEC_IMG_WIDTH], &aptdec->row_buffer[phase], (APTDEC_IMG_WIDTH - phase) * sizeof(float)); | |||
memcpy(&row[APTDEC_IMG_WIDTH - phase], aptdec->row_buffer, phase * sizeof(float)); | |||
return 1; | |||
} |
@@ -26,12 +26,12 @@ | |||
#include "util.h" | |||
#include "filter.h" | |||
void apt_equalize(apt_image_t *img, apt_region_t region) { | |||
void aptdec_equalize(aptdec_image_t *img, aptdec_region_t region) { | |||
// Plot histogram | |||
size_t histogram[256] = {0}; | |||
for (size_t y = 0; y < img->rows; y++) { | |||
for (size_t x = 0; x < region.width; x++) { | |||
histogram[img->data[y * APT_IMG_WIDTH + x + region.offset]]++; | |||
histogram[img->data[y * APTDEC_IMG_WIDTH + x + region.offset]]++; | |||
} | |||
} | |||
@@ -46,8 +46,8 @@ void apt_equalize(apt_image_t *img, apt_region_t region) { | |||
int area = img->rows * region.width; | |||
for (size_t y = 0; y < img->rows; y++) { | |||
for (size_t x = 0; x < region.width; x++) { | |||
int k = (int)img->data[y * APT_IMG_WIDTH + x + region.offset]; | |||
img->data[y * APT_IMG_WIDTH + x + region.offset] = (255.0f / area) * cf[k]; | |||
int k = (int)img->data[y * APTDEC_IMG_WIDTH + x + region.offset]; | |||
img->data[y * APTDEC_IMG_WIDTH + x + region.offset] = (255.0f / area) * cf[k]; | |||
} | |||
} | |||
} | |||
@@ -56,18 +56,18 @@ void apt_equalize(apt_image_t *img, apt_region_t region) { | |||
static void image_apply_linear(uint8_t *data, int rows, int offset, int width, linear_t regr) { | |||
for (int y = 0; y < rows; y++) { | |||
for (int x = 0; x < width; x++) { | |||
float pv = linear_calc(data[y * APT_IMG_WIDTH + x + offset], regr); | |||
data[y * APT_IMG_WIDTH + x + offset] = clamp_int(roundf(pv), 0, 255); | |||
float pv = linear_calc(data[y * APTDEC_IMG_WIDTH + x + offset], regr); | |||
data[y * APTDEC_IMG_WIDTH + x + offset] = clamp_int(roundf(pv), 0, 255); | |||
} | |||
} | |||
} | |||
void apt_stretch(apt_image_t *img, apt_region_t region) { | |||
void aptdec_stretch(aptdec_image_t *img, aptdec_region_t region) { | |||
// Plot histogram | |||
size_t histogram[256] = { 0 }; | |||
for (size_t y = 0; y < img->rows; y++) { | |||
for (size_t x = 0; x < region.width; x++) { | |||
histogram[img->data[y*APT_IMG_WIDTH + x + region.offset]]++; | |||
histogram[img->data[y*APTDEC_IMG_WIDTH + x + region.offset]]++; | |||
} | |||
} | |||
@@ -99,32 +99,32 @@ void apt_stretch(apt_image_t *img, apt_region_t region) { | |||
// Median denoise (with deviation threshold) | |||
void apt_denoise(apt_image_t *img, apt_region_t region) { | |||
void aptdec_denoise(aptdec_image_t *img, aptdec_region_t region) { | |||
for (size_t y = 1; y < img->rows - 1; y++) { | |||
for (size_t x = 1; x < region.width - 1; x++) { | |||
float pixels[9] = { 0.0f }; | |||
int pixeln = 0; | |||
for (int y2 = -1; y2 < 2; y2++) { | |||
for (int x2 = -1; x2 < 2; x2++) { | |||
pixels[pixeln++] = img->data[(y + y2) * APT_IMG_WIDTH + (x + region.offset) + x2]; | |||
pixels[pixeln++] = img->data[(y + y2) * APTDEC_IMG_WIDTH + (x + region.offset) + x2]; | |||
} | |||
} | |||
if (standard_deviation(pixels, 9) > 15) { | |||
img->data[y * APT_IMG_WIDTH + x + region.offset] = medianf(pixels, 9); | |||
img->data[y * APTDEC_IMG_WIDTH + x + region.offset] = medianf(pixels, 9); | |||
} | |||
} | |||
} | |||
} | |||
// Flips a channel, for northbound passes | |||
void apt_flip(apt_image_t *img, apt_region_t region) { | |||
void aptdec_flip(aptdec_image_t *img, aptdec_region_t region) { | |||
for (size_t y = 1; y < img->rows; y++) { | |||
for (int x = 1; x < ceil(region.width / 2.0f); x++) { | |||
// Flip top-left & bottom-right | |||
swap_uint8( | |||
&img->data[(img->rows - y) * APT_IMG_WIDTH + region.offset + x], | |||
&img->data[y * APT_IMG_WIDTH + region.offset + (region.width - x)] | |||
&img->data[(img->rows - y) * APTDEC_IMG_WIDTH + region.offset + x], | |||
&img->data[y * APTDEC_IMG_WIDTH + region.offset + (region.width - x)] | |||
); | |||
} | |||
} | |||
@@ -135,7 +135,7 @@ void apt_flip(apt_image_t *img, apt_region_t region) { | |||
#include "filter.h" | |||
int apt_crop(apt_image_t *img) { | |||
int aptdec_crop(aptdec_image_t *img) { | |||
const float sync_pattern[] = {-1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, | |||
1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 0}; | |||
@@ -146,7 +146,7 @@ int apt_crop(apt_image_t *img) { | |||
for (size_t y = 0; y < img->rows; y++) { | |||
float temp[39] = { 0.0f }; | |||
for (size_t i = 0; i < 39; i++) { | |||
temp[i] = img->data[y * APT_IMG_WIDTH + i]; | |||
temp[i] = img->data[y * APTDEC_IMG_WIDTH + i]; | |||
} | |||
spc_rows[y] = convolve(temp, &sync_pattern[0], 39); | |||
@@ -170,7 +170,7 @@ int apt_crop(apt_image_t *img) { | |||
img->rows = (endCrop - startCrop); | |||
// Remove the noisy rows at start | |||
memmove(img->data, &img->data[startCrop * APT_IMG_WIDTH], img->rows * APT_IMG_WIDTH * sizeof(float)); | |||
memmove(img->data, &img->data[startCrop * APTDEC_IMG_WIDTH], img->rows * APTDEC_IMG_WIDTH * sizeof(float)); | |||
free(spc_rows); | |||
return startCrop; | |||
@@ -27,12 +27,12 @@ | |||
#include "util.h" | |||
#include "calibration.h" | |||
#define APT_COUNT_RATIO (1023.0f/255.0f) | |||
#define APTDEC_COUNT_RATIO (1023.0f/255.0f) | |||
apt_image_t apt_image_clone(apt_image_t img) { | |||
apt_image_t _img = img; | |||
_img.data = calloc(APT_IMG_WIDTH * img.rows, sizeof(uint8_t)); | |||
memcpy(_img.data, img.data, APT_IMG_WIDTH * img.rows); | |||
aptdec_image_t aptdec_image_clone(aptdec_image_t img) { | |||
aptdec_image_t _img = img; | |||
_img.data = calloc(APTDEC_IMG_WIDTH * img.rows, sizeof(uint8_t)); | |||
memcpy(_img.data, img.data, APTDEC_IMG_WIDTH * img.rows); | |||
return _img; | |||
} | |||
@@ -40,29 +40,29 @@ static void decode_telemetry(const float *data, size_t rows, size_t offset, floa | |||
// Calculate row average | |||
float *telemetry_rows = calloc(rows, sizeof(float)); | |||
for (size_t y = 0; y < rows; y++) { | |||
telemetry_rows[y] = meanf(&data[y*APT_IMG_WIDTH + offset + APT_CH_WIDTH], APT_TELEMETRY_WIDTH); | |||
telemetry_rows[y] = meanf(&data[y*APTDEC_IMG_WIDTH + offset + APTDEC_CH_WIDTH], APTDEC_TELEMETRY_WIDTH); | |||
} | |||
// Calculate relative telemetry offset (via step detection, i.e. wedge 8 to 9) | |||
size_t telemetry_offset = 0; | |||
float max_difference = 0.0f; | |||
for (size_t y = APT_WEDGE_HEIGHT; y <= rows - APT_WEDGE_HEIGHT; y++) { | |||
float difference = sumf(&telemetry_rows[y - APT_WEDGE_HEIGHT], APT_WEDGE_HEIGHT) - sumf(&telemetry_rows[y], APT_WEDGE_HEIGHT); | |||
for (size_t y = APTDEC_WEDGE_HEIGHT; y <= rows - APTDEC_WEDGE_HEIGHT; y++) { | |||
float difference = sumf(&telemetry_rows[y - APTDEC_WEDGE_HEIGHT], APTDEC_WEDGE_HEIGHT) - sumf(&telemetry_rows[y], APTDEC_WEDGE_HEIGHT); | |||
// Find the maximum difference | |||
if (difference > max_difference) { | |||
max_difference = difference; | |||
telemetry_offset = (y + 64) % APT_FRAME_LEN; | |||
telemetry_offset = (y + 64) % APTDEC_FRAME_LEN; | |||
} | |||
} | |||
// Find the least noisy frame (via standard deviation) | |||
float best_noise = FLT_MAX; | |||
size_t best_frame = 0; | |||
for (size_t y = telemetry_offset; y < rows-APT_FRAME_LEN; y += APT_FRAME_LEN) { | |||
for (size_t y = telemetry_offset; y < rows-APTDEC_FRAME_LEN; y += APTDEC_FRAME_LEN) { | |||
float noise = 0.0f; | |||
for (size_t i = 0; i < APT_FRAME_WEDGES; i++) { | |||
noise += standard_deviation(&telemetry_rows[y + i*APT_WEDGE_HEIGHT], APT_WEDGE_HEIGHT); | |||
for (size_t i = 0; i < APTDEC_FRAME_WEDGES; i++) { | |||
noise += standard_deviation(&telemetry_rows[y + i*APTDEC_WEDGE_HEIGHT], APTDEC_WEDGE_HEIGHT); | |||
} | |||
if (noise < best_noise) { | |||
@@ -71,22 +71,22 @@ static void decode_telemetry(const float *data, size_t rows, size_t offset, floa | |||
} | |||
} | |||
for (size_t i = 0; i < APT_FRAME_WEDGES; i++) { | |||
wedges[i] = meanf(&telemetry_rows[best_frame + i*APT_WEDGE_HEIGHT], APT_WEDGE_HEIGHT); | |||
for (size_t i = 0; i < APTDEC_FRAME_WEDGES; i++) { | |||
wedges[i] = meanf(&telemetry_rows[best_frame + i*APTDEC_WEDGE_HEIGHT], APTDEC_WEDGE_HEIGHT); | |||
} | |||
free(telemetry_rows); | |||
} | |||
static float average_spc(apt_image_t *img, size_t offset) { | |||
static float average_spc(aptdec_image_t *img, size_t offset) { | |||
float *rows = calloc(img->rows, sizeof(float)); | |||
float average = 0.0f; | |||
for (size_t y = 0; y < img->rows; y++) { | |||
float row_average = 0.0f; | |||
for (size_t x = 0; x < APT_SPC_WIDTH; x++) { | |||
row_average += img->data[y*APT_IMG_WIDTH + offset - APT_SPC_WIDTH + x]; | |||
for (size_t x = 0; x < APTDEC_SPC_WIDTH; x++) { | |||
row_average += img->data[y*APTDEC_IMG_WIDTH + offset - APTDEC_SPC_WIDTH + x]; | |||
} | |||
row_average /= (float)APT_SPC_WIDTH; | |||
row_average /= (float)APTDEC_SPC_WIDTH; | |||
rows[y] = row_average; | |||
average += row_average; | |||
@@ -106,8 +106,8 @@ static float average_spc(apt_image_t *img, size_t offset) { | |||
return weighted_average / (float)n; | |||
} | |||
apt_image_t apt_normalize(const float *data, size_t rows, apt_satellite_t satellite, int *error) { | |||
apt_image_t img; | |||
aptdec_image_t aptdec_normalize(const float *data, size_t rows, aptdec_satellite_t satellite, int *error) { | |||
aptdec_image_t img; | |||
img.rows = rows; | |||
img.satellite = satellite; | |||
@@ -118,12 +118,12 @@ apt_image_t apt_normalize(const float *data, size_t rows, apt_satellite_t satell | |||
} | |||
// Decode and average wedges | |||
float wedges[APT_FRAME_WEDGES]; | |||
float wedges_cha[APT_FRAME_WEDGES]; | |||
float wedges_chb[APT_FRAME_WEDGES]; | |||
decode_telemetry(data, rows, APT_CHA_OFFSET, wedges_cha); | |||
decode_telemetry(data, rows, APT_CHB_OFFSET, wedges_chb); | |||
for (size_t i = 0; i < APT_FRAME_WEDGES; i++) { | |||
float wedges[APTDEC_FRAME_WEDGES]; | |||
float wedges_cha[APTDEC_FRAME_WEDGES]; | |||
float wedges_chb[APTDEC_FRAME_WEDGES]; | |||
decode_telemetry(data, rows, APTDEC_CHA_OFFSET, wedges_cha); | |||
decode_telemetry(data, rows, APTDEC_CHB_OFFSET, wedges_chb); | |||
for (size_t i = 0; i < APTDEC_FRAME_WEDGES; i++) { | |||
wedges[i] = (wedges_cha[i] + wedges_chb[i]) / 2.0f; | |||
} | |||
@@ -136,7 +136,7 @@ apt_image_t apt_normalize(const float *data, size_t rows, apt_satellite_t satell | |||
} | |||
// Normalize telemetry | |||
for (size_t i = 0; i < APT_FRAME_WEDGES; i++) { | |||
for (size_t i = 0; i < APTDEC_FRAME_WEDGES; i++) { | |||
img.telemetry[0][i] = linear_calc(wedges_cha[i], normalization); | |||
img.telemetry[1][i] = linear_calc(wedges_chb[i], normalization); | |||
} | |||
@@ -148,20 +148,20 @@ apt_image_t apt_normalize(const float *data, size_t rows, apt_satellite_t satell | |||
if (img.ch[1] < 1 || img.ch[1] > 6) img.ch[1] = AVHRR_CHANNEL_UNKNOWN; | |||
// Normalize and quantize image data | |||
img.data = (uint8_t *)malloc(rows * APT_IMG_WIDTH); | |||
for (size_t i = 0; i < rows * APT_IMG_WIDTH; i++) { | |||
img.data = (uint8_t *)malloc(rows * APTDEC_IMG_WIDTH); | |||
for (size_t i = 0; i < rows * APTDEC_IMG_WIDTH; i++) { | |||
float count = linear_calc(data[i], normalization); | |||
img.data[i] = clamp_int(roundf(count), 0, 255); | |||
} | |||
// Get space brightness | |||
img.space_view[0] = average_spc(&img, APT_CHA_OFFSET); | |||
img.space_view[1] = average_spc(&img, APT_CHB_OFFSET); | |||
img.space_view[0] = average_spc(&img, APTDEC_CHA_OFFSET); | |||
img.space_view[1] = average_spc(&img, APTDEC_CHB_OFFSET); | |||
return img; | |||
} | |||
static void make_thermal_lut(apt_image_t *img, avhrr_channel_t ch, int satellite, float *lut) { | |||
static void make_thermal_lut(aptdec_image_t *img, avhrr_channel_t ch, int satellite, float *lut) { | |||
ch -= 4; | |||
const calibration_t calibration = get_calibration(satellite); | |||
const float Ns = calibration.cor[ch].Ns; | |||
@@ -172,7 +172,7 @@ static void make_thermal_lut(apt_image_t *img, avhrr_channel_t ch, int satellite | |||
// Compute PRT temperature | |||
float T[4] = { 0.0f }; | |||
for (size_t n = 0; n < 4; n++) { | |||
T[n] = quadratic_calc(img->telemetry[1][n + 9] * APT_COUNT_RATIO, calibration.prt[n]); | |||
T[n] = quadratic_calc(img->telemetry[1][n + 9] * APTDEC_COUNT_RATIO, calibration.prt[n]); | |||
} | |||
float Tbb = meanf(T, 4); // Blackbody temperature | |||
@@ -180,11 +180,11 @@ static void make_thermal_lut(apt_image_t *img, avhrr_channel_t ch, int satellite | |||
float Nbb = C1 * pow(Vc, 3) / (expf(C2 * Vc / Tbbstar) - 1.0f); // Blackbody radiance | |||
float Cs = img->space_view[1] * APT_COUNT_RATIO; | |||
float Cb = img->telemetry[1][14] * APT_COUNT_RATIO; | |||
float Cs = img->space_view[1] * APTDEC_COUNT_RATIO; | |||
float Cb = img->telemetry[1][14] * APTDEC_COUNT_RATIO; | |||
for (size_t i = 0; i < 256; i++) { | |||
float Nl = Ns + (Nbb - Ns) * (Cs - i * APT_COUNT_RATIO) / (Cs - Cb); // Linear radiance estimate | |||
float Nl = Ns + (Nbb - Ns) * (Cs - i * APTDEC_COUNT_RATIO) / (Cs - Cb); // Linear radiance estimate | |||
float Nc = quadratic_calc(Nl, calibration.cor[ch].quadratic); // Non-linear correction | |||
float Ne = Nl + Nc; // Corrected radiance | |||
@@ -196,7 +196,7 @@ static void make_thermal_lut(apt_image_t *img, avhrr_channel_t ch, int satellite | |||
} | |||
} | |||
int apt_calibrate_thermal(apt_image_t *img, apt_region_t region) { | |||
int aptdec_calibrate_thermal(aptdec_image_t *img, aptdec_region_t region) { | |||
if (img->ch[1] != AVHRR_CHANNEL_4 && img->ch[1] != AVHRR_CHANNEL_5 && img->ch[1] != AVHRR_CHANNEL_3B) { | |||
return 1; | |||
} | |||
@@ -206,8 +206,8 @@ int apt_calibrate_thermal(apt_image_t *img, apt_region_t region) { | |||
for (size_t y = 0; y < img->rows; y++) { | |||
for (size_t x = 0; x < region.width; x++) { | |||
float temperature = lut[img->data[y * APT_IMG_WIDTH + region.offset + x]]; | |||
img->data[y * APT_IMG_WIDTH + region.offset + x] = clamp_int(roundf((temperature + 100.0) / 160.0 * 255.0), 0, 255); | |||
float temperature = lut[img->data[y * APTDEC_IMG_WIDTH + region.offset + x]]; | |||
img->data[y * APTDEC_IMG_WIDTH + region.offset + x] = clamp_int(roundf((temperature + 100.0) / 160.0 * 255.0), 0, 255); | |||
} | |||
} | |||
@@ -216,13 +216,13 @@ int apt_calibrate_thermal(apt_image_t *img, apt_region_t region) { | |||
static float calibrate_pixel_visible(float value, int channel, calibration_t cal) { | |||
if (value > cal.visible[channel].cutoff) { | |||
return linear_calc(value * APT_COUNT_RATIO, cal.visible[channel].high) / 100.0f * 255.0f; | |||
return linear_calc(value * APTDEC_COUNT_RATIO, cal.visible[channel].high) / 100.0f * 255.0f; | |||
} else { | |||
return linear_calc(value * APT_COUNT_RATIO, cal.visible[channel].low) / 100.0f * 255.0f; | |||
return linear_calc(value * APTDEC_COUNT_RATIO, cal.visible[channel].low) / 100.0f * 255.0f; | |||
} | |||
} | |||
int apt_calibrate_visible(apt_image_t *img, apt_region_t region) { | |||
int aptdec_calibrate_visible(aptdec_image_t *img, aptdec_region_t region) { | |||
if (img->ch[0] != AVHRR_CHANNEL_1 && img->ch[0] != AVHRR_CHANNEL_2) { | |||
return 1; | |||
} | |||
@@ -230,10 +230,17 @@ int apt_calibrate_visible(apt_image_t *img, apt_region_t region) { | |||
calibration_t calibration = get_calibration(img->satellite); | |||
for (size_t y = 0; y < img->rows; y++) { | |||
for (size_t x = 0; x < region.width; x++) { | |||
float albedo = calibrate_pixel_visible(img->data[y * APT_IMG_WIDTH + region.offset + x], img->ch[0]-1, calibration); | |||
img->data[y * APT_IMG_WIDTH + region.offset + x] = clamp_int(roundf(albedo), 0, 255); | |||
float albedo = calibrate_pixel_visible(img->data[y * APTDEC_IMG_WIDTH + region.offset + x], img->ch[0]-1, calibration); | |||
img->data[y * APTDEC_IMG_WIDTH + region.offset + x] = clamp_int(roundf(albedo), 0, 255); | |||
} | |||
} | |||
return 0; | |||
} | |||
void aptdec_strip(aptdec_image_t* img) { | |||
for (size_t y = 0; y < img->rows; y++) { | |||
memcpy(&img->data[y*APTDEC_IMG_WIDTH], &img->data[y*APTDEC_IMG_WIDTH + APTDEC_CHA_OFFSET], APTDEC_CH_WIDTH); | |||
memcpy(&img->data[y*APTDEC_IMG_WIDTH + APTDEC_CH_WIDTH], &img->data[y*APTDEC_IMG_WIDTH + APTDEC_CHB_OFFSET], APTDEC_CH_WIDTH); | |||
} | |||
} |
@@ -40,30 +40,30 @@ extern "C" { | |||
#endif | |||
// Height of a single telemetry wedge | |||
#define APT_WEDGE_HEIGHT 8 | |||
#define APTDEC_WEDGE_HEIGHT 8 | |||
// Numbers of wedges in a frame | |||
#define APT_FRAME_WEDGES 16 | |||
#define APTDEC_FRAME_WEDGES 16 | |||
// Height of a telemetry frame | |||
#define APT_FRAME_LEN (APT_WEDGE_HEIGHT * APT_FRAME_WEDGES) | |||
#define APTDEC_FRAME_LEN (APTDEC_WEDGE_HEIGHT * APTDEC_FRAME_WEDGES) | |||
// Width of the overall image | |||
#define APT_IMG_WIDTH 2080 | |||
#define APTDEC_IMG_WIDTH 2080 | |||
// Width of sync marker | |||
#define APT_SYNC_WIDTH 39 | |||
#define APTDEC_SYNC_WIDTH 39 | |||
// Width of space view | |||
#define APT_SPC_WIDTH 47 | |||
#define APTDEC_SPC_WIDTH 47 | |||
// Width of telemetry | |||
#define APT_TELEMETRY_WIDTH 45 | |||
#define APTDEC_TELEMETRY_WIDTH 45 | |||
// Width of a single video channel | |||
#define APT_CH_WIDTH 909 | |||
#define APTDEC_CH_WIDTH 909 | |||
// Offset to channel A video data | |||
#define APT_CHA_OFFSET (APT_SYNC_WIDTH + APT_SPC_WIDTH) | |||
#define APTDEC_CHA_OFFSET (APTDEC_SYNC_WIDTH + APTDEC_SPC_WIDTH) | |||
// Offset to channel B video data | |||
#define APT_CHB_OFFSET (APT_SYNC_WIDTH + APT_SPC_WIDTH + APT_CH_WIDTH + APT_TELEMETRY_WIDTH + APT_SYNC_WIDTH + APT_SPC_WIDTH) | |||
#define APTDEC_CHB_OFFSET (APTDEC_SYNC_WIDTH + APTDEC_SPC_WIDTH + APTDEC_CH_WIDTH + APTDEC_TELEMETRY_WIDTH + APTDEC_SYNC_WIDTH + APTDEC_SPC_WIDTH) | |||
// Number of rows needed for apt_normalize to (reliably) work | |||
#define APTDEC_NORMALIZE_ROWS (APT_FRAME_LEN * 2) | |||
// Number of rows needed for aptdec_normalize to (reliably) work | |||
#define APTDEC_NORMALIZE_ROWS (APTDEC_FRAME_LEN * 2) | |||
// Maximum amount of samples that will be requested from aptdec_callback_t | |||
#define APTDEC_BUFFER_SIZE 16384 | |||
@@ -73,7 +73,7 @@ extern "C" { | |||
// Channel 3B: mid-infrared (3.55-3.93 um) | |||
// Channel 4: thermal-infrared (10.3-11.3 um) | |||
// Channel 5: thermal-infrared (11.5-12.5 um) | |||
typedef enum apt_channel { | |||
typedef enum aptdec_channel { | |||
AVHRR_CHANNEL_UNKNOWN, | |||
AVHRR_CHANNEL_1, | |||
AVHRR_CHANNEL_2, | |||
@@ -83,41 +83,41 @@ typedef enum apt_channel { | |||
AVHRR_CHANNEL_3B | |||
} avhrr_channel_t; | |||
typedef enum apt_satellite { | |||
typedef enum aptdec_satellite { | |||
NOAA15, | |||
NOAA18, | |||
NOAA19 | |||
} apt_satellite_t; | |||
} aptdec_satellite_t; | |||
typedef struct { | |||
typedef struct aptdec_image { | |||
uint8_t *data; // Image data | |||
size_t rows; // Number of rows | |||
// Telemetry | |||
apt_satellite_t satellite; | |||
aptdec_satellite_t satellite; | |||
avhrr_channel_t ch[2]; | |||
float space_view[2]; | |||
float telemetry[2][16]; | |||
} apt_image_t; | |||
} aptdec_image_t; | |||
typedef struct { | |||
typedef struct aptdec_rgb { | |||
uint8_t r, g, b; | |||
} apt_rgb_t; | |||
} aptdec_rgb_t; | |||
typedef struct { | |||
typedef struct aptdec_region { | |||
size_t offset; | |||
size_t width; | |||
} apt_region_t; | |||
} aptdec_region_t; | |||
typedef struct aptdec_t aptdec_t; | |||
typedef struct aptdec aptdec_t; | |||
// Callback function to get samples | |||
// `context` is the same as passed to aptdec_getrow | |||
typedef size_t (*aptdec_callback_t)(float *samples, size_t count, void *context); | |||
// Clone an apt_image_t struct | |||
// Clone an aptdec_image_t struct | |||
// Useful for calibration | |||
APTDEC_API apt_image_t apt_image_clone(apt_image_t img); | |||
APTDEC_API aptdec_image_t aptdec_image_clone(aptdec_image_t img); | |||
// Returns version of libaptdec in git tag format | |||
// i.e. v2.0.0 or v2.0.0-1-xxxxxx | |||
@@ -126,43 +126,44 @@ APTDEC_API char *aptdec_get_version(void); | |||
// Create and destroy libaptdec instances | |||
// If aptdec_init fails it will return NULL | |||
APTDEC_API aptdec_t *aptdec_init(float sample_rate); | |||
APTDEC_API void aptdec_free(aptdec_t *apt); | |||
APTDEC_API void aptdec_free(aptdec_t *aptdec); | |||
// Normalize and quantize raw image data | |||
// Data is arranged so that each row starts at APT_IMG_WIDTH*y | |||
APTDEC_API apt_image_t apt_normalize(const float *data, size_t rows, apt_satellite_t satellite, int *error); | |||
// Data is arranged so that each row starts at APTDEC_IMG_WIDTH*y | |||
APTDEC_API aptdec_image_t aptdec_normalize(const float *data, size_t rows, aptdec_satellite_t satellite, int *error); | |||
// Get an entire row of pixels | |||
// Requires that `row` has enough space to store APT_IMG_WIDTH*2 | |||
// Requires that `row` has enough space to store APTDEC_IMG_WIDTH*2 | |||
// Returns 0 when `callback` return value != count | |||
APTDEC_API int aptdec_getrow(aptdec_t *apt, float *row, aptdec_callback_t callback, void *context); | |||
APTDEC_API int aptdec_getrow(aptdec_t *aptdec, float *row, aptdec_callback_t callback, void *context); | |||
// Calibrate channels | |||
APTDEC_API int apt_calibrate_thermal(apt_image_t *img, apt_region_t region); | |||
APTDEC_API int apt_calibrate_visible(apt_image_t *img, apt_region_t region); | |||
APTDEC_API int aptdec_calibrate_thermal(aptdec_image_t *img, aptdec_region_t region); | |||
APTDEC_API int aptdec_calibrate_visible(aptdec_image_t *img, aptdec_region_t region); | |||
APTDEC_API void apt_denoise (apt_image_t *img, apt_region_t region); | |||
APTDEC_API void apt_flip (apt_image_t *img, apt_region_t region); | |||
APTDEC_API void apt_stretch (apt_image_t *img, apt_region_t region); | |||
APTDEC_API void apt_equalize(apt_image_t *img, apt_region_t region); | |||
APTDEC_API int apt_crop (apt_image_t *img); | |||
APTDEC_API void aptdec_denoise (aptdec_image_t *img, aptdec_region_t region); | |||
APTDEC_API void aptdec_flip (aptdec_image_t *img, aptdec_region_t region); | |||
APTDEC_API void aptdec_stretch (aptdec_image_t *img, aptdec_region_t region); | |||
APTDEC_API void aptdec_equalize(aptdec_image_t *img, aptdec_region_t region); | |||
APTDEC_API int aptdec_crop (aptdec_image_t *img); | |||
APTDEC_API void aptdec_strip (aptdec_image_t* img); | |||
// Composite two RGB values as layers, in most cases bottom_a will be 1.0f | |||
APTDEC_API apt_rgb_t apt_composite_rgb(apt_rgb_t top, float top_a, apt_rgb_t bottom, float bottom_a); | |||
APTDEC_API aptdec_rgb_t aptdec_composite_rgb(aptdec_rgb_t top, float top_a, aptdec_rgb_t bottom, float bottom_a); | |||
// Apply a gradient such as temperature_gradient | |||
// Apply a gradient such as aptdec_temperature_gradient | |||
// If gradient is less than 256 elements it is the callers responsibility | |||
// that `val` does not exceed the length of the gradient | |||
APTDEC_API apt_rgb_t apt_gradient(const uint32_t *gradient, uint8_t val); | |||
APTDEC_API aptdec_rgb_t aptdec_gradient(const uint32_t *gradient, uint8_t val); | |||
static const apt_region_t APT_REGION_CHA = { APT_CHA_OFFSET, APT_CH_WIDTH }; | |||
static const apt_region_t APT_REGION_CHB = { APT_CHB_OFFSET, APT_CH_WIDTH }; | |||
static const apt_region_t APT_REGION_CHA_FULL = { 0, APT_IMG_WIDTH/2 }; | |||
static const apt_region_t APT_REGION_CHB_FULL = { APT_IMG_WIDTH/2, APT_IMG_WIDTH/2 }; | |||
static const apt_region_t APT_REGION_FULL = { 0, APT_IMG_WIDTH }; | |||
static const aptdec_region_t APTDEC_REGION_CHA = { APTDEC_CHA_OFFSET, APTDEC_CH_WIDTH }; | |||
static const aptdec_region_t APTDEC_REGION_CHB = { APTDEC_CHB_OFFSET, APTDEC_CH_WIDTH }; | |||
static const aptdec_region_t APTDEC_REGION_CHA_FULL = { 0, APTDEC_IMG_WIDTH/2 }; | |||
static const aptdec_region_t APTDEC_REGION_CHB_FULL = { APTDEC_IMG_WIDTH/2, APTDEC_IMG_WIDTH/2 }; | |||
static const aptdec_region_t APTDEC_REGION_FULL = { 0, APTDEC_IMG_WIDTH }; | |||
APTDEC_API extern const uint32_t temperature_gradient[256]; | |||
APTDEC_API extern const uint32_t precipitation_gradient[58]; | |||
APTDEC_API extern const uint32_t aptdec_temperature_gradient[256]; | |||
APTDEC_API extern const uint32_t aptdec_precipitation_gradient[58]; | |||
#ifdef __cplusplus | |||
} | |||