Редизайн на основе текущей ветки мейнстрима + новые устройства.
[rtl-433.git] / src / devices / radiohead_ask.c
diff --git a/src/devices/radiohead_ask.c b/src/devices/radiohead_ask.c
new file mode 100644 (file)
index 0000000..6f42735
--- /dev/null
@@ -0,0 +1,174 @@
+#include "rtl_433.h"
+#include "pulse_demod.h"
+#include "util.h"
+
+// Transmitter speed in bits per seconds
+#define RH_ASK_SPEED 2000
+#define RH_ASK_BIT_LEN (int)1e6/RH_ASK_SPEED
+
+// Maximum message length (including the headers, byte count and FCS) we are willing to support
+// This is pretty arbitrary
+#define RH_ASK_MAX_PAYLOAD_LEN 67
+#define RH_ASK_HEADER_LEN 4
+#ifndef RH_ASK_MAX_MESSAGE_LEN
+ #define RH_ASK_MAX_MESSAGE_LEN (RH_ASK_MAX_PAYLOAD_LEN - RH_ASK_HEADER_LEN - 3)
+#endif
+
+uint8_t payload[RH_ASK_MAX_PAYLOAD_LEN] = {0};
+int data_payload[RH_ASK_MAX_MESSAGE_LEN];
+
+// Note: all tje "4to6 code" came from RadioHead source code.
+// see: http://www.airspayce.com/mikem/arduino/RadioHead/index.html
+
+// 4 bit to 6 bit symbol converter table
+// Used to convert the high and low nybbles of the transmitted data
+// into 6 bit symbols for transmission. Each 6-bit symbol has 3 1s and 3 0s 
+// with at most 3 consecutive identical bits
+static uint8_t symbols[] = {
+    0xd,  0xe,  0x13, 0x15, 0x16, 0x19, 0x1a, 0x1c, 
+    0x23, 0x25, 0x26, 0x29, 0x2a, 0x2c, 0x32, 0x34
+};
+
+// Convert a 6 bit encoded symbol into its 4 bit decoded equivalent
+uint8_t symbol_6to4(uint8_t symbol)
+{
+    uint8_t i;
+    // Linear search :-( Could have a 64 byte reverse lookup table?
+    // There is a little speedup here courtesy Ralph Doncaster:
+    // The shortcut works because bit 5 of the symbol is 1 for the last 8
+    // symbols, and it is 0 for the first 8.
+    // So we only have to search half the table
+    for (i = (symbol>>2) & 8; i < 16 ; i++){
+        if (symbol == symbols[i]) return i;
+    }
+    return 0xFF; // Not found
+}
+
+
+static int radiohead_ask_callback(bitbuffer_t *bitbuffer) {
+    // Get time
+    char time_str[LOCAL_TIME_BUFLEN];
+    data_t *data;
+    local_time_str(0, time_str);
+
+    uint8_t row = 0; // we are considering only first row
+    unsigned int len = bitbuffer->bits_per_row[row];
+
+    uint8_t msg_len = RH_ASK_MAX_MESSAGE_LEN;
+    unsigned int pos, nb_bytes;
+    uint8_t rxBits[2] = {0};
+
+    uint16_t crc, crc_recompute;
+    uint8_t data_len, header_to, header_from, header_id, header_flags;
+
+
+    // Looking for preamble
+    uint8_t init_pattern[] = {
+      0x55, // 8
+      0x55, // 16
+      0x55, // 24
+      0x51, // 32
+      0xcd, // 40
+    };
+    // The first 0 is ignored by the decoder, so we look only for 28 bits of "01"
+    // and not 32. Also "0x1CD" is 0xb38 (RH_ASK_START_SYMBOL) with LSBit first.
+    uint8_t init_pattern_len = 40;
+
+    pos = bitbuffer_search(bitbuffer, row, 0, init_pattern, init_pattern_len);
+    if(pos == len){
+        if(debug_output) {
+            printf("RH ASK preamble not found\n");
+        }
+        return 0;
+    }
+
+    // read "bytes" of 12 bit
+    nb_bytes=0;
+    pos += init_pattern_len;
+    for(; pos < len && nb_bytes < msg_len; pos += 12){
+        bitbuffer_extract_bytes(bitbuffer, row, pos, rxBits, /*len=*/16);
+        // ^ we should read 16 bits and not 12, elsewhere last 4bits are ignored
+        rxBits[0] = reverse8(rxBits[0]);
+        rxBits[1] = reverse8(rxBits[1]);
+        rxBits[1] = ((rxBits[1] & 0x0F)<<2) + (rxBits[0]>>6);
+        rxBits[0] &= 0x3F;
+        uint8_t hi_nibble = symbol_6to4(rxBits[0]);
+        if(hi_nibble > 0xF){
+            if(debug_output){
+                fprintf(stdout, "Error on 6to4 decoding high nibble: %X\n", rxBits[0]);
+            }
+            return 0;
+        }
+        uint8_t lo_nibble = symbol_6to4(rxBits[1]);
+        if(lo_nibble > 0xF){
+            if(debug_output){
+                fprintf(stdout, "Error on 6to4 decoding low nibble: %X\n", rxBits[1]);
+            }
+            return 0;
+        }
+        uint8_t byte =  hi_nibble<<4 | lo_nibble;
+        payload[nb_bytes] = byte;
+        if(nb_bytes == 0){
+            msg_len = byte;
+        }
+        nb_bytes++;
+    }
+
+    // Get header
+    data_len = msg_len - RH_ASK_HEADER_LEN - 3;
+    header_to = payload[1];
+    header_from = payload[2];
+    header_id = payload[3];
+    header_flags = payload[4];
+
+    // Check CRC
+    crc = payload[5 + data_len] + (payload[5 + data_len + 1]<<8);
+    crc_recompute = ~crc16(payload, msg_len-2, 0x8408, 0xFFFF);
+    if(crc_recompute != crc){
+        if(debug_output){
+            fprintf(stdout, "CRC error: %04X != %04X\n", crc_recompute, crc);
+        }
+        return 0;
+    }
+
+    // Format data
+    for(int j=0; j<msg_len; j++){
+        data_payload[j] = (int)payload[5+j];
+    }
+    data = data_make("time", "",              DATA_STRING, time_str,
+            "model",         "",              DATA_STRING, "RadioHead ASK",
+            "crc",           "",              DATA_STRING, "OK",
+            "len",           "Data len",      DATA_INT, data_len,
+            "to",            "To",            DATA_INT, header_to,
+            "from",          "From",          DATA_INT, header_from,
+            "id",            "Id",            DATA_INT, header_id,
+            "flags",         "Flags",         DATA_INT, header_flags,
+            "payload",       "Payload",       DATA_ARRAY, data_array(data_len, DATA_INT, data_payload),
+    NULL);
+    data_acquired_handler(data);
+
+    return 0;
+}
+
+static char *output_fields[] = {
+    "time",
+    "model",
+    "crc",
+    "len",
+    "to",
+    "from",
+    "id",
+    "flags",
+    "payload",
+    NULL
+};
+
+r_device radiohead_ask = {
+    .name           = "Radiohead ASK",
+    .modulation     = OOK_PULSE_PCM_RZ,
+    .short_limit    = RH_ASK_BIT_LEN,
+    .long_limit     = RH_ASK_BIT_LEN,
+    .reset_limit    = RH_ASK_BIT_LEN*10,
+    .json_callback  = &radiohead_ask_callback,
+    .fields         = output_fields,
+};