You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4 jaren geleden
21 jaren geleden
21 jaren geleden
21 jaren geleden
21 jaren geleden
19 jaren geleden
21 jaren geleden
19 jaren geleden
21 jaren geleden
21 jaren geleden
21 jaren geleden
19 jaren geleden
21 jaren geleden
21 jaren geleden
21 jaren geleden
21 jaren geleden
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. /*
  2. * This file is part of Aptdec.
  3. * Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019-2020
  4. *
  5. * Aptdec 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. */
  19. #include <stdlib.h>
  20. #include <stdio.h>
  21. #include <string.h>
  22. #include <getopt.h>
  23. #include <libgen.h>
  24. #include <math.h>
  25. #include <sndfile.h>
  26. #include <errno.h>
  27. #include "messages.h"
  28. #include "offsets.h"
  29. #define FAILURE 0
  30. #define SUCCESS 1
  31. typedef struct {
  32. char *type; // Output image type
  33. char *effects;
  34. int satnum; // The satellite number
  35. char *map; // Path to a map file
  36. char *path; // Output directory
  37. int realtime;
  38. } options_t;
  39. typedef struct {
  40. float *prow[MAX_HEIGHT]; // Row buffers
  41. int nrow; // Number of rows
  42. int chA, chB; // ID of each channel
  43. char name[256]; // Stripped filename
  44. } image_t;
  45. // DSP
  46. extern int getpixelrow(float *pixelv, int nrow, int *zenith);
  47. extern int init_dsp(double F);
  48. // I/O
  49. extern int readfcconf(char *file);
  50. extern int readRawImage(char *filename, float **prow, int *nrow);
  51. extern int ImageOut(options_t *opts, image_t *img, int offset, int width, char *desc, char *chid, char *palette);
  52. extern void closeWriter();
  53. extern void pushRow(float *row, int width);
  54. extern int initWriter(options_t *opts, image_t *img, int width, int height, char *desc, char *chid);
  55. // Image functions
  56. extern int calibrate(float **prow, int nrow, int offset, int width);
  57. extern void histogramEqualise(float **prow, int nrow, int offset, int width);
  58. extern void temperature(options_t *opts, image_t *img, int offset, int width);
  59. extern int Ngvi(float **prow, int nrow);
  60. extern void denoise(float **prow, int nrow, int offset, int width);
  61. extern void distrib(options_t *opts, image_t *img, char *chid);
  62. // Palettes
  63. extern char GviPalette[256*3];
  64. extern char TempPalette[256*3];
  65. // Row where the satellite is closest to the observer
  66. int zenith = 0;
  67. // Audio file
  68. static SNDFILE *audioFile;
  69. static int initsnd(char *filename);
  70. int getsample(float *sample, int nb);
  71. static int processAudio(char *filename, options_t *opts);
  72. static void usage(void);
  73. int main(int argc, char **argv) {
  74. fprintf(stderr, VERSION"\n");
  75. if(argc == 1)
  76. usage();
  77. // Check if there are actually any input files
  78. if(optind == argc){
  79. fprintf(stderr, "No input files provided.\n");
  80. usage();
  81. }
  82. options_t opts = { "r", "", 19, "", ".", 0 };
  83. // Parse arguments
  84. int opt;
  85. while ((opt = getopt(argc, argv, "c:m:d:i:s:e:r")) != EOF) {
  86. switch (opt) {
  87. case 'd':
  88. opts.path = optarg;
  89. break;
  90. case 'c':
  91. readfcconf(optarg);
  92. break;
  93. case 'm':
  94. opts.map = optarg;
  95. break;
  96. case 'i':
  97. opts.type = optarg;
  98. break;
  99. case 's':
  100. opts.satnum = atoi(optarg);
  101. if(opts.satnum < 15 || opts.satnum > 19){
  102. fprintf(stderr, "Invalid satellite number, it must be the range 15-19\n");
  103. exit(EPERM);
  104. }
  105. break;
  106. case 'e':
  107. opts.effects = optarg;
  108. break;
  109. case 'r':
  110. opts.realtime = 1;
  111. break;
  112. default:
  113. usage();
  114. }
  115. }
  116. // Process the files
  117. for (; optind < argc; optind++) {
  118. processAudio(argv[optind], &opts);
  119. }
  120. exit(0);
  121. }
  122. static int processAudio(char *filename, options_t *opts){
  123. // Image info struct
  124. image_t img;
  125. // Mapping between wedge value and channel ID
  126. static struct {
  127. char *id[7];
  128. char *name[7];
  129. } ch = {
  130. { "?", "1", "2", "3A", "4", "5", "3B" },
  131. { "unknown", "visble", "near-infrared", "mid-infrared", "thermal-infrared", "thermal-infrared", "mid-infrared" }
  132. };
  133. // Buffer for image channel
  134. char desc[60];
  135. // Parse file path
  136. char path[256], extension[32];
  137. strcpy(path, filename);
  138. strcpy(path, dirname(path));
  139. sscanf(basename(filename), "%[^.].%s", img.name, extension);
  140. if(opts->realtime) initWriter(opts, &img, IMG_WIDTH, MAX_HEIGHT, "Unprocessed realtime image", "r");
  141. if(strcmp(extension, "png") == 0){
  142. // Read PNG into image buffer
  143. printf("Reading %s", filename);
  144. if(readRawImage(filename, img.prow, &img.nrow) == 0){
  145. fprintf(stderr, "Skipping %s; see above.\n", img.name);
  146. return FAILURE;
  147. }
  148. }else{
  149. // Attempt to open the audio file
  150. if (initsnd(filename) == 0)
  151. exit(EPERM);
  152. // Build image
  153. for (img.nrow = 0; img.nrow < MAX_HEIGHT; img.nrow++) {
  154. // Allocate memory for this row
  155. img.prow[img.nrow] = (float *) malloc(sizeof(float) * 2150);
  156. // Write into memory and break the loop when there are no more samples to read
  157. if (getpixelrow(img.prow[img.nrow], img.nrow, &zenith) == 0)
  158. break;
  159. if(opts->realtime) pushRow(img.prow[img.nrow], IMG_WIDTH);
  160. fprintf(stderr, "Row: %d\r", img.nrow);
  161. fflush(stderr);
  162. }
  163. // Close stream
  164. sf_close(audioFile);
  165. }
  166. if(opts->realtime) closeWriter();
  167. printf("\nTotal rows: %d\n", img.nrow);
  168. // Fallback for detecting the zenith
  169. // TODO: encode zenith in raw images
  170. if(opts->map != NULL && opts->map[0] != '\0' && zenith == 0){
  171. fprintf(stderr, "Guessing zenith in image, map will most likely be misaligned.\n");
  172. zenith = img.nrow / 2;
  173. }
  174. // Calibrate
  175. img.chA = calibrate(img.prow, img.nrow, CHA_OFFSET, CH_WIDTH);
  176. img.chB = calibrate(img.prow, img.nrow, CHB_OFFSET, CH_WIDTH);
  177. printf("Channel A: %s (%s)\n", ch.id[img.chA], ch.name[img.chA]);
  178. printf("Channel B: %s (%s)\n", ch.id[img.chB], ch.name[img.chB]);
  179. // Denoise
  180. if(CONTAINS(opts->effects, 'd')){
  181. denoise(img.prow, img.nrow, CHA_OFFSET, CH_WIDTH);
  182. denoise(img.prow, img.nrow, CHB_OFFSET, CH_WIDTH);
  183. }
  184. // Temperature
  185. if (CONTAINS(opts->type, 't') && img.chB >= 4) {
  186. temperature(opts, &img, CHB_OFFSET, CH_WIDTH);
  187. ImageOut(opts, &img, CHB_OFFSET, CH_WIDTH, "Temperature", "t", (char *)TempPalette);
  188. }
  189. // False color image
  190. if(CONTAINS(opts->type, 'c')){
  191. if (img.chA == 2 && img.chB >= 4) { // Normal false color
  192. // TODO: use real MSA
  193. // TODO: provide more than just "natural" color images
  194. ImageOut(opts, &img, 0, CH_WIDTH, "False Color", "c", NULL);
  195. } else if (img.chB == 2) { // GVI (global vegetation index) false color
  196. Ngvi(img.prow, img.nrow);
  197. ImageOut(opts, &img, CHB_OFFSET, CH_WIDTH, "GVI False Color", "c", (char *)GviPalette);
  198. } else {
  199. fprintf(stderr, "Skipping False Color generation; lacking required channels.\n");
  200. }
  201. }
  202. // MCIR
  203. if (CONTAINS(opts->type, 'm'))
  204. ImageOut(opts, &img, 0, IMG_WIDTH, "MCIR", "m", NULL);
  205. // Histogram equalise
  206. if(CONTAINS(opts->effects, 'h')){
  207. histogramEqualise(img.prow, img.nrow, CHA_OFFSET, CH_WIDTH);
  208. histogramEqualise(img.prow, img.nrow, CHB_OFFSET, CH_WIDTH);
  209. }
  210. // Raw image
  211. if (CONTAINS(opts->type, 'r')) {
  212. sprintf(desc, "%s (%s) & %s (%s)", ch.id[img.chA], ch.name[img.chA], ch.id[img.chB], ch.name[img.chB]);
  213. ImageOut(opts, &img, 0, IMG_WIDTH, desc, "r", NULL);
  214. }
  215. // Channel A
  216. if (CONTAINS(opts->type, 'a')) {
  217. sprintf(desc, "%s (%s)", ch.id[img.chA], ch.name[img.chA]);
  218. ImageOut(opts, &img, CHA_OFFSET, CH_WIDTH, desc, ch.id[img.chA], NULL);
  219. }
  220. // Channel B
  221. if (CONTAINS(opts->type, 'b')) {
  222. sprintf(desc, "%s (%s)", ch.id[img.chB], ch.name[img.chB]);
  223. ImageOut(opts, &img, CHB_OFFSET, CH_WIDTH, desc, ch.id[img.chB], NULL);
  224. }
  225. // Distribution image
  226. if (CONTAINS(opts->type, 'd'))
  227. distrib(opts, &img, "d");
  228. return SUCCESS;
  229. }
  230. static int initsnd(char *filename) {
  231. SF_INFO infwav;
  232. int res;
  233. // Open audio file
  234. infwav.format = 0;
  235. audioFile = sf_open(filename, SFM_READ, &infwav);
  236. if (audioFile == NULL) {
  237. fprintf(stderr, ERR_FILE_READ, filename);
  238. return FAILURE;
  239. }
  240. res = init_dsp(infwav.samplerate);
  241. printf("Input file: %s\n", filename);
  242. if(res < 0) {
  243. fprintf(stderr, "Input sample rate too low: %d\n", infwav.samplerate);
  244. return FAILURE;
  245. }else if(res > 0) {
  246. fprintf(stderr, "Input sample rate too high: %d\n", infwav.samplerate);
  247. return FAILURE;
  248. }
  249. printf("Input sample rate: %d\n", infwav.samplerate);
  250. // TODO: accept stereo audio
  251. if (infwav.channels != 1) {
  252. fprintf(stderr, "Too many channels in input file: %d\n", infwav.channels);
  253. return FAILURE;
  254. }
  255. return SUCCESS;
  256. }
  257. // Read samples from the wave file
  258. int getsample(float *sample, int nb) {
  259. return sf_read_float(audioFile, sample, nb);
  260. }
  261. static void usage(void) {
  262. fprintf(stderr,
  263. "Aptdec [options] audio files ...\n"
  264. "Options:\n"
  265. " -e [t|h] Effects\n"
  266. " t: Crop telemetry\n"
  267. " h: Histogram equalise\n"
  268. " d: Denoise\n"
  269. " p: Precipitation\n"
  270. " -i [r|a|b|c|t|m] Output image\n"
  271. " r: Raw\n"
  272. " a: Channel A\n"
  273. " b: Channel B\n"
  274. " c: False color\n"
  275. " t: Temperature\n"
  276. " m: MCIR\n"
  277. " -d <dir> Image destination directory.\n"
  278. " -s [15-19] Satellite number\n"
  279. " -c <file> False color config file\n"
  280. " -m <file> Map file\n"
  281. " -r Realtime decode");
  282. exit(EINVAL);
  283. }