Arduino PulseWire Transceiver Library
Loading...
Searching...
No Matches
RZCodec.h
1#pragma once
2#include "Codec.h"
3#include "pulse/tools/RingBuffer.h"
4
5namespace pulsewire {
6
12class RZCodec : public Codec {
13 public:
14 RZCodec(int stopBits = 1) : _stopBits(stopBits) {}
15
17 : Codec(preambleDetector), _stopBits(stopBits) {}
18
19 virtual ~RZCodec() = default;
20
21 CodecEnum getCodecType() const override { return CodecEnum::RZ; }
22
23 bool getIdleLevel() const override { return true; }
24
25 size_t getEdgeCount() const override { return 16; }
26
27 int getEndOfFrameDelayUs() override { return 8 * _bitPeriodUs; }
28
29 void reset() override {
31 _bitCount = 0;
32 _byte = 0;
33 _rxState = WAIT_START;
34 _rxDataBitCount = 0;
35 _rxStopBitCount = 0;
36 _edgeBuffer.clear();
37 }
38
39 size_t encode(uint8_t byte, Vector<OutputEdge>& output) override {
40 size_t edgeCount = 0;
41
42 // Start bit (LOW)
44 start.level = false;
45 start.pulseUs = _bitPeriodUs;
46 output.push_back(start);
47 ++edgeCount;
48
49 // Data bits (LSB first)
50 for (int bit = 0; bit < 8; ++bit) {
51 OutputEdge data;
52 if (byte & (1 << bit)) {
53 data.level = true;
54 data.pulseUs = _bitPeriodUs / 2;
55 output.push_back(data);
56 data.level = false;
57 data.pulseUs = _bitPeriodUs / 2;
58 output.push_back(data);
59
60 } else {
61 data.level = false;
62 data.pulseUs = _bitPeriodUs;
63 output.push_back(data);
64 }
65 ++edgeCount;
66 }
67
68 // Stop bits (HIGH)
69 for (uint8_t s = 0; s < _stopBits; ++s) {
71 stop.level = true;
72 stop.pulseUs = _bitPeriodUs;
73 output.push_back(stop);
74 ++edgeCount;
75 }
76
77 return edgeCount;
78 }
79
80 // Decode a single edge (durationUs, level) and accumulate bits, implementing
81 bool decodeEdge(uint32_t durationUs, bool level, uint8_t& result) override {
82 _edgeBuffer.clear();
83 Logger::debug("[RZCodec] decodeEdge: level=%s, duration=%d us",
84 level ? "HIGH" : "LOW ", durationUs);
85 // Filter idle gaps
86 if (level == getIdleLevel() && durationUs > getEndOfFrameDelayUs()) {
87 reset();
88 return false;
89 }
90
91 // Extracted frame start logic
92 bool frameStarted = handleFrameStart(level, durationUs);
93
94 // If frame just started, do NOT process this edge as data; wait for next
95 // edge
96 if (frameStarted) {
97 return false;
98 }
99 // If already in frame, process as usual
100 if (_inFrame) {
101 return handleInFrameDecoding(durationUs, level, result);
102 }
103 return false;
104 }
105
106 protected:
107 uint8_t _stopBits = 1;
108 uint8_t _bitCount = 0;
109 uint8_t _byte = 0;
110 RingBuffer<OutputEdge> _edgeBuffer{32};
111 enum { WAIT_START, DATA_BITS, STOP_BITS } _rxState = WAIT_START;
112 uint8_t _rxDataBitCount = 0;
113 uint8_t _rxStopBitCount = 0;
114
115 // Helper to check if a pulse duration is within tolerance of a target period
116 bool isValidPeriod(uint32_t pulse, uint32_t target,
117 uint32_t tolerance) const {
118 return abs((int)pulse - (int)target) < (int)tolerance;
119 }
120
121 // Handle preamble and frame start logic
122 bool handleFrameStart(bool level, uint32_t durationUs) {
123 OutputEdge newEdge{level, durationUs};
124 if (!_inFrame) {
125 if (_preamble->preambleLength() == 0) {
126 _decodeEdgeStream.clear();
127 _inFrame = true;
128 Logger::debug("No preamble, starting new frame");
129 return true;
130 } else if (_preamble->detect(newEdge)) {
131 _inFrame = true;
132 _decodeEdgeStream.clear();
133 Logger::debug("Preamble detected, starting new frame");
134 return true;
135 }
136 }
137 return false;
138 }
139
140 // Handle in-frame bit/byte decoding logic
141 bool handleInFrameDecoding(uint32_t durationUs, bool level, uint8_t& result) {
142 // Reconstruct edges as before
143 if (level) {
144 _edgeBuffer.write({level, durationUs});
145 Logger::debug("Reconstructed edge: level=%s, duration=%d us",
146 level ? "HIGH" : "LOW", durationUs);
147 } else if (!level && durationUs >= _bitPeriodUs) {
148 int n = durationUs / _bitPeriodUs;
149 Logger::debug("Reconstructed %d edges: level=LOW, duration=%d us", n,
150 durationUs);
151 for (int i = 0; i < n; ++i) {
152 _edgeBuffer.write({false, _bitPeriodUs});
153 }
154 } else {
155 Logger::debug("Ignored edge: level=LOW, duration=%d us", durationUs);
156 }
157
158 // State machine for start, data, and stop bits
159 result = 0;
160 while (!_edgeBuffer.isEmpty()) {
161 OutputEdge edge;
162 _edgeBuffer.read(edge);
163 switch (_rxState) {
164 case WAIT_START:
165 // Expect start bit (LOW)
166 if (!edge.level) {
167 Logger::debug("Detected start bit");
168 _byte = 0;
169 _rxDataBitCount = 0;
170 _rxStopBitCount = 0;
171 _rxState = DATA_BITS;
172 } else {
173 Logger::warning("Expected start bit (LOW), got HIGH, skipping");
174 }
175 break;
176 case DATA_BITS:
177 // Data bits (LSB first)
178 if (edge.level) {
179 _byte |= (1 << _rxDataBitCount);
180 Logger::debug("Decoded data bit 1 at pos=%d", _rxDataBitCount);
181 } else {
182 Logger::debug("Decoded data bit 0 at pos=%d", _rxDataBitCount);
183 }
184 if (++_rxDataBitCount == 8) {
185 _rxState = STOP_BITS;
186 _rxStopBitCount = 0;
187 }
188 break;
189 case STOP_BITS:
190 // Expect stop bits (HIGH)
191 if (edge.level) {
192 Logger::debug("Detected stop bit %d", _rxStopBitCount + 1);
193 } else {
194 Logger::warning("Expected stop bit (HIGH), got LOW, skipping");
195 }
196 if (++_rxStopBitCount >= _stopBits) {
197 Logger::debug("Decoded byte: 0x%02x", _byte);
198 result = _byte;
199 _rxState = WAIT_START;
200 return true;
201 }
202 break;
203 }
204 }
205 return false;
206 }
207};
208
209} // namespace pulsewire
Abstract base class for IR protocol encoding and decoding.
Definition Codec.h:53
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 warning(const char *format,...)
Log a warning message with formatting.
Definition Logger.h:52
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
bool decodeEdge(uint32_t durationUs, bool level, uint8_t &result) override
Edge-based decoding for protocol-agnostic RX drivers.
Definition RZCodec.h:81
size_t getEdgeCount() const override
Get the number of protocol symbols (bits, pulses, etc.) per encoded byte.
Definition RZCodec.h:25
CodecEnum getCodecType() const override
Definition RZCodec.h:21
size_t encode(uint8_t byte, Vector< OutputEdge > &output) override
Fill output vector with protocol-specific OutputSpec(s) for a byte.
Definition RZCodec.h:39
int getEndOfFrameDelayUs() override
Definition RZCodec.h:27
void reset() override
Reset the internal state of the codec.
Definition RZCodec.h:29
bool getIdleLevel() const override
Provides the initial ldle state (low or hith)
Definition RZCodec.h:23
Small, header-only vector replacement for non-STL environments.
Definition Vector.h:29
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
Specifies a single IR signal segment for protocol-agnostic transmission.
Definition Preamble.h:23