#include "isodriver.h" #include "isobuffer.h" #include "isobuffer_file.h" #include "platformspecific.h" #include #include "daqloadprompt.h" #include #include static constexpr int kSpectrumCounterMax = 4; isoDriver::isoDriver(QWidget *parent) : QLabel(parent) { this->hide(); internalBuffer375_CH1 = new isoBuffer(this, MAX_WINDOW_SIZE*ADC_SPS/20*21, this, 1); internalBuffer375_CH2 = new isoBuffer(this, MAX_WINDOW_SIZE*ADC_SPS/20*21, this, 2); internalBuffer750 = new isoBuffer(this, MAX_WINDOW_SIZE*ADC_SPS/10*21, this, 1); isoTemp = (char *) malloc(TIMER_PERIOD*ADC_SPF + 8); //8-byte header contains (unsigned long) length char volts[2] = "V"; char seconds[2] = "s"; char hertz[3] = "Hz"; v0 = new siprint(volts, 1234); v1 = new siprint(volts, 0); dv = new siprint(volts, 0); t0 = new siprint(seconds, 0); t1 = new siprint(seconds, 0); dt = new siprint(seconds, 0); f = new siprint(hertz, 0); startTimer(); slowTimer = new QTimer; slowTimer->setTimerType(Qt::PreciseTimer); slowTimer->start(MULTIMETER_PERIOD); connect(slowTimer, SIGNAL(timeout()), this, SLOT(slowTimerTick())); } void isoDriver::setDriver(genericUsbDriver *newDriver){ driver = newDriver; qDebug() << "driver = " << driver; } void isoDriver::setAxes(QCustomPlot *newAxes){ axes = newAxes; qDebug() << "axes = " << axes; } void isoDriver::timerTick(void){ //qDebug() << "isoDriver SEZ Tick!"; if(firstFrame){ autoGain(); firstFrame = false; } isoTemp = driver->isoRead(&length); //qDebug() << length << "read in!!"; total_read += length; if(fileModeEnabled){ qDebug() << "File mode is active. Abort live refresh"; return; } if (length==0){ //Zero length packet means something's gone wrong. Probably a disconnect. qDebug() << "Zero length iso packet!"; //driver->killMe(); return; } // TODO: Do we need to invalidate state when the device is reconnected? bool invalidateTwoWireState = true; switch(driver->deviceMode){ case 0: if (deviceMode_prev != 0 && deviceMode_prev != 1 && deviceMode_prev != 2) clearBuffers(true, false, false); frameActionGeneric(1,0); break; case 1: if (deviceMode_prev != 0 && deviceMode_prev != 1 && deviceMode_prev != 2) clearBuffers(true, false, false); if (deviceMode_prev != 1) clearBuffers(false, true, false); internalBuffer375_CH2->m_channel = 1; frameActionGeneric(1,2); if(serialDecodeEnabled_CH1 && serialType == 0){ internalBuffer375_CH2->serialManage(baudRate_CH1, parity_CH1, hexDisplay_CH1); } break; case 2: if (deviceMode_prev != 0 && deviceMode_prev != 1 && deviceMode_prev != 2) clearBuffers(true, false, false); if (deviceMode_prev != 2) clearBuffers(false, true, false); frameActionGeneric(1,1); break; case 3: if (deviceMode_prev != 3 && deviceMode_prev != 4) clearBuffers(true, false, false); frameActionGeneric(2,0); if(serialDecodeEnabled_CH1 && serialType == 0){ internalBuffer375_CH1->serialManage(baudRate_CH1, parity_CH1, hexDisplay_CH1); } break; case 4: if (deviceMode_prev != 3 && deviceMode_prev != 4) clearBuffers(true, false, false); if (deviceMode_prev != 4) clearBuffers(false, true, false); internalBuffer375_CH2->m_channel = 2; frameActionGeneric(2,2); if(serialDecodeEnabled_CH1 && serialType == 0){ internalBuffer375_CH1->serialManage(baudRate_CH1, parity_CH1, hexDisplay_CH1); } if(serialDecodeEnabled_CH2 && serialType == 0){ internalBuffer375_CH2->serialManage(baudRate_CH2, parity_CH2, hexDisplay_CH2); } if (serialDecodeEnabled_CH1 && serialType == 1) { if (twoWireStateInvalid) twoWire->reset(); try { twoWire->run(); } catch(...) { qDebug() << "Resetting I2C"; twoWire->reset(); } invalidateTwoWireState = false; twoWireStateInvalid = false; } break; case 5: break; case 6: if (deviceMode_prev != 6) clearBuffers(false, false, true); frameActionGeneric(-1,0); break; case 7: if (deviceMode_prev != 7) clearBuffers(true, false, false); multimeterAction(); break; default: qFatal("Error in isoDriver::timerTick. Invalid device mode."); } if (invalidateTwoWireState) twoWireStateInvalid = true; deviceMode_prev = driver->deviceMode; //free(isoTemp); } void isoDriver::analogConvert(short *shortPtr, QVector *doublePtr, int TOP, bool AC, int channel){ double scope_gain = (double)(driver->scopeGain); double accumulated = 0; double accumulated_square = 0; currentVmax = -20; currentVmin = 20; double ref = (channel == 1 ? ch1_ref : ch2_ref); double frontendGain = (channel == 1 ? frontendGain_CH1 : frontendGain_CH2); double *data = doublePtr->data(); for (int i=0;isize();i++){ data[i] = (shortPtr[i] * (vcc/2)) / (frontendGain*scope_gain*TOP); if (driver->deviceMode != 7) data[i] += ref; #ifdef INVERT_MM if(driver->deviceMode == 7) data[i] *= -1; #endif accumulated += data[i]; accumulated_square += data[i] * data[i]; if (data[i] > currentVmax) currentVmax = data[i]; if (data[i] < currentVmin) currentVmin = data[i]; } currentVmean = accumulated / doublePtr->size(); currentVRMS = sqrt(accumulated_square / doublePtr->size()); if(AC){ //Previous measurments are wrong, edit and redo. accumulated = 0; accumulated_square = 0; currentVmax = -20; currentVmin = 20; for (int i=0;isize();i++){ data[i] -= currentVmean; accumulated += data[i]; accumulated_square += (data[i] * data[i]); if (data[i] > currentVmax) currentVmax = data[i]; if (data[i] < currentVmin) currentVmin = data[i]; } currentVmean = accumulated / doublePtr->size(); currentVRMS = sqrt(accumulated_square / doublePtr->size()); } //cool_waveform = cool_waveform - AC_offset; } void isoDriver::digitalConvert(short *shortPtr, QVector *doublePtr){ double *data = doublePtr->data(); double top = display.topRange - (display.topRange - display.botRange) / 10; double bot = display.botRange + (display.topRange - display.botRange) / 10; for (int i=0;i *doublePtr){ double *data = doublePtr->data(); for (int i=0;isetTimerType(Qt::PreciseTimer); isoTimer->start(TIMER_PERIOD); connect(isoTimer, SIGNAL(timeout()), this, SLOT(timerTick())); //qFatal("ISO TIMER STARTED");*/ } void isoDriver::clearBuffers(bool ch3751, bool ch3752, bool ch750){ if(ch3751) internalBuffer375_CH1->clearBuffer(); if(ch3752) internalBuffer375_CH2->clearBuffer(); if(ch750) internalBuffer750->clearBuffer(); } void isoDriver::setVisible_CH2(bool visible){ axes->graph(1)->setVisible(visible); } void isoDriver::setVoltageRange(QWheelEvent* event) { if(doNotTouchGraph && !fileModeEnabled) return; bool isProperlyPaused = properlyPaused(); double maxWindowSize = fileModeEnabled ? daq_maxWindowSize : ((double)MAX_WINDOW_SIZE); display.setVoltageRange(event, isProperlyPaused, maxWindowSize, axes); if (!(event->modifiers() == Qt::ControlModifier)) if (autoGainEnabled && !isProperlyPaused) autoGain(); } void DisplayControl::setVoltageRange (QWheelEvent* event, bool isProperlyPaused, double maxWindowSize, QCustomPlot* axes) { if (!(event->modifiers() == Qt::ControlModifier) && event->orientation() == Qt::Orientation::Vertical) { double c = (topRange - botRange) / (double)400; QCPRange range = axes->yAxis->range(); double pixPct = (double)100 - ((double)100 * (((double)axes->yAxis->pixelToCoord(event->y())-range.lower) / range.size())); if (pixPct < 0) pixPct = 0; if (pixPct > 100) pixPct = 100; qDebug() << "WHEEL @ " << pixPct << "%"; qDebug() << range.upper; topRange -= event->delta() / 120.0 * c * pixPct; botRange += event->delta() / 120.0 * c * (100.0 - pixPct); if (topRange > (double)20) topRange = (double)20; if (botRange < -(double)20) botRange = (double)-20; topRangeUpdated(topRange); botRangeUpdated(botRange); } else { double c = (window) / (double)200; QCPRange range = axes->xAxis->range(); double pixPct = (double)100 * ((double)axes->xAxis->pixelToCoord(event->x()) - range.lower); pixPct /= isProperlyPaused ? (double)(range.upper - range.lower) : (double)(window); if (pixPct < 0) pixPct = 0; if (pixPct > 100) pixPct = 100; qDebug() << "WHEEL @ " << pixPct << "%"; qDebug() << event->delta(); if (! isProperlyPaused) { qDebug() << "TIGGERED"; qDebug() << "upper = " << range.upper << "lower = " << range.lower; qDebug() << "window = " << window; qDebug() << c * ((double)pixPct); qDebug() << c * ((double)100 - (double)pixPct) * pixPct / 100; } window -= event->delta() / 120.0 * c * pixPct; delay += event->delta() / 120.0 * c * (100.0 - pixPct) * pixPct / 100.0; // NOTE: delayUpdated and timeWindowUpdated are called more than once beyond here, // maybe they should only be called once at the end? delayUpdated(delay); timeWindowUpdated(window); qDebug() << window << delay; if (window > maxWindowSize) { window = maxWindowSize; timeWindowUpdated(window); } if ((window + delay) > maxWindowSize) { delay = maxWindowSize - window; delayUpdated(delay); } if (delay < 0) { delay = 0; delayUpdated(delay); } } } bool isoDriver::properlyPaused(){ if(paused_CH1 & paused_CH2){ qDebug() << "Properly paused"; return true; } if ((driver->deviceMode == 0) || (driver->deviceMode == 3) || (driver->deviceMode == 6)){ if(paused_CH1) qDebug() << "Properly paused"; else qDebug() << "Not properly paused"; return paused_CH1; } if(paused_multimeter){ qDebug() << "Properly paused"; return true; } qDebug() << "Not properly paused"; return false; } void isoDriver::pauseEnable_CH1(bool enabled){ paused_CH1 = enabled; if(!properlyPaused()) { display.delay = 0; delayUpdated(display.delay); if (autoGainEnabled) autoGain(); } if(!enabled) clearBuffers(1,0,1); qDebug() << "pauseEnable_CH1" << enabled; } void isoDriver::pauseEnable_CH2(bool enabled){ paused_CH2 = enabled; if(!properlyPaused()){ display.delay = 0; delayUpdated(display.delay); if (autoGainEnabled) autoGain(); } if(!enabled) clearBuffers(0,1,0); } void isoDriver::pauseEnable_multimeter(bool enabled){ paused_multimeter = enabled; if(!properlyPaused()) { display.delay = 0; delayUpdated(display.delay); } if(!enabled) clearBuffers(1,0,0); qDebug() << "pauseEnable_multimeter" << enabled; } void isoDriver::autoGain(){ double maxgain = vcc / (2 * ((double)display.topRange - vref) * R4/(R3+R4)); double mingain = vcc / (2 * ((double)display.botRange - vref) * R4/(R3+R4)); maxgain = fmin(fabs(mingain) * 0.98, fabs(maxgain) * 0.98); double snap[8] = {64, 32, 16, 8, 4, 2, 1, 0.5}; for (int i=0;i<8;i++){ if (maxgain>snap[i]){ setGain(snap[i]); return; } } } void isoDriver::gainBuffers(double multiplier){ multi = multiplier; QTimer::singleShot(TIMER_PERIOD*4, this, SLOT(gainTick())); } void isoDriver::gainTick(void){ #ifdef PLATFORM_ANDROID #warning: "gainTick does nothing on Android!!" #else qDebug() << "Multiplying by " << multi; if (driver->deviceMode <5) internalBuffer375_CH1->gainBuffer(log2(multi)); if ((driver->deviceMode == 1) | (driver->deviceMode == 2) | (driver->deviceMode == 4)) internalBuffer375_CH2->gainBuffer(log2(multi)); if ((driver->deviceMode == 6) | (driver->deviceMode == 7)) internalBuffer750->gainBuffer(log2(multi)); #endif } void isoDriver::setAutoGain(bool enabled){ autoGainEnabled = enabled; if(enabled){ autoGain(); } } void isoDriver::graphMousePress(QMouseEvent *event){ qDebug() << event->button(); if (horiCursorEnabled && (event->button() == Qt::LeftButton)){ placingHoriAxes = true; display.y0 = axes->yAxis->pixelToCoord(event->y()); #ifndef PLATFORM_ANDROID }else if(vertCursorEnabled && (event->button() == Qt::RightButton)){ #else }if(vertCursorEnabled){ #endif placingVertAxes = true; display.x0 = axes->xAxis->pixelToCoord(event->x()); } qDebug() << "x0 =" << display.x0 << "x1 =" << display.x1 << "y0 =" << display.y0 << "y1 =" << display.y1; } void isoDriver::graphMouseRelease(QMouseEvent *event){ if(horiCursorEnabled && placingHoriAxes && (event->button() == Qt::LeftButton)){ placingHoriAxes = false; #ifndef PLATFORM_ANDROID } else if (vertCursorEnabled && placingVertAxes && (event->button() == Qt::RightButton)){ #else } if (vertCursorEnabled && placingVertAxes){ #endif placingVertAxes = false; } qDebug() << "x0 =" << display.x0 << "x1 =" << display.x1 << "y0 =" << display.y0 << "y1 =" << display.y1; } void isoDriver::graphMouseMove(QMouseEvent *event){ if(horiCursorEnabled && placingHoriAxes){ display.y1 = axes->yAxis->pixelToCoord(event->y()); #ifndef PLATFORM_ANDROID } else if(vertCursorEnabled && placingVertAxes){ #else } if(vertCursorEnabled && placingVertAxes){ #endif display.x1 = axes->xAxis->pixelToCoord(event->x()); } } void isoDriver::cursorEnableHori(bool enabled){ horiCursorEnabled = enabled; axes->graph(4)->setVisible(enabled); axes->graph(5)->setVisible(enabled); } void isoDriver::cursorEnableVert(bool enabled){ vertCursorEnabled = enabled; axes->graph(2)->setVisible(enabled); axes->graph(3)->setVisible(enabled); } void isoDriver::udateCursors(void){ if(!(vertCursorEnabled || horiCursorEnabled)){ #if QCP_VER == 1 cursorTextPtr->setVisible(0); #endif return; } QVector vert0x(2), vert1x(2), hori0x(2), hori1x(2), vert0y(2), vert1y(2), hori0y(2), hori1y(2); vert0x[0] = display.x0; vert0x[1] = display.x0; vert0y[0] = display.botRange; vert0y[1] = display.topRange; vert1x[0] = display.x1; vert1x[1] = display.x1; vert1y[0] = display.botRange; vert1y[1] = display.topRange; hori0x[0] = -display.window - display.delay; hori0x[1] = -display.delay; hori0y[0] = display.y0; hori0y[1] = display.y0; hori1x[0] = -display.window - display.delay; hori1x[1] = -display.delay; hori1y[0] = display.y1; hori1y[1] = display.y1; if(vertCursorEnabled){ axes->graph(2)->setData(vert0x, vert0y); axes->graph(3)->setData(vert1x, vert1y); } if(horiCursorEnabled){ axes->graph(4)->setData(hori0x, hori0y); axes->graph(5)->setData(hori1x, hori1y); } #if QCP_VER == 1 cursorTextPtr->setVisible(cursorStatsEnabled); #endif if (!cursorStatsEnabled) return; QString *cursorStatsString = new QString(); v0->value = display.y0; v1->value = display.y1; dv->value = display.y0-display.y1; t0->value = display.x0; t1->value = display.x1; dt->value = fabs(display.x0 - display.x1); f->value = 1 / (display.x1 - display.x0); char temp_hori[64]; char temp_vert[64]; char temp_separator[2]; sprintf(temp_hori, "V0 = %s, V1 = %s, ΔV = %s", v0->printVal(), v1->printVal(), dv->printVal()); sprintf(temp_vert, "t0 = %s, t1 = %s, Δt = %s, f = %s", t0->printVal(), t1->printVal(), dt->printVal(), f->printVal()); sprintf(temp_separator, "\n"); //sprintf(temp, "hello!"); if(horiCursorEnabled) cursorStatsString->append(temp_hori); if(horiCursorEnabled && vertCursorEnabled) cursorStatsString->append(temp_separator); if(vertCursorEnabled) cursorStatsString->append(temp_vert); //qDebug() << temp; #if QCP_VER == 1 cursorTextPtr->setText(*(cursorStatsString)); #endif delete cursorStatsString; } short isoDriver::reverseFrontEnd(double voltage){ //qFatal("reverseFrontEnd driver mode 7"); #ifdef INVERT_MM if(driver->deviceMode == 7) voltage *= -1; #endif double vn = vcc * (R2/(R1+R2)); double vx = vn + (voltage - vn) * (R4 / (R3+R4)); double TOP = (driver->deviceMode == 7) ? 2048 : 128; if (driver->deviceMode == 7){ qDebug() << "SEEEKING"; qDebug() << ((vx - vn)/vref * (double)driver->scopeGain * (double)TOP + (double)0.5); qDebug() << "SEEEKING"; return ((vx - vn)/vref * (double)driver->scopeGain * (double)TOP + (double)0.5); } //qDebug() << "seeking" << voltage << "V"; return ((vx - vn)/vref * (double)driver->scopeGain * (double)TOP + (double)0.5); } void isoDriver::setTriggerEnabled(bool enabled) { triggerEnabled = enabled; triggerStateChanged(); } void isoDriver::setTriggerLevel(double level) { internalBuffer375_CH1->setTriggerLevel(level, (driver->deviceMode == 7 ? 2048 : 128), AC_CH1); internalBuffer375_CH2->setTriggerLevel(level, 128, AC_CH2); internalBuffer750->setTriggerLevel(level, 128, AC_CH1); triggerStateChanged(); } void isoDriver::setSingleShotEnabled(bool enabled) { singleShotEnabled = enabled; triggerStateChanged(); } void isoDriver::setTriggerMode(int newMode) { triggerMode = newMode; triggerStateChanged(); } //0 for off, 1 for ana, 2 for dig, -1 for ana750, -2 for file void isoDriver::frameActionGeneric(char CH1_mode, char CH2_mode) { // The Spectrum is computationally expensive to calculate, so we don't want to do it on every frame static int spectrumCounter = 0; if(spectrum) { spectrumCounter = (spectrumCounter + 1) % kSpectrumCounterMax; if (spectrumCounter != 0) return; } //qDebug() << "made it to frameActionGeneric"; if(!paused_CH1 && CH1_mode == - 1){ for (unsigned int i=0;i<(length/ADC_SPF);i++){ internalBuffer750->writeBuffer_char(&isoTemp[ADC_SPF*i], VALID_DATA_PER_750); } } if(!paused_CH1 && CH1_mode > 0){ for (unsigned int i=0;i<(length/ADC_SPF);i++){ internalBuffer375_CH1->writeBuffer_char(&isoTemp[ADC_SPF*i], VALID_DATA_PER_375); } } if(!paused_CH2 && CH2_mode > 0){ for (unsigned int i=0;i<(length/ADC_SPF);i++){ internalBuffer375_CH2->writeBuffer_char(&isoTemp[ADC_SPF*i+ADC_SPF/2], VALID_DATA_PER_375); //+375 to get the second half of the packet } } if(!paused_CH1) { int offset = -2; //No trigger! int backLength = length/750; backLength *= (CH1_mode == -1) ? VALID_DATA_PER_750 : VALID_DATA_PER_375; if(offset>0){ int temp_offset = offset % 750; offset /= 750; offset *= (CH1_mode == -1) ? VALID_DATA_PER_750 : VALID_DATA_PER_375; offset += temp_offset; } //qDebug() << "Now offset = " << offset; } double triggerDelay = 0; if (triggerEnabled) { isoBuffer* internalBuffer_CH1 = (CH1_mode == -1) ? internalBuffer750 : internalBuffer375_CH1; triggerDelay = (triggerMode < 2) ? internalBuffer_CH1->getDelayedTriggerPoint(display.window) - display.window : internalBuffer375_CH2->getDelayedTriggerPoint(display.window) - display.window; if (triggerDelay < 0) triggerDelay = 0; } if(singleShotEnabled && (triggerDelay != 0)) singleShotTriggered(1); if (!spectrum) { readData375_CH1 = internalBuffer375_CH1->readBuffer(display.window,GRAPH_SAMPLES,CH1_mode==2, display.delay + triggerDelay); if(CH2_mode) readData375_CH2 = internalBuffer375_CH2->readBuffer(display.window,GRAPH_SAMPLES,CH2_mode==2, display.delay + triggerDelay); if(CH1_mode == -1) readData750 = internalBuffer750->readBuffer(display.window,GRAPH_SAMPLES,false, display.delay + triggerDelay); if(CH1_mode == -2) readDataFile = internalBufferFile->readBuffer(display.window,GRAPH_SAMPLES,false, display.delay); } else { /*Don't allow moving frequency spectrum right or left * by overwriting display window and delay before reading * the buffer each time. * @TODO improve this limitation. */ double const_displ_window = ((double)internalBuffer375_CH1->async_dft.n_samples)/(internalBuffer375_CH1->m_samplesPerSecond); double const_displ_delay = 0; display.delay = const_displ_delay; display.window = const_displ_window; readData375_CH1 = internalBuffer375_CH1->readBuffer(display.window,GRAPH_SAMPLES,CH1_mode==2, display.delay + triggerDelay); if(CH2_mode) readData375_CH2 = internalBuffer375_CH2->readBuffer(display.window,GRAPH_SAMPLES,CH2_mode==2, display.delay + triggerDelay); if(CH1_mode == -1) readData750 = internalBuffer750->readBuffer(display.window,GRAPH_SAMPLES,false, display.delay + triggerDelay); } /*Convert data also for spectrum CH1 and CH2*/ std::unique_ptr dt_samples1 = internalBuffer375_CH1->async_dft.getWindow(); std::unique_ptr dt_samples2 = internalBuffer375_CH2->async_dft.getWindow(); QVector x(GRAPH_SAMPLES), CH1(GRAPH_SAMPLES), CH2(GRAPH_SAMPLES), converted_dt_samples1(internalBuffer375_CH1->async_dft.n_samples), converted_dt_samples2(internalBuffer375_CH2->async_dft.n_samples); if (CH1_mode == 1){ analogConvert(readData375_CH1.get(), &CH1, 128, AC_CH1, 1); analogConvert(dt_samples1.get(), &converted_dt_samples1, 128, AC_CH1, 1); for (int i=0; i < CH1.size(); i++) { CH1[i] /= m_attenuation_CH1; CH1[i] += m_offset_CH1; } for (int i=0; i < converted_dt_samples1.size(); i++) { converted_dt_samples1[i] /= m_attenuation_CH1; converted_dt_samples1[i] += m_offset_CH1; } xmin = (currentVmin < xmin) ? currentVmin : xmin; xmax = (currentVmax > xmax) ? currentVmax : xmax; broadcastStats(0); } /*After conversion of dt samples, sending them again to asyncDFT*/ if (CH1_mode == 2) digitalConvert(readData375_CH1.get(), &CH1); if (CH2_mode == 1){ analogConvert(readData375_CH2.get(), &CH2, 128, AC_CH2, 2); analogConvert(dt_samples2.get(), &converted_dt_samples2, 128, AC_CH2, 2); for (int i=0; i < GRAPH_SAMPLES; i++) { CH2[i] /= m_attenuation_CH2; CH2[i] += m_offset_CH2; } for (int i=0; i < converted_dt_samples2.size(); i++) { converted_dt_samples2[i] /= m_attenuation_CH1; converted_dt_samples2[i] += m_offset_CH1; } ymin = (currentVmin < ymin) ? currentVmin : ymin; ymax = (currentVmax > ymax) ? currentVmax : ymax; broadcastStats(1); } if (CH2_mode == 2) digitalConvert(readData375_CH2.get(), &CH2); if(CH1_mode == -1) { analogConvert(readData750.get(), &CH1, 128, AC_CH1, 1); xmin = (currentVmin < xmin) ? currentVmin : xmin; xmax = (currentVmax > xmax) ? currentVmax : xmax; broadcastStats(0); } if(CH1_mode == -2) { fileStreamConvert(readDataFile, &CH1); } for (double i=0; i0) { CH1[i] = 0; CH2[i] = 0; } } udateCursors(); if(XYmode){ QCPCurve* curve = reinterpret_cast(axes->plottable(0)); curve->setData(CH1, CH2); axes->xAxis->setRange(xmin, xmax); axes->yAxis->setRange(ymin, ymax); } else{ if (spectrum) { /*If frequency spectrum mode*/ try { /*Creating DFT amplitudes*/ QVector amplitude1 = internalBuffer375_CH1->async_dft.getPowerSpectrum(converted_dt_samples1); /*Getting array of frequencies for display purposes*/ QVector f = internalBuffer375_CH1->async_dft.getFrequenciyWindow(internalBuffer375_CH1->m_samplesPerSecond); /*Max amplitude for display purposes*/ double max1 = internalBuffer375_CH1->async_dft.maximum; double max2 = -1; if(CH2_mode) { QVector amplitude2 = internalBuffer375_CH2->async_dft.getPowerSpectrum(converted_dt_samples2); max2 = internalBuffer375_CH2->async_dft.maximum; /*Normalization with respect to amplitude1*/ amplitude2 = internalBuffer375_CH2->async_dft.normalizeDFT(max1, amplitude2); axes->graph(1)->setData(f,amplitude2); } /*Decision for normalization & display purposes*/ amplitude1 = internalBuffer375_CH1->async_dft.normalizeDFT(max2, amplitude1); axes->graph(0)->setData(f, amplitude1); axes->xAxis->setRange(m_spectrumMinX, m_spectrumMaxX); /*Setting maximum/minimum y-axis 0%-100%*/ axes->yAxis->setRange(100,0); } catch (std::exception) { std::cout << "Cannot yet get correct value for DFT" << std::endl; } } else { axes->graph(0)->setData(x,CH1); if(CH2_mode) axes->graph(1)->setData(x,CH2); axes->xAxis->setRange(-display.window - display.delay, -display.delay); axes->yAxis->setRange(display.topRange, display.botRange); } } if(snapshotEnabled_CH1){ snapshotFile_CH1->open(QIODevice::WriteOnly); snapshotFile_CH1->write("t, v\n"); char tempchar[32]; for(int i=0; iwrite(tempchar); } snapshotEnabled_CH1 = false; snapshotFile_CH1->close(); delete(snapshotFile_CH1); } if(snapshotEnabled_CH2){ snapshotFile_CH2->open(QIODevice::WriteOnly); snapshotFile_CH2->write("t, v\n"); char tempchar[32]; for(int i=0; iwrite(tempchar); } snapshotEnabled_CH2 = false; snapshotFile_CH2->close(); delete(snapshotFile_CH2); } axes->replot(); } void isoDriver::multimeterAction(){ isoTemp_short = (short *)isoTemp; if(!paused_multimeter){ for (unsigned int i=0;i<(length/ADC_SPF);i++){ internalBuffer375_CH1->writeBuffer_short(&isoTemp_short[ADC_SPF/2*i], ADC_SPF/2-1); //Offset because the first 8 bytes of the array contain the length (no samples!!)! } } double triggerDelay = 0; if (triggerEnabled) { triggerDelay = internalBuffer375_CH1->getDelayedTriggerPoint(display.window) - display.window; if (triggerDelay < 0) triggerDelay = 0; } if(singleShotEnabled && (triggerDelay != 0)) singleShotTriggered(1); readData375_CH1 = internalBuffer375_CH1->readBuffer(display.window,GRAPH_SAMPLES, false, display.delay + triggerDelay); QVector x(GRAPH_SAMPLES), CH1(GRAPH_SAMPLES); analogConvert(readData375_CH1.get(), &CH1, 2048, 0, 1); //No AC coupling! for (double i=0; i0) { CH1[i] = 0; } } axes->graph(0)->setData(x,CH1); udateCursors(); axes->xAxis->setRange(-display.window - display.delay, -display.delay); axes->yAxis->setRange(display.topRange, display.botRange); axes->replot(); multimeterStats(); } void isoDriver::setAC_CH1(bool enabled){ AC_CH1 = enabled; } void isoDriver::setAC_CH2(bool enabled){ AC_CH2 = enabled; } void isoDriver::setMultimeterType(int type){ multimeterType = (multimeterType_enum) type; switch (type){ case R: multimeterREnabled(multimeterRsource); break; case C: multimeterREnabled(254); break; default: multimeterREnabled(255); } qDebug() << "multimeterType = " << multimeterType; } void isoDriver::setSeriesResistance(double resistance){ seriesResistance = resistance; qDebug() << "seriesResistance = " << seriesResistance; } void isoDriver::multimeterStats(){ //qDebug() << "Entering isoDriver::multimeterStats()"; if (!multimeterShow) return; QTimer::singleShot(MULTIMETER_PERIOD, this, SLOT(enableMM())); multimeterShow = false; bool mvMax, mvMin, mvMean, mvRMS, maMax, maMin, maMean, maRMS, kOhms, uFarads; //We'll let the compiler work out this one. if(autoMultimeterV){ mvMax = abs(currentVmax) < 1.; mvMin = abs(currentVmin) < 1.; mvMean = abs(currentVmean) < 1.; mvRMS = abs(currentVRMS) < 1.; } if(autoMultimeterI){ maMax = abs(currentVmax / seriesResistance) < 1.; maMin = abs(currentVmin / seriesResistance) < 1.; maMean = abs(currentVmean / seriesResistance) < 1.; maRMS = abs(currentVRMS / seriesResistance) < 1.; } if(forceMillivolts){ mvMax = true; mvMin = true; mvMean = true; mvRMS = true; } if(forceMilliamps){ maMax = true; maMin = true; maMean = true; maRMS = true; } if(forceKiloOhms){ kOhms = true; } if(forceUFarads){ uFarads = true; } if(forceVolts){ mvMax = false; mvMin = false; mvMean = false; mvRMS = false; } if(forceAmps){ maMax = false; maMin = false; maMean = false; maRMS = false; } if(forceOhms){ kOhms = false; } if(forceNFarads){ uFarads = false; } if(multimeterType == V){ if(mvMax){ currentVmax *= 1000; sendMultimeterLabel1("Max (mV)"); }else sendMultimeterLabel1("Max (V)"); if(mvMin){ currentVmin *= 1000; sendMultimeterLabel2("Min (mV)"); }else sendMultimeterLabel2("Min (V)"); if(mvMean){ currentVmean *= 1000; sendMultimeterLabel3("Mean (mV)"); }else sendMultimeterLabel3("Mean (V)"); if(mvRMS){ currentVRMS *= 1000; sendMultimeterLabel4("RMS (mV)"); }else sendMultimeterLabel4("RMS (V)"); multimeterMax(currentVmax); multimeterMin(currentVmin); multimeterMean(currentVmean); multimeterRMS(currentVRMS); return; } if(multimeterType == I){ if(maMax){ currentVmax *= 1000; sendMultimeterLabel1("Max (mA)"); }else sendMultimeterLabel1("Max (A)"); if(maMin){ currentVmin *= 1000; sendMultimeterLabel2("Min (mA)"); }else sendMultimeterLabel2("Min (A)"); if(maMean){ currentVmean *= 1000; sendMultimeterLabel3("Mean (mA)"); }else sendMultimeterLabel3("Mean (A)"); if(maRMS){ currentVRMS *= 1000; sendMultimeterLabel4("RMS (mA)"); }else sendMultimeterLabel4("RMS (A)"); multimeterMax(currentVmax / seriesResistance); multimeterMin(currentVmin / seriesResistance); multimeterMean(currentVmean / seriesResistance); multimeterRMS(currentVRMS / seriesResistance); return; } if(multimeterType == R){ if(estimated_resistance!=estimated_resistance){ estimated_resistance = 0; //Reset resistance if it's NaN } double Vm = meanVoltageLast((double)MULTIMETER_PERIOD/(double)1000, 1, 2048); double rtest_para_r = 1/(1/seriesResistance + 1/estimated_resistance); double perturbation = ch2_ref * (rtest_para_r / (R3 + R4 + rtest_para_r)); Vm = Vm - perturbation; double Vin = (multimeterRsource * 2) + 3; double Vrat = (Vin-Vm)/Vin; double Rp = 1/(1/seriesResistance + 1/(R3+R4)); estimated_resistance = ((1-Vrat)/Vrat) * Rp; //Perturbation term on V2 ignored. V1 = Vin. V2 = Vin(Rp/(R+Rp)) + Vn(Rtest||R / (R34 + (Rtest||R34)); //qDebug() << "Vm = " << Vm; //qDebug() << "Vin = " << Vin; //qDebug() << "perturbation = " << perturbation; //qDebug() << "Vrat = " << Vrat; //qDebug() << "Rp = " << Rp; //qDebug() << "estimated_resistance = " << estimated_resistance; multimeterMax(0); multimeterMin(0); multimeterMean(0); if(autoMultimeterR){ kOhms = (estimated_resistance) > 1000; } if(kOhms){ estimated_resistance /= 1000; sendMultimeterLabel4("Resistance (kΩ)"); }else sendMultimeterLabel4("Resistance (Ω)"); multimeterRMS(estimated_resistance); } if(multimeterType == C){ double cap_vbot = 0.8; double cap_vtop = 2.5; int cap_x0 = internalBuffer375_CH1->cap_x0fromLast(1, cap_vbot); if(cap_x0 == -1){ qDebug() << "cap_x0 == -1"; return; } int cap_x1 = internalBuffer375_CH1->cap_x1fromLast(1, cap_x0, cap_vbot); if(cap_x1 == -1){ qDebug() << "cap_x1 == -1"; return; } int cap_x2 = internalBuffer375_CH1->cap_x2fromLast(1, cap_x1, cap_vtop); if(cap_x2 == -1){ qDebug() << "cap_x2 == -1"; return; } qDebug() << "x0 = " << cap_x0; qDebug() << "x1 = " << cap_x1; qDebug() << "x2 = " << cap_x2; qDebug() << "dt = " << cap_x2-cap_x1; double dt = (double)(cap_x2-cap_x1)/internalBuffer375_CH1->m_samplesPerSecond; double Cm = -dt/(seriesResistance * log((vcc-cap_vtop)/(vcc-cap_vbot))); qDebug() << "Cm = " << Cm; if(autoMultimeterC){ uFarads = (Cm) > 1e-6; } if(uFarads){ sendMultimeterLabel4("Capacitance (μF)"); Cm = Cm*1000000; } else { sendMultimeterLabel4("Capacitance (nF)"); Cm = Cm*1000000000; } multimeterRMS(Cm); } } void isoDriver::enableMM(){ multimeterShow = true; } void isoDriver::setAutoMultimeterV(bool enabled){ autoMultimeterV = enabled; } void isoDriver::setAutoMultimeterI(bool enabled){ autoMultimeterI = enabled; } void isoDriver::setAutoMultimeterR(bool enabled){ autoMultimeterR = enabled; } void isoDriver::setAutoMultimeterC(bool enabled){ autoMultimeterC = enabled; } void isoDriver::setForceMillivolts(bool enabled){ forceMillivolts = enabled; } void isoDriver::setForceMilliamps(bool enabled){ forceMilliamps = enabled; } void isoDriver::setForceKiloOhms(bool enabled){ forceKiloOhms = enabled; } void isoDriver::setForceUFarads(bool enabled){ forceUFarads = enabled; } void isoDriver::setForceVolts(bool enabled){ forceVolts = enabled; } void isoDriver::setForceAmps(bool enabled){ forceAmps = enabled; } void isoDriver::setForceOhms(bool enabled){ forceOhms = enabled; } void isoDriver::setForceNFarads(bool enabled){ forceNFarads = enabled; } void isoDriver::setSerialDecodeEnabled_CH1(bool enabled){ serialDecodeEnabled_CH1 = enabled; } void isoDriver::setSerialDecodeEnabled_CH2(bool enabled){ serialDecodeEnabled_CH2 = enabled; } void isoDriver::setXYmode(bool enabled){ int graphCount = axes->graphCount(); static QVector graphState; graphState.resize(graphCount); if(enabled) // Hide graphs - we only want the X-Y plot to appear { xmin = 20; xmax = -20; ymin = 20; ymax = -20; for (int i=0; i < graphCount; i++) { qDebug() << "isVisible" << axes->graph(i)->visible(); graphState[i] = axes->graph(i)->visible(); axes->graph(i)->setVisible(false); } } else // Restore State { for (int i=0; i < graphCount; i++) { qDebug() << "graphState" << graphState[i]; axes->graph(i)->setVisible(graphState[i]); } } QCPCurve* curve = reinterpret_cast(axes->plottable(0)); curve->setVisible(enabled); emit enableCursorGroup(!enabled); XYmode = enabled; } void isoDriver::triggerGroupStateChange(bool enabled){ if(enabled) sendTriggerValue((currentVmax-currentVmin)*0.85 + currentVmin); } void isoDriver::broadcastStats(bool CH2){ if (CH2 && update_CH2) { update_CH2 = false; sendVmax_CH2(currentVmax); sendVmin_CH2(currentVmin); sendVmean_CH2(currentVmean); sendVRMS_CH2(currentVRMS); } else if (update_CH1) { update_CH1 = false; sendVmax_CH1(currentVmax); sendVmin_CH1(currentVmin); sendVmean_CH1(currentVmean); sendVRMS_CH1(currentVRMS); } } void isoDriver::slowTimerTick(){ update_CH1 = true; update_CH2 = true; bool frequencyLabelVisible = false; if (triggerEnabled) { double triggerFrequency; switch(triggerMode) { case 0: case 1: triggerFrequency = (driver->deviceMode == 6) ? internalBuffer750->getTriggerFrequencyHz() : internalBuffer375_CH1->getTriggerFrequencyHz(); break; case 2: case 3: triggerFrequency = internalBuffer375_CH2->getTriggerFrequencyHz(); break; } if (triggerFrequency > 0.) { frequencyLabelVisible = true; siprint triggerFreqSiprint("Hz", triggerFrequency); siprint periodSiprint("s", 1. / triggerFrequency); QString cursorString; cursorString.sprintf(" Trigger ΔT = %s, f = %s ", periodSiprint.printVal(), triggerFreqSiprint.printVal()); triggerFrequencyLabel->setText(cursorString); } qDebug() << triggerFrequency << "Hz"; } triggerFrequencyLabel->setVisible(frequencyLabelVisible); } void isoDriver::setTopRange(double newTop) { // NOTE: Should this be clamped to 20? display.topRange = newTop; topRangeUpdated(display.topRange); } void isoDriver::setBotRange(double newBot) { // NOTE: Should this be clamped to 20? display.botRange = newBot; botRangeUpdated(display.botRange); } void isoDriver::setTimeWindow(double newWindow){ display.window = newWindow; timeWindowUpdated(display.window); } void isoDriver::setDelay(double newDelay){ display.delay = newDelay; delayUpdated(display.delay); } void isoDriver::takeSnapshot(QString *fileName, unsigned char channel){ if(channel==1){ snapshotEnabled_CH1 = true; QString fname_CH1 = *(fileName); snapshotFile_CH1 = new QFile(fname_CH1); qDebug() << fname_CH1; } else if(channel==2){ snapshotEnabled_CH2 = true; QString fname_CH2 = *(fileName); snapshotFile_CH2 = new QFile(fname_CH2); qDebug() << fname_CH2; } } double isoDriver::meanVoltageLast(double seconds, unsigned char channel, int TOP){ isoBuffer *currentBuffer; switch (channel){ case 1: currentBuffer = internalBuffer375_CH1; break; case 2: currentBuffer = internalBuffer375_CH2; break; case 3: currentBuffer = internalBuffer750; break; } std::unique_ptr tempBuffer = currentBuffer->readBuffer(seconds, 1024, 0, 0); double sum = 0; double temp; for(int i = 0; i<1024; i++){ temp = currentBuffer->sampleConvert(tempBuffer[i], TOP, 0); sum += temp; } return sum / 1024; } void isoDriver::rSourceChanged(int newSource){ multimeterRsource = newSource; } void isoDriver::serialNeedsDisabling(int channel){ qDebug("isoDriver acknowledges disconnect from channel %d", channel); mainWindowPleaseDisableSerial(channel); } //Thank you https://stackoverflow.com/questions/27318631/parsing-through-a-csv-file-in-qt void isoDriver::loadFileBuffer(QFile *fileToLoad){ //Delete the current buffer if it exists disableFileMode(); if(internalBufferFile != NULL){ delete internalBufferFile; } //Load the file if (!fileToLoad->open(QIODevice::ReadOnly)) { qDebug() << fileToLoad->errorString(); return; } QByteArray currentLine; QList tempList; //First Header line currentLine = fileToLoad->readLine(); qDebug() << currentLine; //Averaging line currentLine = fileToLoad->readLine(); qDebug() << currentLine; tempList.append(currentLine.split('\n')); tempList.append(currentLine.split('\r')); tempList.append(tempList.first().split(' ')); qDebug() << tempList; int averages = tempList.back().toInt(); qDebug() << averages; //Mode line tempList.clear(); currentLine = fileToLoad->readLine(); qDebug() << currentLine; tempList.append(currentLine.split('\n')); tempList.append(currentLine.split('\r')); tempList.append(tempList.first().split(' ')); qDebug() << tempList; int mode = tempList.back().toInt(); qDebug() << mode; tempList.clear(); //Count the number of elements qulonglong numel = 0; while (!fileToLoad->atEnd()) { currentLine = fileToLoad->readLine(); tempList.append(currentLine.split(',')); numel += tempList.count() - 1; tempList.clear(); } qDebug("There are %d elements!", numel); //Prompt user for start and end times double defaultSampleRate = 375000; if(mode == 6){ defaultSampleRate = 750000; } double minTime = ((double)averages) / defaultSampleRate; double maxTime = numel * ((double)averages) / defaultSampleRate; qDebug() << "maxTime =" << maxTime; daqLoadPrompt dlp(this, minTime, maxTime); connect(&dlp, SIGNAL(startTime(double)), this, SLOT(daqLoad_startChanged(double))); connect(&dlp, SIGNAL(endTime(double)), this, SLOT(daqLoad_endChanged(double))); //Defaults daqLoad_startTime = minTime; daqLoad_endTime = maxTime; dlp.exec(); //Initialise the (modified) isoBuffer. int bufferLen = (int)(((daqLoad_endTime - daqLoad_startTime)/minTime) * 1.1) + 1; //Add a bit on to account for rounding error. Int conversion rounds down, so we add 1. qDebug() << "daqLoad_endTime" << daqLoad_endTime; qDebug() << "daqLoad_startTime" << daqLoad_startTime; qDebug() << "minTime" << minTime; qDebug() << "bufferLen" << bufferLen; double sampleRate_Hz = defaultSampleRate/averages; internalBufferFile = new isoBuffer_file(this, bufferLen, sampleRate_Hz); //Go to start of data section fileToLoad->seek(0);//Return to start currentLine = fileToLoad->readLine(); //Chew up header qDebug() << currentLine; currentLine = fileToLoad->readLine(); //Chew up averages line qDebug() << currentLine; currentLine = fileToLoad->readLine(); //Chew up mode line qDebug() << currentLine; tempList.clear(); //Copy the data into the (modified) isoBuffer float tempArray[COLUMN_BREAK + 1]; //751 elements per row with the old files; this just avoids a possible crash; int temp_len; qulonglong sampleCount = 0; qulonglong minCount = (daqLoad_startTime / minTime); qulonglong maxCount = (daqLoad_endTime / minTime); qDebug() << "minCount" << minCount; qDebug() << "maxCount" << maxCount; int min_i; int tempCount; qDebug() << "Loading data into isoBuffer_file"; while (!fileToLoad->atEnd()) { currentLine = fileToLoad->readLine(); tempList.append(currentLine.split(',')); tempList.removeLast(); //Last element is a "\n", not a number. temp_len = 0; tempCount = tempList.count(); min_i = 2000000000; //Arbitrary big number. for (int i=0; i minCount) && (sampleCount < maxCount)){ if(i < min_i){ min_i = i; } tempArray[i] = tempList.at(i).toFloat(); temp_len++; } sampleCount++; } internalBufferFile->writeBuffer_float(&tempArray[min_i], temp_len); tempList.clear(); } fileToLoad->close(); qDebug() << "Initialising timer"; //Initialise the file timer. if (fileTimer != NULL){ delete fileTimer; } fileTimer = new QTimer(); fileTimer->setTimerType(Qt::PreciseTimer); fileTimer->start(TIMER_PERIOD); connect(fileTimer, SIGNAL(timeout()), this, SLOT(fileTimerTick())); qDebug() << "File Buffer loaded!"; enableFileMode(); qDebug() << "File Mode Enabled"; } void isoDriver::daqLoad_startChanged(double newStart){ qDebug() << "isoDriver::daqLoad_startChanged" << newStart; daqLoad_startTime = newStart; } void isoDriver::daqLoad_endChanged(double newEnd){ qDebug() << "isoDriver::daqLoad_endChanged" << newEnd; daqLoad_endTime = newEnd; } void isoDriver::fileTimerTick(){ //qDebug() << "isoDriver::fileTimerTick()"; frameActionGeneric(-2,0); } void isoDriver::enableFileMode(){ fileModeEnabled = true; daq_maxWindowSize = daqLoad_endTime - daqLoad_startTime; showRealtimeButton(true); } void isoDriver::disableFileMode(){ fileModeEnabled = false; showRealtimeButton(false); if(fileTimer != NULL){ fileTimer->stop(); } //Shrink screen back, if necessary. double mws = fileModeEnabled ? daq_maxWindowSize : ((double)MAX_WINDOW_SIZE); if (display.window > mws) { display.window = mws; timeWindowUpdated(display.window); } if ((display.window + display.delay) > mws) { display.delay -= display.window + display.delay - mws; delayUpdated(display.delay); } if (display.delay < 0) { display.delay = 0; delayUpdated(display.delay); } } void isoDriver::setSerialType(unsigned char type) { serialType = type; qDebug() << "Serial Type changed to" << serialType; if(serialType == 1) { if (twoWire) delete twoWire; twoWire = new i2c::i2cDecoder(internalBuffer375_CH1, internalBuffer375_CH2, internalBuffer375_CH1->m_console1); } } void isoDriver::hideCH1(bool enable) { axes->graph(0)->setVisible(!enable); } void isoDriver::hideCH2(bool enable) { axes->graph(1)->setVisible(!enable); } void isoDriver::triggerStateChanged() { if (!triggerEnabled) { internalBuffer375_CH1->setTriggerType(TriggerType::Disabled); internalBuffer375_CH2->setTriggerType(TriggerType::Disabled); internalBuffer750->setTriggerType(TriggerType::Disabled); return; } qDebug() << "triggerStateChanged()"; switch(triggerMode) { case 0: { internalBuffer375_CH1->setTriggerType(TriggerType::Rising); internalBuffer375_CH2->setTriggerType(TriggerType::Disabled); internalBuffer750->setTriggerType(TriggerType::Rising); break; } case 1: { internalBuffer375_CH1->setTriggerType(TriggerType::Falling); internalBuffer375_CH2->setTriggerType(TriggerType::Disabled); internalBuffer750->setTriggerType(TriggerType::Falling); break; } case 2: { internalBuffer375_CH1->setTriggerType(TriggerType::Disabled); internalBuffer375_CH2->setTriggerType(TriggerType::Rising); internalBuffer750->setTriggerType(TriggerType::Disabled); break; } case 3: { internalBuffer375_CH1->setTriggerType(TriggerType::Disabled); internalBuffer375_CH2->setTriggerType(TriggerType::Falling); internalBuffer750->setTriggerType(TriggerType::Disabled); break; } } } void isoDriver::offsetChanged_CH1(double newOffset) { m_offset_CH1 = newOffset; } void isoDriver::offsetChanged_CH2(double newOffset) { m_offset_CH2 = newOffset; } void isoDriver::attenuationChanged_CH1(int attenuationIndex) { switch(attenuationIndex) { case 0: m_attenuation_CH1 = 1; break; case 1: m_attenuation_CH1 = 5; break; case 2: m_attenuation_CH1 = 10; break; default: throw std::runtime_error("Unknown attenuation index for CH1"); } } void isoDriver::attenuationChanged_CH2(int attenuationIndex) { switch(attenuationIndex) { case 0: m_attenuation_CH2 = 1; break; case 1: m_attenuation_CH2 = 5; break; case 2: m_attenuation_CH2 = 10; break; default: throw std::runtime_error("Unknown attenuation index for CH2"); } } void isoDriver::setHexDisplay_CH1(bool enabled) { hexDisplay_CH1 = enabled; } void isoDriver::setHexDisplay_CH2(bool enabled) { hexDisplay_CH2 = enabled; } void isoDriver::setMinSpectrum(int minSpectrum) { m_spectrumMinX = static_cast(minSpectrum); } void isoDriver::setMaxSpectrum(int maxSpectrum) { m_spectrumMaxX = static_cast(maxSpectrum); }