/** @file
    Philips AJ3650 outdoor temperature sensor.

    Copyright (C) 2017 Chris Coffey <kpuc@sdf.org>

    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.
*/
/**
Philips outdoor temperature sensor -- used with various Philips clock
radios (tested on AJ3650).

Not tested, but these should also work: AJ260 ... maybe others?

A complete message is 112 bits:
- 4-bit initial preamble, always 0
- 4-bit packet separator, always 0, followed by 32-bit data packet.
- Packets are repeated 3 times for 108 bits total.

32-bit data packet format:

    0001cccc tttttttt tt000000 0b0?ssss

- c: channel: 0=channel 2, 2=channel 1, 4=channel 3 (4 bits)
- t: temperature in Celsius: subtract 500 and divide by 10 (10 bits)
- b: battery status: 0 = OK, 1 = LOW (1 bit)
- ?: unknown: always 1 in every packet I've seen (1 bit)
- s: CRC: non-standard CRC-4, poly 0x9, init 0x1

Pulse width:
- Short: 2000 us = 0
- Long: 6000 us = 1
Gap width:
- Short: 6000 us
- Long: 2000 us
Gap width between packets: 29000 us

Presumably the 4-bit preamble is meant to be a sync of some sort,
but it has the exact same pulse/gap width as a short pulse, and
gets processed as data.
*/

#include "decoder.h"

#define PHILIPS_BITLEN       112
#define PHILIPS_PACKETLEN    4
#define PHILIPS_STARTNIBBLE  0x0

static int philips_aj3650_decode(r_device *decoder, bitbuffer_t *bitbuffer)
{
    /* Map channel values to their real-world counterparts */
    uint8_t const channel_map[] = {2, 0, 1, 0, 3};

    uint8_t *bb;
    unsigned int i;
    uint8_t a, b, c;
    uint8_t packet[PHILIPS_PACKETLEN];
    uint8_t c_crc;
    uint8_t channel, battery_low;
    int temp_raw;
    float temperature;
    data_t *data;

    /* Invert the data bits */
    bitbuffer_invert(bitbuffer);

    /* Correct number of rows? */
    if (bitbuffer->num_rows != 1) {
        decoder_logf(decoder, 2, __func__, "wrong number of rows (%d)", bitbuffer->num_rows);
        return DECODE_ABORT_EARLY;
    }

    /* Correct bit length? */
    if (bitbuffer->bits_per_row[0] != PHILIPS_BITLEN) {
        decoder_logf(decoder, 2, __func__, "wrong number of bits (%d)", bitbuffer->bits_per_row[0]);
        return DECODE_ABORT_LENGTH;
    }

    bb = bitbuffer->bb[0];

    /* Correct start sequence? */
    if ((bb[0] >> 4) != PHILIPS_STARTNIBBLE) {
        decoder_log(decoder, 2, __func__, "wrong start nibble");
        return DECODE_ABORT_EARLY;
    }

    /* Compare and combine the 3 repeated packets, with majority wins */
    for (i = 0; i < PHILIPS_PACKETLEN; i++) {
        a = bb[i+1]; /* First packet - on byte boundary */
        b = (bb[i+5] << 4) | (bb[i+6] >> 4 & 0xf); /* Second packet - not on byte boundary */
        c = bb[i+10]; /* Third packet - on byte boundary */

        packet[i] = (a & b) | (b & c) | (a & c);
    }

    /* If debug enabled, print the combined majority-wins packet */
    decoder_logf_bitrow(decoder, 2, __func__, packet, PHILIPS_PACKETLEN * 8, "combined packet");

    /* Correct CRC? */
    c_crc = crc4(packet, PHILIPS_PACKETLEN, 0x9, 1); /* Including the CRC nibble */
    if (0 != c_crc) {
        decoder_logf(decoder, 1, __func__, "CRC failed, calculated %x", c_crc);
        return DECODE_FAIL_MIC;
    }

    /* Message validated, now parse the data */

    /* Channel */
    channel = packet[0] & 0x0f;
    if (channel >= (sizeof(channel_map) / sizeof(channel_map[0])))
        channel = 0;
    else
        channel = channel_map[channel];

    /* Temperature */
    temp_raw = (packet[1] << 2) | (packet[2] >> 6);
    temperature = (temp_raw - 500) * 0.1f;

    /* Battery status */
    battery_low = packet[PHILIPS_PACKETLEN - 1] & 0x40;

    /* clang-format off */
    data = data_make(
            "model",         "",            DATA_STRING, "Philips-Temperature",
            "channel",       "Channel",     DATA_INT,    channel,
            "battery_ok",    "Battery",     DATA_INT,    !battery_low,
            "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temperature,
            NULL);
    /* clang-format on */

    decoder_output_data(decoder, data);
    return 1;
}

static char const *const output_fields[] = {
        "model",
        "channel",
        "battery_ok",
        "temperature_C",
        NULL,
};

r_device const philips_aj3650 = {
        .name        = "Philips outdoor temperature sensor (type AJ3650)",
        .modulation  = OOK_PULSE_PWM,
        .short_width = 2000,
        .long_width  = 6000,
//        .gap_limit   = 8000,
        .reset_limit = 30000,
        .decode_fn   = &philips_aj3650_decode,
        .fields      = output_fields,
};
