/**
 * @file crc16.c
 * @brief Стандартная библиотека контроллера "Сатурн-PLC".
 * Функции вычисления контрольной суммы.
 * @version 0.1
 * @date 2024-03-30
 * 
 * @copyright Copyright (c) 1999-2018 Lammert Bies
 * https://github.com/lammertb/libcrc
 * 
 * This file is licensed under the MIT License as stated below
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 * 
 */

#include "satplc.h"
#include "satcrc.h"

static uint16_t crc_tab16[256] = {
    0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
    0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
    0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
    0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641, 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
    0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240, 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
    0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
    0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
    0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640, 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
    0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240, 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
    0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
    0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
    0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640, 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
    0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241, 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
    0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
    0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
    0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040
};

/**
 * @brief Расчёт значения контрольной суммы crc16
 * 
 * @param input_str Указатель на массив данных
 * @param num_bytes Длина массива
 * @return uint16_t 
 */
uint16_t crc_16( const unsigned char *input_str, size_t num_bytes ) {
    uint16_t crc;
    const unsigned char *ptr;
    size_t a;

    crc = CRC_START_16;
    ptr = input_str;

    if ( ptr != NULL ) for (a=0; a<num_bytes; a++) {

        crc = (crc >> 8) ^ crc_tab16[ (crc ^ (uint16_t) *ptr++) & 0x00FF ];
    }
    return crc;
}  /* crc_16 */

/**
 * @brief Расчёт значения контрольной суммы crc16 по алгоритму, используемому в протоколе "Modbus RTU"
 * 
 * @param input_str Указатель на массив данных
 * @param num_bytes Длина массива
 * @return uint16_t 
 */
uint16_t crc_modbus( const unsigned char *input_str, size_t num_bytes ) {
    uint16_t crc;
    const unsigned char *ptr;
    size_t a;

    crc = CRC_START_MODBUS;
    ptr = input_str;

    if ( ptr != NULL ) for (a=0; a<num_bytes; a++) {

        crc = (crc >> 8) ^ crc_tab16[ (crc ^ (uint16_t) *ptr++) & 0x00FF ];
    }

    return crc;
}  /* crc_modbus */

/**
 * @brief Побайтное вычисление контрольной суммы по алгоритму crc16.
 * 
 * @param crc Текущее значение контрольной суммы
 * @param c Новый байт
 * @return uint16_t Новое значение контрольной суммы
 */
uint16_t update_crc_16( uint16_t crc, unsigned char c ) {

    return (crc >> 8) ^ crc_tab16[ (crc ^ (uint16_t) c) & 0x00FF ];

}  /* update_crc_16 */

/**
 * @brief Расчёт значения контрольной суммы crc16 по алгоритму, используемому в датчиках производства компании "Sick".
 * 
 * @param input_str Указатель на массив данных
 * @param num_bytes Длина массива
 * @return uint16_t 
 */
uint16_t crc_sick( const unsigned char *input_str, size_t num_bytes ) {

    uint16_t crc;
    uint16_t low_byte;
    uint16_t high_byte;
    uint16_t short_c;
    uint16_t short_p;
    const unsigned char *ptr;
    size_t a;

    crc     = CRC_START_SICK;
    ptr     = input_str;
    short_p = 0;

    if ( ptr != NULL ) for (a=0; a<num_bytes; a++) {

        short_c = 0x00FF & (uint16_t) *ptr;

        if ( crc & 0x8000 ) crc = ( crc << 1 ) ^ CRC_POLY_SICK;
        else                crc =   crc << 1;

        crc    ^= ( short_c | short_p );
        short_p = short_c << 8;

        ptr++;
    }

    low_byte  = (crc & 0xFF00) >> 8;
    high_byte = (crc & 0x00FF) << 8;
    crc       = low_byte | high_byte;

    return crc;

}  /* crc_sick */

/**
 * @brief Побайтное вычисление контрольной суммы по алгоритму, используемому в 
 * датчиках производства компании "Sick".
 * @param crc Текущее значение контрольной суммы
 * @param c Новый байт
 * @return uint16_t Новое значение контрольной суммы
 */
uint16_t update_crc_sick( uint16_t crc, unsigned char c, unsigned char prev_byte ) {

    uint16_t short_c;
    uint16_t short_p;

    short_c  =   0x00FF & (uint16_t) c;
    short_p  = ( 0x00FF & (uint16_t) prev_byte ) << 8;

    if ( crc & 0x8000 ) crc = ( crc << 1 ) ^ CRC_POLY_SICK;
    else                crc =   crc << 1;

    crc ^= ( short_c | short_p );

    return crc;

}  /* update_crc_sick */
