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.
 
 
 
 
 

239 line
7.7 KiB

  1. /*
  2. * aptdec - A lightweight FOSS (NOAA) APT decoder
  3. * Copyright (C) 2004-2009 Thierry Leconte (F4DWV) 2019-2023 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 <float.h>
  19. #include <math.h>
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <string.h>
  23. #include <aptdec.h>
  24. #include "filter.h"
  25. #include "util.h"
  26. #include "algebra.h"
  27. #define LOW_PASS_SIZE 101
  28. #define CARRIER_FREQ 2400.0f
  29. #define MAX_CARRIER_OFFSET 10.0f
  30. const float sync_pattern[] = {-1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1,
  31. 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 0};
  32. #define SYNC_SIZE (sizeof(sync_pattern)/sizeof(sync_pattern[0]))
  33. typedef struct {
  34. float alpha;
  35. float beta;
  36. float min_freq;
  37. float max_freq;
  38. float freq;
  39. float phase;
  40. } pll_t;
  41. typedef struct {
  42. float *ring_buffer;
  43. size_t ring_size;
  44. float *taps;
  45. size_t ntaps;
  46. } fir_t;
  47. struct aptdec {
  48. float sample_rate;
  49. float sync_frequency;
  50. pll_t *pll;
  51. fir_t *hilbert;
  52. float low_pass[LOW_PASS_SIZE];
  53. float row_buffer[APTDEC_IMG_WIDTH + SYNC_SIZE + 2];
  54. float interpolator_buffer[APTDEC_BUFFER_SIZE];
  55. size_t interpolator_n;
  56. float interpolator_offset;
  57. };
  58. char *aptdec_get_version(void) {
  59. return VERSION;
  60. }
  61. fir_t *fir_init(size_t max_size, size_t ntaps) {
  62. fir_t *fir = calloc(1, sizeof(fir_t));
  63. fir->ntaps = ntaps;
  64. fir->ring_size = max_size + ntaps;
  65. fir->taps = calloc(ntaps, sizeof(float));
  66. fir->ring_buffer = calloc(max_size + ntaps, sizeof(float));
  67. return fir;
  68. }
  69. void fir_free(fir_t *fir) {
  70. free(fir->ring_buffer);
  71. free(fir->taps);
  72. free(fir);
  73. }
  74. pll_t *pll_init(float alpha, float beta, float min_freq, float max_freq, float sample_rate) {
  75. pll_t *pll = calloc(1, sizeof(pll_t));
  76. pll->alpha = alpha;
  77. pll->beta = beta;
  78. pll->min_freq = M_TAUf * min_freq / sample_rate;
  79. pll->max_freq = M_TAUf * max_freq / sample_rate;
  80. pll->phase = 0.0f;
  81. pll->freq = 0.0f;
  82. return pll;
  83. }
  84. aptdec_t *aptdec_init(float sample_rate) {
  85. if (sample_rate > 96000 || sample_rate < (CARRIER_FREQ + APTDEC_IMG_WIDTH) * 2.0f) {
  86. return NULL;
  87. }
  88. aptdec_t *aptdec = calloc(1, sizeof(aptdec_t));
  89. aptdec->sample_rate = sample_rate;
  90. aptdec->sync_frequency = 1.0f;
  91. aptdec->interpolator_n = APTDEC_BUFFER_SIZE;
  92. aptdec->interpolator_offset = 0.0f;
  93. // PLL configuration
  94. // https://www.trondeau.com/blog/2011/8/13/control-loop-gain-values.html
  95. float damp = 0.7f;
  96. float bw = 0.005f;
  97. float alpha = (4.0f * damp * bw) / (1.0f + 2.0f * damp * bw + bw * bw);
  98. float beta = (4.0f * bw * bw) / (1.0f + 2.0f * damp * bw + bw * bw);
  99. aptdec->pll = pll_init(alpha, beta, CARRIER_FREQ-MAX_CARRIER_OFFSET, CARRIER_FREQ+MAX_CARRIER_OFFSET, sample_rate);
  100. if (aptdec->pll == NULL) {
  101. free(aptdec);
  102. return NULL;
  103. }
  104. // Hilbert transform
  105. aptdec->hilbert = fir_init(APTDEC_BUFFER_SIZE, 31);
  106. if (aptdec->hilbert == NULL) {
  107. free(aptdec->pll);
  108. free(aptdec);
  109. return NULL;
  110. }
  111. design_hilbert(aptdec->hilbert->taps, aptdec->hilbert->ntaps);
  112. design_low_pass(aptdec->low_pass, aptdec->sample_rate, (2080.0f + CARRIER_FREQ) / 2.0f, LOW_PASS_SIZE);
  113. return aptdec;
  114. }
  115. void aptdec_free(aptdec_t *aptdec) {
  116. fir_free(aptdec->hilbert);
  117. free(aptdec->pll);
  118. free(aptdec);
  119. }
  120. static complexf_t pll_work(pll_t *pll, complexf_t in) {
  121. // Internal oscillator (90deg offset)
  122. complexf_t osc = complex_build(cosf(pll->phase), -sinf(pll->phase));
  123. in = complex_multiply(in, osc);
  124. // Error detector
  125. float error = cargf(in);
  126. // Loop filter (single pole IIR)
  127. pll->freq += pll->beta * error;
  128. pll->freq = clamp(pll->freq, pll->min_freq, pll->max_freq);
  129. pll->phase += pll->freq + (pll->alpha * error);
  130. pll->phase = remainderf(pll->phase, M_TAUf);
  131. return in;
  132. }
  133. static int am_demod(aptdec_t *aptdec, float *out, size_t count, aptdec_callback_t callback, void *context) {
  134. size_t read = callback(&aptdec->hilbert->ring_buffer[aptdec->hilbert->ntaps], count, context);
  135. for (size_t i = 0; i < read; i++) {
  136. complexf_t sample = hilbert_transform(&aptdec->hilbert->ring_buffer[i], aptdec->hilbert->taps, aptdec->hilbert->ntaps);
  137. out[i] = crealf(pll_work(aptdec->pll, sample));
  138. }
  139. memcpy(aptdec->hilbert->ring_buffer, &aptdec->hilbert->ring_buffer[read], aptdec->hilbert->ntaps*sizeof(float));
  140. return read;
  141. }
  142. static int get_pixels(aptdec_t *aptdec, float *out, size_t count, aptdec_callback_t callback, void *context) {
  143. float ratio = aptdec->sample_rate / (4160.0f * aptdec->sync_frequency);
  144. for (size_t i = 0; i < count; i++) {
  145. // Get more samples if there are less than `LOW_PASS_SIZE` available
  146. if (aptdec->interpolator_n + LOW_PASS_SIZE > APTDEC_BUFFER_SIZE) {
  147. memcpy(aptdec->interpolator_buffer, &aptdec->interpolator_buffer[aptdec->interpolator_n], (APTDEC_BUFFER_SIZE-aptdec->interpolator_n) * sizeof(float));
  148. size_t read = am_demod(aptdec, &aptdec->interpolator_buffer[APTDEC_BUFFER_SIZE-aptdec->interpolator_n], aptdec->interpolator_n, callback, context);
  149. if (read != aptdec->interpolator_n) {
  150. return i;
  151. }
  152. aptdec->interpolator_n = 0;
  153. }
  154. out[i] = interpolating_convolve(&aptdec->interpolator_buffer[aptdec->interpolator_n], aptdec->low_pass, LOW_PASS_SIZE, aptdec->interpolator_offset);
  155. // Do not question the sacred code
  156. int shift = ceilf(ratio - aptdec->interpolator_offset);
  157. aptdec->interpolator_offset = shift + aptdec->interpolator_offset - ratio;
  158. aptdec->interpolator_n += shift;
  159. }
  160. return count;
  161. }
  162. // Get an entire row of pixels, aligned with sync markers
  163. int aptdec_getrow(aptdec_t *aptdec, float *row, aptdec_callback_t callback, void *context) {
  164. // Wrap the circular buffer
  165. memcpy(aptdec->row_buffer, &aptdec->row_buffer[APTDEC_IMG_WIDTH], (SYNC_SIZE + 2) * sizeof(float));
  166. // Get a lines worth (APTDEC_IMG_WIDTH) of samples
  167. if (get_pixels(aptdec, &aptdec->row_buffer[SYNC_SIZE + 2], APTDEC_IMG_WIDTH, callback, context) != APTDEC_IMG_WIDTH) {
  168. return 0;
  169. }
  170. // Error detector
  171. float left = FLT_MIN;
  172. float middle = FLT_MIN;
  173. float right = FLT_MIN;
  174. size_t phase = 0;
  175. for (size_t i = 0; i < APTDEC_IMG_WIDTH; i++) {
  176. float _left = convolve(&aptdec->row_buffer[i + 0], sync_pattern, SYNC_SIZE);
  177. float _middle = convolve(&aptdec->row_buffer[i + 1], sync_pattern, SYNC_SIZE);
  178. float _right = convolve(&aptdec->row_buffer[i + 2], sync_pattern, SYNC_SIZE);
  179. if (_middle > middle) {
  180. left = _left;
  181. middle = _middle;
  182. right = _right;
  183. phase = i + 1;
  184. }
  185. }
  186. // Frequency
  187. float bias = (left / middle) - (right / middle);
  188. aptdec->sync_frequency = 1.0f + bias / APTDEC_IMG_WIDTH / 4.0f;
  189. // Phase
  190. memcpy(&row[APTDEC_IMG_WIDTH], &aptdec->row_buffer[phase], (APTDEC_IMG_WIDTH - phase) * sizeof(float));
  191. memcpy(&row[APTDEC_IMG_WIDTH - phase], aptdec->row_buffer, phase * sizeof(float));
  192. return 1;
  193. }