Browse Source

Properly prefix all expored functions with aptdec

v2
Xerbo 1 year ago
parent
commit
9bd911d7e2
No known key found for this signature in database GPG Key ID: 34103F6D8F11CEB0
11 changed files with 238 additions and 230 deletions
  1. +9
    -0
      CMakeLists.txt
  2. +39
    -48
      aptdec-cli/main.c
  3. +12
    -12
      aptdec-cli/pngio.c
  4. +5
    -5
      aptdec-cli/pngio.h
  5. +1
    -1
      libaptdec/calibration.c
  6. +1
    -1
      libaptdec/calibration.h
  7. +6
    -6
      libaptdec/color.c
  8. +49
    -49
      libaptdec/dsp.c
  9. +17
    -17
      libaptdec/effects.c
  10. +51
    -44
      libaptdec/image.c
  11. +48
    -47
      libaptdec/include/aptdec.h

+ 9
- 0
CMakeLists.txt View File

@@ -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})



+ 39
- 48
aptdec-cli/main.c View File

@@ -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) {


+ 12
- 12
aptdec-cli/pngio.c View File

@@ -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];
}



+ 5
- 5
aptdec-cli/pngio.h View File

@@ -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);



+ 1
- 1
libaptdec/calibration.c View File

@@ -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];


+ 1
- 1
libaptdec/calibration.h View File

@@ -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

+ 6
- 6
libaptdec/color.c View File

@@ -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,


+ 49
- 49
libaptdec/dsp.c View File

@@ -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;
}

+ 17
- 17
libaptdec/effects.c View File

@@ -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;


+ 51
- 44
libaptdec/image.c View File

@@ -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);
}
}

+ 48
- 47
libaptdec/include/aptdec.h View File

@@ -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
}


Loading…
Cancel
Save