/** @file Flexible general purpose decoder. Copyright (C) 2017 Christian Zuckschwerdt This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "decoder.h" #include "optparse.h" #include "fatal.h" #include enum uart_mode { UART_MODE_8N1 = 1, UART_MODE_8N2, UART_MODE_8O1, }; static inline int bit(const uint8_t *bytes, unsigned b) { return bytes[b >> 3] >> (7 - (b & 7)) & 1; } /// extract all mask bits skipping unmasked bits of a number up to 32/64 bits static unsigned long compact_number(uint8_t *data, unsigned bit_offset, unsigned long mask) { // clz (fls) is not worth the trouble int top_bit = 0; while (mask >> top_bit) top_bit++; unsigned long val = 0; for (int b = top_bit - 1; b >= 0; --b) { if (mask & (1 << b)) { val <<= 1; val |= bit(data, bit_offset); } bit_offset++; } return val; } /// extract a number up to 32/64 bits from given offset with given bit length static unsigned long extract_number(uint8_t *data, unsigned bit_offset, unsigned bit_count) { unsigned pos = bit_offset / 8; // the first byte we need unsigned shl = bit_offset - pos * 8; // shift left we need to align unsigned len = (shl + bit_count + 7) / 8; // number of bytes we need unsigned shr = 8 * len - shl - bit_count; // actual shift right // fprintf(stderr, "pos: %d, shl: %d, len: %d, shr: %d\n", pos, shl, len, shr); unsigned long val = data[pos]; val = (uint8_t)(val << shl) >> shl; // mask off top bits for (unsigned i = 1; i < len - 1; ++i) { val = val << 8 | data[pos + i]; } // shift down and add the last bits, so we don't potentially loose the top bits if (len > 1) val = (val << (8 - shr)) | (data[pos + len - 1] >> shr); else val >>= shr; return val; } struct flex_map { unsigned key; const char *val; }; #define GETTER_MAP_SLOTS 16 struct flex_get { unsigned bit_offset; unsigned bit_count; unsigned long mask; const char *name; struct flex_map map[GETTER_MAP_SLOTS]; const char *format; }; #define GETTER_SLOTS 12 struct flex_params { char *name; unsigned min_rows; unsigned max_rows; unsigned min_bits; unsigned max_bits; unsigned min_repeats; unsigned max_repeats; unsigned invert; unsigned reflect; unsigned unique; unsigned count_only; unsigned match_len; uint8_t match_bits[128]; unsigned preamble_len; uint8_t preamble_bits[128]; uint32_t symbol_zero; uint32_t symbol_one; uint32_t symbol_sync; struct flex_get getter[GETTER_SLOTS]; unsigned decode_uart; unsigned decode_dm; unsigned decode_mc; char const *fields[7 + GETTER_SLOTS + 1]; // NOTE: needs to match output_fields }; static void print_row_bytes(char *row_bytes, uint8_t *bits, int num_bits) { row_bytes[0] = '\0'; // print byte-wide for (int col = 0; col < (num_bits + 7) / 8; ++col) { sprintf(&row_bytes[2 * col], "%02x", bits[col]); } // remove last nibble if needed row_bytes[2 * (num_bits + 3) / 8] = '\0'; } static void render_getters(data_t *data, uint8_t *bits, struct flex_params *params) { // add a data line for each getter for (int g = 0; g < GETTER_SLOTS && params->getter[g].bit_count > 0; ++g) { struct flex_get *getter = ¶ms->getter[g]; unsigned long val; if (getter->mask) val = compact_number(bits, getter->bit_offset, getter->mask); else val = extract_number(bits, getter->bit_offset, getter->bit_count); int m; for (m = 0; getter->map[m].val; m++) { if (getter->map[m].key == val) { data_str(data, getter->name, "", NULL, getter->map[m].val); break; } } if (!getter->map[m].val) { data_int(data, getter->name, "", getter->format, val); } } } /** Generic flex decoder. */ static int flex_callback(r_device *decoder, bitbuffer_t *bitbuffer) { int i; int match_count = 0; data_t *data; data_t *row_data[BITBUF_ROWS]; char *row_codes[BITBUF_ROWS]; char row_bytes[BITBUF_ROWS * BITBUF_COLS * 2 + 1]; // TODO: this is a lot of stack struct flex_params *params = decoder_user_data(decoder); // discard short / unwanted bitbuffers if ((bitbuffer->num_rows < params->min_rows) || (params->max_rows && bitbuffer->num_rows > params->max_rows)) return DECODE_ABORT_LENGTH; for (i = 0; i < bitbuffer->num_rows; i++) { if ((bitbuffer->bits_per_row[i] >= params->min_bits) && (!params->max_bits || bitbuffer->bits_per_row[i] <= params->max_bits)) match_count++; } if (!match_count) return DECODE_ABORT_LENGTH; // discard unless min_repeats, min_bits // TODO: check max_repeats, max_bits int r = bitbuffer_find_repeated_row(bitbuffer, params->min_repeats, params->min_bits); if (r < 0) return DECODE_ABORT_EARLY; // TODO: set match_count to count of repeated rows if (params->invert) { bitbuffer_invert(bitbuffer); } if (params->reflect) { // TODO: refactor to utils for (i = 0; i < bitbuffer->num_rows; ++i) { reflect_bytes(bitbuffer->bb[i], (bitbuffer->bits_per_row[i] + 7) / 8); } } // discard unless match if (params->match_len) { r = -1; match_count = 0; for (i = 0; i < bitbuffer->num_rows; i++) { if (bitbuffer_search(bitbuffer, i, 0, params->match_bits, params->match_len) < bitbuffer->bits_per_row[i]) { if (r < 0) r = i; match_count++; } } if (!match_count) return DECODE_FAIL_SANITY; } // discard unless match, this should be an AND condition if (params->preamble_len) { r = -1; match_count = 0; for (i = 0; i < bitbuffer->num_rows; i++) { unsigned pos = bitbuffer_search(bitbuffer, i, 0, params->preamble_bits, params->preamble_len); if (pos < bitbuffer->bits_per_row[i]) { if (r < 0) r = i; match_count++; pos += params->preamble_len; // TODO: refactor to bitbuffer_shift_row() unsigned len = bitbuffer->bits_per_row[i] - pos; bitbuffer_t tmp = {0}; bitbuffer_extract_bytes(bitbuffer, i, pos, tmp.bb[0], len); memcpy(bitbuffer->bb[i], tmp.bb[0], (len + 7) / 8); bitbuffer->bits_per_row[i] = len; } } if (!match_count) return DECODE_FAIL_SANITY; } if (params->symbol_zero) { uint32_t zero = params->symbol_zero; uint32_t one = params->symbol_one; uint32_t sync = params->symbol_sync; for (i = 0; i < bitbuffer->num_rows; i++) { // TODO: refactor to bitbuffer_decode_symbol_row() unsigned len = bitbuffer->bits_per_row[i]; bitbuffer_t tmp = {0}; len = extract_bits_symbols(bitbuffer->bb[i], 0, len, zero, one, sync, tmp.bb[0]); memcpy(bitbuffer->bb[i], tmp.bb[0], len); // safe to write over: can only be shorter bitbuffer->bits_per_row[i] = len; } // TODO: apply min_bits, max_bits check } if (params->decode_uart) { for (i = 0; i < bitbuffer->num_rows; i++) { // TODO: refactor to bitbuffer_decode_uart_row() unsigned len = bitbuffer->bits_per_row[i]; bitbuffer_t tmp = {0}; if (params->decode_uart == UART_MODE_8N1) { len = extract_bytes_uart_8n1(bitbuffer->bb[i], 0, len, tmp.bb[0]); } else if (params->decode_uart == UART_MODE_8N2) { len = extract_bytes_uart_8n2(bitbuffer->bb[i], 0, len, tmp.bb[0]); } else if (params->decode_uart == UART_MODE_8O1) { len = extract_bytes_uart_8o1(bitbuffer->bb[i], 0, len, tmp.bb[0]); } memcpy(bitbuffer->bb[i], tmp.bb[0], len); // safe to write over: can only be shorter bitbuffer->bits_per_row[i] = len * 8; } } if (params->decode_dm) { for (i = 0; i < bitbuffer->num_rows; i++) { // TODO: refactor to bitbuffer_decode_dm_row() unsigned len = bitbuffer->bits_per_row[i]; bitbuffer_t tmp = {0}; bitbuffer_differential_manchester_decode(bitbuffer, i, 0, &tmp, len); len = tmp.bits_per_row[0]; memcpy(bitbuffer->bb[i], tmp.bb[0], (len + 7) / 8); // safe to write over: can only be shorter bitbuffer->bits_per_row[i] = len; } } // IEEE 802.3 MC, may need G.E.Thomas option (bitbuffer_invert_row()) if (params->decode_mc) { for (i = 0; i < bitbuffer->num_rows; i++) { // TODO: refactor to bitbuffer_decode_mc_row() unsigned len = bitbuffer->bits_per_row[i]; bitbuffer_t tmp = {0}; bitbuffer_manchester_decode(bitbuffer, i, 0, &tmp, len); len = tmp.bits_per_row[0]; memcpy(bitbuffer->bb[i], tmp.bb[0], (len + 7) / 8); // safe to write over: can only be shorter bitbuffer->bits_per_row[i] = len; } } decoder_log_bitbuffer(decoder, 1, params->name, bitbuffer, ""); // discard duplicates if (params->unique) { print_row_bytes(row_bytes, bitbuffer->bb[r], bitbuffer->bits_per_row[r]); /* clang-format off */ data = data_make( "model", "", DATA_STRING, params->name, // "User-defined" "count", "", DATA_INT, match_count, "num_rows", "", DATA_INT, bitbuffer->num_rows, "len", "", DATA_INT, bitbuffer->bits_per_row[r], "data", "", DATA_STRING, row_bytes, NULL); /* clang-format on */ // add a data line for each getter render_getters(data, bitbuffer->bb[r], params); decoder_output_data(decoder, data); return 1; } if (params->count_only) { /* clang-format off */ data = data_make( "model", "", DATA_STRING, params->name, // "User-defined" "count", "", DATA_INT, match_count, NULL); /* clang-format on */ decoder_output_data(decoder, data); return 1; } for (i = 0; i < bitbuffer->num_rows; i++) { print_row_bytes(row_bytes, bitbuffer->bb[i], bitbuffer->bits_per_row[i]); /* clang-format off */ data = data_make( "len", "", DATA_INT, bitbuffer->bits_per_row[i], "data", "", DATA_STRING, row_bytes, NULL); /* clang-format on */ row_data[i] = data; // add a data line for each getter render_getters(row_data[i], bitbuffer->bb[i], params); // print at least one '0' if (row_bytes[0] == '\0') { snprintf(row_bytes, sizeof(row_bytes), "0"); } // a simpler representation for csv output row_codes[i] = malloc(8 + bitbuffer->bits_per_row[i] / 4 + 1); // "{nnnn}..\0" if (!row_codes[i]) WARN_MALLOC("flex_decode()"); else // NOTE: skipped on alloc failure. sprintf(row_codes[i], "{%d}%s", bitbuffer->bits_per_row[i], row_bytes); } /* clang-format off */ data = data_make( "model", "", DATA_STRING, params->name, // "User-defined" "count", "", DATA_INT, match_count, "num_rows", "", DATA_INT, bitbuffer->num_rows, "rows", "", DATA_ARRAY, data_array(bitbuffer->num_rows, DATA_DATA, row_data), "codes", "", DATA_ARRAY, data_array(bitbuffer->num_rows, DATA_STRING, row_codes), NULL); /* clang-format on */ decoder_output_data(decoder, data); for (i = 0; i < bitbuffer->num_rows; i++) { free(row_codes[i]); } return 1; } static char const *const output_fields[] = { "model", "count", "num_rows", "rows", "codes", // "len", // unique only // "data", // unique only NULL, }; static void usage(void) { fprintf(stderr, "Use -X to add a general purpose decoder. For usage use -X help\n"); exit(1); } static void help(void) { fprintf(stderr, "\t\t= Flex decoder spec =\n" "Use -X to add a flexible general purpose decoder.\n\n" " is \"key=value[,key=value...]\"\n" "Common keys are:\n" "\tname= (or: n=)\n" "\tmodulation= (or: m=)\n" "\tshort= (or: s=)\n" "\tlong= (or: l=)\n" "\tsync= (or: y=)\n" "\treset= (or: r=)\n" "\tgap= (or: g=)\n" "\ttolerance= (or: t=)\n" "\tpriority= : run decoder only as fallback\n" "where:\n" " can be any descriptive name tag you need in the output\n" " is one of:\n" "\tOOK_MC_ZEROBIT : Manchester Code with fixed leading zero bit\n" "\tOOK_PCM : Non Return to Zero coding (Pulse Code)\n" "\tOOK_RZ : Return to Zero coding (Pulse Code)\n" "\tOOK_PPM : Pulse Position Modulation\n" "\tOOK_PWM : Pulse Width Modulation\n" "\tOOK_DMC : Differential Manchester Code\n" "\tOOK_PIWM_RAW : Raw Pulse Interval and Width Modulation\n" "\tOOK_PIWM_DC : Differential Pulse Interval and Width Modulation\n" "\tOOK_MC_OSV1 : Manchester Code for OSv1 devices\n" "\tFSK_PCM : FSK Pulse Code Modulation\n" "\tFSK_PWM : FSK Pulse Width Modulation\n" "\tFSK_MC_ZEROBIT : Manchester Code with fixed leading zero bit\n" ", , are nominal modulation timings in us,\n" ", , are maximum modulation timings in us:\n" "PCM/RZ short: Nominal width of pulse [us]\n" " long: Nominal width of bit period [us]\n" "PPM short: Nominal width of '0' gap [us]\n" " long: Nominal width of '1' gap [us]\n" "PWM short: Nominal width of '1' pulse [us]\n" " long: Nominal width of '0' pulse [us]\n" " sync: Nominal width of sync pulse [us] (optional)\n" "common gap: Maximum gap size before new row of bits [us]\n" " reset: Maximum gap size before End Of Message [us]\n" " tolerance: Maximum pulse deviation [us] (optional).\n" "Available options are:\n" "\tbits= : only match if at least one row has bits\n" "\trows= : only match if there are rows\n" "\trepeats= : only match if some row is repeated times\n" "\t\tuse opt>=n to match at least and opt<=n to match at most \n" "\tinvert : invert all bits\n" "\treflect : reflect each byte (MSB first to MSB last)\n" "\tdecode_uart=<8n1|8n2|8o1> : UART 8n1 (10-to-8), 8n2, 8o1 (11-to-8) decode\n" "\tdecode_dm : Differential Manchester decode\n" "\tdecode_mc : Manchester decode\n" "\tmatch= : only match if the are found\n" "\tpreamble= : match and align at the preamble\n" "\t\t is a row spec of {}\n" "\tunique : suppress duplicate row output\n\n" "\tcountonly : suppress detailed row output\n\n" "E.g. -X \"n=doorbell,m=OOK_PWM,s=400,l=800,r=7000,g=1000,match={24}0xa9878c,repeats>=3\"\n\n"); exit(0); } static float parse_atoiv(char const *str, int def, char const *error_hint) { if (!str) { return def; } if (!*str) { return def; } char *endptr; int val = strtol(str, &endptr, 10); if (str == endptr) { fprintf(stderr, "%sinvalid number argument (%s)\n", error_hint, str); exit(1); } return val; } static float parse_float(char const *str, char const *error_hint) { if (!str) { fprintf(stderr, "%smissing number argument\n", error_hint); exit(1); } if (!*str) { fprintf(stderr, "%sempty number argument\n", error_hint); exit(1); } char *endptr; double val = strtod(str, &endptr); if (str == endptr) { fprintf(stderr, "%sinvalid number argument (%s)\n", error_hint, str); exit(1); } if (*endptr != '\0') { fprintf(stderr, "%strailing characters in number argument (%s)\n", error_hint, str); exit(1); } return val; } static unsigned parse_modulation(char const *str) { if (!strcasecmp(str, "OOK_MC_ZEROBIT")) return OOK_PULSE_MANCHESTER_ZEROBIT; else if (!strcasecmp(str, "OOK_PCM")) return OOK_PULSE_PCM; else if (!strcasecmp(str, "OOK_RZ")) return OOK_PULSE_RZ; else if (!strcasecmp(str, "OOK_PPM")) return OOK_PULSE_PPM; else if (!strcasecmp(str, "OOK_PWM")) return OOK_PULSE_PWM; else if (!strcasecmp(str, "OOK_DMC")) return OOK_PULSE_DMC; else if (!strcasecmp(str, "OOK_PIWM_RAW")) return OOK_PULSE_PIWM_RAW; else if (!strcasecmp(str, "OOK_PIWM_DC")) return OOK_PULSE_PIWM_DC; else if (!strcasecmp(str, "OOK_MC_OSV1")) return OOK_PULSE_PWM_OSV1; else if (!strcasecmp(str, "FSK_PCM")) return FSK_PULSE_PCM; else if (!strcasecmp(str, "FSK_PWM")) return FSK_PULSE_PWM; else if (!strcasecmp(str, "FSK_MC_ZEROBIT")) return FSK_PULSE_MANCHESTER_ZEROBIT; else { fprintf(stderr, "Bad flex spec, unknown modulation!\n"); usage(); } return 0; } // used for match, preamble, getter, limited to 1024 bits (128 byte). static unsigned parse_bits(const char *code, uint8_t *bitrow) { bitbuffer_t bits = {0}; bitbuffer_parse(&bits, code); if (bits.num_rows != 1) { fprintf(stderr, "Bad flex spec, \"match\", \"preamble\", and getter mask need exactly one bit row (%d found)!\n", bits.num_rows); usage(); } unsigned len = bits.bits_per_row[0]; if (len > 1024) { fprintf(stderr, "Bad flex spec, \"match\", \"preamble\", and getter mask may have up to 1024 bits (%u found)!\n", len); usage(); } memcpy(bitrow, bits.bb[0], (len + 7) / 8); return len; } // used for symbol decode, limited to 27 bits (32 - 5). static uint32_t parse_symbol(const char *code) { bitbuffer_t bits = {0}; bitbuffer_parse(&bits, code); if (bits.num_rows != 1) { fprintf(stderr, "Bad flex spec, \"symbol\" needs exactly one bit row (%d found)!\n", bits.num_rows); usage(); } unsigned len = bits.bits_per_row[0]; if (len > 27) { fprintf(stderr, "Bad flex spec, \"symbol\" may have up to 27 bits (%u found)!\n", len); usage(); } uint8_t *b = bits.bb[0]; return ((uint32_t)b[0] << 24) | (b[1] << 16) | (b[2] << 8) | (b[3] << 0) | len; } static const char *parse_map(const char *arg, struct flex_get *getter) { const char *c = arg; int i = 0; while (*c == ' ') c++; if (*c == '[') c++; while (*c) { unsigned long key; char *val; while (*c == ' ') c++; if (*c == ']') return c + 1; // first parse a number key = strtol(c, (char **)&c, 0); // hex, oct, or dec while (*c == ' ') c++; if (*c == ':') c++; while (*c == ' ') c++; // then parse a string const char *e = c; while (*e && *e != ' ' && *e != ']') e++; val = malloc(e - c + 1); if (!val) WARN_MALLOC("parse_map()"); else { // NOTE: skipped on alloc failure. memcpy(val, c, e - c); val[e - c] = '\0'; } c = e; // store result getter->map[i].key = key; getter->map[i].val = val; i++; } return c; } static void parse_getter(const char *arg, struct flex_get *getter) { uint8_t bitrow[128]; while (arg && *arg) { if (*arg == '[') { arg = parse_map(arg, getter); continue; } char *p = strchr(arg, ':'); if (p) *p++ = '\0'; if (*arg == '@') getter->bit_offset = strtol(++arg, NULL, 0); else if (*arg == '{' || (*arg >= '0' && *arg <= '9')) { getter->bit_count = parse_bits(arg, bitrow); getter->mask = extract_number(bitrow, 0, getter->bit_count); } else if (*arg == '%') { getter->format = strdup(arg); if (!getter->format) FATAL_STRDUP("parse_getter()"); } else { getter->name = strdup(arg); if (!getter->name) FATAL_STRDUP("parse_getter()"); } arg = p; } if (!getter->name) { fprintf(stderr, "Bad flex spec, \"get\" missing name!\n"); usage(); } /* fprintf(stderr, "parse_getter() bit_offset: %d bit_count: %d mask: %lx name: %s\n", getter->bit_offset, getter->bit_count, getter->mask, getter->name); */ } static unsigned parse_uart_mode(char const *str) { if (!strcasecmp(str, "8n1")) return UART_MODE_8N1; else if (!strcasecmp(str, "8n2")) return UART_MODE_8N2; else if (!strcasecmp(str, "8o1")) return UART_MODE_8O1; else { fprintf(stderr, "Bad flex spec, unknown uart mode!\n"); usage(); } return 0; } // NOTE: this is declared in rtl_433.c also. r_device *flex_create_device(char *spec); r_device *flex_create_device(char *spec) { if (!spec || !*spec || *spec == '?' || !strncasecmp(spec, "help", strlen(spec))) { help(); } r_device *dev = decoder_create(NULL, sizeof(struct flex_params)); if (!dev) { return NULL; // NOTE: returns NULL on alloc failure. } struct flex_params *params = decoder_user_data(dev); int get_count = 0; spec = strdup(spec); if (!spec) FATAL_STRDUP("flex_create_device()"); dev->decode_fn = flex_callback; dev->fields = output_fields; char *key, *val; while (getkwargs(&spec, &key, &val)) { key = remove_ws(key); val = trim_ws(val); if (!key || !*key) continue; else if (!strcasecmp(key, "n") || !strcasecmp(key, "name")) { params->name = strdup(val); if (!params->name) FATAL_STRDUP("flex_create_device()"); int name_size = strlen(val) + 27; char* flex_name = malloc(name_size); if (!flex_name) FATAL_MALLOC("flex_create_device()"); snprintf(flex_name, name_size, "General purpose decoder '%s'", val); dev->name = flex_name; } else if (!strcasecmp(key, "m") || !strcasecmp(key, "modulation")) dev->modulation = parse_modulation(val); else if (!strcasecmp(key, "s") || !strcasecmp(key, "short")) dev->short_width = parse_float(val, "short: "); else if (!strcasecmp(key, "l") || !strcasecmp(key, "long")) dev->long_width = parse_float(val, "long: "); else if (!strcasecmp(key, "y") || !strcasecmp(key, "sync")) dev->sync_width = parse_float(val, "sync: "); else if (!strcasecmp(key, "g") || !strcasecmp(key, "gap")) dev->gap_limit = parse_float(val, "gap: "); else if (!strcasecmp(key, "r") || !strcasecmp(key, "reset")) dev->reset_limit = parse_float(val, "reset: "); else if (!strcasecmp(key, "t") || !strcasecmp(key, "tolerance")) dev->tolerance = parse_float(val, "tolerance: "); else if (!strcasecmp(key, "prio") || !strcasecmp(key, "priority")) dev->priority = parse_atoiv(val, 0, "priority: "); else if (!strcasecmp(key, "bits>")) params->min_bits = parse_atoiv(val, 0, "bits: "); else if (!strcasecmp(key, "bits<")) params->max_bits = parse_atoiv(val, 0, "bits: "); else if (!strcasecmp(key, "bits")) params->min_bits = params->max_bits = parse_atoiv(val, 0, "bits:"); else if (!strcasecmp(key, "rows>")) params->min_rows = parse_atoiv(val, 0, "rows: "); else if (!strcasecmp(key, "rows<")) params->max_rows = parse_atoiv(val, 0, "rows: "); else if (!strcasecmp(key, "rows")) params->min_rows = params->max_rows = parse_atoiv(val, 0, "rows: "); else if (!strcasecmp(key, "repeats>")) params->min_repeats = parse_atoiv(val, 0, "repeats: "); else if (!strcasecmp(key, "repeats<")) params->max_repeats = parse_atoiv(val, 0, "repeats: "); else if (!strcasecmp(key, "repeats")) params->min_repeats = params->max_repeats = parse_atoiv(val, 0, "repeats: "); else if (!strcasecmp(key, "invert")) params->invert = parse_atoiv(val, 1, "invert: "); else if (!strcasecmp(key, "reflect")) params->reflect = parse_atoiv(val, 1, "reflect: "); else if (!strcasecmp(key, "match")) params->match_len = parse_bits(val, params->match_bits); else if (!strcasecmp(key, "preamble")) params->preamble_len = parse_bits(val, params->preamble_bits); else if (!strcasecmp(key, "countonly")) params->count_only = parse_atoiv(val, 1, "countonly: "); else if (!strcasecmp(key, "unique")) params->unique = parse_atoiv(val, 1, "unique: "); else if (!strcasecmp(key, "decode_uart")) params->decode_uart = parse_uart_mode(val); else if (!strcasecmp(key, "decode_dm")) params->decode_dm = parse_atoiv(val, 1, "decode_dm: "); else if (!strcasecmp(key, "decode_mc")) params->decode_mc = parse_atoiv(val, 1, "decode_mc: "); else if (!strcasecmp(key, "symbol_zero")) params->symbol_zero = parse_symbol(val); else if (!strcasecmp(key, "symbol_one")) params->symbol_one = parse_symbol(val); else if (!strcasecmp(key, "symbol_sync")) params->symbol_sync = parse_symbol(val); else if (!strcasecmp(key, "get")) { if (get_count < GETTER_SLOTS) parse_getter(val, ¶ms->getter[get_count++]); else { fprintf(stderr, "Maximum getter slots exceeded (%d)!\n", GETTER_SLOTS); usage(); } } else { fprintf(stderr, "Bad flex spec, unknown keyword (%s)!\n", key); usage(); } } if (params->min_bits < params->match_len) params->min_bits = params->match_len; if (params->min_bits > 0 && params->min_repeats < 1) params->min_repeats = 1; // add getter fields if unique requested if (params->unique) { int i = 0; for (int f = 0; output_fields[f]; ++f) { params->fields[i++] = output_fields[f]; } params->fields[i++] = "len"; params->fields[i++] = "data"; for (int g = 0; g < GETTER_SLOTS && params->getter[g].name; ++g) { params->fields[i++] = params->getter[g].name; } dev->fields = params->fields; } // sanity checks if (!params->name || !*params->name) { fprintf(stderr, "Bad flex spec, missing name!\n"); usage(); } if (!dev->modulation) { fprintf(stderr, "Bad flex spec, missing modulation!\n"); usage(); } if (!dev->short_width) { fprintf(stderr, "Bad flex spec, missing short width!\n"); usage(); } if (dev->modulation != OOK_PULSE_MANCHESTER_ZEROBIT && dev->modulation != FSK_PULSE_MANCHESTER_ZEROBIT) { if (!dev->long_width) { fprintf(stderr, "Bad flex spec, missing long width!\n"); usage(); } } if (!dev->reset_limit) { fprintf(stderr, "Bad flex spec, missing reset limit!\n"); usage(); } if (dev->modulation == OOK_PULSE_DMC || dev->modulation == OOK_PULSE_PIWM_RAW || dev->modulation == OOK_PULSE_PIWM_DC) { if (!dev->tolerance) { fprintf(stderr, "Bad flex spec, missing tolerance limit!\n"); usage(); } } if (params->symbol_zero && !params->symbol_one) { fprintf(stderr, "Bad flex spec, symbol-one missing!\n"); usage(); } if (params->symbol_one && !params->symbol_zero) { fprintf(stderr, "Bad flex spec, symbol-zero missing!\n"); usage(); } /* fprintf(stderr, "Adding flex decoder \"%s\"\n", params->name); fprintf(stderr, "\tmodulation=%u, short_width=%.0f, long_width=%.0f, reset_limit=%.0f\n", dev->modulation, dev->short_width, dev->long_width, dev->reset_limit); fprintf(stderr, "\tmin_rows=%u, min_bits=%u, min_repeats=%u, invert=%u, reflect=%u, match_len=%u, preamble_len=%u\n", params->min_rows, params->min_bits, params->min_repeats, params->invert, params->reflect, params->match_len, params->preamble_len); */ free(spec); return dev; }