edff65e72883ee0666989c1aaf740239b713b5c5
[ampy.git] / run.py
1 #!/usr/bin/python3
2 """
3 PyAudio example: Record a few seconds of audio and save to a WAVE
4 file.
5 """
6
7 import os
8 import pyaudio
9 import wave
10 import sys
11 import struct
12 import time
13
14 FORMAT = pyaudio.paInt32  # Format
15 STRUCTFORMAT = 'i'  # Format for python struct module
16 DEVICE = 3  # which alsa decive to read
17 RATE = 44100
18 RECORD_SECONDS = 0.5
19
20 VOLUME_CONSTANT = 132360.98315789475
21
22 #amixer -c 1 contents
23 # numid=23,iface=MIXER,name='Capture Volume'
24 #   ; type=INTEGER,access=rw---R--,values=2,min=0,max=46,step=0
25 #   : values=17,17
26 #   | dBscale-min=-16.00dB,step=1.00dB,mute=0
27 # => Set volume to 18
28
29 # https://www.actutem.com/valeur-crete-moyenne-et-efficace-dune-tension-ac/
30 # math.pi/math.sqrt(2)/2 = 1.1107207345395915
31
32 # Pour 30 A à 230V ( 6900 W )
33 # abs(Sndmax) = 2147483520
34 # Sndavg = 2147483520/math.pi = 683565234.8327662
35 # Sndeff = 683565234.8327662 * 1.1107207345395915 = 759250079.7391784
36
37 # https://fr.wikipedia.org/wiki/Compteur_%C3%A9lectrique
38 # Mon compteur tourne à une vitesse proportionnelle à la puissance instantanée
39
40 # https://fr.wikipedia.org/wiki/%C3%89lectricit%C3%A9_domestique#Tension
41 # En france, ERDF fourni 230V EFFICACES (=> 207.0727527161344 moyenne)
42
43 #raw average: 636207384.4738322 - min: -2147483648 - max: 2147483392
44 #average 4806.6W  
45
46 def loop(optrecord, optstats):
47     p = pyaudio.PyAudio()
48     
49     print("opened")
50     NSAMPLE = int(RATE * RECORD_SECONDS)
51     stream = p.open(format=FORMAT,
52                     channels=1,  # Our ampmeter always returns 0 on the second channel
53                     rate=RATE,
54                     input=True,
55                     input_device_index=DEVICE)
56  
57     structformat = '<' + STRUCTFORMAT * NSAMPLE
58     while(True):
59         try:
60             total = 0
61             minvalue = 0
62             maxvalue = 0
63             data = stream.read(NSAMPLE, exception_on_overflow=False)
64             values = struct.unpack(structformat, data)
65             #print(values)
66             #for x in range(100): print('**{:04x}**{}**'.format(values[x], values[x]))
67             for value in values:
68                 if value > 0:
69                     total += value
70                 else:
71                     total -= value
72                 if value > maxvalue:
73                     maxvalue = value
74                 if value < minvalue:
75                     minvalue = value
76             avg = float(total) / NSAMPLE
77             if optstats:
78                 print("raw average: {} - min: {} - max: {}".format(avg, minvalue, maxvalue))
79             watts = avg / VOLUME_CONSTANT
80             print("average {:.1f}W  ".format(watts), end='\r')
81             if optrecord:
82                 #os.system("rrdtool update --daemon /var/run/rrdcached.sock power.rrd N:{}".format(watts))
83                 os.system("rrdtool update --daemon /var/run/rrdcached.sock /var/lib/rrdcached/db/power.rrd {}:{}".format(time.time(), watts))
84         except KeyboardInterrupt:
85             print("Received KeyboardInterrupt: exiting")
86             break
87     
88     stream.stop_stream()
89     stream.close()
90     p.terminate()
91
92 def main():
93     import argparse
94
95     parser = argparse.ArgumentParser(description='Ampemeter processing')
96     parser.add_argument(
97             '--stats',
98             action='store_true',
99             default=False,
100             help='Dump recording level stats for every chunk',
101             )
102     parser.add_argument(
103             '--norecord',
104             action='store_false',
105             default=True,
106             help='Disable recording in RRD file',
107             dest='record',
108             )
109     args = parser.parse_args()
110
111     if not args.record:
112         print('Recording disabled.')
113
114     loop(optrecord=args.record, optstats=args.stats)
115
116 if __name__ == '__main__':
117     main()