Mirror with some correction

Dependencies:   mbed FastIO FastPWM USBDevice

Committer:
arnoz
Date:
Fri Oct 01 08:19:46 2021 +0000
Revision:
116:7a67265d7c19
Parent:
104:6e06e0f4b476
- Correct information regarding your last merge

Who changed what in which revision?

UserRevisionLine numberNew contents of line
mjr 82:4f6209cb5c33 1 // Plunger sensor type for bar-code based absolute position encoders.
mjr 82:4f6209cb5c33 2 // This type of sensor uses an optical sensor that moves with the plunger
mjr 82:4f6209cb5c33 3 // along a guide rail with printed bar codes along its length that encode
mjr 82:4f6209cb5c33 4 // the absolute position at each point. We figure the plunger position
mjr 82:4f6209cb5c33 5 // by reading the bar code and decoding it into a position figure.
mjr 82:4f6209cb5c33 6 //
mjr 82:4f6209cb5c33 7 // The bar code has to be encoded in a specific format that we recognize.
mjr 87:8d35c74403af 8 // We use a reflected Gray code, optically encoded in black/white pixel
mjr 87:8d35c74403af 9 // patterns. Each bit is represented by a fixed-width area. Half the
mjr 87:8d35c74403af 10 // pixels in every bit are white, and half are black. A '0' bit is
mjr 87:8d35c74403af 11 // represented by black pixels in the left half and white pixels in the
mjr 87:8d35c74403af 12 // right half, and a '1' bit is white on the left and black on the right.
mjr 87:8d35c74403af 13 // To read a bit, we identify the set of pixels covering the bit's fixed
mjr 87:8d35c74403af 14 // area in the code, then we see if the left or right half is brighter.
mjr 87:8d35c74403af 15 //
mjr 87:8d35c74403af 16 // (This optical encoding scheme is based on Manchester coding, which is
mjr 87:8d35c74403af 17 // normally used in the context of serial protocols, but translates to
mjr 87:8d35c74403af 18 // bar codes straightforwardly. Replace the serial protocol's time
mjr 87:8d35c74403af 19 // dimension with the spatial dimension across the bar, and replace the
mjr 87:8d35c74403af 20 // high/low wire voltage levels with white/black pixels.)
mjr 82:4f6209cb5c33 21 //
mjr 86:e30a1f60f783 22 // Gray codes are ideal for this type of application. Gray codes are
mjr 86:e30a1f60f783 23 // defined such that each code point differs in exactly one bit from each
mjr 86:e30a1f60f783 24 // adjacent code point. This provides natural error correction when used
mjr 86:e30a1f60f783 25 // as a position scale, since any single-bit error will yield a code point
mjr 86:e30a1f60f783 26 // reading that's only one spot off from the true position. So a bit read
mjr 86:e30a1f60f783 27 // error acts like a reduction in precision. Likewise, any time the sensor
mjr 86:e30a1f60f783 28 // is halfway between two code points, only one bit will be ambiguous, so
mjr 86:e30a1f60f783 29 // the reading will come out as one of points on either side of the true
mjr 86:e30a1f60f783 30 // position. Finally, motion blur will have the same effect, of creating
mjr 86:e30a1f60f783 31 // ambiguity in the least significant bits, and thus giving us a reading
mjr 87:8d35c74403af 32 // that's correct to as many bits as we can make out.
mjr 82:4f6209cb5c33 33 //
mjr 87:8d35c74403af 34 // The half-and-half optical coding also has good properties for our
mjr 87:8d35c74403af 35 // purposes. The fixed-width bit regions require essentially no CPU work
mjr 87:8d35c74403af 36 // to find the bits, which is good because we're using a fairly slow CPU.
mjr 87:8d35c74403af 37 // The half white/half black coding of each pixel makes every pixel
mjr 87:8d35c74403af 38 // self-relative in terms of brightness, so we don't need to figure the
mjr 87:8d35c74403af 39 // black and white thresholds globally for the whole image. That makes
mjr 87:8d35c74403af 40 // the physical device engineering and installation easier because the
mjr 87:8d35c74403af 41 // software can tolerate a fairly wide range of lighting conditions.
mjr 82:4f6209cb5c33 42 //
mjr 82:4f6209cb5c33 43
mjr 82:4f6209cb5c33 44 #ifndef _BARCODESENSOR_H_
mjr 82:4f6209cb5c33 45 #define _BARCODESENSOR_H_
mjr 82:4f6209cb5c33 46
mjr 82:4f6209cb5c33 47 #include "plunger.h"
mjr 87:8d35c74403af 48
mjr 87:8d35c74403af 49 // Gray code to binary mapping for our special coding. This is a custom
mjr 87:8d35c74403af 50 // 7-bit code, minimum run length 6, 110 positions populated. The minimum
mjr 87:8d35c74403af 51 // run length is the minimum number of consecutive code points where each
mjr 87:8d35c74403af 52 // bit must remain fixed. For out optical coding, this defines the smallest
mjr 87:8d35c74403af 53 // "island" size for a black or white bar horizontally. Small features are
mjr 87:8d35c74403af 54 // prone to light scattering that makes them appear gray on the sensor.
mjr 87:8d35c74403af 55 // Larger features are less subject to scatter, making them easier to
mjr 87:8d35c74403af 56 // distinguish by brightness level.
mjr 87:8d35c74403af 57 static const uint8_t grayToBin[] = {
mjr 87:8d35c74403af 58 0, 1, 83, 2, 71, 100, 84, 3, 69, 102, 82, 128, 70, 101, 57, 4, // 0-15
mjr 87:8d35c74403af 59 35, 50, 36, 37, 86, 87, 85, 128, 34, 103, 21, 104, 128, 128, 20, 5, // 16-31
mjr 87:8d35c74403af 60 11, 128, 24, 25, 98, 99, 97, 40, 68, 67, 81, 80, 55, 54, 56, 41, // 32-47
mjr 87:8d35c74403af 61 10, 51, 23, 38, 128, 52, 128, 39, 9, 66, 22, 128, 8, 53, 7, 6, // 48-63
mjr 87:8d35c74403af 62 47, 14, 60, 128, 72, 15, 59, 16, 46, 91, 93, 92, 45, 128, 58, 17, // 64-79
mjr 87:8d35c74403af 63 48, 49, 61, 62, 73, 88, 74, 75, 33, 90, 106, 105, 32, 89, 19, 18, // 80-95
mjr 87:8d35c74403af 64 12, 13, 95, 26, 128, 28, 96, 27, 128, 128, 94, 79, 44, 29, 43, 42, // 96-111
mjr 87:8d35c74403af 65 128, 64, 128, 63, 110, 128, 109, 76, 128, 65, 107, 78, 31, 30, 108, 77 // 112-127
mjr 87:8d35c74403af 66 };
mjr 87:8d35c74403af 67
mjr 87:8d35c74403af 68
mjr 87:8d35c74403af 69 // Auto-exposure counter
mjr 87:8d35c74403af 70 class BarCodeExposureCounter
mjr 87:8d35c74403af 71 {
mjr 87:8d35c74403af 72 public:
mjr 87:8d35c74403af 73 BarCodeExposureCounter()
mjr 87:8d35c74403af 74 {
mjr 87:8d35c74403af 75 nDark = 0;
mjr 87:8d35c74403af 76 nBright = 0;
mjr 87:8d35c74403af 77 nZero = 0;
mjr 87:8d35c74403af 78 nSat = 0;
mjr 87:8d35c74403af 79 }
mjr 87:8d35c74403af 80
mjr 87:8d35c74403af 81 inline void count(int pix)
mjr 87:8d35c74403af 82 {
mjr 87:8d35c74403af 83 if (pix <= 2)
mjr 87:8d35c74403af 84 ++nZero;
mjr 87:8d35c74403af 85 else if (pix < 12)
mjr 87:8d35c74403af 86 ++nDark;
mjr 87:8d35c74403af 87 else if (pix >= 253)
mjr 87:8d35c74403af 88 ++nSat;
mjr 87:8d35c74403af 89 else if (pix > 200)
mjr 87:8d35c74403af 90 ++nBright;
mjr 87:8d35c74403af 91 }
mjr 87:8d35c74403af 92
mjr 87:8d35c74403af 93 int nDark; // dark pixels
mjr 87:8d35c74403af 94 int nBright; // bright pixels
mjr 87:8d35c74403af 95 int nZero; // pixels at zero brightness
mjr 87:8d35c74403af 96 int nSat; // pixels at full saturation
mjr 87:8d35c74403af 97 };
mjr 87:8d35c74403af 98
mjr 82:4f6209cb5c33 99
mjr 82:4f6209cb5c33 100 // Base class for bar-code sensors
mjr 86:e30a1f60f783 101 //
mjr 86:e30a1f60f783 102 // This is a template class with template parameters for the bar
mjr 86:e30a1f60f783 103 // code pixel structure. The bar code layout is fixed for a given
mjr 86:e30a1f60f783 104 // sensor type. We can assume fixed pixel sizes because we don't
mjr 86:e30a1f60f783 105 // have to process arbitrary images. We only have to read scales
mjr 86:e30a1f60f783 106 // specially prepared for this application, so we can count on them
mjr 86:e30a1f60f783 107 // being printed at an exact size relative to the sensor pixels.
mjr 86:e30a1f60f783 108 //
mjr 86:e30a1f60f783 109 // nBits = Number of bits in the code
mjr 86:e30a1f60f783 110 //
mjr 86:e30a1f60f783 111 // leftBarWidth = Width in pixels of delimiting left bar. The code is
mjr 86:e30a1f60f783 112 // delimited by a black bar on the "left" end, nearest pixel 0. This
mjr 86:e30a1f60f783 113 // gives the pixel width of the bar.
mjr 86:e30a1f60f783 114 //
mjr 86:e30a1f60f783 115 // leftBarMaxOfs = Maximum offset of the delimiting bar from the left
mjr 86:e30a1f60f783 116 // edge of the sensor (pixel 0), in pixels
mjr 86:e30a1f60f783 117 //
mjr 86:e30a1f60f783 118 // bitWidth = Width of each bit in pixels. This is the width of the
mjr 86:e30a1f60f783 119 // full bit, including both "half bits" - it's the full white/black or
mjr 86:e30a1f60f783 120 // black/white pattern area.
mjr 86:e30a1f60f783 121
mjr 87:8d35c74403af 122 struct BarCodeProcessResult
mjr 87:8d35c74403af 123 {
mjr 87:8d35c74403af 124 int pixofs;
mjr 87:8d35c74403af 125 int raw;
mjr 87:8d35c74403af 126 int mask;
mjr 87:8d35c74403af 127 };
mjr 87:8d35c74403af 128
mjr 86:e30a1f60f783 129 template <int nBits, int leftBarWidth, int leftBarMaxOfs, int bitWidth>
mjr 87:8d35c74403af 130 class PlungerSensorBarCode: public PlungerSensorImage<BarCodeProcessResult>
mjr 82:4f6209cb5c33 131 {
mjr 82:4f6209cb5c33 132 public:
mjr 104:6e06e0f4b476 133 PlungerSensorBarCode(PlungerSensorImageInterface &sensor, int npix)
mjr 87:8d35c74403af 134 : PlungerSensorImage(sensor, npix, (1 << nBits) - 1)
mjr 87:8d35c74403af 135 {
mjr 87:8d35c74403af 136 startOfs = 0;
mjr 87:8d35c74403af 137 }
mjr 87:8d35c74403af 138
mjr 87:8d35c74403af 139 // process a configuration change
mjr 87:8d35c74403af 140 virtual void onConfigChange(int varno, Config &cfg)
mjr 87:8d35c74403af 141 {
mjr 87:8d35c74403af 142 // check for bar-code variables
mjr 87:8d35c74403af 143 switch (varno)
mjr 87:8d35c74403af 144 {
mjr 87:8d35c74403af 145 case 20:
mjr 87:8d35c74403af 146 // bar code offset
mjr 87:8d35c74403af 147 startOfs = cfg.plunger.barCode.startPix;
mjr 87:8d35c74403af 148 break;
mjr 87:8d35c74403af 149 }
mjr 87:8d35c74403af 150
mjr 87:8d35c74403af 151 // do the generic work
mjr 87:8d35c74403af 152 PlungerSensorImage::onConfigChange(varno, cfg);
mjr 87:8d35c74403af 153 }
mjr 87:8d35c74403af 154
mjr 87:8d35c74403af 155 protected:
mjr 86:e30a1f60f783 156 // process the image
mjr 87:8d35c74403af 157 virtual bool process(const uint8_t *pix, int npix, int &pos, BarCodeProcessResult &res)
mjr 82:4f6209cb5c33 158 {
mjr 87:8d35c74403af 159 // adjust auto-exposure
mjr 87:8d35c74403af 160 adjustExposure(pix, npix);
mjr 87:8d35c74403af 161
mjr 87:8d35c74403af 162 // clear the result descriptor
mjr 87:8d35c74403af 163 res.pixofs = 0;
mjr 87:8d35c74403af 164 res.raw = 0;
mjr 87:8d35c74403af 165 res.mask = 0;
mjr 87:8d35c74403af 166
mjr 86:e30a1f60f783 167 #if 0 // $$$
mjr 86:e30a1f60f783 168
mjr 86:e30a1f60f783 169 // scan from the left edge until we find the fixed '0' start bit
mjr 86:e30a1f60f783 170 for (int i = 0 ; i < leftBarMaxOfs ; ++i, ++pix)
mjr 86:e30a1f60f783 171 {
mjr 86:e30a1f60f783 172 // check for the '0' bit
mjr 86:e30a1f60f783 173 if (readBit8(pix) == 0)
mjr 86:e30a1f60f783 174 {
mjr 86:e30a1f60f783 175 // got it - skip the start bit
mjr 86:e30a1f60f783 176 pix += bitWidth;
mjr 86:e30a1f60f783 177
mjr 86:e30a1f60f783 178 // read the gray code bits
mjr 86:e30a1f60f783 179 int gray = 0;
mjr 86:e30a1f60f783 180 for (int j = 0 ; j < nBits ; ++j, pix += bitWidth)
mjr 86:e30a1f60f783 181 {
mjr 86:e30a1f60f783 182 // read the bit; return failure if we can't decode a bit
mjr 86:e30a1f60f783 183 int bit = readBit8(pix);
mjr 86:e30a1f60f783 184 if (bit < 0)
mjr 86:e30a1f60f783 185 return false;
mjr 86:e30a1f60f783 186
mjr 86:e30a1f60f783 187 // shift it into the code
mjr 86:e30a1f60f783 188 gray = (gray << 1) | bit;
mjr 86:e30a1f60f783 189 }
mjr 86:e30a1f60f783 190 }
mjr 86:e30a1f60f783 191
mjr 86:e30a1f60f783 192 // convert the gray code to binary
mjr 86:e30a1f60f783 193 int bin = grayToBin(gray);
mjr 86:e30a1f60f783 194
mjr 86:e30a1f60f783 195 // compute the parity of the binary value
mjr 86:e30a1f60f783 196 int parity = 0;
mjr 86:e30a1f60f783 197 for (int j = 0 ; j < nBits ; ++j)
mjr 86:e30a1f60f783 198 parity ^= bin >> j;
mjr 86:e30a1f60f783 199
mjr 86:e30a1f60f783 200 // figure the bit required for odd parity
mjr 86:e30a1f60f783 201 int odd = (parity & 0x01) ^ 0x01;
mjr 86:e30a1f60f783 202
mjr 86:e30a1f60f783 203 // read the check bit
mjr 86:e30a1f60f783 204 int bit = readBit8(pix);
mjr 86:e30a1f60f783 205 if (pix < 0)
mjr 86:e30a1f60f783 206 return false;
mjr 86:e30a1f60f783 207
mjr 86:e30a1f60f783 208 // check that it matches the expected parity
mjr 86:e30a1f60f783 209 if (bit != odd)
mjr 86:e30a1f60f783 210 return false;
mjr 86:e30a1f60f783 211
mjr 86:e30a1f60f783 212 // success
mjr 86:e30a1f60f783 213 pos = bin;
mjr 86:e30a1f60f783 214 return true;
mjr 86:e30a1f60f783 215 }
mjr 86:e30a1f60f783 216
mjr 86:e30a1f60f783 217 // no code found
mjr 82:4f6209cb5c33 218 return false;
mjr 86:e30a1f60f783 219
mjr 86:e30a1f60f783 220 #else
mjr 86:e30a1f60f783 221 int barStart = leftBarMaxOfs/2;
mjr 86:e30a1f60f783 222 if (leftBarWidth != 0) // $$$
mjr 86:e30a1f60f783 223 {
mjr 86:e30a1f60f783 224 // Find the black bar on the left side (nearest pixel 0) that
mjr 86:e30a1f60f783 225 // delimits the start of the bar code. To find it, first figure
mjr 86:e30a1f60f783 226 // the average brightness over the left margin up to the maximum
mjr 86:e30a1f60f783 227 // allowable offset, then look for the bar by finding the first
mjr 86:e30a1f60f783 228 // bar-width run of pixels that are darker than the average.
mjr 86:e30a1f60f783 229 int lsum = 0;
mjr 86:e30a1f60f783 230 for (int x = 1 ; x <= leftBarMaxOfs ; ++x)
mjr 86:e30a1f60f783 231 lsum += pix[x];
mjr 86:e30a1f60f783 232 int lavg = lsum / leftBarMaxOfs;
mjr 86:e30a1f60f783 233
mjr 86:e30a1f60f783 234 // now find the first dark edge
mjr 86:e30a1f60f783 235 for (int x = 0 ; x < leftBarMaxOfs ; ++x)
mjr 86:e30a1f60f783 236 {
mjr 86:e30a1f60f783 237 // if this pixel is dark, and one of the next two is dark,
mjr 86:e30a1f60f783 238 // take it as the edge
mjr 86:e30a1f60f783 239 if (pix[x] < lavg && (pix[x+1] < lavg || pix[x+2] < lavg))
mjr 86:e30a1f60f783 240 {
mjr 86:e30a1f60f783 241 // move past the delimier
mjr 86:e30a1f60f783 242 barStart = x + leftBarWidth;
mjr 86:e30a1f60f783 243 break;
mjr 86:e30a1f60f783 244 }
mjr 86:e30a1f60f783 245 }
mjr 86:e30a1f60f783 246 }
mjr 86:e30a1f60f783 247 else
mjr 86:e30a1f60f783 248 {
mjr 87:8d35c74403af 249 // start at the fixed pixel offset
mjr 87:8d35c74403af 250 barStart = startOfs;
mjr 86:e30a1f60f783 251 }
mjr 86:e30a1f60f783 252
mjr 87:8d35c74403af 253 // Start with zero in the barcode and success mask. The mask
mjr 87:8d35c74403af 254 // indicates which bits we were able to read successfully: a
mjr 87:8d35c74403af 255 // '1' bit in the mask indicates that the corresponding bit
mjr 87:8d35c74403af 256 // position in 'barcode' was successfully read, a '0' bit means
mjr 87:8d35c74403af 257 // that the image was too fuzzy to read.
mjr 87:8d35c74403af 258 int barcode = 0, mask = 0;
mjr 87:8d35c74403af 259
mjr 86:e30a1f60f783 260 // Scan the bits
mjr 86:e30a1f60f783 261 for (int bit = 0, x0 = barStart; bit < nBits ; ++bit, x0 += bitWidth)
mjr 86:e30a1f60f783 262 {
mjr 87:8d35c74403af 263 #if 0
mjr 87:8d35c74403af 264 // Figure the extent of this bit. The last bit is double
mjr 87:8d35c74403af 265 // the width of the other bits, to give us a better chance
mjr 87:8d35c74403af 266 // of making out the small features of the last bit.
mjr 87:8d35c74403af 267 int w = bitWidth;
mjr 87:8d35c74403af 268 if (bit == nBits - 1) w *= 2;
mjr 87:8d35c74403af 269 #else
mjr 87:8d35c74403af 270 // width of the bit
mjr 87:8d35c74403af 271 const int w = bitWidth;
mjr 87:8d35c74403af 272 #endif
mjr 87:8d35c74403af 273
mjr 87:8d35c74403af 274 // figure the bit's internal pixel layout
mjr 87:8d35c74403af 275 int halfBitWidth = w / 2;
mjr 87:8d35c74403af 276 int x1 = x0 + halfBitWidth; // midpoint
mjr 87:8d35c74403af 277 int x2 = x0 + w; // right edge
mjr 87:8d35c74403af 278
mjr 87:8d35c74403af 279 // make sure we didn't go out of bounds
mjr 86:e30a1f60f783 280 if (x1 > npix) x1 = npix;
mjr 86:e30a1f60f783 281 if (x2 > npix) x2 = npix;
mjr 86:e30a1f60f783 282
mjr 87:8d35c74403af 283 #if 0
mjr 86:e30a1f60f783 284 // get the average of the pixels over the bit
mjr 86:e30a1f60f783 285 int sum = 0;
mjr 86:e30a1f60f783 286 for (int x = x0 ; x < x2 ; ++x)
mjr 86:e30a1f60f783 287 sum += pix[x];
mjr 87:8d35c74403af 288 int avg = sum / w;
mjr 86:e30a1f60f783 289 // Scan the left and right sections. Classify each
mjr 86:e30a1f60f783 290 // section according to whether the majority of its
mjr 86:e30a1f60f783 291 // pixels are above or below the local average.
mjr 86:e30a1f60f783 292 int lsum = 0, rsum = 0;
mjr 86:e30a1f60f783 293 for (int x = x0 + 1 ; x < x1 - 1 ; ++x)
mjr 86:e30a1f60f783 294 lsum += (pix[x] < avg ? 0 : 1);
mjr 86:e30a1f60f783 295 for (int x = x1 + 1 ; x < x2 - 1 ; ++x)
mjr 86:e30a1f60f783 296 rsum += (pix[x] < avg ? 0 : 1);
mjr 87:8d35c74403af 297 #else
mjr 87:8d35c74403af 298 // Sum the pixel readings in each half-bit. Ignore
mjr 87:8d35c74403af 299 // the first and last bit of each section, since these
mjr 87:8d35c74403af 300 // could be contaminated with scattered light from the
mjr 87:8d35c74403af 301 // adjacent half-bit. On the right half, hew to the
mjr 87:8d35c74403af 302 // right side if the overall pixel width is odd.
mjr 87:8d35c74403af 303 int lsum = 0, rsum = 0;
mjr 87:8d35c74403af 304 for (int x = x0 + 1 ; x < x1 - 1 ; ++x)
mjr 87:8d35c74403af 305 lsum += pix[x];
mjr 87:8d35c74403af 306 for (int x = x2 - halfBitWidth + 1 ; x < x2 - 1 ; ++x)
mjr 87:8d35c74403af 307 rsum += pix[x];
mjr 87:8d35c74403af 308 #endif
mjr 86:e30a1f60f783 309
mjr 87:8d35c74403af 310 // shift a zero bit into the code and success mask
mjr 87:8d35c74403af 311 barcode <<= 1;
mjr 87:8d35c74403af 312 mask <<= 1;
mjr 86:e30a1f60f783 313
mjr 87:8d35c74403af 314 // Brightness difference required per pixel. Higher values
mjr 87:8d35c74403af 315 // require greater contrast to make a reading, which reduces
mjr 87:8d35c74403af 316 // spurious readings at the cost of reducing the overall
mjr 87:8d35c74403af 317 // success rate. The right level depends on the quality of
mjr 87:8d35c74403af 318 // the optical system. Setting this to zero makes us maximally
mjr 87:8d35c74403af 319 // tolerant of low-contrast images, allowing for the simplest
mjr 87:8d35c74403af 320 // optical system. Our simple optical system suffers from
mjr 87:8d35c74403af 321 // poor focus, which in turn causes poor contrast in small
mjr 87:8d35c74403af 322 // features.
mjr 87:8d35c74403af 323 const int minDelta = 2;
mjr 87:8d35c74403af 324
mjr 87:8d35c74403af 325 // see if we could tell the difference in brightness
mjr 87:8d35c74403af 326 int delta = lsum - rsum;
mjr 87:8d35c74403af 327 if (delta < 0) delta = -delta;
mjr 87:8d35c74403af 328 if (delta > minDelta * w/2)
mjr 87:8d35c74403af 329 {
mjr 87:8d35c74403af 330 // got it - black/white = 0, white/black = 1
mjr 87:8d35c74403af 331 if (lsum > rsum) barcode |= 1;
mjr 87:8d35c74403af 332 mask |= 1;
mjr 87:8d35c74403af 333 }
mjr 86:e30a1f60f783 334 }
mjr 86:e30a1f60f783 335
mjr 86:e30a1f60f783 336 // decode the Gray code value to binary
mjr 87:8d35c74403af 337 pos = grayToBin[barcode];
mjr 86:e30a1f60f783 338
mjr 87:8d35c74403af 339 // set the results descriptor structure
mjr 87:8d35c74403af 340 res.pixofs = barStart;
mjr 87:8d35c74403af 341 res.raw = barcode;
mjr 87:8d35c74403af 342 res.mask = mask;
mjr 87:8d35c74403af 343
mjr 87:8d35c74403af 344 // return success if we decoded all bits, and the Gray-to-binary
mjr 87:8d35c74403af 345 // mapping was populated
mjr 87:8d35c74403af 346 return pos != (1 << nBits) && mask == ((1 << nBits) - 1);
mjr 86:e30a1f60f783 347 #endif
mjr 86:e30a1f60f783 348 }
mjr 86:e30a1f60f783 349
mjr 86:e30a1f60f783 350 // read a bar starting at the given pixel
mjr 86:e30a1f60f783 351 int readBit8(const uint8_t *pix)
mjr 86:e30a1f60f783 352 {
mjr 86:e30a1f60f783 353 // pull out the pixels for the bar
mjr 86:e30a1f60f783 354 uint8_t s[8];
mjr 86:e30a1f60f783 355 memcpy(s, pix, 8);
mjr 86:e30a1f60f783 356
mjr 86:e30a1f60f783 357 // sort them in brightness order (using an 8-element network sort)
mjr 86:e30a1f60f783 358 #define SWAP(a, b) if (s[a] > s[b]) { uint8_t tmp = s[a]; s[a] = s[b]; s[b] = tmp; }
mjr 86:e30a1f60f783 359 SWAP(0, 1);
mjr 86:e30a1f60f783 360 SWAP(2, 3);
mjr 86:e30a1f60f783 361 SWAP(0, 2);
mjr 86:e30a1f60f783 362 SWAP(1, 3);
mjr 86:e30a1f60f783 363 SWAP(1, 2);
mjr 86:e30a1f60f783 364 SWAP(4, 5);
mjr 86:e30a1f60f783 365 SWAP(6, 7);
mjr 86:e30a1f60f783 366 SWAP(4, 6);
mjr 86:e30a1f60f783 367 SWAP(5, 7);
mjr 86:e30a1f60f783 368 SWAP(5, 6);
mjr 86:e30a1f60f783 369 SWAP(0, 4);
mjr 86:e30a1f60f783 370 SWAP(1, 5);
mjr 86:e30a1f60f783 371 SWAP(1, 4);
mjr 86:e30a1f60f783 372 SWAP(2, 6);
mjr 86:e30a1f60f783 373 SWAP(3, 7);
mjr 86:e30a1f60f783 374 SWAP(3, 6);
mjr 86:e30a1f60f783 375 SWAP(2, 4);
mjr 86:e30a1f60f783 376 SWAP(3, 5);
mjr 86:e30a1f60f783 377 SWAP(3, 4);
mjr 86:e30a1f60f783 378 #undef SWAP
mjr 86:e30a1f60f783 379
mjr 86:e30a1f60f783 380 // figure the median brightness
mjr 86:e30a1f60f783 381 int median = (int(s[3]) + s[4] + 1) / 2;
mjr 86:e30a1f60f783 382
mjr 86:e30a1f60f783 383 // count pixels below the median on each side
mjr 86:e30a1f60f783 384 int ldark = 0, rdark = 0;
mjr 86:e30a1f60f783 385 for (int i = 0 ; i < 3 ; ++i)
mjr 86:e30a1f60f783 386 {
mjr 86:e30a1f60f783 387 if (pix[i] < median)
mjr 86:e30a1f60f783 388 ldark++;
mjr 86:e30a1f60f783 389 }
mjr 86:e30a1f60f783 390 for (int i = 4 ; i < 8 ; ++i)
mjr 86:e30a1f60f783 391 {
mjr 86:e30a1f60f783 392 if (pix[i] < median)
mjr 86:e30a1f60f783 393 rdark++;
mjr 86:e30a1f60f783 394 }
mjr 86:e30a1f60f783 395
mjr 86:e30a1f60f783 396 // we need >=3 dark + >=3 light or vice versa
mjr 86:e30a1f60f783 397 if (ldark >= 3 && rdark <= 1)
mjr 86:e30a1f60f783 398 {
mjr 86:e30a1f60f783 399 // dark + light = '0' bit
mjr 86:e30a1f60f783 400 return 0;
mjr 86:e30a1f60f783 401 }
mjr 86:e30a1f60f783 402 if (ldark <= 1 && rdark >= 3)
mjr 86:e30a1f60f783 403 {
mjr 86:e30a1f60f783 404 // light + dark = '1' bit
mjr 86:e30a1f60f783 405 return 1;
mjr 86:e30a1f60f783 406 }
mjr 86:e30a1f60f783 407 else
mjr 86:e30a1f60f783 408 {
mjr 86:e30a1f60f783 409 // ambiguous bit
mjr 86:e30a1f60f783 410 return -1;
mjr 86:e30a1f60f783 411 }
mjr 86:e30a1f60f783 412 }
mjr 86:e30a1f60f783 413
mjr 86:e30a1f60f783 414 // bar code sensor orientation is fixed
mjr 86:e30a1f60f783 415 virtual int getOrientation() const { return 1; }
mjr 86:e30a1f60f783 416
mjr 87:8d35c74403af 417 // send extra status report headers
mjr 87:8d35c74403af 418 virtual void extraStatusHeaders(USBJoystick &js, BarCodeProcessResult &res)
mjr 87:8d35c74403af 419 {
mjr 87:8d35c74403af 420 // Send the bar code status report. We use coding type 1 (Gray code,
mjr 87:8d35c74403af 421 // Manchester pixel coding).
mjr 87:8d35c74403af 422 js.sendPlungerStatusBarcode(nBits, 1, res.pixofs, bitWidth, res.raw, res.mask);
mjr 87:8d35c74403af 423 }
mjr 87:8d35c74403af 424
mjr 82:4f6209cb5c33 425 // adjust the exposure
mjr 82:4f6209cb5c33 426 void adjustExposure(const uint8_t *pix, int npix)
mjr 82:4f6209cb5c33 427 {
mjr 86:e30a1f60f783 428 #if 1
mjr 86:e30a1f60f783 429 // The Manchester code has a nice property for auto exposure
mjr 86:e30a1f60f783 430 // control: each bit area has equal numbers of white and black
mjr 86:e30a1f60f783 431 // pixels. So we know exactly how the overall population of
mjr 86:e30a1f60f783 432 // pixels has to look: the bit area will be 50% black and 50%
mjr 86:e30a1f60f783 433 // white, and the margins will be all white. For maximum
mjr 86:e30a1f60f783 434 // contrast, target an exposure level where the black pixels
mjr 87:8d35c74403af 435 // are all below a certain brightness level and the white
mjr 86:e30a1f60f783 436 // pixels are all above. Start by figuring the number of
mjr 86:e30a1f60f783 437 // pixels above and below.
mjr 87:8d35c74403af 438 const int medianTarget = 160;
mjr 87:8d35c74403af 439 int nBelow = 0;
mjr 86:e30a1f60f783 440 for (int i = 0 ; i < npix ; ++i)
mjr 86:e30a1f60f783 441 {
mjr 87:8d35c74403af 442 if (pix[i] < medianTarget)
mjr 87:8d35c74403af 443 ++nBelow;
mjr 86:e30a1f60f783 444 }
mjr 86:e30a1f60f783 445
mjr 87:8d35c74403af 446 // Figure the desired number of black pixels: the left bar is
mjr 86:e30a1f60f783 447 // all black pixels, and 50% of each bit is black pixels.
mjr 87:8d35c74403af 448 int targetBelow = leftBarWidth + (nBits * bitWidth)/2;
mjr 86:e30a1f60f783 449
mjr 86:e30a1f60f783 450 // Increase exposure time if too many pixels are below the
mjr 86:e30a1f60f783 451 // halfway point; decrease it if too many pixels are above.
mjr 87:8d35c74403af 452 int d = nBelow - targetBelow;
mjr 86:e30a1f60f783 453 if (d > 5 || d < -5)
mjr 86:e30a1f60f783 454 {
mjr 86:e30a1f60f783 455 axcTime += d;
mjr 86:e30a1f60f783 456 }
mjr 86:e30a1f60f783 457
mjr 86:e30a1f60f783 458
mjr 86:e30a1f60f783 459 #elif 0 //$$$
mjr 86:e30a1f60f783 460 // Count exposure levels of pixels in the left and right margins
mjr 86:e30a1f60f783 461 BarCodeExposureCounter counter;
mjr 86:e30a1f60f783 462 for (int i = 0 ; i < leftBarMaxOfs/2 ; ++i)
mjr 86:e30a1f60f783 463 {
mjr 86:e30a1f60f783 464 // count the pixels at the left and right margins
mjr 86:e30a1f60f783 465 counter.count(pix[i]);
mjr 86:e30a1f60f783 466 counter.count(pix[npix - i - 1]);
mjr 86:e30a1f60f783 467 }
mjr 86:e30a1f60f783 468
mjr 86:e30a1f60f783 469 // The margin is all white, so try to get all of these pixels
mjr 86:e30a1f60f783 470 // in the bright range, but not saturated. That should give us
mjr 86:e30a1f60f783 471 // the best overall contrast throughout the image.
mjr 86:e30a1f60f783 472 if (counter.nSat > 0)
mjr 86:e30a1f60f783 473 {
mjr 86:e30a1f60f783 474 // overexposed - reduce exposure time
mjr 86:e30a1f60f783 475 if (axcTime > 5)
mjr 86:e30a1f60f783 476 axcTime -= 5;
mjr 86:e30a1f60f783 477 else
mjr 86:e30a1f60f783 478 axcTime = 0;
mjr 86:e30a1f60f783 479 }
mjr 86:e30a1f60f783 480 else if (counter.nBright < leftBarMaxOfs)
mjr 86:e30a1f60f783 481 {
mjr 86:e30a1f60f783 482 // they're not all in the bright range - increase exposure time
mjr 86:e30a1f60f783 483 axcTime += 5;
mjr 86:e30a1f60f783 484 }
mjr 86:e30a1f60f783 485
mjr 86:e30a1f60f783 486 #else // $$$
mjr 82:4f6209cb5c33 487 // Count the number of pixels near total darkness and
mjr 82:4f6209cb5c33 488 // total saturation
mjr 86:e30a1f60f783 489 int nZero = 0, nDark = 0, nBri = 0, nSat = 0;
mjr 82:4f6209cb5c33 490 for (int i = 0 ; i < npix ; ++i)
mjr 82:4f6209cb5c33 491 {
mjr 82:4f6209cb5c33 492 int pi = pix[i];
mjr 86:e30a1f60f783 493 if (pi <= 2)
mjr 86:e30a1f60f783 494 ++nZero;
mjr 86:e30a1f60f783 495 else if (pi < 12)
mjr 82:4f6209cb5c33 496 ++nDark;
mjr 86:e30a1f60f783 497 else if (pi >= 254)
mjr 82:4f6209cb5c33 498 ++nSat;
mjr 86:e30a1f60f783 499 else if (pi > 242)
mjr 86:e30a1f60f783 500 ++nBri;
mjr 82:4f6209cb5c33 501 }
mjr 82:4f6209cb5c33 502
mjr 82:4f6209cb5c33 503 // If more than 30% of pixels are near total darkness, increase
mjr 82:4f6209cb5c33 504 // the exposure time. If more than 30% are near total saturation,
mjr 82:4f6209cb5c33 505 // decrease the exposure time.
mjr 86:e30a1f60f783 506 int pct5 = uint32_t(npix * 3277) >> 16;
mjr 82:4f6209cb5c33 507 int pct30 = uint32_t(npix * 19661) >> 16;
mjr 82:4f6209cb5c33 508 int pct50 = uint32_t(npix) >> 1;
mjr 86:e30a1f60f783 509 if (nSat == 0)
mjr 86:e30a1f60f783 510 {
mjr 86:e30a1f60f783 511 // no saturated pixels - increase exposure time
mjr 86:e30a1f60f783 512 axcTime += 5;
mjr 86:e30a1f60f783 513 }
mjr 86:e30a1f60f783 514 else if (nSat > pct5)
mjr 86:e30a1f60f783 515 {
mjr 86:e30a1f60f783 516 if (axcTime > 5)
mjr 86:e30a1f60f783 517 axcTime -= 5;
mjr 86:e30a1f60f783 518 else
mjr 86:e30a1f60f783 519 axcTime = 0;
mjr 86:e30a1f60f783 520 }
mjr 86:e30a1f60f783 521 else if (nZero == 0)
mjr 86:e30a1f60f783 522 {
mjr 86:e30a1f60f783 523 // no totally dark pixels - decrease exposure time
mjr 86:e30a1f60f783 524 if (axcTime > 5)
mjr 86:e30a1f60f783 525 axcTime -= 5;
mjr 86:e30a1f60f783 526 else
mjr 86:e30a1f60f783 527 axcTime = 0;
mjr 86:e30a1f60f783 528 }
mjr 86:e30a1f60f783 529 else if (nZero > pct5)
mjr 86:e30a1f60f783 530 {
mjr 86:e30a1f60f783 531 axcTime += 5;
mjr 86:e30a1f60f783 532 }
mjr 86:e30a1f60f783 533 else if (nZero > pct30 || (nDark > pct50 && nSat < pct30))
mjr 82:4f6209cb5c33 534 {
mjr 82:4f6209cb5c33 535 // very dark - increase exposure time a lot
mjr 82:4f6209cb5c33 536 if (axcTime < 450)
mjr 82:4f6209cb5c33 537 axcTime += 50;
mjr 82:4f6209cb5c33 538 }
mjr 82:4f6209cb5c33 539 else if (nDark > pct30 && nSat < pct30)
mjr 82:4f6209cb5c33 540 {
mjr 82:4f6209cb5c33 541 // dark - increase exposure time a bit
mjr 82:4f6209cb5c33 542 if (axcTime < 490)
mjr 82:4f6209cb5c33 543 axcTime += 10;
mjr 82:4f6209cb5c33 544 }
mjr 86:e30a1f60f783 545 else if (nSat > pct30 || (nBri > pct50 && nDark < pct30))
mjr 82:4f6209cb5c33 546 {
mjr 82:4f6209cb5c33 547 // very overexposed - decrease exposure time a lot
mjr 82:4f6209cb5c33 548 if (axcTime > 50)
mjr 82:4f6209cb5c33 549 axcTime -= 50;
mjr 82:4f6209cb5c33 550 else
mjr 82:4f6209cb5c33 551 axcTime = 0;
mjr 82:4f6209cb5c33 552 }
mjr 86:e30a1f60f783 553 else if (nBri > pct30 && nDark < pct30)
mjr 82:4f6209cb5c33 554 {
mjr 82:4f6209cb5c33 555 // overexposed - decrease exposure time a little
mjr 82:4f6209cb5c33 556 if (axcTime > 10)
mjr 82:4f6209cb5c33 557 axcTime -= 10;
mjr 82:4f6209cb5c33 558 else
mjr 82:4f6209cb5c33 559 axcTime = 0;
mjr 82:4f6209cb5c33 560 }
mjr 86:e30a1f60f783 561 #endif
mjr 86:e30a1f60f783 562
mjr 100:1ff35c07217c 563 // don't allow the exposure time to go below 0 or over 2.5ms
mjr 86:e30a1f60f783 564 if (int(axcTime) < 0)
mjr 86:e30a1f60f783 565 axcTime = 0;
mjr 86:e30a1f60f783 566 if (axcTime > 2500)
mjr 86:e30a1f60f783 567 axcTime = 2500;
mjr 82:4f6209cb5c33 568 }
mjr 82:4f6209cb5c33 569
mjr 87:8d35c74403af 570 #if 0
mjr 87:8d35c74403af 571 // convert a reflected Gray code value (up to 16 bits) to binary
mjr 87:8d35c74403af 572 static inline int grayToBin(int grayval)
mjr 82:4f6209cb5c33 573 {
mjr 87:8d35c74403af 574 int temp = grayval ^ (grayval >> 8);
mjr 87:8d35c74403af 575 temp ^= (temp >> 4);
mjr 87:8d35c74403af 576 temp ^= (temp >> 2);
mjr 87:8d35c74403af 577 temp ^= (temp >> 1);
mjr 87:8d35c74403af 578 return temp;
mjr 82:4f6209cb5c33 579 }
mjr 87:8d35c74403af 580 #endif
mjr 87:8d35c74403af 581
mjr 87:8d35c74403af 582 // bar code starting pixel offset
mjr 87:8d35c74403af 583 int startOfs;
mjr 82:4f6209cb5c33 584 };
mjr 82:4f6209cb5c33 585
mjr 82:4f6209cb5c33 586 #endif