[med-svn] [SCM] aghermann branch, master, updated. 4b06a66467a52311d413e817136ece62c0e9c24a

Andrei Zavada johnhommer at gmail.com
Sun Jul 21 19:56:37 UTC 2013


The following commit has been merged in the master branch:
commit 6cf9ce525ce7ac2e433c1873e3a0a9fe36878760
Author: Andrei Zavada <johnhommer at gmail.com>
Date:   Sat Jul 20 23:16:46 2013 +0300

    next round of refactoring in libsigfile, around recording_time

diff --git a/src/aghermann/expdesign/recording.hh b/src/aghermann/expdesign/recording.hh
index 07cfedb..8b9ffe7 100644
--- a/src/aghermann/expdesign/recording.hh
+++ b/src/aghermann/expdesign/recording.hh
@@ -15,7 +15,7 @@
 
 #include <cstdarg>
 
-#include "libsigfile/source.hh"
+#include "libsigfile/typed-source.hh"
 #include "libmetrics/all.hh"
 #include "aghermann/model/forward-decls.hh"
 #include "forward-decls.hh"
diff --git a/src/aghermann/rk1968/rk1968.cc b/src/aghermann/rk1968/rk1968.cc
index 29b2e29..a5a80f7 100644
--- a/src/aghermann/rk1968/rk1968.cc
+++ b/src/aghermann/rk1968/rk1968.cc
@@ -13,7 +13,7 @@
 #include <forward_list>
 
 #include "libsigfile/page.hh"
-#include "libsigfile/source.hh"
+#include "libsigfile/typed-source.hh"
 #include "aghermann/expdesign/recording.hh"
 #include "aghermann/expdesign/primaries.hh"
 #include "libmetrics/bands.hh"
diff --git a/src/common/subject_id.cc b/src/common/subject_id.cc
index cacede8..cfd32cf 100644
--- a/src/common/subject_id.cc
+++ b/src/common/subject_id.cc
@@ -182,30 +182,6 @@ update_from( const SSubjectId& j)
 }
 
 
-int
-SSubjectId::
-parse_recording_id_edf_style( const string& s)
-{
-	using namespace agh::str;
-	int_least32_t status = 0;
-	auto subfields = tokens( s, " ");
-	if ( subfields.size() < 4 ) {
-		id = subfields.front();
-		status |= sigfile::CSource::nonconforming_patient_id;
-	} else {
-		if ( subfields.size() > 4 )
-			status |= sigfile::CSource::extra_patientid_subfields;
-		auto i = subfields.begin();
-		id = *i++;
-		gender = agh::SSubjectId::char_to_gender((*i++)[0]);
-		dob = agh::SSubjectId::str_to_dob(*i++);
-		name = join( tokens(*i++, "_"), " ");
-		if ( not valid() )
-			status |= sigfile::CSource::invalid_subject_details;
-	}
-	return status;
-}
-
 
 // Local Variables:
 // Mode: c++
diff --git a/src/libmetrics/mc.cc b/src/libmetrics/mc.cc
index 82b3fed..70fa09d 100644
--- a/src/libmetrics/mc.cc
+++ b/src/libmetrics/mc.cc
@@ -11,7 +11,7 @@
  */
 
 #include "common/lang.hh"
-#include "libsigfile/source.hh"
+#include "libsigfile/typed-source.hh"
 #include "mc.hh"
 
 #if HAVE_CONFIG_H && !defined(VERSION)
diff --git a/src/libmetrics/page-metrics-base.cc b/src/libmetrics/page-metrics-base.cc
index 6de2003..2f261e9 100644
--- a/src/libmetrics/page-metrics-base.cc
+++ b/src/libmetrics/page-metrics-base.cc
@@ -19,7 +19,7 @@
 #include <numeric>
 #include <valarray>
 
-#include "libsigfile/source.hh"
+#include "libsigfile/typed-source.hh"
 #include "page-metrics-base.hh"
 
 #if HAVE_CONFIG_H && !defined(VERSION)
diff --git a/src/libmetrics/page-metrics-base.hh b/src/libmetrics/page-metrics-base.hh
index 219ce0a..2e4d1ae 100644
--- a/src/libmetrics/page-metrics-base.hh
+++ b/src/libmetrics/page-metrics-base.hh
@@ -18,7 +18,7 @@
 
 #include "common/lang.hh"
 #include "common/alg.hh"
-#include "libsigfile/source.hh"
+#include "libsigfile/typed-source.hh"
 #include "forward-decls.hh"
 
 #if HAVE_CONFIG_H && !defined(VERSION)
diff --git a/src/libmetrics/psd.cc b/src/libmetrics/psd.cc
index ed45f6f..7eddf6c 100644
--- a/src/libmetrics/psd.cc
+++ b/src/libmetrics/psd.cc
@@ -26,7 +26,7 @@
 #include "common/lang.hh"
 #include "common/fs.hh"
 #include "libsigproc/sigproc.hh"
