commit 5545917824c44608d7d1757bf8a1a856462e6ec9 Author: Markus Rosenstihl Date: Thu Jun 26 11:10:51 2014 +0000 migrate to standard svn repo layout diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..271e964 --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +############################################################################# +# +# Author: Achim Gaedke +# Created: June 2004 +# +############################################################################# + + +.PHONY: clean install all + +SUBDIRS=core drivers machines tests doc tools +PREFIX=$(shell cd && pwd)/nmr_software + +all: + for d in $(SUBDIRS); do \ + $(MAKE) PREFIX=$(PREFIX) -C $$d all || exit 1; \ + done + +clean: + rm -f *~ && for d in $(SUBDIRS); do $(MAKE) -C $$d clean; done + +install: + install -d $(PREFIX); \ +for d in $(SUBDIRS); do $(MAKE) PREFIX=$(PREFIX) -C $$d install; done diff --git a/core/Makefile b/core/Makefile new file mode 100644 index 0000000..2693148 --- /dev/null +++ b/core/Makefile @@ -0,0 +1,55 @@ +############################################################################# +# +# Author: Achim Gaedke +# Created: June 2004 +# +############################################################################# + +CXX=g++ +CXXFLAGS=-g -O0 -Wshadow -Wall -Wextra -pedantic +CXXCPPFLAGS= -I. -I.. -I/usr/include `pkg-config --cflags glib-2.0` +LIBS=-lexpat -L/usr/lib `pkg-config --libs glib-2.0` +AR=ar + +.PHONY: clean all install + +CORE_CLASSES=job.o job_receiver.o result.o core_config.o core.o states.o xml_states.o xml_result.o backend_config_reader.o + +all: core.a + +../tools/add_endline.exe: ../tools/add_endline.cpp + $(CXX) $< -o $@ + +core.a: $(CORE_CLASSES) + $(AR) rs $@ $^ + +backend_config_reader.o: backend_config_reader.cpp backend_config_reader.h + +core.o: core.cpp core.h core_exception.h job.h + +result.o: result.cpp result.h + +job.o: job.cpp job.h + +job_receiver.o: job_receiver.cpp job_receiver.h job.h + +core_config.o: core_config.cpp core_config.h core_exception.h + +states.o: states.cpp states.h + +xml_states.o: xml_states.cpp xml_states.h states.h + +xml_result.o: xml_result.cpp xml_result.h result.h + +clean: ../tools/add_endline.exe + for f in *.cpp *.h; do ../tools/add_endline.exe $$f; done; \ +rm -f *.o *~ core.a + +DLLS=/bin/cygexpat-0.dll /bin/cygwin1.dll + +install: + for dll in $(DLLS);do test -f $$dll && install $$dll $(PREFIX); done + +.cpp.o: + @echo " Compiling $<" + @$(CXX) -c $(CXXFLAGS) $(CXXCPPFLAGS) $< -o $@ diff --git a/core/backend_config_reader.cpp b/core/backend_config_reader.cpp new file mode 100644 index 0000000..47bcacf --- /dev/null +++ b/core/backend_config_reader.cpp @@ -0,0 +1,91 @@ +/* ************************************************************************** + + Author: Stefan Reutter + Created: August 2013 + +****************************************************************************/ + +#include + +#include "backend_config_reader.h" +namespace DAMARIS +{ +BackendConfigReader::BackendConfigReader(const gchar *configPath): + _damarisConfigPath(configPath), + _keyFile (NULL) +{ + + loadConfig(); + // if loadConfig succeeded, we should have a valid config file +} + +void BackendConfigReader::loadConfig() +{ + bool fileFound = false; + // check if user config file exists + const gchar* usr_config_dir = g_get_user_config_dir(); + int configPathLength = strlen(usr_config_dir) + strlen(_damarisConfigPath); + gchar *cfgFileName = new gchar[configPathLength > 1024 ? configPathLength: 1024]; + + strcpy(cfgFileName, usr_config_dir); + strcat(cfgFileName, _damarisConfigPath); + + _keyFile=g_key_file_new (); + GError *error=NULL; + if (!loadConfigFile(cfgFileName, G_KEY_FILE_NONE, error)) { + // if the user config doesn't exist, attempt to read from a system config + const gchar *const *sys_config_dirs = g_get_system_config_dirs(); + unsigned int i = 0; + while (true) + { + if (sys_config_dirs[i]==NULL) { break;} // no more paths to check + + // avoid buffer overrun + configPathLength = strlen(sys_config_dirs[i]) + strlen(_damarisConfigPath); + if (configPathLength > 1024) + { + gchar *temp = cfgFileName; + cfgFileName = new gchar[configPathLength]; + delete[] temp; + } + + + strcpy(cfgFileName, sys_config_dirs[i]); + strcat(cfgFileName, _damarisConfigPath); + + if (loadConfigFile(cfgFileName, G_KEY_FILE_NONE, error)) + { + // found a config file, breaking + fileFound = true; + break; + } + i++; + } + } else + { + fileFound = true; + } + delete[] cfgFileName; + + if (!fileFound) + { + throw(BackendConfigException("No config file found\n")); + } +} + +bool BackendConfigReader::loadConfigFile(const gchar *keyFileName, GKeyFileFlags flags, GError *error) +{ + if (!g_key_file_load_from_file (_keyFile, keyFileName, flags, &error)) { + if (error->code == G_FILE_ERROR_NOENT) // file not found + { + return false; + } else + { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "%s", error->message); + } + error = NULL; + } + return true; +} + +}// namespace DAMARIS diff --git a/core/backend_config_reader.h b/core/backend_config_reader.h new file mode 100644 index 0000000..b6f80e6 --- /dev/null +++ b/core/backend_config_reader.h @@ -0,0 +1,119 @@ +/* ************************************************************************** + + Author: Stefan Reutter + Created: August 2013 + +****************************************************************************/ +#ifndef DAMARIS_backend_config_reader_h +#define DAMARIS_backend_config_reader_h + + +#include +#include + +namespace DAMARIS +{ + +/** + * \defgroup BackendConfigReader Backend Config Reader + * \ingroup machines + * + * Allows reading in a backend configuration file implemented as a Glib Key-Value file + */ +/*@{*/ + +/** + * Exception class for the config file. + */ +class BackendConfigException : public std::runtime_error +{ +public: + explicit BackendConfigException(const std::string& msg) throw() : std::runtime_error(msg) {}; + explicit BackendConfigException(const char* msg) throw() : std::runtime_error(msg) {}; + virtual ~BackendConfigException() throw() {}; + +}; + +/** + * Class for reading a backend configuration file. Must be constructed with a c string providing the relative path to the config file. + * + * BackendConfigReader will first look in the XDG user config folder for the file, then in all system config folders. If no + * config file is found in any of these locations, a BackendConfigException will be thrown. + * + * Example: + * \code + * DAMARIS::BackendConfigReader cfgReader("/damaris/backend.conf"); + * std::cout << cfgReader.getInteger("ADC", "id") << "\n"; + * \endcode + * + */ +class BackendConfigReader +{ +public: + /** + * \param configPath C String containing the relative path to the config file + */ + BackendConfigReader(const gchar *configPath); + + ~BackendConfigReader() { g_key_file_free(_keyFile); }; // I hope this is exception safe + + /* -------------------------------------------------------------------- */ + /* The following functions are encapsulating the underlying GKeyFile functionality */ + + inline gboolean getBoolean(const gchar *groupName, const gchar *key) {GError* err = NULL; bool ret = g_key_file_get_boolean(_keyFile, groupName, key, &err); if (err) {g_error("GLib Error code %i, message: %s", err->code, err->message);} return ret;}; + inline gdouble getDouble(const gchar *groupName, const gchar *key) {GError* err = NULL; double ret = g_key_file_get_double(_keyFile, groupName, key, &err); if (err) {g_error("GLib Error code %i, message: %s", err->code, err->message);} return ret;}; + inline gint getInteger(const gchar *groupName, const gchar *key) {GError* err = NULL; int ret = g_key_file_get_integer(_keyFile, groupName, key, &err); if (err) {g_error("GLib Error code %i, message: %s", err->code, err->message);} return ret;}; + inline gchar* getValue(const gchar *groupName, const gchar *key) {GError* err = NULL; gchar* ret = g_key_file_get_value(_keyFile, groupName, key, &err); if (err) {g_error("GLib Error code %i, message: %s", err->code, err->message);} return ret;}; + inline gchar* getString(const gchar *groupName, const gchar *key) {GError* err = NULL; gchar* ret = g_key_file_get_string(_keyFile, groupName, key, &err); if (err) {g_error("GLib Error code %i, message: %s", err->code, err->message);} return ret;}; + + /** + * This allows you to pass a function pointer to BackendConfigReader in order to call one of the functions on GKeyFile that are not driectly provided. + * + * Because a function pointer needs to know the full signature, the function pointer must reflect this. + * + * Example usage: + * \code + * gchar* groupName; // initialized + * gchar* key; // initialized + * gint64 (*f)(GKeyFile*, const gchar *, const gchar *, GError*) = &g_key_file_get_int64; + * gint64 value = getSomething (f, groupName, key); + * \endcode + */ + template + returnType getSomething(returnType (*func)(GKeyFile*, const gchar *, const gchar *, GError*), const gchar *groupName, const gchar *key) + { + GError* err = NULL; + returnType ret = func(_keyFile, groupName, key, &err); + if (err) {g_error("GLib Error code %i, message: %s", err->code, err->message);} + return ret; + } + + /* -------------------------------------------------------------------- */ + +private: + + /** + * Attempt to load the defined relative config path. + * + * First, attempt to load the file from the user config dir. If that fails, attempt to load from the system config dir. If that fails as well, exit the program. + */ + void loadConfig(); + + /** + * Load a specific config file. Returns true on success, false on file not found. Throws an error otherwise. + */ + bool loadConfigFile(const gchar *keyFileName, GKeyFileFlags flags, GError *error); + + /** + * (Relative) Path to the damaris backend config + */ + const gchar *_damarisConfigPath; + + GKeyFile *_keyFile; +}; +/*@}*/ + + +} // namespace DAMARIS + +#endif // include guard diff --git a/core/constants.h b/core/constants.h new file mode 100644 index 0000000..816b21d --- /dev/null +++ b/core/constants.h @@ -0,0 +1,50 @@ +/* constants.h + * Author: Stefan Reutter (2011) + */ + +#ifndef CONSTANTS_H +#define CONSTANTS_H + + +/* Settings for the spectrum M2i ADC driver */ + +/** default impedance for the spectrum ADC in Ohm */ +const double ADC_M2I_DEFAULT_IMPEDANCE = 1e6; +/** 50 Ohm impedance is allowed as well */ +const double ADC_M2I_ALLOWED_IMPEDANCE = 50.0; +/** default offset */ +const int ADC_M2I_DEFAULT_OFFSET = 0; +/** default channel bitmask (2 first channels enabled) */ +const int ADC_M2I_DEFAULT_CHANNELS = 3; +/** default sensitivity in volt */ +const double ADC_M2I_DEFAULT_SENSITIVITY = 10.0; +/** default resolution in bits per sample */ +const int ADC_M2I_DEFAULT_RESOLUTION = 14; +/** allowed sensitivity settings */ +const double ADC_M2I_ALLOWED_SENSITIVITY[6] = {0.2, 0.5, 1, 2, 5, 10}; +const int ADC_M2I_ALLOWED_SENSITIVITY_LENGTH = 6; +/** size of the pre trigger, must be at least 4 and increase in steps of 4 */ +const int ADC_M2I_PRETRIGGER = 4; +/** size of the post trigger, must be at least 4 and increase in steps of 4 */ +const int ADC_M2I_POSTTRIGGER = 4; + +/* Settings for the spectrum MI ADC driver */ + +/** default impedance for the spectrum ADC in Ohm */ +const double ADC_MI_DEFAULT_IMPEDANCE = 1e6; +/** 50 Ohm impedance is allowed as well */ +const double ADC_MI_ALLOWED_IMPEDANCE = 50.0; +/** default offset */ +const int ADC_MI_DEFAULT_OFFSET = 0; +/** default channel bitmask (2 first channels enabled) */ +const int ADC_MI_DEFAULT_CHANNELS = 3; +/** default sensitivity in volt */ +const double ADC_MI_DEFAULT_SENSITIVITY = 10.0; +/** default resolution in bits per sample */ +const int ADC_MI_DEFAULT_RESOLUTION = 14; +/** allowed sensitivity settings */ +const double ADC_MI_ALLOWED_SENSITIVITY[6] = {0.2, 0.5, 1, 2, 5, 10}; +const int ADC_MI_ALLOWED_SENSITIVITY_LENGTH = 6; + +#endif /* CONSTANTS_H_ */ + diff --git a/core/core.cpp b/core/core.cpp new file mode 100644 index 0000000..9049c7c --- /dev/null +++ b/core/core.cpp @@ -0,0 +1,303 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + + ****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "job_receiver.h" +#include "xml_result.h" + +#ifndef SIZETPRINTFLETTER +# ifndef __LP64__ +# define SIZETPRINTFLETTER "u" +# else +# define SIZETPRINTFLETTER "lu" +# endif +#endif + +volatile int core::triggered_restart=0; +volatile int core::quit_signal=0; +volatile int core::term_signal=0; + +void restart_signal_handler(int signal) { + if (signal==SIGUSR1) + core::triggered_restart=1; + return; +} + +void quit_signal_handler(int signal) { + if (signal==SIGQUIT) + core::quit_signal=1; + return; +} + +void term_signal_handler(int signal) { + if (signal==SIGINT || signal==SIGTERM) { + core::term_signal=1; + } + return; +} + +core::core(const core_config& configuration) { + // time of start + if (-1==time(&start_time)) + { + throw core_exception("could not determine start time"); + } + + // store away cwd for later reset + _cwd = getcwd(NULL, 0); + if (_cwd == NULL) + { + throw core_exception("Getting the current working directory failed"); + } + + // cd to spool directory if necessary + if (!configuration.spool_directory.empty()) { + // change to given directory + if (0!=chdir(configuration.spool_directory.c_str())) { + throw core_exception(std::string("can not change to \"")+configuration.spool_directory+"\""); + } + } + char* spoolDir = getcwd(NULL, 0); + + the_configuration.spool_directory=spoolDir; + free(spoolDir); + + // allocate job parser + job_parser=new job_receiver(""); + + // reset counter + job_counter=0; + + if (configuration.job_filename_pattern.empty()) + the_configuration.job_filename_pattern="job.%09lu"; + else + the_configuration.job_filename_pattern=configuration.job_filename_pattern; + + if (configuration.result_filename_pattern.empty()) + the_configuration.result_filename_pattern=the_configuration.job_filename_pattern+".result"; + else + the_configuration.result_filename_pattern=configuration.result_filename_pattern; + + + if (configuration.job_poll_wait<0.001) + the_configuration.job_poll_wait=0.1; + else + the_configuration.job_poll_wait=configuration.job_poll_wait; + + // todo valid value?! + the_configuration.result_encoding=configuration.result_encoding; + // init hardware is done by derived class + the_hardware=NULL; + + // install signal handler to reset the job counter + signal(SIGUSR1,restart_signal_handler); + signal(SIGQUIT,quit_signal_handler); + signal(SIGTERM,term_signal_handler); + signal(SIGINT,term_signal_handler); + + quit_mainloop=0; +} + + +int core::write_state() const { + if (!core_name().empty()) + return core_state(*this).dump_state(core_name()+".state"); + else + return core_state(*this).dump_state("core.state"); +} + +int core::remove_state() const { + std::string state_filename; + if (core_name().empty()) + state_filename="core.state"; + else + state_filename=core_name()+".state"; + if (access(state_filename.c_str(),F_OK)!=0) + throw core_exception("the core state file vanished..."); + if (0!=remove(state_filename.c_str())) + throw core_exception(std::string("could not remove the core state file \"")+state_filename+"\""); + return 0; +} + + +int core::run() { + // writes the config and process id to file + write_state(); + + quit_mainloop=0; + + while (!quit_mainloop && term_signal==0 && quit_signal==0) { + // wait for a job to run... + job * this_job=NULL; + result* this_result=NULL; + try { + this_job=wait_for_input(); + } + catch (const job_exception& je) { + this_result=new error_result(job_counter,je); + } + + if (quit_mainloop!=0 || term_signal!=0) { + if (this_job!=NULL) delete this_job; + break; + } + // do the work ... + if (this_job!=NULL) { + this_result=do_the_job(*this_job); + if (term_signal!=0) { + delete this_job; + if (this_result!=NULL) delete this_result; + break; + } + } + else + this_job=new job(job_counter); + if (this_result==NULL) { + this_result=new error_result(job_counter,"unexpected: did not get any result"); + } + // tell them... + write_to_output(*this_job,*this_result); + delete this_result; + delete this_job; + job_counter++; + } + + // deletes config file + remove_state(); + + return 0; +} + +job* core::wait_for_input() { + if (triggered_restart!=0) { + job_counter=0; + triggered_restart=0; + } + if (quit_signal!=0 || term_signal!=0) { + quit_mainloop=1; + return (job*)NULL; + } + std::string job_filename; + struct stat job_stat; + // polling loop + while(1) { + if (job_filename.empty()) { + char* job_filename_buffer=(char*)malloc(the_configuration.job_filename_pattern.size()+100); + if (job_filename_buffer==NULL) + throw core_exception("could not allocate memory for job filename"); + snprintf(job_filename_buffer, + the_configuration.job_filename_pattern.size()+100, + the_configuration.job_filename_pattern.c_str(), + job_counter); + job_filename=job_filename_buffer; + free(job_filename_buffer); + } + int stat_success=stat(job_filename.c_str(), &job_stat); + if (0==stat_success && job_stat.st_size>0) { + int file_access=access(job_filename.c_str(),F_OK); + if (0==file_access) { + // job_reciever creates a job + job* new_job=job_parser->receive(job_filename); + if (new_job->no()!=job_counter) { + new_job->job_no=job_counter; + fprintf(stderr,"%s : corrected job number\n", job_filename.c_str()); + } + return new_job; + } + } + usleep((size_t)floor(the_configuration.job_poll_wait*1.0e6)); + // respect triggered restart of program + if (quit_signal!=0 || term_signal!=0) { + quit_mainloop=1; + break; + } + if (triggered_restart!=0) { + job_counter=0; + job_filename=""; + triggered_restart=0; + } + } + return (job*)NULL; +} + +result* core::do_the_job(job& the_job) { + // look for main loop commands, like quit, wait or nop + control* cjob=dynamic_cast(&the_job); + if (cjob!=NULL) { + return cjob->do_it(this); + + } + + // experiments + experiment* ejob=dynamic_cast(&the_job); + if (ejob!=NULL) { + result* r=ejob->do_it(the_hardware); + return r; + } + + // experiments + configuration* confjob=dynamic_cast(&the_job); + if (confjob!=NULL) { + result* r=confjob->do_it(the_hardware); + return r; + } + + + // execute everything else without parameter ... + return NULL; //the_job.do_it(); +} + +int core::write_to_output(const job& job_done, const result& result_gained){ + if (job_done.no()!=result_gained.no()) throw core_exception(std::string(__FUNCTION__)+": job and result number do not match"); + // create job file name + char* expansion_buffer=(char*)malloc(the_configuration.result_filename_pattern.size()+100); + if (expansion_buffer==NULL) + throw core_exception(std::string(__FUNCTION__)+": could not allocate expansion_buffer"); + snprintf(expansion_buffer,the_configuration.result_filename_pattern.size()+100,the_configuration.result_filename_pattern.c_str(),job_done.no()); + std::string resultfile=expansion_buffer; + free(expansion_buffer); + // remove old result file + if (unlink(resultfile.c_str())==-1 && errno!=ENOENT) { + throw core_exception(std::string(__FUNCTION__)+": could not remove existing old result file"); + } + if (term_signal!=0) return 0; + // write to temporary file + std::string tempfile=resultfile+".temp"; + if (unlink(tempfile.c_str())==-1 && errno!=ENOENT) { + throw core_exception(std::string(__FUNCTION__)+": could not remove existing temporary result file"); + } + if (term_signal!=0) return 0; + int write_result=xml_result_writer(the_configuration.result_encoding).write_to_file(tempfile,&result_gained); + if (term_signal!=0) { + unlink(tempfile.c_str()); + return 0; + } + // finally move temporary file to result file + if (0!=rename(tempfile.c_str(),resultfile.c_str())) + throw core_exception(std::string(__FUNCTION__)+": could not rename result-tempfile"); + return write_result; +} + +core::~core() { + // todo: send termination message + // release hardware + // reset cwd + chdir(_cwd); + if (_cwd != NULL) free(_cwd); + if (the_hardware!=NULL) delete the_hardware; + if (job_parser!=NULL) delete job_parser; +} diff --git a/core/core.h b/core/core.h new file mode 100644 index 0000000..e0df01d --- /dev/null +++ b/core/core.h @@ -0,0 +1,176 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + +****************************************************************************/ +#ifndef CORE_H +#define CORE_H + +#include "core/job.h" +#include "core/result.h" +#include "core/job_receiver.h" +#include "core/core_exception.h" +#include "core/core_config.h" +#include "machines/hardware.h" +#include +#include + +/** +\mainpage + +\section sec_introduction Introduction to DAMARIS backends + +DAMARIS stands for DArmstadt MAgnetic Resonance Instrument Software. There is a homepage avaialbe at http://www.fkp.physik.tu-darmstadt.de/damaris/ + +The project started as study for a common software base for different spectrometer hardware setups. + +The first time of this project was determined by +- the need of software for SpinCore and TiePie Hardware, +- and the wish for a "better" and unified software for our spectrometers. + +Next step is a functional raw spectrometer control for the mobile NMR spectrometer. Main contributors are Achim and Holger. + +\section sec_guide_doc Guide to the documentation +- For the user interface programers the modules about \link job jobs\endlink, \link result results\endlink + and their \link xmlstateinterface xml representation \endlink are useful. +- For the spectrometer maintainer the hardware interface and drivers section is recommended. Each spectrometer ends up + in \link machines machine specification\endlink. This program contains the main procedure. +- The system programer and the people who would like to see how everything is linked together, should visit the core class + implementation and the \link design design considerations\endlink. + +\section sec_source_code Source Code +The source code of this project is available from CVS at svn://element.fkp.physik.tu-darmstadt.de/share/SoftwareDevelopment/svnrepository/damaris and also accessible via a web frontend http://element.fkp.physik.tu-darmstadt.de/cgi-bin/viewcvs.cgi/damaris/ +*/ + +/** +\page design General Design Considerations + +The central ability of a spectormeter driver is to preform a single shot experiment. The selection of the +pulse sequence, the repitition and also the graphical monitoring is beyond the concept of a driver. +So the frontend-backend approach should be suitable. It is base of the picture: + +\image html software_design_overview_scaled.png + +To the left in grey some spectormeters are depicted. Each machine-driver can control one. This machine-driver shown to +the left in blue is called core. It recieves jobs in form of a file, processes the necessary informations and performs +an experiment. After receiving the data from an analog digital converter and waiting for the pulse driver to quit, the +next experiment is taken from a queue. + +On the right side some possible sources for these jobs are listed. This is the users side. In contrast to other +universal spectrometer control software, it is not intended to have only one program, that does everything. +One can identify different tasks in NMR programs: + +- The hardware drivers perform the experiment. +- The monitor functions provide a direct view on the data, to check, if the instrument is running properly. +- The data reduction unit processes data to get the essential measurement data. +- The evaluation routines derive the relevant physical quantity +- The measurement schedule tells the spectrometer, what to do next + +The programs, except the hardware driver, control the experiment without dealing with the details of spectrometer setup. +\todo Gating and dead time should be set by driver. Sofar the experiment file must contain these informations. + +The users side should be dominated by the users focus, monitoring single shots or scheduling long measurment sequences. + +*/ + + +/** works on jobs and gains results or errors */ +class core { + + public: + /** 1: if a fatal error occurs + or just a quit command + 0: go on + */ + int quit_mainloop; + + /** + */ + static volatile int triggered_restart; + + /** + */ + static volatile int quit_signal; + + /** + */ + static volatile int term_signal; + + /** + the path configuration.... + */ + core_config the_configuration; + + /** + the time of instantiation + */ + time_t start_time; + + /** + counts the recieved jobs + counting starts from 0 + */ + size_t job_counter; + + /** + keeps the parser on standby + */ + job_receiver* job_parser; + + /** + polls for the job files + */ + job* wait_for_input(); + + /** + do the experiment or some other things.... + */ + result* do_the_job(job& the_job); + + /** + tell the result by writing to another file + \exception core_exception if job no does not fit to result no + */ + int write_to_output(const job& job_done, const result& result_gained); + + /** + */ + hardware* the_hardware; + + public: + /** + setup the hardware and control structure for experiments + \param configuration an initial configuration, e.g. derived by parameter parsing + */ + core(const core_config& configuration); + + /** + writes the actual configuration to file when mainloop is started + */ + virtual int write_state() const; + + /** + */ + virtual int remove_state() const; + + /** + execute incomming instructions + */ + virtual int run(); + + /** + return the cores name + */ + virtual const std::string& core_name() const=0; + + /** + switch off hardware, end core + */ + virtual ~core(); + + char* _cwd; + +}; + +#endif diff --git a/core/core_config.cpp b/core/core_config.cpp new file mode 100644 index 0000000..857901e --- /dev/null +++ b/core/core_config.cpp @@ -0,0 +1,179 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + +****************************************************************************/ +#include "core_config.h" +#include "core_exception.h" +#include "xml_result.h" +#include "core.h" +#include +#include +#include +#include + + +core_config::core_config(const char** argv, int argc) { + spool_directory="."; + result_filename_pattern="job.%09lu.result"; + job_filename_pattern="job.%09lu"; + job_poll_wait=0.1; + result_encoding=xml_result_writer::base64; + // find spool directory argument + for (int i=1; i=argc || argv[i+1]==NULL) { + throw core_exception("spool directory not specified after --spool argument"); + } + ++i; + spool_directory=argv[i]; + } else if (argv[i][7]=='=') { + spool_directory=(argv[i]+8); + } else { + throw core_exception("spool directory not specified after --spool argument"); + } + } else { + fprintf(stderr, "ignoring argument %s\n", argv[i]); + } + } +} + +int core_config::xml_write_core_config_lines(FILE* f) const { + fprintf(f,"\n", spool_directory.c_str(), job_filename_pattern.c_str(), job_poll_wait); + std::string encoding_name; + switch (result_encoding) { + case xml_result_writer::defaultmode: + encoding_name="default"; break; + case xml_result_writer::separate_file: + encoding_name="separate_file"; break; + case xml_result_writer::base64: + encoding_name="base64"; break; + case xml_result_writer::ascii: + encoding_name="ascii"; break; + case xml_result_writer::csv: + encoding_name="csv"; break; + case xml_result_writer::hex: + encoding_name="hex"; break; + default: + encoding_name="unknown"; + } + fprintf(f,"\n", spool_directory.c_str(), result_filename_pattern.c_str(),encoding_name.c_str()); + return 0; +} + +int core_config::xml_read_core_config_startElement(const std::string& name, const xml_attrs& attrs) { + if (name=="jobs") { + xml_attrs::const_iterator spool_attr=attrs.find("spool"); + xml_attrs::const_iterator pattern_attr=attrs.find("pattern"); + xml_attrs::const_iterator polltime_attr=attrs.find("polltime"); + if (spool_attr!=attrs.end()) spool_directory=spool_attr->second; + if (pattern_attr!=attrs.end()) job_filename_pattern=pattern_attr->second; + char* polltime_char; + job_poll_wait=(size_t)floor(1e6*strtod(polltime_attr->second.c_str(),&polltime_char)); + if (polltime_char==polltime_attr->second.c_str()) + throw core_exception("polltime is no valid number"); + } + else if (name=="results") { + xml_attrs::const_iterator spool_attr=attrs.find("spool"); + xml_attrs::const_iterator pattern_attr=attrs.find("pattern"); + if (spool_attr!=attrs.end()) spool_directory=spool_attr->second; + if (pattern_attr!=attrs.end()) result_filename_pattern=pattern_attr->second; + /* todo: result encoding */ + } + return 0; +} + + +void xml_core_status_startelement_handler(core_state* cs, const char* name, char* const* attrs) { + size_t i; + xml_attrs the_attrs; + for (i=0;attrs[i]!=0;i+=2) { + the_attrs[attrs[i]]=attrs[i+1]; + } + cs->xml_read_core_state_startElement(std::string(name),the_attrs); +} + +int core_state::xml_read_core_state_startElement(const std::string& name, const xml_attrs& attrs) { + if (name=="state") { + xml_attrs::const_iterator pid_attr=attrs.find("pid"); + if (pid_attr==attrs.end()) throw core_exception("no pid attribute found"); + char* pid_char; + pid=strtol(pid_attr->second.c_str(),&pid_char,10); + if (pid_char==pid_attr->second.c_str()) throw core_exception("pid attribute did not contain a number"); + xml_attrs::const_iterator name_attr=attrs.find("name"); + if (name_attr!=attrs.end()) core_name=name_attr->second; + } + else + xml_read_core_config_startElement(name,attrs); + return 0; +} + +int core_state::dump_state(const std::string& statefilename) const { + // first delete old state file + int unlink_result=unlink(statefilename.c_str()); + if (unlink_result!=0 && errno!=ENOENT) { + throw core_exception("could not remove state file "+statefilename+"\""); + } + + //then try to write new state file + FILE* configfile=fopen(statefilename.c_str(),"w"); + if (configfile==0) throw core_exception("could not write core configuration file \""+statefilename+"\""); + fprintf(configfile,"\n"); + fprintf(configfile,"\n", core_name.c_str(), pid, start_time); + xml_write_core_config_lines(configfile); + fprintf(configfile,"\n"); + fclose(configfile); + return 0; +} + +core_state::core_state(const core& the_core): core_config(the_core.the_configuration) { + pid=getpid(); + core_name=the_core.core_name(); + start_time=the_core.start_time; +} + +core_state::core_state(const std::string& filename): core_config() { + + if (access(filename.c_str(),R_OK)!=0) + throw core_exception("could not open status file "+filename); + + FILE* configfile=fopen(filename.c_str(),"r"); + + /* parse document */ + XML_Parser p=XML_ParserCreate(NULL); + + /* set handler and user data*/ + XML_SetStartElementHandler(p,(XML_StartElementHandler)&xml_core_status_startelement_handler); + XML_SetUserData(p,(void*)this); + + /* read buffer */ + for (;;) { + const size_t BUFF_SIZE=4000; + int bytes_read; + void *buff = XML_GetBuffer(p,BUFF_SIZE); + if (buff == NULL) { + /* handle error */ + } + + bytes_read = fread(buff, 1, BUFF_SIZE, configfile); + if (bytes_read < 0) { + fclose(configfile); + XML_ParserFree(p); + throw core_exception("error while reading file "+filename); + } + + if (! XML_ParseBuffer(p, bytes_read, bytes_read == 0)) { + /* handle parse error */ + fclose(configfile); + XML_ParserFree(p); + throw core_exception("error while parsing file "+filename); + } + + if (bytes_read == 0) + break; + } + XML_ParserFree(p); + fclose(configfile); +} diff --git a/core/core_config.h b/core/core_config.h new file mode 100644 index 0000000..06a54a9 --- /dev/null +++ b/core/core_config.h @@ -0,0 +1,78 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + +****************************************************************************/ +#ifndef CORE_CONFIG_H +#define CORE_CONFIG_H + +#include +#include +#include +#include "core/xml_result.h" + +class core; + +/** + useful for xml tag parsing + */ +typedef std::map xml_attrs; + +/** + configuration variables influencing the runtime behaviour of core + */ +class core_config { + public: + /** + the directory for job and result data + + to be more precise: it is the current workdir of the core where state file goes in + */ + std::string spool_directory; + + /** + the pattern to create a result file, should contain %09lu printf token + */ + std::string result_filename_pattern; + + /** + name pattern to read jobs, should contain %09lu printf token + */ + std::string job_filename_pattern; + + /** + wait time in seconds until next read + */ + double job_poll_wait; + /** + encoding of result files + */ + xml_result_writer::data_save_mode_type result_encoding; + + int xml_write_core_config_lines(FILE* f) const; + int xml_read_core_config_startElement(const std::string& name, const xml_attrs& attrs); + core_config():spool_directory("."), result_filename_pattern("job.%09lu.result"), job_filename_pattern("job.%09lu"), job_poll_wait(0.1),result_encoding(xml_result_writer::base64) {} + core_config(const char** argv, int argc); + core_config(const core_config& c): spool_directory(c.spool_directory), + result_filename_pattern(c.result_filename_pattern), + job_filename_pattern(c.job_filename_pattern), + job_poll_wait(c.job_poll_wait), + result_encoding(c.result_encoding) + {} + core_config(const std::string& dir): spool_directory(dir), result_filename_pattern("job.%09lu.result"), job_filename_pattern("job.%09lu"), job_poll_wait(0.1), result_encoding(xml_result_writer::base64) {} +}; + +class core_state: public core_config { + public: + pid_t pid; + time_t start_time; + std::string core_name; + core_state(const core& the_core); + core_state(const std::string& filename); + int xml_read_core_state_startElement(const std::string& name, const std::map& attrs); + int dump_state(const std::string& filename) const; +}; + + +#endif diff --git a/core/core_exception.h b/core/core_exception.h new file mode 100644 index 0000000..a634ec3 --- /dev/null +++ b/core/core_exception.h @@ -0,0 +1,62 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + + ****************************************************************************/ +#ifndef CORE_EXCEPTION_H +#define CORE_EXCEPTION_H + +#include +#include + +/** + * Common base class for all DAMARIS exceptions. Note that a string error message must be provided upon initialization + */ +class DamarisException: public std::runtime_error +{ +public: + explicit DamarisException(const std::string& msg) throw (): std::runtime_error(msg) {} + explicit DamarisException(const char* msg) throw (): std::runtime_error(msg) {} + virtual ~DamarisException() throw () {} + + /** + * Override the what() function to automatically allow adding a prefix (simplifies catch statements) + */ + virtual const char* what() const throw () + { + std::string msg(std::runtime_error::what()); + msg.insert(0, prefix()); + return msg.c_str(); + } +protected: + virtual const std::string prefix() const { return "ERROR (DamarisException): "; } +}; + +/** + * Core-specific exception + */ +class core_exception: public DamarisException +{ +public: + explicit core_exception(const std::string& msg) throw (): DamarisException(msg) {} + explicit core_exception(const char* msg) throw (): DamarisException(msg) {} + virtual ~core_exception() throw () {} +protected: + virtual const std::string prefix() const { return "ERROR (core_exception): "; } +}; + +/** + * Recoverable exception + */ +class RecoverableException: public DamarisException +{ +public: + explicit RecoverableException(const std::string& msg) throw (): DamarisException(msg) {} + explicit RecoverableException(const char* msg) throw (): DamarisException(msg) {} + virtual ~RecoverableException() throw () {} +protected: + virtual const std::string prefix() const { return "ERROR (RecoverableException): "; } +}; + +#endif diff --git a/core/job.cpp b/core/job.cpp new file mode 100644 index 0000000..63444be --- /dev/null +++ b/core/job.cpp @@ -0,0 +1,525 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "core/job.h" +#include "core/core.h" +#include "core/stopwatch.h" +#include "core/xml_states.h" + +result* quit_job::do_it(core* c) { + c->quit_mainloop=1; + return new result(job_no); +} + +result* pause_job::do_it(core* c) { + /* care of SIGNALS */ + pause(); + c->triggered_restart=0; + return new result(job_no); +} + +result* restart_job::do_it(core* c) { + c->triggered_restart=1; + c->job_counter=0; + return new result(job_no); +} + +wait_job::wait_job(const size_t n, const XERCES_CPP_NAMESPACE_QUALIFIER DOMNamedNodeMap* attrs): control(n) { + XMLCh* attr_name=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("time"); + XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* length_attr=attrs->getNamedItem(attr_name); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&attr_name); + if (length_attr==NULL) { + attr_name=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("length"); + length_attr=attrs->getNamedItem((XMLCh*)"time"); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&attr_name); + if (length_attr==NULL) + throw job_exception("length or time attribute required"); + } + char* length_data=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(length_attr->getNodeValue()); + char* parse_end=NULL; + sec=strtod(length_data,&parse_end); + int length_check=strlen(length_data)-(parse_end-length_data); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&length_data); + if (length_check!=0 || sec<=0) + throw job_exception("wait: length attribute requires number>0"); +} + + +result* wait_job::do_it(core* c) { + struct timespec to_sleep; + to_sleep.tv_sec=(time_t)floor(sec); + to_sleep.tv_nsec=(long int)floor(1e9*(sec-to_sleep.tv_sec)); + while (c->term_signal==0) { + struct timespec remaining_time; + int result=nanosleep(&to_sleep,&remaining_time); + if (result==0) break; + to_sleep.tv_sec=remaining_time.tv_sec; + to_sleep.tv_nsec=remaining_time.tv_nsec; + } + /* care of SIGNALS */ + if (c->term_signal!=0) return NULL; + return new result(job_no); +} + +experiment::experiment(size_t n, XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* exp_data ): job(n){ + // create result-document + XMLCh* core_impl_name=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("core"); + XMLCh* doc_name=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("description"); + XERCES_CPP_NAMESPACE_QUALIFIER DOMImplementation* impl=XERCES_CPP_NAMESPACE_QUALIFIER DOMImplementationRegistry::getDOMImplementation(core_impl_name); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&core_impl_name); + XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* description_tags; + description_tags=impl->createDocument();//NULL, doc_name, NULL); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&doc_name); + + // get description by name + XMLCh* description_name=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("description"); + XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* one_child=exp_data->getFirstChild(); + + int found_description=0; + while (one_child!=NULL) { + XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* next_child=one_child->getNextSibling(); + if (one_child->getNodeType()==XERCES_CPP_NAMESPACE_QUALIFIER DOMNode::ELEMENT_NODE && + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::compareIString(one_child->getNodeName(),description_name)==0) { + // remove this one from list + if (found_description==0) { + XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* new_child=description_tags->importNode(one_child, 1); + description_tags->appendChild(new_child); + //XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* old_node=description->replaceChild(new_child, description->getDocumentElement()); + //old_node->release(); + found_description=1; + } + else { + fprintf(stderr,"experiment: found more than one description section, ignoring\n"); + } + exp_data->removeChild(one_child); + one_child->release(); + } + one_child=next_child; + } + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&description_name); + if (description_tags->getDocumentElement()!=NULL) { + XMLCh tempStr[100]; + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("LS", tempStr, 99); + XERCES_CPP_NAMESPACE_QUALIFIER DOMImplementation *impl2=XERCES_CPP_NAMESPACE_QUALIFIER DOMImplementationRegistry::getDOMImplementation(tempStr); + XERCES_CPP_NAMESPACE_QUALIFIER DOMWriter *theSerializer=((XERCES_CPP_NAMESPACE_QUALIFIER DOMImplementationLS*)impl2)->createDOMWriter(); + XERCES_CPP_NAMESPACE_QUALIFIER MemBufFormatTarget mem; + theSerializer->writeNode(&mem,*(description_tags->getDocumentElement())); + theSerializer->release(); + description=std::string((char*)mem.getRawBuffer(),mem.getLen()); + mem.reset(); + } + if (description_tags!=NULL) {description_tags->release();} + + // create state tree from rest of dom + one_child=exp_data->getFirstChild(); + std::list found_states; + while (one_child!=NULL) { + if (one_child->getNodeType()==XERCES_CPP_NAMESPACE_QUALIFIER DOMNode::ELEMENT_NODE) { + state_atom* new_one=state_factory(dynamic_cast(one_child)); + if (new_one!=NULL) { + + state* new_state=dynamic_cast(new_one); + + if (new_state!=NULL) { + found_states.push_back(new_state); + } + else { + fprintf(stderr,"experiment: did find something other than a state... forgetting\n"); + delete new_one; + } + } + } + one_child=one_child->getNextSibling(); + } + + // save found states... + if (found_states.empty()) { + experiment_states=NULL; + } + else if (found_states.size()==1) { + experiment_states=found_states.front(); + } + else { + state_sequent* new_sequent=new state_sequent; + new_sequent->repeat=1; + for (std::list::iterator i=found_states.begin();i!=found_states.end();++i) { + new_sequent->push_back(*i); + (**i).parent=new_sequent; + } + experiment_states=new_sequent; + } +} + +char* experiment::get_parameter(const XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* element, ...) { + if (element==NULL) return NULL; + va_list names; + va_start(names,element); + const char* attr_name=va_arg(names, const char*); + const XERCES_CPP_NAMESPACE_QUALIFIER DOMAttr* attr=NULL; + while (attr_name!=NULL) { + XMLCh* xml_name=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(attr_name); + attr=element->getAttributeNode(xml_name); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&xml_name); + if (attr!=NULL) break; + attr_name=va_arg(names, const char*); + } + va_end(names); + if (attr==NULL) return NULL; + return XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(attr->getValue()); +} + +state_atom* experiment::state_factory(const XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* element) { + if (element==NULL) return NULL; + + char* my_name=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(element->getNodeName()); + + if(strcasecmp(my_name,"state")==0) { + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&my_name); + // read parameters + char* length_string=get_parameter(element,"length","time","t","l",NULL); + if (length_string==NULL) throw job_exception("state requires length"); + char* length_string_end; + double length=strtod(length_string,&length_string_end); + int result=strlen(length_string)-(length_string_end-length_string); + // here, we could search for us, s or ms, d or min + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&length_string); + // but now we do not accept anything + if (result!=0) throw job_exception("state requires length as floating point value"); + if (length<0) throw job_exception("state's length must be non-negative"); + state* new_state=new state(length); + new_state->parent=NULL; + + // read states defintions + XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* one_child=element->getFirstChild(); + while (one_child!=NULL) { + if (one_child->getNodeType()==XERCES_CPP_NAMESPACE_QUALIFIER DOMNode::ELEMENT_NODE) { + state_atom* new_one=state_factory(dynamic_cast(one_child)); + if (new_one!=NULL) new_state->push_back(new_one); + } + one_child=one_child->getNextSibling(); + } + return new_state; + } + else if(strcasecmp(my_name,"sequent")==0) { + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&my_name); + // read parameters + size_t repeat=1; + // read parameters + char* repeat_string=get_parameter(element,"repeat","n",NULL); + if (repeat_string!=NULL) { + char* repeat_string_end; + double repeat_d=strtod(repeat_string,&repeat_string_end); + int result=strlen(repeat_string)-(repeat_string_end-repeat_string); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&repeat_string); + // but now we do not accept anything + if (result!=0 || repeat_d<0) throw job_exception("sequent requires loop count as a nonnegative integer value"); + repeat=(size_t)floor(repeat_d); + if (1.0*repeat-repeat_d!=0) fprintf(stderr,"rounding non integer towards lower integer"); + } + state_sequent* new_sequent=new state_sequent(repeat); + new_sequent->parent=NULL; + + // read substates and subsequences + XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* one_child=element->getFirstChild(); + while (one_child!=NULL) { + if (one_child->getNodeType()==XERCES_CPP_NAMESPACE_QUALIFIER DOMNode::ELEMENT_NODE) { + state_atom* new_one=state_factory(dynamic_cast(one_child)); + if (new_one!=NULL) { + state* new_state=dynamic_cast(new_one); + if (new_state!=NULL) { + new_state->parent=new_sequent; + new_sequent->push_back(new_one); + } + else { + fprintf(stderr,"experiment: found nonstate in sequent element\n"); + delete new_one; + } + } + } + one_child=one_child->getNextSibling(); + } + return new_sequent; + } + else if(strcasecmp(my_name,"ttlout")==0) { + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&my_name); + // read parameters + ttlout* ttls=new ttlout(); + char* id=get_parameter(element,"id","i",NULL); + if (id!=NULL) { + ttls->id=strtol(id,NULL,0); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&id); + } + char* value=get_parameter(element,"value",NULL); + if (value!=NULL) { + ttls->ttls=strtoul(value,NULL,0); + // todo: another error message... + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&value); + } + char* channel=get_parameter(element,"channel",NULL); + if (channel!=NULL) { + char* state_value=get_parameter(element,"state",NULL); + if (state_value!=NULL) { + size_t number=strtoul(channel,NULL,0); + char state_char=(state_value)[0]; + fprintf(stderr, "been here\n"); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&state_value); + if (state_char=='h' || state_char=='1') + ttls->ttls.set(number,1); + else + ttls->ttls.set(number,0); + } + else { + fprintf(stderr,"found invalid ttl state: ignoring\n"); + delete ttls; + ttls=NULL; + } + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&channel); + } + return (state_atom*)ttls; + } + else if(strcasecmp(my_name,"analogout")==0) { + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&my_name); + analogout* aout=new analogout(); + // read parameters + char* channel=get_parameter(element,"channel","c","id","i",(char*)NULL); + if (channel!=NULL) { + aout->id=strtoul(channel,NULL,0); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&channel); + } + char* frequency=get_parameter(element,"frequency","f",(char*)NULL); + if (frequency!=NULL) { + aout->frequency=strtod(frequency,NULL); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&frequency); + } + char* dac_value=get_parameter(element,"dac_value","d",(char*)NULL); + if (dac_value!=NULL) { + aout->dac_value=strtol(dac_value,NULL,0); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&dac_value); + } + + char* phase=get_parameter(element,"phase","p",(char*)NULL); + if (phase!=NULL) { + aout->phase=strtod(phase,NULL); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&phase); + } + char* amplitude=get_parameter(element,"amplitude","a",(char*)NULL); + if (amplitude!=NULL) { + aout->amplitude=strtod(amplitude,NULL); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&litude); + } + else + aout->amplitude=1.0; + return aout; + } + else if(strcasecmp(my_name,"analogin")==0) { + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&my_name); + analogin* ain=new analogin(); + // read parameters + char* id=get_parameter(element,"id","i",(char*)NULL); + if (id!=NULL) { + ain->id=strtoul(id,NULL,0); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&id); + } + char* frequency=get_parameter(element,"f","frequency",(char*)NULL); + if (frequency!=NULL) { + ain->sample_frequency=strtod(frequency,NULL); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&frequency); + if (ain->sample_frequency<0) { + delete ain; + throw job_exception("frequency must be non-negative"); + } + } + + char* samples=get_parameter(element,"s","samples",(char*)NULL); + if (samples!=NULL) { + char* samples_startpos=samples; + while (*samples_startpos!=0 && isspace(*samples_startpos)) ++samples_startpos; + if (*samples_startpos=='-') { + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&samples); + delete ain; + throw job_exception("number of samples must be non-negative"); + } + ain->samples=strtoul(samples,NULL,0); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&samples); + } + + char* channels=get_parameter(element,"c","channels",(char*)NULL); + if (channels!=NULL) { + ain->channels = channel_array(strtoul(channels,NULL,0)); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&channels); + ain->nchannels = ain->channels.count(); + } else { + ain->channels = channel_array(ADC_M2I_DEFAULT_CHANNELS); + ain->nchannels = ain->channels.count(); + } + + ain->sensitivity = new double[ain->nchannels]; // initialize sensitivity array + ain->offset = new int[ain->nchannels]; // initialize offset array (offset is in % of sensitivity) + ain->impedance = new double[ain->nchannels]; // initialize impedance array + // get parameters for each channel + for (int i = 0; i < ain->nchannels; i++) { + if (ain->channels[i] == true) { // check if channel bit is true + + /* get sensitivity */ + char buffer1[100]; + char buffer2[100]; + sprintf(buffer1, "sen%i", i); + sprintf(buffer2, "sensitivity%i", i); + char* sensitivity = get_parameter(element, buffer1, buffer2,(char*)NULL); + if (sensitivity!=NULL) { + ain->sensitivity[i] = strtod(sensitivity,NULL); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&sensitivity); + } else { + ain->sensitivity[i] = ADC_M2I_DEFAULT_SENSITIVITY; // set to default + } + + /* get offset */ + sprintf(buffer1, "offset%i", i); + char* offset = get_parameter(element, buffer1, (char*)NULL); + if (offset!=NULL) { + ain->offset[i] = strtol(offset, NULL, 0); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&offset); + } else { + ain->offset[i] = ADC_M2I_DEFAULT_OFFSET; // default is 0 + } + + /* get impedance */ + sprintf(buffer1, "impedance%i", i); + char* impedance = get_parameter(element, buffer1, (char*)NULL); + if (impedance!=NULL) { + ain->impedance[i] = strtoul(impedance, NULL, 0); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&impedance); + } else { + ain->impedance[i] = ADC_M2I_DEFAULT_IMPEDANCE; // default is 1 MOhm + } + } else { // channel is not enabled, set to defaults + ain->sensitivity[i] = ADC_M2I_DEFAULT_SENSITIVITY; + ain->offset[i] = ADC_M2I_DEFAULT_OFFSET; + ain->impedance[i] = ADC_M2I_DEFAULT_IMPEDANCE; + } + + } + + char* resolution=get_parameter(element,"r","res","resolution",(char*)NULL); + if (resolution!=NULL) { + ain->resolution=strtoul(resolution,NULL,0); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&resolution); + } + else + ain->resolution=ADC_M2I_DEFAULT_RESOLUTION; // was 12 + + return (state_atom*)ain; + } + + return NULL; +} + +result* experiment::do_it(hardware* hw) { + if (experiment_states==NULL) { + return new error_result(job_no,"did not find any states in job file"); + } + fprintf(stderr,"performing experiment job no %" SIZETPRINTFLETTER ", number of exps is %i\n",job_no, static_cast(experiment_states->size())); + result* data=hw->experiment(*experiment_states); + fprintf(stderr,"finished experiment job no %" SIZETPRINTFLETTER "\n\n",job_no); + if (data==NULL) + return new error_result(job_no,"did not get a result from method result* hardware::experiment(const state&) "); + data->description=description; + data->job_no=job_no; + + return data; +} + +configuration::configuration(size_t n, XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* conf_data ):job(n) { + XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* one_child=conf_data->getFirstChild(); + + while (one_child!=NULL) { + if (one_child->getNodeType()==XERCES_CPP_NAMESPACE_QUALIFIER DOMNode::ELEMENT_NODE) { + // found an element + configuration_device_section new_one; + // copy name + char* dev_name=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(one_child->getNodeName()); + new_one.name=dev_name; + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&dev_name); + + // copy attributes + XERCES_CPP_NAMESPACE_QUALIFIER DOMNamedNodeMap* dev_attribs=one_child->getAttributes(); + if (dev_attribs!=NULL) { + + XMLSize_t i=0; + while (1) { + XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* dev_attrib=dev_attribs->item(i); + if (dev_attrib==NULL) break; + char* dev_attrib_name=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(dev_attrib->getNodeName()); + char* dev_attrib_value=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(dev_attrib->getNodeValue()); + new_one.attributes[dev_attrib_name]=dev_attrib_value; + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&dev_attrib_name); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&dev_attrib_value); + dev_attrib->getNodeValue(); + ++i; + } + } + + // now copy string contents + new_one.data=""; + XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* child_child=one_child->getFirstChild(); + while (child_child!=NULL) { + if (child_child->getNodeType()==XERCES_CPP_NAMESPACE_QUALIFIER DOMNode::TEXT_NODE || + child_child->getNodeType()==XERCES_CPP_NAMESPACE_QUALIFIER DOMNode::CDATA_SECTION_NODE) { + char* text_data=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(child_child->getNodeValue()); + new_one.data.append(text_data); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&text_data); + } + child_child=child_child->getNextSibling(); + } + configuration_changes.push_back(new_one); + } + one_child=one_child->getNextSibling(); + } +} + +result* configuration::do_it(hardware* hw) { + result* res=hw->configure(configuration_changes); + configuration_result* cres=dynamic_cast(res); + if (cres!=NULL) + cres->job_no=job_no; + else { + configuration_results* cress=dynamic_cast(res); + if (cress!=NULL) { + cress->job_no=job_no; + for(configuration_results::iterator i=cress->begin(); i!=cress->end(); ++i) + (*i)->job_no=job_no; + } + } + return res; +} + +void configuration_device_section::print(FILE* f) const { + fprintf(f,"name: %s\n",name.c_str()); + for (std::map::const_iterator j=attributes.begin(); + j!=attributes.end(); + ++j) { + fprintf(f," %s: %s\n",j->first.c_str(),j->second.c_str()); + } // j + fprintf(f,"data: %s\n", data.c_str()); +} + +void configuration::print(FILE* f) const { + for (std::list::const_iterator i=configuration_changes.begin(); + i!=configuration_changes.end(); + ++i) { + i->print(f); + } // i +} diff --git a/core/job.h b/core/job.h new file mode 100644 index 0000000..359cb92 --- /dev/null +++ b/core/job.h @@ -0,0 +1,218 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + +****************************************************************************/ +#ifndef JOB_H +#define JOB_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "core/result.h" +#include "core/states.h" +#include "core/constants.h" + +#ifndef SIZETPRINTFLETTER +# ifndef __LP64__ +# define SIZETPRINTFLETTER "u" +# else +# define SIZETPRINTFLETTER "lu" +# endif +#endif + +class core; +/** + \defgroup jobs Jobs + \brief jobs to control core and make experiments + + the file format for a job file is simple xml: the element name of the root section is also the name of the job. + + \verbinclude experiment_job.xml + +*/ +//@{ + +/** + \brief exception for job related things + */ +class job_exception: public std::string { +public: + job_exception(const std::string& s): std::string(s){} +}; + +/** + \brief base class for a job that comes from input + + two groups of classe are available: + - core control jobs + - experiment jobs +*/ + +class job { + public: + + size_t job_no; + + job(const size_t n): job_no(n) { + } + + size_t no() const { + return job_no; + } + + virtual int print() const{ + printf("job no %" SIZETPRINTFLETTER "\n", job_no); + return 0; + } + + + virtual ~job() {} +}; + +/** + base class for core control + */ +class control: public job { + public: + control(size_t n):job(n) {} + virtual result* do_it(core* c)=0; +}; + +/** + end main loop + */ +class quit_job: public control { + public: + quit_job(const size_t n): control(n) {} + virtual result* do_it(core* c); +}; + +/** + just do nothing... + */ +class do_nothing_job: public control { + + public: + do_nothing_job(const size_t n): control(n) {} + + virtual result* do_it(core*) { + /* of course nothing to do...*/ + return new result(job_no); + } + +}; + +/** + wait a specified time period + */ +class wait_job: public control { + + public: + double sec; + + wait_job(const size_t n): control(n) { + sec=0; + } + wait_job(const size_t n, const XERCES_CPP_NAMESPACE_QUALIFIER DOMNamedNodeMap* attrs); + + wait_job(const size_t n, double sec_to_wait): control(n) { + sec=sec_to_wait; + } + + virtual result* do_it(core* c); + +}; + + +/** + infinite pause until a signal comes + */ +class pause_job: public control { + + public: + + pause_job(const size_t n): control(n) {} + virtual result* do_it(core* c); +}; + +/** + restarts the cores job queue + */ +class restart_job: public control { + + public: + + restart_job(const size_t n): control(n) {} + + virtual result* do_it(core* c); +}; + +class hardware; + +/** + base class for experiments + */ +class experiment: public job { + private: + state_atom* state_factory(const XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* element); + char* get_parameter(const XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* element, ...); + public: + /// holds the experiments states + state* experiment_states; + /// contains teh description of this experiment + std::string description; + + /// initialise the experiment data + experiment(size_t n, XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* exp_data=NULL ); + + virtual result* do_it(hardware* hw); + + virtual ~experiment() { + if (experiment_states!=NULL) delete experiment_states; + } +}; + + +class configuration_device_section { + public: + std::string name; + std::map attributes; + std::string data; + void print(FILE* f=stdout) const; +}; + +/** + a configuration job changes the instrument to another state, something like: + * temperature, + * sample position (lateral, axial), + * shim, tuning + these configuration changes generally do not occurr during a pulse sequence and are not very frequent. + */ +class configuration: public job { + public: + /* + suitable data model for each device + */ + + std::list configuration_changes; + + public: + configuration(size_t n, XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* conf_data=NULL ); + void print(FILE* f=stdout) const; + + result* do_it(hardware* hw); + +}; + + +//@} + +#endif diff --git a/core/job_receiver.cpp b/core/job_receiver.cpp new file mode 100644 index 0000000..b3e3b78 --- /dev/null +++ b/core/job_receiver.cpp @@ -0,0 +1,178 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + +****************************************************************************/ +#include "job_receiver.h" +#include +#include +#include +#include + +job_receiver::job_receiver(std::string the_jobfilenamepattern) { + try { + XERCES_CPP_NAMESPACE_QUALIFIER XMLPlatformUtils::Initialize(); + } + catch (const XERCES_CPP_NAMESPACE_QUALIFIER XMLException& toCatch) { + char* ini_error=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(toCatch.getMessage()); + job_exception new_exception(std::string("xerces initialisation error: ")+ini_error); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&ini_error); + throw new_exception; + } + + jobfilename=NULL; + setFilenamePattern(the_jobfilenamepattern); + parser=new XERCES_CPP_NAMESPACE_QUALIFIER XercesDOMParser(); + if (parser==NULL) { + delete jobfilename; + throw job_exception("could not allocate parser"); + } + parser->setValidationScheme(XERCES_CPP_NAMESPACE_QUALIFIER XercesDOMParser::Val_Always); + parser->setDoNamespaces(true); + errHandler = (XERCES_CPP_NAMESPACE_QUALIFIER ErrorHandler*) new XERCES_CPP_NAMESPACE_QUALIFIER HandlerBase(); + parser->setErrorHandler(errHandler); +} + +void job_receiver::setFilenamePattern(const std::string& filenamepattern) { + if (jobfilename!=NULL) delete jobfilename; + jobfilenamepattern=filenamepattern; + jobfilenamesize=filenamepattern.size()+20; + jobfilename=new char[jobfilenamesize]; + if (jobfilename==NULL) throw job_exception("could not allocate memory for filename"); +} + +job* job_receiver::receive(size_t no) { + snprintf(jobfilename,jobfilenamesize+20,jobfilenamepattern.c_str(),no); + job* new_job=receive(std::string(jobfilename)); + if (new_job->no()!=no) fprintf(stderr, "expected job number %" SIZETPRINTFLETTER " and specified number %" SIZETPRINTFLETTER " are different\n", no, new_job->no() ); + new_job->job_no=no; + return new_job; +} + +job* job_receiver::receive(const std::string& filename) { + + try { + parser->parse(filename.c_str()); + } + catch(const XERCES_CPP_NAMESPACE_QUALIFIER XMLException& toCatch) { + char* message = XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(toCatch.getMessage()); + job_exception je(std::string("XML error: ")+message); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&message); + throw je; + } + catch(const XERCES_CPP_NAMESPACE_QUALIFIER DOMException& toCatch) { + char* message = XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(toCatch.msg); + job_exception je(std::string("XML DOM error: ")+message); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&message); + throw je; + } + catch(const XERCES_CPP_NAMESPACE_QUALIFIER SAXParseException& toCatch) { + // more verbose for parser errors + char* message = XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(toCatch.getMessage()); + job_exception je(std::string("XML SAX Parser error: ")+message); + char location[100]; + snprintf(location,sizeof(location),", line %ld column %ld",toCatch.getLineNumber(),toCatch.getColumnNumber()); + je.append(location); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&message); + throw je; + } + catch(const XERCES_CPP_NAMESPACE_QUALIFIER SAXException& toCatch) { + char* message = XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(toCatch.getMessage()); + job_exception je(std::string("XML SAX error: ")+message); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&message); + throw je; + } + + // extract root element, root attributes and root name + XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* doc=parser->getDocument(); + if (doc==NULL) throw job_exception("xml job document not found"); + XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* rootelement=doc->getDocumentElement(); + if (rootelement==NULL) throw job_exception("xml job root document not found"); + char* docname=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(rootelement->getNodeName()); + XERCES_CPP_NAMESPACE_QUALIFIER DOMNamedNodeMap* rootattrs=rootelement->getAttributes(); + + // check the job number + XMLCh* docnoname=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("no"); + XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* jobno_attr=rootattrs->getNamedItem(docnoname); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&docnoname); + if (jobno_attr==NULL) { + docnoname=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("no"); + jobno_attr=rootattrs->getNamedItem(docnoname); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&docnoname); + } + size_t no=0; + if (jobno_attr==NULL) fprintf(stderr,"Warning: job %" SIZETPRINTFLETTER ": root element has no job number\n",no); + else { + char* docno=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(jobno_attr->getNodeValue()); + no=strtoul(docno,NULL,0); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&docno); + } + + job* this_job=NULL; + // determine the job type by name + if (strcasecmp(docname,"quit")==0) { + this_job=new quit_job(no); + } + else if (strcasecmp(docname,"nop")==0) { + this_job=new do_nothing_job(no); + } + else if (strcasecmp(docname,"pause")==0) { + this_job=new pause_job(no); + } /* pause */ + else if (strcasecmp(docname,"restart")==0) { + this_job=new restart_job(no); + } /* restart */ + else if (strcasecmp(docname,"wait")==0) { + this_job=new wait_job(no,rootattrs); + } /* wait */ + else if (strcasecmp(docname,"experiment")==0) { + try { + this_job=new experiment(no, rootelement); + } + catch (const XERCES_CPP_NAMESPACE_QUALIFIER DOMException& de) { + char* domerrmsg=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(de.msg); + char domerrno[5]; + snprintf(domerrno,5,"%d",de.code); + job_exception je("sorry, something happend while parsing experiment job: "); + je.append(domerrmsg); + je.append(", code "); + je.append(domerrno); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&domerrmsg); + // cleanup missing + throw je; + } + } + else if (strcasecmp(docname,"configuration")==0) { + try { + this_job=new configuration(no, rootelement); + } + catch (const XERCES_CPP_NAMESPACE_QUALIFIER DOMException& de) { + char* domerrmsg=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(de.msg); + char domerrno[5]; + snprintf(domerrno,5,"%d",de.code); + job_exception je("sorry, something happend while parsing configuration job: "); + je.append(domerrmsg); + je.append(", code "); + je.append(domerrno); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&domerrmsg); + // cleanup missing + throw je; + } + } + + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&docname); + parser->reset(); + parser->resetDocument(); + parser->resetDocumentPool(); + + return this_job; +} + + +job_receiver::~job_receiver() { + delete jobfilename; + delete errHandler; + delete parser; + XERCES_CPP_NAMESPACE_QUALIFIER XMLPlatformUtils::Terminate(); +} diff --git a/core/job_receiver.h b/core/job_receiver.h new file mode 100644 index 0000000..f55f60f --- /dev/null +++ b/core/job_receiver.h @@ -0,0 +1,53 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + +****************************************************************************/ +#ifndef JOB_RECEIVER_H +#define JOB_RECEIVER_H + +#include +#include "job.h" +#include +#include + + +/** + \ingroup jobs + can create jobs from xml content + */ +class job_receiver { + XERCES_CPP_NAMESPACE_QUALIFIER XercesDOMParser* parser; + XERCES_CPP_NAMESPACE_QUALIFIER ErrorHandler* errHandler; + + std::string jobfilenamepattern; + char* jobfilename; + size_t jobfilenamesize; + + public: + + /** + the receiver should know how to handle the job directory... + */ + job_receiver(const std::string jobfilenamepattern); + + void setFilenamePattern(const std::string& filenamepattern); + + /** + here, only the number should be given + */ + job* receive(const size_t no); + + /** + here, only the filename + */ + job* receive(const std::string& filename); + + /** + free everything + */ + ~job_receiver(); +}; + +#endif diff --git a/core/result.cpp b/core/result.cpp new file mode 100644 index 0000000..e25fea9 --- /dev/null +++ b/core/result.cpp @@ -0,0 +1,21 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + +****************************************************************************/ +#include +#include +#include +#include "result.h" + +configuration_result::configuration_result(size_t _no): result(_no) { + XMLCh* core_impl_name=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("core"); + XERCES_CPP_NAMESPACE_QUALIFIER DOMImplementation* impl=XERCES_CPP_NAMESPACE_QUALIFIER DOMImplementationRegistry::getDOMImplementation(core_impl_name); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&core_impl_name); + tag=impl->createDocument();//NULL, doc_name, NULL); +} + +configuration_result::~configuration_result() { + tag->release(); +} diff --git a/core/result.h b/core/result.h new file mode 100644 index 0000000..db1787c --- /dev/null +++ b/core/result.h @@ -0,0 +1,147 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + +****************************************************************************/ +#ifndef RESULT_H +#define RESULT_H + +#include +#include +#include + +/** + \defgroup results Results of jobs + \brief results are answers to jobs, success, data or errors + Since now only errors, success and adc results are implemented. Also the results are saved in xml. + + ToDo: externalise the xml read/write interface + */ +//@{ + +/** a result that goes to output */ +class result { + public: + /** the coresponding job number */ + size_t job_no; + + /** description, cited from job file */ + std::string description; + + /** jobs can only be instantiated with the number */ + result(size_t _no): job_no(_no) { + } + + size_t no() const { + return job_no; + } + + virtual ~result(){} +}; + + +/** + simple error message + */ +class error_result: public result { + public: + /** the error message should be human understandable */ + std::string error_message; + + /** + \brief save error message inside + + the errormessage should give a hint, what happened, not only which function found an error + */ + error_result(size_t _no, const std::string& s):result(_no) { + error_message=s; + } +}; + + +/** + \brief stores the data of ADC + */ +class adc_result: public result { + + public: + /** + the data: signed 16 bit values of channel 1 and 2 alternating + */ + short int* data; + + /** + samples per channel + */ + size_t samples; + + /** + sampling frequency for each channel + */ + double sampling_frequency; + + /** + number of channels + */ + int nchannels; + + /** + instantiation without data is possible + */ + adc_result(size_t _no, size_t s=0, short int* d=NULL, double freq=0, int nchan=2): result(_no) { + data=d; + samples=s; + sampling_frequency=freq; + nchannels = nchan; + } + + /** + free the data arrays + */ + virtual ~adc_result() { + if (data!=NULL) free(data); + } +}; + +/** + + */ +class configuration_result: public result { + public: + XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* tag; + configuration_result(size_t _no); + + ~configuration_result(); +}; + + +class configuration_results: public std::list, public result { + public: + configuration_results(size_t _no): result(_no) {} + virtual ~configuration_results() { + while (!empty()) { + delete back(); + pop_back(); + } + } + +}; + + +/** + */ +class adc_results: public std::list, public result { + public: + adc_results(size_t _no): result(_no) {} + virtual ~adc_results() { + while (!empty()) { + delete back(); + pop_back(); + } + } +}; + +//@} + +#endif diff --git a/core/states.cpp b/core/states.cpp new file mode 100644 index 0000000..049aa58 --- /dev/null +++ b/core/states.cpp @@ -0,0 +1,111 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + +****************************************************************************/ +#include "states.h" +#include "xml_states.h" + +state* state_parallel::copy_flat(size_t enroll) const { + return copy_new(); +} + +state* state_sequent::copy_flat(size_t max_enroll_loop) const { + state_sequent* flat=new state_sequent; + flat->repeat=repeat; + if (flat->repeat==0) + return flat; + + // start copying everything... + for (const_iterator i=begin(); i!=end(); ++i) { + const state* this_state=dynamic_cast(*i); + // make the state flat, if sequence is no loop + if (this_state!=NULL) { + state* new_flat=this_state->copy_flat(max_enroll_loop); + state_sequent* ss=dynamic_cast(new_flat); + // handle the sequence + if (ss!=NULL) { + // skip this part + if (ss->repeat==0) { + delete ss; + continue; + } + // do not use extra sequent section + if (ss->repeat==1) { + while (!ss->empty()) { + flat->push_back(ss->front()); + ss->pop_front(); + } + delete ss; + continue; + } + flat->push_back(new_flat); + continue; + } + state_parallel* sp=dynamic_cast(new_flat); + if (sp!=NULL) { + flat->push_back(new_flat); + continue; + } + // if this is a state, check length... + if (new_flat->length==0.0) + delete new_flat; + else + flat->push_back(new_flat); + continue; + } + // simply append other things + flat->push_back((**i).copy_new()); + } // for + + // now enrol own loop + if (repeat>1 && repeat<=max_enroll_loop) { + flat->repeat=1; + std::list orig_list(*flat); + for (size_t loop=1;loopinsert(flat->end(),orig_list.begin(),orig_list.end()); + } + + return flat; +} + +state* state_iterator::get_state() { + if (subsequence_stack.empty()) return NULL; // at the end of traversal + // find a state or a subsequence in actual subsequence + state* next_one=NULL; + for (state_sequent::const_iterator i=subsequence_stack.back().subsequence_pos; + i!=subsequence_stack.back().subsequence->end(); + ++i) { + next_one=dynamic_cast(*i); + if (next_one!=NULL) break; + } + + // this subsequence is finished, so go on with the upper one + if (next_one==NULL) { + double substate_time=subsequence_stack.back().elapsed_time*subsequence_stack.back().subsequence->repeat; + subsequence_stack.pop_back(); + if (subsequence_stack.empty()) { + total_time=substate_time; + return NULL; + } + subsequence_stack.back().elapsed_time+=substate_time; + ++subsequence_stack.back().subsequence_pos; + return get_state(); + } + + // find out, if this is a subsequence + state_sequent* next_sequent=dynamic_cast(next_one); + if (next_sequent!=NULL) { + if (next_sequent->repeat!=0) { + subsequence_iterator next_level={next_sequent,next_sequent->begin(),0}; + subsequence_stack.push_back(next_level); + } + else { + ++(subsequence_stack.back().subsequence_pos); + } + return get_state(); + } + // this is a state + return next_one; +} diff --git a/core/states.h b/core/states.h new file mode 100644 index 0000000..e38c106 --- /dev/null +++ b/core/states.h @@ -0,0 +1,375 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + +****************************************************************************/ +#ifndef STATES_H +#define STATES_H + +#include +#include + +/** \defgroup states Devices States + \brief different states of devices are defined + + Pulse devices are driven by \link state_sequent sequences\endlink of \link state states \endlink, + that can be nested and repeated. + So an appropriate data structure is a tree of sequences, that can contain states or sequences. + Each sequence can be repeated. States have a defined time length and define the state of all + outputs of the pulse device once. So the length of a sequence is the sum of all state durations + and the length of the nested sequences. If the sequence is repeated, the duration has to be + multiplicated with the numbers of loops. + \n + ToDo: State sections that allow parallel sequences inside are an intresting extension of this concept + + Inside the \link state states \endlink, the state_atom objects define the state. There are several classes + derived from state_atom . + \n + ToDo: We could define a default state, that is used as template for the states in the sequences. + + This definition results in a tree structure, that is traversed "depth-first". A class for that traversal + is the state_iterator . + + */ +//@{ + +/** + \brief describes the state of one device + the duration of the state is given by the surrounding state section +*/ +class state_atom { + public: + /** no time, no repetiton */ + virtual state_atom* copy_new() const=0; + + virtual ~state_atom() {} +}; + +//@} + +/** + \defgroup derived_stateatom several derived state_atom classes + \ingroup states + */ +//@{ + +/** + channels of a multichannel device +*/ +typedef std::bitset<32> channel_array; + +/** + status of ttl lines +*/ +class ttlout: public state_atom { + public: + /** some reference to the device */ + int id; + /** the ttl levels */ + channel_array ttls; + + /** default: all off */ + ttlout() { + id=0; + ttls=0; + } + + ttlout(const ttlout& orig) { + id=orig.id; + ttls=orig.ttls; + } + + virtual state_atom* copy_new() const { + return new ttlout(*this); + } + + virtual ~ttlout() {} +}; + +/** + definition of analog output +*/ +class analogout: public state_atom { + public: + /** some reference to the device */ + int id; + /** amplitude voltage in V, as fallback 0 means off, !=0 means on */ + double amplitude; + /** phase in degree */ + double phase; + /** frequency in Hz*/ + double frequency; + signed dac_value; + /** default: all of */ + analogout() { + id=0; + amplitude=0.0; + phase=0.0; + frequency=0.0; + dac_value=0; + } + + analogout(const analogout& orig) { + id=orig.id; + amplitude=orig.amplitude; + phase=orig.phase; + frequency=orig.frequency; + dac_value=orig.dac_value; + } + + virtual state_atom* copy_new() const { + return new analogout(*this); + } + + virtual ~analogout() {} +}; + + +/** + definition of a analog input + */ +class analogin: public state_atom { + public: + /** a reference to the device */ + int id; + /** sample frequency */ + double sample_frequency; + /** samples each channel */ + size_t samples; + /** which channels to record */ + channel_array channels; + /** the number of channels */ + int nchannels; + + /** sensitivity in volts for each channel */ + double* sensitivity; + + /** impedance in Ohm for each channel */ + double* impedance; + + /** offset in % of sensitivity for each channel */ + int* offset; + + /** resolution in bit per sample */ + size_t resolution; + + /** default: do nothing */ + analogin() { + id=0; + sample_frequency=0; + samples=0; + resolution=0; + nchannels = 0; + channels = channel_array(0); + } + + analogin(const analogin& orig) { + id=orig.id; + sample_frequency=orig.sample_frequency; + samples=orig.samples; + sensitivity=orig.sensitivity; + resolution=orig.resolution; + offset = orig.offset; + impedance = orig.impedance; + channels = orig.channels; + nchannels = orig.nchannels; + } + + virtual state_atom* copy_new() const { + return new analogin(*this); + } + + virtual ~analogin() {} + +}; + +//@} + +/** + \ingroup states + */ +//@{ + +class state: public state_atom, public std::list { + public: + /** \brief how long in seconds this state should be effective */ + double length; + + /** \brief points to parent state + + the root document has NULL, no cyclic references allowed + */ + const state* parent; + + /** + initializes an empty state + */ + state(double _length, const state* my_parent=NULL): state_atom(), std::list(), length(_length), parent(my_parent) { + } + + state(const state& orig): state_atom(), std::list(), length(orig.length), parent(orig.parent) { + for (state::const_iterator i=orig.begin(); i!=orig.end(); i++) + push_back((**i).copy_new()); + } + + virtual state_atom* copy_new() const { + return new state(*this); + } + + virtual state* copy_flat(size_t enroll = 1) const { + (void) enroll; // get rid of compiler unused parameter warning + return new state(*this); + } + + virtual ~state() { + while (!empty()) { + if (back()!=NULL) delete (back()); + pop_back(); + } + } + +}; + +class state_parallel: public state { + public: + /** + possible time alignments + */ + typedef enum {begin_align,end_align,center_align} states_alignment; + + /** + define time allignments + */ + states_alignment align; + + state_parallel(const state* my_parent=NULL, states_alignment _a=begin_align): state(0.0,my_parent), align(_a) { + } + + state_parallel(const state_parallel& orig): state(orig) { + align=orig.align; + } + + virtual state* copy_new() const { + return new state_parallel(*this); + } + + virtual state* copy_flat(size_t enroll=4) const; + + virtual ~state_parallel() {} + +}; + +/** + \brief sequential states with loops + */ +class state_sequent: public state { + public: + /** \brief how often this state or state sequence should be repeated + */ + size_t repeat; + + state_sequent(size_t _repeat=1, const state* my_parent=NULL): state(0.0,my_parent), repeat(_repeat) { + } + + state_sequent(const state_sequent& orig): state(orig) { + repeat=orig.repeat; + } + + virtual state* copy_new() const { + return new state_sequent(*this); + } + + virtual state* copy_flat(size_t enroll=4) const; + + virtual ~state_sequent() { + while (!empty()) { + if (back()!=NULL) delete (back()); + pop_back(); + } + } +}; + +/** + \brief traverses the state tree from state to state + this iterator leaves out the subsequence borders, but returns correct informations about time of the first + traversal of a state and the total count of traversals. + this is a nonconstant iterator, we have to write a const-iterator... +*/ + +class state_iterator { + public: + typedef struct { + state_sequent* subsequence; + state_sequent::iterator subsequence_pos; + /// time in seconds in a substate until iterator position + double elapsed_time; + } subsequence_iterator; + + std::list subsequence_stack; + /// time in seconds till iterator position for first loop run + double total_time; + + /** + start iteration at the beginning of a sequence + */ + state_iterator(state_sequent& subsequence) { + subsequence_iterator the_beginning={&subsequence,subsequence.begin(),0}; + subsequence_stack.push_back(the_beginning); + total_time=0.0; + (void)get_state(); + } + + /** + time of end of first traversal + */ + double get_time() const { + if (subsequence_stack.empty()) return total_time; + double time=0.0; + for (std::list::const_iterator i=subsequence_stack.begin(); + i!=subsequence_stack.end(); + ++i) + time+=i->elapsed_time; + return time; + } + + /** + get count of traversals of this state + */ + size_t get_count() const { + size_t count=1; + for (std::list::const_iterator i=subsequence_stack.begin(); + i!=subsequence_stack.end(); + ++i) + count*=i->subsequence->repeat; + return count; + } + + /** + advance to next state + \return a pointer to next state or NULL if no state was following + */ + state* next_state() { + if (subsequence_stack.empty()) return NULL; + subsequence_stack.back().elapsed_time+=get_state()->length; + ++subsequence_stack.back().subsequence_pos; + return get_state(); + } + + /** + return true, if iterator is at end + */ + int is_last() const { + return subsequence_stack.empty(); + } + + /** + get a pointer of this state + if the iterator is actually not on a state, it will go to the next (traverse loops and sections) + \return a pointer to the state or NULL, if no state available + */ + state* get_state(); +}; + +/*@}*/ +#endif diff --git a/core/stopwatch.h b/core/stopwatch.h new file mode 100644 index 0000000..26f5f68 --- /dev/null +++ b/core/stopwatch.h @@ -0,0 +1,129 @@ +#ifndef STOPWATCH_H +#define STOPWATCH_H + +#include +#include +#include + +/** + \brief timing utility + This class can take times, up to microseconds exact + */ + +class stopwatch { + /** + time at last start call + */ + timeval starttime; + + /** + time at last stop call + */ + timeval stoptime; + + /** + to accumulate time intervals, previous interval seconds are saved + */ + long int offset_sec; + /** + to accumulate time intervals, previous interval microseconds are saved + */ + long int offset_usec; + + /** + shift elapsed time to offset register and save new start time + */ + void elapsed_save() const { + /* if timer is already stoped, do nothing */ + if (timerisset(&starttime)) { + stopwatch* noconst_this=(stopwatch*)this; + gettimeofday(&(noconst_this->stoptime),NULL); + /* elapsed time added to internal pointer */ + noconst_this->offset_sec+=(stoptime.tv_sec-starttime.tv_sec); + noconst_this->offset_usec+=(stoptime.tv_usec-starttime.tv_usec); + noconst_this->starttime.tv_sec=stoptime.tv_sec; + noconst_this->starttime.tv_usec=stoptime.tv_usec; + while (offset_usec<0) { + noconst_this->offset_usec+=1000000; + noconst_this->offset_sec-=1; + } + while (offset_usec>=1000000) { + noconst_this->offset_usec-=1000000; + noconst_this->offset_sec+=1; + } + } + } + + public: + + /** + creates stopwatch, stopped state, 0 time offset + */ + stopwatch() { + timerclear(&starttime); + offset_sec=offset_usec=0; + } + + /** + creates stopwatch, stopped state, with time offset from double argument + */ + stopwatch(const double& offset) { + timerclear(&starttime); + offset_sec=(long int)floor(offset); + offset_usec=(long int)(1.0e6*(offset-offset_sec)); + } + + /** + creates stopwatch, stopped state, with time offset in seconds and microseconds + */ + stopwatch(const long int& _offset_sec, const long int& _offset_usec) { + timerclear(&starttime); + offset_sec=_offset_sec; + offset_usec=_offset_usec; + } + + stopwatch(const stopwatch& orig) { + starttime.tv_sec=orig.starttime.tv_sec; + starttime.tv_usec=orig.starttime.tv_usec; + offset_sec=orig.offset_sec; + offset_usec=orig.offset_usec; + } + + /** + set timer to zero and start measurement + */ + inline void start() { + gettimeofday(&starttime,NULL); + offset_sec=offset_usec=0; + } + + /** + stop or pause time measurement + */ + inline void stop() { + elapsed_save(); + /* and clear this pointer */ + timerclear(&starttime); + } + + /** + continue time measurement + */ + inline void cont() { + if (!timerisset(&starttime)) gettimeofday(&starttime,NULL); + } + + inline double elapsed() const { + elapsed_save(); + return (1.0e-6*(double)offset_usec)+(double)offset_sec; + } + + inline void elapsed(long int& sec, long int& usec) const { + elapsed_save(); + sec=offset_sec; + usec=offset_usec; + } + +}; + +#endif /* STOPWATCH_H */ diff --git a/core/xml_result.cpp b/core/xml_result.cpp new file mode 100644 index 0000000..d7bd02b --- /dev/null +++ b/core/xml_result.cpp @@ -0,0 +1,616 @@ +#include "xml_result.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* *************************************************************************************************** + +here starts the reader implementation + +*****************************************************************************************************/ +#ifndef SIZETPRINTFLETTER +# ifndef __LP64__ +# define SIZETPRINTFLETTER "u" +# else +# define SIZETPRINTFLETTER "lu" +# endif +#endif + +void start_element_stub(void* userdata,const XML_Char* name, const XML_Char** attrs ); +void end_element_stub(void* userdata,const XML_Char* name); +void data_stub(void* userdata, const XML_Char* data, int len); + +/** + table for translation base64 -> 0 to 63 +*/ +static char base64_table[] = { + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, + 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, + 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, + -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, + 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 +}; + + +class xml_result_handler { + /** + */ + int alloc_stepsize; + /** + maximum allocated size of buffer list + */ + size_t bufferno_max; + /** + actual or new sample buffer + */ + size_t bufferno; + /** + buffers + */ + short int** buffers; + /** + number of samples (2 values for one sample) in each buffer, if actual buffer, this is the expected number + */ + size_t* sampleno; + /** + */ + double* frequencies; + /** + maximum allocated size of last sample buffer in bytes + */ + size_t bufferlength; + /** + position of in actual sample buffer + */ + size_t bufferpos; + /** + base64 byte buffer + */ + char base64buffer[4]; + /** + the length of content in base64buffer + */ + int base64buffer_length; + /** + 1 if we are inside a adcdata section, 2 if we are saving a description + */ + int section_state; + /** + error messages, that are found + */ + std::string error_message; + /** + descripton sections + */ + std::string description_text; + +public: + xml_result_handler(XML_Parser p) { + /* set event handlers */ + alloc_stepsize=1<<10; + XML_SetElementHandler(p,start_element_stub,end_element_stub); + XML_SetCharacterDataHandler(p,data_stub); + XML_SetUserData(p,this); + + /* allocate some buffers */ + sampleno=(size_t*)malloc(alloc_stepsize*sizeof(size_t)); + buffers=(short int**)malloc(alloc_stepsize*sizeof(short int*)); + frequencies=(double*)malloc(alloc_stepsize*sizeof(double)); + if (sampleno==NULL || buffers==NULL) { + if (sampleno!=NULL) free(sampleno); + if (buffers!=NULL) free(buffers); + if (frequencies!=NULL) free(frequencies); + /* todo throw something */ + return; + } + bufferno=0; + buffers[0]=NULL; + bufferno_max=alloc_stepsize; + section_state=0; + } + + void start_element(const XML_Char* name, const XML_Char** attrs) { + if (strcmp(name,"adcdata")==0) { + /* is there enough space in array? */ + if (bufferno>=bufferno_max) { + bufferno_max+=alloc_stepsize; + buffers=(short int**)realloc((void*)buffers,bufferno_max*sizeof(short int*)); + sampleno=(size_t*)realloc((void*)sampleno,bufferno_max*sizeof(size_t)); + frequencies=(double*)realloc((void*)frequencies,bufferno_max*sizeof(double)); + if (buffers==NULL || sampleno==NULL || frequencies==NULL) fprintf(stderr,"could not reallocate the buffer"); + } + /* todo read sample no*/ + const XML_Char** pos=attrs; + while (*pos!=NULL) { + if (strcmp(*pos,"samples")==0 || strcmp(*pos,"s")==0) { + sampleno[bufferno]=strtoul(*(pos+1),NULL,0); + } + else if (strcmp(*pos,"frequency")==0 || strcmp(*pos,"f")==0) { + frequencies[bufferno]=strtod(*(pos+1),NULL); + } + pos+=2; + } + if (sampleno[bufferno]!=0) { + bufferlength=sampleno[bufferno]*2*sizeof(short int); + /* fresh buffer */ + buffers[bufferno]=(short int*)malloc(bufferlength); + } + else { + bufferlength=0; + buffers[bufferno]=NULL; + } + bufferpos=0; + /* todo check result */ + base64buffer_length=0; + section_state=1; + } + if (strcmp(name,"description")) { + section_state=3; + } + if (strcmp(name,"error")==0) { + section_state=2; + } + if (section_state==3) { + description_text+="<"; + description_text+=name; + const XML_Char** attr=attrs; + while(*attr!=NULL) { + description_text+=" "; + description_text+=*(attr++); + description_text+="='"; + description_text+=*(attr++); + description_text+="'"; + } + description_text+=">"; + } + } + + void end_element(const XML_Char* name) { + if (strcmp(name,"adcdata")==0) { + /* empty base64 buffer */ + if (bufferpos+base64buffer_length-1>=bufferlength) { + bufferlength+=alloc_stepsize; + buffers[bufferno]=(short int*)realloc(buffers[bufferno],bufferlength); + /* todo reallocate check */ + } + + if (base64buffer_length>1) { + unsigned char s=base64buffer[0]; + s<<=2; + s|=(base64buffer[1]>>4); + ((char*)buffers[bufferno])[bufferpos]=s; + bufferpos++; + if (base64buffer_length>2) { + s=base64buffer[1]&15; + s<<=4; + s|=(base64buffer[2]>>2); + ((char*)buffers[bufferno])[bufferpos]=s; + bufferpos++; + if (base64buffer_length>3) { + s=base64buffer[2]&3; + s<<=6; + s|=base64buffer[3]; + ((char*)buffers[bufferno])[bufferpos]=s; + bufferpos++; + } + } + } + /* check right sample number */ + if (bufferpos!=sampleno[bufferno]*2*sizeof(short int)) { + sampleno[bufferno]=bufferpos/(2*sizeof(short int)); + } + /* prepare for next buffer */ + bufferlength=0; + bufferno++; + section_state=0; + } + if (strcmp(name,"error")==0) { + if (!error_message.empty() && error_message[error_message.size()-1]!='\n') { + error_message.push_back('\n'); + } + section_state=0; + } + if (section_state==3) { + description_text+="<"; + description_text+=name; + description_text+="/>"; + } + if (strcmp(name,"description")) { + section_state=0; + } + } + + void data(const XML_Char* the_data, int len) { + if (section_state==1) { + int pos=0; + while (pos=bufferlength) { + /* to do reallocate */ + bufferlength+=alloc_stepsize; + buffers[bufferno]=(short int*)realloc(buffers[bufferno],bufferlength); + if (buffers[bufferno]==NULL) fprintf(stderr,"could not enlarge the sample buffer\n"); + } + /* read three bytes from four letters */ + unsigned char s=base64buffer[0]; + s<<=2; + s|=(base64buffer[1]>>4); + ((char*)buffers[bufferno])[bufferpos]=s; + bufferpos++; + s=base64buffer[1]&15; + s<<=4; + s|=(base64buffer[2]>>2); + ((char*)buffers[bufferno])[bufferpos]=s; + bufferpos++; + s=base64buffer[2]&3; + s<<=6; + s|=base64buffer[3]; + ((char*)buffers[bufferno])[bufferpos]=s; + bufferpos++; + base64buffer_length=0; + } + } + } + else if (section_state==3) { + description_text+=the_data; + } + else if (section_state==2) { + error_message+=the_data; + } + } + + ~xml_result_handler() { + if (sampleno!=NULL && buffers!=NULL) { + size_t pos=0; + while (pos<=bufferno) { + if (buffers[pos]!=NULL) free(buffers[pos]); + pos++; + } + } + if (sampleno!=NULL) free(sampleno); + if (buffers!=NULL) free(buffers); + } + + + /** + print number of samples and frequency of the obtained data + */ + void print_info(FILE* f) { + if (buffers==NULL || sampleno==NULL) { + fprintf(f,"nothing saved\n"); + return; + } + for (size_t i=0; istart_element(name,attrs); +} + +void end_element_stub(void* userdata,const XML_Char* name) { + ((xml_result_handler*)userdata)->end_element(name); +} + +void data_stub(void* userdata, const XML_Char* data, int len ) { + ((xml_result_handler*)userdata)->data(data,len); +} + + +xml_result_reader::xml_result_reader() { + jobfilenamepattern=""; +} + +xml_result_reader::~xml_result_reader() { +} + +xml_result_reader::xml_result_reader(const std::string& jobpattern) { + jobfilenamepattern=jobpattern; +} + +/* ToDo error handling */ +result* xml_result_reader::read(size_t no) { + if (jobfilenamepattern=="") return NULL; + char* tmp_name=(char*)malloc(jobfilenamepattern.size()+50); + if (tmp_name==NULL) return NULL; + snprintf(tmp_name,jobfilenamepattern.size()+50,jobfilenamepattern.c_str(),no); + result* new_result=NULL; + new_result=read(std::string(tmp_name)); + free(tmp_name); + return new_result; +} + +result* xml_result_reader::read(const std::string& jobfilename) { + /*open file*/ + FILE* result_file=fopen(jobfilename.c_str(),"rb"); + if (result_file==NULL) return NULL; + + XML_Parser parser=XML_ParserCreate(NULL); + if (parser==NULL) { + fclose(result_file); + return NULL; + } + + xml_result_handler h(parser); + + for (;;) { + const size_t BUFF_SIZE=1<<10; + int bytes_read; + void *buff = XML_GetBuffer(parser, BUFF_SIZE); + if (buff == NULL) { + fprintf(stderr, "could not get the buffer from XML Parser\n"); + fclose(result_file); + return NULL; + } + + bytes_read = fread(buff, 1, BUFF_SIZE, result_file); + if (bytes_read < 0) { + /* handle error */ + fprintf(stderr,"error while reading\n"); + break; + } + + if (! XML_ParseBuffer(parser, bytes_read, bytes_read == 0)) { + /* handle parse error */ + fprintf(stderr,"error while parsing line %d: %s\n",static_cast(XML_GetCurrentLineNumber(parser)),XML_ErrorString(XML_GetErrorCode(parser))); + break; + } + + // peaceful end + if (bytes_read == 0) break; + } + + fclose(result_file); + return NULL; +} + +/* ************************************************************************************************ + +here starts the writer implementation + +**************************************************************************************************/ + +xml_result_writer::xml_result_writer(data_save_mode_type how) { + resultfilename_pattern=""; + data_save_mode=how; +} + +xml_result_writer::xml_result_writer(const std::string& pattern, data_save_mode_type how) { + resultfilename_pattern=pattern; + data_save_mode=how; +} + + +int xml_result_writer::write_to_file(const std::string& filename, const result* res) const { + // find the way to write + const adc_result* adc_res=dynamic_cast(res); + if (adc_res!=NULL) { + write_adc_to_file(filename,adc_res); + return 0; + } + const adc_results* adc_ress=dynamic_cast(res); + if (adc_ress!=NULL) { + write_adcs_to_file(filename, adc_ress); + return 0; + } + const configuration_results* config_ress=dynamic_cast(res); + if (config_ress!=NULL) { + write_configuration_results_to_file(filename, *config_ress); + return 0; + } + + const error_result* err_res=dynamic_cast(res); + if (err_res!=NULL) write_error_to_file(filename,err_res); + else + write_unknown_to_file(filename, res); + return 0; +} + +int xml_result_writer::write_unknown_to_file(const std::string& filename, const result* res) const { + FILE* out=fopen(filename.c_str(),"w"); + if (out==0) fprintf(stderr,"could not open file %s\n",filename.c_str()); + fprintf(out,"\n"); + fprintf(out,"\n",res->job_no); + if (res==NULL) + fprintf(out,"\n"); + else + fprintf(out,"\n"); + fprintf(out,"\n"); + fclose(out); + return 0; +} + + +int xml_result_writer::write_error_to_file(const std::string& filename, const error_result* res) const { + /* write an extra message to stderr */ + fprintf(stderr,"job %" SIZETPRINTFLETTER ": %s\n",res->job_no,res->error_message.c_str()); + FILE* out=fopen(filename.c_str(),"w"); + if (out==0) { + fprintf(stderr,"could not open file %s\n",filename.c_str()); + return 0; + } + fprintf(out,"\n"); + fprintf(out,"\n",res->job_no); + fprintf(out," %s\n",res->error_message.c_str()); + fprintf(out,""); + fclose(out); + return 0; +} + +int xml_result_writer::write_configuration_results_to_file(const std::string& filename, const configuration_results& ress) const { + + FILE* out=fopen(filename.c_str(),"w"); + fprintf(out,"\n"); + fprintf(out,"\n\n",ress.job_no); + XMLCh tempStr[100]; + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("LS", tempStr, 99); + XERCES_CPP_NAMESPACE_QUALIFIER DOMImplementation *impl2=XERCES_CPP_NAMESPACE_QUALIFIER DOMImplementationRegistry::getDOMImplementation(tempStr); + XERCES_CPP_NAMESPACE_QUALIFIER DOMWriter *theSerializer=((XERCES_CPP_NAMESPACE_QUALIFIER DOMImplementationLS*)impl2)->createDOMWriter(); + for (configuration_results::const_iterator i=ress.begin(); i!=ress.end(); ++i) + if ((*i)->tag->getDocumentElement()!=NULL) { + XERCES_CPP_NAMESPACE_QUALIFIER MemBufFormatTarget mem; + theSerializer->writeNode(&mem,*((*i)->tag->getDocumentElement())); + fwrite((char*)mem.getRawBuffer(),sizeof(char),mem.getLen(),out); + mem.reset(); + } + theSerializer->release(); + + // todo + fprintf(out,"\n\n"); + fclose(out); + return 0; +} + +int xml_result_writer::write_adcs_to_file(const std::string& filename, const adc_results* ress) const { + // fall back to some default mode + data_save_mode_type how=data_save_mode; + if (how==defaultmode) how=ascii; + FILE* out=fopen(filename.c_str(),"w"); + if (out==0) { + fprintf(stderr,"could not open file %s\n",filename.c_str()); + return 0; + } + fprintf(out,"\n"); + fprintf(out,"\n",ress->job_no); + fwrite(ress->description.c_str(),ress->description.size(),1,out); + fprintf(out,"\n"); + int num_res=0; + for (adc_results::const_iterator res=ress->begin(); res!=ress->end(); ++res) { + switch (how) { + case separate_file: + { + // write separate data file + char result_filename[1<<10]; + snprintf(result_filename,sizeof(result_filename),"adc.%09" SIZETPRINTFLETTER ".%d.bin",ress->job_no,num_res); + write_adcdata_separate(out, std::string(result_filename), *res); + } + break; + case base64: + write_adcdata_base64(out,*res); + break; + case csv: + write_adcdata_formated(out,"%d; %d\n",*res); + break; + case ascii: + write_adcdata_formated(out,"%d\t%d\n",*res); + break; + default: + /* forget data */ + fprintf(stderr,"forgeting adc data\n"); + } + num_res++; + } + fprintf(out,"\n"); + fclose(out); + return 0; +} + +int xml_result_writer::write_adc_to_file(const std::string& filename, const adc_result* res) const { + // fall back to some default mode + data_save_mode_type how=data_save_mode; + if (how==defaultmode) how=ascii; + FILE* out=fopen(filename.c_str(),"w"); + if (out==0) { + fprintf(stderr,"could not open file %s\n",filename.c_str()); + return 0; + } + fprintf(out,"\n"); + fprintf(out,"\n",res->job_no); + fwrite(res->description.c_str(),res->description.size(),1,out); + fprintf(out,"\n"); + switch (how) { + case separate_file: + { + // write separate data file + write_adcdata_separate(out, filename+".bin", res); + } + break; + case base64: + write_adcdata_base64(out,res); + break; + case csv: + write_adcdata_formated(out,"%d; %d\n",res); + break; + case ascii: + write_adcdata_formated(out,"%d\t%d\n",res); + break; + default: + /* forget data */ + fprintf(stderr,"forgeting adc data\n"); + } + fprintf(out,"\n"); + fclose(out); + return 0; +} + +int xml_result_writer::write_adcdata_separate(FILE* out, const std::string& datafilename, const adc_result* res) const { + /* todo: error handling */ + FILE* binout=fopen(datafilename.c_str(),"wb"); + if (binout==0) { + fprintf(stderr,"could not open file %s\n",datafilename.c_str()); + return 0; + } + fwrite(res->data, sizeof(short int)*res->nchannels, res->samples, binout); + fclose(binout); + fprintf(out, + "\n", + datafilename.c_str(), + res->samples, + res->sampling_frequency, + res->nchannels); + return 0; +} + +int xml_result_writer::write_adcdata_formated(FILE* out, const std::string& format, const adc_result* res) const { + fprintf(out,"\n",res->samples,res->sampling_frequency, res->nchannels); + for(size_t i=0;isamples;++i) { + for (int j = 0; j < res->nchannels; j++) { + fprintf(out, format.c_str(), res->data[i*2 + j]); + } + } + fprintf(out,"\n"); + return 0; +} + +int xml_result_writer::write_adcdata_base64(FILE* out, const adc_result* res) const { + fprintf(out,"\n",res->samples,res->sampling_frequency, res->nchannels); + unsigned int base64length=0; + XMLByte* base64buffer=XERCES_CPP_NAMESPACE_QUALIFIER Base64::encode((XMLByte*)res->data,res->samples*res->nchannels*sizeof(short int),&base64length); + fwrite(base64buffer,1,base64length,out); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&base64buffer); + fprintf(out,"\n"); + return 0; +} diff --git a/core/xml_result.h b/core/xml_result.h new file mode 100644 index 0000000..38d16f5 --- /dev/null +++ b/core/xml_result.h @@ -0,0 +1,158 @@ +/* ****************************************************************************** + +author: Achim Gaedke + + +********************************************************************************/ + +#ifndef XML_RESULT_H +#define XML_RESULT_H + +#include "expat.h" +#include "result.h" +#include +#include + +/** + can read result files +*/ +class xml_result_reader { + std::string jobfilenamepattern; + + public: + /** + instantiates parser + */ + xml_result_reader(); + /** + instantiates parser + */ + xml_result_reader(const std::string& jobpattern); + +#if 1 + /** + returns the number of obtained adc results, and 0 if no result available + */ + result* read(size_t jobno); + + /** + returns the number of obtained adc results, and 0 if no result available + */ + result* read(const std::string& filename); + +#else + /** + returns the number of obtained adc results, and 0 if no result available + */ + size_t read(size_t jobno); + + /** + returns NULL, if no result available, or file contains no result + */ + size_t read(const std::string& filename); +#endif + + /** + returns error message or empty string, if none + */ + std::string get_error_message() const; + + /** + return next adc result data, the result data are no longer available from the handler + if there is no result left, NULL is returned + */ + adc_result* get_result(); + + /** + frees the parser + */ + ~xml_result_reader(); + +}; + +/** + writes results in xml formated files +*/ +class xml_result_writer { + + public: + /** + \brief the adc data store method to choose + */ + typedef enum {defaultmode, separate_file, ascii, csv, base64, hex} data_save_mode_type; + + private: + /** + the chosen save method for adc data + */ + data_save_mode_type data_save_mode; + + /** + the pattern for filename creation from job number by snprintf + */ + std::string resultfilename_pattern; + + public: + /** + also default constructor, no resultfile name pattern + */ + xml_result_writer(data_save_mode_type how=defaultmode); + + /** + also default constructor, resultfile name pattern can be specified + */ + xml_result_writer(const std::string& pattern, data_save_mode_type how=defaultmode); + + /** + the result is written according to the result number and result file pattern + */ + void write(result* res); + + /** + the result is written to the specified file + */ + int write_to_file(const std::string& filename, const result* res) const; + + /** + write an adc result, choose the right method + */ + int write_adc_to_file(const std::string& filename, const adc_result* res) const; + + /** + write an adc result, choose the right method + */ + int write_adcs_to_file(const std::string& filename, const adc_results* res) const; + + /** + write data to a seperate file + */ + int write_adcdata_separate(FILE* out, const std::string& datafilename, const adc_result* res) const; + + /** + write the adcdata formated, pairs of samples each line + */ + int write_adcdata_formated(FILE* out, const std::string& format, const adc_result* res) const; + + /** + write the data in base64 code to file + */ + int write_adcdata_base64(FILE* out, const adc_result* res) const; + + /** + write configuration tags to one file + */ + int write_configuration_results_to_file(const std::string& filename, const configuration_results& ress) const; + + /** + write the error message + */ + int write_error_to_file(const std::string& filename, const error_result* res) const; + + /** + if nothing is konwn, use this one + */ + int write_unknown_to_file(const std::string& filename, const result* res) const; + +}; + +#endif /* XML_RESULT_H */ diff --git a/core/xml_states.cpp b/core/xml_states.cpp new file mode 100644 index 0000000..6b41310 --- /dev/null +++ b/core/xml_states.cpp @@ -0,0 +1,369 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + +****************************************************************************/ +#include "states.h" +#include "xml_states.h" +#include +#include +#include +#include + +#ifndef SIZETPRINTFLETTER +# ifndef __LP64__ +# define SIZETPRINTFLETTER "u" +# else +# define SIZETPRINTFLETTER "lu" +# endif +#endif + +void xml_state_reader_startelement_handler(xml_state_reader* self, + const XML_Char *name, + const XML_Char **atts ) { + self->start_element(name,atts); +} + +void xml_state_reader_endelement_handler(xml_state_reader* self, + const XML_Char *name) { + self->end_element(name); +} + +const XML_Char* xml_state_reader::search_attribute(const XML_Char** atts,const XML_Char* attr_name) const { + if (atts==NULL || atts[0]==NULL) return NULL; + size_t attr_idx=0; + while (strcmp(atts[attr_idx],attr_name)!=0) { + attr_idx+=2; + if (atts[attr_idx]==NULL) return NULL; + } + return atts[attr_idx+1]; +} + +const XML_Char* xml_state_reader::search_attributes(const XML_Char** atts, ...) const { + if (atts==NULL || atts[0]==NULL) return NULL; + va_list names; + va_start(names,atts); + const XML_Char* attr_name=va_arg(names, const XML_Char*); + while (attr_name!=NULL) { + size_t attr_idx=0; + while (atts[attr_idx]!=NULL && strcmp(atts[attr_idx],attr_name)!=0) + attr_idx+=2; + if (atts[attr_idx]!=NULL) { + va_end(names); + return atts[attr_idx+1]; + } + attr_name=va_arg(names, const XML_Char*); + } + va_end(names); + return NULL; +} + + + +state_atom* xml_state_reader::state_factory(const XML_Char *name, + const XML_Char **atts) const { + if (strcmp(name,"state")==0) { + state* s=new state(1); + const XML_Char* length=search_attributes(atts,"length","time",(XML_Char*)NULL); + if (length!=NULL) s->length=strtod(length,NULL); + return s; + } + if (strcmp(name,"sequent")==0) { + state_sequent* s=new state_sequent(); + const XML_Char* repeat=search_attribute(atts,"repeat"); + if (repeat!=NULL) + s->repeat=strtoul(repeat,0,0); + else + s->repeat=1; + return s; + } + if (strcmp(name,"parallel")==0) { + return new state_parallel(); + } + if (strcmp(name,"ttlout")==0) { + ttlout* ttls=new ttlout(); + const XML_Char* id=search_attributes(atts,"id","i",(XML_Char*)NULL); + if (id!=NULL) ttls->id=strtol(id,NULL,0); + const XML_Char* value=search_attribute(atts,"value"); + if (value!=NULL) ttls->ttls=strtoul(value,NULL,0); + const XML_Char* channel=search_attribute(atts,"channel"); + if (channel!=NULL) { + const XML_Char* state=search_attribute(atts,"state"); + if (state!=NULL) { + size_t number=strtoul(channel,0,0); + char state_char=(state)[0]; + if (state_char=='h' || state_char=='1') + ttls->ttls.set(number,1); + else + ttls->ttls.set(number,0); + } + else { + fprintf(stderr,"found invalid ttl state: ignoring\n"); + delete ttls; + ttls=NULL; + } + } + return (state_atom*)ttls; + } + if (strcmp(name,"analogout")==0) { + analogout* aout=new analogout(); + const XML_Char* channel=search_attributes(atts,"channel","c","id","i",(XML_Char*)NULL); + const XML_Char* frequency=search_attributes(atts,"frequency","f",(XML_Char*)NULL); + const XML_Char* phase=search_attributes(atts,"phase","p",(XML_Char*)NULL); + const XML_Char* amplitude=search_attributes(atts,"amplitude","a",(XML_Char*)NULL); + const XML_Char* dac_value=search_attributes(atts,"dac_value","d",(XML_Char*)NULL); + if (dac_value!=NULL) aout->dac_value=strtol(dac_value,NULL,0); + if (channel!=NULL) aout->id=strtoul(channel,NULL,0); + if (frequency!=NULL) aout->frequency=strtod(frequency,NULL); + if (phase!=NULL) aout->phase=strtod(phase,NULL); + if (amplitude!=NULL) + aout->amplitude=strtod(amplitude,NULL); + else + aout->amplitude=1.0; + return (state_atom*)aout; + } + if (strcmp(name,"analogin")==0) { + analogin* ain=new analogin(); + const XML_Char* id=search_attributes(atts,"id","i",(XML_Char*)NULL); + const XML_Char* frequency=search_attributes(atts,"f","frequency",(XML_Char*)NULL); + const XML_Char* samples=search_attributes(atts,"s","samples",(XML_Char*)NULL); + const XML_Char* channels=search_attributes(atts,"c","channels",(XML_Char*)NULL); + const XML_Char* resolution = search_attributes(atts,"r","res","resolution",(XML_Char*)NULL); + + if (resolution!=NULL) + ain->resolution=strtoul(resolution,NULL,0); + else + ain->resolution=14; // was 12 + + if (frequency!=NULL) ain->sample_frequency=strtod(frequency,NULL); + if (samples!=NULL) ain->samples=strtoul(samples,NULL,0); + if (id!=NULL) ain->id=strtoul(id,NULL,0); + if (channels!=NULL) { + ain->channels = channel_array(strtoul(channels,NULL,0)); + ain->nchannels = ain->channels.count(); + } else { + ain->channels = channel_array(3); + ain->nchannels = ain->channels.count(); + } + + // set parameters for each channel + for (int i = 0; i < ain->nchannels; i++) { + if (ain->channels[i] == true) { + char buffer1[100]; + char buffer2[100]; + sprintf(buffer1, "sen%i", i); + sprintf(buffer2, "sensitivity%i", i - 1); + + + // read sensitivity + const XML_Char* sensitivity = search_attributes(atts, buffer1, buffer2,(XML_Char*)NULL); + if (sensitivity != NULL) { + if (ain->sensitivity == NULL) { + ain->sensitivity = new double[ain->nchannels]; + } + ain->sensitivity[i] = strtod(sensitivity, NULL); + } else { + ain->sensitivity[i] = ADC_M2I_DEFAULT_SENSITIVITY; + } + delete sensitivity; + + // read offset + sprintf(buffer1, "offset%i", i); + const XML_Char* offset = search_attributes(atts, buffer1, (XML_Char*)NULL); + if (offset != NULL) { + if (ain->offset == NULL) { + ain->offset = new int[ain->nchannels]; + } + ain->offset[i] = strtol(offset, NULL, 0); + } else { + ain->offset[i] = ADC_M2I_DEFAULT_OFFSET; + } + delete offset; + + // read impedance + sprintf(buffer1, "impedance%i", i); + const XML_Char* impedance = search_attributes(atts, buffer1, (XML_Char*)NULL); + if (impedance != NULL) { + if (ain->impedance == NULL) { + ain->impedance = new double[ain->nchannels]; + } + ain->impedance[i] = strtol(impedance, NULL, 0); + } else { + ain->impedance[i] = ADC_M2I_DEFAULT_IMPEDANCE; + } + delete impedance; + } + } + + return (state_atom*)ain; + } + fprintf(stderr,"unknown tag %s found\n",name); + return NULL; +} + +void xml_state_reader::start_element(const XML_Char *name, + const XML_Char **atts) { + state_atom* new_element=state_factory(name,atts); + if (root==NULL) + root=new_element; + if (new_element!=NULL && !pending_elements.empty()) { + state* container=dynamic_cast(pending_elements.back()); + if (container!=NULL) + container->push_back(new_element); + else + fprintf(stderr,"found invalid nested element %s\n",name); + } + pending_elements.push_back(new_element); +} + +void xml_state_reader::end_element(const XML_Char *name) { + pending_elements.pop_back(); +} + +xml_state_reader::xml_state_reader() { + fprintf(stderr, "xml_state_reader class is decrepated, please shift to xerces supporting classes.\n"); + root=(state_atom*)NULL; + parser=XML_ParserCreate((XML_Char*)NULL); + XML_SetUserData(parser,(void*)this); + XML_SetElementHandler(parser, + (XML_StartElementHandler)xml_state_reader_startelement_handler, + (XML_EndElementHandler)xml_state_reader_endelement_handler); +} + +state_atom* xml_state_reader::read_from_string(const std::string& data) { + if (!XML_Parse(parser,data.c_str(), data.size(),1)) { + /* handle parse error */ + fprintf(stderr,"error while parsing\n"); + delete root; + root=NULL; + return NULL; + } + state_atom* return_value=root; + root=NULL; + return return_value; +} + +state_atom* xml_state_reader::read_from_file(const std::string& filename) { + int docfd=open(filename.c_str(),O_RDONLY); + for (;;) { + const size_t BUFF_SIZE=1<<12; + int bytes_read; + void *buff = XML_GetBuffer(parser, BUFF_SIZE); + if (buff == NULL) { + fprintf(stderr, "could not get the buffer from XML Parser\n"); + close(docfd); + return NULL; + } + + bytes_read = read(docfd, buff, BUFF_SIZE); + if (bytes_read < 0) { + /* handle error */ + fprintf(stderr,"error while reading\n"); + if (root!=NULL) delete root; + root=NULL; + break; + } + + if (! XML_ParseBuffer(parser, bytes_read, bytes_read == 0)) { + /* handle parse error */ + fprintf(stderr,"error while parsing\n"); + if (root!=NULL) delete root; + root=NULL; + break; + } + + // peaceful end + if (bytes_read == 0) break; + }; + close(docfd); + + state_atom* return_value=root; + root=NULL; + return return_value; +} + +xml_state_reader::~xml_state_reader(){ + XML_ParserFree(parser); + if (root!=NULL) delete root; +} + +int xml_state_writer::write_states(FILE* output, const state_atom& states_to_write, int add_header, int indent_size) { + if (add_header) { + fprintf(output,"\n"); + } + + std::string indent_string(indent_size,' '); + + const state_sequent* ss=dynamic_cast(&states_to_write); + if (ss!=NULL) { + if (ss->repeat==1) + fprintf(output,"%s\n",indent_string.c_str()); + else + fprintf(output,"%s\n",indent_string.c_str(),ss->repeat); + for (state::const_iterator i=ss->begin();i!=ss->end(); i++) { + write_states(output,**i,0,indent_size+indent_increase); + } + fprintf(output,"%s\n",indent_string.c_str()); + return 1; + } + const state_parallel* sp=dynamic_cast(&states_to_write); + if (sp!=NULL){ + fprintf(output,"%s\n",indent_string.c_str()); + for (state::const_iterator i=sp->begin();i!=sp->end(); i++) { + write_states(output,**i,0,indent_size+indent_increase); + } + fprintf(output,"%s\n",indent_string.c_str()); + return 1; + } + const state* st=dynamic_cast(&states_to_write); + if (st!=NULL) { + fprintf(output,"%s\n",indent_string.c_str(),st->length); + for (state::const_iterator i=st->begin();i!=st->end(); i++) { + write_states(output,**i,0,indent_size+indent_increase); + } + fprintf(output,"%s\n",indent_string.c_str()); + return 1; + } + const ttlout* ttlo=dynamic_cast(&states_to_write); + if (ttlo!=NULL) { + fprintf(output,"%s\n", + indent_string.c_str(), + ttlo->ttls.to_ulong()); + return 1; + } + const analogout* ao=dynamic_cast(&states_to_write); + if (ao!=NULL) { + fprintf(output, + "%s\n", + indent_string.c_str(), + ao->id, + ao->frequency, + ao->amplitude, + ao->phase, + ao->dac_value); + + return 1; + } + const analogin* ai=dynamic_cast(&states_to_write); + if (ai!=NULL) { + char args[2000]; + char buffer[1000]; + sprintf(args, "%sid, + ai->samples, + ai->sample_frequency, + ai->channels.to_ulong(), + ai->resolution); + for (int i = 0; i < ai->nchannels; i++) { + sprintf(buffer, "sensitivity%d=\"%g\" offset%d=\"%d\" impedance%d=\"%g\"", i, ai->sensitivity[i], i, ai->offset[i], i, ai->impedance[i]); + strcat(args, buffer); + } + strcat(args, "/>\n"); + fprintf(output, args); + return 1; + } + fprintf(output,"%s\n",indent_string.c_str()); + return 0; +} diff --git a/core/xml_states.h b/core/xml_states.h new file mode 100644 index 0000000..e9ce6ca --- /dev/null +++ b/core/xml_states.h @@ -0,0 +1,162 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + +****************************************************************************/ +#ifndef XML_STATES_H +#define XML_STATES_H + +#include "states.h" +#include "constants.h" +#include +#include +#include + +/** + \defgroup xmlstateinterface XML support for all state classes + \ingroup states + The state sequences consist of states and again sequences. A state is defined by a number of state_atom objects. + This definition results in a file with nested sections, that is parsed into a tree formed by the state objects. + The leaves of the tree are normaly state_atom objects. These state_atom objects define the state of a device. + + An example for a sequence definition is: +\verbatim + + + + + + + + + + + + + + + + + +\endverbatim + +The first line of this code is a statement of xml conformity. (More about xml can be read at: +http://www.w3.org/TR/REC-xml .) +\n +The second line defines a new subsequence of states by using the tag \. +The "repeat" value 3 is the loop count. This line effects an instatiation of a state_sequent object. +All information will be collected in this branch, it is closed on the last line by \. +In this example the third line provides a %state tag corresponding to a state object. +So this also line specifies the duration of this state in seconds. +This section is closed with the eigth line containing \. +A xml document can contain only one top-level-element, that is called root or document element. +Applying the xml_state_reader it is not necessary to begin with a sequent element. +\n +A xml section (representing a state or sequence in our case) is opend by a tag with a name in angle braces like +\ and is closed with an extra slash before the name \. Attributes can follow in the form name="value", +attributes must be quoted (single or double quotes). If such a section has no contents there is a short form with a slash +before the closing angle, e.g. \. +\n +Of course outside these tags, text can be supplied. By now, xml_state_reader ignores this text. +Inside a %state section the definition of the state is collected. Implemented are ttlout, analogout and +analogin. One element can occurr several times. How these states are merged is machine dependent. + +So a state sequence is defined by one sequent section, containing again serval state or sequent +sections. The state sections must not contain other state sections or sequent sections. + +*/ +//@{ +/** + \brief gains state sequence from an xml event stream + */ +class xml_state_reader { + state_atom* root; + std::list pending_elements; + XML_Parser parser; + public: + /** + instantiates an expat xml parser object + */ + xml_state_reader(); + /** + frees the parser object + */ + ~xml_state_reader(); + /** + \brief parses xml file containing sequences of states + the name of the file is the only parameter + \return returns tree of states or NULL if an parse error occured + */ + state_atom* read_from_file(const std::string& filename); + + state_atom* read_from_dom(const XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* e) { + // to do work, work and work + return NULL; + } + + /** + \brief parses a string containing sequences of states + \return returns tree of states or NULL if an parse error occured + */ + state_atom* read_from_string(const std::string& data); + + /** \brief finds an attribute in the attributes array + \return returns the pointer to the attributes value or NULL if not in the array + */ + const XML_Char* search_attribute(const XML_Char** atts, const XML_Char* attr) const; + + /** + \brief looks for an attribute with different names + the first alternative, that could be found is taken + \return returns a pointer to the attributes value or NULL if nothing approprate is found + */ + const XML_Char* search_attributes(const XML_Char** atts, ...) const; + + /** + create new states from xml tags and their attributes + */ + state_atom* state_factory(const XML_Char* name, const XML_Char** atts) const; + + /** + start tag callback for expat parser + */ + void start_element(const XML_Char* name,const XML_Char** atts); + + /** + end tag callback for expat parser + */ + void end_element(const XML_Char* name); +}; + +/** + \ingroup states + the writer outputs states to xml files + takes care of formating + */ +class xml_state_writer { + public: + /// have a stack + std::list pending_elements; + /// allow nice formating druing recursive operation + size_t indent_offset; + /// allow nice formating druing recursive operation + size_t indent_increase; + + /// simple initialisation + xml_state_writer(){ + indent_offset=0; + indent_increase=2; + }; + + + /** + write states to a given output + if add_header is !=0 a full xml document is given + */ + int write_states(FILE* output, const state_atom& states_to_write, int add_header=0, int indent_size=0); +}; +//@} + + +#endif diff --git a/doc/Doxyfile b/doc/Doxyfile new file mode 100644 index 0000000..c9661e7 --- /dev/null +++ b/doc/Doxyfile @@ -0,0 +1,282 @@ +# Doxyfile 1.5.5 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = "\"DAMARIS\" - Backends" +PROJECT_NUMBER = 0.11 +OUTPUT_DIRECTORY = +CREATE_SUBDIRS = YES +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = NO +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +DETAILS_AT_TOP = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 8 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +DISTRIBUTE_GROUP_DOC = NO +SUBGROUPING = YES +TYPEDEF_HIDES_STRUCT = NO +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = YES +EXTRACT_PRIVATE = YES +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = YES +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_DIRECTORIES = NO +FILE_VERSION_FILTER = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = ../core/ \ + ../drivers/ \ + ../drivers/SpinCore-PulseBlaster/pulseblaster.c \ + ../drivers/SpinCore-PulseBlaster/pulseblaster.h \ + ../drivers/SpinCore-PulseBlaster/SpinCore-PulseBlaster.h \ + ../drivers/SpinCore-PulseBlaster/SpinCore-PulseBlaster.cpp \ + ../drivers/SpinCore-PulseBlaster/PulseBlasterProgram.h \ + ../drivers/SpinCore-PulseBlaster/PulseBlasterProgram.cpp \ + ../drivers/SpinCore-PulseBlasterDDSIII/SpinCore-PulseBlasterDDSIII.h \ + ../drivers/SpinCore-PulseBlasterDDSIII/SpinCore-PulseBlasterDDSIII.cpp \ + ../drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h \ + ../drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.cpp \ + ../drivers/Eurotherm-2000Series/Eurotherm-2000Series.h \ + ../drivers/Eurotherm-2000Series/Eurotherm-2000Series.cpp \ + ../drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h \ + ../drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.cpp \ + ../drivers/Spectrum-MI40xxSeries/ \ + ../drivers/Spectrum-MI40xxSeries/GatedData.h \ + ../drivers/Spectrum-MI40xxSeries/GatedData.cpp \ + ../drivers/TiePie-HS3/TiePie-HS3.h \ + ../drivers/TiePie-HS3/TiePie-HS3.cpp \ + ../drivers/Datel-PCI416/Datel-PCI416.h \ + ../drivers/Datel-PCI416/Datel-PCI416.cpp \ + ../drivers/PTS-Synthesizer/PTS.cpp \ + ../drivers/PTS-Synthesizer/PTS.h \ + ../drivers/Tecmag-DAC20/DAC20.cpp \ + ../drivers/Tecmag-DAC20/DAC20.h \ + ../drivers/dummy \ + ../machines/ \ + ../tests/ \ + ../tests/stefan/ \ + /home/achim/build/backends/machines/bg_backend.cpp \ + /home/achim/build/backends/machines/deuteron_backend.cpp \ + /home/achim/build/backends/machines/dummycore.cpp \ + /home/achim/build/backends/machines/hardware.cpp \ + /home/achim/build/backends/machines/hardware.h \ + /home/achim/build/backends/machines/magnexgrad_backend.cpp \ + /home/achim/build/backends/machines/magnexgrad_backend_dds.cpp \ + /home/achim/build/backends/machines/Mobilecore.cpp \ + /home/achim/build/backends/machines/Mobile_wo_sync_backend.cpp \ + /home/achim/build/backends/machines/NQRcore.cpp \ + /home/achim/build/backends/machines/PFGcore.cpp \ + /home/achim/build/backends/machines/bg_backend.cpp +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = *.h \ + *.cpp +RECURSIVE = NO +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = . +EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = . +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = YES +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +REFERENCES_LINK_SOURCE = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = backends-html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = footer.html +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_BUNDLE_ID = org.doxygen.Project +HTML_DYNAMIC_SECTIONS = NO +CHM_FILE = backends.chm +HHC_LOCATION = +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = YES +TREEVIEW_WIDTH = 250 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = NO +USE_PDFLATEX = NO +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +MSCGEN_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = YES +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +DOT_PATH = /opt/graphviz-2.2.1/bin +DOTFILE_DIRS = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 0 +DOT_TRANSPARENT = YES +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..f998295 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,30 @@ +############################################################################# +# +# Author: Achim Gaedke +# Created: June 2004 +# +############################################################################# +DOXYGEN=doxygen +PATH:=$(PATH) + +.PHONY: all install docinstall clean doc + +all: + +install: + +doc: backends-html + +backends-html: .htmltag + +software_design_overview_scaled.png: software_design_overview.png + convert -resize 635x476 $< $@ + +.htmltag: software_design_overview_scaled.png + $(DOXYGEN) Doxyfile && touch .htmltag + +clean: + rm -rf backends-html software_design_overview_scaled.png .htmltag *~ \#* + +docinstall: backends-html + install -d $(PREFIX)/doc && cp -r backends-html/* $(PREFIX)/doc diff --git a/doc/experiment_job.xml b/doc/experiment_job.xml new file mode 100644 index 0000000..7721dcc --- /dev/null +++ b/doc/experiment_job.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/footer.html b/doc/footer.html new file mode 100644 index 0000000..4cf3854 --- /dev/null +++ b/doc/footer.html @@ -0,0 +1,6 @@ +

+



+$projectname, version $projectnumber: back to mainpage +

+ + diff --git a/doc/quit_job.xml b/doc/quit_job.xml new file mode 100644 index 0000000..e2cea88 --- /dev/null +++ b/doc/quit_job.xml @@ -0,0 +1,3 @@ + + + diff --git a/doc/software_design_overview.png b/doc/software_design_overview.png new file mode 100644 index 0000000..be3df0f Binary files /dev/null and b/doc/software_design_overview.png differ diff --git a/doc/wait_job.xml b/doc/wait_job.xml new file mode 100644 index 0000000..b40a256 --- /dev/null +++ b/doc/wait_job.xml @@ -0,0 +1,3 @@ + + + diff --git a/drivers/ADC.h b/drivers/ADC.h new file mode 100644 index 0000000..2f5ebb4 --- /dev/null +++ b/drivers/ADC.h @@ -0,0 +1,64 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + + ****************************************************************************/ +#ifndef ADC_H +#define ADC_H + +#include +#include +#include "core/result.h" +#include "core/states.h" +#include "core/core_exception.h" + +/** + \defgroup drivers Drivers + */ + +/** + \defgroup basedrivers Drivers' Base Classes + \ingroup drivers + */ +//@{ +/** + * ADC exception + */ +class ADC_exception: public RecoverableException +{ +public: + explicit ADC_exception(const std::string& msg) throw (): RecoverableException(msg) {} + explicit ADC_exception(const char* msg) throw (): RecoverableException(msg) {} + virtual ~ADC_exception() throw () {} +protected: + virtual const std::string prefix() const { return "ERROR (ADC_exception): "; } +}; +/** + base class for adc drivers + */ +class ADC +{ +public: + /** + start sampling after trigger and return field of int + */ + virtual void sample_after_external_trigger(double rate, size_t samples, double sensitivity = 5.0, size_t resolution = 14)=0; + + /** + here the data aquisition unit is configured and adds the necessary pusle components to the program + */ + virtual void set_daq(state& exp)=0; + + /** + read the sample data + */ + virtual result* get_samples(double timeout = 0.0)=0; + + virtual ~ADC() + { + } +}; + +//@} +#endif diff --git a/drivers/Datel-PCI416/416inc/41632DLL.H b/drivers/Datel-PCI416/416inc/41632DLL.H new file mode 100644 index 0000000..4fe897f --- /dev/null +++ b/drivers/Datel-PCI416/416inc/41632DLL.H @@ -0,0 +1,296 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 41632dll.h: Function definition of pci41632.dll exported functions +// See pci416io.h, pci416libw95.h and pci416_32dll.h +// for the description of each function. +// Copyright (c) Datel, Inc. 1997 +// Platform: Win32, Win95 +// Compiler: MVC4.0 +// Version: 3.0 +// Author: GS +// created: 4/16/97 +// modified: 7/15/97 +/////////////////////////////////////////////////////////////////////////////// +#ifndef _41632DLL_H +#define _41632DLL_H + +#include + +#include "pcilib32.h" +#include "pci416df.h" + +#ifndef LIB_TYPE +#define LIB_TYPE WINAPI +#endif + +// function description -> see pci416io.h +typedef DWORD (LIB_TYPE *TFP_pci416_init)(DWORD *brdcount); +typedef DWORD (LIB_TYPE *TFP_pci416_close)(DWORD brdindex); +typedef DWORD (LIB_TYPE *TFP_find_pci_device)(WORD busno, WORD venid, + WORD deviceid, WORD bufsize, + BYTE *buf); +typedef DWORD (LIB_TYPE *TFP_get_pci_device_info)(WORD busno, WORD devno, + PCI_CONFIG_SPACE *conf); +typedef DWORD (LIB_TYPE *TFP_write_pci_port)(DWORD portadr, DWORD data); +typedef DWORD (LIB_TYPE *TFP_read_pci_port)(DWORD portadr, DWORD *data); + +typedef DWORD (LIB_TYPE *TFP_pci416_count)(DWORD *brdcount); +typedef DWORD (LIB_TYPE *TFP_pci416_get_cm_devnodes)(INT brdindex, + WORD bufsize, + DWORD *devnodebuf); +typedef DWORD (LIB_TYPE *TFP_pci416_get_cmdevinf)(DWORD brdindex, + PCI_CONFIG_SPACE *conf); +typedef DWORD (LIB_TYPE *TFP_pci416_get_badr)(DWORD brdindex, WORD bufsize, + WORD *badrbuf); +typedef DWORD (LIB_TYPE *TFP_pci416_getcaps)(DWORD brdindex, WORD bufsize, DWORD *buf); +typedef DWORD (LIB_TYPE *TFP_pci416_set_cmdreg)(DWORD brdindex, WORD mode, + DWORD regval, DWORD *shregval); +typedef DWORD (LIB_TYPE *TFP_pci416_read_statusreg)(DWORD brdindex, DWORD *regval); +typedef DWORD (LIB_TYPE *TFP_pci416_set_smplcntr)(DWORD brdindex, DWORD samples); +typedef DWORD (LIB_TYPE *TFP_pci416_set_chanadr)(DWORD brdindex, WORD mode, + DWORD regval, DWORD *shregval); +typedef DWORD (LIB_TYPE *TFP_pci416_clear_fifo)(DWORD brdindex); +typedef DWORD (LIB_TYPE *TFP_pci416_enablead)(DWORD brdindex, BOOL enable); +typedef DWORD (LIB_TYPE *TFP_pci416_set_pllreg)(DWORD brdindex, DWORD valA, DWORD valN); +typedef DWORD (LIB_TYPE *TFP_pci416_read_fifo)(DWORD brdindex, WORD count, DWORD *buf); +typedef DWORD (LIB_TYPE *TFP_pci416_set_timer)(DWORD brdindex, WORD mode, + WORD counter02, WORD counter1); +typedef DWORD (LIB_TYPE *TFP_pci416_set_portctrreg)(DWORD brdindex, DWORD regval); +typedef DWORD (LIB_TYPE *TFP_pci416_get_portctrreg)(DWORD brdindex, DWORD *regval); +typedef DWORD (LIB_TYPE *TFP_pci416_read_port)(DWORD brdindex, WORD port, DWORD *data); +typedef DWORD (LIB_TYPE *TFP_pci416_write_port)(DWORD brdindex, WORD port, DWORD data); +typedef DWORD (LIB_TYPE *TFP_pci416_write_dac)(DWORD brdindex, WORD data); +typedef DWORD (LIB_TYPE *TFP_pci416_fifo_status)(DWORD brdindex, DWORD *data); +typedef DWORD (LIB_TYPE *TFP_pci416_check_fifohf)(DWORD brdindex, DWORD *data); +typedef DWORD (LIB_TYPE *TFP_pci416_setup_dma)(DWORD brdindex, DWORD mode, + DWORD *bufsize, DWORD *hndDMAbuf); +typedef DWORD (LIB_TYPE *TFP_pci416_stop_dma)(DWORD brdindex, DWORD *tcount); +typedef DWORD (LIB_TYPE *TFP_pci416_reload_dma)(DWORD brdindex, DWORD bufno, + DWORD *bufsize, DWORD *hndDMAbuf); +typedef DWORD (LIB_TYPE *TFP_pci416_dma_status)(DWORD brdindex, DWORD *data); +typedef DWORD (LIB_TYPE *TFP_pci416_read_intcsrreg)(DWORD brdindex, DWORD *data); +typedef DWORD (LIB_TYPE *TFP_pci416_set_intcsrreg)(DWORD brdindex, WORD mode, + DWORD regval, DWORD *shregval); +typedef DWORD (LIB_TYPE *TFP_pci416_read_mcsrreg)(DWORD brdindex, DWORD *data); +typedef DWORD (LIB_TYPE *TFP_pci416_set_mcsrreg)(DWORD brdindex, WORD mode, + DWORD regval, DWORD *shregval); +typedef DWORD (LIB_TYPE *TFP_pci416_read_mwarreg)(DWORD brdindex, DWORD *data); +typedef DWORD (LIB_TYPE *TFP_pci416_set_mwarreg)(DWORD brdindex, WORD mode, DWORD regval, + DWORD *shregval); +typedef DWORD (LIB_TYPE *TFP_pci416_read_mwtcreg)(DWORD brdindex, DWORD *data); +typedef DWORD (LIB_TYPE *TFP_pci416_set_mwtcreg)(DWORD brdindex, WORD mode, DWORD regval, + DWORD *shregval); +typedef DWORD (LIB_TYPE *TFP_pci416_copy_dmabuffer)(DWORD brdindex, DWORD bufno, + DWORD start, DWORD *count, + DWORD *pDest ); +typedef DWORD (LIB_TYPE *TFP_pci416_get_dmabuf_hndl)(DWORD brdindex, DWORD bufno, + DWORD offset, DWORD *pHndl, + DWORD *bufsize); +typedef DWORD (LIB_TYPE *TFP_pci416_pause_resume_dma)(DWORD brdindex, DWORD flags); + +typedef DWORD (LIB_TYPE *TFP_pci416_getError)(LPTSTR *str); + +// function description -> see pci416libw95.h +typedef INT (LIB_TYPE *TFP_get_adm_stats)(DWORD brdindex, + ADM_STATS *admstats); +typedef DWORD (LIB_TYPE *TFP_read_pt_port_control_reg)(DWORD brdindex); +typedef void (LIB_TYPE *TFP_write_pt_port_control_reg)(DWORD brdindex, UINT val); +typedef DWORD (LIB_TYPE *TFP_read_pt_port_data)(DWORD brdindex, UINT prt); +typedef DWORD (LIB_TYPE *TFP_write_pt_port_data)(DWORD brdindex, UINT prt, UINT val); +typedef void (LIB_TYPE *TFP_enable_pt_dma_logic)(DWORD brdindex, UINT md); +typedef DWORD (LIB_TYPE *TFP_start_stop_pt_dma_logic)(DWORD brdindex, UINT md); +typedef void (LIB_TYPE *TFP_set_pt_trigger_mode)(DWORD brdindex, UINT md); +typedef void (LIB_TYPE *TFP_set_pt_interrupt_mode)(DWORD brdindex, UINT md); +typedef void (LIB_TYPE *TFP_enable_pt_eos_interrupt)(DWORD brdindex); +typedef void (LIB_TYPE *TFP_enable_pt_half_full_interrupt)(DWORD brdindex); +typedef DWORD (LIB_TYPE *TFP_read_pt_interrupt_status)(DWORD brdindex); +typedef void (LIB_TYPE *TFP_set_pt_scan_select)(DWORD brdindex, UINT md); +typedef void (LIB_TYPE *TFP_set_pt_auto_increment)(DWORD brdindex, UINT md); +typedef void (LIB_TYPE *TFP_set_pt_marker_select)(DWORD brdindex, UINT md); +typedef void (LIB_TYPE *TFP_set_pt_pretrigger)(DWORD brdindex, UINT md); +typedef void (LIB_TYPE *TFP_disable_pt_interrupt)(DWORD brdindex); +typedef void (LIB_TYPE *TFP_clear_pt_interrupt)(DWORD brdindex); +typedef void (LIB_TYPE *TFP_enable_pt_interrupt)(DWORD brdindex); +typedef void (LIB_TYPE *TFP_set_pt_ad_clock_source)(DWORD brdindex, UINT src); +typedef void (LIB_TYPE *TFP_set_pt_adm_control)(DWORD brdindex, UINT val); +typedef void (LIB_TYPE *TFP_set_pt_command_reg)(DWORD brdindex,DWORD val); +typedef DWORD (LIB_TYPE *TFP_read_pt_status_reg)(DWORD brdindex); +typedef DWORD (LIB_TYPE *TFP_pt_fifo_empty)(DWORD brdindex); +typedef DWORD (LIB_TYPE *TFP_pt_fifo_half_full)(DWORD brdindex); +typedef DWORD (LIB_TYPE *TFP_pt_fifo_full)(DWORD brdindex); +typedef DWORD (LIB_TYPE *TFP_read_pt_arm_ff)(DWORD brdindex); +typedef DWORD (LIB_TYPE *TFP_read_pt_analog_trigger_status)(DWORD brdindex); +typedef DWORD (LIB_TYPE *TFP_read_pt_acquire_status)(DWORD brdindex); +typedef DWORD (LIB_TYPE *TFP_read_pt_adm)(DWORD brdindex); +typedef void (LIB_TYPE *TFP_set_pt_sample_count)(DWORD brdindex, DWORD count); +typedef void (LIB_TYPE *TFP_set_pt_ad_channel)(DWORD brdindex, UINT chan); +typedef void (LIB_TYPE *TFP_set_pt_ad_scan_count)(DWORD brdindex, UINT count); +typedef void (LIB_TYPE *TFP_set_pt_led)(DWORD brdindex, UINT val); +typedef void (LIB_TYPE *TFP_reset_pt_fifos)(DWORD brdindex); +typedef void (LIB_TYPE *TFP_set_pt_convert_enable)(DWORD brdindex, UINT val); +typedef UINT (LIB_TYPE *TFP_set_pt_pll)(DWORD brdindex, REALTYPE freq); +typedef int (LIB_TYPE *TFP_set_pt_ad_clock_rate)(DWORD brdindex,REALTYPE fs); +typedef UINT (LIB_TYPE *TFP_set_pt_trigger_rate)(DWORD brdindex,REALTYPE ft, + REALTYPE *actual); +typedef void (LIB_TYPE *TFP_generate_single_internal_trigger)(DWORD brdindex); +typedef void (LIB_TYPE *TFP_reset_pt_trigger_timer)(DWORD brdindex); +typedef void (LIB_TYPE *TFP_set_pt_dac_voltage)(DWORD brdindex, REALTYPE volts); +typedef void (LIB_TYPE *TFP_set_pt_dac_code)(DWORD brdindex, UINT code); +typedef DWORD (LIB_TYPE *TFP_read_pt_fifo)(DWORD brdindex); +typedef void (LIB_TYPE *TFP_read_pt_fifo_many)(DWORD brdindex, LPDWORD buffer, + DWORD count); +typedef void (LIB_TYPE *TFP_reset_dma_fifo)( DWORD brdindex); +typedef void (LIB_TYPE *TFP_set_dma_request_half_full)( DWORD brdindex,UINT md); +typedef void (LIB_TYPE *TFP_enable_dma_transfers)( DWORD brdindex,UINT enable); +typedef void (LIB_TYPE *TFP_set_dma_transfer_count)(DWORD brdindex, DWORD bytes); +typedef DWORD (LIB_TYPE *TFP_read_dma_transfer_count)(DWORD brdindex); +// NT only +typedef DWORD (LIB_TYPE *TFP_start_daq_irq)(DWORD brdindex,DWORD TriggerMode, + REALTYPE *TriggerRate); +typedef DWORD (LIB_TYPE *TFP_pci416_close_dmabuf_hndl)(DWORD brdindex); +typedef DWORD (LIB_TYPE *TFP_pci416_map_dmabuf)(DWORD brdindex, DWORD *pHndl, DWORD size); +typedef DWORD (LIB_TYPE *TFP_pci416_unmap_dmabuf)(DWORD brdindex, DWORD Hndl); + +// function description -> see pci416_32dll.h +typedef DWORD (LIB_TYPE *TFP_get_adm_inf) (DWORD brdindex, WORD *model, + WORD *bits, WORD *channels, + WORD *shortcycle, + REALTYPE *fmax_single, REALTYPE *fmax_scan); +typedef DWORD (LIB_TYPE *TFP_set_modes) (DWORD brdindex, + WORD ClockSrc, + REALTYPE SampleRate, + DWORD SamplesPerTrigger, + WORD TriggerSrc, + WORD Channel, + WORD PreTrigger, + WORD Scan, + WORD Marker); +typedef DWORD (LIB_TYPE *TFP_start_daq)(DWORD brdindex, WORD TriggerMode, + REALTYPE *TriggerRate); + +typedef DWORD (LIB_TYPE *TFP_stop_daq)(DWORD brdindex); +typedef INT (LIB_TYPE *TFP_scan_status)(DWORD brdindex, + WORD ClockSrc, + REALTYPE SampleRate, + DWORD TotalSamples, + DWORD SamplesPerTrigger, + DWORD Frames, + WORD TriggerSrc, + REALTYPE TriggerRate, + WORD Channel, + BOOL PreTrigger, + BOOL Scan, + BOOL Marker, + PVOID buffer); + +/* function instance definition + assign function by loading the address from DLL with + func_name = (TFP_func_name)GetProcAddress(hModule, "func_name"); +*/ +TFP_pci416_init pci416_init; +TFP_pci416_close pci416_close; +TFP_find_pci_device find_pci_device; +TFP_get_pci_device_info get_pci_device_info; +TFP_write_pci_port write_pci_port; +TFP_read_pci_port read_pci_port; +TFP_pci416_count pci416_count; +TFP_pci416_get_cm_devnodes pci416_get_cm_devnodes; +TFP_pci416_get_cmdevinf pci416_get_cmdevinf; +TFP_pci416_get_badr pci416_get_badr; +TFP_pci416_getcaps pci416_getcaps; +TFP_pci416_set_cmdreg pci416_set_cmdreg; +TFP_pci416_read_statusreg pci416_read_statusreg; +TFP_pci416_set_smplcntr pci416_set_smplcntr; +TFP_pci416_set_chanadr pci416_set_chanadr; +TFP_pci416_clear_fifo pci416_clear_fifo; +TFP_pci416_enablead pci416_enablead; +TFP_pci416_set_pllreg pci416_set_pllreg; +TFP_pci416_read_fifo pci416_read_fifo; +TFP_pci416_set_timer pci416_set_timer; +TFP_pci416_set_portctrreg pci416_set_portctrreg; +TFP_pci416_get_portctrreg pci416_get_portctrreg; +TFP_pci416_read_port pci416_read_port; +TFP_pci416_write_port pci416_write_port; +TFP_pci416_write_dac pci416_write_dac; +TFP_pci416_fifo_status pci416_fifo_status; +TFP_pci416_check_fifohf pci416_check_fifohf; +TFP_pci416_setup_dma pci416_setup_dma; +TFP_pci416_stop_dma pci416_stop_dma; +TFP_pci416_reload_dma pci416_reload_dma; +TFP_pci416_dma_status pci416_dma_status; +TFP_pci416_read_intcsrreg pci416_read_intcsrreg; +TFP_pci416_set_intcsrreg pci416_set_intcsrreg; +TFP_pci416_read_mcsrreg pci416_read_mcsrreg; +TFP_pci416_set_mcsrreg pci416_set_mcsrreg; +TFP_pci416_read_mwarreg pci416_read_mwarreg; +TFP_pci416_set_mwarreg pci416_set_mwarreg; +TFP_pci416_read_mwtcreg pci416_read_mwtcreg; +TFP_pci416_set_mwtcreg pci416_set_mwtcreg; +TFP_pci416_copy_dmabuffer pci416_copy_dmabuffer; +TFP_pci416_get_dmabuf_hndl pci416_get_dmabuf_hndl; +TFP_pci416_pause_resume_dma pci416_pause_resume_dma; +TFP_pci416_close_dmabuf_hndl pci416_close_dmabuf_hndl; +TFP_pci416_map_dmabuf pci416_map_dmabuf; +TFP_pci416_unmap_dmabuf pci416_unmap_dmabuf; + +TFP_pci416_getError pci416_getError; + +TFP_get_adm_stats get_adm_stats; +TFP_read_pt_port_control_reg read_pt_port_control_reg; +TFP_write_pt_port_control_reg write_pt_port_control_reg; +TFP_read_pt_port_data read_pt_port_data; +TFP_write_pt_port_data write_pt_port_data; +TFP_enable_pt_dma_logic enable_pt_dma_logic; +TFP_start_stop_pt_dma_logic start_stop_pt_dma_logic; +TFP_set_pt_trigger_mode set_pt_trigger_mode; +TFP_set_pt_interrupt_mode set_pt_interrupt_mode; +TFP_enable_pt_eos_interrupt enable_pt_eos_interrupt; +TFP_enable_pt_half_full_interrupt enable_pt_half_full_interrupt; +TFP_read_pt_interrupt_status read_pt_interrupt_status; +TFP_set_pt_scan_select set_pt_scan_select; +TFP_set_pt_auto_increment set_pt_auto_increment; +TFP_set_pt_marker_select set_pt_marker_select; +TFP_set_pt_pretrigger set_pt_pretrigger; +TFP_disable_pt_interrupt disable_pt_interrupt; +TFP_clear_pt_interrupt clear_pt_interrupt; +TFP_enable_pt_interrupt enable_pt_interrupt; +TFP_set_pt_ad_clock_source set_pt_ad_clock_source; +TFP_set_pt_adm_control set_pt_adm_control; +TFP_set_pt_command_reg set_pt_command_reg; +TFP_read_pt_status_reg read_pt_status_reg; +TFP_pt_fifo_empty pt_fifo_empty; +TFP_pt_fifo_half_full pt_fifo_half_full; +TFP_pt_fifo_full pt_fifo_full; +TFP_read_pt_arm_ff read_pt_arm_ff; +TFP_read_pt_analog_trigger_status read_pt_analog_trigger_status; +TFP_read_pt_acquire_status read_pt_acquire_status; +TFP_read_pt_adm read_pt_adm; +TFP_set_pt_sample_count set_pt_sample_count; +TFP_set_pt_ad_channel set_pt_ad_channel; +TFP_set_pt_ad_scan_count set_pt_ad_scan_count; +TFP_set_pt_led set_pt_led; +TFP_reset_pt_fifos reset_pt_fifos; +TFP_set_pt_convert_enable set_pt_convert_enable; +TFP_set_pt_pll set_pt_pll; +TFP_set_pt_ad_clock_rate set_pt_ad_clock_rate; +TFP_set_pt_trigger_rate set_pt_trigger_rate; +TFP_generate_single_internal_trigger generate_single_internal_trigger; +TFP_reset_pt_trigger_timer reset_pt_trigger_timer; +TFP_set_pt_dac_voltage set_pt_dac_voltage; +TFP_set_pt_dac_code set_pt_dac_code; +TFP_read_pt_fifo read_pt_fifo; +TFP_read_pt_fifo_many read_pt_fifo_many; +TFP_reset_dma_fifo reset_dma_fifo; +TFP_set_dma_request_half_full set_dma_request_half_full; +TFP_enable_dma_transfers enable_dma_transfers; +TFP_set_dma_transfer_count set_dma_transfer_count; +TFP_read_dma_transfer_count read_dma_transfer_count; + +TFP_get_adm_inf get_adm_inf; +TFP_set_modes set_modes; +TFP_start_daq start_daq; +TFP_start_daq_irq start_daq_irq; +TFP_stop_daq stop_daq; +TFP_scan_status scan_status; + +#endif diff --git a/drivers/Datel-PCI416/416inc/416vxdio.h b/drivers/Datel-PCI416/416inc/416vxdio.h new file mode 100644 index 0000000..848b65c --- /dev/null +++ b/drivers/Datel-PCI416/416inc/416vxdio.h @@ -0,0 +1,1311 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 416vxdio.h: device driver pci416.vxd and pci416.sys +// IO control function definitions +// Copyright (c) Datel, Inc. 1997, 98 +// Platform: Win95, Win NT 4.0 +// Compiler: MVC4.0 + DDK +// Version: 3.0 +// Author: GS +// created: 2/28/97 +// modified: 7/9/98 +/////////////////////////////////////////////////////////////////////////////// +//@doc +/* For other languages than C: + Examine the function definitions of this header file. + Use the corresponding number as defined in this header file to access the + proper function. +*/ +#if defined (__cplusplus) +extern "C" +{ +#endif +#ifndef _416VXDIO_H +#define _416VXDIO_H + +/* Device driver IOCtl functions for NT */ + +#define CTL_CODE( DeviceType, Function, Method, Access ) ( \ + ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \ +) + +#define METHOD_BUFFERED 0 +#define METHOD_IN_DIRECT 1 +#define METHOD_OUT_DIRECT 2 +#define METHOD_NEITHER 3 + +#define FILE_ANY_ACCESS 0 +#define FILE_READ_ACCESS ( 0x0001 ) // file & pipe +#define FILE_WRITE_ACCESS ( 0x0002 ) // file & pipe + +#define PCI416_TYPE 40010 + + +// general pci functions +/****************************************************************************** + Function FIND_PCI_DEVICE_Proc + Returns the device numbers for a given device on the PCI bus. + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer points to an array of two DWORDs + The first array field contains the bus number. The second field + contains the VendorID in the lower and deviceID in the higher WORD + that the function searches for. + + lpDIOCtl->lpvOutBuffer points to a 32 byte large array that holds the + device numbers found. The field after the last device number found has the + value 0x0ff. + + lpDIOCtl->lpcbBytesReturned : If not NULL holds the bytes returned. + (== sizeof(lpDIOCtl->lpvOutBuffer)) + + Return value: + Error codes as defined in winerror.h + NOERROR +*******************************************************************************/ +#define FIND_PCI_DEVICE_PROC 1 +#define FIND_PCI_DEVICE_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x901, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) +/****************************************************************************** + Function GET_PCI_DEVICE_INFO_Proc + Returns the PCI configuration space for a given device on the PCI bus. + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (WORD*) lpDIOCtl->lpvInBuffer points to an array of two WORDs + The first array field contains the bus number. The second field + contains the device ID as returned by FIND_PCI_DEVICE_Proc. + + lpDIOCtl->lpvOutBuffer points to a PCI_TYPE_CONFIG structure that + will take the return data of the PCI configuration space. + The size of the structure has to be at least 64 bytes, but + no more than 260 bytes. + + lpDIOCtl->lpcbBytesReturned : If not NULL holds the bytes returned. + (== sizeof(lpDIOCtl->lpvOutBuffer)) + + Return value: + Error codes as defined in winerror.h +*******************************************************************************/ +#define GET_PCI_DEVICE_INFO_PROC 2 +#define GET_PCI_DEVICE_INFO_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x902, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) +/****************************************************************************** + Function WRITE_PCI_PORT_PROC + Writes a DWORD (4 Bytes) to an output port + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer points to an array of two DWORDs + The first array field contains the port address. The second field + contains the value to be written. + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! W A R N I N G !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + This function does NOT check if the input values as well as the + size of the input buffer (lpDIOCtl->lpvInBuffer) are valid! + Not valid values may cause system crashes! + If this function is used to perform operations on write only registers their + software shadow register will become invalid. Software shadow registers keep track + of the status of the write only registers. + + lpDIOCtl->lpvOutBuffer : should be zero + + lpDIOCtl->lpcbBytesReturned : not used, should be NULL + + Return value: + Error codes as defined in winerror.h + NOERROR +*******************************************************************************/ +#define WRITE_PCI_PORT_PROC 3 +#define WRITE_PCI_PORT_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x903, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) +/****************************************************************************** + Function READ_PCI_PORT_PROC + Reads a DWORD (4 Bytes) from an input port. + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer contains the port address. + (DWORD*) lpDIOCtl->lpvOutBuffer : takes the value read from the port. + + lpDIOCtl->lpcbBytesReturned : not used, should be NULL + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! W A R N I N G !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + This function does NOT check if the input values (the port address) is valid. + Not valid values may cause system crashes! + + Return value: + Error codes as defined in winerror.h + NOERROR +*******************************************************************************/ +#define READ_PCI_PORT_PROC 4 +#define READ_PCI_PORT_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x904, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) + +// specific pci416 functions +// misc. functions + +/****************************************************************************** + Function PCI416_COUNT_PROC + Returns the number of PCI416 Boards + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + lpDIOCtl->lpvInBuffer: should be 0 + (DWORD*)lpDIOCtl->lpvOutBuffer : holds the number of boards + + lpDIOCtl->lpcbBytesReturned : not used, should be NULL + + Return value: + Error codes as defined in winerror.h + NOERROR +*******************************************************************************/ +#define PCI416_COUNT_PROC 5 +#define PCI416_COUNT_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x905, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) +/****************************************************************************** + Function PCI416_GET_CM_DEVNODES_PROC + Returns the DEVNODEs assigned by the Configuration Manager for PCI416 Boards + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (INT*) lpDIOCtl->lpvInBuffer: Contains the index of the board. The order of the + boards is determind by the Windows 95 Configuration + Manager. + Values: + -1 : The devnodes of all the PCI416 boards + are returned in lpDIOCtl->lpvOutBuffer. + n>=0 : The devnode of the board with index n is + returned in lpDIOCtl->lpvOutBuffer. + + + + lpDIOCtl->lpvOutBuffer points to a DWORD array that + will take the devnode values. + + If lpDIOCtl->lpvInBuffer contains -1 the size of the array pointed by + lpDIOCtl->lpvOutBuffer must be large enough to hold all the devnodes. + + lpDIOCtl->lpcbBytesReturned : If not NULL holds the bytes returned. + (== sizeof(lpDIOCtl->lpvOutBuffer)) + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST + ERROR_INSUFFICIENT_BUFFER +*******************************************************************************/ +#define PCI416_GET_CM_DEVNODES_PROC 6 +#define PCI416_GET_CM_DEVNODES_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x906, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) // not implemented in NT +/****************************************************************************** + Function PCI416_GET_CMDEVINF_PROC + Returns the PCI configuration space information of the PCI416 Boards + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: Contains the index of the board. The order of the + boards is determind by the Windows 95 Configuration + Manager. + + lpDIOCtl->lpvOutBuffer points to a PCI_TYPE_CONFIG structure that + will take the return data of the PCI configuration space. + The size of the structure has to be at least 64 bytes, but + no more than 260 bytes. + + lpDIOCtl->lpcbBytesReturned : If not NULL holds the bytes returned. + (== sizeof(lpDIOCtl->lpvOutBuffer)) + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : no PCI416 board found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer + ERROR_INVALID_HANDLE : CM devnode not found + ERROR_GEN_FAILURE : Device is not functioning. + ERROR_INVALID_DATA : invalid pointer to out buffer or other + API function return error +*******************************************************************************/ +#define PCI416_GET_CMDEVINF_PROC 7 +#define PCI416_GET_CMDEVINF_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x907, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) // not implemented in NT +/****************************************************************************** + Function PCI416_GET_BADR_PROC + Returns base address array of the given PCI416 board. + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: Contains the index of the board. The order of the + boards is determind by the Windows 95 Configuration + Manager. + + (WORD*)lpDIOCtl->lpvOutBuffer: array of 6 WORDs that take base address values + + lpDIOCtl->lpcbBytesReturned : If not NULL holds the bytes returned. + (== sizeof(lpDIOCtl->lpvOutBuffer)) + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer +*******************************************************************************/ +#define PCI416_GET_BADR_PROC 8 +#define PCI416_GET_BADR_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x908, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) +/****************************************************************************** + Function PCI416_GETCAPS_Proc + Returns the Device capabilities (FIFO size, DMA buffer size, + type of ADM module, acquisition mode. + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: Contains the index of the board. The order of the + boards is determind by the Windows 95 Configuration + Manager. + + (DWORD*)lpDIOCtl->lpvOutBuffer: array of 4 DWORDs that take the device info + DWORD sizeFIFO; + DWORD bufsizeDMA; + DWORD indexADM; + DWORD acqmode; + + lpDIOCtl->lpcbBytesReturned : If not NULL holds the bytes returned. + (== sizeof(lpDIOCtl->lpvOutBuffer)) + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer + ERROR_CANTREAD : can't read registry +*******************************************************************************/ +#define PCI416_GETCAPS_PROC 9 +#define PCI416_GETCAPS_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x909, METHOD_BUFFERED, FILE_READ_ACCESS) + + +// pci416 register access functions + +/****************************************************************************** + Function PCI416_SET_CMDREG_PROC + Writes to the PCI416 command register + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: Array of two DWORDs. + The first field contains the index of the board. + The order of the boards is determind by the + Windows 95 Configuration Manager. + The second field holds the write mode. + (0 = overwrite, 1= OR, 2 = AND) + The third field holds the value to be written + to the command register. + DWORD index + DWORD mode + DWORD cmdval + + (DWORD*)lpDIOCtl->lpvOutBuffer: returns the current value of the shadow cmd. reg. + + lpDIOCtl->lpcbBytesReturned : not used, should be NULL + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer + ERROR_INVALID_PARAMETER : wrong mode +*******************************************************************************/ +#define PCI416_SET_CMDREG_PROC 10 +#define PCI416_SET_CMDREG_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x90a, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) + +/****************************************************************************** + Function PCI416_READ_STATUSREG_PROC + Reads the PCI416 Status register. + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: Contains the index of the board. The order of the + boards is determind by the Windows 95 Configuration + Manager. + + (DWORD*)lpDIOCtl->lpvOutBuffer: holds the return lower word + of the 32bit status register + + lpDIOCtl->lpcbBytesReturned : not used, should be NULL + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer +*******************************************************************************/ +#define PCI416_READ_STATUSREG_PROC 11 +#define PCI416_READ_STATUSREG_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x90b, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) + + +/****************************************************************************** + Function PCI416_SET_SMPLCNTR_PROC + Writes to the PCI416 sample counter register + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: Array of two DWORDs. + The first field contains the index of the board. + The order of the boards is determind by the + Windows 95 Configuration Manager. + The second field holds the value to be written + to the sample counter register. + Note: write samples-1 to the register + e.g 1024 samples -> write 1023 + DWORD index + DWORD regval + + lpDIOCtl->lpvOutBuffer: should be 0 + + lpDIOCtl->lpcbBytesReturned : not used, should be NULL + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer +*******************************************************************************/ +#define PCI416_SET_SMPLCNTR_PROC 12 +#define PCI416_SET_SMPLCNTR_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x90C, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) +/****************************************************************************** + Function PCI416_SET_CHANADR_PROC + Writes to the PCI416 channel address register + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: Array of three DWORDs. + The first field contains the index of the board. + The order of the boards is determind by the + Windows 95 Configuration Manager. + The second field holds the write mode. + (0 = overwrite, 1= OR, 2 = AND) + The third field holds the value to be written + to the register. + DWORD index + DWORD mode + DWORD regval + + (DWORD*)lpDIOCtl->lpvOutBuffer: returns the current value of the shadow reg. + + lpDIOCtl->lpcbBytesReturned : not used, should be NULL + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer + ERROR_INVALID_PARAMETER : wrong mode +*******************************************************************************/ +#define PCI416_SET_CHANADR_PROC 13 +#define PCI416_SET_CHANADR_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x90D, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) +/****************************************************************************** + Function PCI416_CLEAR_FIFO_PROC + Resets the A/D FIFO. + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: Contains the index of the board. The order of the + boards is determind by the Windows 95 Configuration + Manager. + + (DWORD*)lpDIOCtl->lpvOutBuffer: should be zero + + lpDIOCtl->lpcbBytesReturned : not used, should be NULL + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found +*******************************************************************************/ +#define PCI416_CLEAR_FIFO_PROC 14 +#define PCI416_CLEAR_FIFO_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x90e, METHOD_BUFFERED, FILE_WRITE_ACCESS) +/****************************************************************************** + Function PCI416_ENABLEAD_PROC + Writes to the PCI416 A/D convert enable register + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: Array of two DWORDs. + The first field contains the index of the board. + The order of the boards is determind by the + Windows 95 Configuration Manager. + If the value of second field 0 the A/D conversion + will be disabled otherwise enabled. + DWORD index + DWORD regval -> 0 disable, all other enable + + lpDIOCtl->lpvOutBuffer: should be 0 + + lpDIOCtl->lpcbBytesReturned : not used, should be NULL + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found +*******************************************************************************/ +#define PCI416_ENABLEAD_PROC 15 +#define PCI416_ENABLEAD_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x90f, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) + +/****************************************************************************** + Function PCI416_SET_PLLREG_PROC + Writes to the PCI416 PLL data register to set the output frequency of the + frequency synthesizer. + This function will cycle through the 3 counters and + set each counter value. + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: Array of 3 DWORDs. + The first field contains the index of the board. + The order of the boards is determind by the + Windows 95 Configuration Manager. + The next two field hold the data to be written + to the A and N counter. + DWORD index + DWORD valA + DWORD valN + + lpDIOCtl->lpvOutBuffer: should be 0 + + lpDIOCtl->lpcbBytesReturned : not used, should be NULL + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer +*******************************************************************************/ +#define PCI416_SET_PLLREG_PROC 16 +#define PCI416_SET_PLLREG_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x910, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) +/****************************************************************************** + Function PCI416_READ_FIFO_PROC + Reads a block of data from the FIFO. + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: Array of two DWORDs. + The first field contains the index of the board. + The order of the boards is determind by the + Windows 95 Configuration Manager. + The second field contains the number of samples + to be read. If count is larger than the FIFO size + the whole FIFO will be read. + DWORD index + DWORD count + + (DWORD*)lpDIOCtl->lpvOutBuffer: Array of count/2 DWORDs that takes the FIFO data. + + lpDIOCtl->lpcbBytesReturned : If not NULL holds the bytes returned. + (== sizeof(lpDIOCtl->lpvOutBuffer)) + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer +*******************************************************************************/ +#define PCI416_READ_FIFO_PROC 17 +#define PCI416_READ_FIFO_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x911, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) +/****************************************************************************** + Function PCI416_SETTIMER_PROC + Programs the 82C54 Timer + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: Array of 4 DWORDs. + The first field contains the index of the board. + The order of the boards is determind by the + Windows 95 Configuration Manager. + The next three fields hold the mode, the counter + selector and the counter values to be set. + DWORD index + DWORD mode + DWORD counter02 + DWORD counter1 + Note! + Mode has to be one of the values as defined in timer.h + TM_SINGLE_TRIGGER, generate a single internal trigger + TM_CONT_TRIGGER, generate continious internal trigger + TM_RESET_TRIGGER, reset (disable) trigger + TM_ADCLOCK set counter 2 for A/D clock + counter02 holds the counter value for counter 0 or 2 in TM_ADCLOCK mode + counter1 holds the counter value for counter 1 (not used in TM_ADCLOCK mode) + Counter values smaller than 2 will be set to 2 and values larger than 0xFFFF will + be set to 0xFFFF. + + (DWORD*)lpDIOCtl->lpvOutBuffer: should be 0 + + lpDIOCtl->lpcbBytesReturned : not used, should be NULL + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer + ERROR_INVALID_PARAMETER : wrong mode +*******************************************************************************/ +#define PCI416_SETTIMER_PROC 18 +#define PCI416_SETTIMER_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x912, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) + +/****************************************************************************** + Function PCI416_SET_PORTCTRREG_PROC + Sets the 82C55 port control register + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: Array of two DWORDs. + The first field contains the index of the board. + The order of the boards is determind by the + Windows 95 Configuration Manager. + The second field holds the register value to + be written. + DWORD index + DWORD regval + + lpDIOCtl->lpvOutBuffer: should be 0 + + lpDIOCtl->lpcbBytesReturned : not used, should be NULL + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer +*******************************************************************************/ +#define PCI416_SET_PORTCTRREG_PROC 19 +#define PCI416_SET_PORTCTRREG_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x913, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) +/****************************************************************************** + Function PCI416_GET_PORTCTRREG_PROC + Reads the 82C55 port control register back + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: Contains the index of the board. + The order of the boards is determind by the + Windows 95 Configuration Manager. + The second field holds the register value to + be written. + DWORD index + + + (DWORD*)lpDIOCtl->lpvOutBuffer: holds the return value + + lpDIOCtl->lpcbBytesReturned : not used, should be NULL + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found +*******************************************************************************/ +#define PCI416_GET_PORTCTRREG_PROC 20 +#define PCI416_GET_PORTCTRREG_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x914, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) +/****************************************************************************** + Function PCI416_READ_PORT_PROC + Reads from one of the 82C55 ports + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: Array of two DWORDs. + The first field contains the index of the board. + The order of the boards is determind by the + Windows 95 Configuration Manager. + The second field holds port to be read. + DWORD index + DWORD port (0==A, 1==B, 2==C) + + (DWORD*)lpDIOCtl->lpvOutBuffer: holds the return data. + + lpDIOCtl->lpcbBytesReturned : not used, should be NULL + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer + ERROR_INVALID_PARAMETER : wrong port +*******************************************************************************/ +#define PCI416_READ_PORT_PROC 21 +#define PCI416_READ_PORT_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x915, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) +/****************************************************************************** + Function PCI416_WRITE_PORT_PROC + Writes to one of the 82C55 ports + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: Array of 3 DWORDs. + The first field contains the index of the board. + The order of the boards is determind by the + Windows 95 Configuration Manager. + The second field holds port to be read and the + third field contains the data. + DWORD index + DWORD port (0==A, 1==B, 2==C) + DWORD data + + (DWORD*)lpDIOCtl->lpvOutBuffer: should be 0. + + lpDIOCtl->lpcbBytesReturned : not used, should be NULL + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer + ERROR_INVALID_PARAMETER : wrong port +*******************************************************************************/ +#define PCI416_WRITE_PORT_PROC 22 +#define PCI416_WRITE_PORT_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x916, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) +/****************************************************************************** + Function PCI416_WRITE_DAC_PROC + Sets the D/A converter register. + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: Array of two DWORDs. + The first field contains the index of the board. + The order of the boards is determind by the + Windows 95 Configuration Manager. + The second field holds port to be read. + DWORD index + DWORD data + + (DWORD*)lpDIOCtl->lpvOutBuffer: should be 0. + + lpDIOCtl->lpcbBytesReturned : not used, should be NULL + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer +*******************************************************************************/ +#define PCI416_WRITE_DAC_PROC 23 +#define PCI416_WRITE_DAC_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x917, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) +// FIFO polling functions + +/****************************************************************************** + Function PCI416_FIFO_STATUS_PROC + Reads the PCI416 FIFO status (bits 13-15 status reg). + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: Contains the index of the board. The order of the + boards is determind by the Windows 95 Configuration + Manager. + + (DWORD*)lpDIOCtl->lpvOutBuffer: holds the FIFO status bits + + lpDIOCtl->lpcbBytesReturned : not used, should be NULL + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer +*******************************************************************************/ +#define PCI416_FIFO_STATUS_PROC 24 +#define PCI416_FIFO_STATUS_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x918, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) +/****************************************************************************** + Function PCI416_CHECK_FIFOHF_PROC + Reads the PCI416 Half Full FIFO flag (bit 14 status reg). + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: Contains the index of the board. The order of the + boards is determind by the Windows 95 Configuration + Manager. + + (DWORD*)lpDIOCtl->lpvOutBuffer: holds the flag (0= FIFO is half full or greater) + (1= FIFO is less than half full) + + lpDIOCtl->lpcbBytesReturned : not used, should be NULL + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer +*******************************************************************************/ +#define PCI416_CHECK_FIFOHF_PROC 25 +#define PCI416_CHECK_FIFOHF_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x919, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) +// DMA functions +/****************************************************************************** + Function PCI416_SETUP_DMA_Proc + Setup DMA. + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: Array of (3)4 DWORDs that contain the index of + the board, the requested DMA buffer size and + the number of samples per trigger. The order + of the boards is determind by the Windows 95 + Configuration Manager. + If the requested size for the DMA buffers is + larger than the size of the buffer allocated + during bootup the allocated buffer size will + be used. The first bufsize field is used as + MWTC count. + If mode is DMA_SINGLE one buffer is set up for + DMA operation. In DMA_DOUBLE mode two buffers + are set up that can be used as ping-pong + buffers. If the sum of the requested buffer + size is larger than the allocated buffer size + the second buffer will be truncated. + DWORD index + DWORD mode; + DWORD bufsize[n] + n=1 if mode = DMA_SINGLE + n=2 if mode = DMA_DOUBLE + + + + (DWORD*)lpDIOCtl->lpvOutBuffer: Array of (3)4 DWORDs that contain + the DMA buffer info. + DWORD hndDMAbuf : handle to first DMA buffer + DWORD physAdrDMAbuf : physical address of DMA + buffer + DWORD bufsizeDMA[n] : size of DMA buffer(s) + used + n=1 if mode = DMA_SINGLE + n=2 if mode = DMA_DOUBLE + + lpDIOCtl->lpcbBytesReturned : If not NULL holds the bytes returned. + (== sizeof(lpDIOCtl->lpvOutBuffer)) + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer + ERROR_INVALID_PARAMETER : wrong mode + ERROR_INVALID_HANDLE : memory handle is invalid +******************************************************************************/ +#define PCI416_SETUP_DMA_PROC 26 +#define PCI416_SETUP_DMA_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x91A, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) +/****************************************************************************** + Function PCI416_STOP_DMA_PROC + Stops DMA without disabeling A/D conversion! + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: Contains the index of the board. The order of the + boards is determind by the Windows 95 Configuration + Manager. + + (DWORD*)lpDIOCtl->lpvOutBuffer: If the buffer is not zero it will hold the + value of the transfer count register (MWTC) + at the time the DMA transfer was stopped. + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found +*******************************************************************************/ +#define PCI416_STOP_DMA_PROC 27 +#define PCI416_STOP_DMA_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x91B, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) +/****************************************************************************** + Function PCI416_RELOAD_DMA_PROC + Reloads the DMA transfer count and start address! + + Parameters: + (DWORD*) lpDIOCtl->lpvInBuffer: Array of 2 DWORDS + DWORD index : Index of the board. The order of + the boards is determind by the + Windows 95 Configuration + Manager. + DWORD bufno : Valid values are 0 and 1 + Selects which buffer to reload + for DMA op. + If mode is DMA_SINGLE bufno has + no effect. + +(DWORD*)lpDIOCtl->lpvOutBuffer: Array of 3 DWORDs that contain + the DMA buffer info. + DWORD hndDMAbuf : handle to DMA buffer + DWORD physAdrDMAbuf : physical address of DMA + buffer + DWORD bufsizeDMA : size of DMA buffer + used + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer +*******************************************************************************/ +#define PCI416_RELOAD_DMA_PROC 28 +#define PCI416_RELOAD_DMA_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x91C, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) +/****************************************************************************** + Function PCI416_DMA_STATUS_PROC + Reads the PCI416 controller interrupt control/status register -> + DMA transfer status bit (bit18) + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: Contains the index of the board. The order of the + boards is determind by the Windows 95 Configuration + Manager. + + (DWORD*)lpDIOCtl->lpvOutBuffer: holds the DMA transfer status bits + (1= interrupt generated-> transfer complete + 0= Bus Master op. in progress) + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer +*******************************************************************************/ +#define PCI416_DMA_STATUS_PROC 29 +#define PCI416_DMA_STATUS_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x91D, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) +/****************************************************************************** + Function PCI416_READ_INTCSRREG_PROC + Reads the PCI controller interrupt control/status register + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: DWORD that contains the index of the board. + The order of the boards is determind by the + Windows 95 Configuration Manager. + + (DWORD*)lpDIOCtl->lpvOutBuffer: Return value of the register. + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer +*******************************************************************************/ +#define PCI416_READ_INTCSRREG_PROC 30 +#define PCI416_READ_INTCSRREG_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x91E, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) +/****************************************************************************** + Function PCI416_SET_INTCSRREG_PROC + Writes to the PCI controller interrupt control/status register + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: Array of three DWORDs. + The first field contains the index of the board. + The order of the boards is determind by the + Windows 95 Configuration Manager. + The second field holds the write mode+ read back + option. + (0 = overwrite, 1= OR, 2 = AND + + 0x80 to enable read back actual register + e.g. mode = 0x81 -> write register using OR op. + and then read register back) + The third field holds the value to be written + to the register. + DWORD index + DWORD mode + DWORD regval + + (DWORD*)lpDIOCtl->lpvOutBuffer: returns the current value of the shadow reg. + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer + ERROR_INVALID_PARAMETER : wrong mode +*******************************************************************************/ +#define PCI416_SET_INTCSRREG_PROC 31 +#define PCI416_SET_INTCSRREG_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x91F, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) +/****************************************************************************** + Function PCI416_READ_MCSRREG_PROC + Reads the PCI controller bus master control/status register + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: DWORD that contains the index of the board. + The order of the boards is determind by the + Windows 95 Configuration Manager. + + (DWORD*)lpDIOCtl->lpvOutBuffer: Return value of the register. + The value returned also contains the control + bits (8-31) that are saved in a shadow register + by the device driver. + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer +*******************************************************************************/ +#define PCI416_READ_MCSRREG_PROC 32 +#define PCI416_READ_MCSRREG_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x920, METHOD_BUFFERED, FILE_READ_ACCESS) +/****************************************************************************** + Function PCI416_SET_MCSRREG_PROC + Writes to the PCI controller bus master control/status register + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: Array of three DWORDs. + The first field contains the index of the board. + The order of the boards is determind by the + Windows 95 Configuration Manager. + The second field holds the write mode+ read back + option. + (0 = overwrite, 1= OR, 2 = AND + + 0x80 to enable read back actual register + e.g. mode = 0x81 -> write register using OR op. + and then read register back) + The third field holds the value to be written + to the register. + DWORD index + DWORD mode + DWORD regval + + (DWORD*)lpDIOCtl->lpvOutBuffer: returns the current value of the shadow reg. + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer + ERROR_INVALID_PARAMETER : wrong mode +*******************************************************************************/ +#define PCI416_SET_MCSRREG_PROC 33 +#define PCI416_SET_MCSRREG_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x921, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) +/****************************************************************************** + Function PCI416_COPY_DMABUFFER_PROC + Copy DMA buffer to another buffer. + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: Array of 4 DWORDs + DWORD index : Index of the board. The order of + the boards is determind by the + Windows 95 Configuration + Manager. + DWORD bufno : Valid values are 0 and 1 + Selects which buffer to copy. + If mode is DMA_SINGLE bufno has + no effect. + DWORD start : start byte + DWORD count : number of bytes to copy + + If start is larger than the size of the + selected buffer no data are copied and the + function returns ERROR_INVALID_HANDLE. + If start+count larger the size of the + selected buffer only the remaining byte + will be copied. + + (DWORD*)lpDIOCtl->lpvOutBuffer: DWORD hnddestbuf : Handle to the + desitination buffer. + The size of the + destination buffer + must be at least count + bytes! + + lpDIOCtl->lpcbBytesReturned : If not NULL holds the bytes copied. + + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer + ERROR_INVALID_HANDLE : memory handle is invalid +*******************************************************************************/ +#define PCI416_COPY_DMABUFFER_PROC 34 +#define PCI416_COPY_DMABUFFER_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x922, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) +/****************************************************************************** + Function PCI416_GET_DMABUF_HNDL_PROC + Returns the handle and size of the selected DMA buffer. + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: Array of 3 DWORDs + DWORD index : Index of the board. The order of + the boards is determind by the + Windows 95 Configuration + Manager. + DWORD bufno : Valid values are 0 and 1 + Selects which buffer to copy. + If mode is DMA_SINGLE bufno has + no effect. + DWORD offset: Offset of handle to be returned. + If offset is larger than the size of + the allocated buffer ERROR_INVALID_HANDLE + will be returned and hnddestbuf is + set to NULL. + + + (DWORD*)lpDIOCtl->lpvOutBuffer: Array of 2 DWORDs + DWORD hndbuf : Handle to the + DMA buffer positioned + at the offset byte. + + DWORD bufsize : Size of the DMA buffer - offset. + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer + ERROR_INVALID_HANDLE : memory handle is invalid +*******************************************************************************/ +#define PCI416_GET_DMABUF_HNDL_PROC 35 +#define PCI416_GET_DMABUF_HNDL_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x923, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) +/****************************************************************************** + Function PCI416_READ_MWARREG_PROC + Reads the PCI controller bus master write address register + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: DWORD that contains the index of the board. + The order of the boards is determind by the + Windows 95 Configuration Manager. + + (DWORD*)lpDIOCtl->lpvOutBuffer: Return value of the register. + + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer +*******************************************************************************/ +#define PCI416_READ_MWARREG_PROC 36 +#define PCI416_READ_MWARREG_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x924, METHOD_BUFFERED, FILE_READ_ACCESS) +/****************************************************************************** + Function PCI416_SET_MWARREG_PROC + Writes to the PCI controller bus master control/status register + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: Array of three DWORDs. + The first field contains the index of the board. + The order of the boards is determind by the + Windows 95 Configuration Manager. + The second field holds the write mode+ read back + option. + (0 = overwrite, 1= OR, 2 = AND) + The third field holds the value to be written + to the register. + DWORD index + DWORD mode + DWORD regval + + (DWORD*)lpDIOCtl->lpvOutBuffer: returns the current value of the shadow reg. + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer + ERROR_INVALID_PARAMETER : wrong mode +*******************************************************************************/ +#define PCI416_SET_MWARREG_PROC 37 +#define PCI416_SET_MWARREG_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x925, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) + +/****************************************************************************** + Function PCI416_READ_MWTCREG_PROC + Reads the PCI controller bus master write transfer count register + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: DWORD that contains the index of the board. + The order of the boards is determind by the + Windows 95 Configuration Manager. + + (DWORD*)lpDIOCtl->lpvOutBuffer: Return value of the register. + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer +*******************************************************************************/ +#define PCI416_READ_MWTCREG_PROC 38 +#define PCI416_READ_MWTCREG_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x926, METHOD_BUFFERED, FILE_READ_ACCESS) +/****************************************************************************** + Function PCI416_SET_MWTCREG_PROC + Writes to the PCI controller bus master write transfer count register + + Parameters: + LPDIOC lpDIOCtl pointer to DIOCPARAMETERS data structure. + + (DWORD*) lpDIOCtl->lpvInBuffer: Array of three DWORDs. + The first field contains the index of the board. + The order of the boards is determind by the + Windows 95 Configuration Manager. + The second field holds the write mode+ read back + option. + (0 = overwrite, 1= OR, 2 = AND) + The third field holds the value to be written + to the register. + DWORD index + DWORD mode + DWORD regval + + (DWORD*)lpDIOCtl->lpvOutBuffer: returns the current value of the shadow reg. + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer + ERROR_INVALID_PARAMETER : wrong mode +*******************************************************************************/ +#define PCI416_SET_MWTCREG_PROC 39 +#define PCI416_SET_MWTCREG_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x927, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) + + +/****************************************************************************** +@func pci416_dma_pause_resume + +@parm Index of board. Max. index = number of PCI-441 boards - 1 +@parm flags : 0 = resume 1 = pause + +@rdesc NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_SERVICE_REQUEST_TIMEOUT : time out accessing dma logic +@comm Pauses/ Resumes DMA +*******************************************************************************/ +#define PCI416_PAUSE_RESUME_DMA_PROC 40 +#define PCI416_PAUSE_RESUME_DMA_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x928, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) + +// NT only +/****************************************************************************** +@func PCI416_START_DMA + +@parm Array of two DWORDs: + TRGMODE: 0== PreTrigger, external trigger, + 1== Single Internal Trigger, + + BRDINDEX:Index of board. Max. index = number of PCI-416 boards - 1 + + +@rdesc NOERROR, + ERROR_DEV_NOT_EXIST, : PCI416 board not found + ERROR_SERVICE_REQUEST_TIMEOUT : time out accessing dma logic +@comm start DAQ asynchronously (Windows NT only) +*******************************************************************************/ +#define PCI416_START_DMA_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x929, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) + +/****************************************************************************** +@func PCI416_CLOSE_DMAHNDL_PROC_NT + +@rdesc NOERROR, + ERROR_DEV_NOT_EXIST, : PCI416 board not found + +@comm Close DMA handle opend with SETUP_DMA + Windows NT only +*******************************************************************************/ +#define PCI416_CLOSE_DMAHNDL_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x92A, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) + +/****************************************************************************** +@func PCI416_MAP_MEMORY_PROC_NT + +@parm : Array of two DWORDs: + param[0] returns handle to beginning of DMA buffer + param[1] size of mapped buffer + + +@rdesc NOERROR, + ERROR_DEV_NOT_EXIST, : PCI416 board not found + +@comm Maps portion of the DMA buffer into user space + Windows NT only +*******************************************************************************/ +#define PCI416_MAP_MEMORY_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x92B, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) + +/****************************************************************************** +@func PCI416_UNMAP_MEMORY_PROC_NT + +@parm : DWORD HANDLE : handle to be unmapped + +@rdesc NOERROR, + ERROR_DEV_NOT_EXIST, : PCI416 board not found + +@comm Unmaps mapped DMA buffer space + Windows NT only +*******************************************************************************/ +#define PCI416_UNMAP_MEMORY_PROC_NT \ + CTL_CODE(PCI416_TYPE, 0x92C, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) + +#if defined (__cplusplus) +} +#endif +#endif \ No newline at end of file diff --git a/drivers/Datel-PCI416/416inc/PCI416IO.H b/drivers/Datel-PCI416/416inc/PCI416IO.H new file mode 100644 index 0000000..d530e22 --- /dev/null +++ b/drivers/Datel-PCI416/416inc/PCI416IO.H @@ -0,0 +1,1032 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// pci416io.h: device driver IO control function definitions +// handles the funtion calls to the pci416.vxd device driver +// Copyright (c) Datel, Inc. 1997 +// Platform: Win95, Win NT +// Compiler: MVC5.0 + DDK +// Version: 3.0 +// Author: GS +// created: 3/3/97 +// modified: 7/9/98 +/////////////////////////////////////////////////////////////////////////////// +#ifndef PCI416IO_H +#define PCI416IO_H + +#define not_VxD + +#include + +#include "pcilib32.h" +#include "pci416df.h" + + +#ifndef LIB_TYPE +#define LIB_TYPE WINAPI +#endif + +#define DMA_THREAD_TERMINATED STILL_ACTIVE + 0x1000 +#define DMA_THREAD_FINISHED STILL_ACTIVE + 0x2000 +typedef struct { + HANDLE hndDevice; + HANDLE hEvent; + HANDLE hThread; + OVERLAPPED ovlp; + BOOL bSuccess; + BOOL bDone; + DWORD dwStatus; + DWORD dwSuccess; +} PCI416_DEVICE; + +typedef PCI416_DEVICE *PCI416_DEVICEPtr; + +/****************************************************************************** + Function pci416_init + Board initialization -> opens device driver initializes all PCI416 boards + present. + + !!!!! This function must be called first !!!!!!!!!!!! + + Parameters: + WORD *brdcount: Holds the number of boards found. + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST +*******************************************************************************/ +DWORD LIB_TYPE pci416_init(DWORD *brdcount); +DWORD LIB_TYPE init_nt(DWORD nDevices); +DWORD LIB_TYPE init_95(void); + +/****************************************************************************** + Function pci416_close + Closes the device driver. + + !!!!! This function must be called before exit DLL !!!!!!!!!!!! + + + Parameters: none + + + Return value: + Error codes as defined in winerror.h + NOERROR +*******************************************************************************/ +DWORD LIB_TYPE pci416_close(DWORD brdindex); + + +// general pci functions +/****************************************************************************** + Function find_pci_device + Returns the device numbers for a given device on the PCI bus. + + Parameters: + Inputs: + WORD busno: PCI bus number. + WORD venid: Vendor ID + WORD deviceid: Device ID + WORD bufsize : size of output buffer + + Outputs: + BYTE *buf: Array that holds the + device numbers found. The field after the last device found + has the value 0x0ff. + The size of the array must be the number of + devices expected +1. + -> minimum size: 2 + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST + ERROR_INSUFFICIENT_BUFFER +*******************************************************************************/ +DWORD LIB_TYPE find_pci_device(WORD busno, WORD venid, WORD deviceid, WORD bufsize, + BYTE *buf); + +/****************************************************************************** + Function get_pci_device_info + Returns the PCI configuration space for a given device on the PCI bus. + + Parameters: + Inputs: + WORD busno: PCI bus number. + WORD devno: Device number + + Outputs: + PCI_CONFIG_SPACE *conf: Pointer to a PCI_CONFIG_SPACE structure that + will take the return data of the PCI configuration + space. + The size of the structure has to be at least 64 bytes, + but no more than 260 bytes. + + + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST + ERROR_INSUFFICIENT_BUFFER +*******************************************************************************/ +DWORD LIB_TYPE get_pci_device_info(WORD busno, WORD devno, + PCI_CONFIG_SPACE *conf); + +/****************************************************************************** + Function write_pci_port + Writes a DWORD (4 Bytes) to an output port + + Parameters: + Inputs: + DWORD portadr: port address + DWORD data: data to be written + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! W A R N I N G !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + This function does NOT check if the input values are valid! + Not valid values may cause system crashes! + If this function is used to perform operations on write only registers on PCI416 + boards than their software shadow register will become invalid. + Software shadow registers keep track of the status of the write only registers. + + Return value: + Error codes as defined in winerror.h + NOERROR +*******************************************************************************/ +DWORD LIB_TYPE write_pci_port(DWORD portadr, DWORD data); + +/****************************************************************************** + Function READ_PCI_PORT_proc + Reads a DWORD (4 Bytes) from an input port + + Parameters: + Inputs: + DWORD portadr: port address + Outputs: + DWORD *data: Data read. + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! W A R N I N G !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + This function does NOT check if the input values (the port address) is valid. + Not valid values may cause system crashes! + + Return value: + Error codes as defined in winerror.h + NOERROR +*******************************************************************************/ +DWORD LIB_TYPE read_pci_port(DWORD portadr, DWORD *data); + +// specific pci416 functions +// misc. functions + +/****************************************************************************** + Function pci416_count + Returns the number of PCI416 Boards + + Parameters: + Inputs: none + + Outputs: + DWORD *brdcount: number of PCI416 boards found + + Return value: + Error codes as defined in winerror.h + NOERROR +*******************************************************************************/ +DWORD LIB_TYPE pci416_count(DWORD *brdcount); + +/****************************************************************************** + Function pci416_get_cm_devnodes + Returns the DEVNODEs assigned by the Configuration Manager for PCI416 Boards + + Parameters: + Inputs: + INT brdindex: Index of board. + Values: + -1 : The devnodes of all the PCI416 boards + are returned in devnodeptr. + n>=0 : The devnode of the board with index n is + returned in devnodeptr. + WORD bufsize: Size of the output array. + Outputs: + DWORD *devnodebuf: Array that takes the devnodes returned. + The array must be large enough to hold the data + returned. + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST + ERROR_INSUFFICIENT_BUFFER +*******************************************************************************/ +DWORD LIB_TYPE pci416_get_cm_devnodes(INT brdindex, WORD bufsize, + DWORD *devnodebuf); + +/****************************************************************************** + Function DWORD pci416_get_cmdevinf + Returns the PCI configuration space information of the PCI416 Boards + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + + Outputs: + PCI_CONFIG_SPACE *conf: Pointer to a PCI_CONFIG_SPACE structure that + will take the return data of the PCI configuration + space. + The size of the structure has to be at least 64 bytes, + but no more than 260 bytes. + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : no PCI416 board found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer + ERROR_INVALID_HANDLE : CM devnode not found + ERROR_GEN_FAILURE : Device is not functioning. + ERROR_INVALID_DATA : invalid pointer to out buffer or other + API function return error +*******************************************************************************/ +DWORD LIB_TYPE pci416_get_cmdevinf(DWORD brdindex, PCI_CONFIG_SPACE *conf); + +/****************************************************************************** + Function pci416_get_badr + Returns base address array of the given PCI416 board. + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + WORD bufsize : Size of output buffer. + Outputs: + WORD *badrbuf: Array that take base address values. The array must hold + at least 6 values! + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer +*******************************************************************************/ +DWORD LIB_TYPE pci416_get_badr(DWORD brdindex, WORD bufsize, WORD *badrbuf); + +/****************************************************************************** + Function pci416_getcaps + Returns the Device capabilities (FIFO size, DMA buffer size, + type of ADM module, acquisition mode. + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + WORD bufsize : Size of output buffer. + + Outputs: + DWORD *buf: array of 4 DWORDs that take the device info + DWORD sizeFIFO; + DWORD bufsizeDMA; + DWORD indexADM; + DWORD acqmode; + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer + ERROR_CANTREAD : can't read registry +*******************************************************************************/ +DWORD LIB_TYPE pci416_getcaps(DWORD brdindex, WORD bufsize, DWORD *buf); + +// pci416 register access functions + +/****************************************************************************** + Function pci416_set_cmdreg + Writes to the PCI416 command register + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + WORD mode : write mode (0 = overwrite, 1= OR, 2 = AND) + DWORD regval: value to be written + + Outputs: + DWORD *shregval: Current value of the shadow cmd. reg. + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INVALID_PARAMETER : wrong mode +*******************************************************************************/ +DWORD LIB_TYPE pci416_set_cmdreg(DWORD brdindex, WORD mode, DWORD regval, + DWORD *shregval); + +/****************************************************************************** + Function pci416_read_statusreg + Reads the PCI416 Status register. + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + + Outputs: + DWORD *regval: Return value of the 32bit status register. + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found +*******************************************************************************/ +DWORD LIB_TYPE pci416_read_statusreg(DWORD brdindex, DWORD *regval); + +/****************************************************************************** + Function pci416_set_smplcntr + Writes to the PCI416 sample counter register + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + DWORD samples : Number of samples. + Note: Functions writes samples-1 to the register + e.g 1024 samples -> writes 1023 + see PCI-416 documentation + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found +*******************************************************************************/ +DWORD LIB_TYPE pci416_set_smplcntr(DWORD brdindex, DWORD samples); + +/****************************************************************************** + Function pci416_set_chanadr + Writes to the PCI416 channel address register + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + WORD mode : write mode (0 = overwrite, 1= OR, 2 = AND) + DWORD regval: value to be written + + Outputs: + DWORD *shregval: Current value of the shadow register. + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INVALID_PARAMETER : wrong mode +*******************************************************************************/ +DWORD LIB_TYPE pci416_set_chanadr(DWORD brdindex, WORD mode, DWORD regval, + DWORD *shregval); + +/****************************************************************************** + Function pci416_clear_fifo + Resets the A/D FIFO. + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found +*******************************************************************************/ +DWORD LIB_TYPE pci416_clear_fifo(DWORD brdindex); + +/****************************************************************************** + Function pci416_enablead + Writes to the PCI416 A/D convert enable register + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + BOOL enable : TRUE= enable, FALSE=disable + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found +*******************************************************************************/ +DWORD LIB_TYPE pci416_enablead(DWORD brdindex, BOOL enable); + +/****************************************************************************** + Function pci416_set_pllreg + Writes to the PCI416 PLL data register to set the output frequency of the + frequency synthesizer. + This function will cycle through the 3 counters and + set each counter value. + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + DWORD valA : Counter value Counter A + DWORD valN : Counter value Counter N + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found +*******************************************************************************/ +DWORD LIB_TYPE pci416_set_pllreg(DWORD brdindex, DWORD valA, DWORD valN); + +/****************************************************************************** + Function pci416_read_fifo + Reads a block of data from the FIFO. + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + WORD count : Number of samples to be read. + If count is larger than the FIFO size + the whole FIFO will be read. + + DWORD *buf: Array of count/2 DWORDs that takes the FIFO data. + The buffer size must be at least 2*count bytes! + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer +*******************************************************************************/ +DWORD LIB_TYPE pci416_read_fifo(DWORD brdindex, WORD count, DWORD *buf); + +/****************************************************************************** + Function pci416_set_timer + Programs the 82C54 Timer + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + WORD mode : Mode has to be one of the values as defined in timer.h + TM_SINGLE_TRIGGER, generate a single internal trigger + TM_CONT_TRIGGER, generate continious internal trigger + TM_RESET_TRIGGER, reset (disable) trigger + TM_ADCLOCK set counter 2 for A/D clock + WORD counter02: Counter02 holds the counter value for counter 0 + or counter 2 in TM_ADCLOCK mode respectively. + WORD counter1 : Counter1 holds the counter value for counter 1 + (not used in TM_ADCLOCK mode). + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INVALID_PARAMETER : wrong mode +*******************************************************************************/ +DWORD LIB_TYPE pci416_set_timer(DWORD brdindex, WORD mode, + WORD counter02, WORD counter1); + + +/****************************************************************************** + Function pci416_set_portctrreg + Sets the 82C55 port control register + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + DWORD regval : Register value to be written. + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found +*******************************************************************************/ +DWORD LIB_TYPE pci416_set_portctrreg(DWORD brdindex, DWORD regval); + +/****************************************************************************** + Function pci416_get_portctrreg + Reads the 82C55 port control register back + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + + Outputs: + DWORD *regval : Holds the register value read. + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found +*******************************************************************************/ +DWORD LIB_TYPE pci416_get_portctrreg(DWORD brdindex, DWORD *regval); + +/****************************************************************************** + Function pci416_read_port + Reads from one of the 82C55 ports + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + WORD port : (0==A, 1==B, 2==C) + + Outputs: + DWORD *data : Holds the data read. + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INVALID_PARAMETER : wrong port +*******************************************************************************/ +DWORD LIB_TYPE pci416_read_port(DWORD brdindex, WORD port, DWORD *data); + +/****************************************************************************** + Function pci416_write_port + Writes to one of the 82C55 ports + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + WORD port : (0==A, 1==B, 2==C) + DWORD data : Data to be written. + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INVALID_PARAMETER : wrong port +*******************************************************************************/ +DWORD LIB_TYPE pci416_write_port(DWORD brdindex, WORD port, DWORD data); + +/****************************************************************************** + Function pci416_write_dac + Sets the D/A converter register. + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + WORD data : Data to be written. + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found +*******************************************************************************/ +DWORD LIB_TYPE pci416_write_dac(DWORD brdindex, WORD data); + +// FIFO polling functions + +/****************************************************************************** + Function pci416_fifo_status + Reads the PCI416 FIFO status (bits 13-15 status reg). + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + + Outputs: + DWORD *data : Holds the FIFO status bits. + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found +*******************************************************************************/ +DWORD LIB_TYPE pci416_fifo_status(DWORD brdindex, DWORD *data); + +/****************************************************************************** + Function pci416_check_fifohf + Reads the PCI416 Half Full FIFO flag (bit 14 status reg). + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + + Outputs: + DWORD *data : Holds the status of the FIFO HF flag. + (0= FIFO is half full or greater) + (1= FIFO is less than half full) + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found +*******************************************************************************/ +DWORD LIB_TYPE pci416_check_fifohf(DWORD brdindex, DWORD *data); + +// DMA functions +/****************************************************************************** + Function pci416_setup_dma + Setup DMA. + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + DWORD mode: DMA_SINGLE or DMA_DOUBLE + DWORD *bufsize: requested size of DMA buffer(s) + + + Outputs: + DWORD *bufsize: size of allocated DMA buffer(s) + DWORD *hndDMAbuf : handle to first DMA buffer + + Note: if mode = DMA_DOUBLE bufsize must point to an array of 2 DWORDs! + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer + ERROR_INVALID_PARAMETER : wrong mode + ERROR_INVALID_HANDLE : memory handle is invalid +*******************************************************************************/ +DWORD LIB_TYPE pci416_setup_dma(DWORD brdindex, DWORD mode, DWORD *bufsize, + DWORD *hndDMAbuf); + + +/****************************************************************************** + Function pci416_stop_dma + Stops the A/D conversion and DMA! + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + + Outputs: + DWORD *tcount: Remainder value of transfer count register after DMA stopped. + Parameter can be NULL. + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found +*******************************************************************************/ +DWORD LIB_TYPE pci416_stop_dma(DWORD brdindex, DWORD *tcount); + +/****************************************************************************** + Function pci416_reload_dma + reloads the DMA transfer count and start address! + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + DWORD bufno : Valid values are 0 and 1 + Selects which buffer to reload for DMA operation. + If setup mode is DMA_SINGLE bufno has no effect. + + Outputs: + DWORD *bufsize: size of allocated DMA buffer + DWORD *hndDMAbuf : handle to DMA buffer + + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found +*******************************************************************************/ +DWORD LIB_TYPE pci416_reload_dma(DWORD brdindex, DWORD bufno, + DWORD *bufsize, DWORD *hndDMAbuf); + +/****************************************************************************** + Function pci416_dma_status + Reads the PCI416 controller interrupt control/status register -> + DMA transfer status bit (bit18) + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + + Outputs: + DWORD *data : Holds the DMA transfer status bits + (1= interrupt generated-> transfer complete + 0= Bus Master op. in progress) + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found +*******************************************************************************/ +DWORD LIB_TYPE pci416_dma_status(DWORD brdindex, DWORD *data); + +/****************************************************************************** + Function pci416_read_intcsrreg + Reads the PCI controller interrupt control/status register + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + + Outputs: + DWORD *data : Holds register data read. + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found +*******************************************************************************/ +DWORD LIB_TYPE pci416_read_intcsrreg(DWORD brdindex, DWORD *data); + +/****************************************************************************** + Function pci416_set_intcsrreg + Writes to the PCI controller interrupt control/status register + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + WORD mode : write mode (0 = overwrite, 1= OR, 2 = AND + + 0x80 to enable read back actual register + e.g. mode = 0x81 -> write register using OR op. + and then read register back) + DWORD regval: value to be written + + Outputs: + DWORD *shregval: Current value of the shadow INTCSR reg. + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INVALID_PARAMETER : wrong mode +*******************************************************************************/ +DWORD LIB_TYPE pci416_set_intcsrreg(DWORD brdindex, WORD mode, DWORD regval, + DWORD *shregval); + +/****************************************************************************** + Function pci416_read_mcsrreg + Reads the PCI controller bus master control/status register + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + + Outputs: + DWORD *data : Holds register data read. + The value returned also contains the control + bits (8-31) that are saved in a shadow register + by the device driver. + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found +*******************************************************************************/ +DWORD LIB_TYPE pci416_read_mcsrreg(DWORD brdindex, DWORD *data); + +/****************************************************************************** + Function pci416_set_mcsrreg + Writes to the PCI controller bus master control/status register + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + WORD mode : write mode (0 = overwrite, 1= OR, 2 = AND + + 0x80 to enable read back actual register + e.g. mode = 0x81 -> write register using OR op. + and then read register back) + DWORD regval: value to be written + + Outputs: + DWORD *shregval: Current value of the shadow reg. + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INVALID_PARAMETER : wrong mode +*******************************************************************************/ +DWORD LIB_TYPE pci416_set_mcsrreg(DWORD brdindex, WORD mode, DWORD regval, + DWORD *shregval); + +/****************************************************************************** +func pci416_read_mwarreg + +parm Index of board. Max. index = number of PCI-416 boards - 1 + +parm Holds register data read. + +rdesc + Return value: + Error codes as defined in winerror.h - + NOERROR, + ERROR_DEV_NOT_EXIST : PCI416 board not found + +comm Reads the PCI controller bus master write address register (MWAR) +*******************************************************************************/ +DWORD LIB_TYPE pci416_read_mwarreg(DWORD brdindex, DWORD *data); + +/****************************************************************************** +func pci416_set_mwarreg + +parm Index of board. Max. index = number of PCI-416 boards - 1 +parm write mode (0 = overwrite, 1= OR, 2 = AND +parm value to be written +parm Current value of the shadow reg. + +rdesc NOERROR, + ERROR_DEV_NOT_EXIST : PCI416 board not found , + ERROR_INVALID_PARAMETER : wrong mode + +comm Writes to the PCI controller bus master write address SHADOW register + NOTE: Normally the device driver handles the DMA buffer Addressing. This function + was created to allow USERS with dedicated memory on other devices to redirect + the pci416 data during DMA to such devices. The value is written to a shadow register. + If the shadow register is non-zero, the contents of the shadow register is loaded into + the hardware register when the pci416_setup_dma function is called. + This value must be a Physical not a Virtual address. + Setting the value to zero will cause pci416_setup_dma to load the address of the + reserved DMA buffer.By default the shadow register is loaded with zeros so this + function does not have to be called under "normal" circumstances. +*******************************************************************************/ +DWORD LIB_TYPE pci416_set_mwarreg(DWORD brdindex, WORD mode, DWORD regval, + DWORD *shregval); + +/****************************************************************************** +func pci416_read_mwtcreg + +parm Index of board. Max. index = number of PCI-416 boards - 1 +parm Holds register data read. + + +rdesc NOERROR, + ERROR_DEV_NOT_EXIST : PCI416 board not found + +comm Reads the PCI controller bus master write transfer count register (MWTC) +*******************************************************************************/ +DWORD LIB_TYPE pci416_read_mwtcreg(DWORD brdindex, DWORD *data); + +/****************************************************************************** +func pci416_set_mwtcreg + +parm Index of board. Max. index = number of PCI-416 boards - 1 +parm write mode (0 = overwrite, 1= OR, 2 = AND) +parm value to be written +parm Current value of the shadow reg. + +rdesc NOERROR, + ERROR_DEV_NOT_EXIST : PCI416 board not found, + ERROR_INVALID_PARAMETER : wrong mode + +comm Writes to the PCI controller bus master write transfer count register (MWTC) + NOTE: Normally the device driver handles the DMA count register. This function + was created to allow USERS with dedicated memory on other devices to redirect + the pci416 data during DMA to such devices. + The value is written to a shadow register. + If the shadow register is non-zero, the contents of the shadow register + is loaded into the hardware register when the pci416_setup_dma function is called. + + Setting the value to zero will cause pci416_setup_dma to load the count from the + DMA bufsize . By default the shadow register is loaded with zeros so this + function does not have to be called under "normal" circumstances. +*******************************************************************************/ +DWORD LIB_TYPE pci416_set_mwtcreg(DWORD brdindex, WORD mode, DWORD regval, + DWORD *shregval); + +/****************************************************************************** + Function pci416_copy_dmabuffer + reloads the DMA transfer count and start address! + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + DWORD bufno : Valid values are 0 and 1 + Selects from which buffer to copy. + If setup mode is DMA_SINGLE bufno has no effect. + DWORD start : start byte + DWORD *count : number of bytes to copy + + If start is larger than the size of the + selected buffer no data are copied and the + function returns ERROR_INVALID_HANDLE. + If start+count larger the size of the + selected buffer only the remaining byte + will be copied. + DWORD *pDest : Destination buffer + The size of the destibation buffer must be at least + count bytes. + Outputs: + DWORD *count: number of bytes copied + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INVALID_HANDLE : memory handle is invalid +*******************************************************************************/ +DWORD LIB_TYPE pci416_copy_dmabuffer(DWORD brdindex, DWORD bufno, + DWORD start, DWORD *count, + DWORD *pDest ); + +/****************************************************************************** + Function pci416_get_dmabuf_hndl + Returns the handle and size of the selected DMA buffer. + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + DWORD bufno : Valid values are 0 and 1 + Selects from which buffer to copy. + If setup mode is DMA_SINGLE bufno has no effect. + + DWORD offset: Offset of handle to be returned. + If offset is larger than the size of + the allocated buffer ERROR_INVALID_HANDLE + will be returned and hnddestbuf is + set to NULL. + + Outputs: + + DWORD *pHndl : Handle to the DMA buffer positioned at the offset byte. + + DWORD bufsize : Size of the DMA buffer - offset. + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INSUFFICIENT_BUFFER : invalid size of in/out buffer + ERROR_INVALID_HANDLE : memory handle is invalid +*******************************************************************************/ +DWORD LIB_TYPE pci416_get_dmabuf_hndl(DWORD brdindex, DWORD bufno, + DWORD offset, DWORD *pHndl, + DWORD *bufsize); + +/****************************************************************************** + Function pci416_close_dmabuf_hndl + Closes the handle to the DMA buffer that was returnd by pci416_setup_dma. + Windows NT only. + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found +*******************************************************************************/ +DWORD LIB_TYPE pci416_close_dmabuf_hndl(DWORD brdindex); + +/****************************************************************************** + Function pci416_map_dmabuf + Map portions of the DMA buffer into user space. + Windows NT only. + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + DWORD size : size of DMA buffer to map into user space + Output: + PVOID pHndl : user space handle + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INVALID_HANDLE : memory handle is invalid +*******************************************************************************/ +DWORD LIB_TYPE pci416_map_dmabuf(DWORD brdindex, DWORD *pHndl, DWORD size); + +/****************************************************************************** + Function pci416_unmap_dmabuf + Unmap DMA buffer space that was mapped into user space with pci416_map_dmabuf. + Windows NT only. + + Parameters: + Inputs: + DWORD brdindex: Index of board. Max. index = number of PCI-416 boards - 1 + PVOID pHndl : handle to unmap + + Return value: + Error codes as defined in winerror.h + NOERROR + ERROR_DEV_NOT_EXIST : PCI416 board not found + ERROR_INVALID_HANDLE : memory handle is invalid +*******************************************************************************/ +DWORD LIB_TYPE pci416_unmap_dmabuf(DWORD brdindex, DWORD Hndl); + +/****************************************************************************** + Function pci416_getError + Returns the last error and clears the global error variable. + + Parameters: + Inputs: + none + + Outputs: + LPTSTR *str : pointer that gets the address of the error message + (not implemented for now) + + Return value: + Error codes as defined in winerror.h +*******************************************************************************/ +DWORD LIB_TYPE pci416_getError(LPTSTR *str); + +/****************************************************************************** +@func start_daq_irq + +@parm Index of board. Max. index = number of PCI-416 boards - 1 +@parm 0== PreTrigger, external trigger, + 1== Single Internal Trigger, + 2== Multi Internal Trigger + other default to single trigger +@parm trigger rate for multi trigger + + +@rdesc NOERROR, + ERROR_DEV_NOT_EXIST, : PCI416 board not found + ERROR_SERVICE_REQUEST_TIMEOUT : time out accessing dma logic +@comm start DAQ asynchronously (Windows NT only) +*******************************************************************************/ +DWORD LIB_TYPE start_daq_irq(DWORD brdindex,DWORD TriggerMode, + REALTYPE *TriggerRate); + +#endif \ No newline at end of file diff --git a/drivers/Datel-PCI416/416inc/PCI416_32dll.h b/drivers/Datel-PCI416/416inc/PCI416_32dll.h new file mode 100644 index 0000000..c2cb0d0 --- /dev/null +++ b/drivers/Datel-PCI416/416inc/PCI416_32dll.h @@ -0,0 +1,178 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// pci416_32dll.h: Header file for DLL functions that perform board setup and +// data acquisition using FIFO polling. +// Copyright (c) Datel, Inc. 1997 +// Platform: Win95, Win NT +// Compiler: MVC4.0 + DDK +// Version: 3.0 +// Author: GS +// created: 2/28/97 +// modified: 7/9/98 +/////////////////////////////////////////////////////////////////////////////// + +#ifndef PCI416_32DLL_H +#define PCI416_32DLL_H + +#include + +// REALTYPE is defined as double (size: 8 bytes) + +/****************************************************************************** +Function: get_adm_inf + +Description: Returns specific info of installed A/D module. + Use this function instead the get_adm_stats function for DLL calls. + The get_adm_stats function expects a pointer to an ADM_STATS + structure which is only available in C programs. Other compilers and + programs may generate structures with different size and boundaries + that could cause sytem crashes! + +Inputs: DWORD brdindex : Index of board. + Max. index = number of PCI-416 boards - 1 + Char *model : ADM model + WORD *bits : Resolution in bits + WORD *channels : no.of channels + WORD *shortcycle : 1== can be short cycled + REALTYPE *fmax_single: max. sample freq. single channel mode + REALTYPE *fmax_scan: max. sample freq. multi channel mode + +return: Error status as define in winerror.h +NOERROR +ERROR_INVALID_HANDLE +ERROR_DEV_NOT_EXIST +******************************************************************************/ +DWORD LIB_TYPE get_adm_inf(DWORD brdindex, WORD *model, WORD *bits, WORD *channels, + WORD *shortcycle, REALTYPE *fmax_single, REALTYPE *fmax_scan); + +/****************************************************************************** +Function: set_modes + +Description: Setup board paramters: sample rate, trigger rate, clock and trigger + source etc. + +Inputs: DWORD brdindex : Index of board. + Max. index = number of PCI-416 boards - 1 + WORD ClockSrc : 0= INTERNAL, 1= EXTERNAL, + other : internal with sample rate set to + max. value in scan mode. + REALTYPE SampleRate : A/D sample rate, if src set to INTERNAL + If ClockSrc is not set to INTERNAL or + EXTERNAL the sample rate is set to + max. value in scan mode. + DWORD SamplesPerTrigger: Number of samples to collect + at each trigger pulse. + WORD TriggerSrc : 0= INTERNAL,1 = EXTERNAL, + 2= ANALOG Rising Edge, 3= ANALOG Falling Edge, + other: INTERNAL + WORD Channel : channel of acquisition in single channel mode + or last scan channel in scan mode if applicable + 0 <= Channel < number of channels + WORD PreTrigger : 1== PreTrigger + WORD Scan : 1== Scan mode + WORD Marker : 1== set marker bit + +return: Error status as define in winerror.h +NOERROR +ERROR_INVALID_HANDLE +ERROR_DEV_NOT_EXIST +******************************************************************************/ +DWORD LIB_TYPE set_modes (DWORD brdindex, + WORD ClockSrc, + REALTYPE SampleRate, + DWORD SamplesPerTrigger, + WORD TriggerSrc, + WORD Channel, + WORD PreTrigger, + WORD Scan, + WORD Marker); +/****************************************************************************** +Function: start_daq + +Description: Starts data acquisition. + All registers have to be set prior this function call with + set_mode! + +Inputs: DWORD brdindex : Index of board. + Max. index = number of PCI-416 boards - 1 + WORD TriggerMode : 0== PreTrigger, external trigger, + 1== Single Internal Trigger, + 2== Multi Internal Trigger + other default to single trigger + REALTYPE *TriggerRate: trigger rate for multi trigger +Output: REALTYPE *TriggerRate: actual trigger rate set + +Note: If PreTrigger was selected in set_modes only an external trigger + is valid. Calls other than start_daq(brdindex, 0) will result in + invalid data. + On the other hand if PreTrigger was NOT set in set_modes + a start_daq(brdindex, 0) call does not start the A/D conversion. + +return: Error status as define in winerror.h +NOERROR +******************************************************************************/ +DWORD LIB_TYPE start_daq(DWORD brdindex, WORD TriggerMode, + REALTYPE *TriggerRate); + + +/****************************************************************************** +Function: stop_daq + +Description: Stops data acquisition. + +Inputs: DWORD brdindex : Index of board. + Max. index = number of PCI-416 boards - 1 + +return: Error status as define in winerror.h +NOERROR +******************************************************************************/ +DWORD LIB_TYPE stop_daq(DWORD brdindex); + + +/****************************************************************************** +Function: scan_status + +Description: Acquires data by polling on the FIFO half full flag. + +Inputs: DWORD brdindex : Index of board. + Max. index = number of PCI-416 boards - 1 + WORD ClockSrc : C_INTERNAL, C_EXTERNAL, + default : internal with sample rate set to + max. value in scan mode. + REALTYPE SampleRate : A/D sample rate, if src set to C_INTERNAL + If ClockSrc is not set to C_INTERNAL or + C_EXTERNAL the sample rate is set to + max. value in scan mode. + DWORD TotalSamples : Total number of samples to collect + DWORD SamplesPerTrigger: Number of samples to collect + at each trigger pulse. + DWORD Frames : Number of triggers. + WORD TriggerSrc : T_INTERNAL,T_EXTERNAL,T_ANALOG, + default: T_INTERNAL + REALTYPE TriggerRate : Trigger rate + WORD Channel : channel of acquisition in single channel mode + or last scan channel in scan mode if applicable + 0 <= Channel < number of channels + BOOL PreTrigger : TRUE== PreTrigger + BOOL Scan : TRUE== Scan mode + BOOL Marker : TRUE== set marker bit + PVOID buffer : User buffer to store the A/D data + +return: state of the FIFO Full flag ( 0 = No overflow, 1 = FIFO FULL (overflow) + -1 error -> retreive error with pci416_getError +******************************************************************************/ +INT LIB_TYPE scan_status (DWORD brdindex, + WORD ClockSrc, + REALTYPE SampleRate, + DWORD TotalSamples, + DWORD SamplesPerTrigger, + DWORD Frames, + WORD TriggerSrc, + REALTYPE TriggerRate, + WORD Channel, + BOOL PreTrigger, + BOOL Scan, + BOOL Marker, + PVOID buffer); + +#endif diff --git a/drivers/Datel-PCI416/416inc/PCI416libW95.h b/drivers/Datel-PCI416/416inc/PCI416libW95.h new file mode 100644 index 0000000..15ebeab --- /dev/null +++ b/drivers/Datel-PCI416/416inc/PCI416libW95.h @@ -0,0 +1,477 @@ +/////////////////////////////////////////////////////////////////////////////// +// pci416libw95.h: Function definitions for Datel PCI-416 Control library +// for both the S593X PCI controller and the pass thru +// data acq.logic +// Copyright (c) Datel, Inc. 1997 +// Platform: Win95 +// Compiler: Microsoft Visual C++ V4.0 +// Version: 1.0 +// Author: GS +// created: 3/3/97 +// modified: 4/16/97 +// Comment: Ports DOS p416_lib to WIN32 +// TYPE_BOARD_CONFIG and TYPE_DAQ_CONFIG structures are obsolete +// and not supported. +// Boards are accessed by a simple board index number. +// A number of functions from the DOS library are not ported +// to Windows. These functions are marked as obsolete. See below. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef P416LIBW95_H +#define P416LIBW95_H + +#ifndef LIB_TYPE +#define LIB_TYPE WINAPI +#endif + +#ifndef REALTYPE +#define REALTYPE double +#endif + + +/****************************************************************************** + Function get_adm_stats + Returns the ADM characteristics. + + Parameters: + Input: + DWORD brdindex: index of board + + Output: + ADM_STATS *admstats : Pointer to ADM_STATS structure that gets the data. + + + Return value: 0 -> no Error + -1 -> Error retreive error with pci416_getError +*******************************************************************************/ +INT LIB_TYPE get_adm_stats(DWORD brdindex, ADM_STATS *admstats); +/***************************************************************************** + PASS THROUGH (A/D control) +*****************************************************************************/ +/////////////////////////////////////////////////////////////////////////////// +// reads 8255 port control register +/////////////////////////////////////////////////////////////////////////////// +DWORD LIB_TYPE read_pt_port_control_reg(DWORD brdindex); + +/////////////////////////////////////////////////////////////////////////////// +// set 8255 port control register +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE write_pt_port_control_reg(DWORD brdindex, UINT val); + +/////////////////////////////////////////////////////////////////////////////// +// Reads from one of the 8255 I/O registers -> 0=a, 1=b, 2=c +/////////////////////////////////////////////////////////////////////////////// +DWORD LIB_TYPE read_pt_port_data(DWORD brdindex, UINT prt); + +/////////////////////////////////////////////////////////////////////////////// +// write to one of the 8255 I/O registers -> 0=a, 1=b, 2=c +/////////////////////////////////////////////////////////////////////////////// +DWORD LIB_TYPE write_pt_port_data(DWORD brdindex, UINT prt, UINT val); + +/////////////////////////////////////////////////////////////////////////////// +// Enables / Disables auto PT(A/D Data) FIFO to Bridge fifo +// +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE enable_pt_dma_logic(DWORD brdindex, UINT md); + +/////////////////////////////////////////////////////////////////////////////// +// Suspends / Resumes auto PT(A/D Data) FIFO to Bridge fifo +// Returns 1 in case of time out otherwise 0 +// time out count is set to 100000 +/////////////////////////////////////////////////////////////////////////////// +DWORD LIB_TYPE start_stop_pt_dma_logic(DWORD brdindex, UINT md); + +/////////////////////////////////////////////////////////////////////////////// +// Sets the trigger mode field in the P.thru command register +// 0 - internal , 1 - ext. digital, 2 - ext. analog rising edge, +// 3 - ext. analog falling edge +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE set_pt_trigger_mode(DWORD brdindex, UINT md); + +/////////////////////////////////////////////////////////////////////////////// +// Sets the Pass thru interrupt condition +// 0 - FIFO HF, 1 - End of Scan +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE set_pt_interrupt_mode(DWORD brdindex, UINT md); + +/////////////////////////////////////////////////////////////////////////////// +// enables pass thru to PCI interrupt on end of scan +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE enable_pt_eos_interrupt(DWORD brdindex); + +/////////////////////////////////////////////////////////////////////////////// +// enables pass thru to PCI interrupt on half full +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE enable_pt_half_full_interrupt(DWORD brdindex); + +/////////////////////////////////////////////////////////////////////////////// +// Returns 1 if a HF or EOS PT interrupt occured +/////////////////////////////////////////////////////////////////////////////// +DWORD LIB_TYPE read_pt_interrupt_status(DWORD brdindex); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// Sets/Resets the Pass Thru A/D scan select bit (used for ADM model A) +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE set_pt_scan_select(DWORD brdindex, UINT md); + +/////////////////////////////////////////////////////////////////////////////// +// Sets/resets the Pass thru A/D channel auto-increment bit +// 1 - Autoincrement, 0 - nope +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE set_pt_auto_increment(DWORD brdindex, UINT md); + +/////////////////////////////////////////////////////////////////////////////// +// Sets/resets the Pass thru A/D Marker mode select bit +// 1 - mark, 0 - no marker +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE set_pt_marker_select(DWORD brdindex, UINT md); + +/////////////////////////////////////////////////////////////////////////////// +// Enables/Disables the Pass thru A/D Pre-trigger mode +// 1 - Pre-trigger, 0 - no pre_trigger +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE set_pt_pretrigger(DWORD brdindex, UINT md); + +/////////////////////////////////////////////////////////////////////////////// +// Disables the Pass thru to Host(PCI) interrupt +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE disable_pt_interrupt(DWORD brdindex); + +/////////////////////////////////////////////////////////////////////////////// +// Clears the PCI interrupt on the S5933 Bridge +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE clear_pt_interrupt(DWORD brdindex); + +/////////////////////////////////////////////////////////////////////////////// +// Enables the Pass thru to Host(PCI) interrupt +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE enable_pt_interrupt(DWORD brdindex); + +/////////////////////////////////////////////////////////////////////////////// +// Set A/D clock source MUX +// FREQ SYNTH DIRECT --> A/D +// 0 20 - 40 MHz 2.5Khz res +// 1 10 - 20 Mhz 1.25 +// 2 5 - 10 625Hz res +// 3 Freq SYNTH -> 82C54 -> A/D +// useful for exact freq < 5 Mhz +// OTHERS +// 4 External Clock +// 5 20MHz x-tal -->a/d +// 6 10Mhz x-tal -->a/d +// 7 10 Mhz -> 8254-> a/d +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE set_pt_ad_clock_source(DWORD brdindex, UINT src); + +/////////////////////////////////////////////////////////////////////////////// +// Set A/D control bits +// +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE set_pt_adm_control(DWORD brdindex, UINT val); + +/////////////////////////////////////////////////////////////////////////////// +// Sets pass thru command register to desired value +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE set_pt_command_reg(DWORD brdindex,DWORD val); + +/* obsolete use pci416_set_cmdreg(brdindex, mode, val, &retval) instead +/////////////////////////////////////////////////////////////////////////////// +// Write current variable value to pass thru command register hardware +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE update_pt_command_reg(DWORD brdindex); + +/////////////////////////////////////////////////////////////////////////////// +// Write to the Pass-Thru Address Register Latch (ARL) +// sets hardware and software value of pt address (register select) register +/////////////////////////////////////////////////////////////////////////////// +// obsolete, use write_pci_port(DWORD portadr, DWORD data) instead +// get portadr with pci416_get_badr(DWORD brdindex, WORD *badrbuf, WORD bufsize); +void LIB_TYPE set_pt_address_reg(DWORD brdindex,DWORD addr); +*/ + +/////////////////////////////////////////////////////////////////////////////// +// Reads the PT. hardware status register +// returns 0xFFFFFFFF in case of error +// use pci416_getError to retreive error code +/////////////////////////////////////////////////////////////////////////////// +DWORD LIB_TYPE read_pt_status_reg(DWORD brdindex); + +/////////////////////////////////////////////////////////////////////////////// +// Returns 1 if pass thru fifo is empty +// returns 0xFFFFFFFF in case of error +// use pci416_getError to retreive error code +/////////////////////////////////////////////////////////////////////////////// +DWORD LIB_TYPE pt_fifo_empty(DWORD brdindex); + +/////////////////////////////////////////////////////////////////////////////// +// Returns 1 if pass thru fifo is half full +// returns 0xFFFFFFFF in case of error +// use pci416_getError to retreive error code +/////////////////////////////////////////////////////////////////////////////// +DWORD LIB_TYPE pt_fifo_half_full(DWORD brdindex); + +/////////////////////////////////////////////////////////////////////////////// +// Returns 1 if pass thru fifo is full +// returns 0xFFFFFFFF in case of error +// use pci416_getError to retreive error code +/////////////////////////////////////////////////////////////////////////////// +DWORD LIB_TYPE pt_fifo_full(DWORD brdindex); + +/////////////////////////////////////////////////////////////////////////////// +// Returns 1 if data PT A/D ARM FF is SET +/////////////////////////////////////////////////////////////////////////////// +DWORD LIB_TYPE read_pt_arm_ff(DWORD brdindex); + +/////////////////////////////////////////////////////////////////////////////// +// Returns 1 if ANATRG bit of PT -SR is high +/////////////////////////////////////////////////////////////////////////////// +DWORD LIB_TYPE read_pt_analog_trigger_status(DWORD brdindex); + +/////////////////////////////////////////////////////////////////////////////// +// Returns 1 if data ACQ bit in PT -CR is high +/////////////////////////////////////////////////////////////////////////////// +DWORD LIB_TYPE read_pt_acquire_status(DWORD brdindex); + +/////////////////////////////////////////////////////////////////////////////// +// Returns the AD Module given by DIP setting +/////////////////////////////////////////////////////////////////////////////// +DWORD LIB_TYPE read_pt_adm(DWORD brdindex); + +/////////////////////////////////////////////////////////////////////////////// +// Set A/D sample count register +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE set_pt_sample_count(DWORD brdindex, DWORD count); + +/////////////////////////////////////////////////////////////////////////////// +// Set A/D Channel field of pt channel register +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE set_pt_ad_channel(DWORD brdindex, UINT chan); + +/////////////////////////////////////////////////////////////////////////////// +// Set A/D Scan Count field of channel register +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE set_pt_ad_scan_count(DWORD brdindex, UINT count); + +/////////////////////////////////////////////////////////////////////////////// +// Sets Test LED bit of channel register +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE set_pt_led(DWORD brdindex, UINT val); + +/////////////////////////////////////////////////////////////////////////////// +// Reset the PT. A/D Fifo +/////////////////////////////////////////////////////////////////////////////// + +void LIB_TYPE reset_pt_fifos(DWORD brdindex); + +/////////////////////////////////////////////////////////////////////////////// +// Set/Reset PT. A/D convert enable bit +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE set_pt_convert_enable(DWORD brdindex, UINT val); + +/////////////////////////////////////////////////////////////////////////////// +// Sets output clock frequency of PLL hardware +// And sets A/D clock source to PLL output +/////////////////////////////////////////////////////////////////////////////// +UINT LIB_TYPE set_pt_pll(DWORD brdindex, REALTYPE freq); + +// obsolete, use pci416_set_pllreg(brdindex, valA, valN) instead +/////////////////////////////////////////////////////////////////////////////// +// writes current software value of the pll data register to hardware +// +/////////////////////////////////////////////////////////////////////////////// +//void LIB_TYPE update_pt_pll_reg(DWORD brdindex); + +// obsolete, use pci416_set_timer(DWORD brdindex, WORD mode, +// WORD counter02, WORD counter1); +/////////////////////////////////////////////////////////////////////////////// +// Updates 8254 counter register hardware with current software values +// +/////////////////////////////////////////////////////////////////////////////// +//UINT LIB_TYPE set_pt_timer(DWORD brdindex, UINT n,UINT mode,UINT count); + +/////////////////////////////////////////////////////////////////////////////// +// Set A/D Clock Rate +// Automatically sets best possible clock source mux and counter values +/////////////////////////////////////////////////////////////////////////////// +int LIB_TYPE set_pt_ad_clock_rate(DWORD brdindex,REALTYPE fs); + +/////////////////////////////////////////////////////////////////////////////// +// Set A/D Trigger Rate (timers 0,1) +// Returns (1) if error +// *actual = real trigger rate produced by hardware +/////////////////////////////////////////////////////////////////////////////// +UINT LIB_TYPE set_pt_trigger_rate(DWORD brdindex,REALTYPE ft, + REALTYPE *actual); + +/////////////////////////////////////////////////////////////////////////////// +// set trigger mode to internal +// generate single trigger +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE generate_single_internal_trigger(DWORD brdindex); + +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE reset_pt_trigger_timer(DWORD brdindex); + +/////////////////////////////////////////////////////////////////////////////// +// Sets DAC hardware voltage +// assumes +-10V range +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE set_pt_dac_voltage(DWORD brdindex, REALTYPE volts); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// Sets DAC hardware code +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE set_pt_dac_code(DWORD brdindex, UINT code); + +/////////////////////////////////////////////////////////////////////////////// +// Reads 1 DWORDSfrom pt a/d sample storage fifo +// returns 0xffffffff in case of error +// get error code with pci416_getError +/////////////////////////////////////////////////////////////////////////////// +DWORD LIB_TYPE read_pt_fifo(DWORD brdindex); + +/////////////////////////////////////////////////////////////////////////////// +// Read Multiple DWORDS from pt a/d sample storage fifo +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE read_pt_fifo_many(DWORD brdindex, LPDWORD buffer, DWORD count); + + +/***************************************************************************** + BRIDGE FUNCTIONS (S593X) +*****************************************************************************/ +/////////////////////////////////////////////////////////////////////////////// +// Resets the (Inbound) DMA FIFO on the S593x controller +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE reset_dma_fifo( DWORD brdindex); + + +/////////////////////////////////////////////////////////////////////////////// +// Sets the (Inbound) DMA fif0 bus master mode 1-> request on HF +// 0-> reqest on not(EF) +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE set_dma_request_half_full( DWORD brdindex,UINT md); + +/////////////////////////////////////////////////////////////////////////////// +// Enable/Disable (T/F) Bus master write transfers from FIFO +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE enable_dma_transfers( DWORD brdindex,UINT enable); + +/////////////////////////////////////////////////////////////////////////////// +// Updates the value of the S593x Bus Master Control/Status Reg +/////////////////////////////////////////////////////////////////////////////// +/* obsolete +// use pci416_set_mcsrreg(brdindex, mode, data, &retdata); +void LIB_TYPE update_mcr(DWORD brdindex); +*/ + +/////////////////////////////////////////////////////////////////////////////// +// Updates the DMA(Write) transfer count register (count is in bytes) +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE set_dma_transfer_count(DWORD brdindex, DWORD bytes); + +/////////////////////////////////////////////////////////////////////////////// +// Reads the DMA(Write) transfer count register (count is in bytes) +// returns 0xffffffff in case of error +/////////////////////////////////////////////////////////////////////////////// +DWORD LIB_TYPE read_dma_transfer_count(DWORD brdindex); + +/* obsolete use pci416_setup_dma and pci416_reload_dma +/////////////////////////////////////////////////////////////////////////////// +// Updates the DMA(Write) transfer Address register +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE set_dma_dest_address(DWORD brdindex, DWORD dest); + +/////////////////////////////////////////////////////////////////////////////// +// Reads the DMA(Write) destination addr. register +/////////////////////////////////////////////////////////////////////////////// +DWORD LIB_TYPE read_dma_dest_address(DWORD brdindex); + +*/ +// not implemented DMA does not work on interrupt +/////////////////////////////////////////////////////////////////////////////// +// Enables/Disables S593x interrupt to HOST on dma completion (Write Transfer) +/////////////////////////////////////////////////////////////////////////////// +//void LIB_TYPE enable_dma_interrupt(DWORD brdindex, UINT enable); + +/* +obsolete use pci416_dma_status +/////////////////////////////////////////////////////////////////////////////// +// Returns the Write(dma) transfer complete status bit (1=complete) +/////////////////////////////////////////////////////////////////////////////// +DWORD LIB_TYPE read_dma_transfer_status(DWORD brdindex); + +*/ +/* not implemented DMA does not work on interrupt +/////////////////////////////////////////////////////////////////////////////// +// Resets the MAILBOX INTERRUPT status bit (INTERRUPT) (write1 clRs) +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE reset_dma_transfer_status(DWORD brdindex); + +*/ +/* obsolete, use pci416_read_intcsr, + pci416_set_intcsr, + pci416_read_mcsr, + pci416_set_mcsr, + read_pci_port, + write_pci_port +/////////////////////////////////////////////////////////////////////////////// +// Returns 1 is the the DMA(Write X-fer) FIFO on the AMCC controller is FULL +/////////////////////////////////////////////////////////////////////////////// +DWORD LIB_TYPE dma_fifo_full(DWORD brdindex); + +/////////////////////////////////////////////////////////////////////////////// +// Returns 1 is the the DMA(Write X-fer) FIFO on the AMCC controller is empty +/////////////////////////////////////////////////////////////////////////////// +DWORD LIB_TYPE dma_fifo_empty(DWORD brdindex); + + +/////////////////////////////////////////////////////////////////////////////// +// Reads the DMA(Write X-fer) FIFO on the AMCC controller +/////////////////////////////////////////////////////////////////////////////// +DWORD LIB_TYPE read_dma_fifo_reg(DWORD brdindex); + + +/////////////////////////////////////////////////////////////////////////////// +// Updates the value of the S593x Interrupt Control/Status Reg +/////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE update_intcsr(DWORD brdindex); + +/////////////////////////////////////////////////////////////////////////////// +// Reads the value of the S593x Interrupt Control/Status Reg +/////////////////////////////////////////////////////////////////////////////// +DWORD LIB_TYPE read_intcsr(DWORD brdindex); + +/////////////////////////////////////////////////////////////////////////////// +// Reads the value of the S593x Master Control/Status Reg +/////////////////////////////////////////////////////////////////////////////// +DWORD LIB_TYPE read_msr(DWORD brdindex); + +// functions not needed for board operation in Windows +// used for internal test only -> see DOS code p416_set +////////////////////////////////////////////////////////////////////////////// +// Checks if NVRAM access pins are busy +// returns non-zero if busy +////////////////////////////////////////////////////////////////////////////// +UINT LIB_TYPE nvram_busy(DWORD brdindex); + +///////////////////////////////////////////////////////////////////////////// +// Set EPROM address for Data access +////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE set_nvram_address(DWORD brdindex, UINT addr); + +////////////////////////////////////////////////////////////////////////////// +// Read byte from NVRAM +////////////////////////////////////////////////////////////////////////////// +unsigned char LIB_TYPE read_nvram( DWORD brdindex, UINT addr); + +////////////////////////////////////////////////////////////////////////////// +// Write byte to NVRAM +////////////////////////////////////////////////////////////////////////////// +void LIB_TYPE write_nvram( DWORD brdindex, UINT addr, + unsigned char data); + +*/ +#endif diff --git a/drivers/Datel-PCI416/416inc/Pcilib32.h b/drivers/Datel-PCI416/416inc/Pcilib32.h new file mode 100644 index 0000000..7af8be1 --- /dev/null +++ b/drivers/Datel-PCI416/416inc/Pcilib32.h @@ -0,0 +1,171 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// pcilib32.h: defines data structures to access Datel PCI devices +// Copyright (c) Datel, Inc. 1997 +// Platform: Win32, Win95 +// Compiler: MVC4.0 + DDK +// Version: 3.0 +// Author: GS +// created: 2/4/97 +// modified: 7/10/98 +// History: V1.1 MCSR and INTCSR moved from "pci416df.h" +// V3.0 new defines for MWAR and MTC Reg +/////////////////////////////////////////////////////////////////////////////// + +#ifndef PCILIB32_H +#define PCILIB32_H +#if defined (__cplusplus) +extern "C" +{ +#endif +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +#ifndef DWORD +#define DWORD unsigned long +#define WORD unsigned short +#define BYTE unsigned char +#endif + +#define HIGH_BYTE(ax) (ax>>8) +#define LOW_BYTE(ax) (ax & 0xff) + +#define HIGH_WORD(ax) (ax>>16) +#define LOW_WORD(ax) (ax & 0xffff) + + +#ifndef LPDWORD +#define LPDWORD unsigned long far * +#endif + +#define PCI_CONFIG_DATA_OPORT 0xCF8 +#define PCI_CONFIG_DATA_IPORT 0xCFC + +// Address offsets for PCI bus opertion registers +#define PCI_OPREG_FIFO 0x20 +#define PCI_OPREG_MWAR 0x24 +#define PCI_OPREG_MWTC 0x28 +#define PCI_OPREG_MRAR 0x2C +#define PCI_OPREG_MRTC 0x30 +#define PCI_OPREG_INTCSR 0x38 +#define PCI_OPREG_MCSR 0x3C +#define PCI_OPREG_IMB4 0x1C + +#define BITS unsigned int + +struct STRUCT_ADDRESS{ + unsigned long loc; + unsigned int type; //TRUE = IO, FALSE = MEM +}; +typedef struct STRUCT_ADDRESS TYPE_ADDRESS; + +// PCI configuration address register fields +typedef struct +{ + BITS Zero : 2; // must be set to zero + BITS RegisterNo : 7; + BITS FunctionNo : 3; + BITS DeviceNo : 5; + BITS BusNo : 7; + BITS Reserved : 7; + BITS MSB : 1; // must be set to 1 +}PCI_CONF_ADDR_REGISTER_FIELDS; + +typedef union +{ + PCI_CONF_ADDR_REGISTER_FIELDS fields; + DWORD all; +} PCI_CONF_ADDR_REGISTER; + + +// PCI Configuration Space +typedef struct { + DWORD dev_ven_id; // = WORD vendor_id + WORD device_id + DWORD stat_cmd_rgs; //= WORD command +WORD status; + DWORD class_code_revision_id; // = BYTE revision_id + BYTE class_code[3] + DWORD bist_head_latt_cach; // =BYTE cache_line_size+ BYTE latency_timer+ + // BYTE header_type+ BYTE bist; + DWORD base_address[6]; + DWORD reserved1[2]; + DWORD expansion_rom_base; + DWORD reserved2[2]; + DWORD mlat_mgrant_irqp_irql; // BYTE interrupt_line+ BYTE interrupt_pin+ + // BYTE min_grant + BYTE max_latency; +// BYTE device[196]; not used on AMCC S5933 PCI controller +}PCI_TYPE_CONFIG; + +//Readout structure for PCI configuration Space Header +typedef union { + PCI_TYPE_CONFIG regs; + DWORD buffer[16]; +} PCI_CONFIG_SPACE; + +typedef PCI_CONFIG_SPACE *PCI_CONFIGS_PTR; + +struct BRIDGE_INTCSR{ + BITS out_mail_box_interrupt_select : 4; // 0:3 + BITS enable_out_mail_box_interrupt : 1; // 4 + BITS reserved1 : 3; // 5:7 + BITS in_mail_box_interrupt_select : 4; // 8:11 + BITS enable_in_mail_box_interrupt : 1; // 12 + BITS reserved2 : 1; // 13 + BITS dma_complete_interrupt_enable : 1; // 14 + BITS enable_read_complete_interrupt: 1; // 15 + BITS out_mail_box_interrupt_status : 1; // 16 + BITS in_mail_box_interrupt_status : 1; // 17 + BITS dma_transfer_status : 1; // 18 + BITS read_transfer_status : 1; // 19 + BITS master_abort_status : 1; // 20 + BITS target_abort_status : 1; // 21 + BITS reserved3 : 1; // 22 + BITS interrupt_status : 1; // 23 + BITS fifo_endian_control : 8; // 24:31 +}; + +typedef union{ + struct BRIDGE_INTCSR fields; + DWORD buffer; +} TBRIDGE_INTCSR_REGISTER; + +struct BRIDGE_MCSR{ + BITS out_fifo_status : 3; // 0:2 + BITS fifo_full : 1; // 3 (inbound fifo) + BITS fifo_half_full : 1; // 4 . + BITS fifo_empty : 1; // 5 . + BITS out_count_zero : 1; // 6 + BITS dma_count_zero : 1; // 7 (incomming count) + BITS read_write_priority : 1; // 8 + BITS dma_request_condition : 1; // 9 (write fifo) + BITS dma_enable : 1; // 10 (write transfer enable) + BITS reserved1 : 1; // 11 + BITS not_needed_by_416 : 4; // 12:15 + BITS eprom_data : 8; // 16:23 + BITS add_on_reset : 1; // 24 + BITS out_fifo_reset : 1; // 25 //used for DMA_ENABLE 22V10 + BITS fifo_reset : 1; // 26 (inbound fifo) + BITS mail_box_reset : 1; // 27 + BITS reserved2 : 1; // 28 + BITS eprom_control : 3; // 29:31 +}; + +typedef union{ + struct BRIDGE_MCSR fields; + DWORD buffer; +} TBRIDGE_MCSR_REGISTER; + + + +typedef struct { + DWORD buffer; +} TBRIDGE_MWAR_REGISTER; + +typedef struct { + DWORD buffer; +} TBRIDGE_MWTC_REGISTER; + +#if defined (__cplusplus) +} +#endif +#endif diff --git a/drivers/Datel-PCI416/416inc/Timer.h b/drivers/Datel-PCI416/416inc/Timer.h new file mode 100644 index 0000000..764e957 --- /dev/null +++ b/drivers/Datel-PCI416/416inc/Timer.h @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// timer.h: 82C54 command register bits +// and mode definition used by the PCI 416-board +// Copyright (c) Datel, Inc. 1997 +// Version: 1.0 +// Author: GS +// created: 2/4/97 +// modified: 4/15/96 +/////////////////////////////////////////////////////////////////////////////// +#ifndef TIMER_H +#define TIMER_H + +#define SEL0 (unsigned int) 0x00 // select counter # 0 +#define SEL1 (unsigned int) 0x40 // select counter # 1 +#define SEL2 (unsigned int) 0x80 // select counter # 2 +#define LSBMSB (unsigned int) 0x30 // load lsb followed by msb +#define MODE0 (unsigned int) 0x00 // modes +#define MODE1 (unsigned int) 0x02 +// 8254 Constants +#define MODE2 (unsigned int) 0x04 // software trigger +#define MODE3 (unsigned int) 0x06 +#define MODE4 (unsigned int) 0x08 // hardware trigger +#define MODE5 (unsigned int) 0x0A +#define BINARY (unsigned int) 0x00 // binary count +#define BCD (unsigned int) 0x01 // bcd count +#define READBACK (unsigned int) 0xC0 // read back counter command +#define CLOCK1 8000000L // clock frequency for Sample rate counter (1) +#define CLOCK2 500000L // clock frequency for internal trigger rate(2) + + + +enum{TM_SINGLE_TRIGGER, TM_CONT_TRIGGER, TM_RESET_TRIGGER, TM_ADCLOCK}; + +#endif \ No newline at end of file diff --git a/drivers/Datel-PCI416/416inc/admstats.h b/drivers/Datel-PCI416/416inc/admstats.h new file mode 100644 index 0000000..d7d6dcd --- /dev/null +++ b/drivers/Datel-PCI416/416inc/admstats.h @@ -0,0 +1,33 @@ +/***************************************************************************** +* +* ADMSTATS.H -- Header File A/D Module Parameters +* Copyright 1997 Datel Corporation. All Rights Reserved. +* Version 3.1. +* Author: GS +* created : 1/24/97 +* modified: 10/5/98 +*****************************************************************************/ +#ifndef ADMSTATS_H +#define ADMSTATS_H +#include "pci416df.h" + +const ADM_STATS adm_stats[]={ + //mod,bits, chs, single, scan,chrg,inc,scne,scnt,ssh,scyc,mnV,mxV,mnc,mxc) + {'A', 12, 4, 1.0e6F, 250e3F, 0, 1, 1, 3, 1, 1, 0.0F,0.0F,0,0}, // A + {'B', 14, 4, 500e3F, 333e3F, 0, 1, 0, 0, 0, 0, 0.0F,0.0F,0,0}, // B + {'C', 12, 4, 1.6e6F, 500e3F, 0, 1, 0, 0, 0, 0, 0.0F,0.0F,0,0}, // C + {'D', 12, 1, 5e6F, 5e6F, 0, 0, 0, 0, 0, 0, 0.0F,0.0F,0,0}, // D + {'E', 12, 16, 2.0e6F, 500e3F, 0, 1, 0, 0, 0, 0, 0.0F,0.0F,0,0}, // E + {'F', 12, 2, 2.0e6F, 2.0e6F, 2, 0, 0, 0, 1, 0, 0.0F,0.0F,0,0}, // F + {'G', 14, 2, 1.0e6F, 1.0e6F, 2, 0, 0, 0, 1, 0, 0.0F,0.0F,0,0}, // G + {'H', 12, 1, 10e6F, 10e6F, 0, 0, 0, 0, 0, 0, 0.0F,0.0F,0,0}, // H + {'J', 12, 8, 400e3F, 250e3F, 15, 0, 0, 0, 1, 1, 0.0F,0.0F,0,0}, // J + {'K', 12, 2, 5.0e6F, 5.0e6F, 2, 0, 0, 0, 1, 0, 0.0F,0.0F,0,0}, // K + {'L', 12, 16, 400e3F, 190e3F, 15, 0, 0, 0, 1, 1, 0.0F,0.0F,0,0}, // L + {'M', 16, 4, 200e3F, 200e3F, 8, 0, 0, 0, 1, 0, 0.0F,0.0F,0,0}, // M + {'N', 14, 2, 5e6F, 5e6F, 2, 0, 0, 0, 1, 1, 0.0F,0.0F,0,0}, // N + {'P', 14, 4, 3e6F, 3e6F, 8, 0, 0, 0, 1, 0, 0.0F,0.0F,0,0}, // P +}; + +#define MAX_ADMNO (sizeof(adm_stats)/sizeof(ADM_STATS)) +#endif diff --git a/drivers/Datel-PCI416/416inc/pci416df.h b/drivers/Datel-PCI416/416inc/pci416df.h new file mode 100644 index 0000000..bb97208 --- /dev/null +++ b/drivers/Datel-PCI416/416inc/pci416df.h @@ -0,0 +1,206 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// pci416df.h: defines data structures to access PCI416 DAQ devices +// Copyright (c) Datel, Inc. 1997 +// Platform: Win95, Win NT +// Compiler: MVC4.0 + DDK +// Version: 3.0 +// Author: GS +// created: 2/4/97 +// modified: 7/9/98 +// History: V1.1 MCSR and INTCSR moved to "pcilib32.h" +// V3.0 now for Win 95 and NT +/////////////////////////////////////////////////////////////////////////////// +#ifndef PCI416DF_H +#define PCI416DF_H + +#include "pcilib32.h" + +#ifndef REALTYPE +#define REALTYPE double +#endif + +#define UROUND(X) (DWORD)(X + 0.5) + +#define FFIFO 0x2000 // samples = WORDS +#define HFIFO 0x1000 // samples + +#define CAPS_SIZE 5 // number of ULONGS for Caps buffer + +// PASS THRU ADDRESS REGISTER LATCH +#define ARL_CMD_STS_REG 0l +#define ARL_SMPLCNT_FIFORD 4l +#define ARL_CHADDRREG_CLEARFIFO 8l +#define ARL_AD_ENABLE 12l +#define ARL_PLLREG 16l + +#define ARL_CNTR0 0l +#define ARL_CNTR1 4l +#define ARL_CNTR2 8l +#define ARL_CNT_CTRL_REG 12l + +#define ARL_PORTA 16l +#define ARL_PORTB 20l +#define ARL_PORTC 24l +#define ARL_PORT_CTRL_REG 28l + +#define ARL_DAC_REG 32l + +/* Bit masks for register write operations*/ +#define RWF_OVR 0x00 // reg = newval ONLY +#define RWF_OR 0x01 // reg |= newval ONE OPTION +#define RWF_AND 0x02 // reg &= newval VALID AT THE TIME +#define RWF_RB 0x80 // read back register value if register is r/w + // can be set with one of the above +/* DMA mode flags */ +#define DMA_SINGLE 0 +#define DMA_DOUBLE 1 + +struct PCI416_COMMAND_REGISTER{ + BITS trigger_select : 2; + BITS interrupt_select : 1; + BITS scan_select : 1; + BITS auto_increment : 1; + BITS marker_select : 1; + BITS pretrigger : 1; + BITS interrupt_enable : 1; + BITS pll : 3; + BITS mux_select : 3; + BITS adm_control : 2; + BITS dma_on : 1; + BITS spare : 15; +}; + +typedef union{ + struct PCI416_COMMAND_REGISTER fields; + DWORD buffer; +} TPCI416_COMMAND_REGISTER; + +struct PCI416_STATUS_REGISTER{ + BITS trigger_select : 2; + BITS scan_select : 1; + BITS auto_increment : 1; + BITS acquire : 1; + BITS marker_select : 1; + BITS pretrigger : 1; + BITS analog_trigger : 1; + BITS model : 4; + BITS arm : 1; + BITS empty : 1; + BITS half_full : 1; + BITS full : 1; + BITS spare : 16; +}; + +typedef union{ + struct PCI416_STATUS_REGISTER fields; + DWORD buffer; +} TPCI416_STATUS_REGISTER; + +struct PCI416_SAMPLE_COUNT_REGISTER{ + unsigned long count; + +}; + +struct PCI416_CHANNEL_REGISTER{ + BITS channel : 4; + BITS scan : 4; + BITS led : 1; + BITS spare1 : 7; + BITS spare2 : 16; +}; + +typedef union{ + struct PCI416_CHANNEL_REGISTER fields; + DWORD buffer; +} TPCI416_CHANNEL_REGISTER; + + +struct PCI416_CONVERT_ENABLE_REGISTER{ + BITS sparel : 15; + BITS convert : 1; + BITS spareu : 16; +}; + +typedef union{ + struct PCI416_CONVERT_ENABLE_REGISTER fields; + DWORD buffer; +} TPCI416_CONVERT_ENABLE_REGISTER; + +struct PCI416_PLL_REGISTER{ + BITS data : 4; + BITS spare1 : 12; + BITS spare2 : 16; +}; + +typedef union{ + struct PCI416_PLL_REGISTER fields; + DWORD buffer; +} TPCI416_PLL_REGISTER; + +struct PCI416_DAC_REGISTER { + BITS data : 12; + BITS spare1 : 4; + BITS spare2 : 16; +}; + +typedef union{ + struct PCI416_DAC_REGISTER fields; + DWORD buffer; +} TPCI416_DAC_REGISTER; + +typedef struct { + union{ + DWORD both; + struct { + int low; + int high; + } word; + }sample; +} TYPE_FIFO_DATA; + +typedef struct{ + WORD model; + WORD bits; + WORD channels; + float fmax_single; + float fmax_scan; + WORD channel_scan; + WORD autoincr_scan; + WORD scan_select; + WORD scan_count; + WORD ssh; + WORD scycle; + float minv; + float maxv; + long int mincode,maxcode; + +}ADM_STATS; + +typedef struct BoardInfo +{ + DWORD ADMindex, + BusNo, + SlotNo, + VendorID, + DeviceID, + CommandR, + StatusR, + ClassCodeR, + IRLR, + BADR[6]; +} TBoardInfo; + +typedef TBoardInfo *TBoardInfoPtr; + +enum{single_trigger,continuous_trigger}; +enum{T_INTERNAL,T_EXTERNAL,T_ANALOG,T_ANALOG_FE}; //trigger source +enum{M_DMA, M_HF_POLL, M_HF_POLL_DDISK,M_HF_INT} ; //acq_mode +enum{D_MEM, D_DIRECT}; // disk_mode: data to memory or direct to disk +enum{F_BINARY}; +enum{C_INTERNAL,C_EXTERNAL}; //clock_source +enum{R_NORMAL,R_BATCH,R_START_EXIT}; //run_mode +enum{I_DMA, I_HF, I_EOS }; // interrupt mode +enum{B_NORMAL, B_MARKER}; //marker mode + +#endif diff --git a/drivers/Datel-PCI416/Datel-PCI416.cpp b/drivers/Datel-PCI416/Datel-PCI416.cpp new file mode 100644 index 0000000..7419c32 --- /dev/null +++ b/drivers/Datel-PCI416/Datel-PCI416.cpp @@ -0,0 +1,233 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + +****************************************************************************/ +#include "core/core.h" +#include "Datel-PCI416.h" + +DatelPCI416::DatelPCI416(const ttlout& t_line){ + trigger_line=t_line; + PCI_DLL = LoadLibrary("PCI41632"); + if (PCI_DLL==NULL) { + throw ADC_exception("found no library\n"); + } + + /* import all functions from dll and exit on error*/ +# define pci41632dll_getproc(name) \ + name = (TFP_ ## name)GetProcAddress(PCI_DLL,#name); \ + if (name == NULL) {FreeLibrary(PCI_DLL); throw ADC_exception("found no " #name " function in DLL");} +# include "PCI416_func_import.cpp" + + if (pci416_init(&brdcount)!=NOERROR) { + brdcount=0; + throw ADC_exception("initialisation failed"); + } + + // now hardcoded, use first board + board=0; + if (NOERROR!=pci416_getcaps(board,4,caps)) + throw ADC_exception("getcaps failed"); + fprintf(stderr,"Datel PCI416 caps: FIFO size: %d, DMA size: %d, index ADM %d, acqmode %d\n",caps[0],caps[1],caps[2],caps[3]); + + sample_count=0; + trigger_count=0; + sample_frequency=0; + + trigger_line.ttls=1<<2; + trigger_line.id=0; + +} + +DatelPCI416::~DatelPCI416() { + pci416_close(board); + FreeLibrary(PCI_DLL); +} + +/** + here the data aquisition unit is configured and adds the necessary pusle components to the program +*/ +void DatelPCI416::set_daq(state& exp) { + + sample_count=0; + trigger_count=0; + sample_frequency=0; + + // todo: cleanup before exception... + state_iterator si(dynamic_cast(exp)); + state* a_state=(state*)si.get_state(); + /* loop over all states */ + while (NULL!=a_state) { + // collect analogin sections in state + std::list inputs; + /* loop over all device definitions in a state */ + state::iterator i=a_state->begin(); + while (i!=a_state->end()) { + analogin* input=dynamic_cast(*i); + if (input!=NULL) { + /* collect appropiate analogin sections, forget others */ + if (input->samples<=0 || input->sample_frequency<=0) + delete input; + else + inputs.push_back(input); + i=a_state->erase(i); + } + else + ++i; + } + if (!inputs.empty()) { + /* evaluate the found analogin definitions */ + if (inputs.size()>1) { + while (!inputs.empty()) {delete inputs.front(); inputs.pop_front();} + throw ADC_exception("can not handle more than one analogin section per state"); + } + if (trigger_count==0) { + sample_count=inputs.front()->samples; + sample_frequency=inputs.front()->sample_frequency; + trigger_count=si.get_count(); + } + else if ( sample_count==inputs.front()->samples && sample_frequency==inputs.front()->sample_frequency ) { + trigger_count+=si.get_count(); + } + else { + while (!inputs.empty()) {delete inputs.front(); inputs.pop_front();} + throw ADC_exception("Sorry, but multittriggering requires same parameters in all analogin sections"); + } + + if (a_state->length<1.0*sample_count/sample_frequency) { + fprintf(stderr,"WARINING: state is shorter than aquisition time\n"); + } + a_state->push_back(trigger_line.copy_new()); + + // to do: more analogin sections per state + while (!inputs.empty()) {delete inputs.front(); inputs.pop_front();} + } + // send a trigger pulse + // todo only short trigger pulse + a_state=(state*)si.next_state(); + } + + /* nothing to do! */ + if (trigger_count==0 || sample_count==0) return; + + + if (sample_count*trigger_count*4>caps[1]) + throw ADC_exception("required dma buffer size is too big for pci slot config"); + fprintf(stderr,"samples: %d, triggers: %d, frequency=%f\n", sample_count, trigger_count, sample_frequency); + + /* initialise board */ + if (NOERROR!=set_modes(board, + 0, // clock source: 0=internal + sample_frequency, // sample rate + sample_count*2, // samples per trigger + 1, // trigger src: 0=internal, 1=external digital + 1, // channel (1= Ch1 and Ch2) + 0, // pretrigger + 1, // scan (multichannel=1) + 0 // marker + )) + throw ADC_exception("set_modes failed..."); + + // clear fifo, even when using dma + if (NOERROR!=pci416_clear_fifo(board)) throw ADC_exception("clear_fifo failed"); + + DWORD dma_buffer_size[2]; + dma_buffer_size[0]=dma_buffer_size[1]=sample_count*trigger_count*4; //bytes + dma_buffer_handle=0; + if (NOERROR!=pci416_setup_dma(board,DMA_SINGLE,dma_buffer_size,&dma_buffer_handle)) + throw ADC_exception("setup_dma failed"); + + // start single aquisition + double freq=1.0; + // start_daq(board, trigger_mode, frequency) trigger mode: 0 external + if (NOERROR!=start_daq(board,0,&freq)) throw ADC_exception("start_daq failed"); + +} + +adc_result* DatelPCI416::get_samples(double timeout) { + // nothing to do here + if (trigger_count==0 || sample_count==0) return new adc_result(1,0,NULL); + + const size_t poll_time=10000; + double time_spent=0; + // busy wait for end + fprintf(stderr,"waiting for ADC"); + while (core::term_signal==0) { + DWORD dma_status=0; + // status NOT DONE=0, Done=1 + if (pci416_dma_status(board,&dma_status)!=NOERROR) + throw ADC_exception("dma_status failed"); + if (dma_status!=0) break; + usleep(poll_time); + time_spent+=1e-6*poll_time; + fprintf(stderr,".",dma_status); + fflush(stderr); + if (time_spent>timeout) { + fprintf(stderr," timeout!\n"); + throw ADC_exception("ran into timeout!"); + } + } + fprintf(stderr," done\n"); + if (core::term_signal!=0) return NULL; + + if (NOERROR!=stop_daq(board)) + throw ADC_exception("stop daq failed"); + if(NOERROR!=pci416_stop_dma(board,NULL)) + throw ADC_exception("stop_dma failed"); + + // copy results... + DWORD resulting_bytes=sample_count*trigger_count*4; // bytes + short int* data=(short int*)malloc(resulting_bytes); + if (data==NULL) throw ADC_exception("Could not allocate enough memory"); + if (NOERROR!=pci416_copy_dmabuffer(board, DMA_SINGLE, 0, &resulting_bytes, (DWORD*)data)) { + free(data); + throw ADC_exception("copy_dmabuffer failed"); + } + +#if 0 + fprintf(stderr,"got %d bytes back\n",resulting_bytes); + FILE* binfile=fopen("/tmp/bla.bin","w+"); + if (binfile!=NULL) { + fwrite(data,1,resulting_bytes, binfile); + fclose(binfile); + } + else + fprintf(stderr,"could not open file\n"); +#endif + /* return them */ + return new adc_result(1, sample_count*trigger_count, data, sample_frequency); +} + + +void DatelPCI416::sample_after_external_trigger(const double rate, const size_t samples, double sensitivity, size_t resolution) { + const int board=0; + + // try dma + if (NOERROR!=set_modes(board, + 0, // clock source: 0=internal + rate, // sample rate + samples*2, // samples per trigger + 1, // trigger src: 0=internal, external digital =1 + 1, // channel (1= Ch1 and Ch2) + 0, // pretrigger + 1, // scan + 0 // marker + )) + throw ADC_exception("set_modes failed..."); + + // clear fifo, even when using dma + if (NOERROR!=pci416_clear_fifo(board)) throw ADC_exception("clear_fifo failed"); + + DWORD dma_buffer_size[2]; + dma_buffer_size[0]=dma_buffer_size[1]=samples*trigger_count*4; //bytes + DWORD dma_buffer_handle=0; + if (NOERROR!=pci416_setup_dma(board,DMA_SINGLE,dma_buffer_size,&dma_buffer_handle)) + throw ADC_exception("setup_dma failed"); + fprintf(stderr,"buffersize %d\n",dma_buffer_size[0]); + + // start single aquisition + double freq=1.0; + if (NOERROR!=start_daq(board,0,&freq)) throw ADC_exception("start_daq failed"); +} + diff --git a/drivers/Datel-PCI416/Datel-PCI416.h b/drivers/Datel-PCI416/Datel-PCI416.h new file mode 100644 index 0000000..f5eb14c --- /dev/null +++ b/drivers/Datel-PCI416/Datel-PCI416.h @@ -0,0 +1,97 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + +****************************************************************************/ +#ifndef DATELPCI416 +#define DATELPCI416 + +#include "drivers/ADC.h" +#include "core/states.h" +#include +#include +#include "416inc/pcilib32.h" +#include "416inc/pci416df.h" + +/** + \defgroup datelpci416 Datel PCI416 adc card + \ingroup drivers + \brief supports Datel PCI416 pci cards with dma data transfer + + @{ + */ +class DatelPCI416: public ADC { + HINSTANCE PCI_DLL; + // make definitions private +# include "416inc/41632DLL.H" + /** the board this driver works on */ + long unsigned int board; + /** number of available boards */ + long unsigned int brdcount; + + + /** + \brief the capabilities of the PCI slot used by this card + + the members of this structure are: + 0 sizeFIFO; + 1 bufsizeDMA; + 2 indexADM; + 3 acqmode; + */ + + DWORD caps[4]; + /** + the sample count per trigger pulse and per channel + */ + size_t sample_count; + /** + the count of expected triggers + */ + size_t trigger_count; + + /** + sampling frequency of initialised card + */ + double sample_frequency; + + /** + when initialised, this handle gives access to data + */ + DWORD dma_buffer_handle; + + /** + the ttl signal for pulse programmer + */ + ttlout trigger_line; + +public: + DatelPCI416(const ttlout& t_line); + + ~DatelPCI416(); + + /** + start sampling after trigger and return field of int + */ + virtual void sample_after_external_trigger(const double rate, const size_t samples, double sensitivity=5.0, size_t resolution=12); + + /** + here the data aquisition unit is configured and adds the necessary pusle components to the program + + \exception ADC_exception if data aquisition can not be done by this driver, reason is given + */ + virtual void set_daq(state& exp); + + /** + read the sample data + */ + virtual adc_result* get_samples(double timeout=0.0); + +}; + +/** + @} +*/ + +#endif diff --git a/drivers/Datel-PCI416/Makefile b/drivers/Datel-PCI416/Makefile new file mode 100644 index 0000000..397041f --- /dev/null +++ b/drivers/Datel-PCI416/Makefile @@ -0,0 +1,32 @@ +############################################################################# +# +# Author: Achim Gaedke +# Created: June 2004 +# +############################################################################# + + +# für den ersten Anlauf +CPP=g++ +CPPFLAGS=-g -I. -I../.. +LDFLAGS= + +.PHONY: all clean install + +../../tools/add_endline.exe: ../../tools/add_endline.cpp + $(CXX) $< -o $@ + +all: Datel-PCI416.o + +Datel-PCI416.o: Datel-PCI416.cpp Datel-PCI416.h PCI416_func_import.cpp + $(CPP) -c $(CPPFLAGS) $< -o $@ + +PCI416_func_import.cpp: 416inc/41632DLL.H + grep "^TFP_" $< |sed 's/.* \(.*\);/pci41632dll_getproc(\1)/' >$@ + +clean: + for f in *.cpp *.h; do ../../tools/add_endline.exe $$f; done; \ +rm -f *.o *~ *.stackdump PCI416_func_import.cpp + +install: + diff --git a/drivers/Eurotherm-2000Series/Eurotherm-2000Series.cpp b/drivers/Eurotherm-2000Series/Eurotherm-2000Series.cpp new file mode 100644 index 0000000..0f9d887 --- /dev/null +++ b/drivers/Eurotherm-2000Series/Eurotherm-2000Series.cpp @@ -0,0 +1,302 @@ +/* ************************************************************************** + + Author: Holger Stork, Achim Gaedke + Created: January 2005 + +****************************************************************************/ + +#include "Eurotherm-2000Series.h" +#include +#include +#include +#include +#include +#include +#include +#include + +Eurotherm2000Series::Eurotherm2000Series(const std::string& dev_name, int dev_address, int failure_status_mask): tempcont() { + /* + initialization of serial device + */ + device_name=dev_name; + failure_mask=failure_status_mask; + serial_dev=open(dev_name.c_str(),O_RDWR|O_NONBLOCK); + if (serial_dev<0) + throw Eurotherm2000Series_error("could not open serial device"); + // set attributes now + termios the_attributes; + the_attributes.c_iflag=0; + the_attributes.c_oflag=0; + // 7 bit per byte, even parity, reciever enabled + the_attributes.c_cflag=CS7|PARENB|CREAD; + the_attributes.c_lflag=0; + // baud rate + cfsetospeed(&the_attributes,B19200); + cfsetispeed(&the_attributes,B19200); + if(tcsetattr(serial_dev,TCSANOW,&the_attributes)!=0) + throw Eurotherm2000Series_error("could not configure serial line"); + address=dev_address; + (void)setlocale(LC_NUMERIC,"C"); + fp_format="%05.1f"; + int_format="%05d."; + hex_format=">%04x"; + std::map config; + // configure for �K output + config["Q1"]="2."; + // configure for nnn.n format output/precision + config["QD"]="1."; + configure(config); + set_history_stepsize(1); + device_failed=0; +} + +void Eurotherm2000Series::configure(const std::map& config) { + std::string error_message; + try { + std::string mode; + const char config_mode[]="0002."; + read_value("IM",mode); + for (std::map::const_iterator i=config.begin();i!=config.end();++i) { + try { + std::string value; + read_value(i->first, value); + fprintf(stderr, "%s: %s (%s)", i->first.c_str(), value.c_str(), i->second.c_str()); + if (value!=i->second) { + if (mode!=config_mode) set_value("IM",config_mode); + set_value(i->first,i->second); + mode=config_mode; + } + } + catch (Eurotherm2000Series_error e){error_message+=i->first+"->"+i->second+", ";} + } + if (mode==config_mode) { + set_value("IM","0000."); + // read superficial zero byte + char buffer[1]; + while (read(serial_dev,buffer,1)<=0) sleep(1); + // wait for restarted communication + int timeout=10; + while (1) { + if (timeout<0) throw Eurotherm2000Series_error("could not establish connection after configuring"); + try {get_summary_status();} catch(Eurotherm2000Series_error){--timeout;sleep(1); continue;} + break; + } + } + } + catch (const Eurotherm2000Series_error& e) { + throw Eurotherm2000Series_error("got error while configuration: "+std::string(e.what())); + } + if (error_message.size()!=0) { + throw Eurotherm2000Series_error("Could not set "+error_message.substr(0,error_message.size()-1)); + } + +} + +void Eurotherm2000Series::reset() { + set_value("IM","0000."); + // read superficial zero byte + char buffer[1]; + while (read(serial_dev,buffer,1)<=0) sleep(1); + // wait for restarted communication + int timeout=10; + while (1) { + if (timeout<0) throw Eurotherm2000Series_error("could not establish connection after reset"); + try {get_summary_status();} catch(Eurotherm2000Series_error){--timeout; sleep(1); continue;} + break; + } +} + +Eurotherm2000Series::~Eurotherm2000Series() { + /** + close serial device, clean up + */ + close(serial_dev); +} + +void Eurotherm2000Series::read_value(const std::string& param_name, std::string& return_value) const{ + // assemble message without channel + std::string message; + message+=4; + message+='0'+(address/10); + message+='0'+(address/10); + message+='0'+(address%10); + message+='0'+(address%10); + message+=param_name; + message+=5; + // write message to device + pthread_mutex_lock((pthread_mutex_t*)&device_lock); + write(serial_dev,message.c_str(),message.size()); + // read answer + unsigned char got_byte=0; + return_value.clear(); + // sleep minimum latency time for parameter reading + timespec read_latency; + read_latency.tv_sec=0; read_latency.tv_nsec=2000000; + nanosleep(&read_latency,NULL); + // now use shorter steps + int timeout=100; + read_latency.tv_nsec=1000000; + while (return_value.size()<2 || return_value[return_value.size()-2]!=3) { + int result=read(serial_dev,&got_byte,1); + if (result<=0) { + if (result<0 && errno!=EAGAIN) { + return_value.clear(); + pthread_mutex_unlock((pthread_mutex_t*)&device_lock); + throw Eurotherm2000Series_error("read_value: error while reading"); + } + if (timeout<0) { + return_value.clear(); + pthread_mutex_unlock((pthread_mutex_t*)&device_lock); + throw Eurotherm2000Series_error("read_value: timeout while reading"); + } + // go on sleeping for a while + nanosleep(&read_latency,NULL); + timeout--; + continue; + } + if (return_value.size()==0 && got_byte==4) throw Eurotherm2000Series_error("read_value: invalid register"); + return_value+=got_byte; + } + pthread_mutex_unlock((pthread_mutex_t*)&device_lock); + + if (0) { + for (std::string::const_iterator i=return_value.begin();i!=return_value.end();++i) + fprintf(stderr,"%02x",*i); + fprintf(stderr,"\n"); + } + // test bcc + unsigned char bcc=return_value[1]; + for (size_t i=2;i1e-4) { + if (i<0) throw Eurotherm2000Series_error("could not confirm setpoint value settings, timeout"); + nanosleep(&write_latency,NULL); + --i; + } + return ct; +} + +double Eurotherm2000Series::get_setpoint() const { + std::string answer; + read_value("SL",answer); + return strtod(answer.c_str(),NULL); +} + +int Eurotherm2000Series::get_summary_status() const { + std::string answer; + read_value("SO",answer); + if (answer[0]!='>') throw Eurotherm2000Series_error("could not handle status data format \'"+answer+"\'"); + answer.erase(0,1); + return strtol(answer.c_str(),NULL,16); +} + +void Eurotherm2000Series::get_setpoint_limits(double& min, double& max) const { + std::string answer; + read_value("LS",answer); + min=strtod(answer.c_str(),NULL); + read_value("HS",answer); + max=strtod(answer.c_str(),NULL); +} diff --git a/drivers/Eurotherm-2000Series/Eurotherm-2000Series.h b/drivers/Eurotherm-2000Series/Eurotherm-2000Series.h new file mode 100644 index 0000000..e566392 --- /dev/null +++ b/drivers/Eurotherm-2000Series/Eurotherm-2000Series.h @@ -0,0 +1,161 @@ +/* ************************************************************************** + + Author: Holger Stork, Achim Gaedke + Created: January 2005 + + ****************************************************************************/ + +#ifndef EUROTHERM2000SERIES_H +#define EUROTHERM2000SERIES_H + +#include +#include +#include "drivers/tempcont.h" + +/** + \defgroup eurotherm2000series Eurotherm 2000 Series + \ingroup drivers + \brief remote control over serial interface + + Wiring the serial interface is explained in Series 2000 Communications Handbook, Chapter 2.7. + This handbook is online available at http://www.eurotherm.com/comms/instcom.htm + The implemented EI-Bisync Protocol is described in caphter 4, the used parameter names are taken from Chapter 5 + + @{ + */ + +/** + special exception for Eurotherm 2000 drivers + */ +class Eurotherm2000Series_error: public tempcont_error +{ +public: + explicit Eurotherm2000Series_error(const std::string& msg) throw (): tempcont_error(msg) {} + explicit Eurotherm2000Series_error(const char* msg) throw (): tempcont_error(msg) {} + virtual ~Eurotherm2000Series_error() throw () {} +protected: + virtual const std::string prefix() const { return "ERROR (Eurotherm2000Series_error): "; } +}; + +/** + this class communicates with Eurotherm 2200 Series temperature controlers + the contolers must be set to a known address, the serial line uses 19200baud, 7bit characters and even parity + there are various timeouts, that are hardcoded to detect transmition failures + */ + +class Eurotherm2000Series: public tempcont +{ +private: + /** + file descriptor for serial device with Eurotherm controler + */ + int serial_dev; + /** + device address, which must be set in Eurotherm device + */ + int address; + /** + serial device name + */ + std::string device_name; + /** + mask defining severe failures (abort of program could be triggered) + */ + int failure_mask; + /** + all these strange data formats of Bisynch: float values + */ + std::string fp_format; + /** + all these strange data formats of Bisynch: integer values with dot! + */ + std::string int_format; + /** + all these strange data formats of Bisynch: hexadecimal values + */ + std::string hex_format; +public: + /** + reads a value from Eurotherm + */ + void read_value(const std::string& param_name, std::string& return_value) const; + + /** + sends a value to Eurotherm + */ + int set_value(const std::string& param_name, const std::string& value); + + /** + send a list of configuration commands to eurotherm, make sure, that nobody uses eurotherm when calling this function + the configuration time is quite long (5 to 10 seconds) because of full device reset + */ + void configure(const std::map& config); + + void reset(); + +public: + /** + \brief initialise serial interface, test device communication, configure eurotherm + \param dev_name absolute path name of serial device used for communication + \param dev_address of Eurotherm device (to be set manualy in Eurotherm configuration) + \param failure_status_mask mask for status sumary to detect failures, see get_summary_status method + + sets up the communication, configures Eurotherm2000 to �K and nnn.n display format, tests the sensor and starts temperature history + */ + Eurotherm2000Series(const std::string& dev_name = std::string("/dev/ttyS0"), int dev_address = 1, int failure_status_mask = 32); + + /** + */ + virtual ~Eurotherm2000Series(); + + /** + get the actual temperature + */ + virtual double get_temperature() const; + + /** + set temperature setpoint value + \return temperature, that is set + */ + virtual double set_setpoint(double temperature); + + /** + get temperature setpoint value + \return temperature, that is set + */ + virtual double get_setpoint() const; + + /** + returns summary for eurotherm as hexadecimal value + + the bits meanings are: + 0: Alarm 1 State + 1: Alarm 2 State + 2: Alarm 3 State + 3: Alarm 4 State + + 4: Manual Mode + 5: Sensor Break + 6: Loop Break + 7: Heater Fail + + 8: Load Fail + 9: Ramp/Program Complete + 10: Process Variable out of Range + 11: SSR Fail + + 12: New Alarm + 13: Remote Input Sensor Break + */ + virtual int get_summary_status() const; + + /** + get the low and high limits for setpoints + */ + virtual void get_setpoint_limits(double& min, double& max) const; +}; + +/** + @} + */ +#endif /* EUROTHERM2000SERIES_H */ diff --git a/drivers/Eurotherm-2000Series/Makefile b/drivers/Eurotherm-2000Series/Makefile new file mode 100644 index 0000000..646280c --- /dev/null +++ b/drivers/Eurotherm-2000Series/Makefile @@ -0,0 +1,37 @@ +############################################################################# +# +# Author: Achim Gaedke +# Created: January 2005 +# +############################################################################# + +CXX=g++ +CXXFLAGS=-O0 -g -Wall -Wshadow -pedantic +CXXCPPFLAGS=-I. -I../.. +LIBS=-lpthread -lxerces-c + +.PHONY: all clean + +all: Eurotherm-2000Series.o + +../../tools/add_endline.exe: ../../tools/add_endline.cpp + $(CXX) $< -o $@ + +../tempcont.o: ../tempcont.h ../tempcont.cpp + $(MAKE) -C .. tempcont.o + +Eurotherm-2000Series.o: Eurotherm-2000Series.cpp Eurotherm-2000Series.h ../tempcont.h + $(CXX) -c $(CXXFLAGS) $(CXXCPPFLAGS) $< -o $@ + +test.exe: test.cpp Eurotherm-2000Series.o Eurotherm-2000Series.h ../tempcont.o + $(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -o $@ $< Eurotherm-2000Series.o ../tempcont.o $(LIBS) + +control: control.cpp Eurotherm-2000Series.o Eurotherm-2000Series.h ../tempcont.o ../../machines/hardware.o ../../core/core.a + $(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -o $@ $< Eurotherm-2000Series.o ../tempcont.o ../../machines/hardware.o ../../core/core.a -lexpat $(LIBS) + +gnuplot_output.exe: gnuplot_output.cpp Eurotherm-2000Series.o ../tempcont.o + $(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -o $@ $< Eurotherm-2000Series.o ../tempcont.o $(LIBS) + +clean: ../../tools/add_endline.exe + for f in Eurotherm-2000Series.cpp Eurotherm-2000Series.h test.cpp Makefile; do ../../tools/add_endline.exe $$f; done; \ +rm -f *~ *.o *.exe *.stackdump core.{0,1,2,3,4,5,6,7,8,9}*; diff --git a/drivers/Eurotherm-2000Series/control.cpp b/drivers/Eurotherm-2000Series/control.cpp new file mode 100644 index 0000000..191582c --- /dev/null +++ b/drivers/Eurotherm-2000Series/control.cpp @@ -0,0 +1,71 @@ +#include "Eurotherm-2000Series.h" +#include +#include + +int main(int argc, char** argv) { + + try { + Eurotherm2000Series e("/dev/ttyS0", 2); + double upper; + double lower; + e.get_setpoint_limits(lower,upper); + fprintf(stderr, "setpoint limits %f %f\n",lower,upper); + + while (1) { + char buffer[100]; + char* nextline=fgets(buffer,sizeof(buffer),stdin); + if (nextline==NULL) { + break; + } + if (nextline[0]==0) { + break; + } + else if (nextline[0]==10 || nextline[0]==12) { + // print the temperature and setpoint + double t=e.get_temperature(); + double s=e.get_setpoint(); + printf("%f %f\n", t,s); + } + else if (nextline[0]=='h') { + //print history + temp_history* h=e.get_history(0); + if (h==NULL) break; + for (temp_history::const_reverse_iterator i=h->rbegin(); + i!=((temp_history::const_reverse_iterator)h->rend()); + ++i) + printf("%f\n",*i); + delete h; + printf("\n"); + } + else if (nextline[0]=='a') { + // autotune + e.set_value("AT","0000."); + e.set_value("mA","0000."); + } + else if (nextline[0]=='r') { + // reset + e.reset(); + } + else { + // get number and set setpoint + char* lineend=NULL; + double s=strtod(nextline,&lineend); + if (nextline!=lineend) { + fprintf (stderr,"new setpoint %f...",s); + fflush(stderr); + e.set_setpoint(s); + fprintf (stderr,"...done\n"); + } + } + + } + + + } + catch (Eurotherm2000Series_error e) { + fprintf(stderr,"%s\n",e.c_str()); + return 1; + } + return 0; + +} diff --git a/drivers/Eurotherm-2000Series/gnuplot_output.cpp b/drivers/Eurotherm-2000Series/gnuplot_output.cpp new file mode 100644 index 0000000..9077605 --- /dev/null +++ b/drivers/Eurotherm-2000Series/gnuplot_output.cpp @@ -0,0 +1,24 @@ +#include "Eurotherm-2000Series.h" +#include + +int main() { + try { + Eurotherm2000Series e("/dev/ttyS1",2); + while (1) { + if (e.device_failure()) return 1; + sleep(2); + temp_history* h=e.get_history(0); + printf("plot '-' w l\n"); + for (temp_history::const_reverse_iterator i=h->rbegin(); i!=((temp_history::const_reverse_iterator)h->rend()); ++i) + printf("%f\n",*i); + delete h; + printf("e\n"); + fflush(stdout); + } + } + catch (Eurotherm2000Series_error e) { + fprintf(stderr, "error: %s",e.c_str()); + return 1; + } + return 0; +} diff --git a/drivers/Eurotherm-2000Series/test.cpp b/drivers/Eurotherm-2000Series/test.cpp new file mode 100644 index 0000000..8c49c3e --- /dev/null +++ b/drivers/Eurotherm-2000Series/test.cpp @@ -0,0 +1,18 @@ +/* ************************************************************************** + + Author: Holger Stork, Achim Gaedke + Created: January 2005 + +****************************************************************************/ + +#include "Eurotherm-2000Series.h" + +int main() { + try { + Eurotherm2000Series eurotherm("/dev/ttyS0",2); + } + catch (Eurotherm2000Series_error e) { + fprintf(stderr,"error: %s\n",e.c_str()); + } + return 0; +} diff --git a/drivers/Makefile b/drivers/Makefile new file mode 100644 index 0000000..9da712b --- /dev/null +++ b/drivers/Makefile @@ -0,0 +1,47 @@ +############################################################################# +# +# Author: Achim Gaedke +# Created: June 2004 +# +############################################################################# + +CXX=g++ +CXXFLAGS=-O0 -g -Wall -Wshadow -pedantic +CXXCPPFLAGS=-I. -I.. + +#ToDo PBP-HS3-test.exe +SUBDIRS:=SpinCore-PulseBlaster SpinCore-PulseBlaster24Bit SpinCore-PulseBlasterDDSIII PTS-Synthesizer Eurotherm-2000Series dummy Spectrum-MI40xxSeries Spectrum-M2i40xxSeries Tecmag-DAC20 +ifeq ($(shell /bin/uname -o),Cygwin) +SUBDIRS+=TiePie-HS3 Datel-PCI416 +endif + +all: tempcont.o + for i in $(SUBDIRS); do make -C $$i all; done + +../tools/add_endline.exe: ../tools/add_endline.cpp + $(CXX) $< -o $@ + +.PHONY: SpinCore-PulseBlasterDDSIII/SpinCore-PulseBlasterDDSIII.o TiePie-HS3/TiePie-HS3.a Eurotherm-2000Series/Eurotherm-2000Series.o clean install + +tempcont.o: tempcont.cpp tempcont.h + $(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -c $< -o $@ + +SpinCore-PulseBlasterDDSIII/SpinCore-PulseBlasterDDSIII.o: + $(MAKE) -C SpinCore-PulseBlasterDDSIII SpinCore-PulseBlasterDDSIII.o + +Eurotherm-2000Series/Eurotherm-2000Series.o: + $(MAKE) -C Eurotherm-2000Series Eurotherm-2000Series.o + +TiePie-HS3/TiePie-HS3.a: + $(MAKE) -C TiePie-HS3 TiePie-HS3.a + +Datel-PCI416/Datel-PCI416.o: + $(MAKE) -C Datel-PCI416 Datel-PCI416.o + +clean: ../tools/add_endline.exe + for f in ADC.h frequgen.h pulsegen.h tempcont.h ; do ../tools/add_endline.exe $$f; done; \ +rm -f *~ *.o *.exe *.stackdump core.{0,1,2,3,4,5,6,7,8,9}*; \ +for i in $(SUBDIRS); do make -C $$i clean; done + +install: all + for i in $(SUBDIRS); do make PREFIX=$(PREFIX) -C $$i install; done diff --git a/drivers/PTS-Synthesizer/Makefile b/drivers/PTS-Synthesizer/Makefile new file mode 100644 index 0000000..13a1ca7 --- /dev/null +++ b/drivers/PTS-Synthesizer/Makefile @@ -0,0 +1,34 @@ +############################################################################# +# +# Author: Achim Gaedke +# Created: October 2004 +# +############################################################################# + +CXX=g++ +CXXFLAGS=-g -O0 -Wall -Wshadow -pedantic +CXXCPPFLAGS=-I. -I../.. +LDFLAGS=../../core/core.a -lexpat + +.PHONY: all clean install + +all: PTS.o PTS_test.exe + +../../tools/add_endline.exe: ../../tools/add_endline.cpp + $(CXX) $< -o $@ + +../../core/core.a: + $(MAKE) -C ../../core core.a + +PTS.o: PTS.cpp ../frequgen.h + $(CXX) -c $(CXXFLAGS) $(CXXCPPFLAGS) -o $@ $< + +PTS_test.o: PTS_test.cpp PTS.h + $(CXX) -c $(CXXFLAGS) $(CXXCPPFLAGS) -o $@ $< + +PTS_test.exe: PTS_test.o PTS.o + $(CXX) -o $@ $^ $(LDFLAGS) + +clean: ../../tools/add_endline.exe + for f in PTS.cpp PTS.h PTS_test.cpp; do $< $$f; done + rm -f *.exe *.o *~ core.* diff --git a/drivers/PTS-Synthesizer/PTS.cpp b/drivers/PTS-Synthesizer/PTS.cpp new file mode 100644 index 0000000..85d9781 --- /dev/null +++ b/drivers/PTS-Synthesizer/PTS.cpp @@ -0,0 +1,273 @@ +/* *************************************************************************** + + Author: Achim Gädke + Date: October 2004 + +**************************************************************************** */ + +#include "PTS.h" +#include +#include + +#ifdef __CYGWIN__ +# define floorl floor +#endif + +PTS::PTS(int myid): id(myid) { + frequency=0; + phase=0; + negative_logic=1; + phase_step=0.225; +} + +PTS::~PTS() { + +} + +unsigned int PTS::phase_ttl_values(double p) const { + // break down to [0;360[ + fprintf(stderr,"PHASE %g\n",phase_step); + p-=floor(p/360.0)*360.0; + unsigned int phasesteps=(unsigned int)fabs(round(p/phase_step)); + int bcd_part=phasesteps/100; + unsigned int ttl_value=bcd_part<<8; + phasesteps-=bcd_part*100; + bcd_part=phasesteps/10; + ttl_value|=bcd_part<<4; + phasesteps-=bcd_part*10; + ttl_value|=phasesteps; + return ttl_value; +} + +void PTS::phase_add_ttls(state& the_state, double p) const { + unsigned int binary_code=phase_ttl_values(p); + std::vector::const_iterator mask=ttl_masks.begin(); + while (mask!=ttl_masks.end()) { + /* obeye negative logic */ + if ((binary_code & 1<<11)==0 ^ negative_logic==0) the_state.push_back(mask->copy_new()); + binary_code<<=1; + binary_code&=0xFFF; + ++mask; + } + if (binary_code!=0) fprintf(stderr,"Warning! Insufficient phase precision for %f\n",p); +} + +long unsigned int PTS::frequency_ttl_values(double f) const { + long unsigned int frequency_int=(long unsigned int)fabs(floor(f)); + long unsigned int freq_bcd=0; + if (sizeof(freq_bcd)<8) { + fprintf(stderr, "warning! possible overflow (f=%f)\n", f); + } + for (long unsigned int divider=100000000; divider>0; divider/=10) { + int part_freq=frequency_int/divider; + freq_bcd<<=4; + freq_bcd|=part_freq; + frequency_int-=part_freq*divider; + } + return freq_bcd; +} + +void PTS::set_frequency(double f) { + frequency=f; + /* todo switch PMD Module to frequency */ +} + +void PTS::set_frequency(state& experiment) { + // find the frequency informations... + // and exchange the phase informations + frequency=0; + phase=0; + state_sequent* exp_sequence=dynamic_cast(&experiment); + if (exp_sequence==NULL) + set_frequency_ttls(experiment); + else { + state_iterator si(*exp_sequence); + state* a_state=(state*)si.get_state(); + while (NULL!=a_state) { + set_frequency_ttls(*a_state); + a_state=(state*)si.next_state(); + } /* state loop */ + } +} + +void PTS::set_frequency_ttls(state& the_state) { + + // find the frequency informations... + // and exchange the phase informations + analogout* pts_aout=NULL; + /* find a analogout section with suitable id */ + state::iterator i=the_state.begin(); + while(i!=the_state.end()) { + analogout* aout=dynamic_cast(*i); + if (aout!=NULL && aout->id==id) { + if (pts_aout==NULL) { + /* save the informations */ + pts_aout=aout; + } + else { + fprintf(stderr, "found another pts decade section, ignoring\n"); + delete aout; + } + /* remove the analog out section */ + the_state.erase(i++); + } + else + ++i; + } /* state members loop */ + + /* now, add the ttl information*/ + if (pts_aout!=NULL) { + phase_add_ttls(the_state, pts_aout->phase); + phase=pts_aout->phase; + if (pts_aout->frequency!=0) { + if (frequency==0) { + set_frequency(pts_aout->frequency); + } + /* different frequencies are forbidden */ + else if (frequency!=pts_aout->frequency) { + fprintf(stderr, "ignoring frequency %g at analogout %d\n",pts_aout->frequency,id); + } + } + delete pts_aout; + } + else { + /* because we use transparent mode, we have to set phase everywhere */ + phase_add_ttls(the_state, phase); + } + +} + + +/* ************************************************************************** + +PTS_latched + +************************************************************************** */ + +void PTS_latched::set_frequency(state& experiment) { + // find the frequency informations... + // and exchange the phase informations + frequency=0; + phase=0; + state_sequent* exp_sequence=dynamic_cast(&experiment); + if (exp_sequence==NULL) + // is a very state on top level, todo: change interface + throw frequ_exception("cannot work on a single state, sorry (todo: change interface)"); + else { + for(state_sequent::iterator child_state=exp_sequence->begin(); child_state!=exp_sequence->end(); ++child_state) + set_frequency_recursive(*exp_sequence, child_state); + } +} + +void PTS_latched::set_frequency_recursive(state_sequent& the_sequence, state::iterator& the_state) { + + state_sequent* a_sequence=dynamic_cast(*the_state); + if (a_sequence!=NULL) { + for(state_sequent::iterator child_state=a_sequence->begin(); child_state!=a_sequence->end(); ++child_state) + set_frequency_recursive(*a_sequence, child_state); + } + else { + // find the frequency informations... + state* this_state=dynamic_cast(*the_state); + if (this_state==NULL) throw frequ_exception("state_atom in state_sequent not expected"); + // and exchange the phase informations + analogout* pts_aout=NULL; + /* find a analogout section with suitable id */ + state::iterator i=this_state->begin(); + while(i!=this_state->end()) { + analogout* aout=dynamic_cast(*i); + if (aout!=NULL && aout->id==id) { + if (pts_aout==NULL) { + /* save the informations */ + pts_aout=aout; + } + else { + fprintf(stderr, "found another pts decade section, ignoring\n"); + delete aout; + } + /* remove the analog out section */ + this_state->erase(i++); + } + else + ++i; + } /* state members loop */ + + if (pts_aout!=NULL) { + if (this_state->length<9e-8*4.0) throw frequ_exception("time is too short to save phase information"); + /* now, insert the ttl information*/ + /* set phase everytime */ + pts_aout->phase-=floor(pts_aout->phase/360.0)*360.0; + int phase_int=(int)floor(pts_aout->phase/phase_step+0.5); + /* copy of original state */ + state* register_state=new state(*this_state); + ttlout* register_ttls=new ttlout(); + register_ttls->id=0; + register_state->length=9e-8; + register_state->push_back(register_ttls); + /* set frequency if necessary */ + if (pts_aout->frequency!=0) { + if (this_state->length<9e-8*12.0) throw frequ_exception("time is too short to save frequency information"); + // enable remote frequency control + register_ttls->ttls=0x8010; + the_sequence.insert(the_state,register_state->copy_new()); + register_ttls->ttls&=0x7fff; + the_sequence.insert(the_state,register_state->copy_new()); + // in 0.1 Hz units ! + unsigned long int frequency_int=(unsigned long int)floorl(fabs(pts_aout->frequency)*10.0+0.5); + if (sizeof(frequency_int)<8 && pts_aout->frequency>4e8) { + fprintf(stderr, "warning! possible overflow (f=%f)\n",pts_aout->frequency); + } + /* 100MHz and 10MHz */ + register_ttls->ttls=(frequency_int/1000000000)%10<<8|((frequency_int/100000000)%10)<<4|15<<12; + the_sequence.insert(the_state,register_state->copy_new()); + register_ttls->ttls&=0x7fff; + the_sequence.insert(the_state,register_state->copy_new()); + /* 1MHz and 100kHz */ + register_ttls->ttls=(frequency_int/10000000)%10<<8|((frequency_int/1000000)%10)<<4|14<<12; + the_sequence.insert(the_state,register_state->copy_new()); + register_ttls->ttls&=0x7fff; + the_sequence.insert(the_state,register_state->copy_new()); + /* 10kHz and 1kHz */ + register_ttls->ttls=(frequency_int/100000)%10<<8|((frequency_int/10000)%10)<<4|13<<12; + the_sequence.insert(the_state,register_state->copy_new()); + register_ttls->ttls&=0x7fff; + the_sequence.insert(the_state,register_state->copy_new()); + /* 100Hz and 10Hz */ + register_ttls->ttls=(frequency_int/1000)%10<<8|((frequency_int/100)%10)<<4|12<<12; + the_sequence.insert(the_state,register_state->copy_new()); + register_ttls->ttls&=0x7fff; + the_sequence.insert(the_state,register_state->copy_new()); + /* 1Hz and 0.1Hz (not used for all models) */ + //register_state->length=0.5e-6; + register_ttls->ttls=(frequency_int/10)%10<<8|((frequency_int)%10)<<4|11<<12; + the_sequence.insert(the_state,register_state->copy_new()); + register_ttls->ttls&=0x7fff; + the_sequence.insert(the_state,register_state->copy_new()); + //register_state->length=9e-8; + /* and shorten the remaining state */ + this_state->length-=9e-8*12; + //this_state->length-=9e-8*11+0.5e-6; + } + if (1) { + /* first entry for phase */ + register_ttls->ttls=((phase_int/100)%16)<<4|9<<12; + the_sequence.insert(the_state,register_state->copy_new()); + register_ttls->ttls&=0x7fff; + the_sequence.insert(the_state,register_state->copy_new()); + /* second entry for phase */ + register_ttls->ttls=(phase_int%10)<<8|((phase_int/10)%10)<<4|10<<12; + the_sequence.insert(the_state,register_state->copy_new()); + register_ttls->ttls&=0x7fff; + the_sequence.insert(the_state,register_state->copy_new()); + /* and shorten the remaining state */ + this_state->length-=9e-8*4; + } + delete register_state; + delete pts_aout; + + } + + + /* end of state modifications */ + } +} diff --git a/drivers/PTS-Synthesizer/PTS.h b/drivers/PTS-Synthesizer/PTS.h new file mode 100644 index 0000000..deb47d2 --- /dev/null +++ b/drivers/PTS-Synthesizer/PTS.h @@ -0,0 +1,127 @@ +/* *************************************************************************** + + Author: Achim Gaedke + Created: October 2004 + +**************************************************************************** */ + +#ifndef PTS_H +#define PTS_H +#include "drivers/frequgen.h" +#include +#include "core/states.h" + + +/** + \defgroup PTSSynthesizer PTS Frequency Synthesizer + \ingroup drivers + \brief driver for %PTS Frequency Synthesizer phase and frequency control + @{ +*/ + +/** + \brief PTS Synthesizer driver for phase and preselected frequency + + The driver can switch phase during experiment, the accuracy is determined by the number of connected phase selector lines. + Frequency support will be soon available, one frequency can be selected, which is configured before the experiment. + The Synthesizer is driven in transperent mode, i.e. no latch is used. Therefore phase information is added to all states. + If no phase is defined by an analogout instruction, the default phase is taken. + */ + +class PTS: public frequgen { + + public: + /** + \brief vector with the ttl output commands, that are added, to achieve the phase shift + + Tuples of 4 masks are for 22.5, 2.25 and 0.225 degree increments, each representing weights of 8, 4, 2 and 1. + the most significant bit (180 degree) is the first member of the vector, significance is decreasing + */ + std::vector ttl_masks; + + /** + \brief the assigned id of this analogout section + */ + int id; + + /** + if negative logic should be used, this has to be set for noninverting cable drivers + */ + int negative_logic; + + /** + define the phase stepping: PTS310 0.225 PTS500 0.36 + */ + float phase_step; + + /** + \brief default constructor + */ + PTS(int myid=0); + + /** + \brief transform phase to ttl values, return them in unsigned int lower bits: 0 - 11 + */ + unsigned int phase_ttl_values(double phase) const; + + /** + \brief transform frequency to ttl values, return them in long unsigned int bits: 0 - 40 + */ + long unsigned int frequency_ttl_values(double frequency) const; + /** + \brief add ttl instructions according to the phase + */ + void phase_add_ttls(state& the_state, double phase) const; + + /** + \brief sets the reference frequency for the experiment time + */ + virtual void set_frequency(double f); + + /** + \brief exchange analogout sections with phase TTLs and determines the experiments reference frequency + */ + virtual void set_frequency_ttls(state& experiment); + + /** + \brief exchange analogout sections in all substates + */ + virtual void set_frequency(state& experiment); + + /** + \brief destructor + nothing to do now... will change with frequency control + */ + virtual ~PTS(); +}; + + +/** + */ +class PTS_latched: public PTS { + +public: + PTS_latched(int myid=0): PTS(myid) {} + + /** + \brief exchange analogout sections with frequency and phase TTLs + */ + virtual void set_frequency_recursive(state_sequent& the_sequence, state_sequent::iterator& the_state); + + /** + \brief exchange analogout sections in all substates + */ + virtual void set_frequency(state& experiment); + + /** + \brief destructor + nothing to do now... will change with frequency control + */ + virtual ~PTS_latched(){} +}; + +/** + @} +*/ + +#endif diff --git a/drivers/PTS-Synthesizer/PTS_test.cpp b/drivers/PTS-Synthesizer/PTS_test.cpp new file mode 100644 index 0000000..a6d0416 --- /dev/null +++ b/drivers/PTS-Synthesizer/PTS_test.cpp @@ -0,0 +1,122 @@ +/* *************************************************************************** + + Author: Achim Gädke + Created: October 2004 + +**************************************************************************** */ + +#include "PTS.h" +#include +#include "core/xml_states.h" + +class PTS_test { +public: + PTS p; + + PTS_test() { + p.id=0; + p.set_frequency(0); + ttlout t; + t.ttls=std::bitset<32>(1<<10); + p.ttl_masks.push_back(t); + t.ttls=std::bitset<32>(1<<9); + p.ttl_masks.push_back(t); + if (0) { + t.ttls=std::bitset<32>(1<<8); + p.ttl_masks.push_back(t); + t.ttls=std::bitset<32>(1<<7); + p.ttl_masks.push_back(t); + } + } + + void phase_digit_translation_test() { + printf("100: %x\n",p.phase_ttl_values(100)); + printf("-260: %x\n",p.phase_ttl_values(-360+100)); + printf("0: %x\n",p.phase_ttl_values(0)); + printf("90: %x\n",p.phase_ttl_values(90)); + printf("180: %x\n",p.phase_ttl_values(180)); + printf("270: %x\n",p.phase_ttl_values(270)); + } + + + void phase_add_test() { + state s(1,NULL); + p.phase_add_ttls(s,270.5); + xml_state_writer().write_states(stdout,s); + } + + void state_modify_test() { + state s(1); + analogout aout; + aout.phase=90; + aout.frequency=2e7; + s.push_back(aout.copy_new()); + p.set_frequency(s); + xml_state_writer().write_states(stdout,s); + } + + void sequent_iterate_test() { + state_sequent ss; + state s(1e-3,&ss); + ss.push_back(s.copy_new()); + s.length=1; + analogout aout; + aout.phase=90; + aout.frequency=2e7; + s.push_back(aout.copy_new()); + ss.push_back(s.copy_new()); + delete s.front(); + aout.frequency=2e6; + s.front()=aout.copy_new(); + ss.push_back(s.copy_new()); + + p.set_frequency(ss); + xml_state_writer().write_states(stdout,ss); + } + + void freq_digit_translation_test() { + printf("270e6: %lx\n",p.frequency_ttl_values(270e6)); + } + +}; + +class PTS_latched_test { + +public: + PTS_latched p; + + PTS_latched_test() { + p.id=0; + } + + void test_simple() { + state_sequent ss; + state s(1e-3,&ss); + ss.push_back(s.copy_new()); + s.length=2e-6; + analogout aout; + aout.phase=90.225; + aout.frequency=0; + s.push_back(aout.copy_new()); + ss.push_back(s.copy_new()); + delete s.front(); + aout.phase=190; + aout.frequency=2e6; + s.front()=aout.copy_new(); + ss.push_back(s.copy_new()); + + xml_state_writer().write_states(stdout,ss); + p.set_frequency(ss); + xml_state_writer().write_states(stdout,ss); + } + + +}; + +int main() { + + PTS_latched_test().test_simple(); + //PTS_test().freq_digit_translation_test(); + return 0; +} + diff --git a/drivers/Spectrum-M2i40xxSeries/GatedData.cpp b/drivers/Spectrum-M2i40xxSeries/GatedData.cpp new file mode 100644 index 0000000..fdad2ab --- /dev/null +++ b/drivers/Spectrum-M2i40xxSeries/GatedData.cpp @@ -0,0 +1,117 @@ +#include "GatedData.h" +#include + +#ifndef SIZETPRINTFLETTER +# ifndef __LP64__ +# define SIZETPRINTFLETTER "u" +# else +# define SIZETPRINTFLETTER "lu" +# endif +#endif + + +/* + size of all childrens' data +*/ +size_t DataManagementNode::size() const { + if (child==NULL) + return n; + + size_t size_n=0; + + for (const DataManagementNode* i=child; i!=NULL; i=i->next) + size_n+=i->size(); + + return size_n*n; +} + +/* + reduce depth recursively +*/ +DataManagementNode* DataManagementNode::reduce() { + // simple case: no children + if (child==NULL) + return this; + + // evaluate all children's nodes and append remaining + DataManagementNode* i=child; + DataManagementNode* last_child=NULL; + + while (i->next!=NULL) { + DataManagementNode* reduced_child=i->reduce(); + if (reduced_child->n!=0) { + if (last_child==NULL) + child=reduced_child; + else + last_child->next=reduced_child; + last_child=reduced_child; + last_child->parent=this; + last_child->next=NULL; + } + DataManagementNode* next_child=i->next; + + // throw away unused one + if (reduced_child->n==0 || i!=reduced_child) { + i->parent=NULL; + i->next=NULL; + delete i; + } + i=next_child; + } + // now look at the remaining list + if (child==NULL) { + // return empty node + n=0; + } else + // reduce depth by one + if (child->next==NULL || child->child!=NULL) { + n*=child->n; + DataManagementNode* superficial=child; + child=child->child; + superficial->parent=NULL; + delete superficial; + } + return this; +} + +void DataManagementNode::print(FILE* f,size_t indent) { + char* indent_space=new char[indent+1]; + memset(indent_space,' ',indent); + indent_space[indent]=0; + + if (child==NULL) + fprintf(f,"%s%" SIZETPRINTFLETTER " samples\n",indent_space, n); + else { + fprintf(f,"%s%" SIZETPRINTFLETTER ":\n",indent_space, n); + child->print(f,indent+6); + } + + delete indent_space; + + if (next!=NULL) + next->print(f,indent); + +} + +DataManagementNode::DataManagementNode(const DataManagementNode& orig) { + parent=NULL; + if (orig.child!=NULL) { + child=new DataManagementNode(*orig.child); + for (DataManagementNode* i=child; i!=NULL; i=i->next) i->parent=this; + } + if (orig.next!=NULL) next=new DataManagementNode(*orig.next); + n=orig.n; +} + +/** + recursive destruction +*/ +DataManagementNode::~DataManagementNode() { + while (child!=NULL) { + DataManagementNode* this_one=child; + child=child->next; + this_one->parent=NULL; + this_one->next=NULL; + delete this_one; + } +} diff --git a/drivers/Spectrum-M2i40xxSeries/GatedData.h b/drivers/Spectrum-M2i40xxSeries/GatedData.h new file mode 100644 index 0000000..95abfeb --- /dev/null +++ b/drivers/Spectrum-M2i40xxSeries/GatedData.h @@ -0,0 +1,50 @@ +#include +#include + +class DataManagementNode; + +/** + manages nested ADC data structure +*/ +class DataManagementNode { + public: + /// parent node, top node has NULL + DataManagementNode* parent; + /// reference to nested loops + DataManagementNode* child; + /// next node in sequences + DataManagementNode* next; + + /** + if the child is NULL, n is the number of real samples + otherwise repeatition number of substructure + */ + size_t n; + + /** + default constructor of NOP node + */ + DataManagementNode(class DataManagementNode* par=NULL): parent(par), child(NULL), next(NULL), n(0) {} + + /** + (deep) copy constructor + */ + DataManagementNode(const DataManagementNode& orig); + + /** + size of all childrens' data + */ + size_t size() const; + /** + reduce depth recursively + */ + DataManagementNode* reduce(); + + + void print(FILE* f,size_t indent=0); + + /** + recursive destruction, call only for root + */ + ~DataManagementNode(); +}; diff --git a/drivers/Spectrum-M2i40xxSeries/Makefile b/drivers/Spectrum-M2i40xxSeries/Makefile new file mode 100644 index 0000000..3313c39 --- /dev/null +++ b/drivers/Spectrum-M2i40xxSeries/Makefile @@ -0,0 +1,37 @@ +CPPFLAGS=-Iinclude -I../.. +CXXFLAGS=-Wshadow -Wall -O0 -g -DSPC_DEBUG=0 +AR=ar +LIBS+= -lpthread -lm -lxerces-c -lexpat + +SPC_HEADERS = include/spcerr.h include/regs.h include/dlltyp.h include/spcioctl.inc +SPC_ZIP = drv_spcm_linux_drv_v214b5633.zip +# SPC_ZIP = drv_new.zip + +all: clean $(SPC_HEADERS) Spectrum-M2i40xxSeries.a + +$(SPC_HEADERS): $(SPC_ZIP) + unzip -u $< "*.h" "*.txt" "*.inc" -d include/ + +Spectrum-M2i40xxSeries.a: Spectrum-M2i40xxSeries.o GatedData.o + $(AR) r $@ $^ + +Spectrum-M2i40xxSeries.o: Spectrum-M2i40xxSeries.cpp Spectrum-M2i40xxSeries.h GatedData.h + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $< + +GatedData.o: GatedData.cpp GatedData.h + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $< + +test.o: test.cpp Spectrum-M2i40xxSeries.h include/spcerr.h + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $< + +test: test.o Spectrum-M2i40xxSeries.a ../../core/core.a +# $(CXX) -o $@ -I../.. ../ADC.h $^ $(LIBS) + $(CXX) -o $@ $(LIBS) -I../ADC.h $^ + + +../../core/core.a: + $(MAKE) -C ../../core core.a + +clean: + rm -f *.o *.a *~ test *.core + rm -rf include diff --git a/drivers/Spectrum-M2i40xxSeries/Spectrum-M2i40xxSeries.cpp b/drivers/Spectrum-M2i40xxSeries/Spectrum-M2i40xxSeries.cpp new file mode 100644 index 0000000..02cc5bd --- /dev/null +++ b/drivers/Spectrum-M2i40xxSeries/Spectrum-M2i40xxSeries.cpp @@ -0,0 +1,607 @@ +/* Spectrum-M2i40xxSeries.cpp + Author: Stefan Reutter (2011) + ***************************************************************** + Driver for the Spectrum M2i Series ADC cards. Based on the driver for the old MI40xx cards. + */ + + +#include +#include +#include +#include +#include "pthread.h" +#include "core/core.h" +#include "core/stopwatch.h" +#include "core/result.h" +#include "core/xml_states.h" +#include "Spectrum-M2i40xxSeries.h" + +#ifndef SIZETPRINTFLETTER +# ifndef __LP64__ +# define SIZETPRINTFLETTER "u" +# else +# define SIZETPRINTFLETTER "lu" +# endif +#endif + +bool SpectrumM2i40xxSeries::Configuration::bOpenCard() { + // open ADC handle and read information to be displayed + char szDrvName[20]; + sprintf (szDrvName, "/dev/spcm%d", lCardID); + hDrv = spcm_hOpen(szDrvName); + + if (!hDrv) { + return false; + } + + // read information to be displayed + spcm_dwGetParam_i32(hDrv, SPC_PCITYP, &lCardType); + spcm_dwGetParam_i32(hDrv, SPC_PCISERIALNO, &lSerialNumber); + spcm_dwGetParam_i32(hDrv, SPC_PCIFEATURES, &lFeatureMap); + spcm_dwGetParam_i64(hDrv, SPC_PCIMEMSIZE, &llInstMemBytes); + spcm_dwGetParam_i32(hDrv, SPC_MIINST_MINADCLOCK, &lMinSampleRate); + spcm_dwGetParam_i32(hDrv, SPC_MIINST_MAXADCLOCK, &lMaxSampleRate); + spcm_dwGetParam_i32(hDrv, SPC_MIINST_MODULES, &lModulesCount); + spcm_dwGetParam_i32(hDrv, SPC_MIINST_CHPERMODULE, &lMaxChannels); + spcm_dwGetParam_i32(hDrv, SPC_MIINST_BYTESPERSAMPLE, &lBytesPerSample); + spcm_dwGetParam_i32(hDrv, SPC_GETDRVVERSION, &lLibVersion); + spcm_dwGetParam_i32(hDrv, SPC_GETKERNELVERSION, &lKernelVersion); + + int32 lTmp; + + spcm_dwGetParam_i32 (hDrv, SPC_PCIVERSION, &lTmp); + lBaseHwVersion = (lTmp >> 16) & 0xffff; + lCtrlFwVersion = lTmp & 0xffff; + + spcm_dwGetParam_i32 (hDrv, SPC_PCIMODULEVERSION, &lTmp); + lModHwVersion = (lTmp >> 16) & 0xffff; + lModFwVersion = lTmp & 0xffff; + + // we need to recalculate the channels value as the driver returns channels per module + lMaxChannels *= lModulesCount; + + int32 lAIFeatures; + + spcm_dwGetParam_i32(hDrv, SPC_MIINST_BITSPERSAMPLE, &lResolution); + spcm_dwGetParam_i32(hDrv, SPC_READAIPATHCOUNT, &lPathCount); + spcm_dwGetParam_i32(hDrv, SPC_READIRCOUNT, &lRangeCount); + for (int i=0; (i> 24, (lKernelVersion >> 16) & 0xff, lKernelVersion & 0xffff); + strcat(sInfo, sTmp); + sprintf(sTmp, " Library Version %d.%02d build %d\n", lLibVersion >> 24, (lLibVersion >> 16) & 0xff, lLibVersion & 0xffff); + strcat(sInfo, sTmp); + +#if SPC_DEBUG + sprintf(sTmp, "Debug Information:\n"); + strcat(sInfo, sTmp); + sprintf(sTmp, " Bytes per sample: %i\n", lBytesPerSample); + strcat(sInfo, sTmp); +#endif + + return sInfo; +} + + +/* + Initialize the card +*/ +SpectrumM2i40xxSeries::SpectrumM2i40xxSeries(const ttlout& t_line, int ext_reference_clock) { + // print neat string to inform the user + fprintf(stderr, "Initializing ADC card\n"); + + // check parameters + trigger_line=t_line; + + default_settings.qwSetChEnableMap = channel_array(ADC_M2I_DEFAULT_CHANNELS); + default_settings.lSetChannels = default_settings.qwSetChEnableMap.count(); + default_settings.impedance = new double[default_settings.lSetChannels]; + default_settings.sensitivity = new double[default_settings.lSetChannels]; + default_settings.offset = new int[default_settings.lSetChannels]; + + for(int i = 0; i < default_settings.lSetChannels; i++) { + default_settings.impedance[i] = ADC_M2I_DEFAULT_IMPEDANCE; + + default_settings.sensitivity[i] = ADC_M2I_DEFAULT_SENSITIVITY; // Set sensitivity in Volts to the default (maximum) value + default_settings.offset[i] = ADC_M2I_DEFAULT_OFFSET; // Set offsets in % of sensitivity to default (0) + } + + default_settings.ext_reference_clock = ext_reference_clock; // Hz + effective_settings=NULL; + + fprintf(stderr, "clock %i\n", ext_reference_clock); + // initializing adc info class with card ID at 0 + default_settings.lCardID = 0; + + if(default_settings.bOpenCard()) + fprintf(stderr, "%s", default_settings.PrintInfo()); + else + throw SpectrumM2i40xxSeries_error("Could not open card"); + + + spcm_dwSetParam_i32(default_settings.hDrv, SPC_CLOCKMODE, SPC_CM_EXTREFCLOCK); + spcm_dwSetParam_i32(default_settings.hDrv, SPC_REFERENCECLOCK, default_settings.ext_reference_clock); + spcm_dwSetParam_i32(default_settings.hDrv, SPC_CLOCK50OHM, 1); // ToDo: test this + + fprintf(stderr, "\nADC card initialized\n"); +} + +void SpectrumM2i40xxSeries::collect_config_recursive(state_sequent& exp, SpectrumM2i40xxSeries::Configuration& settings) { + /* start with dummy node */ + DataManagementNode* new_branch = new DataManagementNode(NULL); + DataManagementNode* where_to_append = new_branch; + double parent_timeout=settings.timeout; + settings.timeout=0.0; + + /* loop over all states and sequences within the current sequence */ + for (state_sequent::iterator i = exp.begin(); i != exp.end(); ++i) { + + state* a_state = dynamic_cast(*i); // cast into a state and check for illegal types + if (a_state == NULL) + throw SpectrumM2i40xxSeries_error("Expecting state or state_sequent object"); + if (dynamic_cast(*i)!=NULL) + throw SpectrumM2i40xxSeries_error("State parallel is not implemented"); + + state_sequent* a_sequence=dynamic_cast(a_state); // cast into a sequence of states + if (a_sequence != NULL) { // if a sequence is found, recurse + DataManagementNode* tmp_structure = settings.data_structure; + settings.data_structure = where_to_append; + collect_config_recursive(*a_sequence, settings); + settings.data_structure = tmp_structure; + } else { // otherwise, we are supposed to have a single analogin state + settings.timeout += a_state->length; + + // collect analogin sections in state + std::list inputs; + + // loop over analogin states for this device + state::iterator k = a_state->begin(); + while (k != a_state->end()) { + analogin* input = dynamic_cast(*k); + if (input != NULL && input->id == device_id) { // check for validity + if (input->samples<=0 || input->sample_frequency<=0) { // don't use an input if it doesn't make sense + delete input; + } else { + inputs.push_back(input); + } + k=a_state->erase(k); + } else { + ++k; + } + } + + + if (!inputs.empty()) { + /* evaluate the found analogin definitions */ + if (inputs.size() > 1) { // only one analogin allowed for each state + while (!inputs.empty()) { delete inputs.front(); inputs.pop_front();} // free list + throw ADC_exception("can not handle more than one analogin section per state"); + } + + /* save sampling frequency */ + if (settings.samplefreq <= 0) { + settings.samplefreq = inputs.front()->sample_frequency; + } else if (settings.samplefreq != inputs.front()->sample_frequency) { + while (!inputs.empty()) { delete inputs.front(); inputs.pop_front();} // free list + throw ADC_exception("Sorry, but gated sampling requires same sampling frequency in all analogin sections"); + } + + /* save sensitvity */ + if (settings.sensitivity != NULL) { // if sensitivity is set, make sure it's valid (i.e. the same for all inputs) + for (int j = 0; j < inputs.front()->nchannels; j++) { + if (settings.sensitivity[j] != inputs.front()->sensitivity[j]) { + fprintf(stderr, "Warning! different sensitivity specified (here %f, elsewhere %f), choosing higher voltage\n", + settings.sensitivity[j], + inputs.front()->sensitivity[j]); + if (settings.sensitivity[j] < inputs.front()->sensitivity[j]) { + settings.sensitivity[j] = inputs.front()->sensitivity[j]; + } + } + } + } else { + settings.sensitivity = inputs.front()->sensitivity; + } + // check if sensitivity is valid + for (int j = 0; j < inputs.front()->nchannels; j++) { + bool sensAllowed = false; + for (int l = 0; l < ADC_M2I_ALLOWED_SENSITIVITY_LENGTH; l++) { + if (settings.sensitivity[j] == ADC_M2I_ALLOWED_SENSITIVITY[l] ) { + sensAllowed = true; + break; + } + } + if (!sensAllowed) { + fprintf(stderr, "Warning! Illegal sensitivity specified for channel %i: %f", j, inputs.front()->sensitivity[j]); + settings.sensitivity[j] = ADC_M2I_DEFAULT_SENSITIVITY; + } + } + + /* save impedance */ + if (settings.impedance != NULL) { + for (int j = 0; j < inputs.front()->nchannels; j++) { + if (settings.impedance[j] != inputs.front()->impedance[j]) { + fprintf(stderr, "Warning! different impedance specified (here %f, elsewhere %f), setting to default\n", + settings.impedance[j], + inputs.front()->impedance[j]); + settings.impedance[j] = ADC_M2I_DEFAULT_IMPEDANCE; + } + if (settings.impedance[j] != ADC_M2I_DEFAULT_IMPEDANCE && settings.impedance[j] != ADC_M2I_ALLOWED_IMPEDANCE) { + fprintf(stderr, "Warning! Illegal impedance specified for channel %i: %f", j, inputs.front()->impedance[j]); + settings.offset[j] = 0; + } + } + } else { + settings.impedance = inputs.front()->impedance; + } + + /* save offsets */ + if (settings.offset != NULL) { + for (int j = 0; j < inputs.front()->nchannels; j++) { + if (settings.offset[j] != inputs.front()->offset[j]) { + fprintf(stderr, "Warning! different impedance specified (here %i, elsewhere %i), setting to default\n", + settings.offset[j], + inputs.front()->offset[j]); + settings.offset[j] = ADC_M2I_DEFAULT_OFFSET; + } + if (inputs.front()->offset[j] > 100 || inputs.front()->offset[j] < -100) { + fprintf(stderr, "Warning! Illegal offset specified for channel %i: %i", j, inputs.front()->offset[j]); + settings.offset[j] = 0; + } + } + } else { + settings.offset = inputs.front()->offset; + } + + if (inputs.front()->samples%4 != 0) { + throw ADC_exception("Number of samples must be a multiple of four"); + } + + /* save channel mask and number of channels */ + if (settings.lSetChannels > 0) { + if (settings.qwSetChEnableMap.to_ulong() > 0) { + if (settings.qwSetChEnableMap != inputs.front()->channels) { + fprintf(stderr, "Warning! different channels enabled in input %lu and in config %lu, setting to default \n", + settings.qwSetChEnableMap.to_ulong(), + inputs.front()->channels.to_ulong()); + settings.qwSetChEnableMap = channel_array(ADC_M2I_DEFAULT_CHANNELS); + settings.lSetChannels = settings.qwSetChEnableMap.count(); + } + } else { + settings.qwSetChEnableMap = inputs.front()->channels; + settings.lSetChannels = inputs.front()->nchannels; + } + } else { + settings.qwSetChEnableMap = inputs.front()->channels; + settings.lSetChannels = inputs.front()->nchannels; + } + + // gating time offsets apparently were fixed in the M2i cards. todo: check + + + // calculate the time required + double time_required; + time_required = (inputs.front()->samples)/settings.samplefreq; + time_required = ceil(1e8*time_required)/1e8; + + // check time requirements + if (a_state->length < time_required) { + char parameter_info[512]; + snprintf(parameter_info,sizeof(parameter_info), + "(%" SIZETPRINTFLETTER " samples, %g samplerate, %e time required, %e state time)", + inputs.front()->samples, + settings.samplefreq, + time_required, + a_state->length); + + // update the state length if it's shorter than the gate. this is usually due to rounding to 10 ns for the pulseblaster + if (ceil(1e8*a_state->length)/1e8 < time_required) { + throw ADC_exception(std::string("state is shorter than acquisition time")+parameter_info); + } else { + a_state->length = time_required; + } + } + + // adapt the pulse program for gated sampling + if (a_state->length == time_required) { // state has proper length + a_state->push_back(trigger_line.copy_new()); + } else { // state is too long... + // create new one with proper time and gated sampling pulse + state* gated_sampling_pulse = new state(*a_state); + gated_sampling_pulse->length = time_required; + gated_sampling_pulse->push_back(trigger_line.copy_new()); + + // insert gate pulse state before remaining (original) state + exp.insert(i,(state_atom*)gated_sampling_pulse); + + // shorten this state + a_state->length -= time_required; + } + +# if SPC_DEBUG + fprintf(stderr, "state sequence:\n"); + xml_state_writer().write_states(stderr, exp); +# endif + + /* insert a new state */ + DataManagementNode* new_one = new DataManagementNode(new_branch); + new_one->n = inputs.front()->samples; + new_one->child = NULL; + new_one->next = where_to_append->next; + where_to_append->next = new_one; + where_to_append = new_one; + + while (!inputs.empty()) {delete inputs.front(); inputs.pop_front();} // free inputs + } /* !inputs.empty() */ + } // end state + } // i + + /* something happened? */ + if (new_branch->next != NULL) { + /* make dummy node to a loop */ + new_branch->n=exp.repeat; + new_branch->child=new_branch->next; + + /* if possible, append it */ + if (settings.data_structure!=NULL) { + new_branch->parent=settings.data_structure->parent; + new_branch->next=settings.data_structure->next; + settings.data_structure->next=new_branch; + } else { + new_branch->parent=NULL; + new_branch->next=NULL; + settings.data_structure=new_branch; + } + } + else + delete new_branch; + + settings.timeout *= exp.repeat; + settings.timeout += parent_timeout; + fprintf(stderr,"setting.timeout %g\n",settings.timeout); + return; +} + +void SpectrumM2i40xxSeries::set_daq(state & exp) { + fprintf(stderr, "Setting up data acquisition\n"); + // check whether the experiment state is legal + state_sequent* exp_sequence=dynamic_cast(&exp); + if (exp_sequence==NULL) + throw SpectrumM2i40xxSeries_error("Spectrum-M2i40xxSeries::set_daq only working on sequences"); + + /* find out what to do */ + Configuration* conf=new Configuration(); + collect_config_recursive(*exp_sequence, *conf); + if (conf->samplefreq <= 0) conf->samplefreq = default_settings.samplefreq; + if (conf->impedance == NULL) conf->impedance = default_settings.impedance; + if (conf->sensitivity == NULL) conf->sensitivity = default_settings.sensitivity; + if (conf->offset == NULL) conf->offset = default_settings.offset; + if (conf->hDrv == NULL) conf->hDrv = default_settings.hDrv; + if (conf->lBytesPerSample == 0) conf->lBytesPerSample = default_settings.lBytesPerSample; + if (conf->lSetChannels == 0) { conf->lSetChannels = default_settings.lSetChannels; conf->qwSetChEnableMap = default_settings.qwSetChEnableMap; } + + size_t sampleno = (conf->data_structure == NULL) ? 0 : conf->data_structure->size(); + + /* nothing to do! */ + if (sampleno == 0) { + delete conf; + effective_settings=NULL; + fprintf(stderr, "Warning: Nothing to do.\n"); + return; + } + + if (sampleno < 16 || sampleno%16 != 0) { + delete conf; + throw SpectrumM2i40xxSeries_error("total number of samples must be multiple of 16 and at least 16"); + } + + effective_settings=conf; + // make sure the board is ready + int actual_status = 0; + spcm_dwGetParam_i32(effective_settings->hDrv, SPC_M2STATUS, &actual_status); + + if ((actual_status & M2STAT_CARD_READY) == 0) { + fprintf(stderr, "Warning: Spectrum board was/is running before starting data aquisition. Status: %i\n", actual_status); + spcm_dwSetParam_i32(effective_settings->hDrv, SPC_M2CMD, M2CMD_CARD_STOP); // stop the board + } + + // set sensitivity, channels, etc. + for (int j = 0; j < effective_settings->lSetChannels; j++) { + spcm_dwSetParam_i32(effective_settings->hDrv, SPC_AMP0 + 100*j, (int)floor(effective_settings->sensitivity[j]*1000)); // +/- 10V input range + spcm_dwSetParam_i32(effective_settings->hDrv, SPC_50OHM0 + 100*j, ((effective_settings->impedance[j] == 50.0) ? 1 : 0)); // 1 = 50 Ohm input impedance, 0 = 1MOhm input impedance + spcm_dwSetParam_i32(effective_settings->hDrv, SPC_OFFS0 + 100*j, effective_settings->offset[j]); // set offset in % of sensitivity +#if SPC_DEBUG + fprintf(stderr, "Input impedance for channel %i is %f\n", j, effective_settings->impedance[j]); +#endif + } + spcm_dwSetParam_i32(effective_settings->hDrv, SPC_CHENABLE, effective_settings->qwSetChEnableMap.to_ulong()); // set channels + spcm_dwSetParam_i32(effective_settings->hDrv, SPC_SAMPLERATE, (int)floor(effective_settings->samplefreq)); // set sample rate + + // check if frequency was set correctly + int setSamplingRate = 0; + spcm_dwGetParam_i32(effective_settings->hDrv, SPC_SAMPLERATE, &setSamplingRate); + if (setSamplingRate != (int)floor(effective_settings->samplefreq)) { + char parameter_info[16]; + snprintf(parameter_info,sizeof(parameter_info), "%d", setSamplingRate); + throw ADC_exception(std::string("DAC sampling rate not available. Try setting to: ")+parameter_info); + } + + + spcm_dwSetParam_i32(effective_settings->hDrv, SPC_MEMSIZE, sampleno + ADC_M2I_PRETRIGGER + ADC_M2I_POSTTRIGGER); // Memory size * effective_settings->lSetChannels * effective_settings->lBytesPerSample + + spcm_dwSetParam_i32(effective_settings->hDrv, SPC_PRETRIGGER, ADC_M2I_PRETRIGGER); + spcm_dwSetParam_i32(effective_settings->hDrv, SPC_POSTTRIGGER, ADC_M2I_POSTTRIGGER); + + // ----- start the board ----- + spcm_dwSetParam_i32(effective_settings->hDrv, SPC_M2CMD, M2CMD_CARD_START | M2CMD_CARD_ENABLETRIGGER); // start the board + + + // check for error messages + char szErrorText[ERRORTEXTLEN]; + if (spcm_dwGetErrorInfo_i32 (effective_settings->hDrv, NULL, NULL, szErrorText) != ERR_OK) { + if (effective_settings!=NULL) delete effective_settings; + effective_settings=NULL; + + fprintf(stderr, "%s", szErrorText); + throw SpectrumM2i40xxSeries_error(szErrorText); + } + fprintf(stderr, "Data acquisition setup successful\n"); +} + +result* SpectrumM2i40xxSeries::get_samples(double _timeout) { + if (core::term_signal != 0) return NULL; + size_t sampleno = (effective_settings == NULL || effective_settings->data_structure == NULL) ? 0 : effective_settings->data_structure->size(); + if (sampleno == 0) return new adc_result(1,0,NULL); + +#if SPC_DEBUG + fprintf(stderr, "samples: %lu\tchannels: %i\tbytes/sample: %i\n", sampleno, effective_settings->lSetChannels, effective_settings->lBytesPerSample); +#endif + int memSize = (sampleno + ADC_M2I_PRETRIGGER + ADC_M2I_POSTTRIGGER) * effective_settings->lSetChannels * effective_settings->lBytesPerSample; + +#if SPC_DEBUG + fprintf(stderr, "memory size: %i\n", memSize); +#endif + short int* adc_data=(short int*)malloc(memSize); + + stopwatch adc_timer; + adc_timer.start(); + int adc_status; + spcm_dwGetParam_i32(effective_settings->hDrv, SPC_M2STATUS, &adc_status); + fprintf(stderr, "card status: %x; waiting for trigger\n", adc_status); + spcm_dwSetParam_i32(effective_settings->hDrv, SPC_TIMEOUT, 1000); + + if (spcm_dwSetParam_i32(effective_settings->hDrv, SPC_M2CMD, M2CMD_CARD_WAITTRIGGER) == ERR_TIMEOUT) { + fprintf(stderr, "no trigger detected, timing out and forcing one now\n"); + spcm_dwSetParam_i32(effective_settings->hDrv, SPC_M2CMD, M2CMD_CARD_FORCETRIGGER); + } + + spcm_dwSetParam_i32(effective_settings->hDrv, SPC_TIMEOUT, 0); + spcm_dwSetParam_i32(effective_settings->hDrv, SPC_M2CMD, M2CMD_CARD_WAITREADY); + + + /*while (core::term_signal == 0 && (adc_status & M2STAT_CARD_READY) == 0 && adc_timer.elapsed() <= effective_settings->timeout) { + timespec sleeptime; + sleeptime.tv_nsec = 10*1000*1000; // 10 ms + sleeptime.tv_sec = 0; + nanosleep(&sleeptime, NULL); + spcm_dwGetParam_i32(effective_settings->hDrv, SPC_M2STATUS, &adc_status); + fprintf(stderr, "card status: %x\n", adc_status); + }*/ + fprintf(stderr, "ADC finished. Stopping\n"); + spcm_dwSetParam_i32(effective_settings->hDrv, SPC_M2CMD, M2CMD_CARD_STOP); + if (core::term_signal != 0) { + free(adc_data); + return NULL; + } + spcm_dwGetParam_i32(effective_settings->hDrv, SPC_M2STATUS, &adc_status); + if ((adc_status & M2STAT_CARD_READY) == 0) { + free(adc_data); + fprintf(stderr, "adc_status not ready: %i \n", adc_status); + throw SpectrumM2i40xxSeries_error("timeout occured while collecting data"); + } + + fprintf(stderr, "Starting data transfer.\n"); + spcm_dwDefTransfer_i64 (effective_settings->hDrv, SPCM_BUF_DATA, SPCM_DIR_CARDTOPC, 0, adc_data, 0, memSize); + spcm_dwSetParam_i32 (effective_settings->hDrv, SPC_M2CMD, M2CMD_DATA_STARTDMA | M2CMD_DATA_WAITDMA); + + + char szErrorText[ERRORTEXTLEN]; + if (spcm_dwGetErrorInfo_i32 (effective_settings->hDrv, NULL, NULL, szErrorText)){ + delete adc_data; + throw SpectrumM2i40xxSeries_error(szErrorText); + } + + short int* data_position=adc_data + ADC_M2I_PRETRIGGER*effective_settings->lSetChannels; // drop first points due to pre trigger + // produced results + adc_results* the_results = new adc_results(0); + data_position = split_adcdata_recursive(data_position, *(effective_settings->data_structure), *the_results); + if (data_position==0 || (size_t)(data_position - adc_data - ADC_M2I_PRETRIGGER * effective_settings->lSetChannels) != (sampleno * effective_settings->lSetChannels)) { + fprintf(stderr,"something went wrong while splitting data\n"); + } + free(adc_data); + + delete effective_settings; + effective_settings=NULL; + fprintf(stderr, "Finished data transfer.\n"); + return the_results; +} + +short int* SpectrumM2i40xxSeries::split_adcdata_recursive(short int* data, const DataManagementNode& structure, adc_results& result_splitted) { + + if (structure.child==NULL) { + // simple case: do real work + // todo: Channel selection + short int* datachunk = (short int*) malloc(effective_settings->lBytesPerSample*structure.n*effective_settings->lSetChannels); + if (datachunk==NULL) { + throw SpectrumM2i40xxSeries_error("not enough memory to create results"); + } + // todo: error checking + memcpy(datachunk, data, effective_settings->lBytesPerSample*structure.n*effective_settings->lSetChannels); + data += structure.n*effective_settings->lSetChannels; + adc_result* the_result = new adc_result(0, structure.n, datachunk, effective_settings->samplefreq, effective_settings->lSetChannels); + result_splitted.push_back(the_result); + if (structure.next != NULL) + data = split_adcdata_recursive(data, *(structure.next), result_splitted); + } else { + for (size_t i=0; i +#include +#include +#include "core/states.h" +#include "core/constants.h" +#include "drivers/ADC.h" +#include "GatedData.h" + +#if defined __linux__ +// ----- linux includes ----- +# include +# include +# include +# include +# include "include/dlltyp.h" +# include "include/regs.h" +# include "include/spcerr.h" +# include "include/spcm_drv.h" +#endif + +#if !(defined __linux__) +# error "sorry, expecting linux" +#endif + +#ifndef SPC_DEBUG +# define SPC_DEBUG 0 +#endif + +/** + \defgroup spectrum2i40xxseries Spectrum M2i40xx ADC + \ingroup drivers + \brief The driver for the Spectrum M2i.40xx series analog to digital converter. + + The M2i.4021/4022 we use supports sampling with up to 20 MS/s on 2/4 channels. This driver requires the GatedSampling option to be installed. + @{ +*/ + +class SpectrumM2i40xxSeries_error: public ADC_exception +{ +public: + explicit SpectrumM2i40xxSeries_error(const std::string& msg) throw (): ADC_exception(msg) {} + explicit SpectrumM2i40xxSeries_error(const char* msg) throw (): ADC_exception(msg) {} + virtual ~SpectrumM2i40xxSeries_error() throw () {} +protected: + virtual const std::string prefix() const { return "ERROR (SpectrumM2i40xxSeries_error): "; } +}; + + +/** + \brief The driver class + */ +class SpectrumM2i40xxSeries: public ADC { + + /** + ttlout triggerline device id + */ + int device_id; + /** + ttlout triggerline ttlmask + */ + ttlout trigger_line; + + /** stuff concerning fifo acquisition */ + size_t fifobufferno; + /** stuff concerning fifo acquisition */ + size_t fifobufferlen; + /** stuff concerning fifo acquisition */ + size_t fifo_minimal_size; + /** stuff concerning fifo acquisition */ + std::vector fifobuffers; + /** the extra data thread should store things to that buffer */ + short int* fifo_adc_data; + pthread_attr_t timeout_pt_attrs; + pthread_t timeout_pt; + + +# if defined __linux__ + // ----- include the easy ioctl commands from the driver ----- +# include "include/spcioctl.inc" +# endif + + /** + \brief Stores card settings and information + */ + class Configuration { + public: + /** sampling frequency in Hz */ + double samplefreq; + /** Data returned by the ADC during the different gating periods is stored in a nested list */ + DataManagementNode* data_structure; + /** a timeout for acquiring data in s*/ + double timeout; + /** configured input impedance for each channel in Ohm*/ + double* impedance; + /** configured input sensitivity for each channel in V*/ + double* sensitivity; + /** coupling 0=DC else AC */ + int coupling; + /** external reference clock **/ + int ext_reference_clock; + /** offsets for each channel in % of sensitivity **/ + int* offset; + + // Information about the card copied from the Spectrum example program + + /** \brief The card's driver handle as returned by the Spectrum driver open function */ + drv_handle hDrv; + /** \brief The card's ID + * Typically 0 if no other card is installed. The device is created as /dev/spcm0 + */ + int32 lCardID; + + /** \brief The card's type as listed in the manual */ + int32 lCardType; + /** \brief Serial Number */ + int32 lSerialNumber; + /** \brief Installed on-board memory in bytes */ + int64 llInstMemBytes; + /** \brief Bitmap with installed card features */ + int32 lFeatureMap; + /** \brief Number of channels (analog or digital) */ + int32 lMaxChannels; + /** \brief Number of bytes for each sample (analog data) */ + int32 lBytesPerSample; + /** \brief Minimum sampling rate */ + int32 lMinSampleRate; + /** \brief Maximum sampling rate */ + int32 lMaxSampleRate; + /** \brief Number of installed modules for data sorting algorithm */ + int32 lModulesCount; + + /** \brief Library version */ + int32 lLibVersion; + /** \brief Kernel version */ + int32 lKernelVersion; + + /** \brief Main control firmware version */ + int32 lCtrlFwVersion; + /** \brief Base hardware version */ + int32 lBaseHwVersion; + /** \brief Module hardware version */ + int32 lModHwVersion; + /** \brief Module firmware version */ + int32 lModFwVersion; + + + // current settings + /** \brief Bitmap for currently enabled channels */ + channel_array qwSetChEnableMap; + /** \brief Programmed memory size */ + int llSetMemsize; + /** \brief Number of used channels for this run */ + int lSetChannels; + /** \brief Currently active oversampling factor */ + int32 lOversampling; + + + // Analog Input settings + /** \brief Resolution of analog input channel */ + int32 lResolution; + int32 lMaxADCValue; // maximum range, normally 2^(Resolution-1) but can be limited + int32 lPathCount; // number of input paths + int32 lRangeCount; // number of analog input ranges + int32 lRangeMin[SPCM_MAX_AIRANGE]; // analog input ranges + int32 lRangeMax[SPCM_MAX_AIRANGE]; // ... + bool bInputTermAvailable; // input termination available + bool bDiffModeAvailable; // differential mode available + bool bACCouplingAvailable; // AC/DC coupling softwar selectable + bool bBWLimitAvailable; // bandwidth limit available + bool bOffsPercentMode; // offset programmed in percent of range + + + + /** print data for debug purpose */ + void print(FILE* f); + + bool bOpenCard(); + char* PrintInfo(); + + Configuration() { + samplefreq=0; + timeout=0; + impedance = NULL; + sensitivity = NULL; + offset = NULL; + coupling=0; + ext_reference_clock=0; + data_structure=NULL; + hDrv = NULL; + + lCardID = 0; + lSetChannels = 0; + lBytesPerSample = 0; + } + + Configuration(const Configuration& orig) { + samplefreq=orig.samplefreq; + timeout=orig.timeout; + impedance=orig.impedance; + sensitivity=orig.sensitivity; + coupling=orig.coupling; + ext_reference_clock=orig.ext_reference_clock; + qwSetChEnableMap = orig.qwSetChEnableMap; + lSetChannels = orig.lSetChannels; + if (orig.data_structure==NULL) + data_structure=NULL; + else + data_structure=new DataManagementNode(*orig.data_structure); + } + + ~Configuration() { + if (data_structure!=NULL) + delete data_structure; + } + }; + + /** + \brief Default settings for the ADC card. + These settings are copied and modified at need to derive effective settings + */ + Configuration default_settings; + + /** + \brief The settings actually used for a measurement. + Once a measurement is started, user settings may override default settings + */ + Configuration* effective_settings; + + short int* split_adcdata_recursive(short int* data, const DataManagementNode& structure, adc_results& result_splitted); + + /** + \brief Collects the settings required for the current job. + */ + void collect_config_recursive(state_sequent& exp, SpectrumM2i40xxSeries::Configuration& settings); + +public: + + SpectrumM2i40xxSeries(const ttlout& t_line, int ext_reference_clock=(int)100e6); + + virtual void sample_after_external_trigger(double rate, size_t samples, double sensitivity=5.0, size_t resolution=14); + + virtual void set_daq(state & exp); + + + /** + for syncronization purposes with sample clock + */ + //double get_sample_clock_frequency() const; + + /** + get results from transient recorder + timeout: 0.0 return immediately + */ + virtual result* get_samples(double timeout=0.0); + + /** + timeout issues during fifo acquisition + */ + //int TimeoutThread(); + + virtual ~SpectrumM2i40xxSeries(); +}; + +/** @{ + */ + +#endif diff --git a/drivers/Spectrum-M2i40xxSeries/drv_spcm_linux_drv_v214b5633.zip b/drivers/Spectrum-M2i40xxSeries/drv_spcm_linux_drv_v214b5633.zip new file mode 100644 index 0000000..a956317 Binary files /dev/null and b/drivers/Spectrum-M2i40xxSeries/drv_spcm_linux_drv_v214b5633.zip differ diff --git a/drivers/Spectrum-M2i40xxSeries/test.cpp b/drivers/Spectrum-M2i40xxSeries/test.cpp new file mode 100644 index 0000000..d01243f --- /dev/null +++ b/drivers/Spectrum-M2i40xxSeries/test.cpp @@ -0,0 +1,67 @@ +#include "Spectrum-M2i40xxSeries.h" +#include "../../machines/hardware.h" +#include "include/regs.h" +#include "core/states.h" + +#include + +int main() { + int i = 3; + fprintf(stderr, "a"+i); + + try { + SpectrumM2i40xxSeries* my_adc; + ttlout trigger; + trigger.id=0; + trigger.ttls=0x400000; /* line 22 */// + my_adc=new SpectrumM2i40xxSeries(trigger, 50.0, 1e7); + + state_sequent* test_sequence = new state_sequent(); + state* test_state = new state(1); + analogin* test_in = new analogin(); + test_in->id = 0; + //test_in->sensitivity = 5; + test_in->sample_frequency = 2e7; + test_in->resolution = 14; + test_in->samples = 4096; + test_in->channels = channel_array(15); + test_in->nchannels = test_in->channels.count(); + + test_in->sensitivity = new double[4]; + test_in->sensitivity[0] = 10.0; + test_in->sensitivity[1] = 10.0; + test_in->sensitivity[2] = 10.0; + test_in->sensitivity[3] = 10.0; + + test_in->impedance = new double[4]; + test_in->impedance[0] = 50.0; + test_in->impedance[1] = 50.0; + test_in->impedance[2] = 50.0; + test_in->impedance[3] = 50.0; + + test_in->offset = new int[4]; + test_in->offset[0] = -50; + test_in->offset[1] = 0; + test_in->offset[2] = 0; + test_in->offset[3] = 0; + + test_state->push_back(test_in); + + test_sequence->push_back(test_state); + + my_adc->set_daq(*test_sequence); + + adc_results* resultat = dynamic_cast(my_adc->get_samples(1)); + FILE* outputFile = fopen("./out.txt","w"); + int j; + for(j = 0; j < 4*resultat->front()->samples; j += 4) { + fprintf(outputFile, "%i\t%i\t%i\t%i\n", resultat->front()->data[j], resultat->front()->data[j+1], resultat->front()->data[j+2], resultat->front()->data[j+3]); + } + fclose(outputFile); + + + delete my_adc; + } catch(ADC_exception ae) { + fprintf(stderr,"adc: %s\n",ae.c_str()); + } +} diff --git a/drivers/Spectrum-MI40xxSeries/GatedData.cpp b/drivers/Spectrum-MI40xxSeries/GatedData.cpp new file mode 100644 index 0000000..cf4c0cc --- /dev/null +++ b/drivers/Spectrum-MI40xxSeries/GatedData.cpp @@ -0,0 +1,109 @@ +#include "GatedData.h" +#include +#include + +#ifndef SIZETPRINTFLETTER +# ifndef __LP64__ +# define SIZETPRINTFLETTER "u" +# else +# define SIZETPRINTFLETTER "lu" +# endif +#endif + + +/* + size of all childrens' data +*/ +size_t DataManagementNode::size() const { + if (child==NULL) return n; + size_t size_n=0; + for (const DataManagementNode* i=child; i!=NULL; i=i->next) size_n+=i->size(); + return size_n*n; +} + +/* + reduce depth recursively +*/ +DataManagementNode* DataManagementNode::reduce() { + // simple case: no children + if (child==NULL) return this; + // evaluate all children's nodes and append remaining + DataManagementNode* i=child; + DataManagementNode* last_child=NULL; + while (i->next!=NULL) { + DataManagementNode* reduced_child=i->reduce(); + if (reduced_child->n!=0) { + if (last_child==NULL) + child=reduced_child; + else + last_child->next=reduced_child; + last_child=reduced_child; + last_child->parent=this; + last_child->next=NULL; + } + DataManagementNode* next_child=i->next; + // throw away unused one + if (reduced_child->n==0 || i!=reduced_child) { + i->parent=NULL; + i->next=NULL; + delete i; + } + i=next_child; + } + // now look at the remaining list + if (child==NULL) { + // return empty node + n=0; + } + else + // reduce depth by one + if (child->next==NULL || child->child!=NULL) { + n*=child->n; + DataManagementNode* superficial=child; + child=child->child; + superficial->parent=NULL; + delete superficial; + } + return this; +} + +void DataManagementNode::print(FILE* f,size_t indent) { + char* indent_space=new char[indent+1]; + memset(indent_space,' ',indent); + indent_space[indent]=0; + if (child==NULL) + fprintf(f,"%s%" SIZETPRINTFLETTER " samples\n",indent_space, n); + else { + fprintf(f,"%s%" SIZETPRINTFLETTER ":\n",indent_space, n); + child->print(f,indent+6); + } + delete indent_space; + if (next!=NULL) + next->print(f,indent); + +} + +DataManagementNode::DataManagementNode(const DataManagementNode& orig) { + parent=NULL; + if (orig.child!=NULL) { + child=new DataManagementNode(*orig.child); + for (DataManagementNode* i=child; i!=NULL; i=i->next) i->parent=this; + } + if (orig.next!=NULL) next=new DataManagementNode(*orig.next); + n=orig.n; +} + +/** + recursive destruction +*/ +DataManagementNode::~DataManagementNode() { + assert(parent==NULL); + assert(next==NULL); + while (child!=NULL) { + DataManagementNode* this_one=child; + child=child->next; + this_one->parent=NULL; + this_one->next=NULL; + delete this_one; + } +} diff --git a/drivers/Spectrum-MI40xxSeries/GatedData.h b/drivers/Spectrum-MI40xxSeries/GatedData.h new file mode 100644 index 0000000..95abfeb --- /dev/null +++ b/drivers/Spectrum-MI40xxSeries/GatedData.h @@ -0,0 +1,50 @@ +#include +#include + +class DataManagementNode; + +/** + manages nested ADC data structure +*/ +class DataManagementNode { + public: + /// parent node, top node has NULL + DataManagementNode* parent; + /// reference to nested loops + DataManagementNode* child; + /// next node in sequences + DataManagementNode* next; + + /** + if the child is NULL, n is the number of real samples + otherwise repeatition number of substructure + */ + size_t n; + + /** + default constructor of NOP node + */ + DataManagementNode(class DataManagementNode* par=NULL): parent(par), child(NULL), next(NULL), n(0) {} + + /** + (deep) copy constructor + */ + DataManagementNode(const DataManagementNode& orig); + + /** + size of all childrens' data + */ + size_t size() const; + /** + reduce depth recursively + */ + DataManagementNode* reduce(); + + + void print(FILE* f,size_t indent=0); + + /** + recursive destruction, call only for root + */ + ~DataManagementNode(); +}; diff --git a/drivers/Spectrum-MI40xxSeries/Makefile b/drivers/Spectrum-MI40xxSeries/Makefile new file mode 100644 index 0000000..902bfec --- /dev/null +++ b/drivers/Spectrum-MI40xxSeries/Makefile @@ -0,0 +1,42 @@ +CPPFLAGS=-Iinclude -I../.. +CXXFLAGS=-Wshadow -Wall -pedantic -O0 +AR=ar + + +SPC_HEADERS = include/spcerr.h include/regs.h include/dlltyp.h include/spcioctl.inc +SPC_ZIP = ../Spectrum-M2i40xxSeries/drv_spcm_linux_drv_v214b5633.zip + +all: clean $(SPC_HEADERS) patch Spectrum-MI40xxSeries.a hw_test_int hw_test_ext + +$(SPC_HEADERS): $(SPC_ZIP) + unzip -u $< "*.h" "*.txt" "*.inc" -d include/ + +patch: + patch -N -p0 < ftbfs_patch.diff + +Spectrum-MI40xxSeries.a: Spectrum-MI40xxSeries.o GatedData.o + $(AR) r $@ $^ + +Spectrum-MI40xxSeries.o: Spectrum-MI40xxSeries.cpp Spectrum-MI40xxSeries.h GatedData.h include/spcerr.h + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $< + +GatedData.o: GatedData.cpp GatedData.h + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $< + +test.o: test.cpp Spectrum-MI40xxSeries.h include/spcerr.h + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $< + +test: test.o Spectrum-MI40xxSeries.o ../../core/core.a + $(CXX) -o $@ -I../.. ../ADC.h $^ -lpthread -lm +hw_test_int: hw_test_intclock.cpp + $(CXX) -o $@ -Iinclude $^ +hw_test_ext: hw_test_extclock.cpp + $(CXX) -o $@ -Iinclude $^ + + +../../core/core.a: + $(MAKE) -C ../../core core.a + +clean: + rm -f *.o *.a *~ test *.core hw_test_ext hw_test_int + rm -rf include diff --git a/drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.cpp b/drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.cpp new file mode 100644 index 0000000..8c44be2 --- /dev/null +++ b/drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.cpp @@ -0,0 +1,971 @@ +#include +#include +#include +#include +#include "pthread.h" +#include "core/core.h" +#include "core/stopwatch.h" +#include "core/result.h" +#include "core/xml_states.h" +#include "Spectrum-MI40xxSeries.h" + +#ifndef SPC_DEBUG +# define SPC_DEBUG 0 +#endif +#ifndef SIZETPRINTFLETTER +# ifndef __LP64__ +# define SIZETPRINTFLETTER "u" +# else +# define SIZETPRINTFLETTER "lu" +# endif +#endif + +void SpectrumMI40xxSeries::Configuration::print(FILE* f) { + fprintf(f, "Spectrum MI40xx Series Configuration:\n"); + fprintf(f, " Sample Frequency %g Hz, Acquisition Timeout %g\n",samplefreq, timeout); + if (data_structure!=NULL) + data_structure->print(f,2); + else + fprintf(f, " No Data to acquire\n"); + + for (int i=0; i < lSetChannels; i++) { + fprintf(f, " Channel %i: Impedance %f Ohm, Sensitivity %f V, Coupling %s\n", i, impedance[i], sensitivity[i], (coupling==0)?"DC":"AC"); + } +} + + +SpectrumMI40xxSeries::SpectrumMI40xxSeries(const ttlout& t_line, float impedance, int ext_reference_clock) { + // print neat string to inform the user + fprintf(stderr, "Initializing ADC card\n"); + + // to be configured + device_id=0; + trigger_line=t_line; + + default_settings.qwSetChEnableMap = channel_array(ADC_MI_DEFAULT_CHANNELS); + default_settings.lSetChannels = default_settings.qwSetChEnableMap.count(); + default_settings.impedance = new double[default_settings.lSetChannels]; + default_settings.sensitivity = new double[default_settings.lSetChannels]; + default_settings.offset = new int[default_settings.lSetChannels]; + + for(int i = 0; i < default_settings.lSetChannels; i++) { + default_settings.impedance[i] = ADC_MI_DEFAULT_IMPEDANCE; + + default_settings.sensitivity[i] = ADC_MI_DEFAULT_SENSITIVITY; // Set sensitivity in Volts to the default (maximum) value + default_settings.offset[i] = ADC_MI_DEFAULT_OFFSET; // Set offsets in % of sensitivity to default (0) + } + + + default_settings.ext_reference_clock=ext_reference_clock; // Hz + effective_settings=NULL; + +# if defined __linux__ + // ----- open driver ----- + deviceno = open ("/dev/spc0", O_RDWR); + if (deviceno == -1) { + std::string error_message="could not open /dev/spc0"; + if (errno==0) throw SpectrumMI40xxSeries_error( error_message+" (unknown error)"); + else throw SpectrumMI40xxSeries_error( error_message+" ("+strerror(errno)+")"); + } +# endif +# if defined __CYGWIN__ + // open library dll + spectrum_driver_dll = LoadLibrary("spectrum.dll"); + if (spectrum_driver_dll==NULL) + throw SpectrumMI40xxSeries_error("could not open driver dll"); + deviceno=0; + // load driver functions +# define load_spectrum_function(name) name = ( name##_type)GetProcAddress(spectrum_driver_dll, #name); \ +if (name == NULL) { FreeLibrary(spectrum_driver_dll); \ + throw SpectrumMI40xxSeries_error("failed to load function" #name "" ); \ +} + load_spectrum_function(SpcInitPCIBoards); + load_spectrum_function(SpcGetParam); + load_spectrum_function(SpcSetParam); + load_spectrum_function(SpcGetData); + + // find spectrum boards + boardcount=0; + SpcInitPCIBoards(&boardcount,&PCIVersion); + if (boardcount<=0) { + FreeLibrary(spectrum_driver_dll); + throw SpectrumMI40xxSeries_error("no spectrum boards found"); + } + +# endif + /* calculate the sample number that can be saved without fifo */ + int memory_size=0; + SpcGetParam(deviceno,SPC_PCIMEMSIZE,&memory_size); + fifo_minimal_size=memory_size/(sizeof(short int)*2); + + /* get the device type */ + SpcGetParam(deviceno, SPC_PCITYP, &cardType); + if (cardType != TYP_MI4020 && cardType != TYP_MI4021 && cardType != TYP_MI4022 && cardType != TYP_MI4030 && cardType != TYP_MI4031 && cardType != TYP_MI4032) { + throw SpectrumMI40xxSeries_error("Board type not supported"); + } + + /* make a check, whether spectrum board is running */ + int actual_status=0; + SpcGetParam(deviceno, SPC_STATUS, &actual_status); + if (actual_status!=SPC_READY) { + fprintf(stderr, "Warning: Spectrum board was is running before reset.\n"); + } +#if SPC_DEBUG + fprintf(stderr,"External reference clock: %i\n",default_settings.ext_reference_clock); +#endif + int ErrorOccurred=1; + SpcSetParam(deviceno, SPC_COMMAND, SPC_RESET)==ERR_OK && // reset device first + SpcSetParam (deviceno, SPC_GATE, 1)==ERR_OK && // Gated Triggering + SpcSetParam (deviceno, SPC_PLL_ENABLE, 1)==ERR_OK && // Internal PLL enabled for clock + SpcSetParam (deviceno, SPC_CLOCKOUT, 0)==ERR_OK && // No clock output + SpcSetParam (deviceno, SPC_REFERENCECLOCK, default_settings.ext_reference_clock)==ERR_OK && // external reference clock + SpcSetParam (deviceno, SPC_EXTERNALCLOCK, 0)==ERR_OK && // but no external sample clock + SpcSetParam (deviceno, SPC_CLOCK50OHM, 0)==ERR_OK && // clock input NOT with 50Ohm impedance + SpcSetParam (deviceno, SPC_TRIGGERMODE, TM_TTLPOS)==ERR_OK && // ttl trigger is used + SpcSetParam (deviceno, SPC_TRIGGEROUT, 0)==ERR_OK && // No trigger output + SpcSetParam (deviceno, SPC_TRIGGER50OHM, 0)==ERR_OK && // Trigger to 1MOhm, necessary for steep slopes + (ErrorOccurred=0); // done, really no error occurred + + // ----- driver error: request error and end program ----- + if (ErrorOccurred!=0) { + int32 lErrorCode, lErrorReg, lErrorValue; + SpcGetParam (deviceno, SPC_LASTERRORCODE, &lErrorCode); + SpcGetParam (deviceno, SPC_LASTERRORREG, &lErrorReg); + SpcGetParam (deviceno, SPC_LASTERRORVALUE, &lErrorValue); + char error_message[256]; + snprintf(error_message, sizeof(error_message),"Configuration error %d in register %d at value %d", lErrorCode, lErrorReg, lErrorValue); + throw SpectrumMI40xxSeries_error(error_message); + } + + /* print lines with useful information: */ + fprintf(stderr, "Spectrum MI40xx series board with %d byte memory\n", memory_size); + fprintf(stderr, + " impedance set to %g Ohm\n expecting trigger on id=%d ttls=0x%lx\n external clock frequency set to %g Hz\n", + impedance, + t_line.id, + t_line.ttls.to_ulong(), + (float)ext_reference_clock ); +} + +void SpectrumMI40xxSeries::sample_after_external_trigger(double rate, size_t samples, double sensitivity, size_t resolution) { + throw SpectrumMI40xxSeries_error("SpectrumMI40xxSeries::sample_after_external_trigger is not jet implemented"); +} + + + +void SpectrumMI40xxSeries::collect_config_recursive(state_sequent& exp, SpectrumMI40xxSeries::Configuration& settings) { + + /* start with dummy node */ + DataManagementNode* new_branch=new DataManagementNode(NULL); + DataManagementNode* where_to_append=new_branch; + double parent_timeout=settings.timeout; + settings.timeout=0.0; + + /* loop over all states and sequences */ + for (state_sequent::iterator i=exp.begin(); i!=exp.end(); ++i) { + + state* a_state=dynamic_cast(*i); + if (a_state==NULL) + throw SpectrumMI40xxSeries_error("state_atom not expected in sate_sequent"); + if (dynamic_cast(*i)!=NULL) + throw SpectrumMI40xxSeries_error("state_parallel handling is not jet implemented"); + state_sequent* a_sequence=dynamic_cast(a_state); + if (a_sequence!=NULL) { + // found a sequence + DataManagementNode* tmp_structure=settings.data_structure; + settings.data_structure=where_to_append; + collect_config_recursive(*a_sequence, settings); + settings.data_structure=tmp_structure; + } /* end working on sequence */ + else { + // found a state, not a sequence + settings.timeout+=a_state->length; +#if SPC_DEBUG + fprintf(stderr,"SETTINGS %e %e\n",settings.timeout,a_state->length); +#endif + // collect analogin sections in state + std::list inputs; + /* loop over all device definitions in a state */ + state::iterator j=a_state->begin(); + while (j!=a_state->end()) { + analogin* input=dynamic_cast(*j); + if (input!=NULL && input->id==device_id) { + /* collect appropiate analogin sections, forget others */ + if (input->samples<=0 || input->sample_frequency<=0) + delete input; + else + inputs.push_back(input); + j=a_state->erase(j); + } else + ++j; + } + if (!inputs.empty()) { + /* evaluate the found analogin definitions */ + if (inputs.size()>1) { + while (!inputs.empty()) { + delete inputs.front(); + inputs.pop_front(); + } + throw ADC_exception("can not handle more than one analogin section per state"); + } + /* save sampling frequency */ + if (settings.samplefreq<=0) + settings.samplefreq=inputs.front()->sample_frequency; + else if (settings.samplefreq != inputs.front()->sample_frequency) { + while (!inputs.empty()) { + delete inputs.front(); + inputs.pop_front(); + } + throw ADC_exception("Sorry, but gated sampling requires same sampling frequency in all analogin sections"); + } + + /* save channel mask and number of channels */ + if (settings.lSetChannels > 0 && settings.qwSetChEnableMap.to_ulong() > 0) { + if (settings.qwSetChEnableMap != inputs.front()->channels) { + fprintf(stderr, "Warning! different channels enabled in input %lu and in config %lu, setting to default \n", + settings.qwSetChEnableMap.to_ulong(), + inputs.front()->channels.to_ulong()); + settings.qwSetChEnableMap = channel_array(ADC_MI_DEFAULT_CHANNELS); + settings.lSetChannels = settings.qwSetChEnableMap.count(); + } + } else { + settings.qwSetChEnableMap = inputs.front()->channels; + settings.lSetChannels = inputs.front()->nchannels; + } + + /* save sensitivity */ + if (settings.sensitivity != NULL) { // if sensitivity is set, make sure it's valid (i.e. the same for all inputs) + for (int k = 0; k < inputs.front()->nchannels; k++) { + if (settings.sensitivity[k] != inputs.front()->sensitivity[k]) { + fprintf(stderr, "Warning! different sensitivity specified (here %f, elsewhere %f), choosing higher voltage\n", + settings.sensitivity[k], + inputs.front()->sensitivity[k]); + if (settings.sensitivity[k] < inputs.front()->sensitivity[k]) { + settings.sensitivity[k] = inputs.front()->sensitivity[k]; + } + } + } + } else { + settings.sensitivity = inputs.front()->sensitivity; + } + // check if sensitivity is valid + for (int k = 0; k < inputs.front()->nchannels; k++) { + bool sensAllowed = false; + for (int l = 0; l < ADC_MI_ALLOWED_SENSITIVITY_LENGTH; l++) { + if (settings.sensitivity[k] == ADC_MI_ALLOWED_SENSITIVITY[l] ) { + sensAllowed = true; + break; + } + } + if (!sensAllowed) { + fprintf(stderr, "Warning! Illegal sensitivity specified for channel %i: %f", k, inputs.front()->sensitivity[k]); + settings.sensitivity[k] = ADC_MI_DEFAULT_SENSITIVITY; + } + } + + /* save impedance */ + if (settings.impedance != NULL) { + for (int k = 0; k < inputs.front()->nchannels; k++) { + if (settings.impedance[k] != inputs.front()->impedance[k]) { + fprintf(stderr, "Warning! different impedance specified (here %f, elsewhere %f), setting to default\n", + settings.impedance[k], + inputs.front()->impedance[k]); + settings.impedance[k] = ADC_MI_DEFAULT_IMPEDANCE; + } + if (settings.impedance[k] != ADC_MI_DEFAULT_IMPEDANCE && settings.impedance[k] != ADC_MI_ALLOWED_IMPEDANCE) { + fprintf(stderr, "Warning! Illegal impedance specified for channel %i: %f",k, inputs.front()->impedance[k]); + settings.offset[k] = 0; + } + } + } else { + settings.impedance = inputs.front()->impedance; + } + + /* save offsets */ + if (settings.offset != NULL) { + for (int k = 0; k < inputs.front()->nchannels; k++) { + if (settings.offset[k] != inputs.front()->offset[k]) { + fprintf(stderr, "Warning! different impedance specified (here %i, elsewhere %i), setting to default\n", + settings.offset[k], + inputs.front()->offset[k]); + settings.offset[k] = ADC_MI_DEFAULT_OFFSET; + } + if (inputs.front()->offset[k] > 100 || inputs.front()->offset[k] < -100) { + fprintf(stderr, "Warning! Illegal offset specified for channel %i: %i", k, inputs.front()->offset[k]); + settings.offset[k] = 0; + } + } + } else { + settings.offset = inputs.front()->offset; + } + + if (inputs.front()->samples%4 != 0) { + throw ADC_exception("Number of samples must be a multiple of four"); + } + + // calculate the time required + double delayed_gating_time=0.0; + // the gating time has an offset, which was found to be 1.5 dwelltimes for <2.5MHz and 4.5 dwelltimes for >=2.5MHz + double gating_time; + +#if SPC_DEBUG + fprintf(stderr, "Channels: %lu\n", settings.qwSetChEnableMap.to_ulong()); +#endif + + /* check if channel mask is legal for the card */ + if (this->IsChannelMaskLegal(inputs.front()->channels.to_ulong())) { + settings.qwSetChEnableMap = inputs.front()->channels; + settings.lSetChannels = inputs.front()->nchannels; + } else { + throw SpectrumMI40xxSeries_error("Selected channels combination not allowed for this card type"); + } + /* apply proper timing */ + if ( settings.qwSetChEnableMap.to_ulong()==(CHANNEL0|CHANNEL1) || settings.qwSetChEnableMap.to_ulong()==(CHANNEL0|CHANNEL1|CHANNEL2|CHANNEL3) ) { +#if SPC_DEBUG + fprintf(stderr, "Default Channels\n"); +#endif + if (settings.samplefreq<2.5e6) { + // if sampling rate is <2.5MHz, there is another data handling mode, + // see MI4021 manual page 79: "Accquisition Delay: -6 Samples" + // it might be necessary to add 0.1 dwelltime to shift the sampling start a little more... + + // edit by Stefan Reutter @2013-06, it seems that the MI4021 cards actually have a second + // threshold at 500 kHz that is not mentioned in the manual. + // this can be tested by disabling the if below and switching over the threshold + gating_time=(inputs.front()->samples)/settings.samplefreq; + if (settings.samplefreq >= 5e5) { + delayed_gating_time=ceil(1e8*6.0/settings.samplefreq)/1e8; + } else { + delayed_gating_time=0.0; + } + } else { + gating_time=(inputs.front()->samples+3)/settings.samplefreq; + delayed_gating_time=0.0; + } + } + // disabled the more exotic channel setup as it is untested with the updated timings and probably not used anyways + /* else if (settings.qwSetChEnableMap.to_ulong()==CHANNEL0 || settings.qwSetChEnableMap.to_ulong()==(CHANNEL0|CHANNEL2) ) { +#if SPC_DEBUG + fprintf(stderr, "Weird Channels\n"); +#endif + if (settings.samplefreq<5e6) { + gating_time=(inputs.front()->samples+1.5)/settings.samplefreq; + delayed_gating_time=ceil(1e8*6.0/settings.samplefreq)/1e8; + } else { + gating_time=(inputs.front()->samples+4.5)/settings.samplefreq; + delayed_gating_time=-ceil(1e8*7.0/settings.samplefreq)/1e8; + + } */else { + throw SpectrumMI40xxSeries_error("Selected channels combination not allowed"); + } + + gating_time=ceil(1e8*gating_time)/1e8; + double time_required=delayed_gating_time+gating_time; + // check time requirements + if (a_state->length < gating_time) { + char parameter_info[512]; + snprintf(parameter_info,sizeof(parameter_info), + "(%" SIZETPRINTFLETTER " samples, %g samplerate, %e time required, %e state time)", + inputs.front()->samples, + settings.samplefreq, + time_required, + a_state->length); +#if SPC_DEBUG + fprintf(stderr, "state is shorter than acquisition time %e time required, %e state time\n", gating_time, a_state->length); +#endif + // update the state length if it's shorter than the gate. this is usually due to rounding to 10 ns for the pulseblaster + if (ceil(1e8*a_state->length)/1e8 < time_required) { + throw ADC_exception(std::string("state is shorter than acquisition time")+parameter_info); + } else { + a_state->length = time_required; + } + } + // if necessary, add the gating pulse delay... + if (delayed_gating_time>0.0) { + state* delayed_gating_state=new state(*a_state); + delayed_gating_state->length=delayed_gating_time; + // insert before me + exp.insert(i,(state_atom*)delayed_gating_state); + } else if (delayed_gating_time < 0.0) { + /* + For +samples delays + 1. get the previous state + 2. if the length is not long enough (6*dw) add the state before + 3. split the state(s) so that we have the gating on 6*dw before the actual recording + */ + double rest_length = delayed_gating_time; + state_sequent::iterator i_prev; + state* prev_state; + i_prev = i; + do { + i_prev--; + prev_state = dynamic_cast(*(i_prev)); + rest_length -= prev_state->length; + fprintf(stderr, "DW rest_length: %g\n", rest_length); + fprintf(stderr, "DW state_length: %g\n", prev_state->length); + if (rest_length >= 0) + prev_state->push_back(trigger_line.copy_new()); // add trigger to this state + else { // split final state + state* prev_state_1 = prev_state->copy_flat(); //create copy of current state + prev_state_1->length += rest_length; // adjust 1st part length + exp.insert(i_prev,(state_atom*) prev_state_1); // insert AFTER prev_state + prev_state->length = -rest_length; // adjust 2nd part length + prev_state->push_back(trigger_line.copy_new()); // add trigger to 2nd part + break; + } + + } while (i_prev!=exp.begin() || rest_length > 0.0); + } +# if SPC_DEBUG + fprintf(stderr, "sequence after pre_trigger correction:\n"); + xml_state_writer().write_states(stderr, exp); +# endif + + // adapt the pulse program for gated sampling + if (a_state->length == gating_time) { + // state has proper length + a_state->push_back(trigger_line.copy_new()); + } else { +# if SPC_DEBUG + fprintf(stderr, "state too long, length %e, time required %e\n", a_state->length, time_required); +# endif + // state is too long... + // create new one with proper time and gated sampling pulse + state* gated_sampling_pulse=new state(*a_state); + gated_sampling_pulse->length=gating_time; + gated_sampling_pulse->push_back(trigger_line.copy_new()); + // insert gate pulse state before remaining (original) state + exp.insert(i,(state_atom*)gated_sampling_pulse); + // shorten this state + a_state->length-=time_required; + } + +# if SPC_DEBUG + fprintf(stderr, "sequence after correcting trigger state:\n"); + xml_state_writer().write_states(stderr, exp); +# endif + + /* save sampleno */ + DataManagementNode* new_one=new DataManagementNode(new_branch); + new_one->n=inputs.front()->samples; + new_one->child=NULL; + new_one->next=where_to_append->next; + where_to_append->next=new_one; + where_to_append=new_one; + while (!inputs.empty()) {delete inputs.front(); inputs.pop_front();} + } /* !inputs.empty() */ + + + + } /* end working on state */ + + + } /* i */ + + /* something happened? */ + if (new_branch->next!=NULL) { + /* make dummy node to a loop */ + new_branch->n=exp.repeat; + new_branch->child=new_branch->next; + + /* if possible, append it */ + if (settings.data_structure!=NULL) { + new_branch->parent=settings.data_structure->parent; + new_branch->next=settings.data_structure->next; + settings.data_structure->next=new_branch; + } + else { + new_branch->parent=NULL; + new_branch->next=NULL; + settings.data_structure=new_branch; + } + } + else + delete new_branch; + + settings.timeout*=exp.repeat; + settings.timeout+=parent_timeout; +#ifdef SPC_DEBUG + fprintf(stderr,"setting.timout %g\n",settings.timeout); +#endif + return; +} + +static void* SpectrumMI40xxSeries_TimeoutThread(void* data) { + int *ret = new int; + *ret = ((SpectrumMI40xxSeries*)data)->TimeoutThread(); + return (void*) ret; +} + +void SpectrumMI40xxSeries::set_daq(state & exp) { + + // cleanup?! + if (!fifobuffers.empty()) { + fprintf(stderr, "normally there should be a cleanup at the end of acquisition\n"); + for (std::vector::iterator i=fifobuffers.begin(); i!=fifobuffers.end(); ++i) free(*i); + fifobuffers.clear(); + } + + /* what could be done: PCIBIT_GATE, PCIBIT_MULTI */ + int features; + SpcGetParam(deviceno,SPC_PCIFEATURES,&features); + + state_sequent* exp_sequence=dynamic_cast(&exp); + if (exp_sequence==NULL) + throw ADC_exception("Spectrum-MI40xxSeries::set_daq only working on sequences"); + +# if SPC_DEBUG + fprintf(stderr, "working on sequence:\n"); + xml_state_writer().write_states(stderr, *exp_sequence); +# endif + + /* find out what to do */ + Configuration* conf=new Configuration(); + collect_config_recursive(*exp_sequence, *conf); + if (conf->samplefreq<=0) conf->samplefreq=default_settings.samplefreq; + if (conf->impedance == NULL) conf->impedance = default_settings.impedance; + if (conf->sensitivity == NULL) conf->sensitivity = default_settings.sensitivity; + if (conf->offset == NULL) conf->offset = default_settings.offset; + + if (conf->lSetChannels == 0) { + conf->lSetChannels = default_settings.lSetChannels; + conf->qwSetChEnableMap = default_settings.qwSetChEnableMap; + } + + size_t sampleno=(conf->data_structure==NULL)?0:conf->data_structure->size(); + + /* nothing to do! */ + if (sampleno==0) { + delete conf; + effective_settings=NULL; + return; + } + + + if (sampleno<16 || sampleno%16!=0) { + delete conf; + throw SpectrumMI40xxSeries_error("Total number of samples must be multiple of 16 and at least 16"); + } + + effective_settings=conf; + +#if SPC_DEBUG + /* settings for this experiment */ + conf->print(stderr); +#endif + + /* make a check, whether spectrum board is running */ + int actual_status=0; + SpcGetParam (deviceno, SPC_STATUS, &actual_status); + if (actual_status!=SPC_READY) { + fprintf(stderr, "Warning: Spectrum board was/is running before starting data aquisition.\n"); + SpcSetParam (deviceno, SPC_COMMAND, SPC_STOP); // stop the board + } + + /* and the dirty things there... + ----- setup board for recording ----- + */ + for (unsigned int j=0; j<(unsigned int)effective_settings->lSetChannels; j++) { + SpcSetParam (deviceno, SPC_AMP0 + 100*j, (int)floor(effective_settings->sensitivity[j]*1000)); // +/- 5V input range + SpcSetParam (deviceno, SPC_50OHM0 + 100*j, ((effective_settings->impedance[j]==50.0)?1:0)); // 1 = 50 Ohm input impedance, 0 = 1MOhm input impedance + SpcSetParam (deviceno, SPC_OFFS0 + 100*j, effective_settings->offset[j]); // set offset to zero + } + + SpcSetParam (deviceno, SPC_CHENABLE, effective_settings->qwSetChEnableMap.to_ulong()); // Enable channels for recording + int activated_channels; + SpcGetParam (deviceno, SPC_CHENABLE, &activated_channels); + + SpcSetParam (deviceno, SPC_SAMPLERATE, (int)floor(effective_settings->samplefreq)); // Samplerate + + // the MI4021 card doesn't accept all sampling frequency settings. check if the sampling rate is set correctly. if not, throw an exception + int setSamplingRate = 0; + SpcGetParam (deviceno, SPC_SAMPLERATE, &setSamplingRate); + if (setSamplingRate != (int)floor(effective_settings->samplefreq)) { + char parameter_info[16]; + snprintf(parameter_info,sizeof(parameter_info), "%d", setSamplingRate); + throw ADC_exception(std::string("DAC sampling rate not available. Try setting to: ")+parameter_info); + } + + // decide for aquisition mode and start it + int16 nErr=ERR_OK; + if (sampleno 1093 + SpcSetAdr (deviceno, SPC_FIFO_BUFADR0+i, newbuffer); +#endif +#if SPC_DEBUG + fprintf(stderr, "success.\n"); +#endif + } +#if SPC_DEBUG + fprintf(stderr, "starting fifo thread\n"); +#endif + // to do: append a new state to overcome problems with fifo... + // need another thread, that collects the data + pthread_attr_init(&timeout_pt_attrs); + pthread_create(&timeout_pt, &timeout_pt_attrs, SpectrumMI40xxSeries_TimeoutThread,(void*)this); + } + // ----- driver error: request error and end program ----- + if (nErr != ERR_OK) { + if (effective_settings!=NULL) delete effective_settings; + effective_settings=NULL; + for (std::vector::iterator i=fifobuffers.begin(); i!=fifobuffers.end(); ++i) free(*i); + fifobuffers.clear(); + // create an error message + int32 lErrorCode, lErrorReg, lErrorValue; + SpcGetParam (deviceno, SPC_LASTERRORCODE, &lErrorCode); + SpcGetParam (deviceno, SPC_LASTERRORREG, &lErrorReg); + SpcGetParam (deviceno, SPC_LASTERRORVALUE, &lErrorValue); + char error_message[256]; + snprintf(error_message, sizeof(error_message),"Configuration error %d in register %d at value %d", lErrorCode, lErrorReg, lErrorValue); + throw SpectrumMI40xxSeries_error(error_message); + } + +} + +double SpectrumMI40xxSeries::get_sample_clock_frequency() const { + + if (effective_settings==NULL || + effective_settings->samplefreq<=0 || + effective_settings->data_structure==NULL || + effective_settings->data_structure->size()==0) + return 0; + + return effective_settings->samplefreq; +} + +int SpectrumMI40xxSeries::TimeoutThread() { + // now collect data + assert(effective_settings!=NULL); + size_t sampleno=effective_settings->data_structure->size(); + size_t buff_number_expected=(sampleno*2*sizeof(short int)+fifobufferlen-1)/fifobufferlen; + SpcSetParam(deviceno, SPC_FIFO_BUFMAXCNT, buff_number_expected); + size_t buff_number=0; + short int* buff_pointer=fifo_adc_data; + + // start fifo aquisition +#if SPC_DEBUG + fprintf(stderr, "SPC_FIFOSTART\n"); +#endif + SpcSetParam(deviceno, SPC_COMMAND, SPC_FIFOSTART); + + do { + // wait for the buffers + pthread_testcancel(); +#if SPC_DEBUG + fprintf(stderr, "reading buffer no %" SIZETPRINTFLETTER "/%" SIZETPRINTFLETTER "\n", buff_number+1, buff_number_expected); +#endif + if (buff_number+1==buff_number_expected) { + // the last one is special, copy only expected bytes + memcpy(buff_pointer, fifobuffers[buff_number%(fifobuffers.size())], sampleno*2*sizeof(short int)-fifobufferlen*buff_number); + break; + } + else + buff_pointer=(short int*)mempcpy(buff_pointer, fifobuffers[buff_number%(fifobuffers.size())], fifobufferlen); + SpcSetParam(deviceno,SPC_FIFO_BUFREADY, buff_number%(fifobuffers.size())); + buff_number++; + int buff_available; + SpcGetParam(deviceno, SPC_FIFO_BUFCOUNT, &buff_available); + if (((size_t)buff_available)>buff_number) { + // get also the next buffer... +#if SPC_DEBUG + fprintf(stderr, "read %" SIZETPRINTFLETTER " buffers, %d buffers gained by the hardware sofar\n", buff_number, buff_available); +#endif + continue; + } + + int adc_status; +#if SPC_DEBUG + fprintf(stderr, "SPC_STATUS\n"); +#endif + SpcGetParam (deviceno, SPC_STATUS, &adc_status); + if (adc_status==SPC_READY) { + // this must be a timeout! + //pthread_cancel(timeout_pt); + //pthread_join(timeout_pt,NULL); + //pthread_attr_destroy(&timeout_pt_attrs); + free(fifo_adc_data); + fifo_adc_data=NULL; + throw SpectrumMI40xxSeries_error("timout occured while collecting data"); + return 0; + } +#if SPC_DEBUG + fprintf(stderr, "waiting for next fifo buffer\n"); + fprintf(stderr, "SPC_FIFOWAIT\n"); +#endif + SpcSetParam(deviceno, SPC_COMMAND, SPC_FIFOWAIT); + } + while (buff_numberlSetChannels; + if (structure.child==NULL) { + // simple case: do real work + // todo: Channel selection + short int* datachunk=(short int*)malloc(sizeof(short int)*structure.n*nchannels); + if (datachunk==NULL) { + throw SpectrumMI40xxSeries_error("not enough memory to create results"); + } + // todo: error checking + memcpy(datachunk, data, sizeof(short int)*structure.n*nchannels); + data+=structure.n*nchannels; + adc_result* the_result=new adc_result(0, structure.n, datachunk, effective_settings->samplefreq,nchannels); + result_splitted.push_back(the_result); + if (structure.next!=NULL) + data=split_adcdata_recursive(data, *(structure.next), result_splitted); + } + else + for (size_t i=0; idata_structure==NULL)?0:effective_settings->data_structure->size(); + if (sampleno==0) return new adc_result(1,0,NULL); + short nchannels=(effective_settings==NULL)?0:effective_settings->lSetChannels; + if (nchannels==0) throw SpectrumMI40xxSeries_error("No channels specified"); + +// double timeout=_timeout; + +#if SPC_DEBUG + fprintf(stderr,"effective_settings in get_samples: %g\n",effective_settings->timeout); +#endif + // allocate a lot of space + // todo: use GatedSampling buffers directly! + + if (fifobuffers.empty()) { +#if SPC_DEBUG + printf("ADC not in FIFO mode\n"); +#endif + short int* adc_data=(short int*)malloc(sampleno*sizeof(short int)*nchannels); + if (adc_data==NULL) { + for (std::vector::iterator i=fifobuffers.begin(); i!=fifobuffers.end(); ++i) free(*i); + fifobuffers.clear(); + throw SpectrumMI40xxSeries_error("could not allocate adc data memory"); + } + + // simple version: standard acquisition, wait and get... +#if SPC_DEBUG + fprintf(stderr, "fetching %" SIZETPRINTFLETTER " samples in normal mode (timeout is %g)\n",sampleno,effective_settings->timeout); +#endif + stopwatch adc_timer; + adc_timer.start(); + int adc_status, i; + double elapsed_time=0; + /* + The gate-end signal can only be recognized at an eigth samples alignement. + The board can record up to 7 more samples, the timeout has to be allowed to be longer + */ + double post_gate_maxtime=(1e8*7.5/effective_settings->samplefreq)/1e8; + i = 0; + SpcGetParam(deviceno, SPC_STATUS, &adc_status); + /* + Sometimes the timeout is not enough, the reason could be that the timer + starts running before the actual start of the pulse program because the OS is not loading and starting + the pulseblaster immediatly. It is not possible to + synchronize the timers for now. + Thus, we add a generous 1s to the timeout to be on the safe side. One second ought to be enoug for everybody! + */ + while (core::term_signal==0 && adc_status!=SPC_READY && elapsed_time<=(effective_settings->timeout + post_gate_maxtime + 0.5) ) { + timespec sleeptime; + sleeptime.tv_nsec=10*1000*1000; // 10 ms + sleeptime.tv_sec=0; + nanosleep(&sleeptime,NULL); + SpcGetParam(deviceno, SPC_STATUS, &adc_status); + elapsed_time=adc_timer.elapsed(); +#if SPC_DEBUG + fprintf(stderr, "(nofifo) loop %i (adc_status, t, t_out, post_gate, core): %i %g %g %g %i\n",i,adc_status, elapsed_time, effective_settings->timeout, post_gate_maxtime, core::term_signal); +#endif + i++; + } +#if SPC_DEBUG + fprintf(stderr, "(nofifo) loop stopped (adc_status, t, t_out, core): %i %g %g %i\n",adc_status, elapsed_time, effective_settings->timeout, core::term_signal); +#endif + /* card ready to transfer samples */ + if (adc_status!=SPC_READY) { + free(adc_data); +#if SPC_DEBUG + fprintf(stderr, "ERROR: adc_status not ready (%i) \n",adc_status ); +#endif + throw SpectrumMI40xxSeries_error("timeout occured while collecting data"); + } + SpcSetParam(deviceno, SPC_COMMAND, SPC_STOP); + if (core::term_signal!=0) { +#if SPC_DEBUG + fprintf(stderr, "received core::term_signal %i\n",core::term_signal); +#endif + free(adc_data); + return NULL; + } + +# if defined __linux__ + size_t data_length=SpcGetData (deviceno, 0, 0, nchannels * sampleno, 2, (dataptr) adc_data); +# if SPC_DEBUG + fprintf(stderr, "SpcGetData returned %" SIZETPRINTFLETTER " bytes, got %" SIZETPRINTFLETTER " out of %" SIZETPRINTFLETTER " expected samples\n", data_length, data_length/sizeof(short int)/2, sampleno); +# endif + if (nchannels*sizeof(short int)*sampleno!=data_length) { + free(adc_data); + throw SpectrumMI40xxSeries_error("wrong amount of data from adc card"); + } + // current position in adc data array +# elif defined __CYGWIN__ + int16 read_result=SpcGetData (deviceno, 0, 0, nchannels * sampleno,(void*) adc_data); +# if SPC_DEBUG + fprintf(stderr, "SpcGetData returned %d\n", read_result); +# endif + if (read_result!=0) { + free(adc_data); + throw SpectrumMI40xxSeries_error("wrong amount of data from adc card"); + } +# endif + short int* data_position=adc_data; + // produced results + adc_results* the_results=new adc_results(0); + data_position=split_adcdata_recursive(data_position, *(effective_settings->data_structure), *the_results); + if (data_position==0 || (size_t)(data_position-adc_data)!=(sampleno*nchannels)) { + fprintf(stderr,"something went wrong while spliting data\n"); + } + free(adc_data); + delete effective_settings; + effective_settings=NULL; + return the_results; + } + else { +#if SPC_DEBUG + printf("ADC in FIFO mode!\n"); + fprintf(stderr, "fetching %" SIZETPRINTFLETTER " samples in fifo mode (timeout is %g)\n",sampleno,effective_settings->timeout); +#endif + // FIFO method + stopwatch timer; + timer.start(); + do { + int32 lStatus; + timespec sleeptime; + sleeptime.tv_sec=0; + sleeptime.tv_nsec=100*1000*1000; + nanosleep(&sleeptime,NULL); + //pthread_testcancel(); + SpcGetParam (deviceno, SPC_STATUS, &lStatus); +#if SPC_DEBUG + fprintf(stderr, "while loop FIFO (lStatis, timer, effective_timeout): %i %g %g\n",lStatus, timer.elapsed(), effective_settings->timeout ); +#endif + if (lStatus == SPC_READY) break; + } while (timer.elapsed()timeout); //rosi _timeout + SpcSetParam(deviceno, SPC_COMMAND, SPC_STOP); + +#if SPC_DEBUG + fprintf(stderr, "waiting for read thread terminating\n"); +#endif + /* wait for read thread */ + pthread_cancel(timeout_pt); + pthread_join(timeout_pt,NULL); + pthread_attr_destroy(&timeout_pt_attrs); + /* free it's resources */ +#if SPC_DEBUG + fprintf(stderr, "freeing fifo data buffers\n"); +#endif + for (std::vector::iterator i=fifobuffers.begin(); i!=fifobuffers.end(); ++i) free(*i); + fifobuffers.clear(); + + adc_results* the_results=NULL; + if (fifo_adc_data!=NULL) { + // split data to (small) pieces + short int* data_position=fifo_adc_data; + the_results=new adc_results(0); + data_position=split_adcdata_recursive(data_position, *(effective_settings->data_structure), *the_results); + if (data_position==0 || (size_t)(data_position-fifo_adc_data)!=(sampleno*nchannels)) { + fprintf(stderr,"something went wrong while spliting data from fifo mode\n"); + } + free(fifo_adc_data); + fifo_adc_data=NULL; + } + else + fprintf(stderr,"something went wrong while collecting data with fifo mode\n"); + + delete effective_settings; + effective_settings=NULL; + return the_results; + } +} + +/* check whether a channel mask is legal for this board */ +bool SpectrumMI40xxSeries::IsChannelMaskLegal(int mask) { + if ( (cardType == TYP_MI4020 || cardType == TYP_MI4030) && mask != CHANNEL0) { + return false; + } else if ( (cardType == TYP_MI4021 || cardType == TYP_MI4031) && mask != (CHANNEL0 | CHANNEL1) && mask != CHANNEL0) { + return false; + } else if ((cardType == TYP_MI4022 || cardType == TYP_MI4032) && mask != (CHANNEL0 | CHANNEL1 | CHANNEL2 | CHANNEL3) && mask != (CHANNEL0 | CHANNEL1) && mask != (CHANNEL0 | CHANNEL2) && mask != CHANNEL0) { + return false; + } + return true; +} + +SpectrumMI40xxSeries::~SpectrumMI40xxSeries() { + SpcSetParam (deviceno, SPC_COMMAND, SPC_STOP); // stop the board +# if defined __linux__ + close (deviceno); +# elif defined __CYGWIN__ + FreeLibrary(spectrum_driver_dll); +# endif +} + diff --git a/drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h b/drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h new file mode 100644 index 0000000..7567ec5 --- /dev/null +++ b/drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h @@ -0,0 +1,221 @@ +#ifndef SPECTRUM_MI40XXSERIES +#define SPECTRUM_MI40XXSERIES + +#include +#include +#include "core/states.h" +#include "core/constants.h" +#include "drivers/ADC.h" +#include "GatedData.h" + +#if defined __linux__ +// ----- linux includes ----- +# include +# include +# include +# include +# include "include/dlltyp.h" +# include "include/regs.h" +# include "include/spcerr.h" +#endif +#if defined __CYGWIN__ +// ------- for cygwin with windows ---------- +# define int16 short int +# define int32 int +# define ptr16 short int* +# include "include/regs.h" +# include "include/spcerr.h" +# include "windows.h" +#endif + +#if !(defined __linux__ || defined __CYGWIN__) +# error "sorry, expecting linux or cygwin" +#endif + +#ifndef SPC_DEBUG +# define SPC_DEBUG 0 +#endif + + +/** + \defgroup spectrummi40xxadc Spectrum MI-40xx ADC + \ingroup drivers + \brief Driver for the Spectrum MI-40xx Series analog-to-digital converter (acquisition) boards + + @{ + */ + +class SpectrumMI40xxSeries_error: public ADC_exception +{ +public: + explicit SpectrumMI40xxSeries_error(const std::string& msg) throw (): ADC_exception(msg) {} + explicit SpectrumMI40xxSeries_error(const char* msg) throw (): ADC_exception(msg) {} + virtual ~SpectrumMI40xxSeries_error() throw () {} +protected: + virtual const std::string prefix() const { return "ERROR (SpectrumMI40xxSeries_error): "; } +}; + + +/** + driver for Spectrum MI40 Series acquisition boards + */ +class SpectrumMI40xxSeries: public ADC { + /** + analogin device number + */ + int deviceno; + + /** + ttlout triggerline device id + */ + int device_id; + /** + ttlout triggerline ttlmask + */ + ttlout trigger_line; + + /** + Type of the card. This is important for the number of allowed channels, etc. + */ + int cardType; + + /** stuff concerning fifo acquisition */ + size_t fifobufferno; + /** stuff concerning fifo acquisition */ + size_t fifobufferlen; + /** stuff concerning fifo acquisition */ + size_t fifo_minimal_size; + /** stuff concerning fifo acquisition */ + std::vector fifobuffers; + /** the extra data thread should store things to that buffer */ + short int* fifo_adc_data; + pthread_attr_t timeout_pt_attrs; + pthread_t timeout_pt; + + +# if defined __linux__ + // ----- include the easy ioctl commands from the driver ----- +# include "include/spcioctl.inc" +# endif + +# if defined __CYGWIN__ + HINSTANCE spectrum_driver_dll; + int16 boardcount; + int16 PCIVersion; +# define define_spectrum_function(name, returntype, ... ) typedef returntype (__attribute__((stdcall)) *name##_type)(__VA_ARGS__); name##_type name + define_spectrum_function(SpcSetParam, int16, int16, int32, int32); + define_spectrum_function(SpcGetParam, int16, int16, int32, int32*); + define_spectrum_function(SpcGetData, int16, int16, int16, int32, int32, void*); + define_spectrum_function(SpcInitPCIBoards, int16, int16*, int16*); +# endif + + class Configuration { + public: + /* sampling frequency in Hz */ + double samplefreq; + /* acquired data description */ + DataManagementNode* data_structure; + /* a timeout for acquiring data in s*/ + double timeout; + /** configured input impedance in Ohm*/ + double* impedance; + /** configured input sensitivity in V*/ + double* sensitivity; + /** coupling 0=DC else AC */ + int coupling; + /** external reference clock **/ + int ext_reference_clock; + + /** offsets for each channel in % of sensitivity **/ + int* offset; + + /** \brief Number of used channels for this run */ + int lSetChannels; + /** \brief Bitmap for currently enabled channels */ + channel_array qwSetChEnableMap; + + /** print data for debug purpose */ + void print(FILE* f); + + + + Configuration() { + samplefreq=0; + timeout=0; + impedance=NULL; + sensitivity=NULL; + offset = NULL; + coupling=0; + ext_reference_clock=0; + data_structure=NULL; + lSetChannels=0; + qwSetChEnableMap=0; + } + + Configuration(const Configuration& orig) { + samplefreq=orig.samplefreq; + timeout=orig.timeout; + impedance=orig.impedance; + sensitivity=orig.sensitivity; + offset=orig.offset; + coupling=orig.coupling; + ext_reference_clock=orig.ext_reference_clock; + qwSetChEnableMap = orig.qwSetChEnableMap; + lSetChannels = orig.lSetChannels; + if (orig.data_structure==NULL) data_structure=NULL; + else data_structure=new DataManagementNode(*orig.data_structure); + } + + ~Configuration() { + if (data_structure!=NULL) delete data_structure; + } + }; + + /** + these settings are copied and modified at need to derive effective settings + */ + Configuration default_settings; + + /** + only available with started measurement + */ + Configuration* effective_settings; + + short int* split_adcdata_recursive(short int* data, const DataManagementNode& structure, adc_results& result_splitted); + + void collect_config_recursive(state_sequent& exp, SpectrumMI40xxSeries::Configuration& settings); + + bool IsChannelMaskLegal(int mask); + +public: + SpectrumMI40xxSeries(const ttlout& t_line, float impedance=1e6, int ext_reference_clock=(int)100e6); + + virtual void sample_after_external_trigger(double rate, size_t samples, double sensitivity=5.0, size_t resolution=14); + + virtual void set_daq(state & exp); + + + /** + for syncronization purposes with sample clock + */ + double get_sample_clock_frequency() const; + + /** + get results from transient recorder + timeout: 0.0 return immediately + */ + virtual result* get_samples(double timeout=0.0); + + /** + timeout issues during fifo acquisition + */ + int TimeoutThread(); + + virtual ~SpectrumMI40xxSeries(); +}; + +/** + @} +*/ + +#endif diff --git a/drivers/Spectrum-MI40xxSeries/drv_header_v323b1742.zip b/drivers/Spectrum-MI40xxSeries/drv_header_v323b1742.zip new file mode 100755 index 0000000..bdfd879 Binary files /dev/null and b/drivers/Spectrum-MI40xxSeries/drv_header_v323b1742.zip differ diff --git a/drivers/Spectrum-MI40xxSeries/drv_header_v402b6844.zip b/drivers/Spectrum-MI40xxSeries/drv_header_v402b6844.zip new file mode 100644 index 0000000..d9f9cb2 Binary files /dev/null and b/drivers/Spectrum-MI40xxSeries/drv_header_v402b6844.zip differ diff --git a/drivers/Spectrum-MI40xxSeries/ftbfs_patch.diff b/drivers/Spectrum-MI40xxSeries/ftbfs_patch.diff new file mode 100644 index 0000000..c67f4ee --- /dev/null +++ b/drivers/Spectrum-MI40xxSeries/ftbfs_patch.diff @@ -0,0 +1,26 @@ +--- include/dlltyp.h 2012-08-06 14:02:18.000000000 +0200 ++++ ../../../dlltyp.h 2012-11-13 10:10:23.000000000 +0100 +@@ -262,14 +262,15 @@ + + // ----- Linux ----- + #ifdef _LINUX +-# define int8 char +-# define int16 short int +-# define int32 int +-# define int64 long long +-# define uint8 unsigned char +-# define uint16 unsigned short int +-# define uint32 unsigned int +-# define uint64 unsigned long long ++#include ++typedef int8_t int8; ++typedef int16_t int16; ++typedef int32_t int32; ++typedef int64_t int64; ++typedef uint8_t uint8; ++typedef uint16_t uint16; ++typedef uint32_t uint32; ++typedef uint64_t uint64; + # define dataptr void * + # define ptr8 int8* + # define ptr16 int16* diff --git a/drivers/Spectrum-MI40xxSeries/hw_test_extclock.cpp b/drivers/Spectrum-MI40xxSeries/hw_test_extclock.cpp new file mode 100644 index 0000000..558b8a0 --- /dev/null +++ b/drivers/Spectrum-MI40xxSeries/hw_test_extclock.cpp @@ -0,0 +1,135 @@ +/* + * simple test program simplified from + * mi40xx.cpp example program (c) Spectrum GmbH + * +*/ + +#include +#include +#include +#include +#include + +#include "dlltyp.h" +#include "regs.h" +#include "spcerr.h" +#include "spcioctl.inc" + + +// ----- main task ----- +int main(int argc, char **argv) +{ + int hDrv; + //int16 nCount, nPCIBusVersion + int16 nChannels; + int32 lChEnable, lStatus, lMemsize; + int32 lErrorCode, lErrorReg, lErrorValue; + ptr16 pnData[4]; + ptr16 pnTmp; + int32 i, k; + int16 j, nErr; + + + hDrv = open ("/dev/spc0", O_RDWR); + if (hDrv <= 0) + { + printf ("device not found\n"); + return -1; + } + + + nChannels = 2; + lChEnable = CHANNEL0 | CHANNEL1; + + // ----- set memsize for recording ---- + lMemsize = 2*1024; + + // ----- setup board for recording ----- + for (j=0; j +#include +#include +#include +#include + +#include "dlltyp.h" +#include "regs.h" +#include "spcerr.h" +#include "spcioctl.inc" + + +// ----- main task ----- +int main(int argc, char **argv) +{ + int hDrv; + //int16 nCount, nPCIBusVersion + int16 nChannels; + int32 lChEnable, lStatus, lMemsize; + int32 lErrorCode, lErrorReg, lErrorValue; + ptr16 pnData[4]; + ptr16 pnTmp; + int32 i, k; + int16 j, nErr; + + + hDrv = open ("/dev/spc0", O_RDWR); + if (hDrv <= 0) + { + printf ("device not found\n"); + return -1; + } + + + nChannels = 2; + lChEnable = CHANNEL0 | CHANNEL1; + + // ----- set memsize for recording ---- + lMemsize = 2*1024; + + // ----- setup board for recording ----- + for (j=0; j/dev/null; pwd) +LIBS=-lm -lxerces-c -lexpat +CXXFLAGS=-g -O0 -Wall -Wshadow -pedantic +CXXCPPFLAGS=-I../.. + +all: PulseBlasterProgram.o SpinCore-PulseBlaster.o + +pulseblaster_test: pulseblaster_test.cpp SpinCore-PulseBlaster.o + $(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -o$@ $^ $(LIBS) + +TestCase: TestCase.cpp SpinCore-PulseBlaster.o ../../core/core.a + $(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -I. -o$@ $^ $(LIBS) + +PulseBlasterProgram.o: PulseBlasterProgram.cpp PulseBlasterProgram.h + $(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -c -o$@ $< + +SpinCore-PulseBlaster.o: SpinCore-PulseBlaster.cpp SpinCore-PulseBlaster.h + $(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -c -o$@ $< + +../../core/core.a: + $(MAKE) -C ../../core core.a + +pulseblaster.ko: + $(MAKE) -C $(KERNELSRC) SUBDIRS=$(THISDIR) + +clean: + rm -f *.o *~ pulseblaster_test +ifeq ($(shell uname -o), GNU/Linux) + $(MAKE) -C $(KERNELSRC) SUBDIRS=$(THISDIR) clean +endif + +endif diff --git a/drivers/SpinCore-PulseBlaster/PulseBlasterProgram.cpp b/drivers/SpinCore-PulseBlaster/PulseBlasterProgram.cpp new file mode 100644 index 0000000..c3814fc --- /dev/null +++ b/drivers/SpinCore-PulseBlaster/PulseBlasterProgram.cpp @@ -0,0 +1,322 @@ +#include "SpinCore-PulseBlaster.h" +#include "PulseBlasterProgram.h" +#include "core/xml_states.h" +#include + +/* *************************************************************************************** + + PulseBlasterCommand + +**************************************************************************************** */ + +PulseBlasterCommand::PulseBlasterCommand() { + ttls=0; + instruction=SpinCorePulseBlaster::CONTINUE; + length=0; + loop_count=0; + program=NULL; +} + +PulseBlasterCommand::PulseBlasterCommand(const PulseBlasterCommand& orig) { + instruction=orig.instruction; + length=orig.length; + ttls=orig.ttls; + program=orig.program; + switch(instruction) { + case SpinCorePulseBlaster::LOOP: + case SpinCorePulseBlaster::LONG_DELAY: + loop_count=orig.loop_count; + jump=program->end(); + break; + case SpinCorePulseBlaster::BRANCH: + case SpinCorePulseBlaster::END_LOOP: + case SpinCorePulseBlaster::JSR: + jump=orig.jump; + loop_count=0; + break; + default: + loop_count=0; + jump=program->end(); + } +} + +PulseBlasterCommand::PulseBlasterCommand(PulseBlasterProgram& p) { + ttls=0; + instruction=SpinCorePulseBlaster::CONTINUE; + length=p.minimum_interval; + program=&p; + loop_count=0; + jump=p.end(); +} + +PulseBlasterCommand::PulseBlasterCommand(PulseBlasterProgram& p, int _ttls, double _length) { + ttls=_ttls; + loop_count=0; + instruction=SpinCorePulseBlaster::CONTINUE; + program=&p; + jump=p.end(); + length=(size_t)floor(_length*p.internal_clock_freq+0.5); // graceful rounding +} + +/* *************************************************************************************** + + PulseBlasterProgram + +**************************************************************************************** */ + + +PulseBlasterProgram::PulseBlasterProgram(const PulseBlasterProgram& orig): std::list() { + // do not copy the list. This must be done by the derived class... + internal_clock_freq=orig.internal_clock_freq; + minimum_interval=orig.minimum_interval; +} + +int PulseBlasterProgram::insert_ref(PulseBlasterProgram::iterator target_pos, + PulseBlasterProgram::const_iterator start_copy, + PulseBlasterProgram::const_iterator end_copy) { + insert(target_pos,start_copy,end_copy); + iterator i=target_pos; + for(const_iterator orig_i=end_copy;orig_i!=start_copy;--orig_i) { + if ((**i).instruction==SpinCorePulseBlaster::END_LOOP || (**i).instruction==SpinCorePulseBlaster::BRANCH || (**i).instruction==SpinCorePulseBlaster::JSR) { + (**i).jump=i; + advance(i,distance(start_copy,(**orig_i).jump)-distance(start_copy,orig_i)); + } + --i; + } + return 0; +} + +PulseBlasterProgram::~PulseBlasterProgram() { + while (!empty()) { + if (back()!=NULL) delete back(); + pop_back(); + } +} + +void PulseBlasterProgram::append_sequence(const state& the_states) { + std::list the_stack; + + // recieve a work copy with partially enrolled loops and flat structures + state* flat=the_states.copy_flat(0); + + + push_back(create_command()); + iterator dummy_pos=--end(); + state_atom* this_tag=flat; + + while (1) { + // operate on a sequence + if (typeid(*this_tag)==typeid(state_sequent)) { + //fprintf(stderr,"found sequence\n"); + state_sequent* sequence=dynamic_cast(this_tag); + // throw away all elements, which were non-states + for(state_sequent::iterator i=sequence->begin(); sequence->end()!=i;) { + if (dynamic_cast(*i)==0) { + // states are expected, nothing else! + fprintf(stderr, "only states are allowed inside a sequent section!\n"); + delete *i; + i=sequence->erase(i); + } + else + // fine + ++i; + } + // skip if empty + if (!sequence->empty() && sequence->repeat>0) { + if (sequence->repeat>1<<20) { + // nested loops as workaround + size_t new_count1=1<<20; + size_t new_count2=sequence->repeat/new_count1; + size_t new_count3=sequence->repeat-new_count1*new_count2; + // if (new_count2==1) {} + // if (new_count3==0) {} + state_sequent* new_sequence1=new state_sequent(); + new_sequence1->repeat=new_count1; + state_sequent* new_sequence2=new state_sequent(*sequence); + new_sequence2->repeat=new_count2; + new_sequence1->push_back(new_sequence2); + state_sequent* new_sequence3=new state_sequent(*sequence); + new_sequence3->repeat=new_count3; + while (!sequence->empty()) { + delete sequence->front(); + sequence->pop_front(); + } + sequence->repeat=1; + sequence->push_back(new_sequence1); + sequence->push_back(new_sequence3); + } + // if necessary enroll loop at begin + // prepare sequence + if (sequence->repeat>1) { + while (1) { + state* first_state=NULL; + state_sequent::iterator i=sequence->begin(); + while( i!=sequence->end()) { + first_state=dynamic_cast(*i); + if (NULL!=first_state) break; + ++i; + } + if (first_state!=NULL) { + state_sequent* first_sequence=dynamic_cast(first_state); + if (first_sequence!=NULL) { + // fprintf(stderr,"enroll begin\n"); + size_t repeat=first_sequence->repeat; + if (repeat==0) { + delete *i; + sequence->erase(i); + } + else if (repeat==1) { + sequence->insert(i,first_sequence->begin(),first_sequence->end()); + first_sequence->clear(); + delete *i; + sequence->erase(i); + } + else { + for (state_sequent::iterator j=first_sequence->begin();j!=first_sequence->end();++j) + sequence->insert(i,(*j)->copy_new()); + first_sequence->repeat=repeat-1; + } + // check again... + continue; + } + } + // nothing to be done + break; + } + // if necessary enroll loop at end + while (1) { + state* last_state=NULL; + state_sequent::iterator i=sequence->end(); + while(i!=sequence->begin()) { + --i; + last_state=dynamic_cast(*i); + if (last_state!=NULL) break; + } + if (last_state!=NULL) { + state_sequent* last_sequence=dynamic_cast(last_state); + if (last_sequence!=NULL) { + if (last_sequence->repeat==0) { + // forget this sequence + delete *i; + sequence->erase(i); + } + else if (last_sequence->repeat==1) { + // forget the sequence around it + sequence->insert(i,last_sequence->begin(),last_sequence->end()); + last_sequence->clear(); + delete *i; + sequence->erase(i); + } + else { + // fprintf(stderr,"enroll end\n"); + last_sequence->repeat-=1; + i++; + for (state_sequent::iterator j=last_sequence->begin();j!=last_sequence->end();++j) + sequence->insert(i,(*j)->copy_new()); + } + // check again + continue; + } + break; + } + } + } + // step down + // push to stack + stackentry new_entry={sequence, sequence->begin(), --end()}; +#if 0 + if (new_entry.command==the_program->end()) + fprintf(stderr,"saved an end iterator\n"); + else + fprintf(stderr,"saved pos no %d\n",distance(begin(),new_entry.command)); + for (PulseBlasterProgram::iterator i=++begin();i!=end();i++) + i->write_to_file(stderr); +#endif + the_stack.push_back(new_entry); + this_tag=*(the_stack.back().subsequence_position); + continue; + } + } + // operate on a single state + else if (typeid(*this_tag)==typeid(state)) { + //fprintf(stderr,"found state\n"); + state* this_state=dynamic_cast(this_tag); + if (this_state->length*internal_clock_freq>pow(2,32)-1) { + // add a loop to state, to gain longer durations + // numerical stability? + // todo: avaoid unnecessary loop or state + const double loop_state_length=42.949; + size_t number_loops=(size_t)floor(this_state->length/loop_state_length); + double remaining_time=this_state->length-loop_state_length*number_loops; + if (remaining_time*internal_clock_freqrepeat=number_loops; + this_state->length=loop_state_length; + new_loop->push_back(this_state->copy_new()); + this_state->length=remaining_time; + if (the_stack.empty()) { + // in addition to this make a sequence around it + state_sequent* sequence_envelope=new state_sequent; + sequence_envelope->push_back(new_loop); + sequence_envelope->push_back(this_state); + this_state=sequence_envelope; + } + else { + // insert the loop after this state + state_sequent::iterator loop_pos=(state_sequent::iterator)the_stack.back().subsequence_position; + the_stack.back().subsequence->insert(++loop_pos,new_loop); + } + continue; + } + else { + // simply append the state + PulseBlasterCommand* c=create_command(*this_state); + if (c==NULL) throw pulse_exception("failed state creation"); + push_back(c); + } + } + else { + fprintf(stderr,"droped something"); + // drop it... + } + + // find the next state... + while (!the_stack.empty()) { + stackentry& last_entry=the_stack.back(); + ++last_entry.subsequence_position; + if (last_entry.subsequence_position!=last_entry.subsequence->end()) { + this_tag=*(the_stack.back().subsequence_position); + break; + } + // finish loop... + if (last_entry.subsequence->repeat>1) { + ++last_entry.command; + if (last_entry.command==--end()) { + // special case long delay + back()->instruction=SpinCorePulseBlaster::LONG_DELAY; + back()->loop_count=last_entry.subsequence->repeat; + } + else { + // loop with several states + (**(last_entry.command)).instruction=SpinCorePulseBlaster::LOOP; + (**(last_entry.command)).loop_count=last_entry.subsequence->repeat; + back()->instruction=SpinCorePulseBlaster::END_LOOP; + back()->jump=last_entry.command; + } + } + // jump to next level + the_stack.pop_back(); + } + + if (the_stack.empty()) break; + } + + delete flat; + // is that ok in all cases? + delete *dummy_pos; + erase(dummy_pos); +} diff --git a/drivers/SpinCore-PulseBlaster/PulseBlasterProgram.h b/drivers/SpinCore-PulseBlaster/PulseBlasterProgram.h new file mode 100644 index 0000000..faa09d7 --- /dev/null +++ b/drivers/SpinCore-PulseBlaster/PulseBlasterProgram.h @@ -0,0 +1,88 @@ +#include +#include "core/states.h" +#include "core/stopwatch.h" +#include "drivers/pulsegen.h" + +class PulseBlasterCommand; + +/** + \brief holds the complete and detailed initialisation of the device +*/ +class PulseBlasterProgram: public std::list { +public: + double internal_clock_freq; + size_t minimum_interval; + + /** + supports recursive traverse of states, in order insert workarounds and assemble PulseBlasterProgram + */ + struct stackentry { + state_sequent* subsequence; + state_sequent::iterator subsequence_position; + PulseBlasterProgram::iterator command; + }; + + + PulseBlasterProgram(size_t mi=9, double cf=1e8) { + internal_clock_freq=cf; + minimum_interval=mi; + } + + /// do not copy the list. This must be done by the derived class... + PulseBlasterProgram(const PulseBlasterProgram& orig); + + /// insert some commands and get references right + int insert_ref(PulseBlasterProgram::iterator target_pos, + PulseBlasterProgram::const_iterator start_copy, + PulseBlasterProgram::const_iterator end_copy); + + /// appends a sequence of states to existing program + void append_sequence(const state& seq); + + /// write all configuration to a file to xml + virtual int write_to_file(FILE* out, size_t indent=0) const=0; + + /// create a suitable state for this kind of program + virtual PulseBlasterCommand* create_command(const state& the_state)=0; + + /// create a copy or a minimal empty state + virtual PulseBlasterCommand* create_command(const PulseBlasterCommand* orig=NULL)=0; + + virtual ~PulseBlasterProgram(); +}; + +/** + \brief parameters for each pulseblaster command +*/ +class PulseBlasterCommand { +public: + int ttls; + /// which instruction is used... + SpinCorePulseBlaster::opcode instruction; + /// data of instruction for loop counts + int loop_count; + /// length in units of clock cycle + size_t length; + + /// the pulse program start iterator + const PulseBlasterProgram* program; + + /// data of instruction for jumps + PulseBlasterProgram::const_iterator jump; + + PulseBlasterCommand(); + + /// one for minimal length + PulseBlasterCommand(PulseBlasterProgram& p); + + // a useful constructor for a simple CONTINUE state + PulseBlasterCommand(PulseBlasterProgram& p, int _ttls, double _length); + + /// copy constructor + PulseBlasterCommand(const PulseBlasterCommand& orig); + + /// write the command to a give file as xml tag + virtual int write_to_file(FILE* out, size_t indent=0) const=0; + + virtual ~PulseBlasterCommand() {} +}; diff --git a/drivers/SpinCore-PulseBlaster/SpinCore-PulseBlaster.cpp b/drivers/SpinCore-PulseBlaster/SpinCore-PulseBlaster.cpp new file mode 100644 index 0000000..8d3eb4b --- /dev/null +++ b/drivers/SpinCore-PulseBlaster/SpinCore-PulseBlaster.cpp @@ -0,0 +1,265 @@ +#include "SpinCore-PulseBlaster.h" +#include "PulseBlasterProgram.h" +#include "core/core.h" + +#ifndef SP_DEBUG +# define SP_DEBUG 0 +#endif + +#ifdef __linux__ +# include +# include +# include + +SpinCorePulseBlasterLowlevel::SpinCorePulseBlasterLowlevel() { + device_file_descriptor=open("/dev/" PULSEBLASTER_DEVICE_FILE_NAME,O_NONBLOCK|O_RDWR); + if (device_file_descriptor<0) + throw SpinCorePulseBlaster_error("could not open the device /dev/" PULSEBLASTER_DEVICE_FILE_NAME "\n"); +} + +SpinCorePulseBlasterLowlevel::~SpinCorePulseBlasterLowlevel() { + close(device_file_descriptor); +} + +int SpinCorePulseBlasterLowlevel::write_data(const unsigned char* data, size_t size) { + size_t orig_size=size; + const unsigned int max_chunk_size=100*1<<10; // 100k commands +#if SP_DEBUG + stopwatch write_data_time; + write_data_time.start(); +#endif + while (size>0) { + int result=write(device_file_descriptor, data, ((size>max_chunk_size)?max_chunk_size:size) ); + // error handling + if (result==-1) throw SpinCorePulseBlaster_error(std::string("write_data: error \"")+strerror(errno)+"\""); + if (result<0) { + char errorno[256]; + snprintf(errorno, 256, "%d",result); + throw SpinCorePulseBlaster_error(std::string("write_register: ioctl returned negative value = ")+errorno); + } + // if (result==0) do some retry magic.... + // success! + data+=result; + size-=result; + } +#if SP_DEBUG + fprintf(stderr, "wrote %d bytes in %f s\n", orig_size, write_data_time.elapsed()); +#endif + return orig_size; +} + +#endif + +#ifdef __CYGWIN__ + +SpinCorePulseBlasterLowlevel::SpinCorePulseBlasterLowlevel() { + + const char spincore_dll[]="spinapi"; + const char sp_outp_func_name[]="pb_outp"; + const char sp_inp_func_name[]="pb_inp"; + const char sp_Close_func_name[]="pb_close"; + const char sp_Init_func_name[]="pb_init"; + + PBP_DLL = LoadLibrary(spincore_dll); + if (PBP_DLL==NULL) { + throw SpinCorePulseBlaster_error(std::string("could not open ")+spincore_dll+" library\n"); + } + sp_outp = (__attribute__((stdcall))int(*)(unsigned short, int))GetProcAddress(PBP_DLL,sp_outp_func_name); + if (sp_outp==NULL) { + FreeLibrary(PBP_DLL); + PBP_DLL=NULL; + throw SpinCorePulseBlaster_error("could not access Pulseblaster PCI communication function sp_outp"); + } + sp_inp = (__attribute__((stdcall))int(*)(unsigned short))GetProcAddress(PBP_DLL, sp_inp_func_name); + if (sp_inp==NULL) { + FreeLibrary(PBP_DLL); + PBP_DLL=NULL; + throw SpinCorePulseBlaster_error("could not access Pulseblaster PCI communication function sp_inp"); + } + sp_Init = (__attribute__((stdcall))int(*)())GetProcAddress(PBP_DLL,sp_Init_func_name); + if (sp_Init==NULL) { + FreeLibrary(PBP_DLL); + PBP_DLL=NULL; + throw SpinCorePulseBlaster_error("could not access Pulseblaster Init function"); + } + sp_Close = (__attribute__((stdcall))int(*)())GetProcAddress(PBP_DLL, sp_Close_func_name); + if (sp_Close==NULL) { + FreeLibrary(PBP_DLL); + PBP_DLL=NULL; + throw SpinCorePulseBlaster_error("could not access Pulseblaster Close function"); + } +#if SP_DEBUG + __attribute__((stdcall))void (*sp_set_debug)(int flag); + sp_set_debug=(__attribute__((stdcall))void(*)(int))GetProcAddress(PBP_DLL,"pb_set_debug"); + if (sp_set_debug!=NULL) sp_set_debug(1); + else fprintf(stderr, "could not load debug function from %s DLL\n",spincore_dll); +#endif + int result=sp_Init(); + if (result!=0) { + fprintf(stderr, "sp_Init returned %d\n", result); + FreeLibrary(PBP_DLL); + PBP_DLL=NULL; + throw SpinCorePulseBlaster_error("could not initialise Pulseblaster card"); + } +} + +SpinCorePulseBlasterLowlevel::~SpinCorePulseBlasterLowlevel() { + if (sp_Close!=NULL) sp_Close(); + sp_inp=NULL; + sp_outp=NULL; + sp_Close=NULL; + sp_Init=NULL; + if (PBP_DLL!=NULL) FreeLibrary(PBP_DLL); +} + +#endif + +void SpinCorePulseBlaster::reset_flags(unsigned int flags) { + unsigned char data[40]; + data[0]=(flags&0xff000000)>>24; + data[1]=(flags&0xff0000)>>16; + data[2]=(flags&0xff00)>>8; + data[3]=flags&0xff; + write_register(0,0); // dev reset + write_register(2,4); // bytes per word + write_register(3,0xFF); // dev to program + write_register(4,0); //reset address counter + write_data(data,4); + write_register(5,0); //strobe clock + write_register(5,0); //strobe clock +} + + +void SpinCorePulseBlaster::set_program(const std::string& data) { + if (command_length==0) + throw SpinCorePulseBlaster_error("command length not set"); + if (data.size()%command_length!=0) + throw SpinCorePulseBlaster_error("program data length does not match command length"); + if (data.size()/command_length>max_commands) { + throw SpinCorePulseBlaster_error("program length exceeds maximum command number"); + } + write_register(0,0); // dev reset + write_register(2,command_length); // bytes per word + write_register(3,0); // dev to program + write_register(4,0); //reset address counter + write_data(data); +} + +void SpinCorePulseBlaster::run_pulse_program_w_sync(state& exp, double sync_freq) { + // set duration + state_sequent* seq=dynamic_cast(&exp); + if (seq==NULL) + throw pulse_exception("pulse program should be a sequence"); + state_iterator i(*seq); + while (!i.is_last()) i.next_state(); + duration=i.get_time(); +#if SP_DEBUG + fprintf(stderr, "caluclated time of pulse program is %g\n",duration); +#endif + PulseBlasterProgram* prog=create_program(exp); + if (prog==NULL) + throw pulse_exception("could not create PulseBlasterProgram"); + if (sync_freq>0 && sync_mask!=0) { + // synchronization with help of board P136 + // P136 derives a single trigger slope from sampling clock of Spectrum MI4021 + // using push_front(): we have to add the two commands in reverse order + PulseBlasterCommand* c; + + // second command: clear sync mask + c=prog->create_command(); + c->ttls=0x0; + c->instruction=SpinCorePulseBlaster::CONTINUE; + c->length=shortest_pulse; + prog->push_front(c); + + // first command: wait for monoflop on P136 up again + c=prog->create_command(); + c->ttls=sync_mask; + c->instruction=SpinCorePulseBlaster::WAIT; + c->length=shortest_pulse; + prog->push_front(c); + + if (0) { + // zeroth command: set sync mask in advance, sometimes necessary + c=prog->create_command(); + c->ttls=sync_mask; + c->instruction=SpinCorePulseBlaster::CONTINUE; + c->length=shortest_pulse+2; + prog->push_front(c); + } + duration+=2.0*shortest_pulse/clock+1.0/sync_freq; + } + // workaround for another PulseBlaster Bug: + // extra CONTINUE opcodes with little more duration to force proper initialization of all internal counters + while ((prog->size()>1 && (*(++(prog->begin())))->instruction!=CONTINUE) || + (prog->size()>0 && (*(prog->begin()))->instruction!=CONTINUE)) { + PulseBlasterCommand* c; + c=prog->create_command(); + c->instruction=SpinCorePulseBlaster::CONTINUE; + c->length=shortest_pulse+2; + prog->push_front(c); + duration+=(1.0+shortest_pulse)/clock; + } + // end: clear flags and stop pulseblaster + prog->push_back(prog->create_command()); + prog->push_back(prog->create_command()); + prog->back()->instruction=STOP; +#if SP_DEBUG + prog->write_to_file(stderr); +#endif + run_pulse_program(*prog); + time_running.start(); + duration+=3.0*shortest_pulse/clock; + delete prog; +} + + +void SpinCorePulseBlaster::wait_till_end() { + + double waittime=duration-time_running.elapsed(); + double timeout=(waittime>10)?(waittime*0.01):0.1; +#if SP_DEBUG + fprintf(stderr,"waiting while pulseprogram running (%f)...",waittime); +#endif + // Bit zero is stopped; bit one is reset; bit two is running; bit three is waiting. + int status=get_status(); +#if SP_DEBUG + fprintf(stderr,"status=0x%04x ",status); +#endif + // with synchronization, also waiting status can occur + while (waittime>-timeout && core::term_signal==0 && (status&(RUNNING|WAITING))!=0) { + if (waittime<1e-2) + waittime=1e-2; + else + waittime*=0.9; +#if SP_DEBUG + fprintf(stderr,"sleeping for %g seconds...",waittime); + fflush(stderr); +#endif + timespec nanosleep_time; + nanosleep_time.tv_sec=(time_t)floor(waittime); + nanosleep_time.tv_nsec=(long)ceil((waittime-nanosleep_time.tv_sec)*1e9); + nanosleep(&nanosleep_time,NULL); + waittime=duration-time_running.elapsed(); + status=get_status(); +#if SP_DEBUG + fprintf(stderr,"status: 0x%04x\n",status); + fflush(stderr); +#endif + } + if (core::term_signal!=0) { + //reset pulseblaster + stop(); + reset_flags(0); + } + if (waittime<=-timeout) { + fprintf(stderr, "Pulseblaster: status=0x%04x, ran into timeout after %f s\nPulseblaster: aborting...", status, time_running.elapsed()); + stop(); + reset_flags(0); + status=get_status(); + fprintf(stderr,"now: status=0x%04x\n", status); + } +#if SP_DEBUG + fprintf(stderr,"done\n"); +#endif +} diff --git a/drivers/SpinCore-PulseBlaster/SpinCore-PulseBlaster.h b/drivers/SpinCore-PulseBlaster/SpinCore-PulseBlaster.h new file mode 100644 index 0000000..d117494 --- /dev/null +++ b/drivers/SpinCore-PulseBlaster/SpinCore-PulseBlaster.h @@ -0,0 +1,273 @@ +#ifndef SPINCORE_PULSEBLASTER_H +#define SPINCORE_PULSEBLASTER_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef SP_DEBUG +# define SP_DEBUG 1 +#endif + +class SpinCorePulseBlaster_error: public pulse_exception +{ +public: + explicit SpinCorePulseBlaster_error(const std::string& msg) throw (): pulse_exception(msg) {} + explicit SpinCorePulseBlaster_error(const char* msg) throw (): pulse_exception(msg) {} + virtual ~SpinCorePulseBlaster_error() throw () {} +protected: + virtual const std::string prefix() const { return "ERROR (SpinCorePulseBlaster_error): "; } +}; + +#if !( defined(__linux__)||defined(__CYGWIN__)) +# error "Sorry, we are not prepared for this target!" +#endif + +#ifdef __linux__ +# include +# include +# include +# include +# include +# include "pulseblaster.h" +#endif + +#ifdef __CYGWIN__ +# include +#endif + +class SpinCorePulseBlasterLowlevel { +protected: +# ifdef __linux__ + /** + device file handle + */ + int device_file_descriptor; +# endif + +# ifdef __CYGWIN__ + /** + the loaded dll + */ + HINSTANCE PBP_DLL; + /** + the pulseblaster communication function sp_outp + \param address address register in board's internal memory + \param value value to store + */ + __attribute__((stdcall)) int (*sp_outp)(unsigned short address, int value); + /** + the pulseblaster communication function sp_inp + \param address addres register in board's internal memory + */ + __attribute__((stdcall)) int (*sp_inp)(unsigned short address); + /** + initialisation of the Pulseblaster board + */ + __attribute__((stdcall)) int (*sp_Init)(); + /** + close access to Pulseblaster board + */ + __attribute__((stdcall)) int (*sp_Close)(); +#endif + + SpinCorePulseBlasterLowlevel(); + +#ifdef __linux__ + inline int write_register(int reg, int data) { + int result=ioctl(device_file_descriptor,IOCTL_OUTB,(reg&0xff)<<8|(data&0xff)); + if (result==-1) throw SpinCorePulseBlaster_error(std::string("write_register: ioctl error \"")+strerror(errno)+"\""); + if (result!=0) { + char errorno[256]; + snprintf(errorno, 256, "%d",result); + throw SpinCorePulseBlaster_error(std::string("write_register: ioctl returned nonzero value = ")+errorno); + } + return result; + } + + /** + writes data to pulseblaster device + + care about buffer length and write in chunks to avoid freeze of user space + */ + int write_data(const unsigned char* data, size_t size); + + inline int read_register(int reg) { + int result=ioctl(device_file_descriptor,IOCTL_INB, reg&0xff); + if (result==-1) throw SpinCorePulseBlaster_error(std::string("write_register: ioctl error \"")+strerror(errno)+"\""); + if (result<0) throw SpinCorePulseBlaster_error("read_register: ioctl returned nonzero value"); + return result; + } + +#endif + +#ifdef __CYGWIN__ + inline int write_register(unsigned int reg, unsigned int data) { + int result=sp_outp(reg,data); +#if SP_DEBUG + fprintf(stderr, "sp_outp(0x%02x,0x%02x)\n",reg,data); +#endif + if (result!=0) throw SpinCorePulseBlaster_error("write_register returned nonzero value"); + return result; + } + + inline int write_data(const unsigned char* data, size_t size) { + int result=0; + size_t i; + for (i=0; i +#include + +class PB_test: SpinCorePulseBlasterLowlevel { + +public: + PB_test(): SpinCorePulseBlasterLowlevel() {} + + int testprog(int n=1) { + + write_register(0x00,0x00); + write_register(0x02,0x0a); + write_register(0x03,0x00); + write_register(0x04,0x00); + // do nothing in first place (no loops in first place!!!!) + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x06); + // pulse 0.6 us + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x07); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x39); + // wait 7.7 us + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,n&0xff); + write_register(0x06,0xff); // change 0xff to 0xfe + // trigger + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x07); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x50); + write_register(0x06,0x0c); + // clear flags + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x06); + // stop + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x01); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x06); + // end programming + write_register(0x07,0x00); + // run + write_register(0x01,0x00); + + sleep(1); + + return 0; + } + + int testinterval(unsigned long int tics) { + write_register(0x00,0x00); + write_register(0x02,0x0a); + write_register(0x03,0x00); + write_register(0x04,0x00); + // do nothing in first place (no loops in first place!!!!) + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x06); + // pulse 0.1 us + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x07); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0xfe); + // wait tics time units + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + // delay (32 bit) + write_register(0x06,(tics>>24)&0xff); + write_register(0x06,(tics>>16)&0xff); + write_register(0x06,(tics>>8)&0xff); + write_register(0x06,tics&0xff); + // trigger + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x07); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0xfe); + // clear flags + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x06); + // stop + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x01); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x00); + write_register(0x06,0x06); + // end programming + write_register(0x07,0x00); + // run + write_register(0x01,0x00); + + sleep(1); + + return 0; + + } + + ~PB_test(){} +}; + + +int main(int argc, char* argv[]) { + int n=9; + if (argc>1 && argv[1]!=0) { + n=strtoul(argv[1],NULL,0); + } + printf("%d\n",n); + return PB_test().testinterval(n); +} diff --git a/drivers/SpinCore-PulseBlaster/pulseblaster.c b/drivers/SpinCore-PulseBlaster/pulseblaster.c new file mode 100644 index 0000000..969ff31 --- /dev/null +++ b/drivers/SpinCore-PulseBlaster/pulseblaster.c @@ -0,0 +1,945 @@ +/* + * pulseblaster.c - Communication with pulseblaster + * author: Achim Gaedke + * created: February 2005 + * the amcc_outb routine comes from SpinCore + * initially the module is built from example 4 of "The Linux Kernel Module Programming Guide" + * the version of June 2008 is written with the help of "Linux device drivers" + * Jonathan Corbet, Alessandro Rubini, and Greg Kroah-Hartman, O' Reilly, 3rd Edition + */ + +#include /* We're doing kernel work */ +#include /* Specifically, a module */ +#include +#include +#include +#include +#include +#include +#include +#include /* for get_user and put_user */ +#include + +#include "pulseblaster.h" +#define SUCCESS 0 +#define DEVICE_NAME "pulseblaster" + +enum pulseblaster_board { + PBB_DEBUG = 0, + PBB_GENERIC_AMCC, + PBB_GENERIC_PCI, +}; + +/* number of found and allocated devices */ +static int pb_dev_no=0; +/* + * dynamic allocated device number + */ +/* start of allocated minor number(s)*/ +static dev_t pb_dev_no_start; + +struct pulseblaster_device { + /* + * Is the device open right now? Used to prevent + * concurent access into the same device + */ + int device_open; + + /** + * The type of this board + */ + enum pulseblaster_board boardtype; + + /** + * The pci device for this pulseblaster + */ + struct pci_dev *pciboard; + + /* + * the base io address + * 0 means debug mode without hardware accesss, writes everything to kernel log + */ + unsigned long base_addr; + + /* + * protect access to io memory + */ + spinlock_t io_lock; + + /* + * char dev associated with it + */ + struct cdev cdev; +}; + +/* array of char_devices */ +/* todo: make this a pointer array with dynamic allocation... */ +#define pulseblaster_max_devno 4 +static struct pulseblaster_device pb_devs[pulseblaster_max_devno]; +// a lock for that structure +static spinlock_t pb_devs_lock; + +// debug version +static struct pulseblaster_device pb_dev_debug; + +static int base_address=-1; + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Achim Gaedke"); +MODULE_DESCRIPTION("Driver for SpinCore's PulseBlaster"); +module_param(base_address, int, S_IRUGO); +MODULE_PARM_DESC(base_address, "not supported anymore, IO base addresses of device are detected automatically"); + +/*************************************************************************************/ + +/* code taken form spincore example: + * This function writes a byte to a board that uses the amcc chip. + * SP3 and previous revision boards use this interface. Later designs + * can be programmed directly. + */ + +#if 1 +// version without delay (faster) +#define pb_out(bwl) out##bwl +#define pb_in(bwl) in##bwl +#else +// version with extra delay (works on odd hardware, too) +#define pb_out(bwl) out##bwl##_p +#define pb_in(bwl) in##bwl##_p +#endif + + +static int pb_outb_debug(char data, unsigned short address, unsigned long my_base_address) +{ + printk("%s: reg %02x=%02x\n",DEVICE_NAME, 0xff&address, 0xff&data); + return 0; +} + +static int amcc_outb(char data, unsigned short address, unsigned long my_base_address) { + + unsigned int MAX_RECV_TIMEOUT = 10; + unsigned int RECV_START, RECV_TOGGLE, RECV_TIMEOUT = 0; + int XFER_ERROR = 0; + int RECV_POLLING = 0; + + unsigned int OGMB = 0x0C; + unsigned int CHK_RECV = 0x1F; + unsigned int SIG_TRNS = 0x0F; + + unsigned int CLEAR31 = 0x00000001; + unsigned int CLEAR24 = 0x000000FF; + unsigned int CLEAR28 = 0x0000000F; + unsigned int SET_XFER = 0x01000000; + + unsigned int Temp_Address = address; + unsigned int Temp_Data = data; + + if (my_base_address==0) { + return pb_outb_debug(data, address, my_base_address); + } + + // Prepare Address Transfer + Temp_Address &= CLEAR28; + Temp_Address |= SET_XFER; + + // Prepare Data Transfer + Temp_Data &= CLEAR24; + Temp_Data |= SET_XFER; + + // Clear the XFER bit (Should already be cleared) + pb_out(b) (0,my_base_address+SIG_TRNS); + + // Transfer Address + + // Read RECV bit from the Board + RECV_START = pb_in(b) (my_base_address+CHK_RECV); + + RECV_START &= CLEAR31; // Only Save LSB + + + // Send Address to OGMB + pb_out(l) (Temp_Address, my_base_address+OGMB); + + RECV_POLLING = 1; // Set Polling Flag + RECV_TIMEOUT = 0; + while ((RECV_POLLING == 1) && (RECV_TIMEOUT < MAX_RECV_TIMEOUT)) + { + RECV_TOGGLE = pb_in(b)(my_base_address+CHK_RECV); + + RECV_TOGGLE &= CLEAR31; // Only Save LSB + if (RECV_TOGGLE != RECV_START) RECV_POLLING = 0; // Finished if Different + else RECV_TIMEOUT++; + if (RECV_TIMEOUT == MAX_RECV_TIMEOUT) XFER_ERROR = -2; + } + + // Transfer Complete (Clear) Signal + pb_out(b)(0, my_base_address+SIG_TRNS); + + // Transfer Data + + // Read RECV bit from the Board + RECV_START = pb_in(b)(my_base_address+CHK_RECV); + + RECV_START &= CLEAR31; // Only Save LSB + + // Send Data to OGMB + pb_out(l)(Temp_Data, my_base_address+OGMB); + + RECV_POLLING = 1; // Set Polling Flag + RECV_TIMEOUT = 0; + while ((RECV_POLLING == 1) && (RECV_TIMEOUT < MAX_RECV_TIMEOUT)) + { + // Check for Toggled RECV + RECV_TOGGLE = pb_in(b)(my_base_address+CHK_RECV); + + RECV_TOGGLE &= CLEAR31; // Only Save LSB + if (RECV_TOGGLE != RECV_START) RECV_POLLING = 0; // Finished if Different + else RECV_TIMEOUT++; + if (RECV_TIMEOUT == MAX_RECV_TIMEOUT) XFER_ERROR = -2; + } + + // Transfer Complete (Clear) Signal + pb_out(b)(0, my_base_address+SIG_TRNS); + + return XFER_ERROR; +} + +static int pb_outb_pci(char data, unsigned short address, unsigned long my_base_address) +{ + if (my_base_address == 0) + return pb_outb_debug(data, address, my_base_address); + pb_out(b)(data, my_base_address + address); + return 0; +} + +/** + * write to byte register on board + */ +static int pb_dev_outb(struct pulseblaster_device *my_dev, char data, unsigned short address) +{ + unsigned long my_base_address; + + if (my_dev == NULL) + return -1; + + my_base_address = my_dev->base_addr; + + switch (my_base_address != 0 + ? my_dev->boardtype + : PBB_DEBUG) + { + case PBB_DEBUG: + return pb_outb_debug(data, address, my_base_address); + case PBB_GENERIC_AMCC: + return amcc_outb(data, address, my_base_address); + case PBB_GENERIC_PCI: + return pb_outb_pci(data, address, my_base_address); + default: + return -1; + } + + return -1; +} + + +static int pb_inb_debug(unsigned int address, unsigned long my_base_address) +{ + printk("%s: reg(%02x)=0 (guessed)\n",DEVICE_NAME, 0xff&address); + return 0; +} + +/** + * Read a byte from a board using the AMCC chip + */ +static int amcc_inb(unsigned int address, unsigned long my_base_address) { + unsigned int MAX_RECV_TIMEOUT = 10000; + unsigned int RECV_START, RECV_STOP, RECV_TOGGLE, RECV_TIMEOUT = 0; + int XFER_ERROR = 0; + int RECV_POLLING = 0; + + unsigned int CHK_RECV = 0x1F; + unsigned int SIG_TRNS = 0x0F; + unsigned int ICMB = 0x1C; + + unsigned int CLEAR24 = 0x000000FF; + + unsigned int BIT1 = 0x00000002; + unsigned int INV1 = 0x000000FD; + + unsigned short READ_ADDR = 0x9; + + int Toggle = 0; + int Toggle_Temp = 0; + + unsigned int Temp_Data = 0; + + if (my_base_address==0) { + return pb_inb_debug(address, my_base_address); + } + + // CHEK FOR 1 in MD1 + if ((XFER_ERROR=amcc_outb(address, 8, my_base_address))!=0 || + (XFER_ERROR=amcc_outb(0, READ_ADDR, my_base_address))!=0) // Tell board to start a read cycle + return XFER_ERROR; + + RECV_POLLING = 1; // Set Polling Flag + RECV_TIMEOUT = 0; + RECV_START = 0x2; // Value expected when data is ready + + while ((RECV_POLLING == 1) && (RECV_TIMEOUT < MAX_RECV_TIMEOUT)) { + // Check for Toggled RECV + //RECV_TOGGLE = _inp(CHK_RECV); + RECV_TOGGLE = pb_in(b) (my_base_address+CHK_RECV); + RECV_TOGGLE &= BIT1; // Only Save Bit 1 + + if (RECV_TOGGLE == RECV_START) RECV_POLLING = 0; // Finished if Different + else RECV_TIMEOUT++; + if (RECV_TIMEOUT == MAX_RECV_TIMEOUT) XFER_ERROR = -2; + } + if (XFER_ERROR != 0) { + return XFER_ERROR; + } + + //Temp_Data = _inp(ICMB); + // Read RECV bit from the Board + Temp_Data = pb_in(b)(my_base_address+ICMB); + Temp_Data &= CLEAR24; + + + //Toggle = _inp(SIG_TRNS); + Toggle = pb_in(b) (my_base_address+SIG_TRNS); + + Toggle_Temp = Toggle & BIT1; // Only Save Bit 1 + if (Toggle_Temp == 0x0) + { + Toggle |= BIT1; // If Bit 1 is zero, set it to 1 + } + else + { + Toggle &= INV1; // IF Bit 1 is 1, set it to 0 + } + + //_outp(SIG_TRNS, Toggle); + pb_out(b) (Toggle,my_base_address+SIG_TRNS); + + RECV_POLLING = 1; // Set Polling Flag + RECV_TIMEOUT = 0; + RECV_STOP = 0x0; + + while ((RECV_POLLING == 1) && (RECV_TIMEOUT < MAX_RECV_TIMEOUT)) { + // Check for Toggled RECV + //RECV_TOGGLE = _inp(CHK_RECV); + RECV_TOGGLE = pb_in(b) (my_base_address + CHK_RECV); + RECV_TOGGLE &= BIT1; // Only Save Bit 1 + + if (RECV_TOGGLE == RECV_STOP) RECV_POLLING = 0; // Finished if Different + else RECV_TIMEOUT++; + if (RECV_TIMEOUT == MAX_RECV_TIMEOUT) XFER_ERROR = -3; + } + if (XFER_ERROR != 0) { + return XFER_ERROR; + } + + return Temp_Data; +} + +static int pb_inb_pci(unsigned int address, unsigned long my_base_address) +{ + if (my_base_address == 0) + return pb_inb_debug(address, my_base_address); + return pb_in(b)(my_base_address + address); +} + + +/** + * Read byte register from board + */ +static int pb_dev_inb(struct pulseblaster_device *my_dev, unsigned short address) +{ + unsigned long my_base_address; + + if (my_dev == NULL) + return -1; + + my_base_address = my_dev->base_addr; + + switch (my_base_address != 0 + ? my_dev->boardtype + : PBB_DEBUG) + { + case PBB_DEBUG: + return pb_inb_debug(address, my_base_address); + case PBB_GENERIC_AMCC: + return amcc_inb(address, my_base_address); + case PBB_GENERIC_PCI: + return pb_inb_pci(address, my_base_address); + default: + return -1; + } + + return -1; +} + +/** + * Setup and get the base_addr + */ +static unsigned long pb_dev_setup_baseaddr(struct pulseblaster_device *my_dev) +{ + unsigned long addr = 0x0; + + if (my_dev == NULL) + return 0x0; + + if ((my_dev->boardtype != PBB_DEBUG) + && (my_dev->pciboard != NULL)) + { + addr = pci_resource_start(my_dev->pciboard, 0); + } + + my_dev->base_addr = addr; + + return addr; +} + +static inline bool pb_is_debug(struct pulseblaster_device *my_dev) +{ + return ((my_dev->boardtype == PBB_DEBUG) + || (my_dev->pciboard == NULL)); +} + + +/* + * This is called whenever a process attempts to open the device file + */ +static int device_open(struct inode *inode, struct file *file) +{ + struct pulseblaster_device* my_dev=container_of(inode->i_cdev, struct pulseblaster_device, cdev); + file->private_data=my_dev; + + if (pb_is_debug(my_dev)) + printk("%s: device_open(%p)\n", DEVICE_NAME,file); + + /* + * We don't want to talk to two processes at the same time + */ + if (my_dev->device_open) + return -EBUSY; + my_dev->device_open++; + try_module_get(THIS_MODULE); + + return SUCCESS; +} + +/* + * Device released (a.k.a. closed) + */ +static int device_release(struct inode *inode, struct file *file) +{ + struct pulseblaster_device* my_dev=container_of(inode->i_cdev, struct pulseblaster_device, cdev); + + pb_dev_setup_baseaddr(my_dev); + + if (pb_is_debug(my_dev)) + printk("%s: device_release(%p,%p)\n",DEVICE_NAME, inode, file); + + /* + * reset device + */ + if (0) { + // this procedure works only on DDS cards + // DDS Manual + spin_lock(&(my_dev->io_lock)); + pb_dev_outb(my_dev, 0, 0); //dev reset + pb_dev_outb(my_dev, 4, 2); //bytes per word + pb_dev_outb(my_dev, 0xFF, 3); //dev to program + pb_dev_outb(my_dev, 0, 4); //reset address counter + pb_dev_outb(my_dev, 0, 6); //data out + pb_dev_outb(my_dev, 0, 6); //data out + pb_dev_outb(my_dev, 0, 6); //data out + pb_dev_outb(my_dev, 0, 6); //data out + pb_dev_outb(my_dev, 0, 5); //strobe clock + pb_dev_outb(my_dev, 0, 5); //strobe clock + spin_unlock(&(my_dev->io_lock)); + + } + if (0) { + spin_lock(&(my_dev->io_lock)); + // this procedure is successful for 24Bit cards + // SpinCore by mail Oct 10th 2007 + pb_dev_outb(my_dev, 0,6);//store 0's to memory + pb_dev_outb(my_dev, 0,6); + pb_dev_outb(my_dev, 0,6); + pb_dev_outb(my_dev, 0,6); + pb_dev_outb(my_dev, 0,6); + pb_dev_outb(my_dev, 0,6); + pb_dev_outb(my_dev, 0,6); + pb_dev_outb(my_dev, 0,6); + + pb_dev_outb(my_dev, 0,0);//dev reset + pb_dev_outb(my_dev, 4,2);//bytes per word + pb_dev_outb(my_dev, 0,3);//write to internal memory + pb_dev_outb(my_dev, 0,4);//clear address counter + pb_dev_outb(my_dev, 0,6);//data out + pb_dev_outb(my_dev, 0,6);//data out + pb_dev_outb(my_dev, 0,6);//data out + pb_dev_outb(my_dev, 0,6);//data out + pb_dev_outb(my_dev, 7,7);//programming finished + pb_dev_outb(my_dev, 7,1);//trigger + spin_unlock(&(my_dev->io_lock)); + } + + /* + * We're now ready for our next caller + */ + my_dev->device_open--; + + module_put(THIS_MODULE); + return SUCCESS; +} + +/* + * This function is called whenever a process which has already opened the + * device file attempts to read from it. + */ +static ssize_t device_read(struct file *file, /* see include/linux/fs.h */ + char __user * buffer, /* buffer to be + * filled with data */ + size_t length, /* length of the buffer */ + loff_t * offset) +{ + + // not implemented + return 0; +} + +/* + * This function is called when somebody tries to + * write into our device file. + * everything goes to register 6: data + */ +static ssize_t +device_write(struct file *file, + const char __user * buffer, size_t length, loff_t * offset) +{ + struct pulseblaster_device* my_dev=(struct pulseblaster_device*)file->private_data; + register int i=0; + + pb_dev_setup_baseaddr(my_dev); + + while (1) { + // sometimes + unsigned char temp_byte; + int retval; + get_user(temp_byte, buffer + i); + // the hardware access + spin_lock(&(my_dev->io_lock)); + retval = pb_dev_outb(my_dev, temp_byte,6); + spin_unlock(&(my_dev->io_lock)); + if (retval!=0) { + printk("%s: IO Error, pb_dev_outb returned %d\n",DEVICE_NAME, retval); + return -EIO; // make an ordinary io error + } + i++; + /* break codition */ + if (i>=length) break; + /* be decent to all other processes --- at least sometimes */ + if ((i & ((1<<14)-1)) == 0) schedule(); + } + + /* + * Again, return the number of input characters used + */ + return i; +} + +/* + * This function is called whenever a process tries to do an ioctl on our + * device file. We get two extra parameters (additional to the inode and file + * structures, which all device functions get): the number of the ioctl called + * and the parameter given to the ioctl function. + * + * If the ioctl is write or read/write (meaning output is returned to the + * calling process), the ioctl call returns the output of this function. + * + */ +static long device_ioctl( + struct file *file, /* ditto */ + unsigned int ioctl_num, /* number and param for ioctl */ + unsigned long ioctl_param) +{ + struct inode *inode = file->f_path.dentry->d_inode; + struct pulseblaster_device* my_dev=container_of(inode->i_cdev, struct pulseblaster_device, cdev); + int ret_val=SUCCESS; + + pb_dev_setup_baseaddr(my_dev); + + if (ioctl_num==IOCTL_OUTB){ + unsigned char reg=(ioctl_param>>8)&0xFF; + unsigned char val=ioctl_param&0xFF; + /* + check for register boundaries! + */ + if (reg>7) { + printk("%s: got bad register number %02x\n",DEVICE_NAME,0x0ff®); + ret_val=-EINVAL; + } + else { + spin_lock(&(my_dev->io_lock)); + ret_val = pb_dev_outb(my_dev, val, reg); + spin_unlock(&(my_dev->io_lock)); + if (ret_val!=0) { + printk("%s: IO Error, pb_dev_outb returned %d\n",DEVICE_NAME, ret_val); + ret_val=-EIO; // make an ordinary io error + } + } + } + else if (ioctl_num==IOCTL_INB) { + unsigned char reg=ioctl_param&0xFF; + //printk("%s: reading register number %02x\n",DEVICE_NAME,0x0ff®); + spin_lock(&(my_dev->io_lock)); + ret_val = pb_dev_inb(my_dev, reg); + spin_unlock(&(my_dev->io_lock)); + //printk("%s: found %02x=%02x\n", DEVICE_NAME, 0x0ff®, 0x0ff&val); + if (ret_val<0) { + printk("%s: IO Error, pb_dev_inb returned %d\n",DEVICE_NAME, ret_val); + ret_val=-EIO; // make an ordinary io error + } + } + else { + printk("%s: unknown ioctl request number %d\n",DEVICE_NAME, ioctl_num); + ret_val=-EINVAL; + } + + return ret_val; +} + +/* Module Declarations */ + +/* + * This structure will hold the functions to be called + * when a process does something to the device we + * created. Since a pointer to this structure is kept in + * the devices table, it can't be local to + * init_module. NULL is for unimplemented functions. + */ +struct file_operations pulseblaster_fops = { + .read = device_read, + .write = device_write, + .unlocked_ioctl = device_ioctl, + .open = device_open, + .release = device_release, /* a.k.a. close */ +}; + +/* + here all the basic pci stuff follows +*/ + +/* + * no hotpluging after initialisation... + */ +static int pulseblaster_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { + int ret_val; + + spin_lock(&pb_devs_lock); + // check wether device numbers were already allocated + if (pb_dev_no_start!=0) { + //we are too late + spin_unlock(&pb_devs_lock); + printk("%s: Sorry, hotpluging not supported!\n", DEVICE_NAME); + return -1; + } + + if (pb_dev_no>=pulseblaster_max_devno) { + spin_unlock(&pb_devs_lock); + printk("%s: Sorry, maximum number of allocatable devices reached !\n", DEVICE_NAME); + return -1; + } + /* do i need this?, what about pci_enable_device_bars() */ + ret_val=pci_enable_device(dev); + if (ret_val!=0) { + spin_unlock(&pb_devs_lock); + printk("%s: failed to enable pci device!\n",DEVICE_NAME); + return -1; + } + +#ifndef CONFIG_XEN + // why is pci_request_region not defined in a XEN kernel? + /* exclusive use */ + ret_val=pci_request_region(dev, 0, DEVICE_NAME); + if (ret_val!=0) { + spin_unlock(&pb_devs_lock); + printk("%s: failed to enable pci device!\n",DEVICE_NAME); + pci_disable_device(dev); + return -1; + } +#else +# warning "in XEN version pci_request_region and pci_release_region are not defined, omitting function call" +#endif + + /* initialize the structure */ + pb_devs[pb_dev_no].device_open=0; + spin_lock_init(&(pb_devs[pb_dev_no].io_lock)); + pb_devs[pb_dev_no].pciboard=dev; + pb_devs[pb_dev_no].boardtype = id->driver_data; + pb_devs[pb_dev_no].base_addr = 0x0; + + // todo: inform about slots as a hint for the physical location of the board! + printk("%s: found a pci board with i/o base address 0x%lx, assigning no %d\n", + DEVICE_NAME, + (unsigned long)pci_resource_start(dev, 0), + pb_dev_no); + + ++pb_dev_no; + spin_unlock(&pb_devs_lock); + + return SUCCESS; +} + +static void pulseblaster_pci_remove(struct pci_dev* dev) { + int i=0; + /* for hotplugging, maintain the list in a smart way + here: remove only dangling device pointer! + */ + spin_lock(&pb_devs_lock); + for (i=0; i= KERNEL_VERSION(2, 6, 26) + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) +#define WITH_DRVDATA +#endif + +static struct class *pulseblaster_class = NULL; + +static void pb_class_setup(void) +{ + /* + * create class and register devices in /sys + */ + pulseblaster_class = class_create(THIS_MODULE, DEVICE_NAME); + if (IS_ERR(pulseblaster_class)) + { + printk(KERN_ERR "%s: failed to register class", DEVICE_NAME); + pulseblaster_class = NULL; + } +} + +static void pb_class_destroy(void) +{ + if (pulseblaster_class) + class_destroy(pulseblaster_class); + pulseblaster_class = NULL; +} + +/** + * Add device to our class + */ +static void pb_class_device_create(dev_t devno, int nr) +{ + struct device *dev; + + if (pulseblaster_class == NULL) + return; + + if (nr == -1) + { + dev = device_create(pulseblaster_class, NULL, devno, +#ifdef WITH_DRVDATA + NULL, +#endif + DEVICE_NAME"_dbg"); + } + else + { + dev = device_create(pulseblaster_class, NULL, devno, +#ifdef WITH_DRVDATA + NULL, +#endif + DEVICE_NAME"%d", nr); + } + + if (IS_ERR(dev)) + { + printk(KERN_WARNING "%s: device_create for %d failed\n", + DEVICE_NAME, nr); + } +} + +/** + * Remove device from class + */ +static void pb_class_device_destroy(dev_t devno) +{ + if (pulseblaster_class == NULL) + return; + + device_destroy(pulseblaster_class, devno); +} +#else +static void pb_class_setup(void) +{ + printk(KERN_WARNING "%s: No udev support\n", DEVICE_NAME); +} +static void pb_class_destroy(void) +{ +} +static void pb_class_device_create(dev_t devno, int nr) +{ +} +static void pb_class_device_destroy(dev_t devno) +{ +} +# warning "No class support, hotplug/udev wont wrok" +#endif + +/* + * Initialize the module - Register the character device + */ + +static int __init init_pulseblaster_module(void) +{ + int ret_val; + int i; + int major_dev_num, minor_dev_num; + + + if (base_address!=-1) { + printk("%s: no longer supporting 'base_address' parameter\n", DEVICE_NAME); + } + pb_dev_no_start=0; + pb_dev_no=0; + // lock for manipulating the device list + spin_lock_init(&pb_devs_lock); + + // always provide a debug device + pb_dev_debug.device_open=0; + spin_lock_init(&(pb_dev_debug.io_lock)); + pb_dev_debug.pciboard=0x0; // this is the debug flag for amcc functions + pb_dev_debug.boardtype = PBB_DEBUG; + pb_dev_debug.base_addr = 0x0; + + // register code for pci bus scanning + ret_val=pci_register_driver(&pulseblaster_pci_driver); + // todo error checking + if (ret_val<0) { + printk("%s: registering the pci driver failed", DEVICE_NAME); + return ret_val; + } + + // get device ids + ret_val= alloc_chrdev_region(&pb_dev_no_start, 0, pb_dev_no+1, DEVICE_NAME); + major_dev_num=MAJOR(pb_dev_no_start); + minor_dev_num=MINOR(pb_dev_no_start); + if (ret_val < 0) { + printk("%s failed with %d\n", + "Sorry, registering the character device failed\n", ret_val); + pci_unregister_driver(&pulseblaster_pci_driver); + return ret_val; + } + + pb_class_setup(); + + // register debug device + { + dev_t devno = MKDEV(major_dev_num, minor_dev_num+pb_dev_no); + cdev_init(&(pb_dev_debug.cdev), &pulseblaster_fops); + pb_dev_debug.cdev.owner=THIS_MODULE; + pb_dev_debug.cdev.ops=&pulseblaster_fops; + ret_val=cdev_add(&(pb_dev_debug.cdev), devno, 1); + + pb_class_device_create(devno, -1); + } + + spin_lock(&pb_devs_lock); + for (i=0; i +#define PULSEBLASTER_DEVICE_MAGIC_NUMBER 'p' + +#define IOCTL_OUTB _IOW(PULSEBLASTER_DEVICE_MAGIC_NUMBER, 1, unsigned long) +#define IOCTL_INB _IOR(PULSEBLASTER_DEVICE_MAGIC_NUMBER, 2, unsigned long) + +/* + * The name of the device file + */ +#define PULSEBLASTER_DEVICE_FILE_NAME "pulseblaster" + +#endif diff --git a/drivers/SpinCore-PulseBlaster/pulseblaster_test.cpp b/drivers/SpinCore-PulseBlaster/pulseblaster_test.cpp new file mode 100644 index 0000000..ec64e2b --- /dev/null +++ b/drivers/SpinCore-PulseBlaster/pulseblaster_test.cpp @@ -0,0 +1,71 @@ +#include +#include +#include +#include +#include "SpinCore-PulseBlaster.h" + +class SpinCorePulseBlaster24Bit: public SpinCorePulseBlaster { +public: + SpinCorePulseBlaster24Bit (double the_clock=100e6): SpinCorePulseBlaster(10,the_clock) { + } + + void append_command(std::string& data, int flags, opcode inst, int inst_data, double length) { + unsigned char command[command_length]; + + if (inst>8) + throw SpinCorePulseBlaster_error("instruction code not known"); + + double tmpdelay=(clock*length-3.0); + if (tmpdelay<(shortest_pulse-3)) + throw SpinCorePulseBlaster_error("pulse is shorter than allowed"); + if (tmpdelay>(pow(2,32)-1)) + throw SpinCorePulseBlaster_error("pulse is longer than allowed"); + unsigned int delay=(unsigned int)tmpdelay; + + // Output, Control Word 1st Byte + command[0]=(flags&0xff0000)>>16; + // Output, Control Word 2nd Byte + command[1]=(flags&0xff00)>>8; + // Output, Control Word 3rd Byte + command[2]=flags&0xff; + // Data Field 1st Byte + command[3]=(inst_data&0x0ff000)>>12; + // Data Field 2nd Byte + command[4]=(inst_data&0xff0)>>4; + // Data Field 3rd Byte and opcode + command[5]=(inst_data&0xf)<<4|(inst&0xf); + // Delay Count 1st Byte + command[6]=(delay&0xff000000)>>24; + // Delay Count 2nd Byte + command[7]=(delay&0xff0000)>>16; + // Delay Count 3rd Byte + command[8]=(delay&0xff00)>>8; + // Delay Count 4th Byte + command[9]=(delay&0xff); + + data.append((char*)command,command_length); + } + +}; + + +int main() { + + try { + SpinCorePulseBlaster24Bit p; + std::string program; + p.append_command(program,1,SpinCorePulseBlaster::LOOP,5,2e-6); + p.append_command(program,3,SpinCorePulseBlaster::END_LOOP,0,1e-6); + p.append_command(program,4,SpinCorePulseBlaster::CONTINUE,0,1e-6); + p.append_command(program,0,SpinCorePulseBlaster::CONTINUE,0,1e0); + p.append_command(program,1,SpinCorePulseBlaster::STOP,0,1e-6); + p.set_program(program); + p.set_initialized(); + p.start(); + } + catch(SpinCorePulseBlaster_error e) { + fprintf(stderr, "%s\n", e.c_str()); + } + + return 0; +} diff --git a/drivers/SpinCore-PulseBlaster24Bit/Makefile b/drivers/SpinCore-PulseBlaster24Bit/Makefile new file mode 100644 index 0000000..0564dcd --- /dev/null +++ b/drivers/SpinCore-PulseBlaster24Bit/Makefile @@ -0,0 +1,23 @@ +CXXFLAGS=-g -O0 -Wshadow -Wall -pedantic +CXXCPPFLAGS=-I../.. +CXX=g++ + +all: SpinCore-PulseBlaster24Bit.o + +test: test.cpp SpinCore-PulseBlaster24Bit.o ../SpinCore-PulseBlaster/SpinCore-PulseBlaster.o ../SpinCore-PulseBlaster/PulseBlasterProgram.o ../../core/core.a + $(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -o $@ $^ -lm -lexpat -lxerces-c + +../SpinCore-PulseBlaster/SpinCore-PulseBlaster.o: ../SpinCore-PulseBlaster/SpinCore-PulseBlaster.cpp ../SpinCore-PulseBlaster/SpinCore-PulseBlaster.h + $(MAKE) -C ../SpinCore-PulseBlaster SpinCore-PulseBlaster.o + +../SpinCore-PulseBlaster/PulseBlasterProgram.o: ../SpinCore-PulseBlaster/PulseBlasterProgram.cpp ../SpinCore-PulseBlaster/PulseBlasterProgram.h + $(MAKE) -C ../SpinCore-PulseBlaster PulseBlasterProgram.o + +../../core/core.a: + $(MAKE) -C ../../core core.a + +SpinCore-PulseBlaster24Bit.o: SpinCore-PulseBlaster24Bit.cpp SpinCore-PulseBlaster24Bit.h + $(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -c -o $@ $< + +clean: + rm -f *.o *~ test diff --git a/drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.cpp b/drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.cpp new file mode 100644 index 0000000..6aa9fa5 --- /dev/null +++ b/drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.cpp @@ -0,0 +1,274 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: March 2005 + +****************************************************************************/ +#include "core/core.h" +#include "SpinCore-PulseBlaster24Bit.h" +#include +#include +#include + +#ifndef SIZETPRINTFLETTER +# ifndef __LP64__ +# define SIZETPRINTFLETTER "u" +# else +# define SIZETPRINTFLETTER "lu" +# endif +#endif + +/********************************************************************* + + PulseBlaster24BitCommand + +*********************************************************************/ + +PulseBlaster24BitCommand::PulseBlaster24BitCommand(): PulseBlasterCommand() { +} + +PulseBlaster24BitCommand::PulseBlaster24BitCommand(const PulseBlaster24BitCommand& orig): PulseBlasterCommand(orig) {} + +PulseBlaster24BitCommand::PulseBlaster24BitCommand(PulseBlaster24BitProgram& p): PulseBlasterCommand(p) {} + +PulseBlaster24BitCommand::PulseBlaster24BitCommand(PulseBlaster24BitProgram& p, + int _ttls, double _length) + :PulseBlasterCommand(p, _ttls, _length) +{} + +int PulseBlaster24BitCommand::write_to_file(FILE* out, size_t indent) const { + + if (program==NULL) throw pulse_exception("PulseBlaster24Bit: Command not associated with Program"); + fprintf(out,"%sbegin(),jump)); + break; + case SpinCorePulseBlaster::END_LOOP: + fprintf(out,"inst=\"END_LOOP\" address=\"%" SIZETPRINTFLETTER "\"",std::distance(program->begin(),jump)); + break; + case SpinCorePulseBlaster::JSR: + fprintf(out,"inst=\"JSR\" address=\"%" SIZETPRINTFLETTER "\"",std::distance(program->begin(),jump)); + break; + case SpinCorePulseBlaster::CONTINUE: + fprintf(out,"inst=\"CONTINUE\""); + break; + case SpinCorePulseBlaster::STOP: + fprintf(out,"inst=\"STOP\""); + break; + case SpinCorePulseBlaster::RTS: + fprintf(out,"inst=\"RTS\""); + break; + case SpinCorePulseBlaster::WAIT: + fprintf(out,"inst=\"WAIT\""); + break; + default: + fprintf(out,"inst=\"%d\" instdata=\"UNKNOWN\"",instruction); + } + fprintf(out," length=\"%g\"/>\n",(1.0*length)/program->internal_clock_freq); + return 1; +} + +/********************************************************************* + + PulseBlaster24BitProgram + +*********************************************************************/ + +PulseBlaster24BitProgram::PulseBlaster24BitProgram(): PulseBlasterProgram() { + internal_clock_freq=100.0e6; + minimum_interval=9; + ttl_device_id=0; +} + +PulseBlaster24BitProgram::PulseBlaster24BitProgram(const SpinCorePulseBlaster24Bit& pb24) { + internal_clock_freq=pb24.clock; + minimum_interval=pb24.shortest_pulse; + ttl_device_id=pb24.ttl_device_id; +} + +PulseBlaster24BitProgram::PulseBlaster24BitProgram(const PulseBlaster24BitProgram& orig): PulseBlasterProgram() { + ttl_device_id=orig.ttl_device_id; + // be sure, there are no old list items left! + clear(); + const_iterator orig_i; + for (orig_i=orig.begin(); orig_i!=orig.end(); ++orig_i) { + const PulseBlaster24BitCommand* the_command=dynamic_cast(*orig_i); + if (the_command==NULL) { + throw SpinCorePulseBlaster_error("wrong command class or NULL pointer found in program"); + } + PulseBlaster24BitCommand* new_one=new PulseBlaster24BitCommand(*the_command); + new_one->program=this; + push_back(new_one); + } + // set correct references... + orig_i=orig.begin(); + for(iterator i=begin(); i!=end(); ++i) { + (**i).program=this; + if ((**i).instruction==SpinCorePulseBlaster::END_LOOP || (**i).instruction==SpinCorePulseBlaster::BRANCH || (**i).instruction==SpinCorePulseBlaster::JSR) { + (**i).jump=i; + advance((**i).jump,distance(orig.begin(),(**orig_i).jump)-distance(orig.begin(),orig_i)); + } + ++orig_i; + } +} + + +PulseBlasterCommand* PulseBlaster24BitProgram::create_command(const state& the_state) { + if (typeid(the_state)==typeid(state_sequent)||typeid(the_state)==typeid(state_parallel)) + throw pulse_exception("PulseBlasterCommands can only be created from states"); + long unsigned int interval = ceil(the_state.length*internal_clock_freq); + if ( interval < minimum_interval) { + fprintf(stderr,"PulseBlaster state too short (%lu < %lu ticks): %e\n", interval, \ + minimum_interval, \ + the_state.length); + throw pulse_exception("PulseBlaster state too short"); + } + // all states or-ed together + int the_ttls=0; + for (state::const_iterator i=the_state.begin(); i!=the_state.end(); ++i) { + const ttlout* to=dynamic_cast(*i); + if (to!=NULL && to->id==ttl_device_id) { + the_ttls|=to->ttls.to_ulong(); + } + } + return new PulseBlaster24BitCommand(*this, the_ttls, the_state.length); +} + +PulseBlasterCommand* PulseBlaster24BitProgram::create_command(const PulseBlasterCommand* orig) { + PulseBlaster24BitCommand* new_command=NULL; + if (orig==NULL) { + new_command=new PulseBlaster24BitCommand(); + new_command->program=this; + new_command->length=minimum_interval; + } + else { + const PulseBlaster24BitCommand* command24Bit=dynamic_cast(orig); + if (command24Bit==NULL) throw pulse_exception("wrong PulseBlasterCommand class"); + new_command=new PulseBlaster24BitCommand(*command24Bit); + } + return new_command; +} + + +int PulseBlaster24BitProgram::write_to_file(FILE* out, size_t indent) const { + std::string indent_string(indent,' '); + fprintf(out,"%s\n",indent_string.c_str()); + + for(const_iterator i=begin(); i!=end(); ++i) { + if (*i==NULL) throw SpinCorePulseBlaster_error("NULL pointer found in command list"); + (**i).write_to_file(out, indent+2); + } + fprintf(out,"%s\n",indent_string.c_str()); + return 1; +} + + +/********************************************************************* + + SpinCorePulseBlaster24Bit + +*********************************************************************/ + +void SpinCorePulseBlaster24Bit::write_command(unsigned char* data, int flags, opcode inst, unsigned int inst_data, size_t delay) { + if (inst>8) + throw SpinCorePulseBlaster_error("instruction code not known"); + + // Output, Control Word 1st Byte + data[0]=(flags&0xff0000)>>16; + // Output, Control Word 2nd Byte + data[1]=(flags&0xff00)>>8; + // Output, Control Word 3rd Byte + data[2]=flags&0xff; + // Data Field 1st Byte + data[3]=(inst_data&0x0ff000)>>12; + // Data Field 2nd Byte + data[4]=(inst_data&0xff0)>>4; + // Data Field 3rd Byte and opcode + data[5]=(inst_data&0xf)<<4|(inst&0xf); + // Delay Count 1st Byte + data[6]=(delay&0xff000000)>>24; + // Delay Count 2nd Byte + data[7]=(delay&0xff0000)>>16; + // Delay Count 3rd Byte + data[8]=(delay&0xff00)>>8; + // Delay Count 4th Byte + data[9]=(delay&0xff); + /* *** BUG-FIX (ToDo: clean solution) *** + there is a nasty error in pulseblaster, affecting all states with 4th byte + equal 0xff and delay >255. In this case reduce state for 10ns. + */ + if (0 && data[9]==0xff && delay>0xff) + data[9]=0xfe; +} + + + +void SpinCorePulseBlaster24Bit::write_command(unsigned char* data, const PulseBlasterCommand& command) { + const PulseBlaster24BitCommand* command24Bit=dynamic_cast(&command); + if (command24Bit==NULL) + throw SpinCorePulseBlaster_error("found wrong command class in PulseBlasterProgram"); + + if (command24Bit->program==NULL) throw SpinCorePulseBlaster_error("Command not associated with Program"); + unsigned int inst_data=0; + switch(command24Bit->instruction) { + case SpinCorePulseBlaster::CONTINUE: + case SpinCorePulseBlaster::STOP: + case SpinCorePulseBlaster::WAIT: + case SpinCorePulseBlaster::RTS: + // no parameter + break; + case SpinCorePulseBlaster::LOOP: + inst_data=command24Bit->loop_count-1; + break; + case SpinCorePulseBlaster::LONG_DELAY: + inst_data=command24Bit->loop_count-2; + break; + case SpinCorePulseBlaster::BRANCH: + case SpinCorePulseBlaster::END_LOOP: + case SpinCorePulseBlaster::JSR: + inst_data=std::distance(command24Bit->program->begin(),command24Bit->jump); + break; + default: + throw SpinCorePulseBlaster_error("instruction code not known"); + } + if (command24Bit->length<3) + throw SpinCorePulseBlaster_error("delay length too small!"); + unsigned int delay=(unsigned int)command24Bit->length-3; + write_command(data,command24Bit->ttls,command24Bit->instruction,inst_data,delay); +} + +void SpinCorePulseBlaster24Bit::write_to_device(const PulseBlaster24BitProgram& p) { + std::string program; + //Begin pulse program + for (PulseBlaster24BitProgram::const_iterator c=p.begin(); c!=p.end(); ++c) { + char command[10]; + write_command((unsigned char*)command,**c); + program.append(command, (size_t)10); + } + set_program(program); + // End of programming registers and pulse program + set_initialized(); +} + +PulseBlasterProgram* SpinCorePulseBlaster24Bit::create_program(state& exp) { + PulseBlaster24BitProgram* prog=new PulseBlaster24BitProgram(); + // the user's code + prog->append_sequence(exp); + return prog; +} + +void SpinCorePulseBlaster24Bit::run_pulse_program(const PulseBlasterProgram& p) { + const PulseBlaster24BitProgram* prog=dynamic_cast(&p); + if (prog==NULL) + throw SpinCorePulseBlaster_error("found wrong program class in SpinCorePulseBlaster24Bit method"); + write_to_device(*prog); + start(); +} diff --git a/drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h b/drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h new file mode 100644 index 0000000..e67dd0a --- /dev/null +++ b/drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h @@ -0,0 +1,143 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: March 2005 + +****************************************************************************/ +#ifndef SPINCOREPULSEBLASTER24BIT_H +#define SPINCOREPULSEBLASTER24BIT_H + +#ifndef SPINCORE_PULSEBLASTER_H +#include "drivers/SpinCore-PulseBlaster/SpinCore-PulseBlaster.h" +#include "drivers/SpinCore-PulseBlaster/PulseBlasterProgram.h" +#endif +#include "core/stopwatch.h" +#include +#include +#include +#include +#include +#include + +/** + \defgroup SpinCorePulseBlaster24Bit SpinCore PulseBlaster 24Bit + \ingroup drivers + \brief classes concerning SpinCore PulseBlaster 24Bit version + @{ +*/ + +class SpinCorePulseBlaster24Bit; +class PulseBlaster24BitCommand; + +/** + \brief holds the complete and detailed initialisation of the device + */ +class PulseBlaster24BitProgram: public PulseBlasterProgram { +public: + /// ttl device id + int ttl_device_id; + + /// standard allocator + PulseBlaster24BitProgram(); + + /// copy constructor has to correct the pointers and references in the command list + PulseBlaster24BitProgram(const PulseBlaster24BitProgram& orig); + + /// create suitable empty program + PulseBlaster24BitProgram(const SpinCorePulseBlaster24Bit& pb24); + + /// write all configuration to a file to xml + int write_to_file(FILE* out, size_t indent=0) const; + + /// create a suitable state for this kind of program + virtual PulseBlasterCommand* create_command(const state& state); + + /// create a copy or a minimal empty state + virtual PulseBlasterCommand* create_command(const PulseBlasterCommand* orig=NULL); +}; + +/** + \brief parameters for each PulseBlaster command +*/ +class PulseBlaster24BitCommand: public PulseBlasterCommand { +public: + /// the simple constructor + PulseBlaster24BitCommand(); + + /// constructs a minimal length state + PulseBlaster24BitCommand(PulseBlaster24BitProgram&); + + /// the copy constructor + PulseBlaster24BitCommand(const PulseBlaster24BitCommand&); + + // a useful constructor for a simple CONTINUE state + PulseBlaster24BitCommand(PulseBlaster24BitProgram& p, int _ttls, double _length); + + /// write the command to a given file as xml tag + int write_to_file(FILE* out, size_t indent=0) const; +}; + +/** + \brief the driver implementation for PulseBlaster24Bit + uses the classes PulseBlaster24BitProgram and PulseBlaster24BitCommand +*/ +class SpinCorePulseBlaster24Bit: public SpinCorePulseBlaster { + public: + + /** + initialises the PulseBlaster24Bit of Spincore + */ + SpinCorePulseBlaster24Bit(int the_id=0, double the_clock=1e8, unsigned int _sync_mask=0): SpinCorePulseBlaster(10,the_clock) { + ttl_device_id=the_id; + sync_mask=_sync_mask; + fprintf(stderr, "SpinCore PulseBlaster 24Bit: clock=%f Hz, device id=%d", the_clock, the_id); + if (sync_mask!=0) + fprintf(stderr, ", sync_mask=0x%06x", sync_mask); + fprintf(stderr, "\n"); + } + + SpinCorePulseBlaster24Bit(const SpinCorePulseBlaster24Bit& orig); + /** + create a full program from state tree + + in contrast to append_sequence, this function expects a complete program, not only a fraction, needed by run_pulse_program method + */ + virtual PulseBlasterProgram* create_program(state& exp); + + /** + run a PulseBlaster Program + */ + virtual void run_pulse_program(const PulseBlasterProgram& p); + + /// write command as a 10 byte machine word + virtual void write_command(unsigned char* data, const PulseBlasterCommand& command); + + /// write raw parameters as a 10 byte machine word + virtual void write_command(unsigned char* data, int flags, opcode inst, unsigned int inst_data, size_t delay); + + /// append raw parameters as a 10 byte machine word + virtual void append_command(std::string& data, int flags, opcode inst, int inst_data, size_t delay) { + unsigned char buffer[10]; + write_command(buffer,flags,inst,inst_data,delay); + data.append((char*)buffer,(size_t)10); + } + + /// append command as a 10 byte machine word + virtual void append_command(std::string& data, const PulseBlasterCommand& command) { + unsigned char buffer[10]; + write_command(buffer,command); + data.append((char*)buffer,(size_t)10); + } + + virtual void write_to_device(const PulseBlaster24BitProgram& p); + + /** + destructor (close device) + */ + virtual ~SpinCorePulseBlaster24Bit() {} +}; + +/** + @} +*/ +#endif diff --git a/drivers/SpinCore-PulseBlaster24Bit/test.cpp b/drivers/SpinCore-PulseBlaster24Bit/test.cpp new file mode 100644 index 0000000..9105aa4 --- /dev/null +++ b/drivers/SpinCore-PulseBlaster24Bit/test.cpp @@ -0,0 +1,172 @@ +#include "SpinCore-PulseBlaster24Bit.h" +#include "core/xml_states.h" +#include +#include + +int lowlevel_test() { + try { + SpinCorePulseBlaster24Bit p; + std::string program; + p.append_command(program,1,SpinCorePulseBlaster::LOOP,5,97); + p.append_command(program,3,SpinCorePulseBlaster::END_LOOP,0,97); + p.append_command(program,4,SpinCorePulseBlaster::CONTINUE,0,97); + p.append_command(program,0,SpinCorePulseBlaster::CONTINUE,0,100000000-3); + p.append_command(program,1,SpinCorePulseBlaster::STOP,0,97); + p.set_program(program); + p.set_initialized(); + p.start(); + } + catch(SpinCorePulseBlaster_error e) { + fprintf(stderr, "%s\n", e.c_str()); + } + + return 0; +} + +int wait_test() { + try { + SpinCorePulseBlaster24Bit p; + std::string program; + //virtual void append_command(std::string& data, int flags, opcode inst, int inst_data, size_t delay) + p.append_command(program,0,SpinCorePulseBlaster::CONTINUE,0,97); + p.append_command(program,8,SpinCorePulseBlaster::WAIT,0,97); + p.append_command(program,4,SpinCorePulseBlaster::CONTINUE,0,97); + p.append_command(program,0,SpinCorePulseBlaster::CONTINUE,0,97); + p.append_command(program,0,SpinCorePulseBlaster::STOP,0,97); + p.set_program(program); + p.set_initialized(); + p.start(); + } + catch(SpinCorePulseBlaster_error e) { + fprintf(stderr, "%s\n", e.c_str()); + } + + return 0; + +} + +int simple_sequence() { + try { + SpinCorePulseBlaster24Bit sp24; + PulseBlaster24BitProgram prog(sp24); + // zero + prog.push_back(prog.create_command()); + prog.back()->ttls=1; + prog.back()->instruction=SpinCorePulseBlaster::LOOP; + prog.back()->loop_count=10; + prog.back()->length=20; + // one + prog.push_back(prog.create_command()); + prog.back()->ttls=0; + prog.back()->instruction=SpinCorePulseBlaster::END_LOOP; + prog.back()->jump=prog.begin(); + prog.back()->length=10; + // two + prog.push_back(prog.create_command()); + prog.back()->ttls=0; + prog.back()->instruction=SpinCorePulseBlaster::STOP; + sp24.run_pulse_program(prog); + + PulseBlaster24BitProgram prog2(prog); + prog.write_to_file(stdout); + sp24.run_pulse_program(prog2); + } + catch(SpinCorePulseBlaster_error e) { + fprintf(stderr, "pulseblaster: %s\n", e.c_str()); + return 1; + } + catch(pulse_exception e) { + fprintf(stderr, "pulse: %s\n", e.c_str()); + return 1; + } + return 0; +} + +class core { +public: + static int term_signal; +}; + + +int state_sequence(const char* filename) { + + try { + SpinCorePulseBlaster24Bit sp24; + PulseBlaster24BitProgram prog; + state_atom* sa=xml_state_reader().read_from_file(filename); + state_sequent* ss=dynamic_cast(sa); + if (ss==NULL) { + delete sa; + throw SpinCorePulseBlaster_error("found no states"); + } + prog.push_front(prog.create_command()); + prog.append_sequence(*ss); + prog.push_back(prog.create_command()); + prog.push_back(prog.create_command()); + prog.back()->instruction=SpinCorePulseBlaster::STOP; + prog.write_to_file(stdout); + state_iterator i(*ss); + while (!i.is_last()) i.next_state(); + sp24.duration=i.get_time()+2*90e-9; + sp24.run_pulse_program(prog); + sp24.time_running.start(); + + sp24.wait_till_end(); + + fprintf(stdout, "%g seconds elapsed\n", sp24.time_running.elapsed()); + } + catch(SpinCorePulseBlaster_error e) { + fprintf(stderr, "pulseblaster: %s\n", e.c_str()); + return 1; + } + catch(pulse_exception e) { + fprintf(stderr, "pulse: %s\n", e.c_str()); + return 1; + } + return 0; +} + + + +int main(int argc, char** argv) { + core::term_signal=0; + size_t n=1; + const char* filename=NULL; + + if (argc<2 || argc>3) { + fprintf(stderr,"%s [repeat] pulsesequence\n",argv[0]); + return 1; + } + + if (argc==3) { + char* endptr=NULL; + n=strtoul(argv[1],&endptr,10); + if (*endptr!=0 && n<1) { + fprintf(stderr,"%s [repeat] pulsesequence\n",argv[0]); + return 1; + } + filename=argv[2]; + } + + + if (argc==2) { + filename=argv[1]; + } + + if (0!=access(filename, R_OK)) { + fprintf(stderr, "%s: could not open file %s, reason: %s\n",argv[0],filename, strerror(errno)); + return 1; + } + + + int value_returned=0; + size_t i=0; + while (i1) { + fprintf(stdout,"%s: executed %s %u times\n",argv[0],filename,i); + } + return value_returned; +} diff --git a/drivers/SpinCore-PulseBlasterDDSIII/Makefile b/drivers/SpinCore-PulseBlasterDDSIII/Makefile new file mode 100644 index 0000000..1c4829f --- /dev/null +++ b/drivers/SpinCore-PulseBlasterDDSIII/Makefile @@ -0,0 +1,35 @@ +############################################################################# +# +# Author: Achim Gaedke +# Created: June 2004 +# +############################################################################# +CXX=g++ +CXXFLAGS=-g -O0 -Wall -Wshadow -pedantic +CXXCPPFLAGS=-I../.. -I. + +.PHONY: all clean intall + +all: SpinCore-PulseBlasterDDSIII.o + +../../tools/add_endline.exe: ../../tools/add_endline.cpp + $(CXX) $< -o $@ + +../SpinCore-PulseBlaster/SpinCore-PulseBlaster.o: ../SpinCore-PulseBlaster/SpinCore-PulseBlaster.cpp ../SpinCore-PulseBlaster/SpinCore-PulseBlaster.h + $(MAKE) -C ../SpinCore-PulseBlaster SpinCore-PulseBlaster.o + +SpinCore-PulseBlasterDDSIII.o: SpinCore-PulseBlasterDDSIII.cpp SpinCore-PulseBlasterDDSIII.h ../SpinCore-PulseBlaster/SpinCore-PulseBlaster.h ../pulsegen.h ../frequgen.h + $(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -c $< -o $@ + +../../core/core.a: + make -C ../../core core.a + +test.exe: test.cpp SpinCore-PulseBlasterDDSIII.o ../SpinCore-PulseBlaster/SpinCore-PulseBlaster.o ../SpinCore-PulseBlaster/PulseBlasterProgram.o ../../core/core.a + $(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -o $@ $^ -lexpat + +clean: ../../tools/add_endline.exe + for f in *.cpp *.h; do ../../tools/add_endline.exe $$f; done; \ +rm -f *.stackdump *.o *.exe \#* *~ + +install: + install PBD03PC.dll $(PREFIX) diff --git a/drivers/SpinCore-PulseBlasterDDSIII/SpinCore-PulseBlasterDDSIII.cpp b/drivers/SpinCore-PulseBlasterDDSIII/SpinCore-PulseBlasterDDSIII.cpp new file mode 100644 index 0000000..6aa9b8c --- /dev/null +++ b/drivers/SpinCore-PulseBlasterDDSIII/SpinCore-PulseBlasterDDSIII.cpp @@ -0,0 +1,510 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + +****************************************************************************/ +#include "SpinCore-PulseBlasterDDSIII.h" +#include +#include +#include +#include "core/core.h" +#include "core/states.h" + +/********************************************************************* + + PulseBlasterDDSIIICommand + +*********************************************************************/ + +PulseBlasterDDSIIICommand::PulseBlasterDDSIIICommand() { + instruction=SpinCorePulseBlaster::CONTINUE; + length=0; + rx_phase_reg=0; + rx_enable=SpinCorePulseBlasterDDSIII::ANALOG_OFF; + tx_phase_reg=0; + tx_enable=SpinCorePulseBlasterDDSIII::ANALOG_OFF; + freq_reg=0; + ttls=0; + program=NULL; +} + +PulseBlasterDDSIIICommand::PulseBlasterDDSIIICommand(const PulseBlasterDDSIIICommand& orig): PulseBlasterCommand(orig) { + rx_phase_reg=orig.rx_phase_reg; + rx_enable=orig.rx_enable; + tx_phase_reg=orig.tx_phase_reg; + tx_enable=orig.tx_enable; + freq_reg=orig.freq_reg; +} + + +PulseBlasterDDSIIICommand::PulseBlasterDDSIIICommand(PulseBlasterDDSIIIProgram& p, + double _frequency, + double _rx_phase, SpinCorePulseBlasterDDSIII::analog_state _rx_enable, + double _tx_phase, SpinCorePulseBlasterDDSIII::analog_state _tx_enable, + int _ttls, double _length) + :PulseBlasterCommand(p,_ttls,_length) { + rx_phase_reg=0; + tx_phase_reg=0; + freq_reg=0; + // allocate frequency and phases only if necessary + if (_rx_enable==SpinCorePulseBlasterDDSIII::ANALOG_ON || _tx_enable==SpinCorePulseBlasterDDSIII::ANALOG_ON) { + freq_reg=p.get_frequency_regno(_frequency); + // todo: be sure, whether tx_phase is really needed for DAC_OUT_0 + // and so on... + //if (_rx_enable==SpinCorePulseBlasterDDSIII::ANALOG_ON) + rx_phase_reg=p.get_rx_phase_regno(_rx_phase); + //if (_tx_enable==SpinCorePulseBlasterDDSIII::ANALOG_ON) + tx_phase_reg=p.get_tx_phase_regno(_tx_phase); + } + rx_enable=_rx_enable; + tx_enable=_tx_enable; +} + + +int PulseBlasterDDSIIICommand::write_to_file(FILE* out, size_t indent) const { + + if (program==NULL) throw pulse_exception("PulseBlasterDDSIII: Command not associated with Program"); + fprintf(out,"%sbegin(),jump)); + break; + case SpinCorePulseBlaster::END_LOOP: + fprintf(out,"inst=\"END_LOOP\" instdata=\"%" SIZETPRINTFLETTER "\"",std::distance(program->begin(),jump)); + break; + case SpinCorePulseBlaster::JSR: + fprintf(out,"inst=\"JSR\" instdata=\"%" SIZETPRINTFLETTER "\"",std::distance(program->begin(),jump)); + break; + case SpinCorePulseBlaster::CONTINUE: + fprintf(out,"inst=\"CONTINUE\""); + break; + case SpinCorePulseBlaster::STOP: + fprintf(out,"inst=\"STOP\""); + break; + case SpinCorePulseBlaster::RTS: + fprintf(out,"inst=\"RTS\""); + break; + case SpinCorePulseBlaster::WAIT: + fprintf(out,"inst=\"WAIT\""); + break; + default: + fprintf(out,"inst=\"%d\" instdata=\"UNKNOWN\"",instruction); + } + fprintf(out," length=\"%g\"/>\n",1.0/program->internal_clock_freq*length); + return 1; +} + +/********************************************************************* + + PulseBlasterDDSIIIProgram + +*********************************************************************/ + + +PulseBlasterDDSIIIProgram::PulseBlasterDDSIIIProgram() { + internal_clock_freq=100.0e6; // Hz + phase_accuracy=360.0/pow(2,12); // degree, manual page 8 + freq_accuracy=internal_clock_freq/pow(2,28); // Hz, manual page 8 + minimum_interval=9; // in clock cycles +} + +PulseBlasterDDSIIIProgram::PulseBlasterDDSIIIProgram(const SpinCorePulseBlasterDDSIII& pbddsiii) { + internal_clock_freq=pbddsiii.clock; // Hz + minimum_interval=pbddsiii.shortest_pulse; // in clock cycles + phase_accuracy=360.0/pow(2,12); // degree, manual page 8 + freq_accuracy=internal_clock_freq/pow(2,28); // Hz, manual page 8 +} + +PulseBlasterDDSIIIProgram::PulseBlasterDDSIIIProgram(const PulseBlasterDDSIIIProgram& orig): PulseBlasterProgram(orig) { + freq_accuracy=orig.freq_accuracy; + phase_accuracy=orig.phase_accuracy; + frequency_registers=orig.frequency_registers; + rx_phase_registers=orig.rx_phase_registers; + + clear(); + const_iterator orig_i; + for (orig_i=orig.begin(); orig_i!=orig.end(); ++orig_i) { + const PulseBlasterDDSIIICommand* the_command=dynamic_cast(*orig_i); + if (the_command==NULL) { + throw SpinCorePulseBlaster_error("wrong command class or NULL pointer found in program"); + } + PulseBlasterDDSIIICommand* new_one=new PulseBlasterDDSIIICommand(*the_command); + new_one->program=this; + push_back(new_one); + } + // set correct references... + orig_i=orig.begin(); + for(iterator i=begin();i!=end();++i) { + (**i).program=this; + if ((**i).instruction==SpinCorePulseBlaster::END_LOOP || (**i).instruction==SpinCorePulseBlaster::BRANCH || (**i).instruction==SpinCorePulseBlaster::JSR) { + (**i).jump=i; + advance((**i).jump,distance(orig.begin(),(**orig_i).jump)-distance(orig.begin(),orig_i)); + } + ++orig_i; + } +} + +int PulseBlasterDDSIIIProgram::write_to_file(FILE* out, size_t indent) const { + std::string indent_string(indent,' '); + fprintf(out,"%s\n",indent_string.c_str()); + + if (!frequency_registers.empty()) { + fprintf(out,"%s ",indent_string.c_str()); + for (size_t i=0; i\n"); + } + + if (!rx_phase_registers.empty()) { + fprintf(out,"%s ",indent_string.c_str()); + for (size_t i=0;i\n"); + } + + if (!tx_phase_registers.empty()) { + fprintf(out,"%s ",indent_string.c_str()); + for (size_t i=0;i\n"); + } + + for(const_iterator i=begin();i!=end();++i) { + (**i).write_to_file(out,indent+2); + } + fprintf(out,"%s\n",indent_string.c_str()); + return 1; +} + +int PulseBlasterDDSIIIProgram::get_frequency_regno(double f) { + size_t i=0; + while (ifreq_accuracy) ++i; + if (i==frequency_registers.size()) + frequency_registers.push_back(f); + return i; +} + +int PulseBlasterDDSIIIProgram::get_rx_phase_regno(double p) { + size_t i=0; + while (iphase_accuracy) ++i; + if (i==rx_phase_registers.size()) + rx_phase_registers.push_back(p); + return i; + +} + +int PulseBlasterDDSIIIProgram::get_tx_phase_regno(double p) { + size_t i=0; + while (iphase_accuracy) ++i; + if (i==tx_phase_registers.size()) + tx_phase_registers.push_back(p); + return i; +} + +PulseBlasterCommand* PulseBlasterDDSIIIProgram::create_command(const state& the_state) { + if (typeid(the_state)!=typeid(state)) + throw pulse_exception("tried to make a state from a sequence"); + + // todo: state's defaults + + /* + There are three analog outputs on this card, but only two phase registers and two gates: + + id=0, DAC_OUT0: influenced by rx1_specified, tx_phase, rx_enable + id=1, DAC_OUT1: influenced by rx2_specified, rx_phase, rx_enable + id=2, DAC_OUT2: influenced by tx_specified, tx_phase, tx_enable + + all channels have the same frequency + + */ + + double length=the_state.length; + double state_frequency=0; + unsigned long ttls=0; + double rx_phase=0; + SpinCorePulseBlasterDDSIII::analog_state rx_enable=SpinCorePulseBlasterDDSIII::ANALOG_OFF; + double tx_phase=0; + SpinCorePulseBlasterDDSIII::analog_state tx_enable=SpinCorePulseBlasterDDSIII::ANALOG_OFF; + int id0_specified=0; + int id1_specified=0; + int id2_specified=0; + + for (state::const_iterator i=the_state.begin(); i!=the_state.end(); ++i) { + // collect states information + const ttlout* to=dynamic_cast(*i); + if (to!=NULL && to->id==0) { + ttls|=to->ttls.to_ulong(); + continue; + } + // add frequency information + const analogout* ao=dynamic_cast(*i); + if (ao!=NULL && ao->id>=0 && ao->id<=2) { + if ((rx_enable!=SpinCorePulseBlasterDDSIII::ANALOG_OFF || + tx_enable!=SpinCorePulseBlasterDDSIII::ANALOG_OFF) && + fabs(state_frequency-ao->frequency)>freq_accuracy) + throw pulse_exception("only one frequency for analog outputs possible"); + state_frequency=ao->frequency; + double phase=ao->phase; + if (phase < 0 || phase >= 360.0) { + phase=fmod(phase, 360.0); + if (phase<0) {phase+=360.0;} + } + assert(phase>=0 && phase<360.0); + switch(ao->id) { + case 0: + if (id0_specified) throw pulse_exception("rx channel (DAC_OUT_0) channel already set"); + //if (rx_enable!=SpinCorePulseBlasterDDSIII::ANALOG_OFF) throw pulse_exception("rx channel already set"); + // rx is identified with channel 0 + if (id2_specified && fabs(phase-tx_phase)>phase_accuracy) fprintf(stderr, "WARNING from PulseBlaster DDSIII: redefining phase of TX (DAC_OUT_2) channel\n"); + tx_phase=phase; + rx_enable=SpinCorePulseBlasterDDSIII::ANALOG_ON; + id0_specified=1; + break; + case 1: + if (id1_specified) throw pulse_exception("rx channel (DAC_OUT_1) channel already set"); + // tx is identified with channel 1 + rx_phase=phase; + rx_enable=SpinCorePulseBlasterDDSIII::ANALOG_ON; + id1_specified=1; + break; + case 2: + if (id2_specified || tx_enable==SpinCorePulseBlasterDDSIII::ANALOG_ON) throw pulse_exception("tx channel (DAC_OUT_2) already set"); + // tx is identified with channel 1 + if (id0_specified && fabs(phase-tx_phase)>phase_accuracy) fprintf(stderr, "WARNING from PulseBlaster DDSIII: redefining phase of RX (DAC_OUT_0) channel\n"); + tx_phase=phase; + tx_enable=SpinCorePulseBlasterDDSIII::ANALOG_ON; + id2_specified=1; + break; + } + continue; + } + } +#if SP_DEBUG + fprintf(stderr, "rx phase=%f, tx phase=%f\n",rx_phase, tx_phase); +#endif + if (!id0_specified && rx_enable==SpinCorePulseBlasterDDSIII::ANALOG_ON) + fprintf(stderr, "WARNING from PulseBlaster DDSIII: RF Output enabled on DAC_OUT_0\n"); + if (!id1_specified && rx_enable==SpinCorePulseBlasterDDSIII::ANALOG_ON) + fprintf(stderr, "WARNING from PulseBlaster DDSIII: RF Output enabled on DAC_OUT_1\n"); + + return new PulseBlasterDDSIIICommand(*this,state_frequency,rx_phase,rx_enable,tx_phase,tx_enable,ttls,length); +} + +PulseBlasterCommand* PulseBlasterDDSIIIProgram::create_command(const PulseBlasterCommand* orig) { + PulseBlasterDDSIIICommand* new_command=NULL; + if (orig==NULL) { + new_command=new PulseBlasterDDSIIICommand(); + new_command->program=this; + new_command->length=minimum_interval; + } + else { + const PulseBlasterDDSIIICommand* commandDDSIII=dynamic_cast(orig); + if (commandDDSIII==NULL) throw pulse_exception("wrong PulseBlasterCommand class in PulseBlasterDDSIIIProgram method"); + new_command=new PulseBlasterDDSIIICommand(*commandDDSIII); + } + return new_command; +} + + +/********************************************************************* + + SpinCorePulseBlasterDDSIII + +*********************************************************************/ + +SpinCorePulseBlasterDDSIII::SpinCorePulseBlasterDDSIII(int the_id, double the_clock, unsigned int _sync_mask): SpinCorePulseBlaster(10,the_clock) { + sync_mask=_sync_mask; + ttl_device_id=the_id; + freq_regno=16; + phase_regno=16; +} + +void SpinCorePulseBlasterDDSIII::set_registers(int device, unsigned int register_size, double multiplier, const std::vector& values) { + if (values.size()>register_size) + throw SpinCorePulseBlaster_error("to many data for registers"); + unsigned char* data=(unsigned char*)malloc(4*register_size); + if (data==NULL) { + throw SpinCorePulseBlaster_error("could not allocate memory for register data"); + } + + for (unsigned int reg=0; regreg) { + double temp=values[reg]*multiplier; + if (temp<0 || temp>pow(2,32)) + throw SpinCorePulseBlaster_error("invalid data value"); + unsigned int val=(unsigned int)temp; + data[reg*4]=(val&0xff000000)>>24; + data[reg*4+1]=(val&0xff0000)>>16; + data[reg*4+2]=(val&0xff00)>>8; + data[reg*4+3]=val&0xff; + } + else { + data[reg*4]=0; + data[reg*4+1]=0; + data[reg*4+2]=0; + data[reg*4+3]=0; + } + } + write_register(0,0); // dev reset + write_register(2,4); // bytes per word + write_register(3,device); // dev to program + write_register(4,0); //reset address counter + write_data(data,4*register_size); + free(data); +} + +void SpinCorePulseBlasterDDSIII::set_phase_registers(std::vector rx_phases, std::vector tx_phases) { + if (tx_phases.empty()) + set_registers(2,phase_regno,1.0,std::vector(1,0.0)); + else + set_registers(2,phase_regno,pow(2,32)/360.0,tx_phases); + if (rx_phases.empty()) + set_registers(3,phase_regno,1.0,std::vector(1,0.0)); + else + set_registers(3,phase_regno,pow(2,32)/360.0,rx_phases); +} + +void SpinCorePulseBlasterDDSIII::set_frequency_registers(const std::vector& values) { + if (values.empty()) + set_registers(1,freq_regno,pow(2,32)/clock,std::vector(1,1e6)); + else + set_registers(1,freq_regno,pow(2,32)/clock,values); +} + +void SpinCorePulseBlasterDDSIII::write_command(unsigned char* data, const PulseBlasterCommand& command) { + //void PulseBlasterDDSIII::append_command(std::string& data, int freqreg, int phasereg_tx, int output_tx, int phasereg_rx, int output_rx, int flags, opcode inst, int inst_data, double length) + const PulseBlasterDDSIIICommand* commandDDSIII=dynamic_cast(&command); + if (commandDDSIII==NULL) + throw SpinCorePulseBlaster_error("found wrong command class in PulseBlasterProgram"); + + if (command.program==NULL) throw SpinCorePulseBlaster_error("Command not associated with Program"); + int inst_data=0; + switch(command.instruction) { + case SpinCorePulseBlaster::CONTINUE: + case SpinCorePulseBlaster::STOP: + case SpinCorePulseBlaster::WAIT: + case SpinCorePulseBlaster::RTS: + // no parameter + break; + case SpinCorePulseBlaster::LOOP: + inst_data=command.loop_count-1; + break; + case SpinCorePulseBlaster::LONG_DELAY: + inst_data=command.loop_count-2; + break; + case SpinCorePulseBlaster::BRANCH: + case SpinCorePulseBlaster::END_LOOP: + case SpinCorePulseBlaster::JSR: + inst_data=std::distance(command.program->begin(),command.jump); + break; + default: + throw SpinCorePulseBlaster_error("instruction code not known"); + } + unsigned int delay=(unsigned int)command.length-3; + + // Output, Control Word 1st Byte + data[0]=(commandDDSIII->freq_reg&0x0f)<<4|(commandDDSIII->tx_phase_reg&0x0f); + // Output, Control Word 2nd Byte + data[1]=((commandDDSIII->rx_phase_reg&0x0f)<<4)|((command.ttls&0x300)>>8); + if (commandDDSIII->rx_enable==ANALOG_OFF) data[1]|=0x04; + if (commandDDSIII->tx_enable==ANALOG_OFF) data[1]|=0x08; + // Output, Control Word 3rd Byte + data[2]=command.ttls&0xff; + // Data Field 1st Byte + data[3]=(inst_data&0x0ff000)>>12; + // Data Field 2nd Byte + data[4]=(inst_data&0xff0)>>4; + // Data Field 3rd Byte and opcode + data[5]=(inst_data&0xf)<<4|(command.instruction&0xf); + // Delay Count 1st Byte + data[6]=(delay&0xff000000)>>24; + // Delay Count 2nd Byte + data[7]=(delay&0xff0000)>>16; + // Delay Count 3rd Byte + data[8]=(delay&0xff00)>>8; + // Delay Count 4th Byte + data[9]=(delay&0xff); + /* *** BUG-FIX (ToDo: clean solution) *** + there is a nasty error in pulseblaster, affecting all states with 4th byte + equal 0xff and delay >255. In this case reduce state for 10ns. + */ + if (data[9]==0xff && delay>0xff) + data[9]=0xfe; +} + +int SpinCorePulseBlasterDDSIII::write_to_device(const PulseBlasterDDSIIIProgram& p) { + set_frequency_registers(p.frequency_registers); + set_phase_registers(p.rx_phase_registers,p.tx_phase_registers); + + std::string program; + //Begin pulse program + for (PulseBlasterDDSIIIProgram::const_iterator c=p.begin(); c!=p.end();++c) { + char command[10]; + write_command((unsigned char*)command,**c); + program.append(command, (size_t)10); + } + + set_program(program); + + // End of programming registers and pulse program + set_initialized(); + return 1; +} + +PulseBlasterProgram* SpinCorePulseBlasterDDSIII::create_program(state& exp) { + PulseBlasterDDSIIIProgram* prog=new PulseBlasterDDSIIIProgram(); + // some initialisiation... + prog->append_sequence(exp); + // some reset code ... + return prog; +} + +void SpinCorePulseBlasterDDSIII::run_pulse_program(const PulseBlasterProgram& p) { + const PulseBlasterDDSIIIProgram* prog=dynamic_cast(&p); + if (prog==NULL) + throw SpinCorePulseBlaster_error("found wrong program class in SpinCorePulseBlasterDDSIII method"); + write_to_device(*prog); + start(); +} + +void SpinCorePulseBlasterDDSIII::wait_till_end() { + + double waittime=duration-time_running.elapsed(); + double timeout=(waittime>10)?(waittime*0.01):0.05; +#if SP_DEBUG + fprintf(stderr,"waiting while DDSIII pulseprogram running (%f s of %f s)...", waittime, duration); +#endif + while (waittime>-timeout && core::term_signal==0) { + if (waittime<1e-2) + waittime=1e-2; + else + waittime*=0.9; +#if SP_DEBUG + fprintf(stderr,"sleeping for %g seconds...",waittime); + fflush(stderr); +#endif + timespec nanosleep_time; + nanosleep_time.tv_sec=(time_t)floor(waittime); + nanosleep_time.tv_nsec=(long)ceil((waittime-nanosleep_time.tv_sec)*1e9); + nanosleep(&nanosleep_time,NULL); + waittime=duration-time_running.elapsed(); + } + if (core::term_signal!=0) { + //reset pulseblaster + stop(); + reset_flags(0); + } +#if SP_DEBUG + fprintf(stderr,"done\n"); +#endif +} + + diff --git a/drivers/SpinCore-PulseBlasterDDSIII/SpinCore-PulseBlasterDDSIII.h b/drivers/SpinCore-PulseBlasterDDSIII/SpinCore-PulseBlasterDDSIII.h new file mode 100644 index 0000000..c6c10b3 --- /dev/null +++ b/drivers/SpinCore-PulseBlasterDDSIII/SpinCore-PulseBlasterDDSIII.h @@ -0,0 +1,202 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + +****************************************************************************/ +#ifndef SPINCOREPULSEBLASTERDDSIII_H +#define SPINCOREPULSEBLASTERDDSIII_H + +#include "drivers/frequgen.h" +#ifndef SPINCORE_PULSEBLASTER_H +#include "drivers/SpinCore-PulseBlaster/SpinCore-PulseBlaster.h" +#include "drivers/SpinCore-PulseBlaster/PulseBlasterProgram.h" +#endif +#include "core/states.h" +#include "core/stopwatch.h" +#include +#include +#include +#include +#include +#include + +/** + \defgroup SpinCorePulseblasterDDSIII SpinCore PulseblasterDDSIII + \ingroup drivers + \brief classes concerning SpinCore PulseBlasterDDSIII + @{ +*/ + +class PulseBlasterDDSIIIProgram; +class PulseBlasterDDSIIICommand; + +class SpinCorePulseBlasterDDSIII: public frequgen, public SpinCorePulseBlaster { + + /** + number of frequency registers + */ + unsigned int freq_regno; + /** + number of phase registers + */ + unsigned int phase_regno; + + + void set_registers(int device, unsigned int register_size, double divider, const std::vector& values); + + void set_phase_registers(std::vector rx_phases, std::vector tx_phases); + + void set_frequency_registers(const std::vector& values); + + /** + makes a machine command word from PulseBlasterDDSIIICommand object + */ + void write_command(unsigned char* data, const PulseBlasterCommand& command); + + /** + send the entire program to the device + */ + int write_to_device(const PulseBlasterDDSIIIProgram& program); + + public: + + typedef enum {ANALOG_OFF=0, ANALOG_ON=1} analog_state; + + analog_state rx_analog; + + analog_state tx_analog; + + /** + initialises the PulseblasterDDSIII of Spincore + */ + SpinCorePulseBlasterDDSIII(int the_id=0, double the_clock=1e8, unsigned int _sync_mask=0); + + /** + sets the frequency for simple frequency generator use + if set, by default the analog output is set to rx and tx channel with 90 degree shift + */ + virtual void set_frequency(state& s) {frequency=10e6;} + + /** + sets the frequency for simple frequency generator use + if set, by default the analog output is set to rx and tx channel with 90 degree shift + */ + virtual void set_frequency(double f) {frequency=f;} + + /** + \brief programm the pulseblaster with the subset of states known to him + + the pulse program is ended with a wait command, that resets all channels and a stop command that stops the pulseblaster in this state + */ + virtual PulseBlasterProgram* create_program(state& the_states); + + /** + run a PulseBlaster Program + */ + virtual void run_pulse_program(const PulseBlasterProgram& p); + + /** + provide fake substitution for get_status + */ + inline int get_status() { + fprintf(stderr,"PulseBlaster DDSIII does not support status readback! (assuming running state)\n"); + return 4; + } + + /** + status queries not working, workaround + */ + void wait_till_end(); + + /** + destructor (close device) + */ + virtual ~SpinCorePulseBlasterDDSIII() {}; +}; + +/** + \brief holds the complete and detailed initialisation of the device + */ +class PulseBlasterDDSIIIProgram: public PulseBlasterProgram { +public: + double freq_accuracy; + double phase_accuracy; + + /// here keep track of the programed frequencies(Hz) + std::vector frequency_registers; + /// here keep track of the programed RX phases (degree) + std::vector rx_phase_registers; + /// here keep track of the programed TX phases (degree) + std::vector tx_phase_registers; + + /// finds or allocates frequency given in Hz + int get_frequency_regno(double f); + /// finds or allocates rx phase given in degree + int get_rx_phase_regno(double p); + /// finds or allocates tx phase given in degree + int get_tx_phase_regno(double p); + + /// standard allocator sets only accuracy + PulseBlasterDDSIIIProgram(); + + /// copy constructor has to correct the pointers and references in the command list + PulseBlasterDDSIIIProgram(const PulseBlasterDDSIIIProgram& orig); + + PulseBlasterDDSIIIProgram(const SpinCorePulseBlasterDDSIII& pb); + /** + create a new continue command, that merges all state_atom features + */ + virtual PulseBlasterCommand* create_command(const state& the_state); + /** + create a new continue command by copying or a default one + */ + virtual PulseBlasterCommand* create_command(const PulseBlasterCommand* c=NULL); + + /// write all configuration to a file to xml + int write_to_file(FILE* out, size_t indent=0) const; +}; + +/** + \brief parameters for each pulseblaster command +*/ +class PulseBlasterDDSIIICommand: public PulseBlasterCommand { +public: + /// register for frequency + int freq_reg; + /// register for rx phase + int rx_phase_reg; + /// enable rx analog output + SpinCorePulseBlasterDDSIII::analog_state rx_enable; + /// register for tx phase + int tx_phase_reg; + /// enable tx analog output + SpinCorePulseBlasterDDSIII::analog_state tx_enable; + /// full ttl state + + /// the simple constructor + PulseBlasterDDSIIICommand(); + + /// the copy constructor + PulseBlasterDDSIIICommand(const PulseBlasterDDSIIICommand&); + + /// produces a simple state + PulseBlasterDDSIIICommand(PulseBlasterDDSIIIProgram& p, + double frequency, + double rx_phase, SpinCorePulseBlasterDDSIII::analog_state rx_enable, + double tx_phase, SpinCorePulseBlasterDDSIII::analog_state tx_enable, + int ttls, double length); + + /// write the command to a give file as xml tag + int write_to_file(FILE* out, size_t indent=0) const; +}; + +/** + \brief the driver implementation for PulseBlasterDDSIII! + uses the classes PulseBlasterDDSIIIProgram and PulseBlasterDDSIIICommand +*/ + +/** + @} +*/ +#endif diff --git a/drivers/SpinCore-PulseBlasterDDSIII/test.cpp b/drivers/SpinCore-PulseBlasterDDSIII/test.cpp new file mode 100644 index 0000000..a910b4c --- /dev/null +++ b/drivers/SpinCore-PulseBlasterDDSIII/test.cpp @@ -0,0 +1,162 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + +****************************************************************************/ +#include "SpinCore-PulseBlasterDDSIII.h" +#include "core/xml_states.h" +#include +#include +#include +/* + run it once +*/ + +#if 0 +int main(int argc, char** argv) { + + if (argc<4) { + fprintf(stderr,"%s frequency_MHz pulselength_microsec pulselength_180deg_microsec [startphase_degree]\n",argv[0]); + return 1; + } + + double freq_MHz=atof(argv[1]); + double pulselength_us=atof(argv[2]); + double pulselength_180deg_microsec=atof(argv[3]); + double phase_deg=0; + if (argc>4) + phase_deg=atof(argv[4]); + if (pulselength_us>100) { + fprintf(stderr,"pulselength resticted to 100 microseconds\n"); + return 1; + } + + SpinCorePulseBlasterPlus pb; + pb.set_frequency(freq_MHz); + pb.single_pulse_and_180deg_program((50.0+M_PI*phase_deg/180.0)/(1.0e6*freq_MHz)-1.0e-5, + pulselength_us*1e-6, + pulselength_180deg_microsec*1e-6, + 1e-3); + return 0; +} +#endif + +#if 0 +int main () { + + PulseBlasterPlusProgram p; + PulseBlasterPlusCommand loop(p,1e6,90,0,0,0,0x0,1e-6); + loop.instruction=2; + loop.loop_count=2; + p.push_back(loop); + p.push_back(PulseBlasterPlusCommand(p,1e6,90,0,0,0,255,1e-6)); + PulseBlasterPlusCommand c_loop_end(p,1e6,90,0,0,0,0x0,1e-6); + c_loop_end.instruction=3; + c_loop_end.jump=p.begin(); + p.push_back(c_loop_end); + + PulseBlasterPlusCommand c_stop(p,1e6,90,1,0,1,0x0,1e-6); + c_stop.instruction=1; + p.push_back(c_stop); + + p.write_to_file(stdout); + try { + SpinCorePulseBlasterPlus().run_program(p); + } + catch(pulse_exception e) { + fprintf(stderr,e.c_str()); + } + return 0; +} +#endif + +#if 0 +int main() { + + state s; + s.length=1e-3; + ttlout* t=new ttlout; + t->ttls=2; + s.push_back(t); + t=new ttlout; + t->ttls=5; + s.push_back(t); + analogout* a=new analogout; + a->id=0; + a->frequency=1e6; + a->phase=90; + s.push_back(a); + a=new analogout; + a->id=1; + a->frequency=1e6; + a->phase=0; + s.push_back(a); + + try { + PulseBlasterPlusProgram* p=SpinCorePulseBlasterPlus().create_program(s); + if (p==NULL) { + fprintf(stderr,"got a NULL pointer from SpinCorePulseBlasterPlus::create_program"); + return 1; + } + p->write_to_file(stdout); + delete p; + } + catch (pulse_exception e) { + fprintf(stderr,"%s\n",e.c_str()); + return 1; + } + return 0; +} +#endif + + +#if 1 + +int main(int argc, char** argv) { + if (argc!=2) { + fprintf(stderr,"%s filename",argv[0]); + return 1; + } + std::string filename(argv[1]); + xml_state_reader reader; + state_atom* state_file=reader.read_from_file(filename); + if (state_file==NULL) { + fprintf(stderr,"reading was not successfull\n"); + return 1; + } + state* s=dynamic_cast(state_file); + if (s==NULL) { + fprintf(stderr,"File does not contain a state or state sequence!"); + delete state_file; + return 1; + } + + try { + SpinCorePulseBlasterDDSIII pb; + PulseBlasterProgram* p=pb.create_program(*s); + if (p==NULL) { + fprintf(stderr,"got a NULL pointer from SpinCorePulseBlasterPlus::create_program"); + return 1; + } + delete p; + //p->write_to_file(stdout); + for (int i=0; i<1;++i) { + pb.SpinCorePulseBlaster::run_pulse_program(*s); + pb.wait_till_end(); + } + } + catch (pulse_exception e) { + delete s; + fprintf(stderr,"%s\n",e.c_str()); + return 1; + } + catch (SpinCorePulseBlaster_error e) { + delete s; + fprintf(stderr,"%s\n",e.c_str()); + return 1; + } + delete s; + return 0; +} +#endif diff --git a/drivers/Tecmag-DAC20/AD1862.pdf b/drivers/Tecmag-DAC20/AD1862.pdf new file mode 100644 index 0000000..f37f929 Binary files /dev/null and b/drivers/Tecmag-DAC20/AD1862.pdf differ diff --git a/drivers/Tecmag-DAC20/DAC20.cpp b/drivers/Tecmag-DAC20/DAC20.cpp new file mode 100644 index 0000000..a251a2f --- /dev/null +++ b/drivers/Tecmag-DAC20/DAC20.cpp @@ -0,0 +1,230 @@ +/** + * \file + * \brief Implementation of the Driver for the DAC20 + * Digital to Analog Converter Board + * + * This board was first used in the PFG, and gets now + * adopted by the Field-Cycling spectrometers. + * + * The board inverts all digital inputs. + * This driver partially honors this. + * Especially the data input is not yet inverted. + * -# Inverting the data line is equivalent to inverting + * the value and add/substracting one. + * -# There are different versions of the board around, + * where one board "undoes" the inversion by having an + * inverting op-amp after the DAC. + * -# The digital units have to be calibrated to physical + * units anyway. A sign doesn't hurt much. + * + * This is still a current discussion topic and might + * change. + */ + +#include "DAC20.h" +#include +#include +#include +#include +#include +#include + +using std::reverse; +using std::cout; +using std::vector; +#ifndef TIMING +#define TIMING 9e-8 +#endif + +// The bit depth of the DAC +#define DAC_BIT_DEPTH 20 + +// The channel configuration +#define DATA_BIT 18//18 +#define CLK_BIT 16//16 + +DAC20::DAC20(int myid): id(myid) { + dac_value = 0; + set_latch_bit(17); +} + +DAC20::~DAC20() {} + +// This sets the dac_value +void DAC20::set_dac(signed dw) { + dac_value = dw; +} + +void DAC20::set_latch_bit(int le_bit) +{ + latch_bit = le_bit; +} + +// This sets the DAC +void DAC20::set_dac(state& experiment) { + + state_sequent* exp_sequence=dynamic_cast(&experiment); + if (exp_sequence == NULL) + // is a very state on top level, todo: change interface + throw pfg_exception( "cannot work on a single state, sorry (todo: change interface)"); + else { + for(state_sequent::iterator child_state = exp_sequence->begin(); child_state != exp_sequence->end(); ++child_state) + set_dac_recursive(*exp_sequence, child_state); +// std::cout << "first state"<< std::endl; + // Set DAC to 0 at start of experiment + set_dac_to_zero(exp_sequence, exp_sequence->begin()); + // And at the end of the experiment + set_dac_to_zero(exp_sequence, exp_sequence->end()); + } +} + +void DAC20::set_dac_to_zero(state_sequent* exp_sequence, state::iterator where) +{ + state s(TIMING); + ttlout* le=new ttlout(); + le->id=0; + s.push_front(le); + state_sequent* rep_sequence=new state_sequent(); + rep_sequence->repeat=DAC_BIT_DEPTH; + le->ttls = 0 + ( 1 << CLK_BIT ); + rep_sequence->push_back(s.copy_new()); + le->ttls = 0 ; + rep_sequence->push_back(s.copy_new()); + exp_sequence->insert(where, rep_sequence); + //read in the word (41st pulse) + le->ttls=0; + exp_sequence->insert(where, s.copy_new()); + // 42nd pulse + // // the state should be 2ms long + // s.length = 2e-3-41*TIMING; + le->ttls= ( 1 << latch_bit ); + exp_sequence->insert(where, s.copy_new()); +} + +// This loops recursive through the state tree + +void DAC20::set_dac_recursive(state_sequent& the_sequence, state::iterator& the_state) { + + state_sequent* a_sequence = dynamic_cast(*the_state); + // Am I a sequence? Yes? Go one sequence further + if (a_sequence != NULL) { + for(state_sequent::iterator child_state = a_sequence->begin(); child_state != a_sequence->end(); ++child_state) + set_dac_recursive(*a_sequence, child_state); + } + // I am not a sequence, but a state + else { + state* this_state = dynamic_cast(*the_state); + if (this_state == NULL) + throw pfg_exception( "state_atom in state_sequent not expected"); + analogout* PFG_aout = NULL; + // find an analogout section with suitable id + state::iterator pos = this_state->begin(); + while(pos!=this_state->end()) { // state members loop + analogout* aout = dynamic_cast(*pos); // initialize new analogout + // This is for me, analogout is != NULL (there is an analogout) and has my ID + if (aout!=NULL && aout->id == id) { + if (PFG_aout == NULL) { + // save the informations + PFG_aout = aout; + } + // there is no place for me here + else { + throw pfg_exception( "found another DAC section, ignoring"); + delete aout; + } + // remove the analog out section + this_state->erase(pos++); + } + else { + ++pos; + } + } // state members loop + + if (PFG_aout != NULL) { // state modifications + //std::cout<<"found a analog out section, value="<dac_value<length < TIMING*41.0) + throw pfg_exception( "time is too short to save DAC information"); + else { + // copy of original state + state* register_state = new state(*this_state); + ttlout* register_ttls = new ttlout(); + register_ttls->id = 0; + register_state->length = TIMING; + register_state->push_back(register_ttls); + if (PFG_aout->dac_value > (pow(2.0, int(DAC_BIT_DEPTH-1))-1) ) + throw pfg_exception("dac_value too high"); + if ( abs(PFG_aout->dac_value) > pow(2.0, int(DAC_BIT_DEPTH-1)) ) + throw pfg_exception("dac_value too low"); + // now, insert the ttl information + vector dac_word; + for (int j = 0; j < DAC_BIT_DEPTH ; j++) { + int bit = PFG_aout->dac_value & 1; + // // invert the bit + // bit = (bit == 0); + dac_word.push_back(bit); + //cout << dac_word[j]; + PFG_aout->dac_value >>= 1; + } + // need one clock cycle to read in bit + // latch enable (LE) should always be high while doing so + // except for the last bit + // reverse the bit pattern + reverse(dac_word.begin(), dac_word.end()); + + // do run length encoding, grouping same bit values in loops + int last_seen_bit=dac_word[0]; + int last_seen_bit_count=1; + for (int i = 1; i < DAC_BIT_DEPTH+1; i++) { + if (i==DAC_BIT_DEPTH || last_seen_bit!=dac_word[i]) { + // so we have to write the bits + // either because the previous were different or we are finished + if (last_seen_bit_count>1) { + // insert a loop + state_sequent* rep_sequence=new state_sequent(); + rep_sequence->repeat=last_seen_bit_count; + register_ttls->ttls = (1 << DATA_BIT)*last_seen_bit + (1 << CLK_BIT) + (1 << latch_bit); + rep_sequence->push_back(register_state->copy_new()); + register_ttls->ttls = (1 << DATA_BIT)*last_seen_bit + (1 << latch_bit); + rep_sequence->push_back(register_state->copy_new()); + the_sequence.insert(the_state, rep_sequence); + } else { + // no loop necessary, insert two states + register_ttls->ttls = (1 << DATA_BIT)*last_seen_bit + (1 << CLK_BIT) + (1 << latch_bit); + the_sequence.insert(the_state, register_state->copy_new()); + register_ttls->ttls = (1 << DATA_BIT)*last_seen_bit + (1 << latch_bit); + the_sequence.insert(the_state, register_state->copy_new()); + } + // reset counter and bits if we are not finished + if (ittls = 0; + the_sequence.insert(the_state,register_state->copy_new()); + + // shorten the remaining state + // and add LE high to this state + ttlout* ttls=new ttlout(); + // 42nd pulse + this_state->length -= TIMING*41; + ttls->ttls = 1 << latch_bit; + this_state->push_front(ttls); + + // cleanup + delete register_state; + delete PFG_aout; + } // state was long enough to work on + } + else { + ttlout* le_ttls=new ttlout(); + le_ttls->ttls = 1 << latch_bit; + this_state->push_back(le_ttls); + } + // end of state modifications + } // I was a state +} diff --git a/drivers/Tecmag-DAC20/DAC20.h b/drivers/Tecmag-DAC20/DAC20.h new file mode 100644 index 0000000..c8809a3 --- /dev/null +++ b/drivers/Tecmag-DAC20/DAC20.h @@ -0,0 +1,47 @@ +/* + Markus Rosenstihl 2005 Nov + */ +#ifndef DAC20_H +#define DAC20_H + +#include "core/states.h" +#include "drivers/pfggen.h" + +/** + * \ingroup drivers + */ +class DAC20: public GenericDAC { +protected: + /// The channel for the latch signal + int latch_bit; + +public: + int id; + // default constructor + DAC20(int myid=0); + virtual void set_dac(signed dw); +// virtual void set_dac_ttls(signed value); + + void set_latch_bit(int le_bit); + + /** + inserts necessary serial data transmission to set the dac, at the end of experiment reset the dac + + assumes, the root sequence is not repeated, because the reset sequence is appended to the root sequence! + */ + virtual void set_dac(state& experiment); + + // destructor + virtual ~DAC20(); + + private: + void set_dac_recursive(state_sequent& the_sequence, state::iterator& the_state); + void set_dac_to_zero(state_sequent* exp_sequence, state::iterator where); +}; + +/*class pfg_exception: public std::string { +public: + pfg_exception(const std::string& s): std::string(s){} +}; +*/ +#endif diff --git a/drivers/Tecmag-DAC20/DAC_test.cpp b/drivers/Tecmag-DAC20/DAC_test.cpp new file mode 100755 index 0000000..ec04df0 --- /dev/null +++ b/drivers/Tecmag-DAC20/DAC_test.cpp @@ -0,0 +1,34 @@ +#include +#include "core/xml_states.h" +#include "drivers/Tecmag-DAC20/DAC20.h" + + +int main(int argc, char** argv) + +{ + int return_result=0; + try { + + signed dac_value=2; + state_atom* a=xml_state_reader().read_from_file("/dev/stdin"); + if (a==NULL) { + fprintf(stderr, "%s: could not read a state tree from stdin\n", argv[0] ); + return 1; + } + state* a_state=dynamic_cast(a); + if (a_state==NULL) { + fprintf(stderr, "%s: did not find a state in input\n", argv[0] ); + delete a; + return 1; + } + DAC20().set_dac(dac_value); + DAC20().set_dac(*a_state); + xml_state_writer().write_states(stdout,*a,1); + delete a; + return 0; + } + catch(pfg_exception pfg_e){ + fprintf(stderr,"DAC20: %s\n",pfg_e.what()); + return_result=1; + } +} diff --git a/drivers/Tecmag-DAC20/DAC_test.xml b/drivers/Tecmag-DAC20/DAC_test.xml new file mode 100755 index 0000000..24b91b3 --- /dev/null +++ b/drivers/Tecmag-DAC20/DAC_test.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/drivers/Tecmag-DAC20/Dac20.pdf b/drivers/Tecmag-DAC20/Dac20.pdf new file mode 100644 index 0000000..f24d098 Binary files /dev/null and b/drivers/Tecmag-DAC20/Dac20.pdf differ diff --git a/drivers/Tecmag-DAC20/Makefile b/drivers/Tecmag-DAC20/Makefile new file mode 100644 index 0000000..6c15619 --- /dev/null +++ b/drivers/Tecmag-DAC20/Makefile @@ -0,0 +1,27 @@ +CXX=g++ +CXXFLAGS=-g -O0 -Wall -Wshadow -pedantic +CXXCPPFLAGS=-I. -I../.. +LDFLAGS=-lexpat -lxerces-c + +.PHONY: all clean install + +all: DAC20.o DAC_test.exe + +../../tools/add_endline.exe: ../../tools/add_endline.cpp + $(CXX) $< -o $@ + +../../core/core.a: + $(MAKE) -C ../../core core.a + +clean: ../../tools/add_endline.exe + for f in DAC20.cpp DAC20.h DAC_test.cpp; do $< $$f; done + rm -f *.exe *.o *~ core.* + +DAC20.o: DAC20.cpp DAC20.h + $(CXX) -c $(CXXFLAGS) $(CXXCPPFLAGS) -o $@ $< + +DAC_test.o: DAC_test.cpp + $(CXX) -c $(CXXFLAGS) $(CXXCPPFLAGS) -o $@ $< + +DAC_test.exe: DAC_test.o DAC20.o ../../core/core.a + $(CXX) -o $@ $^ $(LDFLAGS) diff --git a/drivers/Tecmag-DAC20/xmltest.xml b/drivers/Tecmag-DAC20/xmltest.xml new file mode 100644 index 0000000..27f4da0 --- /dev/null +++ b/drivers/Tecmag-DAC20/xmltest.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/drivers/TiePie-HS3/HS3test.cpp b/drivers/TiePie-HS3/HS3test.cpp new file mode 100644 index 0000000..a68911a --- /dev/null +++ b/drivers/TiePie-HS3/HS3test.cpp @@ -0,0 +1,58 @@ +#include +#include "tiepie.h" +#if 0 +# include "tiepie.cpp" +#endif + +#include "TiePie-HS3.h" + +int test1() { + word retval; + dword serialno; + if (1!=OpenDLL(dtHandyscope3)) { + printf("failed loading dlls\n"); + return 1; + } + + retval=InitInstrument(0); + if (retval!=NO_ERROR) { + printf("failed to initialize the instrument, code=%d\n", retval); + CloseDLL(); + return 1; + } + + retval=GetSerialNumber(&serialno); + if (retval!=NO_ERROR) { + printf("could not read serial number, code=%d\n", retval); + ExitInstrument(); + CloseDLL(); + return 1; + } + + printf("serial no %d\n", serialno); + + ExitInstrument(); + CloseDLL(); + return 0; +} + + +int test2() { + + try { + TiePieHS3 adc; + dword serialno; + word retval=GetSerialNumber(&serialno); + if (retval!=NO_ERROR) throw ADC_exception("could not get serialno"); + printf("serial number %d\n", serialno); + } + catch (ADC_exception e) { + printf("ADC exception: %s\n", e.c_str()); + return 1; + } + return 0; +} + +int main() { + return test2(); +} diff --git a/drivers/TiePie-HS3/Makefile b/drivers/TiePie-HS3/Makefile new file mode 100644 index 0000000..f91d3f3 --- /dev/null +++ b/drivers/TiePie-HS3/Makefile @@ -0,0 +1,39 @@ +############################################################################# +# +# Author: Achim Gaedke +# Created: June 2004 +# +############################################################################# + +CXX=g++ +CXXFLAGS=-g -O0 -I. -I../.. +LDFLAGS= + +.PHONY: all clean install + +all: TiePie-HS3.a + +../../tools/add_endline.exe: ../../tools/add_endline.cpp + $(CXX) $< -o $@ + +TiePie-HS3.o: TiePie-HS3.cpp TiePie-HS3.h + $(CXX) -c $(CXXFLAGS) $< -o $@ + +tiepie.o: tiepie.cpp tiepie.h + $(CXX) -c $(CXXFLAGS) $< -o $@ + +TiePie-HS3.a: TiePie-HS3.o tiepie.o + $(AR) r $@ $^ + +HS3test.exe: HS3test.cpp TiePie-HS3.a ../../core/core.a + $(CXX) $(CXXFLAGS) $^ -o $@ + +../../core/core.a: + $(MAKE) -C ../../core core.a + +clean: + for f in *.cpp *.h; do ../../tools/add_endline.exe $$f; done; \ +rm -f *.o *~ \#* *.stackdump *.a *.exe + +install: + install *.hex *.dll $(PREFIX) diff --git a/drivers/TiePie-HS3/TiePie-HS3.cpp b/drivers/TiePie-HS3/TiePie-HS3.cpp new file mode 100644 index 0000000..d7b9402 --- /dev/null +++ b/drivers/TiePie-HS3/TiePie-HS3.cpp @@ -0,0 +1,217 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + +****************************************************************************/ +#include "drivers/TiePie-HS3/TiePie-HS3.h" +#include "tiepie.h" +#include +#include +#include "core/xml_states.h" + +TiePieHS3::TiePieHS3() { + if (1!=OpenDLL(dtHandyscope3)){ + throw ADC_exception("could not find driver dll for HS3!"); + } + if (NO_ERROR!=InitInstrument(0)) { + ADC_exception("HS3 not available!"); + } + unsigned int serialno=0; + unsigned short int retval=GetSerialNumber(&serialno); + //fprintf(stderr, "sizeof(unsigned int)=%d, sizeof(unsigned short int)=%d\n", sizeof(serialno), sizeof(retval)); + if (NO_ERROR!=retval) { + char buffer[256]; + snprintf(buffer, sizeof(buffer), "could not get device's serial number: GetSerialNumber returned %d", retval); + throw ADC_exception(std::string(buffer)); + } + fprintf(stderr, "successfully initialized HS3, SN=%d\n",serialno); + trigger_line_id=0; + trigger_line_mask=1<<2; +} + +void TiePieHS3::sample_after_external_trigger(double _rate, size_t _samples, double _sensitivity ,size_t _resolution) { + ADC_Abort(); + /* set autoranging off */ + SetAutoRanging(Ch1,0); + SetAutoRanging(Ch2,0); + + samples=_samples; + if (samples==0 || _rate<=0) return; + unsigned short int retval; + //samples=1024*1024*1024; // impossible value... provoke error + retval=SetRecordLength(samples); + if (0!=retval) { + char buffer[256]; + snprintf(buffer, sizeof(buffer), "could not set record length: SetRecrodLength(%d) returned %d", samples, retval); + throw ADC_exception(std::string(buffer)); + } + if (0!=SetPostSamples(samples)){ + throw ADC_exception("could not set post sample number"); + } + fprintf(stderr,"set sample number to %d\n",samples); + + /* set sampling frequency */ + unsigned int freq=(unsigned int)fabs(floor(_rate)+0.5); + unsigned int freq_req=freq; + SetSampleFrequency(&freq_req); + if (freq!=freq_req) + throw ADC_exception("requested frequency could not be set"); + rate=freq_req; + fprintf(stderr,"set rate to %g\n",rate); + + /* set resolution */ + if (0!=SetResolution(_resolution)) + throw ADC_exception("could not set resolution"); + unsigned char resolution_set; + GetResolution(&resolution_set); + if (_resolution!=resolution_set) + throw ADC_exception("requested resolution not supported"); + resolution=_resolution; + fprintf(stderr,"set resolution to %d\n",resolution); + +#if 0 + /* set DC level value to zero */ + const double dclevel_req=-2.0; + if (E_NO_ERRORS!=SetDcLevel(1, dclevel_req)) + throw ADC_exception("could not set dc level for channel 1"); + if (E_NO_ERRORS!=SetDcLevel(2, dclevel_req)) + throw ADC_exception("could not set dc level for channel 2"); +#endif + + /* set input sensitivity for both channels */ + double sensitivity_req1=_sensitivity; + SetSensitivity(1,&sensitivity_req1); + double sensitivity_req2=_sensitivity; + SetSensitivity(2,&sensitivity_req2); + if (sensitivity_req1!=_sensitivity || sensitivity_req2!=_sensitivity) + throw ADC_exception("requested sensitivity could not be set"); + sensitivity=_sensitivity; + fprintf(stderr,"set sensitivity to %g\n",sensitivity); + + /* set input coupling to DC */ + if (0!=SetCoupling(Ch1, ctDC) || 0!=SetCoupling(Ch2, ctDC)) + throw ADC_exception("could not set coupling to dc"); + + /* what to measure */ + if (E_NO_ERRORS!=SetMeasureMode(mmCh12)) /* Channel 1 and 2 */ + throw ADC_exception("could not set measurement mode"); + /* the trigger source */ + if (E_NO_ERRORS!=SetTriggerSource(tsExternal)) /* external trigger */ + throw ADC_exception("could not set trigger source"); + /* which slope to trigger */ + if (E_NO_ERRORS!=SetTriggerMode(tmRising)) /* 0=Rising slope */ + throw ADC_exception("could not set trigger source"); + /* set transfer mode */ + if (E_NO_ERRORS!=SetTransferMode(tmBlock)) + throw ADC_exception("could not set transfer mode"); + + /* finally start the measurement */ + if (0!=ADC_Start()) + throw ADC_exception("could not start triggered adc measurement"); + fprintf(stderr,"started triggered adc measurement with %d samples, rate=%g, sensitivity=%g, resolution=%d\n",samples,rate,sensitivity,resolution); +} + +void TiePieHS3::set_daq(state& exp) { + size_t s=0; + double f=0.0; + size_t res=0; + double sens=0.0; + // todo: exception... + state_iterator si(dynamic_cast(exp)); + state* a_state=(state*)si.get_state(); + while (NULL!=a_state) { + // find a analogin section + std::list inputs; + for(state::iterator i=a_state->begin(); i!=a_state->end(); ++i) { + analogin* input=dynamic_cast(*i); + if (input!=NULL) { + inputs.push_back(input); + // replace this by a trigger pulse + // to do only short one + ttlout* trigger_pulse=new ttlout(); + trigger_pulse->ttls=trigger_line_mask; + trigger_pulse->id=trigger_line_id; + *i=trigger_pulse; + } + } + if (!inputs.empty()) { + if (s!=0 || si.get_count()>1) + throw ADC_exception("Sorry, but TiePie Card does not support mutittriggering"); + s=inputs.front()->samples; + f=inputs.front()->sample_frequency; + res=inputs.front()->resolution; + sens=inputs.front()->sensitivity; + // to do only one channel + while (!inputs.empty()) {delete inputs.front();inputs.pop_front();} + } + a_state=(state*)si.next_state(); + } + //xml_state_writer().write_states(stderr,exp); + fprintf(stderr,"f=%g s=%d sens=%f res=%d\n",f,s,sens,res); + sample_after_external_trigger(f, s, sens, res); +} + +result* TiePieHS3::get_samples(double timeout) { + // return new error_result(1,"for some reasons disabled"); + if (samples==0 || rate<=0) + return new adc_result(1); + /* allocate necessary buffers */ + unsigned short int* data1=(unsigned short int*)calloc(samples,sizeof(unsigned short int)); + if (data1==NULL) throw ADC_exception("could not allocate memory for real data"); + unsigned short int* data2=(unsigned short int*)calloc(samples,sizeof(unsigned short int)); + if (data2==NULL) { + free(data1); + throw ADC_exception("could not allocate memory for imaginary data"); + } + + const double poll_timestep=5.0e-2; + double poll_time=0.0; +#if 1 + fprintf(stderr,"expecting ADC result..."); +#endif + while (1!=ADC_Ready()) { + usleep((unsigned int)floor(poll_timestep*1.0e6)); + poll_time+=poll_timestep; + if (poll_time>=timeout) throw ADC_exception("ran into timeout!"); +#if 1 + fprintf(stderr,"waiting..."); + fflush(stderr); +#endif + } +#if 1 + fprintf(stderr,"done\n"); +#endif + if (ADC_GetData(data1, data2)!=0) { + free(data1); + free(data2); + throw ADC_exception("error while fetching data\n"); + } + + short int* adc_data=(short int*)calloc(2*samples,sizeof(short int)); + if (adc_data==NULL) { + free(data1); + free(data2); + throw ADC_exception("could not allocate memory for adc data"); + } + + for (size_t i=0;i + +/* + * Init / Exit + */ + INITINSTRUMENT InitInstrument = NULL; + EXITINSTRUMENT ExitInstrument = NULL; +/* + * Information retrieval + */ + GETSERIALNUMBER GetSerialNumber = NULL; + GETCALIBRATIONDATE GetCalibrationDate = NULL; + GETMAXRECORDLENGTH GetMaxRecordLength = NULL; + GETMAXSAMPLEFREQUENCY GetMaxSampleFrequency = NULL; + GETDCLEVELSTATUS GetDCLevelStatus = NULL; + GETSQUAREWAVEGENSTATUS GetSquareWaveGenStatus = NULL; + GETFUNCTIONGENSTATUS GetFunctionGenStatus = NULL; + GETFUNCGENMAXAMPLITUDE GetFuncGenMaxAmplitude = NULL; + GETAVAILABLESENSITIVITIES GetAvailableSensitivities = NULL; + GETAVAILABLERESOLUTIONS GetAvailableResolutions = NULL; + GETNRCHANNELS GetNrChannels = NULL; + +/* + * Set / Get instrument status + */ + GETRESOLUTION GetResolution = NULL; + SETRESOLUTION SetResolution = NULL; + GETMEASUREMODE GetMeasureMode = NULL; + SETMEASUREMODE SetMeasureMode = NULL; + + SETSENSITIVITY SetSensitivity = NULL; + GETSENSITIVITY GetSensitivity = NULL; + GETAUTORANGING GetAutoRanging = NULL; + SETAUTORANGING SetAutoRanging = NULL; + SETCOUPLING SetCoupling = NULL; + GETCOUPLING GetCoupling = NULL; + GETDCLEVEL GetDcLevel = NULL; + SETDCLEVEL SetDcLevel = NULL; + + GETRECORDLENGTH GetRecordLength = NULL; + SETRECORDLENGTH SetRecordLength = NULL; + GETPOSTSAMPLES GetPostSamples = NULL; + SETPOSTSAMPLES SetPostSamples = NULL; + GETSAMPLEFREQUENCY GetSampleFrequency = NULL; + SETSAMPLEFREQUENCY SetSampleFrequency = NULL; + + GETEXTERNALCLOCK GetExternalClock = NULL; + SETEXTERNALCLOCK SetExternalClock = NULL; + + GETTRIGGERSOURCE GetTriggerSource = NULL; + SETTRIGGERSOURCE SetTriggerSource = NULL; + GETTRIGGERMODE GetTriggerMode = NULL; + SETTRIGGERMODE SetTriggerMode = NULL; + GETTRIGGERLEVEL GetTriggerLevel = NULL; + SETTRIGGERLEVEL SetTriggerLevel = NULL; + GETTRIGGERHYS GetTriggerHys = NULL; + SETTRIGGERHYS SetTriggerHys = NULL; + SETPXITRIGGERENABLES SetPXITriggerEnables = NULL; + GETPXITRIGGERENABLES GetPXITriggerEnables = NULL; + SETPXITRIGGERSLOPES SetPXITriggerSlopes = NULL; + GETPXITRIGGERSLOPES GetPXITriggerSlopes = NULL; + GETTRIGGERTIMEOUT GetTriggerTimeOut = NULL; + SETTRIGGERTIMEOUT SetTriggerTimeOut = NULL; +/* + * + */ + SETDIGITALOUTPUTS SetDigitalOutputs = NULL; + GETDIGITALOUTPUTS GetDigitalOutputs = NULL; +/* + * perform measurements + */ + STARTMEASUREMENT StartMeasurement = NULL; +/* + * retrieve data + */ + GETMEASUREMENTRAW GetMeasurementRaw = NULL; + GETONEMEASUREMENTRAW GetOneMeasurementRaw = NULL; + + GETMEASUREMENT GetMeasurement = NULL; + GETONEMEASUREMENT GetOneMeasurement = NULL; + + GETDIGITALINPUTVALUES GetDigitalInputValues = NULL; + GETONEDIGITALVALUE GetOneDigitalValue = NULL; + + +/* + * advanced measurements + */ + ADC_START ADC_Start = NULL; + ADC_RUNNING ADC_Running = NULL; + ADC_ABORT ADC_Abort = NULL; + ADC_TRIGGERED ADC_Triggered = NULL; + ADC_READY ADC_Ready = NULL; + ADC_FORCETRIG ADC_ForceTrig = NULL; + ADC_GETDATA ADC_GetData = NULL; + ADC_GETDATAVOLT ADC_GetDataVolt = NULL; + ADC_GETDATACH ADC_GetDataCh = NULL; + ADC_GETDATAVOLTCH ADC_GetDataVoltCh = NULL; +/* + * streaming + */ + SETRECEIVERHANDLE SetReceiverHandle = NULL; + GETMESSAGEID GetMessageID = NULL; + SETTRANSFERMODE SetTransferMode = NULL; + GETTRANSFERMODE GetTransferMode = NULL; + + SETINSTRUMENTCONFIG SetInstrumentConfig = NULL; + + SETSQUAREWAVEGENFREQUENCY SetSquareWaveGenFrequency = NULL; + GETSQUAREWAVEGENFREQUENCY GetSquareWaveGenFrequency = NULL; + + SETFUNCGENSIGNALTYPE SetFuncGenSignalType = NULL; + GETFUNCGENSIGNALTYPE GetFuncGenSignalType = NULL; + SETFUNCGENAMPLITUDE SetFuncGenAmplitude = NULL; + GETFUNCGENAMPLITUDE GetFuncGenAmplitude = NULL; + SETFUNCGENDCOFFSET SetFuncGenDCOffset = NULL; + GETFUNCGENDCOFFSET GetFuncGenDCOffset = NULL; + SETFUNCGENSYMMETRY SetFuncGenSymmetry = NULL; + GETFUNCGENSYMMETRY GetFuncGenSymmetry = NULL; + SETFUNCGENFREQUENCY SetFuncGenFrequency = NULL; + GETFUNCGENFREQUENCY GetFuncGenFrequency = NULL; + FILLFUNCGENMEMORY FillFuncGenMemory = NULL; + SETFUNCGENOUTPUTON SetFuncGenOutputOn = NULL; + GETFUNCGENOUTPUTON GetFuncGenOutputOn = NULL; + FUNCGENBURST FuncGenBurst = NULL; + SETFUNCGENTRIGSOURCE SetFuncGenTrigSource = NULL; + GETFUNCGENTRIGSOURCE GetFuncGenTrigSource = NULL; + + + HINSTANCE TiePieDLL; + + +bool _export OpenDLL( TDeviceTypes Device ) +{ +/* + * Check is a dll was already opened. If so, close it and open the new one + */ + if (TiePieDLL != NULL) + { + FreeLibrary( TiePieDLL ); + TiePieDLL = NULL; + } // if + + /* + * select the correct DLL + */ + switch (Device) + { + case dtHandyprobe2 : TiePieDLL = LoadLibrary( "hp2.dll" ); + break; + case dtHandyscope2 : TiePieDLL = LoadLibrary( "hs2.dll" ); + break; + case dtHandyscope3 : TiePieDLL = LoadLibrary( "hs3.dll" ); + break; + case dtHandyscope4 : TiePieDLL = LoadLibrary( "hs4.dll" ); + break; + case dtHS508 : TiePieDLL = LoadLibrary( "hs508.dll" ); + break; + case dtHS801 : TiePieDLL = LoadLibrary( "hs801.dll" ); + break; + case dtTP112 : TiePieDLL = LoadLibrary( "tp112.dll" ); + break; + case dtTP208 : TiePieDLL = LoadLibrary( "tp208.dll" ); + break; + case dtTP508 : TiePieDLL = LoadLibrary( "tp508.dll" ); + break; + case dtTP801 : TiePieDLL = LoadLibrary( "tp801.dll" ); + break; + case dtPCI801 : TiePieDLL = LoadLibrary( "pci801.dll" ); + break; + } // switch + + if (TiePieDLL != NULL) + { + InitInstrument = (INITINSTRUMENT) GetProcAddress( TiePieDLL, "InitInstrument" ); + ExitInstrument = (EXITINSTRUMENT) GetProcAddress( TiePieDLL, "ExitInstrument" ); + + GetCalibrationDate = (GETCALIBRATIONDATE) GetProcAddress( TiePieDLL, "GetCalibrationDate" ); + GetSerialNumber = (GETSERIALNUMBER) GetProcAddress( TiePieDLL, "GetSerialNumber" ); + GetMaxSampleFrequency = (GETMAXSAMPLEFREQUENCY) GetProcAddress( TiePieDLL, "GetMaxSampleFrequency" ); + GetMaxRecordLength = (GETMAXRECORDLENGTH) GetProcAddress( TiePieDLL, "GetMaxRecordLength" ); + GetDCLevelStatus = (GETDCLEVELSTATUS) GetProcAddress( TiePieDLL, "GetDCLevelStatus" ); + GetSquareWaveGenStatus = (GETSQUAREWAVEGENSTATUS) GetProcAddress( TiePieDLL, "GetSquareWaveGenStatus" ); + GetFunctionGenStatus = (GETFUNCTIONGENSTATUS) GetProcAddress( TiePieDLL, "GetFunctionGenStatus" ); + GetFuncGenMaxAmplitude = (GETFUNCGENMAXAMPLITUDE) GetProcAddress( TiePieDLL, "GetFuncGenMaxAmplitude" ); + GetAvailableResolutions = (GETAVAILABLERESOLUTIONS) GetProcAddress( TiePieDLL, "GetAvailableResolutions" ); + GetAvailableSensitivities = (GETAVAILABLESENSITIVITIES) GetProcAddress( TiePieDLL, "GetAvailableSensitivities" ); + GetNrChannels = (GETNRCHANNELS) GetProcAddress( TiePieDLL, "GetNrChannels" ); + + GetResolution = (GETRESOLUTION) GetProcAddress( TiePieDLL, "GetResolution" ); + SetResolution = (SETRESOLUTION) GetProcAddress( TiePieDLL, "SetResolution" ); + + GetMeasureMode = (GETMEASUREMODE) GetProcAddress( TiePieDLL, "GetMeasureMode" ); + SetMeasureMode = (SETMEASUREMODE) GetProcAddress( TiePieDLL, "SetMeasureMode" ); + + StartMeasurement = (STARTMEASUREMENT) GetProcAddress( TiePieDLL, "StartMeasurement" ); + + GetMeasurementRaw = (GETMEASUREMENTRAW) GetProcAddress( TiePieDLL, "GetMeasurementRaw" ); + GetOneMeasurementRaw = (GETONEMEASUREMENTRAW) GetProcAddress( TiePieDLL, "GetOneMeasurementRaw" ); + GetMeasurement = (GETMEASUREMENT) GetProcAddress( TiePieDLL, "GetMeasurement" ); + GetOneMeasurement = (GETONEMEASUREMENT) GetProcAddress( TiePieDLL, "GetOneMeasurement" ); + GetDigitalInputValues = (GETDIGITALINPUTVALUES) GetProcAddress( TiePieDLL, "GetDigitalInputValues" ); + GetOneDigitalValue = (GETONEDIGITALVALUE) GetProcAddress( TiePieDLL, "GetOneDigitalValue" ); + + ADC_Start = (ADC_START) GetProcAddress( TiePieDLL, "ADC_Start" ); + ADC_Running = (ADC_RUNNING) GetProcAddress( TiePieDLL, "ADC_Running" ); + ADC_Abort = (ADC_ABORT) GetProcAddress( TiePieDLL, "ADC_Abort" ); + ADC_Triggered = (ADC_TRIGGERED) GetProcAddress( TiePieDLL, "ADC_Triggered" ); + ADC_Ready = (ADC_READY) GetProcAddress( TiePieDLL, "ADC_Ready" ); + ADC_ForceTrig = (ADC_FORCETRIG) GetProcAddress( TiePieDLL, "ADC_ForceTrig" ); + ADC_GetData = (ADC_GETDATA) GetProcAddress( TiePieDLL, "ADC_GetData" ); + ADC_GetDataVolt = (ADC_GETDATAVOLT) GetProcAddress( TiePieDLL, "ADC_GetDataVolt" ); + ADC_GetDataCh = (ADC_GETDATACH) GetProcAddress( TiePieDLL, "ADC_GetDataCh" ); + ADC_GetDataVoltCh = (ADC_GETDATAVOLTCH) GetProcAddress( TiePieDLL, "ADC_GetDataVoltCh" ); + + SetReceiverHandle = (SETRECEIVERHANDLE) GetProcAddress( TiePieDLL, "SetReceiverHandle" ); + GetMessageID = (GETMESSAGEID) GetProcAddress( TiePieDLL, "GetMessageID" ); + SetTransferMode = (SETTRANSFERMODE) GetProcAddress( TiePieDLL, "SetTransferMode" ); + GetTransferMode = (GETTRANSFERMODE) GetProcAddress( TiePieDLL, "GetTransferMode" ); + + GetRecordLength = (GETRECORDLENGTH) GetProcAddress( TiePieDLL, "GetRecordLength" ); + SetRecordLength = (SETRECORDLENGTH) GetProcAddress( TiePieDLL, "SetRecordLength" ); + GetPostSamples = (GETPOSTSAMPLES) GetProcAddress( TiePieDLL, "GetPostSamples" ); + SetPostSamples = (SETPOSTSAMPLES) GetProcAddress( TiePieDLL, "SetPostSamples" ); + GetSampleFrequency = (GETSAMPLEFREQUENCY) GetProcAddress( TiePieDLL, "GetSampleFrequency" ); + SetSampleFrequency = (SETSAMPLEFREQUENCY) GetProcAddress( TiePieDLL, "SetSampleFrequency" ); + GetExternalClock = (GETEXTERNALCLOCK) GetProcAddress( TiePieDLL, "GetExternalClock" ); + SetExternalClock = (SETEXTERNALCLOCK) GetProcAddress( TiePieDLL, "SetExternalClock" ); + + SetSensitivity = (SETSENSITIVITY) GetProcAddress( TiePieDLL, "SetSensitivity" ); + GetSensitivity = (GETSENSITIVITY) GetProcAddress( TiePieDLL, "GetSensitivity" ); + GetAutoRanging = (GETAUTORANGING) GetProcAddress( TiePieDLL, "GetAutoRanging" ); + SetAutoRanging = (SETAUTORANGING) GetProcAddress( TiePieDLL, "SetAutoRanging" ); + SetCoupling = (SETCOUPLING) GetProcAddress( TiePieDLL, "SetCoupling" ); + GetCoupling = (GETCOUPLING) GetProcAddress( TiePieDLL, "GetCoupling" ); + GetDcLevel = (GETDCLEVEL) GetProcAddress( TiePieDLL, "GetDcLevel" ); + SetDcLevel = (SETDCLEVEL) GetProcAddress( TiePieDLL, "SetDcLevel" ); + + GetTriggerSource = (GETTRIGGERSOURCE) GetProcAddress( TiePieDLL, "GetTriggerSource" ); + SetTriggerSource = (SETTRIGGERSOURCE) GetProcAddress( TiePieDLL, "SetTriggerSource" ); + GetTriggerMode = (GETTRIGGERMODE) GetProcAddress( TiePieDLL, "GetTriggerMode" ); + SetTriggerMode = (SETTRIGGERMODE) GetProcAddress( TiePieDLL, "SetTriggerMode" ); + GetTriggerLevel = (GETTRIGGERLEVEL) GetProcAddress( TiePieDLL, "GetTriggerLevel" ); + SetTriggerLevel = (SETTRIGGERLEVEL) GetProcAddress( TiePieDLL, "SetTriggerLevel" ); + GetTriggerHys = (GETTRIGGERHYS) GetProcAddress( TiePieDLL, "GetTriggerHys" ); + SetTriggerHys = (SETTRIGGERHYS) GetProcAddress( TiePieDLL, "SetTriggerHys" ); + GetPXITriggerEnables = (GETPXITRIGGERENABLES) GetProcAddress( TiePieDLL, "GetPXITriggerEnables" ); + SetPXITriggerEnables = (SETPXITRIGGERENABLES) GetProcAddress( TiePieDLL, "SetPXITriggerEnables" ); + GetPXITriggerSlopes = (GETPXITRIGGERSLOPES) GetProcAddress( TiePieDLL, "GetPXITriggerSlopes" ); + SetPXITriggerSlopes = (SETPXITRIGGERSLOPES) GetProcAddress( TiePieDLL, "SetPXITriggerSlopes" ); + GetTriggerTimeOut = (GETTRIGGERTIMEOUT) GetProcAddress( TiePieDLL, "GetTriggerTimeOut" ); + SetTriggerTimeOut = (SETTRIGGERTIMEOUT) GetProcAddress( TiePieDLL, "SetTriggerTimeOut" ); + + SetDigitalOutputs = (SETDIGITALOUTPUTS) GetProcAddress( TiePieDLL, "SetDigitalOutputs" ); + GetDigitalOutputs = (GETDIGITALOUTPUTS) GetProcAddress( TiePieDLL, "GetDigitalOutputs" ); + + SetSquareWaveGenFrequency = (SETSQUAREWAVEGENFREQUENCY) GetProcAddress( TiePieDLL, "SetSquareWaveGenFrequency" ); + GetSquareWaveGenFrequency = (GETSQUAREWAVEGENFREQUENCY) GetProcAddress( TiePieDLL, "GetSquareWaveGenFrequency" ); + + SetFuncGenSignalType = (SETFUNCGENSIGNALTYPE) GetProcAddress( TiePieDLL, "SetFuncGenSignalType" ); + GetFuncGenSignalType = (GETFUNCGENSIGNALTYPE) GetProcAddress( TiePieDLL, "GetFuncGenSignalType" ); + SetFuncGenAmplitude = (SETFUNCGENAMPLITUDE) GetProcAddress( TiePieDLL, "SetFuncGenAmplitude" ); + GetFuncGenAmplitude = (GETFUNCGENAMPLITUDE) GetProcAddress( TiePieDLL, "GetFuncGenAmplitude" ); + SetFuncGenDCOffset = (SETFUNCGENDCOFFSET) GetProcAddress( TiePieDLL, "SetFuncGenDCOffset" ); + GetFuncGenDCOffset = (GETFUNCGENDCOFFSET) GetProcAddress( TiePieDLL, "GetFuncGenDCOffset" ); + SetFuncGenSymmetry = (SETFUNCGENSYMMETRY) GetProcAddress( TiePieDLL, "SetFuncGenSymmetry" ); + GetFuncGenSymmetry = (GETFUNCGENSYMMETRY) GetProcAddress( TiePieDLL, "GetFuncGenSymmetry" ); + SetFuncGenFrequency = (SETFUNCGENFREQUENCY) GetProcAddress( TiePieDLL, "SetFuncGenFrequency" ); + GetFuncGenFrequency = (GETFUNCGENFREQUENCY) GetProcAddress( TiePieDLL, "GetFuncGenFrequency" ); + FillFuncGenMemory = (FILLFUNCGENMEMORY) GetProcAddress( TiePieDLL, "FillFuncGenMemory" ); + SetFuncGenOutputOn = (SETFUNCGENOUTPUTON) GetProcAddress( TiePieDLL, "SetFuncGenOutputOn" ); + GetFuncGenOutputOn = (GETFUNCGENOUTPUTON) GetProcAddress( TiePieDLL, "GetFuncGenOutputOn" ); + FuncGenBurst = (FUNCGENBURST) GetProcAddress( TiePieDLL, "FuncGenBurst" ); + SetFuncGenTrigSource = (SETFUNCGENTRIGSOURCE) GetProcAddress( TiePieDLL, "SetFuncGenTrigSource" ); + GetFuncGenTrigSource = (GETFUNCGENTRIGSOURCE) GetProcAddress( TiePieDLL, "GetFuncGenTrigSource" ); + + SetInstrumentConfig = (SETINSTRUMENTCONFIG) GetProcAddress( TiePieDLL, "SetInstrumentConfig" ); + + return( 1 ); + } // if + else + { + return( 0 ); + } // else +}; // OpenDLL + + + + +bool _export CloseDLL( void ) +{ + /* + * Check if a dll was opened. If so, close it and open the new one + */ + if (TiePieDLL != NULL) + { + FreeLibrary( TiePieDLL ); + TiePieDLL = NULL; + return( 1 ); + } // if + else + { + return( 0 ); + } // else +} // CloseDLL + + diff --git a/drivers/TiePie-HS3/tiepie.h b/drivers/TiePie-HS3/tiepie.h new file mode 100644 index 0000000..54a7404 --- /dev/null +++ b/drivers/TiePie-HS3/tiepie.h @@ -0,0 +1,257 @@ +#ifndef _tiepie +#define _tiepie + +/* + * This file contains the declarations for the low level routines + * for the TiePie engineering instrument dll's + */ + +#include + + +/* + * declarations to match pascal-types to c-types + */ + typedef unsigned char byte; + typedef unsigned int dword; + typedef unsigned short int word; + typedef signed long int integer; + +/* + * The instruments + */ + typedef enum { dtHandyprobe2, + dtHandyscope2, + dtHandyscope3, + dtHandyscope4, + dtHS508, + dtHS801, + dtTP112, + dtTP208, + dtTP508, + dtTP801, + dtPCI801 + } TDeviceTypes; + +/* + * Error-codes + */ + #define E_NO_ERRORS 0x0000 + #define E_NO_HARDWARE 0x0001 + #define E_NOT_INITIALISED 0x0002 + #define E_NOT_SUPPORTED 0x0004 + #define E_NO_GENERATOR 0x0008 + #define E_INVALID_CHANNEL 0x0010 + #define E_INVALID_VALUE 0x0020 +/* + * channel indicators + */ + #define Ch1 1 + #define Ch2 2 + #define Ch3 3 + #define Ch4 4 +/* + * Measure mode settings + * When measuring more chanels, simply add the required constants together : e.g. mmCh1 + mmCh2 to measure both Ch1 and Ch2 + */ + #define mmCh1 1 + #define mmCh2 2 + #define mmCh3 4 + #define mmCh4 8 + #define mmCh12 3 +/* + * Coupling settings + */ + #define ctAC 0 + #define ctDC 1 +/* + * Trigger sources + */ + #define tsCh1 0 + #define tsCh2 1 + #define tsCh3 2 + #define tsCh4 3 + #define tsExternal 4 + #define tsAnalogExt 5 + #define tsAnd 6 + #define tsOr 7 + #define tsXor 8 + #define tsNoTrig 9 + #define tsNeverTRig 10 // not used + #define tsPxiExt 11 + #define tsGenStart 12 + #define tsGenStop 13 + #define tsGenNew 14 +/* + * Trigger modes + */ + #define tmRising 0 + #define tmFalling 1 + #define tmInWindow 2 + #define tmOutWindow 3 + #define tmTVLine 4 + #define tmTVFrameOdd 5 + #define tmTVFrameEven 6 +/* + * Function generator signal types + */ + #define stSine 0 + #define stTriangle 1 + #define stSquare 2 + #define stDC 3 + #define stNoise 4 + #define stArbitrary 5 +/* + * Transfer mode constants + */ + #define tmBlock 0 + #define tmStream 1 +/* + * Event message identifiers + */ + #define DLLTransUpDate 1 + #define DLLTransReady 2 + + +bool _export OpenDLL( TDeviceTypes Device ); +bool _export CloseDLL( void ); + +/* + * Declarations for functions from dynamically loaded librarie(s) + * + * Open / Close the instrument + */ + typedef word (WINAPI *INITINSTRUMENT ) (word ); extern word (__stdcall *InitInstrument ) (word ); + typedef word (WINAPI *EXITINSTRUMENT ) (void ); extern word (__stdcall *ExitInstrument ) (void ); +/* + * Information retrieval + */ + typedef word (WINAPI *GETCALIBRATIONDATE ) (dword* ); extern word (__stdcall *GetCalibrationDate ) (dword* ); + typedef word (WINAPI *GETSERIALNUMBER ) (dword* ); extern word (__stdcall *GetSerialNumber ) (dword* ); + typedef dword (WINAPI *GETMAXSAMPLEFREQUENCY ) (void ); extern dword (__stdcall *GetMaxSampleFrequency ) (void ); + typedef dword (WINAPI *GETMAXRECORDLENGTH ) (void ); extern dword (__stdcall *GetMaxRecordLength ) (void ); + typedef word (WINAPI *GETDCLEVELSTATUS ) (void ); extern word (__stdcall *GetDCLevelStatus ) (void ); + typedef word (WINAPI *GETSQUAREWAVEGENSTATUS ) (void ); extern word (__stdcall *GetSquareWaveGenStatus ) (void ); + typedef word (WINAPI *GETFUNCTIONGENSTATUS ) (void ); extern word (__stdcall *GetFunctionGenStatus ) (void ); + typedef word (WINAPI *GETFUNCGENMAXAMPLITUDE ) (double* ); extern word (__stdcall *GetFuncGenMaxAmplitude ) (double* ); + typedef word (WINAPI *GETAVAILABLERESOLUTIONS ) (double* ); extern word (__stdcall *GetAvailableResolutions ) (double* ); + typedef word (WINAPI *GETAVAILABLESENSITIVITIES) (double* ); extern word (__stdcall *GetAvailableSensitivities) (double* ); + typedef word (WINAPI *GETNRCHANNELS ) (word* ); extern word (__stdcall *GetNrChannels ) (word* ); +/* + * Controlling the input resolution + */ + typedef word (WINAPI *GETRESOLUTION ) (byte* ); extern word (__stdcall *GetResolution ) (byte* ); + typedef word (WINAPI *SETRESOLUTION ) (byte ); extern word (__stdcall *SetResolution ) (byte ); +/* + * Control which channels are measured + */ + typedef word (WINAPI *GETMEASUREMODE ) (byte* ); extern word (__stdcall *GetMeasureMode ) (byte* ); + typedef word (WINAPI *SETMEASUREMODE ) (byte ); extern word (__stdcall *SetMeasureMode ) (byte ); +/* + * Perform a measurement + */ + typedef word (WINAPI *STARTMEASUREMENT ) (void ); extern word (__stdcall *StartMeasurement ) (void ); +/* + * Retrieve the measured data + */ + typedef word (WINAPI *GETMEASUREMENTRAW ) (word*, word* ); extern word (__stdcall *GetMeasurementRaw ) (word*, word* ); + typedef word (WINAPI *GETONEMEASUREMENTRAW ) (dword, word*, word* ); extern word (__stdcall *GetOneMeasurementRaw ) (dword, word*, word* ); + typedef word (WINAPI *GETMEASUREMENT ) (double*, double* ); extern word (__stdcall *GetMeasurement ) (double*, double* ); + typedef word (WINAPI *GETONEMEASUREMENT ) (dword, double*, double* ); extern word (__stdcall *GetOneMeasurement ) (dword, double*, double* ); + typedef word (WINAPI *GETDIGITALINPUTVALUES ) (word* ); extern word (__stdcall *GetDigitalInputValues ) (word* ); + typedef word (WINAPI *GETONEDIGITALVALUE ) (dword, word* ); extern word (__stdcall *GetOneDigitalValue ) (dword, word* ); +/* + * Advanced measurement routines + */ + typedef word (WINAPI *ADC_START ) (void ); extern word (__stdcall *ADC_Start ) (void ); + typedef word (WINAPI *ADC_RUNNING ) (void ); extern word (__stdcall *ADC_Running ) (void ); + typedef word (WINAPI *ADC_ABORT ) (void ); extern word (__stdcall *ADC_Abort ) (void ); + typedef word (WINAPI *ADC_TRIGGERED ) (void ); extern word (__stdcall *ADC_Triggered ) (void ); + typedef word (WINAPI *ADC_READY ) (void ); extern word (__stdcall *ADC_Ready ) (void ); + typedef word (WINAPI *ADC_FORCETRIG ) (void ); extern word (__stdcall *ADC_ForceTrig ) (void ); + typedef word (WINAPI *ADC_GETDATA ) (word*, word* ); extern word (__stdcall *ADC_GetData ) (word*, word* ); + typedef word (WINAPI *ADC_GETDATAVOLT ) (double*, double* ); extern word (__stdcall *ADC_GetDataVolt ) (double*, double* ); + typedef word (WINAPI *ADC_GETDATACH ) (word, word* ); extern word (__stdcall *ADC_GetDataCh ) (word, word* ); + typedef word (WINAPI *ADC_GETDATAVOLTCH ) (word, double* ); extern word (__stdcall *ADC_GetDataVoltCh ) (word, double* ); +/* + * streaming measurements + */ + typedef word (WINAPI *SETRECEIVERHANDLE ) (HWND ); extern word (__stdcall *SetReceiverHandle ) (HWND ); + typedef word (WINAPI *GETMESSAGEID ) (dword, dword* ); extern word (__stdcall *GetMessageID ) (dword, dword* ); + typedef word (WINAPI *SETTRANSFERMODE ) (dword ); extern word (__stdcall *SetTransferMode ) (dword ); + typedef word (WINAPI *GETTRANSFERMODE ) (dword* ); extern word (__stdcall *GetTransferMode ) (dword* ); +/* + * Control the time base + */ + typedef dword (WINAPI *GETRECORDLENGTH ) (void ); extern dword (__stdcall *GetRecordLength ) (void ); + typedef word (WINAPI *SETRECORDLENGTH ) (dword ); extern word (__stdcall *SetRecordLength ) (dword ); + typedef dword (WINAPI *GETPOSTSAMPLES ) (void ); extern dword (__stdcall *GetPostSamples ) (void ); + typedef word (WINAPI *SETPOSTSAMPLES ) (dword ); extern word (__stdcall *SetPostSamples ) (dword ); + typedef dword (WINAPI *GETSAMPLEFREQUENCY ) (void ); extern dword (__stdcall *GetSampleFrequency ) (void ); + typedef word (WINAPI *SETSAMPLEFREQUENCY ) (dword* ); extern word (__stdcall *SetSampleFrequency ) (dword* ); + typedef word (WINAPI *GETEXTERNALCLOCK ) (word* ); extern word (__stdcall *GetExternalClock ) (word* ); + typedef word (WINAPI *SETEXTERNALCLOCK ) (word ); extern word (__stdcall *SetExternalClock ) (word ); +/* + * Control the analog input channels + */ + typedef word (WINAPI *SETSENSITIVITY ) (byte, double*); extern word (__stdcall *SetSensitivity ) (byte, double*); + typedef word (WINAPI *GETSENSITIVITY ) (byte, double*); extern word (__stdcall *GetSensitivity ) (byte, double*); + typedef word (WINAPI *GETCOUPLING ) (byte, byte* ); extern word (__stdcall *GetCoupling ) (byte, byte* ); + typedef word (WINAPI *SETCOUPLING ) (byte, byte ); extern word (__stdcall *SetCoupling ) (byte, byte ); + typedef word (WINAPI *GETDCLEVEL ) (byte, double*); extern word (__stdcall *GetDcLevel ) (byte, double*); + typedef word (WINAPI *SETDCLEVEL ) (byte, double ); extern word (__stdcall *SetDcLevel ) (byte, double ); + typedef word (WINAPI *GETAUTORANGING ) (byte, byte* ); extern word (__stdcall *GetAutoRanging ) (byte, byte* ); + typedef word (WINAPI *SETAUTORANGING ) (byte, byte ); extern word (__stdcall *SetAutoRanging ) (byte, byte ); +/* + * Control the trigger system + */ + typedef word (WINAPI *GETTRIGGERSOURCE ) (byte* ); extern word (__stdcall *GetTriggerSource ) (byte* ); + typedef word (WINAPI *SETTRIGGERSOURCE ) (byte ); extern word (__stdcall *SetTriggerSource ) (byte ); + typedef word (WINAPI *GETTRIGGERMODE ) (byte* ); extern word (__stdcall *GetTriggerMode ) (byte* ); + typedef word (WINAPI *SETTRIGGERMODE ) (byte ); extern word (__stdcall *SetTriggerMode ) (byte ); + typedef word (WINAPI *GETTRIGGERLEVEL ) (byte, double*); extern word (__stdcall *GetTriggerLevel ) (byte, double*); + typedef word (WINAPI *SETTRIGGERLEVEL ) (byte, double ); extern word (__stdcall *SetTriggerLevel ) (byte, double ); + typedef word (WINAPI *GETTRIGGERHYS ) (byte, double*); extern word (__stdcall *GetTriggerHys ) (byte, double*); + typedef word (WINAPI *SETTRIGGERHYS ) (byte, double ); extern word (__stdcall *SetTriggerHys ) (byte, double ); + typedef word (WINAPI *SETPXITRIGGERENABLES ) (byte ); extern word (__stdcall *SetPXITriggerEnables ) (byte ); + typedef word (WINAPI *GETPXITRIGGERENABLES ) (byte* ); extern word (__stdcall *GetPXITriggerEnables ) (byte* ); + typedef word (WINAPI *SETPXITRIGGERSLOPES ) (byte ); extern word (__stdcall *SetPXITriggerSlopes ) (byte ); + typedef word (WINAPI *GETPXITRIGGERSLOPES ) (byte* ); extern word (__stdcall *GetPXITriggerSlopes ) (byte* ); + typedef dword (WINAPI *GETTRIGGERTIMEOUT ) (void ); extern dword (__stdcall *GetTriggerTimeOut ) (void ); + typedef word (WINAPI *SETTRIGGERTIMEOUT ) (dword ); extern word (__stdcall *SetTriggerTimeOut ) (dword ); +/* + * Control the digital outputs + */ + typedef word (WINAPI *SETDIGITALOUTPUTS ) (byte ); extern word (__stdcall *SetDigitalOutputs ) (byte ); + typedef word (WINAPI *GETDIGITALOUTPUTS ) (byte* ); extern word (__stdcall *GetDigitalOutputs ) (byte* ); +/* + * Control the Square Wave Generator + */ + typedef word (WINAPI *SETSQUAREWAVEGENFREQUENCY) (double* ); extern word (__stdcall *SetSquareWaveGenFrequency) (double* ); + typedef word (WINAPI *GETSQUAREWAVEGENFREQUENCY) (double* ); extern word (__stdcall *GetSquareWaveGenFrequency) (double* ); +/* + * Control the Arbitrary Waveform Generator + */ + typedef word (WINAPI *SETFUNCGENSIGNALTYPE ) (word ); extern word (__stdcall *SetFuncGenSignalType ) (word ); + typedef word (WINAPI *GETFUNCGENSIGNALTYPE ) (word* ); extern word (__stdcall *GetFuncGenSignalType ) (word* ); + typedef word (WINAPI *SETFUNCGENAMPLITUDE ) (double ); extern word (__stdcall *SetFuncGenAmplitude ) (double ); + typedef word (WINAPI *GETFUNCGENAMPLITUDE ) (double* ); extern word (__stdcall *GetFuncGenAmplitude ) (double* ); + typedef word (WINAPI *SETFUNCGENDCOFFSET ) (double ); extern word (__stdcall *SetFuncGenDCOffset ) (double ); + typedef word (WINAPI *GETFUNCGENDCOFFSET ) (double* ); extern word (__stdcall *GetFuncGenDCOffset ) (double* ); + typedef word (WINAPI *SETFUNCGENSYMMETRY ) (double ); extern word (__stdcall *SetFuncGenSymmetry ) (double ); + typedef word (WINAPI *GETFUNCGENSYMMETRY ) (double* ); extern word (__stdcall *GetFuncGenSymmetry ) (double* ); + typedef word (WINAPI *SETFUNCGENFREQUENCY ) (double* ); extern word (__stdcall *SetFuncGenFrequency ) (double* ); + typedef word (WINAPI *GETFUNCGENFREQUENCY ) (double* ); extern word (__stdcall *GetFuncGenFrequency ) (double* ); + typedef word (WINAPI *FILLFUNCGENMEMORY ) (dword, word* ); extern word (__stdcall *FillFuncGenMemory ) (dword, word* ); + typedef word (WINAPI *SETFUNCGENOUTPUTON ) (word ); extern word (__stdcall *SetFuncGenOutputOn ) (word ); + typedef word (WINAPI *GETFUNCGENOUTPUTON ) (word* ); extern word (__stdcall *GetFuncGenOutputOn ) (word* ); + typedef word (WINAPI *FUNCGENBURST ) (word ); extern word (__stdcall *FuncGenBurst ) (word ); + typedef word (WINAPI *SETFUNCGENTRIGSOURCE ) (byte ); extern word (__stdcall *SetFuncGenTrigSource ) (byte ); + typedef word (WINAPI *GETFUNCGENTRIGSOURCE ) (byte* ); extern word (__stdcall *GetFuncGenTrigSource ) (byte* ); +/* + * + */ + typedef word (WINAPI *SETINSTRUMENTCONFIG ) (word ); extern word (__stdcall *SetInstrumentConfig ) (word ); + + +#endif diff --git a/drivers/device.h b/drivers/device.h new file mode 100644 index 0000000..edaf732 --- /dev/null +++ b/drivers/device.h @@ -0,0 +1,43 @@ +#include +#include "core/job.h" +#include "core/result.h" +#include "core/core_exception.h" + +#ifndef DEVICE_H +#define DEVICE_H + + +/** + * device exception + */ +class device_error: public RecoverableException +{ +public: + explicit device_error(const std::string& msg) throw (): RecoverableException(msg) {} + explicit device_error(const char* msg) throw (): RecoverableException(msg) {} + virtual ~device_error() throw () {} +protected: + virtual const std::string prefix() const { return "ERROR (core_exception): "; } +}; + + + +/** + base class for devices + devices can be configured + */ +class device { + public: + + /** + configuration interface + if no result: call that section again + run is incremented for each call, starting with 0 + */ + virtual configuration_result* configure(const configuration_device_section& conf, int run)=0; + + virtual ~device() {} +}; + + +#endif diff --git a/drivers/dummy/Makefile b/drivers/dummy/Makefile new file mode 100644 index 0000000..0f6daac --- /dev/null +++ b/drivers/dummy/Makefile @@ -0,0 +1,38 @@ +############################################################################# +# +# Author: Achim Gaedke +# Created: June 2004 +# +############################################################################# + + +CXX=g++ +CXXFLAGS=-Wall -Wshadow -pedantic -g -O0 +CXXCPPFLAGS=-I. -I../.. +LIBS=-lpthread + +.PHONY: clean install all + +all: dummy.o + +../tempcont.o: ../tempcont.cpp ../tempcont.h + $(MAKE) -C .. tempcont.o + +../../core/core.a: + $(MAKE) -C ../core core.a + +dummy.o: dummy.h dummy.cpp ../tempcont.h ../ADC.h ../pulsegen.h ../frequgen.h + $(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -c -o dummy.o dummy.cpp + +temperature_dummy.exe: temperature_dummy.cpp dummy.o ../tempcont.o ../../core/core.a + $(CXX) $(CXXFLAGS) -o temperature_dummy.exe temperature_dummy.cpp dummy.o ../tempcont.o ../../core/core.a $(LIBS) + +../../tools/add_endline.exe: ../../tools/add_endline.cpp + $(CXX) $< -o $@ + +clean: ../../tools/add_endline.exe + for f in *.cpp *.h; do ../../tools/add_endline.exe $$f; done; \ +rm -f *~ *.o + +install: + diff --git a/drivers/dummy/dummy.cpp b/drivers/dummy/dummy.cpp new file mode 100644 index 0000000..cbc34b3 --- /dev/null +++ b/drivers/dummy/dummy.cpp @@ -0,0 +1,132 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + +****************************************************************************/ + +#include "dummy.h" +#include +#include "core/states.h" + +dummy::dummy() { + new_results=NULL; + set_history_stepsize(1); +} + +result* dummy::get_samples(double timeout) { + if (new_results==NULL) return new result(0); + if (new_results->empty()) { + delete new_results; + new_results=NULL; + return new result(0); + } + if (new_results->size()==1) { + adc_result* res=new_results->front(); + new_results->pop_front(); + delete new_results; + new_results=NULL; + return res; + } + adc_results* res=new_results; + new_results=NULL; + return res; +} + +void dummy::set_frequency(state& exp) { + return; +} + +void dummy::sample_after_external_trigger(double rate, size_t no, double sens, size_t res) { + // generate artificial result + short int* data=(short int*)malloc(no*sizeof(short int)*2); + const double amplitude=1<<10; + const double noise=1<<9; + const double intermediate_f=1e4; + const double time_offset=1e-5; + const double decay_constant=1e-4; + const double mixing_phase=0; + for (double i=0; ipush_back(new adc_result(0, no, data, rate)); +} + +void dummy::set_daq(state& exp) { + // deallocate all old results + if (new_results!=NULL) { + delete new_results; + } + new_results=new adc_results(0); +} + +/* now run the pulse program */ +void dummy::run_pulse_program(state& exp) { + /* found a state sequence */ + state_sequent* ss=dynamic_cast(&exp); + if (ss!=NULL) { + for (size_t i=0; irepeat;++i) + for (state::iterator j=ss->begin(); j!=ss->end(); j++) { + state* substate=dynamic_cast(*j); + if (substate==NULL) + throw pulse_exception(std::string(__FUNCTION__)+"unknown substate"); + run_pulse_program(*substate); + } + return; + } + + /* can not handle a parallel state */ + state_parallel* sp=dynamic_cast(&exp); + if (sp!=NULL) + throw pulse_exception(std::string(__FUNCTION__)+": state_parallel is not implemented"); + + /* found a state */ + const state* s=dynamic_cast(&exp); + if (s!=NULL) { + fprintf(stdout,"state: t=%g",s->length); + for (state::const_iterator j=s->begin(); j!=s->end(); ++j) { + const analogin* ai=dynamic_cast(*j); + if (ai!=NULL) { + fprintf(stdout,"\n - analogin id=%d channels=%lu",ai->id,ai->channels.to_ulong()); + sample_after_external_trigger(ai->sample_frequency, ai->samples); + continue; + } + const ttlout* to=dynamic_cast(*j); + if (to!=NULL) { + fprintf(stdout,"\n - ttlout id=%d ttls=%lu",to->id,to->ttls.to_ulong()); + continue; + } + const analogout* ao=dynamic_cast(*j); + if (ao!=NULL) { + fprintf(stdout,"\n - analogout id=%d frequency=%g phase=%g",ao->id,ao->frequency,ao->phase); + continue; + } + + // other state atoms... + fprintf(stdout, "\n unknown state"); + } + fprintf(stdout,"\n"); + } +} + +double dummy::get_temperature() const { + pthread_mutex_lock((pthread_mutex_t*)&device_lock); + pthread_mutex_unlock((pthread_mutex_t*)&device_lock); + return 0.0; +} + +double dummy::set_setpoint(double temperature){ + pthread_mutex_lock(&device_lock); + pthread_mutex_unlock(&device_lock); + return 0.0; +} + +double dummy::get_setpoint() const { + pthread_mutex_lock((pthread_mutex_t*)&device_lock); + pthread_mutex_unlock((pthread_mutex_t*)&device_lock); + return 0.0; +} diff --git a/drivers/dummy/dummy.h b/drivers/dummy/dummy.h new file mode 100644 index 0000000..70c1df4 --- /dev/null +++ b/drivers/dummy/dummy.h @@ -0,0 +1,81 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + +****************************************************************************/ + +#include "drivers/device.h" +#include "drivers/ADC.h" +#include "drivers/frequgen.h" +#include "drivers/pulsegen.h" +#include "drivers/tempcont.h" +#include + +class dummy: public virtual device, public ADC, public frequgen, public pulsegen, public tempcont { + double frequency; + /** + save artificial results while running pulse program + */ + adc_results* new_results; + + public: + dummy(); + + virtual void set_frequency(double f) {frequency=f;} + + virtual void set_frequency(state& exp); + + /** + sample once after trigger signal + */ + virtual void sample_after_external_trigger(double rate, size_t no, double sens=5.0, size_t res=12); + + + virtual result* get_samples(double timeout=0.0); + + /** + gets information about daq settings + */ + virtual void set_daq(state& exp); + + /** + print states of the subprogram and generate ADC results + */ + virtual void run_pulse_program(state& exp); + + /** + do not wait, just continue + */ + virtual void wait_till_end(){} + + /** + temperature control: get the actual temperature + */ + virtual double get_temperature() const; + + /** + temperature control: set temperature control value + \return temperature, that is set + */ + virtual double set_setpoint(double temperature); + + /** + temperature control: get temperature control value + \return temperature, that is set + */ + virtual double get_setpoint() const; + + /** + */ + virtual configuration_result* configure(const configuration_device_section& conf, int run) { + if (run<10) { + fprintf(stdout, "%d\n",run); + return NULL; + } + conf.print(); + return new configuration_result(0); + } + + virtual ~dummy() {} +}; diff --git a/drivers/dummy/temperature_dummy.cpp b/drivers/dummy/temperature_dummy.cpp new file mode 100644 index 0000000..373f736 --- /dev/null +++ b/drivers/dummy/temperature_dummy.cpp @@ -0,0 +1,21 @@ +#include "dummy.h" + +int main() { + + // temperature dummy + tempcont* t=new dummy(); + + sleep(5); + t->set_history_stepsize(2); + sleep(10); + temp_history* h=t->get_history(0); + h->print_xml(stdout); + delete h; + sleep(5); + t->set_history_stepsize(0); + h=t->get_history(0); + h->print_xml(stdout); + delete h; + delete t; + +} diff --git a/drivers/frequgen.h b/drivers/frequgen.h new file mode 100644 index 0000000..2a7cd98 --- /dev/null +++ b/drivers/frequgen.h @@ -0,0 +1,64 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + +****************************************************************************/ +#ifndef FREQUGEN_H +#define FREQUGEN_H +#include "core/states.h" +#include "core/core_exception.h" + +/** + \addtogroup basedrivers + @{ +*/ + +/** + * frequency generator exception + */ +class frequ_exception: public RecoverableException +{ +public: + explicit frequ_exception(const std::string& msg) throw (): RecoverableException(msg) {} + explicit frequ_exception(const char* msg) throw (): RecoverableException(msg) {} + virtual ~frequ_exception() throw () {} +protected: + virtual const std::string prefix() const { return "ERROR (frequ_exception): "; } +}; + + +/** + \brief the frequency generator can set the reference frequency and pulse output + also phase shifts and different channels are possible + */ +class frequgen { + protected: + /** reference frequency output */ + double frequency; + /** + default phase offset of the frequency generator + */ + double phase; + /** + should move to frequgen + */ + int pulse_channel; + /** + should move to frequgen + */ + int gate_channel; + /** + should move to frequgen + */ + double gate_time; + public: + virtual void set_frequency(double f)=0; + virtual void set_frequency(state& experiment)=0; + virtual ~frequgen() {} +}; + +/** + @} +*/ +#endif diff --git a/drivers/pfggen.h b/drivers/pfggen.h new file mode 100644 index 0000000..2d99804 --- /dev/null +++ b/drivers/pfggen.h @@ -0,0 +1,58 @@ +/* ************************************************************************** + + Author: Markus Rosenstihl, Achim Gaedke + Created: Nov 2005 + +****************************************************************************/ +#ifndef PFGGEN_H +#define PFGGEN_H +#include "core/states.h" +#include "core/core_exception.h" + +/** + \addtogroup basedrivers + @{ +*/ + +/** + * PFG exception + */ +class pfg_exception: public RecoverableException +{ +public: + explicit pfg_exception(const std::string& msg) throw (): RecoverableException(msg) {} + explicit pfg_exception(const char* msg) throw (): RecoverableException(msg) {} + virtual ~pfg_exception() throw () {} +protected: + virtual const std::string prefix() const { return "ERROR (pfg_exception): "; } +}; + + +/** + * Generic base class for DAC drivers + */ +class GenericDAC { + protected: + /** no use by now... could be for default value at the end of an experiment?! */ + signed dac_value; + + public: + /** + no use by now! could be for default value at the end of an experiment?! + */ + virtual void set_dac(signed d)=0; + /** + does the real work: transmits data to dac during pulse sequence + */ + virtual void set_dac(state& experiment)=0; + + /** + cleanup: nothing to do + */ + virtual ~GenericDAC() {} +}; + +/** + @} +*/ +#endif diff --git a/drivers/pulsegen.h b/drivers/pulsegen.h new file mode 100644 index 0000000..80a2fad --- /dev/null +++ b/drivers/pulsegen.h @@ -0,0 +1,53 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + +****************************************************************************/ +#ifndef PULSEGEN_H +#define PULSEGEN_H + +#include +#include "core/states.h" +#include "core/core_exception.h" + +/** + \addtogroup basedrivers + @{ +*/ + +/** + * pulse generator exception + */ +class pulse_exception: public RecoverableException +{ +public: + explicit pulse_exception(const std::string& msg) throw (): RecoverableException(msg) {} + explicit pulse_exception(const char* msg) throw (): RecoverableException(msg) {} + virtual ~pulse_exception() throw () {} +protected: + virtual const std::string prefix() const { return "ERROR (pulse_exception): "; } +}; + +/** + \brief the pulse generator is the destination of the states sequences + */ + +class pulsegen { + public: + /** + compiles and runs the pulse program + */ + virtual void run_pulse_program(state& exp)=0; + + /** + wait till end of pulseprogram + */ + virtual void wait_till_end()=0; + + virtual ~pulsegen(){} +}; +/** + @} +*/ +#endif diff --git a/drivers/tempcont.cpp b/drivers/tempcont.cpp new file mode 100644 index 0000000..98d8712 --- /dev/null +++ b/drivers/tempcont.cpp @@ -0,0 +1,250 @@ +#include "tempcont.h" +#include +#include "errno.h" +#include +#include + +void temp_history::print_xml(FILE* f) const { + if (step==0) {fprintf(f,"\n");} + char timebuffer[30]; + ctime_r(&latest,timebuffer); + timebuffer[strlen(timebuffer)-1]=0; + fprintf(f,"",timebuffer,step); + if (!empty()) { + fprintf(f,"\n"); + for (const_iterator i=begin(); i!=end();++i) fprintf(f,"%g ",*i); + fprintf(f,"\n"); + } + fprintf(f,"\n"); +} + +configuration_result* temp_history::as_result() const { + configuration_result* res=new configuration_result(0); + XMLCh* myname=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("temphistory"); + XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* newelement=res->tag->createElement(myname); + res->tag->insertBefore(newelement,NULL); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&myname); + + // print step [s] + XMLCh* step_name=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("step"); + char step_value_char_buf[100]; + snprintf(step_value_char_buf,sizeof(step_value_char_buf),"%d",step); + XMLCh* step_value=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(step_value_char_buf); + newelement->setAttribute(step_name,step_value); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&step_name); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&step_value); + // print latest timestamp + XMLCh* latest_name=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("latest"); + char latest_value_char_buf[200]; + struct tm latest_broken_timeinfo; + localtime_r(&latest, &latest_broken_timeinfo); + strftime(latest_value_char_buf,sizeof(latest_value_char_buf),"%Y%m%d %H:%M:%S",&latest_broken_timeinfo); + XMLCh* latest_value=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(latest_value_char_buf); + newelement->setAttribute(latest_name,latest_value); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&latest_name); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&latest_value); + + // append values to buffer + if (!empty()) { + for (const_iterator i=begin(); i!=end(); ++i){ + char temp_char_buf[256]; + snprintf(temp_char_buf,sizeof(temp_char_buf),"%g\n",*i); + XMLCh* temp_line=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(temp_char_buf); + XERCES_CPP_NAMESPACE_QUALIFIER DOMText* textnode=res->tag->createTextNode(temp_line); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&temp_line); + newelement->insertBefore(textnode,NULL); + } + } + return res; +} + +void* history_maintainance_thread(void* temperature_sensor) { + tempcont* ts=(tempcont*)temperature_sensor; + // wait for completely initialised structure + ts->maintain_history(); + return NULL; +} + +temp_history* tempcont::get_history(size_t seconds_back) const { + temp_history* result_history=new temp_history(); + if (result_history==NULL) throw tempcont_error("failed to create new temp_history object"); + if (history_step!=0) { + pthread_mutex_lock((pthread_mutex_t*)&history_lock); + for (size_t i=0; ((seconds_back==0 || i*history_steppush_back(history_buffer[(history_latest_index+history_length-i)%history_length]); + pthread_mutex_unlock((pthread_mutex_t*)&history_lock); + result_history->step=history_step; + result_history->latest=history_latest_time; + } + return result_history; +} + +void tempcont::set_history_stepsize(size_t step) { + if (history_step==step) return; + pthread_mutex_lock((pthread_mutex_t*)&history_lock); + history_step=step; + if (step==0) { + // reinitalise history + history_used=0; + } + else { + history_latest_index=0; + history_used=1; + history_buffer[0]=get_temperature(); + history_latest_time=time(NULL); + } + pthread_mutex_unlock((pthread_mutex_t*)&history_lock); +} + +double tempcont::wait_setpoint_reached(double delta, size_t timeout) const { + // poll temperature every second + time_t end_polling=time(NULL)+timeout; + double setpoint=get_setpoint(); + double temperature=get_temperature(); + while (fabs(temperature-setpoint)>delta && (timeout==0 || end_polling>=time(NULL))) { + sleep(1); + temperature=get_temperature(); + } + return temperature; +} + +void tempcont::maintain_history() { + // wait for initialisation of derived class or failure + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL); + // sleep 0.2 seconds, that assures a check every second + timespec sleeptime; + sleeptime.tv_sec=0; + sleeptime.tv_nsec=200*1000*1000; + while (1) { + // do not maintain history until step is set to a reasonable value + if (history_step!=0) { + pthread_mutex_lock(&history_lock); + time_t now=time(NULL); + if (history_step<=difftime(now, history_latest_time)) { + double new_temp; + try { + new_temp=get_temperature(); + } + catch (tempcont_error e) { + pthread_mutex_unlock(&history_lock); + fprintf(stderr,"history maintainance thread caught exception: %s, terminating\n",e.what()); + device_failed=1; + pthread_exit(NULL); + } + // append the new temperature value + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL); + history_latest_time=now; + if (history_used==0) { + history_latest_index=0; + history_buffer[0]=new_temp; + history_used=1; + } + else { + history_latest_index++; + if (history_latest_index==history_length) history_latest_index=0; + history_buffer[history_latest_index]=new_temp; + if (history_used::const_iterator get=conf.attributes.find("get"); + if (get!=conf.attributes.end()) { + temp_history* h=get_history(0); + configuration_result* res=h->as_result(); + delete h; + return res; + } + + std::map::const_iterator set=conf.attributes.find("set"); + if (set!=conf.attributes.end()) { + // read new temperature setpoint + const char* temp_str=set->second.c_str(); + char* temp_str_end=NULL; + double new_temperature=strtod(temp_str,&temp_str_end); + if (temp_str_end!=set->second.size()+temp_str) { + throw tempcont_error("could not read new temperature setpoint from attributes"); + } + // set the temperature + double new_temp_return=set_setpoint(new_temperature); + if (new_temp_return!=new_temperature) { + char error_message[256]; + snprintf(error_message, sizeof(error_message), "could not set new temperature setpoint %f!=%f", + new_temp_return, + new_temperature); + throw tempcont_error(error_message); + } + + // report success + configuration_result* res=new configuration_result(0); + XMLCh* myname=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(conf.name.c_str()); + XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* newelement=res->tag->createElement(myname); + res->tag->insertBefore(newelement,NULL); + XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&myname); + return res; + } + + return NULL; +} + + +tempcont::tempcont() { + // initialise history + history_step=0; + // one day history (1s steps) + history_length=24*60*60; + history_used=0; + history_buffer=(double*)malloc(sizeof(double)*history_length); + if (history_buffer==NULL) throw tempcont_error("failed to allocate history"); + // no failures detected + device_failed=0; + // create history lock + pthread_mutexattr_t history_lock_attr; + pthread_mutexattr_init(&history_lock_attr); + pthread_mutex_init(&history_lock,&history_lock_attr); + pthread_mutexattr_destroy(&history_lock_attr); + // create device lock + pthread_mutexattr_t device_lock_attr; + pthread_mutexattr_init(&device_lock_attr); + pthread_mutex_init(&device_lock,&device_lock_attr); + pthread_mutexattr_destroy(&device_lock_attr); + // create history update thread + pthread_attr_t history_thread_attr; + pthread_attr_init(&history_thread_attr); + int result=pthread_create(&history_thread,&history_thread_attr,&history_maintainance_thread,this); + pthread_attr_destroy(&history_thread_attr); + // clean up, it this operation failed + if (result!=0) { + free(history_buffer); + pthread_mutex_destroy(&device_lock); + pthread_mutex_destroy(&history_lock); + throw tempcont_error("could not create history maintainance thread"); + } +} + +tempcont::~tempcont() { + // send cancelation + if (0!=pthread_cancel(history_thread) && errno!=EIO) { + fprintf(stderr,"thread cancelation failed\n"); + } + // wait for terminated thread + if (pthread_join(history_thread,NULL)!=0){ + fprintf(stderr,"thread joining failed\n"); + }; + if (history_buffer!=NULL) free(history_buffer); + pthread_mutex_destroy(&device_lock); + pthread_mutex_destroy(&history_lock); +} diff --git a/drivers/tempcont.h b/drivers/tempcont.h new file mode 100644 index 0000000..0fabe15 --- /dev/null +++ b/drivers/tempcont.h @@ -0,0 +1,187 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: January 2005 + +****************************************************************************/ + +#ifndef TEMPCONT_H +#define TEMPCONT_H + +#include +#include +#include +#include "device.h" + +/** + \addtogroup basedrivers + @{ +*/ + +/** + * Temperature control exception + */ +class tempcont_error: public RecoverableException +{ +public: + explicit tempcont_error(const std::string& msg) throw (): RecoverableException(msg) {} + explicit tempcont_error(const char* msg) throw (): RecoverableException(msg) {} + virtual ~tempcont_error() throw () {} +protected: + virtual const std::string prefix() const { return "ERROR (tempcont_error): "; } +}; + +/** + history of temperatures +*/ +class temp_history: public std::vector { + public: + /** state without values */ + temp_history() {latest=0; step=0;} + /** copy constructor */ + temp_history(const temp_history& orig): std::vector(orig) {latest=orig.latest;step=orig.step;} + /** + print xml like data to a file + */ + void print_xml(FILE* f) const; + /** + get back a configuration result object + */ + configuration_result* as_result() const; + /** + time of last element + */ + time_t latest; + /** + time difference between two elemnts + */ + size_t step; +}; + + +/** + abstract base class for temperature sensor with temperature history and controler + */ +class tempcont: public virtual device { + protected: + + /** + time interval between two history entries + */ + size_t history_step; + /** + time of latest entry + */ + time_t history_latest_time; + /** + index of latest value + */ + size_t history_latest_index; + /** + number of used entries + */ + size_t history_used; + /** + full length of history list + */ + size_t history_length; + /** + ring buffer of temperature history whith hist_length entries + */ + double* history_buffer; + /** + for history maintainance + */ + pthread_t history_thread; + /** + a mutex for device communication + */ + pthread_mutex_t device_lock; + /** + a mutex for history reading and updates + */ + pthread_mutex_t history_lock; + + /** + a value different from zero indicates a device failure due to communication errors or broken sensor + it is set by the history maintainance thread + */ + int device_failed; + + public: + /** + sets up the temperature controler to useful values + starts the temperature history thread (in deactivated state), the mutexes are setup + the function set_history_stepsize must be called after the communication is set up + */ + tempcont(); + + /** + + */ + virtual int device_failure() const {return device_failed;} + + /** + get a temperature history, that lasts the given number of seconds + \return an extract of the temperature history + */ + virtual temp_history* get_history(size_t duration) const; + + /** + wait for stable temperature + \return remaining temperature difference + */ + virtual double wait_setpoint_reached(double delta, size_t timeout) const; + + /** + get the actual temperature + function must be reentrant + */ + virtual double get_temperature() const=0; + + /** + the history stepsize is changed by this value. + if the step size is zero, history is disabled + changing the value clears the history, the first new history entry is gained immediately + */ + virtual void set_history_stepsize(size_t step); + + /** + get the length of the time interval between two history entries + \return the length, 0 if history updating is diabled + */ + virtual size_t get_history_stepsize() const {return history_step;}; + + /** + set temperature control value + \return temperature, that is set + */ + virtual double set_setpoint(double temperature)=0; + + /** + get temperature control value + \return temperature, that is set + */ + virtual double get_setpoint() const=0; + + /** + reads the temperature in time intervals and maintains the history + this function is only used by the history thread + */ + void maintain_history(); + + /** + terminates threads and exit + */ + virtual ~tempcont(); + + /** + set and read configuration + */ + virtual configuration_result* configure(const configuration_device_section& conf, int run); + +}; +/** + @} +*/ +#endif /* TEMPCONT_H */ diff --git a/machines/Makefile b/machines/Makefile new file mode 100644 index 0000000..728fe62 --- /dev/null +++ b/machines/Makefile @@ -0,0 +1,308 @@ +############################################################################# +# +# Author: Achim Gaedke +# Created: June 2004 +# +############################################################################# + + +CXX=g++ +CXXFLAGS=-g -O0 -W -Wall -Wextra -Wshadow -pedantic +CXXCPPFLAGS= -I. -I.. `pkg-config --cflags glib-2.0` +LDFLAGS= +LIBS=-lexpat -lxerces-c -lm `pkg-config --libs glib-2.0` + +EXEEXT = + +MACHINES = \ + dummycore$(EXEEXT) \ + deuteron_backend$(EXEEXT) \ + magnexgrad_backend \ + magnexgrad_backend_dds \ + PFGcore$(EXEEXT) \ + fc2_backend$(EXEEXT) \ + fc1neu_backend$(EXEEXT) \ + bg_backend \ + Mobilecore$(EXEEXT) \ + Mobile_wo_sync_backend$(EXEEXT) \ + berta \ + general + +DRV_SPC_MI40xx = ../drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.a +DRV_SPC_M2i40xx = ../drivers/Spectrum-M2i40xxSeries/Spectrum-M2i40xxSeries.a +DRV_PTS = ../drivers/PTS-Synthesizer/PTS.o +DRV_DAC20 = ../drivers/Tecmag-DAC20/DAC20.o +DRV_PB_24BIT = ../drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.o +DRV_PB_DDSIII = ../drivers/SpinCore-PulseBlasterDDSIII/SpinCore-PulseBlasterDDSIII.o +DRV_PB = ../drivers/SpinCore-PulseBlaster/SpinCore-PulseBlaster.o +DRV_PB_PROG = ../drivers/SpinCore-PulseBlaster/PulseBlasterProgram.o +DRV_EUROTHERM2000 = ../drivers/Eurotherm-2000Series/Eurotherm-2000Series.o +DRV_TEMPCONT = ../drivers/tempcont.o + + +LINK = $(CXX) $(LDFLAGS) +LINK_MACHINE = echo " Linking $@" && $(LINK) + +.cpp.o: + @echo " Compiling $<" + @$(CXX) -c $(CXXFLAGS) $(CXXCPPFLAGS) $< -o $@ + + +.PHONY: all clean install + +all: $(MACHINES) + +../tools/add_endline$(EXEEXT): ../tools/add_endline.cpp + $(CXX) $< -o $@ + +ifeq ($(shell uname -o),Cygwin) +../drivers/TiePie-HS3/TiePie-HS3.a: + $(MAKE) -C ../drivers/TiePie-HS3 TiePie-HS3.a + +../drivers/Datel-PCI416/Datel-PCI416.o: + $(MAKE) -C ../drivers/Datel-PCI416 Datel-PCI416.o + + +NQRcore$(EXEEXT): NQRcore.o \ + hardware.o \ + ../drivers/Datel-PCI416/Datel-PCI416.o \ + $(DRV_PB_DDSIII) $(DRV_PB) $(DRV_PB_PROG) \ + ../core/core.a + @$(LINK_MACHINE) $^ $(LIBS) -o $@ + +NQRcore.o: NQRcore.cpp + $(CXX) -c $(CXXFLAGS) $^ -o $@ + +endif + +../drivers/SpinCore-PulseBlasterPlus/SpinCore-PulseBlasterPlus.o: + $(MAKE) -C ../drivers/SpinCore-PulseBlasterPlus SpinCore-PulseBlasterPlus.o + +$(DRV_PB_DDSIII): + $(MAKE) -C ../drivers/SpinCore-PulseBlasterDDSIII SpinCore-PulseBlasterDDSIII.o + +$(DRV_PB_24BIT): + $(MAKE) -C ../drivers/SpinCore-PulseBlaster24Bit SpinCore-PulseBlaster24Bit.o + +$(DRV_PTS): + $(MAKE) -C ../drivers/PTS-Synthesizer PTS.o + +$(DRV_DAC20): + $(MAKE) -C ../drivers/Tecmag-DAC20 DAC20.o + +$(DRV_SPC_MI40xx): + $(MAKE) -C ../drivers/Spectrum-MI40xxSeries Spectrum-MI40xxSeries.o + +$(DRV_SPC_M2i40xx): + $(MAKE) -C ../drivers/Spectrum-M2i40xxSeries Spectrum-M2i40xxSeries.o + +$(DRV_EUROTHERM2000): + $(MAKE) -C ../drivers/Eurotherm-2000Series Eurotherm-2000Series.o + +$(DRV_TEMPCONT): + $(MAKE) -C ../drivers tempcont.o + +../drivers/dummy/dummy.o: + $(MAKE) -C ../drivers/dummy dummy.o + +../core/core.a: + $(MAKE) -C ../core core.a + +hardware.o: hardware.cpp hardware.h + + +clean: ../tools/add_endline$(EXEEXT) + for f in *.cpp *.h; do ../tools/add_endline$(EXEEXT) $$f;done + rm -f $(MACHINES) + rm -f *~ *.o *.exe *.stackdump + rm -f ../tools/add_endline$(EXEEXT) + +install: $(MACHINES) + install $(MACHINES) $(PREFIX) + + +Mobilecore$(EXEEXT): Mobilecore.o \ + hardware.o \ + $(DRV_SPC_MI40xx) \ + $(DRV_PTS) \ + $(DRV_PB_24BIT) $(DRV_PB) $(DRV_PB_PROG) \ + ../core/core.a + @$(LINK_MACHINE) $^ $(LIBS) -o $@ -lpthread + +birgit: birgit.o \ + hardware.o \ + $(DRV_SPC_MI40xx) \ + $(DRV_PTS) \ + $(DRV_PB_24BIT) $(DRV_PB) $(DRV_PB_PROG) \ + ../core/core.a + @$(LINK_MACHINE) $^ $(LIBS) -o $@ -lpthread + +birgit.o: birgit.cpp \ +../drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h ../drivers/SpinCore-PulseBlaster/SpinCore-PulseBlaster.h ../drivers/SpinCore-PulseBlaster/PulseBlasterProgram.h \ +../core/stopwatch.h ../drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h + +Mobilecore.o: Mobilecore.cpp \ +../drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h ../drivers/SpinCore-PulseBlaster/SpinCore-PulseBlaster.h ../drivers/SpinCore-PulseBlaster/PulseBlasterProgram.h \ +../core/stopwatch.h ../drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h ../drivers/Spectrum-M2i40xxSeries/Spectrum-M2i40xxSeries.h + +Mobile_wo_sync_backend$(EXEEXT): Mobile_wo_sync_backend.o \ + hardware.o \ + $(DRV_SPC_MI40xx) \ + $(DRV_PTS) \ + $(DRV_PB_24BIT) $(DRV_PB) $(DRV_PB_PROG) \ + ../core/core.a + @$(LINK_MACHINE) $^ $(LIBS) -o $@ -lpthread + +Mobile_wo_sync_backend.o: Mobile_wo_sync_backend.cpp \ +../drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h ../drivers/SpinCore-PulseBlaster/SpinCore-PulseBlaster.h ../drivers/SpinCore-PulseBlaster/PulseBlasterProgram.h \ +../core/stopwatch.h ../drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h ../drivers/Spectrum-M2i40xxSeries/Spectrum-M2i40xxSeries.h + + +bg_backend: bg_backend.o \ + hardware.o \ + $(DRV_SPC_MI40xx) \ + $(DRV_PTS) \ + $(DRV_PB_24BIT) $(DRV_PB) $(DRV_PB_PROG) \ + $(DRV_EUROTHERM2000) $(DRV_TEMPCONT) \ + ../core/core.a + @$(LINK_MACHINE) $^ $(LIBS) -o $@ -lpthread + +bg_backend.o: bg_backend.cpp ../drivers/PTS-Synthesizer/PTS.h \ +../drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h \ +../drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h + + +magnexgrad_backend_dds: magnexgrad_backend_dds.o \ + hardware.o \ + $(DRV_SPC_MI40xx) \ + $(DRV_PTS) \ + $(DRV_PB_DDSIII) $(DRV_PB) $(DRV_PB_PROG) \ + $(DRV_EUROTHERM2000) $(DRV_TEMPCONT) \ + ../core/core.a + @$(LINK_MACHINE) $^ $(LIBS) -o $@ -lpthread + +magnexgrad_backend_dds.o: magnexgrad_backend_dds.cpp \ +../drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h \ +../drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h + + +magnexgrad_backend: magnexgrad_backend.o \ + hardware.o \ + $(DRV_SPC_MI40xx) \ + $(DRV_PTS) \ + $(DRV_PB_24BIT) $(DRV_PB) $(DRV_PB_PROG) \ + $(DRV_EUROTHERM2000) $(DRV_TEMPCONT) \ + ../core/core.a + @$(LINK_MACHINE) $^ $(LIBS) -o $@ -lpthread + +magnexgrad_backend.o: magnexgrad_backend.cpp \ +../drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h \ +../drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h + +deuteron_backend.o: deuteron_backend.cpp \ +../drivers/SpinCore-PulseBlasterDDSIII/SpinCore-PulseBlasterDDSIII.h \ +../drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h + +deuteron_backend$(EXEEXT): deuteron_backend.o \ + hardware.o \ + $(DRV_SPC_MI40xx) \ + $(DRV_PTS) \ + $(DRV_PB_DDSIII) $(DRV_PB) $(DRV_PB_PROG) \ + ../core/core.a + @$(LINK_MACHINE) $^ $(LIBS) -o $@ -lpthread + +dummycore.o: dummycore.cpp ../drivers/dummy/dummy.o + +dummycore$(EXEEXT): dummycore.o \ + hardware.o \ + ../drivers/dummy/dummy.o \ + $(DRV_TEMPCONT) \ + ../core/core.a + @$(LINK_MACHINE) $^ $(LIBS) -o $@ -lpthread + + +PFGcore$(EXEEXT): PFGcore.o \ + hardware.o \ + $(DRV_SPC_MI40xx) \ + $(DRV_PTS) \ + $(DRV_DAC20) \ + $(DRV_PB_24BIT) $(DRV_PB) $(DRV_PB_PROG) \ + ../core/core.a + @$(LINK_MACHINE) $^ $(LIBS) -o $@ -lpthread + +PFGcore.o: PFGcore.cpp \ +../drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h ../drivers/SpinCore-PulseBlaster/SpinCore-PulseBlaster.h ../drivers/SpinCore-PulseBlaster/PulseBlasterProgram.h ../drivers/Tecmag-DAC20/DAC20.h\ +../core/stopwatch.h ../drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h + +fc2_backend$(EXEEXT): fc2_backend.o \ + hardware.o \ + $(DRV_SPC_MI40xx) \ + $(DRV_PTS) \ + $(DRV_DAC20) \ + $(DRV_PB_24BIT) $(DRV_PB) $(DRV_PB_PROG) \ + ../core/core.a + @$(LINK_MACHINE) $^ $(LIBS) -o $@ -lpthread + +fc2_backend.o: fc2_backend.cpp \ +../drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h ../drivers/SpinCore-PulseBlaster/SpinCore-PulseBlaster.h ../drivers/SpinCore-PulseBlaster/PulseBlasterProgram.h ../drivers/Tecmag-DAC20/DAC20.h\ +../core/stopwatch.h ../drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h + +berta: berta.o \ + hardware.o \ + $(DRV_SPC_MI40xx) \ + $(DRV_PTS) \ + $(DRV_PB_24BIT) $(DRV_PB) $(DRV_PB_PROG) \ + ../core/core.a + @$(LINK_MACHINE) $^ $(LIBS) -o $@ -lpthread + +berta.o: berta.cpp \ +../drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h ../drivers/SpinCore-PulseBlaster/SpinCore-PulseBlaster.h ../drivers/SpinCore-PulseBlaster/PulseBlasterProgram.h \ +../core/stopwatch.h ../drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h + +fc1neu_backend$(EXEEXT): fc1neu_backend.o \ + hardware.o \ + $(DRV_SPC_MI40xx) \ + $(DRV_PTS) \ + $(DRV_DAC20) \ + $(DRV_PB_24BIT) $(DRV_PB) $(DRV_PB_PROG) \ + ../core/core.a + @$(LINK_MACHINE) $^ $(LIBS) -o $@ -lpthread + +fc1neu_backend.o: fc1neu_backend.cpp \ +../drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h ../drivers/SpinCore-PulseBlaster/SpinCore-PulseBlaster.h ../drivers/SpinCore-PulseBlaster/PulseBlasterProgram.h ../drivers/Tecmag-DAC20/DAC20.h\ +../core/stopwatch.h ../drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h + +fc1_vierkanal_backend$(EXEEXT): fc1_vierkanal_backend.o \ + hardware.o \ + $(DRV_SPC_M2i40xx) \ + $(DRV_PTS) \ + $(DRV_DAC20) \ + $(DRV_PB_24BIT) $(DRV_PB) $(DRV_PB_PROG) \ + ../core/core.a + @$(LINK_MACHINE) $^ $(LIBS) -o $@ -lpthread -lspcm_linux + +fc1_vierkanal_backend.o: fc1_vierkanal_backend.cpp \ +../drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h \ +../drivers/SpinCore-PulseBlaster/SpinCore-PulseBlaster.h \ +../drivers/SpinCore-PulseBlaster/PulseBlasterProgram.h \ +../drivers/Tecmag-DAC20/DAC20.h \ +../core/stopwatch.h \ +../drivers/Spectrum-M2i40xxSeries/Spectrum-M2i40xxSeries.h + +general: general.o \ + hardware.o \ + $(DRV_SPC_MI40xx) \ + $(DRV_PTS) \ + $(DRV_PB_24BIT) $(DRV_PB) $(DRV_PB_PROG) \ + ../core/core.a + @$(LINK_MACHINE) $^ $(LIBS) -o $@ -lpthread + +general.o: general.cpp \ +../drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h ../drivers/SpinCore-PulseBlaster/SpinCore-PulseBlaster.h ../drivers/SpinCore-PulseBlaster/PulseBlasterProgram.h \ + +pb_radio_processor_g_backend: pb_radio_processor_g_backend.o \ + ../core/core.a + @$(LINK_MACHINE) $^ $(LIBS) -o $@ -lpthread + +pb_radio_processor_g_backend.o: pb_radio_processor_g_backend.cpp \ + pb_radio_processor_g_backend.h diff --git a/machines/Mobile_wo_sync_backend.cpp b/machines/Mobile_wo_sync_backend.cpp new file mode 100644 index 0000000..260dcd8 --- /dev/null +++ b/machines/Mobile_wo_sync_backend.cpp @@ -0,0 +1,109 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + + ****************************************************************************/ +#include "machines/hardware.h" +#include "core/core.h" +#include "drivers/PTS-Synthesizer/PTS.h" +#include "drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h" +#include "drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h" + +/** + \defgroup mobilemachine Mobile NMR Spectrometer + \ingroup machines + Uses Spincore Pulseblaster 24 Bit and Spectrum MI4021, PTS310 with cable driver with phase control and no synchronization board + \li line 0 for gate + \li line 1 for pulse + \li llne 17 for trigger + \li line 16 free (not using synchronization board) + + \par Starting the hardware + This procedure should assure the correct initialisation of the hardware: + \li Switch off main switches of SpinCore Pulseblaster and Computer (the main switch of the computer is at the rear) + \li Switch on Computer and start Windows or linux + + @{ + */ + +/** + */ +class Mobile_hardware: public hardware +{ + + PTS* my_pts; + SpinCorePulseBlaster24Bit* my_pulseblaster; + SpectrumMI40xxSeries* my_adc; + +public: + Mobile_hardware() + { + ttlout trigger; + trigger.id = 0; + /* trigger on line 17 */ + trigger.ttls = 1 << 17; + my_adc = new SpectrumMI40xxSeries(trigger); + /* device_id=0, clock=100MHz, sync_mask: Bit 16 */ + my_pulseblaster = new SpinCorePulseBlaster24Bit(0, 1e8, 0 << 16); + my_pts = new PTS_latched(0); + + // publish devices + the_pg = my_pulseblaster; + the_adc = my_adc; + the_fg = my_pts; + } + + virtual ~Mobile_hardware() + { + if (the_adc != NULL) + delete the_adc; + if (the_fg != NULL) + delete the_fg; + if (the_pg != NULL) + delete the_pg; + } + +}; + +/** + \brief brings standard core together with the Mobile NMR hardware + */ +class Mobile_core: public core +{ + std::string the_name; +public: + Mobile_core(const core_config& conf) : + core(conf) + { + the_hardware = new Mobile_hardware(); + the_name = "Mobile backend without synchronisation card"; + } + virtual const std::string& core_name() const + { + return the_name; + } +}; + +/** + @} + */ + +int main(int argc, const char** argv) +{ + int return_result = 0; + try + { + core_config my_conf(argv, argc); + // setup input and output + Mobile_core my_core(my_conf); + // start core application + my_core.run(); + } + catch (const DamarisException& e) + { + fprintf(stderr, "%s\n", e.what()); + return_result = 1; + } + return return_result; +} diff --git a/machines/Mobilecore.cpp b/machines/Mobilecore.cpp new file mode 100644 index 0000000..938ecc9 --- /dev/null +++ b/machines/Mobilecore.cpp @@ -0,0 +1,140 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + + ****************************************************************************/ +#include "machines/hardware.h" +#include "core/core.h" +#include "drivers/PTS-Synthesizer/PTS.h" +#include "drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h" +#include "drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h" + +/** + \defgroup mobilemachine Mobile NMR Spectrometer + \ingroup machines + Uses Spincore Pulseblaster 24 Bit and Spectrum MI4021, PTS310 with cabledriver with phase control and a synchronization board + \li line 0 for gate + \li line 1 for pulse + \li line 17 for trigger + \li line 16 for synchronization + \par Starting the hardware + This procedure should assure the correct initialisation of the hardware: + \li Switch off main switches of SpinCore Pulseblaster and Computer (the main switch of the computer is at the rear) + \li Switch on Computer and start Windows or linux + + @{ + */ + +class Mobile_hardware: public hardware +{ + + PTS* my_pts; + SpinCorePulseBlaster24Bit* my_pulseblaster; + SpectrumMI40xxSeries* my_adc; + +public: + Mobile_hardware() + { + ttlout trigger; + trigger.id = 0; + trigger.ttls = 1 << 17; /* trigger on line 17 */ + my_adc = new SpectrumMI40xxSeries(trigger); + // device_id=0, clock=100MHz, sync_mask: Bit 16 + my_pulseblaster = new SpinCorePulseBlaster24Bit(0, 1e8, 1 << 16); + my_pts = new PTS_latched(0); + + // publish devices + the_pg = my_pulseblaster; + the_adc = my_adc; + the_fg = my_pts; + } + + result* experiment(const state& exp) + { + result* r = NULL; + for (size_t tries = 0; r == NULL && core::term_signal == 0 && tries < 102; ++tries) + { + state* work_copy = exp.copy_flat(); + if (work_copy == NULL) + return new error_result(1, "could create work copy of experiment sequence"); + try + { + if (the_fg != NULL) + the_fg->set_frequency(*work_copy); + if (the_adc != NULL) + the_adc->set_daq(*work_copy); + // the pulse generator is necessary + my_pulseblaster->run_pulse_program_w_sync(*work_copy, my_adc->get_sample_clock_frequency()); + // wait for pulse generator + the_pg->wait_till_end(); + // after that, the result must be available + if (the_adc != NULL) + r = the_adc->get_samples(); + else + r = new adc_result(1, 0, NULL); + } + catch (const RecoverableException &e) + { + r = new error_result(1, e.what()); + } + delete work_copy; + if (core::quit_signal != 0) + break; + } + return r; + } + + virtual ~Mobile_hardware() + { + if (the_adc != NULL) + delete the_adc; + if (the_fg != NULL) + delete the_fg; + if (the_pg != NULL) + delete the_pg; + } + +}; + +/** + \brief brings standard core together with the Mobile NMR hardware + */ +class Mobile_core: public core +{ + std::string the_name; +public: + Mobile_core(const core_config& conf) : + core(conf) + { + the_hardware = new Mobile_hardware(); + the_name = "Mobile core"; + } + virtual const std::string& core_name() const + { + return the_name; + } +}; + +/** + @} + */ + +int main(int argc, const char** argv) +{ + int return_result = 0; + try + { + core_config my_conf(argv, argc); + // setup input and output + Mobile_core my_core(my_conf); + // start core application + my_core.run(); + } + catch (const DamarisException& e) + { + fprintf(stderr, "%s\n", e.what()); + return_result = 1; + } + return return_result; +} diff --git a/machines/NQRcore.cpp b/machines/NQRcore.cpp new file mode 100644 index 0000000..e836660 --- /dev/null +++ b/machines/NQRcore.cpp @@ -0,0 +1,81 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + + ****************************************************************************/ +#include "machines/hardware.h" +#include "core/core.h" +#include "drivers/Datel-PCI416/Datel-PCI416.h" +#include "drivers/SpinCore-PulseBlasterDDSIII/SpinCore-PulseBlasterDDSIII.h" + +/** + \defgroup nqrmachine NQR machine + \ingroup machines + @{ + */ +class NQR_hardware: public hardware +{ + +public: + NQR_hardware() + { + ttlout trigger; + trigger.id = 0; + trigger.ttls = 1 << 2; + the_adc = new DatelPCI416(trigger); + SpinCorePulseBlasterDDSIII* PB = new SpinCorePulseBlasterDDSIII(); + the_pg = PB; + the_fg = PB; /* the Pulseblaster Card is used for both*/ + } + + ~NQR_hardware() + { + if (the_adc != NULL) + delete the_adc; + if (the_pg != NULL) + delete the_pg; + } + +}; + +class NQR_core: public core +{ + std::string my_name; + +public: + NQR_core(const core_config& conf) : + core(conf), + my_name("NQR machine") + { + the_hardware = new NQR_hardware(); + } + + virtual const std::string& core_name() const + { + return my_name; + } +}; + +/** + @} + */ + +int main(int argc, const char** argv) +{ + int return_result = 0; + try + { + core_config my_conf(argv, argc); + // setup input and output + NQR_core my_core(my_conf); + // start core application + my_core.run(); + } + catch (const DamarisException& e) + { + fprintf(stderr, "%s\n", e.what()); + return_result = 1; + } + return return_result; +} diff --git a/machines/PFGcore.cpp b/machines/PFGcore.cpp new file mode 100644 index 0000000..01aa7e9 --- /dev/null +++ b/machines/PFGcore.cpp @@ -0,0 +1,146 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + + ****************************************************************************/ +#include "machines/hardware.h" +#include "core/core.h" +#include "drivers/Tecmag-DAC20/DAC20.h" +#include "drivers/PTS-Synthesizer/PTS.h" +#include "drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h" +#include "drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h" + +/** + \defgroup pfgmachine PFG NMR Spectrometer + \ingroup machines + Uses Spincore Pulseblaster 24 Bit, Spectrum MI4021, one DAC20 and a synchronization board. + \li line 0 for gate + \li line 1 for pulse + \li line 22 for trigger + \li line 23 for synchronization + + \par Starting the hardware + This procedure should assure the correct initialisation of the hardware: + \li Switch off main switches of SpinCore Pulseblaster and Computer (the main switch of the computer is at the rear) + \li Switch on Computer and start Windows or linux + + @{ + */ + +class PFG_hardware: public hardware +{ + + SpinCorePulseBlaster24Bit* my_pulseblaster; + SpectrumMI40xxSeries* my_adc; + +public: + PFG_hardware() + { + ttlout trigger; + trigger.id = 0; + /* trigger on line 22 */ + trigger.ttls = 1 << 22; + my_adc = new SpectrumMI40xxSeries(trigger); + /* device_id = 0, clock = 100 MHz, sync_mask = Bit 23 */ + my_pulseblaster = new SpinCorePulseBlaster24Bit(0, 1e8, 1 << 23); + /* PTS has analog id = 0 */ + PTS* my_pts = new PTS_latched(0); + the_fg = my_pts; + the_pg = my_pulseblaster; + the_adc = my_adc; + /* DAC has analog id = 1 */ + DAC20* my_pfg = new DAC20(1); + list_dacs.push_back(my_pfg); + } + + result* experiment(const state& exp) + { + result* r = NULL; + for (size_t tries = 0; r == NULL && core::term_signal == 0 && tries < 102; ++tries) + { + state* work_copy = exp.copy_flat(); + if (work_copy == NULL) + return new error_result(1, "could create work copy of experiment sequence"); + try + { + if (the_fg != NULL) + the_fg->set_frequency(*work_copy); + if (the_adc != NULL) + the_adc->set_daq(*work_copy); + experiment_prepare_dacs(work_copy); + // the pulse generator is necessary + my_pulseblaster->run_pulse_program_w_sync(*work_copy, my_adc->get_sample_clock_frequency()); + // wait for pulse generator + the_pg->wait_till_end(); + // after that, the result must be available + if (the_adc != NULL) + r = the_adc->get_samples(); + else + r = new adc_result(1, 0, NULL); + } + catch (const RecoverableException &e) + { + r = new error_result(1, e.what()); + } + delete work_copy; + if (core::quit_signal != 0) + break; + } + return r; + } + + virtual ~PFG_hardware() + { + if (the_adc != NULL) + delete the_adc; + if (the_pg != NULL) + delete the_pg; + if (the_fg != NULL) + delete the_fg; + } + +}; + +/** + \brief brings standard core together with the Mobile NMR hardware + */ +class PFG_core: public core +{ + std::string the_name; +public: + PFG_core(const core_config& conf) : + core(conf) + { + the_hardware = new PFG_hardware(); + the_name = "PFG core"; + } + virtual const std::string& core_name() const + { + return the_name; + } +}; + +/** + @} + */ + +int main(int argc, const char** argv) +{ + int return_result = 0; + try + { + core_config my_conf(argv, argc); + // setup input and output + PFG_core my_core(my_conf); + // start core application + my_core.run(); + } + catch (const DamarisException& e) + { + fprintf(stderr, "%s\n", e.what()); + return_result = 1; + } + + return return_result; +} diff --git a/machines/berta.cpp b/machines/berta.cpp new file mode 100644 index 0000000..e467a04 --- /dev/null +++ b/machines/berta.cpp @@ -0,0 +1,145 @@ +/* ************************************************************************** + + Author: Markus Rosenstihl + Created: June 2010 + + ****************************************************************************/ +#include "machines/hardware.h" +#include "core/core.h" +#include "drivers/PTS-Synthesizer/PTS.h" +#include "drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h" +#include "drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h" + +/** + \defgroup bertamachine Berta NMR Spectrometer + \ingroup machines + Uses: + \li Spincore Pulseblaster 24 Bit (SP 17) which has a reference clock with 50 MHz + \li Spectrum MI4021 with gated sampling option (PB ref. clock is fed to Ext.Clock, impedance set to 1 MOhm) + \li Programmed Test Sources PTS 310 frequency synthesizer with phase control (0.225 degrees step size) + + + \par Starting the hardware + This procedure should assure the correct initialisation of the hardware: + \li Switch off main switches of SpinCore Pulseblaster and Computer (the main switch of the computer is at the rear) + \li Switch on Computer and start Windows or linux + + @{ + */ + +class Berta_hardware: public hardware +{ + PTS* my_pts; + SpinCorePulseBlaster24Bit* my_pulseblaster; + SpectrumMI40xxSeries* my_adc; + +public: + Berta_hardware() + { + ttlout trigger; + trigger.id = 0; + /* trigger on line 22 */ + trigger.ttls = 1 << 22; + int ext_reference_clock = (int) 50e6; // 50 MHz from PB24 SP17; defaults to 100MHz (PB24 SP 2) + double impedance = 50; // Ohm ( or 50 Ohm) + my_adc = new SpectrumMI40xxSeries(trigger, impedance, ext_reference_clock); + // device_id=0, clock=100MHz, sync_mask: Bit 23 + my_pulseblaster = new SpinCorePulseBlaster24Bit(0, 1e8, 1 << 23); + my_pts = new PTS_latched(0); + // PTS 500 has 0.36 ; PTS 310 has 0.225 degrees/step + my_pts->phase_step = 0.225; + + // publish devices + the_pg = my_pulseblaster; + the_adc = my_adc; + the_fg = my_pts; + } + + result* experiment(const state& exp) + { + result* r = NULL; + for (size_t tries = 0; r == NULL && core::term_signal == 0 && tries < 102; ++tries) + { + state* work_copy = exp.copy_flat(); + if (work_copy == NULL) + return new error_result(1, "could create work copy of experiment sequence"); + try + { + if (the_fg != NULL) + the_fg->set_frequency(*work_copy); + if (the_adc != NULL) + the_adc->set_daq(*work_copy); + // the pulse generator is necessary + my_pulseblaster->run_pulse_program_w_sync(*work_copy, my_adc->get_sample_clock_frequency()); + // wait for pulse generator + the_pg->wait_till_end(); + // after that, the result must be available + if (the_adc != NULL) + r = the_adc->get_samples(); + else + r = new adc_result(1, 0, NULL); + } + catch (const RecoverableException &e) + { + r = new error_result(1, e.what()); + } + delete work_copy; + if (core::quit_signal != 0) + break; + } + return r; + } + + virtual ~Berta_hardware() + { + if (the_adc != NULL) + delete the_adc; + if (the_fg != NULL) + delete the_fg; + if (the_pg != NULL) + delete the_pg; + } + +}; + +/** + \brief brings standard core together with the Berta NMR hardware + */ +class Berta_core: public core +{ + std::string the_name; +public: + Berta_core(const core_config& conf) : + core(conf) + { + the_hardware = new Berta_hardware(); + the_name = "berta core"; + } + virtual const std::string& core_name() const + { + return the_name; + } +}; + +/** + @} + */ + +int main(int argc, const char** argv) +{ + int return_result = 0; + try + { + core_config my_conf(argv, argc); + // setup input and output + Berta_core my_core(my_conf); + // start core application + my_core.run(); + } + catch (const DamarisException& e) + { + fprintf(stderr, "%s\n", e.what()); + return_result = 1; + } + return return_result; +} diff --git a/machines/bg_backend.cpp b/machines/bg_backend.cpp new file mode 100644 index 0000000..a57ce8f --- /dev/null +++ b/machines/bg_backend.cpp @@ -0,0 +1,98 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: October 2006 + + ****************************************************************************/ +#include "machines/hardware.h" +#include "core/core.h" +#include "drivers/PTS-Synthesizer/PTS.h" +#include "drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h" +#include "drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h" + +/** + \defgroup mobilemachine Mobile NMR Spectrometer + \ingroup machines + Uses Spincore Pulseblaster 24 Bit and Spectrum MI4021, but there is no cable driver with phase control + + \par Starting the hardware + This procedure should assure the correct initialisation of the hardware: + \li Switch off main switches of SpinCore Pulseblaster and Computer (the main switch of the computer is at the rear) + \li Switch on Computer and start Windows or linux + + @{ + */ + + + +/** + line 0 for gate + line 1 for pulse + line 2 for trigger + line 3 free + */ +class bg_hardware: public hardware { + +public: + bg_hardware(){ + ttlout trigger; + trigger.id=0; + trigger.ttls=4; /* line 2 */ + int refclock=50e6; /* 50 MHz for Pulseblaster SP-17; 100 MHz for SP-2 */ + double impedance=1e6; /* 1MOhm or 50 Ohm impedance */ + the_adc=new SpectrumMI40xxSeries(trigger, impedance, refclock); + the_pg=new SpinCorePulseBlaster24Bit(); + PTS* my_pts=new PTS(0); + + ttlout t; + for (int i=23; i>15; --i) { + t.ttls=std::bitset<32>(1<ttl_masks.push_back(t); + } + my_pts->negative_logic=0; + the_fg=my_pts; + } + + virtual ~bg_hardware() { + if (the_adc!=NULL) delete the_adc; + if (the_fg!=NULL) delete the_fg; + if (the_pg!=NULL) delete the_pg; + } + +}; + +/** + \brief brings standard core together with the Mobile NMR hardware + */ +class bg_core: public core { + std::string the_name; +public: + bg_core(const core_config& conf): core(conf) { + the_hardware=new bg_hardware(); + the_name="Burkhard's core"; + } + virtual const std::string& core_name() const { + return the_name; + } +}; + +/** + @} + */ + +int main(int argc, const char** argv) { + int return_result=0; + try { + core_config my_conf(argv, argc); + // setup input and output + bg_core my_core(my_conf); + // start core application + my_core.run(); + } + catch(const DamarisException& e) + { + fprintf(stderr,"%s\n",e.what()); + return_result=1; + } + return return_result; +} diff --git a/machines/birgit.cpp b/machines/birgit.cpp new file mode 100644 index 0000000..786b27d --- /dev/null +++ b/machines/birgit.cpp @@ -0,0 +1,125 @@ +/* ************************************************************************** + + Author: Markus Rosenstihl + Created: 2012, based on berta.cpp + + ****************************************************************************/ +#include "machines/hardware.h" +#include "core/core.h" +#include "drivers/PTS-Synthesizer/PTS.h" +#include "drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h" +#include "drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h" + +/** + \defgroup mobilemachine Mobile NMR Spectrometer + \ingroup machines + Uses Spincore Pulseblaster 24 Bit and Spectrum MI4021, PTS310 with cabledriver with phase control and a synchronization board + \li line 0 for gate + \li line 1 for pulse + \li line 17 for trigger + \li line 16 for synchronization + \par Starting the hardware + This procedure should assure the correct initialisation of the hardware: + \li Switch off main switches of SpinCore Pulseblaster and Computer (the main switch of the computer is at the rear) + \li Switch on Computer and start Windows or linux + + @{ + */ + + +class Mobile_hardware: public hardware { + + PTS* my_pts; + SpinCorePulseBlaster24Bit* my_pulseblaster; + SpectrumMI40xxSeries* my_adc; + +public: + Mobile_hardware(){ + ttlout trigger; + trigger.id=0; + trigger.ttls=1<<17; /* trigger on line 17 */ + my_adc=new SpectrumMI40xxSeries(trigger); + // device_id=0, clock=100MHz, sync_mask: Bit 16 + my_pulseblaster=new SpinCorePulseBlaster24Bit(0,1e8,1<<16); + my_pts=new PTS_latched(0); + my_pts->phase_step=0.72; + + // publish devices + the_pg=my_pulseblaster; + the_adc=my_adc; + the_fg=my_pts; + } + + result* experiment(const state& exp) { + result* r=NULL; + for(size_t tries=0; r==NULL && core::term_signal==0 && tries<102; ++tries) { + state* work_copy=exp.copy_flat(); + if (work_copy==NULL) return new error_result(1,"could create work copy of experiment sequence"); + try { + if (the_fg!=NULL) + the_fg->set_frequency(*work_copy); + if (the_adc!=NULL) + the_adc->set_daq(*work_copy); + // the pulse generator is necessary + my_pulseblaster->run_pulse_program_w_sync(*work_copy, my_adc->get_sample_clock_frequency()); + // wait for pulse generator + the_pg->wait_till_end(); + // after that, the result must be available + if (the_adc!=NULL) + r=the_adc->get_samples(); + else + r=new adc_result(1,0,NULL); + } + catch (const RecoverableException &e) + { + r=new error_result(1,e.what()); + } + delete work_copy; + if (core::quit_signal!=0) break; + } + return r; + } + + virtual ~Mobile_hardware() { + if (the_adc!=NULL) delete the_adc; + if (the_fg!=NULL) delete the_fg; + if (the_pg!=NULL) delete the_pg; + } + +}; + +/** + \brief brings standard core together with the Mobile NMR hardware + */ +class Mobile_core: public core { + std::string the_name; +public: + Mobile_core(const core_config& conf): core(conf) { + the_hardware=new Mobile_hardware(); + the_name="Mobile core"; + } + virtual const std::string& core_name() const { + return the_name; + } +}; + +/** + @} + */ + +int main(int argc, const char** argv) { + int return_result=0; + try { + core_config my_conf(argv, argc); + // setup input and output + Mobile_core my_core(my_conf); + // start core application + my_core.run(); + } + catch(const DamarisException& e) + { + fprintf(stderr,"%s\n",e.what()); + return_result=1; + } + return return_result; +} diff --git a/machines/deuteron_backend.cpp b/machines/deuteron_backend.cpp new file mode 100644 index 0000000..df0ae5f --- /dev/null +++ b/machines/deuteron_backend.cpp @@ -0,0 +1,125 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + + ****************************************************************************/ +#include "machines/hardware.h" +#include "core/core.h" +#include "drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h" +#include "drivers/SpinCore-PulseBlasterDDSIII/SpinCore-PulseBlasterDDSIII.h" + +/** + \defgroup deuteronmachine Magnex Static Gradient NMR Spectrometer + \ingroup machines + Uses Spincore Pulseblaster DDSIII and Spectrum MI4021 + + + \par Starting the hardware + Switch on the amplifier + @{ + */ + +/** + line 0 for gate + line 1 for pulse + line 2 for trigger + line 3 for sync active + */ +class deuteron_hardware: public hardware { + + SpinCorePulseBlasterDDSIII* dds; + SpectrumMI40xxSeries* my_adc; + + +public: + deuteron_hardware(){ + ttlout trigger; + trigger.id=0; + trigger.ttls=4; /* line 2 */ + my_adc=new SpectrumMI40xxSeries(trigger); + dds=new SpinCorePulseBlasterDDSIII(0, // ttlout id + 100e6, //clock + 0x8); // sync active: line 3 + + the_adc=my_adc; + the_pg=dds; + the_fg=dds; + the_tc=NULL; + + } + + virtual result* experiment(const state& exp) { + result* r=NULL; + for(size_t tries=0; r==NULL && core::term_signal==0 && tries<102; ++tries) { + state* work_copy=exp.copy_flat(); + if (work_copy==NULL) return new error_result(1,"could create work copy of experiment sequence"); + try { + if (the_fg!=NULL) + the_fg->set_frequency(*work_copy); + if (the_adc!=NULL) + the_adc->set_daq(*work_copy); + // the pulse generator is necessary + dds->run_pulse_program_w_sync(*work_copy, my_adc->get_sample_clock_frequency()); + // wait for pulse generator + the_pg->wait_till_end(); + // after that, the result must be available + if (the_adc!=NULL) + r=the_adc->get_samples(); + else + r=new adc_result(1,0,NULL); + } + catch (const RecoverableException &e) + { + r=new error_result(1,e.what()); + } + delete work_copy; + if (core::quit_signal!=0) break; + } + return r; + } + + + virtual ~deuteron_hardware() { + if (the_adc!=NULL) {delete the_adc; the_adc=my_adc=NULL;} + if (the_pg!=NULL) {delete the_pg; the_pg=dds=NULL;} + if (the_tc!=NULL) delete the_tc; + } + +}; + +/** + \brief brings standard core together with the deuteron spectrometer hardware + */ +class deuteron_core: public core { + std::string the_name; +public: + deuteron_core(const core_config& conf): core(conf) { + the_hardware=new deuteron_hardware(); + the_name="deuteron"; + } + virtual const std::string& core_name() const { + return the_name; + } +}; + +/** + @} + */ + +int main(int argc, const char** argv) { + int return_result=0; + try { + core_config my_conf(argv, argc); + // setup input and output + deuteron_core my_core(my_conf); + // start core application + my_core.run(); + } + catch(const DamarisException& e) + { + fprintf(stderr,"%s\n",e.what()); + return_result=1; + } + return return_result; +} diff --git a/machines/dummycore.cpp b/machines/dummycore.cpp new file mode 100644 index 0000000..68d57e8 --- /dev/null +++ b/machines/dummycore.cpp @@ -0,0 +1,84 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + + ****************************************************************************/ +#include "hardware.h" +#include "core/core.h" +#include "drivers/dummy/dummy.h" + +/** + \defgroup dummymachine Test stub + \brief tests for generic methods of core and drivers + \ingroup machines + should compile under linux and windows and run without any hardware + @{ + */ + + +/** + sets the dummy driver to all devices + */ +class dummy_hardware: public hardware { + +public: + dummy_hardware(){ + /* the dummy driver does everything itself */ + dummy* d=new dummy; + the_adc=d; + the_pg=d; + the_fg=d; + the_tc=d; + configurable_devices["dummy"]=d; + } + + ~dummy_hardware() { + if (the_adc!=NULL) delete the_adc; + } + +}; + + +/** + a boring core with dummy hardware + */ +class dummycore: public core { + /** the dummy core name */ + std::string dummycore_name; + +public: + dummycore(const core_config& conf): core(conf) { + dummycore_name="dummycore"; + the_hardware=new dummy_hardware(); + } + + /** return the name */ + virtual const std::string& core_name() const { + return dummycore_name; + } + +}; + + +/** + @} + */ + +int main( int argc,const char** argv ) { + fprintf(stderr,"!!!CAUTION: you are using a test case!!!\n"); + int return_result=0; + try { + core_config my_config(argv, argc); + // setup input and output + dummycore my_core(my_config); + // start core application + my_core.run(); + } + catch(const DamarisException& e) + { + fprintf(stderr,"%s\n",e.what()); + return_result=1; + } + return return_result; +} diff --git a/machines/fc1_backend.cpp b/machines/fc1_backend.cpp new file mode 100644 index 0000000..a2620af --- /dev/null +++ b/machines/fc1_backend.cpp @@ -0,0 +1,118 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + + ****************************************************************************/ +#include "machines/hardware.h" +#include "core/core.h" +#include "drivers/Tecmag-DAC20/DAC20.h" +#include "drivers/PTS-Synthesizer/PTS.h" +#include "drivers/Spectrum-M2i40xxSeries/Spectrum-M2i40xxSeries.h" +#include "drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h" + +/** + \defgroup fc1neu_machine FC1neu NMR Spectrometer + \ingroup machines + Uses Spincore Pulseblaster 24 Bit and Spectrum M2i4021, and some DACs + + \par Starting the hardware + This procedure should assure the correct initialisation of the hardware: + \li Switch off main switches of SpinCore Pulseblaster and Computer (the main switch of the computer is at the rear) + \li Switch on Computer and start Windows or linux + + @{ + */ + + +/** + line 0 for gate + line 1 for pulse + line 22 for trigger + line 3 free + */ +class FC1_hardware: public hardware { + + SpinCorePulseBlaster24Bit* my_pulseblaster; + SpectrumM2i40xxSeries* my_adc; + +public: + FC1_hardware() { + ttlout trigger; + trigger.id=0; + trigger.ttls=0x400000; /* line 22 */// + my_adc=new SpectrumM2i40xxSeries(trigger); + my_pulseblaster=new SpinCorePulseBlaster24Bit(0,1e8,0x800000); + PTS* my_pts=new PTS_latched(0); // ID of PTS_analogout 0 + the_fg=my_pts; + the_pg=my_pulseblaster; + the_adc=my_adc; + + DAC20* dac; + + dac = new DAC20(1); + list_dacs.push_back(dac); + + dac = new DAC20(2); + dac->set_latch_bit(19); + list_dacs.push_back(dac); + + dac = new DAC20(3); + dac->set_latch_bit(20); + list_dacs.push_back(dac); + + dac = new DAC20(4); + dac->set_latch_bit(21); + list_dacs.push_back(dac); + } + + /* virtual */ void experiment_run_pulse_program(state* work_copy) { + //my_pulseblaster->run_pulse_program_w_sync(*work_copy, my_adc->get_sample_clock_frequency()); + } + + virtual ~FC1_hardware() { + if (the_adc!=NULL) delete the_adc; + if (the_pg!=NULL) delete the_pg; + if (the_fg!=NULL) delete the_fg; + } +}; + +/** + \brief brings standard core together with the Mobile NMR hardware + */ +class FC1_core: public core { + std::string the_name; +public: + FC1_core(const core_config& conf): + core(conf), + the_name("FC1 core") + { + the_hardware=new FC1_hardware(); + } + + virtual const std::string& core_name() const { + return the_name; + } +}; + +/** + @} + */ + +int main(int argc, const char** argv) { + int return_result=0; + try { + core_config my_conf(argv, argc); + // setup input and output + FC1_core my_core(my_conf); + // start core application + my_core.run(); + } + catch(const DamarisException& e) + { + fprintf(stderr,"%s\n",e.what()); + return_result=1; + } + + return return_result; +} diff --git a/machines/fc1_digital_backend.cpp b/machines/fc1_digital_backend.cpp new file mode 100644 index 0000000..38dca2b --- /dev/null +++ b/machines/fc1_digital_backend.cpp @@ -0,0 +1,163 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + + ****************************************************************************/ +#include "machines/hardware.h" +#include "core/core.h" +#include "drivers/Tecmag-DAC20/DAC20.h" +#include "drivers/PTS-Synthesizer/PTS.h" +#include "drivers/Spectrum-M2i40xxSeries/Spectrum-M2i40xxSeries.h" +#include "drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h" + +/** + \defgroup fc1neu_machine FC1neu NMR Spectrometer + \ingroup machines + Uses Spincore Pulseblaster 24 Bit and Spectrum M2i4021, and some DACs + + \par Starting the hardware + This procedure should assure the correct initialisation of the hardware: + \li Switch off main switches of SpinCore Pulseblaster and Computer (the main switch of the computer is at the rear) + \li Switch on Computer and start Windows or linux + + @{ + */ + +/** + line 0 for gate + line 1 for pulse + line 22 for trigger + line 3 free + */ +class FC1_hardware: public hardware +{ + SpinCorePulseBlaster24Bit* my_pulseblaster; + SpectrumM2i40xxSeries* my_adc; + +public: + FC1_hardware() + { + ttlout trigger; + trigger.id = 0; + trigger.ttls = 0x400000; /* line 22 */ // + my_adc = new SpectrumM2i40xxSeries(trigger, 1e8); + my_pulseblaster = new SpinCorePulseBlaster24Bit(0, 1e8, 0); + PTS* my_pts = new PTS_latched(0); // ID of PTS_analogout 0 + the_fg = my_pts; + the_pg = my_pulseblaster; + the_adc = my_adc; + + DAC20* dac; + + dac = new DAC20(1); + list_dacs.push_back(dac); + + dac = new DAC20(2); + dac->set_latch_bit(19); + list_dacs.push_back(dac); + + dac = new DAC20(3); + dac->set_latch_bit(20); + list_dacs.push_back(dac); + + dac = new DAC20(4); + dac->set_latch_bit(21); + list_dacs.push_back(dac); + } + + /* virtual */ //void experiment_run_pulse_program(state* work_copy) { + //my_pulseblaster->run_pulse_program_w_sync(*work_copy, my_adc->get_sample_clock_frequency()); + //} + /** override to implement digital sampling */ + result* experiment(const state& exp) + { + result* r = NULL; + for (size_t tries = 0; r == NULL && core::term_signal == 0 && tries < 102; ++tries) + { + state* work_copy = exp.copy_flat(); + if (work_copy == NULL) + return new error_result(1, "couldn't create work copy of experiment sequence"); + try + { + if (the_fg != NULL) + the_fg->set_frequency(*work_copy); + if (the_adc != NULL) + the_adc->set_daq(*work_copy); + experiment_prepare_dacs(work_copy); + // the pulse generator is necessary + experiment_run_pulse_program(work_copy); + // wait for pulse generator + the_pg->wait_till_end(); + // after that, the result must be available + if (the_adc != NULL) + r = the_adc->get_samples(); + else + r = new adc_result(1, 0, NULL); + // todo: implement digital sampling here + } + catch (const DamarisException &e) + { + r = new error_result(1, e.what()); + } + delete work_copy; + if (core::quit_signal != 0) + break; + } + return r; + } + + virtual ~FC1_hardware() + { + if (the_adc != NULL) + delete the_adc; + if (the_pg != NULL) + delete the_pg; + if (the_fg != NULL) + delete the_fg; + } +}; + +/** + \brief brings standard core together with the Mobile NMR hardware + */ +class FC1_core: public core +{ + std::string the_name; +public: + FC1_core(const core_config& conf) : + core(conf), + the_name("FC1 core") + { + the_hardware = new FC1_hardware(); + } + + virtual const std::string& core_name() const + { + return the_name; + } +}; + +/** + @} + */ + +int main(int argc, const char** argv) +{ + int return_result = 0; + try + { + core_config my_conf(argv, argc); + // setup input and output + FC1_core my_core(my_conf); + // start core application + my_core.run(); + } + catch (const DamarisException& e) + { + fprintf(stderr, "%s\n", e.what()); + return_result = 1; + } + + return return_result; +} diff --git a/machines/fc1_vierkanal_backend.cpp b/machines/fc1_vierkanal_backend.cpp new file mode 100644 index 0000000..b46ee0e --- /dev/null +++ b/machines/fc1_vierkanal_backend.cpp @@ -0,0 +1,126 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + + ****************************************************************************/ +#include "machines/hardware.h" +#include "core/core.h" +#include "drivers/Tecmag-DAC20/DAC20.h" +#include "drivers/PTS-Synthesizer/PTS.h" +#include "drivers/Spectrum-M2i40xxSeries/Spectrum-M2i40xxSeries.h" +#include "drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h" + +/** + \defgroup fc1neu_machine FC1neu NMR Spectrometer + \ingroup machines + Uses Spincore Pulseblaster 24 Bit and Spectrum M2i4021, and some DACs + + \par Starting the hardware + This procedure should assure the correct initialisation of the hardware: + \li Switch off main switches of SpinCore Pulseblaster and Computer (the main switch of the computer is at the rear) + \li Switch on Computer and start Windows or linux + + @{ + */ + +/** + line 0 for gate + line 1 for pulse + line 22 for trigger + line 3 free + */ +class FC1_hardware: public hardware +{ + SpinCorePulseBlaster24Bit* my_pulseblaster; + SpectrumM2i40xxSeries* my_adc; + +public: + FC1_hardware() + { + ttlout trigger; + trigger.id = 0; + trigger.ttls = 0x400000; /* line 22 */ // + my_adc = new SpectrumM2i40xxSeries(trigger, 1e8); + my_pulseblaster = new SpinCorePulseBlaster24Bit(0, 1e8, 0); + PTS* my_pts = new PTS_latched(0); // ID of PTS_analogout 0 + the_fg = my_pts; + the_pg = my_pulseblaster; + the_adc = my_adc; + + DAC20* dac; + + dac = new DAC20(1); + list_dacs.push_back(dac); + + dac = new DAC20(2); + dac->set_latch_bit(19); + list_dacs.push_back(dac); + + dac = new DAC20(3); + dac->set_latch_bit(20); + list_dacs.push_back(dac); + + dac = new DAC20(4); + dac->set_latch_bit(21); + list_dacs.push_back(dac); + } + + /* virtual */ //void experiment_run_pulse_program(state* work_copy) { + //my_pulseblaster->run_pulse_program_w_sync(*work_copy, my_adc->get_sample_clock_frequency()); + //} + + virtual ~FC1_hardware() + { + if (the_adc != NULL) + delete the_adc; + if (the_pg != NULL) + delete the_pg; + if (the_fg != NULL) + delete the_fg; + } +}; + +/** + \brief brings standard core together with the Mobile NMR hardware + */ +class FC1_core: public core +{ + std::string the_name; +public: + FC1_core(const core_config& conf) : + core(conf), + the_name("FC1 core") + { + the_hardware = new FC1_hardware(); + } + + virtual const std::string& core_name() const + { + return the_name; + } +}; + +/** + @} + */ + +int main(int argc, const char** argv) +{ + int return_result = 0; + try + { + core_config my_conf(argv, argc); + // setup input and output + FC1_core my_core(my_conf); + // start core application + my_core.run(); + } + catch (const DamarisException& e) + { + fprintf(stderr, "%s\n", e.what()); + return_result = 1; + } + + return return_result; +} diff --git a/machines/fc1neu_backend.cpp b/machines/fc1neu_backend.cpp new file mode 100644 index 0000000..b6db5d3 --- /dev/null +++ b/machines/fc1neu_backend.cpp @@ -0,0 +1,129 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + + ****************************************************************************/ +#include "machines/hardware.h" +#include "core/core.h" +#include "drivers/Tecmag-DAC20/DAC20.h" +#include "drivers/PTS-Synthesizer/PTS.h" +#include "drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h" +#include "drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h" + +/** + \defgroup fc1neu_machine FC1neu NMR Spectrometer + \ingroup machines + Uses Spincore Pulseblaster 24 Bit and Spectrum MI4021, and some DACs + + \par Starting the hardware + This procedure should assure the correct initialisation of the hardware: + \li Switch off main switches of SpinCore Pulseblaster and Computer (the main switch of the computer is at the rear) + \li Switch on Computer and start Windows or linux + + @{ + */ + +/** + line 0 for gate + line 1 for pulse + line 22 for trigger + line 3 free + */ +class FC1neu_hardware: public hardware +{ + + SpinCorePulseBlaster24Bit* my_pulseblaster; + SpectrumMI40xxSeries* my_adc; + +public: + FC1neu_hardware() + { + ttlout trigger; + trigger.id = 0; + trigger.ttls = 0x400000; /* line 22 */ // + my_adc = new SpectrumMI40xxSeries(trigger); + my_pulseblaster = new SpinCorePulseBlaster24Bit(0, 1e8, 0x800000); + PTS* my_pts = new PTS_latched(0); // ID of PTS_analogout 0 + the_fg = my_pts; + the_pg = my_pulseblaster; + the_adc = my_adc; + + DAC20* dac; + + dac = new DAC20(1); + list_dacs.push_back(dac); + + dac = new DAC20(2); + dac->set_latch_bit(19); + list_dacs.push_back(dac); + + dac = new DAC20(3); + dac->set_latch_bit(20); + list_dacs.push_back(dac); + + dac = new DAC20(4); + dac->set_latch_bit(21); + list_dacs.push_back(dac); + } + + /* virtual */ + void experiment_run_pulse_program(state* work_copy) + { + my_pulseblaster->run_pulse_program_w_sync(*work_copy, my_adc->get_sample_clock_frequency()); + } + + virtual ~FC1neu_hardware() + { + if (the_adc != NULL) + delete the_adc; + if (the_pg != NULL) + delete the_pg; + if (the_fg != NULL) + delete the_fg; + } +}; + +/** + \brief brings standard core together with the Mobile NMR hardware + */ +class FC1neu_core: public core +{ + std::string the_name; +public: + FC1neu_core(const core_config& conf) : + core(conf), + the_name("FC1neu core") + { + the_hardware = new FC1neu_hardware(); + } + + virtual const std::string& core_name() const + { + return the_name; + } +}; + +/** + @} + */ + +int main(int argc, const char** argv) +{ + int return_result = 0; + try + { + core_config my_conf(argv, argc); + // setup input and output + FC1neu_core my_core(my_conf); + // start core application + my_core.run(); + } + catch (const DamarisException& e) + { + fprintf(stderr, "%s\n", e.what()); + return_result = 1; + } + + return return_result; +} diff --git a/machines/fc2_backend.cpp b/machines/fc2_backend.cpp new file mode 100644 index 0000000..3147932 --- /dev/null +++ b/machines/fc2_backend.cpp @@ -0,0 +1,148 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + + ****************************************************************************/ +#include "machines/hardware.h" +#include "core/core.h" +#include "drivers/Tecmag-DAC20/DAC20.h" +#include "drivers/PTS-Synthesizer/PTS.h" +#include "drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h" +#include "drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h" + +/** + \defgroup fc2machine FC2 NMR Spectrometer + \ingroup machines + Uses Spincore Pulseblaster 24 Bit and Spectrum MI4021, and some DACs + + \par Starting the hardware + This procedure should assure the correct initialisation of the hardware: + \li Switch off main switches of SpinCore Pulseblaster and Computer (the main switch of the computer is at the rear) + \li Switch on Computer and start Windows or linux + + @{ + */ + +/** + line 0 for gate + line 1 for pulse + line 22 for trigger + line 3 free + */ +class FC2_hardware: public hardware +{ + + SpinCorePulseBlaster24Bit* my_pulseblaster; + SpectrumMI40xxSeries* my_adc; + +public: + FC2_hardware() + { + ttlout trigger; + trigger.id = 0; + trigger.ttls = 0x400000; /* line 22 */ // + my_adc = new SpectrumMI40xxSeries(trigger); + my_pulseblaster = new SpinCorePulseBlaster24Bit(0, 1e8, 0x800000); + PTS* my_pts = new PTS_latched(0); // ID of PTS_analogout 0 + the_fg = my_pts; + the_pg = my_pulseblaster; + the_adc = my_adc; + DAC20* dac; + dac = new DAC20(1); + list_dacs.push_back(dac); + dac = new DAC20(2); + dac->set_latch_bit(19); + list_dacs.push_back(dac); + } + + result* experiment(const state& exp) + { + result* r = NULL; + for (size_t tries = 0; r == NULL && core::term_signal == 0 && tries < 102; ++tries) + { + state* work_copy = exp.copy_flat(); + if (work_copy == NULL) + return new error_result(1, "could create work copy of experiment sequence"); + try + { + if (the_fg != NULL) + the_fg->set_frequency(*work_copy); + if (the_adc != NULL) + the_adc->set_daq(*work_copy); + experiment_prepare_dacs(work_copy); + // the pulse generator is necessary + my_pulseblaster->run_pulse_program_w_sync(*work_copy, my_adc->get_sample_clock_frequency()); + // wait for pulse generator + the_pg->wait_till_end(); + // after that, the result must be available + if (the_adc != NULL) + r = the_adc->get_samples(); + else + r = new adc_result(1, 0, NULL); + } + catch (const RecoverableException &e) + { + r = new error_result(1, e.what()); + } + delete work_copy; + if (core::quit_signal != 0) + break; + } + return r; + } + + virtual ~FC2_hardware() + { + if (the_adc != NULL) + delete the_adc; + if (the_pg != NULL) + delete the_pg; + if (the_fg != NULL) + delete the_fg; + } +}; + +/** + \brief brings standard core together with the Mobile NMR hardware + */ +class FC2_core: public core +{ + std::string the_name; +public: + FC2_core(const core_config& conf) : + core(conf), + the_name("FC2 core") + { + the_hardware = new FC2_hardware(); + } + + virtual const std::string& core_name() const + { + return the_name; + } +}; + +/** + @} + */ + +int main(int argc, const char** argv) +{ + int return_result = 0; + try + { + core_config my_conf(argv, argc); + // setup input and output + FC2_core my_core(my_conf); + // start core application + my_core.run(); + } + catch (const DamarisException& e) + { + fprintf(stderr, "%s\n", e.what()); + return_result = 1; + } + + return return_result; +} diff --git a/machines/general.cpp b/machines/general.cpp new file mode 100644 index 0000000..0b75c34 --- /dev/null +++ b/machines/general.cpp @@ -0,0 +1,239 @@ +/*************************************************************************** + + Author: Markus Rosenstihl + Created: June 2010 + + ****************************************************************************/ +#include "machines/hardware.h" +#include "core/core.h" +#include "drivers/PTS-Synthesizer/PTS.h" +#include "drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h" +#include "drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h" +#include + +/** + \defgroup General NMR Spectrometer + \ingroup machines + Uses: + \li Spincore Pulseblaster 24 Bit + \li Spectrum MI4021 with gated sampling option (PB ref. clock is fed to Ext.Clock) + \li Programmed Test Sources PTS frequency synthesizer with phase control + This backend uses a configuration file for setting the various lines and configurations. + + \par Starting the hardware + This procedure should assure the correct initialisation of the hardware: + \li Switch off main switches of SpinCore Pulseblaster and Computer (the main switch of the computer is at the rear) + \li Switch on Computer and start Windows or Linux + + @{ + */ + +class general_hardware: public hardware +{ + + PTS* my_pts; + SpinCorePulseBlaster24Bit* my_pulseblaster; + SpectrumMI40xxSeries* my_adc; + +public: + general_hardware() + { + int SYSCONF = 0; + int USRCONF = 0; + GKeyFile* cfg_file; + GError *error = NULL; + char cfg_name[512]; + const gchar* usr_configdir; + const gchar* const * sys_configdirs; + sys_configdirs = g_get_system_config_dirs(); + usr_configdir = g_get_user_config_dir(); + cfg_file = g_key_file_new(); + for (int i = 0; i < 100; i++) + { + if (sys_configdirs[i] == NULL) + break; + else + { + snprintf(cfg_name, 512, "%s/damaris/backend.conf", sys_configdirs[i]); + fprintf(stdout, "reading backend configuration file (system: %i): %s...\n", i, cfg_name); + if (!g_key_file_load_from_file(cfg_file, cfg_name, G_KEY_FILE_NONE, &error)) + { + if (error->code != 4) + { + fprintf(stdout, "found!\n"); + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, error->message); + } + else + printf("not found!\n"); + } + else + SYSCONF = 1; + error = NULL; // reset error + } + + } + snprintf(cfg_name, 512, "%s/damaris/backend.conf", usr_configdir); + fprintf(stdout, "reading backend configuration file (user): %s...\n", cfg_name); + if (!g_key_file_load_from_file(cfg_file, cfg_name, G_KEY_FILE_NONE, &error)) + { + if (error->code != 4) + { + fprintf(stdout, "found!\n"); + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, error->message); + } + else + printf("not found!\n"); + error = NULL; + } + else + USRCONF = 1; + if (!(SYSCONF | USRCONF)) + throw(core_exception("configuration failed!\n")); + printf("done!\n"); + /* configure ADC card */ + ttlout trigger; + trigger.id = g_key_file_get_integer(cfg_file, "ADC", "id", &error); + if (error) + g_error(error->message); + error = NULL; + //g_error(error->message); + trigger.ttls = 1 << g_key_file_get_integer(cfg_file, "ADC", "trigger_line", &error); + if (error) + g_error(error->message); + error = NULL; + int ext_reference_clock = (int) g_key_file_get_double(cfg_file, "ADC", "refclock", &error); // 50 MHz from PB24 SP17; defaults to 100MHz (PB24 SP 2) + if (error) + g_error(error->message); + error = NULL; + double impedance = g_key_file_get_double(cfg_file, "ADC", "impedance", &error); // Ohm ( or 50 Ohm) + if (error) + g_error(error->message); + + error = NULL; + my_adc = new SpectrumMI40xxSeries(trigger, impedance, ext_reference_clock); + + /* configure PulseBlaster */ + int pb_id = g_key_file_get_integer(cfg_file, "PB", "id", &error); + if (error) + g_error(error->message); + error = NULL; + double pb_refclock = g_key_file_get_double(cfg_file, "PB", "refclock", &error); + if (error) + g_error(error->message); + error = NULL; + int pb_sync_bit = g_key_file_get_integer(cfg_file, "PB", "sync_line", &error); + if (error) + g_error(error->message); + error = NULL; + int pb_sync = 0; + if (pb_sync_bit != 128) + pb_sync = 1 << pb_sync_bit; + + my_pulseblaster = new SpinCorePulseBlaster24Bit(pb_id, pb_refclock, pb_sync); + + /* configure PTS */ + int pts_id = g_key_file_get_integer(cfg_file, "PTS", "id", &error); + if (error) + g_error(error->message); + error = NULL; + my_pts = new PTS_latched(pts_id); + // PTS 500 has 0.36 or 0.72 above 200MHz ; PTS 310 has 0.225 degrees/step + my_pts->phase_step = g_key_file_get_double(cfg_file, "PTS", "phase_stepsize", &error); + if (error) + g_error(error->message); + error = NULL; + + // publish devices + the_pg = my_pulseblaster; + the_adc = my_adc; + the_fg = my_pts; + } + + result* experiment(const state& exp) + { + result* r = NULL; + for (size_t tries = 0; r == NULL && core::term_signal == 0 && tries < 102; ++tries) + { + state* work_copy = exp.copy_flat(); + if (work_copy == NULL) + return new error_result(1, "could create work copy of experiment sequence"); + try + { + if (the_fg != NULL) + the_fg->set_frequency(*work_copy); + if (the_adc != NULL) + the_adc->set_daq(*work_copy); + // the pulse generator is necessary + my_pulseblaster->run_pulse_program_w_sync(*work_copy, my_adc->get_sample_clock_frequency()); + // wait for pulse generator + the_pg->wait_till_end(); + // after that, the result must be available + if (the_adc != NULL) + r = the_adc->get_samples(); + else + r = new adc_result(1, 0, NULL); + } + catch (const RecoverableException &e) + { + r = new error_result(1, e.what()); + } + delete work_copy; + if (core::quit_signal != 0) + break; + } + return r; + } + + virtual ~general_hardware() + { + if (the_adc != NULL) + delete the_adc; + if (the_fg != NULL) + delete the_fg; + if (the_pg != NULL) + delete the_pg; + } + +}; + +/** + \brief brings standard core together with the general NMR hardware + */ +class general_core: public core +{ + std::string the_name; +public: + general_core(const core_config& conf) : + core(conf) + { + the_hardware = new general_hardware(); + the_name = "berta core"; + } + virtual const std::string& core_name() const + { + return the_name; + } +}; + +/** + @} + */ + +int main(int argc, const char** argv) +{ + int return_result = 0; + try + { + core_config my_conf(argv, argc); + // setup input and output + general_core my_core(my_conf); + // start core application + my_core.run(); + } + catch (const DamarisException& e) + { + fprintf(stderr, "%s\n", e.what()); + return_result = 1; + } + return return_result; +} diff --git a/machines/hardware.cpp b/machines/hardware.cpp new file mode 100644 index 0000000..c4a2ed7 --- /dev/null +++ b/machines/hardware.cpp @@ -0,0 +1,136 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: August 2004 + + ****************************************************************************/ + +#include "core/core.h" +#include "core/result.h" +#include "hardware.h" + +void hardware::experiment_prepare_dacs(state* work_copy) +{ + if (!list_dacs.empty()) + { + std::list::iterator i; + for (i = list_dacs.begin(); i != list_dacs.end(); i++) + { + (*i)->set_dac(*work_copy); + } + } +} + +hardware::~hardware() +{ + while (!list_dacs.empty()) + { + if (list_dacs.back()) + delete list_dacs.back(); + list_dacs.pop_back(); + } +} + +void hardware::experiment_run_pulse_program(state* work_copy) +{ + the_pg->run_pulse_program(*work_copy); +} + +result* hardware::experiment(const state& exp) +{ + result* r = NULL; + for (size_t tries = 0; r == NULL && core::term_signal == 0 && tries < 102; ++tries) + { + state* work_copy = exp.copy_flat(); + if (work_copy == NULL) + return new error_result(1, "couldn't create work copy of experiment sequence"); + try + { + if (the_fg != NULL) + the_fg->set_frequency(*work_copy); + if (the_adc != NULL) + the_adc->set_daq(*work_copy); + experiment_prepare_dacs(work_copy); + // the pulse generator is necessary + experiment_run_pulse_program(work_copy); + // wait for pulse generator + the_pg->wait_till_end(); + // after that, the result must be available + if (the_adc != NULL) + r = the_adc->get_samples(); + else + r = new adc_result(1, 0, NULL); + } + catch (const RecoverableException &e) + { + r = new error_result(1, e.what()); + } + delete work_copy; + if (core::quit_signal != 0) + break; + } + return r; +} + +configuration_results* hardware::configure(const std::list& d) +{ + configuration_results* r = new configuration_results(0); + + // create a work list + std::list to_configure; + for (std::list::const_iterator i = d.begin(); i != d.end(); ++i) + { + // if the device name is available, we will add a pointer, otherwise a remark about missing device + if (configurable_devices.count(i->name) != 0) + to_configure.push_back(&(*i)); + else + { + // todo generate a remark + r->push_back(new configuration_result(0)); + } + } + + // go through this list again and again, until all devices returned at least something... + for (int run = 0; !to_configure.empty(); ++run) + { + + std::list::iterator i = to_configure.begin(); + while (i != to_configure.end()) + { + configuration_result* config_result = NULL; + std::map::iterator dev_iterator = configurable_devices.find((*i)->name); + device* dev = dev_iterator->second; + if (dev != NULL) + { + try + { + config_result = dev->configure(**i, run); + } + catch (const device_error& e) + { + // error to result... + config_result = new configuration_result(0); + } + } + else + { + // error to result... + config_result = new configuration_result(0); + } + if (config_result != NULL) + { + // do not configure this device again... + r->push_back(config_result); + i = to_configure.erase(i); + } + else + { + // once more + ++i; + } + } + + } + + return r; +} diff --git a/machines/hardware.h b/machines/hardware.h new file mode 100644 index 0000000..01037f1 --- /dev/null +++ b/machines/hardware.h @@ -0,0 +1,102 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + +****************************************************************************/ +#ifndef HARDWARE_H +#define HARDWARE_H + +class configuration_result; +class device_section; + +#include +#include +#include "drivers/device.h" +#include "drivers/pfggen.h" +#include "drivers/ADC.h" +#include "drivers/pulsegen.h" +#include "drivers/frequgen.h" +#include "drivers/tempcont.h" +#include "drivers/device.h" +#include "core/states.h" +#include "core/job.h" +#include "core/result.h" + +/** + \defgroup machines Different Machines + Implementation of different machine setups +*/ + +/** + \addtogroup machines + \brief provides necessary hardware interfaces for a NMR experiment + */ + +class hardware { + public: + /// adc component + ADC* the_adc; + /// frequency generator + frequgen* the_fg; + /// pulse generator + pulsegen* the_pg; + /// the temperature control, optional + tempcont* the_tc; + /** + * \brief List of all DACs in the system + * + * The destructor of this class cleans them up + */ + std::list list_dacs; + + /** + configurable devices dictionary + */ + std::map configurable_devices; + + /** + set all pointers to zero to signal missing hardware components + */ + hardware() {the_adc=NULL; the_fg=NULL; the_pg=NULL; the_tc=NULL;} + + /** + */ + virtual configuration_results* configure(const std::list& d); + + /** + initialise the main components with the experiment + */ + virtual result* experiment(const state& exp); + + /** + \brief shuts down hardware + */ + virtual ~hardware(); + + protected: + /** + * \brief Utility function: Prepare the DACs for the experiment + * + * In this base class, it calls the set_dac() method on + * each DAC in list_dacs. + * + * This might include sending configuration info to the + * DAC, or rewriting the pulse program. + */ + virtual void experiment_prepare_dacs(state* work_copy); + + /** + * \brief Utility function: Finally run pulse program + * + * Called by the experiment() method. + * + * Calls: \code the_pg->run_pulse_program(*work_copy) \endcode + * + * Can be overriden to use other pulse running methods, + * for example a synced one. + */ + virtual void experiment_run_pulse_program(state* work_copy); +}; + +#endif diff --git a/machines/magnexgrad_backend.cpp b/machines/magnexgrad_backend.cpp new file mode 100644 index 0000000..d0b8662 --- /dev/null +++ b/machines/magnexgrad_backend.cpp @@ -0,0 +1,154 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + + ****************************************************************************/ +#include +#include "machines/hardware.h" +#include "core/core.h" +#include "drivers/Eurotherm-2000Series/Eurotherm-2000Series.h" +#include "drivers/PTS-Synthesizer/PTS.h" +#include "drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h" +#include "drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h" + +/** + \defgroup magnexgradmachine Magnex Static Gradient NMR Spectrometer + \ingroup machines + Uses Spincore Pulseblaster 24 Bit and Spectrum MI4021 together with PTS phase and frequency cable driver + Also implements Eurotherm temperature control + + \par Starting the hardware + Switch on the amplifier + @{ + */ + +/** + line 0 for gate + line 1 for pulse + line 2 free + line 3 free + line 16 for trigger + line 17 for syncronization + */ +class magnexgrad_hardware: public hardware +{ + +public: + magnexgrad_hardware() + { + ttlout trigger; + trigger.id = 0; + trigger.ttls = 1 << 16; /* line 16 */ + the_adc = new SpectrumMI40xxSeries(trigger, 50); // 50 Ohm termination + the_pg = new SpinCorePulseBlaster24Bit(0, 1e8, 1 << 17); //sync line on bit 17 + PTS* my_pts = new PTS_latched(0); + the_fg = my_pts; + the_tc = new Eurotherm2000Series("/dev/ttyS0", 2, 0x0); + + configurable_devices["T"] = the_tc; + } + + /** + print out a temperature line + */ + virtual result* experiment(const state& exp) + { + result* r = hardware::experiment(exp); + /** + make a timestamp + */ + time_t timestamp; + time(×tamp); + tm timestamp_broken; + localtime_r(×tamp, ×tamp_broken); + char timestamp_string[254]; + strftime(timestamp_string, 254, "%F %T", ×tamp_broken); + timeval exact_time; + gettimeofday(&exact_time, NULL); + + if (the_tc != NULL) + { + try + { + double temp = the_tc->get_temperature(); + fprintf(stdout, "%s.%03ld temperature=%f\n", timestamp_string, exact_time.tv_usec / 1000, temp); + } + catch (const Eurotherm2000Series_error& e) + { + fprintf(stdout, "%s.%03ld temperature=0.0 (error %s)\n", timestamp_string, exact_time.tv_usec / 1000, e.what()); + } + fflush(stdout); + } + return r; + } + + virtual ~magnexgrad_hardware() + { + if (the_adc != NULL) + delete the_adc; + if (the_pg != NULL) + delete the_pg; + if (the_fg != NULL) + delete the_fg; + if (the_tc != NULL) + delete the_tc; + } + +}; + +/** + \brief brings standard core together with the Mobile NMR hardware + */ +class magnexgrad_core: public core +{ + std::string the_name; +public: + magnexgrad_core(const core_config& conf) : + core(conf) + { + the_hardware = new magnexgrad_hardware(); + the_name = "magnexgrad"; + } + virtual const std::string& core_name() const + { + return the_name; + } +}; + +/** + @} + */ + +int main(int argc, const char** argv) +{ + int return_result = 0; + // renice and strip of suid rights + + if (nice(-10) == -1) + { + fprintf(stderr, "warning: could not increase priority (no setuid?)\n"); + } + + if (getuid() != geteuid()) + { + if (setuid(getuid()) != 0) + { + fprintf(stderr, "warning: could not switch back to uid=%d\n", getuid()); + } + } + try + { + core_config my_conf(argv, argc); + // setup input and output + magnexgrad_core my_core(my_conf); + // start core application + my_core.run(); + } + catch (const DamarisException& e) + { + fprintf(stderr, "%s\n", e.what()); + return_result = 1; + } + return return_result; +} diff --git a/machines/magnexgrad_backend_dds.cpp b/machines/magnexgrad_backend_dds.cpp new file mode 100644 index 0000000..7a7bb78 --- /dev/null +++ b/machines/magnexgrad_backend_dds.cpp @@ -0,0 +1,185 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + + ****************************************************************************/ +#include +#include "machines/hardware.h" +#include "core/core.h" +#include "drivers/Eurotherm-2000Series/Eurotherm-2000Series.h" +#include "drivers/PTS-Synthesizer/PTS.h" +#include "drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h" +#include "drivers/SpinCore-PulseBlasterDDSIII/SpinCore-PulseBlasterDDSIII.h" + +/** + \defgroup magnexgradmachine Magnex Static Gradient NMR Spectrometer + \ingroup machines + Uses Spincore Pulseblaster 24 Bit and Spectrum MI4021 together with PTS phase and frequency cable driver + Also implements Eurotherm temperature control + + \par Starting the hardware + Switch on the amplifier + @{ + */ + +/** + line 0 for gate + line 1 for pulse + line 2 for trigger + line 3 free + line 9 for syncronization + */ +class magnexgrad_hardware: public hardware +{ + + SpectrumMI40xxSeries* my_adc; + SpinCorePulseBlasterDDSIII* my_dds; +public: + magnexgrad_hardware() + { + ttlout trigger; + trigger.id = 0; + trigger.ttls = 1 << 2; /* line 2 */ + my_adc = new SpectrumMI40xxSeries(trigger, 50); // 50 Ohm termination + the_adc = my_adc; + my_dds = new SpinCorePulseBlasterDDSIII(0, 1e8, 1 << 9); //sync line on bit 17 + the_pg = my_dds; + //PTS* my_pts=new PTS_latched(0); + //the_fg=my_pts; + the_tc = NULL; //new Eurotherm2000Series("/dev/ttyS0",2,0x0); + + //configurable_devices["T"]=the_tc; + } + + /** + print out a temperature line + */ + virtual result* experiment(const state& exp) + { + result* r = NULL; + for (size_t tries = 0; r == NULL && core::term_signal == 0 && tries < 102; ++tries) + { + state* work_copy = exp.copy_flat(); + if (work_copy == NULL) + return new error_result(1, "could create work copy of experiment sequence"); + try + { + if (the_adc != NULL) + the_adc->set_daq(*work_copy); + // the pulse generator is necessary + my_dds->run_pulse_program_w_sync(*work_copy, my_adc->get_sample_clock_frequency()); + // wait for pulse generator + the_pg->wait_till_end(); + // after that, the result must be available + if (the_adc != NULL) + r = the_adc->get_samples(); + else + r = new adc_result(1, 0, NULL); + } + catch (const RecoverableException &e) + { + r = new error_result(1, e.what()); + } + delete work_copy; + if (core::quit_signal != 0) + break; + } + + /** + make a timestamp + */ + time_t timestamp; + time(×tamp); + tm timestamp_broken; + localtime_r(×tamp, ×tamp_broken); + char timestamp_string[254]; + strftime(timestamp_string, 254, "%F %T", ×tamp_broken); + timeval exact_time; + gettimeofday(&exact_time, NULL); + + if (the_tc != NULL) + { + try + { + double temp = the_tc->get_temperature(); + fprintf(stdout, "%s.%03ld temperature=%f\n", timestamp_string, exact_time.tv_usec / 1000, temp); + } + catch (const Eurotherm2000Series_error& e) + { + fprintf(stdout, "%s.%03ld temperature=0.0 (error %s)\n", timestamp_string, exact_time.tv_usec / 1000, e.what()); + } + fflush(stdout); + } + return r; + } + + virtual ~magnexgrad_hardware() + { + if (the_adc != NULL) + delete the_adc; + if (the_pg != NULL) + delete the_pg; + if (the_fg != NULL) + delete the_fg; + if (the_tc != NULL) + delete the_tc; + } + +}; + +/** + \brief brings standard core together with the Mobile NMR hardware + */ +class magnexgrad_core: public core +{ + std::string the_name; +public: + magnexgrad_core(const core_config& conf) : + core(conf) + { + the_hardware = new magnexgrad_hardware(); + the_name = "magnexgrad"; + } + virtual const std::string& core_name() const + { + return the_name; + } +}; + +/** + @} + */ + +int main(int argc, const char** argv) +{ + int return_result = 0; + // renice and strip of suid rights + + if (nice(-10) == -1) + { + fprintf(stderr, "warning: could not increase priority (no setuid?)\n"); + } + + if (getuid() != geteuid()) + { + if (setuid(getuid()) != 0) + { + fprintf(stderr, "warning: could not switch back to uid=%d\n", getuid()); + } + } + try + { + core_config my_conf(argv, argc); + // setup input and output + magnexgrad_core my_core(my_conf); + // start core application + my_core.run(); + } + catch (const DamarisException& e) + { + fprintf(stderr, "%s\n", e.what()); + return_result = 1; + } + return return_result; +} diff --git a/machines/pb_radio_processor_g_backend.cpp b/machines/pb_radio_processor_g_backend.cpp new file mode 100644 index 0000000..cd28c58 --- /dev/null +++ b/machines/pb_radio_processor_g_backend.cpp @@ -0,0 +1,21 @@ +/* ************************************************************************** + + Author: Stefan Reutter + Created: August 2013 + +****************************************************************************/ +#include "pb_radio_processor_g_backend.h" +#include + +namespace DAMARIS +{ +} + +int main(int argc, char *argv[]) { + + DAMARIS::BackendConfigReader cfgReader("/damaris/backend.conf"); + + std::cout << cfgReader.getInteger("ADC", "trigger_line") << "\n"; + + return 0; +} diff --git a/machines/pb_radio_processor_g_backend.h b/machines/pb_radio_processor_g_backend.h new file mode 100644 index 0000000..20b79a9 --- /dev/null +++ b/machines/pb_radio_processor_g_backend.h @@ -0,0 +1,43 @@ +/* ************************************************************************** + + Author: Stefan Reutter + Created: August 2013 + +****************************************************************************/ +#ifndef DAMARIS_PB_RADIO_PROCESSOR_G_BACKEND_H +#define DAMARIS_PB_RADIO_PROCESSOR_G_BACKEND_H + +#include "core/backend_config_reader.h" +#include "machines/hardware.h" +#include "core/core.h" + +namespace DAMARIS +{ + + +class pbRadioProcessorGHardware: public hardware { + +}; + +class pbRadioProcessorGCore: public core { + +public: + /* -------------------------------------------------------------------- */ + pbRadioProcessorGCore(const core_config& conf): + core(conf), + the_name("PB RadioProcessorG Core") + { + the_hardware=new pbRadioProcessorGHardware(); + } + + /* -------------------------------------------------------------------- */ + virtual const std::string& core_name() const { + return the_name; + } +private: + std::string the_name; +}; + +} //namespace DAMARIS + +#endif // include guard diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000..66d7c04 --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,46 @@ +############################################################################# +# +# Author: Achim Gaedke +# Created: June 2004 +# +############################################################################# + + +CXX=g++ +CXXFLAGS=-g -O0 +CXXCPPFLAGS=-I.. +LDFLAGS= +LIBS=-lexpat + +.PHONY: ../core/core.a all clean install + +all: state_read_test.exe state_iterate_test.exe iterator_insert_test.exe stopwatch_test.exe + +../core/core.a: + $(MAKE) -C ../core core.a + +iterator_insert_test.exe: iterator_insert_test.cpp + $(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -o $@ $< + +state_read_test.exe: state_read_test.o ../core/core.a + $(CXX) $(LDFLAGS) -o $@ $^ $(LIBS) + +state_read_test.o: state_read_test.cpp ../core/states.h ../core/xml_states.h + $(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -c -o $@ $< + +state_iterate_test.exe: state_iterate_test.o ../core/core.a + $(CXX) $(LDFLAGS) -o $@ $^ $(LIBS) + +state_iterate_test.o: state_iterate_test.cpp ../core/states.h ../core/xml_states.h + $(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -c -o $@ $< + +stopwatch_test.exe: stopwatch_test.cpp ../core/stopwatch.h + $(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -o $@ $< + +xml_result_test.exe: xml_result_test.cpp ../core/xml_result.h ../core/result.h ../core/core.a + $(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -o $@ $< $(LDFLAGS) $(LIBS) ../core/core.a + +clean: + rm -f iterator_insert_test.exe state_read_test.exe state_iterate_test.exe stopwatch_test.exe *.o *~ *.stackdump core.{1,2,3,4,5,6,7,8,9}{0,1,2,3,4,5,6,7,8,9}* + +install: diff --git a/tests/experiment_job.xml b/tests/experiment_job.xml new file mode 100644 index 0000000..7721dcc --- /dev/null +++ b/tests/experiment_job.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/iterator_insert_test.cpp b/tests/iterator_insert_test.cpp new file mode 100644 index 0000000..676895a --- /dev/null +++ b/tests/iterator_insert_test.cpp @@ -0,0 +1,27 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + +****************************************************************************/ +#include +#include + +int main() { + + // empty list + std::list test_list; + + test_list.push_back(2); + std::list::iterator i2=--test_list.end(); + test_list.push_back(4); + + test_list.push_front(0); + test_list.insert(++test_list.begin(),1); + test_list.insert(++i2,3); + + for (std::list::const_iterator i=test_list.begin();i!=test_list.end();i++) + printf("%d\n",*i); + + return 0; +} diff --git a/tests/state_iterate_test.cpp b/tests/state_iterate_test.cpp new file mode 100644 index 0000000..7b4327f --- /dev/null +++ b/tests/state_iterate_test.cpp @@ -0,0 +1,29 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + +****************************************************************************/ +#include "core/states.h" +#include "core/xml_states.h" +#include + +int main() { + std::string filename="experiment_job.xml"; + xml_state_reader reader; + state_atom* states=reader.read_from_file(filename); + if (states==NULL) { + printf("wasn't successfull\n"); + return 1; + } + state* pulse_program=dynamic_cast(states); + if (pulse_program!=NULL) { + state* flat_one=pulse_program->copy_flat(); + xml_state_writer().write_states(stdout,*flat_one,1); + } + else { + printf("this was no pulse program!\n"); + } + delete states; + return 0; +} diff --git a/tests/state_read_test.cpp b/tests/state_read_test.cpp new file mode 100644 index 0000000..34ce6ba --- /dev/null +++ b/tests/state_read_test.cpp @@ -0,0 +1,24 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + +****************************************************************************/ +#include +#include "core/xml_states.h" + +int main() { + std::string filename="states.xml"; + xml_state_reader reader; + state_atom* states=reader.read_from_file(filename); + if (states==NULL) { + printf("wasn't successfull\n"); + return 1; + } + + state_atom* copy_of_states=states->copy_new(); + + delete states; + delete copy_of_states; + return 0; +} diff --git a/tests/states.xml b/tests/states.xml new file mode 100644 index 0000000..38adda3 --- /dev/null +++ b/tests/states.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/tests/stefan/Makefile b/tests/stefan/Makefile new file mode 100644 index 0000000..f3f8b06 --- /dev/null +++ b/tests/stefan/Makefile @@ -0,0 +1,40 @@ +############################################################################# +# +# Author: Stefan Reutter +# Created: August 2013 +# +############################################################################# + + +CXX=g++ +CXXFLAGS=-g -O0 -W -Wall -Wextra -Wshadow -pedantic -std=c++11 +CXXCPPFLAGS=-I../.. +LDFLAGS= +LIBS=-lexpat -lxerces-c + +.PHONY: ../../core/core.a all clean + +all: test + +clean: + rm -f test *.o + +../../core/core.a: + $(MAKE) -C ../../core core.a + +../../machines/hardware.o: + $(MAKE) -C ../../machines hardware.o + +test: testmain.o tjob.o tcore.o ../../machines/hardware.o ../../core/core.a + $(CXX) $(LDFLAGS) -o $@ $^ $(LIBS) + +testmain.o: testmain.cpp + $(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -c -o $@ $< + +tjob.o: tjob.cpp tjob.h + $(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -c -o $@ $< + +result.o: result.hpp + +tcore.o: tcore.cpp tcore.h + $(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -c -o $@ $< diff --git a/tests/stefan/resources/readme.txt b/tests/stefan/resources/readme.txt new file mode 100644 index 0000000..df67b48 --- /dev/null +++ b/tests/stefan/resources/readme.txt @@ -0,0 +1,9 @@ +This folder contains resource files for the unit tests in the parent folder to work on. + +List: + +spool0/job.000000000 Simple job file containing only a wait command +spool0/job.000000001 Quit job file + +spool1/job.000000000 Job file containing: set_phase, ttl_pulse, wait, set_dac, and record statements +spool1/job.000000001 Quit job file diff --git a/tests/stefan/resources/spool0/job.000000000 b/tests/stefan/resources/spool0/job.000000000 new file mode 100644 index 0000000..9ec33f6 --- /dev/null +++ b/tests/stefan/resources/spool0/job.000000000 @@ -0,0 +1,5 @@ + + + + + diff --git a/tests/stefan/resources/spool0/job.000000001 b/tests/stefan/resources/spool0/job.000000001 new file mode 100644 index 0000000..0fa07a0 --- /dev/null +++ b/tests/stefan/resources/spool0/job.000000001 @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/tests/stefan/resources/spool1/job.000000000 b/tests/stefan/resources/spool1/job.000000000 new file mode 100644 index 0000000..faa5234 --- /dev/null +++ b/tests/stefan/resources/spool1/job.000000000 @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/tests/stefan/resources/spool1/job.000000001 b/tests/stefan/resources/spool1/job.000000001 new file mode 100644 index 0000000..0fa07a0 --- /dev/null +++ b/tests/stefan/resources/spool1/job.000000001 @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/tests/stefan/result.hpp b/tests/stefan/result.hpp new file mode 100644 index 0000000..a93eb32 --- /dev/null +++ b/tests/stefan/result.hpp @@ -0,0 +1,12 @@ +/* ************************************************************************** + + Author: Stefan Reutter + Created: August 2013 + +****************************************************************************/ + +/** + * Run a number of unit tests on result-related objects + */ + + diff --git a/tests/stefan/tcore.cpp b/tests/stefan/tcore.cpp new file mode 100644 index 0000000..c9d3236 --- /dev/null +++ b/tests/stefan/tcore.cpp @@ -0,0 +1,48 @@ +/* ************************************************************************** + + Author: Stefan Reutter + Created: August 2013 + +****************************************************************************/ + +#include "tcore.h" +#include + +namespace DAMARIS +{ +namespace test +{ + +bool testCoreSetup() +{ + details::TestCore* c = NULL; + bool success = false; + try + { + // initialize test core + core_config testConf("./resources/spool0"); + c = new details::TestCore(testConf); + success = true; + } + catch (const DamarisException& e) + { + if (c) delete c; + std::cout << "<<<" << e.what() << ">>> "; + } + if (c) delete c; + return success; +} + + + +details::TestHardware::~TestHardware() +{ + if (the_adc!=NULL) delete the_adc; + if (the_fg!=NULL) delete the_fg; + if (the_pg!=NULL) delete the_pg; + if (the_tc!=NULL) delete the_tc; +} + + +} // namespace test +} // namespace DAMARIS diff --git a/tests/stefan/tcore.h b/tests/stefan/tcore.h new file mode 100644 index 0000000..a7d0dfd --- /dev/null +++ b/tests/stefan/tcore.h @@ -0,0 +1,63 @@ +/* ************************************************************************** + + Author: Stefan Reutter + Created: August 2013 + +****************************************************************************/ + +#ifndef DAMARIS_TEST_CORE_H_ +#define DAMARIS_TEST_CORE_H_ + +#include "core/core.h" +#include "machines/hardware.h" + +namespace DAMARIS +{ +namespace test +{ + +/** + * Test whether a core can be successfully set up (constructor test) + */ +bool testCoreSetup(); + +namespace details +{ + +/** + * + */ +class TestHardware: public hardware +{ +public: + virtual ~TestHardware(); +}; + +/** + * Since core is abstract, we have to derive it. + */ +class TestCore: public core { +public: + TestCore(const core_config& conf): + core(conf), + _name("Test Core") + { + the_hardware=new hardware(); + } + virtual const std::string& core_name() const { + return _name; + } + + virtual ~TestCore(){ + }; + +private: + std::string _name; +}; + +} // namespace details + +} // namespace test +} // namespace DAMARIS + +#endif // include guard diff --git a/tests/stefan/testmain.cpp b/tests/stefan/testmain.cpp new file mode 100644 index 0000000..057475b --- /dev/null +++ b/tests/stefan/testmain.cpp @@ -0,0 +1,70 @@ +/* ************************************************************************** + + Author: Stefan Reutter + Created: August 2013 + +****************************************************************************/ + +#include +#include "tjob.h" +#include "tcore.h" +/** + * \defgroup Tests + * Stefan's DAMARIS test suite contains a number of unit tests meant to ensure that the program will + * run correctly in case parts of the software are changed or added. They also provide a good + * starting point for learning how the backend works. + * + * Each test case is a function that should return true if the test succeeded and false otherwise. + * This means that the test functions must be strongly exception safe with respect to any DAMARIS + * exceptions (i.e. the program should not be aborted and the program should be in the same state + * after running the test as before) and ideally should be strongly exception safe in general. + * + * Tests should be designed to be run independently, so they must initialize any necessary data + * for them to run. To avoid code duplication, use shared test classes or functions. + * + * Test functions should reside in the DAMARIS::test namespace, and that is the only thing which + * should reside there. Implementation details (such as shared classes or non-member functions) + * should go into the DAMARIS::test::details namespace. + */ +/*@{*/ + +namespace DAMARIS +{ +namespace test +{ + +/** + * Function wrapper for test functions that automatically creates formatted output with the result + * value. + * + * \param func A function (pointer) to call + * \param name A string identifying the test for output + */ +void runTest(bool (*func)(), const std::string& name) +{ + std::cout << "Running " << name << " test: "; + std::cout << (func() ? "succeeded" : "failed") << "\n"; +} + +/** + * Run all tests and print their result. + */ +void go() +{ + runTest(testCoreSetup, "Core Setup"); + runTest(testJobs, "Job"); + +} + +} // namespace test +} // namespace DAMARIS +/*@}*/ +int main(int argc, char* argv[]) +{ + DAMARIS::test::go(); + return 0; +} + + + + diff --git a/tests/stefan/tjob.cpp b/tests/stefan/tjob.cpp new file mode 100644 index 0000000..1d682ff --- /dev/null +++ b/tests/stefan/tjob.cpp @@ -0,0 +1,49 @@ +/* ************************************************************************** + + Author: Stefan Reutter + Created: August 2013 + +****************************************************************************/ +#include "tjob.h" +#include + +namespace DAMARIS +{ +namespace test +{ +namespace details +{ +bool quitJob(core* c) +{ + quit_job testJob(0); + result* res = testJob.do_it(c); + if (!res) return res; + return true; +} +} // namespace internal + +bool testJobs() +{ + details::TestCore* c = NULL; + bool success = false; + try + { + // initialize test core + core_config testConf("./resources/spool0"); + c = new details::TestCore(testConf); + + success = true; + } + catch (const DamarisException& e) + { + if (c) delete c; + std::cout << "<<<" << e.what() << ">>> "; + } + if (c) delete c; + return success; +} + +} // namespace test +} // namespace DAMARIS + + diff --git a/tests/stefan/tjob.h b/tests/stefan/tjob.h new file mode 100644 index 0000000..3997b04 --- /dev/null +++ b/tests/stefan/tjob.h @@ -0,0 +1,33 @@ +/* ************************************************************************** + + Author: Stefan Reutter + Created: August 2013 + +****************************************************************************/ + +#ifndef DAMARIS_TEST_JOB_H_ +#define DAMARIS_TEST_JOB_H_ + +#include "tcore.h" +#include "core/core_config.h" + + +/** + * Run a number of unit tests on job-related objects + */ +namespace DAMARIS +{ +namespace test +{ +bool testJobs(); + +namespace details +{ +bool quitJob(core* c); +} // namespace details + +} // namespace test +} // namespace DAMARIS + + +#endif // include guard diff --git a/tests/stopwatch_test.cpp b/tests/stopwatch_test.cpp new file mode 100644 index 0000000..38588da --- /dev/null +++ b/tests/stopwatch_test.cpp @@ -0,0 +1,15 @@ +#include "core/stopwatch.h" +#include +#include + +int main() { + stopwatch s; + + for (size_t i=0;i<10;++i) { + s.start(); + usleep(100000); + s.stop(); + fprintf(stdout,"time elapsed: %gs\n",s.elapsed()); + } + return 0; +} diff --git a/tests/xml_result_test.cpp b/tests/xml_result_test.cpp new file mode 100644 index 0000000..47fa39d --- /dev/null +++ b/tests/xml_result_test.cpp @@ -0,0 +1,26 @@ +#include "core/result.h" +#include "core/xml_result.h" +#include +#include + +int main(int argc, char** argv) { + + if (argc>2) { + if (strcmp("-r",argv[1])==0) { + xml_result_reader r; + result* res=r.read(std::string(argv[2])); + return 0; + } + if (strcmp("-w",argv[1])==0) { + size_t samples=4096; + short int* buffer=(short int*)malloc(sizeof(short int)*2*samples); + for (size_t i=0; i + +int main(int argc, char** argv) { + /* get filename and open file for reading */ + if (argc!=2) { + fprintf(stderr,"%s filename\n adds endline/carrige return to c code if necessary\n",argv[0]); + return 1; + } + FILE* inspect_file=fopen(argv[1],"r+"); + if (inspect_file==NULL) { + fprintf(stderr, "%s: could not open file %s",argv[0],argv[1]); + return 1; + } + /* determine, if file ends without newline/carrige return */ + fseek(inspect_file, -2, SEEK_END); + + char lineend[3]; + size_t charread=fread(lineend, 1, 2, inspect_file); + // nothing inside? fileend? + if (charread==0) return 0; + // is this a file with line end? + if (lineend[charread-1]=='\n' || lineend[charread-1]=='\r') { + fclose(inspect_file); + return 0; + } + + /* if not, determine cr/nl file type */ + fseek(inspect_file, 0, SEEK_SET); + while (1==(charread=fread(lineend,1,1,inspect_file)) && lineend[0]!='\r' && lineend[0]!='\n') {} + if (charread!=1) { + // could not find a newline, just using \n + lineend[0]='\n'; + lineend[1]='\0'; + } + else { + charread=fread(lineend+1,1,1,inspect_file); + if (charread!=1 || lineend[0]==lineend[1] || (lineend[1]!='\n' && lineend[1]!='\r')) + lineend[1]='\0'; + else + lineend[2]='\0'; + } + fclose(inspect_file); + + /* fix file by appending cr/nl */ + inspect_file=fopen(argv[1],"a+"); + if (inspect_file==NULL) { + fprintf(stderr,"%s could not open file %s for appending newlines\n",argv[0],argv[1]); + } + fprintf(inspect_file,lineend); + fclose(inspect_file); + + return 0; +} diff --git a/tools/backend.conf b/tools/backend.conf new file mode 100644 index 0000000..6b2df26 --- /dev/null +++ b/tools/backend.conf @@ -0,0 +1,27 @@ +# for example on Berta +[ADC] +id = 0 +trigger_line = 22 +# Impedance: either 50 Ohms or 1e6 Ohms +# lowest sensitivity for 50 Ohm is 5 V +impedance = 50 +# external refclock frequency: PulseBlaster SP17 has 50 Mhz +# while SP2 has 100 MHz +refclock = 50e6 + +[PB] +id = 0 +# synchronization line: set to 128 to disable synchronization +sync_line = 23 +refclock = 100e6 + +[PTS] +id = 0 +# phase steps +# PTS 310: 0.225 +# PTS 500: 0.36 below 200 MHz +# 0.72 above 200 MHz +phase_stepsize = 0.225 + + + diff --git a/tools/nmr_drivers.sh b/tools/nmr_drivers.sh new file mode 100755 index 0000000..dd4a38f --- /dev/null +++ b/tools/nmr_drivers.sh @@ -0,0 +1,157 @@ +#!/bin/sh +### BEGIN INIT INFO +# Provides: damaris-backends +# Required-Start: $syslog +# Required-Stop: $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Startup script to load nmr hardware drivers for use with damaris backends +# Description: loads available nmr hardware drivers and grants rights to nmr group +# these drivers will be typically used by damaris backends and accompanying programs +### END INIT INFO + +# Script Author: Achim Gaedke +# Created: February 28th, 2005, adapted to LSB June 2008 + +PATH=/sbin:/usr/sbin:/bin:/usr/bin + +RETVAL=0 +# failure value for nonexisting hardware +HARDWARE_FAIL_RETVAL=0 + +NMR_MODULE_DIR=/lib/modules/`uname -r`/kernel/damaris +NMR_GROUP="nmr" + +# tools +RMMOD=/sbin/rmmod +MODPROBE=/sbin/modprobe +KERNELCONF=/boot/config-`uname -r` + +# spectrum related stuff +SPECTRUM_NAME=spc +CONFIG_SMP="`grep 'CONFIG_SMP *=' $KERNELCONF | sed 's/.*=//'`" +if test "x$CONFIG_SMP" = "xy"; then + SPECTRUM_SMP_EXT="_smp"; +else + SPECTRUM_SMP_EXT=""; +fi +SPECTRUM_DEV=/dev/${SPECTRUM_NAME}0 +SPECTRUM_MOD_FILE=$NMR_MODULE_DIR/${SPECTRUM_NAME}${SPECTRUM_SMP_EXT}.ko + +# pulseblaster related stuff +PULSEBLASTER_NAME=pulseblaster +PULSEBLASTER_MOD_FILE=$NMR_MODULE_DIR/$PULSEBLASTER_NAME.ko +PULSEBLASTER_DEV=/dev/$PULSEBLASTER_NAME + +start() { + + SPECTRUM_RETVAL=0 + # find out whether spectrum module is running + if egrep "^spc${SPECTRUM_SMP_EXT} " /proc/modules >/dev/null ; then + echo "spectrum module already loaded" + else + # ok, load it + if test -f $SPECTRUM_MOD_FILE; then + if ! $MODPROBE ${SPECTRUM_NAME}${SPECTRUM_SMP_EXT} >/dev/null 2>&1; then + echo "spectrum module failed to load" + SPECTRUM_RETVAL=1 + fi + else + echo "spectrum module not found" + SPECTRUM_RETVAL=1 + fi + fi + + PULSEBLASTER_RETVAL=0 + # find out whether pulseblaster module is running + if egrep "^pulseblaster " /proc/modules >/dev/null ; then + echo "pulseblaster module already loaded" + else + # ok, load it + if test -f $PULSEBLASTER_MOD_FILE; then + if ! $MODPROBE $PULSEBLASTER_NAME >/dev/null 2>&1; then + echo "pulseblaster module failed to load" + PULSEBLASTER_RETVAL=1 + fi + else + echo "pulseblaster module not found" + PULSEBLASTER_RETVAL=1 + fi + fi + + # have to wait for a short time, let udev do the work + if [ -x /sbin/udevsettle ]; then + /sbin/udevsettle + fi + if [ -x /sbin/udevadm ]; then + /sbin/udevadm settle + fi + if ! test $PULSEBLASTER_RETVAL -ne 0 -o -c ${PULSEBLASTER_DEV}0 ; then + echo "no pulseblaster board is found, only the debug device at ${PULSEBLASTER_DEV}_debug is available" + fi + + # with udev, we should have links and also the rights set correctly + if test $PULSEBLASTER_RETVAL -ne 0 -o $SPECTRUM_RETVAL -ne 0; then + RETVAL=$HARDWARE_FAIL_RETVAL + fi + +} + +stop() { + if egrep "^spc$SPECTRUM_SMP_EXT " /proc/modules >/dev/null ; then + # test wheter spectrum driver is still used + # !!!! the usage counter of the spc kernel module is not maintained!!!! + if test "`lsof $SPECTRUM_DEV`x" = "x"; then + $MODPROBE -r ${SPECTRUM_NAME}${SPECTRUM_SMP_EXT} || RETVAL=1 + else + echo "spectrum driver still in use (use lsof)" + RETVAL=1 + fi + fi + if egrep "^${PULSEBLASTER_NAME} " /proc/modules > /dev/null ; then + # test wheter it is still used + $MODPROBE -r ${PULSEBLASTER_NAME} || RETVAL=1 + fi + # have to wait for a short time, let udev do the work + if [ -x /sbin/udevsettle ]; then + /sbin/udevsettle + fi + if [ -x /sbin/udevadm ]; then + /sbin/udevadm settle + fi +} + +status () { + if egrep "^${SPECTRUM_NAME}$SPECTRUM_SMP_EXT " /proc/modules > /dev/null ; then + echo spectrum module loaded + else + echo spectrum module not loaded + RETVAL=3 + fi + if egrep "^${PULSEBLASTER_NAME} " /proc/modules > /dev/null ; then + echo pulseblaster module loaded + else + echo pulseblaster module not loaded + RETVAL=3 + fi +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + restart) + stop + start + ;; + status) + status + ;; + *) + printf "Usage: %s {start|stop|restart|status}\n" "$0" + RETVAL=1 +esac +exit $RETVAL diff --git a/tools/restart_core.cpp b/tools/restart_core.cpp new file mode 100644 index 0000000..bbad4af --- /dev/null +++ b/tools/restart_core.cpp @@ -0,0 +1,56 @@ +/* ************************************************************************** + + Author: Achim Gaedke + Created: June 2004 + +****************************************************************************/ +#include +#include +#include +#include "core/core_config.h" +#include "core/core_exception.h" + +int main(int argc, const char** argv) { + + if (argc<2 || argc>3) { + fprintf(stderr,"%s [--restart|--quit|--abort] config file\n",argv[0]); + return 1; + } + /* find a config file */ + if (access(argv[argc-1],R_OK)!=0) { + fprintf(stderr,"%s could not read file %s\n",argv[0],argv[argc-1]); + return 1; + } + + pid_t the_pid; + try { + /* parse file */ + the_pid=core_state(std::string(argv[argc-1])).pid; + } + catch(core_exception e) { + fprintf(stderr,e.what()); + return 1; + } + + int signal_to_send=SIGUSR1; + + if (argc==3) { + if (strcmp(argv[1],"--restart")==0) + signal_to_send=SIGUSR1; + else if (strcmp(argv[1],"--quit")==0) + signal_to_send=SIGQUIT; + else if (strcmp(argv[1],"--abort")==0) + signal_to_send=SIGTERM; + else { + fprintf(stderr, "%s allowed arguments are --restart, --quit and --abort\n",argv[0]); + return 1; + } + + } + /* send signal */ + if (0!=kill(the_pid, signal_to_send)) { + fprintf(stderr, "could not send restart signal to process %d\n",the_pid); + return 1; + } + return 0; +}