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.
 
 
 
 
 

283 regels
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. // In case your C compiler is so old that Pi hadn't been invented yet
  26. #ifndef M_PI
  27. #define M_PI 3.141592653589793
  28. #endif
  29. // Block sizes
  30. #define BLKAMP 32768
  31. #define BLKIN 32768
  32. #define Fc 2400.0
  33. #define DFc 50.0
  34. #define PixelLine 2080
  35. #define Fp (2 * PixelLine)
  36. #define RSMULT 15
  37. #define Fi (Fp * RSMULT)
  38. static float Fe;
  39. static float offset = 0.0;
  40. static float FreqLine = 1.0;
  41. static float FreqOsc;
  42. static float K1, K2;
  43. // Check the sample rate and calculate some constants
  44. int apt_init(double F) {
  45. if(F > Fi) return 1;
  46. if(F < Fp) return -1;
  47. Fe = F;
  48. K1 = DFc / Fe;
  49. K2 = K1 * K1 / 2.0;
  50. // Number of samples per cycle
  51. FreqOsc = Fc / Fe;
  52. return 0;
  53. }
  54. /* Phase locked loop
  55. * https://arachnoid.com/phase_locked_loop/
  56. * Model of this filter here https://www.desmos.com/calculator/m0uadgkoee
  57. */
  58. static float pll(float I2, float Q) {
  59. // PLL coefficient
  60. static float PhaseOsc = 0.0;
  61. float Io, Qo;
  62. float Ip, Qp;
  63. float DPhi;
  64. // Quadrature oscillator / reference
  65. Io = cos(PhaseOsc);
  66. Qo = sin(PhaseOsc);
  67. // Phase detector
  68. Ip = I2 * Io + Q * Qo;
  69. Qp = Q * Io - I2 * Qo;
  70. DPhi = atan2f(Qp, Ip);
  71. // Loop filter
  72. PhaseOsc += 2.0 * M_PI * (K1 * DPhi + FreqOsc);
  73. if (PhaseOsc > M_PI)
  74. PhaseOsc -= 2.0 * M_PI;
  75. if (PhaseOsc <= -M_PI)
  76. PhaseOsc += 2.0 * M_PI;
  77. FreqOsc += K2 * DPhi;
  78. if (FreqOsc > ((Fc + DFc) / Fe))
  79. FreqOsc = (Fc + DFc) / Fe;
  80. if (FreqOsc < ((Fc - DFc) / Fe))
  81. FreqOsc = (Fc - DFc) / Fe;
  82. return Ip;
  83. }
  84. // Convert samples into pixels
  85. static int getamp(float *ampbuff, int count, apt_getsamples_t getsamples, void *context) {
  86. static float inbuff[BLKIN];
  87. static int idxin = 0;
  88. static int nin = 0;
  89. for (int n = 0; n < count; n++) {
  90. float I2, Q;
  91. // Get some more samples when needed
  92. if (nin < HILBERT_FILTER_SIZE * 2 + 2) {
  93. // Number of samples read
  94. int res;
  95. memmove(inbuff, &(inbuff[idxin]), nin * sizeof(float));
  96. idxin = 0;
  97. // Read some samples
  98. res = getsamples(context, &(inbuff[nin]), BLKIN - nin);
  99. nin += res;
  100. // Make sure there is enough samples to continue
  101. if (nin < HILBERT_FILTER_SIZE * 2 + 2)
  102. return n;
  103. }
  104. // Process read samples into a brightness value
  105. float complex tmp = hilbert_transform(&inbuff[idxin], hilbert_filter, HILBERT_FILTER_SIZE);
  106. I2 = crealf(tmp);
  107. Q = cimagf(tmp);
  108. ampbuff[n] = pll(I2, Q);
  109. // Increment current sample
  110. idxin++;
  111. nin--;
  112. }
  113. return count;
  114. }
  115. // Sub-pixel offsetting
  116. int getpixelv(float *pvbuff, int count, apt_getsamples_t getsamples, void *context) {
  117. // Amplitude buffer
  118. static float ampbuff[BLKAMP];
  119. static int nam = 0;
  120. static int idxam = 0;
  121. float mult;
  122. // Gaussian resampling factor
  123. mult = (float) Fi / Fe * FreqLine;
  124. int m = (int)(LOW_PASS_SIZE / mult + 1);
  125. for (int n = 0; n < count; n++) {
  126. int shift;
  127. if (nam < m) {
  128. int res;
  129. memmove(ampbuff, &(ampbuff[idxam]), nam * sizeof(float));
  130. idxam = 0;
  131. res = getamp(&(ampbuff[nam]), BLKAMP - nam, getsamples, context);
  132. nam += res;
  133. if (nam < m)
  134. return n;
  135. }
  136. pvbuff[n] = interpolating_convolve(&(ampbuff[idxam]), low_pass, LOW_PASS_SIZE, offset, mult) * mult * 256.0;
  137. shift = ((int) floor((RSMULT - offset) / mult)) + 1;
  138. offset = shift * mult + offset - RSMULT;
  139. idxam += shift;
  140. nam -= shift;
  141. }
  142. return count;
  143. }
  144. // Get an entire row of pixels, aligned with sync markers
  145. int apt_getpixelrow(float *pixelv, int nrow, int *zenith, int reset, apt_getsamples_t getsamples, void *context) {
  146. static float pixels[PixelLine + SYNC_PATTERN_SIZE];
  147. static size_t npv;
  148. static int synced = 0;
  149. static float max = 0.0;
  150. static float minDoppler = 1000000000, previous = 0;
  151. if(reset) synced = 0;
  152. float corr, ecorr, lcorr;
  153. int res;
  154. // Move the row buffer into the the image buffer
  155. if (npv > 0)
  156. memmove(pixelv, pixels, npv * sizeof(float));
  157. // Get the sync line
  158. if (npv < SYNC_PATTERN_SIZE + 2) {
  159. res = getpixelv(&(pixelv[npv]), SYNC_PATTERN_SIZE + 2 - npv, getsamples, context);
  160. npv += res;
  161. if (npv < SYNC_PATTERN_SIZE + 2)
  162. return 0;
  163. }
  164. // Calculate the frequency offset
  165. ecorr = convolve(pixelv, sync_pattern, SYNC_PATTERN_SIZE);
  166. corr = convolve(&pixelv[1], sync_pattern, SYNC_PATTERN_SIZE - 1);
  167. lcorr = convolve(&pixelv[2], sync_pattern, SYNC_PATTERN_SIZE - 2);
  168. FreqLine = 1.0+((ecorr-lcorr) / corr / PixelLine / 4.0);
  169. float val = fabs(lcorr - ecorr)*0.25 + previous*0.75;
  170. if(val < minDoppler && nrow > 10){
  171. minDoppler = val;
  172. *zenith = nrow;
  173. }
  174. previous = fabs(lcorr - ecorr);
  175. // The point in which the pixel offset is recalculated
  176. if (corr < 0.75 * max) {
  177. synced = 0;
  178. FreqLine = 1.0;
  179. }
  180. max = corr;
  181. if (synced < 8) {
  182. int mshift;
  183. static int lastmshift;
  184. if (npv < PixelLine + SYNC_PATTERN_SIZE) {
  185. res = getpixelv(&(pixelv[npv]), PixelLine + SYNC_PATTERN_SIZE - npv, getsamples, context);
  186. npv += res;
  187. if (npv < PixelLine + SYNC_PATTERN_SIZE)
  188. return 0;
  189. }
  190. // Test every possible position until we get the best result
  191. mshift = 0;
  192. for (int shift = 0; shift < PixelLine; shift++) {
  193. float corr;
  194. corr = convolve(&(pixelv[shift + 1]), sync_pattern, SYNC_PATTERN_SIZE);
  195. if (corr > max) {
  196. mshift = shift;
  197. max = corr;
  198. }
  199. }
  200. // Stop rows dissapearing into the void
  201. int mshiftOrig = mshift;
  202. if(abs(lastmshift-mshift) > 3 && nrow != 0){
  203. mshift = 0;
  204. }
  205. lastmshift = mshiftOrig;
  206. // If we are already as aligned as we can get, just continue
  207. if (mshift == 0) {
  208. synced++;
  209. } else {
  210. memmove(pixelv, &(pixelv[mshift]), (npv - mshift) * sizeof(float));
  211. npv -= mshift;
  212. synced = 0;
  213. FreqLine = 1.0;
  214. }
  215. }
  216. // Get the rest of this row
  217. if (npv < PixelLine) {
  218. res = getpixelv(&(pixelv[npv]), PixelLine - npv, getsamples, context);
  219. npv += res;
  220. if (npv < PixelLine)
  221. return 0;
  222. }
  223. // Move the sync lines into the output buffer with the calculated offset
  224. if (npv == PixelLine) {
  225. npv = 0;
  226. } else {
  227. memmove(pixels, &(pixelv[PixelLine]), (npv - PixelLine) * sizeof(float));
  228. npv -= PixelLine;
  229. }
  230. return 1;
  231. }