summaryrefslogtreecommitdiffstats
path: root/plot.py
blob: 647a083a5e046674f8fd03cf57122fe8c0cdbc41 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#!/usr/bin/python3

# based on https://github.com/cnvogelg/ardu/blob/master/datalog/tools/plot_log.py

# This script plots a log file of the format
# "unix_timestamp key=value key2=value key3=value ..."
# using rrdtool. Keys are arbitrary values of the format "type:number"
# (e.g. t:1, t:2, h:1 for temperature and humidity) used to identify each sample.
# Note that the keys currently ignored.
#
# The whole log is plotted into one graph.

import sys
import subprocess
import os
import math
from string import Template

if len(sys.argv) != 3:
  print("Usage: plot.py <log> <png>", file=sys.stderr)
  sys.exit(1)

log_file = sys.argv[1]
png_file = sys.argv[2]
rrd_file = "tmp.rrd"
argmax = 50000

#### start of config

# If you change this you also need to change the create and graph lists below.
# Also note that samples have to be in the same order every time (the key is currently ignored).
samples_per_line=2

template_values = {
	# 0 = everything from the input
	# otherwise = only the last n
	"total_samples": 0,
}

# The 4th field of the DS lines is the heartbeat. If you sample at an
# interval < that value you will not see a graph. Simply adjust if necessary.
create = [
	# Index of the 0 after --start is used below. Adjust code if you
	# change the amount of values before it.
	'rrdtool','create',rrd_file,'--step','30','--start',0,

	'DS:temp1:GAUGE:50:U:U',
	'DS:temp2:GAUGE:50:U:U',
	#'DS:hum1:GAUGE:50:U:U',
	'DS:abshum1:GAUGE:50:U:U',

	'RRA:AVERAGE:0.5:1:$total_samples',
	'RRA:MIN:0.5:1:$total_samples',
	'RRA:MAX:0.5:1:$total_samples',
]

graph = [
	# Indices of the two Nones are used below. Adjust the code if you
	# change the amount of values before them..
	'rrdtool','graph',png_file,
	'-s',None,'-e',None,
	'--height=500','--width=1000',
	'--color=BACK#FFFFFF',
	#'--title','Temperature',
	'--vertical-label', 'Temperature °C',
	'--right-axis-label', 'Humidity g/m³',
	'--right-axis', '0.5:0',
	#'--alt-autoscale',
	'-l','10','-u','35', '--rigid',
	'--left-axis-format', '%4.2lf',
	'--right-axis-format', '%4.2lf',

	'DEF:temp1='+rrd_file+':temp1:AVERAGE',
	'LINE1:temp1#339966:temp 1\l',
	'DEF:temp2='+rrd_file+':temp2:AVERAGE',
	'LINE1:temp2#663399:temp 2\l',
	#'DEF:hum1='+rrd_file+':hum1:AVERAGE',
	#'CDEF:hum11=hum1,2,/',
	#'LINE1:hum11#996633:hum 1\l',
	'DEF:abshum1='+rrd_file+':abshum1:AVERAGE',
	'CDEF:abshum11=abshum1,2,*',
	'LINE1:abshum11#996633:abshum 1\l',
]

#### end of config

def chunker(seq, size):
	return (seq[pos:pos + size] for pos in range(0, len(seq), size))

calc_total_from_input = False
if template_values["total_samples"] == 0:
	calc_total_from_input = True

# load samples
samples = []
with open(log_file,"r") as f:
	for line in f:
		if calc_total_from_input:
			template_values["total_samples"] += 1

		elem = line.split(' ')
		values = []
		# timestamp
		values.append(int(elem[0]))
		# values
		for i in range(1, samples_per_line + 1):
			values.append(float(elem[i].split("=")[1]))

		# absolute humidity
		hum=float(elem[3].split("=")[1])
		temp=values[2]
		# Source: https://carnotcycle.wordpress.com/2012/08/04/how-to-convert-relative-humidity-to-absolute-humidity/
		abshum=(6.112 * math.e**(17.67 * temp / (temp + 243.5)) * hum * 2.1674) / (273.15+temp)
		values.append(abshum)

		samples.append(values)

# create rrd
start_ts = samples[0][0]
end_ts = samples[-1][0]
create[6] = str(start_ts - 1)

cmd = []
for elem in create:
	cmd.append(Template(elem).substitute(template_values))
#print("creating rrdb:",create)
ret = subprocess.call(cmd)
if ret != 0:
	#print("ERROR calling: {}".format(" ".join(create)))
	sys.exit(1)


# update rrd
for group in chunker(samples, argmax):
	cmd = ['rrdupdate', rrd_file, '--']
	for sample in group:
		entry = ":".join(map(str, sample))
		cmd.append(entry);

	ret = subprocess.call(cmd)
	if ret != 0:
		#print("ERROR calling: {}".format(" ".join(cmd)))
		sys.exit(1)


# graph
graph[4] = str(start_ts)
graph[6] = str(end_ts)
ret = subprocess.call(graph)
if ret != 0:
	#print("ERROR calling: {}".format(" ".join(graph)))
	sys.exit(1)

os.unlink(rrd_file)

# vim: set noet: