|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- /*
- * aptdec - A lightweight FOSS (NOAA) APT decoder
- * Copyright (C) 2004-2009 Thierry Leconte (F4DWV) 2019-2022 Xerbo (xerbo@protonmail.com)
- *
- * This program 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 <math.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <complex.h>
-
- #include "apt.h"
- #include "filter.h"
- #include "taps.h"
- #include "util.h"
-
- // Block sizes
- #define BLKAMP 32768
- #define BLKIN 32768
-
- #define CARRIER_FREQ 2400.0
- #define MAX_CARRIER_OFFSET 20.0
-
- #define RSMULT 15
- #define Fi (APT_IMG_WIDTH * 2 * RSMULT)
-
- static float _sample_rate;
-
- static float offset = 0.0;
- static float FreqLine = 1.0;
-
- static float oscillator_freq;
- static float pll_alpha;
- static float pll_beta;
-
- // Initalise and configure PLL
- int apt_init(double sample_rate) {
- if (sample_rate > Fi) return 1;
- if (sample_rate < APT_IMG_WIDTH * 2) return -1;
- _sample_rate = sample_rate;
-
- // Pll configuration
- pll_alpha = 50 / _sample_rate;
- pll_beta = pll_alpha * pll_alpha / 2.0;
- oscillator_freq = CARRIER_FREQ / sample_rate;
-
- return 0;
- }
-
- static float pll(complexf_t in) {
- static float oscillator_phase = 0.0;
-
- // Internal oscillator
- #ifdef _MSC_VER
- complexf_t osc = _FCbuild(cos(oscillator_phase), -sin(oscillator_phase));
- in = _FCmulcc(in, osc);
- #else
- complexf_t osc = cos(oscillator_phase) + -sin(oscillator_phase) * I;
- in *= osc;
- #endif
-
- // Error detector
- float error = cargf(in);
-
- // Adjust frequency and phase
- oscillator_freq += pll_beta * error;
- oscillator_freq = clamp_half(oscillator_freq, (CARRIER_FREQ + MAX_CARRIER_OFFSET) / _sample_rate);
- oscillator_phase += M_TAUf * (pll_alpha * error + oscillator_freq);
- oscillator_phase = remainderf(oscillator_phase, M_TAUf);
-
- return crealf(in);
- }
-
- // Convert samples into pixels
- static int getamp(float *ampbuff, int count, apt_getsamples_t getsamples, void *context) {
- static float inbuff[BLKIN];
- static int idxin = 0;
- static int nin = 0;
-
- for (int n = 0; n < count; n++) {
-
- // Get some more samples when needed
- if (nin < HILBERT_FILTER_SIZE * 2 + 2) {
- // Number of samples read
- int res;
- memmove(inbuff, &(inbuff[idxin]), nin * sizeof(float));
- idxin = 0;
-
- // Read some samples
- res = getsamples(context, &(inbuff[nin]), BLKIN - nin);
- nin += res;
-
- // Make sure there is enough samples to continue
- if (nin < HILBERT_FILTER_SIZE * 2 + 2) return n;
- }
-
- // Process read samples into a brightness value
- complexf_t sample = hilbert_transform(&inbuff[idxin], hilbert_filter, HILBERT_FILTER_SIZE);
- ampbuff[n] = pll(sample);
-
- // Increment current sample
- idxin++;
- nin--;
- }
-
- return count;
- }
-
- // Sub-pixel offsetting
- int getpixelv(float *pvbuff, int count, apt_getsamples_t getsamples, void *context) {
- // Amplitude buffer
- static float ampbuff[BLKAMP];
- static int nam = 0;
- static int idxam = 0;
-
- float mult;
-
- // Gaussian resampling factor
- mult = (float)Fi / _sample_rate * FreqLine;
- int m = (int)(LOW_PASS_SIZE / mult + 1);
-
- for (int n = 0; n < count; n++) {
- int shift;
-
- if (nam < m) {
- int res;
- memmove(ampbuff, &(ampbuff[idxam]), nam * sizeof(float));
- idxam = 0;
- res = getamp(&(ampbuff[nam]), BLKAMP - nam, getsamples, context);
- nam += res;
- if (nam < m) return n;
- }
-
- pvbuff[n] = interpolating_convolve(&(ampbuff[idxam]), low_pass, LOW_PASS_SIZE, offset, mult) * mult * 256.0;
-
- shift = ((int)floor((RSMULT - offset) / mult)) + 1;
- offset = shift * mult + offset - RSMULT;
-
- idxam += shift;
- nam -= shift;
- }
-
- return count;
- }
-
- // Get an entire row of pixels, aligned with sync markers
- int apt_getpixelrow(float *pixelv, int nrow, int *zenith, int reset, apt_getsamples_t getsamples, void *context) {
- static float pixels[APT_IMG_WIDTH + SYNC_PATTERN_SIZE];
- static size_t npv;
- static int synced = 0;
- static float max = 0.0;
- static float minDoppler = 1000000000, previous = 0;
-
- if (reset) synced = 0;
-
- float 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 < SYNC_PATTERN_SIZE + 2) {
- res = getpixelv(&(pixelv[npv]), SYNC_PATTERN_SIZE + 2 - npv, getsamples, context);
- npv += res;
- if (npv < SYNC_PATTERN_SIZE + 2) return 0;
- }
-
- // Calculate the frequency offset
- ecorr = convolve(pixelv, sync_pattern, SYNC_PATTERN_SIZE);
- corr = convolve(&pixelv[1], sync_pattern, SYNC_PATTERN_SIZE - 1);
- lcorr = convolve(&pixelv[2], sync_pattern, SYNC_PATTERN_SIZE - 2);
- FreqLine = 1.0 + ((ecorr - lcorr) / corr / APT_IMG_WIDTH / 4.0);
-
- float val = fabs(lcorr - ecorr) * 0.25 + previous * 0.75;
- if (val < minDoppler && nrow > 10) {
- minDoppler = val;
- *zenith = nrow;
- }
- previous = fabs(lcorr - ecorr);
-
- // The point in which the pixel offset is recalculated
- if (corr < 0.75 * max) {
- synced = 0;
- FreqLine = 1.0;
- }
- max = corr;
-
- if (synced < 8) {
- int mshift;
- static int lastmshift;
-
- if (npv < APT_IMG_WIDTH + SYNC_PATTERN_SIZE) {
- res = getpixelv(&(pixelv[npv]), APT_IMG_WIDTH + SYNC_PATTERN_SIZE - npv, getsamples, context);
- npv += res;
- if (npv < APT_IMG_WIDTH + SYNC_PATTERN_SIZE) return 0;
- }
-
- // Test every possible position until we get the best result
- mshift = 0;
- for (int shift = 0; shift < APT_IMG_WIDTH; shift++) {
- float corr;
-
- corr = convolve(&(pixelv[shift + 1]), sync_pattern, SYNC_PATTERN_SIZE);
- if (corr > max) {
- mshift = shift;
- max = corr;
- }
- }
-
- // Stop rows dissapearing into the void
- int mshiftOrig = mshift;
- if (abs(lastmshift - mshift) > 3 && nrow != 0) {
- mshift = 0;
- }
- lastmshift = mshiftOrig;
-
- // 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;
- }
- }
-
- // Get the rest of this row
- if (npv < APT_IMG_WIDTH) {
- res = getpixelv(&(pixelv[npv]), APT_IMG_WIDTH - npv, getsamples, context);
- npv += res;
- if (npv < APT_IMG_WIDTH) return 0;
- }
-
- // Move the sync lines into the output buffer with the calculated offset
- if (npv == APT_IMG_WIDTH) {
- npv = 0;
- } else {
- memmove(pixels, &(pixelv[APT_IMG_WIDTH]), (npv - APT_IMG_WIDTH) * sizeof(float));
- npv -= APT_IMG_WIDTH;
- }
-
- return 1;
- }
|