Mike R / Mbed 2 deprecated Pinscape_Controller_V2

Dependencies:   mbed FastIO FastPWM USBDevice

Fork of Pinscape_Controller by Mike R

Committer:
mjr
Date:
Sat Mar 05 00:16:52 2016 +0000
Revision:
52:8298b2a73eb2
Parent:
51:57eb311faafa
Child:
53:9b2611964afc
New calibration procedure - attempt #1, with separate calibration release sensingi

Who changed what in which revision?

UserRevisionLine numberNew contents of line
mjr 17:ab3cec0c8bf4 1 // CCD plunger sensor
mjr 17:ab3cec0c8bf4 2 //
mjr 35:e959ffba78fd 3 // This class implements our generic plunger sensor interface for the
mjr 35:e959ffba78fd 4 // TAOS TSL1410R and TSL1412R linear sensor arrays. Physically, these
mjr 35:e959ffba78fd 5 // sensors are installed with their image window running parallel to
mjr 35:e959ffba78fd 6 // the plunger rod, spanning the travel range of the plunger tip.
mjr 35:e959ffba78fd 7 // A light source is positioned on the opposite side of the rod, so
mjr 35:e959ffba78fd 8 // that the rod casts a shadow on the sensor. We sense the position
mjr 35:e959ffba78fd 9 // by looking for the edge of the shadow.
mjr 17:ab3cec0c8bf4 10
mjr 35:e959ffba78fd 11 #include "plunger.h"
mjr 17:ab3cec0c8bf4 12
mjr 17:ab3cec0c8bf4 13
mjr 25:e22b88bd783a 14 // PlungerSensor interface implementation for the CCD
mjr 35:e959ffba78fd 15 class PlungerSensorCCD: public PlungerSensor
mjr 17:ab3cec0c8bf4 16 {
mjr 17:ab3cec0c8bf4 17 public:
mjr 47:df7a88cd249c 18 PlungerSensorCCD(int nativePix, PinName si, PinName clock, PinName ao1, PinName ao2)
mjr 43:7a6364d82a41 19 : ccd(nativePix, si, clock, ao1, ao2)
mjr 17:ab3cec0c8bf4 20 {
mjr 47:df7a88cd249c 21 // we don't know the direction yet
mjr 47:df7a88cd249c 22 dir = 0;
mjr 47:df7a88cd249c 23
mjr 48:058ace2aed1d 24 // set the midpoint history arbitrarily to the absolute halfway point
mjr 48:058ace2aed1d 25 memset(midpt, 127, sizeof(midpt));
mjr 48:058ace2aed1d 26 midptIdx = 0;
mjr 48:058ace2aed1d 27
mjr 51:57eb311faafa 28 // no history readings yet
mjr 51:57eb311faafa 29 histIdx = 0;
mjr 17:ab3cec0c8bf4 30 }
mjr 17:ab3cec0c8bf4 31
mjr 17:ab3cec0c8bf4 32 // initialize
mjr 35:e959ffba78fd 33 virtual void init()
mjr 17:ab3cec0c8bf4 34 {
mjr 17:ab3cec0c8bf4 35 // flush any random power-on values from the CCD's integration
mjr 17:ab3cec0c8bf4 36 // capacitors, and start the first integration cycle
mjr 17:ab3cec0c8bf4 37 ccd.clear();
mjr 17:ab3cec0c8bf4 38 }
mjr 17:ab3cec0c8bf4 39
mjr 48:058ace2aed1d 40 // Read the plunger position
mjr 48:058ace2aed1d 41 virtual bool read(PlungerReading &r)
mjr 17:ab3cec0c8bf4 42 {
mjr 48:058ace2aed1d 43 // start reading the next pixel array - this also waits for any
mjr 48:058ace2aed1d 44 // previous read to finish, ensuring that we have stable pixel
mjr 48:058ace2aed1d 45 // data in the capture buffer
mjr 47:df7a88cd249c 46 ccd.startCapture();
mjr 44:b5ac89b9cd5d 47
mjr 48:058ace2aed1d 48 // get the image array from the last capture
mjr 47:df7a88cd249c 49 uint8_t *pix;
mjr 47:df7a88cd249c 50 int n;
mjr 48:058ace2aed1d 51 uint32_t tpix;
mjr 48:058ace2aed1d 52 ccd.getPix(pix, n, tpix);
mjr 17:ab3cec0c8bf4 53
mjr 48:058ace2aed1d 54 // process the pixels and look for the edge position
mjr 48:058ace2aed1d 55 int pixpos;
mjr 48:058ace2aed1d 56 if (process(pix, n, pixpos, 0))
mjr 51:57eb311faafa 57 {
mjr 52:8298b2a73eb2 58 // run the position through the anti-jitter filter
mjr 52:8298b2a73eb2 59 filter(pixpos);
mjr 52:8298b2a73eb2 60
mjr 48:058ace2aed1d 61 // Normalize to the 16-bit range. Our reading from the
mjr 48:058ace2aed1d 62 // sensor is a pixel position, 0..n-1. To rescale to the
mjr 48:058ace2aed1d 63 // normalized range, figure pixpos*65535/(n-1).
mjr 48:058ace2aed1d 64 r.pos = uint16_t(((pixpos << 16) - pixpos) / (n-1));
mjr 48:058ace2aed1d 65 r.t = tpix;
mjr 44:b5ac89b9cd5d 66
mjr 47:df7a88cd249c 67 // success
mjr 47:df7a88cd249c 68 return true;
mjr 47:df7a88cd249c 69 }
mjr 47:df7a88cd249c 70 else
mjr 47:df7a88cd249c 71 {
mjr 47:df7a88cd249c 72 // no position found
mjr 47:df7a88cd249c 73 return false;
mjr 47:df7a88cd249c 74 }
mjr 47:df7a88cd249c 75 }
mjr 17:ab3cec0c8bf4 76
mjr 47:df7a88cd249c 77 // Process an image. Applies noise reduction and looks for edges.
mjr 47:df7a88cd249c 78 // If we detect the plunger position, we set 'pos' to the pixel location
mjr 48:058ace2aed1d 79 // of the edge and return true; otherwise we return false. The 'pos'
mjr 48:058ace2aed1d 80 // value returned, if any, is adjusted for sensor orientation so that
mjr 48:058ace2aed1d 81 // it reflects the logical plunger position.
mjr 48:058ace2aed1d 82 bool process(uint8_t *pix, int &n, int &pos, int visMode)
mjr 47:df7a88cd249c 83 {
mjr 48:058ace2aed1d 84 // Get the levels at each end
mjr 48:058ace2aed1d 85 int a = (int(pix[0]) + pix[1] + pix[2] + pix[3] + pix[4])/5;
mjr 48:058ace2aed1d 86 int b = (int(pix[n-1]) + pix[n-2] + pix[n-3] + pix[n-4] + pix[n-5])/5;
mjr 47:df7a88cd249c 87
mjr 48:058ace2aed1d 88 // Figure the sensor orientation based on the relative
mjr 48:058ace2aed1d 89 // brightness levels at the opposite ends of the image
mjr 48:058ace2aed1d 90 int pi;
mjr 48:058ace2aed1d 91 if (a > b+10)
mjr 48:058ace2aed1d 92 {
mjr 48:058ace2aed1d 93 // left end is brighter - standard orientation
mjr 48:058ace2aed1d 94 dir = 1;
mjr 48:058ace2aed1d 95 pi = 5;
mjr 48:058ace2aed1d 96 }
mjr 48:058ace2aed1d 97 else if (b > a+10)
mjr 48:058ace2aed1d 98 {
mjr 48:058ace2aed1d 99 // right end is brighter - reverse orientation
mjr 48:058ace2aed1d 100 dir = -1;
mjr 48:058ace2aed1d 101 pi = n - 6;
mjr 48:058ace2aed1d 102 }
mjr 48:058ace2aed1d 103 else if (dir != 0)
mjr 17:ab3cec0c8bf4 104 {
mjr 48:058ace2aed1d 105 // We don't have enough contrast to detect the orientation
mjr 48:058ace2aed1d 106 // from this image, so either the image is too overexposed
mjr 48:058ace2aed1d 107 // or underexposed to be useful, or the entire sensor is in
mjr 48:058ace2aed1d 108 // light or darkness. We'll assume the latter: the plunger
mjr 48:058ace2aed1d 109 // is blocking the whole window or isn't in the frame at
mjr 48:058ace2aed1d 110 // all. We'll also assume that the exposure level is
mjr 48:058ace2aed1d 111 // similar to that in recent frames where we *did* detect
mjr 48:058ace2aed1d 112 // the direction. This means that if the new exposure level
mjr 48:058ace2aed1d 113 // (which is about the same over the whole array) is less
mjr 48:058ace2aed1d 114 // than the recent midpoint, we must be entirely blocked
mjr 48:058ace2aed1d 115 // by the plunger, so it's all the way forward; if the
mjr 48:058ace2aed1d 116 // brightness is above the recent midpoint, we must be
mjr 48:058ace2aed1d 117 // entirely exposed, so the plunger is all the way back.
mjr 48:058ace2aed1d 118
mjr 48:058ace2aed1d 119 // figure the average of the recent midpoint brightnesses
mjr 48:058ace2aed1d 120 int sum = 0;
mjr 48:058ace2aed1d 121 for (int i = 0 ; i < countof(midpt) ; sum += midpt[i++]) ;
mjr 48:058ace2aed1d 122 sum /= 10;
mjr 48:058ace2aed1d 123
mjr 48:058ace2aed1d 124 // Figure the average of our two ends. We have very
mjr 48:058ace2aed1d 125 // little contrast overall, so we already know that the
mjr 48:058ace2aed1d 126 // two ends are about the same, but we can't expect the
mjr 48:058ace2aed1d 127 // lighting to be perfectly uniform. Averaging the ends
mjr 48:058ace2aed1d 128 // will smooth out variations due to light source placement,
mjr 48:058ace2aed1d 129 // sensor noise, etc.
mjr 48:058ace2aed1d 130 a = (a+b)/2;
mjr 48:058ace2aed1d 131
mjr 48:058ace2aed1d 132 // Check if we seem to be fully exposed or fully covered
mjr 48:058ace2aed1d 133 pos = a < sum ? 0 : n;
mjr 48:058ace2aed1d 134 return true;
mjr 48:058ace2aed1d 135 }
mjr 48:058ace2aed1d 136 else
mjr 48:058ace2aed1d 137 {
mjr 48:058ace2aed1d 138 // We can't detect the orientation from this image, and
mjr 48:058ace2aed1d 139 // we don't know it from previous images, so we have nothing
mjr 48:058ace2aed1d 140 // to go on. Give up and return failure.
mjr 48:058ace2aed1d 141 return false;
mjr 48:058ace2aed1d 142 }
mjr 48:058ace2aed1d 143
mjr 48:058ace2aed1d 144 // figure the midpoint brigthness
mjr 48:058ace2aed1d 145 int mid = (a+b)/2;
mjr 48:058ace2aed1d 146
mjr 48:058ace2aed1d 147 // Scan from the bright side looking for an edge
mjr 48:058ace2aed1d 148 for (int i = 5 ; i < n-5 ; ++i, pi += dir)
mjr 48:058ace2aed1d 149 {
mjr 48:058ace2aed1d 150 // check to see if we found a dark pixel
mjr 48:058ace2aed1d 151 if (pix[pi] < mid)
mjr 48:058ace2aed1d 152 {
mjr 48:058ace2aed1d 153 // make sure we have a sustained edge
mjr 48:058ace2aed1d 154 int ok = 0;
mjr 48:058ace2aed1d 155 int pi2 = pi + dir;
mjr 48:058ace2aed1d 156 for (int j = 0 ; j < 5 ; ++j, pi2 += dir)
mjr 48:058ace2aed1d 157 {
mjr 48:058ace2aed1d 158 // count this pixel if it's darker than the midpoint
mjr 48:058ace2aed1d 159 if (pix[pi2] < mid)
mjr 48:058ace2aed1d 160 ++ok;
mjr 48:058ace2aed1d 161 }
mjr 48:058ace2aed1d 162
mjr 48:058ace2aed1d 163 // if we're clearly in the dark section, we have our edge
mjr 48:058ace2aed1d 164 if (ok > 3)
mjr 48:058ace2aed1d 165 {
mjr 48:058ace2aed1d 166 // Success. Since we found an edge in this scan, save the
mjr 48:058ace2aed1d 167 // midpoint brightness level in our history list, to help
mjr 48:058ace2aed1d 168 // with any future frames with insufficient contrast.
mjr 48:058ace2aed1d 169 midpt[midptIdx++] = mid;
mjr 48:058ace2aed1d 170 midptIdx %= countof(midpt);
mjr 48:058ace2aed1d 171
mjr 48:058ace2aed1d 172 // return the detected position
mjr 48:058ace2aed1d 173 pos = i;
mjr 48:058ace2aed1d 174 return true;
mjr 48:058ace2aed1d 175 }
mjr 48:058ace2aed1d 176 }
mjr 17:ab3cec0c8bf4 177 }
mjr 17:ab3cec0c8bf4 178
mjr 48:058ace2aed1d 179 // no edge found
mjr 48:058ace2aed1d 180 return false;
mjr 48:058ace2aed1d 181 }
mjr 52:8298b2a73eb2 182
mjr 52:8298b2a73eb2 183 // Filter a result through the jitter reducer. We tend to have some
mjr 52:8298b2a73eb2 184 // very slight jitter - by a pixel or two - even when the plunger is
mjr 52:8298b2a73eb2 185 // stationary. This happens due to analog noise. In the theoretical
mjr 52:8298b2a73eb2 186 // ideal, analog noise wouldn't be a factor for this sensor design,
mjr 52:8298b2a73eb2 187 // in that we'd have enough contrast between the bright and dark
mjr 52:8298b2a73eb2 188 // regions that there'd be no ambiguity as to where the shadow edge
mjr 52:8298b2a73eb2 189 // falls. But in the real system, the shadow edge isn't perfectly
mjr 52:8298b2a73eb2 190 // sharp on the scale of our pixels, so the edge isn't an ideal
mjr 52:8298b2a73eb2 191 // digital 0-1 discontinuity but rather a ramp of gray levels over
mjr 52:8298b2a73eb2 192 // a few pixels. Our edge detector picks the pixel where we cross
mjr 52:8298b2a73eb2 193 // the midpoint brightness threshold. The exact midpoint can vary
mjr 52:8298b2a73eb2 194 // a little from frame to frame due to exposure length variations,
mjr 52:8298b2a73eb2 195 // light source variations, other stray light sources in the cabinet,
mjr 52:8298b2a73eb2 196 // ADC error, sensor pixel noise, and electrical noise. As the
mjr 52:8298b2a73eb2 197 // midpoint varies, the pixel that qualifies as the edge position
mjr 52:8298b2a73eb2 198 // can move by a pixel or two from one from to the next, even
mjr 52:8298b2a73eb2 199 // though the physical shadow isn't moving. This all adds up to
mjr 52:8298b2a73eb2 200 // some slight jitter in the final position reading.
mjr 52:8298b2a73eb2 201 //
mjr 52:8298b2a73eb2 202 // To reduce the jitter, we keep a short history of recent readings.
mjr 52:8298b2a73eb2 203 // When we see a new reading that's close to the whole string of
mjr 52:8298b2a73eb2 204 // recent readings, we peg the new reading to the consensus of the
mjr 52:8298b2a73eb2 205 // recent history. This smooths out these small variations without
mjr 52:8298b2a73eb2 206 // affecting response time or resolution.
mjr 52:8298b2a73eb2 207 void filter(int &pos)
mjr 52:8298b2a73eb2 208 {
mjr 52:8298b2a73eb2 209 // check to see if it's close to all of the history elements
mjr 52:8298b2a73eb2 210 const int dpos = 1;
mjr 52:8298b2a73eb2 211 bool isClose = true;
mjr 52:8298b2a73eb2 212 long sum = 0;
mjr 52:8298b2a73eb2 213 for (int i = 0 ; i < countof(hist) ; ++i)
mjr 47:df7a88cd249c 214 {
mjr 52:8298b2a73eb2 215 int ipos = hist[i];
mjr 52:8298b2a73eb2 216 sum += ipos;
mjr 52:8298b2a73eb2 217 if (pos > ipos + dpos || pos < ipos - dpos)
mjr 48:058ace2aed1d 218 {
mjr 52:8298b2a73eb2 219 isClose = false;
mjr 52:8298b2a73eb2 220 break;
mjr 17:ab3cec0c8bf4 221 }
mjr 17:ab3cec0c8bf4 222 }
mjr 17:ab3cec0c8bf4 223
mjr 52:8298b2a73eb2 224 // check if we're close to all recent readings
mjr 52:8298b2a73eb2 225 if (isClose)
mjr 48:058ace2aed1d 226 {
mjr 52:8298b2a73eb2 227 // We're close, so just stick to the average of recent
mjr 52:8298b2a73eb2 228 // readings. Note that we don't add the new reading to
mjr 52:8298b2a73eb2 229 // the history in this case. If the edge is about halfway
mjr 52:8298b2a73eb2 230 // between two pixels, the history will be about 50/50 on
mjr 52:8298b2a73eb2 231 // an ongoing basis, so if just kept adding samples we'd
mjr 52:8298b2a73eb2 232 // still jitter (just at a slightly reduced rate). By
mjr 52:8298b2a73eb2 233 // stalling the history when it looks like we're stationary,
mjr 52:8298b2a73eb2 234 // we'll just pick one of the pixels and stay there as long
mjr 52:8298b2a73eb2 235 // as the plunger stays where it is.
mjr 52:8298b2a73eb2 236 pos = sum/countof(hist);
mjr 48:058ace2aed1d 237 }
mjr 48:058ace2aed1d 238 else
mjr 48:058ace2aed1d 239 {
mjr 52:8298b2a73eb2 240 // This isn't near enough to the recent stationary position,
mjr 52:8298b2a73eb2 241 // so keep the new reading exactly as it is, and add it to the
mjr 52:8298b2a73eb2 242 // history.
mjr 52:8298b2a73eb2 243 hist[histIdx++] = pos;
mjr 52:8298b2a73eb2 244 histIdx %= countof(hist);
mjr 47:df7a88cd249c 245 }
mjr 44:b5ac89b9cd5d 246 }
mjr 44:b5ac89b9cd5d 247
mjr 52:8298b2a73eb2 248 // Send a status report to the joystick interface.
mjr 48:058ace2aed1d 249 // See plunger.h for details on the flags and visualization modes.
mjr 52:8298b2a73eb2 250 virtual void sendStatusReport(USBJoystick &js, uint8_t flags, uint8_t visMode)
mjr 17:ab3cec0c8bf4 251 {
mjr 48:058ace2aed1d 252 // start a capture
mjr 47:df7a88cd249c 253 ccd.startCapture();
mjr 47:df7a88cd249c 254
mjr 48:058ace2aed1d 255 // get the stable pixel array
mjr 47:df7a88cd249c 256 uint8_t *pix;
mjr 47:df7a88cd249c 257 int n;
mjr 48:058ace2aed1d 258 uint32_t t;
mjr 48:058ace2aed1d 259 ccd.getPix(pix, n, t);
mjr 52:8298b2a73eb2 260
mjr 52:8298b2a73eb2 261 // start a timer to measure the processing time
mjr 52:8298b2a73eb2 262 Timer pt;
mjr 52:8298b2a73eb2 263 pt.start();
mjr 52:8298b2a73eb2 264
mjr 52:8298b2a73eb2 265 // process the pixels and read the position
mjr 52:8298b2a73eb2 266 int pos;
mjr 52:8298b2a73eb2 267 if (process(pix, n, pos, visMode))
mjr 52:8298b2a73eb2 268 filter(pos);
mjr 52:8298b2a73eb2 269 else
mjr 52:8298b2a73eb2 270 pos = 0xFFFF;
mjr 47:df7a88cd249c 271
mjr 52:8298b2a73eb2 272 // note the processing time
mjr 52:8298b2a73eb2 273 uint32_t processTime = pt.read_us();
mjr 47:df7a88cd249c 274
mjr 47:df7a88cd249c 275 // if a low-res scan is desired, reduce to a subset of pixels
mjr 48:058ace2aed1d 276 if (flags & 0x01)
mjr 47:df7a88cd249c 277 {
mjr 48:058ace2aed1d 278 // figure how many sensor pixels we combine into each low-res pixel
mjr 48:058ace2aed1d 279 const int group = 8;
mjr 48:058ace2aed1d 280 int lowResPix = n / group;
mjr 48:058ace2aed1d 281
mjr 48:058ace2aed1d 282 // combine the pixels
mjr 47:df7a88cd249c 283 int src, dst;
mjr 48:058ace2aed1d 284 for (src = dst = 0 ; dst < lowResPix ; ++dst)
mjr 48:058ace2aed1d 285 {
mjr 52:8298b2a73eb2 286 // average this block of pixels
mjr 48:058ace2aed1d 287 int a = 0;
mjr 52:8298b2a73eb2 288 for (int j = 0 ; j < group ; ++j)
mjr 52:8298b2a73eb2 289 a += pix[src++];
mjr 48:058ace2aed1d 290
mjr 52:8298b2a73eb2 291 // we have the sum, so get the average
mjr 52:8298b2a73eb2 292 a /= group;
mjr 52:8298b2a73eb2 293
mjr 48:058ace2aed1d 294 // store the down-res'd pixel in the array
mjr 48:058ace2aed1d 295 pix[dst] = uint8_t(a);
mjr 48:058ace2aed1d 296 }
mjr 48:058ace2aed1d 297
mjr 52:8298b2a73eb2 298 // rescale the position for the reduced resolution
mjr 52:8298b2a73eb2 299 if (pos != 0xFFFF)
mjr 52:8298b2a73eb2 300 pos = pos * (lowResPix-1) / (n-1);
mjr 52:8298b2a73eb2 301
mjr 52:8298b2a73eb2 302 // update the pixel count to the reduced array size
mjr 52:8298b2a73eb2 303 n = lowResPix;
mjr 47:df7a88cd249c 304 }
mjr 43:7a6364d82a41 305
mjr 52:8298b2a73eb2 306 // send the sensor status report report
mjr 52:8298b2a73eb2 307 js.sendPlungerStatus(n, pos, dir, ccd.getAvgScanTime(), processTime);
mjr 52:8298b2a73eb2 308
mjr 52:8298b2a73eb2 309 // If we're not in calibration mode, send the pixels
mjr 52:8298b2a73eb2 310 extern bool plungerCalMode;
mjr 52:8298b2a73eb2 311 if (!plungerCalMode)
mjr 52:8298b2a73eb2 312 {
mjr 52:8298b2a73eb2 313 // send the pixels in report-sized chunks until we get them all
mjr 52:8298b2a73eb2 314 int idx = 0;
mjr 52:8298b2a73eb2 315 while (idx < n)
mjr 52:8298b2a73eb2 316 js.sendPlungerPix(idx, n, pix);
mjr 52:8298b2a73eb2 317 }
mjr 17:ab3cec0c8bf4 318
mjr 48:058ace2aed1d 319 // It takes us a while to send all of the pixels, since we have
mjr 48:058ace2aed1d 320 // to break them up into many USB reports. This delay means that
mjr 48:058ace2aed1d 321 // the sensor has been sitting there integrating for much longer
mjr 48:058ace2aed1d 322 // than usual, so the next frame read will be overexposed. To
mjr 48:058ace2aed1d 323 // mitigate this, make sure we don't have a capture running,
mjr 48:058ace2aed1d 324 // then clear the sensor and start a new capture.
mjr 48:058ace2aed1d 325 ccd.wait();
mjr 48:058ace2aed1d 326 ccd.clear();
mjr 47:df7a88cd249c 327 ccd.startCapture();
mjr 17:ab3cec0c8bf4 328 }
mjr 17:ab3cec0c8bf4 329
mjr 52:8298b2a73eb2 330 // get the average sensor scan time
mjr 52:8298b2a73eb2 331 virtual uint32_t getAvgScanTime() { return ccd.getAvgScanTime(); }
mjr 52:8298b2a73eb2 332
mjr 35:e959ffba78fd 333 protected:
mjr 44:b5ac89b9cd5d 334 // Sensor orientation. +1 means that the "tip" end - which is always
mjr 44:b5ac89b9cd5d 335 // the brighter end in our images - is at the 0th pixel in the array.
mjr 44:b5ac89b9cd5d 336 // -1 means that the tip is at the nth pixel in the array. 0 means
mjr 48:058ace2aed1d 337 // that we haven't figured it out yet. We automatically infer this
mjr 48:058ace2aed1d 338 // from the relative light levels at each end of the array when we
mjr 48:058ace2aed1d 339 // successfully find a shadow edge. The reason we save the information
mjr 48:058ace2aed1d 340 // is that we might occasionally get frames that are fully in shadow
mjr 48:058ace2aed1d 341 // or fully in light, and we can't infer the direction from such
mjr 48:058ace2aed1d 342 // frames. Saving the information from past frames gives us a fallback
mjr 48:058ace2aed1d 343 // when we can't infer it from the current frame. Note that we update
mjr 48:058ace2aed1d 344 // this each time we can infer the direction, so the device will adapt
mjr 48:058ace2aed1d 345 // on the fly even if the user repositions the sensor while the software
mjr 48:058ace2aed1d 346 // is running.
mjr 44:b5ac89b9cd5d 347 int dir;
mjr 51:57eb311faafa 348
mjr 51:57eb311faafa 349 // History of recent position readings. We keep a short history of
mjr 51:57eb311faafa 350 // readings so that we can apply some filtering to the data.
mjr 52:8298b2a73eb2 351 uint16_t hist[8];
mjr 51:57eb311faafa 352 int histIdx;
mjr 48:058ace2aed1d 353
mjr 48:058ace2aed1d 354 // History of midpoint brightness levels for the last few successful
mjr 48:058ace2aed1d 355 // scans. This is a circular buffer that we write on each scan where
mjr 48:058ace2aed1d 356 // we successfully detect a shadow edge. (It's circular, so we
mjr 48:058ace2aed1d 357 // effectively discard the oldest element whenever we write a new one.)
mjr 48:058ace2aed1d 358 //
mjr 48:058ace2aed1d 359 // The history is useful in cases where we have too little contrast
mjr 48:058ace2aed1d 360 // to detect an edge. In these cases, we assume that the entire sensor
mjr 48:058ace2aed1d 361 // is either in shadow or light, which can happen if the plunger is at
mjr 48:058ace2aed1d 362 // one extreme or the other such that the edge of its shadow is out of
mjr 48:058ace2aed1d 363 // the frame. (Ideally, the sensor should be positioned so that the
mjr 48:058ace2aed1d 364 // shadow edge is always in the frame, but it's not always possible
mjr 48:058ace2aed1d 365 // to do this given the constrained space within a cabinet.) The
mjr 48:058ace2aed1d 366 // history helps us decide which case we have - all shadow or all
mjr 48:058ace2aed1d 367 // light - by letting us compare our average pixel level in this
mjr 48:058ace2aed1d 368 // frame to the range in recent frames. This assumes that the
mjr 48:058ace2aed1d 369 // exposure varies minimally from frame to frame, which is usually
mjr 48:058ace2aed1d 370 // true because the physical installation (the light source and
mjr 48:058ace2aed1d 371 // sensor positions) are usually static.
mjr 48:058ace2aed1d 372 //
mjr 48:058ace2aed1d 373 // We always try first to infer the bright and dark levels from the
mjr 48:058ace2aed1d 374 // image, since this lets us adapt automatically to different exposure
mjr 48:058ace2aed1d 375 // levels. The exposure level can vary by integration time and the
mjr 48:058ace2aed1d 376 // intensity and positioning of the light source, and we want
mjr 48:058ace2aed1d 377 // to be as flexible as we can about both.
mjr 48:058ace2aed1d 378 uint8_t midpt[10];
mjr 48:058ace2aed1d 379 uint8_t midptIdx;
mjr 47:df7a88cd249c 380
mjr 44:b5ac89b9cd5d 381 public:
mjr 17:ab3cec0c8bf4 382 // the low-level interface to the CCD hardware
mjr 35:e959ffba78fd 383 TSL1410R ccd;
mjr 17:ab3cec0c8bf4 384 };
mjr 35:e959ffba78fd 385
mjr 35:e959ffba78fd 386
mjr 35:e959ffba78fd 387 // TSL1410R sensor
mjr 35:e959ffba78fd 388 class PlungerSensorTSL1410R: public PlungerSensorCCD
mjr 35:e959ffba78fd 389 {
mjr 35:e959ffba78fd 390 public:
mjr 35:e959ffba78fd 391 PlungerSensorTSL1410R(PinName si, PinName clock, PinName ao1, PinName ao2)
mjr 47:df7a88cd249c 392 : PlungerSensorCCD(1280, si, clock, ao1, ao2)
mjr 35:e959ffba78fd 393 {
mjr 35:e959ffba78fd 394 }
mjr 35:e959ffba78fd 395 };
mjr 35:e959ffba78fd 396
mjr 35:e959ffba78fd 397 // TSL1412R
mjr 35:e959ffba78fd 398 class PlungerSensorTSL1412R: public PlungerSensorCCD
mjr 35:e959ffba78fd 399 {
mjr 35:e959ffba78fd 400 public:
mjr 35:e959ffba78fd 401 PlungerSensorTSL1412R(PinName si, PinName clock, PinName ao1, PinName ao2)
mjr 47:df7a88cd249c 402 : PlungerSensorCCD(1536, si, clock, ao1, ao2)
mjr 35:e959ffba78fd 403 {
mjr 35:e959ffba78fd 404 }
mjr 35:e959ffba78fd 405 };