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.
 
 
 
 
 

329 lines
8.9 KiB

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