00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #include "Devices/BeoChip.H"
00039
00040 #include "Component/OptionManager.H"
00041 #include "Util/Assert.H"
00042 #include "rutz/compat_snprintf.h"
00043
00044 #define NUMSERVO 8
00045 #define NUMPWM 2
00046 #define NUMADC 2
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061 template<unsigned long int N> class binary
00062 { public: enum { bit = N % 10, value = bit + (((binary<N/10>::value)<<1)) }; };
00063 template <> class binary<0> { public: enum { bit = 0, value = 0 }; };
00064 #define BIN(N) (((byte(binary<(unsigned long int)(N##.0)>::value))))
00065
00066
00067 BeoChipListener::~BeoChipListener()
00068 { }
00069
00070
00071 void *BeoChip_run(void *c)
00072 {
00073 BeoChip *d = (BeoChip *)c;
00074 d->run();
00075 return NULL;
00076 }
00077
00078
00079 BeoChip::BeoChip(OptionManager& mgr, const std::string& descrName,
00080 const std::string& tagName) :
00081 ModelComponent(mgr, descrName, tagName),
00082 itsDevName("BeoChipDeviceName", this, "/dev/ttyS0"),
00083 itsLCDrows("BeoChipLCDrows", this, 4),
00084 itsLCDcols("BeoChipLCDcols", this, 20),
00085 itsUseRTSCTS("BeoChipUseRTSCTS", this, true)
00086 {
00087
00088 zeroS = new rutz::shared_ptr<NModelParam<int> >[NUMSERVO];
00089 minS = new rutz::shared_ptr<NModelParam<int> >[NUMSERVO];
00090 maxS = new rutz::shared_ptr<NModelParam<int> >[NUMSERVO];
00091 servopos = new byte[NUMSERVO];
00092 for (uint i = 0; i < NUMSERVO; i ++)
00093 {
00094 servopos[i] = 127; char buf[20];
00095 sprintf(buf, "ZeroS%d", i);
00096 zeroS[i] = NModelParam<int>::make(buf, this, 127);
00097 sprintf(buf, "MinS%d", i);
00098 minS[i] = NModelParam<int>::make(buf, this, 0);
00099 sprintf(buf, "MaxS%d", i);
00100 maxS[i] = NModelParam<int>::make(buf, this, 255);
00101 }
00102
00103 zeroP = new rutz::shared_ptr<NModelParam<int> >[NUMPWM];
00104 minP = new rutz::shared_ptr<NModelParam<int> >[NUMPWM];
00105 maxP = new rutz::shared_ptr<NModelParam<int> >[NUMPWM];
00106 pulseval = new short int[NUMPWM];
00107 for (uint i = 0; i < NUMPWM; i ++)
00108 {
00109 pulseval[i] = 1023; char buf[20];
00110 sprintf(buf, "ZeroP%d", i);
00111 zeroP[i] = NModelParam<int>::make(buf, this, 1023);
00112 sprintf(buf, "MinP%d", i);
00113 minP[i] = NModelParam<int>::make(buf, this, 0);
00114 sprintf(buf, "MaxP%d", i);
00115 maxP[i] = NModelParam<int>::make(buf, this, 2047);
00116 }
00117
00118 zeroA = new rutz::shared_ptr<NModelParam<int> >[NUMADC];
00119 minA = new rutz::shared_ptr<NModelParam<int> >[NUMADC];
00120 maxA = new rutz::shared_ptr<NModelParam<int> >[NUMADC];
00121 adcval = new byte[NUMADC];
00122 for (uint i = 0; i < NUMADC; i ++)
00123 {
00124 adcval[i] = 127; char buf[20];
00125 sprintf(buf, "ZeroA%d", i);
00126 zeroA[i] = NModelParam<int>::make(buf, this, 127);
00127 sprintf(buf, "MinA%d", i);
00128 minA[i] = NModelParam<int>::make(buf, this, 0);
00129 sprintf(buf, "MaxA%d", i);
00130 maxA[i] = NModelParam<int>::make(buf, this, 255);
00131 }
00132
00133 itsFd = -1;
00134 itsListener.reset(NULL);
00135 keyboard = 0;
00136 pthread_mutex_init(&lock, NULL);
00137 pthread_mutex_init(&serlock, NULL);
00138 }
00139
00140
00141 void BeoChip::start1()
00142 {
00143 CLDEBUG("Opening port %s", itsDevName.getVal().c_str());
00144 itsFd = open(itsDevName.getVal().c_str(), O_RDWR | O_NOCTTY | O_SYNC);
00145 if (itsFd < 0)
00146 PLFATAL("Error opening serial port '%s'", itsDevName.getVal().c_str());
00147 CLDEBUG("Opened port %s", itsDevName.getVal().c_str());
00148
00149
00150 if (tcgetattr(itsFd, &itsOldtio) == -1) PLFATAL("tcgetattr() failed");
00151
00152
00153 struct termios newtio;
00154 bzero(&newtio, sizeof(newtio));
00155 newtio.c_cflag = CS8 | CREAD;
00156 if (itsUseRTSCTS.getVal())
00157 newtio.c_cflag |= CRTSCTS;
00158 else
00159 {
00160 newtio.c_cflag |= CLOCAL;
00161 CLDEBUG("WARNING: RTS/CTS flow and modem control lines not used");
00162 }
00163 newtio.c_iflag = IGNBRK | IGNPAR;
00164 newtio.c_oflag = 0;
00165 newtio.c_lflag = 0;
00166 newtio.c_cc[VTIME] = 0;
00167 newtio.c_cc[VMIN] = 1;
00168
00169
00170 if (tcflush(itsFd, TCIFLUSH) == -1) PLFATAL("tcflush() failed");
00171
00172
00173 if (cfsetispeed(&newtio, B19200) == -1)
00174 PLFATAL("Cannot set serial in speed");
00175 if (cfsetospeed(&newtio, B19200) == -1)
00176 PLFATAL("Cannot set serial out speed");
00177 if (tcsetattr(itsFd, TCSANOW, &newtio) == -1)
00178 PLFATAL("tcsetattr() failed");
00179
00180
00181 keepgoing = true;
00182 pthread_create(&runner, NULL, &BeoChip_run, (void *)this);
00183 }
00184
00185
00186 void BeoChip::stop2()
00187 {
00188 keepgoing = false;
00189 usleep(300000);
00190
00191
00192 if (tcsetattr(itsFd, TCSANOW, &itsOldtio) == -1)
00193 PLERROR("tcsetaddr() failed");
00194 close(itsFd);
00195 itsFd = -1;
00196 }
00197
00198
00199 BeoChip::~BeoChip()
00200 {
00201 delete [] zeroS;
00202 delete [] minS;
00203 delete [] maxS;
00204 delete [] servopos;
00205
00206 delete [] zeroP;
00207 delete [] minP;
00208 delete [] maxP;
00209 delete [] pulseval;
00210
00211 delete [] zeroA;
00212 delete [] minA;
00213 delete [] maxA;
00214 delete [] adcval;
00215
00216 pthread_mutex_destroy(&lock);
00217 pthread_mutex_destroy(&serlock);
00218 }
00219
00220
00221 void BeoChip::setListener(rutz::shared_ptr<BeoChipListener>& listener)
00222 { itsListener = listener; }
00223
00224
00225 bool BeoChip::echoRequest()
00226 {
00227
00228 return writeByte(BIN(11111000));
00229 }
00230
00231
00232 bool BeoChip::debugMode(const bool on)
00233 {
00234
00235 return writeByte(BIN(11111100) | (on?1:0));
00236 }
00237
00238
00239 bool BeoChip::resetChip()
00240 {
00241
00242 return writeByte(BIN(11111001));
00243 }
00244
00245
00246 bool BeoChip::lcdGotoXY(const int x, const int y)
00247 {
00248 if (itsLCDrows.getVal() != 4)
00249 LFATAL("FIXME: need some code to support various numbers of rows!");
00250
00251
00252 int offset = x;
00253 switch(y)
00254 {
00255 case 0: break;
00256 case 1: offset += 64; break;
00257 case 2: offset += itsLCDcols.getVal(); break;
00258 case 3: offset += 64 + itsLCDcols.getVal(); break;
00259 default:
00260 LERROR("Row number %d out of range 0..3 -- IGNORING", y);
00261 return false;
00262 }
00263
00264 return lcdGoto(offset);
00265 }
00266
00267
00268 bool BeoChip::lcdGoto(const int i)
00269 {
00270 if (i < 0 || i >= 128)
00271 { LERROR("Offset %d out of range 0..127 -- IGNORING", i); return false; }
00272
00273
00274 return lcdSendRaw(BIN(10000000) | i, false);
00275 }
00276
00277
00278 bool BeoChip::lcdPrintf(const char *fmt, ...)
00279 {
00280
00281 char txt[itsLCDcols.getVal() + 1];
00282 va_list a; va_start(a, fmt);
00283 vsnprintf(txt, itsLCDcols.getVal()+1, fmt, a);
00284 va_end(a);
00285
00286
00287 bool ret = true;
00288 pthread_mutex_lock(&serlock);
00289 for (unsigned int i = 0; i < strlen(txt); i ++)
00290 ret &= lcdSendRaw(txt[i], true, false);
00291 pthread_mutex_unlock(&serlock);
00292
00293 return ret;
00294 }
00295
00296
00297 bool BeoChip::lcdPrintf(const int x, const int y, const char *fmt, ...)
00298 {
00299
00300 if (lcdGotoXY(x, y) == false) return false;
00301
00302
00303 char txt[itsLCDcols.getVal() + 1];
00304 va_list a; va_start(a, fmt);
00305 vsnprintf(txt, itsLCDcols.getVal()+1, fmt, a);
00306 va_end(a);
00307
00308 return lcdPrintf("%s", txt);
00309 }
00310
00311
00312 bool BeoChip::lcdClear()
00313 {
00314 return lcdSendRaw(BIN(00000001), false);
00315 }
00316
00317
00318 bool BeoChip::lcdScrollLeft(const int i)
00319 {
00320 bool ret = true;
00321
00322 pthread_mutex_lock(&serlock);
00323 for (int j = 0; j < i; j ++)
00324 ret &= lcdSendRaw(BIN(00011000), false, false);
00325 pthread_mutex_unlock(&serlock);
00326
00327 return ret;
00328 }
00329
00330
00331 bool BeoChip::lcdScrollRight(const int i)
00332 {
00333 bool ret = true;
00334
00335 pthread_mutex_lock(&serlock);
00336 for (int j = 0; j < i; j ++)
00337 ret &= lcdSendRaw(BIN(00011100), false, false);
00338 pthread_mutex_unlock(&serlock);
00339
00340 return ret;
00341 }
00342
00343
00344 bool BeoChip::lcdMoveCursorLeft(const int i)
00345 {
00346 bool ret = true;
00347
00348 pthread_mutex_lock(&serlock);
00349 for (int j = 0; j < i; j ++)
00350 ret &= lcdSendRaw(BIN(00010000), false, false);
00351 pthread_mutex_unlock(&serlock);
00352
00353 return ret;
00354 }
00355
00356
00357 bool BeoChip::lcdMoveCursorRight(const int i)
00358 {
00359 bool ret = true;
00360
00361 pthread_mutex_lock(&serlock);
00362 for (int j = 0; j < i; j ++)
00363 ret &= lcdSendRaw(BIN(00010100), false, false);
00364 pthread_mutex_unlock(&serlock);
00365
00366 return ret;
00367 }
00368
00369
00370 bool BeoChip::lcdCursorBlock()
00371 { return lcdSendRaw(BIN(00001101), false); }
00372
00373
00374 bool BeoChip::lcdCursorUnderline()
00375 { return lcdSendRaw(BIN(00001110), false); }
00376
00377
00378 bool BeoChip::lcdCursorInvisible()
00379 { return lcdSendRaw(BIN(00001100), false); }
00380
00381
00382 bool BeoChip::lcdLoadFont(const int font)
00383 {
00384 if (font < 0 || font > 7)
00385 {
00386 LERROR("font value %d out of range [0..7] - IGNORED", font);
00387 return false;
00388 }
00389
00390
00391 return writeByte(BIN(11100000) | byte(font));
00392 }
00393
00394
00395 bool BeoChip::lcdLoadFont(const byte data[64])
00396 {
00397 pthread_mutex_lock(&serlock);
00398
00399
00400 if (lcdSendRaw(BIN(01000000), false, false) == false) return false;
00401
00402
00403 bool ret = true;
00404 for (int i = 0; i < 64; i ++) ret &= lcdSendRaw(data[i], true, false);
00405
00406 pthread_mutex_unlock(&serlock);
00407
00408 return ret;
00409 }
00410
00411
00412 bool BeoChip::lcdSetAnimation(const int anim)
00413 {
00414 if (anim < 0 || anim > 7)
00415 {
00416 LERROR("anim value %d out of range [0..7] - IGNORED", anim);
00417 return false;
00418 }
00419
00420
00421 return writeByte((BIN(11101000)) | (byte(anim)));
00422 }
00423
00424
00425 bool BeoChip::lcdSendRaw(const byte val, const bool RS, const bool uselock)
00426 {
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436 if (RS == true && val >= 32 && val < 96)
00437 return writeByte(byte(val - 32), uselock);
00438 else
00439 {
00440 if (uselock) pthread_mutex_lock(&serlock);
00441 bool ret = writeByte((BIN(10100000)) |
00442 (RS == true ? (BIN(00010000)):0) |
00443 (byte(val) & (BIN(00001111))), false);
00444 ret &= writeByte((BIN(11000000)) | byte(val >> 4), false);
00445 if (uselock) pthread_mutex_unlock(&serlock);
00446 return ret;
00447 }
00448 }
00449
00450
00451 bool BeoChip::shimServos(const byte shim)
00452 {
00453 if (shim > 7)
00454 {
00455 LERROR("shim value %d out of range [0..7] - IGNORED", shim);
00456 return false;
00457 }
00458
00459
00460 return writeByte(BIN(11011000) | shim);
00461 }
00462
00463
00464 void BeoChip::calibrateServo(const int servo, const byte neutralval,
00465 const byte minval, const byte maxval)
00466 {
00467 ASSERT(servo >= 0 && servo < NUMSERVO);
00468 zeroS[servo]->setVal(neutralval);
00469 minS[servo]->setVal(minval);
00470 maxS[servo]->setVal(maxval);
00471 }
00472
00473
00474 bool BeoChip::setServo(const int servo, const float position)
00475 {
00476 ASSERT(servo >= 0 && servo < NUMSERVO);
00477 byte raw = calibToRaw(position, zeroS[servo]->getVal(),
00478 minS[servo]->getVal(), maxS[servo]->getVal());
00479 return setServoRaw(servo, raw);
00480 }
00481
00482
00483 float BeoChip::getServo(const int servo) const
00484 {
00485 ASSERT(servo >= 0 && servo < NUMSERVO);
00486
00487 byte raw = getServoRaw(servo);
00488 return rawToCalib(raw, zeroS[servo]->getVal(),
00489 minS[servo]->getVal(), maxS[servo]->getVal());
00490 }
00491
00492
00493 bool BeoChip::setServoRaw(const int servo, const byte val)
00494 {
00495 ASSERT(servo >= 0 && servo < NUMSERVO);
00496
00497
00498
00499
00500 pthread_mutex_lock(&serlock);
00501 bool ret = writeByte(BIN(01000000) | (val & BIN(00111111)), false);
00502 if (ret) ret = writeByte(BIN(10000000) | (servo << 2) | (val >> 6), false);
00503 pthread_mutex_unlock(&serlock);
00504
00505 if (ret == false) { LERROR("Set servo failed - keeping old"); return false; }
00506 servopos[servo] = val;
00507
00508 return true;
00509 }
00510
00511
00512 byte BeoChip::getServoRaw(const int servo) const
00513 {
00514 ASSERT(servo >= 0 && servo < NUMSERVO);
00515 return servopos[servo];
00516 }
00517
00518
00519 bool BeoChip::capturePulse(const int whichone, const bool on)
00520 {
00521 ASSERT(whichone >= 0 && whichone <= 1);
00522
00523
00524 return writeByte(BIN(11010000) | (whichone?2:0) | (on?1:0));
00525 }
00526
00527
00528 void BeoChip::calibratePulse(const int whichone, const int neutralval,
00529 const int minval, const int maxval)
00530 {
00531 ASSERT(whichone >= 0 && whichone <= 1);
00532 zeroP[whichone]->setVal(neutralval);
00533 minP[whichone]->setVal(minval);
00534 maxP[whichone]->setVal(maxval);
00535 }
00536
00537
00538 float BeoChip::getPulse(const int whichone)
00539 {
00540 ASSERT(whichone >= 0 && whichone <= 1);
00541 short int raw = getPulseRaw(whichone);
00542 return rawToCalib(raw, zeroP[whichone]->getVal(),
00543 minP[whichone]->getVal(), maxP[whichone]->getVal());
00544 }
00545
00546
00547 short int BeoChip::getPulseRaw(const int whichone)
00548 {
00549 ASSERT(whichone >= 0 && whichone <= 1);
00550 pthread_mutex_lock(&lock);
00551 short int val = pulseval[whichone];
00552 pthread_mutex_unlock(&lock);
00553 return val;
00554 }
00555
00556
00557 bool BeoChip::captureAnalog(const int whichone, const bool on)
00558 {
00559 ASSERT(whichone >= 0 && whichone <= 1);
00560
00561
00562 return writeByte(BIN(11010100) | (whichone?2:0) | (on?1:0));
00563 }
00564
00565
00566 void BeoChip::calibrateAnalog(const int whichone, const int neutralval,
00567 const int minval, const int maxval)
00568 {
00569 ASSERT(whichone >= 0 && whichone <= 1);
00570 zeroA[whichone]->setVal(neutralval);
00571 minA[whichone]->setVal(minval);
00572 maxA[whichone]->setVal(maxval);
00573 }
00574
00575
00576 float BeoChip::getAnalog(const int whichone)
00577 {
00578 ASSERT(whichone >= 0 && whichone <= 1);
00579 byte raw = getAnalogRaw(whichone);
00580 return rawToCalib(raw, zeroS[whichone]->getVal(),
00581 minS[whichone]->getVal(), maxS[whichone]->getVal());
00582 }
00583
00584
00585 byte BeoChip::getAnalogRaw(const int whichone)
00586 {
00587 ASSERT(whichone >= 0 && whichone <= 1);
00588 pthread_mutex_lock(&lock);
00589 byte val = adcval[whichone];
00590 pthread_mutex_unlock(&lock);
00591 return val;
00592 }
00593
00594
00595 bool BeoChip::captureKeyboard(const bool on)
00596 {
00597
00598 return writeByte(BIN(11111010) | (on?1:0));
00599 }
00600
00601
00602 bool BeoChip::debounceKeyboard(const bool on)
00603 {
00604
00605 return writeByte(BIN(11111110) | (on?1:0));
00606 }
00607
00608
00609 int BeoChip::getKeyboard()
00610 {
00611 pthread_mutex_lock(&lock);
00612 int val = keyboard;
00613 pthread_mutex_unlock(&lock);
00614 return val;
00615 }
00616
00617
00618 bool BeoChip::setDigitalOut(const int outnum, const bool on)
00619 {
00620 ASSERT(outnum >= 0 && outnum < 4);
00621
00622 return writeByte(BIN(11110000) | (outnum << 1) | (on?1:0));
00623 }
00624
00625
00626 bool BeoChip::writeByte(const byte val, const bool uselock)
00627 {
00628 if (MYLOGVERB >= LOG_DEBUG)
00629 {
00630 char txt[9]; txt[8] = '\0';
00631 for (int i = 0; i < 8; i ++) txt[i] = (val >> (7-i)) & 1 ? '1':'0';
00632 LDEBUG("Sending: %s", txt);
00633 }
00634
00635 if (uselock) pthread_mutex_lock(&serlock);
00636 bool ret = (write(itsFd, &val, 1) == 1);
00637 if (uselock) pthread_mutex_unlock(&serlock);
00638
00639 if (ret == false) PLERROR("Write to BeoChip failed");
00640 return ret;
00641 }
00642
00643
00644 void BeoChip::run()
00645 {
00646 int pwm[2], adc[2], pwmidx = 0, adcidx = 0;
00647 while(keepgoing)
00648 {
00649 byte c;
00650 if (read(itsFd, &c, 1) != 1)
00651 PLERROR("Error reading from BeoChip -- IGNORED");
00652 else
00653 {
00654 if (MYLOGVERB >= LOG_DEBUG)
00655 {
00656 char txt[9]; txt[8] = '\0';
00657 for (int i = 0; i < 8; i ++) txt[i] = (c >> (7-i)) & 1 ? '1':'0';
00658 LDEBUG("Received: %s", txt);
00659 }
00660
00661
00662
00663
00664
00665
00666
00667
00668
00669
00670
00671
00672 switch(c >> 6)
00673 {
00674 case BIN(00):
00675 pwmidx = ((c & BIN(00100000)) >> 5);
00676 pwm[pwmidx] = (c & BIN(00011111));
00677 break;
00678 case BIN(01):
00679 pwm[pwmidx] |= ((c & BIN(00111111)) << 5);
00680 pthread_mutex_lock(&lock);
00681 pulseval[pwmidx] = pwm[pwmidx];
00682 pthread_mutex_unlock(&lock);
00683 if (itsListener.get())
00684 itsListener->event((pwmidx == 0 ? PWM0:PWM1),
00685 pwm[pwmidx],
00686 rawToCalib(pwm[pwmidx],
00687 zeroP[pwmidx]->getVal(),
00688 minP[pwmidx]->getVal(),
00689 maxP[pwmidx]->getVal()));
00690 break;
00691 case BIN(10):
00692 if ((c & BIN(11100000)) == BIN(10000000))
00693 {
00694 int kbd = (c & BIN(00011111));
00695 pthread_mutex_lock(&lock);
00696 keyboard = kbd;
00697 pthread_mutex_unlock(&lock);
00698 if (itsListener.get())
00699 itsListener->event(KBD, kbd, 0.0F);
00700 }
00701 else
00702 {
00703 adcidx = ((c & BIN(00010000)) >> 4);
00704 adc[adcidx] = (c & BIN(00001111));
00705 }
00706 break;
00707 case BIN(11):
00708 if ( (c & BIN(11110000)) == BIN(11000000) )
00709 {
00710 adc[adcidx] |= ((c & BIN(00001111)) << 4);
00711 pthread_mutex_lock(&lock);
00712 adcval[adcidx] = adc[adcidx];
00713 pthread_mutex_unlock(&lock);
00714 if (itsListener.get())
00715 itsListener->event((adcidx == 0 ? ADC0:ADC1),
00716 adc[adcidx],
00717 rawToCalib(adc[adcidx],
00718 zeroA[adcidx]->getVal(),
00719 minA[adcidx]->getVal(),
00720 maxA[adcidx]->getVal()));
00721 }
00722 else
00723 switch(c)
00724 {
00725 case BIN(11010000):
00726 if (itsListener.get())
00727 itsListener->event(RESET, 0, 0.0F);
00728 break;
00729 case BIN(11010001):
00730 if (itsListener.get())
00731 itsListener->event(ECHOREP, 0, 0.0F);
00732 break;
00733 case BIN(11010010):
00734 if (itsListener.get())
00735 itsListener->event(INOVERFLOW, 0, 0.0F);
00736 break;
00737 case BIN(11010011):
00738 if (itsListener.get())
00739 itsListener->event(SERIALERROR, 0, 0.0F);
00740 break;
00741 case BIN(11010100):
00742 if (itsListener.get())
00743 itsListener->event(OUTOVERFLOW, 0, 0.0F);
00744 break;
00745 default:
00746 LERROR("Unknown message 0x%x from BeoChip -- IGNORED", c);
00747 }
00748 }
00749 }
00750 }
00751 pthread_exit(0);
00752 }
00753
00754
00755 float BeoChip::rawToCalib(const int raw, const int zero, const int mini,
00756 const int maxi) const
00757 {
00758 if (raw < mini || raw > maxi)
00759 LERROR("Raw value %d out of range [%d..%d]", raw, mini, maxi);
00760
00761 if (raw < zero)
00762 return float(zero - raw) / float(mini - zero);
00763 else
00764 return float(raw - zero) / float(maxi - zero);
00765 }
00766
00767
00768 int BeoChip::calibToRaw(const float calibrated, const int zero, const int mini,
00769 const int maxi, const int bits) const
00770 {
00771 if (calibrated < -1.0F || calibrated > 1.0F)
00772 LERROR("Calibrated value %f out of range [-1..1]", calibrated);
00773
00774 if (calibrated < 0.0F)
00775 return zero + int(calibrated * float(zero - mini) + 0.4999F);
00776 else
00777 return zero + int(calibrated * float(maxi - zero) + 0.4999F);
00778 }
00779
00780
00781
00782
00783
00784