comparison modulator.c @ 8:0249d0cecac4

Use interpolator to compute pulse up. Use interpolator to clamp values. Calculate speed. Do memory clear in routine to be more honest about the speed.
author Daniel O'Connor <darius@dons.net.au>
date Sun, 16 Feb 2025 14:36:33 +1030
parents 4ad473648949
children 3acdebd7eec7
comparison
equal deleted inserted replaced
7:4ad473648949 8:0249d0cecac4
71 // Trigger another pulse read out 71 // Trigger another pulse read out
72 dma_channel_set_read_addr(dma_chan, pulse_data, true); 72 dma_channel_set_read_addr(dma_chan, pulse_data, true);
73 } 73 }
74 74
75 // Calculate pulse shape data 75 // Calculate pulse shape data
76 // TODO: sense, gate, phase, active, T/R switch 76 // TODO: predistortion, proper sense, gate, phase, active, T/R switch
77 // Could encode them as bit stream like data but more compact would be 77 // Could encode them as bit stream like data but more compact would be
78 // (say) a list of counts to toggle pins at 78 // (say) a list of counts to toggle pins at
79 #define SENSE 0x01 79 #define SENSE 0x01
80 #define GATE 0x02 80 #define GATE 0x02
81 #define PHINV 0x04 81 #define PHINV 0x04
87 uint32_t shapesamples, nsamples, idx, bit1startup, bit1stopup; 87 uint32_t shapesamples, nsamples, idx, bit1startup, bit1stopup;
88 q_t dcscale, stepsize; 88 q_t dcscale, stepsize;
89 char tmps[20]; 89 char tmps[20];
90 interp_config cfg; 90 interp_config cfg;
91 91
92 // Setup lane 0 to generate index into shape table
93 // Mask start is 0 because we use 8 bit samples
94 cfg = interp_default_config();
95 interp_config_set_shift(&cfg, QBITS);
96 interp_config_set_mask(&cfg, 0, 32 - QBITS);
97 interp_config_set_blend(&cfg, true);
98 interp_set_config(interp0, 0, &cfg);
99
100 // Setup lane 1 to LERP each sample pair
101 cfg = interp_default_config();
102 interp_config_set_shift(&cfg, QBITS - 8);
103 interp_config_set_signed(&cfg, false);
104 interp_config_set_cross_input(&cfg, true); // unsigned blending
105 interp_set_config(interp0, 1, &cfg);
106
107 interp0->accum[0] = 0; // Initial offset into shape table
108 interp0->base[2] = (uintptr_t)shape; // Start of shape table
109
110 dcscale = qdiv(qsub(qint(255), qint(dcofs)), qint(255));
111 qsprint(dcscale, tmps, sizeof(tmps));
112 printf("dcscale = %s\n", tmps);
113
114 if (ncode == 1) { 92 if (ncode == 1) {
115 // Number of samples for half of the pulse 93 // Number of samples for half of the pulse
116 // Do division first so we don't overflow Q16.16 94 // Do division first so we don't overflow Q16.16
117 shapesamples = qtoi(qmul(qdiv(qint(plen), qint(100)), qint(shapelen / 2))); 95 shapesamples = qtoi(qmul(qdiv(qint(plen), qint(100)), qint(shapelen / 2)));
118 // Number of samples for everything 96 // Number of samples for everything
119 nsamples = shapesamples * 2 + slew1 + slew2; 97 // XXX: Need the +1 otherwise slew2 is truncated
98 nsamples = shapesamples * 2 + slew1 + slew2 + 1;
120 } else { 99 } else {
121 shapesamples = plen / 2; 100 shapesamples = plen / 2;
122 nsamples = shapesamples * 2 * ncode + codegap * (ncode - 1) + slew1 + slew2; 101 nsamples = shapesamples * 2 * ncode + codegap * (ncode - 1) + slew1 + slew2 + 1;
123 } 102 }
124 103
125 // Number of samples per step in the pulse shape 104 // Number of steps per samples in the pulse shape
126 stepsize = qdiv(qint(shapesamples), qint(shapelen)); 105 stepsize = qdiv(qint(shapelen), qint(shapesamples));
127 qsprint(stepsize, tmps, sizeof(tmps)); 106 qsprint(stepsize, tmps, sizeof(tmps));
128 printf("shapelen = %d shapesamples = %lu nsamples = %lu stepsize = %s\n", shapelen, shapesamples, nsamples, tmps); 107 printf("shapelen = %d shapesamples = %lu nsamples = %lu stepsize = %s\n", shapelen, shapesamples, nsamples, tmps);
129 108
109 // Check the requested pulse will not overflow given data
130 if (nsamples > datalen) { 110 if (nsamples > datalen) {
131 printf("Pulse too long (%ld > %u)\n", nsamples, datalen); 111 printf("Pulse too long (%ld > %u)\n", nsamples, datalen);
132 return 0; 112 return 0;
133 } 113 }
114 // Check it is not too short
134 if (shapesamples < 2) { 115 if (shapesamples < 2) {
135 printf("Pulse too short (%lu < %d)\n", shapesamples, 2); 116 printf("Pulse too short (%lu < %d)\n", shapesamples, 2);
136 return 0; 117 return 0;
137 } 118 }
119 // Or too long (will overflow for loop variable)
138 if (qtoi(shapesamples) > 65535) { 120 if (qtoi(shapesamples) > 65535) {
139 printf("Shape too long (%u > %d)\n", qtoi(shapesamples), 65535); 121 printf("Shape too long (%u > %d)\n", qtoi(shapesamples), 65535);
140 return 0; 122 return 0;
141 } 123 }
124
125 // Setup interp 0 lane 0 to generate index into shape table
126 // Mask start is 0 because we use 8 bit samples
127 cfg = interp_default_config();
128 interp_config_set_shift(&cfg, QBITS);
129 interp_config_set_mask(&cfg, 0, 32 - QBITS);
130 interp_config_set_blend(&cfg, true);
131 interp_set_config(interp0, 0, &cfg);
132
133 // Setup interp 0 lane 1 to LERP each sample pair
134 cfg = interp_default_config();
135 interp_config_set_shift(&cfg, QBITS - 8);
136 interp_config_set_signed(&cfg, false);
137 interp_config_set_cross_input(&cfg, true); // unsigned blending
138 interp_set_config(interp0, 1, &cfg);
139
140 // Setup interp 1 lane 0 to clamp 0-255
141 cfg = interp_default_config();
142 interp_config_set_clamp(&cfg, true);
143 interp_config_set_shift(&cfg, 0);
144 interp_config_set_mask(&cfg, 0, 8);
145 interp_config_set_signed(&cfg, false);
146 interp_set_config(interp1, 0, &cfg);
147 interp1->base[0] = 0;
148 interp1->base[1] = 255;
149
150 interp0->accum[0] = 0; // Initial offset into shape table
151 interp0->base[2] = (uintptr_t)shape; // Start of shape table
152
153 dcscale = qdiv(qsub(qint(256), qint(dcofs)), qint(255));
154 qsprint(dcscale, tmps, sizeof(tmps));
155 printf("dcscale = %s\n", tmps);
156
157 memset(pulse_data, 0, sizeof(pulse_data));
158 memset(pulse_ctrl, 0, sizeof(pulse_ctrl));
142 idx = 0; 159 idx = 0;
143 160
144 // Up slew 161 // Up slew
145 for (uint16_t i = 0; i < slew1; i++) { 162 for (uint16_t i = 0; i < slew1; i++) {
146 data[idx++] = qtoi(qdiv(qmul(qint(dcofs), qint(i)), qint(slew1))); 163 data[idx++] = qtoi(qdiv(qmul(qint(dcofs), qint(i)), qint(slew1)));
153 uint ctrltmp = PACTIVE; 170 uint ctrltmp = PACTIVE;
154 if (code[c] == '0') 171 if (code[c] == '0')
155 ctrltmp |= PHINV; 172 ctrltmp |= PHINV;
156 173
157 // Pulse up 174 // Pulse up
175 if (c == 0) {
176 interp0->accum[0] = 0; // Initial offset into shape table
177 interp0->base[2] = (uintptr_t)shape; // Start of shape table
178 }
158 for (uint16_t i = 0; i < shapesamples; i++) { 179 for (uint16_t i = 0; i < shapesamples; i++) {
159 if (c == 0) 180 if (c == 0) {
160 data[idx++] = dcofs + qtoi(qmul(qint(shape[qtoi(qdiv(qint(i), stepsize))]), dcscale)); 181 // Get sample pair
161 else 182 uint8_t *sample_pair = (uint8_t *) interp0->peek[2];
183 // Ask lane 1 for a LERP, using the lane 0 accumulator
184 interp0->base[0] = sample_pair[0];
185 interp0->base[1] = sample_pair[1];
186 uint8_t peek = interp0->peek[1];
187 // Apply DC offset scaling & clamp
188 interp1->accum[0] = dcofs + qtoi(qmul(qint(peek), dcscale));
189 data[idx++] = interp1->peek[0];
190 // Update interpolator for next point
191 interp0->add_raw[0] = stepsize;
192 } else
162 // Already done it before, just copy the previous instance 193 // Already done it before, just copy the previous instance
163 data[idx++] = data[bit1startup + i]; 194 data[idx++] = data[bit1startup + i];
164 ctrl[idx] = ctrltmp; 195 ctrl[idx] = ctrltmp;
165 } 196 }
166 if (c == 0) 197 if (c == 0)
167 bit1stopup = idx - 1; 198 bit1stopup = idx - 1;
168 // Pulse down 199 // Pulse down
169 // Since the pulse is symmetrical just copy the up slope in reverse 200 // Since the pulse is symmetrical just copy the up slope in reverse
201 // XXX: if we had asymmetrical predistortion this wouldn't be true
202 // In that case probably best to do a single pulse up/down, then
203 // add predistortion and copy it to other bits
170 for (uint16_t i = 0; i < shapesamples; i++) { 204 for (uint16_t i = 0; i < shapesamples; i++) {
171 data[idx++] = data[bit1stopup - i]; 205 data[idx++] = data[bit1stopup - i];
172 // Could replace this with a separate loop to poke it into place 206 // Could replace this with a separate loop to poke it into place
173 // Similarly for TR switch when implemented 207 // Similarly for TR switch when implemented
174 if (i == 0 && c == 0) 208 if (i == 0 && c == 0)
184 ctrl[idx] = ctrltmp; 218 ctrl[idx] = ctrltmp;
185 } 219 }
186 } 220 }
187 221
188 // Down slew 222 // Down slew
189 for (uint16_t i = 0; i < slew2; i++) { 223 for (uint16_t i = 0; i < slew2 + 1; i++) {
190 data[idx++] = qtoi(qdiv(qmul(qint(dcofs), qint(slew2 - i)), qint(slew2))); 224 data[idx++] = qtoi(qdiv(qmul(qint(dcofs), qint(slew2 - i)), qint(slew2)));
191 ctrl[idx] |= PACTIVE; 225 ctrl[idx] |= PACTIVE;
192 } 226 }
193 return idx - 1; 227 return idx - 1;
194 } 228 }
256 gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); 290 gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
257 291
258 //interp_test(pulse_data, shaped_trap); 292 //interp_test(pulse_data, shaped_trap);
259 293
260 uint32_t idx; 294 uint32_t idx;
261 uint16_t plen = 4000; 295 uint16_t plen;
262 char *code = "1110010"; 296 char *code;
263 //char *code = "10"; 297 if (0) {
298 plen = 8000;
299 code = "1110010";
300 } else {
301 plen = 53000;
302 code = "1";
303 }
304
264 uint8_t codegap = 4; 305 uint8_t codegap = 4;
265 uint8_t slew1 = 10; 306 uint8_t slew1 = 10;
266 uint8_t slew2 = 10; 307 uint8_t slew2 = 10;
267 uint8_t dcofs = 110; 308 uint8_t dcofs = 110;
268 memset(pulse_data, 0, sizeof(pulse_data));
269 memset(pulse_ctrl, 0, sizeof(pulse_ctrl));
270 then = get_absolute_time(); 309 then = get_absolute_time();
271 if ((idx = compute_pulse(pulse_data, pulse_ctrl, sizeof(pulse_data), 310 if ((idx = compute_pulse(pulse_data, pulse_ctrl, sizeof(pulse_data),
272 plen, code, strlen(code), 311 plen, code, strlen(code),
273 shaped_trap, sizeof(shaped_trap), 312 shaped_trap, sizeof(shaped_trap),
274 codegap, slew1, slew2, dcofs)) == 0) { 313 codegap, slew1, slew2, dcofs)) == 0) {
275 printf("Failed to compute pulse\n"); 314 printf("Failed to compute pulse\n");
276 while (1) 315 while (1)
277 ; 316 ;
278 } 317 }
279 now = get_absolute_time(); 318 now = get_absolute_time();
280 printf("Pulse computation took %lld usec and created %lu samples\n", absolute_time_diff_us(then, now), idx); 319 unsigned long long diff = absolute_time_diff_us(then, now);
281 320 printf("Pulse computation took %lld usec and created %lu samples - %.1f nsec/sample\n",
321 diff, idx, (float)diff * 1000.0 / idx);
322 __breakpoint();
282 pwm_config c = pwm_get_default_config(); 323 pwm_config c = pwm_get_default_config();
283 // 120MHz / 250 = 480kHz base 324 // 120MHz / 250 = 480kHz base
284 // Maximum divisor is only 256 which limits the low end, 325 // Maximum divisor is only 256 which limits the low end,
285 // could further subdivide in the IRQ handler 326 // could further subdivide in the IRQ handler
286 pwm_config_set_clkdiv_int(&c, 250); 327 pwm_config_set_clkdiv_int(&c, 250);