-#include "libsigfile/source.hh"
+#include "libsigfile/typed-source.hh"
 #include "psd.hh"
 
 using namespace std;
diff --git a/src/libmetrics/swu.cc b/src/libmetrics/swu.cc
index a5d7969..e1c123c 100644
--- a/src/libmetrics/swu.cc
+++ b/src/libmetrics/swu.cc
@@ -17,7 +17,7 @@
 #include "common/lang.hh"
 #include "common/fs.hh"
 #include "libsigproc/sigproc.hh"
-#include "libsigfile/source.hh"
+#include "libsigfile/typed-source.hh"
 #include "swu.hh"
 
 using namespace std;
diff --git a/src/libsigfile/Makefile.am b/src/libsigfile/Makefile.am
index b861ba6..4dbc020 100644
--- a/src/libsigfile/Makefile.am
+++ b/src/libsigfile/Makefile.am
@@ -11,8 +11,8 @@ libsigfile_la_SOURCES := \
 	channel.hh \
 	source-base.cc \
 	source-base.hh \
-	source.cc \
-	source.hh \
+	typed-source.cc \
+	typed-source.hh \
 	edf.cc \
 	edf-io.cc \
 	edf.hh \
@@ -42,7 +42,7 @@ BUILT_SOURCES := \
 	forward-decls.hh.gch \
 	channel.hh.gch \
 	source-base.hh.gch \
-	source.hh.gch \
+	typed-source.hh.gch \
 	edf.hh.gch \
 	tsv.hh.gch \
 	page.hh.gch
diff --git a/src/libsigfile/edf.cc b/src/libsigfile/edf.cc
index 4890dd4..579b1c3 100644
--- a/src/libsigfile/edf.cc
+++ b/src/libsigfile/edf.cc
@@ -23,7 +23,7 @@
 #include "common/lang.hh"
 #include "common/string.hh"
 #include "edf.hh"
-#include "source.hh"
+#include "typed-source.hh"
 
 using namespace std;
 
@@ -85,15 +85,16 @@ set_reserved( const string& s)
 
 int
 CEDFFile::
-set_start_time( time_t s)
+set_recording_date( const string& s)
 {
-	char b[9];
-	// set start
-	strftime( b, 9, "%d.%m.%y", localtime(&s));
-	memcpy( header.recording_date, b, 8);
-	strftime( b, 9, "%H.%M.%s", localtime(&s));
-	memcpy( header.recording_time, b, 8);
-
+	memcpy( header.recording_date, s.c_str(), 8);
+	return 0;
+}
+int
+CEDFFile::
+set_recording_time( const string& s)
+{
+	memcpy( header.recording_time, s.c_str(), 8);
 	return 0;
 }
 
@@ -341,8 +342,6 @@ CEDFFile (CEDFFile&& rv)
 	data_record_size = rv.data_record_size;
 
 	_subtype    = rv._subtype;
-	_start_time = rv._start_time;
-	_end_time   = rv._end_time;
 
 	swap( _patient_id,   rv._patient_id);
 	swap( _recording_id, rv._recording_id);
@@ -482,7 +481,7 @@ _parse_header()
 
 		if ( !header_length || !n_data_records || !data_record_size || !n_channels ) {
 			_status |= bad_numfld;
-			if ( not (flags() & sigfile::CSource::no_field_consistency_check) )
+			if ( not (_flags & sigfile::CSource::no_field_consistency_check) )
 				return -2;
 		}
 		if ( n_channels == 0 )  {
@@ -499,50 +498,25 @@ _parse_header()
 		_status |=
 			_subject.parse_recording_id_edf_style( _patient_id);
 
-	      // deal with episode and session
-		{
-			int parsed_status;
-			tie (_session, _episode, parsed_status) =
-				figure_session_and_episode();
-			_status |= parsed_status;
-		}
-
-	      // parse times
-		{
-			struct tm ts;
-			char *p;
-			//memset( &ts, 0, sizeof(struct tm));
-			ts.tm_isdst = 0;  // importantly
-			string tmp (header.recording_date, 8);
-			p = strptime( tmp.c_str(), "%d.%m.%y", &ts);
-			if ( p == NULL || *p != '\0' ) {
-				_status |= date_unparsable;
-				if ( not (flags() & sigfile::CSource::no_field_consistency_check) )
-					return -2;
-			}
-			tmp = {string (header.recording_time, 8)};
-			p = strptime( tmp.c_str(), "%H.%M.%S", &ts);
-			if ( p == NULL || *p != '\0' ) {
-				_status |= time_unparsable;
-				if ( not (flags() & sigfile::CSource::no_field_consistency_check) )
-					return -2;
-			}
+	      // times
+		figure_times(
+			string (header.recording_date, 8),
+			string (header.recording_time, 8),
+			TAcceptTimeFormat::edf_strict);
+		if ( _status & bad_datetime && !(_flags & CSource::TFlags::no_field_consistency_check) )
+			return -2;
+		_end_time = _start_time + (time_t)recording_time();
 
-			// if ( ts.tm_year < 50 )
-			// 	ts.tm_year += 100;
-			_start_time = mktime( &ts);
-			if ( _start_time == (time_t)-1 )
-				_status |= (date_unparsable|time_unparsable);
-			else
-				_end_time = _start_time + n_data_records * data_record_size;
-		}
+	      // deal with episode and session
+		tie (_session, _episode) =
+			figure_session_and_episode();
 
 	      // assign "reserved"
 		_reserved = trim( string (header.reserved, 44));
 
 		if ( n_channels > max_channels ) {
 			_status |= bad_numfld;
-			if ( not (flags() & sigfile::CSource::no_field_consistency_check) )
+			if ( not (_flags & sigfile::CSource::no_field_consistency_check) )
 				return -2;
 		} else {
 			channels.resize( n_channels);
@@ -561,7 +535,7 @@ _parse_header()
 						string suggested_type = tt.front();
 						H.ucd = {(tt.pop_front(), agh::str::join( tt, " "))};
 						if ( suggested_type != H.ucd.type_s() )
-							_status |= recognised_channel_conflicting_type;
+							_status |= conflicting_channel_type;
 					} else {
 						H.ucd = sigfile::SChannel (isolated_label);
 
@@ -588,7 +562,7 @@ _parse_header()
 				if ( sscanf( H.header.physical_min, "%8lg",
 					     &H.physical_min) != 1 ) {
 					_status |= bad_numfld;
-					if ( not (flags() & sigfile::CSource::no_field_consistency_check) )
+					if ( not (_flags & sigfile::CSource::no_field_consistency_check) )
 						return -2;
 				}
 			}
@@ -599,7 +573,7 @@ _parse_header()
 				if ( sscanf( H.header.physical_max, "%8lg",
 					     &H.physical_max) != 1 ) {
 					_status |= bad_numfld;
-					if ( not (flags() & sigfile::CSource::no_field_consistency_check) )
+					if ( not (_flags & sigfile::CSource::no_field_consistency_check) )
 						return -2;
 				}
 			}
