Check-in [26b540b327]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Merge in the scared.lua scripts for the latest blog post.
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 26b540b3274a662fb0375299ed5550083c0a1f08
User & Date: rberteig 2015-05-22 23:23:49
Context
2015-06-16
22:18
Merged to trunk for next blog post release. This has the nominally working email when startled script that can be configured to either email a plot of SPL or an MP3 of the room audio around the event. check-in: 8d8ccc7f52 user: rberteig tags: blog-splear09, trunk
2015-05-22
23:23
Merge in the scared.lua scripts for the latest blog post. check-in: 26b540b327 user: rberteig tags: trunk
22:25
Fix published email address. check-in: 31ad4ca9c2 user: rberteig tags: scared
2015-04-17
22:06
Caught a typo, and renamed readme.txt to readme.md in most folders. check-in: 3b4f54236e user: rberteig tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Added Scared/plotspl.lua.

































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
--[[
Lua module to plot SPL data with gnuplot.

Uses temp files to hold the data and plotting script, with the
script written from a template in this module. Produces a plot
with labeled axes, a title, and annotations for the trigger time,
mean and 1 sigma range about the mean.
]]

-- miminal Lua 5.x module setup, M is the module's table.
local M = {
  _NAME=(...) or "plotspl",
  _DESCRIPTION="Plot SPL data with gnuplot",
  _VERSION="0.001",
}
package.loaded[M._NAME] = M



-- write the data array to a temp file
local function prepdata(data)
  local datfile = "tmp-SPL.data"
  f = assert(io.open(datfile,"w"))
  f:write(data)
  f:close()
  return datfile
end


-- write out a gnuplot script to plot the data file
-- to the named pngfile, with an offset to the trigger
-- time and the specified title.
local function prepscript(datafile, pngfile, trigger, title)
  assert(datafile)
  pngfile = pngfile or "tmp-SPL.png"
  trigger = trigger or 2.6
  title = title or "Scaredy Cat"

  local plotfile = "tmp-SPL.plot"
  f = assert(io.open(plotfile, "w"))

  f:write('# Configuration\n',
    'trigger=', trigger, '\n',
    'datafile="', datafile, '"\n',
    'outfile="', pngfile, '"\n',
    'titletext="', title, '"\n',
    '\n')
  f:write[===[
set terminal push

# Graph Title, Axis labels, key and grid
set title titletext
set xlabel "seconds" 
set ylabel "SPL, dB" 
set key off
set grid xtics ytics

# Compute mean and sd by cheating with a constant function fit to the
# data.
f(x) = mean_y
fit f(x) datafile via mean_y
stddev_y = sqrt(FIT_WSSR / (FIT_NDF + 1 ))

set terminal png
set output outfile

# label a vertical line at trigger time, which will be at x=0 after
# transformations
set label 1 "Trigger" at 0, graph 0 offset character 0.25,0.5 
set arrow 1 from 0, graph 0 to 0, graph 1 nohead linewidth 5 lc rgb "#ccccff"

# plot with stddev bounds and mean, scaling and offsetting x to
# seconds with x=0 at the trigger
plot datafile using ($1 - trigger):2 with lines linewidth 2.5, \
	mean_y with lines lc rgb "#00dd00", \
	mean_y-stddev_y with lines lc rgb "#dd0000", \
	mean_y+stddev_y with lines lc rgb "#dd0000"

set output
set terminal pop
]===]
  f:close()
  return plotfile
end

-- plot the given data string to the named png file.
function M.plot(data, pngfile, trigger, title)
  local datafile = prepdata(data)
  local plotfile = prepscript(datafile, pngfile, trigger, title)
  local cmd = "gnuplot " .. plotfile .. " >" .. plotfile .. ".log 2>&1"
  print(cmd)
  os.execute(cmd)
end

-- return the module table
return M

Added Scared/scared.lua.































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
#! lua
local plot = require "plotspl"

