Improved commenting in dsp.c Updated copyright notices Code tidy up in some partstags/v1.8.0
@@ -1,12 +1,12 @@ | |||||
GNU GENERAL PUBLIC LICENSE | |||||
Version 2, June 1991 | |||||
GNU GENERAL PUBLIC LICENSE | |||||
Version 2, June 1991 | |||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc. | |||||
675 Mass Ave, Cambridge, MA 02139, USA | |||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc., | |||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||||
Everyone is permitted to copy and distribute verbatim copies | Everyone is permitted to copy and distribute verbatim copies | ||||
of this license document, but changing it is not allowed. | of this license document, but changing it is not allowed. | ||||
Preamble | |||||
Preamble | |||||
The licenses for most software are designed to take away your | The licenses for most software are designed to take away your | ||||
freedom to share and change it. By contrast, the GNU General Public | freedom to share and change it. By contrast, the GNU General Public | ||||
@@ -15,7 +15,7 @@ software--to make sure the software is free for all its users. This | |||||
General Public License applies to most of the Free Software | General Public License applies to most of the Free Software | ||||
Foundation's software and to any other program whose authors commit to | Foundation's software and to any other program whose authors commit to | ||||
using it. (Some other Free Software Foundation software is covered by | using it. (Some other Free Software Foundation software is covered by | ||||
the GNU Library General Public License instead.) You can apply it to | |||||
the GNU Lesser General Public License instead.) You can apply it to | |||||
your programs, too. | your programs, too. | ||||
When we speak of free software, we are referring to freedom, not | When we speak of free software, we are referring to freedom, not | ||||
@@ -55,8 +55,8 @@ patent must be licensed for everyone's free use or not licensed at all. | |||||
The precise terms and conditions for copying, distribution and | The precise terms and conditions for copying, distribution and | ||||
modification follow. | modification follow. | ||||
GNU GENERAL PUBLIC LICENSE | |||||
GNU GENERAL PUBLIC LICENSE | |||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | ||||
0. This License applies to any program or other work which contains | 0. This License applies to any program or other work which contains | ||||
@@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions: | |||||
License. (Exception: if the Program itself is interactive but | License. (Exception: if the Program itself is interactive but | ||||
does not normally print such an announcement, your work based on | does not normally print such an announcement, your work based on | ||||
the Program is not required to print an announcement.) | the Program is not required to print an announcement.) | ||||
These requirements apply to the modified work as a whole. If | These requirements apply to the modified work as a whole. If | ||||
identifiable sections of that work are not derived from the Program, | identifiable sections of that work are not derived from the Program, | ||||
and can be reasonably considered independent and separate works in | and can be reasonably considered independent and separate works in | ||||
@@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent | |||||
access to copy the source code from the same place counts as | access to copy the source code from the same place counts as | ||||
distribution of the source code, even though third parties are not | distribution of the source code, even though third parties are not | ||||
compelled to copy the source along with the object code. | compelled to copy the source along with the object code. | ||||
4. You may not copy, modify, sublicense, or distribute the Program | 4. You may not copy, modify, sublicense, or distribute the Program | ||||
except as expressly provided under this License. Any attempt | except as expressly provided under this License. Any attempt | ||||
otherwise to copy, modify, sublicense or distribute the Program is | otherwise to copy, modify, sublicense or distribute the Program is | ||||
@@ -225,7 +225,7 @@ impose that choice. | |||||
This section is intended to make thoroughly clear what is believed to | This section is intended to make thoroughly clear what is believed to | ||||
be a consequence of the rest of this License. | be a consequence of the rest of this License. | ||||
8. If the distribution and/or use of the Program is restricted in | 8. If the distribution and/or use of the Program is restricted in | ||||
certain countries either by patents or by copyrighted interfaces, the | certain countries either by patents or by copyrighted interfaces, the | ||||
original copyright holder who places the Program under this License | original copyright holder who places the Program under this License | ||||
@@ -255,7 +255,7 @@ make exceptions for this. Our decision will be guided by the two goals | |||||
of preserving the free status of all derivatives of our free software and | of preserving the free status of all derivatives of our free software and | ||||
of promoting the sharing and reuse of software generally. | of promoting the sharing and reuse of software generally. | ||||
NO WARRANTY | |||||
NO WARRANTY | |||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY | ||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN | ||||
@@ -277,4 +277,4 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER | |||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE | ||||
POSSIBILITY OF SUCH DAMAGES. | POSSIBILITY OF SUCH DAMAGES. | ||||
END OF TERMS AND CONDITIONS | |||||
END OF TERMS AND CONDITIONS |
@@ -1,4 +1,4 @@ | |||||
CC = clang | |||||
CC = gcc | |||||
BIN = /usr/bin | BIN = /usr/bin | ||||
INCLUDES = -I. | INCLUDES = -I. | ||||
CFLAGS = -O3 -Wall $(INCLUDES) | CFLAGS = -O3 -Wall $(INCLUDES) | ||||
@@ -7,7 +7,7 @@ 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 colorpalette.h offsets.h messages.h | |||||
main.o: main.c palette.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 messages.h | image.o: image.c satcal.h offsets.h messages.h | ||||
@@ -1,6 +1,6 @@ | |||||
![Aptdec logo](textlogo.png) | ![Aptdec logo](textlogo.png) | ||||
Thierry Leconte F4DWV (c) 2004-2009 | |||||
Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019 | |||||
## Description | ## Description | ||||
@@ -89,14 +89,14 @@ Image names are `audiofile-x.png`, where `x` is: | |||||
Currently there are 2 available enchancements: | Currently there are 2 available enchancements: | ||||
- `c` for contrast enhancements, on by default | |||||
- `c` for contrast equalise, on by default | |||||
- `t` for crop telemetry, on by default, only has effects on raw images | - `t` for crop telemetry, on by default, only has effects on raw images | ||||
## Example | ## Example | ||||
`aptdec -d images -i ab *.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. | |||||
This will process all `.wav` files in the current directory, generate equalised channel A and B images and put them in the `images` directory. | |||||
## Further reading | ## Further reading | ||||
@@ -1,44 +1,37 @@ | |||||
/* | |||||
* Aptdec | |||||
* Copyright (c) 2004 by Thierry Leconte (F4DWV) | |||||
/* | |||||
* This file is part of Aptdec. | |||||
* Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019 | |||||
* | * | ||||
* $Id$ | |||||
* Aptdec is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation, either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | * | ||||
* This library is free software; you can redistribute it and/or modify | |||||
* it under the terms of the GNU Library General Public License as | |||||
* published by the Free Software Foundation; either version 2 of | |||||
* the License, or (at your option) any later version. | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | * | ||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU Library General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Library General Public | |||||
* License along with this library; if not, write to the Free Software | |||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | |||||
* | * | ||||
*/ | */ | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <math.h> | #include <math.h> | ||||
#ifndef M_PI | |||||
#define M_PI 3.1415926535 // For OS's that don't include it | |||||
#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); | |||||
// In case your C compiler is so old that Pi hadn't been invented yet | |||||
#ifndef M_PI | |||||
#define M_PI 3.141592653589793 | |||||
#endif | |||||
// Block sizes | |||||
#define BLKAMP 1024 | #define BLKAMP 1024 | ||||
// Block size | |||||
#define BLKIN 1024 | #define BLKIN 1024 | ||||
#define Fc 2400.0 | #define Fc 2400.0 | ||||
@@ -48,6 +41,8 @@ extern int getsample(float *inbuff, int nb); | |||||
#define RSMULT 15 | #define RSMULT 15 | ||||
#define Fi (Fp * RSMULT) | #define Fi (Fp * RSMULT) | ||||
extern int getsample(float *inbuff, int count); | |||||
static double Fe; | static double Fe; | ||||
static double offset = 0.0; | static double offset = 0.0; | ||||
@@ -56,7 +51,7 @@ 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 | |||||
// Check the input sample rate and calculate some constants | |||||
int init_dsp(double F) { | int init_dsp(double F) { | ||||
if(F > Fi) return(1); | if(F > Fi) return(1); | ||||
if(F < Fp) return(-1); | if(F < Fp) return(-1); | ||||
@@ -64,13 +59,15 @@ int init_dsp(double F) { | |||||
K1 = DFc / Fe; | K1 = DFc / Fe; | ||||
K2 = K1 * K1 / 2.0; | K2 = K1 * K1 / 2.0; | ||||
// Number of samples for each cycle | |||||
FreqOsc = Fc / Fe; | FreqOsc = Fc / Fe; | ||||
return(0); | return(0); | ||||
} | } | ||||
// Fast phase estimator | |||||
// Calculates the phase angle of a signal from a IQ sample | |||||
/* Fast phase estimator | |||||
* Calculates the phase angle of a signal from a IQ sample | |||||
*/ | |||||
static inline double Phase(double I, double Q) { | static inline double Phase(double I, double Q) { | ||||
double angle, r; | double angle, r; | ||||
int s; | int s; | ||||
@@ -99,8 +96,11 @@ 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 | |||||
/* Phase locked loop | |||||
* https://en.wikipedia.org/wiki/Phase-locked_loop | |||||
* https://arachnoid.com/phase_locked_loop/ | |||||
* https://simple.wikipedia.org/wiki/Phase-locked_loop | |||||
*/ | |||||
static double pll(double I, double Q) { | static double pll(double I, double Q) { | ||||
// PLL coefficient | // PLL coefficient | ||||
static double PhaseOsc = 0.0; | static double PhaseOsc = 0.0; | ||||
@@ -133,17 +133,16 @@ static double pll(double I, double Q) { | |||||
return(Ip); | return(Ip); | ||||
} | } | ||||
// Convert audio samples into a pixel | |||||
static int getamp(double *ambuff, int nb) { | |||||
// Convert samples into pixels | |||||
static int getamp(double *ampbuff, int count) { | |||||
static float inbuff[BLKIN]; | static float inbuff[BLKIN]; | ||||
static int idxin = 0; | static int idxin = 0; | ||||
static int nin = 0; | static int nin = 0; | ||||
int n; | |||||
for (n = 0; n < nb; n++) { | |||||
for (int n = 0; n < count; n++) { | |||||
double I, Q; | double I, Q; | ||||
// If the amount of samples is small enough to be processed | |||||
// Get some more samples when needed | |||||
if (nin < IQFilterLen * 2 + 2) { | if (nin < IQFilterLen * 2 + 2) { | ||||
// Number of samples read | // Number of samples read | ||||
int res; | int res; | ||||
@@ -153,50 +152,52 @@ static int getamp(double *ambuff, int nb) { | |||||
// Read some samples | // Read some samples | ||||
res = getsample(&(inbuff[nin]), BLKIN - nin); | res = getsample(&(inbuff[nin]), BLKIN - nin); | ||||
nin += res; | nin += res; | ||||
// If we haven't read any more samples, return how far we got | |||||
// Make sure there is enough samples to continue | |||||
if (nin < IQFilterLen * 2 + 2) | if (nin < IQFilterLen * 2 + 2) | ||||
return(n); | return(n); | ||||
} | } | ||||
// Process read samples into a brightness value | // 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); | |||||
ampbuff[n] = pll(I, Q); | |||||
idxin += 1; | |||||
nin -= 1; | |||||
// Increment current sample | |||||
idxin++; | |||||
nin--; | |||||
} | } | ||||
return(n); | |||||
return(count); | |||||
} | } | ||||
// Get an entire row of pixels, without alignment | |||||
int getpixelv(float *pvbuff, int nb) { | |||||
// Sub-pixel offsetting + FIR compensation | |||||
int getpixelv(float *pvbuff, int count) { | |||||
// Amplitude buffer | // Amplitude buffer | ||||
static double ambuff[BLKAMP]; | |||||
static double ampbuff[BLKAMP]; | |||||
static int nam = 0; | static int nam = 0; | ||||
static int idxam = 0; | static int idxam = 0; | ||||
int n, m; | |||||
double mult; | double mult; | ||||
// Sub-pixel offset | |||||
mult = (double) Fi / Fe * FreqLine; | mult = (double) Fi / Fe * FreqLine; | ||||
m = RSFilterLen / mult + 1; | |||||
int m = RSFilterLen / mult + 1; | |||||
for (n = 0; n < nb; n++) { | |||||
for (int n = 0; n < count; n++) { | |||||
int shift; | int shift; | ||||
if (nam < m) { | if (nam < m) { | ||||
int res; | int res; | ||||
memmove(ambuff, &(ambuff[idxam]), nam * sizeof(double)); | |||||
memmove(ampbuff, &(ampbuff[idxam]), nam * sizeof(double)); | |||||
idxam = 0; | idxam = 0; | ||||
res = getamp(&(ambuff[nam]), BLKAMP - nam); | |||||
res = getamp(&(ampbuff[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; | |||||
// Gaussian FIR compensation filter | |||||
pvbuff[n] = rsfir(&(ampbuff[idxam]), rsfilter, RSFilterLen, offset, mult) * mult * 256.0; | |||||
shift = ((int) floor((RSMULT - offset) / mult)) + 1; | shift = ((int) floor((RSMULT - offset) / mult)) + 1; | ||||
offset = shift * mult + offset - RSMULT; | offset = shift * mult + offset - RSMULT; | ||||
@@ -204,44 +205,45 @@ int getpixelv(float *pvbuff, int nb) { | |||||
idxam += shift; | idxam += shift; | ||||
nam -= shift; | nam -= shift; | ||||
} | } | ||||
return(nb); | |||||
return(count); | |||||
} | } | ||||
// Align this line based off of the synchronisation markers | |||||
// Get an entire row of pixels, aligned with sync 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; | |||||
static int synced = 0; | static int synced = 0; | ||||
static double max = 0.0; | |||||
static double max = 0; | |||||
double corr, ecorr, lcorr; | double corr, ecorr, lcorr; | ||||
int res; | int res; | ||||
// Move the row buffer into the the image buffer | |||||
if (npv > 0) | if (npv > 0) | ||||
memmove(pixelv, pixels, npv * sizeof(float)); | memmove(pixelv, pixels, npv * sizeof(float)); | ||||
// Get the sync line | |||||
if (npv < SyncFilterLen + 2) { | if (npv < SyncFilterLen + 2) { | ||||
res = getpixelv(&(pixelv[npv]), SyncFilterLen + 2 - npv); | res = getpixelv(&(pixelv[npv]), SyncFilterLen + 2 - npv); | ||||
npv += res; | npv += res; | ||||
if (npv < SyncFilterLen + 2) | |||||
return(0); | |||||
// Exit if there are no pixels left | |||||
if (npv < SyncFilterLen + 2) return(0); | |||||
} | } | ||||
// Test current synchronisation | |||||
// Calculate the sub-pixel offset | |||||
ecorr = fir(pixelv, Sync, SyncFilterLen); | ecorr = fir(pixelv, Sync, SyncFilterLen); | ||||
corr = fir(&(pixelv[1]), Sync, SyncFilterLen - 1); | corr = fir(&(pixelv[1]), Sync, SyncFilterLen - 1); | ||||
lcorr = fir(&(pixelv[2]), Sync, SyncFilterLen - 2); | 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 | |||||
// The point in which the pixel offset is recalculated | |||||
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 mshift; | int mshift; | ||||
if (npv < PixelLine + SyncFilterLen) { | if (npv < PixelLine + SyncFilterLen) { | ||||
@@ -251,9 +253,9 @@ int getpixelrow(float *pixelv) { | |||||
return(0); | return(0); | ||||
} | } | ||||
// Shift this line until we see the best results | |||||
// Test every possible position until we get the best result | |||||
mshift = 0; | mshift = 0; | ||||
for (int shift = 1; shift < PixelLine; shift++) { | |||||
for (int shift = 0; shift < PixelLine; shift++) { | |||||
double corr; | double corr; | ||||
corr = fir(&(pixelv[shift + 1]), Sync, SyncFilterLen); | corr = fir(&(pixelv[shift + 1]), Sync, SyncFilterLen); | ||||
@@ -263,32 +265,32 @@ int getpixelrow(float *pixelv) { | |||||
} | } | ||||
} | } | ||||
// Shift memory, shifting this row | |||||
if (mshift != 0) { | |||||
// If we are already as aligned as we can get, just continue | |||||
if (mshift == 0) { | |||||
synced++; | |||||
} else { | |||||
memmove(pixelv, &(pixelv[mshift]), (npv - mshift) * sizeof(float)); | memmove(pixelv, &(pixelv[mshift]), (npv - mshift) * sizeof(float)); | ||||
npv -= mshift; | npv -= mshift; | ||||
synced = 0; | synced = 0; | ||||
FreqLine = 1.0; | FreqLine = 1.0; | ||||
} else | |||||
synced += 1; | |||||
} | |||||
} | } | ||||
// If there are not enough pixels try to grab some more | |||||
// Get the rest of this row | |||||
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 | |||||
// Move the sync lines into the output buffer with the calculated offset | |||||
if (npv == PixelLine) { | if (npv == PixelLine) { | ||||
npv = 0; | npv = 0; | ||||
} else { // Move the pixel build buffer to the output buffer | |||||
} else { | |||||
memmove(pixels, &(pixelv[PixelLine]), (npv - PixelLine) * sizeof(float)); | memmove(pixels, &(pixelv[PixelLine]), (npv - PixelLine) * sizeof(float)); | ||||
npv -= PixelLine; | npv -= PixelLine; | ||||
} | } | ||||
return(1); | return(1); | ||||
} | |||||
} |
@@ -1,3 +1,22 @@ | |||||
/* | |||||
* This file is part of Aptdec. | |||||
* Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019 | |||||
* | |||||
* Aptdec is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation, either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | |||||
* | |||||
*/ | |||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <math.h> | #include <math.h> | ||||
#include "offsets.h" | #include "offsets.h" | ||||
@@ -18,8 +37,8 @@ static struct { | |||||
int Cloudthreshold; | int Cloudthreshold; | ||||
int Cloudintensity; | int Cloudintensity; | ||||
} fcinfo = { | } fcinfo = { | ||||
{28, 44, 95, 1}, | |||||
{23, 78, 37, 1}, | |||||
{28, 44, 95, 1}, | |||||
{23, 78, 37, 1}, | |||||
{240, 250, 255, 1}, | {240, 250, 255, 1}, | ||||
50, | 50, | ||||
0.5, | 0.5, | ||||
@@ -89,9 +108,7 @@ void Ngvi(float **prow, int nrow) { | |||||
fflush(stdout); | fflush(stdout); | ||||
for (int n = 0; n < nrow; n++) { | for (int n = 0; n < nrow; n++) { | ||||
float *pixelv; | |||||
pixelv = prow[n]; | |||||
float *pixelv = prow[n]; | |||||
for (int i = 0; i < CH_WIDTH; i++) { | for (int i = 0; i < CH_WIDTH; i++) { | ||||
float pv; | float pv; | ||||
double gvi; | double gvi; | ||||
@@ -1,28 +1,27 @@ | |||||
/* | |||||
* Aptdec | |||||
* Copyright (c) 2004 by Thierry Leconte (F4DWV) | |||||
/* | |||||
* This file is part of Aptdec. | |||||
* Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019 | |||||
* | * | ||||
* $Id$ | |||||
* Aptdec is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation, either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | * | ||||
* This library is free software; you can redistribute it and/or modify | |||||
* it under the terms of the GNU Library General Public License as | |||||
* published by the Free Software Foundation; either version 2 of | |||||
* the License, or (at your option) any later version. | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | * | ||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU Library General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Library General Public | |||||
* License along with this library; if not, write to the Free Software | |||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | |||||
* | * | ||||
*/ | */ | ||||
#include "filter.h" | |||||
#include <math.h> | #include <math.h> | ||||
// Sum of a matrix multiplication of 2 arrays | |||||
#include "filter.h" | |||||
// Finite impulse response | |||||
float fir(float *buff, const float *coeff, const int len) { | float fir(float *buff, const float *coeff, const int len) { | ||||
double r; | double r; | ||||
@@ -33,22 +32,24 @@ float fir(float *buff, const float *coeff, const int len) { | |||||
return(r); | return(r); | ||||
} | } | ||||
// Create an IQ sample from a sample buffer | |||||
/* IQ finite impulse response | |||||
* Turn samples into a single IQ sample | |||||
*/ | |||||
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) { | ||||
double i, q; | double i, q; | ||||
i = q = 0.0; | i = q = 0.0; | ||||
for (int 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]; | ||||
} | } | ||||
// Grab the peak value of the wave and subtract the DC offset | |||||
i = buff[len-1] - (i / len); | i = buff[len-1] - (i / len); | ||||
*I = i, *Q = q; | *I = i, *Q = q; | ||||
} | } | ||||
// Denoise, I don't know how it works, but it does | |||||
/* Gaussian finite impulse responce compensation | |||||
* https://www.recordingblogs.com/wiki/gaussian-window | |||||
*/ | |||||
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) { | ||||
double out; | double out; | ||||
@@ -1,22 +1,19 @@ | |||||
/* | |||||
* Aptdec | |||||
* Copyright (c) 2003 by Thierry Leconte (F4DWV) | |||||
/* | |||||
* This file is part of Aptdec. | |||||
* Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019 | |||||
* | * | ||||
* $Id$ | |||||
* Aptdec is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation, either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | * | ||||
* This library is free software; you can redistribute it and/or modify | |||||
* it under the terms of the GNU Library General Public License as | |||||
* published by the Free Software Foundation; either version 2 of | |||||
* the License, or (at your option) any later version. | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | * | ||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU Library General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Library General Public | |||||
* License along with this library; if not, write to the Free Software | |||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | |||||
* | * | ||||
*/ | */ | ||||
@@ -1,38 +1,35 @@ | |||||
/* | |||||
* Aptdec | |||||
* Copyright (c) 2003 by Thierry Leconte (F4DWV) | |||||
/* | |||||
* This file is part of Aptdec. | |||||
* Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019 | |||||
* | * | ||||
* $Id$ | |||||
* Aptdec is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation, either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | * | ||||
* This library is free software; you can redistribute it and/or modify | |||||
* it under the terms of the GNU Library General Public License as | |||||
* published by the Free Software Foundation; either version 2 of | |||||
* the License, or (at your option) any later version. | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | * | ||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU Library General Public License for more details. | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | |||||
* | * | ||||
* You should have received a copy of the GNU Library General Public | |||||
* License along with this library; if not, write to the Free Software | |||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||||
* | |||||
*/ | |||||
*/ | |||||
// IQ finite impulse response filter | |||||
#define IQFilterLen 32 | #define IQFilterLen 32 | ||||
const float iqfilter[IQFilterLen] = { 0.0205361, 0.0219524, 0.0235785, 0.0254648, 0.0276791, 0.0303152, | const float iqfilter[IQFilterLen] = { 0.0205361, 0.0219524, 0.0235785, 0.0254648, 0.0276791, 0.0303152, | ||||
0.0335063, 0.0374482, 0.0424413, 0.0489708, 0.0578745, 0.0707355, 0.0909457, 0.127324, 0.212207, 0.63662, | 0.0335063, 0.0374482, 0.0424413, 0.0489708, 0.0578745, 0.0707355, 0.0909457, 0.127324, 0.212207, 0.63662, | ||||
-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 | |||||
// Pattern of a NOAA sync line | // Pattern of a NOAA sync line | ||||
#define SyncFilterLen 32 | |||||
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 }; | ||||
// Gaussian finite impulse response compensation filter | |||||
#define RSFilterLen 437 | #define RSFilterLen 437 | ||||
const float rsfilter[RSFilterLen] = { -3.37279e-04, -8.80292e-06, -3.96418e-04, -1.78544e-04, -5.27511e-04, | const float rsfilter[RSFilterLen] = { -3.37279e-04, -8.80292e-06, -3.96418e-04, -1.78544e-04, -5.27511e-04, | ||||
-3.75376e-04, -6.95337e-04, -5.93148e-04, -8.79730e-04, -8.15327e-04, -1.05669e-03, -1.01377e-03, | -3.75376e-04, -6.95337e-04, -5.93148e-04, -8.79730e-04, -8.15327e-04, -1.05669e-03, -1.01377e-03, | ||||
@@ -1,22 +1,19 @@ | |||||
/* | |||||
* Aptdec | |||||
* Copyright (c) 2004 by Thierry Leconte (F4DWV) | |||||
/* | |||||
* This file is part of Aptdec. | |||||
* Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019 | |||||
* | * | ||||
* $Id$ | |||||
* Aptdec is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation, either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | * | ||||
* This library is free software; you can redistribute it and/or modify | |||||
* it under the terms of the GNU Library General Public License as | |||||
* published by the Free Software Foundation; either version 2 of | |||||
* the License, or (at your option) any later version. | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | * | ||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU Library General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Library General Public | |||||
* License along with this library; if not, write to the Free Software | |||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | |||||
* | * | ||||
*/ | */ | ||||
@@ -33,18 +30,21 @@ typedef struct { | |||||
double cf[REGORDER + 1]; | double cf[REGORDER + 1]; | ||||
} rgparam; | } rgparam; | ||||
extern void polyreg(const int m, const int n, const double x[], const double y[], double c[]); | |||||
// Compute regression | |||||
static void rgcomp(double x[16], rgparam * rgpr) { | static void rgcomp(double x[16], rgparam * rgpr) { | ||||
//const double y[9] = { 0.106, 0.215, 0.324, 0.433, 0.542, 0.652, 0.78, 0.87, 0.0 }; | |||||
// { 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[]); | |||||
polyreg(REGORDER, 9, x, y, rgpr -> cf); | polyreg(REGORDER, 9, x, y, rgpr -> cf); | ||||
} | } | ||||
static double rgcal(float x, rgparam * rgpr) { | |||||
// Convert a value to 0-255 based off the provided regression curve | |||||
static double rgcal(float x, rgparam *rgpr) { | |||||
double y, p; | double y, p; | ||||
int i; | |||||
int i; | |||||
for (i = 0, y = 0.0, p = 1.0; i < REGORDER + 1; i++) { | for (i = 0, y = 0.0, p = 1.0; i < REGORDER + 1; i++) { | ||||
y += rgpr->cf[i] * p; | y += rgpr->cf[i] * p; | ||||
p = p * x; | p = p * x; | ||||
@@ -56,35 +56,39 @@ static double tele[16]; | |||||
static double Cs; | static double Cs; | ||||
static int nbtele; | static int nbtele; | ||||
// 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; | |||||
// Brightness equalise, including telemetry | |||||
void equalise(float **prow, int nrow, int offset, int width, int telestart, rgparam regr[30]){ | |||||
offset -= SYNC_WIDTH+SPC_WIDTH; | |||||
pixelv = prow[n]; | |||||
for (i = 0; i < CH_WIDTH; i++) { | |||||
float pv; | |||||
int k, kof; | |||||
for (int n = 0; n < nrow; n++) { | |||||
float *pixelv = prow[n]; | |||||
pv = pixelv[i + offset]; | |||||
for (int i = 0; i < width+SYNC_WIDTH+SPC_WIDTH+TELE_WIDTH; i++) { | |||||
float pv = pixelv[i + offset]; | |||||
k = (n - telestart) / 128; | |||||
// Blend between the calculated regression curves | |||||
/* TODO: this can actually make the image look *worse* | |||||
* if the signal has a constant input gain. | |||||
*/ | |||||
int k, kof; | |||||
k = (n - telestart) / FRAME_LEN; | |||||
if (k >= nbtele) | if (k >= nbtele) | ||||
k = nbtele - 1; | k = nbtele - 1; | ||||
kof = (n - telestart) % 128; | |||||
kof = (n - telestart) % FRAME_LEN; | |||||
if (kof < 64) { | if (kof < 64) { | ||||
if (k < 1) { | if (k < 1) { | ||||
pv = rgcal(pv, &(regr[k])); | pv = rgcal(pv, &(regr[k])); | ||||
} else { | } else { | ||||
pv = rgcal(pv, &(regr[k])) * (64 + kof) / 128.0 + rgcal(pv, &(regr[k - 1])) * (64 - kof) / 128.0; | |||||
pv = rgcal(pv, &(regr[k])) * (64 + kof) / FRAME_LEN + | |||||
rgcal(pv, &(regr[k - 1])) * (64 - kof) / FRAME_LEN; | |||||
} | } | ||||
} else { | } else { | ||||
if ((k + 1) >= nbtele) { | if ((k + 1) >= nbtele) { | ||||
pv = rgcal(pv, &(regr[k])); | pv = rgcal(pv, &(regr[k])); | ||||
} else { | } else { | ||||
pv = rgcal(pv, &(regr[k])) * (192 - kof) / 128.0 + rgcal(pv, &(regr[k + 1])) * (kof - 64) / 128.0; | |||||
pv = rgcal(pv, &(regr[k])) * (192 - kof) / FRAME_LEN + | |||||
rgcal(pv, &(regr[k + 1])) * (kof - 64) / FRAME_LEN; | |||||
} | } | ||||
} | } | ||||
@@ -94,119 +98,105 @@ void equalise(float **prow, int nrow, int offset, int telestart, rgparam regr[30 | |||||
} | } | ||||
} | } | ||||
// Get telemetry data for thermal calibration | |||||
int calibrate(float **prow, int nrow, int offset, int contrastBoost) { | |||||
// Get telemetry data for thermal calibration/equalization | |||||
int calibrate(float **prow, int nrow, int offset, int width, int contrastEqualise) { | |||||
double teleline[3000]; | double teleline[3000]; | ||||
double wedge[16]; | double wedge[16]; | ||||
rgparam regr[30]; | rgparam regr[30]; | ||||
int n; | |||||
int k; | |||||
int mtelestart, telestart; | |||||
int n, k; | |||||
int mtelestart = 0, telestart; | |||||
int channel = -1; | int channel = -1; | ||||
float max; | |||||
// Extract telemetry data, for a single pixel row | |||||
// Calculate average of a row of telemetry | |||||
for (n = 0; n < nrow; n++) { | for (n = 0; n < nrow; n++) { | ||||
int i; | |||||
float *pixelv = prow[n]; | |||||
// Average the center 40px | |||||
teleline[n] = 0.0; | teleline[n] = 0.0; | ||||
// Average the 40 center pixels from the telemetry block | |||||
for (i = 3; i < 43; i++) { | |||||
teleline[n] += prow[n][i + offset + CH_WIDTH]; | |||||
} | |||||
// Compute the average | |||||
for (int i = 3; i < 43; i++) teleline[n] += pixelv[i + offset + width]; | |||||
teleline[n] /= 40.0; | teleline[n] /= 40.0; | ||||
} | } | ||||
// A good minimum amount of pixels to find the telemetry start | |||||
// The minimum rows required to decode a full frame | |||||
if (nrow < 192) { | if (nrow < 192) { | ||||
fprintf(stderr, ERR_TELE_ROW); | fprintf(stderr, ERR_TELE_ROW); | ||||
return(0); | return(0); | ||||
} | } | ||||
// Find the biggest contrast in the telemetry | |||||
max = 0.0; | |||||
mtelestart = 0; | |||||
// Only check the center of the image, where the signal is most likely strongest | |||||
/* Wedge 7 is white and 8 is black, which will have the largest | |||||
* difference in brightness, this will always be in the center of | |||||
* the frame and can thus be used to find the start of the frame | |||||
*/ | |||||
float max = 0; | |||||
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) | // (sum 4px below) / (sum 4px above) | ||||
df = (teleline[n - 4] + teleline[n - 3] + teleline[n - 2] + | |||||
teleline[n - 1]) / (teleline[n] + teleline[n + 1] + | |||||
teleline[n + 2] + teleline[n + 3]); | |||||
// Find the biggest contrast | |||||
df = (teleline[n - 4] + teleline[n - 3] + teleline[n - 2] + teleline[n - 1]) / | |||||
(teleline[n + 0] + teleline[n + 1] + teleline[n + 2] + teleline[n + 3]); | |||||
// Find the maximum difference | |||||
if (df > max) { | if (df > max) { | ||||
mtelestart = n; | mtelestart = n; | ||||
max = df; | max = df; | ||||
} | } | ||||
} | } | ||||
// Calculate relative offset | |||||
telestart = (mtelestart - 64) % 128; | |||||
// Find the start of the first frame | |||||
telestart = (mtelestart - FRAME_LEN/2) % FRAME_LEN; | |||||
// If we cannot find the start of the telemetry or if there is not enough of it | |||||
if (mtelestart < 0 || nrow < telestart + 128) { | |||||
// Make sure that theres at least one full frame in the image | |||||
if (nrow < telestart + FRAME_LEN) { | |||||
fprintf(stderr, ERR_TELE_ROW); | fprintf(stderr, ERR_TELE_ROW); | ||||
return(0); | return(0); | ||||
} | } | ||||
/* 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 each frame | |||||
for (n = telestart, k = 0; n < nrow - FRAME_LEN; n += FRAME_LEN, k++) { | |||||
float *pixelv = prow[n]; | |||||
int j; | int j; | ||||
// Loop through the 16 wedges | |||||
// Turn each wedge into a value | |||||
for (j = 0; j < 16; j++) { | for (j = 0; j < 16; j++) { | ||||
int i; | |||||
// Average the middle 6px | |||||
wedge[j] = 0.0; | wedge[j] = 0.0; | ||||
// Center 6 pixels | |||||
for (i = 1; i < 7; i++){ | |||||
wedge[j] += teleline[(j * 8) + n + i]; | |||||
} | |||||
// Average | |||||
for (int i = 1; i < 7; i++) wedge[j] += teleline[(j * 8) + n + i]; | |||||
wedge[j] /= 6; | wedge[j] /= 6; | ||||
} | } | ||||
// Compute regression on the wedges | |||||
rgcomp(wedge, &(regr[k])); | rgcomp(wedge, &(regr[k])); | ||||
if (k == nrow / 256) { | |||||
int i, l; | |||||
// Read the telemetry values from the middle of the image | |||||
if (k == nrow / (2*FRAME_LEN)) { | |||||
int l; | |||||
// Telemetry calibration | |||||
for (j = 0; j < 16; j++) { | |||||
tele[j] = rgcal(wedge[j], &(regr[k])); | |||||
} | |||||
// Equalise | |||||
for (j = 0; j < 16; j++) tele[j] = rgcal(wedge[j], &(regr[k])); | |||||
// Channel ID | |||||
for (j = 0, max = 10000.0, channel = -1; j < 6; j++) { | |||||
/* Compare the channel ID wedge to the reference | |||||
* wedges, the wedge with the closest match will | |||||
* be the channel ID | |||||
*/ | |||||
float min = 10000; | |||||
for (j = 0; j < 6; j++) { | |||||
float df; | float df; | ||||
df = wedge[15] - wedge[j]; | |||||
df = df * df; | |||||
if (df < max) { | |||||
df = tele[15] - tele[j]; | |||||
df *= df; | |||||
if (df < min) { | |||||
channel = j; | channel = j; | ||||
max = df; | |||||
min = df; | |||||
} | } | ||||
} | } | ||||
/* Cs computation */ | |||||
for (Cs = 0.0, i = 0, j = n; j < n + 128; j++) { | |||||
// Cs computation, still have no idea what this does | |||||
int i; | |||||
for (Cs = 0.0, i = 0, j = n; j < n + FRAME_LEN; j++) { | |||||
double csline; | double csline; | ||||
for (csline = 0.0, l = 3; l < 43; l++) | for (csline = 0.0, l = 3; l < 43; l++) | ||||
csline += prow[n][l + offset -SPC_WIDTH]; | |||||
csline += pixelv[l + offset - SPC_WIDTH]; | |||||
csline /= 40.0; | csline /= 40.0; | ||||
if (csline > 50.0) { | if (csline > 50.0) { | ||||
@@ -220,8 +210,7 @@ int calibrate(float **prow, int nrow, int offset, int contrastBoost) { | |||||
} | } | ||||
nbtele = k; | nbtele = k; | ||||
// Image contrast | |||||
if(contrastBoost) equalise(prow, nrow, offset, telestart, regr); | |||||
if(contrastEqualise) equalise(prow, nrow, offset, width, telestart, regr); | |||||
return(channel + 1); | return(channel + 1); | ||||
} | } | ||||
@@ -237,16 +226,15 @@ typedef struct { | |||||
int ch; | int ch; | ||||
} tempparam; | } tempparam; | ||||
/* temperature compensation for IR channel */ | |||||
static void tempcomp(double t[16], int ch, tempparam * tpr) { | |||||
// IR channel temperature compensation | |||||
static void tempcomp(double t[16], int ch, tempparam *tpr) { | |||||
double Tbb, T[4]; | double Tbb, T[4]; | ||||
double C; | double C; | ||||
int n; | |||||
tpr -> ch = ch - 4; | tpr -> ch = ch - 4; | ||||
/* compute equivalent T black body */ | |||||
for (n = 0; n < 4; n++) { | |||||
// Compute equivalent T blackbody temperature | |||||
for (int n = 0; n < 4; n++) { | |||||
float d0, d1, d2; | float d0, d1, d2; | ||||
C = t[9 + n] * 4.0; | C = t[9 + n] * 4.0; | ||||
@@ -261,15 +249,16 @@ static void tempcomp(double t[16], int ch, tempparam * tpr) { | |||||
Tbb = (T[0] + T[1] + T[2] + T[3]) / 4.0; | Tbb = (T[0] + T[1] + T[2] + T[3]) / 4.0; | ||||
Tbb = satcal[satnum].rad[tpr->ch].A + satcal[satnum].rad[tpr->ch].B * Tbb; | Tbb = satcal[satnum].rad[tpr->ch].A + satcal[satnum].rad[tpr->ch].B * Tbb; | ||||
/* compute radiance Black body */ | |||||
// Compute radiance blackbody | |||||
C = satcal[satnum].rad[tpr->ch].vc; | C = satcal[satnum].rad[tpr->ch].vc; | ||||
tpr->Nbb = c1 * C * C * C / (expm1(c2 * C / Tbb)); | 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; | ||||
tpr->Cb = t[14] * 4.0; | tpr->Cb = t[14] * 4.0; | ||||
} | } | ||||
// IR channel temperature calibration | |||||
static double tempcal(float Ce, tempparam * rgpr) { | static double tempcal(float Ce, tempparam * rgpr) { | ||||
double Nl, Nc, Ns, Ne; | double Nl, Nc, Ns, Ne; | ||||
double T, vc; | double T, vc; | ||||
@@ -286,30 +275,26 @@ static double tempcal(float Ce, tempparam * rgpr) { | |||||
T = c2 * vc / log1p(c1 * vc * vc * vc / Ne); | 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 0-255 for -60'C to +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) { | |||||
// Temperature calibration wrapper | |||||
void temperature(float **prow, int nrow, int channel, int offset) { | |||||
tempparam temp; | tempparam temp; | ||||
int n; | |||||
printf("Temperature... "); | printf("Temperature... "); | ||||
fflush(stdout); | fflush(stdout); | ||||
tempcomp(tele, channel, &temp); | tempcomp(tele, channel, &temp); | ||||
for (n = 0; n < nrow; n++) { | |||||
float *pixelv; | |||||
int i; | |||||
pixelv = prow[n]; | |||||
for (i = 0; i < CH_WIDTH; i++) { | |||||
float pv; | |||||
for (int n = 0; n < nrow; n++) { | |||||
float *pixelv = prow[n]; | |||||
pv = tempcal(pixelv[i + offset], &temp); | |||||
for (int i = 0; i < CH_WIDTH; i++) { | |||||
float pv = tempcal(pixelv[i + offset], &temp); | |||||
pv = CLIP(pv, 0, 255); | pv = CLIP(pv, 0, 255); | ||||
pixelv[i + offset] = pv; | pixelv[i + offset] = pv; | ||||
@@ -1,22 +1,19 @@ | |||||
/* | |||||
* Aptdec | |||||
* Copyright (c) 2004-2005 by Thierry Leconte (F4DWV) | |||||
/* | |||||
* This file is part of Aptdec. | |||||
* Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019 | |||||
* | * | ||||
* $Id$ | |||||
* Aptdec is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation, either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | * | ||||
* This library is free software; you can redistribute it and/or modify | |||||
* it under the terms of the GNU Library General Public License as | |||||
* published by the Free Software Foundation; either version 2 of | |||||
* the License, or (at your option) any later version. | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | * | ||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU Library General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Library General Public | |||||
* License along with this library; if not, write to the Free Software | |||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | |||||
* | * | ||||
*/ | */ | ||||
@@ -24,21 +21,17 @@ | |||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <getopt.h> | #include <getopt.h> | ||||
#include <libgen.h> | #include <libgen.h> | ||||
#include <math.h> | #include <math.h> | ||||
#include <sndfile.h> | #include <sndfile.h> | ||||
#include <png.h> | #include <png.h> | ||||
#include "messages.h" | #include "messages.h" | ||||
#include "offsets.h" | #include "offsets.h" | ||||
#include "colorpalette.h" | |||||
#include "palette.h" | |||||
extern int getpixelrow(float *pixelv); | extern int getpixelrow(float *pixelv); | ||||
extern int init_dsp(double F);; | |||||
extern int init_dsp(double F); | |||||
static SNDFILE *inwav; | static SNDFILE *inwav; | ||||
@@ -73,7 +66,7 @@ static int initsnd(char *filename) { | |||||
return(1); | return(1); | ||||
} | } | ||||
// Get a sample from the wave file | |||||
// Get samples 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); | ||||
} | } | ||||
@@ -84,13 +77,20 @@ static png_text text_ptr[] = { | |||||
{PNG_TEXT_COMPRESSION_NONE, "Description", "NOAA satellite image", 20} | {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, int croptele, int layered) { | |||||
// TODO: this function needs to be tidied up | |||||
/* Effects | |||||
* 0 - Nothing | |||||
* 1 - Crop telemetry | |||||
* 2 - False color | |||||
* 3 - Layered | |||||
*/ | |||||
static int ImageOut(char *filename, char *chid, float **prow, int nrow, int width, int offset, png_color *palette, int effects) { | |||||
FILE *pngfile; | FILE *pngfile; | ||||
png_infop info_ptr; | png_infop info_ptr; | ||||
png_structp png_ptr; | png_structp png_ptr; | ||||
// Reduce the width of the image to componsate for the missing telemetry | // Reduce the width of the image to componsate for the missing telemetry | ||||
if(croptele) width -= TOTAL_TELE; | |||||
if(effects == 1) width -= TOTAL_TELE; | |||||
// Initalise the PNG writer | // 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); | ||||
@@ -107,7 +107,14 @@ static int ImageOut(char *filename, char *chid, float **prow, int nrow, int widt | |||||
return(0); | return(0); | ||||
} | } | ||||
if(palette == NULL) { | |||||
extern void falsecolor(float vis, float temp, float *r, float *g, float *b); | |||||
if(effects == 2){ | |||||
// 8 bit RGB image | |||||
png_set_IHDR(png_ptr, info_ptr, width, nrow, | |||||
8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, | |||||
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); | |||||
}else if(palette == NULL) { | |||||
// Greyscale image | // 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, | ||||
@@ -125,8 +132,13 @@ 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); | |||||
if(effects == 2){ | |||||
printf("Computing false color & writing: %s", filename); | |||||
}else{ | |||||
printf("Writing %s", filename); | |||||
} | |||||
fflush(stdout); | fflush(stdout); | ||||
pngfile = fopen(filename, "wb"); | pngfile = fopen(filename, "wb"); | ||||
if (pngfile == NULL) { | if (pngfile == NULL) { | ||||
fprintf(stderr, ERR_FILE_WRITE, filename); | fprintf(stderr, ERR_FILE_WRITE, filename); | ||||
@@ -136,14 +148,14 @@ static int ImageOut(char *filename, char *chid, float **prow, int nrow, int widt | |||||
png_write_info(png_ptr, info_ptr); | png_write_info(png_ptr, info_ptr); | ||||
for (int n = 0; n < nrow; n++) { | for (int n = 0; n < nrow; n++) { | ||||
float *pixelv; | |||||
png_byte pixel[2*IMG_WIDTH]; | |||||
png_color pix[width]; | |||||
png_byte pixel[width]; | |||||
pixelv = prow[n]; | |||||
float *pixelv = prow[n]; | |||||
int f = 0; | int f = 0; | ||||
for (int i = 0; i < width; i++) { | for (int i = 0; i < width; i++) { | ||||
// Skip parts of the image that are telemtry | |||||
if(croptele){ | |||||
// Skip parts of the image that are telemetry | |||||
if(effects == 1){ | |||||
switch (i) { | switch (i) { | ||||
case 0: | case 0: | ||||
f += SYNC_WIDTH + SPC_WIDTH; | f += SYNC_WIDTH + SPC_WIDTH; | ||||
@@ -156,128 +168,67 @@ static int ImageOut(char *filename, char *chid, float **prow, int nrow, int widt | |||||
} | } | ||||
} | } | ||||
if(layered){ | |||||
// Layered image, overlay highlights in channel B over channel A | |||||
if(effects == 2){ | |||||
float r = 0, g = 0, b = 0; | |||||
falsecolor(pixelv[i+CHA_OFFSET], pixelv[i+CHB_OFFSET], &r, &g, &b); | |||||
pix[i].red = r; | |||||
pix[i].green = g; | |||||
pix[i].blue = b; | |||||
}else if(effects == 3){ | |||||
// Layered image, overlay clouds in channel B over channel A | |||||
float cloud = CLIP(pixelv[i+CHB_OFFSET]-141, 0, 255)/114; | float cloud = CLIP(pixelv[i+CHB_OFFSET]-141, 0, 255)/114; | ||||
pixel[i] = MCOMPOSITE(255, cloud, pixelv[i+CHA_OFFSET], 1); | pixel[i] = MCOMPOSITE(255, cloud, pixelv[i+CHA_OFFSET], 1); | ||||
}else{ | }else{ | ||||
pixel[i] = pixelv[i + offset + f]; | pixel[i] = pixelv[i + offset + f]; | ||||
} | } | ||||
} | } | ||||
png_write_row(png_ptr, pixel); | |||||
} | |||||
png_write_end(png_ptr, info_ptr); | |||||
fclose(pngfile); | |||||
printf("\nDone\n"); | |||||
png_destroy_write_struct(&png_ptr, &info_ptr); | |||||
return(1); | |||||
} | |||||
static int ImageRGBOut(char *filename, float **prow, int nrow) { | |||||
FILE *pngfile; | |||||
png_infop info_ptr; | |||||
png_structp png_ptr; | |||||
extern void falsecolor(float vis, float temp, float *r, float *g, float *b); | |||||
// Initalise the PNG writer | |||||
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | |||||
if (!png_ptr) { | |||||
fprintf(stderr, ERR_PNG_WRITE); | |||||
return(1); | |||||
} | |||||
info_ptr = png_create_info_struct(png_ptr); | |||||
if (!info_ptr) { | |||||
png_destroy_write_struct(&png_ptr, (png_infopp) NULL); | |||||
fprintf(stderr, ERR_PNG_WRITE); | |||||
return(1); | |||||
} | |||||
png_set_IHDR(png_ptr, info_ptr, CH_WIDTH, nrow, | |||||
8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, | |||||
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); | |||||
png_set_pHYs(png_ptr, info_ptr, 4000, 4000, PNG_RESOLUTION_METER); | |||||
text_ptr[1].text = "False Color"; | |||||
text_ptr[1].text_length = strlen(text_ptr[1].text); | |||||
png_set_text(png_ptr, info_ptr, text_ptr, 3); | |||||
printf("Computing false color & writing: %s", filename); | |||||
fflush(stdout); | |||||
pngfile = fopen(filename, "wb"); | |||||
if (pngfile == NULL) { | |||||
fprintf(stderr, ERR_FILE_WRITE, filename); | |||||
return(1); | |||||
} | |||||
png_init_io(png_ptr, pngfile); | |||||
png_write_info(png_ptr, info_ptr); | |||||
for (int n = 0; n < nrow ; n++) { | |||||
png_color pix[CH_WIDTH]; | |||||
float *pixelc; | |||||
pixelc = prow[n]; | |||||
for (int i = 0; i < CH_WIDTH - 1; i++) { | |||||
float r = 0, g = 0, b = 0; | |||||
// False color computation | |||||
falsecolor(pixelc[i+CHA_OFFSET], pixelc[i+CHB_OFFSET], &r, &g, &b); | |||||
pix[i].red = r; | |||||
pix[i].green = g; | |||||
pix[i].blue = b; | |||||
if(effects == 2){ | |||||
png_write_row(png_ptr, (png_bytep) pix); | |||||
}else{ | |||||
png_write_row(png_ptr, pixel); | |||||
} | } | ||||
png_write_row(png_ptr, (png_bytep) pix); | |||||
} | } | ||||
png_write_end(png_ptr, info_ptr); | png_write_end(png_ptr, info_ptr); | ||||
fclose(pngfile); | fclose(pngfile); | ||||
printf("\nDone\n"); | printf("\nDone\n"); | ||||
png_destroy_write_struct(&png_ptr, &info_ptr); | png_destroy_write_struct(&png_ptr, &info_ptr); | ||||
return(0); | |||||
return(1); | |||||
} | } | ||||
// Distribution of values between Channel A and Channel B | |||||
static void Distrib(char *filename, float **prow, int nrow) { | |||||
unsigned int distrib[256][256]; | |||||
int n; | |||||
int x, y; | |||||
// Outputs a image with the value distribution between channel A and B | |||||
static void distrib(char *filename, float **prow, int nrow) { | |||||
float *distrib[256]; | |||||
int max = 0; | int max = 0; | ||||
FILE *df; | |||||
for(y = 0; y < 256; y++) | |||||
for(x = 0; x < 256; x++) | |||||
distrib[y][x] = 0; | |||||
for(n = 0; n < nrow; n++) { | |||||
float *pixelv; | |||||
int i; | |||||
// Assign memory | |||||
for(int i = 0; i < 256; i++) | |||||
distrib[i] = (float *) malloc(sizeof(float) * 256); | |||||
pixelv = prow[n]; | |||||
for(i = 0; i < CH_WIDTH; i++) { | |||||
y = (int)(pixelv[i + CHA_OFFSET]); | |||||
x = (int)(pixelv[i + CHB_OFFSET]); | |||||
for(int n = 0; n < nrow; n++) { | |||||
float *pixelv = prow[n]; | |||||
for(int i = 0; i < CH_WIDTH; i++) { | |||||
int y = (int)(pixelv[i + CHA_OFFSET]); | |||||
int x = (int)(pixelv[i + CHB_OFFSET]); | |||||
distrib[y][x] += 1; | distrib[y][x] += 1; | ||||
if(distrib[y][x] > max) max=distrib[y][x]; | |||||
if(distrib[y][x] > max) max = distrib[y][x]; | |||||
} | } | ||||
} | } | ||||
df = fopen(filename,"w"); | |||||
printf("Writing %s\n",filename); | |||||
// Scale to 0-255 | |||||
for(int x = 0; x < 256; x++) | |||||
for(int y = 0; y < 256; y++) | |||||
distrib[y][x] = distrib[y][x] / max * 255; | |||||
fprintf(df,"P2\n#max %d\n",max); | |||||
fprintf(df,"256 256\n255\n"); | |||||
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)); | |||||
fclose(df); | |||||
ImageOut(filename, "Value distribution", distrib, 256, 256, 0, NULL, 0); | |||||
} | } | ||||
extern int calibrate(float **prow, int nrow, int offset, int contrastBoost); | |||||
extern void Temperature(float **prow, int nrow, int ch, int offset); | |||||
extern int calibrate(float **prow, int nrow, int offset, int width, int contrastEqualise); | |||||
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 readfcconf(char *file); | extern void readfcconf(char *file); | ||||
extern int optind; | extern int optind; | ||||
@@ -290,12 +241,12 @@ static void usage(void) { | |||||
printf("Aptdec [options] audio files ...\n" | printf("Aptdec [options] audio files ...\n" | ||||
"Options:\n" | "Options:\n" | ||||
" -e [c|t] Enhancements\n" | " -e [c|t] Enhancements\n" | ||||
" c: Contrast enhance\n" | |||||
" c: Contrast equalise\n" | |||||
" t: Crop telemetry\n" | " t: Crop telemetry\n" | ||||
" -i [r|a|b|c|t] Output image type\n" | " -i [r|a|b|c|t] Output image type\n" | ||||
" r: Raw\n" | " r: Raw\n" | ||||
" a: A channel\n" | |||||
" b: B channel\n" | |||||
" a: Channel A\n" | |||||
" b: Channel B\n" | |||||
" c: False color\n" | " c: False color\n" | ||||
" t: Temperature\n" | " t: Temperature\n" | ||||
" l: Layered\n" | " l: Layered\n" | ||||
@@ -310,23 +261,28 @@ int main(int argc, char **argv) { | |||||
char name[500]; | char name[500]; | ||||
char pngdirname[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"; | |||||
// Default to a raw image, with equalization and cropped telemetry | |||||
char imgopt[20] = "r"; | |||||
char enchancements[20] = "ct"; | char enchancements[20] = "ct"; | ||||
// Image buffer | // Image buffer | ||||
float *prow[3000]; | float *prow[3000]; | ||||
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" }; | |||||
// Mapping between telemetry wedge value and channel | |||||
static struct { | |||||
char *id[7]; | |||||
char *name[7]; | |||||
} ch = { | |||||
{ "?", "1", "2", "3A", "4", "5", "3B" }, | |||||
{ "unknown", "visble", "near-infrared", "mid-infrared", "thermal-infrared", "thermal-infrared", "mid-infrared" } | |||||
}; | |||||
// The active sensor in each channel | // The active sensor in each channel | ||||
int chA, chB; | int chA, chB; | ||||
// Print version | // Print version | ||||
printf("%s\n", VERSION); | |||||
printf(VERSION"\n"); | |||||
// Print usage if there are no arguments | // Print usage if there are no arguments | ||||
if(argc == 1) | if(argc == 1) | ||||
@@ -345,7 +301,7 @@ int main(int argc, char **argv) { | |||||
break; | break; | ||||
// Output image type | // Output image type | ||||
case 'i': | case 'i': | ||||
strcpy(imgopt, optarg); | |||||
strncpy(imgopt, optarg, 20); | |||||
break; | break; | ||||
// Satellite number (for calibration) | // Satellite number (for calibration) | ||||
case 's': | case 's': | ||||
@@ -358,7 +314,7 @@ int main(int argc, char **argv) { | |||||
break; | break; | ||||
// Enchancements | // Enchancements | ||||
case 'e': | case 'e': | ||||
strcpy(enchancements, optarg); | |||||
strncpy(enchancements, optarg, 20); | |||||
break; | break; | ||||
default: | default: | ||||
usage(); | usage(); | ||||
@@ -369,10 +325,7 @@ int main(int argc, char **argv) { | |||||
usage(); | usage(); | ||||
} | } | ||||
// Pull this in from filtercoeff.h | |||||
extern float Sync[32]; | |||||
// Loop through the provided files | |||||
// Process the provided files | |||||
for (; optind < argc; optind++) { | for (; optind < argc; optind++) { | ||||
chA = chB = 0; | chA = chB = 0; | ||||
@@ -397,100 +350,91 @@ int main(int argc, char **argv) { | |||||
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]; | |||||
// 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); | |||||
printf("Row: %d\r", nrow); | |||||
fflush(stdout); | fflush(stdout); | ||||
} | } | ||||
printf("\nTotal rows: %d\n", nrow); | printf("\nTotal rows: %d\n", nrow); | ||||
sf_close(inwav); | sf_close(inwav); | ||||
// Layered images require contrast enhancements | |||||
int contrastBoost = CONTAINS(enchancements, 'c') || CONTAINS(imgopt, 'l') || CONTAINS(imgopt, 'c'); | |||||
// Layered & false color images both need brightness equalization | |||||
int contrastEqualise = CONTAINS(enchancements, 'c') || CONTAINS(imgopt, 'l') || CONTAINS(imgopt, 'c'); | |||||
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]); | |||||
chA = calibrate(prow, nrow, CHA_OFFSET, CH_WIDTH, 0); | |||||
chB = calibrate(prow, nrow, CHB_OFFSET, CH_WIDTH, 0); | |||||
printf("Channel A: %s (%s)\n", ch.id[chA], ch.name[chA]); | |||||
printf("Channel B: %s (%s)\n", ch.id[chB], ch.name[chB]); | |||||
// Temperature | // Temperature | ||||
if (CONTAINS(imgopt, 't') && chB >= 4) { | if (CONTAINS(imgopt, 't') && chB >= 4) { | ||||
Temperature(prow, nrow, chB, CHB_OFFSET); | |||||
temperature(prow, nrow, chB, CHB_OFFSET); | |||||
sprintf(pngfilename, "%s/%s-t.png", pngdirname, name); | sprintf(pngfilename, "%s/%s-t.png", pngdirname, name); | ||||
ImageOut(pngfilename, "Temperature", prow, nrow, CH_WIDTH, CHB_OFFSET, (png_color*)TempPalette, 0, 0); | |||||
ImageOut(pngfilename, "Temperature", prow, nrow, CH_WIDTH, CHB_OFFSET, (png_color*)TempPalette, 0); | |||||
} | } | ||||
// We have to run the contrast enhance here because the temperature calibration requires real data | |||||
// Yes, this requires running a large chunk of code again, but this will be addressed in the future | |||||
chA = calibrate(prow, nrow, CHA_OFFSET, contrastBoost); | |||||
chB = calibrate(prow, nrow, CHB_OFFSET, contrastBoost); | |||||
// Run the contrast equalise here because the temperature calibration requires raw data | |||||
if(contrastEqualise) | |||||
calibrate(prow, nrow, CHA_OFFSET, CH_WIDTH+TELE_WIDTH+SYNC_WIDTH+SPC_WIDTH+CH_WIDTH, 1); | |||||
// Layered | // Layered | ||||
if (CONTAINS(imgopt, 'l')){ | if (CONTAINS(imgopt, 'l')){ | ||||
sprintf(pngfilename, "%s/%s-l.png", pngdirname, name); | |||||
ImageOut(pngfilename, "Layered", prow, nrow, CH_WIDTH, 0, NULL, 0, 1); | |||||
if(chA == 1){ | |||||
sprintf(pngfilename, "%s/%s-l.png", pngdirname, name); | |||||
ImageOut(pngfilename, "Layered", prow, nrow, CH_WIDTH, 0, NULL, 3); | |||||
}else{ | |||||
fprintf(stderr, "Lacking channels required for generting a layered image.\n"); | |||||
} | |||||
} | } | ||||
// Raw image | // Raw image | ||||
if (CONTAINS(imgopt, 'r')) { | if (CONTAINS(imgopt, 'r')) { | ||||
int croptele = CONTAINS(enchancements, 't'); | int croptele = CONTAINS(enchancements, 't'); | ||||
char channelstr[45]; | char channelstr[45]; | ||||
sprintf(channelstr, "%s (%s) & %s (%s)", chid[chA], chname[chA], chid[chB], chname[chB]); | |||||
sprintf(channelstr, "%s (%s) & %s (%s)", ch.id[chA], ch.name[chA], ch.id[chB], ch.name[chB]); | |||||
sprintf(pngfilename, "%s/%s-r.png", pngdirname, name); | sprintf(pngfilename, "%s/%s-r.png", pngdirname, name); | ||||
ImageOut(pngfilename, channelstr, prow, nrow, IMG_WIDTH, 0, NULL, croptele, 0); | |||||
ImageOut(pngfilename, channelstr, prow, nrow, IMG_WIDTH, 0, NULL, croptele ? 1 : 0); | |||||
} | } | ||||
// Channel A | // Channel A | ||||
if (CONTAINS(imgopt, 'a')) { | if (CONTAINS(imgopt, 'a')) { | ||||
char channelstr[21]; | char channelstr[21]; | ||||
sprintf(channelstr, "%s (%s)", chid[chA], chname[chA]); | |||||
sprintf(channelstr, "%s (%s)", ch.id[chA], ch.name[chA]); | |||||
sprintf(pngfilename, "%s/%s-%s.png", pngdirname, name, chid[chA]); | |||||
ImageOut(pngfilename, channelstr, prow, nrow, CH_WIDTH, CHA_OFFSET, NULL, 0, 0); | |||||
sprintf(pngfilename, "%s/%s-%s.png", pngdirname, name, ch.id[chA]); | |||||
ImageOut(pngfilename, channelstr, prow, nrow, CH_WIDTH, CHA_OFFSET, NULL, 0); | |||||
} | } | ||||
// Channel B | // Channel B | ||||
if (CONTAINS(imgopt, 'b')) { | if (CONTAINS(imgopt, 'b')) { | ||||
char channelstr[21]; | char channelstr[21]; | ||||
sprintf(channelstr, "%s (%s)", chid[chB], chname[chB]); | |||||
sprintf(channelstr, "%s (%s)", ch.id[chB], ch.name[chB]); | |||||
sprintf(pngfilename, "%s/%s-%s.png", pngdirname, name, chid[chB]); | |||||
ImageOut(pngfilename, channelstr, prow, nrow, CH_WIDTH , CHB_OFFSET, NULL, 0, 0); | |||||
sprintf(pngfilename, "%s/%s-%s.png", pngdirname, name, ch.id[chB]); | |||||
ImageOut(pngfilename, channelstr, prow, nrow, CH_WIDTH , CHB_OFFSET, NULL, 0); | |||||
} | } | ||||
// Distribution map | |||||
// Distribution image | |||||
if (CONTAINS(imgopt, 'd')) { | if (CONTAINS(imgopt, 'd')) { | ||||
sprintf(pngfilename, "%s/%s-d.pnm", pngdirname, name); | |||||
Distrib(pngfilename, prow, nrow); | |||||
sprintf(pngfilename, "%s/%s-d.png", pngdirname, name); | |||||
distrib(pngfilename, prow, nrow); | |||||
} | } | ||||
// False color image | // False color image | ||||
if(CONTAINS(imgopt, 'c')){ | if(CONTAINS(imgopt, 'c')){ | ||||
if (chA == 2 && chB >= 4) { // Normal false color | if (chA == 2 && chB >= 4) { // Normal false color | ||||
sprintf(pngfilename, "%s/%s-c.png", pngdirname, name); | sprintf(pngfilename, "%s/%s-c.png", pngdirname, name); | ||||
ImageRGBOut(pngfilename, prow, nrow); | |||||
} else if (chA == 1 && chB == 2) { // GVI (global vegetation index) false color | |||||
//ImageRGBOut(pngfilename, prow, nrow); | |||||
ImageOut(pngfilename, "False Color", prow, nrow, CH_WIDTH, 0, NULL, 2); | |||||
} else if (chB == 2) { // GVI (global vegetation index) false color | |||||
Ngvi(prow, nrow); | Ngvi(prow, nrow); | ||||
sprintf(pngfilename, "%s/%s-c.png", pngdirname, name); | sprintf(pngfilename, "%s/%s-c.png", pngdirname, name); | ||||
ImageOut(pngfilename, "GVI False Color", prow, nrow, CH_WIDTH, CHB_OFFSET, (png_color*)GviPalette, 0, 0); | |||||
ImageOut(pngfilename, "GVI False Color", prow, nrow, CH_WIDTH, CHB_OFFSET, (png_color*)GviPalette, 0); | |||||
} else { | } else { | ||||
fprintf(stderr, "Lacking channels required for false color computation.\n"); | |||||
fprintf(stderr, "Lacking channels required for generating a false color image.\n"); | |||||
} | } | ||||
} | } | ||||
// Free the allocated memory | |||||
for(int i = 0; i < 3000; i++) free(prow[i]); | |||||
} | } | ||||
exit(0); | exit(0); | ||||
@@ -1,6 +1,25 @@ | |||||
/* | |||||
* This file is part of Aptdec. | |||||
* Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019 | |||||
* | |||||
* Aptdec is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation, either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | |||||
* | |||||
*/ | |||||
#define ERR_FILE_WRITE "Could not open %s for writing\n" | #define ERR_FILE_WRITE "Could not open %s for writing\n" | ||||
#define ERR_FILE_READ "Could not open %s for reading\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_WRITE "Could not create a PNG write struct\n" | ||||
#define ERR_PNG_INFO "Could not create a PNG info 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 ERR_TELE_ROW "Telemetry decoding error, not enough rows.\n" | ||||
#define VERSION "Aptdec CVS version (c) 2004-2005 Thierry Leconte F4DWV" | |||||
#define VERSION "Aptdec; copyright (c) 2004-2009 Thierry Leconte F4DWV, Xerbo (xerbo@protonmail.com) 2019" |
@@ -1,7 +1,27 @@ | |||||
/* | |||||
* This file is part of Aptdec. | |||||
* Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019 | |||||
* | |||||
* Aptdec is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation, either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | |||||
* | |||||
*/ | |||||
#define SYNC_WIDTH 39 | #define SYNC_WIDTH 39 | ||||
#define SPC_WIDTH 47 | #define SPC_WIDTH 47 | ||||
#define TELE_WIDTH 45 | #define TELE_WIDTH 45 | ||||
#define CH_WIDTH 909 | #define CH_WIDTH 909 | ||||
#define FRAME_LEN 128 | |||||
#define CH_OFFSET (SYNC_WIDTH+SPC_WIDTH+CH_WIDTH+TELE_WIDTH) | #define CH_OFFSET (SYNC_WIDTH+SPC_WIDTH+CH_WIDTH+TELE_WIDTH) | ||||
#define IMG_WIDTH 2080 | #define IMG_WIDTH 2080 | ||||
#define CHA_OFFSET (SYNC_WIDTH+SPC_WIDTH) | #define CHA_OFFSET (SYNC_WIDTH+SPC_WIDTH) | ||||
@@ -1,3 +1,22 @@ | |||||
/* | |||||
* This file is part of Aptdec. | |||||
* Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019 | |||||
* | |||||
* Aptdec is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation, either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | |||||
* | |||||
*/ | |||||
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" |
@@ -1,3 +1,22 @@ | |||||
/* | |||||
* This file is part of Aptdec. | |||||
* Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019 | |||||
* | |||||
* Aptdec is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU General Public License as published by | |||||
* the Free Software Foundation, either version 2 of the License, or | |||||
* (at your option) any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU General Public License | |||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | |||||
* | |||||
*/ | |||||
const struct { | const struct { | ||||
float d[4][3]; | float d[4][3]; | ||||
struct { | struct { | ||||