@@ -611,7 +585,7 @@ _parse_header()
 				if ( sscanf( H.header.digital_min, "%8d",
 					     &H.digital_min) != 1 ) {
 					_status |= bad_numfld;
-					if ( not (flags() & sigfile::CSource::no_field_consistency_check) )
+					if ( not (_flags & sigfile::CSource::no_field_consistency_check) )
 						return -2;
 				}
 			}
@@ -622,7 +596,7 @@ _parse_header()
 				if ( sscanf( H.header.digital_max, "%8d",
 					     &H.digital_max) != 1 ) {
 					_status |= bad_numfld;
-					if ( not (flags() & sigfile::CSource::no_field_consistency_check) )
+					if ( not (_flags & sigfile::CSource::no_field_consistency_check) )
 						return -2;
 				}
 			}
@@ -638,7 +612,7 @@ _parse_header()
 					strtoul( t.c_str(), &tail, 10);
 				if ( tail == NULL || *tail != '\0' ) {
 					_status |= bad_numfld;
-					if ( not (flags() & sigfile::CSource::no_field_consistency_check) )
+					if ( not (_flags & sigfile::CSource::no_field_consistency_check) )
 						return -2;
 				}
 			}
@@ -651,7 +625,7 @@ _parse_header()
 		return -1;
 	} catch (invalid_argument ex) {
 		_status |= bad_numfld;
-		if ( not (flags() & sigfile::CSource::no_field_consistency_check) )
+		if ( not (_flags & sigfile::CSource::no_field_consistency_check) )
 			return -3;
 	}
 
@@ -868,48 +842,48 @@ CEDFFile::
 explain_status( const int status)
 {
 	list<string> recv;
-	if ( status & sysfail )
-		recv.emplace_back( "* stat or fopen error");
-	if ( status & bad_header )
-		recv.emplace_back( "* Ill-formed header");
 	if ( status & bad_version )
-		recv.emplace_back( "* Bad Version signature (i.e., not an EDF file)");
-	if ( status & missing_patient_id )
-		recv.emplace_back( "* Missing PatientId");
-	if ( status & bad_numfld )
-		recv.emplace_back( "* Garbage in numerical fields");
-	if ( status & date_unparsable )
-		recv.emplace_back( "* Date field ill-formed");
-	if ( status & time_unparsable )
-		recv.emplace_back( "* Time field ill-formed");
-	if ( status & (nosession|noepisode) )
-		recv.emplace_back( "* No session/episode information in RecordingID");
-	if ( status & non1020_channel )
-		recv.emplace_back( "* Channel designation not following the 10-20 system");
+		recv.emplace_back( "Bad Version signature (i.e., not an EDF file)");
 	if ( status & nonconforming_patient_id )
-		recv.emplace_back( "* PatientId not conforming to section 2.1.3.3 of EDF spec");
-	if ( status & invalid_subject_details )
-		recv.emplace_back( "* PatientId has incomplete or ill-formed subject details");
-	if ( status & nonkemp_signaltype )
-		recv.emplace_back( "* Signal type not listed in Kemp et al");
-	if ( status & dup_channels )
-		recv.emplace_back( "* Duplicate channel names");
-	if ( status & nogain )
-		recv.emplace_back( "* Physical or Digital Min value greater than Max");
-	if ( status & too_many_channels )
-		recv.emplace_back( string("* Number of channels grearter than ") + to_string(max_channels));
+		recv.emplace_back( "PatientId not conforming to section 2.1.3.3 of EDF spec");
 	if ( status & file_truncated )
-		recv.emplace_back( "* File truncated");
+		recv.emplace_back( "File truncated");
 	if ( status & trailing_junk )
-		recv.emplace_back( "* File has trailing junk");
+		recv.emplace_back( "File has trailing junk");
 	if ( status & extra_patientid_subfields )
-		recv.emplace_back( "* Extra subfields in PatientId");
-	if ( status & recognised_channel_conflicting_type )
-		recv.emplace_back( "* Explicitly specified signal type does not match type of known channel name");
+		recv.emplace_back( "Extra subfields in PatientId");
 	if ( status & mmap_error )
-		recv.emplace_back( "* mmap error");
+		recv.emplace_back( "mmap error");
+
+	return CSource::explain_status(status) + (recv.empty() ? "" : (join(recv, "\n") + '\n'));
+}
+
+
 
