#include "AWG.h" #include <iostream> #include <cmath> #include <vector> AWG::AWG() noexcept { this->pCardHandle = nullptr; this->cardIdx = -1; this->serialNumber = -1; this->instMemSize = -1; this->bytesPerSample = -1; this->maxSampleRate = -1; } AWG::~AWG() { // TODO: do this if (this->isOpen()) { this->close(); } this->pCardHandle = nullptr; return; } void AWG::checkError() { /** * printout error if detected. * */ if (this->pCardHandle == nullptr) { return; } char errorMsg[ERRORTEXTLEN]; if ( spcm_dwGetErrorInfo_i32(this->pCardHandle, NULL, NULL, errorMsg) != ERR_OK ) { //std::cout << "AWG error" << std::string(errorMsg) // << "\ncard closed\n"; this->close(); throw CardException(errorMsg); } } //void* AWG::getPageAlignedMem(uint64 memSize) { // /** // * returns a pointer to a page aligned mem location. // * // * @param memSize size of memory to allocate, bytes // * @return pointer to start of a page aligned mem location // */ // // // for unknown reasons VirtualAlloc/VirtualFree leaks memory if memSize < 4096 (page size) // // therefore use _aligned_malloc () to get small amounts of page aligned memory // if (memSize >= 4096) // return VirtualAlloc(NULL, (size_t)memSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); // else { // void* pMem = _aligned_malloc((size_t)memSize, 4096); // if (pMem == NULL) // return NULL; // memset(pMem, 0, (size_t)memSize); // return pMem; // } //} // //void AWG::freePageAlignedMem(void* pMem, uint64 memSize) { // /** // * frees a page aligned memory space. // * // * @param pMem pointer to a page aligned memory location // * @param memSize memory size in bytes to deallocate // */ // if (memSize >= 4096) // VirtualFree(pMem, 0, MEM_RELEASE); // else // _aligned_free(pMem); //} // void AWG::open(int openIndex) { /** * opens a connection to AWG by index. * * @param openIndex card index, 0 or 1 */ if (!this->isOpen()) { std::cout << "card open failed" << std::endl; return; } auto openMsg = "/dev/spcm" + std::to_string(openIndex); this->pCardHandle = spcm_hOpen(openMsg.c_str()); if (pCardHandle == nullptr) { std::cout << "card open failed" << std::endl; return; } this->cardIdx = openIndex; spcm_dwGetParam_i32( this->pCardHandle, SPC_PCISERIALNO, &this->serialNumber ); spcm_dwGetParam_i64( this->pCardHandle, SPC_PCISAMPLERATE, &this->maxSampleRate ); spcm_dwGetParam_i64( this->pCardHandle, SPC_PCIMEMSIZE, &this->instMemSize ); spcm_dwGetParam_i32( this->pCardHandle, SPC_MIINST_BYTESPERSAMPLE, &this->bytesPerSample ); this->checkError(); } bool AWG::isOpen() { /** * check if a connection to AWG exists. * * @return true or false */ if (this->pCardHandle == nullptr) { return false; } return true; this->checkError(); } void AWG::close() { /** * close the connection to AWG. * */ if (!this->isOpen()) { return; } spcm_vClose(this->pCardHandle); this->pCardHandle = nullptr; } void AWG::reset() { /** * resets the AWG card, equivalent to a power reset. * */ if (!this->isOpen()) { return; } spcm_dwSetParam_i32( this->pCardHandle, SPC_M2CMD, M2CMD_CARD_RESET ); this->checkError(); } int AWG::getCardIdx() { return this->cardIdx; } int AWG::getSerialNumber() { return this->serialNumber; } int64 AWG::getInstMemSize() { return this->instMemSize; } int AWG::getBytesPerSample() { return this->bytesPerSample; } int64 AWG::getMaxSampleRate() { return this->maxSampleRate; } void AWG::setSampleRate(int64 sampleRate) { if (!this->isOpen()) { return; } if (sampleRate < 0 or sampleRate > this->maxSampleRate) { std::cout << sampleRate << "exceeds max sample rate" << std::endl; return; } spcm_dwSetParam_i64( this->pCardHandle, SPC_SAMPLERATE, sampleRate ); } int64 AWG::getSampleRate() { if (!this->isOpen()) { return 0; } int64 sr; spcm_dwGetParam_i64( this->pCardHandle, SPC_SAMPLERATE, &sr ); this->checkError(); return sr; } void AWG::setActiveChannels(std::set<int> channels) { /** * activate channels for next run, resets last set. * * @param channels vector of channel index (0-3) */ if (!this->isOpen()) { return; } int64 chMask = 0; for (auto ch : channels) { chMask = chMask | int64(pow(2,ch)); } spcm_dwSetParam_i64( this->pCardHandle, SPC_CHENABLE, chMask ); this->checkError(); this->activeChannels = channels; } void AWG::setChannelAmp(int ch, int amp) { /** * set channel amplitude. * * @param ch channel index * @param amp amplitude (mV) */ if (!this->isOpen()) { return; } const auto regStep = SPC_AMP1 - SPC_AMP0; spcm_dwSetParam_i32( this->pCardHandle, SPC_AMP0 + ch * regStep, amp ); this->checkError(); } void AWG::setChannelStopLvl(int ch, CHANNEL_STOPLVL stopLvl) { /** * set channel stop level. * * @param ch channel index * @param stopLvl options: ZERO,LOW,HIGH,HOLDLAST */ if (!this->isOpen()) { return; } const auto regStep = SPC_CH1_STOPLEVEL - SPC_CH0_STOPLEVEL; spcm_dwSetParam_i32( this->pCardHandle, SPC_CH0_STOPLEVEL + ch * regStep, int32(stopLvl) ); this->checkError(); } void AWG::toggleChannelOutput(int ch, bool enable) { /** * toggle channel output. * * @param ch channel index * @param enable true or false */ if (!this->isOpen()) { return; } if (ch < 0 or ch > 3) { std::cout << ch << "is not an allowed channel index" << std::endl; return; } const auto regStep = SPC_ENABLEOUT1 - SPC_ENABLEOUT0; spcm_dwSetParam_i32( this->pCardHandle, SPC_ENABLEOUT0 + ch * regStep, enable ); this->checkError(); } void AWG::setChannel(int ch, int amp, CHANNEL_STOPLVL stopLvl, bool enable) { /** * channel macro set amplitude, stop level, and enables output. * * @param ch channel index * @param amp amplitude (mV) * @param stopLvl options: ZERO, LOW, HIGH, HOLDLAST */ this->setChannelAmp(ch, amp); this->setChannelStopLvl(ch, stopLvl); this->toggleChannelOutput(ch, enable); } int AWG::getChannelCount() { if (!this->isOpen()) { return 0; } int32 numCh; spcm_dwGetParam_i32( this->pCardHandle, SPC_CHCOUNT, &numCh ); this->checkError(); return numCh; } std::set<int> AWG::getChannelActivated() { return this->activeChannels; } void AWG::setChannelDiffMode(int chPair, bool enable) { /** * set channel 0(2) and channel 1(3) to differential output mode. * * @param chPair 0 for channel 0,1, 1 for channel 2,3 * @param enable true or false */ if (!this->isOpen()) { return; } if (chPair != 0 && chPair != 1) { std::cout << "unable to parse input chPair: " << chPair << std::endl; return; } if (enable) { if (chPair == 0 && this->activeChannels.contains(1)) { std::cout << "channel 1 must disabled for chPair=0" "in differential output mode" << std::endl; return; } if (chPair == 1 && this->activeChannels.contains(3)) { std::cout << "channel 3 must disabled for chPair=1" "in differential output mode" << std::endl; return; } } const auto regStep = SPC_DIFF2 - SPC_DIFF0; spcm_dwSetParam_i32( this->pCardHandle, SPC_DIFF0 + chPair * regStep, enable ); this->checkError(); } void AWG::setChannelDoubleMode(int chPair, bool enable) { /** * set channel 0(2) and channel 1(3) to differential output mode. * * @param chPair 0 for channel 0,1, 1 for channel 2,3 * @param enable true or false */ if (!this->isOpen()) { return; } if (chPair != 0 && chPair != 1) { std::cout << "unable to parse input chPair: " << chPair << std::endl; return; } if (enable) { if (chPair == 0 && this->activeChannels.contains(1)) { std::cout << "channel 1 must disabled for chPair=0" "in double output mode" << std::endl; return; } if (chPair == 1 && this->activeChannels.contains(3)) { std::cout << "channel 3 must disabled for chPair=1" "in double output mode" << std::endl; return; } } const auto regStep = SPC_DOUBLEOUT2 - SPC_DOUBLEOUT0; spcm_dwSetParam_i32( this->pCardHandle, SPC_DOUBLEOUT0 + chPair * regStep, enable ); this->checkError(); } void AWG::writeSetup() { /** * write current setup to card without starting hardware, * some settings may not be changed while card is running. * */ if (!this->isOpen()) { return; } spcm_dwSetParam_i32( this->pCardHandle, SPC_M2CMD, M2CMD_CARD_WRITESETUP ); this->checkError(); } void AWG::cardRun() { /** * starts the card with all selected settings. * */ if (!this->isOpen()) { return; } spcm_dwSetParam_i32( this->pCardHandle, SPC_M2CMD, M2CMD_CARD_START ); this->checkError(); } void AWG::cardStop() { /** * stops the card, no effect if not running. * */ if (!this->isOpen()) { return; } spcm_dwSetParam_i32( this->pCardHandle, SPC_M2CMD, M2CMD_CARD_STOP ); this->checkError(); } void AWG::waitReady() { /** * wait until card finished current output run. * TODO: check if this blocks. */ if (!this->isOpen()) { return; } spcm_dwSetParam_i32( this->pCardHandle, SPC_M2CMD, M2CMD_CARD_WAITREADY ); this->checkError(); } void AWG::setTimeOut(int time) { /** * set a timeout time in ms, 0 to disable. * * @param time */ if (!this->isOpen()) { return; } if (time < 0) { std::cout << time << "is an invalid timeout time" << std::endl; } spcm_dwSetParam_i32( this->pCardHandle, SPC_TIMEOUT, time ); this->checkError(); } int AWG::getTimeOut() { if (!this->isOpen()) { return 0; } int32 time; spcm_dwGetParam_i32( this->pCardHandle, SPC_TIMEOUT, &time ); return time; } void AWG::printCardStatus() { if (!this->isOpen()) { return; } int32 status; spcm_dwGetParam_i32( this->pCardHandle, SPC_M2STATUS, &status ); std::cout << STATUS_NAMES.at(status) << std::endl; } void AWG::toggleTrigger(bool enable) { /** * toggle the trigger detection. * * @param enable */ if (!this->isOpen()) { return; } auto command = enable ? M2CMD_CARD_ENABLETRIGGER : M2CMD_CARD_DISABLETRIGGER; spcm_dwSetParam_i32( this->pCardHandle, SPC_M2CMD, command ); this->checkError(); } void AWG::forceTrigger() { /** * sends a software trigger, equivalent to an actual trigger. * */ if (!this->isOpen()) { return; } spcm_dwSetParam_i32( this->pCardHandle, SPC_M2CMD, M2CMD_CARD_FORCETRIGGER ); this->checkError(); } void AWG::waitTrigger() { /** * wait until trigger event. * TODO: check if this blocks. */ if (!this->isOpen()) { return; } spcm_dwSetParam_i32( this->pCardHandle, SPC_M2CMD, M2CMD_CARD_WAITTRIGGER ); this->checkError(); } void AWG::setTrigMaskOr(std::initializer_list<TRIGGER_MASK> trigMasks) { /** * program the OR trigger mask. * * @param trigMasks 1 or more TRIGGER_MASK */ if (!this->isOpen()) { return; } auto mask = 0; for (auto m : trigMasks) { mask = mask | int32(m); } spcm_dwSetParam_i32( this->pCardHandle, SPC_TRIG_ORMASK, mask ); this->checkError(); } void AWG::setTrigMaskOr(std::vector<TRIGGER_MASK> trigMasks) { /** * program the OR trigger mask. * * @param trigMasks 1 or more TRIGGER_MASK */ if (!this->isOpen()) { return; } auto mask = 0; for (auto m : trigMasks) { mask = mask | int32(m); } spcm_dwSetParam_i32( this->pCardHandle, SPC_TRIG_ORMASK, mask ); this->checkError(); } void AWG::setTrigMaskAnd(std::initializer_list<TRIGGER_MASK> trigMasks) { /** * program the AND trigger mask. * * @param trigMasks 1 or more TRIGGER_MASK */ if (!this->isOpen()) { return; } int mask = 0; for (auto m : trigMasks) { mask = mask | int32(m); } spcm_dwSetParam_i32( this->pCardHandle, SPC_TRIG_ANDMASK, mask ); this->checkError(); } void AWG::setTrigMaskAnd(std::vector<TRIGGER_MASK> trigMasks) { /** * program the AND trigger mask. * * @param trigMasks 1 or more TRIGGER_MASK */ if (!this->isOpen()) { return; } int mask = 0; for (auto m : trigMasks) { mask = mask | int32(m); } spcm_dwSetParam_i32( this->pCardHandle, SPC_TRIG_ANDMASK, mask ); this->checkError(); } void AWG::setTrigMode(int trigChannel, TRIGGER_MODE trigMode) { /** * sets the trigger mode of trigger channel. * * @param trigChannel 0 or 1 (labeled EXT0 and EXT1 on board) * @param trigMode see TRIGGER_MODE for options */ if (!this->isOpen()) { return; } if (trigChannel != 0 and trigChannel != 1) { std::cout << "invalid trigger channel: " << trigChannel << std::endl; return; } auto trigRegister = trigChannel ? SPC_TRIG_EXT1_MODE : SPC_TRIG_EXT0_MODE; spcm_dwSetParam_i32( this->pCardHandle, trigRegister, int32(trigMode) ); this->checkError(); } void AWG::setTrigTerm(int term) { /** * sets the trigger input termination. * * \param term 0 for high impedance, 1 for 50 Ohm. */ if (!this->isOpen()) { return; } if (term != 0) { std::cout << "invalid trigger input termination setting: " << term << std::endl; return; } spcm_dwSetParam_i32( this->pCardHandle, SPC_TRIG_TERM, term ); this->checkError(); } int AWG::getTrigTerm() { if (!this->isOpen()) { return -1; } int32 term; spcm_dwGetParam_i32( this->pCardHandle, SPC_TRIG_TERM, &term ); this->checkError(); return term; } void AWG::setTrigCoupling(int channel, int coupling) { /** * sets trigger coupling to DC or AC. * * @param coupling 1 for AC, 0 for DC */ if (!this->isOpen()) { return; } if (coupling != 0 and coupling != 1) { std::cout << "invalid trigger coupling setting :" << coupling << std::endl; return; } auto trigChannelReg = channel ? SPC_TRIG_EXT1_ACDC : SPC_TRIG_EXT0_ACDC; spcm_dwSetParam_i32( this->pCardHandle, trigChannelReg, coupling ); this->checkError(); } int AWG::getTrigCoupling(int channel) { if (!this->isOpen()) { return -1; } auto trigChannelReg = channel ? SPC_TRIG_EXT1_ACDC : SPC_TRIG_EXT0_ACDC; int32 coupling; spcm_dwGetParam_i32( this->pCardHandle, trigChannelReg, &coupling ); this->checkError(); return coupling; } void AWG::setTrigLvl(int channel, int level) { /** * set trigger level. * * @param channel trigger channel * @param level -10000mV to 10000mV */ if (!this->isOpen()) { return; } if (level < -10000 or level > 10000) { std::cout << "invalid trigger level: " << level << std::endl; return; } auto trigChannelReg = channel ? SPC_TRIG_EXT1_LEVEL0 : SPC_TRIG_EXT0_LEVEL0; spcm_dwSetParam_i64( this->pCardHandle, trigChannelReg, level ); this->checkError(); } int AWG::getTrigLvl(int channel) { if (!this->isOpen()) { return 0; } auto trigChannelReg = channel ? SPC_TRIG_EXT1_LEVEL0 : SPC_TRIG_EXT0_LEVEL0; int32 level; spcm_dwGetParam_i32( this->pCardHandle, trigChannelReg, &level ); this->checkError(); return level; } void AWG::setTrigRearmLvl(int channel, int level) { /** * sets rearm trigger level. * * @param channel trigger channel * @param level -10000mV to +10000mV */ if (!this->isOpen()) { return; } auto trigChannelReg = channel ? SPC_TRIG_EXT1_LEVEL1 : SPC_TRIG_EXT0_LEVEL1; spcm_dwSetParam_i64( this->pCardHandle, trigChannelReg, level ); this->checkError(); } int64 AWG::getTrigRearmLvl(int channel) { if (!this->isOpen()) { return 0; } auto trigChannelReg = channel ? SPC_TRIG_EXT1_LEVEL1 : SPC_TRIG_EXT0_LEVEL1; int64 level; spcm_dwGetParam_i64( this->pCardHandle, trigChannelReg, &level ); return level; } //void AWG::setReplayMode(REPLAY_MODE mode) { // /** // * set the data replay mode. // * // * a macro for sequence replay mode is setup in // * initReplayModeSeq(), if you wish to use other modes, you must // * consult the manual for mode specific settings. // * // * @param mode SINGLE, MULTI, GATE, SINGLERESTART, SEQUENCE, // * FIFO_{SINGLE, MULTI, GATE}. // * // */ // if (!this->isOpen()) { return; } // spcm_dwSetParam_i32( // this->pCardHandle, // SPC_CARDMODE, // mode // ); // this->checkError(); //} // //void AWG::setMemSize(int64 sampleSize) { // /** // * set memory size in samples in SINGLE, SINGLE_RESTART, // * MULTI, and GATE mode. // * Please consult the manual for # of channel dependent // * minimum/maximum sample size considerations. // * // * @param sampleSize size of sample // * // */ // if (!this->isOpen()) { return; } // if (sampleSize < 0 or sampleSize > 2e9) { // std::cout << "invalid sampleSize: " << sampleSize << std::endl; // return; // } // spcm_dwSetParam_i64( // this->pCardHandle, // SPC_MEMSIZE, // sampleSize // ); // this->checkError(); //} // //int64 AWG::getMemsize() { // int64 ms; // spcm_dwGetParam_i64( // this->pCardHandle, // SPC_MEMSIZE, // &ms // ); // this->checkError(); // return ms; //} // //void AWG::setLoop(int nLoop) { // /** // * set number of replay loops in SINGLE, SINGLE_RESTART, // * MULTI, and GATE mode. // * // * @param nLoop // */ // if (!this->isOpen()) { return; } // if (nLoop < 0) { // std::cout << "invalid number of loop: " << nLoop << std::endl; // return; // } // spcm_dwSetParam_i32( // this->pCardHandle, // SPC_LOOPS, // nLoop // ); //} // //int64 AWG::getLoop() { // if (!this->isOpen()) { return -1; } // int64 nl; // spcm_dwGetParam_i64( // this->pCardHandle, // SPC_LOOPS, // &nl // ); // return nl; //} void AWG::initReplayModeSeq(int nSeg) { /** * initialize sequence replay mode. * * @param nSeg number of segments memory is divided into */ if (!this->isOpen()) { return; } if (nSeg % 2 != 0) { std::cout << "invalid number of segments for SEQUENCE mode: " << nSeg << std::endl; return; } spcm_dwSetParam_i32( this->pCardHandle, SPC_CARDMODE, int32(REPLAY_MODE::SEQUENCE) ); spcm_dwSetParam_i32( this->pCardHandle, SPC_SEQMODE_MAXSEGMENTS, nSeg ); spcm_dwSetParam_i32( this->pCardHandle, SPC_SEQMODE_STARTSTEP, 0 ); this->checkError(); } void AWG::setSeqModeStep( uint32 step, uint64 segment, uint64 nextStep, uint64 nLoop, SEQ_LOOPCONDITION condition) { /** * configure a step in sequence replay mode. * * @param step step to configure * @param segment memory segment step is associated with * @param nextStep index of next step after end loop condition is met * @param condition end loop condition */ if (!this->isOpen()) { return; } int64 mask = (uint64(condition) << 32) | (nLoop << 32) | (nextStep << 16) | segment; spcm_dwSetParam_i64( this->pCardHandle, SPC_SEQMODE_STEPMEM0 + step, mask ); } void AWG::writeSeqModeSegment( void* pDataBuffer, int size, int segment) { /** * write buffered data into a memory segment in sequence replay mode. * * @param pDataBuffer pointer to data memory, must be page aligned * @param size size of data buffer in number of samples * @param segment memory segment to write to */ if (!this->isOpen()) { return; } spcm_dwSetParam_i32( this->pCardHandle, SPC_SEQMODE_WRITESEGMENT, segment ); spcm_dwSetParam_i32( this->pCardHandle, SPC_SEQMODE_SEGMENTSIZE, size ); this->prepDataTransfer(pDataBuffer, size); this->startDataTransfer(); this->checkError(); } void AWG::prepDataTransfer( void* dataBuffer, uint64 bufferLen, BUFFER_TYPE bufType, TRANSFER_DIR dir, uint32 notifySize, uint64 brdMemOffs ) { /** * prepares the board for data transfer. * * @param dataBuffer pointer to the data buffer * @param bufferLen length of data buffer, bytes * @param bufType buffer type, use only DATA for replay * @param dir direction of data transfer * @param notifySize number of bytes after which an event is sent * @param brdMemOffs offset for transfer in board memory */ if (!this->isOpen()) { return; } spcm_dwDefTransfer_i64( this->pCardHandle, int32(bufType), int32(dir), notifySize, dataBuffer, brdMemOffs, bufferLen ); this->checkError(); } void AWG::startDataTransfer() { /** * starts data transfer for a defined buffer. * */ if (!this->isOpen()) { return; } spcm_dwSetParam_i32( this->pCardHandle, SPC_M2CMD, M2CMD_DATA_STARTDMA ); this->checkError(); } void AWG::waitDataTransfer() { /** * wait for data transfer to complete, blocks, * timeout is considered. * */ if (!this->isOpen()) { return; } spcm_dwSetParam_i32( this->pCardHandle, SPC_M2CMD, M2CMD_DATA_WAITDMA ); this->checkError(); } void AWG::stopDataTransfer() { /** * stops a running data transfer. * */ if (!this->isOpen()) { return; } spcm_dwSetParam_i32( this->pCardHandle, SPC_M2CMD, M2CMD_DATA_STOPDMA ); this->checkError(); } void AWG::setClockMode(CLOCK_MODE cm) { /** * set the clock mode. * * @param cm INTPLL or EXTREFCLK */ if (!this->isOpen()) { return; } spcm_dwSetParam_i32( this->pCardHandle, SPC_CLOCKMODE, int32(cm) ); this->checkError(); } int32 AWG::getClockMode() { if (!this->isOpen()) { return -1; } int32 cm; spcm_dwGetParam_i32( this->pCardHandle, SPC_CLOCKMODE, &cm ); this->checkError(); return cm; } void AWG::setRefClkFreq(int64 frequency) { /** * if using EXTREFCLK mode, set the external clock frequency. * * @param frequency Hz */ if (!this->isOpen()) { return; } spcm_dwSetParam_i64( this->pCardHandle, SPC_REFERENCECLOCK, frequency ); this->checkError(); } void AWG::setClockOut(bool enable) { /** * enable/disable clock output. * * @param enable true/false */ if (!this->isOpen()) { return; } spcm_dwSetParam_i32( this->pCardHandle, SPC_CLOCKOUT, enable ); this->checkError(); } int64 AWG::getClockOutFreq() { /** * Read out frequency of internally synthesized clock. * * @return frequency (Hz) */ if (!this->isOpen()) { return -1; } int64 freq; spcm_dwGetParam_i64( this->pCardHandle, SPC_CLOCKOUTFREQUENCY, &freq ); this->checkError(); return freq; }