#ifndef T1_C1_PACKET_DECODER_H #define T1_C1_PACKET_DECODER_H /*- * Copyright (c) 2021 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #if !defined(PACKET_CAPTURE_THRESHOLD) #define PACKET_CAPTURE_THRESHOLD 5u #endif #define C1_MODE_A 0b010101001100 #define C1_MODE_B 0b010101000011 #define C1_MODE_AB_TRAILER 0b1101 #define PACKET_DATABIT_SHIFT (0u) #define PACKET_PREAMBLE_DETECTED_SHIFT (1u) #define PACKET_DATABIT_MASK (1u<state == &t1_c1_decoder_states[0]) ? 0 : 1; } static void reset_t1_c1_packet_decoder(struct t1_c1_packet_decoder_work *decoder) { memset(decoder, 0, sizeof(*decoder)); decoder->state = &t1_c1_decoder_states[0]; } static void t1_c1_idle(unsigned bit, struct t1_c1_packet_decoder_work *decoder) { if (!(bit & PACKET_PREAMBLE_DETECTED_MASK)) { reset_t1_c1_packet_decoder(decoder); } } static void t1_c1_done(unsigned bit, struct t1_c1_packet_decoder_work *decoder) { (void)bit; (void)decoder; } static void t1_c1_rx_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder) { decoder->byte <<= 1; decoder->byte |= (bit & PACKET_DATABIT_MASK); } static void t1_rx_high_nibble_first_lfield_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder) { decoder->byte = (bit & PACKET_DATABIT_MASK); decoder->packet_rssi = decoder->current_rssi; } static void t1_rx_high_nibble_last_lfield_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder) { decoder->byte <<= 1; decoder->byte |= (bit & PACKET_DATABIT_MASK); decoder->mode = decoder->byte; decoder->L = HIGH_NIBBLE_3OUTOF6[decoder->byte]; decoder->flags = 0; } static void t1_rx_low_nibble_first_lfield_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder) { decoder->byte = (bit & PACKET_DATABIT_MASK); } static void t1_rx_low_nibble_last_lfield_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder) { decoder->byte <<= 1; decoder->byte |= (bit & PACKET_DATABIT_MASK); decoder->mode <<= 6; decoder->mode |= decoder->byte; const unsigned byte = LOW_NIBBLE_3OUTOF6[decoder->byte]; if (decoder->L == 0xFFu || byte == 0xFFu) { if (decoder->mode == C1_MODE_A) { decoder->b_frame_type = 0; decoder->state = &t1_c1_decoder_states[26]; // c1_rx_first_mode_bit } else if (decoder->mode == C1_MODE_B) { decoder->b_frame_type = 1; decoder->state = &t1_c1_decoder_states[26]; // c1_rx_first_mode_bit } else { reset_t1_c1_packet_decoder(decoder); } } else { decoder->b_frame_type = 0; decoder->c1_packet = 0; decoder->L |= byte; decoder->l = 0; decoder->packet[decoder->l++] = decoder->L; decoder->L = FULL_TLG_LENGTH_FROM_L_FIELD[decoder->L]; } } static void t1_rx_high_nibble_first_data_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder) { decoder->byte = (bit & PACKET_DATABIT_MASK); } static void t1_rx_high_nibble_last_data_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder) { decoder->byte <<= 1; decoder->byte |= (bit & PACKET_DATABIT_MASK); const unsigned byte = HIGH_NIBBLE_3OUTOF6[decoder->byte]; if (byte == 0xFFu) decoder->err_3outof = 1; decoder->packet[decoder->l] = byte; } static void t1_rx_low_nibble_first_data_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder) { decoder->byte = (bit & PACKET_DATABIT_MASK); } static void t1_rx_low_nibble_last_data_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder) { decoder->byte <<= 1; decoder->byte |= (bit & PACKET_DATABIT_MASK); const unsigned byte = LOW_NIBBLE_3OUTOF6[decoder->byte]; if (byte == 0xFFu) decoder->err_3outof = 1; decoder->packet[decoder->l++] |= byte; if (decoder->l < decoder->L) { decoder->state = &t1_c1_decoder_states[13]; // rx_high_nibble_first_data_bit } else { time_t now; time(&now); struct tm *timeinfo = gmtime(&now); strftime(decoder->timestamp, sizeof(decoder->timestamp), "%Y-%m-%d %H:%M:%S.000", timeinfo); } } static void c1_rx_first_mode_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder) { decoder->byte = (bit & PACKET_DATABIT_MASK); } static void c1_rx_last_mode_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder) { decoder->byte <<= 1; decoder->byte |= (bit & PACKET_DATABIT_MASK); decoder->mode <<= 4; decoder->mode |= decoder->byte; if (decoder->byte == C1_MODE_AB_TRAILER) { decoder->c1_packet = 1; } else { reset_t1_c1_packet_decoder(decoder); } } static void c1_rx_first_lfield_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder) { decoder->byte = (bit & PACKET_DATABIT_MASK); } static void c1_rx_last_lfield_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder) { decoder->byte <<= 1; decoder->byte |= (bit & PACKET_DATABIT_MASK); decoder->L = decoder->byte; decoder->l = 0; decoder->packet[decoder->l++] = decoder->L; if (decoder->b_frame_type) { decoder->L = get_mode_b_tlg_length(decoder->L); } else { decoder->L = FULL_TLG_LENGTH_FROM_L_FIELD[decoder->L]; } } static void c1_rx_first_data_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder) { decoder->byte = (bit & PACKET_DATABIT_MASK); } static void c1_rx_last_data_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder) { decoder->byte <<= 1; decoder->byte |= (bit & PACKET_DATABIT_MASK); decoder->packet[decoder->l++] = decoder->byte; if (decoder->l < decoder->L) { decoder->state = &t1_c1_decoder_states[38]; // c1_rx_first_data_bit } else { time_t now; time(&now); struct tm *timeinfo = gmtime(&now); strftime(decoder->timestamp, sizeof(decoder->timestamp), "%Y-%m-%d %H:%M:%S.000", timeinfo); } } static uint16_t calc_crc_wmbus(const uint8_t *data, size_t datalen) { uint16_t crc = 0; while (datalen--) crc = CRC16_DNP_TABLE[*data++ ^ (crc >> 8)] ^ (crc << 8); crc = ~crc; return crc; } static bool check_calc_crc_wmbus(const uint8_t *data, size_t datalen) { bool crc_ok = false; uint16_t crc1, crc2; if (datalen >= 12) { crc1 = calc_crc_wmbus(data, 10); crc2 = (data[10] << 8) | (data[11]); data += 12; datalen -= 12; crc_ok = (crc1 == crc2); while (crc_ok && datalen) { if (datalen >= 18) { crc1 = calc_crc_wmbus(data, 16); crc2 = (data[16] << 8) | (data[17]); data += 18; datalen -= 18; crc_ok = (crc1 == crc2); } else { crc1 = calc_crc_wmbus(data, datalen-2); crc2 = (data[datalen-2] << 8) | (data[datalen-1]); data += datalen; datalen -= datalen; crc_ok = (crc1 == crc2); } } } return crc_ok; } static bool check_calc_crc_wmbus_b_frame_type(const uint8_t *data, size_t datalen) { bool crc_ok = (datalen >= 12); uint16_t crc1, crc2; /* The CRC field of Block 2 is calculated on the _concatenation_ of Block 1 and Block 2 data. */ while (crc_ok && datalen) { if (datalen >= 128) { crc1 = calc_crc_wmbus(data, 126); crc2 = (data[126] << 8) | (data[127]); data += 128; datalen -= 128; crc_ok = (crc1 == crc2); } else { crc1 = calc_crc_wmbus(data, datalen-2); crc2 = (data[datalen-2] << 8) | (data[datalen-1]); data += datalen; datalen -= datalen; crc_ok = (crc1 == crc2); } } return crc_ok; } __attribute__((__packed__)) struct wmmbus_header_with_crc { uint8_t L; // 1 uint8_t C; // 1 uint16_t manufacturer; // 2 uint32_t ident_no; // 4 uint8_t version; // 1 uint8_t device_type; // 1 uint16_t crc; // 2 }; // 12 bytes in total. /** @brief Strip CRCs in place. */ static unsigned cook_pkt(uint8_t *data, unsigned datalen) { const uint8_t *const L = data; // Valid for T1 and C1 mode A: L = len(data). (Without CRC bytes included). uint8_t *dst = data; unsigned dstlen = 0; if (*L > 0 && // L should be greater than 0 bytes. datalen >= sizeof(struct wmmbus_header_with_crc)) { dst += 10; dstlen += 10; data += 12; datalen -= 12; while (datalen) { if (datalen >= 18) { memmove(dst, data, 16); dst += 16; dstlen += 16; data += 18; datalen -= 18; } else { memmove(dst, data, datalen-2); dst += (datalen-2); dstlen += (datalen-2); data += datalen; datalen -= datalen; } } } return dstlen; } /** @brief Strip CRCs in place. */ static unsigned cook_pkt_b_frame_type(uint8_t *data, unsigned datalen) { uint8_t *const L = data; // Valid for C1 mode B: L = len(data) + num of CRC bytes. (With CRC bytes included.) uint8_t *dst = data; unsigned dstlen = 0; if (*L >= 2 && // L should be at least 2 bytes long - means only CRC was received :). datalen >= sizeof(struct wmmbus_header_with_crc)) { while (datalen) { /* The CRC field of Block 2 is calculated on the _concatenation_ of Block 1 and Block 2 data. */ if (datalen >= 128) { memmove(dst, data, 126); dst += 126; dstlen += 126; data += 128; datalen -= 128; *L = *L - 2; } else { memmove(dst, data, datalen-2); dst += (datalen-2); dstlen += (datalen-2); data += datalen; datalen -= datalen; *L = *L - 2; } } } return dstlen; } static inline uint32_t get_serial(const uint8_t *const packet) { uint32_t serial; memcpy(&serial, &packet[4], sizeof(serial)); return serial; } extern int opts_show_used_algorithm; static void t1_c1_packet_decoder(unsigned bit, unsigned rssi, struct t1_c1_packet_decoder_work *decoder, const char *algorithm) { decoder->current_rssi = rssi; (*decoder->state++)(bit, decoder); if (*decoder->state == t1_c1_idle) { // nothing } else if (*decoder->state == t1_c1_done) { if (decoder->b_frame_type) { decoder->crc_ok = check_calc_crc_wmbus_b_frame_type(decoder->packet, decoder->L) ? 1 : 0; } else { decoder->crc_ok = check_calc_crc_wmbus(decoder->packet, decoder->L) ? 1 : 0; } if (!opts_show_used_algorithm) algorithm = ""; fprintf(stdout, "%s%s;%u;%u;%s;%u;%u;%08X;", algorithm, decoder->c1_packet ? "C1": "T1", decoder->crc_ok, decoder->err_3outof^1, decoder->timestamp, decoder->packet_rssi, rssi, get_serial(decoder->packet)); #if 0 fprintf(stdout, "0x"); for (size_t l = 0; l < decoder->L; l++) fprintf(stdout, "%02x", decoder->packet[l]); fprintf(stdout, ";"); #endif #if 1 if (decoder->b_frame_type) { decoder->L = cook_pkt_b_frame_type(decoder->packet, decoder->L); } else { decoder->L = cook_pkt(decoder->packet, decoder->L); } fprintf(stdout, "0x"); for (size_t l = 0; l < decoder->L; l++) fprintf(stdout, "%02x", decoder->packet[l]); #endif fprintf(stdout, "\n"); fflush(stdout); reset_t1_c1_packet_decoder(decoder); } else { // Stop receiving packet if current rssi below threshold. // The current packet seems to be collided with an another one. if (rssi < PACKET_CAPTURE_THRESHOLD) { reset_t1_c1_packet_decoder(decoder); } } } #endif /* T1_C1_PACKET_DECODER_H */