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.

main.c 14 KiB

21 years ago
21 years ago
21 years ago
19 years ago
19 years ago
21 years ago
19 years ago
21 years ago
20 years ago
19 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
19 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. /*
  2. * Aptdec
  3. * Copyright (c) 2004-2005 by Thierry Leconte (F4DWV)
  4. *
  5. * $Id$
  6. *
  7. * This library is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU Library General Public License as
  9. * published by the Free Software Foundation; either version 2 of
  10. * the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU Library General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Library General Public
  18. * License along with this library; if not, write to the Free Software
  19. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20. *
  21. */
  22. #include <stdlib.h>
  23. #include <stdio.h>
  24. #include <string.h>
  25. #include <getopt.h>
  26. #include <libgen.h>
  27. #include <math.h>
  28. #include <sndfile.h>
  29. #include <png.h>
  30. #include "messages.h"
  31. #include "offsets.h"
  32. #include "colorpalette.h"
  33. extern int getpixelrow(float *pixelv);
  34. extern int init_dsp(double F);;
  35. static SNDFILE *inwav;
  36. static int initsnd(char *filename) {
  37. SF_INFO infwav;
  38. int res;
  39. // Open audio file
  40. infwav.format = 0;
  41. inwav = sf_open(filename, SFM_READ, &infwav);
  42. if (inwav == NULL) {
  43. fprintf(stderr, ERR_FILE_READ, filename);
  44. return(0);
  45. }
  46. res = init_dsp(infwav.samplerate);
  47. printf("Input file: %s\n", filename);
  48. if(res < 0) {
  49. fprintf(stderr, "Input sample rate too low: %d\n", infwav.samplerate);
  50. return(0);
  51. }else if(res > 0) {
  52. fprintf(stderr, "Input sample rate too high: %d\n", infwav.samplerate);
  53. return(0);
  54. }
  55. printf("Input sample rate: %d\n", infwav.samplerate);
  56. if (infwav.channels != 1) {
  57. fprintf(stderr, "Too many channels in input file: %d\n", infwav.channels);
  58. return(0);
  59. }
  60. return(1);
  61. }
  62. // Get a sample from the wave file
  63. int getsample(float *sample, int nb) {
  64. return sf_read_float(inwav, sample, nb);
  65. }
  66. static png_text text_ptr[] = {
  67. {PNG_TEXT_COMPRESSION_NONE, "Software", VERSION},
  68. {PNG_TEXT_COMPRESSION_NONE, "Channel", NULL, 0},
  69. {PNG_TEXT_COMPRESSION_NONE, "Description", "NOAA satellite image", 20}
  70. };
  71. static int ImageOut(char *filename, char *chid, float **prow, int nrow, int width, int offset, png_color *palette, int croptele, int layered) {
  72. FILE *pngfile;
  73. png_infop info_ptr;
  74. png_structp png_ptr;
  75. // Reduce the width of the image to componsate for the missing telemetry
  76. if(croptele) width -= TOTAL_TELE;
  77. // Initalise the PNG writer
  78. png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  79. if (!png_ptr) {
  80. fprintf(stderr, ERR_PNG_WRITE);
  81. return(0);
  82. }
  83. // Metadata
  84. info_ptr = png_create_info_struct(png_ptr);
  85. if (!info_ptr) {
  86. png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
  87. fprintf(stderr, ERR_PNG_INFO);
  88. return(0);
  89. }
  90. if(palette == NULL) {
  91. // Greyscale image
  92. png_set_IHDR(png_ptr, info_ptr, width, nrow,
  93. 8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE,
  94. PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
  95. } else {
  96. // Palleted color image
  97. png_set_IHDR(png_ptr, info_ptr, width, nrow,
  98. 8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE,
  99. PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
  100. png_set_PLTE(png_ptr, info_ptr, palette, 256);
  101. }
  102. text_ptr[1].text = chid;
  103. text_ptr[1].text_length = strlen(chid);
  104. png_set_text(png_ptr, info_ptr, text_ptr, 3);
  105. png_set_pHYs(png_ptr, info_ptr, 4000, 4000, PNG_RESOLUTION_METER);
  106. printf("Writing %s ", filename);
  107. fflush(stdout);
  108. pngfile = fopen(filename, "wb");
  109. if (pngfile == NULL) {
  110. fprintf(stderr, ERR_FILE_WRITE, filename);
  111. return(1);
  112. }
  113. png_init_io(png_ptr, pngfile);
  114. png_write_info(png_ptr, info_ptr);
  115. for (int n = 0; n < nrow; n++) {
  116. float *pixelv;
  117. png_byte pixel[2*IMG_WIDTH];
  118. pixelv = prow[n];
  119. int f = 0;
  120. for (int i = 0; i < width; i++) {
  121. // Skip parts of the image that are telemtry
  122. if(croptele){
  123. switch (i) {
  124. case 0:
  125. f += SYNC_WIDTH + SPC_WIDTH;
  126. break;
  127. case CH_WIDTH:
  128. f += TELE_WIDTH + SYNC_WIDTH + SPC_WIDTH;
  129. break;
  130. case CH_WIDTH*2:
  131. f += TELE_WIDTH;
  132. }
  133. }
  134. if(layered){
  135. // Layered image, overlay highlights in channel B over channel A
  136. float cloud = CLIP(pixelv[i+CHB_OFFSET]-141, 0, 255)/114;
  137. pixel[i] = MCOMPOSITE(255, cloud, pixelv[i+CHA_OFFSET], 1);
  138. }else{
  139. pixel[i] = pixelv[i + offset + f];
  140. }
  141. }
  142. png_write_row(png_ptr, pixel);
  143. }
  144. png_write_end(png_ptr, info_ptr);
  145. fclose(pngfile);
  146. printf("\nDone\n");
  147. png_destroy_write_struct(&png_ptr, &info_ptr);
  148. return(1);
  149. }
  150. static int ImageRGBOut(char *filename, float **prow, int nrow) {
  151. FILE *pngfile;
  152. png_infop info_ptr;
  153. png_structp png_ptr;
  154. extern void falsecolor(float vis, float temp, float *r, float *g, float *b);
  155. // Initalise the PNG writer
  156. png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  157. if (!png_ptr) {
  158. fprintf(stderr, ERR_PNG_WRITE);
  159. return(1);
  160. }
  161. info_ptr = png_create_info_struct(png_ptr);
  162. if (!info_ptr) {
  163. png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
  164. fprintf(stderr, ERR_PNG_WRITE);
  165. return(1);
  166. }
  167. png_set_IHDR(png_ptr, info_ptr, CH_WIDTH, nrow,
  168. 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
  169. PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
  170. png_set_pHYs(png_ptr, info_ptr, 4000, 4000, PNG_RESOLUTION_METER);
  171. text_ptr[1].text = "False Color";
  172. text_ptr[1].text_length = strlen(text_ptr[1].text);
  173. png_set_text(png_ptr, info_ptr, text_ptr, 3);
  174. printf("Computing false color & writing: %s", filename);
  175. fflush(stdout);
  176. pngfile = fopen(filename, "wb");
  177. if (pngfile == NULL) {
  178. fprintf(stderr, ERR_FILE_WRITE, filename);
  179. return(1);
  180. }
  181. png_init_io(png_ptr, pngfile);
  182. png_write_info(png_ptr, info_ptr);
  183. for (int n = 0; n < nrow ; n++) {
  184. png_color pix[CH_WIDTH];
  185. float *pixelc;
  186. pixelc = prow[n];
  187. for (int i = 0; i < CH_WIDTH - 1; i++) {
  188. float r = 0, g = 0, b = 0;
  189. // False color computation
  190. falsecolor(pixelc[i+CHA_OFFSET], pixelc[i+CHB_OFFSET], &r, &g, &b);
  191. pix[i].red = r;
  192. pix[i].green = g;
  193. pix[i].blue = b;
  194. }
  195. png_write_row(png_ptr, (png_bytep) pix);
  196. }
  197. png_write_end(png_ptr, info_ptr);
  198. fclose(pngfile);
  199. printf("\nDone\n");
  200. png_destroy_write_struct(&png_ptr, &info_ptr);
  201. return(0);
  202. }
  203. // Distribution of values between Channel A and Channel B
  204. static void Distrib(char *filename, float **prow, int nrow) {
  205. unsigned int distrib[256][256];
  206. int n;
  207. int x, y;
  208. int max = 0;
  209. FILE *df;
  210. for(y = 0; y < 256; y++)
  211. for(x = 0; x < 256; x++)
  212. distrib[y][x] = 0;
  213. for(n = 0; n < nrow; n++) {
  214. float *pixelv;
  215. int i;
  216. pixelv = prow[n];
  217. for(i = 0; i < CH_WIDTH; i++) {
  218. y = (int)(pixelv[i + CHA_OFFSET]);
  219. x = (int)(pixelv[i + CHB_OFFSET]);
  220. distrib[y][x] += 1;
  221. if(distrib[y][x] > max) max=distrib[y][x];
  222. }
  223. }
  224. df = fopen(filename,"w");
  225. printf("Writing %s\n",filename);
  226. fprintf(df,"P2\n#max %d\n",max);
  227. fprintf(df,"256 256\n255\n");
  228. for(y = 0; y < 256; y++)
  229. for(x = 0; x < 256; x++)
  230. fprintf(df, "%d\n", (int)((255.0 * (double)(distrib[y][x])) / (double)max));
  231. fclose(df);
  232. }
  233. extern int calibrate(float **prow, int nrow, int offset, int contrastBoost);
  234. extern void Temperature(float **prow, int nrow, int ch, int offset);
  235. extern int Ngvi(float **prow, int nrow);
  236. extern void readfcconf(char *file);
  237. extern int optind;
  238. extern char *optarg;
  239. // Default to NOAA 19
  240. int satnum = 4;
  241. static void usage(void) {
  242. printf("Aptdec [options] audio files ...\n"
  243. "Options:\n"
  244. " -e [c|t] Enhancements\n"
  245. " c: Contrast enhance\n"
  246. " t: Crop telemetry\n"
  247. " -i [r|a|b|c|t] Output image type\n"
  248. " r: Raw\n"
  249. " a: A channel\n"
  250. " b: B channel\n"
  251. " c: False color\n"
  252. " t: Temperature\n"
  253. " l: Layered\n"
  254. " -d <dir> Image destination directory.\n"
  255. " -s [15-19] Satellite number\n"
  256. " -c <file> False color config file\n");
  257. exit(1);
  258. }
  259. int main(int argc, char **argv) {
  260. char pngfilename[1024];
  261. char name[500];
  262. char pngdirname[500] = "";
  263. // Images to create, default to a channel A and channel B image with contrast enhancement and cropped telemetry
  264. char imgopt[20] = "ab";
  265. char enchancements[20] = "ct";
  266. // Image buffer
  267. float *prow[3000];
  268. int nrow;
  269. // Mapping between wedge value and channel ID
  270. char *chid[] = { "?", "1", "2", "3A", "4", "5", "3B" };
  271. char *chname[] = { "unknown", "visble", "near-infrared", "near-infrared", "thermal-infrared", "thermal-infrared", "thermal-infrared" };
  272. // The active sensor in each channel
  273. int chA, chB;
  274. // Print version
  275. printf("%s\n", VERSION);
  276. // Print usage if there are no arguments
  277. if(argc == 1)
  278. usage();
  279. int c;
  280. while ((c = getopt(argc, argv, "c:d:i:s:e:")) != EOF) {
  281. switch (c) {
  282. // Output directory name
  283. case 'd':
  284. strcpy(pngdirname, optarg);
  285. break;
  286. // False color config file
  287. case 'c':
  288. readfcconf(optarg);
  289. break;
  290. // Output image type
  291. case 'i':
  292. strcpy(imgopt, optarg);
  293. break;
  294. // Satellite number (for calibration)
  295. case 's':
  296. satnum = atoi(optarg)-15;
  297. // Check if it's within the valid range
  298. if (satnum < 0 || satnum > 4) {
  299. fprintf(stderr, "Invalid satellite number, it must be the range [15-19]\n");
  300. exit(1);
  301. }
  302. break;
  303. // Enchancements
  304. case 'e':
  305. strcpy(enchancements, optarg);
  306. break;
  307. default:
  308. usage();
  309. }
  310. }
  311. if(optind == argc){
  312. printf("No audio files provided.\n");
  313. usage();
  314. }
  315. // Pull this in from filtercoeff.h
  316. extern float Sync[32];
  317. // Loop through the provided files
  318. for (; optind < argc; optind++) {
  319. chA = chB = 0;
  320. // Generate output name
  321. strcpy(pngfilename, argv[optind]);
  322. strcpy(name, basename(pngfilename));
  323. strtok(name, ".");
  324. if (pngdirname[0] == '\0')
  325. strcpy(pngdirname, dirname(pngfilename));
  326. // Open sound file, exit if that fails
  327. if (initsnd(argv[optind]) == 0)
  328. exit(1);
  329. // Main image building loop
  330. for (nrow = 0; nrow < 3000; nrow++) {
  331. // Allocate 2150 floats worth of memory for every line of the image
  332. prow[nrow] = (float *) malloc(sizeof(float) * 2150);
  333. // Read into prow and break the loop once we reach the end of the image
  334. if (getpixelrow(prow[nrow]) == 0)
  335. break;
  336. // Signal level
  337. double signal = 0;
  338. for (int i = 0; i < 32; i++) signal += prow[nrow][i] - Sync[i];
  339. // Signal level bar
  340. char bar[30];
  341. for (int i = 0; i < 30; i++) {
  342. bar[i] = ' ';
  343. if(i < signal/2000*30) bar[i] = '#';
  344. }
  345. printf("Row: %d, Signal Strength: %s\r", nrow, bar);
  346. fflush(stdout);
  347. }
  348. printf("\nTotal rows: %d\n", nrow);
  349. sf_close(inwav);
  350. // Layered images require contrast enhancements
  351. int contrastBoost = CONTAINS(enchancements, 'c') || CONTAINS(imgopt, 'l') || CONTAINS(imgopt, 'c');
  352. chA = calibrate(prow, nrow, CHA_OFFSET, 0);
  353. chB = calibrate(prow, nrow, CHB_OFFSET, 0);
  354. printf("Channel A: %s (%s)\n", chid[chA], chname[chA]);
  355. printf("Channel B: %s (%s)\n", chid[chB], chname[chB]);
  356. // Temperature
  357. if (CONTAINS(imgopt, 't') && chB >= 4) {
  358. Temperature(prow, nrow, chB, CHB_OFFSET);
  359. sprintf(pngfilename, "%s/%s-t.png", pngdirname, name);
  360. ImageOut(pngfilename, "Temperature", prow, nrow, CH_WIDTH, CHB_OFFSET, (png_color*)TempPalette, 0, 0);
  361. }
  362. // We have to run the contrast enhance here because the temperature calibration requires real data
  363. // Yes, this requires running a large chunk of code again, but this will be addressed in the future
  364. chA = calibrate(prow, nrow, CHA_OFFSET, contrastBoost);
  365. chB = calibrate(prow, nrow, CHB_OFFSET, contrastBoost);
  366. // Layered
  367. if (CONTAINS(imgopt, 'l')){
  368. sprintf(pngfilename, "%s/%s-l.png", pngdirname, name);
  369. ImageOut(pngfilename, "Layered", prow, nrow, CH_WIDTH, 0, NULL, 0, 1);
  370. }
  371. // Raw image
  372. if (CONTAINS(imgopt, 'r')) {
  373. int croptele = CONTAINS(enchancements, 't');
  374. char channelstr[45];
  375. sprintf(channelstr, "%s (%s) & %s (%s)", chid[chA], chname[chA], chid[chB], chname[chB]);
  376. sprintf(pngfilename, "%s/%s-r.png", pngdirname, name);
  377. ImageOut(pngfilename, channelstr, prow, nrow, IMG_WIDTH, 0, NULL, croptele, 0);
  378. }
  379. // Channel A
  380. if (CONTAINS(imgopt, 'a')) {
  381. char channelstr[21];
  382. sprintf(channelstr, "%s (%s)", chid[chA], chname[chA]);
  383. sprintf(pngfilename, "%s/%s-%s.png", pngdirname, name, chid[chA]);
  384. ImageOut(pngfilename, channelstr, prow, nrow, CH_WIDTH, CHA_OFFSET, NULL, 0, 0);
  385. }
  386. // Channel B
  387. if (CONTAINS(imgopt, 'b')) {
  388. char channelstr[21];
  389. sprintf(channelstr, "%s (%s)", chid[chB], chname[chB]);
  390. sprintf(pngfilename, "%s/%s-%s.png", pngdirname, name, chid[chB]);
  391. ImageOut(pngfilename, channelstr, prow, nrow, CH_WIDTH , CHB_OFFSET, NULL, 0, 0);
  392. }
  393. // Distribution map
  394. if (CONTAINS(imgopt, 'd')) {
  395. sprintf(pngfilename, "%s/%s-d.pnm", pngdirname, name);
  396. Distrib(pngfilename, prow, nrow);
  397. }
  398. // False color image
  399. if(CONTAINS(imgopt, 'c')){
  400. if (chA == 2 && chB >= 4) { // Normal false color
  401. sprintf(pngfilename, "%s/%s-c.png", pngdirname, name);
  402. ImageRGBOut(pngfilename, prow, nrow);
  403. } else if (chA == 1 && chB == 2) { // GVI (global vegetation index) false color
  404. Ngvi(prow, nrow);
  405. sprintf(pngfilename, "%s/%s-c.png", pngdirname, name);
  406. ImageOut(pngfilename, "GVI False Color", prow, nrow, CH_WIDTH, CHB_OFFSET, (png_color*)GviPalette, 0, 0);
  407. } else {
  408. fprintf(stderr, "Lacking channels required for false color computation.\n");
  409. }
  410. }
  411. // Free the allocated memory
  412. for(int i = 0; i < 3000; i++) free(prow[i]);
  413. }
  414. exit(0);
  415. }