-	return join(recv, "\n");
+
+
+int
+agh::SSubjectId::
+parse_recording_id_edf_style( const string& s)
+{
+	using namespace agh::str;
+	int_least32_t status = 0;
+	auto subfields = tokens( s, " ");
+	if ( subfields.size() < 4 ) {
+		id = subfields.front();
+		status |= sigfile::CEDFFile::nonconforming_patient_id;
+	} else {
+		if ( subfields.size() > 4 )
+			status |= sigfile::CEDFFile::extra_patientid_subfields;
+		auto i = subfields.begin();
+		id = *i++;
+		gender = agh::SSubjectId::char_to_gender((*i++)[0]);
+		dob = agh::SSubjectId::str_to_dob(*i++);
+		name = join( tokens(*i++, "_"), " ");
+		if ( not valid() )
+			status |= sigfile::CSource::invalid_subject_details;
+	}
+	return status;
 }
 
 
diff --git a/src/libsigfile/edf.hh b/src/libsigfile/edf.hh
index cbf1d9c..8d661ba 100644
--- a/src/libsigfile/edf.hh
+++ b/src/libsigfile/edf.hh
@@ -36,7 +36,6 @@ using namespace std;
 namespace sigfile {
 
 
-
 class CEDFFile
   : public CSource {
 
@@ -91,7 +90,8 @@ class CEDFFile
       // interface
 	// status
 	string explain_status() const
-		{ return explain_status( _status); }
+		{ return move(explain_status( _status)); }
+	static string explain_status( int);
 
 	// identification
 	const char* patient_id() const
@@ -106,10 +106,6 @@ class CEDFFile
 		{ return _reserved.c_str(); }
 
 	// times
-	time_t start_time() const
-		{ return _start_time; }
-	time_t end_time() const
-		{ return _end_time; }
 	double recording_time() const // in seconds
 		{ return n_data_records * data_record_size; }
 
@@ -122,7 +118,9 @@ class CEDFFile
 		{ return 1; }
 	int set_reserved( const string&); // but you can clobber "reserved" field if you must
 
-	int set_start_time( time_t);
+	int set_recording_date( const string&);
+	int set_recording_time( const string&);
+
 	// channels
 	size_t n_channels() const
 		{ return channels.size(); }
@@ -363,12 +361,13 @@ class CEDFFile
 		trailing_junk             = (1 << (COMMON_STATUS_BITS + 3)),
 		mmap_error		  = (1 << (COMMON_STATUS_BITS + 4)),
 		nogain			  = (1 << (COMMON_STATUS_BITS + 5)),
-		recognised_channel_conflicting_type = (1 << (COMMON_STATUS_BITS + 6)),
+		nonconforming_patient_id  = (1 << (COMMON_STATUS_BITS + 6)),
+		extra_patientid_subfields = (1 << (COMMON_STATUS_BITS + 7)),
 
 		inoperable		 = (bad_header
 					   | bad_version
 					   | bad_numfld
-					   | date_unparsable | time_unparsable
+					   | bad_datetime
 					   | dup_channels
 					   | nogain
 					   | sysfail
@@ -376,14 +375,10 @@ class CEDFFile
 					   | file_truncated
 					   | mmap_error)
 	};
-	static string explain_status( int);
 
     private:
 	TSubtype _subtype;
 
-	time_t	_start_time,
-		_end_time;
-
 	string	_patient_id, // this is trimmed, raw; parsed into SSubjectId fields
 		_recording_id,
        // take care of file being named 'episode-1.edf'
diff --git a/src/libsigfile/source-base.cc b/src/libsigfile/source-base.cc
index 4b36926..e9a7340 100644
--- a/src/libsigfile/source-base.cc
+++ b/src/libsigfile/source-base.cc
@@ -269,22 +269,40 @@ CSource (CSource&& rv)
       : _subject (move(rv._subject))
 {
 	swap( _filename, rv._filename);
-	_status = rv._status;
-	_flags = rv._flags;
+	_status     = rv._status;
+	_flags      = rv._flags;
+
+	_start_time = rv._start_time;
+	_end_time   = rv._end_time;
 }
 
 
 
 
+int
+CSource::
+set_start_time( time_t s)
+{
+	_end_time = (_start_time = s)
+		+ (time_t)recording_time();
+
+	char b[9];
+	strftime( b, 9, "%d.%m.%y", localtime(&s));
+	set_recording_date( b);
+	strftime( b, 9, "%H.%M.%s", localtime(&s));
+	set_recording_time( b);
 
+	return 0;
+}
 
 
 
-tuple<string, string, int>
+
+
+tuple<string, string>
 CSource::
 figure_session_and_episode()
 {
-	int status = 0;
 	string session, episode;
 
 	// (a) parsed from RecordingID_raw
@@ -297,7 +315,7 @@ figure_session_and_episode()
 	     sscanf( rec_id_isolated.c_str(), T " (" T ")", int_session, int_episode) == 2 )
 		;
 	else
-		status = (nosession|noepisode);
+		_status |= bad_session_or_episode;
 #undef T
 
 	// (b) identified from file name
@@ -315,7 +333,7 @@ figure_session_and_episode()
 			fn_episode.erase( sz-2, 2);
 	}
 
