changeset 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
files modulator.c
diffstat 1 files changed, 84 insertions(+), 43 deletions(-) [+]
line wrap: on
line diff
--- a/modulator.c	Sun Feb 16 10:09:05 2025 +1030
+++ b/modulator.c	Sun Feb 16 14:36:33 2025 +1030
@@ -73,7 +73,7 @@
 }
 
 // Calculate pulse shape data
-// TODO: sense, gate, phase, active, T/R switch
+// TODO: predistortion, proper sense, gate, phase, active, T/R switch
 // Could encode them as bit stream like data but more compact would be
 // (say) a list of counts to toggle pins at
 #define SENSE		0x01
@@ -89,7 +89,40 @@
   char tmps[20];
   interp_config cfg;
 
-  // Setup lane 0 to generate index into shape table
+  if (ncode == 1) {
+    // Number of samples for half of the pulse
+    // Do division first so we don't overflow Q16.16
+    shapesamples = qtoi(qmul(qdiv(qint(plen), qint(100)), qint(shapelen / 2)));
+    // Number of samples for everything
+    // XXX: Need the +1 otherwise slew2 is truncated
+    nsamples = shapesamples * 2 + slew1 + slew2 + 1;
+  } else {
+    shapesamples = plen / 2;
+    nsamples = shapesamples * 2 * ncode + codegap * (ncode - 1) + slew1 + slew2 + 1;
+  }
+
+  // Number of steps per samples in the pulse shape
+  stepsize = qdiv(qint(shapelen), qint(shapesamples));
+  qsprint(stepsize, tmps, sizeof(tmps));
+  printf("shapelen = %d shapesamples = %lu nsamples = %lu stepsize = %s\n", shapelen, shapesamples, nsamples, tmps);
+
+  // Check the requested pulse will not overflow given data
+  if (nsamples > datalen) {
+    printf("Pulse too long (%ld > %u)\n", nsamples, datalen);
+    return 0;
+  }
+  // Check it is not too short
+  if (shapesamples < 2) {
+    printf("Pulse too short (%lu < %d)\n", shapesamples, 2);
+    return 0;
+  }
+  // Or too long (will overflow for loop variable)
+  if (qtoi(shapesamples) > 65535) {
+    printf("Shape too long (%u > %d)\n", qtoi(shapesamples), 65535);
+    return 0;
+  }
+
+  // Setup interp 0 lane 0 to generate index into shape table
   // Mask start is 0 because we use 8 bit samples
   cfg = interp_default_config();
   interp_config_set_shift(&cfg, QBITS);
@@ -97,48 +130,32 @@
   interp_config_set_blend(&cfg, true);
   interp_set_config(interp0, 0, &cfg);
 
-  // Setup lane 1 to LERP each sample pair
+  // Setup interp 0 lane 1 to LERP each sample pair
   cfg = interp_default_config();
   interp_config_set_shift(&cfg, QBITS - 8);
   interp_config_set_signed(&cfg, false);
   interp_config_set_cross_input(&cfg, true); // unsigned blending
   interp_set_config(interp0, 1, &cfg);
 
+  // Setup interp 1 lane 0 to clamp 0-255
+  cfg = interp_default_config();
+  interp_config_set_clamp(&cfg, true);
+  interp_config_set_shift(&cfg, 0);
+  interp_config_set_mask(&cfg, 0, 8);
+  interp_config_set_signed(&cfg, false);
+  interp_set_config(interp1, 0, &cfg);
+  interp1->base[0] = 0;
+  interp1->base[1] = 255;
+
   interp0->accum[0] = 0; // Initial offset into shape table
   interp0->base[2] = (uintptr_t)shape; // Start of shape table
 
-  dcscale = qdiv(qsub(qint(255), qint(dcofs)), qint(255));
+  dcscale = qdiv(qsub(qint(256), qint(dcofs)), qint(255));
   qsprint(dcscale, tmps, sizeof(tmps));
   printf("dcscale = %s\n", tmps);
 
