6f74e07cc942b6a0fffa45dd451daebedc863cce
[pierogi] / protocols / dishprotocol.cpp
1 #include "dishprotocol.h"
2
3 #include "pirrx51hardware.h"
4
5 #include "pirexception.h"
6
7 // Some global communications stuff:
8 #include <QMutex>
9 extern bool commandInFlight;
10 extern QMutex commandIFMutex;
11
12 // Dish Network, or now just "Dish", has a unique protocol.  It uses "space"
13 // encoding, but otherwise is unlike just about anything else out there.
14 // Here's what I've got on it:
15 // A "zero" is encoded with a 400 usec pulse, 2800 usec space.
16 // A "one" is encoded with a 400 usec pulse, 1600 usec space.
17 // The header is a 400 usec pulse, 6000 usec space.
18 // Commands end with a trailing 400 usec pulse.
19 // Commands are repeated without the header (or, if you like, you can consider
20 // the trailing pulse to be the header for the next repetition).
21 // There is a 6000 usec gap between repetitions.
22 // The carrier frequency is apparently 57600 kHz, or thereabouts.
23 // The duty cycle is 1/3.
24
25 DishProtocol::DishProtocol(
26   QObject *guiObject,
27   unsigned int index)
28   : SpaceProtocol(
29       guiObject, index,
30       400, 2800,
31       400, 1600,
32       400, 6000,
33       400,
34       6000, false)
35 {
36   setCarrierFrequency(57600);
37   setDutyCycle(33);
38 }
39
40
41 void DishProtocol::startSendingCommand(
42   unsigned int threadableID,
43   PIRKeyName command)
44 {
45   // Exceptions here are problematic; I'll try to weed them out by putting the
46   // whole thing in a try/catch block:
47   try
48   {
49     // First, check if we are meant to be the recipient of this command:
50     if (threadableID != id) return;
51
52     clearRepeatFlag();
53
54     KeycodeCollection::const_iterator i = keycodes.find(command);
55
56     // Do we even have this key defined?
57     if (i == keycodes.end())
58     {
59       std::string s = "Tried to send a non-existent command.\n";
60       throw PIRException(s);
61     }
62
63     // construct the device:
64     PIRRX51Hardware rx51device(carrierFrequency, dutyCycle);
65
66     int repeatCount = 0;
67     int commandDuration = 0;
68     while (repeatCount < MAX_REPEAT_COUNT)
69     {
70       // If this is not a repetition, we need to add the initial header:
71       if (repeatCount == 0)
72       {
73         rx51device.addPair(headerPulse, headerSpace);
74         commandDuration += (headerPulse + headerSpace);
75       }
76
77       // generate the rest of the command:
78       commandDuration += generateStandardCommand(i->second, rx51device);
79
80       // Now, tell the device to send the whole command:
81       rx51device.sendCommandToDevice();
82
83       // sleep until the next repetition of command:
84       sleepUntilRepeat(commandDuration);
85
86       // Check whether we've reached the minimum required number of repetitons:
87       if (repeatCount >= minimumRepetitions)
88       {
89         // Check whether we've been asked to stop:
90         if (checkRepeatFlag())
91         {
92           QMutexLocker cifLocker(&commandIFMutex);
93           commandInFlight = false;
94           return;
95         }
96       }
97
98       ++repeatCount;
99     }
100   }
101   catch (PIRException e)
102   {
103     // inform the gui:
104     emit commandFailed(e.getError().c_str());
105   }
106
107   QMutexLocker cifLocker(&commandIFMutex);
108   commandInFlight = false;
109 }
110
111
112 int DishProtocol::generateStandardCommand(
113   const PIRKeyBits &pkb,
114   PIRRX51Hardware &rx51device)
115 {
116   int duration = 0;
117
118   // In the Dish protocol, the pulse train consists of 6 bits of command
119   // data followed by 10 bits of, uhm, other stuff.  I know that at least the
120   // first four bits of these ten can be used for pairing purposes.  It looks
121   // like the last five bits can be used as additional command data.  For
122   // now, I'm going to split this into 5 "address" and 5 "more command" bits.
123   //
124   // The command data is sent MSB-first (I know that at least the first
125   // 6 bits are, I'll represent the last 5 that way as well).
126   // The pairing data is sent LSB-first, god knows why.
127   // - "firstCode" should contain 6-bit value
128   // - "preData" should contain 5 bits of pairing data
129   // - "secondCode" should contain the last 5 bits.
130   duration += pushBits(pkb.firstCode, rx51device);
131   duration += pushReverseBits(postData, rx51device);
132   duration += pushBits(pkb.secondCode, rx51device);
133
134   // Finally add the "trail":
135   rx51device.addSingle(trailerPulse);
136   duration += trailerPulse;
137
138   return duration;
139 }
140