migrate to standard svn repo layout
This commit is contained in:
commit
5545917824
24
Makefile
Normal file
24
Makefile
Normal file
@ -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
|
55
core/Makefile
Normal file
55
core/Makefile
Normal file
@ -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 $@
|
91
core/backend_config_reader.cpp
Normal file
91
core/backend_config_reader.cpp
Normal file
@ -0,0 +1,91 @@
|
||||
/* **************************************************************************
|
||||
|
||||
Author: Stefan Reutter
|
||||
Created: August 2013
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#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
|
119
core/backend_config_reader.h
Normal file
119
core/backend_config_reader.h
Normal file
@ -0,0 +1,119 @@
|
||||
/* **************************************************************************
|
||||
|
||||
Author: Stefan Reutter
|
||||
Created: August 2013
|
||||
|
||||
****************************************************************************/
|
||||
#ifndef DAMARIS_backend_config_reader_h
|
||||
#define DAMARIS_backend_config_reader_h
|
||||
|
||||
|
||||
#include <glib.h>
|
||||
#include <stdexcept>
|
||||
|
||||
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<gint64> (f, groupName, key);
|
||||
* \endcode
|
||||
*/
|
||||
template<typename returnType>
|
||||
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
|
50
core/constants.h
Normal file
50
core/constants.h
Normal file
@ -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_ */
|
||||
|
303
core/core.cpp
Normal file
303
core/core.cpp
Normal file
@ -0,0 +1,303 @@
|
||||
/* **************************************************************************
|
||||
|
||||
Author: Achim Gaedke
|
||||
Created: June 2004
|
||||
|
||||
****************************************************************************/
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <math.h>
|
||||
#include <errno.h>
|
||||
#include <xercesc/util/PlatformUtils.hpp>
|
||||
#include <xercesc/util/XMLException.hpp>
|
||||
|
||||
#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<control*>(&the_job);
|
||||
if (cjob!=NULL) {
|
||||
return cjob->do_it(this);
|
||||
|
||||
}
|
||||
|
||||
// experiments
|
||||
experiment* ejob=dynamic_cast<experiment*>(&the_job);
|
||||
if (ejob!=NULL) {
|
||||
result* r=ejob->do_it(the_hardware);
|
||||
return r;
|
||||
}
|
||||
|
||||
// experiments
|
||||
configuration* confjob=dynamic_cast<configuration*>(&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;
|
||||
}
|
176
core/core.h
Normal file
176
core/core.h
Normal file
@ -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 <string>
|
||||
#include <time.h>
|
||||
|
||||
/**
|
||||
\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 <b>main</b> 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
|
179
core/core_config.cpp
Normal file
179
core/core_config.cpp
Normal file
@ -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 <cerrno>
|
||||
#include <unistd.h>
|
||||
#include <expat.h>
|
||||
#include <math.h>
|
||||
|
||||
|
||||
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; ++i) {
|
||||
if (strncmp(argv[i],"--spool",7)==0) {
|
||||
if (argv[i][7]==0) {
|
||||
if (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,"<jobs spool=\"%s\" pattern=\"%s\" polltime=\"%g\"/>\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,"<results spool=\"%s\" pattern=\"%s\" encoding=\"%s\"/>\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,"<?xml version=\"1.0\"?>\n");
|
||||
fprintf(configfile,"<state name=\"%s\" pid=\"%d\" starttime=\"%ld\">\n", core_name.c_str(), pid, start_time);
|
||||
xml_write_core_config_lines(configfile);
|
||||
fprintf(configfile,"</state>\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);
|
||||
}
|
78
core/core_config.h
Normal file
78
core/core_config.h
Normal file
@ -0,0 +1,78 @@
|
||||
/* **************************************************************************
|
||||
|
||||
Author: Achim Gaedke
|
||||
Created: June 2004
|
||||
|
||||
****************************************************************************/
|
||||
#ifndef CORE_CONFIG_H
|
||||
#define CORE_CONFIG_H
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include "core/xml_result.h"
|
||||
|
||||
class core;
|
||||
|
||||
/**
|
||||
useful for xml tag parsing
|
||||
*/
|
||||
typedef std::map<const std::string, std::string> 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<const std::string, std::string>& attrs);
|
||||
int dump_state(const std::string& filename) const;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
62
core/core_exception.h
Normal file
62
core/core_exception.h
Normal file
@ -0,0 +1,62 @@
|
||||
/* **************************************************************************
|
||||
|
||||
Author: Achim Gaedke
|
||||
Created: June 2004
|
||||
|
||||
****************************************************************************/
|
||||
#ifndef CORE_EXCEPTION_H
|
||||
#define CORE_EXCEPTION_H
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* 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
|
525
core/job.cpp
Normal file
525
core/job.cpp
Normal file
@ -0,0 +1,525 @@
|
||||
/* **************************************************************************
|
||||
|
||||
Author: Achim Gaedke
|
||||
Created: June 2004
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#include <string>
|
||||
#include <stdarg.h>
|
||||
#include <cmath>
|
||||
#include <xercesc/dom/DOM.hpp>
|
||||
#include <xercesc/util/XMLString.hpp>
|
||||
#include <xercesc/util/PlatformUtils.hpp>
|
||||
#include <xercesc/util/XercesDefs.hpp>
|
||||
#include <xercesc/dom/DOMWriter.hpp>
|
||||
#include <xercesc/framework/MemBufFormatTarget.hpp>
|
||||
#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<state*> 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<XERCES_CPP_NAMESPACE_QUALIFIER DOMElement*>(one_child));
|
||||
if (new_one!=NULL) {
|
||||
|
||||
state* new_state=dynamic_cast<state*>(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<state*>::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<XERCES_CPP_NAMESPACE_QUALIFIER DOMElement*>(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<XERCES_CPP_NAMESPACE_QUALIFIER DOMElement*>(one_child));
|
||||
if (new_one!=NULL) {
|
||||
state* new_state=dynamic_cast<state*>(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<int>(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<configuration_result*>(res);
|
||||
if (cres!=NULL)
|
||||
cres->job_no=job_no;
|
||||
else {
|
||||
configuration_results* cress=dynamic_cast<configuration_results*>(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 std::string, std::string>::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<configuration_device_section>::const_iterator i=configuration_changes.begin();
|
||||
i!=configuration_changes.end();
|
||||
++i) {
|
||||
i->print(f);
|
||||
} // i
|
||||
}
|
218
core/job.h
Normal file
218
core/job.h
Normal file
@ -0,0 +1,218 @@
|
||||
/* **************************************************************************
|
||||
|
||||
Author: Achim Gaedke
|
||||
Created: June 2004
|
||||
|
||||
****************************************************************************/
|
||||
#ifndef JOB_H
|
||||
#define JOB_H
|
||||
|
||||
#include <xercesc/dom/DOMNamedNodeMap.hpp>
|
||||
#include <xercesc/dom/DOMDocument.hpp>
|
||||
#include <xercesc/dom/DOMElement.hpp>
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
#include <cstdio>
|
||||
#include <math.h>
|
||||
#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<std::string,std::string> 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_device_section> 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
|
178
core/job_receiver.cpp
Normal file
178
core/job_receiver.cpp
Normal file
@ -0,0 +1,178 @@
|
||||
/* **************************************************************************
|
||||
|
||||
Author: Achim Gaedke
|
||||
Created: June 2004
|
||||
|
||||
****************************************************************************/
|
||||
#include "job_receiver.h"
|
||||
#include <xercesc/util/XMLString.hpp>
|
||||
#include <xercesc/util/PlatformUtils.hpp>
|
||||
#include <xercesc/dom/DOM.hpp>
|
||||
#include <xercesc/dom/DOMException.hpp>
|
||||
|
||||
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();
|
||||
}
|
53
core/job_receiver.h
Normal file
53
core/job_receiver.h
Normal file
@ -0,0 +1,53 @@
|
||||
/* **************************************************************************
|
||||
|
||||
Author: Achim Gaedke
|
||||
Created: June 2004
|
||||
|
||||
****************************************************************************/
|
||||
#ifndef JOB_RECEIVER_H
|
||||
#define JOB_RECEIVER_H
|
||||
|
||||
#include <string>
|
||||
#include "job.h"
|
||||
#include <xercesc/sax/HandlerBase.hpp>
|
||||
#include <xercesc/parsers/XercesDOMParser.hpp>
|
||||
|
||||
|
||||
/**
|
||||
\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
|
21
core/result.cpp
Normal file
21
core/result.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
/* **************************************************************************
|
||||
|
||||
Author: Achim Gaedke
|
||||
Created: June 2004
|
||||
|
||||
****************************************************************************/
|
||||
#include <cstdio>
|
||||
#include <xercesc/util/PlatformUtils.hpp>
|
||||
#include <xercesc/util/XMLString.hpp>
|
||||
#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();
|
||||
}
|
147
core/result.h
Normal file
147
core/result.h
Normal file
@ -0,0 +1,147 @@
|
||||
/* **************************************************************************
|
||||
|
||||
Author: Achim Gaedke
|
||||
Created: June 2004
|
||||
|
||||
****************************************************************************/
|
||||
#ifndef RESULT_H
|
||||
#define RESULT_H
|
||||
|
||||
#include <xercesc/dom/DOM.hpp>
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
\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<configuration_result*>, 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<adc_result*>, public result {
|
||||
public:
|
||||
adc_results(size_t _no): result(_no) {}
|
||||
virtual ~adc_results() {
|
||||
while (!empty()) {
|
||||
delete back();
|
||||
pop_back();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//@}
|
||||
|
||||
#endif
|
111
core/states.cpp
Normal file
111
core/states.cpp
Normal file
@ -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<const state*>(*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<state_sequent*>(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<state_parallel*>(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<state_atom*> orig_list(*flat);
|
||||
for (size_t loop=1;loop<repeat;++loop)
|
||||
flat->insert(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<state*>(*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<state_sequent*>(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;
|
||||
}
|
375
core/states.h
Normal file
375
core/states.h
Normal file
@ -0,0 +1,375 @@
|
||||
/* **************************************************************************
|
||||
|
||||
Author: Achim Gaedke
|
||||
Created: June 2004
|
||||
|
||||
****************************************************************************/
|
||||
#ifndef STATES_H
|
||||
#define STATES_H
|
||||
|
||||
#include <list>
|
||||
#include <bitset>
|
||||
|
||||
/** \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 <b>"depth-first"</b>. 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<state_atom*> {
|
||||
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<state_atom*>(), length(_length), parent(my_parent) {
|
||||
}
|
||||
|
||||
state(const state& orig): state_atom(), std::list<state_atom*>(), 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_iterator> 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<subsequence_iterator>::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<subsequence_iterator>::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
|
129
core/stopwatch.h
Normal file
129
core/stopwatch.h
Normal file
@ -0,0 +1,129 @@
|
||||
#ifndef STOPWATCH_H
|
||||
#define STOPWATCH_H
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
|
||||
/**
|
||||
\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 */
|
616
core/xml_result.cpp
Normal file
616
core/xml_result.cpp
Normal file
@ -0,0 +1,616 @@
|
||||
#include "xml_result.h"
|
||||
#include <cstdlib>
|
||||
#include <xercesc/util/Base64.hpp>
|
||||
#include <xercesc/util/XMLString.hpp>
|
||||
#include <xercesc/dom/DOM.hpp>
|
||||
#include <xercesc/util/XMLString.hpp>
|
||||
#include <xercesc/util/PlatformUtils.hpp>
|
||||
#include <xercesc/util/XercesDefs.hpp>
|
||||
#include <xercesc/dom/DOMWriter.hpp>
|
||||
#include <xercesc/framework/MemBufFormatTarget.hpp>
|
||||
|
||||
/* ***************************************************************************************************
|
||||
|
||||
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<len) {
|
||||
/* fill buffer */
|
||||
while (base64buffer_length<4 && pos<len) {
|
||||
if (((unsigned char)the_data[pos])<128) {
|
||||
char value=base64_table[(unsigned char)the_data[pos]];
|
||||
if (value!=-1) {
|
||||
base64buffer[base64buffer_length]=value;
|
||||
base64buffer_length++;
|
||||
}
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
/* interprete buffer, that has 4*6 bits=3 byte */
|
||||
if (base64buffer_length==4) {
|
||||
/* check the space */
|
||||
if (bufferpos+3>=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; i<bufferno; i++) {
|
||||
fprintf(f,"%" SIZETPRINTFLETTER ": %" SIZETPRINTFLETTER " samples %f Hz frequency\n",i,sampleno[i],frequencies[i]);
|
||||
if (buffers[i]!=NULL) {
|
||||
for (size_t j=0; j<sampleno[i]; j++) {
|
||||
short int a=buffers[i][j*2];
|
||||
short int b=buffers[i][j*2+1];
|
||||
fprintf(f,"%hd %hd\n",a,b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
void start_element_stub(void* userdata,const XML_Char* name, const XML_Char** attrs ) {
|
||||
((xml_result_handler*)userdata)->start_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<int>(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<const adc_result*>(res);
|
||||
if (adc_res!=NULL) {
|
||||
write_adc_to_file(filename,adc_res);
|
||||
return 0;
|
||||
}
|
||||
const adc_results* adc_ress=dynamic_cast<const adc_results*>(res);
|
||||
if (adc_ress!=NULL) {
|
||||
write_adcs_to_file(filename, adc_ress);
|
||||
return 0;
|
||||
}
|
||||
const configuration_results* config_ress=dynamic_cast<const configuration_results*>(res);
|
||||
if (config_ress!=NULL) {
|
||||
write_configuration_results_to_file(filename, *config_ress);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const error_result* err_res=dynamic_cast<const error_result*>(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,"<?xml version=\"1.0\"?>\n");
|
||||
fprintf(out,"<result job=\"%" SIZETPRINTFLETTER "\">\n",res->job_no);
|
||||
if (res==NULL)
|
||||
fprintf(out,"<!-- got NULL pointer result... -->\n");
|
||||
else
|
||||
fprintf(out,"<!-- don't know how to print result... -->\n");
|
||||
fprintf(out,"</result>\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,"<?xml version=\"1.0\"?>\n");
|
||||
fprintf(out,"<result job=\"%" SIZETPRINTFLETTER "\">\n",res->job_no);
|
||||
fprintf(out," <error>%s</error>\n",res->error_message.c_str());
|
||||
fprintf(out,"</result>");
|
||||
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,"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
|
||||
fprintf(out,"<result job=\"%" SIZETPRINTFLETTER "\">\n<configuration>\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,"</configuration>\n</result>\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,"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
|
||||
fprintf(out,"<result job=\"%" SIZETPRINTFLETTER "\">\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,"</result>\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,"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
|
||||
fprintf(out,"<result job=\"%" SIZETPRINTFLETTER "\">\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,"</result>\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,
|
||||
"<adcdatafile path=\"%s\" samples=\"%" SIZETPRINTFLETTER "\" rate=\"%g\" channels=\"%i\"/>\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,"<adcdata samples=\"%" SIZETPRINTFLETTER "\" rate=\"%g\" channels=\"%i\">\n",res->samples,res->sampling_frequency, res->nchannels);
|
||||
for(size_t i=0;i<res->samples;++i) {
|
||||
for (int j = 0; j < res->nchannels; j++) {
|
||||
fprintf(out, format.c_str(), res->data[i*2 + j]);
|
||||
}
|
||||
}
|
||||
fprintf(out,"</adcdata>\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xml_result_writer::write_adcdata_base64(FILE* out, const adc_result* res) const {
|
||||
fprintf(out,"<adcdata samples=\"%" SIZETPRINTFLETTER "\" rate=\"%g\" channels=\"%i\">\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,"</adcdata>\n");
|
||||
return 0;
|
||||
}
|
158
core/xml_result.h
Normal file
158
core/xml_result.h
Normal file
@ -0,0 +1,158 @@
|
||||
/* ******************************************************************************
|
||||
|
||||
author: Achim Gaedke
|
||||
|
||||
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef XML_RESULT_H
|
||||
#define XML_RESULT_H
|
||||
|
||||
#include "expat.h"
|
||||
#include "result.h"
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
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 */
|
369
core/xml_states.cpp
Normal file
369
core/xml_states.cpp
Normal file
@ -0,0 +1,369 @@
|
||||
/* **************************************************************************
|
||||
|
||||
Author: Achim Gaedke
|
||||
Created: June 2004
|
||||
|
||||
****************************************************************************/
|
||||
#include "states.h"
|
||||
#include "xml_states.h"
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
#include <cstring>
|
||||
|
||||
#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<state*>(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,"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
|
||||
}
|
||||
|
||||
std::string indent_string(indent_size,' ');
|
||||
|
||||
const state_sequent* ss=dynamic_cast<const state_sequent*>(&states_to_write);
|
||||
if (ss!=NULL) {
|
||||
if (ss->repeat==1)
|
||||
fprintf(output,"%s<sequent>\n",indent_string.c_str());
|
||||
else
|
||||
fprintf(output,"%s<sequent repeat=\"%" SIZETPRINTFLETTER "\">\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</sequent>\n",indent_string.c_str());
|
||||
return 1;
|
||||
}
|
||||
const state_parallel* sp=dynamic_cast<const state_parallel*>(&states_to_write);
|
||||
if (sp!=NULL){
|
||||
fprintf(output,"%s<parallel>\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</parallel>\n",indent_string.c_str());
|
||||
return 1;
|
||||
}
|
||||
const state* st=dynamic_cast<const state*>(&states_to_write);
|
||||
if (st!=NULL) {
|
||||
fprintf(output,"%s<state length=\"%g\">\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</state>\n",indent_string.c_str());
|
||||
return 1;
|
||||
}
|
||||
const ttlout* ttlo=dynamic_cast<const ttlout*>(&states_to_write);
|
||||
if (ttlo!=NULL) {
|
||||
fprintf(output,"%s<ttlout value=\"0x%lx\"/>\n",
|
||||
indent_string.c_str(),
|
||||
ttlo->ttls.to_ulong());
|
||||
return 1;
|
||||
}
|
||||
const analogout* ao=dynamic_cast<const analogout*>(&states_to_write);
|
||||
if (ao!=NULL) {
|
||||
fprintf(output,
|
||||
"%s<analogout id=\"%d\" frequency=\"%g\" amplitude=\"%g\" phase=\"%g\" dac_value=\"%d\"/>\n",
|
||||
indent_string.c_str(),
|
||||
ao->id,
|
||||
ao->frequency,
|
||||
ao->amplitude,
|
||||
ao->phase,
|
||||
ao->dac_value);
|
||||
|
||||
return 1;
|
||||
}
|
||||
const analogin* ai=dynamic_cast<const analogin*>(&states_to_write);
|
||||
if (ai!=NULL) {
|
||||
char args[2000];
|
||||
char buffer[1000];
|
||||
sprintf(args, "%s<analogin id=\"%d\" samples=\"%" SIZETPRINTFLETTER "\" sample_frequency=\"%g\" channels=\"%lu\" resolution=\"%" SIZETPRINTFLETTER "\"",
|
||||
indent_string.c_str(),
|
||||
ai->id,
|
||||
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<!-- something missing -->\n",indent_string.c_str());
|
||||
return 0;
|
||||
}
|
162
core/xml_states.h
Normal file
162
core/xml_states.h
Normal file
@ -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 <expat.h>
|
||||
#include <cstdio>
|
||||
#include <xercesc/dom/DOMElement.hpp>
|
||||
|
||||
/**
|
||||
\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<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<sequent repeat="3">
|
||||
<state time="3e-3">
|
||||
<ttlout value="4"/>
|
||||
<ttlout channel="2" state="0"/>
|
||||
<analogout channel="1" f="1e6" p="0"/>
|
||||
<analogout channel="2" f="1e6" p="90"/>
|
||||
</state>
|
||||
<sequent repeat="15">
|
||||
<state time="3e-3">
|
||||
<ttlout value="7"/>
|
||||
<analogout channel="1" f="1e6" p="0"/>
|
||||
<analogout channel="2" f="1e6" p="90"/>
|
||||
</state>
|
||||
<state time="1e-3">
|
||||
<analogin f="5e6" s="4096"/>
|
||||
<state/>
|
||||
</sequent>
|
||||
</sequent>\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 \<sequent repeat="3"\>.
|
||||
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 \</sequent\>.
|
||||
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 \</state\>.
|
||||
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
|
||||
\<sequent\> and is closed with an extra slash before the name \</sequent\>. 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. \<ttlout value="7"/\>.
|
||||
\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 <b>one sequent</b> section, containing again serval <b>state</b> or <b>sequent</b>
|
||||
sections. The <b>state</b> sections must not contain other <b>state</b> sections or <b>sequent</b> sections.
|
||||
|
||||
*/
|
||||
//@{
|
||||
/**
|
||||
\brief gains state sequence from an xml event stream
|
||||
*/
|
||||
class xml_state_reader {
|
||||
state_atom* root;
|
||||
std::list<state_atom*> 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<state_atom*> 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
|
282
doc/Doxyfile
Normal file
282
doc/Doxyfile
Normal file
@ -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
|
30
doc/Makefile
Normal file
30
doc/Makefile
Normal file
@ -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
|
33
doc/experiment_job.xml
Normal file
33
doc/experiment_job.xml
Normal file
@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<experiment id="0">
|
||||
<!-- saturation recovery -->
|
||||
<sequent>
|
||||
<!-- saturation comb -->
|
||||
<sequent>
|
||||
<!-- suppose, that gate is on channel 1 -->
|
||||
<!-- suppose, that pulse is on channel 2 -->
|
||||
<state time="100e-6"><ttlout value="1"/><analogout f="1e7"/></state>
|
||||
<state time="5e-6"><ttlout value="3"/><analogout f="1e7"/></state>
|
||||
<state time="2e-6"><ttlout value="1"/><analogout f="1e7"/></state>
|
||||
<state time="5e-6"><ttlout value="3"/><analogout f="1e7"/></state>
|
||||
<state time="1e-6"><ttlout value="1"/><analogout f="1e7"/></state>
|
||||
<state time="5e-6"><ttlout value="3"/><analogout f="1e7"/></state>
|
||||
<state time="1e-6"><ttlout value="1"/><analogout f="1e7"/></state>
|
||||
<state time="5e-6"><ttlout value="3"/><analogout f="1e7"/></state>
|
||||
</sequent>
|
||||
<!-- evolution time -->
|
||||
<sequent>
|
||||
<state time="1e-3"/>
|
||||
<state time="100e-6"><ttlout value="1"/><analogout f="1e7"/></state>
|
||||
</sequent>
|
||||
<!-- ninty degree pulse and data recording after dead time -->
|
||||
<sequent>
|
||||
<state time="5e-6"><ttlout value="3"/><analogout f="1e5"/></state>
|
||||
<state time="1e-6"><ttlout value="0"/><analogout f="1e5"/></state>
|
||||
<state time="1e-2">
|
||||
<analogin s="2000" f="2e6" resolution="12" sensitivity="4"/>
|
||||
<analogout f="1e5"/>
|
||||
</state>
|
||||
</sequent>
|
||||
</sequent>
|
||||
</experiment>
|
6
doc/footer.html
Normal file
6
doc/footer.html
Normal file
@ -0,0 +1,6 @@
|
||||
<p>
|
||||
<hr><br>
|
||||
$projectname, version $projectnumber: back to <a href="main.html">mainpage</a>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
3
doc/quit_job.xml
Normal file
3
doc/quit_job.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<quit/>
|
||||
|
BIN
doc/software_design_overview.png
Normal file
BIN
doc/software_design_overview.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 73 KiB |
3
doc/wait_job.xml
Normal file
3
doc/wait_job.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<wait length="120"/>
|
||||
|
64
drivers/ADC.h
Normal file
64
drivers/ADC.h
Normal file
@ -0,0 +1,64 @@
|
||||
/* **************************************************************************
|
||||
|
||||
Author: Achim Gaedke
|
||||
Created: June 2004
|
||||
|
||||
****************************************************************************/
|
||||
#ifndef ADC_H
|
||||
#define ADC_H
|
||||
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#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
|
296
drivers/Datel-PCI416/416inc/41632DLL.H
Normal file
296
drivers/Datel-PCI416/416inc/41632DLL.H
Normal file
@ -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 <windows.h>
|
||||
|
||||
#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
|
1311
drivers/Datel-PCI416/416inc/416vxdio.h
Normal file
1311
drivers/Datel-PCI416/416inc/416vxdio.h
Normal file
File diff suppressed because it is too large
Load Diff
1032
drivers/Datel-PCI416/416inc/PCI416IO.H
Normal file
1032
drivers/Datel-PCI416/416inc/PCI416IO.H
Normal file
File diff suppressed because it is too large
Load Diff
178
drivers/Datel-PCI416/416inc/PCI416_32dll.h
Normal file
178
drivers/Datel-PCI416/416inc/PCI416_32dll.h
Normal file
@ -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 <windows.h>
|
||||
|
||||
// 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
|
477
drivers/Datel-PCI416/416inc/PCI416libW95.h
Normal file
477
drivers/Datel-PCI416/416inc/PCI416libW95.h
Normal file
@ -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
|
171
drivers/Datel-PCI416/416inc/Pcilib32.h
Normal file
171
drivers/Datel-PCI416/416inc/Pcilib32.h
Normal file
@ -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
|
35
drivers/Datel-PCI416/416inc/Timer.h
Normal file
35
drivers/Datel-PCI416/416inc/Timer.h
Normal file
@ -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
|
33
drivers/Datel-PCI416/416inc/admstats.h
Normal file
33
drivers/Datel-PCI416/416inc/admstats.h
Normal file
@ -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
|
206
drivers/Datel-PCI416/416inc/pci416df.h
Normal file
206
drivers/Datel-PCI416/416inc/pci416df.h
Normal file
@ -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
|
233
drivers/Datel-PCI416/Datel-PCI416.cpp
Normal file
233
drivers/Datel-PCI416/Datel-PCI416.cpp
Normal file
@ -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<state_sequent&>(exp));
|
||||
state* a_state=(state*)si.get_state();
|
||||
/* loop over all states */
|
||||
while (NULL!=a_state) {
|
||||
// collect analogin sections in state
|
||||
std::list<analogin*> inputs;
|
||||
/* loop over all device definitions in a state */
|
||||
state::iterator i=a_state->begin();
|
||||
while (i!=a_state->end()) {
|
||||
analogin* input=dynamic_cast<analogin*>(*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");
|
||||
}
|
||||
|
97
drivers/Datel-PCI416/Datel-PCI416.h
Normal file
97
drivers/Datel-PCI416/Datel-PCI416.h
Normal file
@ -0,0 +1,97 @@
|
||||
/* **************************************************************************
|
||||
|
||||
Author: Achim Gaedke
|
||||
Created: June 2004
|
||||
|
||||
****************************************************************************/
|
||||
#ifndef DATELPCI416
|
||||
#define DATELPCI416
|
||||
|
||||
#include "drivers/ADC.h"
|
||||
#include "core/states.h"
|
||||
#include <cstdlib>
|
||||
#include <windows.h>
|
||||
#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
|
32
drivers/Datel-PCI416/Makefile
Normal file
32
drivers/Datel-PCI416/Makefile
Normal file
@ -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:
|
||||
|
302
drivers/Eurotherm-2000Series/Eurotherm-2000Series.cpp
Normal file
302
drivers/Eurotherm-2000Series/Eurotherm-2000Series.cpp
Normal file
@ -0,0 +1,302 @@
|
||||
/* **************************************************************************
|
||||
|
||||
Author: Holger Stork, Achim Gaedke
|
||||
Created: January 2005
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#include "Eurotherm-2000Series.h"
|
||||
#include <cstdio>
|
||||
#include <cmath>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#include <errno.h>
|
||||
#include <locale.h>
|
||||
#include <string>
|
||||
|
||||
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<std::string,std::string> config;
|
||||
// configure for <20>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<std::string,std::string>& config) {
|
||||
std::string error_message;
|
||||
try {
|
||||
std::string mode;
|
||||
const char config_mode[]="0002.";
|
||||
read_value("IM",mode);
|
||||
for (std::map<std::string,std::string>::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;i<return_value.size()-1;++i) bcc^=return_value[i];
|
||||
if (bcc!=got_byte) {
|
||||
return_value.clear();
|
||||
throw Eurotherm2000Series_error("read_value: bcc missmatch");
|
||||
}
|
||||
|
||||
// compare C1 and C2
|
||||
if (return_value.substr(1,param_name.size())!=param_name) {
|
||||
return_value.clear();
|
||||
throw Eurotherm2000Series_error("read_value: register name echo mismatch");
|
||||
}
|
||||
// cut protocol bytes
|
||||
return_value.erase(0,3);
|
||||
return_value.resize(return_value.size()-2);
|
||||
return;
|
||||
}
|
||||
|
||||
int Eurotherm2000Series::set_value(const std::string& param_name, const std::string& value) {
|
||||
// comunicate with device
|
||||
std::string message;
|
||||
message+=4; //EOT
|
||||
message+='0'+(address/10);
|
||||
message+='0'+(address/10);
|
||||
message+='0'+(address%10);
|
||||
message+='0'+(address%10);
|
||||
message+=2; //STX
|
||||
message+=param_name.substr(0,2);
|
||||
message+=value;
|
||||
message+=3; //ETX
|
||||
|
||||
// calculate bcc byte
|
||||
unsigned char bcc=message[6];
|
||||
for (size_t i=7;i<message.size();++i) bcc^=message[i];
|
||||
message+=(unsigned char)bcc; //BCC
|
||||
|
||||
#if 0
|
||||
printf("param: %s, value: %s: ",param_name.c_str(),value.c_str());
|
||||
for (size_t i=0; i<message.size();i++)
|
||||
printf("%02x ",(unsigned char)message[i]);
|
||||
printf("\n");
|
||||
#endif
|
||||
|
||||
pthread_mutex_lock(&device_lock);
|
||||
write(serial_dev,message.c_str(),message.size());
|
||||
// sleep minimum latency time for value setting
|
||||
timespec write_latency;
|
||||
write_latency.tv_sec=0; write_latency.tv_nsec=5000000;
|
||||
nanosleep(&write_latency,NULL);
|
||||
// wait shorter while polling
|
||||
write_latency.tv_nsec=1000000;
|
||||
int timeout=1000;
|
||||
unsigned char got_byte;
|
||||
int result;
|
||||
while ((result=read(serial_dev,&got_byte,1))<=0) {
|
||||
if (result<0 && errno!=EAGAIN) {
|
||||
pthread_mutex_unlock(&device_lock);
|
||||
throw Eurotherm2000Series_error("set_value: read error");
|
||||
}
|
||||
if (timeout<0) {
|
||||
pthread_mutex_unlock(&device_lock);
|
||||
throw Eurotherm2000Series_error("set_value: timeout occurred");
|
||||
}
|
||||
nanosleep(&write_latency,NULL);
|
||||
timeout--;
|
||||
}
|
||||
pthread_mutex_unlock(&device_lock);
|
||||
if (got_byte==0x15) {
|
||||
char message_buffer[256];
|
||||
std::string ee_answer;
|
||||
read_value("EE",ee_answer);
|
||||
snprintf(message_buffer, 256, "set_value: negative acknoledge (ee=0x%02x)",ee_answer.c_str()[0]);
|
||||
throw Eurotherm2000Series_error(message_buffer);
|
||||
}
|
||||
if (got_byte!=6) {
|
||||
char message_buffer[256];
|
||||
snprintf(message_buffer,256,"(0x%02x)",got_byte);
|
||||
throw Eurotherm2000Series_error(std::string("set_value: no acknoledge ")+message_buffer);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
double Eurotherm2000Series::get_temperature() const {
|
||||
int summary_status=get_summary_status();
|
||||
if (failure_mask&summary_status) {
|
||||
char message_buffer[10];
|
||||
snprintf(message_buffer,10,"0x%04x",summary_status);
|
||||
throw Eurotherm2000Series_error(std::string("sensor/heater/temperature range fault: ")+message_buffer);
|
||||
}
|
||||
std::string answer;
|
||||
read_value("PV",answer);
|
||||
return strtod(answer.c_str(),NULL);
|
||||
}
|
||||
|
||||
double Eurotherm2000Series::set_setpoint(double ct) {
|
||||
char buffer[8];
|
||||
snprintf(buffer,8,fp_format.c_str(),ct);
|
||||
set_value("SL",buffer);
|
||||
// extra wait, until the get_setpoint function returns the same value
|
||||
timespec write_latency;
|
||||
write_latency.tv_sec=0; write_latency.tv_nsec=50000000;
|
||||
nanosleep(&write_latency,NULL);
|
||||
write_latency.tv_nsec=100000000;
|
||||
int i=100;
|
||||
while(fabs(get_setpoint()-ct)>1e-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);
|
||||
}
|
161
drivers/Eurotherm-2000Series/Eurotherm-2000Series.h
Normal file
161
drivers/Eurotherm-2000Series/Eurotherm-2000Series.h
Normal file
@ -0,0 +1,161 @@
|
||||
/* **************************************************************************
|
||||
|
||||
Author: Holger Stork, Achim Gaedke
|
||||
Created: January 2005
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef EUROTHERM2000SERIES_H
|
||||
#define EUROTHERM2000SERIES_H
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#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<std::string, std::string>& 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 <EFBFBD>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 */
|
37
drivers/Eurotherm-2000Series/Makefile
Normal file
37
drivers/Eurotherm-2000Series/Makefile
Normal file
@ -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}*;
|
71
drivers/Eurotherm-2000Series/control.cpp
Normal file
71
drivers/Eurotherm-2000Series/control.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
#include "Eurotherm-2000Series.h"
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
|
||||
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;
|
||||
|
||||
}
|
24
drivers/Eurotherm-2000Series/gnuplot_output.cpp
Normal file
24
drivers/Eurotherm-2000Series/gnuplot_output.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
#include "Eurotherm-2000Series.h"
|
||||
#include <cstdio>
|
||||
|
||||
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;
|
||||
}
|
18
drivers/Eurotherm-2000Series/test.cpp
Normal file
18
drivers/Eurotherm-2000Series/test.cpp
Normal file
@ -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;
|
||||
}
|
47
drivers/Makefile
Normal file
47
drivers/Makefile
Normal file
@ -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
|
34
drivers/PTS-Synthesizer/Makefile
Normal file
34
drivers/PTS-Synthesizer/Makefile
Normal file
@ -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.*
|
273
drivers/PTS-Synthesizer/PTS.cpp
Normal file
273
drivers/PTS-Synthesizer/PTS.cpp
Normal file
@ -0,0 +1,273 @@
|
||||
/* ***************************************************************************
|
||||
|
||||
Author: Achim Gädke
|
||||
Date: October 2004
|
||||
|
||||
**************************************************************************** */
|
||||
|
||||
#include "PTS.h"
|
||||
#include <cstdio>
|
||||
#include <cmath>
|
||||
|
||||
#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<ttlout>::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<state_sequent*>(&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<analogout*>(*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<state_sequent*>(&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<state_sequent*>(*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<state*>(*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<analogout*>(*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 */
|
||||
}
|
||||
}
|
127
drivers/PTS-Synthesizer/PTS.h
Normal file
127
drivers/PTS-Synthesizer/PTS.h
Normal file
@ -0,0 +1,127 @@
|
||||
/* ***************************************************************************
|
||||
|
||||
Author: Achim Gaedke
|
||||
Created: October 2004
|
||||
|
||||
**************************************************************************** */
|
||||
|
||||
#ifndef PTS_H
|
||||
#define PTS_H
|
||||
#include "drivers/frequgen.h"
|
||||
#include <vector>
|
||||
#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<ttlout> 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
|
122
drivers/PTS-Synthesizer/PTS_test.cpp
Normal file
122
drivers/PTS-Synthesizer/PTS_test.cpp
Normal file
@ -0,0 +1,122 @@
|
||||
/* ***************************************************************************
|
||||
|
||||
Author: Achim Gädke
|
||||
Created: October 2004
|
||||
|
||||
**************************************************************************** */
|
||||
|
||||
#include "PTS.h"
|
||||
#include <cstdio>
|
||||
#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;
|
||||
}
|
||||
|
117
drivers/Spectrum-M2i40xxSeries/GatedData.cpp
Normal file
117
drivers/Spectrum-M2i40xxSeries/GatedData.cpp
Normal file
@ -0,0 +1,117 @@
|
||||
#include "GatedData.h"
|
||||
#include <cstring>
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
50
drivers/Spectrum-M2i40xxSeries/GatedData.h
Normal file
50
drivers/Spectrum-M2i40xxSeries/GatedData.h
Normal file
@ -0,0 +1,50 @@
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
|
||||
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();
|
||||
};
|
37
drivers/Spectrum-M2i40xxSeries/Makefile
Normal file
37
drivers/Spectrum-M2i40xxSeries/Makefile
Normal file
@ -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
|
607
drivers/Spectrum-M2i40xxSeries/Spectrum-M2i40xxSeries.cpp
Normal file
607
drivers/Spectrum-M2i40xxSeries/Spectrum-M2i40xxSeries.cpp
Normal file
@ -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 <cmath>
|
||||
#include <stack>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#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<lRangeCount) && (i<SPCM_MAX_AIRANGE); i++) {
|
||||
spcm_dwGetParam_i32(hDrv, SPC_READRANGEMIN0 + i, &lRangeMin[i]);
|
||||
spcm_dwGetParam_i32(hDrv, SPC_READRANGEMAX0 + i, &lRangeMax[i]);
|
||||
}
|
||||
spcm_dwGetParam_i32(hDrv, SPC_READAIFEATURES, &lAIFeatures);
|
||||
|
||||
bInputTermAvailable = (lAIFeatures & SPCM_AI_TERM) != 0;
|
||||
bDiffModeAvailable = (lAIFeatures & SPCM_AI_DIFF) != 0;
|
||||
bACCouplingAvailable =(lAIFeatures & SPCM_AI_ACCOUPLING) != 0;
|
||||
bBWLimitAvailable = (lAIFeatures & SPCM_AI_LOWPASS) != 0;
|
||||
bOffsPercentMode = (lAIFeatures & SPCM_AI_OFFSPERCENT) != 0;
|
||||
|
||||
spcm_dwGetParam_i32(hDrv, SPC_MIINST_MAXADCVALUE, &lMaxADCValue);
|
||||
|
||||
// set up gated sampling mode
|
||||
spcm_dwSetParam_i32(hDrv, SPC_CARDMODE, SPC_REC_STD_GATE);
|
||||
//spcm_dwSetParam_i32(hDrv, SPC_CARDMODE, SPC_REC_STD_SINGLE);
|
||||
|
||||
// set up external TTL trigger
|
||||
spcm_dwSetParam_i32(hDrv, SPC_TRIG_ORMASK, SPC_TMASK_EXT0);
|
||||
spcm_dwSetParam_i32(hDrv, SPC_TRIG_EXT0_MODE, SPC_TM_POS);
|
||||
//spcm_dwSetParam_i32(hDrv, SPC_PRETRIGGER, 16384);
|
||||
//spcm_dwSetParam_i32(hDrv, SPC_TRIG_CH_ORMASK0, SPC_TMASK0_CH0);
|
||||
//spcm_dwSetParam_i32(hDrv, SPC_TRIG_CH0_MODE, SPC_TM_POS);
|
||||
//spcm_dwSetParam_i32(hDrv, SPC_TRIG_TERM, 1);
|
||||
//spcm_dwSetParam_i32(hDrv, SPC_TRIG_DELAY, 2000);
|
||||
|
||||
|
||||
//spcm_dwSetParam_i32(hDrv, SPC_TIMEOUT, 5000);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
char* SpectrumM2i40xxSeries::Configuration::PrintInfo() {
|
||||
char *sInfo = new char[10000];
|
||||
char *sTmp = new char[100];
|
||||
|
||||
// the card type + serial number
|
||||
switch (lCardType & TYP_SERIESMASK){
|
||||
case TYP_M2ISERIES: sprintf(sInfo, "M2i.%04x sn %05d\n",(unsigned int) (lCardType & TYP_VERSIONMASK), lSerialNumber); break;
|
||||
case TYP_M2IEXPSERIES: sprintf(sInfo, "M2i.%04x-Exp sn %05d\n", (unsigned int)( lCardType & TYP_VERSIONMASK), lSerialNumber); break;
|
||||
default: sprintf(sInfo, "Type: %x not supported so far\n", lCardType); break;
|
||||
}
|
||||
sprintf(sTmp, " Installed memory: %lld MByte\n", llInstMemBytes / 1024 / 1024);
|
||||
strcat(sInfo, sTmp);
|
||||
sprintf(sTmp, " Max sampling rate: %.1f MS/s\n", (double) lMaxSampleRate / 1000000);
|
||||
strcat(sInfo, sTmp);
|
||||
sprintf(sTmp, " Channels: %d\n", lMaxChannels);
|
||||
strcat(sInfo, sTmp);
|
||||
sprintf(sTmp, " Kernel Version: %d.%02d build %d\n", lKernelVersion >> 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<state*>(*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<state_parallel*>(*i)!=NULL)
|
||||
throw SpectrumM2i40xxSeries_error("State parallel is not implemented");
|
||||
|
||||
state_sequent* a_sequence=dynamic_cast<state_sequent*>(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<analogin*> inputs;
|
||||
|
||||
// loop over analogin states for this device
|
||||
state::iterator k = a_state->begin();
|
||||
while (k != a_state->end()) {
|
||||
analogin* input = dynamic_cast<analogin*>(*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<state_sequent*>(&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<structure.n; ++i) {
|
||||
data = split_adcdata_recursive(data, *(structure.child), result_splitted);
|
||||
}
|
||||
}
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
void SpectrumM2i40xxSeries::sample_after_external_trigger(double rate, size_t samples, double sensitivity, size_t resolution) {
|
||||
throw SpectrumM2i40xxSeries_error("SpectrumM2i40xxSeries::sample_after_external_trigger is not implemented");
|
||||
}
|
||||
|
||||
SpectrumM2i40xxSeries::~SpectrumM2i40xxSeries() {
|
||||
spcm_vClose(default_settings.hDrv);
|
||||
}
|
||||
|
277
drivers/Spectrum-M2i40xxSeries/Spectrum-M2i40xxSeries.h
Normal file
277
drivers/Spectrum-M2i40xxSeries/Spectrum-M2i40xxSeries.h
Normal file
@ -0,0 +1,277 @@
|
||||
// todo: Multi-Platform support is not implemented.
|
||||
// todo: fifo if needed
|
||||
|
||||
|
||||
#ifndef SPECTRUM_M2i40XXSERIES
|
||||
#define SPECTRUM_M2i40XXSERIES
|
||||
|
||||
#define SPCM_MAX_AIRANGE 8
|
||||
#define SPCM_MAX_AICHANNEL 16
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <math.h>
|
||||
#include "core/states.h"
|
||||
#include "core/constants.h"
|
||||
#include "drivers/ADC.h"
|
||||
#include "GatedData.h"
|
||||
|
||||
#if defined __linux__
|
||||
// ----- linux includes -----
|
||||
# include <cstdio>
|
||||
# include <fcntl.h>
|
||||
# include <sys/ioctl.h>
|
||||
# include <unistd.h>
|
||||
# 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<short int*> 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
|
BIN
drivers/Spectrum-M2i40xxSeries/drv_spcm_linux_drv_v214b5633.zip
Normal file
BIN
drivers/Spectrum-M2i40xxSeries/drv_spcm_linux_drv_v214b5633.zip
Normal file
Binary file not shown.
67
drivers/Spectrum-M2i40xxSeries/test.cpp
Normal file
67
drivers/Spectrum-M2i40xxSeries/test.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
#include "Spectrum-M2i40xxSeries.h"
|
||||
#include "../../machines/hardware.h"
|
||||
#include "include/regs.h"
|
||||
#include "core/states.h"
|
||||
|
||||
#include <typeinfo>
|
||||
|
||||
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<adc_results*>(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());
|
||||
}
|
||||
}
|
109
drivers/Spectrum-MI40xxSeries/GatedData.cpp
Normal file
109
drivers/Spectrum-MI40xxSeries/GatedData.cpp
Normal file
@ -0,0 +1,109 @@
|
||||
#include "GatedData.h"
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
50
drivers/Spectrum-MI40xxSeries/GatedData.h
Normal file
50
drivers/Spectrum-MI40xxSeries/GatedData.h
Normal file
@ -0,0 +1,50 @@
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
|
||||
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();
|
||||
};
|
42
drivers/Spectrum-MI40xxSeries/Makefile
Normal file
42
drivers/Spectrum-MI40xxSeries/Makefile
Normal file
@ -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
|
971
drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.cpp
Normal file
971
drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.cpp
Normal file
@ -0,0 +1,971 @@
|
||||
#include <cmath>
|
||||
#include <stack>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#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<state*>(*i);
|
||||
if (a_state==NULL)
|
||||
throw SpectrumMI40xxSeries_error("state_atom not expected in sate_sequent");
|
||||
if (dynamic_cast<state_parallel*>(*i)!=NULL)
|
||||
throw SpectrumMI40xxSeries_error("state_parallel handling is not jet implemented");
|
||||
state_sequent* a_sequence=dynamic_cast<state_sequent*>(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<analogin*> inputs;
|
||||
/* loop over all device definitions in a state */
|
||||
state::iterator j=a_state->begin();
|
||||
while (j!=a_state->end()) {
|
||||
analogin* input=dynamic_cast<analogin*>(*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<state*>(*(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<short int*>::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<state_sequent*>(&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<fifo_minimal_size) {
|
||||
|
||||
#if SPC_DEBUG
|
||||
fprintf(stderr, "expecting %" SIZETPRINTFLETTER " samples in normal mode\n",sampleno);
|
||||
#endif
|
||||
|
||||
SpcSetParam (deviceno, SPC_MEMSIZE, sampleno); // Memory size
|
||||
|
||||
// ----- start the board -----
|
||||
nErr = SpcSetParam (deviceno, SPC_COMMAND, SPC_START); // start the board
|
||||
} else {
|
||||
|
||||
#if SPC_DEBUG
|
||||
fprintf(stderr, "expecting %" SIZETPRINTFLETTER " samples in fifo mode\n",sampleno);
|
||||
#endif
|
||||
// todo: fifo should really write directly to recursive gated sampling structures
|
||||
fifo_adc_data=(short int*)malloc(sampleno*sizeof(short int)*2);
|
||||
if (fifo_adc_data==NULL) {
|
||||
throw SpectrumMI40xxSeries_error("could not allocate adc data memory for fifo recording");
|
||||
}
|
||||
// ToDo: do some magic calculations
|
||||
fifobufferno=16;
|
||||
fifobufferlen=1<<20;
|
||||
#if SPC_DEBUG
|
||||
fprintf(stderr, "configuring for %" SIZETPRINTFLETTER " buffers, each of size %" SIZETPRINTFLETTER "\n", fifobufferno, fifobufferlen);
|
||||
#endif
|
||||
SpcSetParam(deviceno,SPC_FIFO_BUFFERS,fifobufferno);
|
||||
SpcSetParam(deviceno,SPC_FIFO_BUFLEN,fifobufferlen);
|
||||
// allocate FIFO buffers
|
||||
fifobuffers.resize(fifobufferno,(short int*)NULL);
|
||||
for (size_t i=0; i!=fifobufferno; ++i) {
|
||||
void* newbuffer;
|
||||
#if SPC_DEBUG
|
||||
fprintf(stderr, "allocating fifobuffer %" SIZETPRINTFLETTER "...", i);
|
||||
#endif
|
||||
newbuffer=malloc(fifobufferlen);
|
||||
if (newbuffer==NULL) {
|
||||
// free the buffers, if there is not enough memory
|
||||
for (size_t j=0; j!=i; ++j) free(fifobuffers[j]);
|
||||
fifobuffers.clear();
|
||||
throw SpectrumMI40xxSeries_error("could not allocate buffers for fifo mode");
|
||||
}
|
||||
#if SPC_DEBUG
|
||||
fprintf(stderr, "and registering with ADC driver....");
|
||||
#endif
|
||||
fifobuffers[i]=(short int*)newbuffer;
|
||||
// todo: check for errors
|
||||
#ifndef _LINUX64
|
||||
SpcSetParam (deviceno, SPC_FIFO_BUFADR0+i, (int32) newbuffer);
|
||||
#else
|
||||
// 64 bit Linux needs SetAdr function, 32 bit linux can also use this if driver build > 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<short int*>::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_number<buff_number_expected);
|
||||
#if SPC_DEBUG
|
||||
fprintf(stderr, "SPC_STOP\n");
|
||||
#endif
|
||||
SpcSetParam(deviceno, SPC_COMMAND, SPC_STOP);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
short int* SpectrumMI40xxSeries::split_adcdata_recursive(short int* data, const DataManagementNode& structure, adc_results& result_splitted) {
|
||||
short nchannels = effective_settings->lSetChannels;
|
||||
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; i<structure.n; ++i) {
|
||||
data=split_adcdata_recursive(data, *(structure.child), result_splitted);
|
||||
}
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
result* SpectrumMI40xxSeries::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);
|
||||
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<short int*>::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()<effective_settings->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<short int*>::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
|
||||
}
|
||||
|
221
drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h
Normal file
221
drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h
Normal file
@ -0,0 +1,221 @@
|
||||
#ifndef SPECTRUM_MI40XXSERIES
|
||||
#define SPECTRUM_MI40XXSERIES
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "core/states.h"
|
||||
#include "core/constants.h"
|
||||
#include "drivers/ADC.h"
|
||||
#include "GatedData.h"
|
||||
|
||||
#if defined __linux__
|
||||
// ----- linux includes -----
|
||||
# include <cstdio>
|
||||
# include <fcntl.h>
|
||||
# include <sys/ioctl.h>
|
||||
# include <unistd.h>
|
||||
# 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<short int*> 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
|
BIN
drivers/Spectrum-MI40xxSeries/drv_header_v323b1742.zip
Executable file
BIN
drivers/Spectrum-MI40xxSeries/drv_header_v323b1742.zip
Executable file
Binary file not shown.
BIN
drivers/Spectrum-MI40xxSeries/drv_header_v402b6844.zip
Normal file
BIN
drivers/Spectrum-MI40xxSeries/drv_header_v402b6844.zip
Normal file
Binary file not shown.
26
drivers/Spectrum-MI40xxSeries/ftbfs_patch.diff
Normal file
26
drivers/Spectrum-MI40xxSeries/ftbfs_patch.diff
Normal file
@ -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 <stdint.h>
|
||||
+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*
|
135
drivers/Spectrum-MI40xxSeries/hw_test_extclock.cpp
Normal file
135
drivers/Spectrum-MI40xxSeries/hw_test_extclock.cpp
Normal file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* simple test program simplified from
|
||||
* mi40xx.cpp example program (c) Spectrum GmbH
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#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<nChannels; j++)
|
||||
SpcSetParam (hDrv, SPC_AMP0 + 100*j, 5000); // 5 V sensitivity
|
||||
for (j=0; j<nChannels; j++)
|
||||
SpcSetParam (hDrv, SPC_50OHM0 + 100*j, 1); // 50 Ohm input impedance
|
||||
|
||||
SpcSetParam (hDrv, SPC_CHENABLE, lChEnable); // Enable channels for recording
|
||||
SpcSetParam (hDrv, SPC_POSTTRIGGER, lMemsize/2); // Trigger event in middle of data
|
||||
SpcSetParam (hDrv, SPC_MEMSIZE, lMemsize); // Memory size
|
||||
|
||||
SpcSetParam (hDrv, SPC_PLL_ENABLE, 1); // Internal PLL enabled for clock
|
||||
SpcSetParam (hDrv, SPC_EXTERNALCLOCK, 0); // Internal clock used
|
||||
SpcSetParam (hDrv, SPC_CLOCKOUT, 0); // no clock out
|
||||
SpcSetParam (hDrv, SPC_REFERENCECLOCK, 50000000); // External reference clock used
|
||||
SpcSetParam (hDrv, SPC_CLOCK50OHM, 0); // clock impedance NOT 50 Ohm
|
||||
printf ("Using external internal clock\n");
|
||||
SpcSetParam (hDrv, SPC_SAMPLERATE, 10000000); // Samplerate: 10 MHz
|
||||
SpcSetParam (hDrv, SPC_EXTERNOUT, 0); // No clock output
|
||||
|
||||
SpcSetParam (hDrv, SPC_TRIGGERMODE, TM_SOFTWARE); // Software trigger is used
|
||||
SpcSetParam (hDrv, SPC_PULSEWIDTH, 0); // Not used for software mode
|
||||
SpcSetParam (hDrv, SPC_TRIGGEROUT, 0); // No trigger output
|
||||
|
||||
|
||||
|
||||
// ----- start the board -----
|
||||
printf("Starting recording\n");
|
||||
nErr = SpcSetParam (hDrv, SPC_COMMAND, SPC_START); // start the board
|
||||
|
||||
|
||||
|
||||
// ----- driver error: request error and end program -----
|
||||
if (nErr != ERR_OK)
|
||||
{
|
||||
SpcGetParam (hDrv, SPC_LASTERRORCODE, &lErrorCode);
|
||||
SpcGetParam (hDrv, SPC_LASTERRORREG, &lErrorReg);
|
||||
SpcGetParam (hDrv, SPC_LASTERRORVALUE, &lErrorValue);
|
||||
|
||||
printf ("Driver error: %i in register %i at value %i\n", lErrorCode, lErrorReg, lErrorValue);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ----- Wait for Status Ready -----
|
||||
printf("Wating\n");
|
||||
do
|
||||
{
|
||||
SpcGetParam (hDrv, SPC_STATUS, &lStatus);
|
||||
}
|
||||
while (lStatus != SPC_READY);
|
||||
|
||||
printf ("Board is ready, recording has finished\n");
|
||||
|
||||
|
||||
|
||||
// ----- allocate memory for data -----
|
||||
for (i=0; i<nChannels; i++)
|
||||
pnData[i] = (ptr16) malloc (lMemsize * sizeof(int16));
|
||||
|
||||
// ----- data is muxed and must be splitted -----
|
||||
pnTmp = (ptr16) malloc (lMemsize * nChannels * sizeof(int16));
|
||||
SpcGetData (hDrv, 0, 0, nChannels * lMemsize, 2, (dataptr) pnTmp);
|
||||
for (i=0; i<lMemsize; i++)
|
||||
for (k=0; k<nChannels; k++)
|
||||
pnData[k][i] = pnTmp[nChannels * i + k];
|
||||
free (pnTmp);
|
||||
|
||||
|
||||
|
||||
// ----- output samples -----
|
||||
printf ("# Data (Integer):\n# Ch0 Ch1\n");
|
||||
for (i=0; i<lMemsize; i++)
|
||||
{
|
||||
for (j=0; j<nChannels; j++)
|
||||
printf ("% 5i ", pnData[j][i]);
|
||||
printf ("\n");
|
||||
}
|
||||
|
||||
// ----- free data memory -----
|
||||
for (i=0; i<nChannels; i++)
|
||||
free (pnData[i]);
|
||||
|
||||
close (hDrv);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
135
drivers/Spectrum-MI40xxSeries/hw_test_intclock.cpp
Normal file
135
drivers/Spectrum-MI40xxSeries/hw_test_intclock.cpp
Normal file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* simple test program simplified from
|
||||
* mi40xx.cpp example program (c) Spectrum GmbH
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#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<nChannels; j++)
|
||||
SpcSetParam (hDrv, SPC_AMP0 + 100*j, 5000); // 5 V sensitivity
|
||||
for (j=0; j<nChannels; j++)
|
||||
SpcSetParam (hDrv, SPC_50OHM0 + 100*j, 1); // 50 Ohm input impedance
|
||||
|
||||
SpcSetParam (hDrv, SPC_CHENABLE, lChEnable); // Enable channels for recording
|
||||
SpcSetParam (hDrv, SPC_POSTTRIGGER, lMemsize/2); // Trigger event in middle of data
|
||||
SpcSetParam (hDrv, SPC_MEMSIZE, lMemsize); // Memory size
|
||||
|
||||
SpcSetParam (hDrv, SPC_PLL_ENABLE, 1); // Internal PLL enabled for clock
|
||||
SpcSetParam (hDrv, SPC_EXTERNALCLOCK, 0); // Internal clock used
|
||||
SpcSetParam (hDrv, SPC_CLOCKOUT, 0); // no clock out
|
||||
SpcSetParam (hDrv, SPC_REFERENCECLOCK, 0); // Internal clock used
|
||||
SpcSetParam (hDrv, SPC_CLOCK50OHM, 0); // clock 50 Ohm
|
||||
printf ("Using external internal clock\n");
|
||||
SpcSetParam (hDrv, SPC_SAMPLERATE, 10000000); // Samplerate: 10 MHz
|
||||
SpcSetParam (hDrv, SPC_EXTERNOUT, 0); // No clock output
|
||||
|
||||
SpcSetParam (hDrv, SPC_TRIGGERMODE, TM_SOFTWARE); // Software trigger is used
|
||||
SpcSetParam (hDrv, SPC_PULSEWIDTH, 0); // Not used for software mode
|
||||
SpcSetParam (hDrv, SPC_TRIGGEROUT, 0); // No trigger output
|
||||
|
||||
|
||||
|
||||
// ----- start the board -----
|
||||
printf("Starting recording\n");
|
||||
nErr = SpcSetParam (hDrv, SPC_COMMAND, SPC_START); // start the board
|
||||
|
||||
|
||||
|
||||
// ----- driver error: request error and end program -----
|
||||
if (nErr != ERR_OK)
|
||||
{
|
||||
SpcGetParam (hDrv, SPC_LASTERRORCODE, &lErrorCode);
|
||||
SpcGetParam (hDrv, SPC_LASTERRORREG, &lErrorReg);
|
||||
SpcGetParam (hDrv, SPC_LASTERRORVALUE, &lErrorValue);
|
||||
|
||||
printf ("Driver error: %i in register %i at value %i\n", lErrorCode, lErrorReg, lErrorValue);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ----- Wait for Status Ready -----
|
||||
printf("Wating\n");
|
||||
do
|
||||
{
|
||||
SpcGetParam (hDrv, SPC_STATUS, &lStatus);
|
||||
}
|
||||
while (lStatus != SPC_READY);
|
||||
|
||||
printf ("Board is ready, recording has finished\n");
|
||||
|
||||
|
||||
|
||||
// ----- allocate memory for data -----
|
||||
for (i=0; i<nChannels; i++)
|
||||
pnData[i] = (ptr16) malloc (lMemsize * sizeof(int16));
|
||||
|
||||
// ----- data is muxed and must be splitted -----
|
||||
pnTmp = (ptr16) malloc (lMemsize * nChannels * sizeof(int16));
|
||||
SpcGetData (hDrv, 0, 0, nChannels * lMemsize, 2, (dataptr) pnTmp);
|
||||
for (i=0; i<lMemsize; i++)
|
||||
for (k=0; k<nChannels; k++)
|
||||
pnData[k][i] = pnTmp[nChannels * i + k];
|
||||
free (pnTmp);
|
||||
|
||||
|
||||
|
||||
// ----- output samples -----
|
||||
printf ("# Data (Integer):\n# Ch0 Ch1\n");
|
||||
for (i=0; i<lMemsize; i++)
|
||||
{
|
||||
for (j=0; j<nChannels; j++)
|
||||
printf ("% 5i ", pnData[j][i]);
|
||||
printf ("\n");
|
||||
}
|
||||
|
||||
// ----- free data memory -----
|
||||
for (i=0; i<nChannels; i++)
|
||||
free (pnData[i]);
|
||||
|
||||
close (hDrv);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
6
drivers/Spectrum-MI40xxSeries/test.cpp
Normal file
6
drivers/Spectrum-MI40xxSeries/test.cpp
Normal file
@ -0,0 +1,6 @@
|
||||
#include "Spectrum-MI40xxSeries.h"
|
||||
|
||||
int main() {
|
||||
SpectrumMI40xxSeries s;
|
||||
|
||||
}
|
45
drivers/SpinCore-PulseBlaster/Makefile
Normal file
45
drivers/SpinCore-PulseBlaster/Makefile
Normal file
@ -0,0 +1,45 @@
|
||||
# author: Achim Gaedke
|
||||
# created: February 2005
|
||||
|
||||
ifdef KERNELRELEASE
|
||||
|
||||
obj-m += pulseblaster.o
|
||||
|
||||
else
|
||||
|
||||
ifeq ($(shell uname -o), GNU/Linux)
|
||||
KERNELSRC=/lib/modules/$(shell uname -r)/build
|
||||
endif
|
||||
|
||||
THISDIR=$(shell cd . >/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
|
322
drivers/SpinCore-PulseBlaster/PulseBlasterProgram.cpp
Normal file
322
drivers/SpinCore-PulseBlaster/PulseBlasterProgram.cpp
Normal file
@ -0,0 +1,322 @@
|
||||
#include "SpinCore-PulseBlaster.h"
|
||||
#include "PulseBlasterProgram.h"
|
||||
#include "core/xml_states.h"
|
||||
#include <typeinfo>
|
||||
|
||||
/* ***************************************************************************************
|
||||
|
||||
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<PulseBlasterCommand*>() {
|
||||
// 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<stackentry> 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<state_sequent*>(this_tag);
|
||||
// throw away all elements, which were non-states
|
||||
for(state_sequent::iterator i=sequence->begin(); sequence->end()!=i;) {
|
||||
if (dynamic_cast<state*>(*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<state*>(*i);
|
||||
if (NULL!=first_state) break;
|
||||
++i;
|
||||
}
|
||||
if (first_state!=NULL) {
|
||||
state_sequent* first_sequence=dynamic_cast<state_sequent*>(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<state*>(*i);
|
||||
if (last_state!=NULL) break;
|
||||
}
|
||||
if (last_state!=NULL) {
|
||||
state_sequent* last_sequence=dynamic_cast<state_sequent*>(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<state*>(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_freq<minimum_interval) {
|
||||
// now, the remaining state might be too short
|
||||
remaining_time+=loop_state_length;
|
||||
--number_loops;
|
||||
}
|
||||
state_sequent* new_loop=new state_sequent;
|
||||
new_loop->repeat=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);
|
||||
}
|
88
drivers/SpinCore-PulseBlaster/PulseBlasterProgram.h
Normal file
88
drivers/SpinCore-PulseBlaster/PulseBlasterProgram.h
Normal file
@ -0,0 +1,88 @@
|
||||
#include <list>
|
||||
#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<PulseBlasterCommand*> {
|
||||
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() {}
|
||||
};
|
265
drivers/SpinCore-PulseBlaster/SpinCore-PulseBlaster.cpp
Normal file
265
drivers/SpinCore-PulseBlaster/SpinCore-PulseBlaster.cpp
Normal file
@ -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 <sys/types.h>
|
||||
# include <sys/stat.h>
|
||||
# include <unistd.h>
|
||||
|
||||
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<state_sequent*>(&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
|
||||
}
|
273
drivers/SpinCore-PulseBlaster/SpinCore-PulseBlaster.h
Normal file
273
drivers/SpinCore-PulseBlaster/SpinCore-PulseBlaster.h
Normal file
@ -0,0 +1,273 @@
|
||||
#ifndef SPINCORE_PULSEBLASTER_H
|
||||
#define SPINCORE_PULSEBLASTER_H
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <cerrno>
|
||||
#include <unistd.h>
|
||||
#include <core/stopwatch.h>
|
||||
#include <drivers/pulsegen.h>
|
||||
|
||||
#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 <fcntl.h>
|
||||
# include <sys/types.h>
|
||||
# include <sys/stat.h>
|
||||
# include <sys/ioctl.h>
|
||||
# include <unistd.h>
|
||||
# include "pulseblaster.h"
|
||||
#endif
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
# include <windows.h>
|
||||
#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<size && result==0; ++i) {
|
||||
result=write_register(6,data[i]);
|
||||
}
|
||||
if (result!=0) throw SpinCorePulseBlaster_error("write_data: error while writing");
|
||||
return i;
|
||||
}
|
||||
|
||||
inline int read_register(int reg) {
|
||||
int result=sp_inp(reg);
|
||||
if (result<0) throw SpinCorePulseBlaster_error("read_register: error while reading");
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
inline int write_data(const std::string& data) {
|
||||
return write_data((const unsigned char*)data.c_str(),data.size());
|
||||
}
|
||||
|
||||
~SpinCorePulseBlasterLowlevel();
|
||||
|
||||
};
|
||||
|
||||
|
||||
class PulseBlasterProgram;
|
||||
|
||||
class SpinCorePulseBlaster: protected SpinCorePulseBlasterLowlevel, public pulsegen {
|
||||
|
||||
public:
|
||||
/**
|
||||
clock in Hz
|
||||
*/
|
||||
double clock;
|
||||
|
||||
/**
|
||||
shortest pulse in clock cycles
|
||||
*/
|
||||
unsigned int shortest_pulse;
|
||||
|
||||
/**
|
||||
maximum number of commands
|
||||
*/
|
||||
unsigned int max_commands;
|
||||
|
||||
/**
|
||||
command/flags wordlength
|
||||
*/
|
||||
unsigned int command_length;
|
||||
|
||||
/**
|
||||
is started with pulse program
|
||||
*/
|
||||
stopwatch time_running;
|
||||
|
||||
/**
|
||||
expected duration in seconds, nonzero if pulseprogram is running
|
||||
*/
|
||||
double duration;
|
||||
/**
|
||||
id of ttlout sections affecting this device
|
||||
*/
|
||||
int ttl_device_id;
|
||||
|
||||
/**
|
||||
bit mask to set while WAIT command for synchronization is executed
|
||||
|
||||
no synchronization when zero.
|
||||
*/
|
||||
unsigned int sync_mask;
|
||||
|
||||
public:
|
||||
|
||||
enum opcode {CONTINUE=0, STOP=1, LOOP=2, END_LOOP=3, JSR=4, RTS=5, BRANCH=6, LONG_DELAY=7, WAIT=8};
|
||||
|
||||
/**
|
||||
status codes retured by get_status function
|
||||
|
||||
they are combinable like bit masks
|
||||
*/
|
||||
enum statuscode { STOPPED=1<<0, /// Bit 0 - Stopped
|
||||
RESET=1<<1, /// Bit 1 - Reset
|
||||
RUNNING=1<<2, /// Bit 2 - Running
|
||||
WAITING=1<<3, /// Bit 3 - Waiting
|
||||
SCANNING=1<<4 /// Bit 4 - Scanning (RadioProcessor boards only)
|
||||
};
|
||||
|
||||
SpinCorePulseBlaster(int c_length, double the_clock) {
|
||||
clock=the_clock;
|
||||
shortest_pulse=9;
|
||||
command_length=c_length;
|
||||
max_commands=1<<15;
|
||||
}
|
||||
|
||||
void reset_flags(unsigned int flags=0);
|
||||
|
||||
inline void set_initialized() {
|
||||
write_register(7,0);
|
||||
}
|
||||
|
||||
/**
|
||||
starts execution of pulseprogram, needs an initialized pulseblaster
|
||||
*/
|
||||
inline void start() {
|
||||
write_register(1,0);
|
||||
}
|
||||
|
||||
/**
|
||||
stops execution of pulseprogram
|
||||
*/
|
||||
inline void stop() {
|
||||
write_register(0,0);
|
||||
}
|
||||
|
||||
/**
|
||||
returns status information of pulseblaster board
|
||||
\return status 1: stopped, 2: reset, 4: running, 8: waiting
|
||||
*/
|
||||
inline int get_status() {
|
||||
return read_register(0);
|
||||
}
|
||||
|
||||
void set_program(const std::string& data);
|
||||
|
||||
/**
|
||||
run a experiment given as state sequence
|
||||
*/
|
||||
virtual void run_pulse_program(state& exp) {
|
||||
run_pulse_program_w_sync(exp, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
run a experiment given as state sequence, but optionally synchronize sequence start with ADC sampling clock
|
||||
*/
|
||||
virtual void run_pulse_program_w_sync(state& exp, double sync_freq=0);
|
||||
|
||||
/**
|
||||
needed by run_pulse_program method
|
||||
*/
|
||||
virtual PulseBlasterProgram* create_program(state& exp)=0;
|
||||
|
||||
/**
|
||||
needed by run_pulse_program method
|
||||
*/
|
||||
virtual void run_pulse_program(const PulseBlasterProgram& p)=0;
|
||||
|
||||
/**
|
||||
wait till end of pulseprogram
|
||||
*/
|
||||
virtual void wait_till_end();
|
||||
|
||||
};
|
||||
|
||||
#endif /* SPINCORE_PULSEBLASTER_H */
|
185
drivers/SpinCore-PulseBlaster/TestCase.cpp
Executable file
185
drivers/SpinCore-PulseBlaster/TestCase.cpp
Executable file
@ -0,0 +1,185 @@
|
||||
#include <SpinCore-PulseBlaster.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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);
|
||||
}
|
945
drivers/SpinCore-PulseBlaster/pulseblaster.c
Normal file
945
drivers/SpinCore-PulseBlaster/pulseblaster.c
Normal file
@ -0,0 +1,945 @@
|
||||
/*
|
||||
* pulseblaster.c - Communication with pulseblaster
|
||||
* author: Achim Gaedke <achim.gaedke@physik.tu-darmstadt.de>
|
||||
* 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 <linux/kernel.h> /* We're doing kernel work */
|
||||
#include <linux/module.h> /* Specifically, a module */
|
||||
#include <linux/fs.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/version.h>
|
||||
#include <asm/uaccess.h> /* for get_user and put_user */
|
||||
#include <asm/io.h>
|
||||
|
||||
#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<pb_dev_no; ++i) {
|
||||
if (pb_devs[i].pciboard==dev) {
|
||||
spin_lock(&(pb_devs[i].io_lock));
|
||||
pb_devs[i].pciboard=NULL;
|
||||
pb_devs[i].boardtype = PBB_DEBUG;
|
||||
pb_devs[i].base_addr = 0x0;
|
||||
spin_unlock(&(pb_devs[i].io_lock));
|
||||
printk("%s: releasing device no %d\n", DEVICE_NAME, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock(&pb_devs_lock);
|
||||
/* do the pci stuff only */
|
||||
pci_disable_device(dev);
|
||||
#ifndef CONFIG_XEN
|
||||
// why is pci_release_region not defined in a XEN kernel?
|
||||
pci_release_region(dev, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct pci_device_id pulseblaster_pci_ids[] = {
|
||||
{PCI_DEVICE(0x10e8, 0x8852), .driver_data = PBB_GENERIC_AMCC},
|
||||
{PCI_DEVICE(0x10e8, 0x8878), .driver_data = PBB_GENERIC_PCI},
|
||||
{0,},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, pulseblaster_pci_ids);
|
||||
|
||||
static struct pci_driver pulseblaster_pci_driver = {
|
||||
.name = DEVICE_NAME,
|
||||
.id_table=pulseblaster_pci_ids,
|
||||
.probe=pulseblaster_pci_probe,
|
||||
.remove=pulseblaster_pci_remove,
|
||||
};
|
||||
|
||||
|
||||
#if LINUX_VERSION_CODE >= 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<pb_dev_no; ++i) {
|
||||
dev_t devno = MKDEV(major_dev_num, minor_dev_num+i);
|
||||
/* register char dev */
|
||||
cdev_init(&(pb_devs[i].cdev), &pulseblaster_fops);
|
||||
pb_devs[i].cdev.owner=THIS_MODULE;
|
||||
pb_devs[i].cdev.ops=&pulseblaster_fops;
|
||||
ret_val=cdev_add(&(pb_devs[i].cdev), devno, 1);
|
||||
if (ret_val<0) {
|
||||
printk("%s: failed to register char device", DEVICE_NAME);
|
||||
// todo cleanup and return;
|
||||
}
|
||||
|
||||
pb_class_device_create(devno, i);
|
||||
}
|
||||
spin_unlock(&pb_devs_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(init_pulseblaster_module);
|
||||
|
||||
/*
|
||||
* Cleanup - unregister the appropriate file from /proc
|
||||
*/
|
||||
static void __exit cleanup_pulseblaster_module(void)
|
||||
{
|
||||
/*
|
||||
* Unregister the device
|
||||
* ToDo: check use count?!
|
||||
*/
|
||||
int i;
|
||||
|
||||
int major_dev_num=MAJOR(pb_dev_no_start);
|
||||
int minor_dev_num=MINOR(pb_dev_no_start);
|
||||
|
||||
spin_lock(&pb_devs_lock);
|
||||
for (i=0; i<pb_dev_no; ++i)
|
||||
{
|
||||
pb_class_device_destroy(MKDEV(major_dev_num, minor_dev_num+i));
|
||||
}
|
||||
spin_unlock(&pb_devs_lock);
|
||||
pb_class_device_destroy(MKDEV(major_dev_num, minor_dev_num+pb_dev_no));
|
||||
pb_class_destroy();
|
||||
|
||||
spin_lock(&pb_devs_lock);
|
||||
for (i=0; i<pb_dev_no; ++i) {
|
||||
cdev_del(&(pb_devs[i].cdev));
|
||||
}
|
||||
spin_unlock(&pb_devs_lock);
|
||||
cdev_del(&(pb_dev_debug.cdev));
|
||||
unregister_chrdev_region(pb_dev_no_start, pb_dev_no+1);
|
||||
|
||||
pci_unregister_driver(&pulseblaster_pci_driver);
|
||||
|
||||
pb_dev_no=0;
|
||||
}
|
||||
|
||||
module_exit(cleanup_pulseblaster_module);
|
23
drivers/SpinCore-PulseBlaster/pulseblaster.h
Normal file
23
drivers/SpinCore-PulseBlaster/pulseblaster.h
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* pulseblaster.h - the header file with the ioctl definitions.
|
||||
*
|
||||
* The declarations here have to be in a header file, because
|
||||
* they need to be known both to the kernel module
|
||||
* and the process calling ioctl
|
||||
*/
|
||||
|
||||
#ifndef PULSEBLASTER_H
|
||||
#define PULSEBLASTER_H
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
#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
|
71
drivers/SpinCore-PulseBlaster/pulseblaster_test.cpp
Normal file
71
drivers/SpinCore-PulseBlaster/pulseblaster_test.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#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;
|
||||
}
|
23
drivers/SpinCore-PulseBlaster24Bit/Makefile
Normal file
23
drivers/SpinCore-PulseBlaster24Bit/Makefile
Normal file
@ -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
|
@ -0,0 +1,274 @@
|
||||
/* **************************************************************************
|
||||
|
||||
Author: Achim Gaedke
|
||||
Created: March 2005
|
||||
|
||||
****************************************************************************/
|
||||
#include "core/core.h"
|
||||
#include "SpinCore-PulseBlaster24Bit.h"
|
||||
#include <cmath>
|
||||
#include <typeinfo>
|
||||
#include <iterator>
|
||||
|
||||
#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,"%s<instruction ttls=\"0x%06x\" ",
|
||||
std::string(indent,' ').c_str(),ttls);
|
||||
switch(instruction) {
|
||||
case SpinCorePulseBlaster::LOOP:
|
||||
fprintf(out,"inst=\"LOOP\" loops=\"%d\"",loop_count);
|
||||
break;
|
||||
case SpinCorePulseBlaster::LONG_DELAY:
|
||||
fprintf(out,"inst=\"LONG_DELAY\" loops=\"%d\"",loop_count);
|
||||
break;
|
||||
case SpinCorePulseBlaster::BRANCH:
|
||||
fprintf(out,"inst=\"BRANCH\" address=\"%" SIZETPRINTFLETTER "\"",std::distance(program->begin(),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<const PulseBlaster24BitCommand*>(*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<const ttlout*>(*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<const PulseBlaster24BitCommand*>(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<PulseBlaster24BitProgram>\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</PulseBlaster24BitProgram>\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<const PulseBlaster24BitCommand*>(&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<const PulseBlaster24BitProgram*>(&p);
|
||||
if (prog==NULL)
|
||||
throw SpinCorePulseBlaster_error("found wrong program class in SpinCorePulseBlaster24Bit method");
|
||||
write_to_device(*prog);
|
||||
start();
|
||||
}
|
143
drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h
Normal file
143
drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h
Normal file
@ -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 <cstdio>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <cmath>
|
||||
#include <unistd.h>
|
||||
|
||||
/**
|
||||
\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
|
172
drivers/SpinCore-PulseBlaster24Bit/test.cpp
Normal file
172
drivers/SpinCore-PulseBlaster24Bit/test.cpp
Normal file
@ -0,0 +1,172 @@
|
||||
#include "SpinCore-PulseBlaster24Bit.h"
|
||||
#include "core/xml_states.h"
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
|
||||
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<state_sequent*>(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 (i<n && value_returned==0) {
|
||||
value_returned=state_sequence(filename);
|
||||
i++;
|
||||
}
|
||||
if (n>1) {
|
||||
fprintf(stdout,"%s: executed %s %u times\n",argv[0],filename,i);
|
||||
}
|
||||
return value_returned;
|
||||
}
|
35
drivers/SpinCore-PulseBlasterDDSIII/Makefile
Normal file
35
drivers/SpinCore-PulseBlasterDDSIII/Makefile
Normal file
@ -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)
|
@ -0,0 +1,510 @@
|
||||
/* **************************************************************************
|
||||
|
||||
Author: Achim Gaedke
|
||||
Created: June 2004
|
||||
|
||||
****************************************************************************/
|
||||
#include "SpinCore-PulseBlasterDDSIII.h"
|
||||
#include <cmath>
|
||||
#include <typeinfo>
|
||||
#include <iterator>
|
||||
#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,"%s<instruction frequency=\"%d\" rxphase=\"%d\" rxenable=\"%d\" txphase=\"%d\" txenable=\"%d\" ttls=\"%d\" ",
|
||||
std::string(indent,' ').c_str(),freq_reg,rx_phase_reg,rx_enable,tx_phase_reg,tx_enable,ttls);
|
||||
switch(instruction) {
|
||||
case SpinCorePulseBlaster::LOOP:
|
||||
fprintf(out,"inst=\"LOOP\" instdata=\"%d\"",loop_count);
|
||||
break;
|
||||
case SpinCorePulseBlaster::LONG_DELAY:
|
||||
fprintf(out,"inst=\"LONG_DELAY\" instdata=\"%d\"",loop_count);
|
||||
break;
|
||||
case SpinCorePulseBlaster::BRANCH:
|
||||
fprintf(out,"inst=\"BRANCH\" instdata=\"%" SIZETPRINTFLETTER "\"",std::distance(program->begin(),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<const PulseBlasterDDSIIICommand*>(*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<PulseBlasterDDSIIIProgram>\n",indent_string.c_str());
|
||||
|
||||
if (!frequency_registers.empty()) {
|
||||
fprintf(out,"%s <frequencies>",indent_string.c_str());
|
||||
for (size_t i=0; i<frequency_registers.size(); i++)
|
||||
fprintf(out," %g",frequency_registers[i]);
|
||||
fprintf(out," </frequencies>\n");
|
||||
}
|
||||
|
||||
if (!rx_phase_registers.empty()) {
|
||||
fprintf(out,"%s <rxphases>",indent_string.c_str());
|
||||
for (size_t i=0;i<rx_phase_registers.size();i++)
|
||||
fprintf(out," %g",rx_phase_registers[i]);
|
||||
fprintf(out," </rxphases>\n");
|
||||
}
|
||||
|
||||
if (!tx_phase_registers.empty()) {
|
||||
fprintf(out,"%s <txphases>",indent_string.c_str());
|
||||
for (size_t i=0;i<tx_phase_registers.size();i++)
|
||||
fprintf(out," %g",tx_phase_registers[i]);
|
||||
fprintf(out," </txphases>\n");
|
||||
}
|
||||
|
||||
for(const_iterator i=begin();i!=end();++i) {
|
||||
(**i).write_to_file(out,indent+2);
|
||||
}
|
||||
fprintf(out,"%s</PulseBlasterDDSIIIProgram>\n",indent_string.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int PulseBlasterDDSIIIProgram::get_frequency_regno(double f) {
|
||||
size_t i=0;
|
||||
while (i<frequency_registers.size() && fabs(frequency_registers[i]-f)>freq_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 (i<rx_phase_registers.size() && fabs(rx_phase_registers[i]-p)>phase_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 (i<tx_phase_registers.size() && fabs(tx_phase_registers[i]-p)>phase_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<const ttlout*>(*i);
|
||||
if (to!=NULL && to->id==0) {
|
||||
ttls|=to->ttls.to_ulong();
|
||||
continue;
|
||||
}
|
||||
// add frequency information
|
||||
const analogout* ao=dynamic_cast<const analogout*>(*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<const PulseBlasterDDSIIICommand*>(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<double>& 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; reg<register_size; ++reg) {
|
||||
if (values.size()>reg) {
|
||||
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<double> rx_phases, std::vector<double> tx_phases) {
|
||||
if (tx_phases.empty())
|
||||
set_registers(2,phase_regno,1.0,std::vector<double>(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<double>(1,0.0));
|
||||
else
|
||||
set_registers(3,phase_regno,pow(2,32)/360.0,rx_phases);
|
||||
}
|
||||
|
||||
void SpinCorePulseBlasterDDSIII::set_frequency_registers(const std::vector<double>& values) {
|
||||
if (values.empty())
|
||||
set_registers(1,freq_regno,pow(2,32)/clock,std::vector<double>(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<const PulseBlasterDDSIIICommand*>(&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<const PulseBlasterDDSIIIProgram*>(&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
|
||||
}
|
||||
|
||||
|
@ -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 <cstdio>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <cmath>
|
||||
#include <unistd.h>
|
||||
|
||||
/**
|
||||
\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<double>& values);
|
||||
|
||||
void set_phase_registers(std::vector<double> rx_phases, std::vector<double> tx_phases);
|
||||
|
||||
void set_frequency_registers(const std::vector<double>& 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<double> frequency_registers;
|
||||
/// here keep track of the programed RX phases (degree)
|
||||
std::vector<double> rx_phase_registers;
|
||||
/// here keep track of the programed TX phases (degree)
|
||||
std::vector<double> 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
|
162
drivers/SpinCore-PulseBlasterDDSIII/test.cpp
Normal file
162
drivers/SpinCore-PulseBlasterDDSIII/test.cpp
Normal file
@ -0,0 +1,162 @@
|
||||
/* **************************************************************************
|
||||
|
||||
Author: Achim Gaedke
|
||||
Created: June 2004
|
||||
|
||||
****************************************************************************/
|
||||
#include "SpinCore-PulseBlasterDDSIII.h"
|
||||
#include "core/xml_states.h"
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cmath>
|
||||
/*
|
||||
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*>(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
|
BIN
drivers/Tecmag-DAC20/AD1862.pdf
Normal file
BIN
drivers/Tecmag-DAC20/AD1862.pdf
Normal file
Binary file not shown.
230
drivers/Tecmag-DAC20/DAC20.cpp
Normal file
230
drivers/Tecmag-DAC20/DAC20.cpp
Normal file
@ -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 <cstdio>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
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<state_sequent*>(&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<state_sequent*>(*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<state*>(*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<analogout*>(*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="<<PFG_aout->dac_value<<std::endl;
|
||||
// check the length of the state
|
||||
if (this_state->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<int> 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 (i<DAC_BIT_DEPTH) {
|
||||
last_seen_bit=dac_word[i];
|
||||
last_seen_bit_count=1;
|
||||
}
|
||||
} // finished writing
|
||||
else
|
||||
last_seen_bit_count+=1; // same bit value, so continue
|
||||
} // end of bit loop
|
||||
register_ttls->ttls = 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
|
||||
}
|
47
drivers/Tecmag-DAC20/DAC20.h
Normal file
47
drivers/Tecmag-DAC20/DAC20.h
Normal file
@ -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
|
34
drivers/Tecmag-DAC20/DAC_test.cpp
Executable file
34
drivers/Tecmag-DAC20/DAC_test.cpp
Executable file
@ -0,0 +1,34 @@
|
||||
#include <cstdio>
|
||||
#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<state*>(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;
|
||||
}
|
||||
}
|
18
drivers/Tecmag-DAC20/DAC_test.xml
Executable file
18
drivers/Tecmag-DAC20/DAC_test.xml
Executable file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0"?>
|
||||
<sequent>
|
||||
<state t="1">
|
||||
<ttlout value="1"/>
|
||||
</state>
|
||||
<state t="1">
|
||||
<analogout id="0" dac_value="10" a="5"/>
|
||||
<ttlout id="0" value="2"/>
|
||||
</state>
|
||||
<state t="1">
|
||||
<analogout id="0" dac_value="20" a="5"/>
|
||||
<ttlout id="0" value="3"/>
|
||||
</state>
|
||||
<state t="1">
|
||||
<analogout id="0" dac_value="-20" a="5"/>
|
||||
<ttlout id="0" value="4"/>
|
||||
</state>
|
||||
</sequent>
|
BIN
drivers/Tecmag-DAC20/Dac20.pdf
Normal file
BIN
drivers/Tecmag-DAC20/Dac20.pdf
Normal file
Binary file not shown.
27
drivers/Tecmag-DAC20/Makefile
Normal file
27
drivers/Tecmag-DAC20/Makefile
Normal file
@ -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)
|
29
drivers/Tecmag-DAC20/xmltest.xml
Normal file
29
drivers/Tecmag-DAC20/xmltest.xml
Normal file
@ -0,0 +1,29 @@
|
||||
<?xml version="1.0"?>
|
||||
<sequent>
|
||||
<state time="9.9999999999999995e-07"><ttlout value="61440"/></state>
|
||||
<state time="9.9999999999999995e-07"><ttlout value="32768"/></state>
|
||||
<state time="0.0001"/>
|
||||
<state time="2e-6"><analogout id="1" f="75000000.000000" phase="0.000000"/></state>
|
||||
<state time="0.11999599999999999"/>
|
||||
<state time="1.9999999999999999e-06"><ttlout value="1"/></state>
|
||||
<state time="5.75e-07"><ttlout value="3"/></state>
|
||||
<state time="2e-6"><analogout id="0" f="0.000000" phase="0.000000" dac_value="10002"/></state>
|
||||
<state time="2e-6"><analogout id="1" f="0.000000" phase="0.000000"/></state>
|
||||
<state time="0.00031804892620614084"/>
|
||||
<state time="3.7e-5"><analogout id="1" a="5" dac_value="200"/></state>
|
||||
<state time="3.7e-5"><analogout id="0" a="5" dac_value="10002"/></state>
|
||||
<state time="3.7e-5"><analogout id="1" a="5" dac_value="1"/></state>
|
||||
<state time="3.7e-5"><analogout id="0" a="5" dac_value="5"/></state>
|
||||
<state time="1.9999999999999999e-06"><ttlout value="1"/></state>
|
||||
<state time="5.75e-07"><ttlout value="3"/></state>
|
||||
<state time="2e-6"><analogout id="1" f="0.000000" phase="-123.000000"/></state>
|
||||
<state time="0.00031033642620614083"/>
|
||||
<state time="0.00025999999999999998"><analogin s="4096" f="2e+07" sensitivity="1.000000"/></state>
|
||||
<state time="2e-6"><analogout id="1" f="0.000000" phase="0.000000"/></state>
|
||||
<state time="0.0094230885737938593"/>
|
||||
<state time="1.9999999999999999e-06"><ttlout value="1"/></state>
|
||||
<state time="5.75e-07"><ttlout value="3"/></state>
|
||||
<state time="2e-6"><analogout id="1" f="0.000000" phase="-123.000000"/></state>
|
||||
<state time="0.00031062392620614081"/>
|
||||
<state time="0.00020684799999999998"><analogin s="4096" f="2e+07" sensitivity="1.000000"/></state>
|
||||
</sequent>
|
58
drivers/TiePie-HS3/HS3test.cpp
Normal file
58
drivers/TiePie-HS3/HS3test.cpp
Normal file
@ -0,0 +1,58 @@
|
||||
#include <stdio.h>
|
||||
#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();
|
||||
}
|
39
drivers/TiePie-HS3/Makefile
Normal file
39
drivers/TiePie-HS3/Makefile
Normal file
@ -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)
|
217
drivers/TiePie-HS3/TiePie-HS3.cpp
Normal file
217
drivers/TiePie-HS3/TiePie-HS3.cpp
Normal file
@ -0,0 +1,217 @@
|
||||
/* **************************************************************************
|
||||
|
||||
Author: Achim Gaedke
|
||||
Created: June 2004
|
||||
|
||||
****************************************************************************/
|
||||
#include "drivers/TiePie-HS3/TiePie-HS3.h"
|
||||
#include "tiepie.h"
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#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<state_sequent&>(exp));
|
||||
state* a_state=(state*)si.get_state();
|
||||
while (NULL!=a_state) {
|
||||
// find a analogin section
|
||||
std::list<analogin*> inputs;
|
||||
for(state::iterator i=a_state->begin(); i!=a_state->end(); ++i) {
|
||||
analogin* input=dynamic_cast<analogin*>(*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<samples;i++) {
|
||||
int result=data1[i];
|
||||
// there is one more positive value than in two's complement, we have to map this to the hightest positive value
|
||||
if (result==65535) result-=1;
|
||||
result-=32767;
|
||||
adc_data[i*2]=result;
|
||||
result=data2[i];
|
||||
if (result==65535) result-=1;
|
||||
result-=32767;
|
||||
adc_data[i*2+1]=result;
|
||||
}
|
||||
|
||||
free(data1);
|
||||
free(data2);
|
||||
return new adc_result(1,samples,adc_data,rate);
|
||||
}
|
||||
|
||||
TiePieHS3::~TiePieHS3() {
|
||||
ExitInstrument();
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user