def closecurrenttab(self): try: reply = QMessageBox.question(self, 'Message', "Are you sure to close the current tab?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: #getting tab to close curtab = int(self.whatTab()) curtabstr = "Tab " + str(curtab) indextoclose = self.tabWidget.currentIndex() #add any additional necessary commands (stop threads, prevent memory leaks, etc) here #closing tab self.tabWidget.removeTab(indextoclose) #removing current tab data from the alltabdata dict, correcting tabnumbers variable alltabdata.pop("Tab "+str(curtab)) self.tabnumbers.pop(indextoclose) except Exception: trace_error() self.posterror("Failed to close the current tab")
def run(self): #initialize lastgooddata. This is overwritten if a good fix is achieved and is reinitialized every time the signal is emitted lastgooddata = (1, self.default_lat, self.default_lon, self.default_datetime, self.default_nsat, self.default_qual, self.default_alt) while True: #outer loop- always running try: if self.comport.lower() == "n": self.keepGoing = True while self.keepGoing: #loop indefinitely, do nothing, wait for signal to attempt connection with GPS receiver sleep(0.5) else: #different port listed- attempt to connect self.keepGoing = True last_time = self.default_datetime self.nbadsig = 0 with Serial(self.comport, self.baudrate, timeout=1) as ser: while self.keepGoing: #until interrupted try: nmeaobj = parse(ser.readline(96).decode('ascii', errors='replace').strip()) success, data = self.parsegpsdata(nmeaobj) except (nmea.ParseError, OSError): success = False data = (self.default_lat, self.default_lon, self.default_datetime, self.default_nsat, self.default_qual, self.default_alt) #if all of the data is acceptable overwrite the lastgooddata with the most recent fix info if success and (data[0] != 0 and data[1] != 0 and data[3] > -1 and data[4] > -1 and data[5] > -1): lastgooddata = (0, data[0], data[1], data[2], data[3], data[4], data[5]) cdt = datetime.utcnow() #only transmit an update if a certain amount of time has passed and it was a good signal if (cdt - last_time).total_seconds() >= self.updatenseconds: last_time = cdt #if the lastgooddata has contains a good fix if lastgooddata[0] == 0: #emit the data self.signals.update.emit(lastgooddata[0], lastgooddata[1], lastgooddata[2], lastgooddata[3], lastgooddata[4], lastgooddata[5], lastgooddata[6]) #reset lastgooddata after the signal is emitted lastgooddata = (1, self.default_lat, self.default_lon, self.default_datetime, self.default_nsat, self.default_qual, self.default_alt) #reset the number of bad signals (counted based off the number of failed updates) self.nbadsig = 0 else: #change the self.nbadsig += 1 #add one to the number of bad signals if the update #if there have been too many consecutive failed signals, return to the default if self.nbadsig > self.badsiglimit: self.signals.update.emit(lastgooddata[0], lastgooddata[1], lastgooddata[2], lastgooddata[3], lastgooddata[4], lastgooddata[5], lastgooddata[6]) #reset lastgooddata after the info is emitted lastgooddata = (1, self.default_lat, self.default_lon, self.default_datetime, self.default_nsat, self.default_qual, self.default_alt) self.nbadsig = 0 except Exception: #fails to connect to serial port trace_error() self.comport = "n" self.signals.update.emit(2, 0, 0, datetime(1,1,1), 0, 0, 0)
def updateaudiobuffer(streampointer_int, bufferpointer_int, size, samplerate): try: self.numcontacts += 1 #note that the buffer has been pulled again bufferlength = int(size / 2) bufferpointer = cast(bufferpointer_int, POINTER(c_int16 * bufferlength)) bufferdata = bufferpointer.contents self.f_s = samplerate self.nframes += bufferlength self.audiostream.extend( bufferdata[:]) #append data to end del self.audiostream[: bufferlength] #remove data from start #recording to wav file: this terminates if the file exceeds a certain length if self.isrecordingaudio and self.nframes > self.maxsavedframes: self.isrecordingaudio = False self.killaudiorecording() elif self.isrecordingaudio: wave.Wave_write.writeframes( self.wavfile, bytearray(bufferdata)) except Exception: #error handling for callback trace_error() self.kill(10)
def kill(self, reason): #NOTE: function contains 0.3 seconds of sleep to prevent race conditions between the processor loop, callback function and main GUI event loop try: self.waittoterminate = True #keeps run method from terminating until kill process completes self.keepgoing = False # kills while loop curtabnum = self.curtabnum timemodule.sleep( 0.3) #gives thread 0.1 seconds to finish current segment if reason != 0: #notify event loop that processor failed if non-zero exit code provided self.signals.failed.emit(self.curtabnum, reason) self.isrecordingaudio = False if not self.isfromaudio and not self.isfromtest: self.wrdll.SetupStreams(self.hradio, None, None, None, None) timemodule.sleep( 0.3 ) #additional 0.1 seconds after stream directed to null before closing wav file wave.Wave_write.close(self.wavfile) self.wrdll.CloseRadioDevice(self.hradio) self.signals.terminated.emit( curtabnum) # emits signal that processor has been terminated self.txtfile.close() except Exception: trace_error() self.signals.failed.emit(self.curtabnum, 10) self.waittoterminate = False #allow run method to terminate
def savedataincurtab(self): try: #getting directory to save files from QFileDialog outdir = str(QFileDialog.getExistingDirectory(self, "Select Directory to Save File(s)")) if outdir == '': QApplication.restoreOverrideCursor() return False except: self.posterror("Error raised in directory selection") return try: QApplication.setOverrideCursor(Qt.WaitCursor) #pulling all relevant data curtabstr = "Tab " + str(self.whatTab()) #write code to save open files here except Exception: trace_error() #if something else in the file save code broke self.posterror("Filed to save files") QApplication.restoreOverrideCursor() return False finally: QApplication.restoreOverrideCursor() return True
def killaudiorecording(self): try: self.isrecordingaudio = False wave.Wave_write.close(self.wavfile) #close WAV file self.signals.failed.emit(self.curtabnum, 13) #pass warning message back to GUI except Exception: trace_error() self.kill(10)
def renametab(self): try: curtab = self.tabWidget.currentIndex() name, ok = QInputDialog.getText(self, 'Rename Current Tab', 'Enter new tab name:',QLineEdit.Normal,str(self.tabWidget.tabText(curtab))) if ok: self.tabWidget.setTabText(curtab,name) except Exception: trace_error() self.posterror("Failed to rename the current tab")
def add_asterisk(self): try: curtab = self.tabWidget.currentIndex() curtabstr = "Tab " + str(self.whatTab()) name = self.tabWidget.tabText(curtab) if not self.alltabdata[curtabstr]["profileSaved"] and name[-1] != '*': self.tabWidget.setTabText(curtab, name + '*') except Exception: trace_error() self.posterror("Failed to add unsave asterisk to tab name")
def changecurrentfrequency(self, newfreq): #update VHF frequency for WiNRADIO # change frequency- kill if failed try: self.vhffreq_2WR = c_ulong(int(newfreq * 1E6)) if self.wrdll.SetFrequency(self.hradio, self.vhffreq_2WR) == 0: self.kill(4) except Exception: trace_error() self.kill(4)
def remove_asterisk(self): try: curtab = self.tabWidget.currentIndex() curtabstr = "Tab " + str(self.whatTab()) name = self.tabWidget.tabText(curtab) if self.alltabdata[curtabstr]["profileSaved"] and name[-1] == '*': self.tabWidget.setTabText(curtab, name[:-1]) except Exception: trace_error() self.posterror("Failed to remove unsave asterisk from tab name")
def __init__(self): super().__init__() try: self.initUI() #creates GUI window self.buildmenu() #Creates interactive menu, options to create tabs and start autoQC self.makenewtab() #Opens first tab except Exception: trace_error() self.posterror("Failed to initialize the program.")
def __init__(self): super().__init__() try: self.initUI() #creates GUI window self.buildmenu() #Creates interactive menu, options to create tabs and run ARES systems self.loaddata() #loads climo and bathy data into program first if using the full datasets self.makenewprocessortab() # opens a data acquisition tab on startup except Exception: trace_error() self.posterror("Failed to initialize the program.")
def closecurrenttab(self): try: curtab = int(self.whatTab()) curtabstr = "Tab " + str(curtab) if self.alltabdata[curtabstr]["tabtype"] == "MissionTracker": self.postwarning("You cannot close the mission tracker tab!") return reply = QMessageBox.question(self, 'Message', "Are you sure to close the current tab?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: #getting tab to close indextoclose = self.tabWidget.currentIndex() #check to make sure there isn't a corresponding processor thread, close if there is if self.alltabdata[curtabstr]["isprocessing"]: reply = QMessageBox.question( self, 'Message', "Closing this tab will terminate the current profile and discard the data. Continue?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: return else: self.alltabdata[curtabstr]["processor"].abort() #closing open figures in tab to prevent memory leak if self.alltabdata[curtabstr]["tabtype"] == "ProfileEditor": plt.close(self.alltabdata[curtabstr]["ProfFig"]) plt.close(self.alltabdata[curtabstr]["LocFig"]) elif self.alltabdata[curtabstr][ "tabtype"] == 'SignalProcessor_incomplete' or self.alltabdata[ curtabstr]["tabtype"] == 'SignalProcessor_completed': plt.close(self.alltabdata[curtabstr]["ProcessorFig"]) #closing tab self.tabWidget.removeTab(indextoclose) #removing current tab data from the self.alltabdata dict, correcting tabnumbers variable self.alltabdata.pop("Tab " + str(curtab)) self.tabnumbers.pop(indextoclose) except Exception: trace_error() self.posterror("Failed to close the current tab")
def renametab(self): try: curtab = self.tabWidget.currentIndex() curtabstr = "Tab " + str(self.whatTab()) badcharlist = "[@!#$%^&*()<>?/\|}{~:]" strcheck = re.compile(badcharlist) name, ok = QInputDialog.getText(self, 'Rename Current Tab', 'Enter new tab name:', QLineEdit.Normal, str(self.tabWidget.tabText(curtab))) if ok: if strcheck.search("name") == None: self.tabWidget.setTabText(curtab, name) if not self.alltabdata[curtabstr][ "profileSaved"]: #add an asterisk if profile is unsaved self.add_asterisk() else: self.postwarning("Tab names cannot include the following: " + badcharlist) except Exception: trace_error() self.posterror("Failed to rename the current tab")
def parsestringinputs(self, latstr, lonstr, profdatestr, timestr, identifier, checkcoords, checktime, checkid): try: #parsing and checking data if checkcoords: try: #checking latitude validity latstr = latstr.split(',') latsign = np.sign(float(latstr[0])) if len(latstr) == 3: lat = float(latstr[0]) + latsign * float( latstr[1]) / 60 + latsign * float(latstr[2]) / 3600 elif len(latstr) == 2: lat = float(latstr[0]) + latsign * float(latstr[1]) / 60 else: lat = float(latstr[0]) except: self.postwarning('Invalid Latitude Entered!') return try: #checking longitude validity lonstr = lonstr.split(',') lonsign = np.sign(float(lonstr[0])) if len(lonstr) == 3: lon = float(lonstr[0]) + lonsign * float( lonstr[1]) / 60 + lonsign * float(lonstr[2]) / 3600 elif len(lonstr) == 2: lon = float(lonstr[0]) + lonsign * float(lonstr[1]) / 60 else: lon = float(lonstr[0]) except: self.postwarning('Invalid Longitude Entered!') return if lon < -180 or lon > 180: self.postwarning('Longitude must be between -180 and 180') elif lat < -90 or lat > 90: self.postwarning('Latitude must be between -90 and 90') lon = round(lon, 3) lat = round(lat, 3) else: lon = np.NaN lat = np.NaN if checktime: #checking time if len(timestr) != 4: self.postwarning('Invalid Time Format (must be HHMM)!') return elif len(profdatestr) != 8: self.postwarning('Invalid Date Format (must be YYYYMMDD)!') return try: #checking date year = int(profdatestr[:4]) month = int(profdatestr[4:6]) day = int(profdatestr[6:]) except: self.postwarning('Invalid (non-numeric) Date Entered!') return try: time = int(timestr) hour = int(timestr[:2]) minute = int(timestr[2:4]) except: self.postwarning('Invalid (non-numeric) Time Entered!') return if year < 1938 or year > 3000: #year the bathythermograph was invented and the year by which it was probably made obsolete self.postwarning( 'Invalid Year Entered (< 1938 AD or > 3000 AD)!') return elif month <= 0 or month > 12: self.postwarning( "Invalid Month Entered (must be between 1 and 12)") return elif hour > 23 or hour < 0: self.postwarning( 'Invalid Time Entered (hour must be between 0 and 23') return elif minute >= 60 or minute < 0: self.postwarning( 'Invalid Time Entered (minute must be between 0 and 59') return #figuring out number of days in month monthnames = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ] if month in [1, 3, 5, 7, 8, 10, 12]: maxdays = 31 elif month in [4, 6, 9, 11]: maxdays = 30 elif month == 2 and year % 4 == 0: maxdays = 29 elif month == 2: maxdays = 28 else: self.postwarning('Invalid month entered!') #checking to make sure days are in valid range if day <= 0 or day > maxdays: self.postwarning( f"Invalid Day Entered (must be between 1 and {maxdays} for {monthnames[month-1]})" ) return #making sure the profile is within 12 hours and not in the future, warning if otherwise curtime = timemodule.gmtime() deltat = dt.datetime( curtime[0], curtime[1], curtime[2], curtime[3], curtime[4], curtime[5]) - dt.datetime(year, month, day, hour, minute, 0) option = '' if self.settingsdict["dtgwarn"]: if deltat.days < 0: option = self.postwarning_option( "Drop time appears to be after the current time. Continue anyways?" ) elif deltat.days > 1 or (deltat.days == 0 and deltat.seconds > 12 * 3600): option = self.postwarning_option( "Drop time appears to be more than 12 hours ago. Continue anyways?" ) if option == 'cancel': return else: year = np.NaN month = np.NaN day = np.NaN time = np.NaN hour = np.NaN minute = np.NaN #check length of identifier if checkid and len(identifier) != 5: option = self.postwarning_option( "Identifier is not 5 characters! Continue anyways?") if option == 'cancel': return return lat, lon, year, month, day, time, hour, minute, identifier except Exception: trace_error() self.posterror("Unspecified error in reading profile information!") return
def run(self): #barrier to prevent signal processor loop from starting before __init__ finishes counts = 0 while self.startthread != 100: counts += 1 if counts > 100 or not self.keepgoing: #give up and terminate after 10 seconds waiting for __init__ self.kill(12) return elif self.startthread != 0 and self.startthread != 100: #if the audio file couldn't be read in properly self.kill( self.startthread ) #waits to run kill commands due to errors raised in __init__ until run() since slots+signals may not be connected to parent thread during init return timemodule.sleep(0.1) #if the Run() method gets this far, __init__ has completed successfully (and set self.startthread = 100) try: if not self.isfromaudio and not self.isfromtest: #if source is a receiver #Declaring the callbuck function to update the audio buffer @CFUNCTYPE(None, c_void_p, c_void_p, c_ulong, c_ulong) def updateaudiobuffer(streampointer_int, bufferpointer_int, size, samplerate): try: self.numcontacts += 1 #note that the buffer has been pulled again bufferlength = int(size / 2) bufferpointer = cast(bufferpointer_int, POINTER(c_int16 * bufferlength)) bufferdata = bufferpointer.contents self.f_s = samplerate self.nframes += bufferlength self.audiostream.extend( bufferdata[:]) #append data to end del self.audiostream[: bufferlength] #remove data from start #recording to wav file: this terminates if the file exceeds a certain length if self.isrecordingaudio and self.nframes > self.maxsavedframes: self.isrecordingaudio = False self.killaudiorecording() elif self.isrecordingaudio: wave.Wave_write.writeframes( self.wavfile, bytearray(bufferdata)) except Exception: #error handling for callback trace_error() self.kill(10) #end of callback function # initializes audio callback function if self.wrdll.SetupStreams(self.hradio, None, None, updateaudiobuffer, c_int(self.curtabnum)) == 0: self.kill(7) else: timemodule.sleep(0.3) # gives the buffer time to populate else: #if source is an audio file #configuring sample times for the audio file self.lensignal = len(self.audiostream) self.maxtime = self.lensignal / self.f_s self.sampletimes = np.arange(0.1, self.maxtime - 0.1, 0.1) # setting up thread while loop- terminates when user clicks "STOP" or audio file finishes processing i = -1 #MAIN PROCESSOR LOOP while self.keepgoing: i += 1 # finds time from profile start in seconds curtime = dt.datetime.utcnow() # current time deltat = curtime - self.starttime ctime = deltat.total_seconds() if not self.isfromaudio and not self.isfromtest: #protocal to kill thread if connection with WiNRADIO is lost if self.numcontacts == self.lastcontacts: #checks if the audio stream is receiving new data self.disconnectcount += 1 else: self.disconnectcount = 0 self.lastcontacts = self.numcontacts #if the audio stream hasn't received new data for several iterations and checking device connection fails if self.disconnectcount >= 30 and not self.wrdll.IsDeviceConnected( self.hradio): self.kill(8) # listens to current frequency, gets sound level, set audio stream, and corresponding time currentdata = self.audiostream[-int(self.f_s * self.fftwindow):] else: #kill test/audio threads once time exceeds the max time of the audio file #NOTE: need to do this on the cycle before hitting the max time when processing from audio because # the WAV file processes faster than the thread can kill itself if (self.isfromtest and ctime >= self.maxtime - self.fftwindow) or ( self.isfromaudio and i >= len(self.sampletimes) - 1): self.keepgoing = False self.kill(0) return #getting current time to sample from audio file if self.isfromaudio: ctime = self.sampletimes[i] if i % 10 == 0: #updates progress every 10 data points self.signals.updateprogress.emit( self.curtabnum, int(ctime / self.maxtime * 100)) #getting current data to sample from audio file- using indices like this is much more efficient than calculating times and using logical arrays ctrind = int(np.round(ctime * self.f_s)) pmind = int( np.min([ np.round(self.f_s * self.fftwindow / 2), ctrind, self.lensignal - ctrind - 1 ])) #uses minimum value so no overflow currentdata = self.audiostream[ctrind - pmind:ctrind + pmind] #conducting FFT or skipping, depending on signal strength fp, Sp, Rp = dofft(currentdata, self.f_s, self.flims) #rounding before comparisons happen ctime = np.round(ctime, 1) fp = np.round(fp, 2) Sp = np.round(Sp, 2) Rp = np.round(Rp, 3) #writing raw data to sigdata file (ASCII) for current thread- before correcting for minratio/minsiglev if self.keepgoing: #only writes if thread hasn't been stopped since start of current segment self.txtfile.write(f"{ctime},{fp},{Sp},{Rp}\n") #logic to determine whether or not profile is triggered if not self.istriggered and Sp >= self.triggersiglev and Rp >= self.triggerfftratio: self.istriggered = True self.firstpointtime = ctime if self.keepgoing: #won't send if keepgoing stopped since current iteration began self.signals.triggered.emit(self.curtabnum, ctime) #logic to determine whether or not point is valid if self.istriggered and Sp >= self.minsiglev and Rp >= self.minfftratio: cdepth = btconvert(ctime - self.firstpointtime, self.zcoeff) ctemp = btconvert(fp, self.tcoeff) else: fp = 0 ctemp = cdepth = np.NaN # tells GUI to update data structure, plot, and table ctemp = np.round(ctemp, 2) cdepth = np.round(cdepth, 1) if self.keepgoing: #won't send if keepgoing stopped since current iteration began self.signals.iterated.emit(self.curtabnum, ctemp, cdepth, fp, Sp, np.round(100 * Rp, 1), ctime, i) if not self.isfromaudio: timemodule.sleep( 0.1) #pauses when processing in realtime (fs ~ 10 Hz) else: timemodule.sleep( 0.001 ) #slight pause to free some resources when processing from audio except Exception: #if the thread encounters an error, terminate trace_error() # if there is an error, terminates processing if self.keepgoing: self.kill(10) while self.waittoterminate: #waits for kill process to complete to avoid race conditions with audio buffer callback timemodule.sleep(0.1)
def __init__(self, wrdll, datasource, vhffreq, curtabnum, starttime, istriggered, firstpointtime, fftwindow, minfftratio, minsiglev, triggerfftratio, triggersiglev, tcoeff, zcoeff, flims, slash, tempdir, *args, **kwargs): super(ThreadProcessor, self).__init__() #prevents Run() method from starting before init is finished (value must be changed to 100 at end of __init__) self.startthread = 0 # UI inputs self.curtabnum = curtabnum self.starttime = starttime self.istriggered = istriggered self.firstpointtime = firstpointtime self.keepgoing = True # signal connections self.waittoterminate = False #whether to pause on termination of run loop for kill process to complete self.signals = ThreadProcessorSignals() #FFT thresholds self.fftwindow = fftwindow self.minfftratio = minfftratio self.minsiglev = minsiglev self.triggerfftratio = triggerfftratio self.triggersiglev = triggersiglev #conversion coefficients + parameters self.tcoeff = tcoeff self.zcoeff = zcoeff self.flims = flims #output file names self.txtfilename = tempdir + slash + "sigdata_" + str( self.curtabnum) + '.txt' self.txtfile = open(self.txtfilename, 'w') self.wavfilename = tempdir + slash + "tempwav_" + str( self.curtabnum) + '.WAV' #to prevent ARES from consuming all computer's resources- this limits the size of WAV files used by the signal processor to a number of PCM datapoints corresponding to 1 hour of audio @ fs=64 kHz, that would produce a wav file of ~0.5 GB for 16-bit PCM data self.maxsavedframes = 2.5E8 self.isrecordingaudio = True #initialized to True for all cases (RF, test, and audio) but only matters in the callback function assigned for RF receivers # identifying whether tab is audio, test, or other format self.isfromaudio = False self.isfromtest = False if datasource[:5] == 'Audio': self.chselect = int(datasource[5:10]) self.audiofile = datasource[10:] self.isfromaudio = True #checking file length- wont process files with more frames than max size try: #exception if unable to read audio file if it doesn't exist or isn't WAV formatted file_info = wave.open(self.audiofile) except: self.startthread = 11 return if file_info.getnframes() > self.maxsavedframes: self.startthread = 9 return self.f_s, snd = wavfile.read(self.audiofile) #reading file #if multiple channels, sum them together sndshape = np.shape(snd) #array size (tuple) ndims = len(sndshape) #number of dimensions if ndims == 1: #if one channel, use that self.audiostream = snd elif ndims == 2: #if two channels, pick selected channel, otherwise sum if self.chselect >= 1: self.audiostream = snd[:, self.chselect - 1] else: self.audiostream = np.sum(snd, axis=1) else: #if more than 2D- not a valid file self.audiostream = [0] * 10000 self.startthread = 11 elif datasource == 'Test': #test run- use included audio file self.audiofile = 'testdata/MZ000006.WAV' self.isfromtest = True try: #exception if unable to read audio file if it doesn't exist or isn't WAV formatted self.f_s, snd = wavfile.read(self.audiofile) except: self.startthread = 11 return self.audiostream = snd[:, 0] #if thread is to be connected to a WiNRADIO if not self.isfromaudio and not self.isfromtest: #initializing variables to check if WiNRADIO remains connected self.disconnectcount = 0 self.numcontacts = 0 self.lastcontacts = 0 self.nframes = 0 # initialize audio stream data variables self.f_s = 64000 # default value self.audiostream = [ 0 ] * 2 * self.f_s #initializes the buffer with 2 seconds of zeros # saves WiNRADIO DLL/API library self.wrdll = wrdll # initialize winradio self.serial = datasource # translate winradio identifier self.serialnum_2WR = c_char_p(self.serial.encode('utf-8')) #setup WAV file to write (if audio or test, source file is copied instead) self.wavfile = wave.open(self.wavfilename, 'wb') wave.Wave_write.setnchannels(self.wavfile, 1) wave.Wave_write.setsampwidth(self.wavfile, 2) wave.Wave_write.setframerate(self.wavfile, self.f_s) wave.Wave_write.writeframes(self.wavfile, bytearray(self.audiostream)) #opening current WiNRADIO/establishing contact self.hradio = self.wrdll.Open(self.serialnum_2WR) if self.hradio == 0: self.startthread = 1 return try: # power on- kill if failed if wrdll.SetPower(self.hradio, True) == 0: self.startthread = 2 return # initialize demodulator- kill if failed if wrdll.InitializeDemodulator(self.hradio) == 0: self.startthread = 3 return # change frequency- kill if failed self.vhffreq_2WR = c_ulong(int(vhffreq * 1E6)) if self.wrdll.SetFrequency(self.hradio, self.vhffreq_2WR) == 0: self.startthread = 4 return # set volume- warn if failed if self.wrdll.SetVolume(self.hradio, 31) == 0: self.startthread = 5 return except Exception: #if any WiNRADIO comms/initialization attempts failed, terminate thread trace_error() self.startthread = 6 return else: shcopy(self.audiofile, self.wavfilename ) #copying audio file if datasource = Test or Audio self.startthread = 100
def savedataincurtab(self): successval = True #changes to False if error is raised try: #getting directory to save files from QFileDialog try: outdir = str( QFileDialog.getExistingDirectory( self, "Select Directory to Save File(s)", self.defaultfilewritedir, QFileDialog.DontUseNativeDialog)) except Exception: trace_error() return False #checking directory validity if outdir == '': QApplication.restoreOverrideCursor() return False else: self.defaultfilewritedir = outdir except: self.posterror("Error raised in directory selection") return try: QApplication.setOverrideCursor(Qt.WaitCursor) #pulling all relevant data curtabstr = "Tab " + str(self.whatTab()) if self.alltabdata[curtabstr]["tabtype"] == "ProfileEditor": try: rawtemperature = self.alltabdata[curtabstr]["profdata"][ "temp_raw"] rawdepth = self.alltabdata[curtabstr]["profdata"]["depth_raw"] climotempfill = self.alltabdata[curtabstr]["profdata"][ "climotempfill"] climodepthfill = self.alltabdata[curtabstr]["profdata"][ "climodepthfill"] temperature = self.alltabdata[curtabstr]["profdata"][ "temp_plot"] depth = self.alltabdata[curtabstr]["profdata"]["depth_plot"] day = self.alltabdata[curtabstr]["profdata"]["day"] month = self.alltabdata[curtabstr]["profdata"]["month"] year = self.alltabdata[curtabstr]["profdata"]["year"] time = self.alltabdata[curtabstr]["profdata"]["time"] lat = self.alltabdata[curtabstr]["profdata"]["lat"] lon = self.alltabdata[curtabstr]["profdata"]["lon"] identifier = self.alltabdata[curtabstr]["profdata"]["ID"] num = 99 #placeholder- dont have drop number here currently!! dtg = str(year) + str(month).zfill(2) + str(day).zfill( 2) + str(time).zfill(4) curtab = self.tabWidget.currentIndex() filename = self.check_filename(dtg) if self.settingsdict["overlayclimo"]: matchclimo = self.alltabdata[curtabstr]["profdata"][ "matchclimo"] else: matchclimo = 1 except: self.posterror("Failed to retrieve profile information") QApplication.restoreOverrideCursor() return False if self.settingsdict["savefin"]: try: depth1m = np.arange(0, np.floor(depth[-1])) temperature1m = np.interp(depth1m, depth, temperature) tfio.writefinfile(outdir + slash + filename + '.fin', temperature1m, depth1m, day, month, year, time, lat, lon, num) except Exception: trace_error() self.posterror("Failed to save FIN file") if self.settingsdict["savejjvv"]: isbtmstrike = self.alltabdata[curtabstr]["tabwidgets"][ "isbottomstrike"].isChecked() try: tfio.writejjvvfile(outdir + slash + filename + '.jjvv', temperature, depth, day, month, year, time, lat, lon, identifier, isbtmstrike) except Exception: trace_error() self.posterror("Failed to save JJVV file") if self.settingsdict["savebufr"]: try: tfio.writebufrfile(outdir + slash + filename + '.bufr', temperature, depth, year, month, day, time, lon, lat, identifier, self.settingsdict["originatingcenter"], False, b'\0') except Exception: trace_error() self.posterror("Failed to save BUFR file") if self.settingsdict["saveprof"]: try: fig1 = plt.figure() fig1.clear() ax1 = fig1.add_axes([0.1, 0.1, 0.85, 0.85]) climohandle = tplot.makeprofileplot( ax1, rawtemperature, rawdepth, temperature, depth, climotempfill, climodepthfill, dtg, matchclimo) if self.settingsdict["overlayclimo"] == 0: climohandle.set_visible(False) fig1.savefig(outdir + slash + filename + '_prof.png', format='png') except Exception: trace_error() self.posterror("Failed to save profile image") finally: plt.close('fig1') if self.settingsdict["saveloc"]: try: fig2 = plt.figure() fig2.clear() ax2 = fig2.add_axes([0.1, 0.1, 0.85, 0.85]) _, exportlat, exportlon, exportrelief = oci.getoceandepth( lat, lon, 6, self.bathymetrydata) tplot.makelocationplot(fig2, ax2, lat, lon, dtg, exportlon, exportlat, exportrelief, 6) fig2.savefig(outdir + slash + filename + '_loc.png', format='png') except Exception: trace_error() self.posterror("Failed to save location image") finally: plt.close('fig2') elif self.alltabdata[curtabstr][ "tabtype"] == "SignalProcessor_completed": if self.alltabdata[curtabstr]["isprocessing"]: self.postwarning( 'You must stop processing the current tab before saving data!' ) else: try: #pulling prof data rawtemperature = self.alltabdata[curtabstr]["rawdata"][ "temperature"] rawdepth = self.alltabdata[curtabstr]["rawdata"]["depth"] frequency = self.alltabdata[curtabstr]["rawdata"][ "frequency"] timefromstart = self.alltabdata[curtabstr]["rawdata"][ "time"] #pulling profile metadata if necessary try: lat = self.alltabdata[curtabstr]["rawdata"]["lat"] lon = self.alltabdata[curtabstr]["rawdata"]["lon"] year = self.alltabdata[curtabstr]["rawdata"]["year"] month = self.alltabdata[curtabstr]["rawdata"]["month"] day = self.alltabdata[curtabstr]["rawdata"]["day"] time = self.alltabdata[curtabstr]["rawdata"][ "droptime"] hour = self.alltabdata[curtabstr]["rawdata"]["hour"] minute = self.alltabdata[curtabstr]["rawdata"][ "minute"] except: # pulling data from inputs latstr = self.alltabdata[curtabstr]["tabwidgets"][ "latedit"].text() lonstr = self.alltabdata[curtabstr]["tabwidgets"][ "lonedit"].text() profdatestr = self.alltabdata[curtabstr]["tabwidgets"][ "dateedit"].text() timestr = self.alltabdata[curtabstr]["tabwidgets"][ "timeedit"].text() #flags for capability of saving data edfcapable = True logcapable = True wavcapable = True sigcapable = True #check validity of data #try edf data try: lat, lon, year, month, day, time, hour, minute, _ = self.parsestringinputs( latstr, lonstr, profdatestr, timestr, 'omit', True, True, False) except: edfcapable = False self.postwarning('Cannot save edf file!') #try other data try: _, _, year, month, day, time, hour, minute, _ = self.parsestringinputs( latstr, lonstr, profdatestr, timestr, 'omit', False, True, False) except: logcapable = False, wavcapable = False sigcapable = False self.postwarning("Failed to save raw data files!") QApplication.restoreOverrideCursor() except Exception: trace_error() self.posterror("Failed to pull raw profile data") QApplication.restoreOverrideCursor() return False #date and time strings for LOG file initdatestr = str(year) + '/' + str(month).zfill( 2) + '/' + str(day).zfill(2) inittimestr = str(hour).zfill(2) + ':' + str(minute).zfill( 2) + ':00' filename = self.check_filename( str(year) + str(month).zfill(2) + str(day).zfill(2) + str(time).zfill(4)) if self.settingsdict["savelog"] and logcapable: try: tfio.writelogfile(outdir + slash + filename + '.DTA', initdatestr, inittimestr, timefromstart, rawdepth, frequency, rawtemperature) except Exception: trace_error() self.posterror("Failed to save LOG file") if self.settingsdict["saveedf"] and edfcapable: try: #creating comment for data source: cdatasource = self.alltabdata[curtabstr]["tabwidgets"][ "datasource"].currentText() comments = "//Data source: " + cdatasource if cdatasource.lower() not in ["audio", "test"]: comments += f", VHF Ch. {self.alltabdata[curtabstr]['tabwidgets']['vhfchannel'].value()} ({self.alltabdata[curtabstr]['tabwidgets']['vhffreq'].value()} MHz)" tfio.writeedffile( outdir + slash + filename + '.edf', rawtemperature, rawdepth, year, month, day, hour, minute, 0, lat, lon, self.settingsdict["tcoeff"], self.settingsdict["zcoeff"], comments ) #lat/lon only parsed if self.settingsdict["saveedf"] is True except Exception: trace_error() self.posterror("Failed to save EDF file") if self.settingsdict["savewav"] and wavcapable: try: oldfile = self.tempdir + slash + 'tempwav_' + str( self.alltabdata[curtabstr]["tabnum"]) + '.WAV' newfile = outdir + slash + filename + '.WAV' if path.exists(oldfile) and path.exists( newfile ) and oldfile != newfile: #if file already exists remove(newfile) shcopy(oldfile, newfile) except Exception: trace_error() self.posterror("Failed to save WAV file") if self.settingsdict["savesig"] and sigcapable: try: oldfile = self.tempdir + slash + 'sigdata_' + str( self.alltabdata[curtabstr]["tabnum"]) + '.txt' newfile = outdir + slash + filename + '.sigdata' if path.exists(oldfile) and path.exists( newfile) and oldfile != newfile: remove(newfile) shcopy(oldfile, newfile) except Exception: trace_error() elif self.alltabdata[curtabstr]["tabtype"] == "MissionPlotter": filename = str( self.tabWidget.tabText( self.tabWidget.currentIndex())) #filename is name of tab self.alltabdata[curtabstr]["MissionFig"].savefig(outdir + slash + filename + '.png', format='png') else: self.postwarning( 'You must process a profile before attempting to save data!') except Exception: QApplication.restoreOverrideCursor( ) #restore cursor here as extra measure trace_error() #if something else in the file save code broke self.posterror("Failed to save files") successval = False #notes that process failed finally: QApplication.restoreOverrideCursor() #restore cursor here self.alltabdata[curtabstr][ "profileSaved"] = True #note that profile has been saved if successval: self.alltabdata[curtabstr]["profileSaved"] = True self.remove_asterisk() return successval
def makenewtab(self): try: newtabnum,curtabstr = self.addnewtab() #creates dictionary entry for current tab- you can add additional key/value combinations for the opened tab at any point after the dictionary has been initialized alltabdata[curtabstr] = {"tab":QWidget(),"tablayout":QGridLayout(), "tabtype":"newtab","testvariable":False} self.setnewtabcolor(alltabdata[curtabstr]["tab"]) alltabdata[curtabstr]["tablayout"].setSpacing(10) #creating new tab, assigning basic info self.tabWidget.addTab(alltabdata[curtabstr]["tab"],'New Tab') self.tabWidget.setCurrentIndex(newtabnum) self.tabWidget.setTabText(newtabnum, "New Tab #" + str(self.totaltabs)) alltabdata[curtabstr]["tabnum"] = self.totaltabs #assigning unique, unchanging number to current tab alltabdata[curtabstr]["tablayout"].setSpacing(10) #and add new buttons and other widgets alltabdata[curtabstr]["tabwidgets"] = {} #making widgets alltabdata[curtabstr]["tabwidgets"]["sourcetitle"] = QLabel(' Source:') #1 alltabdata[curtabstr]["tabwidgets"]["refresh"] = QPushButton('Refresh') # 2 alltabdata[curtabstr]["tabwidgets"]["options"] = QComboBox() #3 alltabdata[curtabstr]["tabwidgets"]["options"].addItem('Item A') alltabdata[curtabstr]["tabwidgets"]["options"].addItem('Item B') alltabdata[curtabstr]["currentoption"] = alltabdata[curtabstr]["tabwidgets"]["options"].currentText() alltabdata[curtabstr]["tabwidgets"]["sb1title"] = QLabel('Spinbox 1:') #4 alltabdata[curtabstr]["tabwidgets"]["sb2title"] = QLabel('Spinbox 2:') #5 alltabdata[curtabstr]["tabwidgets"]["sb1"] = QSpinBox() #6 alltabdata[curtabstr]["tabwidgets"]["sb1"].setRange(1,99) alltabdata[curtabstr]["tabwidgets"]["sb1"].setSingleStep(1) alltabdata[curtabstr]["tabwidgets"]["sb1"].setValue(12) alltabdata[curtabstr]["tabwidgets"]["sb2"] = QDoubleSpinBox() #7 alltabdata[curtabstr]["tabwidgets"]["sb2"].setRange(20, 30) alltabdata[curtabstr]["tabwidgets"]["sb2"].setSingleStep(0.25) alltabdata[curtabstr]["tabwidgets"]["sb2"].setDecimals(2) alltabdata[curtabstr]["tabwidgets"]["sb2"].setValue(23.5) #run function every time spinbox value is changed #alltabdata[curtabstr]["tabwidgets"]["sb2"].valueChanged.connect(self.callbackfunction) alltabdata[curtabstr]["tabwidgets"]["b1"] = QPushButton('Button 1') #8 alltabdata[curtabstr]["tabwidgets"]["b2"] = QPushButton('Button 2') #9 alltabdata[curtabstr]["tabwidgets"]["b3"] = QPushButton('Button 3') #10 alltabdata[curtabstr]["tabwidgets"]["t1t"] = QLabel('Entry 1:') #11 alltabdata[curtabstr]["tabwidgets"]["t1"] = QLineEdit('E1 example') #12 alltabdata[curtabstr]["tabwidgets"]["t2t"] = QLabel('Entry 2: ') #13 alltabdata[curtabstr]["tabwidgets"]["t2"] = QLineEdit('E2 example') #14 alltabdata[curtabstr]["tabwidgets"]["t3t"] = QLabel('Entry 3: ') #15 alltabdata[curtabstr]["tabwidgets"]["t3"] = QLineEdit('E3 example') #16 alltabdata[curtabstr]["tabwidgets"]["t4t"] = QLabel('Entry 4: ') #17 alltabdata[curtabstr]["tabwidgets"]["t4"] = QLineEdit('E4 example') #18 alltabdata[curtabstr]["tabwidgets"]["t5t"] = QLabel('Entry 5: ') #19 alltabdata[curtabstr]["tabwidgets"]["t5"] = QLineEdit('E5 example') #20 #formatting widgets alltabdata[curtabstr]["tabwidgets"]["sb1title"].setAlignment(Qt.AlignRight | Qt.AlignVCenter) alltabdata[curtabstr]["tabwidgets"]["sb2title"].setAlignment(Qt.AlignRight | Qt.AlignVCenter) alltabdata[curtabstr]["tabwidgets"]["t1t"].setAlignment(Qt.AlignRight | Qt.AlignVCenter) alltabdata[curtabstr]["tabwidgets"]["t2t"].setAlignment(Qt.AlignRight | Qt.AlignVCenter) alltabdata[curtabstr]["tabwidgets"]["t3t"].setAlignment(Qt.AlignRight | Qt.AlignVCenter) alltabdata[curtabstr]["tabwidgets"]["t4t"].setAlignment(Qt.AlignRight | Qt.AlignVCenter) alltabdata[curtabstr]["tabwidgets"]["t5t"].setAlignment(Qt.AlignRight | Qt.AlignVCenter) #should be 19 entries widgetorder = ["sourcetitle","refresh","options","sb1title","sb2title","sb1","sb2", "b1","b2","b3","t1t","t1","t2t","t2","t3t","t3", "t4t","t4","t5t","t5"] wrows = [1,1,2,3,4,3,4,5,5,6,1,1,2,2,3,3,4,4,5,5] wcols = [2,3,2,2,2,3,3,2,3,3,4,5,4,5,4,5,4,5,4,5] wrext = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] wcolext = [1,1,2,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1] #adding user inputs for i,r,c,re,ce in zip(widgetorder,wrows,wcols,wrext,wcolext): alltabdata[curtabstr]["tablayout"].addWidget(alltabdata[curtabstr]["tabwidgets"][i],r,c,re,ce) #adjusting stretch factors for all rows/columns colstretch = [5,1,1,1,1,1,1] for col,cstr in zip(range(0,len(colstretch)),colstretch): alltabdata[curtabstr]["tablayout"].setColumnStretch(col,cstr) rowstretch = [1,1,1,1,1,1,1,1,10] for row,rstr in zip(range(0,len(rowstretch)),rowstretch): alltabdata[curtabstr]["tablayout"].setRowStretch(row,rstr) #making the current layout for the tab alltabdata[curtabstr]["tab"].setLayout(alltabdata[curtabstr]["tablayout"]) except Exception: #if something breaks trace_error() self.posterror("Failed to build new tab")