📄 solartimer.cpp
字号:
/* * Roadnav * SolarTimer.cpp * * Copyright (c) 2004 - 2007 Richard L. Lynch <rllynch@users.sourceforge.net> * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public License * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ ///////////////////////////////////////////////////////////////////////////////// \file////// Solar Timer - A class that fires events at sunrise and sunset///////////////////////////////////////////////////////////////////////////////// #ifdef HAVE_CONFIG_H# include <config.h>#endif #ifdef _MSC_VER#pragma warning(disable: 4786)#pragma warning(disable: 4800)#endif #ifdef HAVE_MATH_H#include <math.h>#endif#include <wx/wx.h> #include "SolarTimer.h"#include "libroadnav/MapSupport.h"#include "libroadnav/UnitConversion.h"template <class T>void ForceInRange(const T& min,T& value,const T& max){ if ( value < min ) value += max; else if ( max <= value ) value -= max;} #define TIMERID 1000 IMPLEMENT_DYNAMIC_CLASS(SolarEvent, wxEvent) DEFINE_EVENT_TYPE(wxEVT_SOLAR) IMPLEMENT_DYNAMIC_CLASS(wxTimeChangeEvent,wxEvent)DEFINE_EVENT_TYPE(wxEVT_TIMECHANGE) //////////////////////////////////////////////////////////////////////////////////// SolarTimer event table/////////////////////////////////////////////////////////////////////////////////BEGIN_EVENT_TABLE(SolarTimer, wxEvtHandler) EVT_TIMER(TIMERID, SolarTimer::OnTimer) EVT_TIMECHANGE(SolarTimer::OnTimeChange)END_EVENT_TABLE() //////////////////////////////////////////////////////////////////////////////////// Constructor////////////////////////////////////////////////////////////////////////////////SolarTimer::SolarTimer() { Init();} //////////////////////////////////////////////////////////////////////////////////// Constructor////////////////////////////////////////////////////////////////////////////////SolarTimer::SolarTimer( wxEvtHandler * owner, double fLong, double fLat, EventType type) { Init(); SetOwner(owner, fLong, fLat, type);} //////////////////////////////////////////////////////////////////////////////////// Constructor////////////////////////////////////////////////////////////////////////////////void SolarTimer::SetOwner(wxEvtHandler * owner, double fLong, double fLat, EventType type) { m_pOwner = owner; m_EventType = type; SetPosition(fLong, fLat);} //////////////////////////////////////////////////////////////////////////////////// Sets the position for which sunrise/sunset events are to occur////////////////////////////////////////////////////////////////////////////////void SolarTimer::SetPosition(double fLong, double fLat) { if (m_fLong != fLong || m_fLat != fLat) { m_fLong = fLong; m_fLat = fLat; ComputeSunriset(); NotifyIfNeeded(); }} //////////////////////////////////////////////////////////////////////////////////// Starts the solar timer////////////////////////////////////////////////////////////////////////////////void SolarTimer::Start() { if (!m_Timer.IsRunning()) { // start a one time timer that will get us to // the next whole minute wxDateTime now = wxDateTime::Now(); int sec = now.GetSecond(); NotifyIfNeeded(); m_Timer.Start((60 - sec) * 1000, true); }} //////////////////////////////////////////////////////////////////////////////////// Stops the solar timer////////////////////////////////////////////////////////////////////////////////void SolarTimer::Stop() { m_Timer.Stop();} //////////////////////////////////////////////////////////////////////////////////// Returns true if it is daytime////////////////////////////////////////////////////////////////////////////////bool SolarTimer::IsDaytime() { wxDateTime now = wxDateTime::Now(); if (!Valid()) return true; return (m_Sunrise <= now && now < m_Sunset ? true : false);} //////////////////////////////////////////////////////////////////////////////////// Returns the coordinates for which sunrise/sunset are computed////////////////////////////////////////////////////////////////////////////////void SolarTimer::GetPosition(double &fLong, double &fLat) const{ fLong = m_fLong; fLat = m_fLat; } //////////////////////////////////////////////////////////////////////////////////// Returns the type of solar event////////////////////////////////////////////////////////////////////////////////SolarTimer::EventType SolarTimer::GetEventType() const{ return m_EventType;}//////////////////////////////////////////////////////////////////////////////////// Returns the sunrise time, adjusted for DST as necessary, for the/// current position.////////////////////////////////////////////////////////////////////////////////wxDateTime SolarTimer::GetSunrise() const{ return m_Sunrise;}//////////////////////////////////////////////////////////////////////////////////// Returns the sunset time, adjusted for DST as necessary, for the/// current position.////////////////////////////////////////////////////////////////////////////////wxDateTime SolarTimer::GetSunset() const{ return m_Sunset;}//////////////////////////////////////////////////////////////////////////////////// Virtual function called at sunrise and sunset. Subclasses should/// override this method.////////////////////////////////////////////////////////////////////////////////void SolarTimer::Notify() { if (!m_pOwner) return; // Time for an event.... SolarEvent event(m_bIsDaytime); event.SetEventObject(this); m_pOwner->AddPendingEvent(event);} void SolarTimer::OnTimer(wxTimerEvent & event) { if (m_Timer.IsOneShot()) { // Keep timer running on 1 minute interval m_Timer.Start(60000); } NotifyIfNeeded();}void SolarTimer::NotifyIfNeeded(){ bool bIsDaytime = IsDaytime(); if (m_bIsDaytime != bIsDaytime) { m_bIsDaytime = bIsDaytime; Notify(); } if (IsNewDay()) { m_Today = wxDateTime::Today(); ComputeSunriset(); }}void SolarTimer::OnTimeChange(wxTimeChangeEvent& event){ // The clock got changed... stop the timer and restart it Stop(); Start();}bool SolarTimer::IsNewDay() { wxDateTime today = wxDateTime::Today(); return today != m_Today;} void SolarTimer::Init() { m_pOwner = NULL; m_fLong = 0; m_fLat = 0; m_Timer.SetOwner(this, TIMERID); m_EventType = Official; m_Today = wxDateTime::Today(); SetOwner(this, 0, 0, m_EventType); m_bIsDaytime = IsDaytime(); Start();} void SolarTimer::ComputeSunriset() { wxDateTime now = wxDateTime::Now(); int year = now.GetYear(); int month = now.GetMonth() + 1; wxDateTime::wxDateTime_t day = now.GetDay(); double zenith = 90.833333333; switch (m_EventType) { case Official: zenith = 90.833333333; break; case Civil: zenith = 96.; break; case Nautical: zenith = 102.; break; case Astronomical: zenith = 108.; break; } // Compute day of year int N1 = (int) (floor((double) 275 * month / 9)); int N2 = (int) (floor((double) (month + 9) / 12)); int N3 = (int) (1 + floor((double) (year - 4 * floor((double) year / 4) + 2) / 3)); int N = N1 - (N2 * N3) + day - 30; // Convert the m_fLong to hour value and calculate an approximate time double lngHour = m_fLong / 15.; double t_rise = N + (( 6 - lngHour) / 24); double t_set = N + ((18 - lngHour) / 24); // Calculate the Sun's mean anomaly double M_rise = (0.9856 * t_rise) - 3.289; double M_set = (0.9856 * t_set) - 3.289; // Calculate the Sun's true longitude double L_rise = M_rise + (1.916 * sin(DegreesToRadians(M_rise))) + (0.020 * sin(DegreesToRadians(2 * M_rise))) + 282.634; double L_set = M_set + (1.916 * sin(DegreesToRadians(M_set))) + (0.020 * sin(DegreesToRadians(2 * M_set))) + 282.634; ForceInRange(0.,L_rise,360.); ForceInRange(0.,L_set, 360.); // Calculate the Sun's right ascension double RA_rise = RadiansToDegrees(atan(0.91764 * tan(DegreesToRadians(L_rise)))); double RA_set = RadiansToDegrees(atan(0.91764 * tan(DegreesToRadians(L_set)))); // Right ascension value needs to be in the same quadrant as L double Lquad_rise = (floor(L_rise / 90)) * 90; double Lquad_set = (floor(L_set / 90)) * 90; double RAquad_rise = (floor(RA_rise / 90)) * 90; double RAquad_set = (floor(RA_set / 90)) * 90; RA_rise += Lquad_rise - RAquad_rise; RA_set += Lquad_set - RAquad_set; // Right ascension value needs to be convered into hours RA_rise /= 15.; RA_set /= 15.; // Calculate the Sun's declination double sinDec_rise = 0.39782 * sin(DegreesToRadians(L_rise)); double cosDec_rise = cos(asin(sinDec_rise)); double sinDec_set = 0.39782 * sin(DegreesToRadians(L_set)); double cosDec_set = cos(asin(sinDec_set)); // Calculate the Sun's local hour angle double cosH_rise = (cos(DegreesToRadians(zenith)) - sinDec_rise * sin(DegreesToRadians(m_fLat))) /(cosDec_rise * cos(DegreesToRadians(m_fLat))); double cosH_set = (cos(DegreesToRadians(zenith)) - sinDec_set * sin(DegreesToRadians(m_fLat))) /(cosDec_set * cos(DegreesToRadians(m_fLat))); // Finish calcuating H and convert into hours double H_rise = 360 - RadiansToDegrees(acos(cosH_rise)); double H_set = RadiansToDegrees(acos(cosH_set)); H_rise /= 15.; H_set /= 15.; // Calculate local mean time of rising/settings double T_rise = H_rise + RA_rise - (0.06571 * t_rise) - 6.622; double T_set = H_set + RA_set - (0.06571 * t_set) - 6.622; // Adjust back to UTC double UT_rise = T_rise - lngHour; double UT_set = T_set - lngHour; // Adjust to 24 hours ForceInRange(0.,UT_rise,24.); ForceInRange(0.,UT_set, 24.); // Determine the local hour offset based on the computer clock. // UT_rise/UT_set are the sunrise/set times in universal // time at the present location. These times need to be convered to local time, // as determined by the computer's settings, (not the actual local time). // // Sunrise/set need to be computed for the present location, for the time zone the // computer is set to regardless of the actual time zone the computer is in. Assume you // arrive in New York City from Los Angeles and your computer is still set to Pacific // time. Sunset is 11:00PM UT, which is 6:00PM EST. Since your computer is still // on Pacific time, the sunset event needs to fire when the computer's clock hits 4:00PM PST // // ToGMT accounts for daylight savings time#if wxCHECK_VERSION(2, 6, 2) long local_offset = (wxDateTime::Now() - wxDateTime::Now().ToUTC()).GetHours();#else long local_offset = (wxDateTime::Now().ToGMT() - wxDateTime::Now()).GetHours();#endif double LT_rise = UT_rise + local_offset; double LT_set = UT_set + local_offset; // Adjust to 24 hours ForceInRange(0.,LT_rise,24.); ForceInRange(0.,LT_set, 24.); int h = (int) floor(LT_rise); double m = 60 * (LT_rise - h); double s = floor(60 * (m - floor(m))); m = floor(m); m_Sunrise.Set((int) h, (int) m, (int) s, 0); h = (int) floor(LT_set); m = 60 * (LT_set - h); s = floor(60 * (m - floor(m))); m = floor(m); m_Sunset.Set((int) h, (int) m, (int) s, 0);}bool SolarTimer::Valid(){ if (fabs(m_fLong) < 0.01 && fabs(m_fLat) < 0.01) return false; if (!m_Sunrise.IsValid()) return false; if (!m_Sunset.IsValid()) return false; return true;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -