Arduino PulseWire Transceiver Library
Loading...
Searching...
No Matches
PulseDistanceCodec.h
1#pragma once
2
3#include <stddef.h>
4#include <stdint.h>
5
6#include "Codec.h"
7#include "pulse/tools/Vector.h"
8
9namespace pulsewire {
10
22class PulseDistanceCodec : public Codec {
23 public:
24 PulseDistanceCodec() = default;
25
27 uint32_t shortPulseUs = 0, uint32_t longPulseUs = 0)
28 : Codec(detector),
29 _shortPulseUs(shortPulseUs),
30 _longPulseUs(longPulseUs),
31 _toleranceUs(toleranceUs) {}
32
33 // used by ir
34 void init(Preamble& detector, uint32_t shortPulseUs = 0,
35 uint32_t longPulseUs = 0, uint32_t toleranceUs = 0) {
37 _shortPulseUs = shortPulseUs;
38 _longPulseUs = longPulseUs;
39 _toleranceUs = toleranceUs;
40 }
41
42 bool begin(uint32_t bitFrequencyHz) override {
44 if (_shortPulseUs == 0) _shortPulseUs = 1000000UL / (bitFrequencyHz * 2);
45 if (_longPulseUs == 0) _longPulseUs = 1000000UL / (bitFrequencyHz / 2);
46 if (_toleranceUs == 0) _toleranceUs = 0.5 * _bitPeriodUs;
47
48 _inFrame = _preamble->preambleLength() ==
49 0; // If preamble is defined, start in "not in frame" state
50
51 return true;
52 }
53
54 size_t getEdgeCount() const override { return 16; }
55
56 int getEndOfFrameDelayUs() override { return 2 * _longPulseUs; }
57
58 CodecEnum getCodecType() const override { return CodecEnum::PulseDistance; }
59
60 bool getIdleLevel() { return true; }
61
62 size_t encode(uint8_t byte, Vector<OutputEdge>& output) override {
64 bits.reserve(8);
65 encodeByte(byte, bits);
66 size_t total = 0;
67 for (int i = 0; i < 8; ++i) {
68 total += encodeBit(bits[i], output);
69 }
70 return total;
71 }
72
73 bool decodeEdge(uint32_t durationUs, bool level, uint8_t& result) override {
74 // ensure that edges are allocated
75 assert(_decodeEdgeStream.capacity() > 0);
76
77 // Filter idle gaps
78 if (level == getIdleLevel() && durationUs > getEndOfFrameDelayUs()) {
79 Logger::debug("Idle gap detected: %d us, resetting decoder", durationUs);
80 reset();
81 return false;
82 }
83
85 assert(_preamble != nullptr);
86 if (!_inFrame) {
87 if (_preamble->preambleLength() == 0) {
88 // no preamble, the edge is valid playload
89 _decodeEdgeStream.clear();
90 _decodeEdgeStream.push_back(newEdge);
91 _inFrame = true;
92 Logger::debug("No preamble, starting new frame");
93 } else if (_preamble->detect(newEdge)) {
94 // Detected preamble, start new frame
95 _inFrame = true;
96 _decodeEdgeStream.clear();
97 Logger::debug("Preamble detected, starting new frame");
98 }
99 } else {
100 _decodeEdgeStream.push_back(newEdge);
101 }
102
103 // Try to decode bytes if enough edges collected
104 if (_decodeEdgeStream.size() == getEdgeCount()) {
105 bool rc = decodeByte(_decodeEdgeStream, result);
106 Logger::debug("Decoded byte: 0x%x", result);
107 _decodeEdgeStream.clear();
108 return rc;
109 }
110 return false;
111 }
112
113 protected:
114 uint32_t _shortPulseUs = 0;
115 uint32_t _longPulseUs = 0;
116 uint32_t _toleranceUs = 0;
124 size_t encodeBit(bool bit, Vector<OutputEdge>& output) {
126 pulse.level = false;
127 pulse.pulseUs = bit ? _longPulseUs : _shortPulseUs;
128 space.level = true;
129 space.pulseUs = _shortPulseUs;
130
131 output.push_back(pulse);
132 output.push_back(space);
133 return 2;
134 }
135
136 bool decodeByte(Vector<OutputEdge>& edges, uint8_t& result) {
137 if (edges.size() < 16) {
138 Logger::error("Not enough edges to decode byte: %d", edges.size());
139 return false;
140 }
141 uint8_t byte = 0;
142 int bit = 0;
143 for (auto& edge : edges) {
144 // only consider low edges
145 if (edge.level == false) {
146 if (bitMatch(edge.pulseUs, true)) {
147 byte |= (1 << (7 - bit));
148 } else if (bitMatch(edge.pulseUs, false)) {
149 // bit is 0
150 } else {
151 Logger::debug("Invalid pulse duration for bit %d: %d us", bit,
152 edge.pulseUs);
153 }
154 bit++;
155 }
156 }
157 result = byte;
158 return true;
159 }
160
161 bool bitMatch(uint32_t duration, bool bit) const {
162 uint32_t expected = bit ? _longPulseUs : _shortPulseUs;
163 bool rc = (duration >= expected - _toleranceUs &&
164 duration <= expected + _toleranceUs);
166 "Bit match for bit %d: duration=%d, expected=%d, tolerance=%d, "
167 "match=%s",
168 bit ? 1 : 0, duration, expected, _toleranceUs, rc ? "YES" : "NO");
169 return rc;
170 }
171};
172
173} // namespace pulsewire
Abstract base class for IR protocol encoding and decoding.
Definition Codec.h:53
void setPreamble(Preamble &preamble)
Set the Preamble Detector object.
Definition Codec.h:94
virtual void encodeByte(uint8_t byte, std::vector< bool > &bits) const
Encode a byte to protocol bitstream. Default implementation encodes to raw bits (MSB first),...
Definition Codec.h:204
virtual bool begin(uint32_t bitFrequencyHz)
initialization method for codecs that require setup before use (e.g., loading PIO programs,...
Definition Codec.h:70
virtual void reset()
Reset the internal state of the codec.
Definition Codec.h:84
static void debug(const char *format,...)
Log a debug message with formatting.
Definition Logger.h:82
static void error(const char *format,...)
Log an error message with formatting.
Definition Logger.h:37
Abstract base class for preamble detection and generation.
Definition Preamble.h:40
virtual bool detect(const OutputEdge &edge)
Detects if the incoming edge matches the expected preamble pattern.
Definition Preamble.h:52
Pulse-distance encoding/decoding utility class for IR communication.
bool decodeEdge(uint32_t durationUs, bool level, uint8_t &result) override
Edge-based decoding for protocol-agnostic RX drivers.
CodecEnum getCodecType() const override
bool begin(uint32_t bitFrequencyHz) override
initialization method for codecs that require setup before use (e.g., loading PIO programs,...
size_t encodeBit(bool bit, Vector< OutputEdge > &output)
Fill output vector with PulseDistance OutputSpec(s) for a bit.
void init(Preamble &detector, uint32_t shortPulseUs=0, uint32_t longPulseUs=0, uint32_t toleranceUs=0)
size_t getEdgeCount() const override
Get the number of protocol symbols (bits, pulses, etc.) per encoded byte.
size_t encode(uint8_t byte, Vector< OutputEdge > &output) override
Fill output vector with protocol-specific OutputSpec(s) for a byte.
Small, header-only vector replacement for non-STL environments.
Definition Vector.h:29
size_t capacity() const
Current allocated capacity.
Definition Vector.h:167
void clear()
Remove all elements.
Definition Vector.h:120
void push_back(const T &value)
Add element to end of vector.
Definition Vector.h:92
void reserve(size_t cap)
Reserve space for at least cap elements.
Definition Vector.h:157
size_t size() const
Number of elements in vector.
Definition Vector.h:116
Specifies a single IR signal segment for protocol-agnostic transmission.
Definition Preamble.h:23