116 lines
3.4 KiB
C
116 lines
3.4 KiB
C
|
|
||
|
//==============================================================================
|
||
|
// L EEEEEE EEEE OOOO
|
||
|
// L E E O O
|
||
|
// L EEEE E EEE O O
|
||
|
// L E E E O O LEGO Power Functions
|
||
|
// LLLLLL EEEEEE EEEE OOOO Copyright (c) 2016, 2017 Philipp Henkel
|
||
|
//==============================================================================
|
||
|
|
||
|
//+=============================================================================
|
||
|
//
|
||
|
|
||
|
class LegoPfBitStreamEncoder {
|
||
|
private:
|
||
|
uint16_t data;
|
||
|
bool repeatMessage;
|
||
|
uint8_t messageBitIdx;
|
||
|
uint8_t repeatCount;
|
||
|
uint16_t messageLength;
|
||
|
|
||
|
public:
|
||
|
// HIGH data bit = IR mark + high pause
|
||
|
// LOW data bit = IR mark + low pause
|
||
|
static const uint16_t LOW_BIT_DURATION = 421;
|
||
|
static const uint16_t HIGH_BIT_DURATION = 711;
|
||
|
static const uint16_t START_BIT_DURATION = 1184;
|
||
|
static const uint16_t STOP_BIT_DURATION = 1184;
|
||
|
static const uint8_t IR_MARK_DURATION = 158;
|
||
|
static const uint16_t HIGH_PAUSE_DURATION = HIGH_BIT_DURATION - IR_MARK_DURATION;
|
||
|
static const uint16_t LOW_PAUSE_DURATION = LOW_BIT_DURATION - IR_MARK_DURATION;
|
||
|
static const uint16_t START_PAUSE_DURATION = START_BIT_DURATION - IR_MARK_DURATION;
|
||
|
static const uint16_t STOP_PAUSE_DURATION = STOP_BIT_DURATION - IR_MARK_DURATION;
|
||
|
static const uint8_t MESSAGE_BITS = 18;
|
||
|
static const uint16_t MAX_MESSAGE_LENGTH = 16000;
|
||
|
|
||
|
void reset(uint16_t data, bool repeatMessage) {
|
||
|
this->data = data;
|
||
|
this->repeatMessage = repeatMessage;
|
||
|
messageBitIdx = 0;
|
||
|
repeatCount = 0;
|
||
|
messageLength = getMessageLength();
|
||
|
}
|
||
|
|
||
|
int getChannelId() const { return 1 + ((data >> 12) & 0x3); }
|
||
|
|
||
|
uint16_t getMessageLength() const {
|
||
|
// Sum up all marks
|
||
|
uint16_t length = MESSAGE_BITS * IR_MARK_DURATION;
|
||
|
|
||
|
// Sum up all pauses
|
||
|
length += START_PAUSE_DURATION;
|
||
|
for (unsigned long mask = 1UL << 15; mask; mask >>= 1) {
|
||
|
if (data & mask) {
|
||
|
length += HIGH_PAUSE_DURATION;
|
||
|
} else {
|
||
|
length += LOW_PAUSE_DURATION;
|
||
|
}
|
||
|
}
|
||
|
length += STOP_PAUSE_DURATION;
|
||
|
return length;
|
||
|
}
|
||
|
|
||
|
boolean next() {
|
||
|
messageBitIdx++;
|
||
|
if (messageBitIdx >= MESSAGE_BITS) {
|
||
|
repeatCount++;
|
||
|
messageBitIdx = 0;
|
||
|
}
|
||
|
if (repeatCount >= 1 && !repeatMessage) {
|
||
|
return false;
|
||
|
} else if (repeatCount >= 5) {
|
||
|
return false;
|
||
|
} else {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
uint8_t getMarkDuration() const { return IR_MARK_DURATION; }
|
||
|
|
||
|
uint32_t getPauseDuration() const {
|
||
|
if (messageBitIdx == 0)
|
||
|
return START_PAUSE_DURATION;
|
||
|
else if (messageBitIdx < MESSAGE_BITS - 1) {
|
||
|
return getDataBitPause();
|
||
|
} else {
|
||
|
return getStopPause();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
uint16_t getDataBitPause() const {
|
||
|
const int pos = MESSAGE_BITS - 2 - messageBitIdx;
|
||
|
const bool isHigh = data & (1 << pos);
|
||
|
return isHigh ? HIGH_PAUSE_DURATION : LOW_PAUSE_DURATION;
|
||
|
}
|
||
|
|
||
|
uint32_t getStopPause() const {
|
||
|
if (repeatMessage) {
|
||
|
return getRepeatStopPause();
|
||
|
} else {
|
||
|
return STOP_PAUSE_DURATION;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
uint32_t getRepeatStopPause() const {
|
||
|
if (repeatCount == 0 || repeatCount == 1) {
|
||
|
return STOP_PAUSE_DURATION + (uint32_t)5 * MAX_MESSAGE_LENGTH - messageLength;
|
||
|
} else if (repeatCount == 2 || repeatCount == 3) {
|
||
|
return STOP_PAUSE_DURATION
|
||
|
+ (uint32_t)(6 + 2 * getChannelId()) * MAX_MESSAGE_LENGTH - messageLength;
|
||
|
} else {
|
||
|
return STOP_PAUSE_DURATION;
|
||
|
}
|
||
|
}
|
||
|
};
|