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.
 
 
 
 
 

260 regels
6.5 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 <stdlib.h>
  19. #include <stdio.h>
  20. #include <string.h>
  21. #include <math.h>
  22. #include "apt.h"
  23. #include "filter.h"
  24. #include "taps.h"
  25. #include "util.h"
  26. // Block sizes
  27. #define BLKAMP 32768
  28. #define BLKIN 32768
  29. #define CARRIER_FREQ 2400.0
  30. #define MAX_CARRIER_OFFSET 20.0
  31. #define RSMULT 15
  32. #define Fi (APT_IMG_WIDTH*2 * RSMULT)
  33. static float _sample_rate;
  34. static float offset = 0.0;
  35. static float FreqLine = 1.0;
  36. static float oscillator_freq;
  37. static float pll_alpha;
  38. static float pll_beta;
  39. // Initalise and configure PLL
  40. int apt_init(double sample_rate) {
  41. if(sample_rate > Fi) return 1;
  42. if(sample_rate < APT_IMG_WIDTH*2) return -1;
  43. _sample_rate = sample_rate;
  44. // Pll configuration
  45. pll_alpha = 50 / _sample_rate;
  46. pll_beta = pll_alpha * pll_alpha / 2.0;
  47. oscillator_freq = CARRIER_FREQ/sample_rate;
  48. return 0;
  49. }
  50. static float pll(float complex in) {
  51. static float oscillator_phase = 0.0;
  52. // Internal oscillator
  53. float complex osc = cos(oscillator_phase) + -sin(oscillator_phase)*I;
  54. in *= osc;
  55. // Error detector
  56. float error = cargf(in);
  57. // Adjust frequency and phase
  58. oscillator_freq += pll_beta*error;
  59. oscillator_freq = clamp_half(oscillator_freq, (CARRIER_FREQ + MAX_CARRIER_OFFSET) / _sample_rate);
  60. oscillator_phase += M_TAUf * (pll_alpha*error + oscillator_freq);
  61. oscillator_phase = remainderf(oscillator_phase, M_TAUf);
  62. return crealf(in);
  63. }
  64. // Convert samples into pixels
  65. static int getamp(float *ampbuff, int count, apt_getsamples_t getsamples, void *context) {
  66. static float inbuff[BLKIN];
  67. static int idxin = 0;
  68. static int nin = 0;
  69. for (int n = 0; n < count; n++) {
  70. float I2, Q;
  71. // Get some more samples when needed
  72. if (nin < HILBERT_FILTER_SIZE * 2 + 2) {
  73. // Number of samples read
  74. int res;
  75. memmove(inbuff, &(inbuff[idxin]), nin * sizeof(float));
  76. idxin = 0;
  77. // Read some samples
  78. res = getsamples(context, &(inbuff[nin]), BLKIN - nin);
  79. nin += res;
  80. // Make sure there is enough samples to continue
  81. if (nin < HILBERT_FILTER_SIZE * 2 + 2)
  82. return n;
  83. }
  84. // Process read samples into a brightness value
  85. float complex sample = hilbert_transform(&inbuff[idxin], hilbert_filter, HILBERT_FILTER_SIZE);
  86. ampbuff[n] = pll(sample);
  87. // Increment current sample
  88. idxin++;
  89. nin--;
  90. }
  91. return count;
  92. }
  93. // Sub-pixel offsetting
  94. int getpixelv(float *pvbuff, int count, apt_getsamples_t getsamples, void *context) {
  95. // Amplitude buffer
  96. static float ampbuff[BLKAMP];
  97. static int nam = 0;
  98. static int idxam = 0;
  99. float mult;
  100. // Gaussian resampling factor
  101. mult = (float) Fi / _sample_rate * FreqLine;
  102. int m = (int)(LOW_PASS_SIZE / mult + 1);
  103. for (int n = 0; n < count; n++) {
  104. int shift;
  105. if (nam < m) {
  106. int res;
  107. memmove(ampbuff, &(ampbuff[idxam]), nam * sizeof(float));
  108. idxam = 0;
  109. res = getamp(&(ampbuff[nam]), BLKAMP - nam, getsamples, context);
  110. nam += res;
  111. if (nam < m)
  112. return n;
  113. }
  114. pvbuff[n] = interpolating_convolve(&(ampbuff[idxam]), low_pass, LOW_PASS_SIZE, offset, mult) * mult * 256.0;
  115. shift = ((int) floor((RSMULT - offset) / mult)) + 1;
  116. offset = shift * mult + offset - RSMULT;
  117. idxam += shift;
  118. nam -= shift;
  119. }
  120. return count;
  121. }
  122. // Get an entire row of pixels, aligned with sync markers
  123. int apt_getpixelrow(float *pixelv, int nrow, int *zenith, int reset, apt_getsamples_t getsamples, void *context) {
  124. static float pixels[APT_IMG_WIDTH + SYNC_PATTERN_SIZE];
  125. static size_t npv;
  126. static int synced = 0;
  127. static float max = 0.0;
  128. static float minDoppler = 1000000000, previous = 0;
  129. if(reset) synced = 0;
  130. float corr, ecorr, lcorr;
  131. int res;
  132. // Move the row buffer into the the image buffer
  133. if (npv > 0)
  134. memmove(pixelv, pixels, npv * sizeof(float));
  135. // Get the sync line
  136. if (npv < SYNC_PATTERN_SIZE + 2) {
  137. res = getpixelv(&(pixelv[npv]), SYNC_PATTERN_SIZE + 2 - npv, getsamples, context);
  138. npv += res;
  139. if (npv < SYNC_PATTERN_SIZE + 2)
  140. return 0;
  141. }
  142. // Calculate the frequency offset
  143. ecorr = convolve(pixelv, sync_pattern, SYNC_PATTERN_SIZE);
  144. corr = convolve(&pixelv[1], sync_pattern, SYNC_PATTERN_SIZE - 1);
  145. lcorr = convolve(&pixelv[2], sync_pattern, SYNC_PATTERN_SIZE - 2);
  146. FreqLine = 1.0+((ecorr-lcorr) / corr / APT_IMG_WIDTH / 4.0);
  147. float val = fabs(lcorr - ecorr)*0.25 + previous*0.75;
  148. if(val < minDoppler && nrow > 10){
  149. minDoppler = val;
  150. *zenith = nrow;
  151. }
  152. previous = fabs(lcorr - ecorr);
  153. // The point in which the pixel offset is recalculated
  154. if (corr < 0.75 * max) {
  155. synced = 0;
  156. FreqLine = 1.0;
  157. }
  158. max = corr;
  159. if (synced < 8) {
  160. int mshift;
  161. static int lastmshift;
  162. if (npv < APT_IMG_WIDTH + SYNC_PATTERN_SIZE) {
  163. res = getpixelv(&(pixelv[npv]), APT_IMG_WIDTH + SYNC_PATTERN_SIZE - npv, getsamples, context);
  164. npv += res;
  165. if (npv < APT_IMG_WIDTH + SYNC_PATTERN_SIZE)
  166. 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)
  199. return 0;
  200. }
  201. // Move the sync lines into the output buffer with the calculated offset
  202. if (npv == APT_IMG_WIDTH) {
  203. npv = 0;
  204. } else {
  205. memmove(pixels, &(pixelv[APT_IMG_WIDTH]), (npv - APT_IMG_WIDTH) * sizeof(float));
  206. npv -= APT_IMG_WIDTH;
  207. }
  208. return 1;
  209. }