Skip to content
Snippets Groups Projects
Select Git revision
  • 11c4b50e789742db8e0c16cde57a65f32d603ee0
  • master default protected
2 results

AmptekHardwareInterface.cpp

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    AmptekHardwareInterface.cpp 30.09 KiB
    #include "AmptekHardwareInterface.h"
    #include "packet.h"
    #include <iostream>
    #include <iomanip>
    #include <cstring>
    #include "AmptekUdpConnectionHandler.h"
    #include "AmptekUsbConnectionHandler.h"
    #include "AmptekSimulatorConnectionHandler.h"
    
    AmptekHardwareInterface::AmptekHardwareInterface(){
    
    }
    
    AmptekHardwareInterface::~AmptekHardwareInterface(){
        std::cout << "in AmptekHardwareInterface::~AmptekHardwareInterface()" << std::endl;
        if (connection_handler != nullptr){
            std::cout << "delete connection_handler" << std::endl;
            delete connection_handler;
        }
    }
    
    
    /**
     * @brief Connect via UDP to the detector
     * 
     * @param hostname host of the detector, can be IP address
     * @param port port on which the amptek is listening. Should be 10001
     * @param timeout connection timeout in seconds
     */
    void AmptekHardwareInterface::connectUDP(std::string hostname, int port, double timeout){
    
        if (connection_handler != nullptr){
            throw AmptekException("Amptek is already connected");
        }
        else{
            connection_handler = new AmptekUdpConnectionHandler(hostname, port, timeout);
            current_state = ON;
        }
    }
    
    /**
     * @brief Connect via USB to the detector
     * 
     * @param serialNb the serial number of the detector to connect with
     */
    void AmptekHardwareInterface::connectUSB(int serialNb){
    if (connection_handler != nullptr){
            throw AmptekException("Amptek is already connected");
        }
        else{
            connection_handler = new AmptekUsbConnectionHandler(serialNb);
            current_state = ON;
        }
    }
    
    /**
     * @brief Connect to a simulator interface
     * This allows debugging of interface logic without an available hardware to connect to
     * 
     */
    void AmptekHardwareInterface::connectSimulator(){
    
        if (connection_handler != nullptr){
            throw AmptekException("Amptek is already connected");
        }
        else{
            connection_handler = new AmptekSimulatorConnectionHandler();
            current_state = ON;
        }
    }
    
    
    /**
     * @brief Checks if the stored last spectrum is younger than the given maximum age
     * 
     * @param max_age_ms maximum allowed age in milliseconds
     * @return true if spectrum is young enough
     * @return false if it is too old
     */
    bool AmptekHardwareInterface::spectrumAgeOk( double max_age_ms ) const {
        if (max_age_ms < 0){
            return false;
        }
        return last_spectrum.AgeMillis() < max_age_ms;
    }
    
    
    /**
     * @brief  Checks if the stored last status is younger than the given maximum age
     * 
     * @param max_age_ms maximum allowed age in milliseconds
     * @return true true if status is young enough
     * @return false false if it is too old
     */
    bool AmptekHardwareInterface::statusAgeOk( double max_age_ms ) const {
        if (max_age_ms < 0){
            return false;
        }
        return last_status.AgeMillis() < max_age_ms;
    }
    
    
    /**
     * @brief Enables the acquisition on the detector
     * 
     * @return true on success
     * @return false on failure
     */
    bool AmptekHardwareInterface::Enable(){
        try{
            expectAcknowledge( connection_handler->sendAndReceive( Packet::DP5_PKT_REQUEST_ENABLE ) );
        }
        catch( AmptekException& e){
            std::cerr << e.what() << std::endl;
            current_state = ERROR;
            return false;
        }
        return true;
    }
    
    
    /**
     * @brief Disable the acquisition on the detector
     * 
     * @return true on success
     * @return false on failure
     */
    bool AmptekHardwareInterface::Disable(){
        try{
            expectAcknowledge( connection_handler->sendAndReceive( Packet::DP5_PKT_REQUEST_DISABLE ) );
        }
        catch( AmptekException& e){
            std::cerr << e.what() << std::endl;
            current_state = ERROR;
            return false;
        }
        return true;
    }
    
    /**
     * @brief Send a test packet to the detector
     * 
     * @return true on success
     * @return false on failure
     */
    bool AmptekHardwareInterface::Ping(){
        try{
            expectAcknowledge( connection_handler->sendAndReceive( Packet::DP5_PKT_REQUEST_KEEP_ALIVE_NO_SHARING ) );
        }
        catch( AmptekException& e){
            std::cerr << e.what() << std::endl;
            current_state = ERROR;
            return false;
        }
        return true;
    }
    
    /**
     * @brief Clear the current spectrum buffer on the detector
     * 
     * @return true on success
     * @return false on failure
     */
    bool AmptekHardwareInterface::ClearSpectrum(){
        try{
            expectAcknowledge( connection_handler->sendAndReceive( Packet::DP5_PKT_REQUEST_CLEAR_SPECTRUM ) );
        }
        catch( AmptekException& e){
            std::cerr << e.what() << std::endl;
            current_state = ERROR;
            return false;
        }
        return true;
    }
    
    
    /**
     * @brief Set the accumulation time for a measurement. The detector will stop automatically when the duration is reached
     * 
     * @note The Accumulation timer is stopped while the detector is gated, in transfer mode, reset lockout
     * or any other mode that temporarily blockes the MCA. Therefore, accumulation time is larger than the real measurement duration
     * 
     * @see SetPresetAccumulationTime, SetPresetRealTime, SetPresetCounts
     * @param t accumulation time in seconds. Minimum is 0.1. If below zero, the accumulation time is not part of the stop condition during acquisition
     * @return true on success
     * @return false on failure
     */
    bool AmptekHardwareInterface::SetPresetAccumulationTime(double t){
        std::stringstream cmd_stream;
        cmd_stream << "PRET=";
        if (t>0){
            cmd_stream <<  std::fixed << std::setprecision(1) << t;
        }
        else{
            cmd_stream << "OFF";
        }
        return SetTextConfiguration( {cmd_stream.str()} );
    }
    
    
    /**
     * @brief Set the real time for a measurement. The detector will stop automatically when the duration is reached
     * 
     * @see SetPresetAccumulationTime, SetPresetRealTime, SetPresetCounts
     * @param t real time in seconds. If below zero, the real time is not part of the stop condition during acquisition
     * @return true on success
     * @return false on failure
     */
    bool AmptekHardwareInterface::SetPresetRealTime(double t){
        std::stringstream cmd_stream;
        cmd_stream << "PRER=";
        if (t>0){
            cmd_stream <<  std::fixed << std::setprecision(1) << t;
        }
        else{
            cmd_stream << "OFF";
        }    
        return SetTextConfiguration( {cmd_stream.str()} );
    }
    
    
    /**
     * @brief Set the count limit for a measurement. The detector will stop automatically when the slow count is reached
     * 
     * @see SetPresetAccumulationTime, SetPresetRealTime, SetPresetCounts
     * @param c maximum slow counts. If below zero, the slow count is not part of the stop condition during acquisition
     * @return true on success
     * @return false on failure
     */
    bool AmptekHardwareInterface::SetPresetCounts(int c){
        std::stringstream cmd_stream;
        cmd_stream << "PREC=";
        if (c>0){
            cmd_stream << c;
        }
        else{
            cmd_stream << "OFF";
        }
        return SetTextConfiguration( {cmd_stream.str()} );
    }
    
    /**
     * @brief Send text configuration parameters to the detector. See DP5 Programmers Guide for available commands
     * 
     * @param commands vector of strings in the format "CMD=VAL", each entry in the vector being one config
     * @return true on success
     * @return false on failure
     */
    bool AmptekHardwareInterface::SetTextConfiguration(std::vector<std::string> commands){
        std::cout << "Configuration is\n";
            for (auto cmd :commands){
                std::cout << "\t" << cmd << "\n";
            }
            std::cout << std::endl;
        try{
    
            stringstream cmdstream;
            string cmd;
            int streamsize = 0;
            
            for (int i = 0; i < commands.size(); ++i){
                    cmd = commands[i];
    
                // if max packet size (512) would be exceeded when adding this command, send the previous commands and clear the input stringstream
                if(  streamsize + cmd.size() > 511){
                    std::cout << "Send " << cmdstream.str() << std::endl;
                    expectAcknowledge( connection_handler->sendAndReceive( Packet::gernerateSetConfigurationRequest( cmdstream.str() ) ) );
                    cmdstream = stringstream();
                }
                
                //append the current command to the stringstream and add seperator
                cmdstream << cmd << ";";
                streamsize += cmd.size() + 1;
    
                // if this is the last command in the loop, send the stringstream even if max size is not reached
                if(  i == commands.size()-1 ){
                    std::cout << "Send " << cmdstream.str() << std::endl;
                    expectAcknowledge( connection_handler->sendAndReceive( Packet::gernerateSetConfigurationRequest( cmdstream.str() ) ) );
                }
            }
        }
        catch( AmptekException& e){
            std::cerr << "Failed sending Text Config: " << e.what() <<  "\n";
            std::cerr << "Configuration was\n";
            for (auto cmd :commands){
                std::cerr << "\t" << cmd << "\n";
            }
            std::cerr << std::endl;
            current_state = ERROR;
            return false;
        }
        return true;
    }
    
    
    /**
     * @brief Configuration read back from the detector
     * 
     * @param commands  each element being one configuration parameter CMD
     * @return std::vector<std::string> vector of strings in the format "CMD=VAL"
     */
    std::vector<std::string> AmptekHardwareInterface::GetTextConfiguration(std::vector<std::string> commands){
        
            stringstream cmdstream;
            string cmd;
            int streamsize = 0;
            char configbuffer[MAX_UDP_PACKET_SIZE];
    
            std::vector<std::string> configs;
            std::string config_segment;
    
            int max_commands_per_request = 32; //command config return strings are below 16 chars (except few), meaning that 32 command will always be below 512 byte
            for (int i = 0; i < commands.size(); ++i){
                cmd = commands[i];
                cmdstream << cmd << ";";
                if (  ( i > 0 && i%max_commands_per_request ==0) //if max is reached
                     || i == commands.size() - 1)                // or last config
                {
                    Packet settingResponse = connection_handler->sendAndReceive( Packet::gernerateGetConfigurationRequest( cmdstream.str() ) );
                    
                    if (settingResponse.at(PID1) != DP5_P1_DATA_RESPONSE
                    || settingResponse.at(PID2) != DP5_P2_DATA_RESPONSE_CONFIG_READBACK)
                    {
                        throw AmptekException("Failed in AmptekHardwareInterface::GetTextConfiguration: \
                                                    Response Packet did not match expected PIDs. \n\
                                                    Raw Packet Data:\n" + settingResponse.toString() );
                    }
                    
                    memcpy(configbuffer, &(settingResponse.at(DATA)), settingResponse.dataLength);
                    configbuffer[ settingResponse.dataLength ] = NULL;
                    std::stringstream configstream(configbuffer);
                    while(std::getline(configstream, config_segment, ';')){
                        configs.push_back(config_segment);
                    }
                }
            }
            return configs;
    }
    
    
    /**
     * @brief Update the detector status if too old
     * 
     * @param max_age_ms maximum age of status in milliseconds. If too old, the status will be read from the detector
     * @return AmptekStatus& reference to the current status object
     */
    AmptekStatus& AmptekHardwareInterface::updateStatus(double max_age_ms){
        if ( !statusAgeOk(max_age_ms)){
            Packet statusResponse = connection_handler->sendAndReceive( Packet::DP5_PKT_REQUEST_STATUS );
            if (statusResponse.at(PID1) != DP5_P1_STATUS_RESPONSE
              || statusResponse.at(PID2) != DP5_P2_STATUS_RESPONSE_INFO)
            {
                  throw AmptekException("Response Packet did not match expected PIDs: " + statusResponse.toString() );
                  return last_status;
            }
            last_status = AmptekStatus(&(statusResponse.at(DATA)));
        }
        return last_status;
    }
    
    
    /**
     * @brief Update the spectrum data if too old
     * 
     * @param max_age_ms maximum age of spectrum in milliseconds. If too old, the spectrum will be read from the detector
     * @return true on success
     * @return false on failure
     */
    bool AmptekHardwareInterface::updateSpectrum(double max_age_ms){
        if ( !spectrumAgeOk(max_age_ms)){
            Packet spectrumResponse = connection_handler->sendAndReceive( Packet::DP5_PKT_REQUEST_SPECTRUM_AND_STATUS );
            //std::cout << spectrumResponse.size() << std::endl;
            if (spectrumResponse.at(PID1) != DP5_P1_SPECTRUM_RESPONSE )
            {
                  std::cerr<< "Response Packet is not of type Spectrum: " << spectrumResponse.toString() << std::endl;
                  return false;
            }
            bool with_status = true;
            int spectrum_length;
            switch( spectrumResponse.at(PID2) ){
                case DP5_P2_SPECTRUM_RESPONSE_SPECTRUM256:
                    with_status = false;        // not break! use the respose with status setting the length
                case DP5_P2_SPECTRUM_RESPONSE_SPECTRUM256_STATUS:
                    spectrum_length=256;
                    break;
    
                case DP5_P2_SPECTRUM_RESPONSE_SPECTRUM512:
                    with_status = false;        // not break! use the respose with status setting the length
                case DP5_P2_SPECTRUM_RESPONSE_SPECTRUM512_STATUS:
                    spectrum_length=512;
                    break;
    
                case DP5_P2_SPECTRUM_RESPONSE_SPECTRUM1024:
                    with_status = false;        // not break! use the respose with status setting the length
                case DP5_P2_SPECTRUM_RESPONSE_SPECTRUM1024_STATUS:
                    spectrum_length=1024;
                    break;
    
                case DP5_P2_SPECTRUM_RESPONSE_SPECTRUM2048:
                    with_status = false;        // not break! use the respose with status setting the length
                case DP5_P2_SPECTRUM_RESPONSE_SPECTRUM2048_STATUS:
                    spectrum_length=2048;
                    break;
    
                case DP5_P2_SPECTRUM_RESPONSE_SPECTRUM4096:
                    with_status = false;        // not break! use the respose with status setting the length
                case DP5_P2_SPECTRUM_RESPONSE_SPECTRUM4096_STATUS:
                    spectrum_length=4096;
                    break;
    
                
                case DP5_P2_SPECTRUM_RESPONSE_SPECTRUM8192:
                    with_status = false;        // not break! use the respose with status setting the length
                case DP5_P2_SPECTRUM_RESPONSE_SPECTRUM8192_STATUS:
                    spectrum_length=8192;
                    break;
                default:
                    throw AmptekException("Unknown PID2. Cannot read Spectrum Response");
            }
            
            int spectrum_bytesize = 3*spectrum_length;
            try{
                last_spectrum = AmptekSpectrum( &(spectrumResponse.at(DATA) ), spectrum_length );
    
            }
            catch(std::runtime_error& e){
                throw AmptekException(std::string("Failed in AmptekHardwareInterface::updateSpectrum \
                                       during spectrum update: ") + e.what());
            }
    
            try{
                if (with_status){
                    last_status = AmptekStatus(&(spectrumResponse.at(DATA + spectrum_bytesize)));
                }
            }
            catch(std::runtime_error& e){
                throw AmptekException(std::string("Failed in AmptekHardwareInterface::updateSpectrum \
                                       during status update: ") + e.what());
            }
            
        }
        return true;
    }
    
    
    /**
     * @brief Enables the list mode and streams the records into the targetfile 
     * 
     * The targetfile will be filled with the packet content of list data response messages
     * The sync and checksum bytes are stripped from the response, but PIDs and LEN bytes will be written as well.
     * This allows extending the output with other packet types later on if needed, 
     * as well as having a clean way to find FIFO overflow packets during analysis
     * 
     * This function will invoke a continous loop in a worker thread and will result in 
     * high load on the USB interface and possibly CPU
     * 
     * @param targetfile the path of the file to stream the responses
     * @return true on success
     * @return false on failure
     */
    bool AmptekHardwareInterface::EnableListMode(std::string targetfile){
    
        streamfile.open( targetfile, ios::binary );
        //f.write( static_cast <char*> ( &(pts.count) ), sizeof( unsigned ) );
        listmode_flag = true;
        ResetListModeTimer();
        list_reader_thread = new std::thread([&](){
            while(listmode_flag){
                try{
                     Packet listResponse = connection_handler->sendAndReceive( Packet::DP5_PKT_REQUEST_LIST_DATA );
                    //std::cout << spectrumResponse.size() << std::endl;
                    if (listResponse.at(PID1) != DP5_P1_DATA_RESPONSE )
                    {
                        throw AmptekException("Response Packet is not of type DATA_RESPONSE: " + listResponse.toString());
                    }
                    if( listResponse.at(PID2) != DP5_P2_DATA_RESPONSE_LISTDATA && listResponse.at(PID2) != DP5_P2_DATA_RESPONSE_LISTDATA_FULL ){
                        throw AmptekException("Response Packet is not of subtype LISTDATA: " + listResponse.toString());
                    }
                    //write the packet without the sync and checksum to file : [PID1,PID2,LEN_MSB;LEN_LSB,DATA_0,....,DATA_N]
                    if (listResponse.dataLength > 0){
                        streamfile.write( reinterpret_cast<char*>( &listResponse[PID1] ), listResponse.dataLength + 4 );
                    }
                    if (listResponse.dataLength < 1024){ // only sleep if not too many entries
                        std::this_thread::sleep_for(std::chrono::microseconds(20));
                    }
                }
                catch(AmptekException& e){
                    std::cerr << e.what() << std::endl;
                }
                
            }
        });
        return true;
    }
    
    
    
    
    
    /**
     * @brief Disable the list mode streaming.
     * 
     * This will close the target file and delete the worker thread
     * 
     * @return true on success
     * @return false on failure
     */
    bool AmptekHardwareInterface::DisableListMode(){
        listmode_flag = false;
        if (list_reader_thread != nullptr ){
            list_reader_thread->join();
            streamfile.close();
            delete list_reader_thread;
            list_reader_thread = nullptr;
            return true;
        }
        return false;
    }
    
    
    /**
     * @brief Reset the frame and counter registers of the list mode timer
     * 
     * This will create a 0 time record in the data stream. 
     * 
     * @note The list mode timer is free running! This does not stop the timer, only resets it!
     * @return true on success
     * @return false on failure
     */
    bool AmptekHardwareInterface::ResetListModeTimer(){
        try{
            expectAcknowledge(connection_handler->sendAndReceive( Packet::DP5_PKT_REQUEST_CLEAR_LIST_TIMER) );
        }
        catch(AmptekException& e){
        
                std::cerr<< "Failed clearing list-mode timer: " << e.what() << std::endl;
                return false;
        }
        return true;
    }
    
    
    /**
     * @brief Starts the hardware sequential buffering mode. 
     * 
     * THe sequential buffering mode ends, when all buffers are full or the buffering is canceled using stopBuffering.
     * The hardware channel for buffer triggers is AUX IN 2
     * 
     * @note Hardware signals have to be provided to buffer a spectrum
     * @note On PX5, the Connector input has to be configured to the right AUX input
     * @note While in buffering mode, spectrum requests will be blocked with an ACK BUSY response packet
     * 
     * @return true on success
     * @return false on failure
     */
    bool AmptekHardwareInterface::startBuffering(){
        try{
            expectAcknowledge(connection_handler->sendAndReceive( Packet::DP5_PKT_REQUEST_RESTART_SEQ_BUFFERING) );
        }
        catch(AmptekException& e){
        
                std::cerr<< "Failed clearing list-mode timer: " << e.what() << std::endl;
                return false;
        }
        return true;
    }
    
    
    /**
     * @brief Cancels the hardware sequential buffering mode. 
     * 
     * @return true on success
     * @return false on failure
     */
    bool AmptekHardwareInterface::stopBuffering(){
        try{
            expectAcknowledge(connection_handler->sendAndReceive( Packet::DP5_PKT_REQUEST_CANCEL_SEQ_BUFFERING) );
        }
        catch(AmptekException& e){
        
                std::cerr<< "Failed clearing list-mode timer: " << e.what() << std::endl;
                return false;
        }
        return true;
    }
    
    /**
     * @brief Get Status and spectrum stored in an hardware buffer on the detector
     * 
     * @note This will always return data. It as to be assured externally, that the buffer is really filled
     * @param id index of the buffer to read from
     * @return std::pair<AmptekSpectrum, AmptekStatus> spectrum and status stored in the buffer slot
     */
    std::pair<AmptekSpectrum, AmptekStatus>  AmptekHardwareInterface::GetBufferedSpectrum(size_t id){
        Packet spectrumResponse = connection_handler->sendAndReceive( Packet::generateGetBufferRequest(id) );
        
        //std::cout << spectrumResponse.size() << std::endl;
        if (spectrumResponse.at(PID1) != DP5_P1_SPECTRUM_RESPONSE )
        {
                throw AmptekException( "Response Packet is not of type Spectrum: " + spectrumResponse.toString() );
                
        }
        int spectrum_length;
        switch( spectrumResponse.at(PID2) ){
            case DP5_P2_SPECTRUM_RESPONSE_SPECTRUM256_STATUS:
                spectrum_length=256;
                break;
    
            case DP5_P2_SPECTRUM_RESPONSE_SPECTRUM512_STATUS:
                spectrum_length=512;
                break;
    
            case DP5_P2_SPECTRUM_RESPONSE_SPECTRUM1024_STATUS:
                spectrum_length=1024;
                break;
    
            case DP5_P2_SPECTRUM_RESPONSE_SPECTRUM2048_STATUS:
                spectrum_length=2048;
                break;
    
            case DP5_P2_SPECTRUM_RESPONSE_SPECTRUM4096_STATUS:
                spectrum_length=4096;
                break;
    
            
            case DP5_P2_SPECTRUM_RESPONSE_SPECTRUM8192_STATUS:
                spectrum_length=8192;
                break;
            
            default:
                throw AmptekException("Invalid PID2. No Status Attached: " + spectrumResponse.toString() );
        }
        
        int spectrum_bytesize = 3*spectrum_length;
        AmptekSpectrum buffered_spectrum( &(spectrumResponse.at(DATA) ), spectrum_length );
        AmptekStatus buffered_status(&(spectrumResponse.at(DATA + spectrum_bytesize)));
        return  std::pair<AmptekSpectrum, AmptekStatus>( buffered_spectrum, buffered_status );
    }
    
    
    
    /**
     * @brief This starts the streaming commtest allowing debugging without a signal source 
     * 
     * This will generate predictable counts in the digital backend of the detector, which can be used for MCA, MCS and List Mode.
     * It will generate signals starting at amplitude min_channel, incrementing by increment until max_channel is reached. Then the generated 
     * amplitude falls down to min_channel again.
     * 
     * @param min_channel lower amplitude limit for generated events
     * @param max_channel upper amplitude limit for generated events
     * @param increment amplitude increment between two generated events
     * @param rate event rate in evt/sec
     * @return true on success 
     * @return false on failure
     */
    bool AmptekHardwareInterface::StartCommtestStreaming(uint16_t min_channel,uint16_t max_channel, 
                                        uint16_t increment, uint32_t rate)
    {
        // convert from rate (cts/sec) to number of fpga clock cycles between two events
        uint32_t period = std::max(8., last_status.FpgaClock()*1e6/rate );
        std::cout << "Pulse Period: " << period << std::endl;
        try{
            Packet commtest_packet = Packet::generateCommtestStreamingRequest(min_channel, max_channel, increment, period);
            std::cout << commtest_packet.toString() << std::endl;
            expectAcknowledge(connection_handler->sendAndReceive( commtest_packet) );
        }
        catch(AmptekException& e){
        
                std::cerr<< "Failed starting comm test: " << e.what() << std::endl;
                return false;
        }
        return true;
    }
    
    
    /**
     * @brief Disables the commtest streaming mode 
     * 
     * @return true on success
     * @return false on failure
     */
    bool AmptekHardwareInterface::StopCommtestStreaming(){
        try{
            expectAcknowledge(connection_handler->sendAndReceive( Packet::DP5_PKT_REQUEST_STOP_STREAM_COMMTEST) );
        }
        catch(AmptekException& e){
        
                std::cerr<< "Failed stopping comm test: " << e.what() << std::endl;
                return false;
        }
        return true;
    }
    
    
    
    
    
    
    
    
    std::vector<unsigned int> AmptekHardwareInterface::GetSpectrum(double max_age_ms){
        updateSpectrum(max_age_ms);
    	return last_spectrum.bins;
    }
    
    
    // int AmptekHardwareInterface::GetSpectrum(unsigned int* spectrum, double max_age_ms) {
    //     updateSpectrum(max_age_ms);
    //     spectrum = last_spectrum;
    //     return spectrum_length;
    // }
    
    
    
    
    
    /**
     * @brief Throws AmptekException if packet is not of type Acknowledge OK.
     * 
     * This function can be wrapped around sendAndReceive calls if the request package ony expects an acknowledge response
     * 
     * 
     * @param packet 
     */
    void AmptekHardwareInterface::expectAcknowledge(Packet packet){
        if (packet.at(PID1) != DP5_P1_ACK){
            throw AmptekException("Response Package was not an Acknowledge: " + packet.toString());
        }else{
            if (   packet.at(PID2) == DP5_P2_ACK_OK
                || packet.at(PID2) == DP5_P2_ACK_OK_SHARING
                || packet.at(PID2) == DP5_P2_ACK_OK_FPGAADDR){
                return;
            }else{
                std::string error_msg;
                char databuffer[packet.dataLength];
                switch (packet.at(PID2) ){
    
                    case DP5_P2_ACK_SYNC_ERR:
                        error_msg = "Sync bytes in Request Packet were not correct, \
                                                            and therefore, the Request Packet was rejected.";
                        break;
                    case DP5_P2_ACK_PID_ERR:
                        error_msg =  "PID1 & PID2 combination is not recognized as a valid \
                                                         Request Packet, and therefore, the Request Packet was rejected.";
                        break;
    
                    case DP5_P2_ACK_LEN_ERR:
                        error_msg = "LEN field of the Request Packet was not consistent with \
                                                         Request Packet type defined by the PID1 & PID2 combination. \
                                                         It is not recognized as a valid Request Packet, \
                                                         and therefore, the Request Packet was rejected.";
                        break;
    
                    case DP5_P2_ACK_CHECKSUM_ERR:
                        error_msg = "Checksum of the Request Packet was incorrect, \
                                                         and therefore, the Request Packet was rejected.";
                        break;
    
    
                    case DP5_P2_ACK_BADPARAM_ERR:
                        error_msg = "Bad parameter.";
                        break;
    
    
                    case DP5_P2_ACK_BADHEX_ERR:
                        error_msg = "Microcontroller or FPGA upload packets error: \
                                                         hex record contained in the data field \
                                                         of the Request Packet had a checksum or other structural error.";
                        break;
    
    
    
                    case DP5_P2_ACK_BADCMD_ERR:
                        
                        byteToChar(&(packet.at(DATA)), databuffer, packet.dataLength);
                        error_msg ="Unrecognized command: " + std::string(databuffer);
                        break;
    
    
    
                    case DP5_P2_ACK_FPGA_ERR:
                        error_msg =  "FPGA initialization failed.";
                        break;
    
    
    
                    case DP5_P2_ACK_CP2201_ERR:
                        error_msg = "Set Ethernet Settings Request Packet was received, \
                                                         but an Ethernet controller was not detected on the DP5.";
                        break;
    
    
    
                    case DP5_P2_ACK_SCOPE_ERR:
                        error_msg = "Send Scope Data Request Packet was received, \
                                                         but the digital oscilloscope hasn’t triggered, \
                                                         so no data is available. [The digital oscilloscope must be armed, \
                                                         and then a trigger must occur for data to be available.]";
                        break;
    
    
                    case DP5_P2_ACK_PC5_ERR:
                        error_msg = "ASCII command errors – the data field will contain the \
                                                         ASCII command and parameter which caused the error. “Bad Parameter” \
                                                         means that the parameter isn’t recognized \
                                                         or exceeds the range of the command. “Unrecognized Command” \
                                                         means that the 4-character command isn’t recognized. \
                                                         “PC5 Not Present” is returned if a PC5 is not mated to the DP5, \
                                                         and a command requiring a PC5 is sent. (i.e. “HVSE”, Set High \
                                                         Voltage.) [A ‘Bad Parameter’ ACK packet may also be returned \
                                                         for a malformed I2C Request Packet, in which case LEN=0.] \
                                                         If an incomplete or garbled command is returned in the data field, \
                                                         it may mean that the ASCII Configuration Packet has structural \
                                                         issues. (Disallowed whitespace, missing semicolon, etc.)";
                        break;
                    
    
                    case DP5_P2_ACK_BUSY_ERR:
                        error_msg = "Busy, another interface in use.";
                        break;
    
                    case DP5_P2_ACK_I2C_ERR:
                        error_msg =  "‘I2C Transfer’ Request Packet, but no I2C ACK \
                                                         was detected from an I2C Slave.";
                        break;
    
    
                    case DP5_P2_ACK_VERSION_ERR:
                        error_msg ="Request Packet has been recognized as valid by the firmware, \
                                                         but it is not supported by the installed FPGA version. \
                                                         Update the FPGA to the latest FP version.";
                        break;
                        
    
                    case DP5_P2_ACK_CALIB_ERR:
                        error_msg = "Calibration Error";
                        break;
                    default:
                        error_msg = "Unknown wrong PID2 in ACK Response Package.";
    
                }
                throw AmptekException( error_msg );
            }
        }
    }