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.
 
 
 
 
 

501 lines
13 KiB

  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 <sndfile.h>
  28. #include <png.h>
  29. #include "messages.h"
  30. #include "offsets.h"
  31. #include "temppalette.h"
  32. #include "gvipalette.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(1);
  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(1);
  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, basically overlay highlights in channel B over channel A
  136. float cloud = CLIP(pixelv[i+CHB_OFFSET]-141, 0, 255)/114*255;
  137. pixel[i] = CLIP(pixelv[i+CHA_OFFSET] + cloud, 0, 255);
  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(0);
  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(double v, double t, 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 v, t;
  189. float r, g, b;
  190. v = pixelc[i+CHA_OFFSET];
  191. t = pixelc[i+CHB_OFFSET];
  192. falsecolor(v, t, &r, &g, &b);
  193. pix[i].red = 255.0 * r;
  194. pix[i].green = 255.0 * g;
  195. pix[i].blue = 255.0 * b;
  196. }
  197. png_write_row(png_ptr, (png_bytep) pix);
  198. }
  199. png_write_end(png_ptr, info_ptr);
  200. fclose(pngfile);
  201. printf("Done\n");
  202. png_destroy_write_struct(&png_ptr, &info_ptr);
  203. return(0);
  204. }
  205. // Distribution of values between Channel A and Channel B
  206. static void Distrib(char *filename, float **prow, int nrow) {
  207. unsigned int distrib[256][256];
  208. int n;
  209. int x, y;
  210. int max = 0;
  211. FILE *df;
  212. for(y = 0; y < 256; y++)
  213. for(x = 0; x < 256; x++)
  214. distrib[y][x] = 0;
  215. for(n = 0; n < nrow; n++) {
  216. float *pixelv;
  217. int i;
  218. pixelv = prow[n];
  219. for(i = 0; i < CH_WIDTH; i++) {
  220. y = (int)(pixelv[i + CHA_OFFSET]);
  221. x = (int)(pixelv[i + CHB_OFFSET]);
  222. distrib[y][x] += 1;
  223. if(distrib[y][x] > max) max=distrib[y][x];
  224. }
  225. }
  226. df = fopen(filename,"w");
  227. printf("Writing %s\n",filename);
  228. fprintf(df,"P2\n#max %d\n",max);
  229. fprintf(df,"256 256\n255\n");
  230. for(y = 0; y < 256; y++)
  231. for(x = 0; x < 256; x++)
  232. fprintf(df, "%d\n", (int)((255.0 * (double)(distrib[y][x])) / (double)max));
  233. fclose(df);
  234. }
  235. extern int calibrate(float **prow, int nrow, int offset, int contrastBoost);
  236. extern void Temperature(float **prow, int nrow, int ch, int offset);
  237. extern int Ngvi(float **prow, int nrow);
  238. extern void readfconf(char *file);
  239. extern int optind;
  240. extern char *optarg;
  241. // Default to NOAA 19
  242. int satnum = 4;
  243. static void usage(void) {
  244. printf("Aptdec [options] audio files ...\n"
  245. "Options:\n"
  246. " -e [c|t] Enhancements\n"
  247. " c: Contrast enhance\n"
  248. " t: Crop telemetry\n"
  249. " -i [r|a|b|c|t] Output image type\n"
  250. " r: Raw\n"
  251. " a: A channel\n"
  252. " b: B channel\n"
  253. " c: False color\n"
  254. " t: Temperature\n"
  255. " l: Layered\n"
  256. " -d <dir> Image destination directory.\n"
  257. " -s [15-19] Satellite number\n"
  258. " -c <file> False color config file\n");
  259. exit(1);
  260. }
  261. int main(int argc, char **argv) {
  262. char pngfilename[1024];
  263. char name[500];
  264. char pngdirname[500] = "";
  265. // Images to create, default to a channel A and channel B image with contrast enhancement and cropped telemetry
  266. char imgopt[20] = "ab";
  267. char enchancements[20] = "ct";
  268. // Image buffer
  269. float *prow[3000];
  270. int nrow;
  271. // Mapping between wedge value and channel ID
  272. char *chid[] = { "?", "1", "2", "3A", "4", "5", "3B" };
  273. char *chname[] = { "unknown", "visble", "near-infrared", "near-infrared", "thermal-infrared", "thermal-infrared", "thermal-infrared" };
  274. // The active sensor in each channel
  275. int chA, chB;
  276. // Print version
  277. printf("%s\n", VERSION);
  278. // Print usage if there are no arguments
  279. if(argc == 1)
  280. usage();
  281. int c;
  282. while ((c = getopt(argc, argv, "c:d:i:s:e:")) != EOF) {
  283. switch (c) {
  284. // Output directory name
  285. case 'd':
  286. strcpy(pngdirname, optarg);
  287. break;
  288. // False color config file
  289. case 'c':
  290. readfconf(optarg);
  291. break;
  292. // Output image type
  293. case 'i':
  294. strcpy(imgopt, optarg);
  295. break;
  296. // Satellite number (for calibration)
  297. case 's':
  298. satnum = atoi(optarg)-15;
  299. // Check if it's within the valid range
  300. if (satnum < 0 || satnum > 4) {
  301. fprintf(stderr, "Invalid satellite number, it must be the range [15-19]\n");
  302. exit(1);
  303. }
  304. break;
  305. // Enchancements
  306. case 'e':
  307. strcpy(enchancements, optarg);
  308. break;
  309. default:
  310. usage();
  311. }
  312. }
  313. if(optind == argc){
  314. printf("No audio files provided.\n");
  315. usage();
  316. }
  317. // Pull this in from filtercoeff.h
  318. extern float Sync[32];
  319. // Loop through the provided files
  320. for (; optind < argc; optind++) {
  321. chA = chB = 0;
  322. // Generate output name
  323. strcpy(pngfilename, argv[optind]);
  324. strcpy(name, basename(pngfilename));
  325. strtok(name, ".");
  326. if (pngdirname[0] == '\0')
  327. strcpy(pngdirname, dirname(pngfilename));
  328. // Open sound file, exit if that fails
  329. if (initsnd(argv[optind]) == 0)
  330. exit(1);
  331. // Main image building loop
  332. for (nrow = 0; nrow < 3000; nrow++) {
  333. // Allocate 2150 floats worth of memory for every line of the image
  334. prow[nrow] = (float *) malloc(sizeof(float) * 2150);
  335. // Read into prow and break the loop once we reach the end of the image
  336. if (getpixelrow(prow[nrow]) == 0)
  337. break;
  338. // Signal level
  339. double signal = 0;
  340. for (int i = 0; i < 32; i++) signal += prow[nrow][i] - Sync[i];
  341. // Signal level bar
  342. char bar[30];
  343. for (int i = 0; i < 30; i++) {
  344. bar[i] = ' ';
  345. if(i < signal/2000*30) bar[i] = '#';
  346. }
  347. printf("Row: %d, Signal Strength: %s\r", nrow, bar);
  348. fflush(stdout);
  349. }
  350. printf("\nTotal rows: %d\n", nrow);
  351. sf_close(inwav);
  352. // Layered images require contrast enhancements
  353. int contrastBoost = CONTAINS(enchancements, 'c') || CONTAINS(imgopt, 'l');
  354. chA = calibrate(prow, nrow, CHA_OFFSET, 0);
  355. chB = calibrate(prow, nrow, CHB_OFFSET, 0);
  356. printf("Channel A: %s (%s)\n", chid[chA], chname[chA]);
  357. printf("Channel B: %s (%s)\n", chid[chB], chname[chB]);
  358. // Temperature
  359. if (CONTAINS(imgopt, 't') && chB >= 4) {
  360. Temperature(prow, nrow, chB, CHB_OFFSET);
  361. sprintf(pngfilename, "%s/%s-t.png", pngdirname, name);
  362. ImageOut(pngfilename, "Temperature", prow, nrow, CH_WIDTH, CHB_OFFSET, (png_color*)TempPalette, 0, 0);
  363. }
  364. // We have to run the contrast enhance here because the temperature function requires real data
  365. // Yes, this is bodgy, yes I should replace this with something more elegant
  366. chA = calibrate(prow, nrow, CHA_OFFSET, contrastBoost);
  367. chB = calibrate(prow, nrow, CHB_OFFSET, contrastBoost);
  368. // Layered
  369. if (CONTAINS(imgopt, 'l')){
  370. sprintf(pngfilename, "%s/%s-l.png", pngdirname, name);
  371. ImageOut(pngfilename, "Layered", prow, nrow, CH_WIDTH, 0, NULL, 0, 1);
  372. }
  373. // Raw image
  374. if (CONTAINS(imgopt, 'r')) {
  375. int croptele = CONTAINS(enchancements, 't');
  376. char channelstr[45];
  377. sprintf(channelstr, "%s (%s) & %s (%s)", chid[chA], chname[chA], chid[chB], chname[chB]);
  378. sprintf(pngfilename, "%s/%s-r.png", pngdirname, name);
  379. ImageOut(pngfilename, channelstr, prow, nrow, IMG_WIDTH, 0, NULL, croptele, 0);
  380. }
  381. // Channel A
  382. if (CONTAINS(imgopt, 'a')) {
  383. char channelstr[21];
  384. sprintf(channelstr, "%s (%s)", chid[chA], chname[chA]);
  385. sprintf(pngfilename, "%s/%s-%s.png", pngdirname, name, chid[chA]);
  386. ImageOut(pngfilename, channelstr, prow, nrow, CH_WIDTH, CHA_OFFSET, NULL, 0, 0);
  387. }
  388. // Channel B
  389. if (CONTAINS(imgopt, 'b')) {
  390. char channelstr[21];
  391. sprintf(channelstr, "%s (%s)", chid[chB], chname[chB]);
  392. sprintf(pngfilename, "%s/%s-%s.png", pngdirname, name, chid[chB]);
  393. ImageOut(pngfilename, channelstr, prow, nrow, CH_WIDTH , CHB_OFFSET, NULL, 0, 0);
  394. }
  395. // Distribution map
  396. if (CONTAINS(imgopt, 'd')) {
  397. sprintf(pngfilename, "%s/%s-d.pnm", pngdirname, name);
  398. Distrib(pngfilename, prow, nrow);
  399. }
  400. // False color image
  401. if(CONTAINS(imgopt, 'c')){
  402. if (chA == 2 && chB == 4) { // Normal false color
  403. sprintf(pngfilename, "%s/%s-c.png", pngdirname, name);
  404. ImageRGBOut(pngfilename, prow, nrow);
  405. } else if (chA == 1 && chB == 2) { // GVI false color
  406. Ngvi(prow, nrow);
  407. sprintf(pngfilename, "%s/%s-c.png", pngdirname, name);
  408. ImageOut(pngfilename, "GVI False Color", prow, nrow, CH_WIDTH, CHB_OFFSET, (png_color*)GviPalette, 0, 0);
  409. } else {
  410. fprintf(stderr, "Lacking channels required for false color computation.\n");
  411. }
  412. }
  413. // Free the allocated memory
  414. for(int i = 0; i < 3000; i++) free(prow[i]);
  415. }
  416. exit(0);
  417. }