367 lines
11 KiB
Python
367 lines
11 KiB
Python
"""
|
|
|
|
Siehe auch: Steamrock Digital Barometer
|
|
|
|
Meßwert alls 15 Minuten:
|
|
Es wird in hPa gemessen mit einer Nachkommastelle
|
|
84 Stunden * 4 Werte je Stunde = 336 Meßwerte
|
|
Tendenzwert über 3 Stunden
|
|
|
|
Je Zoomstufe wird ein eigener Buffer vorgehalten um ein sauberes
|
|
Diagramm zu erhalten. Überall gilt: Je Pixel ein Meßwert.
|
|
|
|
Drucktendenz:
|
|
- 1 hour tendency
|
|
- 3 hour tendency
|
|
|
|
Verschiedene Datenquellen auswählbar:
|
|
- intern (BME280, BMP280)
|
|
- N2K generisch
|
|
- Historie von
|
|
- Yacht devices
|
|
- Capteurs?
|
|
|
|
Das Diagramm wird mit Ursprung rechts unten (x0, y0) gezeichnet,
|
|
da die Werte in der Vergangenhait liegen, also links vom Ursprung.
|
|
|
|
Damit eine saubere Skala auf der Y-Achse erreicht wird, gibt einige
|
|
feste Skalierungen.
|
|
Standard: 20hPa von unten nach oben, z.B. 1015, 1020, 1025, 1030, 1035
|
|
|
|
"""
|
|
|
|
import time
|
|
import cairo
|
|
from .page import Page
|
|
|
|
class Barograph(Page):
|
|
|
|
def __init__(self, pageno, cfg, boatdata):
|
|
super().__init__(pageno, cfg, boatdata)
|
|
# Meßwert alle 15 Minuten:
|
|
# 84 Stunden * 4 Werte je Stunde = 336 Meßwerte
|
|
self.bd = boatdata
|
|
self.source = 'I' # (I)ntern, e(X)tern
|
|
self.zoom = (1, 2, 3, 6, 12)
|
|
self.zoomindex = 4
|
|
self.series = (75, 150, 300, 600, 900)
|
|
|
|
# Y-Axis
|
|
self.vmin = 0
|
|
self.vmax = 0
|
|
self.scalemin = 1000
|
|
self.scalemax = 1020
|
|
self.scalestep = 5
|
|
|
|
# Tendenzwert über 3 Stunden
|
|
self.hist3 = None
|
|
self.hist1 = None
|
|
|
|
self.buttonlabel[1] = '+'
|
|
self.buttonlabel[2] = '-'
|
|
self.buttonlabel[5] = 'SRC'
|
|
|
|
self.refresh = time.time() - 30
|
|
|
|
def handle_key(self, buttonid):
|
|
# TODO Serie auswählen aufgrund Zoomlevel
|
|
if buttonid == 1:
|
|
# Zoom in
|
|
if self.zoomindex > 0:
|
|
self.zoomindex -= 1
|
|
self.refresh = time.time() - 30
|
|
elif buttonid == 2:
|
|
# Zoom out
|
|
if self.zoomindex < len(self.zoom) - 1:
|
|
self.zoomindex += 1
|
|
self.refresh = time.time() - 30
|
|
if buttonid == 5:
|
|
# Source
|
|
if self.source == 'I':
|
|
self.source = 'X'
|
|
else:
|
|
self.source = 'I'
|
|
|
|
# Testausgabe der Datenerfassung
|
|
data = []
|
|
vmin = data[0]
|
|
vmax = data[0]
|
|
i = self.series[self.zoomindex]
|
|
for value in self.bd.history['press'].series[i].get():
|
|
v = value / 10
|
|
data.append(v)
|
|
if v < vmin and v != 0:
|
|
vmin = v
|
|
elif v > vmax and v != 0:
|
|
vmax = v
|
|
print(f"Werte: vmin={vmin}, vmax={vmax}")
|
|
ymin, ymax, step = self.getYScale(vmin, vmax)
|
|
print(f"Skala: ymin={ymin}, ymax={ymax}, step={step}")
|
|
print(f"zoomindex={self.zoomindex}, series={self.series[self.zoomindex]}")
|
|
|
|
hist1a = self.bd.history['press'].series[i].getvalue(3600)
|
|
hist1b = self.bd.history['press'].series[i].getvalue3(3600)
|
|
trend1 = data[0] - hist1b
|
|
print(f"{hist1a} / {hist1b} -> Trend1: {trend1:.1f}")
|
|
|
|
def loadData(self):
|
|
"""
|
|
Transfer data from history to page buffer
|
|
set y-axis according to data
|
|
"""
|
|
self.data = []
|
|
self.vmin = 9999
|
|
self.vmax = 0
|
|
i = self.series[self.zoomindex]
|
|
for value in self.bd.history['press'].series[i].get():
|
|
v = value / 10
|
|
self.data.append(v)
|
|
if v < self.vmin and v != 0:
|
|
self.vmin = v
|
|
elif v > self.vmax and v != 0:
|
|
self.vmax = v
|
|
self.scalemin, self.scalemax, self.scalestep = self.getYScale(self.vmin, self.vmax)
|
|
return True
|
|
|
|
def drawTrend(self, ctx, code, x, y, w):
|
|
"""
|
|
One hour Trend
|
|
0: Stationary <= 1 hPa
|
|
1: Rising >1 and <= 2 hPa
|
|
2: Rising fast >2 and <= 3 hPa
|
|
3: Rising very fast >3 hPa
|
|
-1: Falling
|
|
-2: Falling fast
|
|
-3: Falling very fast
|
|
"""
|
|
trend1map = {
|
|
-3: "Falling_Very_Fast.png", # > 3 hPa
|
|
-2: "Falling_Fast.png", # > 2 and <= 3 hPa
|
|
-1: "Falling.png", # > 1 and <= 2 hPa
|
|
0: "Stationary.png", # <= +/- 1 hPa
|
|
1: "Rising.png", # < -1 and >= -2 hPa
|
|
2: "Rising_Fast.png", # < -2 and >= -3 hPa
|
|
3: "Rising_Very_Fast.png" # < -3 hPa
|
|
}
|
|
|
|
if code == 0:
|
|
# Pfeil horizontal rechts
|
|
ctx.move_to(x, y - w / 2)
|
|
ctx.line_to(x + w, y - w / 2)
|
|
ctx.draw()
|
|
# Position merken
|
|
ctx.line_to(x - w / 4, y - w)
|
|
ctx.line_to(x - w / 4, y)
|
|
ctx.line_to(x + w, y - w / 2)
|
|
ctx.fill()
|
|
elif code == 1:
|
|
# Pfeil schräg nach oben
|
|
pass
|
|
elif code == 2:
|
|
# Pfeil gerade nach oben
|
|
pass
|
|
elif code == 3:
|
|
# Doppelpfeil nach oben
|
|
pass
|
|
elif code == -1:
|
|
# Pfeil schräg nach unten
|
|
pass
|
|
elif code == -2:
|
|
# Pfeil gerade nach unten
|
|
pass
|
|
elif code == -3:
|
|
# Doppelpfeil nach unten
|
|
pass
|
|
|
|
def drawWMOCode(self, ctx, code, x, y, w):
|
|
"""
|
|
Three hour code
|
|
Code 0 to 8:
|
|
0: Increasing, then decreasing; athmospheric pressure the same
|
|
as or higher than three hours ago
|
|
1: Increasing then steady; or increasing, then increasing more
|
|
slowly; athmospheric pressure now higher than three hours ago
|
|
2: Increasing (steadily or unsteadily); athmospheric pressure
|
|
now higher than three hours ago
|
|
3: Decreasing or steady, then increasing; or increasing then
|
|
increasing more rapidly; athmospheric pressure now higher
|
|
than three hours ago
|
|
4: Steady; athmospheric pressure is the same as three hours ago
|
|
5: Decreasing, then increasing; athmospheric pressure now is the
|
|
same as or lower than three hours ago
|
|
6:
|
|
7:
|
|
8:
|
|
"""
|
|
pass
|
|
|
|
def getYScale(self, vmin, vmax):
|
|
# Y-Achse aufgrund Meßwerten einstellen
|
|
diff = vmax - vmin
|
|
if diff < 20:
|
|
step = 5
|
|
elif diff <= 40:
|
|
step = 10
|
|
else:
|
|
step = 15
|
|
vmin = int(vmin - (vmin % step)) # Nächstes Vielfaches nach oben
|
|
vmax = int(vmax + step - (vmax % step)) # Nächstes Vielfaches nach unten
|
|
return (vmin, vmax, step)
|
|
|
|
def draw(self, ctx):
|
|
"""
|
|
Darstellung angelehnt an klassisches Gerät
|
|
Daten werden im nichtflüchtigen Speicher gehalten
|
|
Da sich die Daten langsam verändern, reicht es, diese z.B. nur alle
|
|
30 Sekunden oder langsamer zu laden.
|
|
Der aktuelle Wert oben ist natürlich nicht alt.
|
|
|
|
Datenreihen
|
|
- 1 Woche, stündlich: 7 * 24 = 168 Meßwerte
|
|
- 1 Tag, alle 10 min: 24 * 6 = 144 Meßwerte
|
|
Der Druck wird in zwei Bytes abgespeichert. Es wird eine Nachkommastelle
|
|
verwendet. Um ohne Fließkommazahlen auszukommen wird der Maßwert einfach
|
|
mit 10 multipliziert.
|
|
|
|
Darstellung wie Steamrock:
|
|
1 Pixel entspricht einem Meßwert alle 15min.
|
|
1 Tag hat dementsprechend eine Breite von 48px
|
|
"""
|
|
|
|
timestamp = time.time()
|
|
if timestamp - self.refresh >= 30:
|
|
self.refresh = timestamp
|
|
self.loadData()
|
|
|
|
ctx.set_source_rgb(0, 0, 0)
|
|
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
|
|
|
|
# Datenquelle rechts oben
|
|
ctx.set_font_size(16)
|
|
ctx.move_to(330, 50)
|
|
if self.source == 'I':
|
|
ctx.show_text("BMP280")
|
|
else:
|
|
ctx.show_text("N2K Bus")
|
|
ctx.stroke()
|
|
# Zoomstufe
|
|
datastep = self.series[self.zoomindex]
|
|
if datastep > 120:
|
|
if datastep % 60 == 0:
|
|
fmt = "{:.0f} min"
|
|
else:
|
|
fmt = "{:.1f} min"
|
|
datastep /= 60
|
|
else:
|
|
fmt = '{} s'
|
|
self.draw_text_center(ctx, 360, 62, fmt.format(datastep))
|
|
|
|
# Aktueller Luftdruck hPa
|
|
ctx.set_font_size(32)
|
|
self.draw_text_center(ctx, 200, 40, self.bd.pressure.format())
|
|
#self.draw_text_center(ctx, 200, 40, "1019.2")
|
|
ctx.set_font_size(16)
|
|
self.draw_text_center(ctx, 200, 62, "hPa")
|
|
|
|
# Trend
|
|
ctx.set_font_size(16)
|
|
# TODO Trend linie
|
|
#trend =
|
|
self.draw_text_center(ctx, 295, 62, "0.0")
|
|
|
|
# min/max
|
|
ctx.move_to(10, 38)
|
|
ctx.show_text(f"min: {self.vmin}")
|
|
ctx.move_to(10, 50)
|
|
ctx.show_text(f"max: {self.vmax}")
|
|
|
|
# Alarm
|
|
self.draw_text_center(ctx, 70, 62, "Alarm Off")
|
|
|
|
# Hintergrundrahmen
|
|
ctx.set_line_width(2)
|
|
|
|
ctx.move_to(0, 75)
|
|
ctx.line_to(400, 75)
|
|
|
|
ctx.move_to(130, 20)
|
|
ctx.line_to(130, 75)
|
|
|
|
ctx.move_to(270, 20)
|
|
ctx.line_to(270, 75)
|
|
|
|
ctx.move_to(325, 20)
|
|
ctx.line_to(325, 75)
|
|
|
|
ctx.stroke()
|
|
|
|
|
|
# Diagramm
|
|
# --------
|
|
ymin = self.scalemin
|
|
ymax = self.scalemax
|
|
yn = self.scalestep
|
|
ystep = (ymax - ymin) / yn
|
|
|
|
xstep = 48
|
|
|
|
# Ursprung ist rechts unten
|
|
x0 = 350
|
|
y0 = 270
|
|
w = 7 * 48
|
|
h = 180
|
|
|
|
ctx.set_line_width(1)
|
|
ctx.rectangle(x0 - w + 0.5, y0 - h + 0.5, w, h)
|
|
ctx.stroke()
|
|
|
|
# X-Achse sind Stunden
|
|
xn = 0
|
|
for xt in [x * -1 * self.zoom[self.zoomindex] for x in range(1,7)]:
|
|
xn += 1
|
|
ctx.move_to(x0 - xn * xstep + 0.5, y0)
|
|
ctx.line_to(x0 - xn * xstep + 0.5, y0 - h)
|
|
ctx.stroke()
|
|
self.draw_text_center(ctx, x0 - xn * xstep + 0.5, y0 - 8, str(xt), fill=True)
|
|
ctx.stroke()
|
|
#for x in (1, 2, 3, 4, 5, 6):
|
|
# ctx.move_to(x0 - x * 48 + 0.5, y0 + 0.5)
|
|
# ctx.line_to(x0 - x * 48 + 0.5, y0 - h + 0.5)
|
|
#ctx.stroke()
|
|
|
|
# Y-Achse
|
|
ctx.move_to(x0 + 5.5, y0 + 0.5)
|
|
ctx.line_to(x0 + 5.5, y0 - h)
|
|
ctx.move_to(x0 - w - 5.5, y0 + 0.5)
|
|
ctx.line_to(x0 - w - 5.5, y0 -h )
|
|
ctx.stroke()
|
|
|
|
dy = 9 # Pixel je hPa
|
|
ysmin = self.scalemin
|
|
ysmax = self.scalemax
|
|
|
|
y = y0 + 0.5
|
|
ystep = self.scalestep
|
|
ys = ysmin
|
|
while y >= y0 - h:
|
|
if ys % ystep == 0:
|
|
ctx.move_to(x0 + 10, y + 5.5)
|
|
ctx.show_text(str(ys))
|
|
ctx.move_to(x0 - w - 5, y)
|
|
ctx.line_to(x0 + 5, y)
|
|
else:
|
|
ctx.move_to(x0, y)
|
|
ctx.line_to(x0 + 5, y)
|
|
ctx.move_to(x0 - w - 5, y)
|
|
ctx.line_to(x0 - w, y)
|
|
y -= dy
|
|
ys += 1
|
|
ctx.stroke()
|
|
|
|
# Meßdaten
|
|
for v in self.data:
|
|
x0 -= 1
|
|
if v > 0:
|
|
ctx.rectangle(x0, y0 - (v - ysmin) * dy, 1.5, 1.5)
|
|
ctx.fill()
|