You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

259 lines
7.4 KiB

  1. /*
  2. * aptdec - A lightweight FOSS (NOAA) APT decoder
  3. * Copyright (C) 2004-2009 Thierry Leconte (F4DWV) 2019-2022 Xerbo (xerbo@protonmail.com)
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. #include <math.h>
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <complex.h>
  23. #include "apt.h"
  24. #include "filter.h"
  25. #include "taps.h"
  26. #include "util.h"
  27. // Block sizes
  28. #define BLKAMP 32768
  29. #define BLKIN 32768
  30. #define CARRIER_FREQ 2400.0
  31. #define MAX_CARRIER_OFFSET 20.0
  32. #define RSMULT 15
  33. #define Fi (APT_IMG_WIDTH * 2 * RSMULT)
  34. static float _sample_rate;
  35. static float offset = 0.0;
  36. static float FreqLine = 1.0;
  37. static float oscillator_freq;
  38. static float pll_alpha;
  39. static float pll_beta;
  40. // Initalise and configure PLL
  41. int apt_init(double sample_rate) {
  42. if (sample_rate > Fi) return 1;
  43. if (sample_rate < APT_IMG_WIDTH * 2) return -1;
  44. _sample_rate = sample_rate;
  45. // Pll configuration
  46. pll_alpha = 50 / _sample_rate;
  47. pll_beta = pll_alpha * pll_alpha / 2.0;
  48. oscillator_freq = CARRIER_FREQ / sample_rate;
  49. return 0;
  50. }
  51. static float pll(complexf_t in) {
  52. static float oscillator_phase = 0.0;
  53. // Internal oscillator
  54. #ifdef _MSC_VER
  55. complexf_t osc = _FCbuild(cos(oscillator_phase), -sin(oscillator_phase));
  56. in = _FCmulcc(in, osc);
  57. #else
  58. complexf_t osc = cos(oscillator_phase) + -sin(oscillator_phase) * I;
  59. in *= osc;
  60. #endif
  61. // Error detector
  62. float error = cargf(in);
  63. // Adjust frequency and phase
  64. oscillator_freq += pll_beta * error;
  65. oscillator_freq = clamp_half(oscillator_freq, (CARRIER_FREQ + MAX_CARRIER_OFFSET) / _sample_rate);
  66. oscillator_phase += M_TAUf * (pll_alpha * error + oscillator_freq);
  67. oscillator_phase = remainderf(oscillator_phase, M_TAUf);
  68. return crealf(in);
  69. }
  70. // Convert samples into pixels
  71. static int getamp(float *ampbuff, int count, apt_getsamples_t getsamples, void *context) {
  72. static float inbuff[BLKIN];
  73. static int idxin = 0;
  74. static int nin = 0;
  75. for (int n = 0; n < count; n++) {
  76. // Get some more samples when needed
  77. if (nin < HILBERT_FILTER_SIZE * 2 + 2) {
  78. // Number of samples read
  79. int res;
  80. memmove(inbuff, &(inbuff[idxin]), nin * sizeof(float));
  81. idxin = 0;
  82. // Read some samples
  83. res = getsamples(context, &(inbuff[nin]), BLKIN - nin);
  84. nin += res;
  85. // Make sure there is enough samples to continue
  86. if (nin < HILBERT_FILTER_SIZE * 2 + 2) return n;
  87. }
  88. // Process read samples into a brightness value
  89. complexf_t sample = hilbert_transform(&inbuff[idxin], hilbert_filter, HILBERT_FILTER_SIZE);
  90. ampbuff[n] = pll(sample);
  91. // Increment current sample
  92. idxin++;
  93. nin--;
  94. }
  95. return count;
  96. }
  97. // Sub-pixel offsetting
  98. int getpixelv(float *pvbuff, int count, apt_getsamples_t getsamples, void *context) {
  99. // Amplitude buffer
  100. static float ampbuff[BLKAMP];
  101. static int nam = 0;
  102. static int idxam = 0;
  103. float mult;
  104. // Gaussian resampling factor
  105. mult = (float)Fi / _sample_rate * FreqLine;
  106. int m = (int)(LOW_PASS_SIZE / mult + 1);
  107. for (int n = 0; n < count; n++) {
  108. int shift;
  109. if (nam < m) {
  110. int res;
  111. memmove(ampbuff, &(ampbuff[idxam]), nam * sizeof(float));
  112. idxam = 0;
  113. res = getamp(&(ampbuff[nam]), BLKAMP - nam, getsamples, context);
  114. nam += res;
  115. if (nam < m) return n;
  116. }
  117. pvbuff[n] = interpolating_convolve(&(ampbuff[idxam]), low_pass, LOW_PASS_SIZE, offset, mult) * mult * 256.0;
  118. shift = ((int)floor((RSMULT - offset) / mult)) + 1;
  119. offset = shift * mult + offset - RSMULT;
  120. idxam += shift;
  121. nam -= shift;
  122. }
  123. return count;
  124. }
  125. // Get an entire row of pixels, aligned with sync markers
  126. int apt_getpixelrow(float *pixelv, int nrow, int *zenith, int reset, apt_getsamples_t getsamples, void *context) {
  127. static float pixels[APT_IMG_WIDTH + SYNC_PATTERN_SIZE];
  128. static size_t npv;
  129. static int synced = 0;
  130. static float max = 0.0;
  131. static float minDoppler = 1000000000, previous = 0;
  132. if (reset) synced = 0;
  133. float corr, ecorr, lcorr;
  134. int res;
  135. // Move the row buffer into the the image buffer
  136. if (npv > 0) memmove(pixelv, pixels, npv * sizeof(float));
  137. // Get the sync line
  138. if (npv < SYNC_PATTERN_SIZE + 2) {
  139. res = getpixelv(&(pixelv[npv]), SYNC_PATTERN_SIZE + 2 - npv, getsamples, context);
  140. npv += res;
  141. if (npv < SYNC_PATTERN_SIZE + 2) return 0;
  142. }
  143. // Calculate the frequency offset
  144. ecorr = convolve(pixelv, sync_pattern, SYNC_PATTERN_SIZE);
  145. corr = convolve(&pixelv[1], sync_pattern, SYNC_PATTERN_SIZE - 1);
  146. lcorr = convolve(&pixelv[2], sync_pattern, SYNC_PATTERN_SIZE - 2);
  147. FreqLine = 1.0 + ((ecorr - lcorr) / corr / APT_IMG_WIDTH / 4.0);
  148. float val = fabs(lcorr - ecorr) * 0.25 + previous * 0.75;
  149. if (val < minDoppler && nrow > 10) {
  150. minDoppler = val;
  151. *zenith = nrow;
  152. }
  153. previous = fabs(lcorr - ecorr);
  154. // The point in which the pixel offset is recalculated
  155. if (corr < 0.75 * max) {
  156. synced = 0;
  157. FreqLine = 1.0;
  158. }
  159. max = corr;
  160. if (synced < 8) {
  161. int mshift;
  162. static int lastmshift;
  163. if (npv < APT_IMG_WIDTH + SYNC_PATTERN_SIZE) {
  164. res = getpixelv(&(pixelv[npv]), APT_IMG_WIDTH + SYNC_PATTERN_SIZE - npv, getsamples, context);
  165. npv += res;
  166. if (npv < APT_IMG_WIDTH + SYNC_PATTERN_SIZE) return 0;
  167. }
  168. // Test every possible position until we get the best result
  169. mshift = 0;
  170. for (int shift = 0; shift < APT_IMG_WIDTH; shift++) {
  171. float corr;
  172. corr = convolve(&(pixelv[shift + 1]), sync_pattern, SYNC_PATTERN_SIZE);
  173. if (corr > max) {
  174. mshift = shift;
  175. max = corr;
  176. }
  177. }
  178. // Stop rows dissapearing into the void
  179. int mshiftOrig = mshift;
  180. if (abs(lastmshift - mshift) > 3 && nrow != 0) {
  181. mshift = 0;
  182. }
  183. lastmshift = mshiftOrig;
  184. // If we are already as aligned as we can get, just continue
  185. if (mshift == 0) {
  186. synced++;
  187. } else {
  188. memmove(pixelv, &(pixelv[mshift]), (npv - mshift) * sizeof(float));
  189. npv -= mshift;
  190. synced = 0;
  191. FreqLine = 1.0;
  192. }
  193. }
  194. // Get the rest of this row
  195. if (npv < APT_IMG_WIDTH) {
  196. res = getpixelv(&(pixelv[npv]), APT_IMG_WIDTH - npv, getsamples, context);
  197. npv += res;
  198. if (npv < APT_IMG_WIDTH) return 0;
  199. }
  200. // Move the sync lines into the output buffer with the calculated offset
  201. if (npv == APT_IMG_WIDTH) {
  202. npv = 0;
  203. } else {
  204. memmove(pixels, &(pixelv[APT_IMG_WIDTH]), (npv - APT_IMG_WIDTH) * sizeof(float));
  205. npv -= APT_IMG_WIDTH;
  206. }
  207. return 1;
  208. }