A couple of UI additions
[pierogi] / protocols / rc6skyprotocol.cpp
1 #include "rc6skyprotocol.h"
2
3 #include "pirrx51hardware.h"
4
5 #include "pirexception.h"
6
7 #include <QMutex>
8 extern bool commandInFlight;
9 extern QMutex commandIFMutex;
10
11 // These defines might need to be turned into variables, for odd devices.
12 #define HEADER_PULSE 2666
13 #define HEADER_SPACE 888
14 #define TRAILER_BIPHASE 888
15
16 // This version of Mode 6 RC6 is used in Sky and Sky+ receivers.  It seems to
17 // be pretty close to vanilla RC6.
18 // The biphase unit of time is 444 usec.
19 // The RC6 header block starts with the normal 2666 usec pulse, 888 usec space.
20 // The next bit is fixed as a "1", as usual.
21 // The next three bits are 110, marking this as a mode 6 protocol.
22 // The trailer bit has an 888 usec biphase.  It is normally a toggle bit, but
23 // for Sky, it appears to be fixed at "0".
24 // Next comes 8 bits of address, 4 bits I don't know about (subdevice?),
25 // and finally 8 bits of command.
26 // A space of (at least) 2666 usec must follow any command.
27 // The carrier frequency is 36 kHZ, duty cycle between 25 and 50 %.
28
29 RC6SkyProtocol::RC6SkyProtocol(
30   QObject *guiObject,
31   unsigned int index)
32   : PIRProtocol(guiObject, index, 2666, false),
33     biphaseUnit(444),
34     buffer(0),
35     keypressCount(0)
36 {
37   setCarrierFrequency(36000);
38 }
39
40
41 void RC6SkyProtocol::startSendingCommand(
42   unsigned int threadableID,
43   PIRKeyName command)
44 {
45   try
46   {
47     // Is this command meant for us?
48     if (threadableID != id) return;
49
50     clearRepeatFlag();
51
52     KeycodeCollection::const_iterator i = keycodes.find(command);
53
54     // Sanity check:
55     if (i == keycodes.end())
56     {
57       std::string s = "Tried to send a non-existent command.\n";
58       throw PIRException(s);
59     }
60
61     PIRRX51Hardware rx51device(carrierFrequency, dutyCycle);
62
63     int repeatCount = 0;
64     int duration = 0;
65     while (repeatCount < MAX_REPEAT_COUNT)
66     {
67       bufferContainsSpace = false;
68       bufferContainsPulse = false;
69       // First, construct the "Header" segment of the pulse train.
70       //
71       // The header involves:
72       // a) a "lead" of 2666 us pulse, 888 us space;
73       // b) a "start bit", value 1 (so 444 us pulse, 444 us space)
74       // c) three control bits, set to "110" (i.e., mode "6")
75       // d) the double-sized "trailer" bit, set based on the keypress count:
76       // d2) I'm trying out setting the toggle bit to always be 0.
77
78       rx51device.addSingle(HEADER_PULSE); // lead pulse
79       duration += HEADER_PULSE;
80       rx51device.addSingle(HEADER_SPACE); // lead space
81       duration += HEADER_SPACE;
82       rx51device.addSingle(biphaseUnit); // start bit pulse
83       duration += biphaseUnit;
84       rx51device.addSingle(biphaseUnit); // start bit space
85       duration += biphaseUnit;
86       rx51device.addSingle(biphaseUnit); // bit 1 pulse;
87       duration += biphaseUnit;
88       rx51device.addSingle(biphaseUnit); // bit 1 space;
89       duration += biphaseUnit;
90       rx51device.addSingle(biphaseUnit); // bit 2 pulse;
91       duration += biphaseUnit;
92       rx51device.addSingle(2 * biphaseUnit); // bit 2 space + bit 3 space;
93       duration += 2 * biphaseUnit;
94       rx51device.addSingle(biphaseUnit); // bit 3 pulse;
95       duration += biphaseUnit;
96       rx51device.addSingle(2 * biphaseUnit); // trailer space
97       duration += 2 * biphaseUnit;
98       buffer = 2 * biphaseUnit; // trailer pulse goes into the buffer
99       bufferContainsPulse = true;
100
101       // Now, we can start the normal buffering process:
102
103       // push the address data:
104       duration += pushBits(preData, rx51device);
105
106       // push the command data:
107       duration += pushBits((*i).second.firstCode, rx51device);
108
109       // Flush out the buffer, if necessary:
110       if (buffer)
111       {
112         rx51device.addSingle(buffer);
113         duration += buffer;
114         buffer = 0;
115       }
116
117       // Actually send out the command:
118       rx51device.sendCommandToDevice();
119
120       // Sleep for an amount of time.  (RC6 demands an addtional 6 unit space
121       // at the end of any command...)
122       sleepUntilRepeat(duration + 6 * biphaseUnit);
123
124       // Have we been told to stop yet?
125       if (checkRepeatFlag())
126       {
127         // Yes, we can now quit repeating:
128         ++keypressCount;
129         QMutexLocker ciflocker(&commandIFMutex);
130         commandInFlight = false;
131         return;
132       }
133     }
134   }
135   catch (PIRException e)
136   {
137     emit commandFailed(e.getError().c_str());
138   }
139
140   ++keypressCount;
141   QMutexLocker cifLocker(&commandIFMutex);
142   commandInFlight = false;
143 }
144
145
146 int RC6SkyProtocol::pushBits(
147   const CommandSequence &bits,
148   PIRRX51Hardware &rx51device)
149 {
150   int duration = 0;
151
152   CommandSequence::const_iterator i = bits.begin();
153
154   while (i != bits.end())
155   {
156     if (*i)
157     {
158       duration += pushOne(rx51device);
159     }
160     else
161     {
162       duration += pushZero(rx51device);
163     }
164
165     ++i;
166   }
167
168   return duration;
169 }
170
171
172 // This should be part of a general RC6 parent maybe?
173 int RC6SkyProtocol::pushZero(
174   PIRRX51Hardware &rx51device)
175 {
176   // Need to add a space, then a pulse.
177   int duration = 0;
178
179   if (bufferContainsSpace)
180   {
181     // Merge this space and the previous one, and send to device:
182     rx51device.addSingle(buffer + biphaseUnit);
183     duration += (buffer + biphaseUnit);
184     buffer = 0;
185      bufferContainsSpace = false;
186   }
187   else
188   {
189     if (bufferContainsPulse)
190     {
191       // Flush out the buffer:
192       rx51device.addSingle(buffer);
193       duration += buffer;
194       buffer = 0;
195       bufferContainsPulse = false;
196     }
197
198     // push a space onto the device:
199     rx51device.addSingle(biphaseUnit);
200     duration += biphaseUnit;
201   }
202
203   // Put a pulse into the buffer to wait:
204   buffer = biphaseUnit;
205   bufferContainsPulse = true;
206
207   return duration;
208 }
209
210
211 int RC6SkyProtocol::pushOne(
212   PIRRX51Hardware &rx51device)
213 {
214   // Need to add a pulse, then a space.
215   int duration = 0;
216
217   // First, the pulse:
218   if (bufferContainsPulse)
219   {
220     rx51device.addSingle(buffer + biphaseUnit);
221     duration += (buffer + biphaseUnit);
222     buffer = 0;
223     bufferContainsPulse = false;
224   }
225   else
226   {
227     if (bufferContainsSpace)
228     {
229       // Flush the buffer:
230       rx51device.addSingle(buffer);
231       duration += buffer;
232       buffer = 0;
233       bufferContainsSpace = false;
234     }
235     // Now, add the pulse:
236     rx51device.addSingle(biphaseUnit);
237     duration += biphaseUnit;
238   }
239
240   // Next, push a space onto the buffer:
241   buffer = biphaseUnit;
242   bufferContainsSpace = true;
243
244   return duration;
245 }