-  if (ncode == 1) {
-    // Number of samples for half of the pulse
-    // Do division first so we don't overflow Q16.16
-    shapesamples = qtoi(qmul(qdiv(qint(plen), qint(100)), qint(shapelen / 2)));
-    // Number of samples for everything
-    nsamples = shapesamples * 2 + slew1 + slew2;
-  } else {
-    shapesamples = plen / 2;
-    nsamples = shapesamples * 2 * ncode + codegap * (ncode - 1) + slew1 + slew2;
-  }
-
-  // Number of samples per step in the pulse shape
-  stepsize = qdiv(qint(shapesamples), qint(shapelen));
-  qsprint(stepsize, tmps, sizeof(tmps));
-  printf("shapelen = %d shapesamples = %lu nsamples = %lu stepsize = %s\n", shapelen, shapesamples, nsamples, tmps);
-
-  if (nsamples > datalen) {
-    printf("Pulse too long (%ld > %u)\n", nsamples, datalen);
-    return 0;
-  }
-  if (shapesamples < 2) {
-    printf("Pulse too short (%lu < %d)\n", shapesamples, 2);
-    return 0;
-  }
-  if (qtoi(shapesamples) > 65535) {
-    printf("Shape too long (%u > %d)\n", qtoi(shapesamples), 65535);
-    return 0;
-  }
+  memset(pulse_data, 0, sizeof(pulse_data));
+  memset(pulse_ctrl, 0, sizeof(pulse_ctrl));
   idx = 0;
 
   // Up slew
@@ -155,10 +172,24 @@
       ctrltmp |= PHINV;
 
     // Pulse up
+    if (c == 0) {
+      interp0->accum[0] = 0; // Initial offset into shape table
+      interp0->base[2] = (uintptr_t)shape; // Start of shape table
+    }
     for (uint16_t i = 0; i < shapesamples; i++) {
-      if (c == 0)
-	data[idx++] = dcofs + qtoi(qmul(qint(shape[qtoi(qdiv(qint(i), stepsize))]), dcscale));
-      else
+      if (c == 0) {
+	// Get sample pair
+	uint8_t *sample_pair = (uint8_t *) interp0->peek[2];
+	// Ask lane 1 for a LERP, using the lane 0 accumulator
+	interp0->base[0] = sample_pair[0];
+	interp0->base[1] = sample_pair[1];
+	uint8_t peek = interp0->peek[1];
+	// Apply DC offset scaling & clamp
+	interp1->accum[0] = dcofs + qtoi(qmul(qint(peek), dcscale));
+	data[idx++] = interp1->peek[0];
+	// Update interpolator for next point
+	interp0->add_raw[0] = stepsize;
+      } else
 	// Already done it before, just copy the previous instance
 	data[idx++] = data[bit1startup + i];
       ctrl[idx] = ctrltmp;
@@ -167,6 +198,9 @@
       bit1stopup = idx - 1;
     // Pulse down
     // Since the pulse is symmetrical just copy the up slope in reverse
+    // XXX: if we had asymmetrical predistortion this wouldn't be true
+    // In that case probably best to do a single pulse up/down, then
+    // add predistortion and copy it to other bits
     for (uint16_t i = 0; i < shapesamples; i++) {
       data[idx++] = data[bit1stopup - i];
       // Could replace this with a separate loop to poke it into place
@@ -186,7 +220,7 @@
   }
 
   // Down slew
-  for (uint16_t i = 0; i < slew2; i++) {
+  for (uint16_t i = 0; i < slew2 + 1; i++) {
     data[idx++] = qtoi(qdiv(qmul(qint(dcofs), qint(slew2 - i)), qint(slew2)));
     ctrl[idx] |= PACTIVE;
   }
@@ -258,15 +292,20 @@
     //interp_test(pulse_data, shaped_trap);
 
     uint32_t idx;
-    uint16_t plen = 4000;
-    char *code = "1110010";
-    //char *code = "10";
+    uint16_t plen;
+    char *code;
+    if (0) {
+      plen = 8000;
+      code = "1110010";
+    } else {
+      plen = 53000;
+      code = "1";
+    }
+
     uint8_t codegap = 4;
     uint8_t slew1 = 10;
     uint8_t slew2 = 10;
     uint8_t dcofs = 110;
-    memset(pulse_data, 0, sizeof(pulse_data));
-    memset(pulse_ctrl, 0, sizeof(pulse_ctrl));
     then = get_absolute_time();
     if ((idx = compute_pulse(pulse_data, pulse_ctrl, sizeof(pulse_data),
 			     plen, code, strlen(code),
@@ -277,8 +316,10 @@
 	;
     }
     now = get_absolute_time();
-    printf("Pulse computation took %lld usec and created %lu samples\n", absolute_time_diff_us(then, now), idx);
-
+    unsigned long long diff = absolute_time_diff_us(then, now);
+    printf("Pulse computation took %lld usec and created %lu samples - %.1f nsec/sample\n",
+	   diff, idx, (float)diff * 1000.0 / idx);
+    __breakpoint();
     pwm_config c = pwm_get_default_config();
     // 120MHz / 250 = 480kHz base
     // Maximum divisor is only 256 which limits the low end,