Improved to "half-way usable" (version 0.5)
[pierogi] / necprotocol.cpp
index 036c15e..d3de072 100644 (file)
@@ -4,6 +4,45 @@
 #include <string>
 //#include <iostream>
 
+// Some global communications stuff:
+#include <QMutex>
+extern bool commandInFlight;
+extern QMutex commandIFMutex;
+
+// The official NEC protocol, as I understand it, has the following attributes:
+// A "zero" is encoded with a 560 usec pulse, 560 usec space.
+// A "one" is encoded with a 560 usec pulse, and 3*560 (1680) usec space.
+// The header is a 9000 usec pulse, 4500 usec space.
+// Commands end with a trailing 560 usec pulse.
+// A repeat block is a 9000 usec pulse, 2250 usec space, then trailing pulse.
+// Each command runs for 110000 usec before another can be executed.
+
+// For standard NEC, use this constructor:
+NECProtocol::NECProtocol(
+  QObject *guiObject,
+  unsigned int index,
+  NECKeyFormat fmt)
+  : PIRProtocol(guiObject, index, 110000, true),
+    zeroPulse(560),
+    zeroSpace(560),
+    onePulse(560),
+    oneSpace(1680),
+    headerPulse(9000),
+    headerSpace(4500),
+    hasHeaderPair(true),
+    trailerPulse(560),
+    hasTrailerPulse(true),
+    repeatPulse(9000),
+    repeatSpace(2250),
+    hasRepeatPair(true),
+    repeatNeedsHeader(false),
+    fullHeadlessRepeat(false),
+    elevenBitToggle(false),
+    encodingFormat(fmt)
+{
+}
+
+// For non-standard NEC, use this constructor:
 NECProtocol::NECProtocol(
   QObject *guiObject,
   unsigned int index,
@@ -12,17 +51,20 @@ NECProtocol::NECProtocol(
   unsigned int oPulse,
   unsigned int oSpace,
   unsigned int gSpace,
-  bool iclflag)
+  bool iclflag,
+  NECKeyFormat fmt)
   : PIRProtocol(guiObject, index, gSpace, iclflag),
     zeroPulse(zPulse),
     zeroSpace(zSpace),
     onePulse(oPulse),
     oneSpace(oSpace),
-    hasTrailerPulse(false),
     hasHeaderPair(false),
+    hasTrailerPulse(false),
     hasRepeatPair(false),
     repeatNeedsHeader(false),
-    fullHeadlessRepeat(false)
+    fullHeadlessRepeat(false),
+    elevenBitToggle(false),
+    encodingFormat(fmt)
 {
 }
 
@@ -63,18 +105,10 @@ void NECProtocol::setFullHeadlessRepeat(
   fullHeadlessRepeat = flag;
 }
 