-	if ( status ) { // (a) failed
+	if ( _status & bad_session_or_episode ) { // (a) failed
 		episode.assign( fn_episode);    // use RecordingID_raw as Session
 		session.assign( rec_id_isolated);
 	} else {
@@ -323,12 +341,35 @@ figure_session_and_episode()
 		session.assign( int_session);
 	}
 
-	return make_tuple( session, episode, status);
+	return make_tuple( session, episode);
 }
 
 
 
 
+void
+CSource::
+figure_times( const string& date_s, const string& time_s, TAcceptTimeFormat option)
+{
+	struct tm ts;
+	char *p;
+	//memset( &ts, 0, sizeof(struct tm));
+	ts.tm_isdst = 0;  // importantly
+	p = strptime( date_s.c_str(), "%d.%m.%y", &ts);
+	if ( p == NULL || *p != '\0' ) {
+		_status |= bad_datetime;
+	}
+	p = strptime( time_s.c_str(), "%H.%M.%S", &ts);
+	if ( p == NULL || *p != '\0' ) {
+		_status |= bad_datetime;
+	}
+
+	// if ( ts.tm_year < 50 )
+	// 	ts.tm_year += 100;
+	_start_time = mktime( &ts);
+	if ( _start_time == (time_t)-1 )
+		_status |= bad_datetime;
+}
 
 
 
@@ -463,6 +504,41 @@ export_filtered( const int h,
 }
 
 
+
+string
+CSource::
+explain_status( const int status)
+{
+	list<string> recv;
+	if ( status & sysfail )
+		recv.emplace_back( "stat or fopen error");
+	if ( status & bad_header )
+		recv.emplace_back( "Ill-formed header");
+	if ( status & missing_patient_id )
+		recv.emplace_back( "Missing PatientId");
+	if ( status & bad_numfld )
+		recv.emplace_back( "Garbage in numerical fields");
+	if ( status & bad_datetime )
+		recv.emplace_back( "Date/time field ill-formed");
+	if ( status & bad_session_or_episode )
+		recv.emplace_back( "No session/episode information in RecordingID");
+	if ( status & non1020_channel )
+		recv.emplace_back( "Channel designation not following the 10-20 system");
+	if ( status & invalid_subject_details )
+		recv.emplace_back( "PatientId has incomplete or ill-formed subject details");
+	if ( status & nonkemp_signaltype )
+		recv.emplace_back( "Signal type not listed in Kemp et al");
+	if ( status & dup_channels )
+		recv.emplace_back( "Duplicate channel names");
+	if ( status & too_many_channels )
+		recv.emplace_back( string("Number of channels grearter than ") + to_string(max_channels));
+	if ( status & conflicting_channel_type )
+		recv.emplace_back( "Explicitly specified signal type does not match type of known channel name");
+
+	return recv.empty() ? "" : agh::str::join(recv, "\n") + "\n";
+}
+
+
 // Local Variables:
 // Mode: c++
 // indent-tabs-mode: 8
