From 8db2182b0546a30042bc0672cb2636fba193a731 Mon Sep 17 00:00:00 2001 From: spdis Date: Sat, 20 Sep 2025 23:24:30 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=86=9C=E5=8E=86=E8=BD=AC?= =?UTF-8?q?=E6=8D=A2=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IRReceiver.h | 101 ------------- LunarCalendarAndHolidayJudge.h | 265 ++++++++++++++++++++++----------- 2 files changed, 179 insertions(+), 187 deletions(-) delete mode 100644 IRReceiver.h diff --git a/IRReceiver.h b/IRReceiver.h deleted file mode 100644 index a08db2d..0000000 --- a/IRReceiver.h +++ /dev/null @@ -1,101 +0,0 @@ -#ifndef IR_RECEIVER_H -#define IR_RECEIVER_H - -#include -#include - -// 红外接收结果结构体 -struct IRReceiveResult { - bool success; // 是否成功接收到信号 - uint32_t protocol; // 协议类型 - uint32_t address; // 地址码 - uint32_t command; // 命令码 - uint16_t bits; // 数据位数 - uint16_t* rawData; // 原始数据指针 - size_t rawLength; // 原始数据长度 -}; - -class IRReceiver { -private: - static uint8_t receivePin; - static bool initialized; - static uint16_t rawBuffer[RAW_BUFFER_LENGTH]; - -public: - // 初始化红外接收器 - static void init(uint8_t pin = 15); - - // 读取红外信号直到信号结束,返回读取结果 - static IRReceiveResult readIRSignal(uint32_t timeoutMs = 5000); - - // 释放资源 - static void cleanup(); -}; - -// 静态成员变量定义 -uint8_t IRReceiver::receivePin = 15; -bool IRReceiver::initialized = false; -uint16_t IRReceiver::rawBuffer[RAW_BUFFER_LENGTH]; - -// 初始化红外接收器 -void IRReceiver::init(uint8_t pin) { - receivePin = pin; - IrReceiver.begin(receivePin, ENABLE_LED_FEEDBACK); - initialized = true; -} - -// 读取红外信号直到信号结束,返回读取结果 -IRReceiveResult IRReceiver::readIRSignal(uint32_t timeoutMs) { - IRReceiveResult result = {false, 0, 0, 0, 0, nullptr, 0}; - - if (!initialized) { - return result; - } - - unsigned long startTime = millis(); - - // 等待接收到红外信号 - while (!IrReceiver.decode()) { - if (millis() - startTime > timeoutMs) { - return result; // 超时返回失败结果 - } - delay(1); - } - - // 成功接收到信号 - result.success = true; - result.protocol = IrReceiver.decodedIRData.protocol; - result.address = IrReceiver.decodedIRData.address; - result.command = IrReceiver.decodedIRData.command; - result.bits = IrReceiver.decodedIRData.numberOfBits; - - // 复制原始数据到缓冲区 - if (IrReceiver.decodedIRData.rawDataPtr != nullptr) { - result.rawLength = IrReceiver.decodedIRData.rawlen; - // 确保不超过缓冲区大小 - if (result.rawLength > RAW_BUFFER_LENGTH) { - result.rawLength = RAW_BUFFER_LENGTH; - } - - // 复制原始数据 - for (size_t i = 0; i < result.rawLength; i++) { - rawBuffer[i] = IrReceiver.decodedIRData.rawDataPtr->rawbuf[i]; - } - result.rawData = rawBuffer; - } - - // 准备接收下一个信号 - IrReceiver.resume(); - - return result; -} - -// 释放资源 -void IRReceiver::cleanup() { - if (initialized) { - IrReceiver.stop(); - initialized = false; - } -} - -#endif \ No newline at end of file diff --git a/LunarCalendarAndHolidayJudge.h b/LunarCalendarAndHolidayJudge.h index 1ac696a..11a0fe4 100644 --- a/LunarCalendarAndHolidayJudge.h +++ b/LunarCalendarAndHolidayJudge.h @@ -4,6 +4,13 @@ #include #include +struct LunarDate { + int year; + int month; + int day; + bool isLeapMonth; +}; + class LunarCalendar { private: // 农历数据表 (1900-2100年) @@ -20,6 +27,10 @@ private: static int getDaysInMonth(int year, int month, bool isLeap = false); static bool isLeapYear(int year); static int getDayOfYear(int year, int month, int day); + static uint32_t calcSolarDateInterval(int year1, int month1, int day1, int year2, int month2, int day2); + static bool hasLeapMonth(int solarYear, int& leapMonth, bool& leapIs30Days); + static int getLunarMonthDays(int solarYear, int lunarMonth); + static bool calcLunarFromInterval(uint32_t dateInterval, LunarDate& lunar); static void calculateLunar(int solarYear, int solarMonth, int solarDay, int& lunarYear, int& lunarMonth, int& lunarDay, bool& isLeap); @@ -38,28 +49,29 @@ public: // 农历数据表 (1900-2100年,每年用32位表示) // 前4位表示闰月月份,后12位表示每月天数(0=29天,1=30天) +// 数据来源:CSDN Tyrion.Mon,经过验证的标准农历数据 const uint32_t LunarCalendar::lunarInfo[] = { - 0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, - 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, - 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, - 0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, - 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, - 0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, - 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, - 0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, - 0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, - 0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x055c0, 0x0ab60, 0x096d5, 0x092e0, - 0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, - 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, - 0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, - 0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, - 0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, - 0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, - 0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, - 0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, - 0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, - 0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, - 0x0d520 + 0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, //1900-1909 + 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, //1910-1919 + 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, //1920-1929 + 0x06566, 0x0d4a0, 0x0ea50, 0x16a95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, //1930-1939 + 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, //1940-1949 + 0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, //1950-1959 + 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, //1960-1969 + 0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, //1970-1979 + 0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, //1980-1989 + 0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, //1990-1999 + 0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, //2000-2009 + 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, //2010-2019 + 0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, //2020-2029 + 0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, //2030-2039 + 0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, //2040-2049 + 0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06aa0, 0x1a6c4, 0x0aae0, //2050-2059 + 0x092e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, //2060-2069 + 0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, //2070-2079 + 0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, //2080-2089 + 0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, //2090-2099 + 0x0d520 //2100 }; // 农历月份名称 @@ -122,10 +134,7 @@ int LunarCalendar::getDaysInMonth(int year, int month, bool isLeap) { } } -// 判断公历年是否为闰年 -bool LunarCalendar::isLeapYear(int year) { - return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); -} + // 获取公历日期在当年的天数 int LunarCalendar::getDayOfYear(int year, int month, int day) { @@ -139,74 +148,158 @@ int LunarCalendar::getDayOfYear(int year, int month, int day) { return dayOfYear; } -// 核心转换算法 +// 计算公历年份是否为闰年 +bool LunarCalendar::isLeapYear(int year) { + return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0); +} + +// 计算两个公历日期之间的天数间隔 +uint32_t LunarCalendar::calcSolarDateInterval(int year1, int month1, int day1, int year2, int month2, int day2) { + uint8_t monthDays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + uint32_t totalDays1 = 0; + uint32_t totalDays2 = 0; + uint32_t totalDays = 0; + + // 计算第一个日期从年初到该日期的天数 + monthDays[1] = (isLeapYear(year1) ? 29 : 28); + for (int i = 1; i < month1; i++) { + totalDays1 += monthDays[i - 1]; + } + totalDays1 += day1; + + // 计算第二个日期从年初到该日期的天数 + monthDays[1] = (isLeapYear(year2) ? 29 : 28); + for (int i = 1; i < month2; i++) { + totalDays2 += monthDays[i - 1]; + } + totalDays2 += day2; + + // 如果是同一年 + if (year1 == year2) { + totalDays = totalDays2 - totalDays1; + } else { + // 计算两年之间的天数 + for (int i = year1 + 1; i < year2; i++) { + totalDays += (isLeapYear(i) ? 366 : 365); + } + totalDays += ((isLeapYear(year1) ? 366 : 365) - totalDays1 + totalDays2); + } + + return totalDays; +} + +// 计算某农历年是否有闰月 +bool LunarCalendar::hasLeapMonth(int solarYear, int& leapMonth, bool& leapIs30Days) { + if (solarYear < 1900 || solarYear > 2100) return false; + + uint32_t info = lunarInfo[solarYear - 1900]; + leapMonth = info & 0x0000000f; // 低4位表示闰月月份 + + if (leapMonth == 0 || leapMonth > 12) { + return false; + } + + leapIs30Days = (info >> 16) & 0x00000001; // bit16表示闰月大小 + return true; +} + +// 计算某农历月的天数 +int LunarCalendar::getLunarMonthDays(int solarYear, int lunarMonth) { + if (solarYear < 1900 || solarYear > 2100) return -1; + if (lunarMonth < 1 || lunarMonth > 12) return -1; + + uint32_t info = lunarInfo[solarYear - 1900]; + // 农历1月对应bit15,2月对应bit14,...,12月对应bit4 + if ((info >> ((12 - lunarMonth) + 4)) & 0x00000001) { + return 30; + } else { + return 29; + } +} + +// 根据天数间隔计算农历日期 +bool LunarCalendar::calcLunarFromInterval(uint32_t dateInterval, LunarDate& lunar) { + int solarYear = 1900; + uint32_t tempInterval = 0; + + // 初始化农历日期 (1900年正月初一) + lunar.year = 1900; + lunar.month = 1; + lunar.day = 1; + lunar.isLeapMonth = false; + + while (true) { + int leapMonth = 0; + bool leapIs30Days = false; + bool hasLeap = hasLeapMonth(solarYear, leapMonth, leapIs30Days); + + // 如果当前月是闰月 + if (lunar.isLeapMonth) { + tempInterval += leapIs30Days ? 30 : 29; + } else { + // 当前月是正常月 + tempInterval += getLunarMonthDays(solarYear, lunar.month); + } + + // 如果总天数已达到目标 + if (tempInterval >= dateInterval) { + if (lunar.isLeapMonth) { + lunar.day = (leapIs30Days ? 30 : 29) - (tempInterval - dateInterval); + } else { + lunar.day = getLunarMonthDays(solarYear, lunar.month) - (tempInterval - dateInterval); + } + return true; + } + + // 处理月份递增 + if (hasLeap && !lunar.isLeapMonth && leapMonth == lunar.month) { + // 进入闰月 + lunar.isLeapMonth = true; + } else { + // 进入下一个月 + lunar.isLeapMonth = false; + lunar.month++; + + // 年份递增 + if (lunar.month > 12) { + solarYear++; + if (solarYear > 2100) return false; + lunar.year = solarYear; + lunar.month = 1; + } + } + } +} + +// 核心转换算法 - 基于标准农历算法重新实现 void LunarCalendar::calculateLunar(int solarYear, int solarMonth, int solarDay, int& lunarYear, int& lunarMonth, int& lunarDay, bool& isLeap) { - // 1900年1月31日为农历1900年正月初一 - int baseYear = 1900; - int baseMonth = 1; - int baseDay = 31; - - // 计算距离基准日期的天数 - int offset = 0; - - // 计算年份差 - for (int i = baseYear; i < solarYear; i++) { - offset += isLeapYear(i) ? 366 : 365; + // 边界检查 + if (solarYear < 1900 || solarYear > 2100) { + lunarYear = solarYear; + lunarMonth = 1; + lunarDay = 1; + isLeap = false; + return; } - // 加上当年已过天数 - offset += getDayOfYear(solarYear, solarMonth, solarDay) - getDayOfYear(baseYear, baseMonth, baseDay); + // 计算与基准日期(1900年1月31日,农历正月初一)的天数差 + uint32_t interval = calcSolarDateInterval(1900, 1, 31, solarYear, solarMonth, solarDay) + 1; - // 计算农历日期 - lunarYear = baseYear; - int daysInLunarYear = getDaysInYear(lunarYear); - - while (offset >= daysInLunarYear) { - offset -= daysInLunarYear; - lunarYear++; - daysInLunarYear = getDaysInYear(lunarYear); + // 根据天数间隔计算农历日期 + LunarDate result; + if (!calcLunarFromInterval(interval, result)) { + lunarYear = solarYear; + lunarMonth = 1; + lunarDay = 1; + isLeap = false; + return; } - // 计算月份 - lunarMonth = 1; - isLeap = false; - int leapMonth = getLeapMonth(lunarYear); - - while (offset > 0 && lunarMonth <= 12) { - int daysInCurrentMonth; - - if (lunarMonth == leapMonth && !isLeap && leapMonth > 0) { - // 闰月 - daysInCurrentMonth = getDaysInMonth(lunarYear, lunarMonth, true); - if (offset >= daysInCurrentMonth) { - offset -= daysInCurrentMonth; - isLeap = true; - } else { - isLeap = true; - break; - } - } - - // 正常月份 - daysInCurrentMonth = getDaysInMonth(lunarYear, lunarMonth, false); - if (offset >= daysInCurrentMonth) { - offset -= daysInCurrentMonth; - if (isLeap) { - isLeap = false; - } else { - lunarMonth++; - } - } else { - break; - } - } - - // 确保月份在有效范围内 - if (lunarMonth > 12) lunarMonth = 12; - if (lunarMonth < 1) lunarMonth = 1; - - lunarDay = offset + 1; + lunarYear = result.year; + lunarMonth = result.month; + lunarDay = result.day; + isLeap = result.isLeapMonth; } // 主要转换函数