address = "user@example.com"

-- today's date in (almost) ISO-8601 format
local function isodate() 
  return os.date"%Y-%m-%d %H:%M:%S" 
end

-- make its argument be a single string
function flatten(s)
  if type(s)=="string" then return '"'..s..'"' end
  if type(s)~="table" then return nil end
  return '"'..table.concat(s, '" "')..'"'
end

-- Send an email to addr with subject and body. If body
-- is nil then just repeat the subject. This assumes that
-- a "mail" command is available with the usual options.
local function email(addr, subj, body)
  body = body or subj
  local mail = ("mail -s '%q' %s"):format(subj,addr)
  local b = assert(io.popen(mail, "w"))
  b:write([[I heard a noise on ]], isodate(), "\r\n\r\n")
  b:write(body, "\r\n\r\n")
  b:write"(This an automated message.)\r\n"
  b:close()
end

-- Send a MIME packed email containing an image of the
-- event to a recipient with a subject
-- mpack(address, eek, "scared.png")
local function mpack(address, subject, pngfile)
  assert(address)
  assert(subject)
  assert(pngfile)
  local cmd = ("mpack -a -s %q %q %s"):format(subject, pngfile, address)
  print(cmd)
  os.execute(cmd)
end

-- Implement a buffer of samples. For simplicity, let the 
-- index increment without bound. Note that this will have
-- a problem after 2^53 samples, which is about 30 million 
-- years at 10 samples per second.
local recent = {
  depth=200,
  n=1,
  add=function(self,spl)
    local n = self.n
    self[n] = spl
    self[n-self.depth] = nil
    self.n = n + 1
  end,
  text=function(self)
    local t = {"0.1s, dB"}
    local offset = self.n-self.depth-1
    for i=1,self.depth do
      if self[i+offset] then
        t[#t+1] = ("%0.1f, %s"):format(i/10,self[i+offset])
      end
    end
    return table.concat(t,"\n")
  end
}

-- Quick and dirty way to force the UART to have the 
-- tty driver settings we need for clean raw access
-- to the port.
os.execute("stty -F /dev/ttyAMA0 115200"
	.." pass8 raw -iexten"
	.." -echo -echoe -echok -echoke -echoctl")

-- Open the UART for both reading and writing, and send
-- the SPLear command to go into raw SPL sample mode. The
-- output must be unbuffered because the current SPLear 
-- firmware would revert to 0.5 Hz summary mode if it
-- receives a newline.
local port = assert(io.open('/dev/ttyAMA0','r+'))
port:setvbuf"no"   -- no output buffer at all
port:write"S"      -- so this write is immediate

-- Loop reading the SPL samples and watching for loud
-- noises, keeping a log of recent samples as we go.
-- When a loud noise is heard, keep logging for a bit
-- before sending email. Include the log in the email.
local spl, spl0 
local triggered = false
local eek = ""
local addr = flatten(address)
print("Notifying "..addr)


while true do
  local line = port:read"*l" -- read a single line
  spl = tonumber(line)
  if spl then
    spl = 0.75 * spl
    io.write("SPL "..(triggered and "TRG " or "    "), spl, "\r")
    recent:add(spl)

    -- Watch for the trigger condition: 12 dB louder 
    if not triggered and spl0 and (spl - spl0 > 12.) then
      triggered = math.floor(recent.depth * 2. / 3.)
      eek = "EEK! "..spl.." dB"
    end

    if triggered then
      triggered = triggered - 1
      if triggered == 0 then 
        triggered = false
        local data = recent:text()
        local pngfile = "startled-plot.png"
        plot.plot(data,
          pngfile,
          recent.depth/30., -- 1/3 trigger pos, 1/10 for time scale
          "Startled at "..isodate())
        local body = eek.."\n"..data
        print("email " .. addr, eek, isodate())
        mpack(addr, eek, pngfile)
      end
    end
    spl0 = spl
  end
end