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 | |||
of this license document, but changing it is not allowed. | |||
Preamble | |||
Preamble | |||
The licenses for most software are designed to take away your | |||
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 | |||
Foundation's software and to any other program whose authors commit to | |||
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. | |||
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 | |||
modification follow. | |||
GNU GENERAL PUBLIC LICENSE | |||
GNU GENERAL PUBLIC LICENSE | |||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |||
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 | |||
does not normally print such an announcement, your work based on | |||
the Program is not required to print an announcement.) | |||
These requirements apply to the modified work as a whole. If | |||
identifiable sections of that work are not derived from the Program, | |||
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 | |||
distribution of the source code, even though third parties are not | |||
compelled to copy the source along with the object code. | |||
4. You may not copy, modify, sublicense, or distribute the Program | |||
except as expressly provided under this License. Any attempt | |||
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 | |||
be a consequence of the rest of this License. | |||
8. If the distribution and/or use of the Program is restricted in | |||
certain countries either by patents or by copyrighted interfaces, the | |||
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 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 | |||
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 | |||
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 | |||
INCLUDES = -I. | |||
CFLAGS = -O3 -Wall $(INCLUDES) | |||
@@ -7,7 +7,7 @@ OBJS = main.o image.o dsp.o filter.o reg.o fcolor.o | |||
aptdec: $(OBJS) | |||
$(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 | |||
filter.o: filter.c filter.h | |||
image.o: image.c satcal.h offsets.h messages.h | |||
@@ -1,6 +1,6 @@ | |||
![Aptdec logo](textlogo.png) | |||
Thierry Leconte F4DWV (c) 2004-2009 | |||
Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019 | |||
## Description | |||
@@ -89,14 +89,14 @@ Image names are `audiofile-x.png`, where `x` is: | |||
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 | |||
## Example | |||
`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 | |||
@@ -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 <stdio.h> | |||
#include <string.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 "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 | |||
// Block size | |||
#define BLKIN 1024 | |||
#define Fc 2400.0 | |||
@@ -48,6 +41,8 @@ extern int getsample(float *inbuff, int nb); | |||
#define RSMULT 15 | |||
#define Fi (Fp * RSMULT) | |||
extern int getsample(float *inbuff, int count); | |||
static double Fe; | |||
static double offset = 0.0; | |||
@@ -56,7 +51,7 @@ static double FreqLine = 1.0; | |||
static double FreqOsc; | |||
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) { | |||
if(F > Fi) return(1); | |||
if(F < Fp) return(-1); | |||
@@ -64,13 +59,15 @@ int init_dsp(double F) { | |||
K1 = DFc / Fe; | |||
K2 = K1 * K1 / 2.0; | |||
// Number of samples for each cycle | |||
FreqOsc = Fc / Fe; | |||
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) { | |||
double angle, r; | |||
int s; | |||
@@ -99,8 +96,11 @@ static inline double Phase(double I, double Q) { | |||
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) { | |||
// PLL coefficient | |||
static double PhaseOsc = 0.0; | |||
@@ -133,17 +133,16 @@ static double pll(double I, double Q) { | |||
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 int idxin = 0; | |||
static int nin = 0; | |||
int n; | |||
for (n = 0; n < nb; n++) { | |||
for (int n = 0; n < count; n++) { | |||
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) { | |||
// Number of samples read | |||
int res; | |||
@@ -153,50 +152,52 @@ static int getamp(double *ambuff, int nb) { | |||
// Read some samples | |||
res = getsample(&(inbuff[nin]), BLKIN - nin); | |||
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) | |||
return(n); | |||
} | |||
// Process read samples into a brightness value | |||
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 | |||
static double ambuff[BLKAMP]; | |||
static double ampbuff[BLKAMP]; | |||
static int nam = 0; | |||
static int idxam = 0; | |||
int n, m; | |||
double mult; | |||
// Sub-pixel offset | |||
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; | |||
if (nam < m) { | |||
int res; | |||
memmove(ambuff, &(ambuff[idxam]), nam * sizeof(double)); | |||
memmove(ampbuff, &(ampbuff[idxam]), nam * sizeof(double)); | |||
idxam = 0; | |||
res = getamp(&(ambuff[nam]), BLKAMP - nam); | |||
res = getamp(&(ampbuff[nam]), BLKAMP - nam); | |||
nam += res; | |||
if (nam < m) | |||
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; | |||
offset = shift * mult + offset - RSMULT; | |||
@@ -204,44 +205,45 @@ int getpixelv(float *pvbuff, int nb) { | |||
idxam += 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) { | |||
// Create an array for this row | |||
static float pixels[PixelLine + SyncFilterLen]; | |||
static int npv = 0; | |||
static int npv; | |||
static int synced = 0; | |||
static double max = 0.0; | |||
static double max = 0; | |||
double corr, ecorr, lcorr; | |||
int res; | |||
// Move the row buffer into the the image buffer | |||
if (npv > 0) | |||
memmove(pixelv, pixels, npv * sizeof(float)); | |||
// Get the sync line | |||
if (npv < SyncFilterLen + 2) { | |||
res = getpixelv(&(pixelv[npv]), SyncFilterLen + 2 - npv); | |||
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); | |||
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); | |||
// Maximum acceptable offset | |||
// The point in which the pixel offset is recalculated | |||
if (corr < 0.75 * max) { | |||
synced = 0; | |||
FreqLine = 1.0; | |||
} | |||
max = corr; | |||
if (synced < 8) { | |||
if (synced < 8) { | |||
int mshift; | |||
if (npv < PixelLine + SyncFilterLen) { | |||
@@ -251,9 +253,9 @@ int getpixelrow(float *pixelv) { | |||
return(0); | |||
} | |||
// Shift this line until we see the best results | |||
// Test every possible position until we get the best result | |||
mshift = 0; | |||
for (int shift = 1; shift < PixelLine; shift++) { | |||
for (int shift = 0; shift < PixelLine; shift++) { | |||
double corr; | |||
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)); | |||
npv -= mshift; | |||
synced = 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) { | |||
res = getpixelv(&(pixelv[npv]), PixelLine - npv); | |||
npv += res; | |||
// If we fail this then exit with 0 (which breaks the loop at main.c:338) | |||
if (npv < PixelLine) | |||
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) { | |||
npv = 0; | |||
} else { // Move the pixel build buffer to the output buffer | |||
} else { | |||
memmove(pixels, &(pixelv[PixelLine]), (npv - PixelLine) * sizeof(float)); | |||
npv -= PixelLine; | |||
} | |||
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 <math.h> | |||
#include "offsets.h" | |||
@@ -18,8 +37,8 @@ static struct { | |||
int Cloudthreshold; | |||
int Cloudintensity; | |||
} fcinfo = { | |||
{28, 44, 95, 1}, | |||
{23, 78, 37, 1}, | |||
{28, 44, 95, 1}, | |||
{23, 78, 37, 1}, | |||
{240, 250, 255, 1}, | |||
50, | |||
0.5, | |||
@@ -89,9 +108,7 @@ void Ngvi(float **prow, int nrow) { | |||
fflush(stdout); | |||
for (int n = 0; n < nrow; n++) { | |||
float *pixelv; | |||
pixelv = prow[n]; | |||
float *pixelv = prow[n]; | |||
for (int i = 0; i < CH_WIDTH; i++) { | |||
float pv; | |||
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> | |||
// Sum of a matrix multiplication of 2 arrays | |||
#include "filter.h" | |||
// Finite impulse response | |||
float fir(float *buff, const float *coeff, const int len) { | |||
double r; | |||
@@ -33,22 +32,24 @@ float fir(float *buff, const float *coeff, const int len) { | |||
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) { | |||
double i, q; | |||
i = q = 0.0; | |||
for (int k = 0; k < len; k++) { | |||
q += buff[2*k] * coeff[k]; | |||
// Average out the I samples, which gives us the DC offset | |||
i += buff[2*k]; | |||
} | |||
// 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 | |||
/* 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) { | |||
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 | |||
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.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 }; | |||
// Length of Sync | |||
#define SyncFilterLen 32 | |||
// 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, | |||
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 | |||
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, | |||
@@ -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]; | |||
} 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) { | |||
//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 }; | |||
extern void polyreg(const int m, const int n, const double x[], const double y[], double c[]); | |||
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; | |||
int i; | |||
int i; | |||
for (i = 0, y = 0.0, p = 1.0; i < REGORDER + 1; i++) { | |||
y += rgpr->cf[i] * p; | |||
p = p * x; | |||
@@ -56,35 +56,39 @@ static double tele[16]; | |||
static double Cs; | |||
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) | |||
k = nbtele - 1; | |||
kof = (n - telestart) % 128; | |||
kof = (n - telestart) % FRAME_LEN; | |||
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; | |||
pv = rgcal(pv, &(regr[k])) * (64 + kof) / FRAME_LEN + | |||
rgcal(pv, &(regr[k - 1])) * (64 - kof) / FRAME_LEN; | |||
} | |||
} 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 = 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 wedge[16]; | |||
rgparam regr[30]; | |||
int n; | |||
int k; | |||
int mtelestart, telestart; | |||
int n, k; | |||
int mtelestart = 0, telestart; | |||
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++) { | |||
int i; | |||
float *pixelv = prow[n]; | |||
// Average the center 40px | |||
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; | |||
} | |||
// A good minimum amount of pixels to find the telemetry start | |||
// The minimum rows required to decode a full frame | |||
if (nrow < 192) { | |||
fprintf(stderr, ERR_TELE_ROW); | |||
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++) { | |||
float df; | |||
// Calculate the contrast, in other words | |||
// (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) { | |||
mtelestart = n; | |||
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); | |||
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; | |||
// Loop through the 16 wedges | |||
// Turn each wedge into a value | |||
for (j = 0; j < 16; j++) { | |||
int i; | |||
// Average the middle 6px | |||
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; | |||
} | |||
// Compute regression on the wedges | |||
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; | |||
df = wedge[15] - wedge[j]; | |||
df = df * df; | |||
if (df < max) { | |||
df = tele[15] - tele[j]; | |||
df *= df; | |||
if (df < min) { | |||
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; | |||
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; | |||
if (csline > 50.0) { | |||
@@ -220,8 +210,7 @@ int calibrate(float **prow, int nrow, int offset, int contrastBoost) { | |||
} | |||
nbtele = k; | |||
// Image contrast | |||
if(contrastBoost) equalise(prow, nrow, offset, telestart, regr); | |||
if(contrastEqualise) equalise(prow, nrow, offset, width, telestart, regr); | |||
return(channel + 1); | |||
} | |||
@@ -237,16 +226,15 @@ typedef struct { | |||
int ch; | |||
} 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 C; | |||
int n; | |||
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; | |||
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 = 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; | |||
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->Cb = t[14] * 4.0; | |||
} | |||
// IR channel temperature calibration | |||
static double tempcal(float Ce, tempparam * rgpr) { | |||
double Nl, Nc, Ns, Ne; | |||
double T, vc; | |||
@@ -286,30 +275,26 @@ static double tempcal(float Ce, tempparam * rgpr) { | |||
T = c2 * vc / log1p(c1 * vc * vc * vc / Ne); | |||
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; | |||
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; | |||
int n; | |||
printf("Temperature... "); | |||
fflush(stdout); | |||
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); | |||
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 <string.h> | |||
#include <getopt.h> | |||
#include <libgen.h> | |||
#include <math.h> | |||
#include <sndfile.h> | |||
#include <png.h> | |||
#include "messages.h" | |||
#include "offsets.h" | |||
#include "colorpalette.h" | |||
#include "palette.h" | |||
extern int getpixelrow(float *pixelv); | |||
extern int init_dsp(double F);; | |||
extern int init_dsp(double F); | |||
static SNDFILE *inwav; | |||
@@ -73,7 +66,7 @@ static int initsnd(char *filename) { | |||
return(1); | |||
} | |||
// Get a sample from the wave file | |||
// Get samples from the wave file | |||
int getsample(float *sample, int 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} | |||
}; | |||
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; | |||
png_infop info_ptr; | |||
png_structp png_ptr; | |||
// 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 | |||
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); | |||
} | |||
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 | |||
png_set_IHDR(png_ptr, info_ptr, width, nrow, | |||
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_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); | |||
pngfile = fopen(filename, "wb"); | |||
if (pngfile == NULL) { | |||
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); | |||
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; | |||
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) { | |||
case 0: | |||
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; | |||
pixel[i] = MCOMPOSITE(255, cloud, pixelv[i+CHA_OFFSET], 1); | |||
}else{ | |||
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); | |||
fclose(pngfile); | |||
printf("\nDone\n"); | |||
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; | |||
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; | |||
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 void readfcconf(char *file); | |||
extern int optind; | |||
@@ -290,12 +241,12 @@ static void usage(void) { | |||
printf("Aptdec [options] audio files ...\n" | |||
"Options:\n" | |||
" -e [c|t] Enhancements\n" | |||
" c: Contrast enhance\n" | |||
" c: Contrast equalise\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" | |||
" a: Channel A\n" | |||
" b: Channel B\n" | |||
" c: False color\n" | |||
" t: Temperature\n" | |||
" l: Layered\n" | |||
@@ -310,23 +261,28 @@ int main(int argc, char **argv) { | |||
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"; | |||
// Default to a raw image, with equalization and cropped telemetry | |||
char imgopt[20] = "r"; | |||
char enchancements[20] = "ct"; | |||
// Image buffer | |||
float *prow[3000]; | |||
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 | |||
int chA, chB; | |||
// Print version | |||
printf("%s\n", VERSION); | |||
printf(VERSION"\n"); | |||
// Print usage if there are no arguments | |||
if(argc == 1) | |||
@@ -345,7 +301,7 @@ int main(int argc, char **argv) { | |||
break; | |||
// Output image type | |||
case 'i': | |||
strcpy(imgopt, optarg); | |||
strncpy(imgopt, optarg, 20); | |||
break; | |||
// Satellite number (for calibration) | |||
case 's': | |||
@@ -358,7 +314,7 @@ int main(int argc, char **argv) { | |||
break; | |||
// Enchancements | |||
case 'e': | |||
strcpy(enchancements, optarg); | |||
strncpy(enchancements, optarg, 20); | |||
break; | |||
default: | |||
usage(); | |||
@@ -369,10 +325,7 @@ int main(int argc, char **argv) { | |||
usage(); | |||
} | |||
// Pull this in from filtercoeff.h | |||
extern float Sync[32]; | |||
// Loop through the provided files | |||
// Process the provided files | |||
for (; optind < argc; optind++) { | |||
chA = chB = 0; | |||
@@ -397,100 +350,91 @@ int main(int argc, char **argv) { | |||
if (getpixelrow(prow[nrow]) == 0) | |||
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); | |||
} | |||
printf("\nTotal rows: %d\n", nrow); | |||
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 | |||
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); | |||
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 | |||
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 | |||
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(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); | |||
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 | |||
if (CONTAINS(imgopt, 'a')) { | |||
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 | |||
if (CONTAINS(imgopt, 'b')) { | |||
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')) { | |||
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 | |||
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 (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); | |||
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 { | |||
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); | |||
@@ -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_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" | |||
#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 SPC_WIDTH 47 | |||
#define TELE_WIDTH 45 | |||
#define CH_WIDTH 909 | |||
#define FRAME_LEN 128 | |||
#define CH_OFFSET (SYNC_WIDTH+SPC_WIDTH+CH_WIDTH+TELE_WIDTH) | |||
#define IMG_WIDTH 2080 | |||
#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] = { | |||
"\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" |
@@ -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 { | |||
float d[4][3]; | |||
struct { | |||