diff --git a/src/libsigfile/source-base.hh b/src/libsigfile/source-base.hh
index bea96f8..a96dd32 100644
--- a/src/libsigfile/source-base.hh
+++ b/src/libsigfile/source-base.hh
@@ -225,22 +225,18 @@ class CSource {
 		ok			  = 0,
 		bad_header		  = (1 <<  0),
 		bad_numfld		  = (1 <<  1),
-		date_unparsable		  = (1 <<  2),
-		time_unparsable		  = (1 <<  3),
-		nosession		  = (1 <<  4),
-		noepisode		  = (1 <<  5),
-		nonkemp_signaltype	  = (1 <<  6),
-		non1020_channel		  = (1 <<  7),
-		dup_channels		  = (1 <<  8),
-		sysfail			  = (1 <<  9),
-		too_many_channels	  = (1 << 10),
-		nonconforming_patient_id  = (1 << 11),
-		missing_patient_id        = (1 << 12),
-		invalid_subject_details   = (1 << 13),
-		extra_patientid_subfields = (1 << 14),
-		bad_channel_count         = (1 << 15)
+		bad_datetime		  = (1 <<  2),
+		bad_session_or_episode	  = (1 <<  3),
+		nonkemp_signaltype	  = (1 <<  4),
+		non1020_channel		  = (1 <<  5),
+		dup_channels		  = (1 <<  6),
+		sysfail			  = (1 <<  7),
+		too_many_channels	  = (1 <<  8),
+		missing_patient_id        = (1 <<  9),
+		invalid_subject_details   = (1 << 10),
+		conflicting_channel_type  = (1 << 11),
 	};
-	const static unsigned COMMON_STATUS_BITS = 15;
+	const static unsigned COMMON_STATUS_BITS = 11;
     protected:
 	string	_filename;
 
@@ -274,7 +270,10 @@ class CSource {
 	int status()	const { return _status; }
 	int flags()	const { return _flags; }
 
-	virtual string explain_status()			const = 0;
+	static string explain_status( int);
+	virtual string explain_status() const
+		{ return move(explain_status( _status)); }
+
 	enum TDetails { with_channels = 1, with_annotations = 2 };
 	virtual string details( int which_details)	const = 0;
 
@@ -295,10 +294,18 @@ class CSource {
 	virtual const char* session()			const = 0;
 
       // recording time and duration
-	virtual time_t start_time()			const = 0;
-	virtual time_t end_time()			const = 0;
+	time_t	_start_time,
+		_end_time;
+	virtual time_t start_time() const
+		{ return _start_time; }
+	virtual time_t end_time() const
+		{ return _end_time; }
 	virtual double recording_time()			const = 0;
 
+	virtual int set_start_time( time_t);
+	virtual int set_recording_date( const string&) = 0;
+	virtual int set_recording_time( const string&) = 0;
+
       // channels
 	const static size_t max_channels = 1024;
 
@@ -352,7 +359,6 @@ class CSource {
 	virtual int set_episode( const string&)	      = 0;
 	virtual int set_session( const string&)	      = 0;
 	virtual int set_comment( const string&)	      = 0;
-	virtual int set_start_time( time_t)	      = 0;
 
       // get samples
 	// original
@@ -437,8 +443,12 @@ class CSource {
 		}
 
       // supporting functions
-	tuple<string, string, int>
-	figure_session_and_episode();
+	tuple<string, string>
+	figure_session_and_episode(); // hand over the pair separately for specific ways to store these fields in derived classes
+
+	enum class TAcceptTimeFormat { edf_strict, any };
+	void
+	figure_times( const string&, const string&, TAcceptTimeFormat);
 };
 
 
diff --git a/src/libsigfile/tsv.cc b/src/libsigfile/tsv.cc
index b4799ce..176cde8 100644
--- a/src/libsigfile/tsv.cc
+++ b/src/libsigfile/tsv.cc
@@ -21,7 +21,7 @@
 #include "common/lang.hh"
 #include "common/string.hh"
 #include "tsv.hh"
-#include "source.hh"
+#include "typed-source.hh"
 
 using namespace std;
 
@@ -33,19 +33,6 @@ using agh::str::tokens_trimmed;
 
 using sigfile::CTSVFile;
 
-int
-CTSVFile::
-set_start_time( time_t s)
-{
-	char b[9];
-	strftime( b, 9, "%d.%m.%y", localtime(&s));
-	metadata["recording_date"].assign( b);
-	strftime( b, 9, "%H.%M.%s", localtime(&s));
-	metadata["recording_time"].assign( b);
-
-	return 0;
-}
-
 
 
 
@@ -126,8 +113,6 @@ CTSVFile (CTSVFile&& rv)
 	swap( metadata, rv.metadata);
 
 	_subtype    = rv._subtype;
-	_start_time = rv._start_time;
-	_end_time   = rv._end_time;
 
 	swap( channels, rv.channels);
 	swap( common_annotations, rv.common_annotations);
@@ -145,7 +130,7 @@ CTSVFile (CTSVFile&& rv)
 CTSVFile::
 ~CTSVFile ()
 {
-	if ( not (flags() & sigfile::CSource::no_ancillary_files) )
+	if ( not (_flags & sigfile::CSource::no_ancillary_files) )
 		save_ancillary_files();
 	if ( _line0 )
 		free( (void*)_line0);
@@ -153,6 +138,20 @@ CTSVFile::
 
 
 
+int
+CTSVFile::
+set_recording_date( const string& s)
+{
+	metadata["recording_date"] = s;
+	return 0;
+}
+int
+CTSVFile::
+set_recording_time( const string& s)
+{
+	metadata["recording_time"] = s;
+	return 0;
+}
 
 
 int
@@ -181,7 +180,7 @@ _parse_header()
       // 2. pick essential bits
 	if ( metadata.find( "recording_id") == metadata.end() ) {
 		fprintf( stderr, "No session/episode in header\n");
-		_status |= (nosession | noepisode);
+		_status |= bad_session_or_episode;
 		return -1;
 	}
 
@@ -191,6 +190,20 @@ _parse_header()
 		return -1;
 	}
 
+	if ( metadata.find( "recording_date") == metadata.end() ||
+	     metadata.find( "recording_time") == metadata.end() ) {
+		fprintf( stderr, "No recording_date in header\n");
+		_status |= CSource::bad_datetime;
+		return -1;
+	}
+
+	figure_times(
+		metadata["recording_date"],
+		metadata["recording_time"],
+		TAcceptTimeFormat::any);
+	if ( _status & bad_datetime && !(_flags & CSource::TFlags::no_field_consistency_check) )
+		return -1;
+
 	if ( metadata.find( "comment") == metadata.end() )
 		;
 
@@ -210,11 +223,8 @@ _parse_header()
 		channels.emplace_back( h);
 
       // 3. deal with episode and session
-	int parsed_with_issues;
-	tie( _session, _episode, parsed_with_issues) =
+	tie( _session, _episode) =
 		figure_session_and_episode();
-	if ( parsed_with_issues )
-		_status |= (nosession | noepisode);
 
       // 4. are channels unique?
 	for ( auto &H : channels )
@@ -264,7 +274,6 @@ outer_break:
 		return -1;
 	}
 
-	printf( "read %zu samples in %zu channels\n", ll/channels.size(), channels.size());
 	// vector -> valarray
 	for ( size_t h = 0; h < channels.size(); ++h ) {
 		channels[h].data.resize( ll);
@@ -272,6 +281,10 @@ outer_break:
 			channels[h].data[i] = c2[h][i];
 	}
 
+
+	// only now as late
+	_end_time = _start_time + (time_t)recording_time();
+
 	return 0;
 }
 
