@@ -0,0 +1,53 @@ | |||||
# Created by https://www.gitignore.io/api/c | |||||
# Edit at https://www.gitignore.io/?templates=c | |||||
# Object files | |||||
*.o | |||||
*.ko | |||||
*.obj | |||||
*.elf | |||||
# Linker output | |||||
*.ilk | |||||
*.map | |||||
*.exp | |||||
# Precompiled Headers | |||||
*.gch | |||||
*.pch | |||||
# Libraries | |||||
*.lib | |||||
*.a | |||||
*.la | |||||
*.lo | |||||
# Shared objects (inc. Windows DLLs) | |||||
*.dll | |||||
*.so | |||||
*.so.* | |||||
*.dylib | |||||
# Executables | |||||
*.exe | |||||
*.out | |||||
*.app | |||||
*.i*86 | |||||
*.x86_64 | |||||
*.hex | |||||
# Debug files | |||||
*.dSYM/ | |||||
*.su | |||||
*.idb | |||||
*.pdb | |||||
# Program specifics | |||||
*.png | |||||
*.wav | |||||
!textlogo.png | |||||
*.pnm | |||||
aptdec | |||||
# VSCode | |||||
.vscode |
@@ -1,16 +1,16 @@ | |||||
CC=gcc | |||||
BIN=/usr/bin | |||||
INCLUDES=-I. | |||||
CFLAGS= -O3 -Wall $(INCLUDES) | |||||
OBJS= main.o image.o dsp.o filter.o reg.o fcolor.o | |||||
CC = gcc | |||||
BIN = /usr/bin | |||||
INCLUDES = -I. | |||||
CFLAGS = -O3 -Wall $(INCLUDES) | |||||
OBJS = main.o image.o dsp.o filter.o reg.o fcolor.o | |||||
aptdec: $(OBJS) | aptdec: $(OBJS) | ||||
$(CC) -o $@ $(OBJS) -lm -lsndfile -lpng | $(CC) -o $@ $(OBJS) -lm -lsndfile -lpng | ||||
main.o: main.c version.h temppalette.h gvipalette.h offsets.h | |||||
main.o: main.c temppalette.h gvipalette.h offsets.h messages.h | |||||
dsp.o: dsp.c filtercoeff.h filter.h | dsp.o: dsp.c filtercoeff.h filter.h | ||||
filter.o: filter.c filter.h | filter.o: filter.c filter.h | ||||
image.o: image.c satcal.h offsets.h | |||||
image.o: image.c satcal.h offsets.h messages.h | |||||
fcolor.o: fcolor.c offsets.h | fcolor.o: fcolor.c offsets.h | ||||
clean: | clean: | ||||
@@ -1,79 +0,0 @@ | |||||
ATPDEC README (Thierry Leconte F4DWV (c) 2004-2009) | |||||
DESCRIPTION | |||||
Atpdec is an open source program that decodes images transmitted by POES | |||||
NOAA weather satellite series. | |||||
These satellites transmit continuously, among other things, medium | |||||
resolution images of the earth on 137Mhz. | |||||
These transmissions could be easily received with an inexpensive antenna | |||||
and dedicated receiver. | |||||
Output from such a receiver, is an audio signal that could be recorded | |||||
into a soundfile with any soundcard. | |||||
Atpdec will convert these sounfiles into .png images. | |||||
For each soundfile up to 6 images could be generated : | |||||
1. Raw image : contains the 2 transmitted channel images + telemetry | |||||
and synchro pulses. | |||||
2. Calibrated channel A image | |||||
3. Calibrated channel B image | |||||
4. Temperature compensed I.R image | |||||
5. False color image | |||||
Input soundfiles must be mono signal sampled at 11025 Hz. | |||||
Atpdec use libsndfile to read soundfile, so any sound file format supported by libsndfile | |||||
could be read.(Only tested with .wav file). | |||||
COMPILATION | |||||
Atpdec is written in plain standart C and must be very portable. | |||||
It was only tested on Linux Fedora , but must work on any Unix platform. | |||||
Just adapt the Makefile and type make (sorry no configure). | |||||
atpdec use libsndfile, libpng and libm. | |||||
snd.h and png.h header must be present on your system. | |||||
If they are not on standard path, edit the include path in the Makefile. | |||||
USAGE | |||||
atpdec [options] soundfiles ... | |||||
OPTIONS | |||||
-i [r|a|b|c|t] | |||||
Toggle raw (r) , channel A (a) , channel B (b) , false color (c) , | |||||
or temperature (t) output. | |||||
Default : "ac" | |||||
-d directory | |||||
Optional images destination directory. | |||||
Default : soundfile directory. | |||||
-s n | |||||
Satellite number 15 to 19 | |||||
Used for Temperature compensation. | |||||
Default : NOAA-19 | |||||
-c conf_file | |||||
Use configuration file for false color generation. | |||||
Default : Internal parameters. | |||||
OUTPUT | |||||
Generated image are in png format, 8bits greyscale for raw and channel A|B images, | |||||
24bits RVB for false color. | |||||
Image names are soundfilename-x.png, where x is : | |||||
-r for raw images | |||||
-satellite instrument number (1,2,3A,3B,4,5) for channel A|B images | |||||
-c for false colors. | |||||
EXAMPLE | |||||
atpdec -d image -i ac *.wav | |||||
Will process all .wav files in the current directory, generate only channel A and false color images and put them in the image directory. | |||||
@@ -1,33 +1,31 @@ | |||||
# Aptdec | |||||
![Aptdec logo](textlogo.png) | |||||
Thierry Leconte F4DWV (c) 2004-2009 | Thierry Leconte F4DWV (c) 2004-2009 | ||||
## Description | ## Description | ||||
Aptec is an FOSS program that decodes images transmitted by POES NOAA weather satellites. | |||||
These satellites transmit continuously (among other things), medium resolution (1px/4km) images of the earth on 137 MHz. | |||||
These transmissions could be easily received with an simple antenna and cheap SDR. | |||||
Then the transimssion can easily be decoded in narrow band FM mode. | |||||
Aptdec is an FOSS program that decodes images transmitted by NOAA weather satellites. These satellites transmit continuously (among other things), medium resolution (1px/4km) images of the earth on 137 MHz. | |||||
These transmissions could be easily received with an simple antenna and cheap SDR. Then the transmission can easily be decoded in narrow band FM mode. | |||||
Aptdec can convert these recordings into .png images. | |||||
Aptdec can convert these audio files into .png images. | |||||
For each recording up to 6 images can be generated: | |||||
For each audio file up to 6 images can be generated: | |||||
1. Raw image: contains the 2 transmitted channel images + telemetry | |||||
and synchronisation pulses. | |||||
1. Raw image: contains the 2 transmitted channel images + telemetry and synchronization pulses. | |||||
2. Calibrated channel A image | 2. Calibrated channel A image | ||||
3. Calibrated channel B image | 3. Calibrated channel B image | ||||
4. Temperature compensated I.R image | |||||
4. Temperature compensated IR image | |||||
5. False color image | 5. False color image | ||||
6. Layered image, boosts cloud visibility | |||||
Input recordings must be mono with a sample rate of 11025 Hz. | |||||
Aptdec uses `libsndfile` to read the input recording, so any format supported by `libsndfile` may be used (only tested with .wav files). | |||||
The input audio file must be mono with a sample rate in the range of 4160-62400 Hz, lower samples rates will process faster. | |||||
Aptdec uses `libsndfile` to read the input audio, so any format supported by `libsndfile` may be used (however it has only tested with `.wav` files). | |||||
## Compilation | ## Compilation | ||||
Aptdec is written is portable since it is written in standard C. | |||||
It has only tested on Fedora and Debian, but will work on any Unix platform. | |||||
Just edit the Makefile and run `make` (no configure script as of now). | |||||
Aptdec is portable since it is written in standard C. | |||||
It has successfully compiled and ran on Debian with both `gcc` and `clang` and will most likely work on any Unix platform. | |||||
Just edit the Makefile and run `make` (no configure script as of right now). | |||||
Aptdec uses `libsndfile`, `libpng` and `libm`. | Aptdec uses `libsndfile`, `libpng` and `libm`. | ||||
The `snd.h` and `png.h` headers must be present on your system. | The `snd.h` and `png.h` headers must be present on your system. | ||||
@@ -35,55 +33,75 @@ If they are not on standard path, edit the include path in the Makefile. | |||||
## Usage | ## Usage | ||||
To compile | |||||
`make` | |||||
To run without installing | To run without installing | ||||
`./Aptdec [options] recordings ...` | |||||
`./aptdec [options] audio files...` | |||||
To install | To install | ||||
`sudo make install` | `sudo make install` | ||||
To run once installed | |||||
`aptdec [options] audio files...` | |||||
To uninstall | To uninstall | ||||
`sudo make uninstall` | `sudo make uninstall` | ||||
To run once installed | |||||
`Aptdec [options] recordings ...` | |||||
## Options | ## Options | ||||
``` | ``` | ||||
-i [r|a|b|c|t] | |||||
Toggle raw (r), channel A (a), channel B (b), false color (c), | |||||
or temperature (t) output. | |||||
Default: ac | |||||
-i [r|a|b|c|t|l] | |||||
Output image type | |||||
Raw (r), Channel A (a), Channel B (b), False Color (c), Temperature (t), Layered (l) | |||||
Default: ab | |||||
-d <dir> | -d <dir> | ||||
Optional images destination directory. | |||||
Default: Recording directory. | |||||
Images destination directory (optional). | |||||
Default: Current directory | |||||
-s [15|16|17|18|19] | -s [15|16|17|18|19] | ||||
Satellite number | Satellite number | ||||
For temperature and false color generation | For temperature and false color generation | ||||
Default: 19 | Default: 19 | ||||
-e [c|t] | |||||
Enhancements | |||||
Contrast (c) or Crop Telemetry (t) | |||||
Defaults: ct | |||||
-c <file> | -c <file> | ||||
Use configuration file for false color generation. | Use configuration file for false color generation. | ||||
Default: Internal parameters. | |||||
Default: Internal parameters | |||||
``` | ``` | ||||
## Output | ## Output | ||||
Generated images are outputted in PNG, 8 bit greyscale for raw and channel A|B images, 24 bit RGB for false color. | Generated images are outputted in PNG, 8 bit greyscale for raw and channel A|B images, 24 bit RGB for false color. | ||||
Image names are `recordingname-x.png`, where `x` is: | |||||
Image names are `audiofile-x.png`, where `x` is: | |||||
- `r` for raw images | |||||
- Sensor ID (1, 2, 3A, 3B, 4, 5) for channel A|B images | |||||
- `c` for false color. | |||||
- `t` for temperature calibrated images | |||||
- `l` for layered images | |||||
Currently there are 2 available enchancements: | |||||
- r for raw images | |||||
- satellite instrument number (1, 2, 3A, 3B, 4, 5) for channel A|B images | |||||
- c for false colors. | |||||
- `c` for contrast enhancements, on by default | |||||
- `t` for crop telemetry, on by default, only has effects on raw images | |||||
## Example | ## Example | ||||
`Aptdec -d images -i ac *.wav` | |||||
`aptdec -d images -i ab *.wav` | |||||
This will process all `.wav` files in the current directory, generate contrast enhanced channel A and B images and put them in the `images` directory. | |||||
## Further reading | |||||
This will process all .wav files in the current directory, generate channel A and false color images and put them in the `images` directory. | |||||
[https://noaasis.noaa.gov/NOAASIS/pubs/Users_Guide-Building_Receive_Stations_March_2009.pdf](https://noaasis.noaa.gov/NOAASIS/pubs/Users_Guide-Building_Receive_Stations_March_2009.pdf) | |||||
[https://web.archive.org/web/20141220021557/https://www.ncdc.noaa.gov/oa/pod-guide/ncdc/docs/klm/tables.htm](https://web.archive.org/web/20141220021557/https://www.ncdc.noaa.gov/oa/pod-guide/ncdc/docs/klm/tables.htm) | |||||
## License | ## License | ||||
@@ -1,5 +1,5 @@ | |||||
/* | /* | ||||
* Aptec | |||||
* Aptdec | |||||
* Copyright (c) 2004 by Thierry Leconte (F4DWV) | * Copyright (c) 2004 by Thierry Leconte (F4DWV) | ||||
* | * | ||||
* $Id$ | * $Id$ | ||||
@@ -20,17 +20,25 @@ | |||||
* | * | ||||
*/ | */ | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <stdio.h> | |||||
#include <string.h> | #include <string.h> | ||||
#include <math.h> | #include <math.h> | ||||
#ifndef M_PI | #ifndef M_PI | ||||
#define M_PI 3.14159265358979323846 /* for OS's that don't include it */ | |||||
#define M_PI 3.1415926535 // For OS's that don't include it | |||||
#endif | #endif | ||||
#include "filter.h" | #include "filter.h" | ||||
#include "filtercoeff.h" | #include "filtercoeff.h" | ||||
/* WARNING | |||||
* The comments in this file are at best educated guesses. | |||||
* I am not a DSP god and can not figure out what a complicated | |||||
* math function does just by looking at it. | |||||
*/ | |||||
extern int getsample(float *inbuff, int nb); | extern int getsample(float *inbuff, int nb); | ||||
#define BLKAMP 1024 | #define BLKAMP 1024 | ||||
// Block size | |||||
#define BLKIN 1024 | #define BLKIN 1024 | ||||
#define Fc 2400.0 | #define Fc 2400.0 | ||||
@@ -48,10 +56,10 @@ static double FreqLine = 1.0; | |||||
static double FreqOsc; | static double FreqOsc; | ||||
static double K1, K2; | static double K1, K2; | ||||
// Check if the input sample rate is correct | |||||
int init_dsp(double F) { | int init_dsp(double F) { | ||||
if(F > Fi) return (1); | |||||
if(F < Fp) return (-1); | |||||
if(F > Fi) return(1); | |||||
if(F < Fp) return(-1); | |||||
Fe = F; | Fe = F; | ||||
K1 = DFc / Fe; | K1 = DFc / Fe; | ||||
@@ -61,9 +69,10 @@ int init_dsp(double F) { | |||||
return(0); | return(0); | ||||
} | } | ||||
/* fast phase estimator */ | |||||
static inline double Phase(double I,double Q) { | |||||
double angle,r; | |||||
// Fast phase estimator | |||||
// Calculates the phase angle of a signal from a IQ sample | |||||
static inline double Phase(double I, double Q) { | |||||
double angle, r; | |||||
int s; | int s; | ||||
if(I == 0.0 && Q == 0.0) | if(I == 0.0 && Q == 0.0) | ||||
@@ -90,68 +99,79 @@ static inline double Phase(double I,double Q) { | |||||
return(-angle); | return(-angle); | ||||
} | } | ||||
// Phase locked loop | |||||
// Used to get value from an IQ sample with noise reduction | |||||
static double pll(double I, double Q) { | static double pll(double I, double Q) { | ||||
/* pll coeff */ | |||||
// PLL coefficient | |||||
static double PhaseOsc = 0.0; | static double PhaseOsc = 0.0; | ||||
double Io, Qo; | double Io, Qo; | ||||
double Ip, Qp; | double Ip, Qp; | ||||
double DPhi; | double DPhi; | ||||
/* quadrature oscillator */ | |||||
// Quadrature oscillator / reference | |||||
Io = cos(PhaseOsc); | Io = cos(PhaseOsc); | ||||
Qo = sin(PhaseOsc); | Qo = sin(PhaseOsc); | ||||
/* phase detector */ | |||||
// Phase detector | |||||
Ip = I * Io + Q * Qo; | Ip = I * Io + Q * Qo; | ||||
Qp = Q * Io - I * Qo; | Qp = Q * Io - I * Qo; | ||||
DPhi = Phase(Ip, Qp); | DPhi = Phase(Ip, Qp); | ||||
/* loop filter */ | |||||
// Loop filter | |||||
PhaseOsc += 2.0 * M_PI * (K1 * DPhi + FreqOsc); | PhaseOsc += 2.0 * M_PI * (K1 * DPhi + FreqOsc); | ||||
if (PhaseOsc > M_PI) | if (PhaseOsc > M_PI) | ||||
PhaseOsc -= 2.0 * M_PI; | |||||
PhaseOsc -= 2.0 * M_PI; | |||||
if (PhaseOsc <= -M_PI) | if (PhaseOsc <= -M_PI) | ||||
PhaseOsc += 2.0 * M_PI; | |||||
PhaseOsc += 2.0 * M_PI; | |||||
FreqOsc += K2 * DPhi; | FreqOsc += K2 * DPhi; | ||||
if (FreqOsc > ((Fc + DFc) / Fe)) | if (FreqOsc > ((Fc + DFc) / Fe)) | ||||
FreqOsc = (Fc + DFc) / Fe; | |||||
FreqOsc = (Fc + DFc) / Fe; | |||||
if (FreqOsc < ((Fc - DFc) / Fe)) | if (FreqOsc < ((Fc - DFc) / Fe)) | ||||
FreqOsc = (Fc - DFc) / Fe; | |||||
FreqOsc = (Fc - DFc) / Fe; | |||||
return (Ip); | |||||
return(Ip); | |||||
} | } | ||||
// Convert audio samples into a pixel | |||||
static int getamp(double *ambuff, int nb) { | static int getamp(double *ambuff, int nb) { | ||||
static float inbuff[BLKIN]; | static float inbuff[BLKIN]; | ||||
static int idxin=0; | |||||
static int nin=0; | |||||
int n; | |||||
static int idxin = 0; | |||||
static int nin = 0; | |||||
int n; | |||||
for (n = 0; n < nb; n++) { | for (n = 0; n < nb; n++) { | ||||
double I, Q; | double I, Q; | ||||
// If the amount of samples is small enough to be processed | |||||
if (nin < IQFilterLen * 2 + 2) { | if (nin < IQFilterLen * 2 + 2) { | ||||
// Number of samples read | |||||
int res; | int res; | ||||
memmove(inbuff, &(inbuff[idxin]), nin * sizeof(float)); | memmove(inbuff, &(inbuff[idxin]), nin * sizeof(float)); | ||||
idxin = 0; | idxin = 0; | ||||
res = getsample(&(inbuff[nin]), BLKIN - nin); | |||||
// Read some samples | |||||
res = getsample(&(inbuff[nin]), BLKIN - nin); | |||||
nin += res; | nin += res; | ||||
// If we haven't read any more samples, return how far we got | |||||
if (nin < IQFilterLen * 2 + 2) | if (nin < IQFilterLen * 2 + 2) | ||||
return (n); | |||||
return(n); | |||||
} | } | ||||
// Process read samples into a brightness value | |||||
iqfir(&inbuff[idxin], iqfilter, IQFilterLen, &I, &Q); | iqfir(&inbuff[idxin], iqfilter, IQFilterLen, &I, &Q); | ||||
ambuff[n] = pll(I, Q); | ambuff[n] = pll(I, Q); | ||||
idxin += 1; | idxin += 1; | ||||
nin -= 1; | nin -= 1; | ||||
} | } | ||||
return (n); | |||||
return(n); | |||||
} | } | ||||
// Get an entire row of pixels, without alignment | |||||
int getpixelv(float *pvbuff, int nb) { | int getpixelv(float *pvbuff, int nb) { | ||||
// Amplitude buffer | |||||
static double ambuff[BLKAMP]; | static double ambuff[BLKAMP]; | ||||
static int nam = 0; | static int nam = 0; | ||||
static int idxam = 0; | static int idxam = 0; | ||||
@@ -160,7 +180,6 @@ int getpixelv(float *pvbuff, int nb) { | |||||
double mult; | double mult; | ||||
mult = (double) Fi / Fe * FreqLine; | mult = (double) Fi / Fe * FreqLine; | ||||
m = RSFilterLen / mult + 1; | m = RSFilterLen / mult + 1; | ||||
for (n = 0; n < nb; n++) { | for (n = 0; n < nb; n++) { | ||||
@@ -173,23 +192,24 @@ int getpixelv(float *pvbuff, int nb) { | |||||
res = getamp(&(ambuff[nam]), BLKAMP - nam); | res = getamp(&(ambuff[nam]), BLKAMP - nam); | ||||
nam += res; | nam += res; | ||||
if (nam < m) | if (nam < m) | ||||
return (n); | |||||
return(n); | |||||
} | } | ||||
// Denoise | |||||
pvbuff[n] = rsfir(&(ambuff[idxam]), rsfilter, RSFilterLen, offset, mult) * mult * 256.0; | pvbuff[n] = rsfir(&(ambuff[idxam]), rsfilter, RSFilterLen, offset, mult) * mult * 256.0; | ||||
//printf("%g\n",pvbuff[n]); | |||||
shift = ((int) floor((RSMULT - offset) / mult)) + 1; | shift = ((int) floor((RSMULT - offset) / mult)) + 1; | ||||
offset = shift * mult + offset - RSMULT ; | |||||
offset = shift * mult + offset - RSMULT; | |||||
idxam += shift; | idxam += shift; | ||||
nam -= shift; | nam -= shift; | ||||
} | } | ||||
return (nb); | |||||
return(nb); | |||||
} | } | ||||
// Align this line based off of the synchronisation markers | |||||
int getpixelrow(float *pixelv) { | int getpixelrow(float *pixelv) { | ||||
// Create an array for this row | |||||
static float pixels[PixelLine + SyncFilterLen]; | static float pixels[PixelLine + SyncFilterLen]; | ||||
static int npv = 0; | static int npv = 0; | ||||
static int synced = 0; | static int synced = 0; | ||||
@@ -205,61 +225,70 @@ int getpixelrow(float *pixelv) { | |||||
res = getpixelv(&(pixelv[npv]), SyncFilterLen + 2 - npv); | res = getpixelv(&(pixelv[npv]), SyncFilterLen + 2 - npv); | ||||
npv += res; | npv += res; | ||||
if (npv < SyncFilterLen + 2) | if (npv < SyncFilterLen + 2) | ||||
return (0); | |||||
return(0); | |||||
} | } | ||||
/* test sync */ | |||||
// Test current synchronisation | |||||
ecorr = fir(pixelv, Sync, SyncFilterLen); | ecorr = fir(pixelv, Sync, SyncFilterLen); | ||||
corr = fir(&(pixelv[1]), Sync, SyncFilterLen); | |||||
lcorr = fir(&(pixelv[2]), Sync, SyncFilterLen); | |||||
corr = fir(&(pixelv[1]), Sync, SyncFilterLen - 1); | |||||
lcorr = fir(&(pixelv[2]), Sync, SyncFilterLen - 2); | |||||
// Calculate the per pixel offset | |||||
FreqLine = 1.0+((ecorr-lcorr) / corr / PixelLine / 4.0); | FreqLine = 1.0+((ecorr-lcorr) / corr / PixelLine / 4.0); | ||||
// Maximum acceptable offset | |||||
if (corr < 0.75 * max) { | if (corr < 0.75 * max) { | ||||
synced = 0; | synced = 0; | ||||
FreqLine = 1.0; | FreqLine = 1.0; | ||||
} | } | ||||
max = corr; | max = corr; | ||||
if (synced < 8) { | if (synced < 8) { | ||||
int shift, mshift; | |||||
int mshift; | |||||
if (npv < PixelLine + SyncFilterLen) { | |||||
res = getpixelv(&(pixelv[npv]), PixelLine + SyncFilterLen - npv); | |||||
npv += res; | |||||
if (npv < PixelLine + SyncFilterLen) | |||||
return (0); | |||||
} | |||||
if (npv < PixelLine + SyncFilterLen) { | |||||
res = getpixelv(&(pixelv[npv]), PixelLine + SyncFilterLen - npv); | |||||
npv += res; | |||||
if (npv < PixelLine + SyncFilterLen) | |||||
return(0); | |||||
} | |||||
/* lookup sync start */ | |||||
mshift = 0; | |||||
for (shift = 1; shift < PixelLine; shift++) { | |||||
double corr; | |||||
// Shift this line until we see the best results | |||||
mshift = 0; | |||||
for (int shift = 1; shift < PixelLine; shift++) { | |||||
double corr; | |||||
corr = fir(&(pixelv[shift + 1]), Sync, SyncFilterLen); | |||||
if (corr > max) { | |||||
mshift = shift; | |||||
max = corr; | |||||
} | |||||
corr = fir(&(pixelv[shift + 1]), Sync, SyncFilterLen); | |||||
if (corr > max) { | |||||
mshift = shift; | |||||
max = corr; | |||||
} | |||||
} | |||||
// Shift memory, shifting this row | |||||
if (mshift != 0) { | |||||
memmove(pixelv, &(pixelv[mshift]), (npv - mshift) * sizeof(float)); | |||||
npv -= mshift; | |||||
synced = 0; | |||||
FreqLine = 1.0; | |||||
} else | |||||
synced += 1; | |||||
} | } | ||||
if (mshift != 0) { | |||||
memmove(pixelv, &(pixelv[mshift]), (npv - mshift) * sizeof(float)); | |||||
npv -= mshift; | |||||
synced = 0; | |||||
FreqLine = 1.0; | |||||
} else | |||||
synced += 1; | |||||
} | |||||
// If there are not enough pixels try to grab some more | |||||
if (npv < PixelLine) { | if (npv < PixelLine) { | ||||
res = getpixelv(&(pixelv[npv]), PixelLine - npv); | res = getpixelv(&(pixelv[npv]), PixelLine - npv); | ||||
npv += res; | npv += res; | ||||
// If we fail this then exit with 0 (which breaks the loop at main.c:338) | |||||
if (npv < PixelLine) | if (npv < PixelLine) | ||||
return (0); | |||||
return(0); | |||||
} | } | ||||
// If we're finished reset npv to 0 | |||||
if (npv == PixelLine) { | if (npv == PixelLine) { | ||||
npv = 0; | npv = 0; | ||||
} else { | |||||
} else { // Move the pixel build buffer to the output buffer | |||||
memmove(pixels, &(pixelv[PixelLine]), (npv - PixelLine) * sizeof(float)); | memmove(pixels, &(pixelv[PixelLine]), (npv - PixelLine) * sizeof(float)); | ||||
npv -= PixelLine; | npv -= PixelLine; | ||||
} | } | ||||
return (1); | |||||
return(1); | |||||
} | } |
@@ -104,18 +104,18 @@ void falsecolor(double v, double t, float *r, float *g, float *b) { | |||||
if (t > fcinfo.Threshold) { | if (t > fcinfo.Threshold) { | ||||
if (v < fcinfo.Seathreshold) { | if (v < fcinfo.Seathreshold) { | ||||
/* sea */ | |||||
// Sea | |||||
top = fcinfo.SeaTop, bot = fcinfo.SeaBot; | top = fcinfo.SeaTop, bot = fcinfo.SeaBot; | ||||
scv = v / fcinfo.Seathreshold; | scv = v / fcinfo.Seathreshold; | ||||
sct = (256.0 - t) / (256.0 - fcinfo.Threshold); | sct = (256.0 - t) / (256.0 - fcinfo.Threshold); | ||||
} else { | } else { | ||||
/* ground */ | |||||
// Ground | |||||
top = fcinfo.GroundTop, bot = fcinfo.GroundBot; | top = fcinfo.GroundTop, bot = fcinfo.GroundBot; | ||||
scv = (v - fcinfo.Seathreshold) / (fcinfo.Landthreshold - fcinfo.Seathreshold); | scv = (v - fcinfo.Seathreshold) / (fcinfo.Landthreshold - fcinfo.Seathreshold); | ||||
sct = (256.0 - t) / (256.0 - fcinfo.Threshold); | sct = (256.0 - t) / (256.0 - fcinfo.Threshold); | ||||
} | } | ||||
} else { | } else { | ||||
/* clouds */ | |||||
// Clouds | |||||
top = fcinfo.CloudTop, bot = fcinfo.CloudBot; | top = fcinfo.CloudTop, bot = fcinfo.CloudBot; | ||||
scv = v / 256.0; | scv = v / 256.0; | ||||
sct = (256.0 - t) / 256.0; | sct = (256.0 - t) / 256.0; | ||||
@@ -129,27 +129,21 @@ void falsecolor(double v, double t, float *r, float *g, float *b) { | |||||
}; | }; | ||||
void Ngvi(float **prow, int nrow) { | void Ngvi(float **prow, int nrow) { | ||||
int n; | |||||
printf("GVI... "); | printf("GVI... "); | ||||
fflush(stdout); | fflush(stdout); | ||||
for (n = 0; n < nrow; n++) { | |||||
for (int n = 0; n < nrow; n++) { | |||||
float *pixelv; | float *pixelv; | ||||
int i; | |||||
pixelv = prow[n]; | pixelv = prow[n]; | ||||
for (i = 0; i < CH_WIDTH; i++) { | |||||
for (int i = 0; i < CH_WIDTH; i++) { | |||||
float pv; | float pv; | ||||
double gvi; | double gvi; | ||||
gvi = (pixelv[i + CHA_OFFSET] - pixelv[i + CHB_OFFSET]) / (pixelv[i + CHA_OFFSET] + pixelv[i + CHB_OFFSET]); | gvi = (pixelv[i + CHA_OFFSET] - pixelv[i + CHB_OFFSET]) / (pixelv[i + CHA_OFFSET] + pixelv[i + CHB_OFFSET]); | ||||
pv = (gvi + 0.1) * 340.0; | pv = (gvi + 0.1) * 340.0; | ||||
if (pv > 255.0) | |||||
pv = 255.0; | |||||
if (pv < 0.0) | |||||
pv = 0.0; | |||||
pv = CLIP(pv, 0, 255); | |||||
pixelv[i + CHB_OFFSET] = pv; | pixelv[i + CHB_OFFSET] = pv; | ||||
} | } | ||||
@@ -1,5 +1,5 @@ | |||||
/* | /* | ||||
* Aptec | |||||
* Aptdec | |||||
* Copyright (c) 2004 by Thierry Leconte (F4DWV) | * Copyright (c) 2004 by Thierry Leconte (F4DWV) | ||||
* | * | ||||
* $Id$ | * $Id$ | ||||
@@ -22,37 +22,39 @@ | |||||
#include "filter.h" | #include "filter.h" | ||||
#include <math.h> | #include <math.h> | ||||
// Sum of a matrix multiplication of 2 arrays | |||||
float fir(float *buff, const float *coeff, const int len) { | float fir(float *buff, const float *coeff, const int len) { | ||||
int i; | |||||
double r; | double r; | ||||
r = 0.0; | r = 0.0; | ||||
for (i = 0; i < len; i++) { | |||||
for (int i = 0; i < len; i++) { | |||||
r += buff[i] * coeff[i]; | r += buff[i] * coeff[i]; | ||||
} | } | ||||
return r; | |||||
return(r); | |||||
} | } | ||||
// Create an IQ sample from a sample buffer | |||||
void iqfir(float *buff, const float *coeff, const int len, double *I, double *Q) { | void iqfir(float *buff, const float *coeff, const int len, double *I, double *Q) { | ||||
int k; | |||||
double i, q; | double i, q; | ||||
i = q = 0.0; | i = q = 0.0; | ||||
for (k = 0; k < len; k++) { | |||||
for (int k = 0; k < len; k++) { | |||||
q += buff[2*k] * coeff[k]; | q += buff[2*k] * coeff[k]; | ||||
// Average out the I samples, which gives us the DC offset | |||||
i += buff[2*k]; | i += buff[2*k]; | ||||
} | } | ||||
i= buff[len-1] - i / len; | |||||
*I=i, *Q=q; | |||||
// Grab the peak value of the wave and subtract the DC offset | |||||
i = buff[len-1] - (i / len); | |||||
*I = i, *Q = q; | |||||
} | } | ||||
// Denoise, I don't know how it works, but it does | |||||
float rsfir(double *buff, const float *coeff, const int len, const double offset, const double delta) { | float rsfir(double *buff, const float *coeff, const int len, const double offset, const double delta) { | ||||
int i; | |||||
double n; | |||||
double out; | double out; | ||||
out = 0.0; | out = 0.0; | ||||
for (i = 0, n = offset; i < (len-1)/delta-1; n += delta, i++) { | |||||
double n = offset; | |||||
for (int i = 0; i < (len-1)/delta-1; n += delta, i++) { | |||||
int k; | int k; | ||||
double alpha; | double alpha; | ||||
@@ -60,6 +62,5 @@ float rsfir(double *buff, const float *coeff, const int len, const double offset | |||||
alpha = n - k; | alpha = n - k; | ||||
out += buff[i] * (coeff[k] * (1.0 - alpha) + coeff[k + 1] * alpha); | out += buff[i] * (coeff[k] * (1.0 - alpha) + coeff[k + 1] * alpha); | ||||
} | } | ||||
return out; | |||||
return(out); | |||||
} | } | ||||
@@ -1,5 +1,5 @@ | |||||
/* | /* | ||||
* Aptec | |||||
* Aptdec | |||||
* Copyright (c) 2003 by Thierry Leconte (F4DWV) | * Copyright (c) 2003 by Thierry Leconte (F4DWV) | ||||
* | * | ||||
* $Id$ | * $Id$ | ||||
@@ -1,5 +1,5 @@ | |||||
/* | /* | ||||
* Aptec | |||||
* Aptdec | |||||
* Copyright (c) 2003 by Thierry Leconte (F4DWV) | * Copyright (c) 2003 by Thierry Leconte (F4DWV) | ||||
* | * | ||||
* $Id$ | * $Id$ | ||||
@@ -26,8 +26,9 @@ const float iqfilter[IQFilterLen] = { 0.0205361, 0.0219524, 0.0235785, 0.0254648 | |||||
-0.63662, -0.212207, -0.127324, -0.0909457, -0.0707355, -0.0578745, -0.0489708, -0.0424413, -0.0374482, | -0.63662, -0.212207, -0.127324, -0.0909457, -0.0707355, -0.0578745, -0.0489708, -0.0424413, -0.0374482, | ||||
-0.0335063, -0.0303152, -0.0276791, -0.0254648, -0.0235785, -0.0219524, -0.0205361 }; | -0.0335063, -0.0303152, -0.0276791, -0.0254648, -0.0235785, -0.0219524, -0.0205361 }; | ||||
// Length of Sync | |||||
#define SyncFilterLen 32 | #define SyncFilterLen 32 | ||||
// Pattern of a NOAA sync line | |||||
const float Sync[SyncFilterLen] = { -14, -14, -14, 18, 18, -14, -14, 18, 18, -14, -14, 18, 18, -14, -14, | const float Sync[SyncFilterLen] = { -14, -14, -14, 18, 18, -14, -14, 18, 18, -14, -14, 18, 18, -14, -14, | ||||
18, 18, -14, -14, 18, 18, -14, -14, 18, 18, -14, -14, 18, 18, -14, -14, -14 }; | 18, 18, -14, -14, 18, 18, -14, -14, 18, 18, -14, -14, 18, 18, -14, -14, -14 }; | ||||
@@ -95,6 +96,4 @@ const float rsfilter[RSFilterLen] = { -3.37279e-04, -8.80292e-06, -3.96418e-04, | |||||
-6.46202e-04, -8.02450e-04, -9.64235e-04, -1.08660e-03, -1.15302e-03, -1.23904e-03, -1.20955e-03, | -6.46202e-04, -8.02450e-04, -9.64235e-04, -1.08660e-03, -1.15302e-03, -1.23904e-03, -1.20955e-03, | ||||
-1.26937e-03, -1.15443e-03, -1.19836e-03, -1.01377e-03, -1.05669e-03, -8.15327e-04, -8.79730e-04, | -1.26937e-03, -1.15443e-03, -1.19836e-03, -1.01377e-03, -1.05669e-03, -8.15327e-04, -8.79730e-04, | ||||
-5.93148e-04, -6.95337e-04, -3.75376e-04, -5.27511e-04, -1.78544e-04, -3.96418e-04, -8.80292e-06, | -5.93148e-04, -6.95337e-04, -3.75376e-04, -5.27511e-04, -1.78544e-04, -3.96418e-04, -8.80292e-06, | ||||
-3.37279e-04 }; | |||||
-3.37279e-04 }; |
@@ -1,4 +1,4 @@ | |||||
unsigned char GviPalette[256*3] = { | |||||
unsigned char GviPalette[256*3] = { | |||||
"\230t\17\233x\22\236{\27\241\200\33\244\203\37\247\210#\252\214'\255\220" | "\230t\17\233x\22\236{\27\241\200\33\244\203\37\247\210#\252\214'\255\220" | ||||
",\260\2240\264\2305\267\2358\272\240=\274\245A\300\251E\303\255I\306\262" | ",\260\2240\264\2305\267\2358\272\240=\274\245A\300\251E\303\255I\306\262" | ||||
"M\311\266Q\314\272V\317\276Z\322\302^\325\306b\330\312g\334\317k\337\323" | "M\311\266Q\314\272V\317\276Z\322\302^\325\306b\330\312g\334\317k\337\323" | ||||
@@ -1,5 +1,5 @@ | |||||
/* | /* | ||||
* Aptec | |||||
* Aptdec | |||||
* Copyright (c) 2004 by Thierry Leconte (F4DWV) | * Copyright (c) 2004 by Thierry Leconte (F4DWV) | ||||
* | * | ||||
* $Id$ | * $Id$ | ||||
@@ -26,6 +26,7 @@ | |||||
#include <math.h> | #include <math.h> | ||||
#include "offsets.h" | #include "offsets.h" | ||||
#include "messages.h" | |||||
#define REGORDER 3 | #define REGORDER 3 | ||||
typedef struct { | typedef struct { | ||||
@@ -33,7 +34,7 @@ typedef struct { | |||||
} rgparam; | } rgparam; | ||||
static void rgcomp(double x[16], rgparam * rgpr) { | static void rgcomp(double x[16], rgparam * rgpr) { | ||||
/*{ 0.106,0.215,0.324,0.433,0.542,0.652,0.78,0.87 ,0.0 }; */ | |||||
//const double y[9] = { 0.106, 0.215, 0.324, 0.433, 0.542, 0.652, 0.78, 0.87, 0.0 }; | |||||
const double y[9] = { 31.07, 63.02, 94.96, 126.9, 158.86, 191.1, 228.62, 255.0, 0.0 }; | const double y[9] = { 31.07, 63.02, 94.96, 126.9, 158.86, 191.1, 228.62, 255.0, 0.0 }; | ||||
extern void polyreg(const int m, const int n, const double x[], const double y[], double c[]); | extern void polyreg(const int m, const int n, const double x[], const double y[], double c[]); | ||||
@@ -48,15 +49,53 @@ static double rgcal(float x, rgparam * rgpr) { | |||||
y += rgpr->cf[i] * p; | y += rgpr->cf[i] * p; | ||||
p = p * x; | p = p * x; | ||||
} | } | ||||
return (y); | |||||
return(y); | |||||
} | } | ||||
static double tele[16]; | static double tele[16]; | ||||
static double Cs; | static double Cs; | ||||
static int nbtele; | static int nbtele; | ||||
int Calibrate(float **prow, int nrow, int offset) { | |||||
// Contrast enchance | |||||
void equalise(float **prow, int nrow, int offset, int telestart, rgparam regr[30]){ | |||||
for (int n = 0; n < nrow; n++) { | |||||
float *pixelv; | |||||
int i; | |||||
pixelv = prow[n]; | |||||
for (i = 0; i < CH_WIDTH; i++) { | |||||
float pv; | |||||
int k, kof; | |||||
pv = pixelv[i + offset]; | |||||
k = (n - telestart) / 128; | |||||
if (k >= nbtele) | |||||
k = nbtele - 1; | |||||
kof = (n - telestart) % 128; | |||||
if (kof < 64) { | |||||
if (k < 1) { | |||||
pv = rgcal(pv, &(regr[k])); | |||||
} else { | |||||
pv = rgcal(pv, &(regr[k])) * (64 + kof) / 128.0 + rgcal(pv, &(regr[k - 1])) * (64 - kof) / 128.0; | |||||
} | |||||
} else { | |||||
if ((k + 1) >= nbtele) { | |||||
pv = rgcal(pv, &(regr[k])); | |||||
} else { | |||||
pv = rgcal(pv, &(regr[k])) * (192 - kof) / 128.0 + rgcal(pv, &(regr[k + 1])) * (kof - 64) / 128.0; | |||||
} | |||||
} | |||||
pv = CLIP(pv, 0, 255); | |||||
pixelv[i + offset] = pv; | |||||
} | |||||
} | |||||
} | |||||
// Get telemetry data for thermal calibration | |||||
int calibrate(float **prow, int nrow, int offset, int contrastBoost) { | |||||
double teleline[3000]; | double teleline[3000]; | ||||
double wedge[16]; | double wedge[16]; | ||||
rgparam regr[30]; | rgparam regr[30]; | ||||
@@ -66,58 +105,77 @@ int Calibrate(float **prow, int nrow, int offset) { | |||||
int channel = -1; | int channel = -1; | ||||
float max; | float max; | ||||
printf("Calibration... "); | |||||
fflush(stdout); | |||||
/* build telemetry values lines */ | |||||
// Extract telemetry data, for a single pixel row | |||||
for (n = 0; n < nrow; n++) { | for (n = 0; n < nrow; n++) { | ||||
int i; | int i; | ||||
teleline[n] = 0.0; | teleline[n] = 0.0; | ||||
// Average the 40 center pixels from the telemetry block | |||||
for (i = 3; i < 43; i++) { | for (i = 3; i < 43; i++) { | ||||
teleline[n] += prow[n][i + offset + CH_WIDTH]; | teleline[n] += prow[n][i + offset + CH_WIDTH]; | ||||
} | } | ||||
// Compute the average | |||||
teleline[n] /= 40.0; | teleline[n] /= 40.0; | ||||
} | } | ||||
// A good minimum amount of pixels to find the telemetry start | |||||
if (nrow < 192) { | if (nrow < 192) { | ||||
fprintf(stderr, " not possible, not enough rows!\n"); | |||||
return (0); | |||||
fprintf(stderr, ERR_TELE_ROW); | |||||
return(0); | |||||
} | } | ||||
/* find telemetry start in the 2nd third */ | |||||
// Find the biggest contrast in the telemetry | |||||
max = 0.0; | max = 0.0; | ||||
mtelestart = 0; | mtelestart = 0; | ||||
// Only check the center of the image, where the signal is most likely strongest | |||||
for (n = nrow / 3 - 64; n < 2 * nrow / 3 - 64; n++) { | for (n = nrow / 3 - 64; n < 2 * nrow / 3 - 64; n++) { | ||||
float df; | float df; | ||||
// Calculate the contrast, in other words | |||||
// (sum 4px below) / (sum 4px above) | |||||
df = (teleline[n - 4] + teleline[n - 3] + teleline[n - 2] + | df = (teleline[n - 4] + teleline[n - 3] + teleline[n - 2] + | ||||
teleline[n - 1]) / (teleline[n] + teleline[n + 1] + | teleline[n - 1]) / (teleline[n] + teleline[n + 1] + | ||||
teleline[n + 2] + teleline[n + 3]); | teleline[n + 2] + teleline[n + 3]); | ||||
// Find the biggest contrast | |||||
if (df > max) { | if (df > max) { | ||||
mtelestart = n; | mtelestart = n; | ||||
max = df; | max = df; | ||||
} | } | ||||
} | } | ||||
mtelestart -= 64; | |||||
telestart = mtelestart % 128; | |||||
// Calculate relative offset | |||||
telestart = (mtelestart - 64) % 128; | |||||
// If we cannot find the start of the telemetry or if there is not enough of it | |||||
if (mtelestart < 0 || nrow < telestart + 128) { | if (mtelestart < 0 || nrow < telestart + 128) { | ||||
fprintf(stderr, " impossible, not enough row\n"); | |||||
return (0); | |||||
fprintf(stderr, ERR_TELE_ROW); | |||||
return(0); | |||||
} | } | ||||
/* compute wedges and regression */ | |||||
/* Compute wedges and regression | |||||
* | |||||
* This loop loops every 128 pixels after the relative telemetry start. | |||||
* Gets the values of where the wedges should be and then feeds into a | |||||
* regression algorithm which calculates the amount of noise on the | |||||
* telemetry. | |||||
* | |||||
* It then finds the part of the telemetry data with the least noise and | |||||
* turns it into digital values. | |||||
*/ | |||||
for (n = telestart, k = 0; n < nrow - 128; n += 128, k++) { | for (n = telestart, k = 0; n < nrow - 128; n += 128, k++) { | ||||
int j; | int j; | ||||
// Loop through the 16 wedges | |||||
for (j = 0; j < 16; j++) { | for (j = 0; j < 16; j++) { | ||||
int i; | int i; | ||||
wedge[j] = 0.0; | wedge[j] = 0.0; | ||||
for (i = 1; i < 7; i++) | |||||
wedge[j] += teleline[n + j * 8 + i]; | |||||
// Center 6 pixels | |||||
for (i = 1; i < 7; i++){ | |||||
wedge[j] += teleline[(j * 8) + n + i]; | |||||
} | |||||
// Average | |||||
wedge[j] /= 6; | wedge[j] /= 6; | ||||
} | } | ||||
@@ -126,12 +184,12 @@ int Calibrate(float **prow, int nrow, int offset) { | |||||
if (k == nrow / 256) { | if (k == nrow / 256) { | ||||
int i, l; | int i, l; | ||||
/* telemetry calibration */ | |||||
// Telemetry calibration | |||||
for (j = 0; j < 16; j++) { | for (j = 0; j < 16; j++) { | ||||
tele[j] = rgcal(wedge[j], &(regr[k])); | tele[j] = rgcal(wedge[j], &(regr[k])); | ||||
} | } | ||||
/* channel ID */ | |||||
// Channel ID | |||||
for (j = 0, max = 10000.0, channel = -1; j < 6; j++) { | for (j = 0, max = 10000.0, channel = -1; j < 6; j++) { | ||||
float df; | float df; | ||||
@@ -162,49 +220,13 @@ int Calibrate(float **prow, int nrow, int offset) { | |||||
} | } | ||||
nbtele = k; | nbtele = k; | ||||
/* calibrate */ | |||||
for (n = 0; n < nrow; n++) { | |||||
float *pixelv; | |||||
int i; | |||||
pixelv = prow[n]; | |||||
for (i = 0; i < CH_WIDTH; i++) { | |||||
float pv; | |||||
int k, kof; | |||||
pv = pixelv[i + offset]; | |||||
k = (n - telestart) / 128; | |||||
if (k >= nbtele) | |||||
k = nbtele - 1; | |||||
kof = (n - telestart) % 128; | |||||
if (kof < 64) { | |||||
if (k < 1) { | |||||
pv = rgcal(pv, &(regr[k])); | |||||
} else { | |||||
pv = rgcal(pv, &(regr[k])) * (64 + kof) / 128.0 + rgcal(pv, &(regr[k - 1])) * (64 - kof) / 128.0; | |||||
} | |||||
} else { | |||||
if ((k + 1) >= nbtele) { | |||||
pv = rgcal(pv, &(regr[k])); | |||||
} else { | |||||
pv = rgcal(pv, &(regr[k])) * (192 - kof) / 128.0 + rgcal(pv, &(regr[k + 1])) * (kof - 64) / 128.0; | |||||
} | |||||
} | |||||
// Image contrast | |||||
if(contrastBoost) equalise(prow, nrow, offset, telestart, regr); | |||||
if (pv > 255.0) | |||||
pv = 255.0; | |||||
if (pv < 0.0) | |||||
pv = 0.0; | |||||
pixelv[i + offset] = pv; | |||||
} | |||||
} | |||||
printf("Done\n"); | |||||
return (channel + 1); | |||||
return(channel + 1); | |||||
} | } | ||||
/* ------------------------------temperature calibration -----------------------*/ | |||||
// --- Temperature Calibration --- // | |||||
extern int satnum; | extern int satnum; | ||||
#include "satcal.h" | #include "satcal.h" | ||||
@@ -241,7 +263,7 @@ static void tempcomp(double t[16], int ch, tempparam * tpr) { | |||||
/* compute radiance Black body */ | /* compute radiance Black body */ | ||||
C = satcal[satnum].rad[tpr->ch].vc; | C = satcal[satnum].rad[tpr->ch].vc; | ||||
tpr->Nbb = c1 * C * C * C / (exp(c2 * C / Tbb) - 1.0); | |||||
tpr->Nbb = c1 * C * C * C / (expm1(c2 * C / Tbb)); | |||||
/* store Count Blackbody and space */ | /* store Count Blackbody and space */ | ||||
tpr->Cs = Cs * 4.0; | tpr->Cs = Cs * 4.0; | ||||
@@ -261,13 +283,13 @@ static double tempcal(float Ce, tempparam * rgpr) { | |||||
Ne = Nl + Nc; | Ne = Nl + Nc; | ||||
vc = satcal[satnum].rad[rgpr->ch].vc; | vc = satcal[satnum].rad[rgpr->ch].vc; | ||||
T = c2 * vc / log(c1 * vc * vc * vc / Ne + 1.0); | |||||
T = c2 * vc / log1p(c1 * vc * vc * vc / Ne); | |||||
T = (T - satcal[satnum].rad[rgpr->ch].A) / satcal[satnum].rad[rgpr->ch].B; | T = (T - satcal[satnum].rad[rgpr->ch].A) / satcal[satnum].rad[rgpr->ch].B; | ||||
/* rescale to range 0-255 for -60 +40 'C */ | /* rescale to range 0-255 for -60 +40 'C */ | ||||
T = (T - 273.15 + 60.0) / 100.0 * 256.0; | T = (T - 273.15 + 60.0) / 100.0 * 256.0; | ||||
return (T); | |||||
return(T); | |||||
} | } | ||||
void Temperature(float **prow, int nrow, int channel, int offset) { | void Temperature(float **prow, int nrow, int channel, int offset) { | ||||
@@ -289,10 +311,7 @@ void Temperature(float **prow, int nrow, int channel, int offset) { | |||||
pv = tempcal(pixelv[i + offset], &temp); | pv = tempcal(pixelv[i + offset], &temp); | ||||
if (pv > 255.0) | |||||
pv = 255.0; | |||||
if (pv < 0.0) | |||||
pv = 0.0; | |||||
pv = CLIP(pv, 0, 255); | |||||
pixelv[i + offset] = pv; | pixelv[i + offset] = pv; | ||||
} | } | ||||
} | } | ||||
@@ -25,16 +25,12 @@ | |||||
#include <string.h> | #include <string.h> | ||||
#include <getopt.h> | #include <getopt.h> | ||||
#ifdef WIN32 | |||||
#include "w32util.h" | |||||
#else | |||||
#include <libgen.h> | #include <libgen.h> | ||||
#endif | |||||
#include <sndfile.h> | #include <sndfile.h> | ||||
#include <png.h> | #include <png.h> | ||||
#include "version.h" | |||||
#include "messages.h" | |||||
#include "offsets.h" | #include "offsets.h" | ||||
#include "temppalette.h" | #include "temppalette.h" | ||||
@@ -49,70 +45,74 @@ static int initsnd(char *filename) { | |||||
SF_INFO infwav; | SF_INFO infwav; | ||||
int res; | int res; | ||||
/* open wav input file */ | |||||
// Open audio file | |||||
infwav.format = 0; | infwav.format = 0; | ||||
inwav = sf_open(filename, SFM_READ, &infwav); | inwav = sf_open(filename, SFM_READ, &infwav); | ||||
if (inwav == NULL) { | if (inwav == NULL) { | ||||
fprintf(stderr, "Could not open %s for reading\n", filename); | |||||
return (1); | |||||
fprintf(stderr, ERR_FILE_READ, filename); | |||||
return(0); | |||||
} | } | ||||
res = init_dsp(infwav.samplerate); | res = init_dsp(infwav.samplerate); | ||||
printf("Input file: %s\n", filename); | |||||
if(res < 0) { | if(res < 0) { | ||||
fprintf(stderr, "Sample rate too low: %d\n", infwav.samplerate); | |||||
return (1); | |||||
fprintf(stderr, "Input sample rate too low: %d\n", infwav.samplerate); | |||||
return(0); | |||||
}else if(res > 0) { | |||||
fprintf(stderr, "Input sample rate too high: %d\n", infwav.samplerate); | |||||
return(0); | |||||
} | } | ||||
if(res > 0) { | |||||
fprintf(stderr, "Sample rate too hight: %d\n", infwav.samplerate); | |||||
return (1); | |||||
} | |||||
fprintf(stderr, "Sample rate: %d\n", infwav.samplerate); | |||||
printf("Input sample rate: %d\n", infwav.samplerate); | |||||
if (infwav.channels != 1) { | if (infwav.channels != 1) { | ||||
fprintf(stderr, "Too many channels in input file: %d\n", infwav.channels); | fprintf(stderr, "Too many channels in input file: %d\n", infwav.channels); | ||||
return (1); | |||||
return(0); | |||||
} | } | ||||
return (0); | |||||
return(1); | |||||
} | } | ||||
// Get a sample from the wave file | |||||
int getsample(float *sample, int nb) { | int getsample(float *sample, int nb) { | ||||
return (sf_read_float(inwav, sample, nb)); | |||||
return(sf_read_float(inwav, sample, nb)); | |||||
} | } | ||||
static png_text text_ptr[] = { | static png_text text_ptr[] = { | ||||
{PNG_TEXT_COMPRESSION_NONE, "Software", version, sizeof(version)}, | |||||
{PNG_TEXT_COMPRESSION_NONE, "Software", VERSION}, | |||||
{PNG_TEXT_COMPRESSION_NONE, "Channel", NULL, 0}, | {PNG_TEXT_COMPRESSION_NONE, "Channel", NULL, 0}, | ||||
{PNG_TEXT_COMPRESSION_NONE, "Description", "NOAA POES satellite image", 25} | |||||
{PNG_TEXT_COMPRESSION_NONE, "Description", "NOAA satellite image", 20} | |||||
}; | }; | ||||
static int ImageOut(char *filename, char *chid, float **prow, int nrow, int width, int offset, png_color *palette) { | |||||
static int ImageOut(char *filename, char *chid, float **prow, int nrow, int width, int offset, png_color *palette, int croptele, int layered) { | |||||
FILE *pngfile; | FILE *pngfile; | ||||
png_infop info_ptr; | png_infop info_ptr; | ||||
png_structp png_ptr; | png_structp png_ptr; | ||||
int n; | |||||
/* init png lib */ | |||||
// Reduce the width of the image to componsate for the missing telemetry | |||||
if(croptele) width -= TOTAL_TELE; | |||||
// Initalise the PNG writer | |||||
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | ||||
if (!png_ptr) { | if (!png_ptr) { | ||||
fprintf(stderr, "Could not create a PNG write struct\n"); | |||||
return (1); | |||||
fprintf(stderr, ERR_PNG_WRITE); | |||||
return(1); | |||||
} | } | ||||
// Metadata | |||||
info_ptr = png_create_info_struct(png_ptr); | info_ptr = png_create_info_struct(png_ptr); | ||||
if (!info_ptr) { | if (!info_ptr) { | ||||
png_destroy_write_struct(&png_ptr, (png_infopp) NULL); | png_destroy_write_struct(&png_ptr, (png_infopp) NULL); | ||||
fprintf(stderr, "Could not create a PNG info struct\n"); | |||||
return (1); | |||||
fprintf(stderr, ERR_PNG_INFO); | |||||
return(1); | |||||
} | } | ||||
if(palette == NULL) { | if(palette == NULL) { | ||||
/* greyscale */ | |||||
// Greyscale image | |||||
png_set_IHDR(png_ptr, info_ptr, width, nrow, | png_set_IHDR(png_ptr, info_ptr, width, nrow, | ||||
8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, | 8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, | ||||
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); | PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); | ||||
} else { | } else { | ||||
/* palette color mage */ | |||||
// Palleted color image | |||||
png_set_IHDR(png_ptr, info_ptr, width, nrow, | png_set_IHDR(png_ptr, info_ptr, width, nrow, | ||||
8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, | 8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, | ||||
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); | PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); | ||||
@@ -124,57 +124,77 @@ static int ImageOut(char *filename, char *chid, float **prow, int nrow, int widt | |||||
png_set_text(png_ptr, info_ptr, text_ptr, 3); | png_set_text(png_ptr, info_ptr, text_ptr, 3); | ||||
png_set_pHYs(png_ptr, info_ptr, 4000, 4000, PNG_RESOLUTION_METER); | png_set_pHYs(png_ptr, info_ptr, 4000, 4000, PNG_RESOLUTION_METER); | ||||
printf("Writing %s... ", filename); | |||||
printf("Writing %s ", filename); | |||||
fflush(stdout); | fflush(stdout); | ||||
pngfile = fopen(filename, "wb"); | pngfile = fopen(filename, "wb"); | ||||
if (pngfile == NULL) { | if (pngfile == NULL) { | ||||
fprintf(stderr, "Could not open %s for writing\n", filename); | |||||
return (1); | |||||
fprintf(stderr, ERR_FILE_WRITE, filename); | |||||
return(1); | |||||
} | } | ||||
png_init_io(png_ptr, pngfile); | png_init_io(png_ptr, pngfile); | ||||
png_write_info(png_ptr, info_ptr); | png_write_info(png_ptr, info_ptr); | ||||
for (n = 0; n < nrow; n++) { | |||||
for (int n = 0; n < nrow; n++) { | |||||
float *pixelv; | float *pixelv; | ||||
png_byte pixel[2*IMG_WIDTH]; | png_byte pixel[2*IMG_WIDTH]; | ||||
int i; | |||||
pixelv = prow[n]; | pixelv = prow[n]; | ||||
for (i = 0; i < width; i++) { | |||||
pixel[i] = pixelv[i + offset]; | |||||
int f = 0; | |||||
for (int i = 0; i < width; i++) { | |||||
// Skip parts of the image that are telemtry | |||||
if(croptele){ | |||||
switch (i) { | |||||
case 0: | |||||
f += SYNC_WIDTH + SPC_WIDTH; | |||||
break; | |||||
case CH_WIDTH: | |||||
f += TELE_WIDTH + SYNC_WIDTH + SPC_WIDTH; | |||||
break; | |||||
case CH_WIDTH*2: | |||||
f += TELE_WIDTH; | |||||
} | |||||
} | |||||
if(layered){ | |||||
// Layered image, basically overlay highlights in channel B over channel A | |||||
float cloud = CLIP(pixelv[i+CHB_OFFSET]-141, 0, 255)/114*255; | |||||
pixel[i] = CLIP(pixelv[i+CHA_OFFSET] + cloud, 0, 255); | |||||
}else{ | |||||
pixel[i] = pixelv[i + offset + f]; | |||||
} | |||||
} | } | ||||
png_write_row(png_ptr, pixel); | png_write_row(png_ptr, pixel); | ||||
} | } | ||||
png_write_end(png_ptr, info_ptr); | png_write_end(png_ptr, info_ptr); | ||||
fclose(pngfile); | fclose(pngfile); | ||||
printf("Done\n"); | |||||
printf("\nDone\n"); | |||||
png_destroy_write_struct(&png_ptr, &info_ptr); | png_destroy_write_struct(&png_ptr, &info_ptr); | ||||
return (0); | |||||
return(0); | |||||
} | } | ||||
static int ImageRGBOut(char *filename, float **prow, int nrow) { | static int ImageRGBOut(char *filename, float **prow, int nrow) { | ||||
FILE *pngfile; | FILE *pngfile; | ||||
png_infop info_ptr; | png_infop info_ptr; | ||||
png_structp png_ptr; | png_structp png_ptr; | ||||
int n; | |||||
extern void falsecolor(double v, double t, float *r, float *g, float *b); | extern void falsecolor(double v, double t, float *r, float *g, float *b); | ||||
/* init png lib */ | |||||
// Initalise the PNG writer | |||||
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | ||||
if (!png_ptr) { | if (!png_ptr) { | ||||
fprintf(stderr, "Could not create a PNG write struct\n"); | |||||
return (1); | |||||
fprintf(stderr, ERR_PNG_WRITE); | |||||
return(1); | |||||
} | } | ||||
info_ptr = png_create_info_struct(png_ptr); | info_ptr = png_create_info_struct(png_ptr); | ||||
if (!info_ptr) { | if (!info_ptr) { | ||||
png_destroy_write_struct(&png_ptr, (png_infopp) NULL); | png_destroy_write_struct(&png_ptr, (png_infopp) NULL); | ||||
fprintf(stderr, "Could not create a PNG info struct\n"); | |||||
return (1); | |||||
fprintf(stderr, ERR_PNG_WRITE); | |||||
return(1); | |||||
} | } | ||||
png_set_IHDR(png_ptr, info_ptr, CH_WIDTH , nrow , | |||||
png_set_IHDR(png_ptr, info_ptr, CH_WIDTH, nrow, | |||||
8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, | 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, | ||||
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); | PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); | ||||
@@ -188,20 +208,19 @@ static int ImageRGBOut(char *filename, float **prow, int nrow) { | |||||
fflush(stdout); | fflush(stdout); | ||||
pngfile = fopen(filename, "wb"); | pngfile = fopen(filename, "wb"); | ||||
if (pngfile == NULL) { | if (pngfile == NULL) { | ||||
fprintf(stderr, "Could not open %s for writing\n", filename); | |||||
return (1); | |||||
fprintf(stderr, ERR_FILE_WRITE, filename); | |||||
return(1); | |||||
} | } | ||||
png_init_io(png_ptr, pngfile); | png_init_io(png_ptr, pngfile); | ||||
png_write_info(png_ptr, info_ptr); | png_write_info(png_ptr, info_ptr); | ||||
for (n = 0; n < nrow ; n++) { | |||||
for (int n = 0; n < nrow ; n++) { | |||||
png_color pix[CH_WIDTH]; | png_color pix[CH_WIDTH]; | ||||
float *pixelc; | |||||
int i; | |||||
float *pixelc; | |||||
pixelc = prow[n]; | pixelc = prow[n]; | ||||
for (i = 0; i < CH_WIDTH - 1; i++) { | |||||
for (int i = 0; i < CH_WIDTH - 1; i++) { | |||||
float v, t; | float v, t; | ||||
float r, g, b; | float r, g, b; | ||||
@@ -220,11 +239,11 @@ static int ImageRGBOut(char *filename, float **prow, int nrow) { | |||||
fclose(pngfile); | fclose(pngfile); | ||||
printf("Done\n"); | printf("Done\n"); | ||||
png_destroy_write_struct(&png_ptr, &info_ptr); | png_destroy_write_struct(&png_ptr, &info_ptr); | ||||
return (0); | |||||
return(0); | |||||
} | } | ||||
static void Distrib(char *filename,float **prow,int nrow) { | |||||
// Distribution of values between Channel A and Channel B | |||||
static void Distrib(char *filename, float **prow, int nrow) { | |||||
unsigned int distrib[256][256]; | unsigned int distrib[256][256]; | ||||
int n; | int n; | ||||
int x, y; | int x, y; | ||||
@@ -253,152 +272,229 @@ static void Distrib(char *filename,float **prow,int nrow) { | |||||
fprintf(df,"P2\n#max %d\n",max); | fprintf(df,"P2\n#max %d\n",max); | ||||
fprintf(df,"256 256\n255\n"); | fprintf(df,"256 256\n255\n"); | ||||
for(y=0;y<256;y++) | |||||
for(x=0;x<256;x++) | |||||
for(y = 0; y < 256; y++) | |||||
for(x = 0; x < 256; x++) | |||||
fprintf(df, "%d\n", (int)((255.0 * (double)(distrib[y][x])) / (double)max)); | fprintf(df, "%d\n", (int)((255.0 * (double)(distrib[y][x])) / (double)max)); | ||||
fclose(df); | fclose(df); | ||||
} | } | ||||
extern int Calibrate(float **prow, int nrow, int offset); | |||||
extern int calibrate(float **prow, int nrow, int offset, int contrastBoost); | |||||
extern void Temperature(float **prow, int nrow, int ch, int offset); | extern void Temperature(float **prow, int nrow, int ch, int offset); | ||||
extern int Ngvi(float **prow, int nrow); | extern int Ngvi(float **prow, int nrow); | ||||
extern void readfconf(char *file); | extern void readfconf(char *file); | ||||
extern int optind, opterr; | |||||
extern int optind; | |||||
extern char *optarg; | extern char *optarg; | ||||
// Default to NOAA 19 | |||||
int satnum = 4; | int satnum = 4; | ||||
static void usage(void) { | static void usage(void) { | ||||
fprintf(stderr, "Aptdec [options] recordings ...\n"); | |||||
fprintf(stderr, "Options:\n -i [r|a|b|c|t] Output image type\n r: Raw\n a: A channel\n b: B channel\n c: False color\n t: Temperature\n -d <dir> Image destination directory.\n -s [15|16|17|18|19] Satellite number\n -c <file> False color config file\n"); | |||||
printf("Aptdec [options] audio files ...\n" | |||||
"Options:\n" | |||||
" -e [c|t] Enhancements\n" | |||||
" c: Contrast enhance\n" | |||||
" t: Crop telemetry\n" | |||||
" -i [r|a|b|c|t] Output image type\n" | |||||
" r: Raw\n" | |||||
" a: A channel\n" | |||||
" b: B channel\n" | |||||
" c: False color\n" | |||||
" t: Temperature\n" | |||||
" l: Layered\n" | |||||
" -d <dir> Image destination directory.\n" | |||||
" -s [15-19] Satellite number\n" | |||||
" -c <file> False color config file\n"); | |||||
exit(1); | exit(1); | ||||
} | } | ||||
int main(int argc, char **argv) { | int main(int argc, char **argv) { | ||||
char pngfilename[1024]; | char pngfilename[1024]; | ||||
char name[1024]; | |||||
char pngdirname[1024] = ""; | |||||
char imgopt[20] = "ac"; | |||||
char name[500]; | |||||
char pngdirname[500] = ""; | |||||
// Images to create, default to a channel A and channel B image with contrast enhancement and cropped telemetry | |||||
char imgopt[20] = "ab"; | |||||
char enchancements[20] = "ct"; | |||||
// Image buffer | |||||
float *prow[3000]; | float *prow[3000]; | ||||
char *chid[] = { "?", "1", "2", "3A", "4", "5", "3B" }; | |||||
int nrow; | |||||
int nrow; | |||||
// Mapping between wedge value and channel ID | |||||
char *chid[] = { "?", "1", "2", "3A", "4", "5", "3B" }; | |||||
char *chname[] = { "unknown", "visble", "near-infrared", "near-infrared", "thermal-infrared", "thermal-infrared", "thermal-infrared" }; | |||||
// The active sensor in each channel | |||||
int chA, chB; | int chA, chB; | ||||
int c; | |||||
printf("%s\n", version); | |||||
// Print version | |||||
printf("%s\n", VERSION); | |||||
opterr = 0; | |||||
if(argc == 1){ | |||||
// Print usage if there are no arguments | |||||
if(argc == 1) | |||||
usage(); | usage(); | ||||
} | |||||
while ((c = getopt(argc, argv, "c:d:i:s:")) != EOF) { | |||||
int c; | |||||
while ((c = getopt(argc, argv, "c:d:i:s:e:")) != EOF) { | |||||
switch (c) { | switch (c) { | ||||
// Output directory name | |||||
case 'd': | case 'd': | ||||
strcpy(pngdirname, optarg); | strcpy(pngdirname, optarg); | ||||
break; | break; | ||||
// False color config file | |||||
case 'c': | case 'c': | ||||
readfconf(optarg); | readfconf(optarg); | ||||
break; | break; | ||||
// Output image type | |||||
case 'i': | case 'i': | ||||
strcpy(imgopt, optarg); | strcpy(imgopt, optarg); | ||||
break; | break; | ||||
// Satellite number (for calibration) | |||||
case 's': | case 's': | ||||
satnum = atoi(optarg)-15; | satnum = atoi(optarg)-15; | ||||
// Check if it's within the valid range | |||||
if (satnum < 0 || satnum > 4) { | if (satnum < 0 || satnum > 4) { | ||||
fprintf(stderr, "Invalid satellite number, must be in the range [15-19]\n"); | |||||
fprintf(stderr, "Invalid satellite number, it must be the range [15-19]\n"); | |||||
exit(1); | exit(1); | ||||
} | } | ||||
break; | break; | ||||
// Enchancements | |||||
case 'e': | |||||
strcpy(enchancements, optarg); | |||||
break; | |||||
default: | default: | ||||
usage(); | usage(); | ||||
} | } | ||||
} | } | ||||
for (nrow = 0; nrow < 3000; nrow++) | |||||
prow[nrow] = NULL; | |||||
if(optind == argc){ | |||||
printf("No audio files provided.\n"); | |||||
usage(); | |||||
} | |||||
// Pull this in from filtercoeff.h | |||||
extern float Sync[32]; | |||||
// Loop through the provided files | |||||
for (; optind < argc; optind++) { | for (; optind < argc; optind++) { | ||||
chA = chB = 0; | chA = chB = 0; | ||||
// Generate output name | |||||
strcpy(pngfilename, argv[optind]); | strcpy(pngfilename, argv[optind]); | ||||
strcpy(name, basename(pngfilename)); | strcpy(name, basename(pngfilename)); | ||||
strtok(name, "."); | strtok(name, "."); | ||||
if (pngdirname[0] == '\0') { | |||||
strcpy(pngfilename, argv[optind]); | |||||
if (pngdirname[0] == '\0') | |||||
strcpy(pngdirname, dirname(pngfilename)); | strcpy(pngdirname, dirname(pngfilename)); | ||||
} | |||||
/* open snd input */ | |||||
if (initsnd(argv[optind])) | |||||
// Open sound file, exit if that fails | |||||
if (initsnd(argv[optind]) == 0) | |||||
exit(1); | exit(1); | ||||
/* main loop */ | |||||
printf("Decoding: %s \n", argv[optind]); | |||||
// Main image building loop | |||||
for (nrow = 0; nrow < 3000; nrow++) { | for (nrow = 0; nrow < 3000; nrow++) { | ||||
if (prow[nrow] == NULL) | |||||
// Allocate 2150 floats worth of memory for every line of the image | |||||
prow[nrow] = (float *) malloc(sizeof(float) * 2150); | prow[nrow] = (float *) malloc(sizeof(float) * 2150); | ||||
// Read into prow and break the loop once we reach the end of the image | |||||
if (getpixelrow(prow[nrow]) == 0) | if (getpixelrow(prow[nrow]) == 0) | ||||
break; | break; | ||||
// Signal level | |||||
double signal = 0; | |||||
for (int i = 0; i < 32; i++) signal += prow[nrow][i] - Sync[i]; | |||||
printf("%d\r", nrow); | |||||
// Signal level bar | |||||
char bar[30]; | |||||
for (int i = 0; i < 30; i++) { | |||||
bar[i] = ' '; | |||||
if(i < signal/2000*30) bar[i] = '#'; | |||||
} | |||||
printf("Row: %d, Signal Strength: %s\r", nrow, bar); | |||||
fflush(stdout); | fflush(stdout); | ||||
} | } | ||||
printf("\nDone\n"); | |||||
printf("\nTotal rows: %d\n", nrow); | |||||
sf_close(inwav); | sf_close(inwav); | ||||
/* raw image */ | |||||
if (strchr(imgopt, (int) 'r') != NULL) { | |||||
// Layered images require contrast enhancements | |||||
int contrastBoost = CONTAINS(enchancements, 'c') || CONTAINS(imgopt, 'l'); | |||||
chA = calibrate(prow, nrow, CHA_OFFSET, 0); | |||||
chB = calibrate(prow, nrow, CHB_OFFSET, 0); | |||||
printf("Channel A: %s (%s)\n", chid[chA], chname[chA]); | |||||
printf("Channel B: %s (%s)\n", chid[chB], chname[chB]); | |||||
// Temperature | |||||
if (CONTAINS(imgopt, 't') && chB >= 4) { | |||||
Temperature(prow, nrow, chB, CHB_OFFSET); | |||||
sprintf(pngfilename, "%s/%s-t.png", pngdirname, name); | |||||
ImageOut(pngfilename, "Temperature", prow, nrow, CH_WIDTH, CHB_OFFSET, (png_color*)TempPalette, 0, 0); | |||||
} | |||||
// We have to run the contrast enhance here because the temperature function requires real data | |||||
// Yes, this is bodgy, yes I should replace this with something more elegant | |||||
chA = calibrate(prow, nrow, CHA_OFFSET, contrastBoost); | |||||
chB = calibrate(prow, nrow, CHB_OFFSET, contrastBoost); | |||||
// Layered | |||||
if (CONTAINS(imgopt, 'l')){ | |||||
sprintf(pngfilename, "%s/%s-l.png", pngdirname, name); | |||||
ImageOut(pngfilename, "Layered", prow, nrow, CH_WIDTH, 0, NULL, 0, 1); | |||||
} | |||||
// Raw image | |||||
if (CONTAINS(imgopt, 'r')) { | |||||
int croptele = CONTAINS(enchancements, 't'); | |||||
char channelstr[45]; | |||||
sprintf(channelstr, "%s (%s) & %s (%s)", chid[chA], chname[chA], chid[chB], chname[chB]); | |||||
sprintf(pngfilename, "%s/%s-r.png", pngdirname, name); | sprintf(pngfilename, "%s/%s-r.png", pngdirname, name); | ||||
ImageOut(pngfilename, "raw", prow, nrow, IMG_WIDTH, 0, NULL); | |||||
ImageOut(pngfilename, channelstr, prow, nrow, IMG_WIDTH, 0, NULL, croptele, 0); | |||||
} | } | ||||
/* Channel A */ | |||||
if (((strchr(imgopt, (int) 'a') != NULL) | |||||
|| (strchr(imgopt, (int) 'c') != NULL) | |||||
|| (strchr(imgopt, (int) 'd') != NULL))) { | |||||
chA = Calibrate(prow, nrow, CHA_OFFSET); | |||||
if (chA >= 0 && strchr(imgopt, (int) 'a') != NULL) { | |||||
sprintf(pngfilename, "%s/%s-%s.png", pngdirname, name, chid[chA]); | |||||
ImageOut(pngfilename, chid[chA], prow, nrow, CH_WIDTH , CHA_OFFSET, NULL); | |||||
} | |||||
// Channel A | |||||
if (CONTAINS(imgopt, 'a')) { | |||||
char channelstr[21]; | |||||
sprintf(channelstr, "%s (%s)", chid[chA], chname[chA]); | |||||
sprintf(pngfilename, "%s/%s-%s.png", pngdirname, name, chid[chA]); | |||||
ImageOut(pngfilename, channelstr, prow, nrow, CH_WIDTH, CHA_OFFSET, NULL, 0, 0); | |||||
} | } | ||||
/* Channel B */ | |||||
if ((strchr(imgopt, (int) 'b') != NULL) | |||||
|| (strchr(imgopt, (int) 'c') != NULL) | |||||
|| (strchr(imgopt, (int) 't') != NULL) | |||||
|| (strchr(imgopt, (int) 'd') != NULL)) { | |||||
chB = Calibrate(prow, nrow, CHB_OFFSET); | |||||
if (chB >= 0 && strchr(imgopt, (int) 'b') != NULL) { | |||||
sprintf(pngfilename, "%s/%s-%s.png", pngdirname, name, chid[chB]); | |||||
ImageOut(pngfilename, chid[chB], prow, nrow, CH_WIDTH , CHB_OFFSET ,NULL); | |||||
} | |||||
if (chB > 3) { | |||||
Temperature(prow, nrow, chB, CHB_OFFSET); | |||||
if (strchr(imgopt, (int) 't') != NULL) { | |||||
sprintf(pngfilename, "%s/%s-t.png", pngdirname, name); | |||||
ImageOut(pngfilename, "Temperature", prow, nrow, CH_WIDTH, CHB_OFFSET, (png_color*)TempPalette); | |||||
} | |||||
} | |||||
// Channel B | |||||
if (CONTAINS(imgopt, 'b')) { | |||||
char channelstr[21]; | |||||
sprintf(channelstr, "%s (%s)", chid[chB], chname[chB]); | |||||
sprintf(pngfilename, "%s/%s-%s.png", pngdirname, name, chid[chB]); | |||||
ImageOut(pngfilename, channelstr, prow, nrow, CH_WIDTH , CHB_OFFSET, NULL, 0, 0); | |||||
} | } | ||||
/* distribution */ | |||||
if (chA && chB && strchr(imgopt, (int) 'd') != NULL) { | |||||
// Distribution map | |||||
if (CONTAINS(imgopt, 'd')) { | |||||
sprintf(pngfilename, "%s/%s-d.pnm", pngdirname, name); | sprintf(pngfilename, "%s/%s-d.pnm", pngdirname, name); | ||||
Distrib(pngfilename, prow, nrow); | Distrib(pngfilename, prow, nrow); | ||||
} | } | ||||
/* color image */ | |||||
if (chA == 2 && chB == 4 && strchr(imgopt, (int) 'c') != NULL) { | |||||
sprintf(pngfilename, "%s/%s-c.png", pngdirname, name); | |||||
ImageRGBOut(pngfilename, prow, nrow); | |||||
// False color image | |||||
if(CONTAINS(imgopt, 'c')){ | |||||
if (chA == 2 && chB == 4) { // Normal false color | |||||
sprintf(pngfilename, "%s/%s-c.png", pngdirname, name); | |||||
ImageRGBOut(pngfilename, prow, nrow); | |||||
} else if (chA == 1 && chB == 2) { // GVI false color | |||||
Ngvi(prow, nrow); | |||||
sprintf(pngfilename, "%s/%s-c.png", pngdirname, name); | |||||
ImageOut(pngfilename, "GVI False Color", prow, nrow, CH_WIDTH, CHB_OFFSET, (png_color*)GviPalette, 0, 0); | |||||
} else { | |||||
fprintf(stderr, "Lacking channels required for false color computation.\n"); | |||||
} | |||||
} | } | ||||
/* GVI image */ | |||||
if (chA == 1 && chB == 2 && strchr(imgopt, (int) 'c') != NULL) { | |||||
Ngvi(prow, nrow); | |||||
sprintf(pngfilename, "%s/%s-c.png", pngdirname, name); | |||||
ImageOut(pngfilename, "GVI", prow, nrow, CH_WIDTH, CHB_OFFSET, (png_color*)GviPalette); | |||||
} | |||||
} | |||||
// Free the allocated memory | |||||
for(int i = 0; i < 3000; i++) free(prow[i]); | |||||
} | |||||
exit(0); | exit(0); | ||||
} | } |
@@ -0,0 +1,6 @@ | |||||
#define ERR_FILE_WRITE "Could not open %s for writing\n" | |||||
#define ERR_FILE_READ "Could not open %s for reading\n" | |||||
#define ERR_PNG_WRITE "Could not create a PNG write struct\n" | |||||
#define ERR_PNG_INFO "Could not create a PNG info struct\n" | |||||
#define ERR_TELE_ROW "Telemetry decoding error, not enough rows.\n" | |||||
#define VERSION "Aptdec CVS version (c) 2004-2005 Thierry Leconte F4DWV" |
@@ -6,3 +6,7 @@ | |||||
#define IMG_WIDTH 2080 | #define IMG_WIDTH 2080 | ||||
#define CHA_OFFSET (SYNC_WIDTH+SPC_WIDTH) | #define CHA_OFFSET (SYNC_WIDTH+SPC_WIDTH) | ||||
#define CHB_OFFSET (SYNC_WIDTH+SPC_WIDTH+CH_WIDTH+TELE_WIDTH+SYNC_WIDTH+SPC_WIDTH) | #define CHB_OFFSET (SYNC_WIDTH+SPC_WIDTH+CH_WIDTH+TELE_WIDTH+SYNC_WIDTH+SPC_WIDTH) | ||||
#define TOTAL_TELE (SYNC_WIDTH+SPC_WIDTH+TELE_WIDTH+SYNC_WIDTH+SPC_WIDTH+TELE_WIDTH) | |||||
#define CLIP(val, bottom, top) (val > top ? top : (val > bottom ? val : bottom)) | |||||
#define CONTAINS(str, char) (strchr(str, (int) char) != NULL) |
@@ -12,10 +12,11 @@ | |||||
#include <math.h> | #include <math.h> | ||||
#define DMAX 5 /* Maximum degree of polynomial */ | |||||
#define NMAX 10 /* Maximum number of points */ | |||||
#define DMAX 5 /* Maximum degree of polynomial */ | |||||
#define NMAX 10 /* Maximum number of points */ | |||||
static void FactPiv(int N, double A[DMAX][DMAX], double B[], double Cf[]); | static void FactPiv(int N, double A[DMAX][DMAX], double B[], double Cf[]); | ||||
void polyreg(const int M, const int N, const double X[], const double Y[], double C[]) { | void polyreg(const int M, const int N, const double X[], const double Y[], double C[]) { | ||||
int R, K, J; /* Loop counters */ | int R, K, J; /* Loop counters */ | ||||
double A[DMAX][DMAX]; /* A */ | double A[DMAX][DMAX]; /* A */ | ||||
@@ -41,7 +42,8 @@ void polyreg(const int M, const int N, const double X[], const double Y[], doubl | |||||
/* Zero the array */ | /* Zero the array */ | ||||
for (J = 1; J <= 2 * M; J++) | for (J = 1; J <= 2 * M; J++) | ||||
P[J] = 0; | |||||
P[J] = 0; | |||||
P[0] = N; | P[0] = N; | ||||
/* Compute the sum of powers of x_(K-1) */ | /* Compute the sum of powers of x_(K-1) */ | ||||
@@ -74,9 +76,8 @@ static void FactPiv(int N, double A[DMAX][DMAX], double B[], double Cf[]) { | |||||
double SUM, DET = 1.0; | double SUM, DET = 1.0; | ||||
int T; | int T; | ||||
/* Initialize the pointer vector */ | /* Initialize the pointer vector */ | ||||
for (J = 0; J < N; J++) | |||||
for (J = 0; J < NMAX; J++) | |||||
Row[J] = J; | Row[J] = J; | ||||
/* Start LU factorization */ | /* Start LU factorization */ | ||||
@@ -108,13 +109,15 @@ static void FactPiv(int N, double A[DMAX][DMAX], double B[], double Cf[]) { | |||||
A[Row[K]][C] -= A[Row[K]][P] * A[Row[P]][C]; | A[Row[K]][C] -= A[Row[K]][P] * A[Row[P]][C]; | ||||
} | } | ||||
} | } | ||||
} /* End of L*U factorization routine */ | |||||
} | |||||
/* End of L*U factorization routine */ | |||||
DET = DET * A[Row[N - 1]][N - 1]; | DET = DET * A[Row[N - 1]][N - 1]; | ||||
/* Start the forward substitution */ | /* Start the forward substitution */ | ||||
for (K = 0; K < N; K++) | for (K = 0; K < N; K++) | ||||
Y[K] = B[K]; | Y[K] = B[K]; | ||||
Y[0] = B[Row[0]]; | |||||
Y[0] = B[Row[0]]; | |||||
for (K = 1; K < N; K++) { | for (K = 1; K < N; K++) { | ||||
SUM = 0; | SUM = 0; | ||||
@@ -6,103 +6,101 @@ const struct { | |||||
struct { | struct { | ||||
float Ns; | float Ns; | ||||
float b[3]; | float b[3]; | ||||
} cor[3]; | |||||
} satcal[] = | |||||
{ | |||||
/* calibration coeff from NOAA KLM POES satellite user guide */ | |||||
/* http://www.ncdc.noaa.gov/oa/pod-guide/ncdc/docs/klm/tables.htm */ | |||||
{/* NOAA-15 */ | |||||
{ /* PRT coeff d0,d1,d2 */ | |||||
{276.60157 , 0.051045 , 1.36328E-06}, | |||||
{276.62531 , 0.050909 , 1.47266E-06}, | |||||
{276.67413 , 0.050907 , 1.47656E-06}, | |||||
{276.59258 , 0.050966 , 1.47656E-06} | |||||
}, | |||||
{ /* channel radiance coeff vc,A,B*/ | |||||
{925.4075 , 0.337810 , 0.998719}, /* channel 4 */ | |||||
{839.8979 , 0.304558 , 0.999024}, /* channel 5 */ | |||||
{2695.9743 , 1.621256 , 0.998015} /* channel 3B */ | |||||
}, | |||||
{ /* nonlinear radiance correction Ns,b0,b1,b2 */ | |||||
{-4.50 , {4.76 , -0.0932 , 0.0004524}}, /* channel 4 */ | |||||
{-3.61 , {3.83 , -0.0659 , 0.0002811}}, /* channel 5*/ | |||||
{0.0,{0.0,0.0,0.0}} /* channel 3B*/ | |||||
} | |||||
}, | |||||
{/* NOAA-16 */ | |||||
{ /* PRT coeff d0,d1,d2 */ | |||||
{276.355 , 5.562E-02 ,-1.590E-05}, | |||||
{276.142 , 5.605E-02 ,-1.707E-05}, | |||||
{275.996 , 5.486E-02 ,-1.223E-05}, | |||||
{276.132 , 5.494E-02 ,-1.344E-05} | |||||
}, | |||||
{ /* channel radiance coeff vc,A,B*/ | |||||
{917.2289 ,0.332380 ,0.998522}, /* channel 4 */ | |||||
{838.1255 ,0.674623 ,0.998363}, /* channel 5 */ | |||||
{2700.1148 ,1.592459,0.998147} /* channel 3B */ | |||||
}, | |||||
{ /* nonlinear radiance correction Ns,b0,b1,b2 */ | |||||
{-2.467, {2.96,-0.05411,0.00024532}}, /* channel 4 */ | |||||
{-2.009,{2.25,-0.03665,0.00014854}}, /* channel 5*/ | |||||
{0.0,{0.0,0.0,0.0}} /* channel 3B*/ | |||||
} | |||||
}, | |||||
{/* NOAA 17 */ | |||||
{ /* PRT coeff d0,d1,d2 */ | |||||
{276.628 , 0.05098 , 1.371e-06}, | |||||
{276.538 , 0.05098 , 1.371e-06}, | |||||
{276.761 , 0.05097 , 1.369e-06}, | |||||
{276.660 , 0.05100 , 1.348e-06} | |||||
}, | |||||
{ /* channel radiance coeff vc,A,B*/ | |||||
{926.2947 , 0.271683 , 0.998794}, /* channel 4 */ | |||||
{839.8246 , 0.309180 , 0.999012}, /* channel 5 */ | |||||
{2669.3554 , 1.702380 , 0.997378} /* channel 3B */ | |||||
}, | |||||
{ /* nonlinear radiance correction Ns,b0,b1,b2 */ | |||||
{-8.55 , {8.22 , -0.15795 , 0.00075579}}, /* channel 4 */ | |||||
{-3.97 , {4.31 , -0.07318 , 0.00030976}}, /* channel 5 */ | |||||
{0.0,{0.0,0.0,0.0}} /* channel 3B*/ | |||||
} | |||||
}, | |||||
{/* NOAA 18 */ | |||||
{ /* PRT coeff d0,d1,d2 */ | |||||
{276.601 , 0.05090 , 1.657e-06}, | |||||
{276.683 , 0.05101 , 1.482e-06}, | |||||
{276.565 , 0.05117 , 1.313e-06}, | |||||
{276.615 , 0.05103 , 1.484e-06} | |||||
}, | |||||
{ /* channel radiance coeff vc,A,B*/ | |||||
{928.1460 , 0.436645 , 0.998607}, /* channel 4 */ | |||||
{833.2532 , 0.253179 , 0.999057}, /* channel 5 */ | |||||
{2659.7952 , 1.698704 , 0.996960} /* channel 3B */ | |||||
} cor[3]; | |||||
/* Calibration corficcients taken from the NOAA KLM satellite user guide | |||||
* https://web.archive.org/web/20141220021557/https://www.ncdc.noaa.gov/oa/pod-guide/ncdc/docs/klm/tables.htm | |||||
*/ | |||||
} satcal[] = { | |||||
{ // NOAA 15 | |||||
{ // PRT coefficient d0, d1, d2 | |||||
{276.60157, 0.051045, 1.36328E-06}, | |||||
{276.62531, 0.050909, 1.47266E-06}, | |||||
{276.67413, 0.050907, 1.47656E-06}, | |||||
{276.59258, 0.050966, 1.47656E-06} | |||||
}, | |||||
{ // Channel radiance coefficient vc, A, B | |||||
{925.4075, 0.337810, 0.998719}, // Channel 4 | |||||
{839.8979, 0.304558, 0.999024}, // Channel 5 | |||||
{2695.9743, 1.621256, 0.998015} // Channel 3B | |||||
}, | |||||
{ // Nonlinear radiance correction Ns, b0, b1, b2 | |||||
{-4.50, {4.76, -0.0932, 0.0004524}}, // Channel 4 | |||||
{-3.61, {3.83, -0.0659, 0.0002811}}, // Channel 5 | |||||
{0.0, {0.0, 0.0, 0.0}} // Channel 3B | |||||
} | |||||
}, | }, | ||||
{ /* nonlinear radiance correction Ns,b0,b1,b2 */ | |||||
{-5.53 , {5.82 , -0.11069 , 0.00052337}}, /* channel 4 */ | |||||
{-2.22 , {2.67 , -0.04360 , 0.00017715}}, /* channel 5 */ | |||||
{0.0,{0.0,0.0,0.0}} /* channel 3B*/ | |||||
} | |||||
{ // NOAA 16 | |||||
{ // PRT coeff d0, d1, d2 | |||||
{276.355, 5.562E-02, -1.590E-05}, | |||||
{276.142, 5.605E-02, -1.707E-05}, | |||||
{275.996, 5.486E-02, -1.223E-05}, | |||||
{276.132, 5.494E-02, -1.344E-05} | |||||
}, | |||||
{ // Channel radiance coefficient vc, A, B | |||||
{917.2289, 0.332380, 0.998522}, // Channel 4 | |||||
{838.1255, 0.674623, 0.998363}, // Channel 5 | |||||
{2700.1148, 1.592459, 0.998147} // Channel 3B | |||||
}, | |||||
{ // Nonlinear radiance correction Ns, b0, b1, b2 | |||||
{-2.467, {2.96, -0.05411, 0.00024532}}, // Channel 4 | |||||
{-2.009, {2.25, -0.03665, 0.00014854}}, // Channel 5 | |||||
{0.0, {0.0, 0.0, 0.0}} // Channel 3B | |||||
} | |||||
}, | }, | ||||
{/* NOAA 19 */ | |||||
{ /* PRT coeff d0,d1,d2 */ | |||||
{276.6067 , 0.051111 , 1.405783E-06}, | |||||
{276.6119 , 0.051090 , 1.496037E-06}, | |||||
{276.6311 , 0.051033 , 1.496990E-06}, | |||||
{276.6268 , 0.051058 , 1.493110E-06} | |||||
{ // NOAA 17 | |||||
{ // PRT coefficient d0, d1, d2 | |||||
{276.628, 0.05098, 1.371e-06}, | |||||
{276.538, 0.05098, 1.371e-06}, | |||||
{276.761, 0.05097, 1.369e-06}, | |||||
{276.660, 0.05100, 1.348e-06} | |||||
}, | |||||
{ // Channel radiance coefficient vc, A, B | |||||
{926.2947, 0.271683, 0.998794}, // Channel 4 | |||||
{839.8246, 0.309180, 0.999012}, // Channel 5 | |||||
{2669.3554, 1.702380, 0.997378} // Channel 3B | |||||
}, | |||||
{ // Nonlinear radiance correction Ns, b0, b1, b2 | |||||
{-8.55, {8.22, -0.15795, 0.00075579}}, // Channel 4 | |||||
{-3.97, {4.31, -0.07318, 0.00030976}}, // Channel 5 | |||||
{0.0, {0.0, 0.0, 0.0}} // Channel 3B | |||||
} | |||||
}, | }, | ||||
{ /* channel radiance coeff vc,A,B*/ | |||||
{928.9 , 0.53959 , 0.998534}, /* channel 4 */ | |||||
{831.9 , 0.36064 , 0.998913}, /* channel 5 */ | |||||
{2670.0 , 1.67396 , 0.997364} /* channel 3B */ | |||||
{ // NOAA 18 | |||||
{ // PRT coefficient d0, d1, d2 | |||||
{276.601, 0.05090, 1.657e-06}, | |||||
{276.683, 0.05101, 1.482e-06}, | |||||
{276.565, 0.05117, 1.313e-06}, | |||||
{276.615, 0.05103, 1.484e-06} | |||||
}, | |||||
{ // Channel radiance coefficient vc, A, B | |||||
{928.1460, 0.436645, 0.998607}, // Channel 4 | |||||
{833.2532, 0.253179, 0.999057}, // Channel 5 | |||||
{2659.7952, 1.698704, 0.996960} // Channel 3B | |||||
}, | |||||
{ // Nonlinear radiance correction Ns, b0, b1, b2 | |||||
{-5.53, {5.82, -0.11069, 0.00052337}}, // Channel 4 | |||||
{-2.22, {2.67, -0.04360, 0.00017715}}, // Channel 5 | |||||
{0.0, {0.0, 0.0, 0.0}} // Channel 3B | |||||
} | |||||
}, | }, | ||||
{ /* nonlinear radiance correction Ns,b0,b1,b2 */ | |||||
{-5.49 , {5.70 -0.11187 , 0.00054668}}, /* channel 4 */ | |||||
{-3.39 , {3.58 -0.05991 , 0.00024985}}, /* channel 5 */ | |||||
{0.0,{0.0,0.0,0.0}} /* channel 3B*/ | |||||
} | |||||
} | |||||
}; | |||||
{ // NOAA 19 | |||||
{ // PRT coefficient d0, d1, d2 | |||||
{276.6067, 0.051111, 1.405783E-06}, | |||||
{276.6119, 0.051090, 1.496037E-06}, | |||||
{276.6311, 0.051033, 1.496990E-06}, | |||||
{276.6268, 0.051058, 1.493110E-06} | |||||
}, | |||||
{ // Channel radiance coefficient vc, A, B | |||||
{928.9, 0.53959, 0.998534}, // Channel 4 | |||||
{831.9, 0.36064, 0.998913}, // Channel 5 | |||||
{2670.0, 1.67396, 0.997364} // Channel 3B | |||||
}, | |||||
{ // Nonlinear radiance correction Ns, b0, b1, b2 | |||||
{-5.49, {5.70 -0.11187, 0.00054668}}, // Channel 4 | |||||
{-3.39, {3.58 -0.05991, 0.00024985}}, // Channel 5 | |||||
{0.0, {0.0, 0.0, 0.0}} // Channel 3B | |||||
} | |||||
}}; | |||||
const float c1=1.1910427e-5; | |||||
const float c2=1.4387752; | |||||
const float c1 = 1.1910427e-5; | |||||
const float c2 = 1.4387752; |
@@ -1,4 +1,4 @@ | |||||
unsigned char TempPalette[256*3] = { | |||||
unsigned char TempPalette[256*3] = { | |||||
"\376\376\376\376\376\376\375\375\376\374\375\376\374\375\375\374\373\375" | "\376\376\376\376\376\376\375\375\376\374\375\376\374\375\375\374\373\375" | ||||
"\373\373\375\372\373\375\372\373\374\372\372\374\371\372\374\371\371\375" | "\373\373\375\372\373\375\372\373\374\372\372\374\371\372\374\371\371\375" | ||||
"\370\371\374\367\370\375\367\370\374\367\367\374\366\367\373\366\366\373" | "\370\371\374\367\370\375\367\370\374\367\367\374\366\367\373\366\366\373" | ||||
@@ -40,4 +40,3 @@ unsigned char TempPalette[256*3] = { | |||||
"\2261\373\2161\373\206.\373}-\374u,\375k*\374b(\375Y'\375O%\375E#\375;\"" | "\2261\373\2161\373\206.\373}-\374u,\375k*\374b(\375Y'\375O%\375E#\375;\"" | ||||
"\3762\40\376(\37\376\35\35", | "\3762\40\376(\37\376\35\35", | ||||
}; | }; | ||||
@@ -1,2 +0,0 @@ | |||||
char version[] = "Aptec CVS version (c) 2004-2005 Thierry Leconte F4DWV"; | |||||
@@ -1,123 +0,0 @@ | |||||
#include <stdio.h> | |||||
#include <string.h> | |||||
char * basename (const char *filename) { | |||||
const char *p = filename + strlen (filename); | |||||
while (p != filename) { | |||||
p--; | |||||
if (*p == '/' || *p == '\\') | |||||
return ((char *) p + 1); | |||||
} | |||||
return ((char *) filename); | |||||
} | |||||
char *dirname(const char *path) { | |||||
static char bname[1024]; | |||||
register const char *endp; | |||||
/* Empty or NULL string gets treated as "." */ | |||||
if (path == NULL || *path == '\0') { | |||||
(void)strncpy(bname, ".", sizeof bname); | |||||
return(bname); | |||||
} | |||||
/* Strip trailing slashes */ | |||||
endp = path + strlen(path) - 1; | |||||
while (endp > path && *endp == '\\') | |||||
endp--; | |||||
/* Find the start of the dir */ | |||||
while (endp > path && *endp != '\\') | |||||
endp--; | |||||
/* Either the dir is "/" or there are no slashes */ | |||||
if (endp == path) { | |||||
(void)strncpy(bname, *endp == '\\' ? "\\": ".", sizeof bname); | |||||
return(bname); | |||||
} else { | |||||
do { | |||||
endp--; | |||||
} while (endp > path && *endp == '\\'); | |||||
} | |||||
if (endp - path + 2 > sizeof(bname)) { | |||||
return(NULL); | |||||
} | |||||
strncpy(bname, path, endp - path + 2); | |||||
return(bname); | |||||
} | |||||
int opterr = 1, /* if error message should be printed */ | |||||
optind = 1, /* index into parent argv vector */ | |||||
optopt, /* character checked for validity */ | |||||
optreset; /* reset getopt */ | |||||
char *optarg; /* argument associated with option */ | |||||
#define BADCH (int)'?' | |||||
#define BADARG (int)':' | |||||
#define EMSG "" | |||||
/* | |||||
* getopt -- | |||||
* Parse argc/argv argument vector. | |||||
*/ | |||||
int getopt(nargc, nargv, ostr) | |||||
int nargc; | |||||
char * const *nargv; | |||||
const char *ostr; | |||||
{ | |||||
#define __progname nargv[0] | |||||
static char *place = EMSG; /* option letter processing */ | |||||
char *oli; /* option letter list index */ | |||||
if (optreset || !*place) { /* update scanning pointer */ | |||||
optreset = 0; | |||||
if (optind >= nargc || *(place = nargv[optind]) != '-') { | |||||
place = EMSG; | |||||
return (-1); | |||||
} | |||||
if (place[1] && *++place == '-') { /* found "--" */ | |||||
++optind; | |||||
place = EMSG; | |||||
return (-1); | |||||
} | |||||
} /* option letter okay? */ | |||||
if ((optopt = (int)*place++) == (int)':' || | |||||
!(oli = strchr(ostr, optopt))) { | |||||
/* | |||||
* if the user didn't specify '-' as an option, | |||||
* assume it means -1. | |||||
*/ | |||||
if (optopt == (int)'-') | |||||
return (-1); | |||||
if (!*place) | |||||
++optind; | |||||
if (opterr && *ostr != ':') | |||||
(void)printf("%s: illegal option -- %c\n", __progname, optopt); | |||||
return (BADCH); | |||||
} | |||||
if (*++oli != ':') { /* don't need argument */ | |||||
optarg = NULL; | |||||
if (!*place) | |||||
++optind; | |||||
} | |||||
else { /* need an argument */ | |||||
if (*place) /* no white space */ | |||||
optarg = place; | |||||
else if (nargc <= ++optind) { /* no arg */ | |||||
place = EMSG; | |||||
if (*ostr == ':') | |||||
return (BADARG); | |||||
if (opterr) | |||||
(void)printf( | |||||
"%s: option requires an argument -- %c\n", | |||||
__progname, optopt); | |||||
return (BADCH); | |||||
} else /* white space */ | |||||
optarg = nargv[optind]; | |||||
place = EMSG; | |||||
++optind; | |||||
} | |||||
return (optopt); /* dump back option letter */ | |||||
} | |||||
@@ -1,12 +0,0 @@ | |||||
extern char * basename(const char *filename); | |||||
extern char *dirname(const char *path); | |||||
extern int opterr, /* if error message should be printed */ | |||||
optind , /* index into parent argv vector */ | |||||
optopt, /* character checked for validity */ | |||||
optreset; /* reset getopt */ | |||||
extern char *optarg; /* argument associated with option */ | |||||
extern int getopt(int nargc, char * const *nargv, const char *ostr); |