diff --git a/lasp/c/lasp_fft.c b/lasp/c/lasp_fft.c index f238083..46eeec5 100644 --- a/lasp/c/lasp_fft.c +++ b/lasp/c/lasp_fft.c @@ -28,7 +28,7 @@ typedef struct Fft_s { d* real_storage; } Fft_s; #else -#error "Cannot compile lasp_ffc.c, no FFT backend specified" +#error "Cannot compile lasp_ffc.c, no FFT backend specified. Should either be FFTPack, or FFTW" #endif void load_fft_wisdom(const char* wisdom) { @@ -130,7 +130,7 @@ void Fft_ifft_single(const Fft* fft,const vc* freqdata,vd* result) { getvdval(&fft->fft_work,0)); -#elif defined LASP_FFT_BACKEND_FFTW +#elif LASP_FFT_BACKEND == FFTW c* freqdata_ptr = (c*) getvcval(freqdata,0); c_copy(fft->complex_storage, freqdata_ptr,nfft/2+1); @@ -219,7 +219,7 @@ void Fft_fft_single(const Fft* fft,const vd* timedata,vc* result) { result_ptr[nfft+1] = 0; } check_overflow_vx(fft->fft_work); -#elif defined LASP_FFT_BACKEND_FFTW +#elif LASP_FFT_BACKEND == FFTW d* timedata_ptr = getvdval(timedata,0); c* result_ptr = getvcval(result,0); diff --git a/lasp/c/lasp_mq.c b/lasp/c/lasp_mq.c index 8e96130..b5031f9 100644 --- a/lasp/c/lasp_mq.c +++ b/lasp/c/lasp_mq.c @@ -96,10 +96,10 @@ void print_job_queue(JobQueue* jq) { JobQueue* JobQueue_alloc(const us max_jobs) { TRACE(15,"JobQueue_alloc"); - if(max_jobs > LASP_MAX_NUM_CHANNELS) { - WARN("Max jobs restricted to LASP_MAX_NUM_CHANNELS"); - return NULL; - } + /* if(max_jobs > LASP_MAX_NUM_CHANNELS) { */ + /* WARN("Max jobs restricted to LASP_MAX_NUM_CHANNELS"); */ + /* return NULL; */ + /* } */ JobQueue* jq = a_malloc(sizeof(JobQueue)); diff --git a/lasp/device/lasp_cppdaq.cpp b/lasp/device/lasp_cppdaq.cpp index 04d7aa8..af4997e 100644 --- a/lasp/device/lasp_cppdaq.cpp +++ b/lasp/device/lasp_cppdaq.cpp @@ -153,7 +153,7 @@ Daq::Daq(const DeviceInfo &devinfo, const DaqConfiguration &config) if (monitorOutput && !(nenoutchannels() > 0)) { throw runtime_error( - "Output monitoring only possible when output is enabled"); + "Output monitoring only possible when at least one output channel is enabled. Please make sure to enable at least one output channel"); } } diff --git a/lasp/device/lasp_cppuldaq.cpp b/lasp/device/lasp_cppuldaq.cpp index 2d260a2..f92837d 100644 --- a/lasp/device/lasp_cppuldaq.cpp +++ b/lasp/device/lasp_cppuldaq.cpp @@ -87,7 +87,9 @@ public: if (handle == 0) { throw runtime_error("Unable to create a handle to the specified DAQ " - "device. Is the device currently in use?"); + "device. Is the device currently in use? Please make sure to set " + "the DAQ configuration in duplex mode if simultaneous input and " + "output is required."); } err = ulConnectDaqDevice(handle); @@ -579,14 +581,14 @@ void fillUlDaqDeviceInfo(vector &devinfolist) { devinfo.availableSampleRates.push_back(50400); devinfo.availableSampleRates.push_back(51000); - devinfo.prefSampleRateIndex = 5; + devinfo.prefSampleRateIndex = 11; devinfo.availableFramesPerBlock.push_back(512); devinfo.availableFramesPerBlock.push_back(1024); devinfo.availableFramesPerBlock.push_back(2048); devinfo.availableFramesPerBlock.push_back(4096); devinfo.availableFramesPerBlock.push_back(8192); - devinfo.prefFramesPerBlockIndex = 1; + devinfo.prefFramesPerBlockIndex = 2; devinfo.availableInputRanges = {1.0, 10.0}; devinfo.prefInputRangeIndex = 0; diff --git a/lasp/lasp_avstream.py b/lasp/lasp_avstream.py index 03a8071..0ec6108 100644 --- a/lasp/lasp_avstream.py +++ b/lasp/lasp_avstream.py @@ -231,7 +231,12 @@ class AvStreamProcess(mp.Process): self.siggen_activated = Atomic(False) while True: - msg, data = self.pipe.recv() + try: + msg, data = self.pipe.recv() + except OSError: + logging.error("Error with pipe, terminating process") + self.stopAllStreams() + self.terminate() logging.debug(f"Streamprocess obtained message {msg}") if msg == StreamMsg.activateSiggen: @@ -419,18 +424,28 @@ class AvStreamProcess(mp.Process): # explanation. def sendInQueues(self, msg, *data): # logging.debug('sendInQueues()') - for q in self.indata_qlist: - # Fan out the input data to all queues in the queue list - q.put((msg, data)) + try: + for q in self.indata_qlist: + # Fan out the input data to all queues in the queue list + q.put((msg, data)) + except ValueError: + logging.error("Error with data queue, terminating process") + self.stopAllStreams() + self.terminate() def sendAllQueues(self, msg, *data): """ Destined for all queues, including capture data queues """ self.sendInQueues(msg, *data) - for q in self.msg_qlist: - # Fan out the input data to all queues in the queue list - q.put((msg, data)) + try: + for q in self.msg_qlist: + # Fan out the input data to all queues in the queue list + q.put((msg, data)) + except ValueError: + logging.error("Error with data queue, terminating process") + self.stopAllStreams() + self.terminate() @dataclass diff --git a/lasp/lasp_siggen.py b/lasp/lasp_siggen.py index 1f73647..a8cfada 100644 --- a/lasp/lasp_siggen.py +++ b/lasp/lasp_siggen.py @@ -68,6 +68,7 @@ class SiggenMessage(Enum): newSiggenData = auto() # Forward new equalizer settings ready = auto() # Send out once, once the signal generator is ready with # pre-generating data. + mute = auto() # Mute / unmute siggen # These messages are send back to the main thread over the pipe error = auto() @@ -87,6 +88,9 @@ class SiggenData: # The data type to output dtype: np.dtype + # Muted? + muted: bool + # Level of output signal [dBFS]el level_dB: float @@ -146,6 +150,9 @@ class SiggenProcess(mp.Process): signaltype = siggendata.signaltype signaltypedata = siggendata.signaltypedata + # Muted state + self.muted = siggendata.muted + if signaltype == SignalType.Periodic: freq, = signaltypedata siggen = pyxSiggen.sineWave(fs, freq, level_dB) @@ -181,7 +188,16 @@ class SiggenProcess(mp.Process): if np.issubdtype(dtype, np.integer): bitdepth_fixed = dtype.itemsize * 8 signal *= 2 ** (bitdepth_fixed - 1) - 1 - self.dataq.put(signal.astype(dtype)) + if self.muted: + # Mute it + signal *= 0 + try: + self.dataq.put(signal.astype(dtype)) + except ValueError: + # As of Python 3.8, a value error on a Queue means that the oter + # end of the process died. + logging.error("Error with data queue, terminating process") + self.terminate() def newEqualizer(self, eqdata): """ @@ -207,6 +223,13 @@ class SiggenProcess(mp.Process): eq.setLevels(eq_levels) return eq + def sendPipe(self, msgtype, msg): + try: + self.pipe.send((msgtype, msg)) + except OSError: + logging.error("Error with pipe, terminating process") + self.terminate() + def run(self): # The main function of the actual process # First things first @@ -215,26 +238,33 @@ class SiggenProcess(mp.Process): try: self.siggen = self.newSiggen(self.siggendata) except Exception as e: - self.pipe.send((SiggenMessage.error, str(e))) + self.sendPipe(SiggenMessage.error, str(e)) try: self.eq = self.newEqualizer(self.siggendata.eqdata) except Exception as e: - self.pipe.send((SiggenMessage.error, str(e))) + self.sendPipe(SiggenMessage.error, str(e)) # Pre-generate blocks of signal data while self.dataq.qsize() < self.nblocks_buffer: self.generate() - self.pipe.send((SiggenMessage.ready, None)) + self.sendPipe(SiggenMessage.ready, None) while True: # Wait here for a while, to check for messages to consume if self.pipe.poll(timeout=QUEUE_BUFFER_TIME / 4): - msg, data = self.pipe.recv() + try: + msg, data = self.pipe.recv() + except OSError: + logging.error("Error with pipe, terminating process") + self.terminate() + if msg == SiggenMessage.endProcess: logging.debug("Signal generator caught 'endProcess' message. Exiting.") return 0 + elif msg == SiggenMessage.mute: + self.muted = data elif msg == SiggenMessage.adjustVolume: level_dB = data logging.debug(f"Signal generator caught 'adjustVolume' message. New volume = {level_dB:.1f} dB FS") @@ -254,7 +284,7 @@ class SiggenProcess(mp.Process): try: self.generate() except SiggenWorkerDone: - self.pipe.send(SiggenMessage.done) + self.sendPipe(SiggenMessage.done, None) return 0 return 1 @@ -302,6 +332,9 @@ class Siggen: """ self.pipe.send((SiggenMessage.adjustVolume, new_level)) + def mute(self, mute): + self.pipe.send((SiggenMessage.mute, mute)) + def setEqData(self, eqdata): self.pipe.send((SiggenMessage.newEqSettings, eqdata)) diff --git a/lasp/wrappers.pyx b/lasp/wrappers.pyx index f7620c8..444bf49 100644 --- a/lasp/wrappers.pyx +++ b/lasp/wrappers.pyx @@ -449,7 +449,7 @@ cdef class SosFilterBank: """ if sos.shape[0] != self.nsections: - raise RuntimeError('Invalid number of sections in filter data, should be {self.nsections.}') + raise RuntimeError(f'Invalid number of sections in filter data, should be {self.nsections}.') elif sos.shape[1] != 6: raise RuntimeError('Illegal number of filter coefficients in section. Should be 6.') cdef dmat coefs = dmat_foreign_data(sos.size,1,