Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 
 

247 řádky
8.9 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 <aptdec.h>
  24. #include "algebra.h"
  25. #include "util.h"
  26. #include "calibration.h"
  27. #define APTDEC_COUNT_RATIO (1023.0f/255.0f)
  28. aptdec_image_t aptdec_image_clone(aptdec_image_t img) {
  29. aptdec_image_t _img = img;
  30. _img.data = calloc(APTDEC_IMG_WIDTH * img.rows, sizeof(uint8_t));
  31. memcpy(_img.data, img.data, APTDEC_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 = calloc(rows, sizeof(float));
  37. for (size_t y = 0; y < rows; y++) {
  38. telemetry_rows[y] = meanf(&data[y*APTDEC_IMG_WIDTH + offset + APTDEC_CH_WIDTH], APTDEC_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 = APTDEC_WEDGE_HEIGHT; y <= rows - APTDEC_WEDGE_HEIGHT; y++) {
  44. float difference = sumf(&telemetry_rows[y - APTDEC_WEDGE_HEIGHT], APTDEC_WEDGE_HEIGHT) - sumf(&telemetry_rows[y], APTDEC_WEDGE_HEIGHT);
  45. // Find the maximum difference
  46. if (difference > max_difference) {
  47. max_difference = difference;
  48. telemetry_offset = (y + 64) % APTDEC_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-APTDEC_FRAME_LEN; y += APTDEC_FRAME_LEN) {
  55. float noise = 0.0f;
  56. for (size_t i = 0; i < APTDEC_FRAME_WEDGES; i++) {
  57. noise += standard_deviation(&telemetry_rows[y + i*APTDEC_WEDGE_HEIGHT], APTDEC_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 < APTDEC_FRAME_WEDGES; i++) {
  65. wedges[i] = meanf(&telemetry_rows[best_frame + i*APTDEC_WEDGE_HEIGHT], APTDEC_WEDGE_HEIGHT);
  66. }
  67. free(telemetry_rows);
  68. }
  69. static float average_spc(aptdec_image_t *img, size_t offset) {
  70. float *rows = calloc(img->rows, sizeof(float));
  71. float average = 0.0f;
  72. for (size_t y = 0; y < img->rows; y++) {
  73. float row_average = 0.0f;
  74. for (size_t x = 0; x < APTDEC_SPC_WIDTH; x++) {
  75. row_average += img->data[y*APTDEC_IMG_WIDTH + offset - APTDEC_SPC_WIDTH + x];
  76. }
  77. row_average /= (float)APTDEC_SPC_WIDTH;
  78. rows[y] = row_average;
  79. average += row_average;
  80. }
  81. average /= (float)img->rows;
  82. float weighted_average = 0.0f;
  83. size_t n = 0;
  84. for (size_t y = 0; y < img->rows; y++) {
  85. if (fabsf(rows[y] - average) < 50.0f) {
  86. weighted_average += rows[y];
  87. n++;
  88. }
  89. }
  90. free(rows);
  91. return weighted_average / (float)n;
  92. }
  93. aptdec_image_t aptdec_normalize(const float *data, size_t rows, aptdec_satellite_t satellite, int *error) {
  94. aptdec_image_t img;
  95. img.rows = rows;
  96. img.satellite = satellite;
  97. *error = 0;
  98. if (rows < APTDEC_NORMALIZE_ROWS) {
  99. *error = -1;
  100. return img;
  101. }
  102. // Decode and average wedges
  103. float wedges[APTDEC_FRAME_WEDGES];
  104. float wedges_cha[APTDEC_FRAME_WEDGES];
  105. float wedges_chb[APTDEC_FRAME_WEDGES];
  106. decode_telemetry(data, rows, APTDEC_CHA_OFFSET, wedges_cha);
  107. decode_telemetry(data, rows, APTDEC_CHB_OFFSET, wedges_chb);
  108. for (size_t i = 0; i < APTDEC_FRAME_WEDGES; i++) {
  109. wedges[i] = (wedges_cha[i] + wedges_chb[i]) / 2.0f;
  110. }
  111. // Calculate normalization
  112. const float reference[9] = { 31, 63, 95, 127, 159, 191, 223, 255, 0 };
  113. linear_t normalization = linear_regression(wedges, reference, 9);
  114. if (normalization.a < 0.0f) {
  115. *error = -1;
  116. return img;
  117. }
  118. // Normalize telemetry
  119. for (size_t i = 0; i < APTDEC_FRAME_WEDGES; i++) {
  120. img.telemetry[0][i] = linear_calc(wedges_cha[i], normalization);
  121. img.telemetry[1][i] = linear_calc(wedges_chb[i], normalization);
  122. }
  123. // Decode channel ID wedges
  124. img.ch[0] = roundf(img.telemetry[0][15] / 32.0f);
  125. img.ch[1] = roundf(img.telemetry[1][15] / 32.0f);
  126. if (img.ch[0] < 1 || img.ch[0] > 6) img.ch[0] = AVHRR_CHANNEL_UNKNOWN;
  127. if (img.ch[1] < 1 || img.ch[1] > 6) img.ch[1] = AVHRR_CHANNEL_UNKNOWN;
  128. // Normalize and quantize image data
  129. img.data = (uint8_t *)malloc(rows * APTDEC_IMG_WIDTH);
  130. for (size_t i = 0; i < rows * APTDEC_IMG_WIDTH; i++) {
  131. float count = linear_calc(data[i], normalization);
  132. img.data[i] = clamp_int(roundf(count), 0, 255);
  133. }
  134. // Get space brightness
  135. img.space_view[0] = average_spc(&img, APTDEC_CHA_OFFSET);
  136. img.space_view[1] = average_spc(&img, APTDEC_CHB_OFFSET);
  137. return img;
  138. }
  139. static void make_thermal_lut(aptdec_image_t *img, avhrr_channel_t ch, int satellite, float *lut) {
  140. ch -= 4;
  141. const calibration_t calibration = get_calibration(satellite);
  142. const float Ns = calibration.cor[ch].Ns;
  143. const float Vc = calibration.rad[ch].vc;
  144. const float A = calibration.rad[ch].A;
  145. const float B = calibration.rad[ch].B;
  146. // Compute PRT temperature
  147. float T[4] = { 0.0f };
  148. for (size_t n = 0; n < 4; n++) {
  149. T[n] = quadratic_calc(img->telemetry[1][n + 9] * APTDEC_COUNT_RATIO, calibration.prt[n]);
  150. }
  151. float Tbb = meanf(T, 4); // Blackbody temperature
  152. float Tbbstar = A + Tbb * B; // Effective blackbody temperature
  153. float Nbb = C1 * pow(Vc, 3) / (expf(C2 * Vc / Tbbstar) - 1.0f); // Blackbody radiance
  154. float Cs = img->space_view[1] * APTDEC_COUNT_RATIO;
  155. float Cb = img->telemetry[1][14] * APTDEC_COUNT_RATIO;
  156. for (size_t i = 0; i < 256; i++) {
  157. float Nl = Ns + (Nbb - Ns) * (Cs - i * APTDEC_COUNT_RATIO) / (Cs - Cb); // Linear radiance estimate
  158. float Nc = quadratic_calc(Nl, calibration.cor[ch].quadratic); // Non-linear correction
  159. float Ne = Nl + Nc; // Corrected radiance
  160. float Testar = C2 * Vc / logf(C1 * powf(Vc, 3) / Ne + 1.0); // Equivalent black body temperature
  161. float Te = (Testar - A) / B; // Temperature (kelvin)
  162. // Convert to celsius
  163. lut[i] = Te - 273.15;
  164. }
  165. }
  166. int aptdec_calibrate_thermal(aptdec_image_t *img, aptdec_region_t region) {
  167. if (img->ch[1] != AVHRR_CHANNEL_4 && img->ch[1] != AVHRR_CHANNEL_5 && img->ch[1] != AVHRR_CHANNEL_3B) {
  168. return 1;
  169. }
  170. float lut[256] = { 0.0f };
  171. make_thermal_lut(img, img->ch[1], img->satellite, lut);
  172. for (size_t y = 0; y < img->rows; y++) {
  173. for (size_t x = 0; x < region.width; x++) {
  174. float temperature = lut[img->data[y * APTDEC_IMG_WIDTH + region.offset + x]];
  175. img->data[y * APTDEC_IMG_WIDTH + region.offset + x] = clamp_int(roundf((temperature + 100.0) / 160.0 * 255.0), 0, 255);
  176. }
  177. }
  178. return 0;
  179. }
  180. static float calibrate_pixel_visible(float value, int channel, calibration_t cal) {
  181. if (value > cal.visible[channel].cutoff) {
  182. return linear_calc(value * APTDEC_COUNT_RATIO, cal.visible[channel].high) / 100.0f * 255.0f;
  183. } else {
  184. return linear_calc(value * APTDEC_COUNT_RATIO, cal.visible[channel].low) / 100.0f * 255.0f;
  185. }
  186. }
  187. int aptdec_calibrate_visible(aptdec_image_t *img, aptdec_region_t region) {
  188. if (img->ch[0] != AVHRR_CHANNEL_1 && img->ch[0] != AVHRR_CHANNEL_2) {
  189. return 1;
  190. }
  191. calibration_t calibration = get_calibration(img->satellite);
  192. for (size_t y = 0; y < img->rows; y++) {
  193. for (size_t x = 0; x < region.width; x++) {
  194. float albedo = calibrate_pixel_visible(img->data[y * APTDEC_IMG_WIDTH + region.offset + x], img->ch[0]-1, calibration);
  195. img->data[y * APTDEC_IMG_WIDTH + region.offset + x] = clamp_int(roundf(albedo), 0, 255);
  196. }
  197. }
  198. return 0;
  199. }
  200. void aptdec_strip(aptdec_image_t* img) {
  201. for (size_t y = 0; y < img->rows; y++) {
  202. memcpy(&img->data[y*APTDEC_IMG_WIDTH], &img->data[y*APTDEC_IMG_WIDTH + APTDEC_CHA_OFFSET], APTDEC_CH_WIDTH);
  203. memcpy(&img->data[y*APTDEC_IMG_WIDTH + APTDEC_CH_WIDTH], &img->data[y*APTDEC_IMG_WIDTH + APTDEC_CHB_OFFSET], APTDEC_CH_WIDTH);
  204. }
  205. }