sun.c


1
// --- [ sun.c ] --------------------------------------------------------------
2
//       02.01.09
3
// Der Rechenweg für den Sonnenstand kommt von:
4
//-----------------------------------------------------------------------------
5
// Prozessor : ATmega8
6
// Sprache   : C
7
// Datum     : 20.09.2008
8
// Version   : 1.0
9
// Autor     : Cusoe
10
// Programm  : vereinfachte Sonnenstandsberechnung
11
//-----------------------------------------------------------------------------
12
13
#include  <avr\io.h>        //AVR Register und Konstantendefinitionen
14
#include  <avr\interrupt.h>   //AVR Interrupt Vektoren
15
#include  <stdlib.h>
16
#include  <inttypes.h>
17
#include  <math.h>
18
#include "global.h"
19
#include "lcd.h"
20
#include "sun.h"
21
22
#ifdef SUN
23
// ----------------------------------------------------------------------------
24
// zeigt die Daten an
25
// stellt zuerst das Vorzeichen fest, wandelt ggf. in positiven Wert um
26
// multipliziert mit 100, damit 2 Nachkommastellen mitgenommen werden
27
// wandelt dann die Floats um in uint16_t
28
// und legt upper und lower Byte in Array SUN_data ab.
29
// und zeigt natürlich die Werte auch noch an.
30
31
void SUN_display (void)
32
{
33
static uint8_t x = 0;
34
uint16_t tmp;
35
36
DCF_make_UTC_time();              // UTC-Zeit aus der aktuellen Uhrzeit erzeugen
37
38
SUN_calc();                    // Elevation und Azimuth für UTC-Zeit berechnen 
39
                          // und in die globalen Variablen schreiben
40
41
// Elevation und Azimuth umwandeln von double nach 2x uint8_t/uint16_t
42
// die Werte speichern und ausgeben
43
44
if (elevation > 0)               // Elevation -90 ... + 90, Vorzeichen bestimmen
45
  {
46
  SUN_data[SUN_EL_SIGN] = PLUS; 
47
  }
48
  
49
else
50
  {
51
  SUN_data[SUN_EL_SIGN] = MINUS;
52
  elevation = 0 - elevation;        // zu positivem Wert machen
53
  }
54
tmp = (uint16_t) (elevation * 100);    // für zwei Nachkommastellen mit 100 multiplizieren
55
SUN_data[SUN_EL_HIGH] = (uint8_t) (tmp / 256); // zerlegen und speichern
56
SUN_data[SUN_EL_LOW] = (uint8_t) tmp;
57
58
#ifdef LCD
59
lcd_goto_xy(3,6);                // 0 - 90
60
SUN_print(tmp, SUN_data[SUN_EL_SIGN], 2);  // Ausgeben
61
#endif
62
63
tmp = (uint16_t) (azimuth * 100);    // Azimuth 0 ... 360
64
SUN_data[SUN_AZ_HIGH] = (uint8_t) (tmp / 256); // zerlegen und speichern
65
SUN_data[SUN_AZ_LOW] = (uint8_t) tmp;
66
67
#ifdef LCD
68
lcd_goto_xy(4,5);  
69
SUN_print(tmp, '+', 3);
70
#endif
71
72
if (!(x == (CLK_time[TAG])))        // 1mal am jedem Tag und direkt nach dem Programmstart
73
  {                        // Sonnenauf-Untergang berechnen 
74
  x = CLK_time[TAG];            // das dauert einige Zeit ... 
75
  SUN_check_sunrise();            // Durch Iteration Sonnenaufgang/Untergang bestimmen
76
  }
77
78
#ifdef LCD
79
lcd_goto_xy(3, 13);              // Sonnenaufgang ausdrucken
80
lcd_dezprint(SUN_data[SUN_UP_H]);
81
lcd_putc('.');
82
lcd_dezprint(SUN_data[SUN_UP_M]);
83
84
lcd_goto_xy(4, 13);              // Sonnenuntergang ausdrucken
85
lcd_dezprint(SUN_data[SUN_DN_H]);
86
lcd_putc('.');
87
lcd_dezprint(SUN_data[SUN_DN_M]);
88
#endif
89
90
}
91
92
//----------------------------------------------------------------------
93
// Dezimaltag vom aktuellen Tag berechnen
94
//----------------------------------------------------------------------
95
96
float dezimal_day(void)
97
{
98
// wenn nur zu voller Minuten berechnet wird sollte dies ausreichen:
99
// die Sekundenbruchteile werden vernachlässigt
100
return(UTC_time[STUNDE] / 24.0 + UTC_time[MINUTE] / 1440.0 );
101
}
102
103
//----------------------------------------------------------------------
104
// Anzahl Dezimaltage seit 2000 bis aktuelles Datum berechnen
105
//----------------------------------------------------------------------
106
107
float julian_day(void)
108
{
109
return((float) (UTC_time[JAHR] * 365 + floor((UTC_time[JAHR] / 4)) + day_of_year(UTC_time) - 0.5));
110
}
111
112
//----------------------------------------------------------------------
113
// Reduzierung eines Winkel auf 0... 2PI rad, Stunden auf 0... 24 h etc.
114
// x=Wert der reduziert werden soll; y=Reduzierbereich z.B. auf 24h
115
//----------------------------------------------------------------------
116
117
float degr(float x, float y)
118
{
119
120
if(x < 0) x = x + y;
121
return((x - (floor(x / y) * y)));
122
}
123
124
// --------------------------------------------------------------------
125
// Den Sonnenauf/untergang durch "Probieren" bestimmen
126
// entspricht ungefähr der 'Bürgerlichen Dämmerung' ??
127
// von 09.00h in 1 Stunden Intervallen bis unter -x Grad,
128
// dann in 1 Minuten Intervallen zurück > -x Grad
129
130
// von 15.00h in Stunden-Intervallen bis unter -x Grad,
131
// dann in 1 Minuten-Intervallen zurück bis > -x Grad
132
133
void SUN_check_sunrise(void)
134
{
135
136
// die UTC-Zeit muss am Ende der Funktion neu aufgebaut werden !
137
UTC_time[MINUTE] = 0;
138
UTC_time[STUNDE] = 10;
139
140
do  {                         // Sonnenaufgang bestimmen
141
  UTC_time[STUNDE]--;            // um eine Stunde decrementieren
142
  SUN_calc();
143
  } while (elevation > (ELEVATION));  // bis der gewählte Grenzwert unterschritten ist
144
145
do {
146
  DCF_incr_time(UTC_time, 1);       // um 1 Minute incrementieren
147
  SUN_calc();
148
  } while (elevation < (ELEVATION));  // bis der gewählte Grenzwert überschritten ist
149
150
SUN_data[SUN_UP_M] = UTC_time[MINUTE];  // die Uhrzeit speichern
151
SUN_data[SUN_UP_H] = UTC_time[STUNDE] + 1;// für unsere Zeitzone im Winter + 1 Stunde
152
                           // während der Sommerzeit + 2 Stunden
153
if (is_summertime(CLK_time)) UTC_time[STUNDE]++;
154
155
UTC_time[MINUTE] = 59;
156
UTC_time[STUNDE] = 14;
157
158
do  {                        // Sonnenuntergang bestimmen
159
  UTC_time[STUNDE]++;            // um eine Stunde incrementieren
160
  SUN_calc();
161
  }
162
  while (elevation > (ELEVATION));    // bis der gewählte Grenzwert unterschritten ist
163
164
do  {
165
  DCF_decr_time(UTC_time, 1);       // um 1 Minute decrementieren
166
  SUN_calc();
167
  } while (elevation < (ELEVATION));  // bis der gewählte Grenzwert überschritten ist
168
  
169
SUN_data[SUN_DN_M] = UTC_time[MINUTE]; // die Uhrzeit speichern
170
SUN_data[SUN_DN_H] = UTC_time[STUNDE] + 1;// für unsere Zeitzone im Winter + 1 Stunde
171
                          // während der Sommerzeit + 2 Stunden
172
if (is_summertime(CLK_time)) UTC_time[STUNDE]++;
173
174
DCF_make_UTC_time();              // UTC_Zeit neu aus der Uhr kopieren
175
}
176
177
//----------------------------------------------------------------------
178
// Vereinfachte Sonnenstandsberechnung
179
//----------------------------------------------------------------------
180
void SUN_calc(void)
181
{
182
double j, l, g, a, e, rekt, dekl, t0, sh, t, mz, rz, el, kr;
183
double jd0, dt, la, br;
184
double azi, elev;
185
186
jd0 = julian_day();
187
dt  = dezimal_day();
188
189
la  = geo_laenge / KO; 
190
br  = geo_breite / KO;
191
192
j = jd0 + dt;
193
l = degr(4.894950420 + 0.01720279239 * j, M_2PI);   // Postion der Sonne auf der Ekliptik
194
g = degr(6.240040768 + 0.01720197034 * j, M_2PI);   // mittlere Anomalie
195
a = l + 0.03342305518 * sin(g) + 0.0003490658504 * sin(2 * g); // ekliptikale Länge der Sonne
196
e = 0.4090928042 - 0.000000006981317008 * j;     // Schiefe der Ekliptik
197
198
rekt = atan(cos(e) * sin(a) / cos(a));         // Rektaszension
199
200
if(cos(a) < 0) rekt = rekt + M_PI;
201
202
dekl = asin(sin(e) * sin(a));               // Deklination
203
204
t0 = jd0 / 36525.0;                       // Tageszahl in julianischen Jahrhunderten
205
sh = degr(6.697376 + 2400.05134 * t0 + 1.002738 * dt * 24, 24); // mittlere Sternzeit
206
t = sh * 0.2617993878 + la - rekt;             // Stundenwinkel der Sonne bezogen auf den Ort
207
208
mz = cos(t) * sin(br) - tan(dekl) * cos(br);     // Nenner Azimut
209
rz = atan(sin(t) / mz) + M_PI;               // Azimunt wird von 0° Nord gezählt
210
211
if(mz < 0) rz = rz + M_PI;                  // wenn Nenner vom Azimut kleiner 0
212
                                  // dann den Winkel in den richtigen Quadranten bringen
213
azi = degr(rz, M_2PI);                     // Azimut reduzieren
214
                                  // Elevation
215
el = asin(cos(dekl) * cos(t) * cos(br) + sin(dekl) * sin(br));
216
                                  // Korrekturwert für Refraktion
217
kr = 0.0002967059728 / tan(el + 0.179768913 / (el + 0.08918632478));
218
elev = el + kr;                         // Elevation einschliesslich Refraktion
219
220
// Ausgabe des Ergebnisses in globale Variable
221
222
azimuth   = (azi * KO);
223
elevation = (elev * KO);
224
}
225
226
227
// ---------------------------------------------------------------------------
228
// eine bereits 'vorverdaute' Float-Zahl ausgeben
229
#ifdef LCD
230
void SUN_print(uint16_t j, uint8_t c, uint8_t vorkomma)
231
{
232
register uint8_t i = 0;
233
234
lcd_putc(c);                  // Vorzeichen ausgeben
235
236
if (vorkomma > 2)                // bei dreistelligen Ziffern
237
  {
238
  i = 0;
239
  while (j > 9999)
240
    {
241
    j -= 10000;
242
    i++;
243
    }
244
  lcd_putc('0' + i);            // Hunderterstelle
245
  }
246
  
247
i = 0;
248
while (j > 999)
249
  {
250
  j -= 1000;
251
  i++;
252
  }
253
lcd_putc('0' + i);              // Zehnerstelle
254
255
i = 0;
256
while (j > 99)
257
  {
258
  j -= 100;
259
  i++;
260
  }
261
lcd_putc('0' + i);              // Einerstelle
262
263
lcd_putc('.');                  // Dezimalpunkt einschmuggeln
264
265
i = 0;
266
while (j > 9)
267
  {
268
  j -= 10;
269
  i++;
270
  }
271
lcd_putc('0' + i);              // 1. Nachkommastelle
272
lcd_putc('0' + j);              // 2. Nachkommastelle
273
}
274
#endif
275
276
// ----------------------------------------------------------------------------
277
// Ausgabe einer Dezimalzahl mit Nachkommastellen ohne Systemfunktion
278
// hier durchgängig mit float Variable (diese Code scheint kleiner zu sein)
279
/*
280
void out_float(double f, uint8_t vorkomma)
281
{
282
register uint8_t i = 0;
283
double ff;
284
285
if (f < 0) 
286
  {
287
  f = 0 - f;
288
  lcd_putc('-');
289
  }
290
else lcd_putc('+');
291
292
if (vorkomma > 2)
293
  {
294
  ff = 99.9999;
295
  while (f > ff)
296
    {
297
    f -= 100;
298
    i++;
299
    }
300
  lcd_putc('0' + i);            // Hunderterstelle
301
  }
302
303
i = 0; 
304
ff = 9.9999;
305
while (f > ff)
306
  {
307
  f -= 10;
308
  i++;
309
  }
310
lcd_putc('0' + i);              // Zehnerstelle
311
312
i = 0;
313
ff = 0.9999;
314
while (f > ff)
315
  {
316
  f -= 1;
317
  i++;
318
  }
319
lcd_putc('0' + i);              // Einerstelle
320
lcd_putc('.');
321
322
i = 0;
323
ff = 0.09999;
324
while (f > ff)            
325
  {
326
  f -= 0.1;
327
  i++;
328
  }
329
lcd_putc('0' + i);              // 1. Dezimalstelle
330
331
i = 0;                      // ohne 2. Dez. können 50 Byte eingespart werden
332
ff = 0.00999;
333
while (f > ff)
334
  {
335
  f -= 0.01;
336
  i++;
337
  }
338
lcd_putc('0' + i);              // 2. Dezimalstelle
339
340
}
341
342
*/
343
344
/*
345
// ----------------------------------------------------------------------------
346
// Ausgabe einer Dezimalzahl mit Nachkommastellen ohne Systemfunktion
347
// hier über uint16_t und Multiplikation mit 100 für 2 Nachkommastellen
348
void out_float(double f, uint8_t vorkomma)
349
{
350
uint16_t j;
351
uint8_t i;
352
  
353
if (f < 0) 
354
  {
355
  f = 0 - f;
356
  lcd_putc('-');
357
  }
358
else lcd_putc('+');
359
360
j = (uint16_t) (f * 100);
361
362
if (vorkomma > 2)
363
  {
364
  i = 0;
365
  while (j > 9999)
366
    {
367
    j -= 10000;
368
    i++;
369
    }
370
  lcd_putc('0' + i);            // Hunderterstelle
371
  }
372
  
373
i = 0;
374
while (j > 999)
375
  {
376
  j -= 1000;
377
  i++;
378
  }
379
lcd_putc('0' + i);              // Zehnerstelle
380
381
i = 0;
382
while (j > 99)
383
  {
384
  j -= 100;
385
  i++;
386
  }
387
lcd_putc('0' + i);              // Einerstelle
388
389
lcd_putc('.');                  // Dezimalpunkt
390
391
i = 0;
392
while (j > 9)
393
  {
394
  j -= 10;
395
  i++;
396
  }
397
lcd_putc('0' + i);              // 1. Nachkommastelle
398
lcd_putc('0' + j);              // 2. Nachkommastelle
399
400
}
401
*/
402
#endif
403
404
// --- [ eof ] ----------------------------------------------------------------