Перенес на ардуино код из книги Оливер Монтенбрук, Томас Пфлегер "Астрономия на персональном компьютере", касающийся рассчета времени восхода и захода Луны и Солнца для отображения на дисплее метеостанции. Получается отклонение от реальных данных до двух минут. Возникает вопрос. Это может быть погрешность вычислений с плавающей точкой такая большая, или продолжать искать ошибку? Код, собранный на ПК дает правильные результаты. Вроде переносил все практически без изменений (разве только заменил #include <cmath> на #include <math.h> и выкинул весь вывод в cerr). Разрядность int вроде влиять не должна, поскольку когда дело доходит до целочисленных значений, они явно не выходят за границу.
Не знаю, уместно ли публиковать здесь код из диска-приложения к книге. Я-то не против, но защитники авторских прав не дремлют. Вот таким образом я использую код из книги. Код (Text): void GetInput ( int year, int month, int day, double& MJD, double& lambda, double& phi, double& zone) { lambda*=Rad; phi*=Rad; zone/=24.0; MJD = Mjd(year,month,day) - zone; } int LTimeToReal(double t) { double HRound; int H, M; double S; HRound=floor(60.0*t+0.5)/60.0+0.00001; DMS (HRound, H, M, S); return(H*60+M); } void sunsetrise(int &T_Rise, int &T_Set, bool &rise, bool &sett, int year, int month, int day, double longitude, double latitude, int tz) { bool above; double start_date; double lambda=longitude; double phi=latitude; double zone=tz; double LT_Rise, LT_Set; GetInput(year,month,day,start_date,lambda,phi,zone); FindEvents ( (enEvent)1, start_date, lambda, phi, LT_Rise, LT_Set, rise, sett, above ); T_Rise=LTimeToReal(LT_Rise); T_Set=LTimeToReal(LT_Set); } void moonsetrise(int &T_Rise, int &T_Set, bool &rise, bool &sett, int year, int month, int day, double longitude, double latitude, int tz) { bool above; double start_date; double lambda=longitude; double phi=latitude; double zone=tz; double LT_Rise, LT_Set; GetInput(year,month,day,start_date,lambda,phi,zone); FindEvents ( (enEvent)0, start_date, lambda, phi, LT_Rise, LT_Set, rise, sett, above ); T_Rise=LTimeToReal(LT_Rise); T_Set=LTimeToReal(LT_Set); } В программе вызываются две последние функции, которые возвращают в параметрах два целых : T_Rise и T_Set и два булевских: rise и sett значения. Целые значения - это время восхода и захода в минутах с начала суток. булевские показывают, был ли этот самый восход или заход. Остальное без изменений (за исключением косметической правки, о которой ранее упоминал).
http://arduino.cc/en/Reference/Double Здесь пишут что Double на всех Arduino (кроме Arduino Due) занимает памяти как обычный float - 4 bytes, вместо должных 8(64bit). Поэтому чисто физически Double не может сохранять большую точность. Проблема в том что точно измерить точность сложно, ибо зависит от значений переменных и применяемых операций. Float, при использовании в диапазоне [-1..1] может хранить значения с точностью 1e-5. А ваши 2 минуты это (2/60) * (pi/180) = 5.8e-4 Что в принципе не так уж и далеко учитывая что у вас рабочий диапазон значений выходит за рамки [-1..1]. Думаю, правильность алгоритма можно проверить так: заменить на ПК все вычисления double на float. При одинаковых входных данных разница должна быть значительно меньше.
Спасибо. Все похоже на то, что все-таки дело в точности вычислений на МК, а не в случайно внесенной ошибке. Интересно, а как-нибудь можно добиться приличной точности с плавающей точкой на ардуино? Например, какая-нибудь библиотека с программной реализацией этого дела, чтобы double был полноценным?
Таких либ к сожалению не знаю. Если кто знает поделитесь. Видел как успешно пытаются добиться высокой точности на флоатах в шейдере GLSL(те что на видеокартах) вычисляя фрактал( https://ru.wikipedia.org/wiki/Множество_Мандельброта) Даже с учетверённой точностью относительно флоата https://www.thasler.com/blog/blog/glsl-part5
Ее можно сделать, но насколько это осмысленно - большой вопрос. Вот тут расписали ситуацию с gcc / avr-libc: http://www.avrfreaks.net/comment/779607#comment-779607 Повозиться придется порядочно... Может быть, лучше будет сменить платформу, чем мучать 8-битную AVR-ку?
Спасибо. Решения, получается, есть. Только придется очень сильно повозиться с исходниками, уж очень много там разнообразных вычислений с double. Возникает даже вопрос, а стоит ли улучшение точности вычисления времени восходов-заходов на домашней метеостанции таких расходов времени. Платформу менять не хочется, поскольку мега в обозримом будущем уже никуда не пригодится. Слишком большая, зараза, сопоставима по размерам с целой RPi, да и по цене не сильно отличается. А проектов, действительно требующих столь большого количества входов-выходов, не предвидится.