-void NECProtocol::setPreData(
-  unsigned long data,
-  unsigned int bits)
-{
-  appendToBitSeq(preData, data, bits);
-}
-
-void NECProtocol::setPostData(
-  unsigned long data,
-  unsigned int bits)
+void NECProtocol::setElevenBitToggle(
+  bool flag)
 {
-  appendToBitSeq(postData, data, bits);
+  elevenBitToggle = flag;
 }
 
 void NECProtocol::startSendingCommand(
@@ -85,11 +119,18 @@ void NECProtocol::startSendingCommand(
   // whole thing in a try/catch block:
   try
   {
-    clearRepeatFlag();
-
-    // Check if we are meant to be the recipient of this command:
+    // First, check if we are meant to be the recipient of this command:
     if (threadableID != id) return;
 
+    // An object that helps keep track of the number of commands:
+//    PIRCommandCounter commandCounter;
+
+    // Ok, we're going to lock down this method and make sure
+    // only one guy at a time passes this point:
+//    QMutexLocker commandLocker(&commandMutex);
+
+    clearRepeatFlag();
+
     KeycodeCollection::const_iterator i = keycodes.find(command);
 
     // Do we even have this key defined?
@@ -103,10 +144,9 @@ void NECProtocol::startSendingCommand(
     PIRRX51Hardware rx51device(carrierFrequency, dutyCycle);
 
     int repeatCount = 0;
+    int commandDuration = 0;
     while (repeatCount < MAX_REPEAT_COUNT)
     {
-      int commandDuration;
-
       // If we are currently repeating, and have a special "repeat signal",
       // use that signal.  Otherwise, generate a normal command string.
       if (hasRepeatPair && repeatCount)
@@ -117,6 +157,10 @@ void NECProtocol::startSendingCommand(
       {
         commandDuration = generateHeadlessCommand((*i).second, rx51device);
       }
+      else if (elevenBitToggle && (repeatCount % 2))
+      {
+        commandDuration = generateToggledCommand((*i).second, rx51device);
+      }
       else
       {
         commandDuration = generateStandardCommand((*i).second, rx51device);
@@ -134,6 +178,8 @@ void NECProtocol::startSendingCommand(
         // Check whether we've been asked to stop:
         if (checkRepeatFlag())
         {
+          QMutexLocker cifLocker(&commandIFMutex);
+          commandInFlight = false;
           return;
         }
       }
@@ -146,6 +192,9 @@ void NECProtocol::startSendingCommand(
     // inform the gui:
     emit commandFailed(e.getError().c_str());
   }
+
+  QMutexLocker cifLocker(&commandIFMutex);
+  commandInFlight = false;
 }
 
 
@@ -162,14 +211,38 @@ int NECProtocol::generateStandardCommand(
     duration += (headerPulse + headerSpace);
   }
 
-  // Next, the "pre" data:
-  duration += pushBits(preData, rx51device);
-
-  // Next, add the actual command:
-  duration += pushBits(bits, rx51device);
-
-  // Next, add the "post" data:
-  duration += pushBits(postData, rx51device);
+  // Now, check the encoding format:
+  switch(encodingFormat)
+  {
+  case Standard_NEC:
+    // Standard NEC is made up of an eight-bit "address" and an eight-bit
+    // "command".  First the address bits are sent (in reverse order), then
+    // the address bits are inverted and sent again (in reverse order).
+    // Next, we do the same to the command bits.
+    // - "preData" should contain 8-bit value
+    // - "bits" should contain 8-bit value
+    duration += pushReverseBits(preData, rx51device);
+    duration += pushInvertedReverseBits(preData, rx51device);
+    duration += pushReverseBits(bits, rx51device);
+    duration += pushInvertedReverseBits(bits, rx51device);
+    break;
+  case Extended_NEC:
+    // In extended NEC, the address has been extended to 16 bits, but only
+    // the reversed bits are sent, not inverted.  The command portion stays
+    // the same.
+    // - "preData" should contain 16-bit value
+    // - "bits" should contain 8-bit value
+    duration += pushReverseBits(preData, rx51device);
+    duration += pushReverseBits(bits, rx51device);
+    duration += pushInvertedReverseBits(bits, rx51device);
+    break;
+  case LIRC_NEC: default:
+    // In this case, we just dump the raw bits into the device:
+    duration += pushBits(preData, rx51device);
+    duration += pushBits(bits, rx51device);
+    duration += pushBits(postData, rx51device);
+    break;
+  }
 
   // Finally add the "trail":
   if (hasTrailerPulse)
@@ -240,6 +313,67 @@ int NECProtocol::generateRepeatCommand(
 }
 
 
+// NOTE!  The following is a special command to toggle the last eleven bits
+// of the fifteen-bit commands used by Denon, Sharp, and a few others.  It
+// assumes the command sequence will contain all fifteen bits.  If this
+// is not the case, it will work incorrectly!
+int NECProtocol::generateToggledCommand(
+  const CommandSequence &bits,
+  PIRRX51Hardware &rx51device)
+{
+  int duration = 0;
+
+  CommandSequence::const_iterator i = bits.begin();
+
+  int bitcount = 0;
+  // First 4 bits:
+  while ((bitcount < 4) && (i != bits.end()))
+  {
+    if (*i)
+    {
+      // Send pulse for "one":
+      rx51device.addPair(onePulse, oneSpace);
+      duration += (onePulse + oneSpace);
+    }
+    else
+    {
+      // Send pulse for "zero":
+      rx51device.addPair(zeroPulse, zeroSpace);
+      duration += (zeroPulse + zeroSpace);
+    }
+    ++i;
+    ++bitcount;
+  }
+
+  // Now, invert the last eleven bits:
+  while (i != bits.end())
+  {
+    if (*i)
+    {
+      // Send pulse for "zero":
+      rx51device.addPair(zeroPulse, zeroSpace);
+      duration += (zeroPulse + zeroSpace);
+    }
+    else
+    {
+      // Send pulse for "one":
+      rx51device.addPair(onePulse, oneSpace);
+      duration += (onePulse + oneSpace);
+    }
+    ++i;
+  }
+
+  // Add trail on end:
+  if (hasTrailerPulse)
+  {
+    rx51device.addSingle(trailerPulse);
+    duration += trailerPulse;
+  }
+
+  return duration;
+}
+
+
 int NECProtocol::pushBits(
   const CommandSequence &bits,
   PIRRX51Hardware &rx51device)
@@ -265,3 +399,57 @@ int NECProtocol::pushBits(
 
   return duration;
 }
+
+
+int NECProtocol::pushReverseBits(
+  const CommandSequence &bits,
+  PIRRX51Hardware &rx51device)
+{
+  int duration = 0;
+  CommandSequence::const_reverse_iterator i = bits.rbegin();
+  while (i != bits.rend())
+  {
+    if (*i)
+    {
+      // Send the pulse for "One":
+      rx51device.addPair(onePulse, oneSpace);
+      duration += (onePulse + oneSpace);
+    }
+    else
+    {
+      // Send the pulse for "Zero":
+      rx51device.addPair(zeroPulse, zeroSpace);
+      duration += (zeroPulse + zeroSpace);
+    }
+    ++i;
+  }
+
+  return duration;
+}
+
+
+int NECProtocol::pushInvertedReverseBits(
+  const CommandSequence &bits,
+  PIRRX51Hardware &rx51device)
+{
+  int duration = 0;
+  CommandSequence::const_reverse_iterator i = bits.rbegin();
+  while (i != bits.rend())
+  {
+    if (*i)
+    {
+      // Send the pulse for "Zero":
+      rx51device.addPair(zeroPulse, zeroSpace);
+      duration += (zeroPulse + zeroSpace);
+    }
+    else
+    {
+      // Send the pulse for "One":
+      rx51device.addPair(onePulse, oneSpace);
+      duration += (onePulse + oneSpace);
+    }
+    ++i;
+  }
+
+  return duration;
+}