No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 
 
 

237 líneas
8.2 KiB

  1. /*
  2. * aptdec - A lightweight FOSS (NOAA) APT decoder
  3. * Copyright (C) 2004-2009 Thierry Leconte (F4DWV) 2019-2023 Xerbo (xerbo@protonmail.com)
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. #include <math.h>
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <float.h>
  23. #include "algebra.h"
  24. #include <aptdec.h>
  25. #include "util.h"
  26. #include "calibration.h"
  27. #define APT_COUNT_RATIO (1023.0f/255.0f)
  28. apt_image_t apt_image_clone(apt_image_t img) {
  29. apt_image_t _img = img;
  30. _img.data = (uint8_t *)malloc(APT_IMG_WIDTH * img.rows);
  31. memcpy(_img.data, img.data, APT_IMG_WIDTH * img.rows);
  32. return _img;
  33. }
  34. static void decode_telemetry(const float *data, size_t rows, size_t offset, float *wedges) {
  35. // Calculate row average
  36. float telemetry_rows[rows];
  37. for (size_t y = 0; y < rows; y++) {
  38. telemetry_rows[y] = meanf(&data[y*APT_IMG_WIDTH + offset + APT_CH_WIDTH], APT_TELEMETRY_WIDTH);
  39. }
  40. // Calculate relative telemetry offset (via step detection, i.e. wedge 8 to 9)
  41. size_t telemetry_offset = 0;
  42. float max_difference = 0.0f;
  43. for (size_t y = APT_WEDGE_HEIGHT; y <= rows - APT_WEDGE_HEIGHT; y++) {
  44. float difference = sumf(&telemetry_rows[y - APT_WEDGE_HEIGHT], APT_WEDGE_HEIGHT) - sumf(&telemetry_rows[y], APT_WEDGE_HEIGHT);
  45. // Find the maximum difference
  46. if (difference > max_difference) {
  47. max_difference = difference;
  48. telemetry_offset = (y + 64) % APT_FRAME_LEN;
  49. }
  50. }
  51. // Find the least noisy frame (via standard deviation)
  52. float best_noise = FLT_MAX;
  53. size_t best_frame = 0;
  54. for (size_t y = telemetry_offset; y < rows; y += APT_FRAME_LEN) {
  55. float noise = 0.0f;
  56. for (size_t i = 0; i < APT_FRAME_WEDGES; i++) {
  57. noise += standard_deviation(&telemetry_rows[y + i*APT_WEDGE_HEIGHT], APT_WEDGE_HEIGHT);
  58. }
  59. if (noise < best_noise) {
  60. best_noise = noise;
  61. best_frame = y;
  62. }
  63. }
  64. for (size_t i = 0; i < APT_FRAME_WEDGES; i++) {
  65. wedges[i] = meanf(&telemetry_rows[best_frame + i*APT_WEDGE_HEIGHT], APT_WEDGE_HEIGHT);
  66. }
  67. }
  68. static float average_spc(apt_image_t *img, size_t offset) {
  69. float rows[img->rows];
  70. float average = 0.0f;
  71. for (size_t y = 0; y < img->rows; y++) {
  72. float row_average = 0.0f;
  73. for (size_t x = 0; x < APT_SPC_WIDTH; x++) {
  74. row_average += img->data[y*APT_IMG_WIDTH + offset - APT_SPC_WIDTH + x];
  75. }
  76. row_average /= (float)APT_SPC_WIDTH;
  77. rows[y] = row_average;
  78. average += row_average;
  79. }
  80. average /= (float)img->rows;
  81. float weighted_average = 0.0f;
  82. size_t n = 0;
  83. for (size_t y = 0; y < img->rows; y++) {
  84. if (fabsf(rows[y] - average) < 50.0f) {
  85. weighted_average += rows[y];
  86. n++;
  87. }
  88. }
  89. return weighted_average / (float)n;
  90. }
  91. apt_image_t apt_normalize(const float *data, size_t rows, apt_satellite_t satellite, int *error) {
  92. apt_image_t img;
  93. img.rows = rows;
  94. img.satellite = satellite;
  95. *error = 0;
  96. if (rows < APTDEC_NORMALIZE_ROWS) {
  97. *error = -1;
  98. return img;
  99. }
  100. // Decode and average wedges
  101. float wedges[APT_FRAME_WEDGES];
  102. float wedges_cha[APT_FRAME_WEDGES];
  103. float wedges_chb[APT_FRAME_WEDGES];
  104. decode_telemetry(data, rows, APT_CHA_OFFSET, wedges_cha);
  105. decode_telemetry(data, rows, APT_CHB_OFFSET, wedges_chb);
  106. for (size_t i = 0; i < APT_FRAME_WEDGES; i++) {
  107. wedges[i] = (wedges_cha[i] + wedges_chb[i]) / 2.0f;
  108. }
  109. // Calculate normalization
  110. const float reference[9] = { 31, 63, 95, 127, 159, 191, 223, 255, 0 };
  111. linear_t normalization = linear_regression(wedges, reference, 9);
  112. if (normalization.a < 0.0f) {
  113. *error = -1;
  114. return img;
  115. }
  116. // Normalize telemetry
  117. for (size_t i = 0; i < APT_FRAME_WEDGES; i++) {
  118. img.telemetry[0][i] = linear_calc(wedges_cha[i], normalization);
  119. img.telemetry[1][i] = linear_calc(wedges_chb[i], normalization);
  120. }
  121. // Decode channel ID wedges
  122. img.ch[0] = roundf(img.telemetry[0][15] / 32.0f);
  123. img.ch[1] = roundf(img.telemetry[1][15] / 32.0f);
  124. if (img.ch[0] < 1 || img.ch[0] > 6) img.ch[0] = AVHRR_CHANNEL_UNKNOWN;
  125. if (img.ch[1] < 1 || img.ch[1] > 6) img.ch[1] = AVHRR_CHANNEL_UNKNOWN;
  126. // Normalize and quantize image data
  127. img.data = (uint8_t *)malloc(rows * APT_IMG_WIDTH);
  128. for (size_t i = 0; i < rows * APT_IMG_WIDTH; i++) {
  129. float count = linear_calc(data[i], normalization);
  130. img.data[i] = clamp_int(roundf(count), 0, 255);
  131. }
  132. // Get space brightness
  133. img.space_view[0] = average_spc(&img, APT_CHA_OFFSET);
  134. img.space_view[1] = average_spc(&img, APT_CHB_OFFSET);
  135. return img;
  136. }
  137. static void make_thermal_lut(apt_image_t *img, avhrr_channel_t ch, int satellite, float *lut) {
  138. ch -= 4;
  139. const calibration_t calibration = get_calibration(satellite);
  140. const float Ns = calibration.cor[ch].Ns;
  141. const float Vc = calibration.rad[ch].vc;
  142. const float A = calibration.rad[ch].A;
  143. const float B = calibration.rad[ch].B;
  144. // Compute PRT temperature
  145. float T[4];
  146. for (size_t n = 0; n < 4; n++) {
  147. T[n] = quadratic_calc(img->telemetry[1][n + 9] * APT_COUNT_RATIO, calibration.prt[n]);
  148. }
  149. float Tbb = meanf(T, 4); // Blackbody temperature
  150. float Tbbstar = A + Tbb * B; // Effective blackbody temperature
  151. float Nbb = C1 * pow(Vc, 3) / (expf(C2 * Vc / Tbbstar) - 1.0f); // Blackbody radiance
  152. float Cs = img->space_view[1] * APT_COUNT_RATIO;
  153. float Cb = img->telemetry[1][14] * APT_COUNT_RATIO;
  154. for (size_t i = 0; i < 256; i++) {
  155. float Nl = Ns + (Nbb - Ns) * (Cs - i * APT_COUNT_RATIO) / (Cs - Cb); // Linear radiance estimate
  156. float Nc = quadratic_calc(Nl, calibration.cor[ch].quadratic); // Non-linear correction
  157. float Ne = Nl + Nc; // Corrected radiance
  158. float Testar = C2 * Vc / logf(C1 * powf(Vc, 3) / Ne + 1.0); // Equivalent black body temperature
  159. float Te = (Testar - A) / B; // Temperature (kelvin)
  160. // Convert to celsius
  161. lut[i] = Te - 273.15;
  162. }
  163. }
  164. int apt_calibrate_thermal(apt_image_t *img, apt_region_t region) {
  165. if (img->ch[1] != AVHRR_CHANNEL_4 && img->ch[1] != AVHRR_CHANNEL_5 && img->ch[1] != AVHRR_CHANNEL_3B) {
  166. return 1;
  167. }
  168. float lut[256];
  169. make_thermal_lut(img, img->ch[1], img->satellite, lut);
  170. for (size_t y = 0; y < img->rows; y++) {
  171. for (size_t x = 0; x < region.width; x++) {
  172. float temperature = lut[img->data[y * APT_IMG_WIDTH + region.offset + x]];
  173. img->data[y * APT_IMG_WIDTH + region.offset + x] = clamp_int(roundf((temperature + 100.0) / 160.0 * 255.0), 0, 255);
  174. }
  175. }
  176. return 0;
  177. }
  178. static float calibrate_pixel_visible(float value, int channel, calibration_t cal) {
  179. if (value > cal.visible[channel].cutoff) {
  180. return linear_calc(value * APT_COUNT_RATIO, cal.visible[channel].high) / 100.0f * 255.0f;
  181. } else {
  182. return linear_calc(value * APT_COUNT_RATIO, cal.visible[channel].low) / 100.0f * 255.0f;
  183. }
  184. }
  185. int apt_calibrate_visible(apt_image_t *img, apt_region_t region) {
  186. if (img->ch[0] != AVHRR_CHANNEL_1 && img->ch[0] != AVHRR_CHANNEL_2) {
  187. return 1;
  188. }
  189. calibration_t calibration = get_calibration(img->satellite);
  190. for (size_t y = 0; y < img->rows; y++) {
  191. for (size_t x = 0; x < region.width; x++) {
  192. float albedo = calibrate_pixel_visible(img->data[y * APT_IMG_WIDTH + region.offset + x], img->ch[0]-1, calibration);
  193. img->data[y * APT_IMG_WIDTH + region.offset + x] = clamp_int(roundf(albedo), 0, 255);
  194. }
  195. }
  196. return 0;
  197. }