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.
 
 
 
 
 

356 regels
9.7 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, int reset);
  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. extern void cropNoise(image_t *img);
  48. // Palettes
  49. extern char GviPalette[256*3];
  50. extern char TempPalette[256*3];
  51. // Audio file
  52. static SNDFILE *audioFile;
  53. // Number of channels in audio file
  54. int channels = 1;
  55. // Function declarations
  56. static int initsnd(char *filename);
  57. int getsample(float *sample, int nb);
  58. static int processAudio(char *filename, options_t *opts);
  59. static void usage(void);
  60. int main(int argc, char **argv) {
  61. fprintf(stderr, VERSION"\n");
  62. // Check if there are actually any input files
  63. if(argc == optind || argc == 1){
  64. fprintf(stderr, "No input files provided.\n");
  65. usage();
  66. }
  67. options_t opts = { "r", "", 19, "", ".", 0, "", "", 1.0, 0 };
  68. // Parse arguments
  69. int opt;
  70. while ((opt = getopt(argc, argv, "o:m:d:i:s:e:p:g:k:r")) != EOF) {
  71. switch (opt) {
  72. case 'd':
  73. opts.path = optarg;
  74. break;
  75. case 'm':
  76. opts.map = optarg;
  77. break;
  78. case 'i':
  79. opts.type = optarg;
  80. break;
  81. case 's':
  82. opts.satnum = atoi(optarg);
  83. if(opts.satnum < 15 || opts.satnum > 19){
  84. fprintf(stderr, "Invalid satellite number, it must be the range 15-19\n");
  85. exit(EPERM);
  86. }
  87. break;
  88. case 'e':
  89. opts.effects = optarg;
  90. break;
  91. case 'r':
  92. opts.realtime = 1;
  93. break;
  94. case 'o':
  95. opts.filename = optarg;
  96. break;
  97. case 'p':
  98. opts.palette = optarg;
  99. break;
  100. case 'g':
  101. opts.gamma = atof(optarg);
  102. break;
  103. case 'k':
  104. opts.mapOffset = atoi(optarg);
  105. break;
  106. default:
  107. usage();
  108. }
  109. }
  110. // Process the files
  111. for (; optind < argc; optind++) {
  112. processAudio(argv[optind], &opts);
  113. }
  114. exit(0);
  115. }
  116. static int processAudio(char *filename, options_t *opts){
  117. // Image info struct
  118. image_t img;
  119. // Mapping between wedge value and channel ID
  120. static struct {
  121. char *id[7];
  122. char *name[7];
  123. } ch = {
  124. { "?", "1", "2", "3A", "4", "5", "3B" },
  125. { "unknown", "visble", "near-infrared", "mid-infrared", "thermal-infrared", "thermal-infrared", "mid-infrared" }
  126. };
  127. // Buffer for image channel
  128. char desc[60];
  129. // Parse file path
  130. char path[256], extension[32];
  131. strcpy(path, filename);
  132. strcpy(path, dirname(path));
  133. sscanf(basename(filename), "%255[^.].%31s", img.name, extension);
  134. if(opts->realtime){
  135. // Set output filename to current time when in realtime mode
  136. time_t t;
  137. time(&t);
  138. strncpy(img.name, ctime(&t), 24);
  139. // Init a row writer
  140. initWriter(opts, &img, IMG_WIDTH, MAX_HEIGHT, "Unprocessed realtime image", "r");
  141. }
  142. if(strcmp(extension, "png") == 0){
  143. // Read PNG into image buffer
  144. printf("Reading %s\n", filename);
  145. if(readRawImage(filename, img.prow, &img.nrow) == 0){
  146. exit(EPERM);
  147. }
  148. }else{
  149. // Attempt to open the audio file
  150. if (initsnd(filename) == 0)
  151. exit(EPERM);
  152. // Build image
  153. // TODO: multithreading, would require some sort of input buffer
  154. for (img.nrow = 0; img.nrow < MAX_HEIGHT; img.nrow++) {
  155. // Allocate memory for this row
  156. img.prow[img.nrow] = (float *) malloc(sizeof(float) * 2150);
  157. // Write into memory and break the loop when there are no more samples to read
  158. if (getpixelrow(img.prow[img.nrow], img.nrow, &img.zenith, (img.nrow == 0)) == 0)
  159. break;
  160. if(opts->realtime) pushRow(img.prow[img.nrow], IMG_WIDTH);
  161. fprintf(stderr, "Row: %d\r", img.nrow);
  162. fflush(stderr);
  163. }
  164. // Close stream
  165. sf_close(audioFile);
  166. }
  167. if(opts->realtime) closeWriter();
  168. printf("Total rows: %d\n", img.nrow);
  169. // Fallback for detecting the zenith
  170. // TODO: encode metadata in raw images
  171. if(opts->map != NULL && opts->map[0] != '\0' && img.zenith == 0){
  172. fprintf(stderr, "Guessing zenith in image, map will most likely be misaligned.\n");
  173. img.zenith = img.nrow / 2;
  174. }
  175. // Calibrate
  176. img.chA = calibrate(img.prow, img.nrow, CHA_OFFSET, CH_WIDTH);
  177. img.chB = calibrate(img.prow, img.nrow, CHB_OFFSET, CH_WIDTH);
  178. printf("Channel A: %s (%s)\n", ch.id[img.chA], ch.name[img.chA]);
  179. printf("Channel B: %s (%s)\n", ch.id[img.chB], ch.name[img.chB]);
  180. // Crop noise from start and end of image
  181. if(CONTAINS(opts->effects, Crop_Noise)){
  182. cropNoise(&img);
  183. }
  184. // Denoise
  185. if(CONTAINS(opts->effects, Denoise)){
  186. denoise(img.prow, img.nrow, CHA_OFFSET, CH_WIDTH);
  187. denoise(img.prow, img.nrow, CHB_OFFSET, CH_WIDTH);
  188. }
  189. // Flip, for northbound passes
  190. if(CONTAINS(opts->effects, Flip_Image)){
  191. flipImage(&img, CH_WIDTH, CHA_OFFSET);
  192. flipImage(&img, CH_WIDTH, CHB_OFFSET);
  193. }
  194. // Temperature
  195. if (CONTAINS(opts->type, Temperature) && img.chB >= 4) {
  196. temperature(opts, &img, CHB_OFFSET, CH_WIDTH);
  197. ImageOut(opts, &img, CHB_OFFSET, CH_WIDTH, "Temperature", Temperature, (char *)TempPalette);
  198. }
  199. // MCIR
  200. if (CONTAINS(opts->type, MCIR))
  201. ImageOut(opts, &img, CHA_OFFSET, CH_WIDTH, "MCIR", MCIR, NULL);
  202. // Linear equalise
  203. if(CONTAINS(opts->effects, Linear_Equalise)){
  204. linearEnhance(img.prow, img.nrow, CHA_OFFSET, CH_WIDTH);
  205. linearEnhance(img.prow, img.nrow, CHB_OFFSET, CH_WIDTH);
  206. }
  207. // Histogram equalise
  208. if(CONTAINS(opts->effects, Histogram_Equalise)){
  209. histogramEqualise(img.prow, img.nrow, CHA_OFFSET, CH_WIDTH);
  210. histogramEqualise(img.prow, img.nrow, CHB_OFFSET, CH_WIDTH);
  211. }
  212. // Raw image
  213. if (CONTAINS(opts->type, Raw_Image)) {
  214. sprintf(desc, "%s (%s) & %s (%s)", ch.id[img.chA], ch.name[img.chA], ch.id[img.chB], ch.name[img.chB]);
  215. ImageOut(opts, &img, 0, IMG_WIDTH, desc, Raw_Image, NULL);
  216. }
  217. // Palette image
  218. if (CONTAINS(opts->type, Palleted)) {
  219. img.palette = opts->palette;
  220. strcpy(desc, "Palette composite");
  221. ImageOut(opts, &img, CHA_OFFSET, 909, desc, Palleted, NULL);
  222. }
  223. // Channel A
  224. if (CONTAINS(opts->type, Channel_A)) {
  225. sprintf(desc, "%s (%s)", ch.id[img.chA], ch.name[img.chA]);
  226. ImageOut(opts, &img, CHA_OFFSET, CH_WIDTH, desc, Channel_A, NULL);
  227. }
  228. // Channel B
  229. if (CONTAINS(opts->type, Channel_B)) {
  230. sprintf(desc, "%s (%s)", ch.id[img.chB], ch.name[img.chB]);
  231. ImageOut(opts, &img, CHB_OFFSET, CH_WIDTH, desc, Channel_B, NULL);
  232. }
  233. // Value distribution image
  234. if (CONTAINS(opts->type, Distribution))
  235. distrib(opts, &img, Distribution);
  236. return 1;
  237. }
  238. static int initsnd(char *filename) {
  239. SF_INFO infwav;
  240. int res;
  241. // Open audio file
  242. infwav.format = 0;
  243. audioFile = sf_open(filename, SFM_READ, &infwav);
  244. if (audioFile == NULL) {
  245. fprintf(stderr, "Could not open %s\n", filename);
  246. return 0;
  247. }
  248. res = init_dsp(infwav.samplerate);
  249. printf("Input file: %s\n", filename);
  250. if(res < 0) {
  251. fprintf(stderr, "Input sample rate too low: %d\n", infwav.samplerate);
  252. return 0;
  253. }else if(res > 0) {
  254. fprintf(stderr, "Input sample rate too high: %d\n", infwav.samplerate);
  255. return 0;
  256. }
  257. printf("Input sample rate: %d\n", infwav.samplerate);
  258. channels = infwav.channels;
  259. return 1;
  260. }
  261. // Read samples from the audio file
  262. int getsample(float *sample, int nb) {
  263. if(channels == 1){
  264. return sf_read_float(audioFile, sample, nb);
  265. }else{
  266. /* Multi channel audio is encoded such as:
  267. * Ch1,Ch2,Ch1,Ch2,Ch1,Ch2
  268. */
  269. float buf[nb * channels]; // Something like BLKIN*2 could also be used
  270. int samples = sf_read_float(audioFile, buf, nb * channels);
  271. for(int i = 0; i < nb; i++) sample[i] = buf[i * channels];
  272. return samples / channels;
  273. }
  274. }
  275. static void usage(void) {
  276. fprintf(stderr,
  277. "Aptdec [options] audio files ...\n"
  278. "Options:\n"
  279. " -i [r|a|b|t|m|p] Output image\n"
  280. " r: Raw\n"
  281. " a: Channel A\n"
  282. " b: Channel B\n"
  283. " t: Temperature\n"
  284. " m: MCIR\n"
  285. " p: Paletted image\n"
  286. " -e [t|h|d|p|f|l] Effects\n"
  287. " t: Crop telemetry\n"
  288. " h: Histogram equalise\n"
  289. " d: Denoise\n"
  290. " p: Precipitation\n"
  291. " f: Flip image\n"
  292. " l: Linear equalise\n"
  293. " c: Crop noise\n"
  294. " -o <path> Output filename\n"
  295. " -d <path> Image destination directory.\n"
  296. " -s [15-19] Satellite number\n"
  297. " -m <path> Map file\n"
  298. " -p <path> Path to palette\n"
  299. " -r Realtime decode\n"
  300. " -g Gamma adjustment (1.0 = off)\n"
  301. " -k Map offset (in px, default: 0)"
  302. "\nRefer to the README for more infomation\n");
  303. exit(EINVAL);
  304. }