827 lines
21 KiB
C
827 lines
21 KiB
C
/*
|
||
* @Author:
|
||
* @day: 2023-04-11 08:21:19
|
||
* @LastEditors: xxx
|
||
* @LastEditTime: 2023-08-15 10:14:58
|
||
* @Description:
|
||
* email:
|
||
* Copyright (c) 2023 by xxx, All Rights Reserved.
|
||
*/
|
||
|
||
#include "../inc/lib.h"
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include "cmac.h"
|
||
const uint8_t _days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||
const uint16_t _month_days[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
|
||
static uint32_t crc32_table[256]; // CRC32表
|
||
|
||
// ASSIC码数组转字符串
|
||
void assic_to_str(uint8_t *assic, uint8_t len, uint8_t *str)
|
||
{
|
||
for (uint8_t i = 0; i < len; i++)
|
||
{
|
||
if (assic[i] == 0)
|
||
{
|
||
str[i] = 48;
|
||
}
|
||
else
|
||
{
|
||
str[i] = (char)assic[i];
|
||
}
|
||
}
|
||
}
|
||
|
||
// 每隔三位数加逗号,将数字转为每隔3位整数由逗号“,”分隔的字符串
|
||
void add_commas(uint32_t num, char *result)
|
||
{
|
||
char temp[64];
|
||
sprintf(temp, "%d", num);
|
||
|
||
int32_t len = strlen(temp);
|
||
int32_t commas = (len - 1) / 3;
|
||
|
||
int32_t index = 0;
|
||
int32_t resultIndex = 0;
|
||
for (int32_t i = len - 1; i >= 0; i--)
|
||
{
|
||
result[resultIndex++] = temp[i];
|
||
index++;
|
||
if (index % 3 == 0 && commas > 0)
|
||
{
|
||
result[resultIndex++] = ',';
|
||
commas--;
|
||
}
|
||
}
|
||
result[resultIndex] = '\0';
|
||
|
||
// Reverse the result string
|
||
int32_t start = 0;
|
||
int32_t end = resultIndex - 1;
|
||
while (start < end)
|
||
{
|
||
char temp = result[start];
|
||
result[start] = result[end];
|
||
result[end] = temp;
|
||
start++;
|
||
end--;
|
||
}
|
||
}
|
||
|
||
void get_cpu_id(uint32_t *id)
|
||
{
|
||
#ifdef STM32
|
||
// 获取CPU唯一的ID
|
||
id[0] = *(uint32_t *)(UID_BASE);
|
||
id[1] = *(uint32_t *)(UID_BASE + 4);
|
||
id[2] = *(uint32_t *)(UID_BASE + 8);
|
||
#endif
|
||
}
|
||
|
||
uint32_t cpu_encrypt(void)
|
||
{
|
||
uint32_t cpuid[3];
|
||
#ifdef STM32
|
||
// 获取CPU唯一的ID
|
||
cpuid[0] = *(uint32_t *)(UID_BASE);
|
||
cpuid[1] = *(uint32_t *)(UID_BASE + 4);
|
||
cpuid[2] = *(uint32_t *)(UID_BASE + 8);
|
||
#endif
|
||
// 加密算法,很简单的加密算法
|
||
uint32_t encrypt_code = (cpuid[0] >> 3) + (cpuid[1] >> 1) + (cpuid[2] >> 2);
|
||
return encrypt_code;
|
||
}
|
||
|
||
// 判断加密
|
||
BOOL cpu_judge_encrypt(uint32_t cupid_encrypt)
|
||
{
|
||
uint32_t cpuid[4];
|
||
#ifdef STM32
|
||
// 获取CPU唯一的ID
|
||
cpuid[0] = *(uint32_t *)(UID_BASE);
|
||
cpuid[1] = *(uint32_t *)(UID_BASE + 4);
|
||
cpuid[2] = *(uint32_t *)(UID_BASE + 8);
|
||
#endif
|
||
// 加密算法,很简单的加密算法
|
||
cpuid[3] = (cpuid[0] >> 3) + (cpuid[1] >> 1) + (cpuid[2] >> 2);
|
||
// 检查Flash中的UID是否合法
|
||
return (cupid_encrypt == cpuid[3]);
|
||
}
|
||
|
||
void convert_seconds(uint32_t total_seconds, char *date)
|
||
{
|
||
uint32_t days = total_seconds / 86400;
|
||
uint32_t hours = (total_seconds % 86400) / 3600;
|
||
uint32_t minutes = (total_seconds % 3600) / 60;
|
||
uint32_t seconds = total_seconds % 60;
|
||
if (days > 0)
|
||
{
|
||
if (days > 1000)
|
||
{
|
||
sprintf(date, "%02d d %02d h", days, hours);
|
||
}
|
||
else
|
||
{
|
||
sprintf(date, "%02d d %02d h %02d m", days, hours, minutes);
|
||
}
|
||
}
|
||
else if (hours > 0)
|
||
{
|
||
sprintf(date, "%02d h %02d m %02d s", hours, minutes, seconds);
|
||
}
|
||
else if (minutes > 0)
|
||
{
|
||
sprintf(date, "%02d m %02d s", minutes, seconds);
|
||
}
|
||
else
|
||
{
|
||
sprintf(date, "%02d s", seconds);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief Generate the CRC32 lookup table.
|
||
*
|
||
* This function generates the CRC32 lookup table used for fast computation of the
|
||
* CRC32 checksum. The table is generated using the polynomial 0xEDB88320, which is a
|
||
* common polynomial used in CRC32 calculations.
|
||
*/
|
||
static void generate_crc32_table()
|
||
{
|
||
static BOOL is_init = FALSE;
|
||
if (is_init)
|
||
{
|
||
return;
|
||
}
|
||
uint32_t polynomial = 0xEDB88320;
|
||
for (uint32_t i = 0; i < 256; i++)
|
||
{
|
||
uint32_t crc = i;
|
||
for (uint32_t j = 0; j < 8; j++)
|
||
{
|
||
crc = (crc & 1) ? (crc >> 1) ^ polynomial : crc >> 1;
|
||
}
|
||
crc32_table[i] = crc;
|
||
}
|
||
is_init = TRUE;
|
||
}
|
||
|
||
/**
|
||
* @brief 版本号1.0拆解成1和0
|
||
* @param {uint8_t} *version_str
|
||
* @param {uint8_t} *hi
|
||
* @param {uint8_t} *lo
|
||
* @return {*}
|
||
*/
|
||
void version_split(uint8_t *version_str, uint8_t *hi, uint8_t *lo)
|
||
{
|
||
uint8_t flag = 1;
|
||
|
||
for (uint8_t i = 0; version_str[i] != '\0'; i++)
|
||
{
|
||
if (version_str[i] == '.')
|
||
{
|
||
flag = 0;
|
||
continue;
|
||
}
|
||
|
||
if (flag)
|
||
{
|
||
*hi = *hi * 10 + (version_str[i] - '0');
|
||
}
|
||
else
|
||
{
|
||
*lo = *lo * 10 + (version_str[i] - '0');
|
||
}
|
||
}
|
||
}
|
||
|
||
// 反序数组
|
||
void reverse(uint8_t *buf, uint16_t len)
|
||
{
|
||
uint8_t tmp;
|
||
uint16_t i;
|
||
for (i = 0; i < len / 2; i++)
|
||
{
|
||
tmp = buf[i];
|
||
buf[i] = buf[len - i - 1];
|
||
buf[len - i - 1] = tmp;
|
||
}
|
||
}
|
||
|
||
/***
|
||
* @brief 判断是否在数组中
|
||
* @param {uint8_t} *arr 数组
|
||
* @param {uint8_t} len 数组长度
|
||
* @param {uint8_t} val 要判断的值
|
||
* @return {*} TRUE: 在数组中
|
||
*/
|
||
BOOL is_in_array(uint16_t *arr, uint16_t len, uint16_t val)
|
||
{
|
||
uint16_t i;
|
||
for (i = 0; i < len; i++)
|
||
{
|
||
if (arr[i] == val)
|
||
{
|
||
return TRUE;
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
/**
|
||
* 计算并返回指定数据区域crc的值
|
||
*
|
||
* @param data: 待计算的数据区首地址
|
||
* @param length: 待计算的数据区长度
|
||
*
|
||
* @return crc计算的结果
|
||
*/
|
||
uint16_t crc16_compute(const uint8_t *const data, uint16_t length)
|
||
{
|
||
uint16_t crcVal = 0xffff;
|
||
const uint8_t *ptr = data;
|
||
|
||
for (uint16_t i = 0; i < length; i++)
|
||
{
|
||
crcVal ^= (uint16_t)*ptr++;
|
||
|
||
for (uint8_t j = 0; j < 8; j++)
|
||
{
|
||
if (crcVal & 0x0001)
|
||
{
|
||
crcVal = (crcVal >> 1) ^ 0x8401;
|
||
}
|
||
else
|
||
{
|
||
crcVal >>= 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
return crcVal;
|
||
}
|
||
|
||
/**
|
||
* @brief Calculate the CRC32 value of a data buffer.
|
||
*
|
||
* This function calculates the CRC32 value of a data buffer using the lookup table method.
|
||
* The lookup table is generated using the polynomial 0xEDB88320, which is a common polynomial used in CRC32 calculations.
|
||
*
|
||
* @param data The data buffer to calculate the CRC32 value of.
|
||
* @param length The length of the data buffer in bytes.
|
||
* @return The CRC32 value of the data buffer.
|
||
*/
|
||
uint32_t crc32_compute(const uint8_t *const data, uint16_t length)
|
||
{
|
||
generate_crc32_table();
|
||
uint32_t crc = 0xFFFFFFFF;
|
||
for (size_t i = 0; i < length; i++)
|
||
{
|
||
crc = (crc >> 8) ^ crc32_table[(crc ^ data[i]) & 0xFF];
|
||
}
|
||
return crc ^ 0xFFFFFFFF;
|
||
}
|
||
|
||
/**
|
||
* @brief 计算 64 位 CRC 值
|
||
*
|
||
* 根据给定的数据块和长度,使用 AES-CMAC 算法和 CRC32 算法计算 64 位 CRC 值。
|
||
* 使用这个函数heap设置0x800
|
||
*
|
||
* @param data 数据块指针
|
||
* @param length 数据块长度
|
||
*
|
||
* @return 返回计算得到的 64 位 CRC 值
|
||
*/
|
||
uint64_t crc64_compute(const uint8_t *const data, const uint16_t length)
|
||
{
|
||
uint8_t cmac_key[] = {
|
||
0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6,
|
||
0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C}; // 密钥
|
||
uint8_t dst[4];
|
||
uint8_t mic[16];
|
||
uint8_t *p;
|
||
uint32_t lo = 0, hi = 0;
|
||
AES_CMAC_CTX AesCmacCtx[1]; // 密钥扩展表
|
||
AES_CMAC_Init(AesCmacCtx); // 完成密钥扩展表的初始化
|
||
AES_CMAC_SetKey(AesCmacCtx, cmac_key); // 完成密钥扩展表数据 // 存放生成校验数据的数组
|
||
AES_CMAC_Update(AesCmacCtx, data, length & 0xFF); // 完成数据的奇偶校验
|
||
AES_CMAC_Final(mic, AesCmacCtx); // 生成16个字节的校验表
|
||
uint32_t xor_vol = (uint32_t)((uint32_t)mic[3] << 24 | (uint32_t)mic[2] << 16 | (uint32_t)mic[1] << 8 | (uint32_t)mic[0]); // 取表4个字节作为校验码
|
||
p = (uint8_t *)&xor_vol;
|
||
osel_memcpy(dst, p, 4);
|
||
lo = (uint32_t)dst[0] << 24 | (uint32_t)dst[1] << 16 | (uint32_t)dst[2] << 8 | (uint32_t)dst[3];
|
||
hi = crc32_compute(data, length);
|
||
return ((uint64_t)hi << 32) | lo;
|
||
}
|
||
|
||
/**
|
||
* 计算并返回指定数据区域异或的值
|
||
*
|
||
* @param data: 待计算的数据区首地址
|
||
* @param length: 待计算的数据区长度
|
||
*
|
||
* @return 异或计算的结果
|
||
*/
|
||
uint8_t xor_compute(const uint8_t *const data, uint16_t length)
|
||
{
|
||
uint16_t i;
|
||
const uint8_t *ptr = data;
|
||
uint8_t xor = 0;
|
||
for (i = 0; i < length; i++)
|
||
{
|
||
xor ^= *ptr;
|
||
ptr++;
|
||
}
|
||
return xor;
|
||
}
|
||
|
||
// 通过bit位获取置1个数量
|
||
uint8_t get_bit_num(uint8_t bit)
|
||
{
|
||
uint8_t num = 0;
|
||
while (bit)
|
||
{
|
||
if (bit & 0x01)
|
||
{
|
||
num++;
|
||
}
|
||
bit >>= 1;
|
||
}
|
||
return num;
|
||
}
|
||
|
||
// 通过bit位获取置1的位置
|
||
BOOL is_bit_set(int32_t x, int32_t k)
|
||
{
|
||
int32_t mask = 1 << k;
|
||
return (x & mask) != 0;
|
||
}
|
||
|
||
// 判断数组是否全是同一个值
|
||
BOOL is_same_value(uint8_t *buf, uint16_t len, uint8_t value)
|
||
{
|
||
uint16_t i;
|
||
for (i = 0; i < len; i++)
|
||
{
|
||
if (buf[i] != value)
|
||
{
|
||
return FALSE;
|
||
}
|
||
}
|
||
return TRUE;
|
||
}
|
||
|
||
// 检查是否是闰年
|
||
uint8_t is_leap_year(uint16_t year)
|
||
{
|
||
return (year % 400 == 0) || (year % 100 != 0 && year % 4 == 0);
|
||
}
|
||
|
||
// 检查日期是否合法
|
||
BOOL is_valid_date(uint8_t year, uint8_t month, uint8_t day)
|
||
{
|
||
if (year < 1 || month < 1 || month > 12 || day < 1)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
uint8_t days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||
|
||
// 如果是闰年,2月有29天
|
||
if (is_leap_year(year))
|
||
{
|
||
days_in_month[1] = 29;
|
||
}
|
||
|
||
return day <= days_in_month[month - 1];
|
||
}
|
||
|
||
// 检查时间是否合法
|
||
BOOL is_valid_time(uint8_t hour, uint8_t minute, uint8_t second)
|
||
{
|
||
return (hour < 24) &&
|
||
(minute < 60) &&
|
||
(second < 60);
|
||
}
|
||
|
||
// 检查日期和时间是否合法
|
||
BOOL is_valid_datetime(uint8_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second)
|
||
{
|
||
return is_valid_date(year, month, day) && is_valid_time(hour, minute, second);
|
||
}
|
||
|
||
// 计算从1970年1月1日到给定年月日的天数
|
||
uint32_t days_since_1970(uint16_t year, uint8_t month, uint8_t day)
|
||
{
|
||
static const uint8_t month_days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||
uint32_t days = 0;
|
||
uint16_t y = 1970;
|
||
|
||
// 计算整年过去的天数
|
||
while (y < year)
|
||
{
|
||
days += is_leap_year(y) ? 366 : 365;
|
||
y++;
|
||
}
|
||
|
||
// 计算当前年份中过去的天数
|
||
for (int32_t m = 0; m < month - 1; m++)
|
||
{
|
||
days += month_days[m];
|
||
if (m == 1 && is_leap_year(year))
|
||
{ // 如果是2月且是闰年,多加一天
|
||
days++;
|
||
}
|
||
}
|
||
|
||
// 加上当前月的天数
|
||
days += day - 1; // 因为day是从1开始的,而我们需要的是从0开始的偏移量
|
||
|
||
return days;
|
||
}
|
||
|
||
// 将日期转换为秒数
|
||
uint32_t date_to_seconds(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second)
|
||
{
|
||
uint32_t secs = 0;
|
||
uint32_t days = days_since_1970(year, month, day);
|
||
secs = days * 24 * 60 * 60; // 一天有24小时,每小时有60分钟,每分钟有60秒
|
||
secs += hour * 60 * 60 + minute * 60 + second;
|
||
return secs;
|
||
}
|
||
|
||
// 函数用于将秒数转换为日期和时间,年份4位
|
||
void seconds_to_date(uint32_t total_seconds, rtc_date_t *date, rtc_time_t *time)
|
||
{
|
||
uint16_t year = 1970; // Unix时间戳的起始年份
|
||
uint8_t month = 1;
|
||
uint8_t day = 1;
|
||
uint32_t seconds = total_seconds;
|
||
uint8_t hours, minutes, secs;
|
||
|
||
// 正确处理小时、分钟和秒
|
||
hours = (seconds / 3600) % 24;
|
||
minutes = (seconds / 60) % 60;
|
||
secs = seconds % 60;
|
||
|
||
// 计算日期
|
||
uint32_t days = total_seconds / (24 * 3600);
|
||
seconds = total_seconds % (24 * 3600); // 更新seconds为剩余秒数
|
||
|
||
for (; days > 0; days--)
|
||
{
|
||
// 当前月份的天数
|
||
uint8_t days_in_month = 31;
|
||
if (month == 2)
|
||
{
|
||
days_in_month = is_leap_year(year) ? 29 : 28;
|
||
}
|
||
else if (month == 4 || month == 6 || month == 9 || month == 11)
|
||
{
|
||
days_in_month = 30;
|
||
}
|
||
|
||
if (++day > days_in_month)
|
||
{
|
||
day = 1;
|
||
if (++month > 12)
|
||
{
|
||
month = 1;
|
||
year++;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 将结果存储到结构体中
|
||
date->year = year;
|
||
date->month = month;
|
||
date->day = day;
|
||
time->hour = hours;
|
||
time->minute = minutes;
|
||
time->second = secs;
|
||
}
|
||
|
||
// 计算一年中的第几天
|
||
uint16_t dayOfyear(uint16_t year, uint8_t month, uint8_t day)
|
||
{
|
||
uint8_t month_days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||
uint16_t total;
|
||
total = day;
|
||
if (month > 2 && is_leap_year(year))
|
||
total += 1;
|
||
for (uint8_t i = 0; i < month - 1; i++)
|
||
{
|
||
total += month_days[i];
|
||
}
|
||
return total;
|
||
}
|
||
|
||
// 计算一年中的第几周
|
||
uint16_t weekOfyear(uint16_t year, uint8_t month, uint8_t day)
|
||
{
|
||
uint16_t day_of_year = dayOfyear(year, month, day);
|
||
return (day_of_year - 1) / 7 + 1;
|
||
}
|
||
|
||
// 获取今天星期几
|
||
uint8_t get_weekday(uint16_t year, uint8_t month, uint8_t day)
|
||
{
|
||
uint8_t w = 0;
|
||
if (month == 1 || month == 2)
|
||
{
|
||
month += 12;
|
||
year--;
|
||
}
|
||
w = (day + 2 * month + 3 * (month + 1) / 5 + year + year / 4 - year / 100 + year / 400) % 7;
|
||
return w + 1;
|
||
}
|
||
|
||
// 传入十六进制0x23 返回十进制23
|
||
uint8_t hex_format_dec(uint8_t hex)
|
||
{
|
||
char buf[4];
|
||
osel_memset((uint8_t *)buf, 0, 4);
|
||
sprintf(buf, "%x", hex);
|
||
int32_t dec = 0;
|
||
int32_t weight = 1;
|
||
int32_t len = strlen(buf);
|
||
for (int32_t i = len - 1; i >= 0; i--)
|
||
{
|
||
if (buf[i] >= '0' && buf[i] <= '9')
|
||
{
|
||
dec += (buf[i] - '0') * weight;
|
||
}
|
||
else if (buf[i] >= 'A' && buf[i] <= 'F')
|
||
{
|
||
dec += (buf[i] - 'A' + 10) * weight;
|
||
}
|
||
else if (buf[i] >= 'a' && buf[i] <= 'f')
|
||
{
|
||
dec += (buf[i] - 'a' + 10) * weight;
|
||
}
|
||
weight *= 10;
|
||
}
|
||
return dec;
|
||
}
|
||
|
||
// 传入十进制23 返回十六进制0x23
|
||
uint8_t dec_format_hex(uint8_t dec)
|
||
{
|
||
char buf[4];
|
||
osel_memset((uint8_t *)buf, 0, 4);
|
||
sprintf(buf, "%d", dec);
|
||
uint8_t hex = 0;
|
||
uint8_t len = strlen(buf);
|
||
for (uint8_t i = 0; i < len; i++)
|
||
{
|
||
char c = buf[i];
|
||
if (c >= '0' && c <= '9')
|
||
{
|
||
hex = hex * 16 + (c - '0');
|
||
}
|
||
else
|
||
{
|
||
continue;
|
||
}
|
||
}
|
||
return hex;
|
||
}
|
||
|
||
/**
|
||
* @brief 日期时间转时间戳
|
||
* @param {rtc_date_t} date
|
||
* @param {rtc_time_t} time
|
||
* @return {*}
|
||
* @note 年份是从2000年开始的
|
||
*/
|
||
uint32_t time2stamp(const rtc_date_t *const date, const rtc_time_t *const time)
|
||
{
|
||
uint32_t result;
|
||
uint16_t year = date->year + 2000;
|
||
// (time->hour - 0) * 3600 中的0改成8是北京时间的时区
|
||
result = (year - 1970) * 365 * 24 * 3600 + (_month_days[date->month - 1] + date->day - 1) * 24 * 3600 + (time->hour - 0) * 3600 + time->minute * 60 + time->second;
|
||
result += (date->month > 2 && (year % 4 == 0) && (year % 100 != 0 || year % 400 == 0)) * 24 * 3600; // 闰月
|
||
year -= 1969;
|
||
result += (year / 4 - year / 100 + year / 400) * 24 * 3600; // 闰年
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* @brief 时间戳转日期时间
|
||
* @param {uint32_t} stamp
|
||
* @param {rtc_date_t} *date
|
||
* @param {rtc_time_t} *time
|
||
* @return {*}
|
||
* @note
|
||
*/
|
||
void stamp2time(uint32_t stamp, rtc_date_t *date, rtc_time_t *time)
|
||
{
|
||
uint32_t days;
|
||
uint16_t leap_num;
|
||
|
||
time->second = stamp % 60;
|
||
stamp /= 60; // 获取分
|
||
time->minute = stamp % 60;
|
||
// stamp += 8 * 60; // 不需要加8小时
|
||
stamp /= 60; // 获取小时
|
||
time->hour = stamp % 24;
|
||
days = stamp / 24;
|
||
leap_num = (days + 365) / 1461;
|
||
if (((days + 366) % 1461) == 0)
|
||
{
|
||
date->year = (days / 366) + 1970 - 2000;
|
||
date->month = 12;
|
||
date->day = 31;
|
||
}
|
||
else
|
||
{
|
||
days -= leap_num;
|
||
date->year = (days / 365) + 1970 - 2000;
|
||
days %= 365;
|
||
days += 1;
|
||
if (((date->year % 4) == 0) && (days == 60))
|
||
{
|
||
date->month = 2;
|
||
date->day = 29;
|
||
}
|
||
else
|
||
{
|
||
if (((date->year % 4) == 0) && (days > 60))
|
||
--days;
|
||
for (date->month = 0; _days[date->month] < days; date->month++)
|
||
{
|
||
days -= _days[date->month];
|
||
}
|
||
++date->month;
|
||
date->day = days;
|
||
}
|
||
}
|
||
}
|
||
|
||
/**************************排序**************************/
|
||
static void swap(uint16_t *a, uint16_t *b)
|
||
{
|
||
uint16_t t = *a;
|
||
*a = *b;
|
||
*b = t;
|
||
}
|
||
|
||
static int32_t partition(uint16_t arr[], int32_t low, int32_t high)
|
||
{
|
||
uint16_t pivot = arr[high];
|
||
int32_t i = (low - 1);
|
||
for (int32_t j = low; j <= high - 1; j++)
|
||
{
|
||
if (arr[j] < pivot)
|
||
{
|
||
i++;
|
||
swap(&arr[i], &arr[j]);
|
||
}
|
||
}
|
||
swap(&arr[i + 1], &arr[high]);
|
||
return (i + 1);
|
||
}
|
||
|
||
// 快速排序
|
||
void quicksort(uint16_t arr[], int32_t low, int32_t high)
|
||
{
|
||
if (low < high)
|
||
{
|
||
int32_t pi = partition(arr, low, high);
|
||
quicksort(arr, low, pi - 1);
|
||
quicksort(arr, pi + 1, high);
|
||
}
|
||
}
|
||
|
||
// 插入排序 数据集小的时候效率高
|
||
void insertion_sort(uint16_t arr[], uint16_t n)
|
||
{
|
||
uint16_t i, j;
|
||
uint16_t key;
|
||
for (i = 1; i < n; i++)
|
||
{
|
||
key = arr[i];
|
||
j = i;
|
||
|
||
// 将大于key的元素向后移动一个位置
|
||
while (j > 0 && arr[j - 1] > key)
|
||
{
|
||
arr[j] = arr[j - 1];
|
||
j = j - 1;
|
||
}
|
||
arr[j] = key;
|
||
}
|
||
}
|
||
|
||
/**************************线性拟合**************************/
|
||
/**
|
||
* @brief 计算平均值
|
||
*
|
||
* 根据给定的点集和指示标志,计算 X 或 Y 坐标的平均值。
|
||
*
|
||
* @param points 点集指针
|
||
* @param count 点集数量
|
||
* @param isX 是否计算 X 坐标的平均值,为 1 则计算 X 坐标的平均值,为 0 则计算 Y 坐标的平均值
|
||
*
|
||
* @return 返回计算得到的平均值
|
||
*/
|
||
static float32 average(const point_t *points, int32_t count, int32_t isX)
|
||
{
|
||
float32 sum = 0.0f;
|
||
for (int32_t i = 0; i < count; ++i)
|
||
{
|
||
sum += isX ? points[i].x : points[i].y;
|
||
}
|
||
return sum / count;
|
||
}
|
||
|
||
/**
|
||
* @brief 计算线性回归函数值
|
||
*
|
||
* 根据给定的 x 值和线性函数参数,计算并返回线性回归函数的值。 y = a * x + b
|
||
*
|
||
* @param x 输入的 x 值
|
||
* @param param 线性函数参数结构体,包含斜率 a 和截距 b
|
||
*
|
||
* @return 线性回归函数的值
|
||
*/
|
||
static float32 calculate_linear_regression_func(float32 x, linear_func_param_t param)
|
||
{
|
||
return param.a * x + param.b;
|
||
}
|
||
|
||
/**
|
||
* @brief 计算线性回归参数
|
||
*
|
||
* 根据给定的数据点集合,计算线性回归的参数。
|
||
*
|
||
* @param points 数据点集合指针
|
||
* @param count 数据点数量
|
||
*
|
||
* @return 线性回归参数结构体
|
||
*
|
||
* @note 如果数据点数量少于2,则会输出错误日志并断言失败
|
||
*/
|
||
linear_func_param_t calculate_linear_regression(const point_t *points, int32_t count)
|
||
{
|
||
if (count < 2)
|
||
{
|
||
LOG_PRINT("参数points至少需要两组数据\n");
|
||
DBG_ASSERT(FALSE __DBG_LINE);
|
||
}
|
||
|
||
float32 xAverage = average(points, count, 1);
|
||
float32 yAverage = average(points, count, 0);
|
||
float32 sumXY = 0.0, sumXX = 0.0;
|
||
|
||
for (int32_t i = 0; i < count; ++i)
|
||
{
|
||
float32 dX = points[i].x - xAverage;
|
||
float32 dY = points[i].y - yAverage;
|
||
sumXY += dX * dY;
|
||
sumXX += dX * dX;
|
||
}
|
||
|
||
linear_func_param_t result;
|
||
result.a = sumXY / sumXX;
|
||
result.b = yAverage - result.a * xAverage;
|
||
return result;
|
||
}
|
||
|
||
// 求线性度 公式 dYmax / Y * 100%
|
||
float32 get_linearity_value(const point_t *points, int32_t count, linear_func_param_t param)
|
||
{
|
||
float32 dYMax = 0.0f;
|
||
for (int32_t i = 0; i < count; ++i)
|
||
{
|
||
float32 y = calculate_linear_regression_func(points[i].x, param);
|
||
float32 dY = ABS(y - points[i].y);
|
||
if (dY > dYMax)
|
||
dYMax = dY;
|
||
}
|
||
|
||
float32 yFirst = calculate_linear_regression_func(points[0].x, param);
|
||
float32 yLast = calculate_linear_regression_func(points[count - 1].x, param);
|
||
return dYMax / ABS(yLast - yFirst) * 100;
|
||
}
|
||
|
||
/**
|
||
// 示例使用
|
||
int main()
|
||
{
|
||
point_t points[] = {{1, 2}, {2, 3}, {3, 4}}; // 示例点数组
|
||
int count = sizeof(points) / sizeof(points[0]);
|
||
linear_func_param_t param = calculate_linear_regression(points, count);
|
||
printf("Linear Regression: y = %f * x + %f\n", param.a, param.b);
|
||
|
||
double linearity = get_linearity_value(points, count, param);
|
||
printf("Linearity Value: %f%%\n", linearity);
|
||
|
||
return 0;
|
||
}
|
||
*/
|
||
|
||
/**************************线性拟合**************************/
|