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.

dsp.c 7.0 KiB

20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
20 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
20 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
20 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
20 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
20 years ago
4 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. /*
  2. * This file is part of Aptdec.
  3. * Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019-20222
  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_getsamples_t getsamples, void *context) {
  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 = getsamples(context, &(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_getsamples_t getsamples, void *context) {
  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, getsamples, context);
  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_getsamples_t getsamples, void *context) {
  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, getsamples, context);
  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, getsamples, context);
  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, getsamples, context);
  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. }