1. 程式人生 > 實用技巧 >基於NI 採集卡,實現訊號實時顯示與儲存,以及濾波

基於NI 採集卡,實現訊號實時顯示與儲存,以及濾波

最近一個需求,需要基於NI 採集卡,實現訊號實時顯示與儲存,以及濾波,在研究了NIDAQmx的API說明後,開始在C# 4.0和QT5下實現了相關的邏輯,現在免費分享QT的部分程式碼。如下:

QT5:

1.初始化介面

void NIDAQTest::InitUI()
{
	int size = DAQmxGetSystemInfoAttribute(DAQmx_Sys_DevNames, NULL, 0);
	if (size > 0)
	{
		char *devNames = (char*)malloc(size); //get the device list 
		DAQmxGetSystemInfoAttribute(DAQmx_Sys_DevNames, devNames, size);

		char* seg = ",";
		deviceName = strtok(devNames, seg);//only frist dev
		size = DAQmxGetDeviceAttribute(deviceName, DAQmx_Dev_ProductType, 0);
		char *dev1Type = (char*)malloc(size);
		DAQmxGetDeviceAttribute(deviceName, DAQmx_Dev_ProductType, dev1Type, size);

		ui.lblDeviceInfo->setText(QString("Name:  %1 ( %2 ) ").arg(deviceName).arg(dev1Type));
		size = DAQmxGetDevAIPhysicalChans(deviceName, NULL, 0);
		channelNames = (char*)malloc(size);
		DAQmxGetDevAIPhysicalChans(deviceName, channelNames, size);
	}
	else
	{
		ui.lblDeviceInfo->setText("No NI Device, please check.");
		ui.btnStart->setEnabled(false);
	}
	ui.cbxFilter->addItem("No filter");
	ui.cbxFilter->addItem("Low pass filter");
	ui.cbxFilter->addItem("High pass filter");
	ui.cbxFilter->addItem("Band pass filter");
	ui.cbxFilter->setCurrentIndex(1);
	ui.btnInputChannel->setVisible(false);
	ui.btnOutputChannel->setVisible(false);
	ui.chxTimes->setChecked(true);
	ui.txtRecordPath->setEnabled(false);

	ui.btnSave->setEnabled(false);
	//ui.btnStart->setEnabled(false); 
	ui.btnHoldContinue->setEnabled(false);
	ui.btnStop->setEnabled(false);
}

2.開始過程

void NIDAQTest::on_btnStart_clicked()
{
	if (sizeof(deviceName) == 0)
		return;

	int32       error = 0;
	char        errBuff[2048] = { '\0' };
	rate = ui.txtRate->value();
	sample = ui.txtSamples->value();
	QString qChanNames = QString(channelNames);
	QStringList channs = qChanNames.split(",");

	error += DAQmxCreateTask("", &taskHandle);
	//error = DAQmxCreateAIVoltageChan(taskHandle, "Dev1/ai0", "", DAQmx_Val_Cfg_Default, -10.0, 10.0, DAQmx_Val_Volts, NULL);
	int maxCount = MAXCHANN_COUNT;
	int bSize = sample * maxCount;
	for (int i = 0; i < maxCount; i++)
	{
		error += DAQmxCreateAIVoltageChan(taskHandle, channs[i].toLatin1(), "", DAQmx_Val_Cfg_Default, -5.0, 5.0, DAQmx_Val_Volts, NULL);
	}
	error += DAQmxCfgSampClkTiming(taskHandle, "", rate, DAQmx_Val_Rising, DAQmx_Val_ContSamps, sample);
	error += DAQmxSetBufInputBufSize(taskHandle, bSize * 10);

	error += DAQmxRegisterEveryNSamplesEvent(taskHandle, DAQmx_Val_Acquired_Into_Buffer, sample, 0, EveryNCallback, NULL);
	error += DAQmxRegisterDoneEvent(taskHandle, 0, DoneCallback, NULL);

	error += DAQmxStartTask(taskHandle);

	if (error < 0)
	{
		DAQmxGetExtendedErrorInfo(errBuff, 2048);
		QMessageBox::information(this, "ERROR", QString("DAQmx Error: %1").arg(errBuff));
		on_btnStop_clicked();
	}
	else
	{
		deltaT = (double)(ui.txtSamples->value()) / (double)(ui.txtRate->value());
		InitChartEx();
		totalRead = 0;
		currentT = 0;
		isRecording = false;
		isHold = false;
		isRunning = true; 
		receivedData = new  float64[bSize];
		ui.btnStart->setEnabled(false);
		ui.gpbParam->setEnabled(false);
		ui.btnHoldContinue->setEnabled(true);
		ui.btnStop->setEnabled(true);
		ui.btnSave->setEnabled(true);
	}
}

  3.獲取訊號過程,此過程最核心

int32 CVICALLBACK NIDAQTest::EveryNCallback(TaskHandle taskHandle, int32 everyNsamplesEventType, uInt32 nSamples, void * callbackData)
{
	if (s_this->isHold || !(s_this->isRunning)) return 1;

	int32       error = 0;
	char        errBuff[2048] = { '\0' };
	int32       read = 0;
	int         perFrame = 0;
	int         frameCount = 0;

	int        bufferSize = nSamples * MAXCHANN_COUNT;// buffer size = channel count * samples per channel
	//float64	   *data = new  float64[bufferSize];
	while (true)
	{
		if (s_this->isHold || !(s_this->isRunning))
			break;
		error = DAQmxReadAnalogF64(taskHandle, nSamples, 10.0, DAQmx_Val_GroupByChannel, s_this->receivedData, bufferSize, &read, NULL);//DAQmx_Val_GroupByScanNumber
		if (error < 0)
			break;
		perFrame += read;
		s_this->totalRead += read;
		if (perFrame == bufferSize)
		{
			frameCount++;
			perFrame = 0; 
			//s_this->UpdateDisplayStyle();
			QFuture<void> future = QtConcurrent::run([=] { s_this->UpdateDisplayStyle(); });//display 
			while (!future.isFinished())
			{
				QApplication::processEvents(QEventLoop::AllEvents, 100);
			}
			//recording data
			s_this->RecordToText();
			//QFuture<void> recTh = QtConcurrent::run([=] { s_this->RecordToText(); });//recording data
			//if (recTh.isFinished())
			//{
			//}
		}
		bool res = QMetaObject::invokeMethod(s_this, [=] {
			s_this->ui.lblRunInfo1->setText(QString("Need gain %1, Acquired frames count %2, Total size %3").arg(bufferSize).arg(frameCount).arg(s_this->totalRead));
			}, Qt::AutoConnection);

	}
	if (error < 0) {

		DAQmxGetExtendedErrorInfo(errBuff, 2048);
		QMetaObject::invokeMethod(s_this, [=] {
			QMessageBox::information(s_this, "ERROR", QString("DAQmx Error: %1").arg(errBuff));
			s_this->on_btnStop_clicked();
			}, Qt::QueuedConnection);
		return 0;
	}
	return 1;
}

在實現過程中有些坑,比較QT自帶Chart重新整理問題,比如設定NIdata buffer的問題等,後續會貼出C#的。

實現過程中參考了https://wiki.freepascal.org/NI-DAQmx_and_NI-DAQmx_Base_examples

如需要原碼可聯絡