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.
 
 
 
 
 

313 lines
6.9 KiB

  1. /*
  2. * This file is part of Aptdec.
  3. * Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019-2020
  4. *
  5. * Aptdec 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. */
  19. #include <stdlib.h>
  20. #include <stdio.h>
  21. #include <string.h>
  22. #include <math.h>
  23. #include "apt.h"
  24. #include "filter.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 double Fe;
  39. static double offset = 0.0;
  40. static double FreqLine = 1.0;
  41. static double FreqOsc;
  42. static double 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. /* Fast phase estimator
  55. * Calculates the phase angle of a signal from a IQ sample
  56. */
  57. static inline double Phase(double I, double Q) {
  58. double angle, r;
  59. int s;
  60. if(I == 0.0 && Q == 0.0) return 0.0;
  61. if (Q < 0) {
  62. s = -1;
  63. Q = -Q;
  64. } else {
  65. s = 1;
  66. }
  67. if (I >= 0) {
  68. r = (I - Q) / (I + Q);
  69. angle = 0.25 - 0.25 * r;
  70. } else {
  71. r = (I + Q) / (Q - I);
  72. angle = 0.75 - 0.25 * r;
  73. }
  74. if(s > 0){
  75. return angle;
  76. }else{
  77. return -angle;
  78. }
  79. }
  80. /* Phase locked loop
  81. * https://arachnoid.com/phase_locked_loop/
  82. * Model of this filter here https://www.desmos.com/calculator/m0uadgkoee
  83. */
  84. static double pll(double I, double Q) {
  85. // PLL coefficient
  86. static double PhaseOsc = 0.0;
  87. double Io, Qo;
  88. double Ip, Qp;
  89. double DPhi;
  90. // Quadrature oscillator / reference
  91. Io = cos(PhaseOsc);
  92. Qo = sin(PhaseOsc);
  93. // Phase detector
  94. Ip = I * Io + Q * Qo;
  95. Qp = Q * Io - I * Qo;
  96. DPhi = Phase(Ip, Qp);
  97. // Loop filter
  98. PhaseOsc += 2.0 * M_PI * (K1 * DPhi + FreqOsc);
  99. if (PhaseOsc > M_PI)
  100. PhaseOsc -= 2.0 * M_PI;
  101. if (PhaseOsc <= -M_PI)
  102. PhaseOsc += 2.0 * M_PI;
  103. FreqOsc += K2 * DPhi;
  104. if (FreqOsc > ((Fc + DFc) / Fe))
  105. FreqOsc = (Fc + DFc) / Fe;
  106. if (FreqOsc < ((Fc - DFc) / Fe))
  107. FreqOsc = (Fc - DFc) / Fe;
  108. return Ip;
  109. }
  110. // Convert samples into pixels
  111. static int getamp(double *ampbuff, int count, apt_getsample_t getsample) {
  112. static float inbuff[BLKIN];
  113. static int idxin = 0;
  114. static int nin = 0;
  115. for (int n = 0; n < count; n++) {
  116. double I, Q;
  117. // Get some more samples when needed
  118. if (nin < IQFilterLen * 2 + 2) {
  119. // Number of samples read
  120. int res;
  121. memmove(inbuff, &(inbuff[idxin]), nin * sizeof(float));
  122. idxin = 0;
  123. // Read some samples
  124. res = getsample(&(inbuff[nin]), BLKIN - nin);
  125. nin += res;
  126. // Make sure there is enough samples to continue
  127. if (nin < IQFilterLen * 2 + 2)
  128. return n;
  129. }
  130. // Process read samples into a brightness value
  131. iqfir(&inbuff[idxin], iqfilter, IQFilterLen, &I, &Q);
  132. ampbuff[n] = pll(I, Q);
  133. // Increment current sample
  134. idxin++;
  135. nin--;
  136. }
  137. return count;
  138. }
  139. // Sub-pixel offsetting + FIR compensation
  140. int getpixelv(float *pvbuff, int count, apt_getsample_t getsample) {
  141. // Amplitude buffer
  142. static double ampbuff[BLKAMP];
  143. static int nam = 0;
  144. static int idxam = 0;
  145. double mult;
  146. // Gaussian resampling factor
  147. mult = (double) Fi / Fe * FreqLine;
  148. int m = (int)(RSFilterLen / mult + 1);
  149. for (int n = 0; n < count; n++) {
  150. int shift;
  151. if (nam < m) {
  152. int res;
  153. memmove(ampbuff, &(ampbuff[idxam]), nam * sizeof(double));
  154. idxam = 0;
  155. res = getamp(&(ampbuff[nam]), BLKAMP - nam, getsample);
  156. nam += res;
  157. if (nam < m)
  158. return n;
  159. }
  160. // Gaussian FIR compensation filter
  161. pvbuff[n] = (float)(rsfir(&(ampbuff[idxam]), rsfilter, RSFilterLen, offset, mult) * mult * 256.0);
  162. shift = ((int) floor((RSMULT - offset) / mult)) + 1;
  163. offset = shift * mult + offset - RSMULT;
  164. idxam += shift;
  165. nam -= shift;
  166. }
  167. return count;
  168. }
  169. // Get an entire row of pixels, aligned with sync markers
  170. int apt_getpixelrow(float *pixelv, int nrow, int *zenith, int reset, apt_getsample_t getsample) {
  171. static float pixels[PixelLine + SyncFilterLen];
  172. static int npv;
  173. static int synced = 0;
  174. static double max = 0.0;
  175. static double minDoppler = 1000000000, previous = 0;
  176. if(reset) synced = 0;
  177. double corr, ecorr, lcorr;
  178. int res;
  179. // Move the row buffer into the the image buffer
  180. if (npv > 0)
  181. memmove(pixelv, pixels, npv * sizeof(float));
  182. // Get the sync line
  183. if (npv < SyncFilterLen + 2) {
  184. res = getpixelv(&(pixelv[npv]), SyncFilterLen + 2 - npv, getsample);
  185. npv += res;
  186. if (npv < SyncFilterLen + 2)
  187. return 0;
  188. }
  189. // Calculate the frequency offset
  190. ecorr = fir(pixelv, Sync, SyncFilterLen);
  191. corr = fir(&pixelv[1], Sync, SyncFilterLen - 1);
  192. lcorr = fir(&pixelv[2], Sync, SyncFilterLen - 2);
  193. FreqLine = 1.0+((ecorr-lcorr) / corr / PixelLine / 4.0);
  194. double val = fabs(lcorr - ecorr)*0.25 + previous*0.75;
  195. if(val < minDoppler && nrow > 10){
  196. minDoppler = val;
  197. *zenith = nrow;
  198. }
  199. previous = fabs(lcorr - ecorr);
  200. // The point in which the pixel offset is recalculated
  201. if (corr < 0.75 * max) {
  202. synced = 0;
  203. FreqLine = 1.0;
  204. }
  205. max = corr;
  206. if (synced < 8) {
  207. int mshift;
  208. static int lastmshift;
  209. if (npv < PixelLine + SyncFilterLen) {
  210. res = getpixelv(&(pixelv[npv]), PixelLine + SyncFilterLen - npv, getsample);
  211. npv += res;
  212. if (npv < PixelLine + SyncFilterLen)
  213. return 0;
  214. }
  215. // Test every possible position until we get the best result
  216. mshift = 0;
  217. for (int shift = 0; shift < PixelLine; shift++) {
  218. double corr;
  219. corr = fir(&(pixelv[shift + 1]), Sync, SyncFilterLen);
  220. if (corr > max) {
  221. mshift = shift;
  222. max = corr;
  223. }
  224. }
  225. // Stop rows dissapearing into the void
  226. int mshiftOrig = mshift;
  227. if(abs(lastmshift-mshift) > 3 && nrow != 0){
  228. mshift = 0;
  229. }
  230. lastmshift = mshiftOrig;
  231. // If we are already as aligned as we can get, just continue
  232. if (mshift == 0) {
  233. synced++;
  234. } else {
  235. memmove(pixelv, &(pixelv[mshift]), (npv - mshift) * sizeof(float));
  236. npv -= mshift;
  237. synced = 0;
  238. FreqLine = 1.0;
  239. }
  240. }
  241. // Get the rest of this row
  242. if (npv < PixelLine) {
  243. res = getpixelv(&(pixelv[npv]), PixelLine - npv, getsample);
  244. npv += res;
  245. if (npv < PixelLine)
  246. return 0;
  247. }
  248. // Move the sync lines into the output buffer with the calculated offset
  249. if (npv == PixelLine) {
  250. npv = 0;
  251. } else {
  252. memmove(pixels, &(pixelv[PixelLine]), (npv - PixelLine) * sizeof(float));
  253. npv -= PixelLine;
  254. }
  255. return 1;
  256. }