Advanced Settings Panel
[pierogi] / protocols / pioneerprotocol.cpp
1 #include "pioneerprotocol.h"
2
3 #include "pirrx51hardware.h"
4
5 #include "pirexception.h"
6 #include <string>
7 //#include <iostream>
8
9 // Some global communications stuff:
10 #include <QMutex>
11 extern bool commandInFlight;
12 extern QMutex commandIFMutex;
13
14 // Pioneer's protocol seems almost the same as standard NEC protocol, but
15 // a few details are slightly different.  (Can't seem to find exact details.)
16 // A "zero" is encoded with a 530 usec pulse, 530 usec space.
17 // A "one" is encoded with a 530 usec pulse, and 3*530 (1590) usec space.
18 // The header is a 8500 usec pulse, 4250 usec space.
19 // Commands end with a trailing 530 usec pulse.
20 // Commands are repeated by re-sending the entire pulse train.
21 // Each command is separated by 25000 usec.
22 // The carrier frequency is probably 40 kHz, duty cycle probably 50%.
23
24 PioneerProtocol::PioneerProtocol(
25   QObject *guiObject,
26   unsigned int index)
27   : SpaceProtocol(
28       guiObject, index,
29       530, 530,
30       530, 1590,
31       8500, 4250,
32       530,
33       25000, false)
34 {
35   setCarrierFrequency(40000);
36   setDutyCycle(50);
37 }
38
39
40 void PioneerProtocol::startSendingCommand(
41   unsigned int threadableID,
42   PIRKeyName command)
43 {
44   // Exceptions here are problematic; I'll try to weed them out by putting the
45   // whole thing in a try/catch block:
46   try
47   {
48     // First, check if we are meant to be the recipient of this command:
49     if (threadableID != id) return;
50
51     clearRepeatFlag();
52
53     KeycodeCollection::const_iterator i = keycodes.find(command);
54
55     // Do we even have this key defined?
56     if (i == keycodes.end())
57     {
58       QMutexLocker cifLocker(&commandIFMutex);
59       commandInFlight = false;
60       return;
61 //      std::string s = "Tried to send a non-existent command.\n";
62 //      throw PIRException(s);
63     }
64
65     // construct the device:
66     PIRRX51Hardware rx51device(carrierFrequency, dutyCycle);
67
68     int repeatCount = 0;
69     int commandDuration = 0;
70     while (repeatCount < MAX_REPEAT_COUNT)
71     {
72       // If we are on an odd repetition, and we have a secondary keycode,
73       // we'll generate the secondary command for this key.  Otherwise,
74       // we always generate a standard command.
75       if ((repeatCount % 2) && (!(i->second.thirdCode.empty())))
76       {
77         commandDuration = generateSecondaryCommand(i->second, rx51device);
78       }
79       else
80       {
81         commandDuration = generateStandardCommand(i->second, rx51device);
82       }
83
84       // Now, tell the device to send the whole command:
85       rx51device.sendCommandToDevice();
86
87       // sleep until the next repetition of command:
88       sleepUntilRepeat(commandDuration);
89
90       // Check whether we've reached the minimum required number of repetitons:
91 //      if (repeatCount >= minimumRepetitions)
92 //      if (repeatCount >= 3)
93 //      {
94         // Check whether we've been asked to stop:
95         if (checkRepeatFlag())
96         {
97           break;
98 /*
99           QMutexLocker cifLocker(&commandIFMutex);
100           commandInFlight = false;
101           return;
102 */
103         }
104 //      }
105
106       ++repeatCount;
107     }
108
109     QMutexLocker cifLocker(&commandIFMutex);
110     commandInFlight = false;
111   }
112   catch (PIRException e)
113   {
114     // inform the gui:
115     emit commandFailed(e.getError().c_str());
116   }
117 }
118
119
120 int PioneerProtocol::generateStandardCommand(
121   const PIRKeyBits &pkb,
122   PIRRX51Hardware &rx51device)
123 {
124   int duration = 0;
125
126   // First, the "header" pulse:
127   rx51device.addPair(headerPulse, headerSpace);
128   duration += (headerPulse + headerSpace);
129
130   // Now, the data, following standard NEC rules.  (Note that we are not
131   // using the "preData" value here, most Pioneer devices require more than
132   // one address value.  Therefore, I'm requiring all keys to explicitly
133   // load the address value into the "firstCode" member.)
134   duration += pushReverseBits(pkb.firstCode, rx51device);
135   duration += pushInvertedReverseBits(pkb.firstCode, rx51device);
136   duration += pushReverseBits(pkb.secondCode, rx51device);
137   duration += pushInvertedReverseBits(pkb.secondCode, rx51device);
138
139   // Finally add the "trail":
140   rx51device.addSingle(trailerPulse);
141   duration += trailerPulse;
142
143   return duration;
144 }
145
146
147 int PioneerProtocol::generateSecondaryCommand(
148   const PIRKeyBits &pkb,
149   PIRRX51Hardware &rx51device)
150 {
151   int duration = 0;
152
153   // First, the "header" pulse:
154   rx51device.addPair(headerPulse, headerSpace);
155   duration += (headerPulse + headerSpace);
156
157   // Now, the data, following standard NEC rules.  (The secondary command
158   // uses the third and fourth key codes.)
159   duration += pushReverseBits(pkb.thirdCode, rx51device);
160   duration += pushInvertedReverseBits(pkb.thirdCode, rx51device);
161   duration += pushReverseBits(pkb.fourthCode, rx51device);
162   duration += pushInvertedReverseBits(pkb.fourthCode, rx51device);
163
164   // Finally add the "trail":
165   rx51device.addSingle(trailerPulse);
166   duration += trailerPulse;
167
168   return duration;
169 }