-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathProcessedPlot.py
More file actions
368 lines (300 loc) · 12.7 KB
/
ProcessedPlot.py
File metadata and controls
368 lines (300 loc) · 12.7 KB
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
PROCESSED FILE VERSION V1.0 built on multiPlot v1.2
multiPlot version v1.2 cleans up to make Pi and Windows use easier
multiPlot version v1.1 adds sunrise sunset times for location of lat long in first file
requires suntime library https://github.com/SatAgro/suntime
multiPlot version v1.0 plots up to 10 PSWS "rawdata" files and average value
modified from WWV_plt2.py @authors dkazdan jgibbons
expects a homepath directory with processed files in homepath or subdirs
leaves plot in Mplot directory
plots files from multiple subdir to compare node results
plot title from first file
windows version hardcoded homepath directory location
for Pi comment out windows homepath and uncomment Pi lines
uses WWV_utility2.py
Bob Benedict, KD8CGH, 7/29/2021
create text file "Processfiles.txt" in homepath directory
keyword ('Doppler' or 'Power')
keyword ('Average' or 'No Average')
subdir/filename1
subdir/filename2
filename3
...
Note - expects all data from the same beacon
if found 'Doppler' will plot Doppler shifts, else will plot Power
if found "Average" will add average plot
loads file names in list
plots first file and create axis and title info
plots rest in loop as curves on first plot
calculates average and plots
leaves plotfile in Mplot directory
uses
WWV_utility2.py
20 February 2020
WWV utility file
Routines and classes used in WWV file management and graphing
David Kazdan, AD8Y
John Gibbons, N8OBJ - mods to plot header 2/3/20
"""
#import os # uncomment for pi
from os import path
import sys
import csv
import math
#import shutil # uncomment for pi
#from datetime import date, timedelta # uncomment for pi
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import filtfilt, butter
import datetime
from suntime import Sun
#import subprocess
from WWV_utility2 import time_string_to_decimals
from Beacon import readheader
''' #uncomment for Pi
# ~ points to users home directory - usually /home/pi/
homepath = os.path.expanduser('~')
# imbed the trailing / in the home path
homepath = homepath + "/PSWS/"
#comment out windows homepath
'''
homepath = "E:\\Documents\\PSWS\\" # set your windows path, comment out for Pi
names = open(homepath+"Processfiles.txt","r")
PlotTarget = names.readline()
PlotTarget = PlotTarget.strip('\n')
PlotAverage = names.readline()
if PlotAverage[0:7] == 'Average':
doavg=True
else:
doavg=False
Filenames=['a' for a in range (10)]
Filedates=['a' for a in range (10)]
#PrFilenames=['a' for a in range (10)]
Nodenum=['a' for a in range (10)]
Beaconname=['a' for a in range (10)]
beaconfreq=np.zeros(10)
nfiles = 0 # holder for number of files to plot
colors=['b','g','r','c','m','y','tab:orange','tab:gray','tab:purple','tab:brown']
while True:
temp = names.readline()
if len(temp) <= 1:
break
Filenames[nfiles]=temp.strip("\n")
fdate = Filenames[nfiles].find("/") # find start of filename after subdirectory
if fdate==-1 : # if / not found try \
fdate = Filenames[nfiles].find("\\")
# print(" position ",fdate)
# if neith / nor \ found file is in homepath, fdate=-1, following assignment still works
Filedates[nfiles]=temp[fdate+1:fdate+11]
nfiles=nfiles + 1
#print(Filenames[0:9])
#print(Filedates[0:9])
print('number of files',nfiles)
if nfiles > 10 :
print('10 file limit')
sys.exit(0)
PROCESSDIR = homepath
#saved plot directrory
PlotDir = homepath + 'Mplot/'
'''
read first file
'''
PrFilenames=(PROCESSDIR + Filenames[0])
if (path.exists(PrFilenames)):
print('File ' + PrFilenames + ' found!\nProcessing...')
else:
print('File ' + PrFilenames + ' not available.\nExiting disappointed...')
sys.exit(0)
with open(PrFilenames, 'r') as dataFile:
dataReader=csv.reader(dataFile)
data = list(dataReader)
Header = data.pop(0)
# print('return',readheader(0,Header))
Nodenum[0], Beaconname[0], beaconfreq[0], Lat, Long = readheader(Header)
# print('\n returned ', Nodenum[0], Beaconname[0], beaconfreq[0], Lat, Long, '\n')
''' ###########################################################################
'''
print('Ready to start processing records')
# Prepare data arrays
hours=[[],[],[],[],[],[],[],[],[],[]]
Doppler=[[],[],[],[],[],[],[],[],[],[]]
#Vpk=[[],[],[],[],[],[],[],[],[],[]]
Power_dB=[[],[],[],[],[],[],[],[],[],[]] # will be second data set, received power 9I20
filtDoppler=[[],[],[],[],[],[],[],[],[],[]]
filtPower=[[],[],[],[],[],[],[],[],[],[]]
LateHour=False # flag for loop going past 23:00 hours
# eliminate all metadata saved at start of file - Look for UTC (CSV headers)
#find first row of data0
FindUTC = 0
for row in data:
if (FindUTC == 0):
#print('looking for UTC - row[0] =',row[0])
if (row[0] == 'UTC'):
FindUTC = 1
# print('UTC found =', row[0])
else:
#print('Processing record')
decHours=time_string_to_decimals(row[0])
# if (NewHdr != 'New'):
# if (calccnt < 101):
# calcnt = calcnt+1
# freqcalc = freqcalc + (float(row[1])/100)
# if decHours > 23:
# LateHour=True # went past 23:00 hours
if (not LateHour) or (LateHour and (decHours>23)): # Otherwise past 23:59:59. Omit time past midnight.
hours[0].append(decHours) # already in float because of conversion to decimal hours.
Doppler[0].append(float(row[1])-beaconfreq[0]) # frequency offset from col 2
# Vpk[0].append (float(row[2])) # Get Volts peak from col 3
Power_dB[0].append (20*math.log10(float(row[2]))) # log power from col 4
#print('nf ',0,'len hours',len(hours[0]))
###############################################################################################
# Find max and min of Power_dB for graph preparation:
min_power=np.amin(Power_dB[0]) # will use for graph axis min
max_power=np.amax(Power_dB[0]) # will use for graph axis max
min_Doppler=np.amin(Doppler[0]) # min Doppler
max_Doppler=np.amax(Doppler[0]) # max Doppler
print('\nDoppler min: ', min_Doppler, '; Doppler max: ', max_Doppler)
print('dB min: ', min_power, '; dB max: ', max_power)
#%% Create an order 3 lowpass butterworth filter.
# This is a digital filter (analog=False)
# Filtering at .01 to .004 times the Nyquist rate seems "about right."
# The filtering argument (Wn, the second argument to butter()) of.01
# represents filtering at .05 Hz, or 20 second weighted averaging.
# That corresponds with the 20 second symmetric averaging window used in the 1 October 2019
# Excel spreadsheet for the Festival of Frequency Measurement data.
#FILTERBREAK=.005 #filter breakpoint in Nyquist rates. N. rate here is 1/sec, so this is in Hz.
FILTERBREAK=0.005 #filter breakpoint in Nyquist rates. N. rate here is 1/sec, so this is in Hz.
FILTERORDER=6
b, a = butter(FILTERORDER, FILTERBREAK, analog=False, btype='low')
#print (b, a)
#%%
# Use the just-created filter coefficients for a noncausal filtering (filtfilt is forward-backward noncausal)
filtDoppler[0] = filtfilt(b, a, Doppler[0])
filtPower[0] = filtfilt(b, a, Power_dB[0])
##################################################################################################
# sunrise sunset times in UTC
sun = Sun(float(Lat), float(Long))
UTC_DT=Filedates[0]
print(UTC_DT)
SDAY=int(UTC_DT[8:10])
SMON=int(UTC_DT[5:7])
SYEAR=int(UTC_DT[0:4])
sdate = datetime.date(SYEAR, SMON, SDAY)
today_sr = sun.get_sunrise_time(sdate)
today_ss = sun.get_sunset_time(sdate)
#print(today_sr)
srh=int(format(today_sr.strftime('%H')))
srm=int(format(today_sr.strftime('%M')))
srx=srh+srm/60
ssh=int(format(today_ss.strftime('%H')))
ssm=int(format(today_ss.strftime('%M')))
ssx=ssh+ssm/60
# set up x-axis with time
fig = plt.figure(figsize=(19,10)) # inches x, y with 72 dots per inch
ax = fig.add_subplot(111)
ax.set_xlabel('UTC Hour')
ax.set_xlim(0,24) # UTC day
ax.set_xticks([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24], minor=False)
# plot first curve
if (PlotTarget == 'Doppler'):
ax.plot([srx,srx],[-1,1],'y',label='sunrise',linestyle='dashed')
ax.plot([ssx,ssx],[-1,1],'b',label='sunset',linestyle='dashed')
ax.plot(hours[0], filtDoppler[0], colors[0],label=Beaconname[0] +' '+Nodenum[0]+' '+Filedates[0]) # color k for black
ax.set_ylabel('Doppler shift, Hz '+ Filedates[0])
ax.set_ylim([-1.0, 1.0]) # -1 to 1 Hz for Doppler shift
plt.axhline(y=0, color="gray", lw=1) # plot a zero freq reference line for 0.000 Hz Doppler shift
else:
ax.plot([srx,srx],[-90,0],'y',label='sunrise',linestyle='dashed')
ax.plot([ssx,ssx],[-90,0],'b',label='sunset',linestyle='dashed')
ax.plot(hours[0], filtPower[0], colors[0],label=Beaconname[0] +' '+ Nodenum[0]+' '+Filedates[0]) # color k for black
ax.set_ylabel('Power, dB '+ Filedates[0])
ax.set_ylim(-90, 0)
# add grid lines - RLB
plt.grid(axis='both')
'''
######################################################################
read and plot files loop
'''
for nf in range(1, nfiles):
# splot second curve
# read second file, skip header
print('process file ',nf, Filenames[nf])
PrFilenames=(PROCESSDIR + Filenames[nf])
with open(PrFilenames, 'r') as dataFile: # read second set
dataReader=csv.reader(dataFile)
data = list(dataReader)
Header = data.pop(0)
Nodenum[nf]= Header[2]
FindUTC = 0
Nodenum[nf], Beaconname[nf], beaconfreq[nf], Lat, Long = readheader(Header)
# print('\n returned ', Nodenum[nf], Beaconname[nf], beaconfreq[nf], Lat, Long, '\n')
for row in data:
if (FindUTC == 0):
#print('looking for UTC - row[0] =',row[0])
if (row[0] == 'UTC'):
FindUTC = 1
# print('UTC found =', row[0])
else:
decHours=time_string_to_decimals(row[0])
hours[nf].append(decHours) # already in float because of conversion to decimal hours.
Doppler[nf].append(float(row[1])-beaconfreq[nf]) # frequency offset from col 2
# Vpk[nf].append (float(row[2])) # Get Volts peak from col 3
# Power_dB[nf].append (float(row[4])) # log power from col 4
Power_dB[nf].append (20*math.log10(float(row[2]))) # log power
# filter file data
filtDoppler[nf] = filtfilt(b, a, Doppler[nf])
filtPower[nf] = filtfilt(b, a, Power_dB[nf])
if (PlotTarget == 'Doppler'):
ax.plot(hours[nf], filtDoppler[nf], colors[nf], label=Beaconname[nf] +' '+ Nodenum[nf]+' '+Filedates[nf]) # color k for black
else:
ax.plot(hours[nf], filtPower[nf], colors[nf], label=Beaconname[nf] +' '+ Nodenum[nf]+' '+Filedates[nf]) # color k for black
'''
#############################################################################
end for read and plot loop, start average
'''
# find shortest data set, limit average to that
if doavg :
al=1000000
ak=0
for k in range(nfiles):
templ=len(hours[k])
if templ < al:
al=templ
ak=k
avg=[]
if (PlotTarget == 'Doppler'):
for i in range(al):
temp=0.0
for j in range(nfiles):
temp=temp+filtDoppler[j][i]
temp=temp/nfiles
avg.append(temp)
else:
for i in range(al):
temp=0.0
for j in range(nfiles):
temp=temp+filtPower[j][i]
temp=temp/nfiles
avg.append(temp)
#print('avg',len(avg))
ax.plot(hours[ak], avg, 'k', label='Average') # color k for black
'''
end average
'''
ax.legend(loc="lower right", frameon=False)
# Create Plot Title
plt.title(' Grape Data Plot')
#plt.title(beaconlabel + ' Grape Data Plot\nNode: ' + node + ' Gridsquare: '+ GridSqr + '\nLat= ' + Lat + ' Long= ' + Long + ' Elev= ' + Elev + ' M\n' )
# Create Plot File Nam
#GraphFile = yesterdaystr + '_' + node + '_' + RadioID + '_' + GridSqr + '_' + beacon + '_graph.png'
GraphFile = 'multi'+ PlotTarget + '.png'
PlotGraphFile = PlotDir + GraphFile
# create plot
#plt.savefig(PlotDir + yesterdaystr + '_' + node + '_' + GridSqr + '_' + RadioID + '_' + beacon + '_graph.png', dpi=250, orientation='landscape')
plt.savefig(PlotDir + GraphFile, dpi=250, orientation='landscape')
# =============================================================================
print('Plot File: ' + GraphFile + '\n') # indicate plot file name for crontab printout
#-------------------------------------------------------------------
print('Exiting python multi plot program gracefully')