here.
For the purpose of this exercise, we will be storing and plotting temperature-related information, but you should get an idea on how to process other kind of information as well.
Assume we have two values - temperature and dew point, and we want to plot both on the same graph. We measure these values from some sensors (or from your local weather station's website) every 300 seconds.
The code to create the RRD file is:
rrdtool.create('test.rrd' ,
'--step','300',
'DS:temp:GAUGE:600:-50:50' ,
'DS:dewpoint:GAUGE:600:-50:50',
'RRA:AVERAGE:0.5:1:576', #day
'RRA:AVERAGE:0.5:6:672', #week
'RRA:AVERAGE:0.5:24:732', #month
'RRA:AVERAGE:0.5:144:1460' #year)
The parameters are identical to what is described in the official rrdcreate documentation.
You can use the same values if you want to invoke the command line application, as opposed to doing it programatically.
The first one is obviously the filename (you can have any name/extension).
The second one is the interval at which we will be measuring our data. If you don't explicitly set this, it will default to 300 seconds.
Following are the two data sources (DS) we will be using, their names (temp and dewpoint), GAUGE is to be used for temperature (there are other types as well, which are described in more detail in the documentation).
600 is the heartbeat, which specifies how many seconds need to pass before two consecutive updates, before the value is assumed to be unknown. Twice the measure interval seems reasonable in this case.
Finally, -50:50 is the interval between which the measured values are expected to be found.
The next thing to do is to define the round robin archives (RRA).
AVERAGE is our CF (consolidation function), which does exactly what the name says. Again, there are others, which you can learn about in the documentation.
0.5 is ratio of unknown measurements (PDPs, or primary data point; we get a new one every 300 seconds in this case) to the total number of measurements in the specified interval.
For the "day" RRA, 1 means the number of PDPs that build a consolidated datapoint (CDP), and 576 means how many of these get stored in the RRA before being overwritten.
This is actually easier than it sounds - 1 PDP means we just store the data directly, every 300 seconds (5 minutes). We store 576 of these CDPs (576*300 seconds=2 days) in the "day" RRA, which means we will be able to plot data for the last 2 days with a 5 minutes resolution.
The exact same applies for the week, month and year RRAs: for "week" we have 2 weeks worth of data, with averages of every half hour (6 means 6*300 seconds, which is 30 minutes; we store 672 of these, which means 672*30 minutes = 14 days), for "month" there will be 2 months worth of data, containing 2 hours averages, and finally "year" stores 2 years worth of data, containing 12 hours averages.
In order to have some data to test things out (and not having to wait say one month to see if the code works), we'll just generate some random values.
Note that RRDTool will refuse to insert data that is timestamped (seconds since epoch) before the creation of the database.
Let's add some data:
for i in xrange(nr_samples):
now = random.randint(12 , 17)
fake_time = fake_time + 300
rrdtool.update(rrd_file , '%d:%d:%d' % (fake_time,now,now - \
10 + random.randint(1,8)))
fake_time gets the current time.
Then we loop nr_samples time, produce a random temperature between 12 and 17C, while making sure to increment the time offset by 300 seconds.
The actual update operation is simple; the parameters are the database filename, following by the "current" time (which we fake here by incrementing fake_time after each update; in a real situation you would just want to get the current time here) and the measured values, how many there may be.
Our fake dew point, which is the second value, is adjusted by a random delta from the temperature, to make the graph a little more realistic.
Finally, it's time to generate the picture, along with the legend:
rrdtool.graph(pic_file ,
'--start' , str(fake_time - 60*60*24*2) ,
'--end' , str(fake_time) ,
#'--slope-mode',
'--vertical-label' , 'Degrees Celsius' ,
'--imgformat' , 'PNG' ,
'--title' , 'Temperature Data (by DAY)' ,
'-w 500', '-h 200',
'DEF:temp=%s:temp:AVERAGE' % rrd_file,
'DEF:dewpoint=%s:dewpoint:AVERAGE' % rrd_file,
'AREA:temp#ccedff:Temperature',
'LINE1:temp#aaaaaa',
'GPRINT:temp:LAST:CUR\: %2.1lf',
'GPRINT:temp:AVERAGE:AVG\: %2.1lf',
'GPRINT:temp:MIN:MIN\: %2.1lf',
'GPRINT:temp:MAX:MAX\: %2.1lf\\n',
'LINE1:dewpoint#007000:Dewpoint',
'GPRINT:dewpoint:LAST: CUR\: %2.1lf',
'GPRINT:dewpoint:AVERAGE:AVG\: %2.1lf',
'GPRINT:dewpoint:MIN:MIN\:%2.1lf',
'GPRINT:dewpoint:MAX:MAX\:%2.1lf\\n')
Graph generation is pretty easy, you just have to pay attention to the start and end points. In a real world situation, you could use --start -2day, or calculate the UNIX time for 2 days ago yourself.
The rest of the parameters, as well as many others are described in more detail in the rrdgraph documentation.
The picture should look like this.
Complete source file for this demo is rrdtool_example.py.
While we're on the subject of temperature, here is some code to calculate the dewpoint, if you have the temperature (in degrees Celsius) and the relative humidity:
def dewpoint(temp,hum):
f = 17.271 * temp / float(237.7 + temp) + log(H / float(100))
return 237.7 * f / (17.271 - f)
If you're using Python 3 and up, you don't need the float(100), you can just use 100, and the result will still be a float.
Finally, here is another function that calculates the heat index, along with helper functions to covert around between Fahrenheit and Celsius:
def c2f(t):
return 9/float(5) * t + 32
def f2c(t):
return 5/float(9)*(t-32)
def heatindex(temp,hum):
# temperature needs to be in Fahrenheit
# return value is degrees Fahrenheit as well
if temp < 80: return 0
return -42.379 + (2.04901523 * temp) + (10.14333127 * hum) - \
(0.22475541 * temp * hum) - (6.83783 * pow(10,-3) * \
pow(temp,2)) - (5.481717 * pow(10,-2) * pow(hum,2)) + \
(1.22874 * pow(10,-3) * pow(temp,2) * hum) + (8.5282 * \
pow(10,-4) * temp * pow(hum,2)) - (1.99 * pow(10,-6) * \
pow(temp,2) * pow(hum,2))
The calculations above are from Wikipedia and US NOAA, and i also tested them against known values, so they should be good.