bdbccfeeb40306eace3780b99f76d8fe9e46119d
[pierogi] / protocols / rc6protocol.cpp
1 #include "rc6protocol.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 889
14 #define TRAILER_PULSE 889
15 #define TRAILER_SPACE 889
16
17 // I'm requiring standard RC6 initialization to include the 8-bit control
18 // section:
19 RC6Protocol::RC6Protocol(
20   QObject *guiObject,
21   unsigned int index,
22   unsigned int eightBitControl)
23   : PIRProtocol(guiObject, index, 108000, true),
24     biphaseUnit(444),
25     buffer(0),
26     keypressCount(0)
27 {
28   setCarrierFrequency(36000);
29   setPreData(eightBitControl, 8);
30 }
31
32
33 void RC6Protocol::startSendingCommand(
34   unsigned int threadableID,
35   PIRKeyName command)
36 {
37   try
38   {
39     // Is this command meant for us?
40     if (threadableID != id) return;
41
42     clearRepeatFlag();
43
44     KeycodeCollection::const_iterator i = keycodes.find(command);
45
46     // Sanity check:
47     if (i == keycodes.end())
48     {
49       std::string s = "Tried to send a non-existent command.\n";
50       throw PIRException(s);
51     }
52
53     PIRRX51Hardware rx51device(carrierFrequency, dutyCycle);
54
55     int repeatCount = 0;
56     int duration = 0;
57     while (repeatCount < MAX_REPEAT_COUNT)
58     {
59       // First, construct the "Header" segment of the pulse train.  For now,
60       // I'm only supporting the "consumer electronics mode" of RC6; this code
61       // must be changed if we want to support more than that!
62       //
63       // The header involves:
64       // a) a "lead" of 2666 us pulse, 889 us space;
65       // b) a "start bit", value 1 (so 444 us pulse, 444 us space)
66       // c) three control bits, always set to 0 (so 444 us space,
67       //    444 us pulse each)
68       // d) the double-sized "trailer" bit, toggled on each keypress (so
69       //    either 889 pulse 889 space, or 889 space 889 pulse)
70
71       rx51device.addSingle(HEADER_PULSE); // lead pulse
72       duration += HEADER_PULSE;
73       rx51device.addSingle(HEADER_SPACE); // lead space
74       duration += HEADER_SPACE;
75       rx51device.addSingle(biphaseUnit); // start bit pulse
76       duration += biphaseUnit;
77
78       // start bit space + control bit 1 space:
79       rx51device.addSingle(2 * biphaseUnit);
80       duration += 2 * biphaseUnit;
81       rx51device.addSingle(biphaseUnit); // bit 1 pulse;
82       duration += biphaseUnit;
83       rx51device.addSingle(biphaseUnit); // bit 2 space;
84       duration += biphaseUnit;
85       rx51device.addSingle(biphaseUnit); // bit 2 pulse;
86       duration += biphaseUnit;
87       rx51device.addSingle(biphaseUnit); // bit 3 space;
88       duration += biphaseUnit;
89
90       // Next, need to check whether we should toggle or not:
91       if (keypressCount % 2)
92       {
93         // bit 3 pulse plus long trailer bit pulse:
94         rx51device.addSingle(3 * biphaseUnit);
95         duration += 3 * biphaseUnit;
96
97         // load the trailer bit space onto the buffer:
98         buffer = 2 * biphaseUnit;
99         bufferContainsSpace = true;
100         bufferContainsPulse = false;
101       }
102       else
103       {
104         rx51device.addSingle(biphaseUnit); // bit three pulse
105         duration += biphaseUnit;
106         rx51device.addSingle(2 * biphaseUnit); // trailer bit space
107         duration += 2 * biphaseUnit;
108
109         // load the trailer bit pulse onto the buffer:
110         buffer = 2 * biphaseUnit;
111         bufferContainsPulse = true;
112         bufferContainsSpace = false;
113       }
114
115       // Now, we can start the normal buffering process:
116
117       // push any pre-data onto the device:
118       duration += pushBits(preData, rx51device);
119
120       // push the actual command:
121       duration += pushBits((*i).second.firstCode, rx51device);
122
123       // Flush out the buffer, if necessary:
124       if (buffer)
125       {
126         rx51device.addSingle(buffer);
127         duration += buffer;
128         buffer = 0;
129       }
130
131       // Actually send out the command:
132       rx51device.sendCommandToDevice();
133
134       // Sleep for an amount of time.  (RC6 demands an addtional 6 unit space
135       // at the end of any command...)
136       sleepUntilRepeat(duration + 6 * biphaseUnit);
137
138       // Have we been told to stop yet?
139       if (checkRepeatFlag())
140       {
141         // Yes, we can now quit repeating:
142         ++keypressCount;
143         QMutexLocker ciflocker(&commandIFMutex);
144         commandInFlight = false;
145         return;
146       }
147     }
148   }
149   catch (PIRException e)
150   {
151     emit commandFailed(e.getError().c_str());
152   }
153
154   ++keypressCount;
155   QMutexLocker cifLocker(&commandIFMutex);
156   commandInFlight = false;
157 }
158
159
160 int RC6Protocol::pushBits(
161   const CommandSequence &bits,
162   PIRRX51Hardware &rx51device)
163 {
164   int bitsDuration = 0;
165
166   CommandSequence::const_iterator i = bits.begin();
167   bool bitValue;
168
169   while (i != bits.end())
170   {
171     bitValue = *i;
172
173     // In RC6, a "0" is represented by a space followed by a pulse,
174     // and a "1" is represented by a pulse followed by a space.
175     if (bitValue)
176     {
177       // This is a 1, so add a pulse, then a space.
178       // First, the pulse:
179       if (bufferContainsPulse)
180       {
181         rx51device.addSingle(buffer + biphaseUnit);
182         bitsDuration += (buffer + biphaseUnit);
183         buffer = 0;
184         bufferContainsPulse = false;
185       }
186       else
187       {
188         if (bufferContainsSpace)
189         {
190           // Flush the buffer:
191           rx51device.addSingle(buffer);
192           bitsDuration += buffer;
193           buffer = 0;
194           bufferContainsSpace = false;
195         }
196         // Now, add the pulse:
197         rx51device.addSingle(biphaseUnit);
198         bitsDuration += biphaseUnit;
199       }
200
201       // Next, push a space onto the buffer:
202       buffer = biphaseUnit;
203       bufferContainsSpace = true;
204     }
205     else
206     {
207       // This is a 0, so add a space, then a pulse.
208       if (bufferContainsSpace)
209       {
210         // Merge this space and the previous one, and send to device:
211         rx51device.addSingle(buffer + biphaseUnit);
212         bitsDuration += (buffer + biphaseUnit);
213         buffer = 0;
214         bufferContainsSpace = false;
215       }
216       else
217       {
218         if (bufferContainsPulse)
219         {
220           // Flush out the buffer:
221           rx51device.addSingle(buffer);
222           bitsDuration += buffer;
223           buffer = 0;
224           bufferContainsPulse = false;
225         }
226
227         // push a space onto the device:
228         rx51device.addSingle(biphaseUnit);
229         bitsDuration += biphaseUnit;
230       }
231
232       // Put a pulse into the buffer to wait:
233       buffer = biphaseUnit;
234       bufferContainsPulse = true;
235     }
236
237     ++i;
238   }
239
240   return bitsDuration;
241 }