ESP32 Arduino日出和日落计算偏移一个半小时(C/C++)



我在根据当前时间和经纬度数据计算太阳周期时遇到了问题。

我使用的是带有Arduino框架的ESP32。我的日照时间总是错开一个或一个半小时。我从一个外部API获取了地理位置数据,它将为我提供纬度和经度。这在某种程度上是可靠的,它使我处于我所在的同一个城市。

当前时间来自NTP服务器,如果没有NTP,我的ESP32旁边有一个RTC模块,我从那里得到时间。这样做的准确率为98%。

计算也是如此。我所在国家/地区的当前dst偏移量为0。如果现在是+1小时,计算结果几乎是正确的。(几分钟后(

所以我开始从API请求我的地理数据,如下所示:

void gSystem::checkConfigGeoData(){
if( hsh_timeSystem.timeIsOk() && gotGeoData && hsh_fileSystem.config.lastGeoDay != hsh_timeSystem.getDayOfWeek() ){
gotGeoData          = false;
sunTimesCalculated  = false;
geoAttempts         = 0;
}
}
void gSystem::getInfo(){
checkConfigGeoData();
if( !gotGeoData && hsh_networkSystem.isConnected() && hsh_networkSystem.connectionMode != AP_CONNECTION && 
( geoAttempts < MAX_GEO_ATTEMPT_COUNT ) && ( millis() - lastGeoAttemptMS >= GEO_ATTEMPT_RETRY_MS ) ){

lastGeoAttemptMS = millis();
if( hsh_fileSystem.config.latitude != 0 && hsh_timeSystem.getUnixSec() - hsh_fileSystem.config.lastGeoEpoch <= GEO_EPOCH_SEC_DAY ){
#if GEO_DEBUG_MODE
Serial.printf("Geo data has been requested less then a day. Abort request...n");
#endif
gotGeoData          = true;
sunTimesCalculated  = false;
geoAttempts         = MAX_GEO_ATTEMPT_COUNT + 1;
return;
}
geoAttempts++;
HTTPClient http;
http.begin( ipStackURL ); // url is coming from there: http://api.ipstack.com/
int httpResponseCode = http.GET();
if (httpResponseCode > 0) {
DynamicJsonDocument doc(GEO_RESPONSE_JSON_SIZE);
DeserializationError error = deserializeJson(doc,http.getString());
if(error){
char errorMsg[128];
sprintf(errorMsg,"geolocateAPI response deserialization failed with error: %s",error.c_str());
hsh_fileSystem.logToFile(errorMsg,"error","gSystem");
#if GEO_DEBUG_MODE
Serial.println( errorMsg );
#endif
}else{
if( doc["success"] == false ){
char errorMsg[300];
sprintf( errorMsg,"Error: %s",doc["error"]["info"].as<const char*>() );
hsh_fileSystem.logToFile(errorMsg,"error","gSystem" );
#if GEO_DEBUG_MODE
Serial.println( errorMsg );
#endif
}else{
sunTimesCalculated  = false;
gotGeoData          = true;
hsh_fileSystem.config.publicIP.fromString(doc["ip"].as<const char*>());
strncpy(hsh_fileSystem.config.city, doc["city"], sizeof(hsh_fileSystem.config.city));
hsh_fileSystem.config.latitude    = doc["latitude"].as<float>();
hsh_fileSystem.config.longitude   = doc["longitude"].as<float>();
hsh_fileSystem.config.lastGeoDay  = hsh_timeSystem.getDayOfWeek();
hsh_fileSystem.config.lastGeoEpoch = hsh_timeSystem.getUnixSec();
#if GEO_DEBUG_MODE
Serial.printf("n**** GOT GEO DATA ****n");
Serial.printf("GS - City: %sn",hsh_fileSystem.config.city);
Serial.printf("GS - Public IP: %sn",hsh_fileSystem.config.publicIP.toString().c_str());
Serial.printf("GS - Latitude: %fn",hsh_fileSystem.config.latitude);
Serial.printf("GS - Longitude: %fnn",hsh_fileSystem.config.longitude);
#endif
hsh_fileSystem.makeConfig();
geoAttempts = 0;
}
}
}
}
}

如果我得到了数据,并且我的时间还可以,我就开始用这个函数计算日出和日落:

void gSystem::startCalculateSunTimes(){
if( hsh_timeSystem.timeIsOk() && gotGeoData && !sunTimesCalculated ){
boolean timesIsOk = true;
time_t seconds;
time_t tseconds;
struct tm *ptm = NULL;
struct tm tm;
int year    = hsh_timeSystem.getYear(),
month   = hsh_timeSystem.getMonth(),
day     = hsh_timeSystem.getDayOfMonth(),
hour    = hsh_timeSystem.getHour(),
min     = hsh_timeSystem.getMinute(),
sec     = hsh_timeSystem.getSecond();
float JD = calcJD(year, month, day);
tm.tm_year = year - 1900;
tm.tm_mon  = month - 1;
tm.tm_mday = day;
tm.tm_hour = hour; //0;
tm.tm_min  = min; //0;
tm.tm_sec  = sec; //0;
#if GEO_DEBUG_MODE
Serial.printf("n**** START CALCULATE SUN TIMES ****n");
Serial.printf("GS - JD: %fn",JD);
Serial.printf("GS - Time: %d-%d-%d %d:%d:%dn",year,month,day,hour,min,sec);
Serial.printf("GS - Latitude: %fn",hsh_fileSystem.config.latitude);
Serial.printf("GS - Longitude: %fn",hsh_fileSystem.config.longitude);
#endif
seconds = mktime(&tm);
int delta;
ptm = gmtime(&seconds);
delta = ptm->tm_hour;
tseconds = seconds;
seconds = seconds + calcSunriseUTC(JD, hsh_fileSystem.config.latitude, -hsh_fileSystem.config.longitude) * 60;
seconds = seconds - delta * 3600;
ptm = gmtime(&seconds);
int calculatedYear = ptm->tm_year + 1900;
if( calculatedYear == hsh_timeSystem.getYear() ){
hsh_fileSystem.config.sunRiseHour   = ptm->tm_hour + hsh_fileSystem.config.dst;
hsh_fileSystem.config.sunRiseMinute = ptm->tm_min;
}else{
timesIsOk = false;
}
seconds = tseconds;
seconds += calcSunsetUTC(JD, hsh_fileSystem.config.latitude, -hsh_fileSystem.config.longitude) * 60;
seconds = seconds - delta * 3600;
ptm = gmtime(&seconds);
calculatedYear = ptm->tm_year + 1900;
if( calculatedYear == hsh_timeSystem.getYear() ){
hsh_fileSystem.config.sunSetHour   = ptm->tm_hour + hsh_fileSystem.config.dst;
hsh_fileSystem.config.sunSetMinute = ptm->tm_min;
}else{
timesIsOk = false;
}
if(timesIsOk){
sunTimesCalculated = true;
hsh_fileSystem.makeConfig();
#if GEO_DEBUG_MODE
Serial.println("n**** Sun times calculated ****");
Serial.printf("GS - Sun Rise Info: %02d:%02dn",hsh_fileSystem.config.sunRiseHour,hsh_fileSystem.config.sunRiseMinute);
Serial.printf("GS - Sun Set Info: %02d:%02dnn",hsh_fileSystem.config.sunSetHour,hsh_fileSystem.config.sunSetMinute);
#endif
}
}
}

这两个函数位于一个任务和一个无限循环中。像这样:

void gSystemLoopTask(void* parameter) {
for (;;) {
hsh_GeoSystem.getInfo();
hsh_GeoSystem.startCalculateSunTimes();
vTaskDelay(1000);
}
}

剩下的计算在这里(我从一个开源C项目中得到(:

/* Convert degree angle to radians */
double gSystem::degToRad(double angleDeg) {
return (PI * angleDeg / 180.0);
}
double gSystem::radToDeg(double angleRad) {
return (180.0 * angleRad / PI);
}
double gSystem::calcMeanObliquityOfEcliptic(double t) {
double seconds = 21.448 - t * (46.8150 + t * (0.00059 - t * (0.001813)));
double e0 = 23.0 + (26.0 + (seconds / 60.0)) / 60.0;
return e0;  // in degrees
}
double gSystem::calcGeomMeanLongSun(double t) {
double L = 280.46646 + t * (36000.76983 + 0.0003032 * t);
while ((int)L > 360) {
L -= 360.0;
}
while (L < 0) {
L += 360.0;
}
return L;  // in degrees
}
double gSystem::calcObliquityCorrection(double t) {
double e0 = calcMeanObliquityOfEcliptic(t);
double omega = 125.04 - 1934.136 * t;
double e = e0 + 0.00256 * cos(degToRad(omega));
return e;  // in degrees
}
double gSystem::calcEccentricityEarthOrbit(double t) {
double e = 0.016708634 - t * (0.000042037 + 0.0000001267 * t);
return e;  // unitless
}
double gSystem::calcGeomMeanAnomalySun(double t) {
double M = 357.52911 + t * (35999.05029 - 0.0001537 * t);
return M;  // in degrees
}
double gSystem::calcEquationOfTime(double t) {
double epsilon = calcObliquityCorrection(t);
double l0 = calcGeomMeanLongSun(t);
double e = calcEccentricityEarthOrbit(t);
double m = calcGeomMeanAnomalySun(t);
double y = tan(degToRad(epsilon) / 2.0);
y *= y;
double sin2l0 = sin(2.0 * degToRad(l0));
double sinm = sin(degToRad(m));
double cos2l0 = cos(2.0 * degToRad(l0));
double sin4l0 = sin(4.0 * degToRad(l0));
double sin2m = sin(2.0 * degToRad(m));
double Etime = y * sin2l0 - 2.0 * e * sinm + 4.0 * e * y * sinm * cos2l0 - 0.5 * y * y * sin4l0 - 1.25 * e * e * sin2m;
return radToDeg(Etime) * 4.0;  // in minutes of time
}
double gSystem::calcTimeJulianCent(double jd) {
double T = (jd - 2451545.0) / 36525.0;
return T;
}
double gSystem::calcSunTrueLong(double t) {
double l0 = calcGeomMeanLongSun(t);
double c = calcSunEqOfCenter(t);
double O = l0 + c;
return O;  // in degrees
}
double gSystem::calcSunApparentLong(double t) {
double o = calcSunTrueLong(t);
double omega = 125.04 - 1934.136 * t;
double lambda = o - 0.00569 - 0.00478 * sin(degToRad(omega));
return lambda;  // in degrees
}
double gSystem::calcSunDeclination(double t) {
double e = calcObliquityCorrection(t);
double lambda = calcSunApparentLong(t);
double sint = sin(degToRad(e)) * sin(degToRad(lambda));
double theta = radToDeg(asin(sint));
return theta;  // in degrees
}
double gSystem::calcHourAngleSunrise(double lat, double solarDec) {
double latRad = degToRad(lat);
double sdRad = degToRad(solarDec);
double HA = (acos(cos(degToRad(90.833)) / (cos(latRad) * cos(sdRad)) - tan(latRad) * tan(sdRad)));
return HA;  // in radians
}
double gSystem::calcHourAngleSunset(double lat, double solarDec) {
double latRad = degToRad(lat);
double sdRad = degToRad(solarDec);
double HA = (acos(cos(degToRad(90.833)) / (cos(latRad) * cos(sdRad)) - tan(latRad) * tan(sdRad)));
return -HA;  // in radians
}
double gSystem::calcJD(int year, int month, int day) {
if (month <= 2) {
year -= 1;
month += 12;
}
int A = floor(year / 100);
int B = 2 - A + floor(A / 4);
double JD = floor(365.25 * (year + 4716)) + floor(30.6001 * (month + 1)) + day + B - 1524.5;
return JD;
}
double gSystem::calcJDFromJulianCent(double t) {
double JD = t * 36525.0 + 2451545.0;
return JD;
}
double gSystem::calcSunEqOfCenter(double t) {
double m = calcGeomMeanAnomalySun(t);
double mrad = degToRad(m);
double sinm = sin(mrad);
double sin2m = sin(mrad + mrad);
double sin3m = sin(mrad + mrad + mrad);
double C = sinm * (1.914602 - t * (0.004817 + 0.000014 * t)) + sin2m * (0.019993 - 0.000101 * t) + sin3m * 0.000289;
return C;  // in degrees
}
double gSystem::calcSunriseUTC(double JD, double latitude, double longitude) {
double t = calcTimeJulianCent(JD);
double eqTime = calcEquationOfTime(t);
double solarDec = calcSunDeclination(t);
double hourAngle = calcHourAngleSunrise(latitude, solarDec);
double delta = longitude - radToDeg(hourAngle);
double timeDiff = 4 * delta;               // in minutes of time
double timeUTC = 720 + timeDiff - eqTime;  // in minutes
double newt = calcTimeJulianCent(calcJDFromJulianCent(t) + timeUTC / 1440.0);
eqTime = calcEquationOfTime(newt);
solarDec = calcSunDeclination(newt);
hourAngle = calcHourAngleSunrise(latitude, solarDec);
delta = longitude - radToDeg(hourAngle);
timeDiff = 4 * delta;
timeUTC = 720 + timeDiff - eqTime;
return timeUTC;
}
double gSystem::calcSunsetUTC(double JD, double latitude, double longitude) {
double t = calcTimeJulianCent(JD);
double eqTime = calcEquationOfTime(t);
double solarDec = calcSunDeclination(t);
double hourAngle = calcHourAngleSunset(latitude, solarDec);
double delta = longitude - radToDeg(hourAngle);
double timeDiff = 4 * delta;               // in minutes of time
double timeUTC = 720 + timeDiff - eqTime;  // in minutes
double newt = calcTimeJulianCent(calcJDFromJulianCent(t) + timeUTC / 1440.0);
eqTime = calcEquationOfTime(newt);
solarDec = calcSunDeclination(newt);
hourAngle = calcHourAngleSunset(latitude, solarDec);
delta = longitude - radToDeg(hourAngle);
timeDiff = 4 * delta;
timeUTC = 720 + timeDiff - eqTime;
return timeUTC;
}

这个片段错误地计算了以下内容:

**** START CALCULATE SUN TIMES ****
GS - JD: 2459676.500000
GS - Time: 2022-4-7 9:38:47
GS - Latitude: 47.943272
GS - Longitude: 22.316900
**** Sun times calculated ****
GS - Sun Rise Info: 04:36
GS - Sun Set Info: 17:47

根据谷歌关于这个纬度和经度的信息,应该是:

**** Sun times FROM GOOGLE ****
Sun Rise Info: 05:55
Sun Set Info: 19:09

我做错了什么?逻辑运行良好,但差异很大。

我用esp32制作了一个日出程序,使用wifi获取时间和日期,并使用timelord.h库进行日落和日出。在我的程序中我使用年份=tm.tm_year+1900;月份=tm.tm_mon+1;

祝好运

相关内容

  • 没有找到相关文章

最新更新