Arduino PulseWire Transceiver Library
Loading...
Searching...
No Matches
Preamble.h
1#pragma once
2#include <stdint.h>
3
4#include "TransceiverConfig.h"
5#include "pulse/tools/Logger.h"
6#include "pulse/tools/Vector.h"
7
8namespace pulsewire {
9
23struct OutputEdge {
24 bool level =
25 false; // Logic level during the pulse (true = HIGH, false = LOW)
26 uint32_t pulseUs = 0; // Duration of the pulse in microseconds
27 OutputEdge() = default;
28 OutputEdge(bool l, uint32_t p) : level(l), pulseUs(p) {}
29};
30
40class Preamble {
41 public:
42 virtual ~Preamble() = default;
43
44 virtual bool begin(uint16_t bitFrequencyHz) {
45 _history.reserve(preambleLength());
46 return true;
47 }
48
49 void reset() { _history.clear(); }
50
52 virtual bool detect(const OutputEdge& edge) {
53 Logger::debug("Preamble Detecting edge: level=%s, pulseUs=%d", edge.level ? "HIGH" : "LOW",
54 edge.pulseUs);
55 size_t N = preambleLength();
56 if (N == 0) return true;
57 _history.push_back(edge);
58 if (_history.size() > N) _history.erase(_history.begin());
59 if (_history.size() < N) {
60 Logger::debug("Not enough edges for preamble detection: %d/%d",
61 _history.size(), N);
62 return false;
63 }
64
65 for (size_t i = 0; i < N; ++i) {
66 if (_history[i].level != _expected[i].level) {
67 Logger::debug("Invalid level idx %d: expected %s, got %s - %d us", i,
68 _expected[i].level ? "HIGH" : "LOW",
69 _history[i].level ? "HIGH" : "LOW ",
70 _history[i].pulseUs);
71 return false;
72 }
73 // Use 20% tolerance or 50us minimum
74 uint32_t tol = 0.2 * _expected[i].pulseUs;
75 if (tol < 50) tol = 50;
76 if (!inRange(_history[i].pulseUs, _expected[i].pulseUs, tol)) {
77 Logger::debug("Invalid pulse duration: expected %d us, got %d us",
78 _expected[i].pulseUs, _history[i].pulseUs);
79 return false;
80 }
81 }
82 Logger::debug("Preamble detected");
83 _history.clear();
84 return true;
85 }
86
88 virtual int getEdges(
90
91 virtual size_t preambleLength() const = 0;
92
96 return (value >= target - tolerance) && (value <= target + tolerance);
97 }
98
99 protected:
100 // Default history buffer for edge detection
103};
104
110class NoPreamble : public Preamble {
111 public:
112 bool begin(uint16_t bitFrequencyHz) override { return true; }
113 bool detect(const pulsewire::OutputEdge& edge) override { return true; }
115 pulsewire::Vector<pulsewire::OutputEdge>& output) const override {
116 return 0;
117 }
118 size_t preambleLength() const override { return 0; }
119};
128 public:
129 CustomPreambleUs() = default;
131 _expected = edges;
132 }
133 // Uses default detect()
134 void setEdges(const pulsewire::Vector<pulsewire::OutputEdge>& edges) {
135 _expected = edges;
136 }
137 void addEdge(const pulsewire::OutputEdge& edge) { _expected.push_back(edge); }
138 void addEdge(bool level, uint32_t pulseUs) {
139 OutputEdge edge{level, pulseUs};
140 _expected.push_back(edge);
141 }
142 void addEdge(bool level, uint16_t pulseCount, uint32_t frequencyHz) {
143 uint32_t pulseUs = (1000000 / frequencyHz) * pulseCount;
144 OutputEdge edge{level, pulseUs};
145 _expected.push_back(edge);
146 }
147 void clear() { _expected.clear(); }
148 int getEdges(Vector<OutputEdge>& output) const override {
149 for (const auto& edge : _expected) {
150 output.push_back(edge);
151 }
152 return _expected.size();
153 }
154 size_t preambleLength() const override { return _expected.size(); }
155};
156
166 public:
167 CustomPreamble() = default;
169 _expected = edges;
170 }
171 virtual bool begin(uint16_t bitFrequencyHz) {
172 bool rc = Preamble::begin(bitFrequencyHz);
173 uint32_t us = 1000000 / bitFrequencyHz;
174 for (auto& edge : _expected) {
175 edge.pulseUs = edge.pulseUs * us;
176 }
177
178 return rc;
179 }
180};
181
203 public:
204 ManchesterPreamble(uint8_t runInCycles = 2) : _runInCycles(runInCycles) {}
205
206 bool begin(uint16_t bitFrequencyHz) override {
207 // double the frequency for Manchester since each bit is two edges
208 _history.clear();
209 _history.reserve(_runInCycles * 2 + 1); // run-in edges + start pulse
210 _expected.reserve(_runInCycles * 2 + 1);
211 _freqHz = bitFrequencyHz * 2;
212 _bitPeriod = 1000000 / _freqHz;
213
214 // Generate the expected edges for the Manchester preamble
215 _expected.clear();
216 bool level = true;
217 // Run-in: alternating edges for clock recovery
218 for (uint8_t i = 0; i < _runInCycles * 2; ++i) {
219 addEdge(level, _bitPeriod);
220 level = !level;
221 }
222
223 return true;
224 }
225
226 private:
227 uint8_t _runInCycles = 0;
228 uint32_t _freqHz = 0;
229 uint32_t _bitPeriod = 0;
230};
231
241 public:
242 NRZPreamble() = default;
243
244 bool begin(uint16_t bitFrequencyHz) override {
245 _history.clear();
246 _history.reserve(4);
247 _expected.clear();
248 uint32_t bp = 1000000 / bitFrequencyHz;
249 addEdge(false, bp); // LOW
250 addEdge(true, bp); // HIGH
251 addEdge(false, bp); // LOW
252 addEdge(true, bp); // HIGH — ends HIGH, matching NRZ idle
253 return true;
254 }
255};
256
257} // namespace pulsewire
CustomPreambleUs: Allows users to define their own preamble by setting expected edges....
Definition Preamble.h:127
int getEdges(Vector< OutputEdge > &output) const override
Returns the expected preamble edges for this protocol.
Definition Preamble.h:148
CustomPreamble: Allows users to define their own preamble by setting expected edges....
Definition Preamble.h:165
static void debug(const char *format,...)
Log a debug message with formatting.
Definition Logger.h:82
Custom Manchester preamble detector: run-in of alternating edges plus unique start pulse.
Definition Preamble.h:202
A Preamble implementation for NRZ protocols: alternating edges at full bit period,...
Definition Preamble.h:240
NoPreamble: For protocols that do not require a preamble. Always returns true for detect,...
Definition Preamble.h:110
bool detect(const pulsewire::OutputEdge &edge) override
Detects if the incoming edge matches the expected preamble pattern.
Definition Preamble.h:113
int getEdges(pulsewire::Vector< pulsewire::OutputEdge > &output) const override
Returns the expected preamble edges for this protocol.
Definition Preamble.h:114
Abstract base class for preamble detection and generation.
Definition Preamble.h:40
bool inRange(uint32_t value, uint32_t target, uint32_t tolerance) const
Definition Preamble.h:95
virtual int getEdges(pulsewire::Vector< pulsewire::OutputEdge > &output) const =0
Returns the expected preamble edges for this protocol.
virtual bool detect(const OutputEdge &edge)
Detects if the incoming edge matches the expected preamble pattern.
Definition Preamble.h:52
Small, header-only vector replacement for non-STL environments.
Definition Vector.h:29
iterator begin()
Iterator to first element.
Definition Vector.h:97
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
iterator erase(iterator pos)
Erase single element at iterator pos, return iterator to next element.
Definition Vector.h:128
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