home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.hitl.washington.edu
/
ftp.hitl.washington.edu.tar
/
ftp.hitl.washington.edu
/
pub
/
people
/
peter
/
ER
/
ECG_driver.cxx
< prev
next >
Wrap
C/C++ Source or Header
|
1998-07-07
|
14KB
|
563 lines
/*
ECG_driver.cc
*/
#include "ECG_driver.h"
#include <iostream.h>
#include <vector.h>
#include <map.h>
#include <math.h>
#include "v3dSystem.h"
#include "v3dUniverse.h"
//#define TIMESTAMP // Time-stamp data changes.
#ifdef TIMESTAMP
#include "timestamp.h"
#endif
///////////////////////////
// ECGDriver //
/////////////////////////
//const double ECGDriver::ms_Interval = 25.0; // 25 ms per interval
const double ECGDriver::ms_Interval = 40.0; // 40 ms per interval
template<class T>
vector<T> to_vec (int num_values, T *data_ary)
{
vector<T> new_vec;
for (int i = 0; i < num_values; ++i)
new_vec.push_back (data_ary[i]);
return new_vec;
}
#include <stdio.h>
bool
ECGDriver::read_data (char *filename)
{
FILE *rhythm_fp = fopen(filename, "r");
if (rhythm_fp == NULL) {
cerr << "ECGDriver::read_data: Error opening file: " <<
filename << endl;
return false;
}
ECG_DATA_TYPE alt;
int p, qrs, t;
vector<ECG_DATA_TYPE> alt_vec;
vector<char> p_vec, qrs_vec, t_vec;
while (!feof(rhythm_fp)) {
fscanf (rhythm_fp, "%f %d %d %d", &alt, &p, &qrs, &t);
if (feof(rhythm_fp))
break;
alt_vec.push_back((ECG_DATA_TYPE)alt);
p_vec.push_back((char)p);
qrs_vec.push_back((char)qrs);
t_vec.push_back((char)t);
}
test_values_.push_back(alt_vec);
P_values_.push_back(p_vec);
QRS_values_.push_back(qrs_vec);
T_values_.push_back(t_vec);
fclose (rhythm_fp);
return true;
}
ECGDriver::ECGDriver() : v3dEventSender()
{
read_data ("normal.data");
read_data ("VT.data");
read_data ("normal2.data");
read_data ("heart_block.data");
read_data ("PVC.data");
read_data ("flatline.data");
data_list = 0;
P_list = 0;
QRS_list = 0;
T_list = 0;
// Register the driver with the current universe.
// Later, the universe should be passed as a parameter.
v3dSystem::GetCurrentUniverse()->RegisterEventSender(this);
// Register an event to tell receivers that new data has arrived.
RegisterEvent(new ECGDataUpdateEvent);
// Register an event to tell receivers that the data stream has been
// switched.
data_switch_event_ = new ECGDataSwitchEvent;
data_switch_event_->SetActivityLevel(Inactive);
// The data switch event must be manually fired by the driver.
RegisterEvent(data_switch_event_);
Unpause();
using_sequence_ = false;
using_fixed_range_ = false;
switch_data (0);
}
ECGDriver::ECGDriver(char *sequence_filename, ECG_DATA_TYPE min_val,
ECG_DATA_TYPE max_val) : v3dEventSender()
{
// Open the file containing the sequence of ECG rhythms to play.
FILE *fp = fopen(sequence_filename, "r");
if (fp == NULL) {
cerr << "ECGDriver::ECGDriver: Error opening sequence file: " <<
sequence_filename << endl;
return;
}
// Read number of rhythms in this sequence.
int num_rhythms;
map <char, int, less<char> > code_map;
fscanf (fp, "%d", &num_rhythms);
cerr << "Number of rhythms: " << num_rhythms << endl;
for (int i = 0; i < num_rhythms; ++i) {
char rhythm_code[20], rhythm_filename[255];
fscanf (fp, "%s %s", &rhythm_code, &rhythm_filename);
if (feof (fp))
break;
cerr << "Reading file " << rhythm_filename << ", code " << rhythm_code[0] << endl;
code_map[rhythm_code[0]] = i;
read_data (rhythm_filename);
}
while (!feof(fp)) {
char rhythm_code[20];
int cycle_count;
fscanf (fp, "%s %d", &rhythm_code, &cycle_count);
if (feof(fp))
break;
//cerr << "Rhythm code: " << rhythm_code[0] << " Count: " << cycle_count << endl;
RhythmSequenceNode sequence_node;
map <char, int, less<char> >::iterator finder
= code_map.find (rhythm_code[0]);
if (finder == code_map.end()) {
cerr << "ECGDriver: Error: code " << rhythm_code[0] <<
" is undefined in file " << sequence_filename <<
". Ignoring..." << endl;
continue;
}
sequence_node.rhythm_no = (*finder).second;
sequence_node.cycle_count = cycle_count;
master_sequence_.push_back(sequence_node);
}
fclose (fp);
rhythm_sequence_ = master_sequence_;
data_list = 0;
P_list = 0;
QRS_list = 0;
T_list = 0;
// Register the driver with the current universe.
// Later, the universe should be passed as a parameter.
v3dSystem::GetCurrentUniverse()->RegisterEventSender(this);
// Register an event to tell receivers that new data has arrived.
RegisterEvent(new ECGDataUpdateEvent);
// Register an event to tell receivers that the data stream has been
// switched.
data_switch_event_ = new ECGDataSwitchEvent;
data_switch_event_->SetActivityLevel(Inactive);
// The data switch event must be manually fired by the driver.
RegisterEvent(data_switch_event_);
// Register an event to tell receivers that the rhythm sequence has
// ended.
sequence_end_event_ = new ECGSequenceEndEvent;
sequence_end_event_->SetActivityLevel(Inactive);
// The sequence end event must be manually fired by the driver.
RegisterEvent(sequence_end_event_);
using_sequence_ = true;
if (max_val == min_val)
using_fixed_range_ = false;
else {
data_range_.maximum_value = max_val;
data_range_.minimum_value = min_val;
using_fixed_range_ = true;
}
switch_data (0);
Unpause();
}
ECGDriver::~ECGDriver()
{
delete [] data_list;
delete [] P_list;
delete [] QRS_list;
delete [] T_list;
}
void
ECGDriver::TestEvents()
{
// Update the ECG driver index.
if (!IsPaused()) {
if (using_sequence_)
update_current_sequence_index();
else
update_current_index();
v3dEventSender::TestEvents();
} else {
// Next, tell all event senders registered with this object to
// test their events.
EventSenderVector::iterator event_sender_iter
= event_senders_.begin();
for (; event_sender_iter != event_senders_.end();
++event_sender_iter)
(*event_sender_iter)->TestEvents ();
}
}
// ECGDriver::Reset
// Reset the sequence data stream.
void
ECGDriver::Reset()
{
rhythm_sequence_ = master_sequence_;
if (rhythm_sequence_.size())
using_sequence_ = true;
switch_data(0);
}
ECG_DATA_RANGE
ECGDriver::switch_data (int set_no)
{
int set_size, num_cycles;
if (rhythm_sequence_.size()) {
current_set_ = (*rhythm_sequence_.begin()).rhythm_no;
cerr << "Next set in sequence: " << current_set_;
// Choose set 0 if the specified set is out of range.
if (current_set_ >= test_values_.size()) {
cerr << " Set out of range! Using set 0.";
current_set_ = 0;
}
set_size = test_values_[current_set_].size();
cerr << " size: " << set_size;
num_cycles = (*rhythm_sequence_.begin()).cycle_count;
cerr << " number of cycles: " << num_cycles;
rhythm_sequence_.pop_front();
cerr << endl;
} else {
// A negative set number parameter increments the current set
// number.
if (set_no < 0)
++current_set_;
else
current_set_ = set_no;
// Choose set 0 if the specified set is out of range.
if (current_set_ >= test_values_.size())
current_set_ = 0;
set_size = test_values_[current_set_].size();
num_cycles = 100 / set_size + 1;
}
#ifdef TIMESTAMP
// For time-stamping purposes.
char tmp_buff[255];
sprintf (tmp_buff, "ECG data switch: rhythm %d", current_set_);
TrackedEvent *ECG_data_switch_event
= new TrackedEvent(TrackedEvent::SystemEvent, 10 + current_set_, tmp_buff, false);
TimestampedEvent::AddEvent(ECG_data_switch_event);
#endif // TIMESTAMP
int p_set_size = P_values_[current_set_].size();
max_index_ = set_size * num_cycles;
cerr << "Set size: " << set_size << " Number of cycles: " << num_cycles <<
" Max index: " << max_index_ << endl;
// Allocate a new array to hold the data values, and copy them from
// the vector of values.
delete [] data_list;
delete [] P_list;
delete [] QRS_list;
delete [] T_list;
data_list = new ECG_DATA_TYPE [max_index_];
P_list = new char [max_index_];
QRS_list = new char [max_index_];
T_list = new char [max_index_];
for (int i = 0; i < max_index_; ++i) {
data_list[i] = test_values_[current_set_][i % set_size];
P_list[i] = P_values_[current_set_][i % p_set_size];
QRS_list[i] = QRS_values_[current_set_][i % set_size];
T_list[i] = T_values_[current_set_][i % p_set_size];
}
// Set the index to the start of the data.
reset_current_index();
// Get the ranges of the data.
if (!using_fixed_range_) {
data_range_.maximum_value = data_list[0];
data_range_.minimum_value = data_list[0];
for (int i = 1; i < set_size; ++i) {
if (data_list[i] > data_range_.maximum_value)
data_range_.maximum_value = data_list[i];
if (data_list[i] < data_range_.minimum_value)
data_range_.minimum_value = data_list[i];
}
if (fabs(data_range_.maximum_value) > fabs(data_range_.minimum_value))
data_range_.minimum_value = - fabs(data_range_.maximum_value);
else
data_range_.maximum_value = fabs(data_range_.minimum_value);
} // Not using fixed range end.
// Tell all interested clients that the data stream has been switched.
data_switch_event_->InvokeHandlers(0);
return data_range_;
}
void
ECGDriver::ECG_update_callback(const v3dEventMessage *message)
{
((ECGDriver *)message->GetEventReceiver())->update_current_index();
}
// usec_diff
// Calculate the difference in microseconds between the two times
// passed in.
inline float
usec_diff (const timeval& current_time, const timeval& past_time)
{
return (current_time.tv_sec - past_time.tv_sec) * 1000000 +
current_time.tv_usec - past_time.tv_usec;
}
// ECGDriver::update_current_index
// Changes the current index to the ECG data depending on how much
// time has elapsed since the last update.
// Each value in the array represents a piece of ECG data read at
// 40ms intervals.
int
ECGDriver::update_current_index ()
{
timeval current_time;
//cerr << "clock / sec: " << CLOCKS_PER_SEC;
gettimeofday (¤t_time);
//cerr << " elapsed clock: " << current_time.tv_usec - last_time_.tv_usec;
//cerr << " elapsed int: " << ((current_time.tv_sec - last_time_.tv_sec) *
// 1000000 + current_time.tv_usec - last_time_.tv_usec)
// / (1000 * ms_Interval);
//cerr << endl;
// true_index_ += ((current_time.tv_sec - last_time_.tv_sec) * 1000000 +
// current_time.tv_usec - last_time_.tv_usec) /
// (1000 * ms_Interval);
true_index_ += usec_diff (current_time, last_time_) /
(1000 * ms_Interval);
while (true_index_ >= max_index_)
true_index_ -= max_index_;
current_index_ = (int) ffloor (true_index_);
last_time_ = current_time;
//cerr << " true index: " << true_index_;
//cerr << " current index: " << current_index_ << endl;
return current_index_;
}
int
ECGDriver::reset_current_index ()
{
// last_time_ = clock();
gettimeofday(&last_time_);
true_index_ = 0;
return current_index_ = 0;
}
void
ECGDriver::ECG_sequence_update_callback(const v3dEventMessage *message)
{
((ECGDriver *)message->GetEventReceiver())->update_current_sequence_index();
}
// ECGDriver::update_current_sequence_index
// Changes the current index to the ECG data depending on how much
// time has elapsed since the last update.
// Each value in the array represents a piece of ECG data read at
// 40ms intervals.
int
ECGDriver::update_current_sequence_index ()
{
#ifdef TIMESTAMP
static TrackedEvent sequence_end_event(TrackedEvent::SystemEvent, 99,
"Rhythm sequence finished");
#endif // TIMESTAMP
timeval current_time;
gettimeofday (¤t_time);
true_index_ += usec_diff (current_time, last_time_) /
(1000 * ms_Interval);
if (true_index_ >= max_index_) {
while (true_index_ >= max_index_)
true_index_ -= max_index_;
current_index_ = (int) ffloor (true_index_);
// Switch the data if there is more data in the sequence.
if (rhythm_sequence_.size()) {
cerr << "Switching to new sequence." << endl;
switch_data();
} else {
cerr << "Sequence completed." << endl;
#ifdef TIMESTAMP
TimestampedEvent::AddEvent(&sequence_end_event);
#endif // TIMESTAMP
// Tell all interested clients that the rhythm sequence
// has finished.
/*
using_sequence_ = false;
Pause();
sequence_end_event_->InvokeHandlers(0);
*/
Reset();
}
} else
current_index_ = (int) ffloor (true_index_);
last_time_ = current_time;
//cerr << " true index: " << true_index_;
//cerr << " current index: " << current_index_ << endl;
return current_index_;
}
/////////////////////////////////////////////////////////////////////////////
// Events
ECGDataUpdateEvent::ECGDataUpdateEvent() : v3dEvent(ECG_DATA_UPDATE)
{
}
void ECGDataUpdateEvent::Test()
{
InvokeHandlers(0);
}
ECGDataSwitchEvent::ECGDataSwitchEvent() : v3dEvent(ECG_DATA_SWITCH)
{
}
void ECGDataSwitchEvent::Test()
{
// The data switch event never fires by itself -- it must always be
// fired from within the ECG driver.
return;
}
ECGSequenceEndEvent::ECGSequenceEndEvent() : v3dEvent(ECG_SEQUENCE_END)
{
}
void ECGSequenceEndEvent::Test()
{
// The sequence end event never fires by itself -- it must always be
// fired from within the ECG driver.
return;
}
////////////////////////////////////////////////////////////////////////////
ECG_DATA_TYPE
ECGDriver::GetMaximumValue () const
{
return data_range_.maximum_value;
}
ECG_DATA_TYPE
ECGDriver::GetMinimumValue () const
{
return data_range_.minimum_value;
}
ECG_DATA_RANGE
ECGDriver::GetDataRange () const
{
return data_range_;
}
ECG_DATA_TYPE
ECGDriver::GetData (int index) const
{
return data_list[index];
}
int
ECGDriver::GetCurrentDataIndex () const
{
return current_index_;
}
int
ECGDriver::GetNextDataIndex (int last_index) const
{
return (++last_index >= max_index_) ? 0 : last_index;
}
int
ECGDriver::GetPreviousDataIndex (int last_index) const
{
return (--last_index < 0) ? max_index_ - 1 : last_index;
}
void
ECGDriver::Pause()
{
paused_ = true;
}
void
ECGDriver::Unpause()
{
paused_ = false;
}
bool
ECGDriver::IsPaused() const
{
return paused_;
}
void
ECGDriver::TogglePause()
{
if (paused_)
Unpause();
else
Pause();
}