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