Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 
 
 

265 рядки
6.7 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(complexf_t in) {
  51. static float oscillator_phase = 0.0;
  52. // Internal oscillator
  53. #ifdef _MSC_VER
  54. complexf_t osc = _FCbuild(cos(oscillator_phase), -sin(oscillator_phase));
  55. in = _FCmulcc(in, osc);
  56. #else
  57. complexf_t osc = cos(oscillator_phase) + -sin(oscillator_phase)*I;
  58. in *= osc;
  59. #endif
  60. // Error detector
  61. float error = cargf(in);
  62. // Adjust frequency and phase
  63. oscillator_freq += pll_beta*error;
  64. oscillator_freq = clamp_half(oscillator_freq, (CARRIER_FREQ + MAX_CARRIER_OFFSET) / _sample_rate);
  65. oscillator_phase += M_TAUf * (pll_alpha*error + oscillator_freq);
  66. oscillator_phase = remainderf(oscillator_phase, M_TAUf);
  67. return crealf(in);
  68. }
  69. // Convert samples into pixels
  70. static int getamp(float *ampbuff, int count, apt_getsamples_t getsamples, void *context) {
  71. static float inbuff[BLKIN];
  72. static int idxin = 0;
  73. static int nin = 0;
  74. for (int n = 0; n < count; n++) {
  75. float I2, Q;
  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)
  87. return n;
  88. }
  89. // Process read samples into a brightness value
  90. complexf_t sample = hilbert_transform(&inbuff[idxin], hilbert_filter, HILBERT_FILTER_SIZE);
  91. ampbuff[n] = pll(sample);
  92. // Increment current sample
  93. idxin++;
  94. nin--;
  95. }
  96. return count;
  97. }
  98. // Sub-pixel offsetting
  99. int getpixelv(float *pvbuff, int count, apt_getsamples_t getsamples, void *context) {
  100. // Amplitude buffer
  101. static float ampbuff[BLKAMP];
  102. static int nam = 0;
  103. static int idxam = 0;
  104. float mult;
  105. // Gaussian resampling factor
  106. mult = (float) Fi / _sample_rate * FreqLine;
  107. int m = (int)(LOW_PASS_SIZE / mult + 1);
  108. for (int n = 0; n < count; n++) {
  109. int shift;
  110. if (nam < m) {
  111. int res;
  112. memmove(ampbuff, &(ampbuff[idxam]), nam * sizeof(float));
  113. idxam = 0;
  114. res = getamp(&(ampbuff[nam]), BLKAMP - nam, getsamples, context);
  115. nam += res;
  116. if (nam < m)
  117. return n;
  118. }
  119. pvbuff[n] = interpolating_convolve(&(ampbuff[idxam]), low_pass, LOW_PASS_SIZE, offset, mult) * mult * 256.0;
  120. shift = ((int) floor((RSMULT - offset) / mult)) + 1;
  121. offset = shift * mult + offset - RSMULT;
  122. idxam += shift;
  123. nam -= shift;
  124. }
  125. return count;
  126. }
  127. // Get an entire row of pixels, aligned with sync markers
  128. int apt_getpixelrow(float *pixelv, int nrow, int *zenith, int reset, apt_getsamples_t getsamples, void *context) {
  129. static float pixels[APT_IMG_WIDTH + SYNC_PATTERN_SIZE];
  130. static size_t npv;
  131. static int synced = 0;
  132. static float max = 0.0;
  133. static float minDoppler = 1000000000, previous = 0;
  134. if(reset) synced = 0;
  135. float corr, ecorr, lcorr;
  136. int res;
  137. // Move the row buffer into the the image buffer
  138. if (npv > 0)
  139. memmove(pixelv, pixels, npv * sizeof(float));
  140. // Get the sync line
  141. if (npv < SYNC_PATTERN_SIZE + 2) {
  142. res = getpixelv(&(pixelv[npv]), SYNC_PATTERN_SIZE + 2 - npv, getsamples, context);
  143. npv += res;
  144. if (npv < SYNC_PATTERN_SIZE + 2)
  145. return 0;
  146. }
  147. // Calculate the frequency offset
  148. ecorr = convolve(pixelv, sync_pattern, SYNC_PATTERN_SIZE);
  149. corr = convolve(&pixelv[1], sync_pattern, SYNC_PATTERN_SIZE - 1);
  150. lcorr = convolve(&pixelv[2], sync_pattern, SYNC_PATTERN_SIZE - 2);
  151. FreqLine = 1.0+((ecorr-lcorr) / corr / APT_IMG_WIDTH / 4.0);
  152. float val = fabs(lcorr - ecorr)*0.25 + previous*0.75;
  153. if(val < minDoppler && nrow > 10){
  154. minDoppler = val;
  155. *zenith = nrow;
  156. }
  157. previous = fabs(lcorr - ecorr);
  158. // The point in which the pixel offset is recalculated
  159. if (corr < 0.75 * max) {
  160. synced = 0;
  161. FreqLine = 1.0;
  162. }
  163. max = corr;
  164. if (synced < 8) {
  165. int mshift;
  166. static int lastmshift;
  167. if (npv < APT_IMG_WIDTH + SYNC_PATTERN_SIZE) {
  168. res = getpixelv(&(pixelv[npv]), APT_IMG_WIDTH + SYNC_PATTERN_SIZE - npv, getsamples, context);
  169. npv += res;
  170. if (npv < APT_IMG_WIDTH + SYNC_PATTERN_SIZE)
  171. return 0;
  172. }
  173. // Test every possible position until we get the best result
  174. mshift = 0;
  175. for (int shift = 0; shift < APT_IMG_WIDTH; shift++) {
  176. float corr;
  177. corr = convolve(&(pixelv[shift + 1]), sync_pattern, SYNC_PATTERN_SIZE);
  178. if (corr > max) {
  179. mshift = shift;
  180. max = corr;
  181. }
  182. }
  183. // Stop rows dissapearing into the void
  184. int mshiftOrig = mshift;
  185. if(abs(lastmshift-mshift) > 3 && nrow != 0){
  186. mshift = 0;
  187. }
  188. lastmshift = mshiftOrig;
  189. // If we are already as aligned as we can get, just continue
  190. if (mshift == 0) {
  191. synced++;
  192. } else {
  193. memmove(pixelv, &(pixelv[mshift]), (npv - mshift) * sizeof(float));
  194. npv -= mshift;
  195. synced = 0;
  196. FreqLine = 1.0;
  197. }
  198. }
  199. // Get the rest of this row
  200. if (npv < APT_IMG_WIDTH) {
  201. res = getpixelv(&(pixelv[npv]), APT_IMG_WIDTH - npv, getsamples, context);
  202. npv += res;
  203. if (npv < APT_IMG_WIDTH)
  204. return 0;
  205. }
  206. // Move the sync lines into the output buffer with the calculated offset
  207. if (npv == APT_IMG_WIDTH) {
  208. npv = 0;
  209. } else {
  210. memmove(pixels, &(pixelv[APT_IMG_WIDTH]), (npv - APT_IMG_WIDTH) * sizeof(float));
  211. npv -= APT_IMG_WIDTH;
  212. }
  213. return 1;
  214. }