16 if (str.length() < 10) {
19 for (
int n = 0; n < 10; n++) {
20 if (!isDigit(str.charAt(n))) {
24 DEBUG(
"Pulsetrain::maybe() returns true.\n");
29 IRAM_ATTR Pulsetrain::operator
bool() {
30 return (transitions.size() > 0);
34 void IRAM_ATTR Pulsetrain::zap() {
45 bool IRAM_ATTR Pulsetrain::sameAs(
const Pulsetrain &other_train) {
46 if (transitions.size() != other_train.transitions.size()) {
49 if (bins.size() != other_train.bins.size()) {
52 for (
int n = 0; n < transitions.size(); n++) {
53 if (transitions[n] != other_train.transitions[n]) {
57 for (
int m = 0; m < bins.size(); m++) {
58 if (abs(bins[m].average - abs(other_train.bins[m].average)) > 100) {
67 String Pulsetrain::toString()
const {
68 if (transitions.size() == 0) {
69 return "<empty Pulsetrain>";
72 for (
int transition: transitions) {
75 for (
auto bin : bins) {
87 bool Pulsetrain::fromString(String in) {
89 int first_comma = in.indexOf(
",");
90 if (first_comma == -1) {
91 ERROR(
"ERROR: cannot convert String to Pulsetrain, no commas present.\n");
96 for (
int n = 0; n < first_comma; n++) {
97 int digit = in.charAt(n);
98 if (!isDigit(digit)) {
100 ERROR(
"ERROR: cannot convert String to Pulsetrain, non-digits in wrong place.\n");
104 transitions.push_back(digit);
105 if (digit > num_bins) {
110 int end_binlist = in.indexOf(
"*");
111 if (end_binlist == -1) {
112 end_binlist = in.length();
115 int at_sign = in.indexOf(
"@");
118 ERROR(
"ERROR: cannot convert String to Pulsetrain, * but no @ found.\n");
121 repeats = in.substring(end_binlist + 1, at_sign).toInt();
122 gap = in.substring(at_sign + 1).toInt();
123 if (gap == 0 || repeats == 0) {
125 ERROR(
"ERROR: cannot convert String to Pulsetrain, invalid values for repeats or gap.\n");
129 int bin_start = first_comma + 1;
130 for (
int n = 0; n < num_bins; n++) {
132 int next_comma = in.indexOf(
",", bin_start);
133 if (next_comma == -1) {
134 next_comma = in.length();
136 new_bin.average = in.substring(bin_start, next_comma).toInt();
137 new_bin.min = new_bin.average;
138 new_bin.max = new_bin.average;
139 if (new_bin.average == 0) {
141 ERROR(
"ERROR: cannot convert String to Pulsetrain, invalid bin value found.\n");
144 bin_start = next_comma + 1;
145 bins.push_back(new_bin);
147 for (
int transition : transitions) {
148 bins[transition].count++;
149 duration += bins[transition].average;
156 String Pulsetrain::summary()
const {
158 snprintf_append(res, 80,
"%i pulses over %i µs", (transitions.size() + 1) / 2, duration);
160 snprintf_append(res, 80,
", repeated %i times with gaps of %i µs", repeats, gap);
168 bool IRAM_ATTR Pulsetrain::fromRawTimings(
const RawTimings &raw) {
172 std::vector<uint16_t> sorted = raw.intervals;
173 std::sort(sorted.begin(), sorted.end());
175 bool just_begun =
true;
176 for (
auto interval : sorted) {
177 if (just_begun || interval > bins.back().min + bin_width) {
180 new_bin.min = interval;
181 bins.push_back(new_bin);
183 bins.back().max = interval;
187 for (
auto interval : raw.intervals) {
188 duration += interval;
189 for (
int m = 0; m < bins.size(); m++) {
190 if (interval >= bins[m].min && interval <= bins[m].max) {
191 transitions.push_back(m);
192 bins[m].average += interval;
199 for (
auto& bin : bins) {
201 bin.average = bin.average / bin.count;
205 first_at = esp_timer_get_time();
206 last_at = esp_timer_get_time();
213 RawTimings Pulsetrain::toRawTimings() {
215 res.fromPulsetrain(*
this);
221 String Pulsetrain::binList() {
224 for (
int m = 0; m < bins.size(); m++) {
225 snprintf_append(res, 50,
"\n%4i %7i %7i %7i %6i", m, bins[m].min, bins[m].average, bins[m].max, bins[m].count);
233 String Pulsetrain::visualizer() {
234 int visualizer_pixel;
236 return visualizer(visualizer_pixel);
241 String Pulsetrain::visualizer(
int base) {
245 uint8_t multiples[bins.size()];
246 for (
int m = 0; m < bins.size(); m++) {
247 multiples[m] = max(((
int)bins[m].average + (base / 2)) / base, 1);
249 String ones_and_zeroes;
251 for (
int n = 0; n < transitions.size(); n++) {
252 curstate = (n % 2 == 0) ?
"1" :
"0";
253 for (
int m = 0; m < multiples[transitions[n]]; m++) {
254 ones_and_zeroes += curstate;
257 ones_and_zeroes +=
"0";
259 for (
int n = 0; n < ones_and_zeroes.length(); n += 2) {
260 String chunk = ones_and_zeroes.substring(n, n + 2);
263 }
else if (chunk ==
"00") {
265 }
else if (chunk ==
"01") {
267 }
else if (chunk ==
"10") {
274 bool Pulsetrain::fromMeaning(
const Meaning &meaning) {
277 for (
const auto& el : meaning.elements) {
278 if (el.type == PULSE || el.type == GAP) {
281 if (el.type == PWM) {
285 if (el.type == PPM) {
292 std::sort(bins.begin(), bins.end(), [](pulseBin a, pulseBin b) {
293 return a.average < b.average;
296 for (
int n = 0; n < meaning.elements.size(); n++) {
297 MeaningElement el = meaning.elements[n];
298 if (el.type == PULSE) {
300 if (transitions.size() % 2) {
302 if (n > 0 && meaning.elements[n - 1].type == PPM) {
303 transitions.push_back(binFromTime(meaning.elements[n - 1].time3));
304 }
else if (n > 0 && meaning.elements[n - 1].type == PWM) {
305 transitions.push_back(binFromTime(meaning.elements[n - 1].time1));
308 ERROR(
"ERROR: cannot have a pulse where a gap is expected at element %i.\n", n);
312 transitions.push_back(binFromTime(el.time1));
314 if (el.type == GAP) {
315 if (transitions.size() % 2 == 0) {
317 ERROR(
"ERROR: cannot have a gap where a pulse is expected at element %i.\n", n);
320 transitions.push_back(binFromTime(el.time1));
322 if (el.type == PWM || el.type == PPM) {
324 int data_bytes = (el.data_len + 7) / 8;
325 uint8_t tmp_data[data_bytes];
326 for (
int m = 0; m < data_bytes; m++) {
327 tmp_data[m] = el.data[m];
331 int shift_left_by = (8 - (el.data_len % 8)) % 8;
332 for (
int j = 0; j < shift_left_by; j++) {
333 tools::shiftOutBit(tmp_data, el.data_len);
335 if (el.type == PWM) {
336 for (
int m = 0; m < el.data_len; m++) {
337 if (tools::shiftOutBit(tmp_data, el.data_len)) {
338 transitions.push_back(binFromTime(el.time2));
339 transitions.push_back(binFromTime(el.time1));
341 transitions.push_back(binFromTime(el.time1));
342 transitions.push_back(binFromTime(el.time2));
346 if (el.type == PPM) {
347 if (transitions.size() % 2) {
348 transitions.push_back(binFromTime(el.time3));
350 for (
int m = 0; m < el.data_len; m++) {
351 if (tools::shiftOutBit(tmp_data, el.data_len)) {
352 transitions.push_back(binFromTime(el.time2));
353 transitions.push_back(binFromTime(el.time3));
355 transitions.push_back(binFromTime(el.time1));
356 transitions.push_back(binFromTime(el.time3));
359 transitions.pop_back();
364 for (
int transition : transitions) {
365 bins[transition].count++;
366 duration += bins[transition].average;
368 repeats = meaning.repeats;
375 Meaning Pulsetrain::toMeaning() {
377 res.fromPulsetrain(*
this);
381 void Pulsetrain::addToBins(
int time) {
382 for (
auto bin : bins) {
383 if (bin.average == time || bins.size() ==
MAX_BINS) {
390 new_bin.average = time;
391 bins.push_back(new_bin);
395 int Pulsetrain::binFromTime(
int time) {
396 for (
int m = 0; m < bins.size(); m++) {
397 if (bins[m].average == time) {