@@ -355,37 +368,9 @@ CTSVFile::
 explain_status( const int status)
 {
 	list<string> recv;
-	if ( status & sysfail )
-		recv.emplace_back( "* stat or fopen error");
-	if ( status & bad_header )
-		recv.emplace_back( "* Ill-formed header");
-	if ( status & missing_patient_id )
-		recv.emplace_back( "* Missing PatientId");
-	if ( status & bad_numfld )
-		recv.emplace_back( "* Garbage in numerical fields");
-	if ( status & date_unparsable )
-		recv.emplace_back( "* Date field ill-formed");
-	if ( status & time_unparsable )
-		recv.emplace_back( "* Time field ill-formed");
-	if ( status & nosession )
-		recv.emplace_back( "* No session information in field RecordingID");
-	if ( status & non1020_channel )
-		recv.emplace_back( "* Channel designation not following the 10-20 system");
-	if ( status & nonconforming_patient_id )
-		recv.emplace_back( "* PatientId not conforming to section 2.1.3.3 of EDF spec");
-	if ( status & invalid_subject_details )
-		recv.emplace_back( "* PatientId has incomplete or ill-formed subject details");
-	if ( status & nonkemp_signaltype )
-		recv.emplace_back( "* Signal type not listed in Kemp et al");
-	if ( status & dup_channels )
-		recv.emplace_back( "* Duplicate channel names");
-	if ( status & too_many_channels )
-		recv.emplace_back( string("* Number of channels grearter than ") + to_string(max_channels));
-	if ( status & extra_patientid_subfields )
-		recv.emplace_back( "* Extra subfields in PatientId");
 	if ( status & bad_channel_count )
-		recv.emplace_back( "* Number of channels declared in header different from number of columns of data");
-	return join(recv, "\n");
+		recv.emplace_back( "Number of channels declared in header different from number of columns of data");
+	return CSource::explain_status(status) + (recv.empty() ? "" : (join(recv, "\n") + '\n'));
 }
 
 
diff --git a/src/libsigfile/tsv.hh b/src/libsigfile/tsv.hh
index 4d84c3e..2a8b061 100644
--- a/src/libsigfile/tsv.hh
+++ b/src/libsigfile/tsv.hh
@@ -111,12 +111,10 @@ class CTSVFile
 		{ return _session.c_str(); }
 
 	// times
-	time_t start_time() const
-		{ return _start_time; }
-	time_t end_time() const
-		{ return _end_time; }
 	double recording_time() const // in seconds
 		{ return (double)channels.front().data.size() / _samplerate; } // all channels have the same sr, obviously
+	int set_recording_date( const string&);
+	int set_recording_time( const string&);
 
 	// setters
 	int set_patient_id( const string& s)
@@ -146,8 +144,6 @@ class CTSVFile
 			return 0;
 		}
 
