Advanced Settings Panel
[pierogi] / protocols / directvprotocol.cpp
1 #include "directvprotocol.h"
2
3 #include "pirrx51hardware.h"
4
5 #include "pirexception.h"
6 #include <string>
7
8 // Some global communications stuff:
9 #include <QMutex>
10 extern bool commandInFlight;
11 extern QMutex commandIFMutex;
12
13 // Directv is using a protocol that is novel to me: each pulse and space
14 // encodes an entire bit in an of themselves.
15 // Here are the details I've got:
16 // For a 0 bit, a duration of 600 usecs is used.
17 // For a 1 bit, the duration is 1200 usecs.
18 // The header is a 6000 usec pulse, 1200 usec space.
19 // Commands end with a trailing 600 usec pulse.
20 // Commands are repeated by re-sending the entire pulse train, except that
21 // the header pulse is cut in half to 3000 usec.
22 // There are two different sizes of gap between commands: 9000 usec and
23 // 30000 usec.
24 // There are three different carrier frequencies: 38 kHz, 40 kHz, and 57 kHz.
25
26 DirectvProtocol::DirectvProtocol(
27   QObject *guiObject,
28   unsigned int index)
29   : PIRProtocol(guiObject, index, 9000, false)
30 {
31 }
32
33
34 void DirectvProtocol::setProtocolParms(
35   DirectvGapSize gap,
36   DirectvFreq freq)
37 {
38   if (gap == ShortGap_Directv)
39   {
40     setGapSize(9000, false);
41   }
42   else
43   {
44     setGapSize(30000, false);
45   }
46
47   switch (freq)
48   {
49   case LowFreq_Directv:
50     setCarrierFrequency(38000);
51     break;
52
53   case MediumFreq_Directv:
54     setCarrierFrequency(40000);
55     break;
56
57   case HighFreq_Directv: default:
58     setCarrierFrequency(57000);
59     break;
60   }
61 }
62
63
64 void DirectvProtocol::startSendingCommand(
65   unsigned int threadableID,
66   PIRKeyName command)
67 {
68   // Exceptions here are problematic; I'll try to weed them out by putting the
69   // whole thing in a try/catch block:
70   try
71   {
72     // First, check if we are meant to be the recipient of this command:
73     if (threadableID != id) return;
74
75     clearRepeatFlag();
76
77     KeycodeCollection::const_iterator i = keycodes.find(command);
78
79     // Do we even have this key defined?
80     if (i == keycodes.end())
81     {
82       QMutexLocker cifLocker(&commandIFMutex);
83       commandInFlight = false;
84       return;
85 //      std::string s = "Tried to send a non-existent command.\n";
86 //      throw PIRException(s);
87     }
88
89     // construct the device:
90     PIRRX51Hardware rx51device(carrierFrequency, dutyCycle);
91
92     int repeatCount = 0;
93     int commandDuration = 0;
94     while (repeatCount < MAX_REPEAT_COUNT)
95     {
96       // When repeating, we use a short-pulse header.  Otherwise, we use
97       // a long-pulse header.
98       if (repeatCount)
99       {
100         rx51device.addPair(3000, 1200);
101         commandDuration += 4200;
102       }
103       else
104       {
105         rx51device.addPair(6000, 1200);
106         commandDuration += 7200;
107       }
108
109       // Now, generate the rest of the command:
110       commandDuration += generateStandardCommand((*i).second, rx51device);
111
112       // Now, tell the device to send the whole command:
113       rx51device.sendCommandToDevice();
114
115       // sleep until the next repetition of command:
116       sleepUntilRepeat(commandDuration);
117
118       // Check whether we've reached the minimum required number of repetitons:
119       if (repeatCount >= minimumRepetitions)
120       {
121         // Check whether we've been asked to stop:
122         if (checkRepeatFlag())
123         {
124           break;
125 /*
126           QMutexLocker cifLocker(&commandIFMutex);
127           commandInFlight = false;
128           return;
129 */
130         }
131       }
132
133       ++repeatCount;
134     }
135
136     QMutexLocker cifLocker(&commandIFMutex);
137     commandInFlight = false;
138   }
139   catch (PIRException e)
140   {
141     // inform the gui:
142     emit commandFailed(e.getError().c_str());
143   }
144 }
145
146
147 int DirectvProtocol::generateStandardCommand(
148   const PIRKeyBits &pkb,
149   PIRRX51Hardware &rx51device)
150 {
151   int duration = 0;
152
153   // DirecTV's protocol appears to consist of four bits of address data,
154   // eight bits of command data, and four bits of checksum data.
155   // The checksum is calculated off of the command, as so: Add up:
156   // 7 * the first two bits of the command, plus
157   // 5 * the second two bits of the command, plus
158   // 3 * the third two bits of the command, plus
159   // 1 * the last two bits of the command,
160   // then just take the last four bits of this value.
161
162   // - "preData" will contain the address
163   // - "firstCode" will contain the command
164   duration += pushDTVBits(preData, rx51device);
165   duration += pushDTVBits(pkb.firstCode, rx51device);
166
167   CommandSequence checksumBits;
168   generateChecksum(pkb.firstCode, checksumBits);
169   duration += pushDTVBits(checksumBits, rx51device);
170
171   // Finally add the "trail":
172   rx51device.addSingle(600);
173   duration += 600;
174
175   return duration;
176 }
177
178
179 int DirectvProtocol::pushDTVBits(
180   const CommandSequence &bits,
181   PIRRX51Hardware &rx51device)
182 {
183   int duration = 0;
184   CommandSequence::const_iterator i = bits.begin();
185   while (i != bits.end())
186   {
187     if (*i)
188     {
189       // Send the value for "One":
190       rx51device.addSingle(1200);
191       duration += 1200;
192     }
193     else
194     {
195       // Send the value for "Zero":
196       rx51device.addSingle(600);
197       duration += 600;
198     }
199     ++i;
200   }
201
202   return duration;
203 }
204
205
206 void DirectvProtocol::generateChecksum(
207   const CommandSequence &bits,
208   CommandSequence &checksumBits)
209 {
210   // We'll generate a 4-bit value based on the 8 bits of the command:
211
212   int checksumVal = 0;
213
214   CommandSequence::const_iterator i = bits.begin();
215
216   if (i == bits.end()) return;
217
218   if (*i) checksumVal += 14;
219
220   ++i;
221
222   if (i == bits.end()) return;
223
224   if (*i) checksumVal += 7;
225
226   ++i;
227
228   if (i == bits.end()) return;
229
230   if (*i) checksumVal += 10;
231
232   ++i;
233
234   if (i == bits.end()) return;
235
236   if (*i) checksumVal += 5;
237
238   ++i;
239
240   if (i == bits.end()) return;
241
242   if (*i) checksumVal += 6;
243
244   ++i;
245
246   if (i == bits.end()) return;
247
248   if (*i) checksumVal += 3;
249
250   ++i;
251
252   if (i == bits.end()) return;
253
254   if (*i) checksumVal += 2;
255
256   ++i;
257
258   if (i == bits.end()) return;
259
260   if (*i) checksumVal += 1;
261
262   // Push the last four bits into the command sequence:
263   if (checksumVal & 0x8)  
264   {
265     checksumBits.push_back(1);
266   }
267   else
268   {
269     checksumBits.push_back(0);
270   }
271
272   if (checksumVal & 0x4)
273   {
274     checksumBits.push_back(1);
275   }
276   else
277   {
278     checksumBits.push_back(0);
279   }
280
281   if (checksumVal & 0x2)
282   {
283     checksumBits.push_back(1);
284   }
285   else
286   {
287     checksumBits.push_back(0);
288   }
289
290   if (checksumVal & 0x1)
291   {
292     checksumBits.push_back(1);
293   }
294   else
295   {
296     checksumBits.push_back(0);
297   }
298 }