#include <TimeLib.h>
#include "MoonCalc.h"
// Baza danych charakterystycznych kraterów
struct LunarObject {
const char* name;
double longitude;
double latitude;
bool isMare;
};
// struktura dla pozycji horyzontalnej
struct HorizontalPosition {
double altitude;
double azimuth;
};
const LunarObject lunarObjects[] = {
{"Morze Spokoju", 30.0, 8.5, true},
{"Morze Deszczow", -20.0, 38.0, true},
{"Morze Jasnosci", 18.0, 28.0, true},
{"Morze Przesilen", 58.0, 17.0, true},
{"Krater Tycho", -11.0, -43.0, false},
{"Krater Kopernik", -20.0, 10.0, false},
{"Krater Plato", -9.0, 52.0, false},
{"Krater Arystarch", -47.0, 24.0, false},
{"Krater Grimaldi", -68.0, -5.0, false}
};
const int objectCount = 9;
MoonCalc moonCalc;
// Współrzędne obserwatora (Caino, Włochy, okolice Gardy)
double observer_lat = 45.6; // szerokość geograficzna -90° do +90°
double observer_lon = 10.3; // długość geograficzna -180° do +180°
double observer_alt = 385.0; // wysokość n.p.m. w metrach
void setup() {
Serial.begin(115200);
// Inicjalizacja czasu - ustaw aktualny czas (przykładowo na 27.11.2025 15:30:00)
setTime(15, 30, 0, 27, 11, 2025); // godzina, minuta, sekunda, dzień, miesiąc, rok
Serial.println("================================================");
Serial.println(" OBSERWATOR KSIĘŻYCA - KONSOLA");
Serial.println("================================================");
Serial.println();
}
void loop() {
static unsigned long lastUpdate = 0;
// Aktualizuj dane co 10 sekund
if (millis() - lastUpdate > 10000) {
updateMoonData();
displayAllData();
lastUpdate = millis();
Serial.println();
Serial.println("Następna aktualizacja za 10 sekund...");
Serial.println("================================================");
}
delay(1000);
}
void updateMoonData() {
// Pobierz aktualny czas z TimeLib
time_t t_now = now();
// Konwersja na składniki daty - używaj funkcji z TimeLib z przedrostkiem
int year = ::year(t_now); // ::year aby użyć funkcji globalnej
int month = ::month(t_now); // ::month aby użyć funkcji globalnej
int day = ::day(t_now); // ::day aby użyć funkcji globalnej
int hour = ::hour(t_now); // ::hour aby użyć funkcji globalnej
int minute = ::minute(t_now); // ::minute aby użyć funkcji globalnej
// Obliczenia księżycowe
moonCalc.calculate(year, month, day, hour, minute);
// Debug: wyświetl aktualną datę
Serial.print("✅ Aktualna data: ");
Serial.print(day);
Serial.print(".");
Serial.print(month);
Serial.print(".");
Serial.print(year);
Serial.print(" ");
Serial.print(hour);
Serial.print(":");
Serial.println(minute);
}
void displayAllData() {
// Pobierz aktualny czas
time_t t = now();
int hour = ::hour(t); // ::hour aby użyć funkcji globalnej
int minute = ::minute(t); // ::minute aby użyć funkcji globalnej
Serial.println();
Serial.print("=== DANE AKTUALNE: ");
Serial.print(String(hour) + ":" + (minute < 10 ? "0" : "") + String(minute));
Serial.println(" ===");
// 1. PODSTAWOWE INFORMACJE O KSIĘŻYCE
displayBasicInfo();
// 2. POZYCJA NA NIEBIE
displayCelestialPosition();
// 3. LIBRACJA (DRGANIA KSIĘŻYCA)
displayLibrations();
// 4. PUNKT PODSŁONECZNY I TERMINATOR
displaySolarPoint();
// 5. KĄTY POZYCYJNE
displayPositionAngles();
// 6. ANALIZA FAZY KSIĘŻYCA
displayPhaseAnalysis();
// 7. WIDOCZNE OBIEKTY KSIĘŻYCOWE
displayVisibleObjects();
// 8. DODATKOWE INFORMACJE
displayExtraInfo();
// 9. ASTROLOGIA I ZNAKI ZODIAKU
displayAstrology();
// 10. LOKALNA POZYCJA I WIDOCZNOŚĆ (OBSERWATORA)
displayObserverPosition();
// WARUNKI OBSERWACYJNE
checkObservationConditions();
// PROGNOZA NA DZIŚ
showForecast();
// HOROSKOP NA DZIŚ
displayDailyHoroscope();
}
void displayBasicInfo() {
MoonCalc::MoonData moon = moonCalc.getMoonData();
Serial.println();
Serial.println("1. PODSTAWOWE INFORMACJE O KSIĘŻYCU:");
Serial.println("----------------------------------------");
Serial.print(" Faza oświetlenia: ");
Serial.print(moon.illumination * 100, 1);
Serial.print("% (");
Serial.print(getPhaseName(moon.illumination));
Serial.println(")");
Serial.print(" Wiek Księżyca: ");
Serial.print(calculateMoonAge(moon.illumination), 1);
Serial.println(" dni");
Serial.print(" Odległość od Ziemi: ");
Serial.print(moon.distance, 1);
Serial.println(" promieni Ziemi");
Serial.print(" Stan: ");
if (moon.illumination < 0.1)
Serial.println("NIEWIDOCZNY (nów)");
else if (moon.illumination > 0.9)
Serial.println("PEŁNIA");
else if (moon.illumination < 0.5)
Serial.println("PRZYBYWA");
else Serial.println("UBYWA");
}
void displayCelestialPosition() {
MoonCalc::MoonData moon = moonCalc.getMoonData();
MoonCalc::SunData sun = moonCalc.getSunData();
Serial.println();
Serial.println("2. POZYCJA NA NIEBIE:");
Serial.println("----------------------------------------");
Serial.println(" KSIĘŻYC:");
Serial.print(" Rektascensja: ");
Serial.print(moon.rightAscension, 3);
Serial.println(" h");
Serial.print(" Deklinacja: ");
Serial.print(moon.declination, 2);
Serial.println("°");
Serial.println(" SŁOŃCE:");
Serial.print(" Rektascensja: ");
Serial.print(sun.rightAscension, 3);
Serial.println(" h");
Serial.print(" Deklinacja: ");
Serial.print(sun.declination, 2);
Serial.println("°");
Serial.print(" Odległość: ");
Serial.print(sun.distance, 4);
Serial.println(" AU");
// Różnica pozycji
double differenceRA = abs(moon.rightAscension - sun.rightAscension);
Serial.print(" Odległość kątowa Słońce-Księżyc: ");
Serial.print(differenceRA * 15, 1);
Serial.println("°");
}
void displayLibrations() {
MoonCalc::Libration libration = moonCalc.getLibration();
Serial.println();
Serial.println("3. LIBRACJA (DRGANIA KSIĘŻYCA):");
Serial.println("----------------------------------------");
Serial.print(" Długość selenograficzna: ");
Serial.print(libration.selenographicLongitude, 1);
Serial.println("°");
Serial.print(" Szerokość selenograficzna: ");
Serial.print(libration.selenographicLatitude, 1);
Serial.println("°");
Serial.print(" Widoczny region: ");
Serial.println(determineVisibleRegion(libration.selenographicLongitude, libration.selenographicLatitude));
Serial.print(" Strona boczna: ");
if (libration.selenographicLongitude > 0) {
Serial.println("WIDOCZNA ZACHODNIA STRONA");
} else {
Serial.println("WIDOCZNA WSCHODNIA STRONA");
}
Serial.print(" Biegun: ");
if (libration.selenographicLatitude > 0) {
Serial.println("WIDOCZNY BIEGUN PÓŁNOCNY");
} else {
Serial.println("WIDOCZNY BIEGUN POŁUDNIOWY");
}
}
void displaySolarPoint() {
MoonCalc::SolarPoint solarPoint = moonCalc.getSolarPoint();
Serial.println();
Serial.println("4. PUNKT PODSŁONECZNY I TERMINATOR:");
Serial.println("----------------------------------------");
Serial.print(" Długość selenograficzna Słońca: ");
Serial.print(solarPoint.selenographicLongitude, 1);
Serial.println("°");
Serial.print(" Szerokość selenograficzna Słońca: ");
Serial.print(solarPoint.selenographicLatitude, 1);
Serial.println("°");
Serial.print(" Kolongituda: ");
Serial.print(solarPoint.colongitude, 1);
Serial.println("°");
Serial.print(" Pozycja terminatora: ");
Serial.print(solarPoint.terminatorLongitude, 1);
Serial.println("°");
// Interpretacja terminatora
Serial.print(" Terminator: ");
if (solarPoint.terminatorLongitude > 0) {
Serial.println("ZACHODZĄCE SŁOŃCE (wieczór)");
} else {
Serial.println("WSCHODZĄCE SŁOŃCE (rano)");
}
}
void displayPositionAngles() {
MoonCalc::PositionAngles angles = moonCalc.getPositionAngles();
Serial.println();
Serial.println("5. KĄTY POZYCYJNE:");
Serial.println("----------------------------------------");
Serial.print(" Kąt pozycyjny jasnego brzegu: ");
Serial.print(angles.brightLimb, 1);
Serial.println("°");
Serial.print(" Kąt pozycyjny bieguna: ");
Serial.print(angles.pole, 1);
Serial.println("°");
Serial.println(" Interpretacja:");
Serial.println(" - Kąt jasnego brzegu: orientacja oświetlonej krawędzi");
Serial.println(" - Kąt bieguna: orientacja osi obrotu Księżyca");
}
void displayPhaseAnalysis() {
MoonCalc::MoonData moon = moonCalc.getMoonData();
Serial.println();
Serial.println("6. ANALIZA FAZY KSIĘŻYCA:");
Serial.println("----------------------------------------");
double phase = moon.illumination;
Serial.print(" Faza: ");
Serial.print(phase * 100, 1);
Serial.print("% - ");
Serial.println(getDetailedPhase(phase));
Serial.print(" Dzień cyklu: ");
Serial.print(calculateMoonAge(phase), 1);
Serial.print(" z 29.53 dni (");
Serial.print((calculateMoonAge(phase) / 29.53) * 100, 1);
Serial.println("% cyklu)");
Serial.print(" Następny nów za: ");
Serial.print(29.53 - calculateMoonAge(phase), 1);
Serial.println(" dni");
Serial.print(" Następna pełnia za: ");
double untilFull = 14.77 - calculateMoonAge(phase);
if (untilFull < 0) untilFull += 29.53;
Serial.print(untilFull, 1);
Serial.println(" dni");
}
void displayVisibleObjects() {
MoonCalc::Libration libration = moonCalc.getLibration();
Serial.println();
Serial.println("7. WIDOCZNE OBIEKTY KSIĘŻYCOWE:");
Serial.println("----------------------------------------");
int visibleCount = 0;
for (int i = 0; i < objectCount; i++) {
double differenceLongitude = abs(lunarObjects[i].longitude - libration.selenographicLongitude);
double differenceLatitude = abs(lunarObjects[i].latitude - libration.selenographicLatitude);
if (differenceLongitude < 7.0 && differenceLatitude < 7.0) {
Serial.print(" ★ ");
Serial.print(lunarObjects[i].name);
Serial.print(" (Dł: ");
Serial.print(lunarObjects[i].longitude, 1);
Serial.print("°, Sz: ");
Serial.print(lunarObjects[i].latitude, 1);
Serial.print("°) - ");
Serial.println(lunarObjects[i].isMare ? "MORZE" : "KRATER");
visibleCount++;
}
}
if (visibleCount == 0) {
Serial.println(" Brak charakterystycznych obiektów w centrum tarczy");
} else {
Serial.print(" Łącznie widocznych obiektów: ");
Serial.println(visibleCount);
}
}
void displayExtraInfo() {
double daysSinceJ2000 = moonCalc.getDaysSinceJ2000();
double julianDay = moonCalc.getJulianDay();
// Pobierz aktualny czas
time_t t = now();
int hour = ::hour(t); // ::hour aby użyć funkcji globalnej
int minute = ::minute(t); // ::minute aby użyć funkcji globalnej
Serial.println();
Serial.println("8. DODATKOWE INFORMACJE:");
Serial.println("----------------------------------------");
Serial.print(" Dni od epoki J2000: ");
Serial.println(daysSinceJ2000, 2);
Serial.print(" Dzień juliański: ");
Serial.println(julianDay, 4);
Serial.print(" Czas uniwersalny (UTC): ");
Serial.print(String(hour) + ":" + (minute < 10 ? "0" : "") + String(minute));
Serial.println();
}
String getPhaseName(double illumination) {
if (illumination < 0.02) return "NÓW";
if (illumination < 0.25) return "Rosnący Sierp";
if (illumination < 0.35) return "I KWADRRA";
if (illumination < 0.65) return "Rosnący Garbaty";
if (illumination < 0.75) return "PEŁNIA";
if (illumination < 0.85) return "Malejący Garbaty";
if (illumination < 0.98) return "III KWADRA";
return "MALEjący Sierp";
}
String getDetailedPhase(double illumination) {
if (illumination < 0.02) return "KSIĘŻYC NIEWIDOCZNY";
if (illumination < 0.1) return "CIENKI SIERP (wieczorem)";
if (illumination < 0.25) return "MIŁODZIK";
if (illumination < 0.35) return "PIERWSZA KWADRRA";
if (illumination < 0.5) return "KSIĘŻYC GARBATY (przed pełnią)";
if (illumination < 0.65) return "PEŁNIA BLISKA";
if (illumination < 0.75) return "PEŁNIA";
if (illumination < 0.85) return "PEŁNIA UBYWAJĄCA";
if (illumination < 0.98) return "OSTATNIA KWADRRA";
return "KSIĘŻYC PORANNY";
}
double calculateMoonAge(double illumination) {
return illumination * 29.53;
}
String determineVisibleRegion(double longitude, double latitude) {
if (longitude > 6) return "SKRAJ ZACHODNI";
if (longitude < -6) return "SKRAJ WSCHODNI";
if (latitude > 4) return "OKOLICE BIEGUNA PÓŁNOCNEGO";
if (latitude < -4) return "OKOLICE BIEGUNA POŁUDNIOWEGO";
return "ŚRODEK TARCZY";
}
void checkObservationConditions() {
MoonCalc::MoonData moon = moonCalc.getMoonData();
MoonCalc::Libration lib = moonCalc.getLibration();
Serial.println("\n=== WARUNKI OBSERWACYJNE ===");
if (moon.illumination > 0.9) {
Serial.println("❌ PEŁNIA - słabe warunki do obserwacji kraterów");
} else if (moon.illumination < 0.3 || moon.illumination > 0.7) {
Serial.println("✅ Dobra faza - terminator widoczny");
}
if (abs(lib.selenographicLongitude) > 5) {
Serial.println("✅ Dobre libracje - widoczna strona boczna");
}
}
void showForecast() {
Serial.println("\n=== PROGNOZA NA DZIŚ ===");
Serial.println("Wieczorem: Księżyc garbaty, widoczna wschodnia krawędź");
Serial.println("Dobrze widoczne: kratery przy terminatorze");
Serial.println("Unikać: obszarów w pełnym słońcu (pośrodku tarczy)");
}
String getZodiacSign(double declination, double rightAscension) {
double eclipticLongitude = rightAscension * 15.0;
eclipticLongitude = fmod(eclipticLongitude, 360.0);
if (eclipticLongitude < 0) eclipticLongitude += 360.0;
if (eclipticLongitude < 30) return "♈ Baran";
else if (eclipticLongitude < 60) return "♉ Byk";
else if (eclipticLongitude < 90) return "♊ Bliźnięta";
else if (eclipticLongitude < 120) return "♋ Rak";
else if (eclipticLongitude < 150) return "♌ Lew";
else if (eclipticLongitude < 180) return "♍ Panna";
else if (eclipticLongitude < 210) return "♎ Waga";
else if (eclipticLongitude < 240) return "♏ Skorpion";
else if (eclipticLongitude < 270) return "♐ Strzelec";
else if (eclipticLongitude < 300) return "♑ Koziorożec";
else if (eclipticLongitude < 330) return "♒ Wodnik";
else return "♓ Ryby";
}
String getMoonHouse(double moonRA, double sunRA) {
double difference = moonRA - sunRA;
if (difference < 0) difference += 24.0;
int house = (int)((difference / 24.0) * 12) + 1;
switch (house) {
case 1: return "I Dom - Jaństwo";
case 2: return "II Dom - Bogactwo";
case 3: return "III Dom - Komunikacja";
case 4: return "IV Dom - Dom i rodzina";
case 5: return "V Dom - Kreatywność";
case 6: return "VI Dom - Praca i zdrowie";
case 7: return "VII Dom - Partnerstwo";
case 8: return "VIII Dom - Przemiana";
case 9: return "IX Dom - Podróże";
case 10: return "X Dom - Kariera";
case 11: return "XI Dom - Przyjaźnie";
case 12: return "XII Dom - Duchowość";
default: return "Nieokreślony dom";
}
}
String getMoonPhaseAstrological(double illumination) {
if (illumination < 0.02) return "♑ NOW - Nowy początek";
else if (illumination < 0.25) return "♒ Rosnący sierp - Zamiary";
else if (illumination < 0.35) return "♓ I Kwadra - Działanie";
else if (illumination < 0.65) return "♈ Rosnący garbaty - Rozwój";
else if (illumination < 0.75) return "♉ PEŁNIA - Kulminacja";
else if (illumination < 0.85) return "♊ Malejący garbaty - Dzielenie się";
else if (illumination < 0.98) return "♋ III Kwadra - Oczyszczanie";
else return "♌ Malejący sierp - Zakończenia";
}
void displayAstrology() {
MoonCalc::MoonData moon = moonCalc.getMoonData();
MoonCalc::SunData sun = moonCalc.getSunData();
Serial.println();
Serial.println("9. ASTROLOGIA I ZNAKI ZODIAKU:");
Serial.println("----------------------------------------");
String signSun = getZodiacSign(sun.declination, sun.rightAscension);
String signMoon = getZodiacSign(moon.declination, moon.rightAscension);
String houseMoon = getMoonHouse(moon.rightAscension, sun.rightAscension);
String astroPhase = getMoonPhaseAstrological(moon.illumination);
Serial.println(" SŁOŃCE:");
Serial.print(" Znak zodiaku: ");
Serial.println(signSun);
Serial.println(" KSIĘŻYC:");
Serial.print(" Znak zodiaku: ");
Serial.println(signMoon);
Serial.print(" Dom księżycowy: ");
Serial.println(houseMoon);
Serial.print(" Faza astrologiczna: ");
Serial.println(astroPhase);
// Aspekt koniunkcji/opozycji
double differenceRA = abs(moon.rightAscension - sun.rightAscension);
if (differenceRA < 1.0) {
Serial.println(" ⚡ KONIUNKCJA ze Słońcem (nów)");
} else if (differenceRA > 11.0 && differenceRA < 13.0) {
Serial.println(" ⚡ OPÓZYCJA ze Słońcem (pełnia)");
}
// Kompatybilność znaków (uproszczona)
Serial.print(" Relacja Słońce-Księżyc: ");
if (signSun.charAt(0) == signMoon.charAt(0)) {
Serial.println("Harmonijna");
} else {
Serial.println("Różnorodna");
}
}
void displayDailyHoroscope() {
MoonCalc::MoonData moon = moonCalc.getMoonData();
String house = getMoonHouse(moon.rightAscension, moonCalc.getSunData().rightAscension);
Serial.println("\n=== HOROSKOP NA DZIŚ ===");
Serial.print("Księżyc w: ");
Serial.println(house);
if (house.startsWith("V"))
Serial.println("Dzień kreatywności i zabawy!");
else if (house.startsWith("X"))
Serial.println("Skup się na karierze!");
else if (house.startsWith("VII"))
Serial.println("Dobry dzień na spotkania!");
else
Serial.println("Standardowy dzień - wykorzystaj go jak najlepiej!");
}
void displayObserverPosition() {
MoonCalc::MoonData moon = moonCalc.getMoonData();
double jd = moonCalc.getJulianDay();
// 1. Oblicz Lokalny Czas Sideralny (LST)
double lst = calculateLST(jd, observer_lon);
// 2. Oblicz Wysokość i Azymut Księżyca
HorizontalPosition moonPos = calculateAltAz(observer_lat, lst, moon.rightAscension, moon.declination);
// 3. Oblicz Wysokość i Azymut Słońca (dla porównania)
MoonCalc::SunData sun = moonCalc.getSunData();
HorizontalPosition sunPos = calculateAltAz(observer_lat, lst, sun.rightAscension, sun.declination);
// Wypisywanie wyników
Serial.println();
Serial.println("10. LOKALNA POZYCJA I WIDOCZNOŚĆ (OBSERWATORA):");
Serial.println("----------------------------------------");
Serial.print(" Lokalizacja ustawiona na: ");
Serial.print(observer_lat, 2);
Serial.print("° N, ");
Serial.print(observer_lon, 2);
Serial.print("° E (Alt: ");
Serial.print(observer_alt, 0);
Serial.println("m)");
Serial.print(" Lokalny Czas Sideralny (LST): ");
Serial.print(lst / 15.0, 3);
Serial.println(" h");
Serial.println("\n KSIĘŻYC:");
Serial.print(" Wysokość (Altitude): ");
Serial.print(moonPos.altitude, 2);
Serial.println("°");
Serial.print(" Azymut (Azimuth): ");
Serial.print(moonPos.azimuth, 1);
Serial.println("° (N=0, E=90, S=180, W=270)");
// Interpretacja położenia
if (moonPos.altitude > 0) {
Serial.println(" ✅ Księżyc jest nad horyzontem.");
} else {
Serial.println(" ❌ Księżyc jest pod horyzontem.");
}
Serial.println("\n SŁOŃCE:");
Serial.print(" Wysokość (Altitude): ");
Serial.print(sunPos.altitude, 2);
Serial.println("°");
Serial.print(" Azymut (Azimuth): ");
Serial.print(sunPos.azimuth, 1);
Serial.println("°");
if (sunPos.altitude > 0) {
Serial.println(" Dzień (Słońce jest nad horyzontem).");
} else {
Serial.println(" Noc (Słońce jest pod horyzontem).");
}
}
double calculateLST(double jd, double longitude) {
// Obliczenie Czasu Sideralnego Greenwich (GST)
double T = (jd - 2451545.0) / 36525.0; // Wiek juliański od J2000
double GMST = 280.46061837 + 360.98564736629 * (jd - 2451545.0) + 0.000387933 * T * T - T * T * T / 38710000.0;
// Normalizacja do zakresu 0-360 stopni
GMST = fmod(GMST, 360.0);
if (GMST < 0) GMST += 360.0;
// Lokalny Czas Sideralny (LST) = GST + Długość Geograficzna
double LST = GMST + longitude;
LST = fmod(LST, 360.0);
if (LST < 0) LST += 360.0;
return LST; // LST w stopniach
}
HorizontalPosition calculateAltAz(double observerLat, double lst, double ra, double dec) {
// Konwersja RA z godzin na stopnie i normalizacja
double ra_deg = ra * 15.0;
// Kąt godzinny (Hour Angle, HA)
double HA = lst - ra_deg;
HA = fmod(HA, 360.0);
if (HA < 0) HA += 360.0;
// Konwersja na radiany
double lat_rad = observerLat * M_PI / 180.0;
double dec_rad = dec * M_PI / 180.0;
double ha_rad = HA * M_PI / 180.0;
// Obliczenie wysokości (Altitude, Alt)
double sin_alt = sin(dec_rad) * sin(lat_rad) + cos(dec_rad) * cos(lat_rad) * cos(ha_rad);
double alt_rad = asin(sin_alt);
// Obliczenie azymutu (Azimuth, Az)
double cos_az_num = sin(dec_rad) - sin_alt * sin(lat_rad);
double cos_az_den = cos(alt_rad) * cos(lat_rad);
double cos_az = cos_az_num / cos_az_den;
// Użycie atan2 do poprawnego określenia ćwiartki
double sin_A = -cos(dec_rad) * sin(ha_rad) / cos(alt_rad);
double azimuth = atan2(sin_A, cos_az) * 180.0 / M_PI;
// Normalizacja Azymutu do 0-360 stopni (N = 0)
azimuth = fmod(azimuth, 360.0);
if (azimuth < 0) azimuth += 360.0;
HorizontalPosition pos;
pos.altitude = alt_rad * 180.0 / M_PI;
pos.azimuth = azimuth;
return pos;
}Loading
esp32-c3-devkitm-1
esp32-c3-devkitm-1