-	int set_start_time( time_t);
-
 	// channels
 	size_t n_channels() const
 		{ return channels.size(); }
@@ -335,9 +331,10 @@ class CTSVFile
 
 
 	enum TStatus : int_least32_t {
+		bad_channel_count         = (1 << (COMMON_STATUS_BITS + 1)),
 		inoperable		 = (bad_header
 					   | bad_numfld
-					   | date_unparsable | time_unparsable
+					   | bad_datetime
 					   | dup_channels
 					   | sysfail
 					   | too_many_channels)
@@ -352,8 +349,6 @@ class CTSVFile
 	TSubtype _subtype;
 
 	size_t	_samplerate;
-	time_t	_start_time,
-		_end_time;
 
 	FILE	*_f;
 	char	*_line0;
diff --git a/src/libsigfile/source.cc b/src/libsigfile/typed-source.cc
similarity index 96%
rename from src/libsigfile/source.cc
rename to src/libsigfile/typed-source.cc
index cceb968..26049a2 100644
--- a/src/libsigfile/source.cc
+++ b/src/libsigfile/typed-source.cc
@@ -1,5 +1,5 @@
 /*
- *       File name:  libsigfile/source.cc
+ *       File name:  libsigfile/typed-source.cc
  *         Project:  Aghermann
  *          Author:  Andrei Zavada <johnhommer at gmail.com>
  * Initial version:  2011-11-14
@@ -10,7 +10,7 @@
  */
 
 
-#include "source.hh"
+#include "typed-source.hh"
 #include "edf.hh"
 #include "tsv.hh"
 
diff --git a/src/libsigfile/source.hh b/src/libsigfile/typed-source.hh
similarity index 97%
rename from src/libsigfile/source.hh
rename to src/libsigfile/typed-source.hh
index b82261c..f372b92 100644
--- a/src/libsigfile/source.hh
+++ b/src/libsigfile/typed-source.hh
@@ -1,5 +1,5 @@
 /*
- *       File name:  libsigfile/source.hh
+ *       File name:  libsigfile/typed-source.hh
  *         Project:  Aghermann
  *          Author:  Andrei Zavada <johnhommer at gmail.com>
  * Initial version:  2011-11-11
diff --git a/src/tools/agh-profile-gen.cc b/src/tools/agh-profile-gen.cc
index 58e6144..e86c8c6 100644
--- a/src/tools/agh-profile-gen.cc
+++ b/src/tools/agh-profile-gen.cc
@@ -23,8 +23,7 @@
 #include "common/alg.hh"
 #include "common/fs.hh"
 #include "common/string.hh"
-#include "libsigfile/edf.hh"
-#include "libsigfile/source.hh"
+#include "libsigfile/typed-source.hh"
 #include "libmetrics/all.hh"
 
 #if HAVE_CONFIG_H && !defined(VERSION)
diff --git a/src/tools/edfcat.cc b/src/tools/edfcat.cc
index afb5b8c..ad72592 100644
--- a/src/tools/edfcat.cc
+++ b/src/tools/edfcat.cc
@@ -21,7 +21,7 @@
 #include <fstream>
 #include "libsigproc/sigproc.hh"
 #include "libsigfile/edf.hh"
-#include "libsigfile/source.hh"
+#include "libsigfile/typed-source.hh"
 #include "common/alg.hh"
 #include "common/fs.hh"
 #include "common/string.hh"
diff --git a/src/tools/edfhed-gtk.cc b/src/tools/edfhed-gtk.cc
index 4a81384..091b394 100644
--- a/src/tools/edfhed-gtk.cc
+++ b/src/tools/edfhed-gtk.cc
@@ -12,7 +12,7 @@
 
 #include <gtk/gtk.h>
 #include "libsigfile/edf.hh"
-#include "libsigfile/source.hh"
+#include "libsigfile/typed-source.hh"
 
 
 void
diff --git a/src/tools/edfhed.cc b/src/tools/edfhed.cc
index 15cdb0d..1c96000 100644
--- a/src/tools/edfhed.cc
+++ b/src/tools/edfhed.cc
@@ -18,7 +18,7 @@
 
 //#include <argp.h>
 #include "common/fs.hh"
-#include "libsigfile/source.hh"
+#include "libsigfile/typed-source.hh"
 #include "libsigfile/edf.hh"
 
 // there's some deep and curious issue argp.h brings up if it is
@@ -261,8 +261,7 @@ set_recording_datetime_from_mtime( sigfile::CEDFFile& F)
 static int
 set_mtime_from_recording_datetime( sigfile::CEDFFile& F)
 {
-	if ( F.status() & sigfile::CEDFFile::date_unparsable ||
-	     F.status() & sigfile::CEDFFile::time_unparsable ) {
+	if ( F.status() & sigfile::CSource::bad_datetime ) {
 		fprintf( stderr, "Error: Bad recording_date or _time fields; not setting file mtime");
 		return -1;
 	}

-- 
Sleep experiment manager



More information about the debian-med-commit mailing list