libmanta
A cross-platform API for the Snyderphonics Manta
Manta.cpp
00001 #include <cmath>
00002 #include "Manta.h"
00003 #include "MantaExceptions.h"
00004 
00005 
00006 /************************************************************************/
00011 Manta::Manta(void) {
00012    for(int i = 0; i < 53; ++i)
00013    {
00014       LastInReport[i] = 0;
00015       MaxSensorValues[i] = AverageMaxSensorValues[i];
00016    }
00017    for(int i = 53; i < 57; ++i)
00018    {
00019       LastInReport[i] = 0xFF;
00020    }
00021    for(unsigned int i = 0; i < sizeof(CurrentOutReport); ++i)
00022    {
00023       CurrentOutReport[i] = 0;
00024    }
00025    for(unsigned int i = 0; i < sizeof(VelocityWaiting) / sizeof(VelocityWaiting[0]); ++i)
00026    {
00027       VelocityWaiting[i] = false;
00028    }
00029 }
00030 
00031 /************************************************************************/
00035 void Manta::FrameReceived(int8_t *frame)
00036 {
00037    uint8_t *uframe = (uint8_t *)frame;
00038    for(int i = 1; i < 53; ++i)
00039    {
00040       uframe[i] = ScaleSensorValue(frame[i] + 128, i);
00041    }
00042    /* apply the offset to the slider bytes without scaling them */
00043    for(int i = 53; i < 57; ++i)
00044    {
00045       uframe[i] = frame[i] + 128;
00046    }
00047    FrameEvent(uframe);
00048    /* input frames have one reportID byte at the beginning */
00049    for(int i = 1; i < 53; ++i)
00050    {
00051       /* check to see if there's a previous sample waiting to have
00052        * the velocity algorithm run */
00053       if(true == VelocityWaiting[i])
00054       {
00055          if(i < 49)
00056             PadVelocityEvent((i - 1) / 8, (i - 1) % 8, i - 1,
00057                   CalculateVelocity(LastInReport[i], uframe[i]));
00058          else
00059             ButtonVelocityEvent(i - 49, CalculateVelocity(LastInReport[i], uframe[i]));
00060          VelocityWaiting[i] = false;
00061       }
00062 
00063       if(uframe[i] != LastInReport[i])
00064       {
00065          /* check to see if this is a release */
00066          if(0 == uframe[i])
00067          {
00068             if(i < 49)
00069                PadVelocityEvent((i - 1) / 8, (i - 1) % 8, i - 1, 0);
00070             else
00071                ButtonVelocityEvent(i - 49, 0);
00072          }
00073          /* check to see if this is the first nonzero sample */
00074          else if(0 == LastInReport[i])
00075          {
00076             VelocityWaiting[i] = true;
00077          }
00078          if(i < 49)
00079             PadEvent((i - 1) / 8, (i - 1) % 8, i - 1, uframe[i]);
00080          else
00081             ButtonEvent(i - 49, uframe[i]);
00082       }
00083       LastInReport[i] = uframe[i];
00084    }
00085    if(uframe[53] != LastInReport[53] || uframe[54] != LastInReport[54])
00086    {
00087       SliderEvent(0, (uframe[53]) | ((uframe[54]) << 8 ));
00088    }
00089    if(uframe[55] != LastInReport[55] || uframe[56] != LastInReport[56])
00090    {
00091       SliderEvent(1, (uframe[55]) | ((uframe[56]) << 8 ));
00092    }
00093    for(int i = 53; i < 57; ++i)
00094    {
00095       LastInReport[i] = uframe[i];
00096    }
00097 }
00098 
00099 /************************************************************************/
00104 void Manta::SetPadLED(LEDState state, int ledID)
00105 {
00106    int row = ledID / 8;
00107    int column = ledID % 8;
00108 
00109    if(ledID < 0 || ledID > 47)
00110    {
00111       throw std::invalid_argument("Invalid Pad Index");
00112    }
00113 
00114    switch(state)
00115    {
00116       case Amber:
00117          CurrentOutReport[AmberIndex + row] |= (1 << column);
00118          CurrentOutReport[RedIndex + row] &= ~(1 << column);
00119          break;
00120       case Red:
00121          CurrentOutReport[RedIndex + row] |= (1 << column);
00122          CurrentOutReport[AmberIndex + row] &= ~(1 << column);
00123          break;
00124       case Off:
00125          CurrentOutReport[AmberIndex + row] &= ~(1 << column);
00126          CurrentOutReport[RedIndex + row] &= ~(1 << column);
00127          break;
00128       default:
00129          throw std::invalid_argument("Invalid state");
00130    }
00131 
00132    if(IsConnected())
00133    {
00134       WriteFrame(CurrentOutReport, false);
00135    }
00136 }
00137 
00138 /************************************************************************/
00145 void Manta::SetPadLEDRow(LEDState state, int row, uint8_t mask)
00146 {
00147    if(row < 0 || row > 5)
00148    {
00149       throw std::invalid_argument("Invalid Row Index");
00150    }
00151 
00152    MantaClient::DebugPrint("Called SetPadLEDRow(%s, %d, %X)",
00153          state == Off ? "Off" : state == Amber ? "Amber" : "Red", row, mask);
00154    MantaClient::DebugPrint("ByteReverse(0x%X) = 0x%X", 0xA0, byteReverse(0xA0));
00155    switch(state)
00156    {
00157       case Amber:
00158          CurrentOutReport[AmberIndex + row] |= byteReverse(mask);
00159          CurrentOutReport[RedIndex + row] &= ~byteReverse(mask);
00160          break;
00161       case Red:
00162          CurrentOutReport[RedIndex + row] |= byteReverse(mask);
00163          CurrentOutReport[AmberIndex + row] &= ~byteReverse(mask);
00164          break;
00165       case Off:
00166          CurrentOutReport[RedIndex + row] &= ~byteReverse(mask);
00167          CurrentOutReport[AmberIndex + row] &= ~byteReverse(mask);
00168          break;
00169       default:
00170          throw std::invalid_argument("Invalid state");
00171    }
00172    if(IsConnected())
00173    {
00174       WriteFrame(CurrentOutReport, false);
00175    }
00176 }
00177 
00178 /************************************************************************/
00185 void Manta::SetPadLEDColumn(LEDState state, int column, uint8_t mask)
00186 {
00187    if(column < 0 || column > 7)
00188    {
00189       throw std::invalid_argument("Invalid Column Index");
00190    }
00191    
00192    MantaClient::DebugPrint("Called SetPadLEDColumn(%s, %d, %X)",
00193          state == Off ? "Off" : state == Amber ? "Amber" : "Red", column, mask);
00194    switch(state)
00195    {
00196       case Amber:
00197          for(int i = 0; i < 6; ++i)
00198          {
00199             if((mask >> i) & 0x01)
00200             {
00201                CurrentOutReport[AmberIndex + i] |= (0x01 << column);
00202                CurrentOutReport[RedIndex + i] &= ~(0x01 << column);
00203             }
00204          }
00205          break;
00206       case Red:
00207          for(int i = 0; i < 6; ++i)
00208          {
00209             if((mask >> i) & 0x01)
00210             {
00211                CurrentOutReport[RedIndex + i] |= (0x01 << column);
00212                CurrentOutReport[AmberIndex + i] &= ~(0x01 << column);
00213             }
00214          }
00215          break;
00216       case Off:
00217          for(int i = 0; i < 6; ++i)
00218          {
00219             if((mask >> i) & 0x01)
00220             {
00221                CurrentOutReport[RedIndex + i] &= ~(0x01 << column);
00222                CurrentOutReport[AmberIndex + i] &= ~(0x01 << column);
00223             }
00224          }
00225          break;
00226       default:
00227          throw std::invalid_argument("Invalid state");
00228    }
00229 
00230    if(IsConnected())
00231    {
00232       WriteFrame(CurrentOutReport, false);
00233    }
00234 }
00235 
00236 /************************************************************************/
00243 void Manta::SetPadLEDFrame(LEDState state, LEDFrame mask)
00244 {
00245    switch(state)
00246    {
00247       case Amber:
00248          for(unsigned int i = 0; i < sizeof(LEDFrame); ++i)
00249          {
00250             CurrentOutReport[AmberIndex + i] = byteReverse(mask[i]);
00251             CurrentOutReport[RedIndex + i] &= ~byteReverse(mask[i]);
00252          }
00253          break;
00254       case Red:
00255          for(unsigned int i = 0; i < sizeof(LEDFrame); ++i)
00256          {
00257             CurrentOutReport[RedIndex + i] = byteReverse(mask[i]);
00258             CurrentOutReport[AmberIndex + i] &= ~byteReverse(mask[i]);
00259          }
00260          break;
00261       case Off:
00262          for(unsigned int i = 0; i < sizeof(LEDFrame); ++i)
00263          {
00264             CurrentOutReport[RedIndex + i] &= ~byteReverse(mask[i]);
00265             CurrentOutReport[AmberIndex + i] &= ~byteReverse(mask[i]);
00266          }
00267          break;
00268       default:
00269          throw std::invalid_argument("Invalid state");
00270    }
00271    if(IsConnected())
00272    {
00273       WriteFrame(CurrentOutReport, false);
00274    }
00275 }
00276 
00277 /************************************************************************/
00284 void Manta::SetSliderLED(LEDState state, int id, uint8_t mask)
00285 {
00286    if(id < 0 || id > 1)
00287    {
00288       throw std::invalid_argument("Invalid Slider Index");
00289    }
00290    switch(state)
00291    {
00292       case Amber:
00293          CurrentOutReport[SliderIndex + id] |= byteReverse(mask);
00294          break;
00295       case Red:
00296          /* no Red slider LEDs, do nothing */
00297          break;
00298       case Off:
00299          CurrentOutReport[SliderIndex + id] &= ~byteReverse(mask);
00300          break;
00301       default:
00302          throw std::invalid_argument("Invalid state");
00303    }
00304    if(IsConnected())
00305    {
00306       WriteFrame(CurrentOutReport, false);
00307    }
00308 }
00309 
00310 /************************************************************************/
00315 void Manta::SetButtonLED(LEDState state, int id)
00316 {
00317    if(id < 0 || id > 3)
00318    {
00319       throw std::invalid_argument("Invalid Button Index");
00320    }
00321    
00322    switch(state)
00323    {
00324       case Amber:
00325          CurrentOutReport[ButtonIndex] |= (0x01 << (id));
00326          CurrentOutReport[ButtonIndex] &= ~(0x01 << (id + 4));
00327          break;
00328       case Red:
00329          CurrentOutReport[ButtonIndex] |= (0x01 << (id + 4));
00330          CurrentOutReport[ButtonIndex] &= ~(0x01 << (id));
00331          break;
00332       case Off:
00333          CurrentOutReport[ButtonIndex] &= ~(0x01 << (id + 4));
00334          CurrentOutReport[ButtonIndex] &= ~(0x01 << (id));
00335          break;
00336       default:
00337          throw std::invalid_argument("Invalid state");
00338    }
00339    if(IsConnected())
00340    {
00341       WriteFrame(CurrentOutReport, false);
00342    }
00343 }
00344 
00345 /************************************************************************/
00348 void Manta::ResendLEDState(void)
00349 {
00350    if(IsConnected())
00351    {
00352       WriteFrame(CurrentOutReport, false);
00353    }
00354 }
00355 
00356 /************************************************************************/
00359 void Manta::ClearButtonLEDs(void)
00360 {
00361    CurrentOutReport[ButtonIndex] = 0;
00362 
00363    if(IsConnected())
00364    {
00365       WriteFrame(CurrentOutReport, false);
00366    }
00367 }
00368 
00369 /************************************************************************/
00372 void Manta::ClearPadAndButtonLEDs(void)
00373 {
00374    for(unsigned int i = 0; i < sizeof(LEDFrame); ++i)
00375    {
00376       CurrentOutReport[AmberIndex + i] = 0;
00377       CurrentOutReport[RedIndex + i] = 0;
00378    }
00379 
00380    CurrentOutReport[ButtonIndex] = 0;
00381 
00382    if(IsConnected())
00383    {
00384       WriteFrame(CurrentOutReport, false);
00385    }
00386 }
00387 
00388 /************************************************************************/
00391 void Manta::Recalibrate(void)
00392 {
00393    if(! IsConnected())
00394    {
00395       throw MantaNotConnectedException(this);
00396    }
00397    
00398    /* make sure these messages get queued so that they
00399     * don't just cancel each other out */
00400    CurrentOutReport[ConfigIndex] |= 0x40;
00401    WriteFrame(CurrentOutReport, true);
00402    CurrentOutReport[ConfigIndex] &= ~0x40;
00403    WriteFrame(CurrentOutReport, true);
00404 }
00405 
00406 /************************************************************************/
00417 void Manta::SetLEDControl(LEDControlType control, bool state)
00418 {
00419    uint8_t flag;
00420    
00421    switch(control)
00422    {
00423       case PadAndButton:
00424          flag = 0x01;
00425          break;
00426       case Slider:
00427          flag = 0x02;
00428          break;
00429       case Button:
00430          flag = 0x20;
00431          break;
00432       default:
00433          throw std::invalid_argument("Invalid Control Type");
00434    }
00435 
00436    if(state)
00437       CurrentOutReport[ConfigIndex] |= flag;
00438    else
00439       CurrentOutReport[ConfigIndex] &= ~flag;
00440    if(IsConnected())
00441    {
00442       /* if we're disabling LEDControl, we want to make sure that this
00443        * message gets queued so that any pending LED messages get sent
00444        * down before we disable LEDs */
00445       WriteFrame(CurrentOutReport, !state);
00446    }
00447 }
00448 
00449 /************************************************************************/
00455 void Manta::SetTurboMode(bool Enabled)
00456 {
00457    if(Enabled)
00458       CurrentOutReport[ConfigIndex] |= 0x04;
00459    else
00460       CurrentOutReport[ConfigIndex] &= ~0x04;
00461    if(IsConnected())
00462    {
00463       WriteFrame(CurrentOutReport, false);
00464    }
00465 }
00466 
00467 /************************************************************************/
00477 void Manta::SetRawMode(bool Enabled)
00478 {
00479    if(Enabled)
00480       CurrentOutReport[ConfigIndex] |= 0x08;
00481    else
00482       CurrentOutReport[ConfigIndex] &= ~0x08;
00483    if(IsConnected())
00484    {
00485       WriteFrame(CurrentOutReport, false);
00486    }
00487 }
00488 
00489 /************************************************************************/
00509 void Manta::SetMaxSensorValues(int *values)
00510 {
00511    for(int i = 0; i < 53; ++i)
00512    {
00513       MaxSensorValues[i] = values[i];
00514    }
00515 }
00516 
00517 uint8_t Manta::byteReverse(uint8_t inByte)
00518 {
00519    // Algorithm from Bit Twiddling Hacks
00520    uint8_t outByte = inByte; // first get LSB of inByte
00521    int s = 7; // extra shift needed at end
00522 
00523    for (inByte >>= 1; inByte; inByte >>= 1)
00524    {   
00525       outByte <<= 1;
00526       outByte |= inByte & 1;
00527       s--;
00528    }
00529    outByte <<= s; // shift when inByte's highest bits are zero
00530    return outByte;
00531 }
00532 
00533 int Manta::CalculateVelocity(int LastValue, int CurrentValue)
00534 {
00535    float LOG1, LOG2;
00536    float MAX;
00537    float MIN;
00538    float RELATIVE1, RELATIVE2;
00539    float LOG_RELATIVE1, LOG_RELATIVE2;
00540    float SUM_RAW;
00541    float LOG_SUM_RAW;
00542    float LOG_SUM_RELATIVE;
00543    float UP1;
00544    float VELOCITY = 0;
00545    int VELint = 0;
00546 
00547 
00548    // now do the velocity calculation
00549    LOG1 = log(1.0 + (float)LastValue);
00550    LOG2 = log(1.0 + (float)CurrentValue);
00551 
00552    MIN = LastValue;
00553    if (CurrentValue < MIN)
00554    {
00555       MIN = CurrentValue;
00556    }
00557    MAX = LastValue;
00558    if (CurrentValue > MAX)
00559    {
00560       MAX = CurrentValue;
00561    }
00562    RELATIVE1 = LastValue/MAX;
00563    RELATIVE2 = CurrentValue/MAX;
00564    LOG_RELATIVE1 = log(1.0 + RELATIVE1);
00565    LOG_RELATIVE2 = log(1.0 + RELATIVE2);
00566    SUM_RAW = LastValue+CurrentValue;
00567    LOG_SUM_RAW = log(1.0 + SUM_RAW);
00568    LOG_SUM_RELATIVE = log(1.0 + SUM_RAW/MAX);
00569    UP1 = 0;
00570    if (CurrentValue>LastValue) { UP1 = 1; }
00571    VELOCITY =
00572       -14.997037  +
00573       LastValue      *  0.009361  +
00574       MIN           * -0.014234  +
00575       LOG1          *  1.099763  +
00576       RELATIVE2     * -9.588311  +
00577       LOG_RELATIVE1  *-27.595303  +
00578       LOG_RELATIVE2  * -8.803761  +
00579       LOG_SUM_RELATIVE * 44.013138  +
00580       UP1           *  0.221622;
00581    //Then trim value to [0.4] range:
00582    if (VELOCITY < 0.)
00583    {
00584       VELOCITY = 0.;
00585    }
00586    if (VELOCITY > 4.)
00587    {
00588       VELOCITY = 4.;
00589    }
00590    //get it to 0. to 1. range
00591    VELOCITY = VELOCITY / 4.;
00592    // curve it exponentially
00593    VELOCITY = VELOCITY * VELOCITY;
00594    //get it to 0-126 range
00595    VELOCITY = VELOCITY * 126;
00596    //get it to 1-127 range
00597    VELOCITY = VELOCITY+ 1;
00598    //round to ints
00599    VELint = (int)VELOCITY;
00600    return VELint;
00601 }
00602 
00603 int Manta::ScaleSensorValue(int rawValue, int index)
00604 {
00605    float div = (float)rawValue / MaxSensorValues[index];
00606    return (int)((div * 210) + 0.5);
00607 }
00608 
00609 const int Manta::AverageMaxSensorValues[53] = 
00610 {0, 177, 184, 188, 189, 191, 190, 181, 181, 188, 193, 198, 200, 201, 199, 191,
00611    189, 192, 197, 202, 205, 206, 204, 199, 192, 202, 207, 211, 216, 215, 213,
00612    209, 201, 205, 210, 215, 220, 213, 218, 212, 204, 212, 216, 222, 227, 223,
00613    227, 221, 213, 200, 170, 190, 185};
00614 
00615 /************************************************************************/