astropanel 1.1.5 → 2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (5) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +108 -0
  3. data/bin/astropanel +1023 -0
  4. metadata +13 -18
  5. data/bin/astropanel.rb +0 -1152
data/bin/astropanel.rb DELETED
@@ -1,1152 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # encoding: utf-8
3
-
4
- # PROGRAM INFO
5
- # Name: AstroPanel
6
- # Language: Pure Ruby, best viewed in VIM
7
- # Author: Geir Isene <g@isene.com>
8
- # Web_site: http://isene.com/
9
- # Github: https://github.com/isene/AstroPanel
10
- # License: I release all copyright claims. This code is in the public domain.
11
- # Permission is granted to use, copy modify, distribute, and sell
12
- # this software for any purpose. I make no guarantee about the
13
- # suitability of this software for any purpose and I am not liable
14
- # for any damages resulting from its use. Further, I am under no
15
- # obligation to maintain or extend this software. It is provided
16
- # on an 'as is' basis without any expressed or implied warranty.
17
-
18
- # PRELIMINARIES
19
- @help = <<HELPTEXT
20
- AstroPanel (https://github.com/isene/AstroPanel)
21
-
22
- KEYS
23
- ? = Show this help text ENTER = Refresh starchart/image
24
- l = Edit Location r = Refresh all data
25
- a = Edit Latitude s = Get starchart for selected time
26
- o = Edit Longitude S = Open starchart in image program
27
- c = Edit Cloud limit A = Show Astronomy Picture Of the Day
28
- h = Edit Humidity limit e = Show upcoming events
29
- t = Edit Temperature limit W = Write to config file
30
- w = Edit Wind limit q = Quit (write to config file)
31
- b = Edit Bortle value Q = Quit (no config write)
32
-
33
- COPYRIGHT: Geir Isene, 2020. No rights reserved. See http://isene.com for more.
34
- HELPTEXT
35
- begin # BASIC SETUP
36
- require 'net/http'
37
- require 'open-uri'
38
- require 'json'
39
- require 'date'
40
- require 'time'
41
- require 'readline'
42
- require 'io/console'
43
- require 'curses'
44
- include Curses
45
-
46
- def cmd?(command)
47
- system("which #{command} > /dev/null 2>&1")
48
- end
49
- if cmd?('/usr/lib/w3m/w3mimgdisplay')
50
- @w3mimgdisplay = "/usr/lib/w3m/w3mimgdisplay"
51
- @showimage = true
52
- else
53
- @showimage = false
54
- end
55
- @showimage = false unless (cmd?('xwininfo') and cmd?('xdotool'))
56
-
57
- begin # Check if network is available
58
- URI.open("https://www.met.no/", :open_timeout=>5)
59
- rescue
60
- puts "\nUnable to get data from met.no\n\n"
61
- exit
62
- end
63
-
64
- # INITIALIZE VARIABLES
65
- @loc, @lat, @lon, @cloudlimit, @humiditylimit, @templimit, @windlimit = ""
66
- @noimage = false
67
- if File.exist?(Dir.home+'/.ap.conf')
68
- load(Dir.home+'/.ap.conf')
69
- else
70
- until @loc.match(/\w+\/\w+/)
71
- puts "\nEnter Location (format like Europe/Oslo): "
72
- @loc = Readline.readline('> ', true).chomp.to_s
73
- end
74
- until (-90.0..90.0).include?(@lat)
75
- puts "\nEnter Latitude (format like 59.4351 or -14.54):"
76
- @lat = Readline.readline('> ', true).chomp.to_f
77
- end
78
- until (-180.0..180.0).include?(@lon)
79
- puts "\nEnter Longitude (between -180 and 180):"
80
- @lon = Readline.readline('> ', true).chomp.to_f
81
- end
82
- until (0..100.0).include?(@cloudlimit)
83
- puts "\nLimit for Cloud Coverage (format like 35 for 35%):"
84
- @cloudlimit = Readline.readline('> ', true).chomp.to_i
85
- end
86
- until (0..100.0).include?(@humiditylimit)
87
- puts "\nLimit for Humidity (format 70 for 70%):"
88
- @humiditylimit = Readline.readline('> ', true).chomp.to_i
89
- end
90
- until (-100.0..100.0).include?(@templimit)
91
- puts "\nMinimum observation temperature in °C (format like -15):"
92
- @templimit =Readline.readline('> ', true).chomp.to_i
93
- end
94
- until (0..50.0).include?(@windlimit)
95
- puts "\nLimit for Wind in m/s (format like 6):"
96
- @windlimit = Readline.readline('> ', true).chomp.to_i
97
- end
98
- conf = "@loc = \"#{@loc}\"\n"
99
- conf += "@lat = #{@lat}\n"
100
- conf += "@lon = #{@lon}\n"
101
- conf += "@cloudlimit = #{@cloudlimit}\n"
102
- conf += "@humiditylimit = #{@humiditylimit}\n"
103
- conf += "@templimit = #{@templimit}\n"
104
- conf += "@windlimit = #{@windlimit}\n"
105
- File.write(Dir.home+'/.ap.conf', conf)
106
- end
107
- ## Don't change these
108
- @lat > 23 ? @image = "/tmp/starchart.jpg" : @image = "/tmp/apod.jpg"
109
- @w_l_width = 70
110
- @weather_point = []
111
- @weather = []
112
- @history = []
113
- @index = 0
114
-
115
- ## Curses setup
116
- Curses.init_screen
117
- Curses.start_color
118
- Curses.curs_set(0)
119
- Curses.noecho
120
- Curses.cbreak
121
- Curses.stdscr.keypad = true
122
-
123
- ## Initialize colors
124
- init_pair(1, 118, 0) # green bg
125
- init_pair(2, 214, 0) # orange bg
126
- init_pair(3, 160, 0) # red bg
127
- end
128
- # CLASSES
129
- class Numeric # NUMERIC CLASS EXTENSION
130
- def deg
131
- self * Math::PI / 180
132
- end
133
- def hms
134
- hrs = self.to_i
135
- m = ((self - hrs)*60).abs
136
- min = m.to_i
137
- sec = ((m - min)*60).to_i.abs
138
- return hrs, min, sec
139
- end
140
- def to_hms
141
- hrs, min, sec = self.hms
142
- return "#{hrs.to_s.rjust(2, "0")}:#{min.to_s.rjust(2, "0")}:#{sec.to_s.rjust(2, "0")}"
143
- end
144
- end
145
- class Ephemeris # THE CORE EPHEMERIS CLASS
146
- # The repo for this class: https://github.com/isene/ephemeris
147
- attr_reader :sun, :moon, :mphase, :mph_s, :mercury, :venus, :mars, :jupiter, :saturn, :uranus, :neptune
148
-
149
- def body_data
150
- @body = {
151
- "sun" => {
152
- "N" => 0.0,
153
- "i" => 0.0,
154
- "w" => 282.9404 + 4.70935e-5 * @d,
155
- "a" => 1.000000,
156
- "e" => 0.016709 - 1.151e-9 * @d,
157
- "M" => 356.0470 + 0.98555 * @d},
158
- #"M" => 356.0470 + 0.9856002585 * @d},
159
- "moon" => {
160
- "N" => 125.1228 - 0.0529538083 * @d,
161
- "i" => 5.1454,
162
- "w" => 318.0634 + 0.1643573223 * @d,
163
- "a" => 60.2666,
164
- "e" => 0.054900,
165
- "M" => 115.3654 + 13.064886 * @d},
166
- #"M" => 115.3654 + 13.0649929509 * @d},
167
- "mercury" => {
168
- "N" => 48.3313 + 3.24587e-5 * @d,
169
- "i" => 7.0047 + 5.00e-8 * @d,
170
- "w" => 29.1241 + 1.01444e-5 * @d,
171
- "a" => 0.387098,
172
- "e" => 0.205635 + 5.59e-10 * @d,
173
- #"M" => 168.6562 + 4.09257 * @d},
174
- "M" => 168.6562 + 4.0923344368 * @d},
175
- "venus" => {
176
- "N" => 76.6799 + 2.46590e-5 * @d,
177
- "i" => 3.3946 + 2.75e-8 * @d,
178
- "w" => 54.8910 + 1.38374e-5 * @d,
179
- "a" => 0.723330,
180
- "e" => 0.006773 - 1.302e-9 * @d,
181
- #"M" => 48.0052 + 1.602206 * @d},
182
- "M" => 48.0052 + 1.6021302244 * @d},
183
- "mars" => {
184
- "N" => 49.5574 + 2.11081e-5 * @d,
185
- "i" => 1.8497 - 1.78e-8 * @d,
186
- "w" => 286.5016 + 2.92961e-5 * @d,
187
- "a" => 1.523688,
188
- "e" => 0.093405 + 2.516e-9 * @d,
189
- "M" => 18.6021 + 0.52398 * @d},
190
- #"M" => 18.6021 + 0.5240207766 * @d},
191
- "jupiter" => {
192
- "N" => 100.4542 + 2.76854e-5 * @d,
193
- "i" => 1.3030 - 1.557e-7 * @d,
194
- "w" => 273.8777 + 1.64505e-5 * @d,
195
- "a" => 5.20256,
196
- "e" => 0.048498 + 4.469e-9 * @d,
197
- "M" => 19.8950 + 0.083052 * @d},
198
- #"M" => 19.8950 + 0.0830853001 * @d},
199
- "saturn" => {
200
- "N" => 113.6634 + 2.38980e-5 * @d,
201
- "i" => 2.4886 - 1.081e-7 * @d,
202
- "w" => 339.3939 + 2.97661e-5 * @d,
203
- "a" => 9.55475,
204
- "e" => 0.055546 - 9.499e-9 * @d,
205
- "M" => 316.9670 + 0.03339 * @d},
206
- #"M" => 316.9670 + 0.0334442282 * @d},
207
- "uranus" => {
208
- "N" => 74.0005 + 1.3978e-5 * @d,
209
- "i" => 0.7733 + 1.9e-8 * @d,
210
- "w" => 96.6612 + 3.0565e-5 * @d,
211
- "a" => 19.18171 - 1.55e-8 * @d,
212
- "e" => 0.047318 + 7.45e-9 * @d,
213
- "M" => 142.5905 + 0.01168 * @d},
214
- #"M" => 142.5905 + 0.011725806 * @d},
215
- "neptune" => {
216
- "N" => 131.7806 + 3.0173e-5 * @d,
217
- "i" => 1.7700 - 2.55e-7 * @d,
218
- "w" => 272.8461 - 6.027e-6 * @d,
219
- "a" => 30.05826 + 3.313e-8 * @d,
220
- "e" => 0.008606 + 2.15e-9 * @d,
221
- "M" => 260.2471 + 0.005953 * @d}}
222
- #"M" => 260.2471 + 0.005995147 * @d}}
223
- end
224
-
225
- def hms_dms(ra, dec) # Show HMS & DMS
226
- h, m, s = (ra/15).hms
227
- #ra_hms = "#{h.to_s.rjust(2)}h #{m.to_s.rjust(2)}m #{s.to_s.rjust(2)}s"
228
- ra_hms = "#{h.to_s.rjust(2)}h #{m.to_s.rjust(2)}m"
229
- d, m, s = dec.hms
230
- #dec_dms = "#{d.to_s.rjust(3)}° #{m.to_s.rjust(2)}´ #{s.to_s.rjust(2)}˝"
231
- dec_dms = "#{d.to_s.rjust(3)}° #{m.to_s.rjust(2)}´"
232
- return ra_hms, dec_dms
233
- end
234
-
235
- def alt_az(ra, dec, time)
236
- pi = Math::PI
237
- ra_h = ra/15
238
- #ha = (@sidtime - ra_h)*15
239
- ha = (time - ra_h)*15
240
- x = Math.cos(ha.deg) * Math.cos(dec.deg)
241
- y = Math.sin(ha.deg) * Math.cos(dec.deg)
242
- z = Math.sin(dec.deg)
243
- xhor = x * Math.sin(@lat.deg) - z * Math.cos(@lat.deg)
244
- yhor = y
245
- zhor = x * Math.cos(@lat.deg) + z * Math.sin(@lat.deg)
246
- az = Math.atan2(yhor, xhor)*180/pi + 180
247
- alt = Math.asin(zhor)*180/pi
248
- return alt, az
249
- end
250
-
251
- def body_alt_az(body, time)
252
- self.alt_az(self.body_calc(body)[0], self.body_calc(body)[1], time)
253
- end
254
-
255
- def rts(ra, dec, h)
256
- pi = Math::PI
257
- transit = (ra - @ls - @lon)/15 + 12 + @tz
258
- transit = (transit + 24) % 24
259
- cos_lha = (Math.sin(h.deg) - (Math.sin(@lat.deg)*Math.sin(dec.deg))) / (Math.cos(@lat.deg) * Math.cos(dec.deg))
260
- if cos_lha < -1
261
- rise = "always"
262
- set = "never"
263
- elsif cos_lha > 1
264
- rise = "never"
265
- set = "always"
266
- else
267
- lha = Math.acos(cos_lha) * 180/pi
268
- lha_h = lha/15
269
- rise = ((transit - lha_h + 24) % 24).to_hms
270
- set = ((transit + lha_h + 24) % 24).to_hms
271
- end
272
- trans = transit.to_hms
273
- return rise, trans, set
274
- end
275
-
276
- def print
277
-
278
- def distf(d)
279
- int = d.to_i.to_s.rjust(2)
280
- f = d % 1
281
- frc = "%.4f" % f
282
- return int + frc[1..5]
283
- end
284
-
285
- out = "Planet │ RA │ Dec │ d=AU │ Rise │ Trans │ Set \n"
286
- out += "────────┼─────────┼──────────┼───────┼───────┼───────┼────── \n"
287
-
288
- ["sun", "moon", "mercury", "venus", "mars", "jupiter", "saturn", "uranus", "neptune"].each do |p|
289
- o = self.body_calc(p)
290
- n_o = (p[0].upcase + p[1..-1]).ljust(7)
291
- ra_o = o[3].ljust(7)
292
- dec_o = o[4].ljust(2)
293
- o[2].class == Float ? d_o = distf(o[2])[0..-3] : d_o = o[2]
294
- ris_o = o[5][0..-4].rjust(5)
295
- tra_o = o[6][0..-4].rjust(5)
296
- set_o = o[7][0..-4].rjust(5)
297
-
298
- out += "#{n_o } │ #{ra_o } │ #{dec_o } │ #{d_o } │ #{ris_o} │ #{tra_o} │ #{set_o} \n"
299
- end
300
- return out
301
- end
302
-
303
- def initialize (date, lat, lon, tz)
304
- pi = Math::PI
305
-
306
- def get_vars(body) # GET VARIABLES FOR THE BODY
307
- b = @body[body]
308
- return b["N"], b["i"], b["w"], b["a"], b["e"], b["M"]
309
- end
310
-
311
- def body_calc(body) # CALCULATE FOR THE BODY
312
- pi = Math::PI
313
- n_b, i_b, w_b, a_b, e_b, m_b = self.get_vars(body)
314
- w_b = (w_b + 360) % 360
315
- m_b = m_b % 360
316
- e1 = m_b + (180/pi) * e_b * Math.sin(m_b.deg) * (1 + e_b*Math.cos(m_b.deg))
317
- e0 = 0
318
- while (e1 - e0).abs > 0.0005
319
- e0 = e1
320
- e1 = e0 - (e0 - (180/pi) * e_b * Math.sin(e0.deg) - m_b) / (1 - e_b * Math.cos(e0.deg))
321
- end
322
- e = e1
323
- x = a_b * (Math.cos(e.deg) - e_b)
324
- y = a_b * Math.sqrt(1 - e_b*e_b) * Math.sin(e.deg)
325
- r = Math.sqrt(x*x + y*y)
326
- v = (Math.atan2(y, x)*180/pi + 360) % 360
327
- xeclip = r * (Math.cos(n_b.deg) * Math.cos((v+w_b).deg) - Math.sin(n_b.deg) * Math.sin((v+w_b).deg) * Math.cos(i_b.deg))
328
- yeclip = r * (Math.sin(n_b.deg) * Math.cos((v+w_b).deg) + Math.cos(n_b.deg) * Math.sin((v+w_b).deg) * Math.cos(i_b.deg))
329
- zeclip = r * Math.sin((v+w_b).deg) * Math.sin(i_b.deg)
330
- lon = (Math.atan2(yeclip, xeclip)*180/pi + 360) % 360
331
- lat = Math.atan2(zeclip, Math.sqrt(xeclip*xeclip + yeclip*yeclip))*180/pi
332
- r_b = Math.sqrt(xeclip*xeclip + yeclip*yeclip + zeclip*zeclip)
333
- m_J = @body["jupiter"]["M"]
334
- m_S = @body["saturn"]["M"]
335
- m_U = @body["uranus"]["M"]
336
- plon = 0
337
- plat = 0
338
- pdist = 0
339
- case body
340
- when "moon"
341
- lb = (n_b + w_b + m_b) % 360
342
- db = (lb - @ls + 360) % 360
343
- fb = (lb - n_b + 360) % 360
344
- plon += -1.274 * Math.sin((m_b - 2*db).deg)
345
- plon += 0.658 * Math.sin((2*db).deg)
346
- plon += -0.186 * Math.sin(@ms.deg)
347
- plon += -0.059 * Math.sin((2*m_b - 2*db).deg)
348
- plon += -0.057 * Math.sin((m_b - 2*db + @ms).deg)
349
- plon += 0.053 * Math.sin((m_b + 2*db).deg)
350
- plon += 0.046 * Math.sin((2*db - @ms).deg)
351
- plon += 0.041 * Math.sin((m_b - @ms).deg)
352
- plon += -0.035 * Math.sin(db.deg)
353
- plon += -0.031 * Math.sin((m_b + @ms).deg)
354
- plon += -0.015 * Math.sin((2*fb - 2*db).deg)
355
- plon += 0.011 * Math.sin((m_b - 4*db).deg)
356
- plat += -0.173 * Math.sin((fb - 2*db).deg)
357
- plat += -0.055 * Math.sin((m_b - fb - 2*db).deg)
358
- plat += -0.046 * Math.sin((m_b + fb - 2*db).deg)
359
- plat += 0.033 * Math.sin((fb + 2*db).deg)
360
- plat += 0.017 * Math.sin((2*m_b + fb).deg)
361
- pdist += -0.58 * Math.cos((m_b - 2*db).deg)
362
- pdist += -0.46 * Math.cos(2*db.deg)
363
- when "jupiter"
364
- plon += -0.332 * Math.sin((2*m_J - 5*m_S - 67.6).deg)
365
- plon += -0.056 * Math.sin((2*m_J - 2*m_S + 21).deg)
366
- plon += 0.042 * Math.sin((3*m_J - 5*m_S + 21).deg)
367
- plon += -0.036 * Math.sin((m_J - 2*m_S).deg)
368
- plon += 0.022 * Math.cos((m_J - m_S).deg)
369
- plon += 0.023 * Math.sin((2*m_J - 3*m_S + 52).deg)
370
- plon += -0.016 * Math.sin((m_J - 5*m_S - 69).deg)
371
- when "saturn"
372
- plon += 0.812 * Math.sin((2*m_J - 5*m_S - 67.6).deg)
373
- plon += -0.229 * Math.cos((2*m_J - 4*m_S - 2).deg)
374
- plon += 0.119 * Math.sin((m_J - 2*m_S - 3).deg)
375
- plon += 0.046 * Math.sin((2*m_J - 6*m_S - 69).deg)
376
- plon += 0.014 * Math.sin((m_J - 3*m_S + 32).deg)
377
- plat += -0.020 * Math.cos((2*m_J - 4*m_S - 2).deg)
378
- plat += 0.018 * Math.sin((2*m_J - 6*m_S - 49).deg)
379
- when "uranus"
380
- plon += 0.040 * Math.sin((m_S - 2*m_U + 6).deg)
381
- plon += 0.035 * Math.sin((m_S - 3*m_U + 33).deg)
382
- plon += -0.015 * Math.sin((m_J - m_U + 20).deg)
383
- end
384
- lon += plon
385
- lat += plat
386
- r_b += pdist
387
- if body == "moon"
388
- xeclip = Math.cos(lon.deg) * Math.cos(lat.deg)
389
- yeclip = Math.sin(lon.deg) * Math.cos(lat.deg)
390
- zeclip = Math.sin(lat.deg)
391
- else
392
- xeclip += @xs
393
- yeclip += @ys
394
- end
395
- xequat = xeclip
396
- yequat = yeclip * Math.cos(@ecl.deg) - zeclip * Math.sin(@ecl.deg)
397
- zequat = yeclip * Math.sin(@ecl.deg) + zeclip * Math.cos(@ecl.deg)
398
- ra = (Math.atan2(yequat, xequat)*180/pi + 360) % 360
399
- dec = Math.atan2(zequat, Math.sqrt(xequat*xequat + yequat*yequat))*180/pi
400
- body == "moon" ? par = Math.asin(1/r_b)*180/pi : par = (8.794/3600)/r_b
401
- gclat = @lat - 0.1924 * Math.sin(2*@lat.deg)
402
- rho = 0.99833 + 0.00167 * Math.cos(2*@lat.deg)
403
- lst = @sidtime * 15
404
- ha = (lst - ra + 360) % 360
405
- g = Math.atan(Math.tan(gclat.deg) / Math.cos(ha.deg))*180/pi
406
- topRA = ra - par * rho * Math.cos(gclat.deg) * Math.sin(ha.deg) / Math.cos(dec.deg)
407
- topDecl = dec - par * rho * Math.sin(gclat.deg) * Math.sin((g - dec).deg) / Math.sin(g.deg)
408
- ra = topRA.round(4)
409
- dec = topDecl.round(4)
410
- r = Math.sqrt(xequat*xequat + yequat*yequat + zequat*zequat).round(4)
411
- h = 0
412
- if body == "moon"
413
- r = " - "
414
- h = -0.833
415
- elsif body == "sun"
416
- ra = @ra_s
417
- dec = @dec_s
418
- r = 1.0
419
- h = -0.833
420
- end
421
- ri, tr, se = self.rts(ra, dec, h)
422
- object = [ra, dec, r, self.hms_dms(ra, dec), ri, tr, se].flatten
423
- return object
424
- end
425
-
426
- # START OF INITIALIZE
427
- @lat = lat
428
- @lon = lon
429
- @tz = tz
430
- y = date[0..3].to_i
431
- m = date[5..6].to_i
432
- d = date[8..9].to_i
433
- @d = 367*y - 7*(y + (m+9)/12) / 4 + 275*m/9 + d - 730530
434
- @ecl = 23.4393 - 3.563E-7*@d
435
-
436
- self.body_data
437
-
438
- # SUN
439
- n_s, i_s, w_s, a_s, e_s, m_s = self.get_vars("sun")
440
- w_s = (w_s + 360) % 360
441
- @ms = m_s % 360
442
- es = @ms + (180/pi) * e_s * Math.sin(@ms.deg) * (1 + e_s*Math.cos(@ms.deg))
443
- x = Math.cos(es.deg) - e_s
444
- y = Math.sin(es.deg) * Math.sqrt(1 - e_s*e_s)
445
- v = Math.atan2(y,x)*180/pi
446
- r = Math.sqrt(x*x + y*y)
447
- tlon = (v + w_s)%360
448
- @xs = r * Math.cos(tlon.deg)
449
- @ys = r * Math.sin(tlon.deg)
450
- xe = @xs
451
- ye = @ys * Math.cos(@ecl.deg)
452
- ze = @ys * Math.sin(@ecl.deg)
453
- r = Math.sqrt(xe*xe + ye*ye + ze*ze)
454
- ra = Math.atan2(ye,xe)*180/pi
455
- @ra_s = ((ra + 360)%360).round(4)
456
- @dec_s = (Math.atan2(ze,Math.sqrt(xe*xe + ye*ye))*180/pi).round(4)
457
-
458
- @ls = (w_s + @ms)%360
459
- gmst0 = (@ls + 180)/15%24
460
- @sidtime = gmst0 + @lon/15
461
-
462
- @alt_s, @az_s = self.alt_az(@ra_s, @dec_s, @sidtime)
463
-
464
- @sun = self.body_calc("sun").flatten
465
- @moon = self.body_calc("moon").flatten
466
-
467
- mp = 29.530588861
468
- nm = 2459198.177777778
469
- jd = Date.parse(date).ajd.to_f
470
- @mphase = 100*((jd - nm) % mp) / mp
471
- if @mphase < 2.5
472
- @mph_s = "New moon"
473
- elsif @mphase < 27.5
474
- @mph_s = "Waxing crescent"
475
- elsif @mphase < 32.5
476
- @mph_s = "First quarter"
477
- elsif @mphase < 47.5
478
- @mph_s = "Waxing gibbous"
479
- elsif @mphase < 52.5
480
- @mph_s = "Full moon"
481
- elsif @mphase < 72.5
482
- @mph_s = "Waning gibbous"
483
- elsif @mphase < 77.5
484
- @mph_s = "Last quarter"
485
- elsif @mphase < 97.5
486
- @mph_s = "Waning crescent"
487
- else
488
- @mph_s = "New moon"
489
- end
490
-
491
- @mercury = self.body_calc("mercury").flatten
492
- @venus = self.body_calc("venus").flatten
493
- @mars = self.body_calc("mars").flatten
494
- @jupiter = self.body_calc("jupiter").flatten
495
- @saturn = self.body_calc("saturn").flatten
496
- @uranus = self.body_calc("uranus").flatten
497
- @neptune = self.body_calc("neptune").flatten
498
-
499
- end
500
- end
501
- class String # CLASS EXTENSION
502
- def decoder
503
- self.gsub(/&#(\d+);/) { |m| $1.to_i(10).chr(Encoding::UTF_8) }
504
- end
505
- end
506
- class Curses::Window # CLASS EXTENSION
507
- attr_accessor :fg, :bg, :attr, :text, :update, :pager, :pager_more, :pager_cmd, :locate, :nohistory
508
- # General extensions (see https://github.com/isene/Ruby-Curses-Class-Extension)
509
- def clr
510
- self.setpos(0, 0)
511
- self.maxy.times {self.deleteln()}
512
- self.refresh
513
- self.setpos(0, 0)
514
- end
515
- def fill # Fill window with color as set by :bg
516
- self.setpos(0, 0)
517
- self.bg = 0 if self.bg == nil
518
- self.fg = 255 if self.fg == nil
519
- init_pair(self.fg, self.fg, self.bg)
520
- blank = " " * self.maxx
521
- self.maxy.times {self.attron(color_pair(self.fg)) {self << blank}}
522
- self.refresh
523
- self.setpos(0, 0)
524
- end
525
- def write # Write context of :text to window with attributes :attr
526
- self.bg = 0 if self.bg == nil
527
- self.fg = 255 if self.fg == nil
528
- init_pair(self.fg, self.fg, self.bg)
529
- self.attr = 0 if self.attr == nil
530
- self.attron(color_pair(self.fg) | self.attr) { self << self.text }
531
- self.refresh
532
- self.text = ""
533
- end
534
- def p(fg, bg, attr, text)
535
- init_pair(fg, fg, bg)
536
- self.attron(color_pair(fg) | attr) { self << text }
537
- self.refresh
538
- end
539
- end
540
- # GENERIC FUNCTIONS
541
- def getchr # PROCESS KEY PRESSES
542
- # Note: Curses.getch blanks out @w_t
543
- # @w_l.getch makes Curses::KEY_DOWN etc not work
544
- # Therefore resorting to the generic method
545
- c = STDIN.getch(min: 0, time: 1)
546
- case c
547
- when "\e" # ANSI escape sequences
548
- case $stdin.getc
549
- when '[' # CSI
550
- case $stdin.getc
551
- when 'A' then chr = "UP"
552
- when 'B' then chr = "DOWN"
553
- when 'C' then chr = "RIGHT"
554
- when 'D' then chr = "LEFT"
555
- when 'Z' then chr = "S-TAB"
556
- when '2' then chr = "INS" ; STDIN.getc
557
- when '3' then chr = "DEL" ; STDIN.getc
558
- when '5' then chr = "PgUP" ; STDIN.getc
559
- when '6' then chr = "PgDOWN" ; STDIN.getc
560
- when '7' then chr = "HOME" ; STDIN.getc
561
- when '8' then chr = "END" ; STDIN.getc
562
- end
563
- end
564
- when "", "" then chr = "BACK"
565
- when "" then chr = "WBACK"
566
- when "" then chr = "LDEL"
567
- when "" then chr = "C-T"
568
- when "\r" then chr = "ENTER"
569
- when "\t" then chr = "TAB"
570
- when /./ then chr = c
571
- end
572
- return chr
573
- end
574
- def main_getkey # GET KEY FROM USER
575
- chr = getchr
576
- case chr
577
- when '?' # Show helptext in right window
578
- w_u_msg(@help)
579
- when 'UP'
580
- @index = @index <= @min_index ? @max_index : @index - 1
581
- @w_u.update = true
582
- when 'DOWN'
583
- @index = @index >= @max_index ? @min_index : @index + 1
584
- @w_u.update = true
585
- when 'PgUP'
586
- @index -= @w_l.maxy - 2
587
- @index = @min_index if @index < @min_index
588
- @w_u.update = true
589
- when 'PgDOWN'
590
- @index += @w_l.maxy - 2
591
- @index = @max_index if @index > @max_index
592
- @w_u.update = true
593
- when 'HOME'
594
- @index = @min_index
595
- @w_u.update = true
596
- when 'END'
597
- @index = @max_index
598
- @w_u.update = true
599
- when 'l'
600
- @loc = w_b_getstr("Loc: ", @loc)
601
- @w_u.update = true
602
- when 'a'
603
- @lat = w_b_getstr("Lat: ", @lat.to_s).to_f
604
- @w_u.update = true
605
- when 'o'
606
- @lon = w_b_getstr("Lon: ", @lon.to_s).to_f
607
- @w_u.update = true
608
- when 'c'
609
- @cloudlimit = w_b_getstr("Cloudlimit: ", @cloudlimit.to_s).to_i
610
- @w_u.update = true
611
- when 'h'
612
- @humiditylimit = w_b_getstr("Humiditylimit: ", @humiditylimit.to_s).to_i
613
- @w_u.update = true
614
- when 't'
615
- @templimit = w_b_getstr("Templimit: ", @templimit.to_s).to_i
616
- @w_u.update = true
617
- when 'w'
618
- @windlimit = w_b_getstr("Windlimit: ", @windlimit.to_s).to_i
619
- @w_u.update = true
620
- when 'b'
621
- @bortle = w_b_getstr("Bortle: ", @bortle.to_s).to_f.round(1)
622
- @w_u.update = true
623
- when 'e'
624
- ev = "\nUPCOMING EVENTS:\n\n"
625
- @events.each do |key, val|
626
- ev += key + " " + val["time"] + " " + val["event"] + "\n"
627
- end
628
- w_u_msg(ev)
629
- when 's'
630
- starchart
631
- @image = "/tmp/starchart.jpg"
632
- image_show("clear")
633
- image_show(@image)
634
- when 'S'
635
- begin
636
- Thread.new { system("xdg-open '/tmp/starchart.jpg'") }
637
- rescue
638
- end
639
- @break = true
640
- when 'A'
641
- image_show("clear")
642
- @image = "/tmp/apod.jpg"
643
- image_show(@image)
644
- when 'ENTER' # Refresh image
645
- image_show(@image)
646
- @w_u.update = true
647
- when 'r' # Refresh all windows
648
- @break = true
649
- when '@' # Enter "Ruby debug"
650
- @w_b.nohistory = false
651
- cmd = w_b_getstr("◆ ", "")
652
- begin
653
- @w_b.text = eval(cmd)
654
- @w_b.fill
655
- @w_b.write
656
- rescue StandardError => e
657
- w_b_info("Error: #{e.inspect}")
658
- end
659
- @w_b.update = false
660
- when 'R' # Reload .ap.conf
661
- if File.exist?(Dir.home+'/.ap.conf')
662
- load(Dir.home+'/.ap.conf')
663
- end
664
- w_b_info(" Config reloaded")
665
- @w_b.update = false
666
- when 'W' # Write all parameters to .ap.conf
667
- @write_conf_all = true
668
- conf_write
669
- when 'q' # Exit
670
- @write_conf = true
671
- exit 0
672
- when 'Q' # Exit without writing to .ap.conf
673
- @write_conf = false
674
- exit 0
675
- end
676
- end
677
- def get_weather # WEATHER FORECAST FROM MET.NO
678
- begin
679
- uri = URI.parse("https://api.met.no/weatherapi/locationforecast/2.0/complete?lat=#{@lat}&lon=#{@lon}")
680
- req = Net::HTTP::Get.new(uri)
681
- req["User-Agent"] = "astropanel/1.0 g@isene.com"
682
- json = Net::HTTP.start(uri.hostname, uri.port, :use_ssl => true) do |http|
683
- http.request(req)
684
- end
685
- weather_data = JSON.parse(json.body)
686
- @weather_point = weather_data["properties"]["timeseries"]
687
- weather_size = @weather_point.size
688
- @weather = []
689
- weather_size.times do |t|
690
- details = @weather_point[t]["data"]["instant"]["details"]
691
- time = @weather_point[t]["time"]
692
- date = time[0..9]
693
- hour = time[11..12]
694
- wthr = details["cloud_area_fraction"].to_i.to_s.rjust(5) + "%"
695
- wthr += details["relative_humidity"].to_s.rjust(7)
696
- wthr += details["air_temperature"].to_s.rjust(6)
697
- wind = details["wind_speed"].to_s + " ("
698
- case details["wind_from_direction"].to_i
699
- when 0..22
700
- wdir = "N"
701
- when 23..67
702
- wdir = "NE"
703
- when 68..112
704
- wdir = "E"
705
- when 113..158
706
- wdir = "SE"
707
- when 159..203
708
- wdir = "S"
709
- when 204..249
710
- wdir = "SW"
711
- when 250..294
712
- wdir = "W"
713
- when 295..340
714
- wdir = "NW"
715
- else
716
- wdir = "N"
717
- end
718
- wind += wdir.rjust(2)
719
- wthr += wind.rjust(10) + ")"
720
- info = date + " (" + Date.parse(date).strftime("%A") + ") #{hour}:00\n\n"
721
- cld = "Clouds (-/+) " + details["cloud_area_fraction"].to_i.to_s + "% ("
722
- cld += details["cloud_area_fraction_low"].to_i.to_s + "% " + details["cloud_area_fraction_high"].to_i.to_s + "%)"
723
- info += cld.ljust(34)
724
- details["fog_area_fraction"] == 0 ? fog = "-" : fog = (details["fog_area_fraction"].to_f.round(1)).to_s + "%"
725
- info += "Humidity (fog) " + details["relative_humidity"].to_s + "% (" + fog + ")\n"
726
- wnd = "Wind [gusts] " + details["wind_speed"].to_s + " m/s (" + wdir + ") [" + details["wind_speed_of_gust"].to_s + "]"
727
- info += wnd.ljust(34)
728
- info += "Temp (dew) " + details["air_temperature"].to_s + "°C ("
729
- info += details["dew_point_temperature"].to_s + "°C)\n"
730
- air = "Air pressure " + details["air_pressure_at_sea_level"].to_i.to_s + " hPa "
731
- info += air.ljust(34)
732
- uv = details["ultraviolet_index_clear_sky"].to_s
733
- uv = "-" if uv == ""
734
- info += "UV index " + uv + "\n"
735
- @weather.push([date, hour, wthr, info])
736
- rescue
737
- w_b_info("Not able to retrieve weather data from met.no")
738
- end
739
- end
740
- end
741
- def get_planets # PLANET EPHEMERIS DATA
742
- planets = {}
743
- 12.times do |x|
744
- date = (Time.now + 86400 * (x - 1)).strftime("%F")
745
- p = Ephemeris.new(date, @lat, @lon, @tz.to_i)
746
- planets[date] = {"table" => p.print,
747
- "srise" => p.sun[5], "sset" => p.sun[7],
748
- "mrise" => p.moon[5], "mset" => p.moon[7],
749
- "phase" => p.mphase, "ph_s" => p.mph_s,
750
- "Mrise" => p.mercury[5], "Mset" => p.mercury[7],
751
- "Vrise" => p.venus[5], "Vset" => p.venus[7],
752
- "Arise" => p.mars[5], "Aset" => p.mars[7],
753
- "Jrise" => p.jupiter[5], "Jset" => p.jupiter[7],
754
- "Srise" => p.saturn[5], "Sset" => p.saturn[7],
755
- "Urise" => p.uranus[5], "Uset" => p.uranus[7],
756
- "Nrise" => p.neptune[5], "Nset" => p.neptune[7]}
757
- end
758
- return planets
759
- end
760
- def get_events # ASTRONOMICAL EVENTS
761
- events = {}
762
- eventsURI = "https://in-the-sky.org//rss.php?feed=dfan&latitude=#{@lat}&longitude=#{@lon}&timezone=#{@loc}"
763
- events_rss = Net::HTTP.get(URI(eventsURI))
764
- events_data = events_rss.scan(/<item>.*?<\/item>/m)
765
- events_data.each do |e|
766
- date = Time.parse(e[/<title>(.{11})/,1]).strftime("%F")
767
- time = e[/\d\d:\d\d:\d\d/]
768
- event = e[/<description>&lt;p&gt;(.*?).&lt;\/p&gt;<\/description>/,1].decoder
769
- event.gsub!(/&amp;deg;/, "°")
770
- event.gsub!(/&amp;#39;/, "'")
771
- link = e[/<link>(.*?)<\/link>/,1]
772
- events[date] = {"time" => time, "event" => event, "link" => link} if date >= @today
773
- end
774
- return events
775
- end
776
- def get_cond(t) # GREEN/YELLOW/RED FROM CONDITIONS
777
- details = @weather_point[t]["data"]["instant"]["details"]
778
- cond = 0
779
- cond += 1 if details["cloud_area_fraction"].to_i > @cloudlimit
780
- cond += 1 if details["cloud_area_fraction"].to_i > @cloudlimit + (100 - @cloudlimit)/2
781
- cond += 2 if details["cloud_area_fraction"].to_i > 90
782
- cond += 1 if details["relative_humidity"].to_i > @humiditylimit
783
- cond += 1 if details["air_temperature"].to_i < @templimit
784
- cond += 1 if details["air_temperature"].to_i + 7 < @templimit
785
- cond += 1 if details["wind_speed"].to_i > @windlimit
786
- cond += 1 if details["wind_speed"].to_i > @windlimit * 2
787
- case cond
788
- when 0..1
789
- return 1
790
- when 2..3
791
- return 2
792
- else
793
- return 3
794
- end
795
- end
796
- def conf_write # WRITE TO .AP.CONF
797
- if File.exist?(Dir.home+'/.ap.conf')
798
- conf = File.read(Dir.home+'/.ap.conf')
799
- else
800
- conf = ""
801
- end
802
- if @write_conf_all
803
- conf.sub!(/^@loc.*/, "@loc = \"#{@loc}\"")
804
- conf.sub!(/^@lat.*/, "@lat = #{@lat}")
805
- conf.sub!(/^@lon.*/, "@lon = #{@lon}")
806
- conf.sub!(/^@cloudlimit.*/, "@cloudlimit = #{@cloudlimit}")
807
- conf.sub!(/^@humiditylimit.*/, "@humiditylimit = #{@humiditylimit}")
808
- conf.sub!(/^@templimit.*/, "@templimit = #{@templimit}")
809
- conf.sub!(/^@windlimit.*/, "@windlimit = #{@windlimit}")
810
- w_u_msg("Press W again to write this to .ap.conf:\n\n" + conf)
811
- if getchr == 'W'
812
- w_b_info(" Parameters written to .ap.conf")
813
- @w_b.update = false
814
- else
815
- w_b_info(" Config NOT updated")
816
- @w_b.update = false
817
- return
818
- end
819
- end
820
- File.write(Dir.home+'/.ap.conf', conf)
821
- end
822
- # TOP WINDOW FUNCTIONS
823
- def w_t_info # SHOW INFO IN @w_t
824
- @w_t.clr
825
- text = " #{@loc} (tz=#{'%02d' % @tz}) Lat: #{@lat}, Lon: #{@lon} "
826
- text += "(Bortle #{@bortle}) "
827
- text += "Updated: #{@time} (JD: 24#{DateTime.now.amjd().to_f.round(4) + 0.5})"
828
- text += " " * (@w_t.maxx - text.length) if text.length < @w_t.maxx
829
- @w_t.text = text
830
- @w_t.write
831
- end
832
- # LEFT WINDOW FUNCTIONS
833
- def print_p(ix, date, rise, set, c)
834
- @w_l << " "
835
- m = "┃"
836
- m = "█" if rise == "srise" or rise == "mrise"
837
- if @planets[date][set] == "never"
838
- @w_l.p(c,0,0,m)
839
- elsif @planets[date][set][0..1] < @planets[date][rise][0..1] and @weather[ix][1] <= @planets[date][set][0..1]
840
- @w_l.p(c,0,0,m)
841
- elsif @planets[date][set][0..1] < @planets[date][rise][0..1] and @weather[ix][1] >= @planets[date][rise][0..1]
842
- @w_l.p(c,0,0,m)
843
- elsif @weather[ix][1] >= @planets[date][rise][0..1] and @weather[ix][1] <= @planets[date][set][0..1]
844
- @w_l.p(c,0,0,m)
845
- else
846
- @w_l << " "
847
- end
848
- end
849
- def w_l_info # SHOW WEATHER CONDITION AND RISE/SET IN @w_l
850
- @w_l.clr
851
- @w_l.p(254, 238, 0, "YYYY-MM-DD HH Cld% Hum% °C Wind m/s * ● ○ m V M J S U N\n")
852
- ix = 0; t = 1; prev_date = ""
853
- ix = @index - @w_l.maxy/2 if @index > @w_l.maxy/2 and @weather.size > @w_l.maxy
854
- while ix < @weather.size and t < @w_l.maxy do
855
- marker = 0
856
- color = color_pair(get_cond(ix))
857
- date = @weather[ix][0]
858
- date == prev_date ? line = " " : line = date + " "
859
- @w_l.attron(color) { @w_l << line }
860
- marker = Curses::A_UNDERLINE if ix == @index
861
- line = @weather[ix][1] + @weather[ix][2]
862
- @w_l.attron(color | marker) { @w_l << line }
863
- if @events.has_key?(date)
864
- @events[date]["time"][0..1] == @weather[ix][1] ? line = " !" : line = " "
865
- else
866
- line = " "
867
- end
868
- @w_l.attron(color) { @w_l << line }
869
- begin
870
- print_p(ix, date, "srise", "sset", 226)
871
- c0 = ((50 - (@planets[date]["phase"] - 50).abs)/2.7 + 237).to_i
872
- print_p(ix, date, "mrise", "mset", c0)
873
- print_p(ix, date, "Mrise", "Mset", 130)
874
- print_p(ix, date, "Vrise", "Vset", 153)
875
- print_p(ix, date, "Arise", "Aset", 124)
876
- print_p(ix, date, "Jrise", "Jset", 108)
877
- print_p(ix, date, "Srise", "Sset", 142)
878
- print_p(ix, date, "Urise", "Uset", 24)
879
- print_p(ix, date, "Nrise", "Nset", 27)
880
- rescue
881
- end
882
- clrtoeol
883
- @w_l << "\n"
884
- prev_date = date unless date == prev_date
885
- ix += 1
886
- t += 1
887
- end
888
- @w_l.refresh
889
- end
890
- # RIGHT UPPER WINDOW FUNCTIONS
891
- def w_u_msg(msg) # MESSAGES IN @w_u
892
- @w_u.clr
893
- @w_u.text = msg
894
- @w_u.write
895
- @w_u.update = false
896
- end
897
- def w_u_info # ASTRO INFO IN @w_u
898
- @w_u.clr
899
- color = color_pair(get_cond(@index))
900
- info = @weather[@index][3].split("\n")
901
- # Moon phase
902
- mp = 29.530588861
903
- nm = 2459198.177777778
904
- fm = nm + mp/2
905
- y = @weather[@index][0][0..3].to_i
906
- m = @weather[@index][0][5..6].to_i
907
- d = @weather[@index][0][8..9].to_i
908
- h = @weather[@index][1].to_i
909
- jd = DateTime.new(y, m, d, h, 0, 0, @tz).ajd.to_f
910
- mp_n = (100*((jd - nm) % mp) / mp).round(1)
911
- ph_a = ((jd - fm) % mp) / mp * 360
912
- mp_ip = ((1 + Math.cos(ph_a.deg))*50).to_i
913
- mp_s = @planets[@weather[@index][0]]["ph_s"]
914
- title = info[0] + " (Moon: #{mp_n}/#{mp_ip}% #{mp_s})"
915
- @w_u.attron(color) { @w_u << title }
916
- @w_u.write
917
- info.shift
918
- @w_u.maxx < Curses.cols ? maxx = @w_u.maxx : maxx = Curses.cols
919
- info.each_with_index do |line, index|
920
- line += " "*(maxx - line.length - 1)
921
- info[index] = line
922
- end
923
- @w_u.text = info.join("\n")
924
- @w_u.text += "\n\n"
925
- @w_u.text += @planets[@weather[@index][0]]["table"]
926
- @w_u.write
927
- date = @weather[@index][0]
928
-
929
- @w_u << "\n"
930
- if @events.has_key?(date)
931
- text = "@ " + @events[date]["time"] + ": "
932
- text += @events[date]["event"] + "\n"
933
- text += @events[date]["link"] + "\n"
934
- if @events[date]["time"][0..1] == @weather[@index][1]
935
- @w_u.p(111,0,Curses::A_BOLD,text)
936
- else
937
- @w_u.text = text
938
- @w_u.write
939
- end
940
- end
941
- end
942
- # RIGHT LOWER WINDOW FUNCTIONS
943
- def image_show(image)# SHOW THE SELECTED IMAGE IN TOP RIGHT WINDOW
944
- return unless @showimage
945
- return if @noimage
946
- # Pass "clear" to clear the window for previous image
947
- begin
948
- terminfo = `xwininfo -id $(xdotool getactivewindow)`
949
- term_w = terminfo.match(/Width: (\d+)/)[1].to_i
950
- term_h = terminfo.match(/Height: (\d+)/)[1].to_i
951
- char_w = term_w / Curses.cols
952
- char_h = term_h / Curses.lines
953
- img_x = char_w * (@w_l_width + 1)
954
- img_y = char_h * 23
955
- img_max_w = char_w * (Curses.cols - @w_l_width - 2)
956
- img_max_h = char_h * (@w_d.maxy - 2)
957
- if image == "clear"
958
- img_max_w += 2
959
- img_max_h += 2
960
- `echo "6;#{img_x};#{img_y};#{img_max_w};#{img_max_h};\n4;\n3;" | #{@w3mimgdisplay} 2>/dev/null`
961
- else
962
- img_w,img_h = `identify -format "%[fx:w]x%[fx:h]" #{image} 2>/dev/null`.split('x')
963
- img_w = img_w.to_i
964
- img_h = img_h.to_i
965
- if img_w > img_max_w
966
- img_h = img_h * img_max_w / img_w
967
- img_w = img_max_w
968
- end
969
- if img_h > img_max_h
970
- img_w = img_w * img_max_h / img_h
971
- img_h = img_max_h
972
- end
973
- `echo "0;1;#{img_x};#{img_y};#{img_w};#{img_h};;;;;\"#{image}\"\n4;\n3;" | #{@w3mimgdisplay} 2>/dev/null`
974
- end
975
- rescue
976
- w_b_info("Error showing image")
977
- end
978
- end
979
- def starchart # GET AND SHOW STARCHART FOR SELECTED TIME
980
- d = Time.parse(@weather[@index][0]).strftime("%d").to_i
981
- m = Time.parse(@weather[@index][0]).strftime("%m").to_i
982
- y = Time.parse(@weather[@index][0]).strftime("%Y").to_i
983
- starchartURI = "https://www.stelvision.com/carte-ciel/visu_carte.php?stelmarq=C&mode_affichage=normal&req=stel&date_j_carte=#{d}&date_m_carte=#{m}&date_a_carte=#{y}&heure_h=#{@weather[@index][1].to_i}&heure_m=00&longi=#{@lon}&lat=#{@lat}&tzone=#{@tz.to_i}.0&dst_offset=1&taille_carte=1200&fond_r=255&fond_v=255&fond_b=255&lang=en"
984
- `curl -s "#{starchartURI}" > /tmp/stars.png`
985
- `convert /tmp/stars.png /tmp/starchart.jpg`
986
- end
987
- def apod # GET ASTRONOMY PICTRUE OF THE DAY
988
- apod = Net::HTTP.get(URI("https://apod.nasa.gov/apod/astropix.html"))
989
- apod.sub!(/^.*IMG SRC=./m, "")
990
- apod.sub!(/\".*/m, "")
991
- apod = "https://apod.nasa.gov/apod/" + apod
992
- `curl -s "#{apod}" > /tmp/apod.jpg`
993
- end
994
- # BOTTOM WINDOW FUNCTIONS
995
- def w_b_info(info) # SHOW INFO IN @W_B
996
- @w_b.clr
997
- info = "?=Show Help | Edit: l=Loc a=Lat o=Lon | s=Starchart for selected time | ENTER=Refresh r=Redraw q=Quit Q=Quit (no config save)" if info == nil
998
- info = info[1..(@w_b.maxx - 3)] + "…" if info.length + 3 > @w_b.maxx
999
- info += " " * (@w_b.maxx - info.length) if info.length < @w_b.maxx
1000
- @w_b.text = info
1001
- @w_b.write
1002
- @w_b.update = false
1003
- end
1004
- def w_b_getstr(pretext, text) # A SIMPLE READLINE-LIKE ROUTINE
1005
- Curses.curs_set(1)
1006
- Curses.echo
1007
- stk = 0
1008
- @history.insert(stk, text)
1009
- pos = @history[stk].length
1010
- chr = ""
1011
- while chr != "ENTER"
1012
- @w_b.setpos(0,0)
1013
- @w_b.text = pretext + @history[stk]
1014
- @w_b.text += " " * (@w_b.maxx - text.length) if text.length < @w_b.maxx
1015
- @w_b.write
1016
- @w_b.setpos(0,pretext.length + pos)
1017
- @w_b.refresh
1018
- chr = getchr
1019
- case chr
1020
- when 'UP'
1021
- unless @w_b.nohistory
1022
- unless stk == @history.length - 1
1023
- stk += 1
1024
- pos = @history[stk].length
1025
- end
1026
- end
1027
- when 'DOWN'
1028
- unless @w_b.nohistory
1029
- unless stk == 0
1030
- stk -= 1
1031
- pos = @history[stk].length
1032
- end
1033
- end
1034
- when 'RIGHT'
1035
- pos += 1 unless pos > @history[stk].length
1036
- when 'LEFT'
1037
- pos -= 1 unless pos == 0
1038
- when 'HOME'
1039
- pos = 0
1040
- when 'END'
1041
- pos = @history[stk].length
1042
- when 'DEL'
1043
- @history[stk][pos] = ""
1044
- when 'BACK'
1045
- unless pos == 0
1046
- pos -= 1
1047
- @history[stk][pos] = ""
1048
- end
1049
- when 'WBACK'
1050
- unless pos == 0
1051
- until @history[stk][pos - 1] == " " or pos == 0
1052
- pos -= 1
1053
- @history[stk][pos] = ""
1054
- end
1055
- if @history[stk][pos - 1] == " "
1056
- pos -= 1
1057
- @history[stk][pos] = ""
1058
- end
1059
- end
1060
- when 'LDEL'
1061
- @history[stk] = ""
1062
- pos = 0
1063
- when /^.$/
1064
- @history[stk].insert(pos,chr)
1065
- pos += 1
1066
- end
1067
- end
1068
- curstr = @history[stk]
1069
- @history.shift if @w_b.nohistory
1070
- unless @w_b.nohistory
1071
- @history.uniq!
1072
- @history.compact!
1073
- @history.delete("")
1074
- end
1075
- Curses.curs_set(0)
1076
- Curses.noecho
1077
- return curstr
1078
- end
1079
-
1080
- # MAIN PROGRAM
1081
- loop do # OUTER LOOP - (catching refreshes via 'r')
1082
- @break = false # Initialize @break variable (set if user hits 'r')
1083
- @today = Time.now.strftime("%F")
1084
- @tz = Time.now.strftime("%z")[0..2]
1085
- @time = Time.now.strftime("%H:%M")
1086
- get_weather
1087
- @planets = get_planets
1088
- @events = get_events
1089
- Thread.new {apod}
1090
- Thread.new {starchart} if @lat > 23
1091
- begin # Create the four windows/panels
1092
- Curses.stdscr.bg = 236 # Use for borders
1093
- Curses.stdscr.fill
1094
- maxx = Curses.cols
1095
- exit if maxx < @w_l_width
1096
- maxy = Curses.lines
1097
- # Curses::Window.new(h,w,y,x)
1098
- @w_t = Curses::Window.new(1, maxx, 0, 0)
1099
- @w_b = Curses::Window.new(1, maxx, maxy - 1, 0)
1100
- @w_l = Curses::Window.new(maxy - 2, @w_l_width - 1, 1, 0)
1101
- @w_u = Curses::Window.new(21, maxx - @w_l_width, 1, @w_l_width)
1102
- @w_d = Curses::Window.new(maxy - 23, maxx - @w_l_width, 23, @w_l_width)
1103
- @w_t.fg, @w_t.bg = 7, 18
1104
- @w_t.attr = Curses::A_BOLD
1105
- @w_b.fg, @w_b.bg = 7, 18
1106
- @w_d.fill
1107
- @w_t.update = true
1108
- @w_b.update = true
1109
- @w_l.update = true
1110
- @w_u.update = true
1111
- @w_d.update = true
1112
- ix_change = true
1113
- loop do # INNER, CORE LOOP
1114
- @min_index = 0
1115
- @max_index = @weather.size - 1
1116
- # Top window (info line)
1117
- w_t_info
1118
- # Bottom window (command line) Before @w_u to avoid image dropping out on startup
1119
- w_b_info(nil) if @w_b.update
1120
- @w_b.update = true
1121
- # Left and right windows (browser & content viewer)
1122
- if ix_change
1123
- w_l_info
1124
- begin
1125
- w_u_info if @w_u.update
1126
- rescue
1127
- w_u_msg("== No info for past time ==")
1128
- end
1129
- end
1130
- Curses.curs_set(1) # Clear residual cursor
1131
- Curses.curs_set(0) # ...from editing files
1132
- ix = @index
1133
- main_getkey # Get key from user
1134
- @index == ix ? ix_change = false : ix_change = true
1135
- @w_u.text = ""
1136
- if @w_d.update
1137
- image_show("clear")
1138
- image_show(@image)
1139
- end
1140
- @w_d.update = false
1141
- break if @break # Break to outer loop, redrawing windows, if user hit 'r'
1142
- break if Curses.cols != maxx or Curses.lines != maxy # break on terminal resize
1143
- end
1144
- ensure # On exit: close curses, clear terminal
1145
- @write_conf_all = false
1146
- conf_write if @write_conf # Write marks to config file
1147
- image_show("clear")
1148
- close_screen
1149
- end
1150
- end
1151
-
1152
- # vim: set sw=2 sts=2 et fdm=syntax fdn=2 fcs=fold\:\ :